blob: aaab272fb6b35074a7116189f55caffbb931397c [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;
185 int bypass_lut;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186 int pwm_period;
187 int pwm_duty;
188 u8 pwm_ctl[PM8058_LPG_CTL_REGS];
189 int irq;
190 struct pm8058_pwm_chip *chip;
191};
192
193struct pm8058_pwm_chip {
194 struct pwm_device pwm_dev[PM8058_PWM_CHANNELS];
195 u8 bank_mask;
196 struct mutex pwm_mutex;
197 struct pm8058_chip *pm_chip;
198 struct pm8058_pwm_pdata *pdata;
199};
200
201static struct pm8058_pwm_chip *pwm_chip;
202
Willie Ruand3337ed2011-07-04 23:16:22 -0700203struct pm8058_pwm_lut {
204 /* LUT parameters */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700205 int lut_duty_ms;
206 int lut_lo_index;
207 int lut_hi_index;
208 int lut_pause_hi;
209 int lut_pause_lo;
210 int flags;
211};
212
213static u16 duty_msec[PM8058_PWM_1KHZ_COUNT_MAX + 1] = {
214 0, 1, 2, 3, 4, 6, 8, 16, 18, 24, 32, 36, 64, 128, 256, 512
215};
216
217static u16 pause_count[PM8058_PWM_PAUSE_COUNT_MAX + 1] = {
218 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
219 23, 28, 31, 42, 47, 56, 63, 83, 94, 111, 125, 167, 188, 222, 250, 333,
220 375, 500, 667, 750, 800, 900, 1000, 1100,
221 1200, 1300, 1400, 1500, 1600, 1800, 2000, 2500,
222 3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500,
223 7000
224};
225
226/* Internal functions */
227static int pm8058_pwm_bank_enable(struct pwm_device *pwm, int enable)
228{
229 int rc;
230 u8 reg;
231 struct pm8058_pwm_chip *chip;
232
233 chip = pwm->chip;
234
235 if (enable)
236 reg = chip->bank_mask | (1 << pwm->pwm_id);
237 else
238 reg = chip->bank_mask & ~(1 << pwm->pwm_id);
239
240 rc = pm8058_write(chip->pm_chip, SSBI_REG_ADDR_LPG_BANK_EN, &reg, 1);
241 if (rc) {
Willie Ruan0d9acd92011-07-04 21:31:30 -0700242 pr_err("pm8058_write(): rc=%d (Enable LPG Bank)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243 goto bail_out;
244 }
245 chip->bank_mask = reg;
246
247bail_out:
248 return rc;
249}
250
251static int pm8058_pwm_bank_sel(struct pwm_device *pwm)
252{
253 int rc;
254 u8 reg;
255
256 reg = pwm->pwm_id;
257 rc = pm8058_write(pwm->chip->pm_chip, SSBI_REG_ADDR_LPG_BANK_SEL,
258 &reg, 1);
259 if (rc)
Willie Ruan0d9acd92011-07-04 21:31:30 -0700260 pr_err("pm8058_write(): rc=%d (Select PWM Bank)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700261 return rc;
262}
263
264static int pm8058_pwm_start(struct pwm_device *pwm, int start, int ramp_start)
265{
266 int rc;
267 u8 reg;
268
269 if (start) {
270 reg = pwm->pwm_ctl[0] | PM8058_PWM_PWM_START;
271 if (ramp_start)
272 reg |= PM8058_PWM_RAMP_GEN_START;
273 else
274 reg &= ~PM8058_PWM_RAMP_GEN_START;
275 } else {
276 reg = pwm->pwm_ctl[0] & ~PM8058_PWM_PWM_START;
277 reg &= ~PM8058_PWM_RAMP_GEN_START;
278 }
279
280 rc = pm8058_write(pwm->chip->pm_chip, SSBI_REG_ADDR_LPG_CTL(0),
281 &reg, 1);
282 if (rc)
Willie Ruan0d9acd92011-07-04 21:31:30 -0700283 pr_err("pm8058_write(): rc=%d (Enable PWM Ctl 0)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700284 else
285 pwm->pwm_ctl[0] = reg;
286 return rc;
287}
288
289static void pm8058_pwm_calc_period(unsigned int period_us,
Willie Ruand3337ed2011-07-04 23:16:22 -0700290 struct pm8058_pwm_period *period)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291{
292 int n, m, clk, div;
293 int best_m, best_div, best_clk;
294 int last_err, cur_err, better_err, better_m;
295 unsigned int tmp_p, last_p, min_err, period_n;
296
297 /* PWM Period / N : handle underflow or overflow */
298 if (period_us < (PM_PWM_PERIOD_MAX / NSEC_PER_USEC))
299 period_n = (period_us * NSEC_PER_USEC) >> 6;
300 else
301 period_n = (period_us >> 6) * NSEC_PER_USEC;
302 if (period_n >= MAX_MPT) {
303 n = 9;
304 period_n >>= 3;
305 } else
306 n = 6;
307
308 min_err = MAX_MPT;
309 best_m = 0;
310 best_clk = 0;
311 best_div = 0;
312 for (clk = 0; clk < NUM_CLOCKS; clk++) {
313 for (div = 0; div < NUM_PRE_DIVIDE; div++) {
314 tmp_p = period_n;
315 last_p = tmp_p;
316 for (m = 0; m <= PM8058_PWM_M_MAX; m++) {
317 if (tmp_p <= pt_t[div][clk]) {
318 /* Found local best */
319 if (!m) {
320 better_err = pt_t[div][clk] -
321 tmp_p;
322 better_m = m;
323 } else {
324 last_err = last_p -
325 pt_t[div][clk];
326 cur_err = pt_t[div][clk] -
327 tmp_p;
328
329 if (cur_err < last_err) {
330 better_err = cur_err;
331 better_m = m;
332 } else {
333 better_err = last_err;
334 better_m = m - 1;
335 }
336 }
337
338 if (better_err < min_err) {
339 min_err = better_err;
340 best_m = better_m;
341 best_clk = clk;
342 best_div = div;
343 }
344 break;
345 } else {
346 last_p = tmp_p;
347 tmp_p >>= 1;
348 }
349 }
350 }
351 }
352
Willie Ruand3337ed2011-07-04 23:16:22 -0700353 period->pwm_size = n;
354 period->clk = best_clk;
355 period->pre_div = best_div;
356 period->pre_div_exp = best_m;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357
Willie Ruan0d9acd92011-07-04 21:31:30 -0700358 pr_debug("period=%u: n=%d, m=%d, clk[%d]=%s, div[%d]=%d\n",
359 (unsigned)period_us, n, best_m,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700360 best_clk, clks[best_clk], best_div, pre_div[best_div]);
361}
362
363static int pm8058_pwm_configure(struct pwm_device *pwm,
Willie Ruand3337ed2011-07-04 23:16:22 -0700364 struct pm8058_pwm_lut *pwm_conf)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700365{
366 int i, rc, len;
367 u8 reg, ramp_enabled = 0;
Willie Ruand3337ed2011-07-04 23:16:22 -0700368 struct pm8058_pwm_period *period = &pwm->period;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700369
Willie Ruand3337ed2011-07-04 23:16:22 -0700370 reg = (period->pwm_size > 6) ? PM8058_PWM_SIZE_9_BIT : 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700371 pwm->pwm_ctl[5] = reg;
372
Willie Ruand3337ed2011-07-04 23:16:22 -0700373 reg = ((period->clk + 1) << PM8058_PWM_CLK_SEL_SHIFT)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700374 & PM8058_PWM_CLK_SEL_MASK;
Willie Ruand3337ed2011-07-04 23:16:22 -0700375 reg |= (period->pre_div << PM8058_PWM_PREDIVIDE_SHIFT)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700376 & PM8058_PWM_PREDIVIDE_MASK;
Willie Ruand3337ed2011-07-04 23:16:22 -0700377 reg |= period->pre_div_exp & PM8058_PWM_M_MASK;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700378 pwm->pwm_ctl[4] = reg;
379
Willie Ruand3337ed2011-07-04 23:16:22 -0700380 if (pwm->bypass_lut) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700381 pwm->pwm_ctl[0] &= PM8058_PWM_PWM_START; /* keep enabled */
382 pwm->pwm_ctl[1] = PM8058_PWM_BYPASS_LUT;
383 pwm->pwm_ctl[2] = 0;
384
Willie Ruand3337ed2011-07-04 23:16:22 -0700385 if (period->pwm_size > 6) {
386 pwm->pwm_ctl[3] = pwm->pwm_value
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387 & PM8058_PWM_VALUE_BIT7_0;
Willie Ruand3337ed2011-07-04 23:16:22 -0700388 pwm->pwm_ctl[4] |= (pwm->pwm_value >> 1)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700389 & PM8058_PWM_VALUE_BIT8;
390 } else {
Willie Ruand3337ed2011-07-04 23:16:22 -0700391 pwm->pwm_ctl[3] = pwm->pwm_value
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392 & PM8058_PWM_VALUE_BIT5_0;
393 }
394
395 len = 6;
396 } else {
397 int pause_cnt, j;
398
399 /* Linear search for duty time */
400 for (i = 0; i < PM8058_PWM_1KHZ_COUNT_MAX; i++) {
401 if (duty_msec[i] >= pwm_conf->lut_duty_ms)
402 break;
403 }
404
405 ramp_enabled = pwm->pwm_ctl[0] & PM8058_PWM_RAMP_GEN_START;
406 pwm->pwm_ctl[0] &= PM8058_PWM_PWM_START; /* keep enabled */
407 pwm->pwm_ctl[0] |= (i << PM8058_PWM_1KHZ_COUNT_SHIFT) &
408 PM8058_PWM_1KHZ_COUNT_MASK;
409 pwm->pwm_ctl[1] = pwm_conf->lut_hi_index &
410 PM8058_PWM_HIGH_INDEX_MASK;
411 pwm->pwm_ctl[2] = pwm_conf->lut_lo_index &
412 PM8058_PWM_LOW_INDEX_MASK;
413
414 if (pwm_conf->flags & PM_PWM_LUT_REVERSE)
415 pwm->pwm_ctl[1] |= PM8058_PWM_REVERSE_EN;
416 if (pwm_conf->flags & PM_PWM_LUT_RAMP_UP)
417 pwm->pwm_ctl[2] |= PM8058_PWM_RAMP_UP;
418 if (pwm_conf->flags & PM_PWM_LUT_LOOP)
419 pwm->pwm_ctl[2] |= PM8058_PWM_LOOP_EN;
420
421 /* Pause time */
422 if (pwm_conf->flags & PM_PWM_LUT_PAUSE_HI_EN) {
423 /* Linear search for pause time */
424 pause_cnt = (pwm_conf->lut_pause_hi + duty_msec[i] / 2)
425 / duty_msec[i];
426 for (j = 0; j < PM8058_PWM_PAUSE_COUNT_MAX; j++) {
427 if (pause_count[j] >= pause_cnt)
428 break;
429 }
430 pwm->pwm_ctl[5] = (j <<
431 PM8058_PWM_PAUSE_COUNT_HI_SHIFT) &
432 PM8058_PWM_PAUSE_COUNT_HI_MASK;
433 pwm->pwm_ctl[5] |= PM8058_PWM_PAUSE_ENABLE_HIGH;
434 } else
435 pwm->pwm_ctl[5] = 0;
436
437 if (pwm_conf->flags & PM_PWM_LUT_PAUSE_LO_EN) {
438 /* Linear search for pause time */
439 pause_cnt = (pwm_conf->lut_pause_lo + duty_msec[i] / 2)
440 / duty_msec[i];
441 for (j = 0; j < PM8058_PWM_PAUSE_COUNT_MAX; j++) {
442 if (pause_count[j] >= pause_cnt)
443 break;
444 }
445 pwm->pwm_ctl[6] = (j <<
446 PM8058_PWM_PAUSE_COUNT_LO_SHIFT) &
447 PM8058_PWM_PAUSE_COUNT_LO_MASK;
448 pwm->pwm_ctl[6] |= PM8058_PWM_PAUSE_ENABLE_LOW;
449 } else
450 pwm->pwm_ctl[6] = 0;
451
452 len = 7;
453 }
454
455 pm8058_pwm_bank_sel(pwm);
456
457 for (i = 0; i < len; i++) {
458 rc = pm8058_write(pwm->chip->pm_chip,
459 SSBI_REG_ADDR_LPG_CTL(i),
460 &pwm->pwm_ctl[i], 1);
461 if (rc) {
Willie Ruan0d9acd92011-07-04 21:31:30 -0700462 pr_err("pm8058_write(): rc=%d (PWM Ctl[%d])\n", rc, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700463 break;
464 }
465 }
466
467 if (ramp_enabled) {
468 pwm->pwm_ctl[0] |= ramp_enabled;
469 pm8058_write(pwm->chip->pm_chip, SSBI_REG_ADDR_LPG_CTL(0),
470 &pwm->pwm_ctl[0], 1);
471 }
472
473 return rc;
474}
475
476/* APIs */
477/*
478 * pwm_request - request a PWM device
479 */
480struct pwm_device *pwm_request(int pwm_id, const char *label)
481{
482 struct pwm_device *pwm;
483
484 if (pwm_id > PM8058_PWM_CHANNELS || pwm_id < 0)
485 return ERR_PTR(-EINVAL);
486 if (pwm_chip == NULL)
487 return ERR_PTR(-ENODEV);
488
489 mutex_lock(&pwm_chip->pwm_mutex);
490 pwm = &pwm_chip->pwm_dev[pwm_id];
491 if (!pwm->in_use) {
492 pwm->in_use = 1;
493 pwm->label = label;
494
495 if (pwm_chip->pdata && pwm_chip->pdata->config)
496 pwm_chip->pdata->config(pwm, pwm_id, 1);
497 } else
498 pwm = ERR_PTR(-EBUSY);
499 mutex_unlock(&pwm_chip->pwm_mutex);
500
501 return pwm;
502}
503EXPORT_SYMBOL(pwm_request);
504
505/*
506 * pwm_free - free a PWM device
507 */
508void pwm_free(struct pwm_device *pwm)
509{
510 if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL)
511 return;
512
513 mutex_lock(&pwm->chip->pwm_mutex);
514 if (pwm->in_use) {
515 pm8058_pwm_bank_sel(pwm);
516 pm8058_pwm_start(pwm, 0, 0);
517
518 if (pwm->chip->pdata && pwm->chip->pdata->config)
519 pwm->chip->pdata->config(pwm, pwm->pwm_id, 0);
520
521 pwm->in_use = 0;
522 pwm->label = NULL;
523 }
524 pm8058_pwm_bank_enable(pwm, 0);
525 mutex_unlock(&pwm->chip->pwm_mutex);
526}
527EXPORT_SYMBOL(pwm_free);
528
529/*
530 * pwm_config - change a PWM device configuration
531 *
532 * @pwm: the PWM device
533 * @period_us: period in micro second
534 * @duty_us: duty cycle in micro second
535 */
536int pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
537{
Willie Ruand3337ed2011-07-04 23:16:22 -0700538 struct pm8058_pwm_lut pwm_conf;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700539 unsigned int max_pwm_value, tmp;
Willie Ruand3337ed2011-07-04 23:16:22 -0700540 int rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541
542 if (pwm == NULL || IS_ERR(pwm) ||
543 (unsigned)duty_us > (unsigned)period_us ||
544 (unsigned)period_us > PM_PWM_PERIOD_MAX ||
545 (unsigned)period_us < PM_PWM_PERIOD_MIN)
546 return -EINVAL;
547 if (pwm->chip == NULL)
548 return -ENODEV;
549
550 mutex_lock(&pwm->chip->pwm_mutex);
551
552 if (!pwm->in_use) {
553 rc = -EINVAL;
554 goto out_unlock;
555 }
556
Willie Ruand3337ed2011-07-04 23:16:22 -0700557 pm8058_pwm_calc_period(period_us, &pwm->period);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700558
559 /* Figure out pwm_value with overflow handling */
Willie Ruand3337ed2011-07-04 23:16:22 -0700560 if ((unsigned)period_us > (1 << pwm->period.pwm_size)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700561 tmp = period_us;
Willie Ruand3337ed2011-07-04 23:16:22 -0700562 tmp >>= pwm->period.pwm_size;
563 pwm->pwm_value = (unsigned)duty_us / tmp;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700564 } else {
565 tmp = duty_us;
Willie Ruand3337ed2011-07-04 23:16:22 -0700566 tmp <<= pwm->period.pwm_size;
567 pwm->pwm_value = tmp / (unsigned)period_us;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700568 }
Willie Ruand3337ed2011-07-04 23:16:22 -0700569 max_pwm_value = (1 << pwm->period.pwm_size) - 1;
570 if (pwm->pwm_value > max_pwm_value)
571 pwm->pwm_value = max_pwm_value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700572
Willie Ruand3337ed2011-07-04 23:16:22 -0700573 pwm->bypass_lut = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700574
Willie Ruan0d9acd92011-07-04 21:31:30 -0700575 pr_debug("duty/period=%u/%u usec: pwm_value=%d (of %d)\n",
576 (unsigned)duty_us, (unsigned)period_us,
Willie Ruand3337ed2011-07-04 23:16:22 -0700577 pwm->pwm_value, 1 << pwm->period.pwm_size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700578
579 rc = pm8058_pwm_configure(pwm, &pwm_conf);
580
581out_unlock:
582 mutex_unlock(&pwm->chip->pwm_mutex);
583 return rc;
584}
585EXPORT_SYMBOL(pwm_config);
586
587/*
588 * pwm_enable - start a PWM output toggling
589 */
590int pwm_enable(struct pwm_device *pwm)
591{
592 int rc;
593
594 if (pwm == NULL || IS_ERR(pwm))
595 return -EINVAL;
596 if (pwm->chip == NULL)
597 return -ENODEV;
598
599 mutex_lock(&pwm->chip->pwm_mutex);
600 if (!pwm->in_use)
601 rc = -EINVAL;
602 else {
603 if (pwm->chip->pdata && pwm->chip->pdata->enable)
604 pwm->chip->pdata->enable(pwm, pwm->pwm_id, 1);
605
606 rc = pm8058_pwm_bank_enable(pwm, 1);
607
608 pm8058_pwm_bank_sel(pwm);
609 pm8058_pwm_start(pwm, 1, 0);
610 }
611 mutex_unlock(&pwm->chip->pwm_mutex);
612 return rc;
613}
614EXPORT_SYMBOL(pwm_enable);
615
616/*
617 * pwm_disable - stop a PWM output toggling
618 */
619void pwm_disable(struct pwm_device *pwm)
620{
621 if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL)
622 return;
623
624 mutex_lock(&pwm->chip->pwm_mutex);
625 if (pwm->in_use) {
626 pm8058_pwm_bank_sel(pwm);
627 pm8058_pwm_start(pwm, 0, 0);
628
629 pm8058_pwm_bank_enable(pwm, 0);
630
631 if (pwm->chip->pdata && pwm->chip->pdata->enable)
632 pwm->chip->pdata->enable(pwm, pwm->pwm_id, 0);
633 }
634 mutex_unlock(&pwm->chip->pwm_mutex);
635}
636EXPORT_SYMBOL(pwm_disable);
637
638/*
639 * pm8058_pwm_lut_config - change a PWM device configuration to use LUT
640 *
641 * @pwm: the PWM device
642 * @period_us: period in micro second
643 * @duty_pct: arrary of duty cycles in percent, like 20, 50.
644 * @duty_time_ms: time for each duty cycle in millisecond
645 * @start_idx: start index in lookup table from 0 to MAX-1
646 * @idx_len: number of index
647 * @pause_lo: pause time in millisecond at low index
648 * @pause_hi: pause time in millisecond at high index
649 * @flags: control flags
650 *
651 */
652int pm8058_pwm_lut_config(struct pwm_device *pwm, int period_us,
653 int duty_pct[], int duty_time_ms, int start_idx,
654 int idx_len, int pause_lo, int pause_hi, int flags)
655{
Willie Ruand3337ed2011-07-04 23:16:22 -0700656 struct pm8058_pwm_lut pwm_conf;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700657 unsigned int pwm_value, max_pwm_value;
658 u8 cfg0, cfg1;
659 int i, len;
660 int rc;
661
662 if (pwm == NULL || IS_ERR(pwm) || !idx_len)
663 return -EINVAL;
664 if (duty_pct == NULL && !(flags & PM_PWM_LUT_NO_TABLE))
665 return -EINVAL;
666 if (pwm->chip == NULL)
667 return -ENODEV;
668 if (idx_len >= PM_PWM_LUT_SIZE && start_idx)
669 return -EINVAL;
670 if ((start_idx + idx_len) > PM_PWM_LUT_SIZE)
671 return -EINVAL;
672 if ((unsigned)period_us > PM_PWM_PERIOD_MAX ||
673 (unsigned)period_us < PM_PWM_PERIOD_MIN)
674 return -EINVAL;
675
676 mutex_lock(&pwm->chip->pwm_mutex);
677
678 if (!pwm->in_use) {
679 rc = -EINVAL;
680 goto out_unlock;
681 }
682
Willie Ruand3337ed2011-07-04 23:16:22 -0700683 pm8058_pwm_calc_period(period_us, &pwm->period);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700684
685 len = (idx_len > PM_PWM_LUT_SIZE) ? PM_PWM_LUT_SIZE : idx_len;
686
687 if (flags & PM_PWM_LUT_NO_TABLE)
688 goto after_table_write;
689
Willie Ruand3337ed2011-07-04 23:16:22 -0700690 max_pwm_value = (1 << pwm->period.pwm_size) - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700691 for (i = 0; i < len; i++) {
Willie Ruand3337ed2011-07-04 23:16:22 -0700692 pwm_value = (duty_pct[i] << pwm->period.pwm_size) / 100;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700693 /* Avoid overflow */
694 if (pwm_value > max_pwm_value)
695 pwm_value = max_pwm_value;
696 cfg0 = pwm_value & 0xff;
697 cfg1 = (pwm_value >> 1) & 0x80;
698 cfg1 |= start_idx + i;
699
Willie Ruan0d9acd92011-07-04 21:31:30 -0700700 pr_debug("%d: pwm=%d\n", i, pwm_value);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700701
702 pm8058_write(pwm->chip->pm_chip,
703 SSBI_REG_ADDR_LPG_LUT_CFG0,
704 &cfg0, 1);
705 pm8058_write(pwm->chip->pm_chip,
706 SSBI_REG_ADDR_LPG_LUT_CFG1,
707 &cfg1, 1);
708 }
709
710after_table_write:
711 pwm_conf.lut_duty_ms = duty_time_ms;
712 pwm_conf.lut_lo_index = start_idx;
713 pwm_conf.lut_hi_index = start_idx + len - 1;
714 pwm_conf.lut_pause_lo = pause_lo;
715 pwm_conf.lut_pause_hi = pause_hi;
716 pwm_conf.flags = flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700717
718 rc = pm8058_pwm_configure(pwm, &pwm_conf);
719
720out_unlock:
721 mutex_unlock(&pwm->chip->pwm_mutex);
722 return rc;
723}
724EXPORT_SYMBOL(pm8058_pwm_lut_config);
725
726/*
727 * pm8058_pwm_lut_enable - control a PWM device to start/stop LUT ramp
728 *
729 * @pwm: the PWM device
730 * @start: to start (1), or stop (0)
731 */
732int pm8058_pwm_lut_enable(struct pwm_device *pwm, int start)
733{
734 if (pwm == NULL || IS_ERR(pwm))
735 return -EINVAL;
736 if (pwm->chip == NULL)
737 return -ENODEV;
738
739 mutex_lock(&pwm->chip->pwm_mutex);
740 if (start) {
741 pm8058_pwm_bank_enable(pwm, 1);
742
743 pm8058_pwm_bank_sel(pwm);
744 pm8058_pwm_start(pwm, 1, 1);
745 } else {
746 pm8058_pwm_bank_sel(pwm);
747 pm8058_pwm_start(pwm, 0, 0);
748
749 pm8058_pwm_bank_enable(pwm, 0);
750 }
751 mutex_unlock(&pwm->chip->pwm_mutex);
752 return 0;
753}
754EXPORT_SYMBOL(pm8058_pwm_lut_enable);
755
756#define SSBI_REG_ADDR_LED_BASE 0x131
757#define SSBI_REG_ADDR_LED(n) (SSBI_REG_ADDR_LED_BASE + (n))
758#define SSBI_REG_ADDR_FLASH_BASE 0x48
759#define SSBI_REG_ADDR_FLASH_DRV_1 0xFB
760#define SSBI_REG_ADDR_FLASH(n) (((n) < 2 ? \
761 SSBI_REG_ADDR_FLASH_BASE + (n) : \
762 SSBI_REG_ADDR_FLASH_DRV_1))
763
764#define PM8058_LED_CURRENT_SHIFT 3
765#define PM8058_LED_MODE_MASK 0x07
766
767#define PM8058_FLASH_CURRENT_SHIFT 4
768#define PM8058_FLASH_MODE_MASK 0x03
769#define PM8058_FLASH_MODE_NONE 0
770#define PM8058_FLASH_MODE_DTEST1 1
771#define PM8058_FLASH_MODE_DTEST2 2
772#define PM8058_FLASH_MODE_PWM 3
773
774int pm8058_pwm_config_led(struct pwm_device *pwm, int id,
775 int mode, int max_current)
776{
777 int rc;
778 u8 conf;
779
780 switch (id) {
781 case PM_PWM_LED_0:
782 case PM_PWM_LED_1:
783 case PM_PWM_LED_2:
784 conf = mode & PM8058_LED_MODE_MASK;
785 conf |= (max_current / 2) << PM8058_LED_CURRENT_SHIFT;
786 rc = pm8058_write(pwm->chip->pm_chip,
787 SSBI_REG_ADDR_LED(id), &conf, 1);
788 break;
789
790 case PM_PWM_LED_KPD:
791 case PM_PWM_LED_FLASH:
792 case PM_PWM_LED_FLASH1:
793 switch (mode) {
794 case PM_PWM_CONF_PWM1:
795 case PM_PWM_CONF_PWM2:
796 case PM_PWM_CONF_PWM3:
797 conf = PM8058_FLASH_MODE_PWM;
798 break;
799 case PM_PWM_CONF_DTEST1:
800 conf = PM8058_FLASH_MODE_DTEST1;
801 break;
802 case PM_PWM_CONF_DTEST2:
803 conf = PM8058_FLASH_MODE_DTEST2;
804 break;
805 default:
806 conf = PM8058_FLASH_MODE_NONE;
807 break;
808 }
809 conf |= (max_current / 20) << PM8058_FLASH_CURRENT_SHIFT;
810 id -= PM_PWM_LED_KPD;
811 rc = pm8058_write(pwm->chip->pm_chip,
812 SSBI_REG_ADDR_FLASH(id), &conf, 1);
813 break;
814 default:
815 rc = -EINVAL;
816 break;
817 }
818
819 return rc;
820}
821EXPORT_SYMBOL(pm8058_pwm_config_led);
822
823int pm8058_pwm_set_dtest(struct pwm_device *pwm, int enable)
824{
825 int rc;
826 u8 reg;
827
828 if (pwm == NULL || IS_ERR(pwm))
829 return -EINVAL;
830 if (pwm->chip == NULL)
831 return -ENODEV;
832
833 if (!pwm->in_use)
834 rc = -EINVAL;
835 else {
836 reg = pwm->pwm_id & PM8058_PWM_DTEST_BANK_MASK;
837 if (enable)
838 /* Only Test 1 available */
839 reg |= (1 << PM8058_PWM_DTEST_SHIFT) &
840 PM8058_PWM_DTEST_MASK;
841 rc = pm8058_write(pwm->chip->pm_chip, SSBI_REG_ADDR_LPG_TEST,
842 &reg, 1);
843 if (rc)
Willie Ruan0d9acd92011-07-04 21:31:30 -0700844 pr_err("pm8058_write(DTEST=0x%x): rc=%d\n", reg, rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700845
846 }
847 return rc;
848}
849EXPORT_SYMBOL(pm8058_pwm_set_dtest);
850
851static int __devinit pmic8058_pwm_probe(struct platform_device *pdev)
852{
853 struct pm8058_chip *pm_chip;
854 struct pm8058_pwm_chip *chip;
855 int i;
856
857 pm_chip = dev_get_drvdata(pdev->dev.parent);
858 if (pm_chip == NULL) {
Willie Ruan0d9acd92011-07-04 21:31:30 -0700859 pr_err("no parent data passed in.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700860 return -EFAULT;
861 }
862
863 chip = kzalloc(sizeof *chip, GFP_KERNEL);
864 if (chip == NULL) {
Willie Ruan0d9acd92011-07-04 21:31:30 -0700865 pr_err("kzalloc() failed.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700866 return -ENOMEM;
867 }
868
869 for (i = 0; i < PM8058_PWM_CHANNELS; i++) {
870 chip->pwm_dev[i].pwm_id = i;
871 chip->pwm_dev[i].chip = chip;
872 }
873
874 mutex_init(&chip->pwm_mutex);
875
876 chip->pdata = pdev->dev.platform_data;
877 chip->pm_chip = pm_chip;
878 pwm_chip = chip;
879 platform_set_drvdata(pdev, chip);
880
Willie Ruan0d9acd92011-07-04 21:31:30 -0700881 pr_notice("OK\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700882 return 0;
883}
884
885static int __devexit pmic8058_pwm_remove(struct platform_device *pdev)
886{
887 struct pm8058_pwm_chip *chip = platform_get_drvdata(pdev);
888
889 platform_set_drvdata(pdev, NULL);
890 kfree(chip);
891 return 0;
892}
893
894static struct platform_driver pmic8058_pwm_driver = {
895 .probe = pmic8058_pwm_probe,
896 .remove = __devexit_p(pmic8058_pwm_remove),
897 .driver = {
898 .name = "pm8058-pwm",
899 .owner = THIS_MODULE,
900 },
901};
902
903static int __init pm8058_pwm_init(void)
904{
905 return platform_driver_register(&pmic8058_pwm_driver);
906}
907
908static void __exit pm8058_pwm_exit(void)
909{
910 platform_driver_unregister(&pmic8058_pwm_driver);
911}
912
913subsys_initcall(pm8058_pwm_init);
914module_exit(pm8058_pwm_exit);
915
916MODULE_LICENSE("GPL v2");
917MODULE_DESCRIPTION("PMIC8058 PWM driver");
918MODULE_VERSION("1.0");
919MODULE_ALIAS("platform:pmic8058_pwm");