blob: f65a1835112c69e0a99409a8bf348a08d0857b99 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
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 * Qualcomm PM8XXX Pulse Width Modulation (PWM) driver
14 *
15 * The HW module is also called LPG (Light Pulse Generator).
16 */
17
18#define pr_fmt(fmt) "%s: " fmt, __func__
19
20#include <linux/module.h>
21#include <linux/platform_device.h>
22#include <linux/slab.h>
23#include <linux/err.h>
24#include <linux/debugfs.h>
25#include <linux/mfd/pm8xxx/core.h>
26#include <linux/mfd/pm8xxx/pwm.h>
27
Jay Chokshi57656862011-09-07 12:02:22 -070028#define PM8XXX_PWM_CHANNELS 3
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029
Jay Chokshi57656862011-09-07 12:02:22 -070030#define PM8XXX_LPG_BANKS 8
31#define PM8XXX_LPG_PWM_CHANNELS PM8XXX_LPG_BANKS
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032#define PM8XXX_LPG_CTL_REGS 7
33
34/* PM8XXX PWM */
Jay Chokshi57656862011-09-07 12:02:22 -070035#define SSBI_REG_ADDR_PWM1_CTRL1 0x88
36#define SSBI_REG_ADDR_PWM1_CTRL2 0x89
37#define SSBI_REG_ADDR_PWM_CTL(id, base) (id == 0 ? base : (base + (id << 1)))
38#define SSBI_REG_ADDR_PWM_CTL1(id) SSBI_REG_ADDR_PWM_CTL(id, \
39 SSBI_REG_ADDR_PWM1_CTRL1)
40#define SSBI_REG_ADDR_PWM_CTL2(id) SSBI_REG_ADDR_PWM_CTL(id, \
41 SSBI_REG_ADDR_PWM1_CTRL2)
42
43#define PM8XXX_PWM_CLK_SEL_SHIFT 6
44#define PM8XXX_PWM_CLK_SEL_MASK 0xC0
45#define PM8XXX_PWM_PREDIVIDE_SHIFT 5
46#define PM8XXX_PWM_PREDIVIDE_MASK 0x20
47#define PM8XXX_PWM_M_SHIFT 2
48#define PM8XXX_PWM_M_MASK 0x1C
49#define PM8XXX_PWM_SIZE_SHIFT 1
50#define PM8XXX_PWM_SIZE_MASK 0x02
51#define PM8XXX_PWM_VALUE_BIT0 0x01
52#define PM8XXX_PWM_DISABLE 0x3F
53
54/* PM8XXX LPG PWM */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070055#define SSBI_REG_ADDR_LPG_CTL_BASE 0x13C
56#define SSBI_REG_ADDR_LPG_CTL(n) (SSBI_REG_ADDR_LPG_CTL_BASE + (n))
57#define SSBI_REG_ADDR_LPG_BANK_SEL 0x143
58#define SSBI_REG_ADDR_LPG_BANK_EN 0x144
59#define SSBI_REG_ADDR_LPG_LUT_CFG0 0x145
60#define SSBI_REG_ADDR_LPG_LUT_CFG1 0x146
61
Jay Chokshi57656862011-09-07 12:02:22 -070062/* LPG Control 0 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070063#define PM8XXX_PWM_1KHZ_COUNT_MASK 0xF0
64#define PM8XXX_PWM_1KHZ_COUNT_SHIFT 4
65
66#define PM8XXX_PWM_1KHZ_COUNT_MAX 15
67
68#define PM8XXX_PWM_OUTPUT_EN 0x08
69#define PM8XXX_PWM_PWM_EN 0x04
70#define PM8XXX_PWM_RAMP_GEN_EN 0x02
71#define PM8XXX_PWM_RAMP_START 0x01
72
73#define PM8XXX_PWM_PWM_START (PM8XXX_PWM_OUTPUT_EN \
74 | PM8XXX_PWM_PWM_EN)
75#define PM8XXX_PWM_RAMP_GEN_START (PM8XXX_PWM_RAMP_GEN_EN \
76 | PM8XXX_PWM_RAMP_START)
77
Jay Chokshi57656862011-09-07 12:02:22 -070078/* LPG Control 1 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079#define PM8XXX_PWM_REVERSE_EN 0x80
80#define PM8XXX_PWM_BYPASS_LUT 0x40
81#define PM8XXX_PWM_HIGH_INDEX_MASK 0x3F
82
Jay Chokshi57656862011-09-07 12:02:22 -070083/* LPG Control 2 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070084#define PM8XXX_PWM_LOOP_EN 0x80
85#define PM8XXX_PWM_RAMP_UP 0x40
86#define PM8XXX_PWM_LOW_INDEX_MASK 0x3F
87
Jay Chokshi57656862011-09-07 12:02:22 -070088/* LPG Control 3 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070089#define PM8XXX_PWM_VALUE_BIT7_0 0xFF
90#define PM8XXX_PWM_VALUE_BIT5_0 0x3F
91
Jay Chokshi57656862011-09-07 12:02:22 -070092/* LPG Control 4 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070093#define PM8XXX_PWM_VALUE_BIT8 0x80
94
Jay Chokshi57656862011-09-07 12:02:22 -070095#define PM8XXX_LPG_PWM_CLK_SEL_MASK 0x60
96#define PM8XXX_LPG_PWM_CLK_SEL_SHIFT 5
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070097
98#define PM8XXX_PWM_CLK_SEL_NO 0
99#define PM8XXX_PWM_CLK_SEL_1KHZ 1
100#define PM8XXX_PWM_CLK_SEL_32KHZ 2
101#define PM8XXX_PWM_CLK_SEL_19P2MHZ 3
102
Jay Chokshi57656862011-09-07 12:02:22 -0700103#define PM8XXX_LPG_PWM_PREDIVIDE_MASK 0x18
104#define PM8XXX_LPG_PWM_PREDIVIDE_SHIFT 3
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105
106#define PM8XXX_PWM_PREDIVIDE_2 0
107#define PM8XXX_PWM_PREDIVIDE_3 1
108#define PM8XXX_PWM_PREDIVIDE_5 2
109#define PM8XXX_PWM_PREDIVIDE_6 3
110
Jay Chokshi57656862011-09-07 12:02:22 -0700111#define PM8XXX_LPG_PWM_M_MASK 0x07
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112#define PM8XXX_PWM_M_MIN 0
113#define PM8XXX_PWM_M_MAX 7
114
Jay Chokshi57656862011-09-07 12:02:22 -0700115/* LPG Control 5 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116#define PM8XXX_PWM_PAUSE_COUNT_HI_MASK 0xFC
117#define PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT 2
118
119#define PM8XXX_PWM_PAUSE_ENABLE_HIGH 0x02
120#define PM8XXX_PWM_SIZE_9_BIT 0x01
121
Jay Chokshi57656862011-09-07 12:02:22 -0700122/* LPG Control 6 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700123#define PM8XXX_PWM_PAUSE_COUNT_LO_MASK 0xFC
124#define PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT 2
125
126#define PM8XXX_PWM_PAUSE_ENABLE_LOW 0x02
127#define PM8XXX_PWM_RESERVED 0x01
128
129#define PM8XXX_PWM_PAUSE_COUNT_MAX 56 /* < 2^6 = 64 */
130
Jay Chokshi57656862011-09-07 12:02:22 -0700131/* LPG LUT_CFG1 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132#define PM8XXX_PWM_LUT_READ 0x40
133
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700134
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700135/*
136 * PWM Frequency = Clock Frequency / (N * T)
137 * or
138 * PWM Period = Clock Period * (N * T)
139 * where
140 * N = 2^9 or 2^6 for 9-bit or 6-bit PWM size
141 * T = Pre-divide * 2^m, where m = 0..7 (exponent)
142 *
143 * This is the formula to figure out m for the best pre-divide and clock:
144 * (PWM Period / N) / 2^m = (Pre-divide * Clock Period)
145 */
146#define NUM_CLOCKS 3
147
148#define NSEC_1000HZ (NSEC_PER_SEC / 1000)
149#define NSEC_32768HZ (NSEC_PER_SEC / 32768)
150#define NSEC_19P2MHZ (NSEC_PER_SEC / 19200000)
151
152#define CLK_PERIOD_MIN NSEC_19P2MHZ
153#define CLK_PERIOD_MAX NSEC_1000HZ
154
Jay Chokshi57656862011-09-07 12:02:22 -0700155#define NUM_LPG_PRE_DIVIDE 3 /* No default support for pre-divide = 6 */
156#define NUM_PWM_PRE_DIVIDE 2
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157
158#define PRE_DIVIDE_0 2
159#define PRE_DIVIDE_1 3
160#define PRE_DIVIDE_2 5
161
162#define PRE_DIVIDE_MIN PRE_DIVIDE_0
163#define PRE_DIVIDE_MAX PRE_DIVIDE_2
164
Jay Chokshi57656862011-09-07 12:02:22 -0700165static unsigned int pt_t[NUM_LPG_PRE_DIVIDE][NUM_CLOCKS] = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700166 { PRE_DIVIDE_0 * NSEC_1000HZ,
167 PRE_DIVIDE_0 * NSEC_32768HZ,
168 PRE_DIVIDE_0 * NSEC_19P2MHZ,
169 },
170 { PRE_DIVIDE_1 * NSEC_1000HZ,
171 PRE_DIVIDE_1 * NSEC_32768HZ,
172 PRE_DIVIDE_1 * NSEC_19P2MHZ,
173 },
174 { PRE_DIVIDE_2 * NSEC_1000HZ,
175 PRE_DIVIDE_2 * NSEC_32768HZ,
176 PRE_DIVIDE_2 * NSEC_19P2MHZ,
177 },
178};
179
180#define MIN_MPT ((PRE_DIVIDE_MIN * CLK_PERIOD_MIN) << PM8XXX_PWM_M_MIN)
181#define MAX_MPT ((PRE_DIVIDE_MAX * CLK_PERIOD_MAX) << PM8XXX_PWM_M_MAX)
182
183/* Private data */
184struct pm8xxx_pwm_chip;
185
186struct pwm_device {
187 int pwm_id; /* = bank/channel id */
188 int in_use;
189 const char *label;
Willie Ruan719c6762011-08-25 11:03:14 -0700190 struct pm8xxx_pwm_period period;
191 int pwm_value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700192 int pwm_period;
193 int pwm_duty;
Jay Chokshi57656862011-09-07 12:02:22 -0700194 u8 pwm_lpg_ctl[PM8XXX_LPG_CTL_REGS];
195 u8 pwm_ctl1;
196 u8 pwm_ctl2;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197 int irq;
198 struct pm8xxx_pwm_chip *chip;
Willie Ruan719c6762011-08-25 11:03:14 -0700199 int bypass_lut;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200};
201
202struct pm8xxx_pwm_chip {
Jay Chokshi57656862011-09-07 12:02:22 -0700203 struct pwm_device *pwm_dev;
204 u8 pwm_channels;
205 u8 pwm_total_pre_divs;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206 u8 bank_mask;
207 struct mutex pwm_mutex;
208 struct device *dev;
Jay Chokshi57656862011-09-07 12:02:22 -0700209 bool is_lpg_supported;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210};
211
212static struct pm8xxx_pwm_chip *pwm_chip;
213
Willie Ruan719c6762011-08-25 11:03:14 -0700214struct pm8xxx_pwm_lut {
215 /* LUT parameters */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216 int lut_duty_ms;
217 int lut_lo_index;
218 int lut_hi_index;
219 int lut_pause_hi;
220 int lut_pause_lo;
221 int flags;
222};
223
224static const u16 duty_msec[PM8XXX_PWM_1KHZ_COUNT_MAX + 1] = {
225 0, 1, 2, 3, 4, 6, 8, 16, 18, 24, 32, 36, 64, 128, 256, 512
226};
227
228static const u16 pause_count[PM8XXX_PWM_PAUSE_COUNT_MAX + 1] = {
229 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
230 23, 28, 31, 42, 47, 56, 63, 83, 94, 111, 125, 167, 188, 222, 250, 333,
231 375, 500, 667, 750, 800, 900, 1000, 1100,
232 1200, 1300, 1400, 1500, 1600, 1800, 2000, 2500,
233 3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500,
234 7000
235};
236
237/* Internal functions */
Willie Ruan8de2f382011-08-25 11:04:50 -0700238static void pm8xxx_pwm_save(u8 *u8p, u8 mask, u8 val)
239{
240 *u8p &= ~mask;
241 *u8p |= val & mask;
242}
243
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700244static int pm8xxx_pwm_bank_enable(struct pwm_device *pwm, int enable)
245{
246 int rc;
247 u8 reg;
248 struct pm8xxx_pwm_chip *chip;
249
250 chip = pwm->chip;
251
252 if (enable)
253 reg = chip->bank_mask | (1 << pwm->pwm_id);
254 else
255 reg = chip->bank_mask & ~(1 << pwm->pwm_id);
256
257 rc = pm8xxx_writeb(chip->dev->parent, SSBI_REG_ADDR_LPG_BANK_EN, reg);
258 if (rc) {
Willie Ruan8de2f382011-08-25 11:04:50 -0700259 pr_err("pm8xxx_writeb(): rc=%d (Enable LPG Bank)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700260 return rc;
261 }
262 chip->bank_mask = reg;
263
264 return 0;
265}
266
267static int pm8xxx_pwm_bank_sel(struct pwm_device *pwm)
268{
269 int rc;
270
271 rc = pm8xxx_writeb(pwm->chip->dev->parent, SSBI_REG_ADDR_LPG_BANK_SEL,
272 pwm->pwm_id);
273 if (rc)
Willie Ruan8de2f382011-08-25 11:04:50 -0700274 pr_err("pm8xxx_writeb(): rc=%d (Select PWM Bank)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700275 return rc;
276}
277
278static int pm8xxx_pwm_start(struct pwm_device *pwm, int start, int ramp_start)
279{
280 int rc;
281 u8 reg;
282
283 if (start) {
Jay Chokshi57656862011-09-07 12:02:22 -0700284 reg = pwm->pwm_lpg_ctl[0] | PM8XXX_PWM_PWM_START;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700285 if (ramp_start)
286 reg |= PM8XXX_PWM_RAMP_GEN_START;
287 else
288 reg &= ~PM8XXX_PWM_RAMP_GEN_START;
289 } else {
Jay Chokshi57656862011-09-07 12:02:22 -0700290 reg = pwm->pwm_lpg_ctl[0] & ~PM8XXX_PWM_PWM_START;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291 reg &= ~PM8XXX_PWM_RAMP_GEN_START;
292 }
293
294 rc = pm8xxx_writeb(pwm->chip->dev->parent, SSBI_REG_ADDR_LPG_CTL(0),
295 reg);
296 if (rc)
Willie Ruan8de2f382011-08-25 11:04:50 -0700297 pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl 0)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700298 else
Jay Chokshi57656862011-09-07 12:02:22 -0700299 pwm->pwm_lpg_ctl[0] = reg;
300 return rc;
301}
302
303static int pm8xxx_pwm_disable(struct pwm_device *pwm)
304{
305 int rc;
306 u8 reg;
307
308 reg = pwm->pwm_ctl1 & PM8XXX_PWM_DISABLE;
309
310 rc = pm8xxx_writeb(pwm->chip->dev->parent,
311 SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), reg);
312
313 if (rc)
314 pr_err("pm8xxx_writeb(): rc=%d (Disable PWM Ctl %d)\n", rc,
315 pwm->pwm_id);
316 return rc;
317}
318
319static int pm8xxx_pwm_enable(struct pwm_device *pwm)
320{
321 /**
322 * A kind of best Effort: Just write the clock information that
323 * we have in the register.
324 */
325 int rc;
326
327 rc = pm8xxx_writeb(pwm->chip->dev->parent,
328 SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), pwm->pwm_ctl1);
329
330 if (rc)
331 pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl %d)\n", rc,
332 pwm->pwm_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700333 return rc;
334}
335
336static void pm8xxx_pwm_calc_period(unsigned int period_us,
Willie Ruan719c6762011-08-25 11:03:14 -0700337 struct pm8xxx_pwm_period *period)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700338{
339 int n, m, clk, div;
340 int best_m, best_div, best_clk;
341 int last_err, cur_err, better_err, better_m;
342 unsigned int tmp_p, last_p, min_err, period_n;
343
344 /* PWM Period / N */
345 if (period_us < (40 * USEC_PER_SEC)) { /* ~6-bit max */
346 period_n = (period_us * NSEC_PER_USEC) >> 6;
347 n = 6;
348 } else if (period_us < (274 * USEC_PER_SEC)) { /* overflow threshold */
349 period_n = (period_us >> 6) * NSEC_PER_USEC;
350 if (period_n >= MAX_MPT) {
351 n = 9;
352 period_n >>= 3;
353 } else {
354 n = 6;
355 }
356 } else {
357 period_n = (period_us >> 9) * NSEC_PER_USEC;
358 n = 9;
359 }
360
361 min_err = MAX_MPT;
362 best_m = 0;
363 best_clk = 0;
364 best_div = 0;
365 for (clk = 0; clk < NUM_CLOCKS; clk++) {
Jay Chokshi57656862011-09-07 12:02:22 -0700366 for (div = 0; div < pwm_chip->pwm_total_pre_divs; div++) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700367 tmp_p = period_n;
368 last_p = tmp_p;
369 for (m = 0; m <= PM8XXX_PWM_M_MAX; m++) {
370 if (tmp_p <= pt_t[div][clk]) {
371 /* Found local best */
372 if (!m) {
373 better_err = pt_t[div][clk] -
374 tmp_p;
375 better_m = m;
376 } else {
377 last_err = last_p -
378 pt_t[div][clk];
379 cur_err = pt_t[div][clk] -
380 tmp_p;
381
382 if (cur_err < last_err) {
383 better_err = cur_err;
384 better_m = m;
385 } else {
386 better_err = last_err;
387 better_m = m - 1;
388 }
389 }
390
391 if (better_err < min_err) {
392 min_err = better_err;
393 best_m = better_m;
394 best_clk = clk;
395 best_div = div;
396 }
397 break;
398 } else {
399 last_p = tmp_p;
400 tmp_p >>= 1;
401 }
402 }
403 }
404 }
405
Willie Ruan75d9e5b2011-08-25 10:59:17 -0700406 /* Use higher resolution */
407 if (best_m >= 3 && n == 6) {
408 n += 3;
409 best_m -= 3;
410 }
411
Willie Ruan719c6762011-08-25 11:03:14 -0700412 period->pwm_size = n;
413 period->clk = best_clk;
414 period->pre_div = best_div;
415 period->pre_div_exp = best_m;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700416}
417
Willie Ruan8de2f382011-08-25 11:04:50 -0700418static void pm8xxx_pwm_calc_pwm_value(struct pwm_device *pwm,
419 unsigned int period_us,
420 unsigned int duty_us)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700421{
Willie Ruan8de2f382011-08-25 11:04:50 -0700422 unsigned int max_pwm_value, tmp;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700423
Willie Ruan8de2f382011-08-25 11:04:50 -0700424 /* Figure out pwm_value with overflow handling */
425 tmp = 1 << (sizeof(tmp) * 8 - pwm->period.pwm_size);
426 if (duty_us < tmp) {
427 tmp = duty_us << pwm->period.pwm_size;
428 pwm->pwm_value = tmp / period_us;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700429 } else {
Willie Ruan8de2f382011-08-25 11:04:50 -0700430 tmp = period_us >> pwm->period.pwm_size;
431 pwm->pwm_value = duty_us / tmp;
432 }
433 max_pwm_value = (1 << pwm->period.pwm_size) - 1;
434 if (pwm->pwm_value > max_pwm_value)
435 pwm->pwm_value = max_pwm_value;
436}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437
Willie Ruan8de2f382011-08-25 11:04:50 -0700438static int pm8xxx_pwm_change_table(struct pwm_device *pwm, int duty_pct[],
439 int start_idx, int len, int raw_value)
440{
441 unsigned int pwm_value, max_pwm_value;
442 u8 cfg0, cfg1;
443 int i, pwm_size;
444 int rc = 0;
445
Jay Chokshi57656862011-09-07 12:02:22 -0700446 pwm_size = (pwm->pwm_lpg_ctl[5] & PM8XXX_PWM_SIZE_9_BIT) ? 9 : 6;
Willie Ruan8de2f382011-08-25 11:04:50 -0700447 max_pwm_value = (1 << pwm_size) - 1;
448 for (i = 0; i < len; i++) {
449 if (raw_value)
450 pwm_value = duty_pct[i];
451 else
452 pwm_value = (duty_pct[i] << pwm_size) / 100;
453
454 if (pwm_value > max_pwm_value)
455 pwm_value = max_pwm_value;
456 cfg0 = pwm_value;
457 cfg1 = (pwm_value >> 1) & 0x80;
458 cfg1 |= start_idx + i;
459
460 rc = pm8xxx_writeb(pwm->chip->dev->parent,
461 SSBI_REG_ADDR_LPG_LUT_CFG0, cfg0);
462 if (rc)
463 break;
464
465 rc = pm8xxx_writeb(pwm->chip->dev->parent,
466 SSBI_REG_ADDR_LPG_LUT_CFG1, cfg1);
467 if (rc)
468 break;
469 }
470 return rc;
471}
472
473static void pm8xxx_pwm_save_index(struct pwm_device *pwm,
474 int low_idx, int high_idx, int flags)
475{
Jay Chokshi57656862011-09-07 12:02:22 -0700476 pwm->pwm_lpg_ctl[1] = high_idx & PM8XXX_PWM_HIGH_INDEX_MASK;
477 pwm->pwm_lpg_ctl[2] = low_idx & PM8XXX_PWM_LOW_INDEX_MASK;
Willie Ruan8de2f382011-08-25 11:04:50 -0700478
479 if (flags & PM_PWM_LUT_REVERSE)
Jay Chokshi57656862011-09-07 12:02:22 -0700480 pwm->pwm_lpg_ctl[1] |= PM8XXX_PWM_REVERSE_EN;
Willie Ruan8de2f382011-08-25 11:04:50 -0700481 if (flags & PM_PWM_LUT_RAMP_UP)
Jay Chokshi57656862011-09-07 12:02:22 -0700482 pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_RAMP_UP;
Willie Ruan8de2f382011-08-25 11:04:50 -0700483 if (flags & PM_PWM_LUT_LOOP)
Jay Chokshi57656862011-09-07 12:02:22 -0700484 pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_LOOP_EN;
Willie Ruan8de2f382011-08-25 11:04:50 -0700485}
486
487static void pm8xxx_pwm_save_period(struct pwm_device *pwm)
488{
489 u8 mask, val;
490
Jay Chokshi57656862011-09-07 12:02:22 -0700491 if (pwm_chip->is_lpg_supported) {
492 val = ((pwm->period.clk + 1) << PM8XXX_LPG_PWM_CLK_SEL_SHIFT)
493 & PM8XXX_LPG_PWM_CLK_SEL_MASK;
494 val |= (pwm->period.pre_div << PM8XXX_LPG_PWM_PREDIVIDE_SHIFT)
495 & PM8XXX_LPG_PWM_PREDIVIDE_MASK;
496 val |= pwm->period.pre_div_exp & PM8XXX_LPG_PWM_M_MASK;
497 mask = PM8XXX_LPG_PWM_CLK_SEL_MASK |
498 PM8XXX_LPG_PWM_PREDIVIDE_MASK | PM8XXX_LPG_PWM_M_MASK;
499 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
Willie Ruan8de2f382011-08-25 11:04:50 -0700500
Jay Chokshi57656862011-09-07 12:02:22 -0700501 val = (pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0;
502 mask = PM8XXX_PWM_SIZE_9_BIT;
503 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
504 } else {
505 val = ((pwm->period.clk + 1) << PM8XXX_PWM_CLK_SEL_SHIFT)
506 & PM8XXX_PWM_CLK_SEL_MASK;
507 val |= (pwm->period.pre_div << PM8XXX_PWM_PREDIVIDE_SHIFT)
508 & PM8XXX_PWM_PREDIVIDE_MASK;
509 val |= (pwm->period.pre_div_exp << PM8XXX_PWM_M_SHIFT)
510 & PM8XXX_PWM_M_MASK;
511 val |= (((pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0)
512 << PM8XXX_PWM_SIZE_SHIFT) & PM8XXX_PWM_SIZE_MASK;
513
514 mask = PM8XXX_PWM_CLK_SEL_MASK | PM8XXX_PWM_PREDIVIDE_MASK |
515 PM8XXX_PWM_M_MASK | PM8XXX_PWM_SIZE_MASK;
516 pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
517 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700518}
519
520static void pm8xxx_pwm_save_pwm_value(struct pwm_device *pwm)
521{
522 u8 mask, val;
523
Jay Chokshi57656862011-09-07 12:02:22 -0700524 if (pwm_chip->is_lpg_supported) {
525 val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 1) : 0;
526 pwm->pwm_lpg_ctl[3] = pwm->pwm_value;
527 mask = PM8XXX_PWM_VALUE_BIT8;
528 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
529 } else {
530 val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 8) : 0;
531 pwm->pwm_ctl2 = pwm->pwm_value;
532 mask = PM8XXX_PWM_VALUE_BIT0;
533 pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
534 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700535}
536
537static void pm8xxx_pwm_save_duty_time(struct pwm_device *pwm,
538 struct pm8xxx_pwm_lut *lut)
539{
540 int i;
541 u8 mask, val;
542
543 /* Linear search for duty time */
544 for (i = 0; i < PM8XXX_PWM_1KHZ_COUNT_MAX; i++) {
545 if (duty_msec[i] >= lut->lut_duty_ms)
546 break;
547 }
548 val = i << PM8XXX_PWM_1KHZ_COUNT_SHIFT;
549
550 mask = PM8XXX_PWM_1KHZ_COUNT_MASK;
Jay Chokshi57656862011-09-07 12:02:22 -0700551 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[0], mask, val);
Willie Ruan8de2f382011-08-25 11:04:50 -0700552}
553
554static void pm8xxx_pwm_save_pause(struct pwm_device *pwm,
555 struct pm8xxx_pwm_lut *lut)
556{
557 int i, pause_cnt, time_cnt;
558 u8 mask, val;
559
Jay Chokshi57656862011-09-07 12:02:22 -0700560 time_cnt = (pwm->pwm_lpg_ctl[0] & PM8XXX_PWM_1KHZ_COUNT_MASK)
Willie Ruan8de2f382011-08-25 11:04:50 -0700561 >> PM8XXX_PWM_1KHZ_COUNT_SHIFT;
562 if (lut->flags & PM_PWM_LUT_PAUSE_HI_EN) {
563 pause_cnt = (lut->lut_pause_hi + duty_msec[time_cnt] / 2)
564 / duty_msec[time_cnt];
565 /* Linear search for pause time */
566 for (i = 0; i < PM8XXX_PWM_PAUSE_COUNT_MAX; i++) {
567 if (pause_count[i] >= pause_cnt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700568 break;
569 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700570 val = (i << PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT) &
571 PM8XXX_PWM_PAUSE_COUNT_HI_MASK;
572 val |= PM8XXX_PWM_PAUSE_ENABLE_HIGH;
573 } else {
574 val = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700575 }
576
Willie Ruan8de2f382011-08-25 11:04:50 -0700577 mask = PM8XXX_PWM_PAUSE_COUNT_HI_MASK | PM8XXX_PWM_PAUSE_ENABLE_HIGH;
Jay Chokshi57656862011-09-07 12:02:22 -0700578 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700579
Willie Ruan8de2f382011-08-25 11:04:50 -0700580 if (lut->flags & PM_PWM_LUT_PAUSE_LO_EN) {
581 /* Linear search for pause time */
582 pause_cnt = (lut->lut_pause_lo + duty_msec[time_cnt] / 2)
583 / duty_msec[time_cnt];
584 for (i = 0; i < PM8XXX_PWM_PAUSE_COUNT_MAX; i++) {
585 if (pause_count[i] >= pause_cnt)
586 break;
587 }
588 val = (i << PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT) &
589 PM8XXX_PWM_PAUSE_COUNT_LO_MASK;
590 val |= PM8XXX_PWM_PAUSE_ENABLE_LOW;
591 } else {
592 val = 0;
593 }
594
595 mask = PM8XXX_PWM_PAUSE_COUNT_LO_MASK | PM8XXX_PWM_PAUSE_ENABLE_LOW;
Jay Chokshi57656862011-09-07 12:02:22 -0700596 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[6], mask, val);
Willie Ruan8de2f382011-08-25 11:04:50 -0700597}
598
Jay Chokshi57656862011-09-07 12:02:22 -0700599static int pm8xxx_pwm_write(struct pwm_device *pwm)
600{
601 int rc = 0;
602
603 rc = pm8xxx_writeb(pwm->chip->dev->parent,
604 SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id),
605 pwm->pwm_ctl1);
606 if (rc) {
607 pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl1[%d])\n",
608 rc, pwm->pwm_id);
609 return rc;
610 }
611
612 rc = pm8xxx_writeb(pwm->chip->dev->parent,
613 SSBI_REG_ADDR_PWM_CTL2(pwm->pwm_id),
614 pwm->pwm_ctl2);
615 if (rc) {
616 pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl2[%d])\n",
617 rc, pwm->pwm_id);
618 return rc;
619 }
620
621 return rc;
622}
623
624static int pm8xxx_lpg_pwm_write(struct pwm_device *pwm, int start, int end)
Willie Ruan8de2f382011-08-25 11:04:50 -0700625{
626 int i, rc;
627
628 /* Write in reverse way so 0 would be the last */
629 for (i = end - 1; i >= start; i--) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700630 rc = pm8xxx_writeb(pwm->chip->dev->parent,
631 SSBI_REG_ADDR_LPG_CTL(i),
Jay Chokshi57656862011-09-07 12:02:22 -0700632 pwm->pwm_lpg_ctl[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700633 if (rc) {
Willie Ruan8de2f382011-08-25 11:04:50 -0700634 pr_err("pm8xxx_writeb(): rc=%d (PWM Ctl[%d])\n", rc, i);
635 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700636 }
637 }
638
Willie Ruan8de2f382011-08-25 11:04:50 -0700639 return 0;
640}
641
642static int pm8xxx_pwm_change_lut(struct pwm_device *pwm,
643 struct pm8xxx_pwm_lut *lut)
644{
645 int rc;
646
647 pm8xxx_pwm_save_index(pwm, lut->lut_lo_index,
648 lut->lut_hi_index, lut->flags);
649 pm8xxx_pwm_save_duty_time(pwm, lut);
650 pm8xxx_pwm_save_pause(pwm, lut);
Jay Chokshi57656862011-09-07 12:02:22 -0700651 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1], PM8XXX_PWM_BYPASS_LUT, 0);
Willie Ruan8de2f382011-08-25 11:04:50 -0700652
653 pm8xxx_pwm_bank_sel(pwm);
Jay Chokshi57656862011-09-07 12:02:22 -0700654 rc = pm8xxx_lpg_pwm_write(pwm, 0, 7);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700655
656 return rc;
657}
658
659/* APIs */
660/**
661 * pwm_request - request a PWM device
662 * @pwm_id: PWM id or channel
663 * @label: the label to identify the user
664 */
665struct pwm_device *pwm_request(int pwm_id, const char *label)
666{
667 struct pwm_device *pwm;
668
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700669 if (pwm_chip == NULL) {
670 pr_err("No pwm_chip\n");
671 return ERR_PTR(-ENODEV);
672 }
673
Jay Chokshi57656862011-09-07 12:02:22 -0700674 if (pwm_id >= pwm_chip->pwm_channels || pwm_id < 0) {
675 pr_err("Invalid pwm_id: %d with %s\n",
676 pwm_id, label ? label : ".");
677 return ERR_PTR(-EINVAL);
678 }
679
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700680 mutex_lock(&pwm_chip->pwm_mutex);
681 pwm = &pwm_chip->pwm_dev[pwm_id];
682 if (!pwm->in_use) {
683 pwm->in_use = 1;
684 pwm->label = label;
685 } else {
686 pwm = ERR_PTR(-EBUSY);
687 }
688 mutex_unlock(&pwm_chip->pwm_mutex);
689
690 return pwm;
691}
692EXPORT_SYMBOL_GPL(pwm_request);
693
694/**
695 * pwm_free - free a PWM device
696 * @pwm: the PWM device
697 */
698void pwm_free(struct pwm_device *pwm)
699{
700 if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
701 pr_err("Invalid pwm handle\n");
702 return;
703 }
704
705 mutex_lock(&pwm->chip->pwm_mutex);
706 if (pwm->in_use) {
Jay Chokshi57656862011-09-07 12:02:22 -0700707 if (pwm_chip->is_lpg_supported) {
708 pm8xxx_pwm_bank_sel(pwm);
709 pm8xxx_pwm_start(pwm, 0, 0);
710 } else {
711 pm8xxx_pwm_disable(pwm);
712 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700713 pwm->in_use = 0;
714 pwm->label = NULL;
715 }
Jay Chokshi57656862011-09-07 12:02:22 -0700716 if (pwm_chip->is_lpg_supported)
717 pm8xxx_pwm_bank_enable(pwm, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700718 mutex_unlock(&pwm->chip->pwm_mutex);
719}
720EXPORT_SYMBOL_GPL(pwm_free);
721
722/**
723 * pwm_config - change a PWM device configuration
724 * @pwm: the PWM device
725 * @period_us: period in microseconds
726 * @duty_us: duty cycle in microseconds
727 */
728int pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
729{
Willie Ruan719c6762011-08-25 11:03:14 -0700730 struct pm8xxx_pwm_period *period;
Jay Chokshi57656862011-09-07 12:02:22 -0700731 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700732
733 if (pwm == NULL || IS_ERR(pwm) ||
734 duty_us > period_us ||
735 (unsigned)period_us > PM8XXX_PWM_PERIOD_MAX ||
736 (unsigned)period_us < PM8XXX_PWM_PERIOD_MIN) {
737 pr_err("Invalid pwm handle or parameters\n");
738 return -EINVAL;
739 }
740 if (pwm->chip == NULL) {
741 pr_err("No pwm_chip\n");
742 return -ENODEV;
743 }
744
Willie Ruan719c6762011-08-25 11:03:14 -0700745 period = &pwm->period;
746
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700747 mutex_lock(&pwm->chip->pwm_mutex);
748
749 if (!pwm->in_use) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700750 rc = -EINVAL;
751 goto out_unlock;
752 }
753
Willie Ruan8de2f382011-08-25 11:04:50 -0700754 if (pwm->pwm_period != period_us) {
755 pm8xxx_pwm_calc_period(period_us, period);
756 pm8xxx_pwm_save_period(pwm);
757 pwm->pwm_period = period_us;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700758 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700759
Willie Ruan8de2f382011-08-25 11:04:50 -0700760 pm8xxx_pwm_calc_pwm_value(pwm, period_us, duty_us);
761 pm8xxx_pwm_save_pwm_value(pwm);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700762
Jay Chokshi57656862011-09-07 12:02:22 -0700763 if (pwm_chip->is_lpg_supported) {
764 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
765 PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
766
767 pm8xxx_pwm_bank_sel(pwm);
768 rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
769 } else {
770 rc = pm8xxx_pwm_write(pwm);
771 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700772
773 pr_debug("duty/period=%u/%u usec: pwm_value=%d (of %d)\n",
774 (unsigned)duty_us, (unsigned)period_us,
775 pwm->pwm_value, 1 << period->pwm_size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700776
777out_unlock:
778 mutex_unlock(&pwm->chip->pwm_mutex);
779 return rc;
780}
781EXPORT_SYMBOL_GPL(pwm_config);
782
783/**
784 * pwm_enable - start a PWM output toggling
785 * @pwm: the PWM device
786 */
787int pwm_enable(struct pwm_device *pwm)
788{
Jay Chokshi57656862011-09-07 12:02:22 -0700789 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700790
791 if (pwm == NULL || IS_ERR(pwm)) {
792 pr_err("Invalid pwm handle\n");
793 return -EINVAL;
794 }
795 if (pwm->chip == NULL) {
796 pr_err("No pwm_chip\n");
797 return -ENODEV;
798 }
799
800 mutex_lock(&pwm->chip->pwm_mutex);
801 if (!pwm->in_use) {
802 pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
803 rc = -EINVAL;
804 } else {
Jay Chokshi57656862011-09-07 12:02:22 -0700805 if (pwm_chip->is_lpg_supported) {
806 rc = pm8xxx_pwm_bank_enable(pwm, 1);
807 pm8xxx_pwm_bank_sel(pwm);
808 pm8xxx_pwm_start(pwm, 1, 0);
809 } else {
810 pm8xxx_pwm_enable(pwm);
811 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700812 }
813 mutex_unlock(&pwm->chip->pwm_mutex);
814 return rc;
815}
816EXPORT_SYMBOL_GPL(pwm_enable);
817
818/**
819 * pwm_disable - stop a PWM output toggling
820 * @pwm: the PWM device
821 */
822void pwm_disable(struct pwm_device *pwm)
823{
824 if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
825 pr_err("Invalid pwm handle or no pwm_chip\n");
826 return;
827 }
828
829 mutex_lock(&pwm->chip->pwm_mutex);
830 if (pwm->in_use) {
Jay Chokshi57656862011-09-07 12:02:22 -0700831 if (pwm_chip->is_lpg_supported) {
832 pm8xxx_pwm_bank_sel(pwm);
833 pm8xxx_pwm_start(pwm, 0, 0);
834 pm8xxx_pwm_bank_enable(pwm, 0);
835 } else {
836 pm8xxx_pwm_disable(pwm);
837 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700838 }
839 mutex_unlock(&pwm->chip->pwm_mutex);
840}
841EXPORT_SYMBOL_GPL(pwm_disable);
842
843/**
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700844 * pm8xxx_pwm_config_period - change PWM period
845 *
846 * @pwm: the PWM device
847 * @pwm_p: period in struct pm8xxx_pwm_period
848 */
849int pm8xxx_pwm_config_period(struct pwm_device *pwm,
850 struct pm8xxx_pwm_period *period)
851{
852 int rc;
853
854 if (pwm == NULL || IS_ERR(pwm) || period == NULL)
855 return -EINVAL;
856 if (pwm->chip == NULL)
857 return -ENODEV;
858
859 mutex_lock(&pwm->chip->pwm_mutex);
860
861 if (!pwm->in_use) {
862 rc = -EINVAL;
863 goto out_unlock;
864 }
865
866 pwm->period.pwm_size = period->pwm_size;
867 pwm->period.clk = period->clk;
868 pwm->period.pre_div = period->pre_div;
869 pwm->period.pre_div_exp = period->pre_div_exp;
870
871 pm8xxx_pwm_save_period(pwm);
Jay Chokshi57656862011-09-07 12:02:22 -0700872
873 if (pwm_chip->is_lpg_supported) {
874 pm8xxx_pwm_bank_sel(pwm);
875 rc = pm8xxx_lpg_pwm_write(pwm, 4, 6);
876 } else {
877 rc = pm8xxx_pwm_write(pwm);
878 }
879
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700880
881out_unlock:
882 mutex_unlock(&pwm->chip->pwm_mutex);
883 return rc;
884}
885EXPORT_SYMBOL(pm8xxx_pwm_config_period);
886
887/**
888 * pm8xxx_pwm_config_pwm_value - change a PWM device configuration
889 * @pwm: the PWM device
890 * @pwm_value: the duty cycle in raw PWM value (< 2^pwm_size)
891 */
892int pm8xxx_pwm_config_pwm_value(struct pwm_device *pwm, int pwm_value)
893{
894 int rc = 0;
895
896 if (pwm == NULL || IS_ERR(pwm))
897 return -EINVAL;
898 if (pwm->chip == NULL)
899 return -ENODEV;
900
901 mutex_lock(&pwm->chip->pwm_mutex);
902
903 if (!pwm->in_use || !pwm->pwm_period) {
904 rc = -EINVAL;
905 goto out_unlock;
906 }
907
908 if (pwm->pwm_value == pwm_value)
909 goto out_unlock;
910
911 pwm->pwm_value = pwm_value;
912
913 pm8xxx_pwm_save_pwm_value(pwm);
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700914
Jay Chokshi57656862011-09-07 12:02:22 -0700915 if (pwm_chip->is_lpg_supported) {
916 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
917 PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
918 pm8xxx_pwm_bank_sel(pwm);
919 rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
920 } else {
921 rc = pm8xxx_pwm_write(pwm);
922 }
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700923
924 if (rc)
925 pr_err("[%d]: pm8xxx_pwm_write: rc=%d\n", pwm->pwm_id, rc);
926
927out_unlock:
928 mutex_unlock(&pwm->chip->pwm_mutex);
929 return rc;
930}
931EXPORT_SYMBOL_GPL(pm8xxx_pwm_config_pwm_value);
932
933/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700934 * pm8xxx_pwm_lut_config - change a PWM device configuration to use LUT
935 * @pwm: the PWM device
936 * @period_us: period in microseconds
937 * @duty_pct: arrary of duty cycles in percent, like 20, 50.
938 * @duty_time_ms: time for each duty cycle in milliseconds
939 * @start_idx: start index in lookup table from 0 to MAX-1
940 * @idx_len: number of index
941 * @pause_lo: pause time in milliseconds at low index
942 * @pause_hi: pause time in milliseconds at high index
943 * @flags: control flags
944 */
945int pm8xxx_pwm_lut_config(struct pwm_device *pwm, int period_us,
946 int duty_pct[], int duty_time_ms, int start_idx,
947 int idx_len, int pause_lo, int pause_hi, int flags)
948{
Willie Ruan719c6762011-08-25 11:03:14 -0700949 struct pm8xxx_pwm_lut lut;
950 struct pm8xxx_pwm_period *period;
Willie Ruan8de2f382011-08-25 11:04:50 -0700951 int len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700952 int rc;
953
954 if (pwm == NULL || IS_ERR(pwm) || !idx_len) {
955 pr_err("Invalid pwm handle or idx_len=0\n");
956 return -EINVAL;
957 }
958 if (duty_pct == NULL && !(flags & PM_PWM_LUT_NO_TABLE)) {
959 pr_err("Invalid duty_pct with flag\n");
960 return -EINVAL;
961 }
962 if (pwm->chip == NULL) {
963 pr_err("No pwm_chip\n");
964 return -ENODEV;
965 }
Jay Chokshi57656862011-09-07 12:02:22 -0700966
967 if (pwm->chip->is_lpg_supported == 0) {
968 pr_err("LPG module isn't supported\n");
969 return -EINVAL;
970 }
971
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700972 if (idx_len >= PM_PWM_LUT_SIZE && start_idx) {
973 pr_err("Wrong LUT size or index\n");
974 return -EINVAL;
975 }
976 if ((start_idx + idx_len) > PM_PWM_LUT_SIZE) {
977 pr_err("Exceed LUT limit\n");
978 return -EINVAL;
979 }
980 if ((unsigned)period_us > PM8XXX_PWM_PERIOD_MAX ||
981 (unsigned)period_us < PM8XXX_PWM_PERIOD_MIN) {
982 pr_err("Period out of range\n");
983 return -EINVAL;
984 }
985
Willie Ruan719c6762011-08-25 11:03:14 -0700986 period = &pwm->period;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700987 mutex_lock(&pwm->chip->pwm_mutex);
988
989 if (!pwm->in_use) {
990 pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
991 rc = -EINVAL;
992 goto out_unlock;
993 }
994
Willie Ruan8de2f382011-08-25 11:04:50 -0700995 if (pwm->pwm_period != period_us) {
996 pm8xxx_pwm_calc_period(period_us, period);
997 pm8xxx_pwm_save_period(pwm);
998 pwm->pwm_period = period_us;
999 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001000
1001 len = (idx_len > PM_PWM_LUT_SIZE) ? PM_PWM_LUT_SIZE : idx_len;
1002
1003 if (flags & PM_PWM_LUT_NO_TABLE)
1004 goto after_table_write;
1005
Willie Ruan8de2f382011-08-25 11:04:50 -07001006 rc = pm8xxx_pwm_change_table(pwm, duty_pct, start_idx, len, 0);
1007 if (rc) {
1008 pr_err("pm8xxx_pwm_change_table: rc=%d\n", rc);
1009 goto out_unlock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001010 }
1011
1012after_table_write:
Willie Ruan719c6762011-08-25 11:03:14 -07001013 lut.lut_duty_ms = duty_time_ms;
1014 lut.lut_lo_index = start_idx;
1015 lut.lut_hi_index = start_idx + len - 1;
1016 lut.lut_pause_lo = pause_lo;
1017 lut.lut_pause_hi = pause_hi;
1018 lut.flags = flags;
1019 pwm->bypass_lut = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001020
Willie Ruan8de2f382011-08-25 11:04:50 -07001021 rc = pm8xxx_pwm_change_lut(pwm, &lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001022
1023out_unlock:
1024 mutex_unlock(&pwm->chip->pwm_mutex);
1025 return rc;
1026}
1027EXPORT_SYMBOL_GPL(pm8xxx_pwm_lut_config);
1028
1029/**
1030 * pm8xxx_pwm_lut_enable - control a PWM device to start/stop LUT ramp
1031 * @pwm: the PWM device
1032 * @start: to start (1), or stop (0)
1033 */
1034int pm8xxx_pwm_lut_enable(struct pwm_device *pwm, int start)
1035{
1036 if (pwm == NULL || IS_ERR(pwm)) {
1037 pr_err("Invalid pwm handle\n");
1038 return -EINVAL;
1039 }
1040 if (pwm->chip == NULL) {
1041 pr_err("No pwm_chip\n");
1042 return -ENODEV;
1043 }
Jay Chokshi57656862011-09-07 12:02:22 -07001044 if (pwm->chip->is_lpg_supported == 0) {
1045 pr_err("LPG module isn't supported\n");
1046 return -EINVAL;
1047 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001048
1049 mutex_lock(&pwm->chip->pwm_mutex);
1050 if (start) {
1051 pm8xxx_pwm_bank_enable(pwm, 1);
1052
1053 pm8xxx_pwm_bank_sel(pwm);
1054 pm8xxx_pwm_start(pwm, 1, 1);
1055 } else {
1056 pm8xxx_pwm_bank_sel(pwm);
1057 pm8xxx_pwm_start(pwm, 0, 0);
1058
1059 pm8xxx_pwm_bank_enable(pwm, 0);
1060 }
1061 mutex_unlock(&pwm->chip->pwm_mutex);
1062 return 0;
1063}
1064EXPORT_SYMBOL_GPL(pm8xxx_pwm_lut_enable);
1065
1066#if defined(CONFIG_DEBUG_FS)
1067
1068struct pm8xxx_pwm_dbg_device;
1069
1070struct pm8xxx_pwm_user {
1071 int pwm_id;
1072 struct pwm_device *pwm;
1073 int period;
1074 int duty_cycle;
1075 int enable;
1076 struct pm8xxx_pwm_dbg_device *dbgdev;
1077};
1078
1079struct pm8xxx_pwm_dbg_device {
1080 struct mutex dbg_mutex;
1081 struct device *dev;
1082 struct dentry *dent;
1083
Jay Chokshi57656862011-09-07 12:02:22 -07001084 struct pm8xxx_pwm_user *user;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001085};
1086
1087static struct pm8xxx_pwm_dbg_device *pmic_dbg_device;
1088
1089static int dbg_pwm_check_period(int period)
1090{
1091 if (period < PM8XXX_PWM_PERIOD_MIN || period > PM8XXX_PWM_PERIOD_MAX) {
1092 pr_err("period is invalid: %d\n", period);
1093 return -EINVAL;
1094 }
1095 return 0;
1096}
1097
1098static int dbg_pwm_check_duty_cycle(int duty_cycle, const char *func_name)
1099{
1100 if (duty_cycle <= 0 || duty_cycle > 100) {
1101 pr_err("%s: duty_cycle is invalid: %d\n",
1102 func_name, duty_cycle);
1103 return -EINVAL;
1104 }
1105 return 0;
1106}
1107
1108static void dbg_pwm_check_handle(struct pm8xxx_pwm_user *puser)
1109{
1110 struct pwm_device *tmp;
1111
1112 if (puser->pwm == NULL) {
1113 tmp = pwm_request(puser->pwm_id, "pwm-dbg");
1114 if (PTR_ERR(puser->pwm)) {
1115 pr_err("pwm_request: err=%ld\n", PTR_ERR(puser->pwm));
1116 puser->pwm = NULL;
Willie Ruan10976ea2011-08-25 11:01:15 -07001117 } else {
1118 pr_debug("[id=%d] pwm_request ok\n", puser->pwm_id);
1119 puser->pwm = tmp;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001120 }
1121 }
1122}
1123
1124static int dbg_pwm_enable_set(void *data, u64 val)
1125{
1126 struct pm8xxx_pwm_user *puser = data;
1127 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1128 int rc;
1129
1130 mutex_lock(&dbgdev->dbg_mutex);
1131 rc = dbg_pwm_check_duty_cycle(puser->duty_cycle, __func__);
1132 if (!rc) {
1133 puser->enable = val;
1134 dbg_pwm_check_handle(puser);
1135 if (puser->pwm) {
1136 if (puser->enable)
1137 pwm_enable(puser->pwm);
1138 else
1139 pwm_disable(puser->pwm);
1140 }
1141 }
1142 mutex_unlock(&dbgdev->dbg_mutex);
1143 return 0;
1144}
1145
1146static int dbg_pwm_enable_get(void *data, u64 *val)
1147{
1148 struct pm8xxx_pwm_user *puser = data;
1149 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1150
1151 mutex_lock(&dbgdev->dbg_mutex);
1152 *val = puser->enable;
1153 mutex_unlock(&dbgdev->dbg_mutex);
1154 return 0;
1155}
1156
1157DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_enable_fops,
1158 dbg_pwm_enable_get, dbg_pwm_enable_set,
1159 "%lld\n");
1160
1161static int dbg_pwm_duty_cycle_set(void *data, u64 val)
1162{
1163 struct pm8xxx_pwm_user *puser = data;
1164 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1165 int rc;
1166
1167 mutex_lock(&dbgdev->dbg_mutex);
1168 rc = dbg_pwm_check_duty_cycle(val, __func__);
1169 if (!rc) {
1170 puser->duty_cycle = val;
1171 dbg_pwm_check_handle(puser);
1172 if (puser->pwm) {
1173 int duty_us;
1174
Willie Ruan10976ea2011-08-25 11:01:15 -07001175 duty_us = puser->duty_cycle * puser->period / 100;
1176 pwm_config(puser->pwm, duty_us, puser->period);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001177 }
1178 }
1179 mutex_unlock(&dbgdev->dbg_mutex);
1180 return 0;
1181}
1182
1183static int dbg_pwm_duty_cycle_get(void *data, u64 *val)
1184{
1185 struct pm8xxx_pwm_user *puser = data;
1186 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1187
1188 mutex_lock(&dbgdev->dbg_mutex);
1189 *val = puser->duty_cycle;
1190 mutex_unlock(&dbgdev->dbg_mutex);
1191 return 0;
1192}
1193
1194DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_duty_cycle_fops,
1195 dbg_pwm_duty_cycle_get, dbg_pwm_duty_cycle_set,
1196 "%lld\n");
1197
1198static int dbg_pwm_period_set(void *data, u64 val)
1199{
1200 struct pm8xxx_pwm_user *puser = data;
1201 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1202 int rc;
1203
1204 mutex_lock(&dbgdev->dbg_mutex);
1205 rc = dbg_pwm_check_period(val);
1206 if (!rc)
1207 puser->period = val;
1208 mutex_unlock(&dbgdev->dbg_mutex);
1209 return 0;
1210}
1211
1212static int dbg_pwm_period_get(void *data, u64 *val)
1213{
1214 struct pm8xxx_pwm_user *puser = data;
1215 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1216
1217 mutex_lock(&dbgdev->dbg_mutex);
1218 *val = puser->period;
1219 mutex_unlock(&dbgdev->dbg_mutex);
1220 return 0;
1221}
1222
1223DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_period_fops,
1224 dbg_pwm_period_get, dbg_pwm_period_set, "%lld\n");
1225
1226static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)
1227{
1228 struct pm8xxx_pwm_dbg_device *dbgdev;
1229 struct dentry *dent;
1230 struct dentry *temp;
1231 struct pm8xxx_pwm_user *puser;
1232 int i;
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001233 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001234
1235 if (dev == NULL) {
1236 pr_err("no parent data passed in.\n");
1237 return -EINVAL;
1238 }
1239
1240 dbgdev = kzalloc(sizeof *dbgdev, GFP_KERNEL);
1241 if (dbgdev == NULL) {
1242 pr_err("kzalloc() failed.\n");
1243 return -ENOMEM;
1244 }
1245
Jay Chokshi57656862011-09-07 12:02:22 -07001246 dbgdev->user = kcalloc(pwm_chip->pwm_channels,
1247 sizeof(struct pm8xxx_pwm_user), GFP_KERNEL);
1248 if (dbgdev->user == NULL) {
1249 pr_err("kcalloc() failed.\n");
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001250 rc = -ENOMEM;
1251 goto user_error;
Jay Chokshi57656862011-09-07 12:02:22 -07001252 }
1253
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001254 mutex_init(&dbgdev->dbg_mutex);
1255
1256 dbgdev->dev = dev;
1257
1258 dent = debugfs_create_dir("pm8xxx-pwm-dbg", NULL);
1259 if (dent == NULL || IS_ERR(dent)) {
1260 pr_err("ERR debugfs_create_dir: dent=%p\n", dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001261 rc = -ENOMEM;
1262 goto dir_error;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001263 }
1264
1265 dbgdev->dent = dent;
1266
Jay Chokshi57656862011-09-07 12:02:22 -07001267 for (i = 0; i < pwm_chip->pwm_channels; i++) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001268 char pwm_ch[] = "0";
1269
1270 pwm_ch[0] = '0' + i;
1271 dent = debugfs_create_dir(pwm_ch, dbgdev->dent);
1272 if (dent == NULL || IS_ERR(dent)) {
1273 pr_err("ERR: pwm=%d: dir: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001274 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001275 goto debug_error;
1276 }
1277
1278 puser = &dbgdev->user[i];
1279 puser->dbgdev = dbgdev;
1280 puser->pwm_id = i;
1281 temp = debugfs_create_file("period", S_IRUGO | S_IWUSR,
1282 dent, puser, &dbg_pwm_period_fops);
1283 if (temp == NULL || IS_ERR(temp)) {
1284 pr_err("ERR: pwm=%d: period: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001285 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001286 goto debug_error;
1287 }
1288
1289 temp = debugfs_create_file("duty-cycle", S_IRUGO | S_IWUSR,
1290 dent, puser, &dbg_pwm_duty_cycle_fops);
1291 if (temp == NULL || IS_ERR(temp)) {
1292 pr_err("ERR: pwm=%d: duty-cycle: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001293 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001294 goto debug_error;
1295 }
1296
1297 temp = debugfs_create_file("enable", S_IRUGO | S_IWUSR,
1298 dent, puser, &dbg_pwm_enable_fops);
1299 if (temp == NULL || IS_ERR(temp)) {
1300 pr_err("ERR: pwm=%d: enable: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001301 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001302 goto debug_error;
1303 }
1304 }
1305
1306 pmic_dbg_device = dbgdev;
1307
1308 return 0;
1309
1310debug_error:
1311 debugfs_remove_recursive(dbgdev->dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001312dir_error:
1313 kfree(dbgdev->user);
1314user_error:
1315 kfree(dbgdev);
1316 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001317}
1318
1319static int __devexit pm8xxx_pwm_dbg_remove(void)
1320{
1321 if (pmic_dbg_device) {
Jay Chokshi57656862011-09-07 12:02:22 -07001322 kfree(pmic_dbg_device->user);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001323 debugfs_remove_recursive(pmic_dbg_device->dent);
1324 kfree(pmic_dbg_device);
1325 }
1326 return 0;
1327}
1328
1329#else
1330
1331static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)
1332{
1333 return 0;
1334}
1335
1336static int __devexit pm8xxx_pwm_dbg_remove(void)
1337{
1338 return 0;
1339}
1340
1341#endif
1342
1343static int __devinit pm8xxx_pwm_probe(struct platform_device *pdev)
1344{
1345 struct pm8xxx_pwm_chip *chip;
1346 int i;
Jay Chokshi57656862011-09-07 12:02:22 -07001347 enum pm8xxx_version version;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001348
1349 chip = kzalloc(sizeof *chip, GFP_KERNEL);
1350 if (chip == NULL) {
1351 pr_err("kzalloc() failed.\n");
1352 return -ENOMEM;
1353 }
1354
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001355 mutex_init(&chip->pwm_mutex);
1356
1357 chip->dev = &pdev->dev;
1358 pwm_chip = chip;
Jay Chokshi57656862011-09-07 12:02:22 -07001359
1360 version = pm8xxx_get_version(chip->dev->parent);
1361
1362 if (version == PM8XXX_VERSION_8921 ||
David Collins999480d2011-11-16 08:52:30 -08001363 version == PM8XXX_VERSION_8058 ||
1364 version == PM8XXX_VERSION_8922) {
Jay Chokshi57656862011-09-07 12:02:22 -07001365 chip->is_lpg_supported = 1;
1366 }
1367 if (chip->is_lpg_supported) {
1368 chip->pwm_channels = PM8XXX_LPG_PWM_CHANNELS;
1369 chip->pwm_total_pre_divs = NUM_LPG_PRE_DIVIDE;
1370 } else {
1371 chip->pwm_channels = PM8XXX_PWM_CHANNELS;
1372 chip->pwm_total_pre_divs = NUM_PWM_PRE_DIVIDE;
1373 }
1374
1375 chip->pwm_dev = kcalloc(chip->pwm_channels, sizeof(struct pwm_device),
1376 GFP_KERNEL);
1377 if (chip->pwm_dev == NULL) {
1378 pr_err("kcalloc() failed.\n");
1379 mutex_destroy(&chip->pwm_mutex);
1380 kfree(chip);
1381 return -ENOMEM;
1382 }
1383
1384 for (i = 0; i < chip->pwm_channels; i++) {
1385 chip->pwm_dev[i].pwm_id = i;
1386 chip->pwm_dev[i].chip = chip;
1387 }
1388
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001389 platform_set_drvdata(pdev, chip);
1390
1391 if (pm8xxx_pwm_dbg_probe(&pdev->dev) < 0)
1392 pr_err("could not set up debugfs\n");
1393
1394 pr_notice("OK\n");
1395 return 0;
1396}
1397
1398static int __devexit pm8xxx_pwm_remove(struct platform_device *pdev)
1399{
1400 struct pm8xxx_pwm_chip *chip = dev_get_drvdata(pdev->dev.parent);
1401
1402 pm8xxx_pwm_dbg_remove();
Jay Chokshi57656862011-09-07 12:02:22 -07001403 kfree(chip->pwm_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001404 mutex_destroy(&chip->pwm_mutex);
1405 platform_set_drvdata(pdev, NULL);
1406 kfree(chip);
1407 return 0;
1408}
1409
1410static struct platform_driver pm8xxx_pwm_driver = {
1411 .probe = pm8xxx_pwm_probe,
1412 .remove = __devexit_p(pm8xxx_pwm_remove),
1413 .driver = {
1414 .name = PM8XXX_PWM_DEV_NAME,
1415 .owner = THIS_MODULE,
1416 },
1417};
1418
1419static int __init pm8xxx_pwm_init(void)
1420{
1421 return platform_driver_register(&pm8xxx_pwm_driver);
1422}
1423
1424static void __exit pm8xxx_pwm_exit(void)
1425{
1426 platform_driver_unregister(&pm8xxx_pwm_driver);
1427}
1428
1429subsys_initcall(pm8xxx_pwm_init);
1430module_exit(pm8xxx_pwm_exit);
1431
1432MODULE_LICENSE("GPL v2");
1433MODULE_DESCRIPTION("PM8XXX PWM driver");
1434MODULE_VERSION("1.0");
1435MODULE_ALIAS("platform:" PM8XXX_PWM_DEV_NAME);