blob: 05f02c4f74ced1de1b9191db94b94a3d5265f1f2 [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 Chokshi0c220cd2011-12-09 17:18:20 -080030/*
31 * For the lack of better term to distinguish functional
32 * differences, hereby, LPG version 0 (V0, v0) denotes
33 * PM8058/8921, and version 1 (V1, v1) denotes
34 * PM8922/8038.
35 */
36#define PM8XXX_LPG_V0_PWM_CHANNELS 8
37#define PM8XXX_LPG_V1_PWM_CHANNELS 6
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038#define PM8XXX_LPG_CTL_REGS 7
39
40/* PM8XXX PWM */
Jay Chokshi57656862011-09-07 12:02:22 -070041#define SSBI_REG_ADDR_PWM1_CTRL1 0x88
42#define SSBI_REG_ADDR_PWM1_CTRL2 0x89
43#define SSBI_REG_ADDR_PWM_CTL(id, base) (id == 0 ? base : (base + (id << 1)))
44#define SSBI_REG_ADDR_PWM_CTL1(id) SSBI_REG_ADDR_PWM_CTL(id, \
45 SSBI_REG_ADDR_PWM1_CTRL1)
46#define SSBI_REG_ADDR_PWM_CTL2(id) SSBI_REG_ADDR_PWM_CTL(id, \
47 SSBI_REG_ADDR_PWM1_CTRL2)
48
49#define PM8XXX_PWM_CLK_SEL_SHIFT 6
50#define PM8XXX_PWM_CLK_SEL_MASK 0xC0
51#define PM8XXX_PWM_PREDIVIDE_SHIFT 5
52#define PM8XXX_PWM_PREDIVIDE_MASK 0x20
53#define PM8XXX_PWM_M_SHIFT 2
54#define PM8XXX_PWM_M_MASK 0x1C
55#define PM8XXX_PWM_SIZE_SHIFT 1
56#define PM8XXX_PWM_SIZE_MASK 0x02
57#define PM8XXX_PWM_VALUE_BIT0 0x01
58#define PM8XXX_PWM_DISABLE 0x3F
59
60/* PM8XXX LPG PWM */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061#define SSBI_REG_ADDR_LPG_CTL_BASE 0x13C
62#define SSBI_REG_ADDR_LPG_CTL(n) (SSBI_REG_ADDR_LPG_CTL_BASE + (n))
63#define SSBI_REG_ADDR_LPG_BANK_SEL 0x143
64#define SSBI_REG_ADDR_LPG_BANK_EN 0x144
65#define SSBI_REG_ADDR_LPG_LUT_CFG0 0x145
66#define SSBI_REG_ADDR_LPG_LUT_CFG1 0x146
67
Jay Chokshi57656862011-09-07 12:02:22 -070068/* LPG Control 0 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069#define PM8XXX_PWM_1KHZ_COUNT_MASK 0xF0
70#define PM8XXX_PWM_1KHZ_COUNT_SHIFT 4
71
72#define PM8XXX_PWM_1KHZ_COUNT_MAX 15
73
74#define PM8XXX_PWM_OUTPUT_EN 0x08
75#define PM8XXX_PWM_PWM_EN 0x04
76#define PM8XXX_PWM_RAMP_GEN_EN 0x02
77#define PM8XXX_PWM_RAMP_START 0x01
78
79#define PM8XXX_PWM_PWM_START (PM8XXX_PWM_OUTPUT_EN \
80 | PM8XXX_PWM_PWM_EN)
81#define PM8XXX_PWM_RAMP_GEN_START (PM8XXX_PWM_RAMP_GEN_EN \
82 | PM8XXX_PWM_RAMP_START)
83
Jay Chokshi57656862011-09-07 12:02:22 -070084/* LPG Control 1 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070085#define PM8XXX_PWM_REVERSE_EN 0x80
86#define PM8XXX_PWM_BYPASS_LUT 0x40
87#define PM8XXX_PWM_HIGH_INDEX_MASK 0x3F
88
Jay Chokshi57656862011-09-07 12:02:22 -070089/* LPG Control 2 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070090#define PM8XXX_PWM_LOOP_EN 0x80
91#define PM8XXX_PWM_RAMP_UP 0x40
92#define PM8XXX_PWM_LOW_INDEX_MASK 0x3F
93
Jay Chokshi57656862011-09-07 12:02:22 -070094/* LPG Control 3 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095#define PM8XXX_PWM_VALUE_BIT7_0 0xFF
96#define PM8XXX_PWM_VALUE_BIT5_0 0x3F
97
Jay Chokshi57656862011-09-07 12:02:22 -070098/* LPG Control 4 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070099#define PM8XXX_PWM_VALUE_BIT8 0x80
100
Jay Chokshi57656862011-09-07 12:02:22 -0700101#define PM8XXX_LPG_PWM_CLK_SEL_MASK 0x60
102#define PM8XXX_LPG_PWM_CLK_SEL_SHIFT 5
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700103
104#define PM8XXX_PWM_CLK_SEL_NO 0
105#define PM8XXX_PWM_CLK_SEL_1KHZ 1
106#define PM8XXX_PWM_CLK_SEL_32KHZ 2
107#define PM8XXX_PWM_CLK_SEL_19P2MHZ 3
108
Jay Chokshi57656862011-09-07 12:02:22 -0700109#define PM8XXX_LPG_PWM_PREDIVIDE_MASK 0x18
110#define PM8XXX_LPG_PWM_PREDIVIDE_SHIFT 3
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111
112#define PM8XXX_PWM_PREDIVIDE_2 0
113#define PM8XXX_PWM_PREDIVIDE_3 1
114#define PM8XXX_PWM_PREDIVIDE_5 2
115#define PM8XXX_PWM_PREDIVIDE_6 3
116
Jay Chokshi57656862011-09-07 12:02:22 -0700117#define PM8XXX_LPG_PWM_M_MASK 0x07
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118#define PM8XXX_PWM_M_MIN 0
119#define PM8XXX_PWM_M_MAX 7
120
Jay Chokshi57656862011-09-07 12:02:22 -0700121/* LPG Control 5 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122#define PM8XXX_PWM_PAUSE_COUNT_HI_MASK 0xFC
123#define PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT 2
124
125#define PM8XXX_PWM_PAUSE_ENABLE_HIGH 0x02
126#define PM8XXX_PWM_SIZE_9_BIT 0x01
127
Jay Chokshi57656862011-09-07 12:02:22 -0700128/* LPG Control 6 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129#define PM8XXX_PWM_PAUSE_COUNT_LO_MASK 0xFC
130#define PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT 2
131
132#define PM8XXX_PWM_PAUSE_ENABLE_LOW 0x02
133#define PM8XXX_PWM_RESERVED 0x01
134
135#define PM8XXX_PWM_PAUSE_COUNT_MAX 56 /* < 2^6 = 64 */
136
Jay Chokshi57656862011-09-07 12:02:22 -0700137/* LPG LUT_CFG1 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700138#define PM8XXX_PWM_LUT_READ 0x40
139
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700140
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700141/*
142 * PWM Frequency = Clock Frequency / (N * T)
143 * or
144 * PWM Period = Clock Period * (N * T)
145 * where
146 * N = 2^9 or 2^6 for 9-bit or 6-bit PWM size
147 * T = Pre-divide * 2^m, where m = 0..7 (exponent)
148 *
149 * This is the formula to figure out m for the best pre-divide and clock:
Willie Ruan4a0a7002012-01-10 15:39:44 -0800150 * (PWM Period / N) = (Pre-divide * Clock Period) * 2^m
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700151 */
152#define NUM_CLOCKS 3
153
Willie Ruandb78e942012-01-10 15:00:28 -0800154#define NSEC_1024HZ (NSEC_PER_SEC / 1024)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700155#define NSEC_32768HZ (NSEC_PER_SEC / 32768)
156#define NSEC_19P2MHZ (NSEC_PER_SEC / 19200000)
157
Willie Ruanb10be972012-01-12 11:30:11 -0800158#define NUM_LPG_PRE_DIVIDE 4
Jay Chokshi57656862011-09-07 12:02:22 -0700159#define NUM_PWM_PRE_DIVIDE 2
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160
Jay Chokshi0c220cd2011-12-09 17:18:20 -0800161#define PRE_DIVIDE_1 1 /* v1 */
Willie Ruanb10be972012-01-12 11:30:11 -0800162#define PRE_DIVIDE_2 2
163#define PRE_DIVIDE_3 3
164#define PRE_DIVIDE_5 5
165#define PRE_DIVIDE_6 6
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700166
Jay Chokshi57656862011-09-07 12:02:22 -0700167static unsigned int pt_t[NUM_LPG_PRE_DIVIDE][NUM_CLOCKS] = {
Willie Ruandb78e942012-01-10 15:00:28 -0800168 { PRE_DIVIDE_2 * NSEC_1024HZ,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700169 PRE_DIVIDE_2 * NSEC_32768HZ,
170 PRE_DIVIDE_2 * NSEC_19P2MHZ,
171 },
Willie Ruanb10be972012-01-12 11:30:11 -0800172 { PRE_DIVIDE_3 * NSEC_1024HZ,
173 PRE_DIVIDE_3 * NSEC_32768HZ,
174 PRE_DIVIDE_3 * NSEC_19P2MHZ,
175 },
176 { PRE_DIVIDE_5 * NSEC_1024HZ,
177 PRE_DIVIDE_5 * NSEC_32768HZ,
178 PRE_DIVIDE_5 * NSEC_19P2MHZ,
179 },
180 { PRE_DIVIDE_6 * NSEC_1024HZ,
181 PRE_DIVIDE_6 * NSEC_32768HZ,
182 PRE_DIVIDE_6 * NSEC_19P2MHZ,
183 },
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184};
185
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186/* Private data */
187struct pm8xxx_pwm_chip;
188
189struct pwm_device {
190 int pwm_id; /* = bank/channel id */
191 int in_use;
192 const char *label;
Willie Ruan719c6762011-08-25 11:03:14 -0700193 struct pm8xxx_pwm_period period;
194 int pwm_value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700195 int pwm_period;
196 int pwm_duty;
Jay Chokshi57656862011-09-07 12:02:22 -0700197 u8 pwm_lpg_ctl[PM8XXX_LPG_CTL_REGS];
198 u8 pwm_ctl1;
199 u8 pwm_ctl2;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200 int irq;
201 struct pm8xxx_pwm_chip *chip;
Willie Ruan719c6762011-08-25 11:03:14 -0700202 int bypass_lut;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700203};
204
205struct pm8xxx_pwm_chip {
Jay Chokshi57656862011-09-07 12:02:22 -0700206 struct pwm_device *pwm_dev;
207 u8 pwm_channels;
208 u8 pwm_total_pre_divs;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209 u8 bank_mask;
210 struct mutex pwm_mutex;
211 struct device *dev;
Jay Chokshi57656862011-09-07 12:02:22 -0700212 bool is_lpg_supported;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213};
214
215static struct pm8xxx_pwm_chip *pwm_chip;
216
Willie Ruan719c6762011-08-25 11:03:14 -0700217struct pm8xxx_pwm_lut {
218 /* LUT parameters */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219 int lut_duty_ms;
220 int lut_lo_index;
221 int lut_hi_index;
222 int lut_pause_hi;
223 int lut_pause_lo;
224 int flags;
225};
226
227static const u16 duty_msec[PM8XXX_PWM_1KHZ_COUNT_MAX + 1] = {
228 0, 1, 2, 3, 4, 6, 8, 16, 18, 24, 32, 36, 64, 128, 256, 512
229};
230
231static const u16 pause_count[PM8XXX_PWM_PAUSE_COUNT_MAX + 1] = {
232 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
233 23, 28, 31, 42, 47, 56, 63, 83, 94, 111, 125, 167, 188, 222, 250, 333,
234 375, 500, 667, 750, 800, 900, 1000, 1100,
235 1200, 1300, 1400, 1500, 1600, 1800, 2000, 2500,
236 3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500,
237 7000
238};
239
240/* Internal functions */
Willie Ruan8de2f382011-08-25 11:04:50 -0700241static void pm8xxx_pwm_save(u8 *u8p, u8 mask, u8 val)
242{
243 *u8p &= ~mask;
244 *u8p |= val & mask;
245}
246
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700247static int pm8xxx_pwm_bank_enable(struct pwm_device *pwm, int enable)
248{
249 int rc;
250 u8 reg;
251 struct pm8xxx_pwm_chip *chip;
252
253 chip = pwm->chip;
254
255 if (enable)
256 reg = chip->bank_mask | (1 << pwm->pwm_id);
257 else
258 reg = chip->bank_mask & ~(1 << pwm->pwm_id);
259
260 rc = pm8xxx_writeb(chip->dev->parent, SSBI_REG_ADDR_LPG_BANK_EN, reg);
261 if (rc) {
Willie Ruan8de2f382011-08-25 11:04:50 -0700262 pr_err("pm8xxx_writeb(): rc=%d (Enable LPG Bank)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700263 return rc;
264 }
265 chip->bank_mask = reg;
266
267 return 0;
268}
269
270static int pm8xxx_pwm_bank_sel(struct pwm_device *pwm)
271{
272 int rc;
273
274 rc = pm8xxx_writeb(pwm->chip->dev->parent, SSBI_REG_ADDR_LPG_BANK_SEL,
275 pwm->pwm_id);
276 if (rc)
Willie Ruan8de2f382011-08-25 11:04:50 -0700277 pr_err("pm8xxx_writeb(): rc=%d (Select PWM Bank)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700278 return rc;
279}
280
281static int pm8xxx_pwm_start(struct pwm_device *pwm, int start, int ramp_start)
282{
283 int rc;
284 u8 reg;
285
286 if (start) {
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 if (ramp_start)
289 reg |= PM8XXX_PWM_RAMP_GEN_START;
290 else
291 reg &= ~PM8XXX_PWM_RAMP_GEN_START;
292 } else {
Jay Chokshi57656862011-09-07 12:02:22 -0700293 reg = pwm->pwm_lpg_ctl[0] & ~PM8XXX_PWM_PWM_START;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294 reg &= ~PM8XXX_PWM_RAMP_GEN_START;
295 }
296
297 rc = pm8xxx_writeb(pwm->chip->dev->parent, SSBI_REG_ADDR_LPG_CTL(0),
298 reg);
299 if (rc)
Willie Ruan8de2f382011-08-25 11:04:50 -0700300 pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl 0)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301 else
Jay Chokshi57656862011-09-07 12:02:22 -0700302 pwm->pwm_lpg_ctl[0] = reg;
303 return rc;
304}
305
306static int pm8xxx_pwm_disable(struct pwm_device *pwm)
307{
308 int rc;
309 u8 reg;
310
311 reg = pwm->pwm_ctl1 & PM8XXX_PWM_DISABLE;
312
313 rc = pm8xxx_writeb(pwm->chip->dev->parent,
314 SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), reg);
315
316 if (rc)
317 pr_err("pm8xxx_writeb(): rc=%d (Disable PWM Ctl %d)\n", rc,
318 pwm->pwm_id);
319 return rc;
320}
321
322static int pm8xxx_pwm_enable(struct pwm_device *pwm)
323{
324 /**
325 * A kind of best Effort: Just write the clock information that
326 * we have in the register.
327 */
328 int rc;
329
330 rc = pm8xxx_writeb(pwm->chip->dev->parent,
331 SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), pwm->pwm_ctl1);
332
333 if (rc)
334 pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl %d)\n", rc,
335 pwm->pwm_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700336 return rc;
337}
338
339static void pm8xxx_pwm_calc_period(unsigned int period_us,
Willie Ruan719c6762011-08-25 11:03:14 -0700340 struct pm8xxx_pwm_period *period)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700341{
342 int n, m, clk, div;
343 int best_m, best_div, best_clk;
Willie Ruan4a0a7002012-01-10 15:39:44 -0800344 unsigned int last_err, cur_err, min_err;
345 unsigned int tmp_p, period_n;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700346
347 /* PWM Period / N */
Willie Ruan70f989d2012-01-10 10:09:36 -0800348 if (period_us < ((unsigned)(-1) / NSEC_PER_USEC)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700349 period_n = (period_us * NSEC_PER_USEC) >> 6;
350 n = 6;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700351 } else {
352 period_n = (period_us >> 9) * NSEC_PER_USEC;
353 n = 9;
354 }
355
Willie Ruan4a0a7002012-01-10 15:39:44 -0800356 min_err = last_err = (unsigned)(-1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357 best_m = 0;
358 best_clk = 0;
359 best_div = 0;
360 for (clk = 0; clk < NUM_CLOCKS; clk++) {
Jay Chokshi57656862011-09-07 12:02:22 -0700361 for (div = 0; div < pwm_chip->pwm_total_pre_divs; div++) {
Willie Ruan4a0a7002012-01-10 15:39:44 -0800362 /* period_n = (PWM Period / N) */
363 /* tmp_p = (Pre-divide * Clock Period) * 2^m */
364 tmp_p = pt_t[div][clk];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700365 for (m = 0; m <= PM8XXX_PWM_M_MAX; m++) {
Willie Ruan4a0a7002012-01-10 15:39:44 -0800366 if (period_n > tmp_p)
367 cur_err = period_n - tmp_p;
368 else
369 cur_err = tmp_p - period_n;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700370
Willie Ruan4a0a7002012-01-10 15:39:44 -0800371 if (cur_err < min_err) {
372 min_err = cur_err;
373 best_m = m;
374 best_clk = clk;
375 best_div = div;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700376 }
Willie Ruan4a0a7002012-01-10 15:39:44 -0800377
378 if (m && cur_err > last_err)
379 /* Break for bigger cur_err */
380 break;
381
382 last_err = cur_err;
383 tmp_p <<= 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700384 }
385 }
386 }
387
Willie Ruan75d9e5b2011-08-25 10:59:17 -0700388 /* Use higher resolution */
389 if (best_m >= 3 && n == 6) {
390 n += 3;
391 best_m -= 3;
392 }
393
Willie Ruan719c6762011-08-25 11:03:14 -0700394 period->pwm_size = n;
395 period->clk = best_clk;
396 period->pre_div = best_div;
397 period->pre_div_exp = best_m;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700398}
399
Willie Ruan8de2f382011-08-25 11:04:50 -0700400static void pm8xxx_pwm_calc_pwm_value(struct pwm_device *pwm,
401 unsigned int period_us,
402 unsigned int duty_us)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700403{
Willie Ruan8de2f382011-08-25 11:04:50 -0700404 unsigned int max_pwm_value, tmp;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700405
Willie Ruan8de2f382011-08-25 11:04:50 -0700406 /* Figure out pwm_value with overflow handling */
407 tmp = 1 << (sizeof(tmp) * 8 - pwm->period.pwm_size);
408 if (duty_us < tmp) {
409 tmp = duty_us << pwm->period.pwm_size;
410 pwm->pwm_value = tmp / period_us;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700411 } else {
Willie Ruan8de2f382011-08-25 11:04:50 -0700412 tmp = period_us >> pwm->period.pwm_size;
413 pwm->pwm_value = duty_us / tmp;
414 }
415 max_pwm_value = (1 << pwm->period.pwm_size) - 1;
416 if (pwm->pwm_value > max_pwm_value)
417 pwm->pwm_value = max_pwm_value;
418}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700419
Willie Ruan8de2f382011-08-25 11:04:50 -0700420static int pm8xxx_pwm_change_table(struct pwm_device *pwm, int duty_pct[],
421 int start_idx, int len, int raw_value)
422{
423 unsigned int pwm_value, max_pwm_value;
424 u8 cfg0, cfg1;
425 int i, pwm_size;
426 int rc = 0;
427
Jay Chokshi57656862011-09-07 12:02:22 -0700428 pwm_size = (pwm->pwm_lpg_ctl[5] & PM8XXX_PWM_SIZE_9_BIT) ? 9 : 6;
Willie Ruan8de2f382011-08-25 11:04:50 -0700429 max_pwm_value = (1 << pwm_size) - 1;
430 for (i = 0; i < len; i++) {
431 if (raw_value)
432 pwm_value = duty_pct[i];
433 else
434 pwm_value = (duty_pct[i] << pwm_size) / 100;
435
436 if (pwm_value > max_pwm_value)
437 pwm_value = max_pwm_value;
438 cfg0 = pwm_value;
439 cfg1 = (pwm_value >> 1) & 0x80;
440 cfg1 |= start_idx + i;
441
442 rc = pm8xxx_writeb(pwm->chip->dev->parent,
443 SSBI_REG_ADDR_LPG_LUT_CFG0, cfg0);
444 if (rc)
445 break;
446
447 rc = pm8xxx_writeb(pwm->chip->dev->parent,
448 SSBI_REG_ADDR_LPG_LUT_CFG1, cfg1);
449 if (rc)
450 break;
451 }
452 return rc;
453}
454
455static void pm8xxx_pwm_save_index(struct pwm_device *pwm,
456 int low_idx, int high_idx, int flags)
457{
Jay Chokshi57656862011-09-07 12:02:22 -0700458 pwm->pwm_lpg_ctl[1] = high_idx & PM8XXX_PWM_HIGH_INDEX_MASK;
459 pwm->pwm_lpg_ctl[2] = low_idx & PM8XXX_PWM_LOW_INDEX_MASK;
Willie Ruan8de2f382011-08-25 11:04:50 -0700460
461 if (flags & PM_PWM_LUT_REVERSE)
Jay Chokshi57656862011-09-07 12:02:22 -0700462 pwm->pwm_lpg_ctl[1] |= PM8XXX_PWM_REVERSE_EN;
Willie Ruan8de2f382011-08-25 11:04:50 -0700463 if (flags & PM_PWM_LUT_RAMP_UP)
Jay Chokshi57656862011-09-07 12:02:22 -0700464 pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_RAMP_UP;
Willie Ruan8de2f382011-08-25 11:04:50 -0700465 if (flags & PM_PWM_LUT_LOOP)
Jay Chokshi57656862011-09-07 12:02:22 -0700466 pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_LOOP_EN;
Willie Ruan8de2f382011-08-25 11:04:50 -0700467}
468
469static void pm8xxx_pwm_save_period(struct pwm_device *pwm)
470{
471 u8 mask, val;
472
Jay Chokshi57656862011-09-07 12:02:22 -0700473 if (pwm_chip->is_lpg_supported) {
474 val = ((pwm->period.clk + 1) << PM8XXX_LPG_PWM_CLK_SEL_SHIFT)
475 & PM8XXX_LPG_PWM_CLK_SEL_MASK;
476 val |= (pwm->period.pre_div << PM8XXX_LPG_PWM_PREDIVIDE_SHIFT)
477 & PM8XXX_LPG_PWM_PREDIVIDE_MASK;
478 val |= pwm->period.pre_div_exp & PM8XXX_LPG_PWM_M_MASK;
479 mask = PM8XXX_LPG_PWM_CLK_SEL_MASK |
480 PM8XXX_LPG_PWM_PREDIVIDE_MASK | PM8XXX_LPG_PWM_M_MASK;
481 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
Willie Ruan8de2f382011-08-25 11:04:50 -0700482
Jay Chokshi57656862011-09-07 12:02:22 -0700483 val = (pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0;
484 mask = PM8XXX_PWM_SIZE_9_BIT;
485 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
486 } else {
487 val = ((pwm->period.clk + 1) << PM8XXX_PWM_CLK_SEL_SHIFT)
488 & PM8XXX_PWM_CLK_SEL_MASK;
489 val |= (pwm->period.pre_div << PM8XXX_PWM_PREDIVIDE_SHIFT)
490 & PM8XXX_PWM_PREDIVIDE_MASK;
491 val |= (pwm->period.pre_div_exp << PM8XXX_PWM_M_SHIFT)
492 & PM8XXX_PWM_M_MASK;
493 val |= (((pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0)
494 << PM8XXX_PWM_SIZE_SHIFT) & PM8XXX_PWM_SIZE_MASK;
495
496 mask = PM8XXX_PWM_CLK_SEL_MASK | PM8XXX_PWM_PREDIVIDE_MASK |
497 PM8XXX_PWM_M_MASK | PM8XXX_PWM_SIZE_MASK;
498 pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
499 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700500}
501
502static void pm8xxx_pwm_save_pwm_value(struct pwm_device *pwm)
503{
504 u8 mask, val;
505
Jay Chokshi57656862011-09-07 12:02:22 -0700506 if (pwm_chip->is_lpg_supported) {
507 val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 1) : 0;
508 pwm->pwm_lpg_ctl[3] = pwm->pwm_value;
509 mask = PM8XXX_PWM_VALUE_BIT8;
510 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
511 } else {
512 val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 8) : 0;
513 pwm->pwm_ctl2 = pwm->pwm_value;
514 mask = PM8XXX_PWM_VALUE_BIT0;
515 pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
516 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700517}
518
519static void pm8xxx_pwm_save_duty_time(struct pwm_device *pwm,
520 struct pm8xxx_pwm_lut *lut)
521{
522 int i;
523 u8 mask, val;
524
525 /* Linear search for duty time */
526 for (i = 0; i < PM8XXX_PWM_1KHZ_COUNT_MAX; i++) {
527 if (duty_msec[i] >= lut->lut_duty_ms)
528 break;
529 }
530 val = i << PM8XXX_PWM_1KHZ_COUNT_SHIFT;
531
532 mask = PM8XXX_PWM_1KHZ_COUNT_MASK;
Jay Chokshi57656862011-09-07 12:02:22 -0700533 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[0], mask, val);
Willie Ruan8de2f382011-08-25 11:04:50 -0700534}
535
536static void pm8xxx_pwm_save_pause(struct pwm_device *pwm,
537 struct pm8xxx_pwm_lut *lut)
538{
539 int i, pause_cnt, time_cnt;
540 u8 mask, val;
541
Jay Chokshi57656862011-09-07 12:02:22 -0700542 time_cnt = (pwm->pwm_lpg_ctl[0] & PM8XXX_PWM_1KHZ_COUNT_MASK)
Willie Ruan8de2f382011-08-25 11:04:50 -0700543 >> PM8XXX_PWM_1KHZ_COUNT_SHIFT;
544 if (lut->flags & PM_PWM_LUT_PAUSE_HI_EN) {
545 pause_cnt = (lut->lut_pause_hi + duty_msec[time_cnt] / 2)
546 / duty_msec[time_cnt];
547 /* Linear search for pause time */
548 for (i = 0; i < PM8XXX_PWM_PAUSE_COUNT_MAX; i++) {
549 if (pause_count[i] >= pause_cnt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700550 break;
551 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700552 val = (i << PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT) &
553 PM8XXX_PWM_PAUSE_COUNT_HI_MASK;
554 val |= PM8XXX_PWM_PAUSE_ENABLE_HIGH;
555 } else {
556 val = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700557 }
558
Willie Ruan8de2f382011-08-25 11:04:50 -0700559 mask = PM8XXX_PWM_PAUSE_COUNT_HI_MASK | PM8XXX_PWM_PAUSE_ENABLE_HIGH;
Jay Chokshi57656862011-09-07 12:02:22 -0700560 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700561
Willie Ruan8de2f382011-08-25 11:04:50 -0700562 if (lut->flags & PM_PWM_LUT_PAUSE_LO_EN) {
563 /* Linear search for pause time */
564 pause_cnt = (lut->lut_pause_lo + duty_msec[time_cnt] / 2)
565 / duty_msec[time_cnt];
566 for (i = 0; i < PM8XXX_PWM_PAUSE_COUNT_MAX; i++) {
567 if (pause_count[i] >= pause_cnt)
568 break;
569 }
570 val = (i << PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT) &
571 PM8XXX_PWM_PAUSE_COUNT_LO_MASK;
572 val |= PM8XXX_PWM_PAUSE_ENABLE_LOW;
573 } else {
574 val = 0;
575 }
576
577 mask = PM8XXX_PWM_PAUSE_COUNT_LO_MASK | PM8XXX_PWM_PAUSE_ENABLE_LOW;
Jay Chokshi57656862011-09-07 12:02:22 -0700578 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[6], mask, val);
Willie Ruan8de2f382011-08-25 11:04:50 -0700579}
580
Jay Chokshi57656862011-09-07 12:02:22 -0700581static int pm8xxx_pwm_write(struct pwm_device *pwm)
582{
583 int rc = 0;
584
585 rc = pm8xxx_writeb(pwm->chip->dev->parent,
586 SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id),
587 pwm->pwm_ctl1);
588 if (rc) {
589 pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl1[%d])\n",
590 rc, pwm->pwm_id);
591 return rc;
592 }
593
594 rc = pm8xxx_writeb(pwm->chip->dev->parent,
595 SSBI_REG_ADDR_PWM_CTL2(pwm->pwm_id),
596 pwm->pwm_ctl2);
597 if (rc) {
598 pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl2[%d])\n",
599 rc, pwm->pwm_id);
600 return rc;
601 }
602
603 return rc;
604}
605
606static int pm8xxx_lpg_pwm_write(struct pwm_device *pwm, int start, int end)
Willie Ruan8de2f382011-08-25 11:04:50 -0700607{
608 int i, rc;
609
610 /* Write in reverse way so 0 would be the last */
611 for (i = end - 1; i >= start; i--) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700612 rc = pm8xxx_writeb(pwm->chip->dev->parent,
613 SSBI_REG_ADDR_LPG_CTL(i),
Jay Chokshi57656862011-09-07 12:02:22 -0700614 pwm->pwm_lpg_ctl[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700615 if (rc) {
Willie Ruan8de2f382011-08-25 11:04:50 -0700616 pr_err("pm8xxx_writeb(): rc=%d (PWM Ctl[%d])\n", rc, i);
617 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700618 }
619 }
620
Willie Ruan8de2f382011-08-25 11:04:50 -0700621 return 0;
622}
623
624static int pm8xxx_pwm_change_lut(struct pwm_device *pwm,
625 struct pm8xxx_pwm_lut *lut)
626{
627 int rc;
628
629 pm8xxx_pwm_save_index(pwm, lut->lut_lo_index,
630 lut->lut_hi_index, lut->flags);
631 pm8xxx_pwm_save_duty_time(pwm, lut);
632 pm8xxx_pwm_save_pause(pwm, lut);
Jay Chokshi57656862011-09-07 12:02:22 -0700633 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1], PM8XXX_PWM_BYPASS_LUT, 0);
Willie Ruan8de2f382011-08-25 11:04:50 -0700634
635 pm8xxx_pwm_bank_sel(pwm);
Jay Chokshi57656862011-09-07 12:02:22 -0700636 rc = pm8xxx_lpg_pwm_write(pwm, 0, 7);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700637
638 return rc;
639}
640
641/* APIs */
642/**
643 * pwm_request - request a PWM device
644 * @pwm_id: PWM id or channel
645 * @label: the label to identify the user
646 */
647struct pwm_device *pwm_request(int pwm_id, const char *label)
648{
649 struct pwm_device *pwm;
650
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700651 if (pwm_chip == NULL) {
652 pr_err("No pwm_chip\n");
653 return ERR_PTR(-ENODEV);
654 }
655
Jay Chokshi57656862011-09-07 12:02:22 -0700656 if (pwm_id >= pwm_chip->pwm_channels || pwm_id < 0) {
657 pr_err("Invalid pwm_id: %d with %s\n",
658 pwm_id, label ? label : ".");
659 return ERR_PTR(-EINVAL);
660 }
661
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700662 mutex_lock(&pwm_chip->pwm_mutex);
663 pwm = &pwm_chip->pwm_dev[pwm_id];
664 if (!pwm->in_use) {
665 pwm->in_use = 1;
666 pwm->label = label;
667 } else {
668 pwm = ERR_PTR(-EBUSY);
669 }
670 mutex_unlock(&pwm_chip->pwm_mutex);
671
672 return pwm;
673}
674EXPORT_SYMBOL_GPL(pwm_request);
675
676/**
677 * pwm_free - free a PWM device
678 * @pwm: the PWM device
679 */
680void pwm_free(struct pwm_device *pwm)
681{
682 if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
683 pr_err("Invalid pwm handle\n");
684 return;
685 }
686
687 mutex_lock(&pwm->chip->pwm_mutex);
688 if (pwm->in_use) {
Jay Chokshi57656862011-09-07 12:02:22 -0700689 if (pwm_chip->is_lpg_supported) {
690 pm8xxx_pwm_bank_sel(pwm);
691 pm8xxx_pwm_start(pwm, 0, 0);
692 } else {
693 pm8xxx_pwm_disable(pwm);
694 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700695 pwm->in_use = 0;
696 pwm->label = NULL;
697 }
Jay Chokshi57656862011-09-07 12:02:22 -0700698 if (pwm_chip->is_lpg_supported)
699 pm8xxx_pwm_bank_enable(pwm, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700700 mutex_unlock(&pwm->chip->pwm_mutex);
701}
702EXPORT_SYMBOL_GPL(pwm_free);
703
704/**
705 * pwm_config - change a PWM device configuration
706 * @pwm: the PWM device
707 * @period_us: period in microseconds
708 * @duty_us: duty cycle in microseconds
709 */
710int pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
711{
Willie Ruan719c6762011-08-25 11:03:14 -0700712 struct pm8xxx_pwm_period *period;
Jay Chokshi57656862011-09-07 12:02:22 -0700713 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700714
715 if (pwm == NULL || IS_ERR(pwm) ||
716 duty_us > period_us ||
717 (unsigned)period_us > PM8XXX_PWM_PERIOD_MAX ||
718 (unsigned)period_us < PM8XXX_PWM_PERIOD_MIN) {
719 pr_err("Invalid pwm handle or parameters\n");
720 return -EINVAL;
721 }
722 if (pwm->chip == NULL) {
723 pr_err("No pwm_chip\n");
724 return -ENODEV;
725 }
726
Willie Ruan719c6762011-08-25 11:03:14 -0700727 period = &pwm->period;
728
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700729 mutex_lock(&pwm->chip->pwm_mutex);
730
731 if (!pwm->in_use) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700732 rc = -EINVAL;
733 goto out_unlock;
734 }
735
Willie Ruan8de2f382011-08-25 11:04:50 -0700736 if (pwm->pwm_period != period_us) {
737 pm8xxx_pwm_calc_period(period_us, period);
738 pm8xxx_pwm_save_period(pwm);
739 pwm->pwm_period = period_us;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700740 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700741
Willie Ruan8de2f382011-08-25 11:04:50 -0700742 pm8xxx_pwm_calc_pwm_value(pwm, period_us, duty_us);
743 pm8xxx_pwm_save_pwm_value(pwm);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700744
Jay Chokshi57656862011-09-07 12:02:22 -0700745 if (pwm_chip->is_lpg_supported) {
746 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
747 PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
748
749 pm8xxx_pwm_bank_sel(pwm);
750 rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
751 } else {
752 rc = pm8xxx_pwm_write(pwm);
753 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700754
755 pr_debug("duty/period=%u/%u usec: pwm_value=%d (of %d)\n",
756 (unsigned)duty_us, (unsigned)period_us,
757 pwm->pwm_value, 1 << period->pwm_size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700758
759out_unlock:
760 mutex_unlock(&pwm->chip->pwm_mutex);
761 return rc;
762}
763EXPORT_SYMBOL_GPL(pwm_config);
764
765/**
766 * pwm_enable - start a PWM output toggling
767 * @pwm: the PWM device
768 */
769int pwm_enable(struct pwm_device *pwm)
770{
Jay Chokshi57656862011-09-07 12:02:22 -0700771 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700772
773 if (pwm == NULL || IS_ERR(pwm)) {
774 pr_err("Invalid pwm handle\n");
775 return -EINVAL;
776 }
777 if (pwm->chip == NULL) {
778 pr_err("No pwm_chip\n");
779 return -ENODEV;
780 }
781
782 mutex_lock(&pwm->chip->pwm_mutex);
783 if (!pwm->in_use) {
784 pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
785 rc = -EINVAL;
786 } else {
Jay Chokshi57656862011-09-07 12:02:22 -0700787 if (pwm_chip->is_lpg_supported) {
788 rc = pm8xxx_pwm_bank_enable(pwm, 1);
789 pm8xxx_pwm_bank_sel(pwm);
790 pm8xxx_pwm_start(pwm, 1, 0);
791 } else {
792 pm8xxx_pwm_enable(pwm);
793 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700794 }
795 mutex_unlock(&pwm->chip->pwm_mutex);
796 return rc;
797}
798EXPORT_SYMBOL_GPL(pwm_enable);
799
800/**
801 * pwm_disable - stop a PWM output toggling
802 * @pwm: the PWM device
803 */
804void pwm_disable(struct pwm_device *pwm)
805{
806 if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
807 pr_err("Invalid pwm handle or no pwm_chip\n");
808 return;
809 }
810
811 mutex_lock(&pwm->chip->pwm_mutex);
812 if (pwm->in_use) {
Jay Chokshi57656862011-09-07 12:02:22 -0700813 if (pwm_chip->is_lpg_supported) {
814 pm8xxx_pwm_bank_sel(pwm);
815 pm8xxx_pwm_start(pwm, 0, 0);
816 pm8xxx_pwm_bank_enable(pwm, 0);
817 } else {
818 pm8xxx_pwm_disable(pwm);
819 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700820 }
821 mutex_unlock(&pwm->chip->pwm_mutex);
822}
823EXPORT_SYMBOL_GPL(pwm_disable);
824
825/**
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700826 * pm8xxx_pwm_config_period - change PWM period
827 *
828 * @pwm: the PWM device
829 * @pwm_p: period in struct pm8xxx_pwm_period
830 */
831int pm8xxx_pwm_config_period(struct pwm_device *pwm,
832 struct pm8xxx_pwm_period *period)
833{
834 int rc;
835
836 if (pwm == NULL || IS_ERR(pwm) || period == NULL)
837 return -EINVAL;
838 if (pwm->chip == NULL)
839 return -ENODEV;
840
841 mutex_lock(&pwm->chip->pwm_mutex);
842
843 if (!pwm->in_use) {
844 rc = -EINVAL;
845 goto out_unlock;
846 }
847
848 pwm->period.pwm_size = period->pwm_size;
849 pwm->period.clk = period->clk;
850 pwm->period.pre_div = period->pre_div;
851 pwm->period.pre_div_exp = period->pre_div_exp;
852
853 pm8xxx_pwm_save_period(pwm);
Jay Chokshi57656862011-09-07 12:02:22 -0700854
855 if (pwm_chip->is_lpg_supported) {
856 pm8xxx_pwm_bank_sel(pwm);
857 rc = pm8xxx_lpg_pwm_write(pwm, 4, 6);
858 } else {
859 rc = pm8xxx_pwm_write(pwm);
860 }
861
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700862
863out_unlock:
864 mutex_unlock(&pwm->chip->pwm_mutex);
865 return rc;
866}
867EXPORT_SYMBOL(pm8xxx_pwm_config_period);
868
869/**
870 * pm8xxx_pwm_config_pwm_value - change a PWM device configuration
871 * @pwm: the PWM device
872 * @pwm_value: the duty cycle in raw PWM value (< 2^pwm_size)
873 */
874int pm8xxx_pwm_config_pwm_value(struct pwm_device *pwm, int pwm_value)
875{
876 int rc = 0;
877
878 if (pwm == NULL || IS_ERR(pwm))
879 return -EINVAL;
880 if (pwm->chip == NULL)
881 return -ENODEV;
882
883 mutex_lock(&pwm->chip->pwm_mutex);
884
885 if (!pwm->in_use || !pwm->pwm_period) {
886 rc = -EINVAL;
887 goto out_unlock;
888 }
889
890 if (pwm->pwm_value == pwm_value)
891 goto out_unlock;
892
893 pwm->pwm_value = pwm_value;
894
895 pm8xxx_pwm_save_pwm_value(pwm);
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700896
Jay Chokshi57656862011-09-07 12:02:22 -0700897 if (pwm_chip->is_lpg_supported) {
898 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
899 PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
900 pm8xxx_pwm_bank_sel(pwm);
901 rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
902 } else {
903 rc = pm8xxx_pwm_write(pwm);
904 }
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700905
906 if (rc)
907 pr_err("[%d]: pm8xxx_pwm_write: rc=%d\n", pwm->pwm_id, rc);
908
909out_unlock:
910 mutex_unlock(&pwm->chip->pwm_mutex);
911 return rc;
912}
913EXPORT_SYMBOL_GPL(pm8xxx_pwm_config_pwm_value);
914
915/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700916 * pm8xxx_pwm_lut_config - change a PWM device configuration to use LUT
917 * @pwm: the PWM device
918 * @period_us: period in microseconds
919 * @duty_pct: arrary of duty cycles in percent, like 20, 50.
920 * @duty_time_ms: time for each duty cycle in milliseconds
921 * @start_idx: start index in lookup table from 0 to MAX-1
922 * @idx_len: number of index
923 * @pause_lo: pause time in milliseconds at low index
924 * @pause_hi: pause time in milliseconds at high index
925 * @flags: control flags
926 */
927int pm8xxx_pwm_lut_config(struct pwm_device *pwm, int period_us,
928 int duty_pct[], int duty_time_ms, int start_idx,
929 int idx_len, int pause_lo, int pause_hi, int flags)
930{
Willie Ruan719c6762011-08-25 11:03:14 -0700931 struct pm8xxx_pwm_lut lut;
932 struct pm8xxx_pwm_period *period;
Willie Ruan8de2f382011-08-25 11:04:50 -0700933 int len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700934 int rc;
935
936 if (pwm == NULL || IS_ERR(pwm) || !idx_len) {
937 pr_err("Invalid pwm handle or idx_len=0\n");
938 return -EINVAL;
939 }
940 if (duty_pct == NULL && !(flags & PM_PWM_LUT_NO_TABLE)) {
941 pr_err("Invalid duty_pct with flag\n");
942 return -EINVAL;
943 }
944 if (pwm->chip == NULL) {
945 pr_err("No pwm_chip\n");
946 return -ENODEV;
947 }
Jay Chokshi57656862011-09-07 12:02:22 -0700948
949 if (pwm->chip->is_lpg_supported == 0) {
950 pr_err("LPG module isn't supported\n");
951 return -EINVAL;
952 }
953
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700954 if (idx_len >= PM_PWM_LUT_SIZE && start_idx) {
955 pr_err("Wrong LUT size or index\n");
956 return -EINVAL;
957 }
958 if ((start_idx + idx_len) > PM_PWM_LUT_SIZE) {
959 pr_err("Exceed LUT limit\n");
960 return -EINVAL;
961 }
962 if ((unsigned)period_us > PM8XXX_PWM_PERIOD_MAX ||
963 (unsigned)period_us < PM8XXX_PWM_PERIOD_MIN) {
964 pr_err("Period out of range\n");
965 return -EINVAL;
966 }
967
Willie Ruan719c6762011-08-25 11:03:14 -0700968 period = &pwm->period;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700969 mutex_lock(&pwm->chip->pwm_mutex);
970
971 if (!pwm->in_use) {
972 pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
973 rc = -EINVAL;
974 goto out_unlock;
975 }
976
Willie Ruan8de2f382011-08-25 11:04:50 -0700977 if (pwm->pwm_period != period_us) {
978 pm8xxx_pwm_calc_period(period_us, period);
979 pm8xxx_pwm_save_period(pwm);
980 pwm->pwm_period = period_us;
981 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700982
983 len = (idx_len > PM_PWM_LUT_SIZE) ? PM_PWM_LUT_SIZE : idx_len;
984
985 if (flags & PM_PWM_LUT_NO_TABLE)
986 goto after_table_write;
987
Willie Ruan8de2f382011-08-25 11:04:50 -0700988 rc = pm8xxx_pwm_change_table(pwm, duty_pct, start_idx, len, 0);
989 if (rc) {
990 pr_err("pm8xxx_pwm_change_table: rc=%d\n", rc);
991 goto out_unlock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700992 }
993
994after_table_write:
Willie Ruan719c6762011-08-25 11:03:14 -0700995 lut.lut_duty_ms = duty_time_ms;
996 lut.lut_lo_index = start_idx;
997 lut.lut_hi_index = start_idx + len - 1;
998 lut.lut_pause_lo = pause_lo;
999 lut.lut_pause_hi = pause_hi;
1000 lut.flags = flags;
1001 pwm->bypass_lut = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001002
Willie Ruan8de2f382011-08-25 11:04:50 -07001003 rc = pm8xxx_pwm_change_lut(pwm, &lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001004
1005out_unlock:
1006 mutex_unlock(&pwm->chip->pwm_mutex);
1007 return rc;
1008}
1009EXPORT_SYMBOL_GPL(pm8xxx_pwm_lut_config);
1010
1011/**
1012 * pm8xxx_pwm_lut_enable - control a PWM device to start/stop LUT ramp
1013 * @pwm: the PWM device
1014 * @start: to start (1), or stop (0)
1015 */
1016int pm8xxx_pwm_lut_enable(struct pwm_device *pwm, int start)
1017{
1018 if (pwm == NULL || IS_ERR(pwm)) {
1019 pr_err("Invalid pwm handle\n");
1020 return -EINVAL;
1021 }
1022 if (pwm->chip == NULL) {
1023 pr_err("No pwm_chip\n");
1024 return -ENODEV;
1025 }
Jay Chokshi57656862011-09-07 12:02:22 -07001026 if (pwm->chip->is_lpg_supported == 0) {
1027 pr_err("LPG module isn't supported\n");
1028 return -EINVAL;
1029 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001030
1031 mutex_lock(&pwm->chip->pwm_mutex);
1032 if (start) {
1033 pm8xxx_pwm_bank_enable(pwm, 1);
1034
1035 pm8xxx_pwm_bank_sel(pwm);
1036 pm8xxx_pwm_start(pwm, 1, 1);
1037 } else {
1038 pm8xxx_pwm_bank_sel(pwm);
1039 pm8xxx_pwm_start(pwm, 0, 0);
1040
1041 pm8xxx_pwm_bank_enable(pwm, 0);
1042 }
1043 mutex_unlock(&pwm->chip->pwm_mutex);
1044 return 0;
1045}
1046EXPORT_SYMBOL_GPL(pm8xxx_pwm_lut_enable);
1047
1048#if defined(CONFIG_DEBUG_FS)
1049
1050struct pm8xxx_pwm_dbg_device;
1051
1052struct pm8xxx_pwm_user {
1053 int pwm_id;
1054 struct pwm_device *pwm;
1055 int period;
1056 int duty_cycle;
1057 int enable;
1058 struct pm8xxx_pwm_dbg_device *dbgdev;
1059};
1060
1061struct pm8xxx_pwm_dbg_device {
1062 struct mutex dbg_mutex;
1063 struct device *dev;
1064 struct dentry *dent;
1065
Jay Chokshi57656862011-09-07 12:02:22 -07001066 struct pm8xxx_pwm_user *user;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001067};
1068
1069static struct pm8xxx_pwm_dbg_device *pmic_dbg_device;
1070
1071static int dbg_pwm_check_period(int period)
1072{
1073 if (period < PM8XXX_PWM_PERIOD_MIN || period > PM8XXX_PWM_PERIOD_MAX) {
1074 pr_err("period is invalid: %d\n", period);
1075 return -EINVAL;
1076 }
1077 return 0;
1078}
1079
1080static int dbg_pwm_check_duty_cycle(int duty_cycle, const char *func_name)
1081{
1082 if (duty_cycle <= 0 || duty_cycle > 100) {
1083 pr_err("%s: duty_cycle is invalid: %d\n",
1084 func_name, duty_cycle);
1085 return -EINVAL;
1086 }
1087 return 0;
1088}
1089
1090static void dbg_pwm_check_handle(struct pm8xxx_pwm_user *puser)
1091{
1092 struct pwm_device *tmp;
1093
1094 if (puser->pwm == NULL) {
1095 tmp = pwm_request(puser->pwm_id, "pwm-dbg");
1096 if (PTR_ERR(puser->pwm)) {
1097 pr_err("pwm_request: err=%ld\n", PTR_ERR(puser->pwm));
1098 puser->pwm = NULL;
Willie Ruan10976ea2011-08-25 11:01:15 -07001099 } else {
1100 pr_debug("[id=%d] pwm_request ok\n", puser->pwm_id);
1101 puser->pwm = tmp;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001102 }
1103 }
1104}
1105
1106static int dbg_pwm_enable_set(void *data, u64 val)
1107{
1108 struct pm8xxx_pwm_user *puser = data;
1109 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1110 int rc;
1111
1112 mutex_lock(&dbgdev->dbg_mutex);
1113 rc = dbg_pwm_check_duty_cycle(puser->duty_cycle, __func__);
1114 if (!rc) {
1115 puser->enable = val;
1116 dbg_pwm_check_handle(puser);
1117 if (puser->pwm) {
1118 if (puser->enable)
1119 pwm_enable(puser->pwm);
1120 else
1121 pwm_disable(puser->pwm);
1122 }
1123 }
1124 mutex_unlock(&dbgdev->dbg_mutex);
1125 return 0;
1126}
1127
1128static int dbg_pwm_enable_get(void *data, u64 *val)
1129{
1130 struct pm8xxx_pwm_user *puser = data;
1131 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1132
1133 mutex_lock(&dbgdev->dbg_mutex);
1134 *val = puser->enable;
1135 mutex_unlock(&dbgdev->dbg_mutex);
1136 return 0;
1137}
1138
1139DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_enable_fops,
1140 dbg_pwm_enable_get, dbg_pwm_enable_set,
1141 "%lld\n");
1142
1143static int dbg_pwm_duty_cycle_set(void *data, u64 val)
1144{
1145 struct pm8xxx_pwm_user *puser = data;
1146 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1147 int rc;
1148
1149 mutex_lock(&dbgdev->dbg_mutex);
1150 rc = dbg_pwm_check_duty_cycle(val, __func__);
1151 if (!rc) {
1152 puser->duty_cycle = val;
1153 dbg_pwm_check_handle(puser);
1154 if (puser->pwm) {
1155 int duty_us;
1156
Willie Ruan10976ea2011-08-25 11:01:15 -07001157 duty_us = puser->duty_cycle * puser->period / 100;
1158 pwm_config(puser->pwm, duty_us, puser->period);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001159 }
1160 }
1161 mutex_unlock(&dbgdev->dbg_mutex);
1162 return 0;
1163}
1164
1165static int dbg_pwm_duty_cycle_get(void *data, u64 *val)
1166{
1167 struct pm8xxx_pwm_user *puser = data;
1168 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1169
1170 mutex_lock(&dbgdev->dbg_mutex);
1171 *val = puser->duty_cycle;
1172 mutex_unlock(&dbgdev->dbg_mutex);
1173 return 0;
1174}
1175
1176DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_duty_cycle_fops,
1177 dbg_pwm_duty_cycle_get, dbg_pwm_duty_cycle_set,
1178 "%lld\n");
1179
1180static int dbg_pwm_period_set(void *data, u64 val)
1181{
1182 struct pm8xxx_pwm_user *puser = data;
1183 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1184 int rc;
1185
1186 mutex_lock(&dbgdev->dbg_mutex);
1187 rc = dbg_pwm_check_period(val);
1188 if (!rc)
1189 puser->period = val;
1190 mutex_unlock(&dbgdev->dbg_mutex);
1191 return 0;
1192}
1193
1194static int dbg_pwm_period_get(void *data, u64 *val)
1195{
1196 struct pm8xxx_pwm_user *puser = data;
1197 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1198
1199 mutex_lock(&dbgdev->dbg_mutex);
1200 *val = puser->period;
1201 mutex_unlock(&dbgdev->dbg_mutex);
1202 return 0;
1203}
1204
1205DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_period_fops,
1206 dbg_pwm_period_get, dbg_pwm_period_set, "%lld\n");
1207
1208static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)
1209{
1210 struct pm8xxx_pwm_dbg_device *dbgdev;
1211 struct dentry *dent;
1212 struct dentry *temp;
1213 struct pm8xxx_pwm_user *puser;
1214 int i;
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001215 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001216
1217 if (dev == NULL) {
1218 pr_err("no parent data passed in.\n");
1219 return -EINVAL;
1220 }
1221
1222 dbgdev = kzalloc(sizeof *dbgdev, GFP_KERNEL);
1223 if (dbgdev == NULL) {
1224 pr_err("kzalloc() failed.\n");
1225 return -ENOMEM;
1226 }
1227
Jay Chokshi57656862011-09-07 12:02:22 -07001228 dbgdev->user = kcalloc(pwm_chip->pwm_channels,
1229 sizeof(struct pm8xxx_pwm_user), GFP_KERNEL);
1230 if (dbgdev->user == NULL) {
1231 pr_err("kcalloc() failed.\n");
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001232 rc = -ENOMEM;
1233 goto user_error;
Jay Chokshi57656862011-09-07 12:02:22 -07001234 }
1235
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001236 mutex_init(&dbgdev->dbg_mutex);
1237
1238 dbgdev->dev = dev;
1239
1240 dent = debugfs_create_dir("pm8xxx-pwm-dbg", NULL);
1241 if (dent == NULL || IS_ERR(dent)) {
1242 pr_err("ERR debugfs_create_dir: dent=%p\n", dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001243 rc = -ENOMEM;
1244 goto dir_error;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001245 }
1246
1247 dbgdev->dent = dent;
1248
Jay Chokshi57656862011-09-07 12:02:22 -07001249 for (i = 0; i < pwm_chip->pwm_channels; i++) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001250 char pwm_ch[] = "0";
1251
1252 pwm_ch[0] = '0' + i;
1253 dent = debugfs_create_dir(pwm_ch, dbgdev->dent);
1254 if (dent == NULL || IS_ERR(dent)) {
1255 pr_err("ERR: pwm=%d: dir: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001256 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001257 goto debug_error;
1258 }
1259
1260 puser = &dbgdev->user[i];
1261 puser->dbgdev = dbgdev;
1262 puser->pwm_id = i;
1263 temp = debugfs_create_file("period", S_IRUGO | S_IWUSR,
1264 dent, puser, &dbg_pwm_period_fops);
1265 if (temp == NULL || IS_ERR(temp)) {
1266 pr_err("ERR: pwm=%d: period: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001267 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001268 goto debug_error;
1269 }
1270
1271 temp = debugfs_create_file("duty-cycle", S_IRUGO | S_IWUSR,
1272 dent, puser, &dbg_pwm_duty_cycle_fops);
1273 if (temp == NULL || IS_ERR(temp)) {
1274 pr_err("ERR: pwm=%d: duty-cycle: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001275 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001276 goto debug_error;
1277 }
1278
1279 temp = debugfs_create_file("enable", S_IRUGO | S_IWUSR,
1280 dent, puser, &dbg_pwm_enable_fops);
1281 if (temp == NULL || IS_ERR(temp)) {
1282 pr_err("ERR: pwm=%d: enable: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001283 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001284 goto debug_error;
1285 }
1286 }
1287
1288 pmic_dbg_device = dbgdev;
1289
1290 return 0;
1291
1292debug_error:
1293 debugfs_remove_recursive(dbgdev->dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001294dir_error:
1295 kfree(dbgdev->user);
1296user_error:
1297 kfree(dbgdev);
1298 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001299}
1300
1301static int __devexit pm8xxx_pwm_dbg_remove(void)
1302{
1303 if (pmic_dbg_device) {
Jay Chokshi57656862011-09-07 12:02:22 -07001304 kfree(pmic_dbg_device->user);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001305 debugfs_remove_recursive(pmic_dbg_device->dent);
1306 kfree(pmic_dbg_device);
1307 }
1308 return 0;
1309}
1310
1311#else
1312
1313static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)
1314{
1315 return 0;
1316}
1317
1318static int __devexit pm8xxx_pwm_dbg_remove(void)
1319{
1320 return 0;
1321}
1322
1323#endif
1324
1325static int __devinit pm8xxx_pwm_probe(struct platform_device *pdev)
1326{
1327 struct pm8xxx_pwm_chip *chip;
1328 int i;
Jay Chokshi57656862011-09-07 12:02:22 -07001329 enum pm8xxx_version version;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001330
1331 chip = kzalloc(sizeof *chip, GFP_KERNEL);
1332 if (chip == NULL) {
1333 pr_err("kzalloc() failed.\n");
1334 return -ENOMEM;
1335 }
1336
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001337 mutex_init(&chip->pwm_mutex);
1338
1339 chip->dev = &pdev->dev;
1340 pwm_chip = chip;
Jay Chokshi57656862011-09-07 12:02:22 -07001341
1342 version = pm8xxx_get_version(chip->dev->parent);
1343
1344 if (version == PM8XXX_VERSION_8921 ||
David Collins999480d2011-11-16 08:52:30 -08001345 version == PM8XXX_VERSION_8058 ||
Jay Chokshi0c220cd2011-12-09 17:18:20 -08001346 version == PM8XXX_VERSION_8922 ||
1347 version == PM8XXX_VERSION_8038) {
Jay Chokshi57656862011-09-07 12:02:22 -07001348 chip->is_lpg_supported = 1;
1349 }
1350 if (chip->is_lpg_supported) {
Jay Chokshi0c220cd2011-12-09 17:18:20 -08001351 if (version == PM8XXX_VERSION_8922 ||
1352 version == PM8XXX_VERSION_8038) {
1353 for (i = 0; i < NUM_CLOCKS; i++)
1354 pt_t[0][i] /= PRE_DIVIDE_2;
1355 chip->pwm_channels = PM8XXX_LPG_V1_PWM_CHANNELS;
1356 } else {
1357 chip->pwm_channels = PM8XXX_LPG_V0_PWM_CHANNELS;
1358 }
Jay Chokshi57656862011-09-07 12:02:22 -07001359 chip->pwm_total_pre_divs = NUM_LPG_PRE_DIVIDE;
1360 } else {
1361 chip->pwm_channels = PM8XXX_PWM_CHANNELS;
1362 chip->pwm_total_pre_divs = NUM_PWM_PRE_DIVIDE;
1363 }
1364
1365 chip->pwm_dev = kcalloc(chip->pwm_channels, sizeof(struct pwm_device),
1366 GFP_KERNEL);
1367 if (chip->pwm_dev == NULL) {
1368 pr_err("kcalloc() failed.\n");
1369 mutex_destroy(&chip->pwm_mutex);
1370 kfree(chip);
1371 return -ENOMEM;
1372 }
1373
1374 for (i = 0; i < chip->pwm_channels; i++) {
1375 chip->pwm_dev[i].pwm_id = i;
1376 chip->pwm_dev[i].chip = chip;
1377 }
1378
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001379 platform_set_drvdata(pdev, chip);
1380
1381 if (pm8xxx_pwm_dbg_probe(&pdev->dev) < 0)
1382 pr_err("could not set up debugfs\n");
1383
1384 pr_notice("OK\n");
1385 return 0;
1386}
1387
1388static int __devexit pm8xxx_pwm_remove(struct platform_device *pdev)
1389{
1390 struct pm8xxx_pwm_chip *chip = dev_get_drvdata(pdev->dev.parent);
1391
1392 pm8xxx_pwm_dbg_remove();
Jay Chokshi57656862011-09-07 12:02:22 -07001393 kfree(chip->pwm_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001394 mutex_destroy(&chip->pwm_mutex);
1395 platform_set_drvdata(pdev, NULL);
1396 kfree(chip);
1397 return 0;
1398}
1399
1400static struct platform_driver pm8xxx_pwm_driver = {
1401 .probe = pm8xxx_pwm_probe,
1402 .remove = __devexit_p(pm8xxx_pwm_remove),
1403 .driver = {
1404 .name = PM8XXX_PWM_DEV_NAME,
1405 .owner = THIS_MODULE,
1406 },
1407};
1408
1409static int __init pm8xxx_pwm_init(void)
1410{
1411 return platform_driver_register(&pm8xxx_pwm_driver);
1412}
1413
1414static void __exit pm8xxx_pwm_exit(void)
1415{
1416 platform_driver_unregister(&pm8xxx_pwm_driver);
1417}
1418
1419subsys_initcall(pm8xxx_pwm_init);
1420module_exit(pm8xxx_pwm_exit);
1421
1422MODULE_LICENSE("GPL v2");
1423MODULE_DESCRIPTION("PM8XXX PWM driver");
1424MODULE_VERSION("1.0");
1425MODULE_ALIAS("platform:" PM8XXX_PWM_DEV_NAME);