blob: 451bb565b09f832e428f2183005e51d8ee9c52b4 [file] [log] [blame]
Willie Ruan0d9acd92011-07-04 21:31:30 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13/*
14 * Qualcomm PMIC8058 PWM driver
15 *
16 */
17
Willie Ruan0d9acd92011-07-04 21:31:30 -070018#define pr_fmt(fmt) "%s: " fmt, __func__
19
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <linux/module.h>
21#include <linux/platform_device.h>
22#include <linux/err.h>
23#include <linux/pwm.h>
24#include <linux/mfd/pmic8058.h>
25#include <linux/pmic8058-pwm.h>
26#include <linux/slab.h>
27
28#define PM8058_LPG_BANKS 8
29#define PM8058_PWM_CHANNELS PM8058_LPG_BANKS /* MAX=8 */
30
31#define PM8058_LPG_CTL_REGS 7
32
33/* PMIC8058 LPG/PWM */
34#define SSBI_REG_ADDR_LPG_CTL_BASE 0x13C
35#define SSBI_REG_ADDR_LPG_CTL(n) (SSBI_REG_ADDR_LPG_CTL_BASE + (n))
36#define SSBI_REG_ADDR_LPG_BANK_SEL 0x143
37#define SSBI_REG_ADDR_LPG_BANK_EN 0x144
38#define SSBI_REG_ADDR_LPG_LUT_CFG0 0x145
39#define SSBI_REG_ADDR_LPG_LUT_CFG1 0x146
40#define SSBI_REG_ADDR_LPG_TEST 0x147
41
42/* Control 0 */
43#define PM8058_PWM_1KHZ_COUNT_MASK 0xF0
44#define PM8058_PWM_1KHZ_COUNT_SHIFT 4
45
46#define PM8058_PWM_1KHZ_COUNT_MAX 15
47
48#define PM8058_PWM_OUTPUT_EN 0x08
49#define PM8058_PWM_PWM_EN 0x04
50#define PM8058_PWM_RAMP_GEN_EN 0x02
51#define PM8058_PWM_RAMP_START 0x01
52
53#define PM8058_PWM_PWM_START (PM8058_PWM_OUTPUT_EN \
54 | PM8058_PWM_PWM_EN)
55#define PM8058_PWM_RAMP_GEN_START (PM8058_PWM_RAMP_GEN_EN \
56 | PM8058_PWM_RAMP_START)
57
58/* Control 1 */
59#define PM8058_PWM_REVERSE_EN 0x80
60#define PM8058_PWM_BYPASS_LUT 0x40
61#define PM8058_PWM_HIGH_INDEX_MASK 0x3F
62
63/* Control 2 */
64#define PM8058_PWM_LOOP_EN 0x80
65#define PM8058_PWM_RAMP_UP 0x40
66#define PM8058_PWM_LOW_INDEX_MASK 0x3F
67
68/* Control 3 */
69#define PM8058_PWM_VALUE_BIT7_0 0xFF
70#define PM8058_PWM_VALUE_BIT5_0 0x3F
71
72/* Control 4 */
73#define PM8058_PWM_VALUE_BIT8 0x80
74
75#define PM8058_PWM_CLK_SEL_MASK 0x60
76#define PM8058_PWM_CLK_SEL_SHIFT 5
77
78#define PM8058_PWM_CLK_SEL_NO 0
79#define PM8058_PWM_CLK_SEL_1KHZ 1
80#define PM8058_PWM_CLK_SEL_32KHZ 2
81#define PM8058_PWM_CLK_SEL_19P2MHZ 3
82
83#define PM8058_PWM_PREDIVIDE_MASK 0x18
84#define PM8058_PWM_PREDIVIDE_SHIFT 3
85
86#define PM8058_PWM_PREDIVIDE_2 0
87#define PM8058_PWM_PREDIVIDE_3 1
88#define PM8058_PWM_PREDIVIDE_5 2
89#define PM8058_PWM_PREDIVIDE_6 3
90
91#define PM8058_PWM_M_MASK 0x07
92#define PM8058_PWM_M_MIN 0
93#define PM8058_PWM_M_MAX 7
94
95/* Control 5 */
96#define PM8058_PWM_PAUSE_COUNT_HI_MASK 0xFC
97#define PM8058_PWM_PAUSE_COUNT_HI_SHIFT 2
98
99#define PM8058_PWM_PAUSE_ENABLE_HIGH 0x02
100#define PM8058_PWM_SIZE_9_BIT 0x01
101
102/* Control 6 */
103#define PM8058_PWM_PAUSE_COUNT_LO_MASK 0xFC
104#define PM8058_PWM_PAUSE_COUNT_LO_SHIFT 2
105
106#define PM8058_PWM_PAUSE_ENABLE_LOW 0x02
107#define PM8058_PWM_RESERVED 0x01
108
109#define PM8058_PWM_PAUSE_COUNT_MAX 56 /* < 2^6 = 64*/
110
111/* LUT_CFG1 */
112#define PM8058_PWM_LUT_READ 0x40
113
114/* TEST */
115#define PM8058_PWM_DTEST_MASK 0x38
116#define PM8058_PWM_DTEST_SHIFT 3
117
118#define PM8058_PWM_DTEST_BANK_MASK 0x07
119
120/* PWM frequency support
121 *
122 * PWM Frequency = Clock Frequency / (N * T)
123 * or
124 * PWM Period = Clock Period * (N * T)
125 * where
126 * N = 2^9 or 2^6 for 9-bit or 6-bit PWM size
127 * T = Pre-divide * 2^m, m = 0..7 (exponent)
128 *
129 * We use this formula to figure out m for the best pre-divide and clock:
130 * (PWM Period / N) / 2^m = (Pre-divide * Clock Period)
131*/
132#define NUM_CLOCKS 3
133
134#define NSEC_1000HZ (NSEC_PER_SEC / 1000)
135#define NSEC_32768HZ (NSEC_PER_SEC / 32768)
136#define NSEC_19P2MHZ (NSEC_PER_SEC / 19200000)
137
138#define CLK_PERIOD_MIN NSEC_19P2MHZ
139#define CLK_PERIOD_MAX NSEC_1000HZ
140
141#define NUM_PRE_DIVIDE 3 /* No default support for pre-divide = 6 */
142
143#define PRE_DIVIDE_0 2
144#define PRE_DIVIDE_1 3
145#define PRE_DIVIDE_2 5
146
147#define PRE_DIVIDE_MIN PRE_DIVIDE_0
148#define PRE_DIVIDE_MAX PRE_DIVIDE_2
149
150static char *clks[NUM_CLOCKS] = {
151 "1K", "32768", "19.2M"
152};
153
154static unsigned pre_div[NUM_PRE_DIVIDE] = {
155 PRE_DIVIDE_0, PRE_DIVIDE_1, PRE_DIVIDE_2
156};
157
158static unsigned int pt_t[NUM_PRE_DIVIDE][NUM_CLOCKS] = {
159 { PRE_DIVIDE_0 * NSEC_1000HZ,
160 PRE_DIVIDE_0 * NSEC_32768HZ,
161 PRE_DIVIDE_0 * NSEC_19P2MHZ,
162 },
163 { PRE_DIVIDE_1 * NSEC_1000HZ,
164 PRE_DIVIDE_1 * NSEC_32768HZ,
165 PRE_DIVIDE_1 * NSEC_19P2MHZ,
166 },
167 { PRE_DIVIDE_2 * NSEC_1000HZ,
168 PRE_DIVIDE_2 * NSEC_32768HZ,
169 PRE_DIVIDE_2 * NSEC_19P2MHZ,
170 },
171};
172
173#define MIN_MPT ((PRE_DIVIDE_MIN * CLK_PERIOD_MIN) << PM8058_PWM_M_MIN)
174#define MAX_MPT ((PRE_DIVIDE_MAX * CLK_PERIOD_MAX) << PM8058_PWM_M_MAX)
175
176/* Private data */
177struct pm8058_pwm_chip;
178
179struct pwm_device {
180 int pwm_id; /* = bank/channel id */
181 int in_use;
182 const char *label;
Willie Ruand3337ed2011-07-04 23:16:22 -0700183 struct pm8058_pwm_period period;
184 int pwm_value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700185 int pwm_period;
186 int pwm_duty;
187 u8 pwm_ctl[PM8058_LPG_CTL_REGS];
188 int irq;
189 struct pm8058_pwm_chip *chip;
190};
191
192struct pm8058_pwm_chip {
193 struct pwm_device pwm_dev[PM8058_PWM_CHANNELS];
194 u8 bank_mask;
195 struct mutex pwm_mutex;
196 struct pm8058_chip *pm_chip;
197 struct pm8058_pwm_pdata *pdata;
198};
199
200static struct pm8058_pwm_chip *pwm_chip;
201
Willie Ruand3337ed2011-07-04 23:16:22 -0700202struct pm8058_pwm_lut {
203 /* LUT parameters */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204 int lut_duty_ms;
205 int lut_lo_index;
206 int lut_hi_index;
207 int lut_pause_hi;
208 int lut_pause_lo;
209 int flags;
210};
211
212static u16 duty_msec[PM8058_PWM_1KHZ_COUNT_MAX + 1] = {
213 0, 1, 2, 3, 4, 6, 8, 16, 18, 24, 32, 36, 64, 128, 256, 512
214};
215
216static u16 pause_count[PM8058_PWM_PAUSE_COUNT_MAX + 1] = {
217 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
218 23, 28, 31, 42, 47, 56, 63, 83, 94, 111, 125, 167, 188, 222, 250, 333,
219 375, 500, 667, 750, 800, 900, 1000, 1100,
220 1200, 1300, 1400, 1500, 1600, 1800, 2000, 2500,
221 3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500,
222 7000
223};
224
225/* Internal functions */
Willie Ruanefc2fa42011-07-05 00:12:41 -0700226static void pm8058_pwm_save(u8 *u8p, u8 mask, u8 val)
227{
228 *u8p &= ~mask;
229 *u8p |= val & mask;
230}
231
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700232static int pm8058_pwm_bank_enable(struct pwm_device *pwm, int enable)
233{
234 int rc;
235 u8 reg;
236 struct pm8058_pwm_chip *chip;
237
238 chip = pwm->chip;
239
240 if (enable)
241 reg = chip->bank_mask | (1 << pwm->pwm_id);
242 else
243 reg = chip->bank_mask & ~(1 << pwm->pwm_id);
244
245 rc = pm8058_write(chip->pm_chip, SSBI_REG_ADDR_LPG_BANK_EN, &reg, 1);
246 if (rc) {
Willie Ruan0d9acd92011-07-04 21:31:30 -0700247 pr_err("pm8058_write(): rc=%d (Enable LPG Bank)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248 goto bail_out;
249 }
250 chip->bank_mask = reg;
251
252bail_out:
253 return rc;
254}
255
256static int pm8058_pwm_bank_sel(struct pwm_device *pwm)
257{
258 int rc;
259 u8 reg;
260
261 reg = pwm->pwm_id;
262 rc = pm8058_write(pwm->chip->pm_chip, SSBI_REG_ADDR_LPG_BANK_SEL,
263 &reg, 1);
264 if (rc)
Willie Ruan0d9acd92011-07-04 21:31:30 -0700265 pr_err("pm8058_write(): rc=%d (Select PWM Bank)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700266 return rc;
267}
268
269static int pm8058_pwm_start(struct pwm_device *pwm, int start, int ramp_start)
270{
271 int rc;
272 u8 reg;
273
274 if (start) {
275 reg = pwm->pwm_ctl[0] | PM8058_PWM_PWM_START;
276 if (ramp_start)
277 reg |= PM8058_PWM_RAMP_GEN_START;
278 else
279 reg &= ~PM8058_PWM_RAMP_GEN_START;
280 } else {
281 reg = pwm->pwm_ctl[0] & ~PM8058_PWM_PWM_START;
282 reg &= ~PM8058_PWM_RAMP_GEN_START;
283 }
284
285 rc = pm8058_write(pwm->chip->pm_chip, SSBI_REG_ADDR_LPG_CTL(0),
286 &reg, 1);
287 if (rc)
Willie Ruan0d9acd92011-07-04 21:31:30 -0700288 pr_err("pm8058_write(): rc=%d (Enable PWM Ctl 0)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700289 else
290 pwm->pwm_ctl[0] = reg;
291 return rc;
292}
293
294static void pm8058_pwm_calc_period(unsigned int period_us,
Willie Ruand3337ed2011-07-04 23:16:22 -0700295 struct pm8058_pwm_period *period)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296{
297 int n, m, clk, div;
298 int best_m, best_div, best_clk;
299 int last_err, cur_err, better_err, better_m;
300 unsigned int tmp_p, last_p, min_err, period_n;
301
302 /* PWM Period / N : handle underflow or overflow */
303 if (period_us < (PM_PWM_PERIOD_MAX / NSEC_PER_USEC))
304 period_n = (period_us * NSEC_PER_USEC) >> 6;
305 else
306 period_n = (period_us >> 6) * NSEC_PER_USEC;
307 if (period_n >= MAX_MPT) {
308 n = 9;
309 period_n >>= 3;
310 } else
311 n = 6;
312
313 min_err = MAX_MPT;
314 best_m = 0;
315 best_clk = 0;
316 best_div = 0;
317 for (clk = 0; clk < NUM_CLOCKS; clk++) {
318 for (div = 0; div < NUM_PRE_DIVIDE; div++) {
319 tmp_p = period_n;
320 last_p = tmp_p;
321 for (m = 0; m <= PM8058_PWM_M_MAX; m++) {
322 if (tmp_p <= pt_t[div][clk]) {
323 /* Found local best */
324 if (!m) {
325 better_err = pt_t[div][clk] -
326 tmp_p;
327 better_m = m;
328 } else {
329 last_err = last_p -
330 pt_t[div][clk];
331 cur_err = pt_t[div][clk] -
332 tmp_p;
333
334 if (cur_err < last_err) {
335 better_err = cur_err;
336 better_m = m;
337 } else {
338 better_err = last_err;
339 better_m = m - 1;
340 }
341 }
342
343 if (better_err < min_err) {
344 min_err = better_err;
345 best_m = better_m;
346 best_clk = clk;
347 best_div = div;
348 }
349 break;
350 } else {
351 last_p = tmp_p;
352 tmp_p >>= 1;
353 }
354 }
355 }
356 }
357
Willie Ruand3337ed2011-07-04 23:16:22 -0700358 period->pwm_size = n;
359 period->clk = best_clk;
360 period->pre_div = best_div;
361 period->pre_div_exp = best_m;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700362
Willie Ruan0d9acd92011-07-04 21:31:30 -0700363 pr_debug("period=%u: n=%d, m=%d, clk[%d]=%s, div[%d]=%d\n",
364 (unsigned)period_us, n, best_m,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700365 best_clk, clks[best_clk], best_div, pre_div[best_div]);
366}
367
Willie Ruanefc2fa42011-07-05 00:12:41 -0700368static void pm8058_pwm_calc_pwm_value(struct pwm_device *pwm,
369 unsigned int period_us,
370 unsigned int duty_us)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700371{
Willie Ruanefc2fa42011-07-05 00:12:41 -0700372 unsigned int max_pwm_value, tmp;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700373
Willie Ruanefc2fa42011-07-05 00:12:41 -0700374 /* Figure out pwm_value with overflow handling */
375 tmp = 1 << (sizeof(tmp) * 8 - pwm->period.pwm_size);
376 if (duty_us < tmp) {
377 tmp = duty_us << pwm->period.pwm_size;
378 pwm->pwm_value = tmp / period_us;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700379 } else {
Willie Ruanefc2fa42011-07-05 00:12:41 -0700380 tmp = period_us >> pwm->period.pwm_size;
381 pwm->pwm_value = duty_us / tmp;
382 }
383 max_pwm_value = (1 << pwm->period.pwm_size) - 1;
384 if (pwm->pwm_value > max_pwm_value)
385 pwm->pwm_value = max_pwm_value;
386}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387
Willie Ruanefc2fa42011-07-05 00:12:41 -0700388static int pm8058_pwm_change_table(struct pwm_device *pwm, int duty_pct[],
389 int start_idx, int len, int raw_value)
390{
391 unsigned int pwm_value, max_pwm_value;
392 u8 cfg0, cfg1;
393 int i, pwm_size;
394 int rc = 0;
395
396 pwm_size = (pwm->pwm_ctl[5] & PM8058_PWM_SIZE_9_BIT) ? 9 : 6;
397 max_pwm_value = (1 << pwm_size) - 1;
398 for (i = 0; i < len; i++) {
399 if (raw_value)
400 pwm_value = duty_pct[i];
401 else
402 pwm_value = (duty_pct[i] << pwm_size) / 100;
403
404 if (pwm_value > max_pwm_value)
405 pwm_value = max_pwm_value;
406 cfg0 = pwm_value;
407 cfg1 = (pwm_value >> 1) & 0x80;
408 cfg1 |= start_idx + i;
409
410 rc = pm8058_write(pwm->chip->pm_chip,
411 SSBI_REG_ADDR_LPG_LUT_CFG0,
412 &cfg0, 1);
413 if (rc)
414 break;
415
416 rc = pm8058_write(pwm->chip->pm_chip,
417 SSBI_REG_ADDR_LPG_LUT_CFG1,
418 &cfg1, 1);
419 if (rc)
420 break;
421 }
422 return rc;
423}
424
425static void pm8058_pwm_save_index(struct pwm_device *pwm,
426 int low_idx, int high_idx, int flags)
427{
428 pwm->pwm_ctl[1] = high_idx & PM8058_PWM_HIGH_INDEX_MASK;
429 pwm->pwm_ctl[2] = low_idx & PM8058_PWM_LOW_INDEX_MASK;
430
431 if (flags & PM_PWM_LUT_REVERSE)
432 pwm->pwm_ctl[1] |= PM8058_PWM_REVERSE_EN;
433 if (flags & PM_PWM_LUT_RAMP_UP)
434 pwm->pwm_ctl[2] |= PM8058_PWM_RAMP_UP;
435 if (flags & PM_PWM_LUT_LOOP)
436 pwm->pwm_ctl[2] |= PM8058_PWM_LOOP_EN;
437}
438
439static void pm8058_pwm_save_period(struct pwm_device *pwm)
440{
441 u8 mask, val;
442
443 val = ((pwm->period.clk + 1) << PM8058_PWM_CLK_SEL_SHIFT)
444 & PM8058_PWM_CLK_SEL_MASK;
445 val |= (pwm->period.pre_div << PM8058_PWM_PREDIVIDE_SHIFT)
446 & PM8058_PWM_PREDIVIDE_MASK;
447 val |= pwm->period.pre_div_exp & PM8058_PWM_M_MASK;
448 mask = PM8058_PWM_CLK_SEL_MASK | PM8058_PWM_PREDIVIDE_MASK |
449 PM8058_PWM_M_MASK;
450 pm8058_pwm_save(&pwm->pwm_ctl[4], mask, val);
451
452 val = (pwm->period.pwm_size > 6) ? PM8058_PWM_SIZE_9_BIT : 0;
453 mask = PM8058_PWM_SIZE_9_BIT;
454 pm8058_pwm_save(&pwm->pwm_ctl[5], mask, val);
455}
456
457static void pm8058_pwm_save_pwm_value(struct pwm_device *pwm)
458{
459 u8 mask, val;
460
461 pwm->pwm_ctl[3] = pwm->pwm_value;
462
463 val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 1) : 0;
464 mask = PM8058_PWM_VALUE_BIT8;
465 pm8058_pwm_save(&pwm->pwm_ctl[4], mask, val);
466}
467
468static void pm8058_pwm_save_duty_time(struct pwm_device *pwm,
469 struct pm8058_pwm_lut *lut)
470{
471 int i;
472 u8 mask, val;
473
474 /* Linear search for duty time */
475 for (i = 0; i < PM8058_PWM_1KHZ_COUNT_MAX; i++) {
476 if (duty_msec[i] >= lut->lut_duty_ms)
477 break;
478 }
479 val = i << PM8058_PWM_1KHZ_COUNT_SHIFT;
480
481 mask = PM8058_PWM_1KHZ_COUNT_MASK;
482 pm8058_pwm_save(&pwm->pwm_ctl[0], mask, val);
483}
484
485static void pm8058_pwm_save_pause(struct pwm_device *pwm,
486 struct pm8058_pwm_lut *lut)
487{
488 int i, pause_cnt, time_cnt;
489 u8 mask, val;
490
491 time_cnt = (pwm->pwm_ctl[0] & PM8058_PWM_1KHZ_COUNT_MASK)
492 >> PM8058_PWM_1KHZ_COUNT_SHIFT;
493 if (lut->flags & PM_PWM_LUT_PAUSE_HI_EN) {
494 pause_cnt = (lut->lut_pause_hi + duty_msec[time_cnt] / 2)
495 / duty_msec[time_cnt];
496 /* Linear search for pause time */
497 for (i = 0; i < PM8058_PWM_PAUSE_COUNT_MAX; i++) {
498 if (pause_count[i] >= pause_cnt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700499 break;
500 }
Willie Ruanefc2fa42011-07-05 00:12:41 -0700501 val = (i << PM8058_PWM_PAUSE_COUNT_HI_SHIFT) &
502 PM8058_PWM_PAUSE_COUNT_HI_MASK;
503 val |= PM8058_PWM_PAUSE_ENABLE_HIGH;
504 } else
505 val = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700506
Willie Ruanefc2fa42011-07-05 00:12:41 -0700507 mask = PM8058_PWM_PAUSE_COUNT_HI_MASK | PM8058_PWM_PAUSE_ENABLE_HIGH;
508 pm8058_pwm_save(&pwm->pwm_ctl[5], mask, val);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700509
Willie Ruanefc2fa42011-07-05 00:12:41 -0700510 if (lut->flags & PM_PWM_LUT_PAUSE_LO_EN) {
511 /* Linear search for pause time */
512 pause_cnt = (lut->lut_pause_lo + duty_msec[time_cnt] / 2)
513 / duty_msec[time_cnt];
514 for (i = 0; i < PM8058_PWM_PAUSE_COUNT_MAX; i++) {
515 if (pause_count[i] >= pause_cnt)
516 break;
517 }
518 val = (i << PM8058_PWM_PAUSE_COUNT_LO_SHIFT) &
519 PM8058_PWM_PAUSE_COUNT_LO_MASK;
520 val |= PM8058_PWM_PAUSE_ENABLE_LOW;
521 } else
522 val = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700523
Willie Ruanefc2fa42011-07-05 00:12:41 -0700524 mask = PM8058_PWM_PAUSE_COUNT_LO_MASK | PM8058_PWM_PAUSE_ENABLE_LOW;
525 pm8058_pwm_save(&pwm->pwm_ctl[6], mask, val);
526}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700527
Willie Ruanefc2fa42011-07-05 00:12:41 -0700528static int pm8058_pwm_write(struct pwm_device *pwm, int start, int end)
529{
530 int i, rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700531
Willie Ruanefc2fa42011-07-05 00:12:41 -0700532 /* Write in reverse way so 0 would be the last */
533 for (i = end - 1; i >= start; i--) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700534 rc = pm8058_write(pwm->chip->pm_chip,
535 SSBI_REG_ADDR_LPG_CTL(i),
536 &pwm->pwm_ctl[i], 1);
537 if (rc) {
Willie Ruan0d9acd92011-07-04 21:31:30 -0700538 pr_err("pm8058_write(): rc=%d (PWM Ctl[%d])\n", rc, i);
Willie Ruanefc2fa42011-07-05 00:12:41 -0700539 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700540 }
541 }
542
Willie Ruanefc2fa42011-07-05 00:12:41 -0700543 return 0;
544}
545
546static int pm8058_pwm_change_lut(struct pwm_device *pwm,
547 struct pm8058_pwm_lut *lut)
548{
549 int rc;
550
551 pm8058_pwm_save_index(pwm, lut->lut_lo_index,
552 lut->lut_hi_index, lut->flags);
553 pm8058_pwm_save_duty_time(pwm, lut);
554 pm8058_pwm_save_pause(pwm, lut);
555 pm8058_pwm_save(&pwm->pwm_ctl[1], PM8058_PWM_BYPASS_LUT, 0);
556
557 pm8058_pwm_bank_sel(pwm);
558 rc = pm8058_pwm_write(pwm, 0, 7);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700559
560 return rc;
561}
562
563/* APIs */
564/*
565 * pwm_request - request a PWM device
566 */
567struct pwm_device *pwm_request(int pwm_id, const char *label)
568{
569 struct pwm_device *pwm;
570
571 if (pwm_id > PM8058_PWM_CHANNELS || pwm_id < 0)
572 return ERR_PTR(-EINVAL);
573 if (pwm_chip == NULL)
574 return ERR_PTR(-ENODEV);
575
576 mutex_lock(&pwm_chip->pwm_mutex);
577 pwm = &pwm_chip->pwm_dev[pwm_id];
578 if (!pwm->in_use) {
579 pwm->in_use = 1;
580 pwm->label = label;
581
582 if (pwm_chip->pdata && pwm_chip->pdata->config)
583 pwm_chip->pdata->config(pwm, pwm_id, 1);
584 } else
585 pwm = ERR_PTR(-EBUSY);
586 mutex_unlock(&pwm_chip->pwm_mutex);
587
588 return pwm;
589}
590EXPORT_SYMBOL(pwm_request);
591
592/*
593 * pwm_free - free a PWM device
594 */
595void pwm_free(struct pwm_device *pwm)
596{
597 if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL)
598 return;
599
600 mutex_lock(&pwm->chip->pwm_mutex);
601 if (pwm->in_use) {
602 pm8058_pwm_bank_sel(pwm);
603 pm8058_pwm_start(pwm, 0, 0);
604
605 if (pwm->chip->pdata && pwm->chip->pdata->config)
606 pwm->chip->pdata->config(pwm, pwm->pwm_id, 0);
607
608 pwm->in_use = 0;
609 pwm->label = NULL;
610 }
611 pm8058_pwm_bank_enable(pwm, 0);
612 mutex_unlock(&pwm->chip->pwm_mutex);
613}
614EXPORT_SYMBOL(pwm_free);
615
616/*
617 * pwm_config - change a PWM device configuration
618 *
619 * @pwm: the PWM device
620 * @period_us: period in micro second
621 * @duty_us: duty cycle in micro second
622 */
623int pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
624{
Willie Ruand3337ed2011-07-04 23:16:22 -0700625 int rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700626
627 if (pwm == NULL || IS_ERR(pwm) ||
Willie Ruan8a08b962011-07-05 00:43:37 -0700628 duty_us > period_us ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700629 (unsigned)period_us > PM_PWM_PERIOD_MAX ||
630 (unsigned)period_us < PM_PWM_PERIOD_MIN)
631 return -EINVAL;
632 if (pwm->chip == NULL)
633 return -ENODEV;
634
635 mutex_lock(&pwm->chip->pwm_mutex);
636
637 if (!pwm->in_use) {
638 rc = -EINVAL;
639 goto out_unlock;
640 }
641
Willie Ruan8a08b962011-07-05 00:43:37 -0700642 if (pwm->pwm_period != period_us) {
643 pm8058_pwm_calc_period(period_us, &pwm->period);
644 pm8058_pwm_save_period(pwm);
645 pwm->pwm_period = period_us;
646 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700647
Willie Ruanefc2fa42011-07-05 00:12:41 -0700648 pm8058_pwm_calc_pwm_value(pwm, period_us, duty_us);
649 pm8058_pwm_save_pwm_value(pwm);
650 pm8058_pwm_save(&pwm->pwm_ctl[1],
651 PM8058_PWM_BYPASS_LUT, PM8058_PWM_BYPASS_LUT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700652
Willie Ruanefc2fa42011-07-05 00:12:41 -0700653 pm8058_pwm_bank_sel(pwm);
654 rc = pm8058_pwm_write(pwm, 1, 6);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700655
Willie Ruan0d9acd92011-07-04 21:31:30 -0700656 pr_debug("duty/period=%u/%u usec: pwm_value=%d (of %d)\n",
657 (unsigned)duty_us, (unsigned)period_us,
Willie Ruand3337ed2011-07-04 23:16:22 -0700658 pwm->pwm_value, 1 << pwm->period.pwm_size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700659
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700660out_unlock:
661 mutex_unlock(&pwm->chip->pwm_mutex);
662 return rc;
663}
664EXPORT_SYMBOL(pwm_config);
665
666/*
667 * pwm_enable - start a PWM output toggling
668 */
669int pwm_enable(struct pwm_device *pwm)
670{
671 int rc;
672
673 if (pwm == NULL || IS_ERR(pwm))
674 return -EINVAL;
675 if (pwm->chip == NULL)
676 return -ENODEV;
677
678 mutex_lock(&pwm->chip->pwm_mutex);
679 if (!pwm->in_use)
680 rc = -EINVAL;
681 else {
682 if (pwm->chip->pdata && pwm->chip->pdata->enable)
683 pwm->chip->pdata->enable(pwm, pwm->pwm_id, 1);
684
685 rc = pm8058_pwm_bank_enable(pwm, 1);
686
687 pm8058_pwm_bank_sel(pwm);
688 pm8058_pwm_start(pwm, 1, 0);
689 }
690 mutex_unlock(&pwm->chip->pwm_mutex);
691 return rc;
692}
693EXPORT_SYMBOL(pwm_enable);
694
695/*
696 * pwm_disable - stop a PWM output toggling
697 */
698void pwm_disable(struct pwm_device *pwm)
699{
700 if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL)
701 return;
702
703 mutex_lock(&pwm->chip->pwm_mutex);
704 if (pwm->in_use) {
705 pm8058_pwm_bank_sel(pwm);
706 pm8058_pwm_start(pwm, 0, 0);
707
708 pm8058_pwm_bank_enable(pwm, 0);
709
710 if (pwm->chip->pdata && pwm->chip->pdata->enable)
711 pwm->chip->pdata->enable(pwm, pwm->pwm_id, 0);
712 }
713 mutex_unlock(&pwm->chip->pwm_mutex);
714}
715EXPORT_SYMBOL(pwm_disable);
716
717/*
718 * pm8058_pwm_lut_config - change a PWM device configuration to use LUT
719 *
720 * @pwm: the PWM device
721 * @period_us: period in micro second
722 * @duty_pct: arrary of duty cycles in percent, like 20, 50.
723 * @duty_time_ms: time for each duty cycle in millisecond
724 * @start_idx: start index in lookup table from 0 to MAX-1
725 * @idx_len: number of index
726 * @pause_lo: pause time in millisecond at low index
727 * @pause_hi: pause time in millisecond at high index
728 * @flags: control flags
729 *
730 */
731int pm8058_pwm_lut_config(struct pwm_device *pwm, int period_us,
732 int duty_pct[], int duty_time_ms, int start_idx,
733 int idx_len, int pause_lo, int pause_hi, int flags)
734{
Willie Ruanefc2fa42011-07-05 00:12:41 -0700735 struct pm8058_pwm_lut pwm_conf;
736 int len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700737 int rc;
738
739 if (pwm == NULL || IS_ERR(pwm) || !idx_len)
740 return -EINVAL;
741 if (duty_pct == NULL && !(flags & PM_PWM_LUT_NO_TABLE))
742 return -EINVAL;
743 if (pwm->chip == NULL)
744 return -ENODEV;
745 if (idx_len >= PM_PWM_LUT_SIZE && start_idx)
746 return -EINVAL;
747 if ((start_idx + idx_len) > PM_PWM_LUT_SIZE)
748 return -EINVAL;
749 if ((unsigned)period_us > PM_PWM_PERIOD_MAX ||
750 (unsigned)period_us < PM_PWM_PERIOD_MIN)
751 return -EINVAL;
752
753 mutex_lock(&pwm->chip->pwm_mutex);
754
755 if (!pwm->in_use) {
756 rc = -EINVAL;
757 goto out_unlock;
758 }
759
Willie Ruan8a08b962011-07-05 00:43:37 -0700760 if (pwm->pwm_period != period_us) {
761 pm8058_pwm_calc_period(period_us, &pwm->period);
762 pm8058_pwm_save_period(pwm);
763 pwm->pwm_period = period_us;
764 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700765
766 len = (idx_len > PM_PWM_LUT_SIZE) ? PM_PWM_LUT_SIZE : idx_len;
767
768 if (flags & PM_PWM_LUT_NO_TABLE)
769 goto after_table_write;
770
Willie Ruanefc2fa42011-07-05 00:12:41 -0700771 rc = pm8058_pwm_change_table(pwm, duty_pct, start_idx, len, 0);
772 if (rc) {
773 pr_err("pm8058_pwm_change_table: rc=%d\n", rc);
774 goto out_unlock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700775 }
776
777after_table_write:
778 pwm_conf.lut_duty_ms = duty_time_ms;
779 pwm_conf.lut_lo_index = start_idx;
780 pwm_conf.lut_hi_index = start_idx + len - 1;
781 pwm_conf.lut_pause_lo = pause_lo;
782 pwm_conf.lut_pause_hi = pause_hi;
783 pwm_conf.flags = flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700784
Willie Ruanefc2fa42011-07-05 00:12:41 -0700785 rc = pm8058_pwm_change_lut(pwm, &pwm_conf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700786
787out_unlock:
788 mutex_unlock(&pwm->chip->pwm_mutex);
789 return rc;
790}
791EXPORT_SYMBOL(pm8058_pwm_lut_config);
792
793/*
794 * pm8058_pwm_lut_enable - control a PWM device to start/stop LUT ramp
795 *
796 * @pwm: the PWM device
797 * @start: to start (1), or stop (0)
798 */
799int pm8058_pwm_lut_enable(struct pwm_device *pwm, int start)
800{
801 if (pwm == NULL || IS_ERR(pwm))
802 return -EINVAL;
803 if (pwm->chip == NULL)
804 return -ENODEV;
805
806 mutex_lock(&pwm->chip->pwm_mutex);
807 if (start) {
808 pm8058_pwm_bank_enable(pwm, 1);
809
810 pm8058_pwm_bank_sel(pwm);
811 pm8058_pwm_start(pwm, 1, 1);
812 } else {
813 pm8058_pwm_bank_sel(pwm);
814 pm8058_pwm_start(pwm, 0, 0);
815
816 pm8058_pwm_bank_enable(pwm, 0);
817 }
818 mutex_unlock(&pwm->chip->pwm_mutex);
819 return 0;
820}
821EXPORT_SYMBOL(pm8058_pwm_lut_enable);
822
823#define SSBI_REG_ADDR_LED_BASE 0x131
824#define SSBI_REG_ADDR_LED(n) (SSBI_REG_ADDR_LED_BASE + (n))
825#define SSBI_REG_ADDR_FLASH_BASE 0x48
826#define SSBI_REG_ADDR_FLASH_DRV_1 0xFB
827#define SSBI_REG_ADDR_FLASH(n) (((n) < 2 ? \
828 SSBI_REG_ADDR_FLASH_BASE + (n) : \
829 SSBI_REG_ADDR_FLASH_DRV_1))
830
831#define PM8058_LED_CURRENT_SHIFT 3
832#define PM8058_LED_MODE_MASK 0x07
833
834#define PM8058_FLASH_CURRENT_SHIFT 4
835#define PM8058_FLASH_MODE_MASK 0x03
836#define PM8058_FLASH_MODE_NONE 0
837#define PM8058_FLASH_MODE_DTEST1 1
838#define PM8058_FLASH_MODE_DTEST2 2
839#define PM8058_FLASH_MODE_PWM 3
840
841int pm8058_pwm_config_led(struct pwm_device *pwm, int id,
842 int mode, int max_current)
843{
844 int rc;
845 u8 conf;
846
847 switch (id) {
848 case PM_PWM_LED_0:
849 case PM_PWM_LED_1:
850 case PM_PWM_LED_2:
851 conf = mode & PM8058_LED_MODE_MASK;
852 conf |= (max_current / 2) << PM8058_LED_CURRENT_SHIFT;
853 rc = pm8058_write(pwm->chip->pm_chip,
854 SSBI_REG_ADDR_LED(id), &conf, 1);
855 break;
856
857 case PM_PWM_LED_KPD:
858 case PM_PWM_LED_FLASH:
859 case PM_PWM_LED_FLASH1:
860 switch (mode) {
861 case PM_PWM_CONF_PWM1:
862 case PM_PWM_CONF_PWM2:
863 case PM_PWM_CONF_PWM3:
864 conf = PM8058_FLASH_MODE_PWM;
865 break;
866 case PM_PWM_CONF_DTEST1:
867 conf = PM8058_FLASH_MODE_DTEST1;
868 break;
869 case PM_PWM_CONF_DTEST2:
870 conf = PM8058_FLASH_MODE_DTEST2;
871 break;
872 default:
873 conf = PM8058_FLASH_MODE_NONE;
874 break;
875 }
876 conf |= (max_current / 20) << PM8058_FLASH_CURRENT_SHIFT;
877 id -= PM_PWM_LED_KPD;
878 rc = pm8058_write(pwm->chip->pm_chip,
879 SSBI_REG_ADDR_FLASH(id), &conf, 1);
880 break;
881 default:
882 rc = -EINVAL;
883 break;
884 }
885
886 return rc;
887}
888EXPORT_SYMBOL(pm8058_pwm_config_led);
889
890int pm8058_pwm_set_dtest(struct pwm_device *pwm, int enable)
891{
892 int rc;
893 u8 reg;
894
895 if (pwm == NULL || IS_ERR(pwm))
896 return -EINVAL;
897 if (pwm->chip == NULL)
898 return -ENODEV;
899
900 if (!pwm->in_use)
901 rc = -EINVAL;
902 else {
903 reg = pwm->pwm_id & PM8058_PWM_DTEST_BANK_MASK;
904 if (enable)
905 /* Only Test 1 available */
906 reg |= (1 << PM8058_PWM_DTEST_SHIFT) &
907 PM8058_PWM_DTEST_MASK;
908 rc = pm8058_write(pwm->chip->pm_chip, SSBI_REG_ADDR_LPG_TEST,
909 &reg, 1);
910 if (rc)
Willie Ruan0d9acd92011-07-04 21:31:30 -0700911 pr_err("pm8058_write(DTEST=0x%x): rc=%d\n", reg, rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700912
913 }
914 return rc;
915}
916EXPORT_SYMBOL(pm8058_pwm_set_dtest);
917
918static int __devinit pmic8058_pwm_probe(struct platform_device *pdev)
919{
920 struct pm8058_chip *pm_chip;
921 struct pm8058_pwm_chip *chip;
922 int i;
923
924 pm_chip = dev_get_drvdata(pdev->dev.parent);
925 if (pm_chip == NULL) {
Willie Ruan0d9acd92011-07-04 21:31:30 -0700926 pr_err("no parent data passed in.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700927 return -EFAULT;
928 }
929
930 chip = kzalloc(sizeof *chip, GFP_KERNEL);
931 if (chip == NULL) {
Willie Ruan0d9acd92011-07-04 21:31:30 -0700932 pr_err("kzalloc() failed.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700933 return -ENOMEM;
934 }
935
936 for (i = 0; i < PM8058_PWM_CHANNELS; i++) {
937 chip->pwm_dev[i].pwm_id = i;
938 chip->pwm_dev[i].chip = chip;
939 }
940
941 mutex_init(&chip->pwm_mutex);
942
943 chip->pdata = pdev->dev.platform_data;
944 chip->pm_chip = pm_chip;
945 pwm_chip = chip;
946 platform_set_drvdata(pdev, chip);
947
Willie Ruan0d9acd92011-07-04 21:31:30 -0700948 pr_notice("OK\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700949 return 0;
950}
951
952static int __devexit pmic8058_pwm_remove(struct platform_device *pdev)
953{
954 struct pm8058_pwm_chip *chip = platform_get_drvdata(pdev);
955
956 platform_set_drvdata(pdev, NULL);
957 kfree(chip);
958 return 0;
959}
960
961static struct platform_driver pmic8058_pwm_driver = {
962 .probe = pmic8058_pwm_probe,
963 .remove = __devexit_p(pmic8058_pwm_remove),
964 .driver = {
965 .name = "pm8058-pwm",
966 .owner = THIS_MODULE,
967 },
968};
969
970static int __init pm8058_pwm_init(void)
971{
972 return platform_driver_register(&pmic8058_pwm_driver);
973}
974
975static void __exit pm8058_pwm_exit(void)
976{
977 platform_driver_unregister(&pmic8058_pwm_driver);
978}
979
980subsys_initcall(pm8058_pwm_init);
981module_exit(pm8058_pwm_exit);
982
983MODULE_LICENSE("GPL v2");
984MODULE_DESCRIPTION("PMIC8058 PWM driver");
985MODULE_VERSION("1.0");
986MODULE_ALIAS("platform:pmic8058_pwm");