blob: ddf29c58d92a2a8e979c064cb6e5258e6ff98927 [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) ||
628 (unsigned)duty_us > (unsigned)period_us ||
629 (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 Ruand3337ed2011-07-04 23:16:22 -0700642 pm8058_pwm_calc_period(period_us, &pwm->period);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700643
Willie Ruanefc2fa42011-07-05 00:12:41 -0700644 pm8058_pwm_save_period(pwm);
645 pm8058_pwm_calc_pwm_value(pwm, period_us, duty_us);
646 pm8058_pwm_save_pwm_value(pwm);
647 pm8058_pwm_save(&pwm->pwm_ctl[1],
648 PM8058_PWM_BYPASS_LUT, PM8058_PWM_BYPASS_LUT);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700649
Willie Ruanefc2fa42011-07-05 00:12:41 -0700650 pm8058_pwm_bank_sel(pwm);
651 rc = pm8058_pwm_write(pwm, 1, 6);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700652
Willie Ruan0d9acd92011-07-04 21:31:30 -0700653 pr_debug("duty/period=%u/%u usec: pwm_value=%d (of %d)\n",
654 (unsigned)duty_us, (unsigned)period_us,
Willie Ruand3337ed2011-07-04 23:16:22 -0700655 pwm->pwm_value, 1 << pwm->period.pwm_size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700656
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700657out_unlock:
658 mutex_unlock(&pwm->chip->pwm_mutex);
659 return rc;
660}
661EXPORT_SYMBOL(pwm_config);
662
663/*
664 * pwm_enable - start a PWM output toggling
665 */
666int pwm_enable(struct pwm_device *pwm)
667{
668 int rc;
669
670 if (pwm == NULL || IS_ERR(pwm))
671 return -EINVAL;
672 if (pwm->chip == NULL)
673 return -ENODEV;
674
675 mutex_lock(&pwm->chip->pwm_mutex);
676 if (!pwm->in_use)
677 rc = -EINVAL;
678 else {
679 if (pwm->chip->pdata && pwm->chip->pdata->enable)
680 pwm->chip->pdata->enable(pwm, pwm->pwm_id, 1);
681
682 rc = pm8058_pwm_bank_enable(pwm, 1);
683
684 pm8058_pwm_bank_sel(pwm);
685 pm8058_pwm_start(pwm, 1, 0);
686 }
687 mutex_unlock(&pwm->chip->pwm_mutex);
688 return rc;
689}
690EXPORT_SYMBOL(pwm_enable);
691
692/*
693 * pwm_disable - stop a PWM output toggling
694 */
695void pwm_disable(struct pwm_device *pwm)
696{
697 if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL)
698 return;
699
700 mutex_lock(&pwm->chip->pwm_mutex);
701 if (pwm->in_use) {
702 pm8058_pwm_bank_sel(pwm);
703 pm8058_pwm_start(pwm, 0, 0);
704
705 pm8058_pwm_bank_enable(pwm, 0);
706
707 if (pwm->chip->pdata && pwm->chip->pdata->enable)
708 pwm->chip->pdata->enable(pwm, pwm->pwm_id, 0);
709 }
710 mutex_unlock(&pwm->chip->pwm_mutex);
711}
712EXPORT_SYMBOL(pwm_disable);
713
714/*
715 * pm8058_pwm_lut_config - change a PWM device configuration to use LUT
716 *
717 * @pwm: the PWM device
718 * @period_us: period in micro second
719 * @duty_pct: arrary of duty cycles in percent, like 20, 50.
720 * @duty_time_ms: time for each duty cycle in millisecond
721 * @start_idx: start index in lookup table from 0 to MAX-1
722 * @idx_len: number of index
723 * @pause_lo: pause time in millisecond at low index
724 * @pause_hi: pause time in millisecond at high index
725 * @flags: control flags
726 *
727 */
728int pm8058_pwm_lut_config(struct pwm_device *pwm, int period_us,
729 int duty_pct[], int duty_time_ms, int start_idx,
730 int idx_len, int pause_lo, int pause_hi, int flags)
731{
Willie Ruanefc2fa42011-07-05 00:12:41 -0700732 struct pm8058_pwm_lut pwm_conf;
733 int len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700734 int rc;
735
736 if (pwm == NULL || IS_ERR(pwm) || !idx_len)
737 return -EINVAL;
738 if (duty_pct == NULL && !(flags & PM_PWM_LUT_NO_TABLE))
739 return -EINVAL;
740 if (pwm->chip == NULL)
741 return -ENODEV;
742 if (idx_len >= PM_PWM_LUT_SIZE && start_idx)
743 return -EINVAL;
744 if ((start_idx + idx_len) > PM_PWM_LUT_SIZE)
745 return -EINVAL;
746 if ((unsigned)period_us > PM_PWM_PERIOD_MAX ||
747 (unsigned)period_us < PM_PWM_PERIOD_MIN)
748 return -EINVAL;
749
750 mutex_lock(&pwm->chip->pwm_mutex);
751
752 if (!pwm->in_use) {
753 rc = -EINVAL;
754 goto out_unlock;
755 }
756
Willie Ruand3337ed2011-07-04 23:16:22 -0700757 pm8058_pwm_calc_period(period_us, &pwm->period);
Willie Ruanefc2fa42011-07-05 00:12:41 -0700758 pm8058_pwm_save_period(pwm);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700759
760 len = (idx_len > PM_PWM_LUT_SIZE) ? PM_PWM_LUT_SIZE : idx_len;
761
762 if (flags & PM_PWM_LUT_NO_TABLE)
763 goto after_table_write;
764
Willie Ruanefc2fa42011-07-05 00:12:41 -0700765 rc = pm8058_pwm_change_table(pwm, duty_pct, start_idx, len, 0);
766 if (rc) {
767 pr_err("pm8058_pwm_change_table: rc=%d\n", rc);
768 goto out_unlock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700769 }
770
771after_table_write:
772 pwm_conf.lut_duty_ms = duty_time_ms;
773 pwm_conf.lut_lo_index = start_idx;
774 pwm_conf.lut_hi_index = start_idx + len - 1;
775 pwm_conf.lut_pause_lo = pause_lo;
776 pwm_conf.lut_pause_hi = pause_hi;
777 pwm_conf.flags = flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700778
Willie Ruanefc2fa42011-07-05 00:12:41 -0700779 rc = pm8058_pwm_change_lut(pwm, &pwm_conf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700780
781out_unlock:
782 mutex_unlock(&pwm->chip->pwm_mutex);
783 return rc;
784}
785EXPORT_SYMBOL(pm8058_pwm_lut_config);
786
787/*
788 * pm8058_pwm_lut_enable - control a PWM device to start/stop LUT ramp
789 *
790 * @pwm: the PWM device
791 * @start: to start (1), or stop (0)
792 */
793int pm8058_pwm_lut_enable(struct pwm_device *pwm, int start)
794{
795 if (pwm == NULL || IS_ERR(pwm))
796 return -EINVAL;
797 if (pwm->chip == NULL)
798 return -ENODEV;
799
800 mutex_lock(&pwm->chip->pwm_mutex);
801 if (start) {
802 pm8058_pwm_bank_enable(pwm, 1);
803
804 pm8058_pwm_bank_sel(pwm);
805 pm8058_pwm_start(pwm, 1, 1);
806 } else {
807 pm8058_pwm_bank_sel(pwm);
808 pm8058_pwm_start(pwm, 0, 0);
809
810 pm8058_pwm_bank_enable(pwm, 0);
811 }
812 mutex_unlock(&pwm->chip->pwm_mutex);
813 return 0;
814}
815EXPORT_SYMBOL(pm8058_pwm_lut_enable);
816
817#define SSBI_REG_ADDR_LED_BASE 0x131
818#define SSBI_REG_ADDR_LED(n) (SSBI_REG_ADDR_LED_BASE + (n))
819#define SSBI_REG_ADDR_FLASH_BASE 0x48
820#define SSBI_REG_ADDR_FLASH_DRV_1 0xFB
821#define SSBI_REG_ADDR_FLASH(n) (((n) < 2 ? \
822 SSBI_REG_ADDR_FLASH_BASE + (n) : \
823 SSBI_REG_ADDR_FLASH_DRV_1))
824
825#define PM8058_LED_CURRENT_SHIFT 3
826#define PM8058_LED_MODE_MASK 0x07
827
828#define PM8058_FLASH_CURRENT_SHIFT 4
829#define PM8058_FLASH_MODE_MASK 0x03
830#define PM8058_FLASH_MODE_NONE 0
831#define PM8058_FLASH_MODE_DTEST1 1
832#define PM8058_FLASH_MODE_DTEST2 2
833#define PM8058_FLASH_MODE_PWM 3
834
835int pm8058_pwm_config_led(struct pwm_device *pwm, int id,
836 int mode, int max_current)
837{
838 int rc;
839 u8 conf;
840
841 switch (id) {
842 case PM_PWM_LED_0:
843 case PM_PWM_LED_1:
844 case PM_PWM_LED_2:
845 conf = mode & PM8058_LED_MODE_MASK;
846 conf |= (max_current / 2) << PM8058_LED_CURRENT_SHIFT;
847 rc = pm8058_write(pwm->chip->pm_chip,
848 SSBI_REG_ADDR_LED(id), &conf, 1);
849 break;
850
851 case PM_PWM_LED_KPD:
852 case PM_PWM_LED_FLASH:
853 case PM_PWM_LED_FLASH1:
854 switch (mode) {
855 case PM_PWM_CONF_PWM1:
856 case PM_PWM_CONF_PWM2:
857 case PM_PWM_CONF_PWM3:
858 conf = PM8058_FLASH_MODE_PWM;
859 break;
860 case PM_PWM_CONF_DTEST1:
861 conf = PM8058_FLASH_MODE_DTEST1;
862 break;
863 case PM_PWM_CONF_DTEST2:
864 conf = PM8058_FLASH_MODE_DTEST2;
865 break;
866 default:
867 conf = PM8058_FLASH_MODE_NONE;
868 break;
869 }
870 conf |= (max_current / 20) << PM8058_FLASH_CURRENT_SHIFT;
871 id -= PM_PWM_LED_KPD;
872 rc = pm8058_write(pwm->chip->pm_chip,
873 SSBI_REG_ADDR_FLASH(id), &conf, 1);
874 break;
875 default:
876 rc = -EINVAL;
877 break;
878 }
879
880 return rc;
881}
882EXPORT_SYMBOL(pm8058_pwm_config_led);
883
884int pm8058_pwm_set_dtest(struct pwm_device *pwm, int enable)
885{
886 int rc;
887 u8 reg;
888
889 if (pwm == NULL || IS_ERR(pwm))
890 return -EINVAL;
891 if (pwm->chip == NULL)
892 return -ENODEV;
893
894 if (!pwm->in_use)
895 rc = -EINVAL;
896 else {
897 reg = pwm->pwm_id & PM8058_PWM_DTEST_BANK_MASK;
898 if (enable)
899 /* Only Test 1 available */
900 reg |= (1 << PM8058_PWM_DTEST_SHIFT) &
901 PM8058_PWM_DTEST_MASK;
902 rc = pm8058_write(pwm->chip->pm_chip, SSBI_REG_ADDR_LPG_TEST,
903 &reg, 1);
904 if (rc)
Willie Ruan0d9acd92011-07-04 21:31:30 -0700905 pr_err("pm8058_write(DTEST=0x%x): rc=%d\n", reg, rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700906
907 }
908 return rc;
909}
910EXPORT_SYMBOL(pm8058_pwm_set_dtest);
911
912static int __devinit pmic8058_pwm_probe(struct platform_device *pdev)
913{
914 struct pm8058_chip *pm_chip;
915 struct pm8058_pwm_chip *chip;
916 int i;
917
918 pm_chip = dev_get_drvdata(pdev->dev.parent);
919 if (pm_chip == NULL) {
Willie Ruan0d9acd92011-07-04 21:31:30 -0700920 pr_err("no parent data passed in.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700921 return -EFAULT;
922 }
923
924 chip = kzalloc(sizeof *chip, GFP_KERNEL);
925 if (chip == NULL) {
Willie Ruan0d9acd92011-07-04 21:31:30 -0700926 pr_err("kzalloc() failed.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700927 return -ENOMEM;
928 }
929
930 for (i = 0; i < PM8058_PWM_CHANNELS; i++) {
931 chip->pwm_dev[i].pwm_id = i;
932 chip->pwm_dev[i].chip = chip;
933 }
934
935 mutex_init(&chip->pwm_mutex);
936
937 chip->pdata = pdev->dev.platform_data;
938 chip->pm_chip = pm_chip;
939 pwm_chip = chip;
940 platform_set_drvdata(pdev, chip);
941
Willie Ruan0d9acd92011-07-04 21:31:30 -0700942 pr_notice("OK\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700943 return 0;
944}
945
946static int __devexit pmic8058_pwm_remove(struct platform_device *pdev)
947{
948 struct pm8058_pwm_chip *chip = platform_get_drvdata(pdev);
949
950 platform_set_drvdata(pdev, NULL);
951 kfree(chip);
952 return 0;
953}
954
955static struct platform_driver pmic8058_pwm_driver = {
956 .probe = pmic8058_pwm_probe,
957 .remove = __devexit_p(pmic8058_pwm_remove),
958 .driver = {
959 .name = "pm8058-pwm",
960 .owner = THIS_MODULE,
961 },
962};
963
964static int __init pm8058_pwm_init(void)
965{
966 return platform_driver_register(&pmic8058_pwm_driver);
967}
968
969static void __exit pm8058_pwm_exit(void)
970{
971 platform_driver_unregister(&pmic8058_pwm_driver);
972}
973
974subsys_initcall(pm8058_pwm_init);
975module_exit(pm8058_pwm_exit);
976
977MODULE_LICENSE("GPL v2");
978MODULE_DESCRIPTION("PMIC8058 PWM driver");
979MODULE_VERSION("1.0");
980MODULE_ALIAS("platform:pmic8058_pwm");