blob: 55e5ed0cfb80bdbf9441037fbc896ca5b1da3ec1 [file] [log] [blame]
Willie Ruan70f989d2012-01-10 10:09:36 -08001/* Copyright (c) 2011-2012, 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 * 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
Willie Ruandb78e942012-01-10 15:00:28 -0800148#define NSEC_1024HZ (NSEC_PER_SEC / 1024)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700149#define NSEC_32768HZ (NSEC_PER_SEC / 32768)
150#define NSEC_19P2MHZ (NSEC_PER_SEC / 19200000)
151
152#define CLK_PERIOD_MIN NSEC_19P2MHZ
Willie Ruandb78e942012-01-10 15:00:28 -0800153#define CLK_PERIOD_MAX NSEC_1024HZ
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154
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] = {
Willie Ruandb78e942012-01-10 15:00:28 -0800166 { PRE_DIVIDE_0 * NSEC_1024HZ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167 PRE_DIVIDE_0 * NSEC_32768HZ,
168 PRE_DIVIDE_0 * NSEC_19P2MHZ,
169 },
Willie Ruandb78e942012-01-10 15:00:28 -0800170 { PRE_DIVIDE_1 * NSEC_1024HZ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171 PRE_DIVIDE_1 * NSEC_32768HZ,
172 PRE_DIVIDE_1 * NSEC_19P2MHZ,
173 },
Willie Ruandb78e942012-01-10 15:00:28 -0800174 { PRE_DIVIDE_2 * NSEC_1024HZ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175 PRE_DIVIDE_2 * NSEC_32768HZ,
176 PRE_DIVIDE_2 * NSEC_19P2MHZ,
177 },
178};
179
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180/* Private data */
181struct pm8xxx_pwm_chip;
182
183struct pwm_device {
184 int pwm_id; /* = bank/channel id */
185 int in_use;
186 const char *label;
Willie Ruan719c6762011-08-25 11:03:14 -0700187 struct pm8xxx_pwm_period period;
188 int pwm_value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189 int pwm_period;
190 int pwm_duty;
Jay Chokshi57656862011-09-07 12:02:22 -0700191 u8 pwm_lpg_ctl[PM8XXX_LPG_CTL_REGS];
192 u8 pwm_ctl1;
193 u8 pwm_ctl2;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700194 int irq;
195 struct pm8xxx_pwm_chip *chip;
Willie Ruan719c6762011-08-25 11:03:14 -0700196 int bypass_lut;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197};
198
199struct pm8xxx_pwm_chip {
Jay Chokshi57656862011-09-07 12:02:22 -0700200 struct pwm_device *pwm_dev;
201 u8 pwm_channels;
202 u8 pwm_total_pre_divs;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700203 u8 bank_mask;
204 struct mutex pwm_mutex;
205 struct device *dev;
Jay Chokshi57656862011-09-07 12:02:22 -0700206 bool is_lpg_supported;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207};
208
209static struct pm8xxx_pwm_chip *pwm_chip;
210
Willie Ruan719c6762011-08-25 11:03:14 -0700211struct pm8xxx_pwm_lut {
212 /* LUT parameters */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213 int lut_duty_ms;
214 int lut_lo_index;
215 int lut_hi_index;
216 int lut_pause_hi;
217 int lut_pause_lo;
218 int flags;
219};
220
221static const u16 duty_msec[PM8XXX_PWM_1KHZ_COUNT_MAX + 1] = {
222 0, 1, 2, 3, 4, 6, 8, 16, 18, 24, 32, 36, 64, 128, 256, 512
223};
224
225static const u16 pause_count[PM8XXX_PWM_PAUSE_COUNT_MAX + 1] = {
226 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
227 23, 28, 31, 42, 47, 56, 63, 83, 94, 111, 125, 167, 188, 222, 250, 333,
228 375, 500, 667, 750, 800, 900, 1000, 1100,
229 1200, 1300, 1400, 1500, 1600, 1800, 2000, 2500,
230 3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500,
231 7000
232};
233
234/* Internal functions */
Willie Ruan8de2f382011-08-25 11:04:50 -0700235static void pm8xxx_pwm_save(u8 *u8p, u8 mask, u8 val)
236{
237 *u8p &= ~mask;
238 *u8p |= val & mask;
239}
240
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700241static int pm8xxx_pwm_bank_enable(struct pwm_device *pwm, int enable)
242{
243 int rc;
244 u8 reg;
245 struct pm8xxx_pwm_chip *chip;
246
247 chip = pwm->chip;
248
249 if (enable)
250 reg = chip->bank_mask | (1 << pwm->pwm_id);
251 else
252 reg = chip->bank_mask & ~(1 << pwm->pwm_id);
253
254 rc = pm8xxx_writeb(chip->dev->parent, SSBI_REG_ADDR_LPG_BANK_EN, reg);
255 if (rc) {
Willie Ruan8de2f382011-08-25 11:04:50 -0700256 pr_err("pm8xxx_writeb(): rc=%d (Enable LPG Bank)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700257 return rc;
258 }
259 chip->bank_mask = reg;
260
261 return 0;
262}
263
264static int pm8xxx_pwm_bank_sel(struct pwm_device *pwm)
265{
266 int rc;
267
268 rc = pm8xxx_writeb(pwm->chip->dev->parent, SSBI_REG_ADDR_LPG_BANK_SEL,
269 pwm->pwm_id);
270 if (rc)
Willie Ruan8de2f382011-08-25 11:04:50 -0700271 pr_err("pm8xxx_writeb(): rc=%d (Select PWM Bank)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272 return rc;
273}
274
275static int pm8xxx_pwm_start(struct pwm_device *pwm, int start, int ramp_start)
276{
277 int rc;
278 u8 reg;
279
280 if (start) {
Jay Chokshi57656862011-09-07 12:02:22 -0700281 reg = pwm->pwm_lpg_ctl[0] | PM8XXX_PWM_PWM_START;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700282 if (ramp_start)
283 reg |= PM8XXX_PWM_RAMP_GEN_START;
284 else
285 reg &= ~PM8XXX_PWM_RAMP_GEN_START;
286 } else {
Jay Chokshi57656862011-09-07 12:02:22 -0700287 reg = pwm->pwm_lpg_ctl[0] & ~PM8XXX_PWM_PWM_START;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700288 reg &= ~PM8XXX_PWM_RAMP_GEN_START;
289 }
290
291 rc = pm8xxx_writeb(pwm->chip->dev->parent, SSBI_REG_ADDR_LPG_CTL(0),
292 reg);
293 if (rc)
Willie Ruan8de2f382011-08-25 11:04:50 -0700294 pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl 0)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700295 else
Jay Chokshi57656862011-09-07 12:02:22 -0700296 pwm->pwm_lpg_ctl[0] = reg;
297 return rc;
298}
299
300static int pm8xxx_pwm_disable(struct pwm_device *pwm)
301{
302 int rc;
303 u8 reg;
304
305 reg = pwm->pwm_ctl1 & PM8XXX_PWM_DISABLE;
306
307 rc = pm8xxx_writeb(pwm->chip->dev->parent,
308 SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), reg);
309
310 if (rc)
311 pr_err("pm8xxx_writeb(): rc=%d (Disable PWM Ctl %d)\n", rc,
312 pwm->pwm_id);
313 return rc;
314}
315
316static int pm8xxx_pwm_enable(struct pwm_device *pwm)
317{
318 /**
319 * A kind of best Effort: Just write the clock information that
320 * we have in the register.
321 */
322 int rc;
323
324 rc = pm8xxx_writeb(pwm->chip->dev->parent,
325 SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), pwm->pwm_ctl1);
326
327 if (rc)
328 pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl %d)\n", rc,
329 pwm->pwm_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700330 return rc;
331}
332
333static void pm8xxx_pwm_calc_period(unsigned int period_us,
Willie Ruan719c6762011-08-25 11:03:14 -0700334 struct pm8xxx_pwm_period *period)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700335{
336 int n, m, clk, div;
337 int best_m, best_div, best_clk;
338 int last_err, cur_err, better_err, better_m;
339 unsigned int tmp_p, last_p, min_err, period_n;
340
341 /* PWM Period / N */
Willie Ruan70f989d2012-01-10 10:09:36 -0800342 if (period_us < ((unsigned)(-1) / NSEC_PER_USEC)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700343 period_n = (period_us * NSEC_PER_USEC) >> 6;
344 n = 6;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700345 } else {
346 period_n = (period_us >> 9) * NSEC_PER_USEC;
347 n = 9;
348 }
349
Willie Ruan9f5488d2012-01-10 14:35:02 -0800350 min_err = (unsigned)(-1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700351 best_m = 0;
352 best_clk = 0;
353 best_div = 0;
354 for (clk = 0; clk < NUM_CLOCKS; clk++) {
Jay Chokshi57656862011-09-07 12:02:22 -0700355 for (div = 0; div < pwm_chip->pwm_total_pre_divs; div++) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700356 tmp_p = period_n;
357 last_p = tmp_p;
358 for (m = 0; m <= PM8XXX_PWM_M_MAX; m++) {
359 if (tmp_p <= pt_t[div][clk]) {
360 /* Found local best */
361 if (!m) {
362 better_err = pt_t[div][clk] -
363 tmp_p;
364 better_m = m;
365 } else {
366 last_err = last_p -
367 pt_t[div][clk];
368 cur_err = pt_t[div][clk] -
369 tmp_p;
370
371 if (cur_err < last_err) {
372 better_err = cur_err;
373 better_m = m;
374 } else {
375 better_err = last_err;
376 better_m = m - 1;
377 }
378 }
379
380 if (better_err < min_err) {
381 min_err = better_err;
382 best_m = better_m;
383 best_clk = clk;
384 best_div = div;
385 }
386 break;
387 } else {
388 last_p = tmp_p;
389 tmp_p >>= 1;
390 }
391 }
392 }
393 }
394
Willie Ruan75d9e5b2011-08-25 10:59:17 -0700395 /* Use higher resolution */
396 if (best_m >= 3 && n == 6) {
397 n += 3;
398 best_m -= 3;
399 }
400
Willie Ruan719c6762011-08-25 11:03:14 -0700401 period->pwm_size = n;
402 period->clk = best_clk;
403 period->pre_div = best_div;
404 period->pre_div_exp = best_m;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700405}
406
Willie Ruan8de2f382011-08-25 11:04:50 -0700407static void pm8xxx_pwm_calc_pwm_value(struct pwm_device *pwm,
408 unsigned int period_us,
409 unsigned int duty_us)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700410{
Willie Ruan8de2f382011-08-25 11:04:50 -0700411 unsigned int max_pwm_value, tmp;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700412
Willie Ruan8de2f382011-08-25 11:04:50 -0700413 /* Figure out pwm_value with overflow handling */
414 tmp = 1 << (sizeof(tmp) * 8 - pwm->period.pwm_size);
415 if (duty_us < tmp) {
416 tmp = duty_us << pwm->period.pwm_size;
417 pwm->pwm_value = tmp / period_us;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700418 } else {
Willie Ruan8de2f382011-08-25 11:04:50 -0700419 tmp = period_us >> pwm->period.pwm_size;
420 pwm->pwm_value = duty_us / tmp;
421 }
422 max_pwm_value = (1 << pwm->period.pwm_size) - 1;
423 if (pwm->pwm_value > max_pwm_value)
424 pwm->pwm_value = max_pwm_value;
425}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700426
Willie Ruan8de2f382011-08-25 11:04:50 -0700427static int pm8xxx_pwm_change_table(struct pwm_device *pwm, int duty_pct[],
428 int start_idx, int len, int raw_value)
429{
430 unsigned int pwm_value, max_pwm_value;
431 u8 cfg0, cfg1;
432 int i, pwm_size;
433 int rc = 0;
434
Jay Chokshi57656862011-09-07 12:02:22 -0700435 pwm_size = (pwm->pwm_lpg_ctl[5] & PM8XXX_PWM_SIZE_9_BIT) ? 9 : 6;
Willie Ruan8de2f382011-08-25 11:04:50 -0700436 max_pwm_value = (1 << pwm_size) - 1;
437 for (i = 0; i < len; i++) {
438 if (raw_value)
439 pwm_value = duty_pct[i];
440 else
441 pwm_value = (duty_pct[i] << pwm_size) / 100;
442
443 if (pwm_value > max_pwm_value)
444 pwm_value = max_pwm_value;
445 cfg0 = pwm_value;
446 cfg1 = (pwm_value >> 1) & 0x80;
447 cfg1 |= start_idx + i;
448
449 rc = pm8xxx_writeb(pwm->chip->dev->parent,
450 SSBI_REG_ADDR_LPG_LUT_CFG0, cfg0);
451 if (rc)
452 break;
453
454 rc = pm8xxx_writeb(pwm->chip->dev->parent,
455 SSBI_REG_ADDR_LPG_LUT_CFG1, cfg1);
456 if (rc)
457 break;
458 }
459 return rc;
460}
461
462static void pm8xxx_pwm_save_index(struct pwm_device *pwm,
463 int low_idx, int high_idx, int flags)
464{
Jay Chokshi57656862011-09-07 12:02:22 -0700465 pwm->pwm_lpg_ctl[1] = high_idx & PM8XXX_PWM_HIGH_INDEX_MASK;
466 pwm->pwm_lpg_ctl[2] = low_idx & PM8XXX_PWM_LOW_INDEX_MASK;
Willie Ruan8de2f382011-08-25 11:04:50 -0700467
468 if (flags & PM_PWM_LUT_REVERSE)
Jay Chokshi57656862011-09-07 12:02:22 -0700469 pwm->pwm_lpg_ctl[1] |= PM8XXX_PWM_REVERSE_EN;
Willie Ruan8de2f382011-08-25 11:04:50 -0700470 if (flags & PM_PWM_LUT_RAMP_UP)
Jay Chokshi57656862011-09-07 12:02:22 -0700471 pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_RAMP_UP;
Willie Ruan8de2f382011-08-25 11:04:50 -0700472 if (flags & PM_PWM_LUT_LOOP)
Jay Chokshi57656862011-09-07 12:02:22 -0700473 pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_LOOP_EN;
Willie Ruan8de2f382011-08-25 11:04:50 -0700474}
475
476static void pm8xxx_pwm_save_period(struct pwm_device *pwm)
477{
478 u8 mask, val;
479
Jay Chokshi57656862011-09-07 12:02:22 -0700480 if (pwm_chip->is_lpg_supported) {
481 val = ((pwm->period.clk + 1) << PM8XXX_LPG_PWM_CLK_SEL_SHIFT)
482 & PM8XXX_LPG_PWM_CLK_SEL_MASK;
483 val |= (pwm->period.pre_div << PM8XXX_LPG_PWM_PREDIVIDE_SHIFT)
484 & PM8XXX_LPG_PWM_PREDIVIDE_MASK;
485 val |= pwm->period.pre_div_exp & PM8XXX_LPG_PWM_M_MASK;
486 mask = PM8XXX_LPG_PWM_CLK_SEL_MASK |
487 PM8XXX_LPG_PWM_PREDIVIDE_MASK | PM8XXX_LPG_PWM_M_MASK;
488 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
Willie Ruan8de2f382011-08-25 11:04:50 -0700489
Jay Chokshi57656862011-09-07 12:02:22 -0700490 val = (pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0;
491 mask = PM8XXX_PWM_SIZE_9_BIT;
492 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
493 } else {
494 val = ((pwm->period.clk + 1) << PM8XXX_PWM_CLK_SEL_SHIFT)
495 & PM8XXX_PWM_CLK_SEL_MASK;
496 val |= (pwm->period.pre_div << PM8XXX_PWM_PREDIVIDE_SHIFT)
497 & PM8XXX_PWM_PREDIVIDE_MASK;
498 val |= (pwm->period.pre_div_exp << PM8XXX_PWM_M_SHIFT)
499 & PM8XXX_PWM_M_MASK;
500 val |= (((pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0)
501 << PM8XXX_PWM_SIZE_SHIFT) & PM8XXX_PWM_SIZE_MASK;
502
503 mask = PM8XXX_PWM_CLK_SEL_MASK | PM8XXX_PWM_PREDIVIDE_MASK |
504 PM8XXX_PWM_M_MASK | PM8XXX_PWM_SIZE_MASK;
505 pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
506 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700507}
508
509static void pm8xxx_pwm_save_pwm_value(struct pwm_device *pwm)
510{
511 u8 mask, val;
512
Jay Chokshi57656862011-09-07 12:02:22 -0700513 if (pwm_chip->is_lpg_supported) {
514 val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 1) : 0;
515 pwm->pwm_lpg_ctl[3] = pwm->pwm_value;
516 mask = PM8XXX_PWM_VALUE_BIT8;
517 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
518 } else {
519 val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 8) : 0;
520 pwm->pwm_ctl2 = pwm->pwm_value;
521 mask = PM8XXX_PWM_VALUE_BIT0;
522 pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
523 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700524}
525
526static void pm8xxx_pwm_save_duty_time(struct pwm_device *pwm,
527 struct pm8xxx_pwm_lut *lut)
528{
529 int i;
530 u8 mask, val;
531
532 /* Linear search for duty time */
533 for (i = 0; i < PM8XXX_PWM_1KHZ_COUNT_MAX; i++) {
534 if (duty_msec[i] >= lut->lut_duty_ms)
535 break;
536 }
537 val = i << PM8XXX_PWM_1KHZ_COUNT_SHIFT;
538
539 mask = PM8XXX_PWM_1KHZ_COUNT_MASK;
Jay Chokshi57656862011-09-07 12:02:22 -0700540 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[0], mask, val);
Willie Ruan8de2f382011-08-25 11:04:50 -0700541}
542
543static void pm8xxx_pwm_save_pause(struct pwm_device *pwm,
544 struct pm8xxx_pwm_lut *lut)
545{
546 int i, pause_cnt, time_cnt;
547 u8 mask, val;
548
Jay Chokshi57656862011-09-07 12:02:22 -0700549 time_cnt = (pwm->pwm_lpg_ctl[0] & PM8XXX_PWM_1KHZ_COUNT_MASK)
Willie Ruan8de2f382011-08-25 11:04:50 -0700550 >> PM8XXX_PWM_1KHZ_COUNT_SHIFT;
551 if (lut->flags & PM_PWM_LUT_PAUSE_HI_EN) {
552 pause_cnt = (lut->lut_pause_hi + duty_msec[time_cnt] / 2)
553 / duty_msec[time_cnt];
554 /* Linear search for pause time */
555 for (i = 0; i < PM8XXX_PWM_PAUSE_COUNT_MAX; i++) {
556 if (pause_count[i] >= pause_cnt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700557 break;
558 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700559 val = (i << PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT) &
560 PM8XXX_PWM_PAUSE_COUNT_HI_MASK;
561 val |= PM8XXX_PWM_PAUSE_ENABLE_HIGH;
562 } else {
563 val = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700564 }
565
Willie Ruan8de2f382011-08-25 11:04:50 -0700566 mask = PM8XXX_PWM_PAUSE_COUNT_HI_MASK | PM8XXX_PWM_PAUSE_ENABLE_HIGH;
Jay Chokshi57656862011-09-07 12:02:22 -0700567 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700568
Willie Ruan8de2f382011-08-25 11:04:50 -0700569 if (lut->flags & PM_PWM_LUT_PAUSE_LO_EN) {
570 /* Linear search for pause time */
571 pause_cnt = (lut->lut_pause_lo + duty_msec[time_cnt] / 2)
572 / duty_msec[time_cnt];
573 for (i = 0; i < PM8XXX_PWM_PAUSE_COUNT_MAX; i++) {
574 if (pause_count[i] >= pause_cnt)
575 break;
576 }
577 val = (i << PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT) &
578 PM8XXX_PWM_PAUSE_COUNT_LO_MASK;
579 val |= PM8XXX_PWM_PAUSE_ENABLE_LOW;
580 } else {
581 val = 0;
582 }
583
584 mask = PM8XXX_PWM_PAUSE_COUNT_LO_MASK | PM8XXX_PWM_PAUSE_ENABLE_LOW;
Jay Chokshi57656862011-09-07 12:02:22 -0700585 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[6], mask, val);
Willie Ruan8de2f382011-08-25 11:04:50 -0700586}
587
Jay Chokshi57656862011-09-07 12:02:22 -0700588static int pm8xxx_pwm_write(struct pwm_device *pwm)
589{
590 int rc = 0;
591
592 rc = pm8xxx_writeb(pwm->chip->dev->parent,
593 SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id),
594 pwm->pwm_ctl1);
595 if (rc) {
596 pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl1[%d])\n",
597 rc, pwm->pwm_id);
598 return rc;
599 }
600
601 rc = pm8xxx_writeb(pwm->chip->dev->parent,
602 SSBI_REG_ADDR_PWM_CTL2(pwm->pwm_id),
603 pwm->pwm_ctl2);
604 if (rc) {
605 pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl2[%d])\n",
606 rc, pwm->pwm_id);
607 return rc;
608 }
609
610 return rc;
611}
612
613static int pm8xxx_lpg_pwm_write(struct pwm_device *pwm, int start, int end)
Willie Ruan8de2f382011-08-25 11:04:50 -0700614{
615 int i, rc;
616
617 /* Write in reverse way so 0 would be the last */
618 for (i = end - 1; i >= start; i--) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700619 rc = pm8xxx_writeb(pwm->chip->dev->parent,
620 SSBI_REG_ADDR_LPG_CTL(i),
Jay Chokshi57656862011-09-07 12:02:22 -0700621 pwm->pwm_lpg_ctl[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700622 if (rc) {
Willie Ruan8de2f382011-08-25 11:04:50 -0700623 pr_err("pm8xxx_writeb(): rc=%d (PWM Ctl[%d])\n", rc, i);
624 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700625 }
626 }
627
Willie Ruan8de2f382011-08-25 11:04:50 -0700628 return 0;
629}
630
631static int pm8xxx_pwm_change_lut(struct pwm_device *pwm,
632 struct pm8xxx_pwm_lut *lut)
633{
634 int rc;
635
636 pm8xxx_pwm_save_index(pwm, lut->lut_lo_index,
637 lut->lut_hi_index, lut->flags);
638 pm8xxx_pwm_save_duty_time(pwm, lut);
639 pm8xxx_pwm_save_pause(pwm, lut);
Jay Chokshi57656862011-09-07 12:02:22 -0700640 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1], PM8XXX_PWM_BYPASS_LUT, 0);
Willie Ruan8de2f382011-08-25 11:04:50 -0700641
642 pm8xxx_pwm_bank_sel(pwm);
Jay Chokshi57656862011-09-07 12:02:22 -0700643 rc = pm8xxx_lpg_pwm_write(pwm, 0, 7);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700644
645 return rc;
646}
647
648/* APIs */
649/**
650 * pwm_request - request a PWM device
651 * @pwm_id: PWM id or channel
652 * @label: the label to identify the user
653 */
654struct pwm_device *pwm_request(int pwm_id, const char *label)
655{
656 struct pwm_device *pwm;
657
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700658 if (pwm_chip == NULL) {
659 pr_err("No pwm_chip\n");
660 return ERR_PTR(-ENODEV);
661 }
662
Jay Chokshi57656862011-09-07 12:02:22 -0700663 if (pwm_id >= pwm_chip->pwm_channels || pwm_id < 0) {
664 pr_err("Invalid pwm_id: %d with %s\n",
665 pwm_id, label ? label : ".");
666 return ERR_PTR(-EINVAL);
667 }
668
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700669 mutex_lock(&pwm_chip->pwm_mutex);
670 pwm = &pwm_chip->pwm_dev[pwm_id];
671 if (!pwm->in_use) {
672 pwm->in_use = 1;
673 pwm->label = label;
674 } else {
675 pwm = ERR_PTR(-EBUSY);
676 }
677 mutex_unlock(&pwm_chip->pwm_mutex);
678
679 return pwm;
680}
681EXPORT_SYMBOL_GPL(pwm_request);
682
683/**
684 * pwm_free - free a PWM device
685 * @pwm: the PWM device
686 */
687void pwm_free(struct pwm_device *pwm)
688{
689 if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
690 pr_err("Invalid pwm handle\n");
691 return;
692 }
693
694 mutex_lock(&pwm->chip->pwm_mutex);
695 if (pwm->in_use) {
Jay Chokshi57656862011-09-07 12:02:22 -0700696 if (pwm_chip->is_lpg_supported) {
697 pm8xxx_pwm_bank_sel(pwm);
698 pm8xxx_pwm_start(pwm, 0, 0);
699 } else {
700 pm8xxx_pwm_disable(pwm);
701 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700702 pwm->in_use = 0;
703 pwm->label = NULL;
704 }
Jay Chokshi57656862011-09-07 12:02:22 -0700705 if (pwm_chip->is_lpg_supported)
706 pm8xxx_pwm_bank_enable(pwm, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700707 mutex_unlock(&pwm->chip->pwm_mutex);
708}
709EXPORT_SYMBOL_GPL(pwm_free);
710
711/**
712 * pwm_config - change a PWM device configuration
713 * @pwm: the PWM device
714 * @period_us: period in microseconds
715 * @duty_us: duty cycle in microseconds
716 */
717int pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
718{
Willie Ruan719c6762011-08-25 11:03:14 -0700719 struct pm8xxx_pwm_period *period;
Jay Chokshi57656862011-09-07 12:02:22 -0700720 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700721
722 if (pwm == NULL || IS_ERR(pwm) ||
723 duty_us > period_us ||
724 (unsigned)period_us > PM8XXX_PWM_PERIOD_MAX ||
725 (unsigned)period_us < PM8XXX_PWM_PERIOD_MIN) {
726 pr_err("Invalid pwm handle or parameters\n");
727 return -EINVAL;
728 }
729 if (pwm->chip == NULL) {
730 pr_err("No pwm_chip\n");
731 return -ENODEV;
732 }
733
Willie Ruan719c6762011-08-25 11:03:14 -0700734 period = &pwm->period;
735
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700736 mutex_lock(&pwm->chip->pwm_mutex);
737
738 if (!pwm->in_use) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700739 rc = -EINVAL;
740 goto out_unlock;
741 }
742
Willie Ruan8de2f382011-08-25 11:04:50 -0700743 if (pwm->pwm_period != period_us) {
744 pm8xxx_pwm_calc_period(period_us, period);
745 pm8xxx_pwm_save_period(pwm);
746 pwm->pwm_period = period_us;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700747 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700748
Willie Ruan8de2f382011-08-25 11:04:50 -0700749 pm8xxx_pwm_calc_pwm_value(pwm, period_us, duty_us);
750 pm8xxx_pwm_save_pwm_value(pwm);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700751
Jay Chokshi57656862011-09-07 12:02:22 -0700752 if (pwm_chip->is_lpg_supported) {
753 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
754 PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
755
756 pm8xxx_pwm_bank_sel(pwm);
757 rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
758 } else {
759 rc = pm8xxx_pwm_write(pwm);
760 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700761
762 pr_debug("duty/period=%u/%u usec: pwm_value=%d (of %d)\n",
763 (unsigned)duty_us, (unsigned)period_us,
764 pwm->pwm_value, 1 << period->pwm_size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700765
766out_unlock:
767 mutex_unlock(&pwm->chip->pwm_mutex);
768 return rc;
769}
770EXPORT_SYMBOL_GPL(pwm_config);
771
772/**
773 * pwm_enable - start a PWM output toggling
774 * @pwm: the PWM device
775 */
776int pwm_enable(struct pwm_device *pwm)
777{
Jay Chokshi57656862011-09-07 12:02:22 -0700778 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700779
780 if (pwm == NULL || IS_ERR(pwm)) {
781 pr_err("Invalid pwm handle\n");
782 return -EINVAL;
783 }
784 if (pwm->chip == NULL) {
785 pr_err("No pwm_chip\n");
786 return -ENODEV;
787 }
788
789 mutex_lock(&pwm->chip->pwm_mutex);
790 if (!pwm->in_use) {
791 pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
792 rc = -EINVAL;
793 } else {
Jay Chokshi57656862011-09-07 12:02:22 -0700794 if (pwm_chip->is_lpg_supported) {
795 rc = pm8xxx_pwm_bank_enable(pwm, 1);
796 pm8xxx_pwm_bank_sel(pwm);
797 pm8xxx_pwm_start(pwm, 1, 0);
798 } else {
799 pm8xxx_pwm_enable(pwm);
800 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700801 }
802 mutex_unlock(&pwm->chip->pwm_mutex);
803 return rc;
804}
805EXPORT_SYMBOL_GPL(pwm_enable);
806
807/**
808 * pwm_disable - stop a PWM output toggling
809 * @pwm: the PWM device
810 */
811void pwm_disable(struct pwm_device *pwm)
812{
813 if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
814 pr_err("Invalid pwm handle or no pwm_chip\n");
815 return;
816 }
817
818 mutex_lock(&pwm->chip->pwm_mutex);
819 if (pwm->in_use) {
Jay Chokshi57656862011-09-07 12:02:22 -0700820 if (pwm_chip->is_lpg_supported) {
821 pm8xxx_pwm_bank_sel(pwm);
822 pm8xxx_pwm_start(pwm, 0, 0);
823 pm8xxx_pwm_bank_enable(pwm, 0);
824 } else {
825 pm8xxx_pwm_disable(pwm);
826 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700827 }
828 mutex_unlock(&pwm->chip->pwm_mutex);
829}
830EXPORT_SYMBOL_GPL(pwm_disable);
831
832/**
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700833 * pm8xxx_pwm_config_period - change PWM period
834 *
835 * @pwm: the PWM device
836 * @pwm_p: period in struct pm8xxx_pwm_period
837 */
838int pm8xxx_pwm_config_period(struct pwm_device *pwm,
839 struct pm8xxx_pwm_period *period)
840{
841 int rc;
842
843 if (pwm == NULL || IS_ERR(pwm) || period == NULL)
844 return -EINVAL;
845 if (pwm->chip == NULL)
846 return -ENODEV;
847
848 mutex_lock(&pwm->chip->pwm_mutex);
849
850 if (!pwm->in_use) {
851 rc = -EINVAL;
852 goto out_unlock;
853 }
854
855 pwm->period.pwm_size = period->pwm_size;
856 pwm->period.clk = period->clk;
857 pwm->period.pre_div = period->pre_div;
858 pwm->period.pre_div_exp = period->pre_div_exp;
859
860 pm8xxx_pwm_save_period(pwm);
Jay Chokshi57656862011-09-07 12:02:22 -0700861
862 if (pwm_chip->is_lpg_supported) {
863 pm8xxx_pwm_bank_sel(pwm);
864 rc = pm8xxx_lpg_pwm_write(pwm, 4, 6);
865 } else {
866 rc = pm8xxx_pwm_write(pwm);
867 }
868
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700869
870out_unlock:
871 mutex_unlock(&pwm->chip->pwm_mutex);
872 return rc;
873}
874EXPORT_SYMBOL(pm8xxx_pwm_config_period);
875
876/**
877 * pm8xxx_pwm_config_pwm_value - change a PWM device configuration
878 * @pwm: the PWM device
879 * @pwm_value: the duty cycle in raw PWM value (< 2^pwm_size)
880 */
881int pm8xxx_pwm_config_pwm_value(struct pwm_device *pwm, int pwm_value)
882{
883 int rc = 0;
884
885 if (pwm == NULL || IS_ERR(pwm))
886 return -EINVAL;
887 if (pwm->chip == NULL)
888 return -ENODEV;
889
890 mutex_lock(&pwm->chip->pwm_mutex);
891
892 if (!pwm->in_use || !pwm->pwm_period) {
893 rc = -EINVAL;
894 goto out_unlock;
895 }
896
897 if (pwm->pwm_value == pwm_value)
898 goto out_unlock;
899
900 pwm->pwm_value = pwm_value;
901
902 pm8xxx_pwm_save_pwm_value(pwm);
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700903
Jay Chokshi57656862011-09-07 12:02:22 -0700904 if (pwm_chip->is_lpg_supported) {
905 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
906 PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
907 pm8xxx_pwm_bank_sel(pwm);
908 rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
909 } else {
910 rc = pm8xxx_pwm_write(pwm);
911 }
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700912
913 if (rc)
914 pr_err("[%d]: pm8xxx_pwm_write: rc=%d\n", pwm->pwm_id, rc);
915
916out_unlock:
917 mutex_unlock(&pwm->chip->pwm_mutex);
918 return rc;
919}
920EXPORT_SYMBOL_GPL(pm8xxx_pwm_config_pwm_value);
921
922/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700923 * pm8xxx_pwm_lut_config - change a PWM device configuration to use LUT
924 * @pwm: the PWM device
925 * @period_us: period in microseconds
926 * @duty_pct: arrary of duty cycles in percent, like 20, 50.
927 * @duty_time_ms: time for each duty cycle in milliseconds
928 * @start_idx: start index in lookup table from 0 to MAX-1
929 * @idx_len: number of index
930 * @pause_lo: pause time in milliseconds at low index
931 * @pause_hi: pause time in milliseconds at high index
932 * @flags: control flags
933 */
934int pm8xxx_pwm_lut_config(struct pwm_device *pwm, int period_us,
935 int duty_pct[], int duty_time_ms, int start_idx,
936 int idx_len, int pause_lo, int pause_hi, int flags)
937{
Willie Ruan719c6762011-08-25 11:03:14 -0700938 struct pm8xxx_pwm_lut lut;
939 struct pm8xxx_pwm_period *period;
Willie Ruan8de2f382011-08-25 11:04:50 -0700940 int len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700941 int rc;
942
943 if (pwm == NULL || IS_ERR(pwm) || !idx_len) {
944 pr_err("Invalid pwm handle or idx_len=0\n");
945 return -EINVAL;
946 }
947 if (duty_pct == NULL && !(flags & PM_PWM_LUT_NO_TABLE)) {
948 pr_err("Invalid duty_pct with flag\n");
949 return -EINVAL;
950 }
951 if (pwm->chip == NULL) {
952 pr_err("No pwm_chip\n");
953 return -ENODEV;
954 }
Jay Chokshi57656862011-09-07 12:02:22 -0700955
956 if (pwm->chip->is_lpg_supported == 0) {
957 pr_err("LPG module isn't supported\n");
958 return -EINVAL;
959 }
960
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700961 if (idx_len >= PM_PWM_LUT_SIZE && start_idx) {
962 pr_err("Wrong LUT size or index\n");
963 return -EINVAL;
964 }
965 if ((start_idx + idx_len) > PM_PWM_LUT_SIZE) {
966 pr_err("Exceed LUT limit\n");
967 return -EINVAL;
968 }
969 if ((unsigned)period_us > PM8XXX_PWM_PERIOD_MAX ||
970 (unsigned)period_us < PM8XXX_PWM_PERIOD_MIN) {
971 pr_err("Period out of range\n");
972 return -EINVAL;
973 }
974
Willie Ruan719c6762011-08-25 11:03:14 -0700975 period = &pwm->period;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700976 mutex_lock(&pwm->chip->pwm_mutex);
977
978 if (!pwm->in_use) {
979 pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
980 rc = -EINVAL;
981 goto out_unlock;
982 }
983
Willie Ruan8de2f382011-08-25 11:04:50 -0700984 if (pwm->pwm_period != period_us) {
985 pm8xxx_pwm_calc_period(period_us, period);
986 pm8xxx_pwm_save_period(pwm);
987 pwm->pwm_period = period_us;
988 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700989
990 len = (idx_len > PM_PWM_LUT_SIZE) ? PM_PWM_LUT_SIZE : idx_len;
991
992 if (flags & PM_PWM_LUT_NO_TABLE)
993 goto after_table_write;
994
Willie Ruan8de2f382011-08-25 11:04:50 -0700995 rc = pm8xxx_pwm_change_table(pwm, duty_pct, start_idx, len, 0);
996 if (rc) {
997 pr_err("pm8xxx_pwm_change_table: rc=%d\n", rc);
998 goto out_unlock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700999 }
1000
1001after_table_write:
Willie Ruan719c6762011-08-25 11:03:14 -07001002 lut.lut_duty_ms = duty_time_ms;
1003 lut.lut_lo_index = start_idx;
1004 lut.lut_hi_index = start_idx + len - 1;
1005 lut.lut_pause_lo = pause_lo;
1006 lut.lut_pause_hi = pause_hi;
1007 lut.flags = flags;
1008 pwm->bypass_lut = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001009
Willie Ruan8de2f382011-08-25 11:04:50 -07001010 rc = pm8xxx_pwm_change_lut(pwm, &lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001011
1012out_unlock:
1013 mutex_unlock(&pwm->chip->pwm_mutex);
1014 return rc;
1015}
1016EXPORT_SYMBOL_GPL(pm8xxx_pwm_lut_config);
1017
1018/**
1019 * pm8xxx_pwm_lut_enable - control a PWM device to start/stop LUT ramp
1020 * @pwm: the PWM device
1021 * @start: to start (1), or stop (0)
1022 */
1023int pm8xxx_pwm_lut_enable(struct pwm_device *pwm, int start)
1024{
1025 if (pwm == NULL || IS_ERR(pwm)) {
1026 pr_err("Invalid pwm handle\n");
1027 return -EINVAL;
1028 }
1029 if (pwm->chip == NULL) {
1030 pr_err("No pwm_chip\n");
1031 return -ENODEV;
1032 }
Jay Chokshi57656862011-09-07 12:02:22 -07001033 if (pwm->chip->is_lpg_supported == 0) {
1034 pr_err("LPG module isn't supported\n");
1035 return -EINVAL;
1036 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001037
1038 mutex_lock(&pwm->chip->pwm_mutex);
1039 if (start) {
1040 pm8xxx_pwm_bank_enable(pwm, 1);
1041
1042 pm8xxx_pwm_bank_sel(pwm);
1043 pm8xxx_pwm_start(pwm, 1, 1);
1044 } else {
1045 pm8xxx_pwm_bank_sel(pwm);
1046 pm8xxx_pwm_start(pwm, 0, 0);
1047
1048 pm8xxx_pwm_bank_enable(pwm, 0);
1049 }
1050 mutex_unlock(&pwm->chip->pwm_mutex);
1051 return 0;
1052}
1053EXPORT_SYMBOL_GPL(pm8xxx_pwm_lut_enable);
1054
1055#if defined(CONFIG_DEBUG_FS)
1056
1057struct pm8xxx_pwm_dbg_device;
1058
1059struct pm8xxx_pwm_user {
1060 int pwm_id;
1061 struct pwm_device *pwm;
1062 int period;
1063 int duty_cycle;
1064 int enable;
1065 struct pm8xxx_pwm_dbg_device *dbgdev;
1066};
1067
1068struct pm8xxx_pwm_dbg_device {
1069 struct mutex dbg_mutex;
1070 struct device *dev;
1071 struct dentry *dent;
1072
Jay Chokshi57656862011-09-07 12:02:22 -07001073 struct pm8xxx_pwm_user *user;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001074};
1075
1076static struct pm8xxx_pwm_dbg_device *pmic_dbg_device;
1077
1078static int dbg_pwm_check_period(int period)
1079{
1080 if (period < PM8XXX_PWM_PERIOD_MIN || period > PM8XXX_PWM_PERIOD_MAX) {
1081 pr_err("period is invalid: %d\n", period);
1082 return -EINVAL;
1083 }
1084 return 0;
1085}
1086
1087static int dbg_pwm_check_duty_cycle(int duty_cycle, const char *func_name)
1088{
1089 if (duty_cycle <= 0 || duty_cycle > 100) {
1090 pr_err("%s: duty_cycle is invalid: %d\n",
1091 func_name, duty_cycle);
1092 return -EINVAL;
1093 }
1094 return 0;
1095}
1096
1097static void dbg_pwm_check_handle(struct pm8xxx_pwm_user *puser)
1098{
1099 struct pwm_device *tmp;
1100
1101 if (puser->pwm == NULL) {
1102 tmp = pwm_request(puser->pwm_id, "pwm-dbg");
1103 if (PTR_ERR(puser->pwm)) {
1104 pr_err("pwm_request: err=%ld\n", PTR_ERR(puser->pwm));
1105 puser->pwm = NULL;
Willie Ruan10976ea2011-08-25 11:01:15 -07001106 } else {
1107 pr_debug("[id=%d] pwm_request ok\n", puser->pwm_id);
1108 puser->pwm = tmp;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001109 }
1110 }
1111}
1112
1113static int dbg_pwm_enable_set(void *data, u64 val)
1114{
1115 struct pm8xxx_pwm_user *puser = data;
1116 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1117 int rc;
1118
1119 mutex_lock(&dbgdev->dbg_mutex);
1120 rc = dbg_pwm_check_duty_cycle(puser->duty_cycle, __func__);
1121 if (!rc) {
1122 puser->enable = val;
1123 dbg_pwm_check_handle(puser);
1124 if (puser->pwm) {
1125 if (puser->enable)
1126 pwm_enable(puser->pwm);
1127 else
1128 pwm_disable(puser->pwm);
1129 }
1130 }
1131 mutex_unlock(&dbgdev->dbg_mutex);
1132 return 0;
1133}
1134
1135static int dbg_pwm_enable_get(void *data, u64 *val)
1136{
1137 struct pm8xxx_pwm_user *puser = data;
1138 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1139
1140 mutex_lock(&dbgdev->dbg_mutex);
1141 *val = puser->enable;
1142 mutex_unlock(&dbgdev->dbg_mutex);
1143 return 0;
1144}
1145
1146DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_enable_fops,
1147 dbg_pwm_enable_get, dbg_pwm_enable_set,
1148 "%lld\n");
1149
1150static int dbg_pwm_duty_cycle_set(void *data, u64 val)
1151{
1152 struct pm8xxx_pwm_user *puser = data;
1153 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1154 int rc;
1155
1156 mutex_lock(&dbgdev->dbg_mutex);
1157 rc = dbg_pwm_check_duty_cycle(val, __func__);
1158 if (!rc) {
1159 puser->duty_cycle = val;
1160 dbg_pwm_check_handle(puser);
1161 if (puser->pwm) {
1162 int duty_us;
1163
Willie Ruan10976ea2011-08-25 11:01:15 -07001164 duty_us = puser->duty_cycle * puser->period / 100;
1165 pwm_config(puser->pwm, duty_us, puser->period);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001166 }
1167 }
1168 mutex_unlock(&dbgdev->dbg_mutex);
1169 return 0;
1170}
1171
1172static int dbg_pwm_duty_cycle_get(void *data, u64 *val)
1173{
1174 struct pm8xxx_pwm_user *puser = data;
1175 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1176
1177 mutex_lock(&dbgdev->dbg_mutex);
1178 *val = puser->duty_cycle;
1179 mutex_unlock(&dbgdev->dbg_mutex);
1180 return 0;
1181}
1182
1183DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_duty_cycle_fops,
1184 dbg_pwm_duty_cycle_get, dbg_pwm_duty_cycle_set,
1185 "%lld\n");
1186
1187static int dbg_pwm_period_set(void *data, u64 val)
1188{
1189 struct pm8xxx_pwm_user *puser = data;
1190 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1191 int rc;
1192
1193 mutex_lock(&dbgdev->dbg_mutex);
1194 rc = dbg_pwm_check_period(val);
1195 if (!rc)
1196 puser->period = val;
1197 mutex_unlock(&dbgdev->dbg_mutex);
1198 return 0;
1199}
1200
1201static int dbg_pwm_period_get(void *data, u64 *val)
1202{
1203 struct pm8xxx_pwm_user *puser = data;
1204 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1205
1206 mutex_lock(&dbgdev->dbg_mutex);
1207 *val = puser->period;
1208 mutex_unlock(&dbgdev->dbg_mutex);
1209 return 0;
1210}
1211
1212DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_period_fops,
1213 dbg_pwm_period_get, dbg_pwm_period_set, "%lld\n");
1214
1215static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)
1216{
1217 struct pm8xxx_pwm_dbg_device *dbgdev;
1218 struct dentry *dent;
1219 struct dentry *temp;
1220 struct pm8xxx_pwm_user *puser;
1221 int i;
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001222 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001223
1224 if (dev == NULL) {
1225 pr_err("no parent data passed in.\n");
1226 return -EINVAL;
1227 }
1228
1229 dbgdev = kzalloc(sizeof *dbgdev, GFP_KERNEL);
1230 if (dbgdev == NULL) {
1231 pr_err("kzalloc() failed.\n");
1232 return -ENOMEM;
1233 }
1234
Jay Chokshi57656862011-09-07 12:02:22 -07001235 dbgdev->user = kcalloc(pwm_chip->pwm_channels,
1236 sizeof(struct pm8xxx_pwm_user), GFP_KERNEL);
1237 if (dbgdev->user == NULL) {
1238 pr_err("kcalloc() failed.\n");
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001239 rc = -ENOMEM;
1240 goto user_error;
Jay Chokshi57656862011-09-07 12:02:22 -07001241 }
1242
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001243 mutex_init(&dbgdev->dbg_mutex);
1244
1245 dbgdev->dev = dev;
1246
1247 dent = debugfs_create_dir("pm8xxx-pwm-dbg", NULL);
1248 if (dent == NULL || IS_ERR(dent)) {
1249 pr_err("ERR debugfs_create_dir: dent=%p\n", dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001250 rc = -ENOMEM;
1251 goto dir_error;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001252 }
1253
1254 dbgdev->dent = dent;
1255
Jay Chokshi57656862011-09-07 12:02:22 -07001256 for (i = 0; i < pwm_chip->pwm_channels; i++) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001257 char pwm_ch[] = "0";
1258
1259 pwm_ch[0] = '0' + i;
1260 dent = debugfs_create_dir(pwm_ch, dbgdev->dent);
1261 if (dent == NULL || IS_ERR(dent)) {
1262 pr_err("ERR: pwm=%d: dir: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001263 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001264 goto debug_error;
1265 }
1266
1267 puser = &dbgdev->user[i];
1268 puser->dbgdev = dbgdev;
1269 puser->pwm_id = i;
1270 temp = debugfs_create_file("period", S_IRUGO | S_IWUSR,
1271 dent, puser, &dbg_pwm_period_fops);
1272 if (temp == NULL || IS_ERR(temp)) {
1273 pr_err("ERR: pwm=%d: period: 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 temp = debugfs_create_file("duty-cycle", S_IRUGO | S_IWUSR,
1279 dent, puser, &dbg_pwm_duty_cycle_fops);
1280 if (temp == NULL || IS_ERR(temp)) {
1281 pr_err("ERR: pwm=%d: duty-cycle: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001282 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001283 goto debug_error;
1284 }
1285
1286 temp = debugfs_create_file("enable", S_IRUGO | S_IWUSR,
1287 dent, puser, &dbg_pwm_enable_fops);
1288 if (temp == NULL || IS_ERR(temp)) {
1289 pr_err("ERR: pwm=%d: enable: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001290 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001291 goto debug_error;
1292 }
1293 }
1294
1295 pmic_dbg_device = dbgdev;
1296
1297 return 0;
1298
1299debug_error:
1300 debugfs_remove_recursive(dbgdev->dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001301dir_error:
1302 kfree(dbgdev->user);
1303user_error:
1304 kfree(dbgdev);
1305 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001306}
1307
1308static int __devexit pm8xxx_pwm_dbg_remove(void)
1309{
1310 if (pmic_dbg_device) {
Jay Chokshi57656862011-09-07 12:02:22 -07001311 kfree(pmic_dbg_device->user);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001312 debugfs_remove_recursive(pmic_dbg_device->dent);
1313 kfree(pmic_dbg_device);
1314 }
1315 return 0;
1316}
1317
1318#else
1319
1320static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)
1321{
1322 return 0;
1323}
1324
1325static int __devexit pm8xxx_pwm_dbg_remove(void)
1326{
1327 return 0;
1328}
1329
1330#endif
1331
1332static int __devinit pm8xxx_pwm_probe(struct platform_device *pdev)
1333{
1334 struct pm8xxx_pwm_chip *chip;
1335 int i;
Jay Chokshi57656862011-09-07 12:02:22 -07001336 enum pm8xxx_version version;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001337
1338 chip = kzalloc(sizeof *chip, GFP_KERNEL);
1339 if (chip == NULL) {
1340 pr_err("kzalloc() failed.\n");
1341 return -ENOMEM;
1342 }
1343
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001344 mutex_init(&chip->pwm_mutex);
1345
1346 chip->dev = &pdev->dev;
1347 pwm_chip = chip;
Jay Chokshi57656862011-09-07 12:02:22 -07001348
1349 version = pm8xxx_get_version(chip->dev->parent);
1350
1351 if (version == PM8XXX_VERSION_8921 ||
David Collins999480d2011-11-16 08:52:30 -08001352 version == PM8XXX_VERSION_8058 ||
1353 version == PM8XXX_VERSION_8922) {
Jay Chokshi57656862011-09-07 12:02:22 -07001354 chip->is_lpg_supported = 1;
1355 }
1356 if (chip->is_lpg_supported) {
1357 chip->pwm_channels = PM8XXX_LPG_PWM_CHANNELS;
1358 chip->pwm_total_pre_divs = NUM_LPG_PRE_DIVIDE;
1359 } else {
1360 chip->pwm_channels = PM8XXX_PWM_CHANNELS;
1361 chip->pwm_total_pre_divs = NUM_PWM_PRE_DIVIDE;
1362 }
1363
1364 chip->pwm_dev = kcalloc(chip->pwm_channels, sizeof(struct pwm_device),
1365 GFP_KERNEL);
1366 if (chip->pwm_dev == NULL) {
1367 pr_err("kcalloc() failed.\n");
1368 mutex_destroy(&chip->pwm_mutex);
1369 kfree(chip);
1370 return -ENOMEM;
1371 }
1372
1373 for (i = 0; i < chip->pwm_channels; i++) {
1374 chip->pwm_dev[i].pwm_id = i;
1375 chip->pwm_dev[i].chip = chip;
1376 }
1377
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001378 platform_set_drvdata(pdev, chip);
1379
1380 if (pm8xxx_pwm_dbg_probe(&pdev->dev) < 0)
1381 pr_err("could not set up debugfs\n");
1382
1383 pr_notice("OK\n");
1384 return 0;
1385}
1386
1387static int __devexit pm8xxx_pwm_remove(struct platform_device *pdev)
1388{
1389 struct pm8xxx_pwm_chip *chip = dev_get_drvdata(pdev->dev.parent);
1390
1391 pm8xxx_pwm_dbg_remove();
Jay Chokshi57656862011-09-07 12:02:22 -07001392 kfree(chip->pwm_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001393 mutex_destroy(&chip->pwm_mutex);
1394 platform_set_drvdata(pdev, NULL);
1395 kfree(chip);
1396 return 0;
1397}
1398
1399static struct platform_driver pm8xxx_pwm_driver = {
1400 .probe = pm8xxx_pwm_probe,
1401 .remove = __devexit_p(pm8xxx_pwm_remove),
1402 .driver = {
1403 .name = PM8XXX_PWM_DEV_NAME,
1404 .owner = THIS_MODULE,
1405 },
1406};
1407
1408static int __init pm8xxx_pwm_init(void)
1409{
1410 return platform_driver_register(&pm8xxx_pwm_driver);
1411}
1412
1413static void __exit pm8xxx_pwm_exit(void)
1414{
1415 platform_driver_unregister(&pm8xxx_pwm_driver);
1416}
1417
1418subsys_initcall(pm8xxx_pwm_init);
1419module_exit(pm8xxx_pwm_exit);
1420
1421MODULE_LICENSE("GPL v2");
1422MODULE_DESCRIPTION("PM8XXX PWM driver");
1423MODULE_VERSION("1.0");
1424MODULE_ALIAS("platform:" PM8XXX_PWM_DEV_NAME);