blob: eb8320f82d950fcc525316d50a6dae14f3534ca9 [file] [log] [blame]
Prasad Sodagudi5af87922013-06-05 17:33:52 +05301/* Copyright (c) 2011-2013, The Linux Foundation. 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 */
Prasad Sodagudi5af87922013-06-05 17:33:52 +053061#define SSBI_REG_ADDR_LPG_BANK_LOW_EN 0x130
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062#define SSBI_REG_ADDR_LPG_CTL_BASE 0x13C
63#define SSBI_REG_ADDR_LPG_CTL(n) (SSBI_REG_ADDR_LPG_CTL_BASE + (n))
64#define SSBI_REG_ADDR_LPG_BANK_SEL 0x143
Prasad Sodagudi5af87922013-06-05 17:33:52 +053065#define SSBI_REG_ADDR_LPG_BANK_HIGH_EN 0x144
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066#define SSBI_REG_ADDR_LPG_LUT_CFG0 0x145
67#define SSBI_REG_ADDR_LPG_LUT_CFG1 0x146
Jay Chokshib0a0fa52012-02-23 16:18:44 -080068#define SSBI_REG_ADDR_LPG_TEST 0x147
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069
Jay Chokshi57656862011-09-07 12:02:22 -070070/* LPG Control 0 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071#define PM8XXX_PWM_1KHZ_COUNT_MASK 0xF0
72#define PM8XXX_PWM_1KHZ_COUNT_SHIFT 4
73
74#define PM8XXX_PWM_1KHZ_COUNT_MAX 15
75
76#define PM8XXX_PWM_OUTPUT_EN 0x08
77#define PM8XXX_PWM_PWM_EN 0x04
78#define PM8XXX_PWM_RAMP_GEN_EN 0x02
79#define PM8XXX_PWM_RAMP_START 0x01
80
81#define PM8XXX_PWM_PWM_START (PM8XXX_PWM_OUTPUT_EN \
82 | PM8XXX_PWM_PWM_EN)
83#define PM8XXX_PWM_RAMP_GEN_START (PM8XXX_PWM_RAMP_GEN_EN \
84 | PM8XXX_PWM_RAMP_START)
85
Jay Chokshi57656862011-09-07 12:02:22 -070086/* LPG Control 1 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087#define PM8XXX_PWM_REVERSE_EN 0x80
88#define PM8XXX_PWM_BYPASS_LUT 0x40
89#define PM8XXX_PWM_HIGH_INDEX_MASK 0x3F
90
Jay Chokshi57656862011-09-07 12:02:22 -070091/* LPG Control 2 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070092#define PM8XXX_PWM_LOOP_EN 0x80
93#define PM8XXX_PWM_RAMP_UP 0x40
94#define PM8XXX_PWM_LOW_INDEX_MASK 0x3F
95
Jay Chokshi57656862011-09-07 12:02:22 -070096/* LPG Control 3 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070097#define PM8XXX_PWM_VALUE_BIT7_0 0xFF
98#define PM8XXX_PWM_VALUE_BIT5_0 0x3F
99
Jay Chokshi57656862011-09-07 12:02:22 -0700100/* LPG Control 4 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101#define PM8XXX_PWM_VALUE_BIT8 0x80
102
Jay Chokshi57656862011-09-07 12:02:22 -0700103#define PM8XXX_LPG_PWM_CLK_SEL_MASK 0x60
104#define PM8XXX_LPG_PWM_CLK_SEL_SHIFT 5
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105
106#define PM8XXX_PWM_CLK_SEL_NO 0
107#define PM8XXX_PWM_CLK_SEL_1KHZ 1
108#define PM8XXX_PWM_CLK_SEL_32KHZ 2
109#define PM8XXX_PWM_CLK_SEL_19P2MHZ 3
110
Jay Chokshi57656862011-09-07 12:02:22 -0700111#define PM8XXX_LPG_PWM_PREDIVIDE_MASK 0x18
112#define PM8XXX_LPG_PWM_PREDIVIDE_SHIFT 3
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113
114#define PM8XXX_PWM_PREDIVIDE_2 0
115#define PM8XXX_PWM_PREDIVIDE_3 1
116#define PM8XXX_PWM_PREDIVIDE_5 2
117#define PM8XXX_PWM_PREDIVIDE_6 3
118
Jay Chokshi57656862011-09-07 12:02:22 -0700119#define PM8XXX_LPG_PWM_M_MASK 0x07
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700120#define PM8XXX_PWM_M_MIN 0
121#define PM8XXX_PWM_M_MAX 7
122
Jay Chokshi57656862011-09-07 12:02:22 -0700123/* LPG Control 5 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124#define PM8XXX_PWM_PAUSE_COUNT_HI_MASK 0xFC
125#define PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT 2
126
127#define PM8XXX_PWM_PAUSE_ENABLE_HIGH 0x02
128#define PM8XXX_PWM_SIZE_9_BIT 0x01
129
Jay Chokshi57656862011-09-07 12:02:22 -0700130/* LPG Control 6 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700131#define PM8XXX_PWM_PAUSE_COUNT_LO_MASK 0xFC
132#define PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT 2
133
134#define PM8XXX_PWM_PAUSE_ENABLE_LOW 0x02
135#define PM8XXX_PWM_RESERVED 0x01
136
137#define PM8XXX_PWM_PAUSE_COUNT_MAX 56 /* < 2^6 = 64 */
138
Jay Chokshi57656862011-09-07 12:02:22 -0700139/* LPG LUT_CFG1 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140#define PM8XXX_PWM_LUT_READ 0x40
141
Jay Chokshib0a0fa52012-02-23 16:18:44 -0800142/* TEST */
143#define PM8XXX_PWM_DTEST_MASK 0x38
144#define PM8XXX_PWM_DTEST_SHIFT 3
145#define PM8XXX_PWM_DTEST_BANK_MASK 0x07
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700146
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700147/*
148 * PWM Frequency = Clock Frequency / (N * T)
149 * or
150 * PWM Period = Clock Period * (N * T)
151 * where
152 * N = 2^9 or 2^6 for 9-bit or 6-bit PWM size
153 * T = Pre-divide * 2^m, where m = 0..7 (exponent)
154 *
155 * This is the formula to figure out m for the best pre-divide and clock:
Willie Ruan4a0a7002012-01-10 15:39:44 -0800156 * (PWM Period / N) = (Pre-divide * Clock Period) * 2^m
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157 */
158#define NUM_CLOCKS 3
159
Willie Ruandb78e942012-01-10 15:00:28 -0800160#define NSEC_1024HZ (NSEC_PER_SEC / 1024)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161#define NSEC_32768HZ (NSEC_PER_SEC / 32768)
162#define NSEC_19P2MHZ (NSEC_PER_SEC / 19200000)
163
Willie Ruanb10be972012-01-12 11:30:11 -0800164#define NUM_LPG_PRE_DIVIDE 4
Jay Chokshi57656862011-09-07 12:02:22 -0700165#define NUM_PWM_PRE_DIVIDE 2
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700166
Jay Chokshi0c220cd2011-12-09 17:18:20 -0800167#define PRE_DIVIDE_1 1 /* v1 */
Willie Ruanb10be972012-01-12 11:30:11 -0800168#define PRE_DIVIDE_2 2
169#define PRE_DIVIDE_3 3
170#define PRE_DIVIDE_5 5
171#define PRE_DIVIDE_6 6
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172
Jay Chokshi57656862011-09-07 12:02:22 -0700173static unsigned int pt_t[NUM_LPG_PRE_DIVIDE][NUM_CLOCKS] = {
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 },
Willie Ruanb10be972012-01-12 11:30:11 -0800178 { PRE_DIVIDE_3 * NSEC_1024HZ,
179 PRE_DIVIDE_3 * NSEC_32768HZ,
180 PRE_DIVIDE_3 * NSEC_19P2MHZ,
181 },
182 { PRE_DIVIDE_5 * NSEC_1024HZ,
183 PRE_DIVIDE_5 * NSEC_32768HZ,
184 PRE_DIVIDE_5 * NSEC_19P2MHZ,
185 },
186 { PRE_DIVIDE_6 * NSEC_1024HZ,
187 PRE_DIVIDE_6 * NSEC_32768HZ,
188 PRE_DIVIDE_6 * NSEC_19P2MHZ,
189 },
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700190};
191
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700192/* Private data */
193struct pm8xxx_pwm_chip;
194
195struct pwm_device {
196 int pwm_id; /* = bank/channel id */
197 int in_use;
198 const char *label;
Willie Ruan719c6762011-08-25 11:03:14 -0700199 struct pm8xxx_pwm_period period;
200 int pwm_value;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201 int pwm_period;
202 int pwm_duty;
Jay Chokshi57656862011-09-07 12:02:22 -0700203 u8 pwm_lpg_ctl[PM8XXX_LPG_CTL_REGS];
204 u8 pwm_ctl1;
205 u8 pwm_ctl2;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206 int irq;
207 struct pm8xxx_pwm_chip *chip;
Willie Ruan719c6762011-08-25 11:03:14 -0700208 int bypass_lut;
Jay Chokshib0a0fa52012-02-23 16:18:44 -0800209 int dtest_mode_supported;
Prasad Sodagudi5af87922013-06-05 17:33:52 +0530210 int banks;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700211};
212
213struct pm8xxx_pwm_chip {
Jay Chokshi57656862011-09-07 12:02:22 -0700214 struct pwm_device *pwm_dev;
215 u8 pwm_channels;
216 u8 pwm_total_pre_divs;
Prasad Sodagudi5af87922013-06-05 17:33:52 +0530217 u8 lo_bank_mask;
218 u8 hi_bank_mask;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219 struct mutex pwm_mutex;
220 struct device *dev;
Jay Chokshi57656862011-09-07 12:02:22 -0700221 bool is_lpg_supported;
Jay Chokshi90485222012-08-07 20:22:39 -0700222 bool is_pwm_enable_sync_workaround_needed;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223};
224
225static struct pm8xxx_pwm_chip *pwm_chip;
226
Willie Ruan719c6762011-08-25 11:03:14 -0700227struct pm8xxx_pwm_lut {
228 /* LUT parameters */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700229 int lut_duty_ms;
230 int lut_lo_index;
231 int lut_hi_index;
232 int lut_pause_hi;
233 int lut_pause_lo;
234 int flags;
235};
236
237static const u16 duty_msec[PM8XXX_PWM_1KHZ_COUNT_MAX + 1] = {
238 0, 1, 2, 3, 4, 6, 8, 16, 18, 24, 32, 36, 64, 128, 256, 512
239};
240
241static const u16 pause_count[PM8XXX_PWM_PAUSE_COUNT_MAX + 1] = {
242 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
243 23, 28, 31, 42, 47, 56, 63, 83, 94, 111, 125, 167, 188, 222, 250, 333,
244 375, 500, 667, 750, 800, 900, 1000, 1100,
245 1200, 1300, 1400, 1500, 1600, 1800, 2000, 2500,
246 3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500,
247 7000
248};
249
250/* Internal functions */
Willie Ruan8de2f382011-08-25 11:04:50 -0700251static void pm8xxx_pwm_save(u8 *u8p, u8 mask, u8 val)
252{
253 *u8p &= ~mask;
254 *u8p |= val & mask;
255}
256
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700257static int pm8xxx_pwm_bank_enable(struct pwm_device *pwm, int enable)
258{
259 int rc;
260 u8 reg;
261 struct pm8xxx_pwm_chip *chip;
262
263 chip = pwm->chip;
264
Prasad Sodagudi6a79c842013-06-12 19:29:36 +0530265 if (!pwm->banks)
266 pwm->banks = (PM_PWM_BANK_LO | PM_PWM_BANK_HI);
267
Prasad Sodagudi5af87922013-06-05 17:33:52 +0530268 if (pwm->banks & PM_PWM_BANK_LO) {
269 if (enable)
270 reg = chip->lo_bank_mask | (1 << pwm->pwm_id);
271 else
272 reg = chip->lo_bank_mask & ~(1 << pwm->pwm_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700273
Prasad Sodagudi5af87922013-06-05 17:33:52 +0530274 rc = pm8xxx_writeb(chip->dev->parent,
275 SSBI_REG_ADDR_LPG_BANK_LOW_EN, reg);
276 if (rc) {
277 pr_err("pm8xxx_writeb(): Enable Bank Low =%d\n", rc);
278 return rc;
279 }
280
281 chip->lo_bank_mask = reg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700282 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700283
Prasad Sodagudi5af87922013-06-05 17:33:52 +0530284 if (pwm->banks & PM_PWM_BANK_HI) {
285 if (enable)
286 reg = chip->hi_bank_mask | (1 << pwm->pwm_id);
287 else
288 reg = chip->hi_bank_mask & ~(1 << pwm->pwm_id);
289
290 rc = pm8xxx_writeb(chip->dev->parent,
291 SSBI_REG_ADDR_LPG_BANK_HIGH_EN, reg);
292 if (rc) {
293 pr_err("pm8xxx_writeb(): Enable Bank High =%d\n", rc);
294 return rc;
295 }
296
297 chip->hi_bank_mask = reg;
298 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700299 return 0;
300}
301
302static int pm8xxx_pwm_bank_sel(struct pwm_device *pwm)
303{
304 int rc;
305
306 rc = pm8xxx_writeb(pwm->chip->dev->parent, SSBI_REG_ADDR_LPG_BANK_SEL,
307 pwm->pwm_id);
308 if (rc)
Willie Ruan8de2f382011-08-25 11:04:50 -0700309 pr_err("pm8xxx_writeb(): rc=%d (Select PWM Bank)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310 return rc;
311}
312
313static int pm8xxx_pwm_start(struct pwm_device *pwm, int start, int ramp_start)
314{
315 int rc;
316 u8 reg;
317
318 if (start) {
Jay Chokshi57656862011-09-07 12:02:22 -0700319 reg = pwm->pwm_lpg_ctl[0] | PM8XXX_PWM_PWM_START;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700320 if (ramp_start)
321 reg |= PM8XXX_PWM_RAMP_GEN_START;
322 else
323 reg &= ~PM8XXX_PWM_RAMP_GEN_START;
324 } else {
Jay Chokshi57656862011-09-07 12:02:22 -0700325 reg = pwm->pwm_lpg_ctl[0] & ~PM8XXX_PWM_PWM_START;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700326 reg &= ~PM8XXX_PWM_RAMP_GEN_START;
327 }
328
329 rc = pm8xxx_writeb(pwm->chip->dev->parent, SSBI_REG_ADDR_LPG_CTL(0),
330 reg);
331 if (rc)
Willie Ruan8de2f382011-08-25 11:04:50 -0700332 pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl 0)\n", rc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700333 else
Jay Chokshi57656862011-09-07 12:02:22 -0700334 pwm->pwm_lpg_ctl[0] = reg;
335 return rc;
336}
337
338static int pm8xxx_pwm_disable(struct pwm_device *pwm)
339{
340 int rc;
341 u8 reg;
342
343 reg = pwm->pwm_ctl1 & PM8XXX_PWM_DISABLE;
344
345 rc = pm8xxx_writeb(pwm->chip->dev->parent,
346 SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), reg);
347
348 if (rc)
349 pr_err("pm8xxx_writeb(): rc=%d (Disable PWM Ctl %d)\n", rc,
350 pwm->pwm_id);
351 return rc;
352}
353
354static int pm8xxx_pwm_enable(struct pwm_device *pwm)
355{
356 /**
357 * A kind of best Effort: Just write the clock information that
358 * we have in the register.
359 */
360 int rc;
361
362 rc = pm8xxx_writeb(pwm->chip->dev->parent,
363 SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id), pwm->pwm_ctl1);
364
365 if (rc)
366 pr_err("pm8xxx_writeb(): rc=%d (Enable PWM Ctl %d)\n", rc,
367 pwm->pwm_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700368 return rc;
369}
370
371static void pm8xxx_pwm_calc_period(unsigned int period_us,
Willie Ruan719c6762011-08-25 11:03:14 -0700372 struct pm8xxx_pwm_period *period)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700373{
374 int n, m, clk, div;
375 int best_m, best_div, best_clk;
Willie Ruan4a0a7002012-01-10 15:39:44 -0800376 unsigned int last_err, cur_err, min_err;
377 unsigned int tmp_p, period_n;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700378
379 /* PWM Period / N */
Willie Ruan70f989d2012-01-10 10:09:36 -0800380 if (period_us < ((unsigned)(-1) / NSEC_PER_USEC)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700381 period_n = (period_us * NSEC_PER_USEC) >> 6;
382 n = 6;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700383 } else {
384 period_n = (period_us >> 9) * NSEC_PER_USEC;
385 n = 9;
386 }
387
Willie Ruan4a0a7002012-01-10 15:39:44 -0800388 min_err = last_err = (unsigned)(-1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700389 best_m = 0;
390 best_clk = 0;
391 best_div = 0;
392 for (clk = 0; clk < NUM_CLOCKS; clk++) {
Jay Chokshi57656862011-09-07 12:02:22 -0700393 for (div = 0; div < pwm_chip->pwm_total_pre_divs; div++) {
Willie Ruan4a0a7002012-01-10 15:39:44 -0800394 /* period_n = (PWM Period / N) */
395 /* tmp_p = (Pre-divide * Clock Period) * 2^m */
396 tmp_p = pt_t[div][clk];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700397 for (m = 0; m <= PM8XXX_PWM_M_MAX; m++) {
Willie Ruan4a0a7002012-01-10 15:39:44 -0800398 if (period_n > tmp_p)
399 cur_err = period_n - tmp_p;
400 else
401 cur_err = tmp_p - period_n;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700402
Willie Ruan4a0a7002012-01-10 15:39:44 -0800403 if (cur_err < min_err) {
404 min_err = cur_err;
405 best_m = m;
406 best_clk = clk;
407 best_div = div;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700408 }
Willie Ruan4a0a7002012-01-10 15:39:44 -0800409
410 if (m && cur_err > last_err)
411 /* Break for bigger cur_err */
412 break;
413
414 last_err = cur_err;
415 tmp_p <<= 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700416 }
417 }
418 }
419
Willie Ruan75d9e5b2011-08-25 10:59:17 -0700420 /* Use higher resolution */
421 if (best_m >= 3 && n == 6) {
422 n += 3;
423 best_m -= 3;
424 }
425
Willie Ruan719c6762011-08-25 11:03:14 -0700426 period->pwm_size = n;
427 period->clk = best_clk;
428 period->pre_div = best_div;
429 period->pre_div_exp = best_m;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700430}
431
Willie Ruan8de2f382011-08-25 11:04:50 -0700432static void pm8xxx_pwm_calc_pwm_value(struct pwm_device *pwm,
433 unsigned int period_us,
434 unsigned int duty_us)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700435{
Willie Ruan8de2f382011-08-25 11:04:50 -0700436 unsigned int max_pwm_value, tmp;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437
Willie Ruan8de2f382011-08-25 11:04:50 -0700438 /* Figure out pwm_value with overflow handling */
439 tmp = 1 << (sizeof(tmp) * 8 - pwm->period.pwm_size);
440 if (duty_us < tmp) {
441 tmp = duty_us << pwm->period.pwm_size;
442 pwm->pwm_value = tmp / period_us;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700443 } else {
Willie Ruan8de2f382011-08-25 11:04:50 -0700444 tmp = period_us >> pwm->period.pwm_size;
445 pwm->pwm_value = duty_us / tmp;
446 }
447 max_pwm_value = (1 << pwm->period.pwm_size) - 1;
448 if (pwm->pwm_value > max_pwm_value)
449 pwm->pwm_value = max_pwm_value;
450}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700451
Willie Ruan8de2f382011-08-25 11:04:50 -0700452static int pm8xxx_pwm_change_table(struct pwm_device *pwm, int duty_pct[],
453 int start_idx, int len, int raw_value)
454{
455 unsigned int pwm_value, max_pwm_value;
456 u8 cfg0, cfg1;
457 int i, pwm_size;
458 int rc = 0;
459
Jay Chokshi57656862011-09-07 12:02:22 -0700460 pwm_size = (pwm->pwm_lpg_ctl[5] & PM8XXX_PWM_SIZE_9_BIT) ? 9 : 6;
Willie Ruan8de2f382011-08-25 11:04:50 -0700461 max_pwm_value = (1 << pwm_size) - 1;
462 for (i = 0; i < len; i++) {
463 if (raw_value)
464 pwm_value = duty_pct[i];
465 else
466 pwm_value = (duty_pct[i] << pwm_size) / 100;
467
468 if (pwm_value > max_pwm_value)
469 pwm_value = max_pwm_value;
470 cfg0 = pwm_value;
471 cfg1 = (pwm_value >> 1) & 0x80;
472 cfg1 |= start_idx + i;
473
474 rc = pm8xxx_writeb(pwm->chip->dev->parent,
475 SSBI_REG_ADDR_LPG_LUT_CFG0, cfg0);
476 if (rc)
477 break;
478
479 rc = pm8xxx_writeb(pwm->chip->dev->parent,
480 SSBI_REG_ADDR_LPG_LUT_CFG1, cfg1);
481 if (rc)
482 break;
483 }
484 return rc;
485}
486
487static void pm8xxx_pwm_save_index(struct pwm_device *pwm,
488 int low_idx, int high_idx, int flags)
489{
Jay Chokshi57656862011-09-07 12:02:22 -0700490 pwm->pwm_lpg_ctl[1] = high_idx & PM8XXX_PWM_HIGH_INDEX_MASK;
491 pwm->pwm_lpg_ctl[2] = low_idx & PM8XXX_PWM_LOW_INDEX_MASK;
Willie Ruan8de2f382011-08-25 11:04:50 -0700492
493 if (flags & PM_PWM_LUT_REVERSE)
Jay Chokshi57656862011-09-07 12:02:22 -0700494 pwm->pwm_lpg_ctl[1] |= PM8XXX_PWM_REVERSE_EN;
Willie Ruan8de2f382011-08-25 11:04:50 -0700495 if (flags & PM_PWM_LUT_RAMP_UP)
Jay Chokshi57656862011-09-07 12:02:22 -0700496 pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_RAMP_UP;
Willie Ruan8de2f382011-08-25 11:04:50 -0700497 if (flags & PM_PWM_LUT_LOOP)
Jay Chokshi57656862011-09-07 12:02:22 -0700498 pwm->pwm_lpg_ctl[2] |= PM8XXX_PWM_LOOP_EN;
Willie Ruan8de2f382011-08-25 11:04:50 -0700499}
500
501static void pm8xxx_pwm_save_period(struct pwm_device *pwm)
502{
503 u8 mask, val;
504
Jay Chokshi57656862011-09-07 12:02:22 -0700505 if (pwm_chip->is_lpg_supported) {
506 val = ((pwm->period.clk + 1) << PM8XXX_LPG_PWM_CLK_SEL_SHIFT)
507 & PM8XXX_LPG_PWM_CLK_SEL_MASK;
508 val |= (pwm->period.pre_div << PM8XXX_LPG_PWM_PREDIVIDE_SHIFT)
509 & PM8XXX_LPG_PWM_PREDIVIDE_MASK;
510 val |= pwm->period.pre_div_exp & PM8XXX_LPG_PWM_M_MASK;
511 mask = PM8XXX_LPG_PWM_CLK_SEL_MASK |
512 PM8XXX_LPG_PWM_PREDIVIDE_MASK | PM8XXX_LPG_PWM_M_MASK;
513 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
Willie Ruan8de2f382011-08-25 11:04:50 -0700514
Jay Chokshi57656862011-09-07 12:02:22 -0700515 val = (pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0;
516 mask = PM8XXX_PWM_SIZE_9_BIT;
517 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
518 } else {
519 val = ((pwm->period.clk + 1) << PM8XXX_PWM_CLK_SEL_SHIFT)
520 & PM8XXX_PWM_CLK_SEL_MASK;
521 val |= (pwm->period.pre_div << PM8XXX_PWM_PREDIVIDE_SHIFT)
522 & PM8XXX_PWM_PREDIVIDE_MASK;
523 val |= (pwm->period.pre_div_exp << PM8XXX_PWM_M_SHIFT)
524 & PM8XXX_PWM_M_MASK;
525 val |= (((pwm->period.pwm_size > 6) ? PM8XXX_PWM_SIZE_9_BIT : 0)
526 << PM8XXX_PWM_SIZE_SHIFT) & PM8XXX_PWM_SIZE_MASK;
527
528 mask = PM8XXX_PWM_CLK_SEL_MASK | PM8XXX_PWM_PREDIVIDE_MASK |
529 PM8XXX_PWM_M_MASK | PM8XXX_PWM_SIZE_MASK;
530 pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
531 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700532}
533
534static void pm8xxx_pwm_save_pwm_value(struct pwm_device *pwm)
535{
536 u8 mask, val;
537
Jay Chokshi57656862011-09-07 12:02:22 -0700538 if (pwm_chip->is_lpg_supported) {
539 val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 1) : 0;
540 pwm->pwm_lpg_ctl[3] = pwm->pwm_value;
541 mask = PM8XXX_PWM_VALUE_BIT8;
542 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[4], mask, val);
543 } else {
544 val = (pwm->period.pwm_size > 6) ? (pwm->pwm_value >> 8) : 0;
545 pwm->pwm_ctl2 = pwm->pwm_value;
546 mask = PM8XXX_PWM_VALUE_BIT0;
547 pm8xxx_pwm_save(&pwm->pwm_ctl1, mask, val);
548 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700549}
550
551static void pm8xxx_pwm_save_duty_time(struct pwm_device *pwm,
552 struct pm8xxx_pwm_lut *lut)
553{
554 int i;
555 u8 mask, val;
556
557 /* Linear search for duty time */
558 for (i = 0; i < PM8XXX_PWM_1KHZ_COUNT_MAX; i++) {
559 if (duty_msec[i] >= lut->lut_duty_ms)
560 break;
561 }
562 val = i << PM8XXX_PWM_1KHZ_COUNT_SHIFT;
563
564 mask = PM8XXX_PWM_1KHZ_COUNT_MASK;
Jay Chokshi57656862011-09-07 12:02:22 -0700565 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[0], mask, val);
Willie Ruan8de2f382011-08-25 11:04:50 -0700566}
567
568static void pm8xxx_pwm_save_pause(struct pwm_device *pwm,
569 struct pm8xxx_pwm_lut *lut)
570{
571 int i, pause_cnt, time_cnt;
572 u8 mask, val;
573
Jay Chokshi57656862011-09-07 12:02:22 -0700574 time_cnt = (pwm->pwm_lpg_ctl[0] & PM8XXX_PWM_1KHZ_COUNT_MASK)
Willie Ruan8de2f382011-08-25 11:04:50 -0700575 >> PM8XXX_PWM_1KHZ_COUNT_SHIFT;
576 if (lut->flags & PM_PWM_LUT_PAUSE_HI_EN) {
577 pause_cnt = (lut->lut_pause_hi + duty_msec[time_cnt] / 2)
578 / duty_msec[time_cnt];
579 /* Linear search for pause time */
580 for (i = 0; i < PM8XXX_PWM_PAUSE_COUNT_MAX; i++) {
581 if (pause_count[i] >= pause_cnt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700582 break;
583 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700584 val = (i << PM8XXX_PWM_PAUSE_COUNT_HI_SHIFT) &
585 PM8XXX_PWM_PAUSE_COUNT_HI_MASK;
586 val |= PM8XXX_PWM_PAUSE_ENABLE_HIGH;
587 } else {
588 val = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700589 }
590
Willie Ruan8de2f382011-08-25 11:04:50 -0700591 mask = PM8XXX_PWM_PAUSE_COUNT_HI_MASK | PM8XXX_PWM_PAUSE_ENABLE_HIGH;
Jay Chokshi57656862011-09-07 12:02:22 -0700592 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[5], mask, val);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700593
Willie Ruan8de2f382011-08-25 11:04:50 -0700594 if (lut->flags & PM_PWM_LUT_PAUSE_LO_EN) {
595 /* Linear search for pause time */
596 pause_cnt = (lut->lut_pause_lo + duty_msec[time_cnt] / 2)
597 / duty_msec[time_cnt];
598 for (i = 0; i < PM8XXX_PWM_PAUSE_COUNT_MAX; i++) {
599 if (pause_count[i] >= pause_cnt)
600 break;
601 }
602 val = (i << PM8XXX_PWM_PAUSE_COUNT_LO_SHIFT) &
603 PM8XXX_PWM_PAUSE_COUNT_LO_MASK;
604 val |= PM8XXX_PWM_PAUSE_ENABLE_LOW;
605 } else {
606 val = 0;
607 }
608
609 mask = PM8XXX_PWM_PAUSE_COUNT_LO_MASK | PM8XXX_PWM_PAUSE_ENABLE_LOW;
Jay Chokshi57656862011-09-07 12:02:22 -0700610 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[6], mask, val);
Willie Ruan8de2f382011-08-25 11:04:50 -0700611}
612
Jay Chokshi57656862011-09-07 12:02:22 -0700613static int pm8xxx_pwm_write(struct pwm_device *pwm)
614{
615 int rc = 0;
616
617 rc = pm8xxx_writeb(pwm->chip->dev->parent,
618 SSBI_REG_ADDR_PWM_CTL1(pwm->pwm_id),
619 pwm->pwm_ctl1);
620 if (rc) {
621 pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl1[%d])\n",
622 rc, pwm->pwm_id);
623 return rc;
624 }
625
626 rc = pm8xxx_writeb(pwm->chip->dev->parent,
627 SSBI_REG_ADDR_PWM_CTL2(pwm->pwm_id),
628 pwm->pwm_ctl2);
629 if (rc) {
630 pr_err("pm8xxx_writeb() failed: rc=%d (PWM Ctl2[%d])\n",
631 rc, pwm->pwm_id);
632 return rc;
633 }
634
635 return rc;
636}
637
638static int pm8xxx_lpg_pwm_write(struct pwm_device *pwm, int start, int end)
Willie Ruan8de2f382011-08-25 11:04:50 -0700639{
640 int i, rc;
641
642 /* Write in reverse way so 0 would be the last */
643 for (i = end - 1; i >= start; i--) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700644 rc = pm8xxx_writeb(pwm->chip->dev->parent,
645 SSBI_REG_ADDR_LPG_CTL(i),
Jay Chokshi57656862011-09-07 12:02:22 -0700646 pwm->pwm_lpg_ctl[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700647 if (rc) {
Willie Ruan8de2f382011-08-25 11:04:50 -0700648 pr_err("pm8xxx_writeb(): rc=%d (PWM Ctl[%d])\n", rc, i);
649 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700650 }
651 }
652
Willie Ruan8de2f382011-08-25 11:04:50 -0700653 return 0;
654}
655
656static int pm8xxx_pwm_change_lut(struct pwm_device *pwm,
657 struct pm8xxx_pwm_lut *lut)
658{
659 int rc;
660
661 pm8xxx_pwm_save_index(pwm, lut->lut_lo_index,
662 lut->lut_hi_index, lut->flags);
663 pm8xxx_pwm_save_duty_time(pwm, lut);
664 pm8xxx_pwm_save_pause(pwm, lut);
Jay Chokshi57656862011-09-07 12:02:22 -0700665 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1], PM8XXX_PWM_BYPASS_LUT, 0);
Willie Ruan8de2f382011-08-25 11:04:50 -0700666
667 pm8xxx_pwm_bank_sel(pwm);
Jay Chokshi57656862011-09-07 12:02:22 -0700668 rc = pm8xxx_lpg_pwm_write(pwm, 0, 7);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700669
670 return rc;
671}
672
Jay Chokshib0a0fa52012-02-23 16:18:44 -0800673static int pm8xxx_pwm_set_dtest(struct pwm_device *pwm, int enable)
674{
675 int rc;
676 u8 reg;
677
678 reg = pwm->pwm_id & PM8XXX_PWM_DTEST_BANK_MASK;
679
680 if (enable) {
681 /* Observe LPG_OUT on DTEST1*/
682 reg |= (1 << PM8XXX_PWM_DTEST_SHIFT) &
683 PM8XXX_PWM_DTEST_MASK;
684 }
685
686 rc = pm8xxx_writeb(pwm->chip->dev->parent,
687 SSBI_REG_ADDR_LPG_TEST, reg);
688 if (rc)
689 pr_err("pm8xxx_write(DTEST=0x%x) failed: rc=%d\n",
690 reg, rc);
691
692 return rc;
693}
694
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700695/* APIs */
696/**
697 * pwm_request - request a PWM device
698 * @pwm_id: PWM id or channel
699 * @label: the label to identify the user
700 */
701struct pwm_device *pwm_request(int pwm_id, const char *label)
702{
703 struct pwm_device *pwm;
704
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700705 if (pwm_chip == NULL) {
706 pr_err("No pwm_chip\n");
707 return ERR_PTR(-ENODEV);
708 }
709
Jay Chokshi57656862011-09-07 12:02:22 -0700710 if (pwm_id >= pwm_chip->pwm_channels || pwm_id < 0) {
711 pr_err("Invalid pwm_id: %d with %s\n",
712 pwm_id, label ? label : ".");
713 return ERR_PTR(-EINVAL);
714 }
715
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700716 mutex_lock(&pwm_chip->pwm_mutex);
717 pwm = &pwm_chip->pwm_dev[pwm_id];
718 if (!pwm->in_use) {
719 pwm->in_use = 1;
720 pwm->label = label;
721 } else {
722 pwm = ERR_PTR(-EBUSY);
723 }
724 mutex_unlock(&pwm_chip->pwm_mutex);
725
726 return pwm;
727}
728EXPORT_SYMBOL_GPL(pwm_request);
729
730/**
731 * pwm_free - free a PWM device
732 * @pwm: the PWM device
733 */
734void pwm_free(struct pwm_device *pwm)
735{
736 if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
737 pr_err("Invalid pwm handle\n");
738 return;
739 }
740
741 mutex_lock(&pwm->chip->pwm_mutex);
742 if (pwm->in_use) {
Jay Chokshi57656862011-09-07 12:02:22 -0700743 if (pwm_chip->is_lpg_supported) {
744 pm8xxx_pwm_bank_sel(pwm);
745 pm8xxx_pwm_start(pwm, 0, 0);
746 } else {
747 pm8xxx_pwm_disable(pwm);
748 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700749 pwm->in_use = 0;
750 pwm->label = NULL;
751 }
Jay Chokshi57656862011-09-07 12:02:22 -0700752 if (pwm_chip->is_lpg_supported)
753 pm8xxx_pwm_bank_enable(pwm, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700754 mutex_unlock(&pwm->chip->pwm_mutex);
755}
756EXPORT_SYMBOL_GPL(pwm_free);
757
758/**
759 * pwm_config - change a PWM device configuration
760 * @pwm: the PWM device
761 * @period_us: period in microseconds
762 * @duty_us: duty cycle in microseconds
763 */
764int pwm_config(struct pwm_device *pwm, int duty_us, int period_us)
765{
Willie Ruan719c6762011-08-25 11:03:14 -0700766 struct pm8xxx_pwm_period *period;
Jay Chokshi57656862011-09-07 12:02:22 -0700767 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700768
769 if (pwm == NULL || IS_ERR(pwm) ||
770 duty_us > period_us ||
771 (unsigned)period_us > PM8XXX_PWM_PERIOD_MAX ||
772 (unsigned)period_us < PM8XXX_PWM_PERIOD_MIN) {
773 pr_err("Invalid pwm handle or parameters\n");
774 return -EINVAL;
775 }
776 if (pwm->chip == NULL) {
777 pr_err("No pwm_chip\n");
778 return -ENODEV;
779 }
780
Willie Ruan719c6762011-08-25 11:03:14 -0700781 period = &pwm->period;
782
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700783 mutex_lock(&pwm->chip->pwm_mutex);
784
785 if (!pwm->in_use) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700786 rc = -EINVAL;
787 goto out_unlock;
788 }
789
Willie Ruan8de2f382011-08-25 11:04:50 -0700790 if (pwm->pwm_period != period_us) {
791 pm8xxx_pwm_calc_period(period_us, period);
792 pm8xxx_pwm_save_period(pwm);
793 pwm->pwm_period = period_us;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700794 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700795
Willie Ruan8de2f382011-08-25 11:04:50 -0700796 pm8xxx_pwm_calc_pwm_value(pwm, period_us, duty_us);
797 pm8xxx_pwm_save_pwm_value(pwm);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700798
Jay Chokshi57656862011-09-07 12:02:22 -0700799 if (pwm_chip->is_lpg_supported) {
800 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
801 PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
802
803 pm8xxx_pwm_bank_sel(pwm);
804 rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
805 } else {
806 rc = pm8xxx_pwm_write(pwm);
807 }
Willie Ruan8de2f382011-08-25 11:04:50 -0700808
809 pr_debug("duty/period=%u/%u usec: pwm_value=%d (of %d)\n",
810 (unsigned)duty_us, (unsigned)period_us,
811 pwm->pwm_value, 1 << period->pwm_size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700812
813out_unlock:
814 mutex_unlock(&pwm->chip->pwm_mutex);
815 return rc;
816}
817EXPORT_SYMBOL_GPL(pwm_config);
818
819/**
820 * pwm_enable - start a PWM output toggling
821 * @pwm: the PWM device
822 */
823int pwm_enable(struct pwm_device *pwm)
824{
Jay Chokshi57656862011-09-07 12:02:22 -0700825 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700826
827 if (pwm == NULL || IS_ERR(pwm)) {
828 pr_err("Invalid pwm handle\n");
829 return -EINVAL;
830 }
831 if (pwm->chip == NULL) {
832 pr_err("No pwm_chip\n");
833 return -ENODEV;
834 }
835
836 mutex_lock(&pwm->chip->pwm_mutex);
837 if (!pwm->in_use) {
838 pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
839 rc = -EINVAL;
840 } else {
Jay Chokshi57656862011-09-07 12:02:22 -0700841 if (pwm_chip->is_lpg_supported) {
Jay Chokshib0a0fa52012-02-23 16:18:44 -0800842 if (pwm->dtest_mode_supported)
843 pm8xxx_pwm_set_dtest(pwm, 1);
Jay Chokshi90485222012-08-07 20:22:39 -0700844
Jay Chokshi57656862011-09-07 12:02:22 -0700845 pm8xxx_pwm_bank_sel(pwm);
Jay Chokshif3db97a2012-08-08 19:32:15 -0700846 rc = pm8xxx_pwm_bank_enable(pwm, 1);
Jay Chokshi57656862011-09-07 12:02:22 -0700847 pm8xxx_pwm_start(pwm, 1, 0);
Jay Chokshi90485222012-08-07 20:22:39 -0700848
849 /* In PM8038, due to hardware bug, PWM_VALUE register
850 * needs to be written one more time after enabling
851 * PWM mode.
852 */
853 if (pwm->chip->is_pwm_enable_sync_workaround_needed)
854 rc = pm8xxx_lpg_pwm_write(pwm, 3, 4);
855
Jay Chokshi57656862011-09-07 12:02:22 -0700856 } else {
857 pm8xxx_pwm_enable(pwm);
858 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700859 }
860 mutex_unlock(&pwm->chip->pwm_mutex);
861 return rc;
862}
863EXPORT_SYMBOL_GPL(pwm_enable);
864
865/**
866 * pwm_disable - stop a PWM output toggling
867 * @pwm: the PWM device
868 */
869void pwm_disable(struct pwm_device *pwm)
870{
871 if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
872 pr_err("Invalid pwm handle or no pwm_chip\n");
873 return;
874 }
875
876 mutex_lock(&pwm->chip->pwm_mutex);
877 if (pwm->in_use) {
Jay Chokshi57656862011-09-07 12:02:22 -0700878 if (pwm_chip->is_lpg_supported) {
Jay Chokshib0a0fa52012-02-23 16:18:44 -0800879 if (pwm->dtest_mode_supported)
880 pm8xxx_pwm_set_dtest(pwm, 0);
Jay Chokshi57656862011-09-07 12:02:22 -0700881 pm8xxx_pwm_bank_sel(pwm);
882 pm8xxx_pwm_start(pwm, 0, 0);
883 pm8xxx_pwm_bank_enable(pwm, 0);
884 } else {
885 pm8xxx_pwm_disable(pwm);
886 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700887 }
888 mutex_unlock(&pwm->chip->pwm_mutex);
889}
890EXPORT_SYMBOL_GPL(pwm_disable);
891
892/**
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700893 * pm8xxx_pwm_config_period - change PWM period
894 *
895 * @pwm: the PWM device
896 * @pwm_p: period in struct pm8xxx_pwm_period
897 */
898int pm8xxx_pwm_config_period(struct pwm_device *pwm,
899 struct pm8xxx_pwm_period *period)
900{
901 int rc;
902
903 if (pwm == NULL || IS_ERR(pwm) || period == NULL)
904 return -EINVAL;
905 if (pwm->chip == NULL)
906 return -ENODEV;
907
908 mutex_lock(&pwm->chip->pwm_mutex);
909
910 if (!pwm->in_use) {
911 rc = -EINVAL;
912 goto out_unlock;
913 }
914
915 pwm->period.pwm_size = period->pwm_size;
916 pwm->period.clk = period->clk;
917 pwm->period.pre_div = period->pre_div;
918 pwm->period.pre_div_exp = period->pre_div_exp;
919
920 pm8xxx_pwm_save_period(pwm);
Jay Chokshi57656862011-09-07 12:02:22 -0700921
922 if (pwm_chip->is_lpg_supported) {
923 pm8xxx_pwm_bank_sel(pwm);
924 rc = pm8xxx_lpg_pwm_write(pwm, 4, 6);
925 } else {
926 rc = pm8xxx_pwm_write(pwm);
927 }
928
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700929
930out_unlock:
931 mutex_unlock(&pwm->chip->pwm_mutex);
932 return rc;
933}
934EXPORT_SYMBOL(pm8xxx_pwm_config_period);
935
936/**
937 * pm8xxx_pwm_config_pwm_value - change a PWM device configuration
938 * @pwm: the PWM device
939 * @pwm_value: the duty cycle in raw PWM value (< 2^pwm_size)
940 */
941int pm8xxx_pwm_config_pwm_value(struct pwm_device *pwm, int pwm_value)
942{
943 int rc = 0;
944
945 if (pwm == NULL || IS_ERR(pwm))
946 return -EINVAL;
947 if (pwm->chip == NULL)
948 return -ENODEV;
949
950 mutex_lock(&pwm->chip->pwm_mutex);
951
952 if (!pwm->in_use || !pwm->pwm_period) {
953 rc = -EINVAL;
954 goto out_unlock;
955 }
956
957 if (pwm->pwm_value == pwm_value)
958 goto out_unlock;
959
960 pwm->pwm_value = pwm_value;
961
962 pm8xxx_pwm_save_pwm_value(pwm);
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700963
Jay Chokshi57656862011-09-07 12:02:22 -0700964 if (pwm_chip->is_lpg_supported) {
965 pm8xxx_pwm_save(&pwm->pwm_lpg_ctl[1],
966 PM8XXX_PWM_BYPASS_LUT, PM8XXX_PWM_BYPASS_LUT);
967 pm8xxx_pwm_bank_sel(pwm);
968 rc = pm8xxx_lpg_pwm_write(pwm, 1, 6);
969 } else {
970 rc = pm8xxx_pwm_write(pwm);
971 }
Jay Chokshi4acbdd52011-09-16 17:09:44 -0700972
973 if (rc)
974 pr_err("[%d]: pm8xxx_pwm_write: rc=%d\n", pwm->pwm_id, rc);
975
976out_unlock:
977 mutex_unlock(&pwm->chip->pwm_mutex);
978 return rc;
979}
980EXPORT_SYMBOL_GPL(pm8xxx_pwm_config_pwm_value);
981
982/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700983 * pm8xxx_pwm_lut_config - change a PWM device configuration to use LUT
984 * @pwm: the PWM device
985 * @period_us: period in microseconds
986 * @duty_pct: arrary of duty cycles in percent, like 20, 50.
987 * @duty_time_ms: time for each duty cycle in milliseconds
988 * @start_idx: start index in lookup table from 0 to MAX-1
989 * @idx_len: number of index
990 * @pause_lo: pause time in milliseconds at low index
991 * @pause_hi: pause time in milliseconds at high index
992 * @flags: control flags
993 */
994int pm8xxx_pwm_lut_config(struct pwm_device *pwm, int period_us,
995 int duty_pct[], int duty_time_ms, int start_idx,
996 int idx_len, int pause_lo, int pause_hi, int flags)
997{
Willie Ruan719c6762011-08-25 11:03:14 -0700998 struct pm8xxx_pwm_lut lut;
999 struct pm8xxx_pwm_period *period;
Willie Ruan8de2f382011-08-25 11:04:50 -07001000 int len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001001 int rc;
1002
1003 if (pwm == NULL || IS_ERR(pwm) || !idx_len) {
1004 pr_err("Invalid pwm handle or idx_len=0\n");
1005 return -EINVAL;
1006 }
1007 if (duty_pct == NULL && !(flags & PM_PWM_LUT_NO_TABLE)) {
1008 pr_err("Invalid duty_pct with flag\n");
1009 return -EINVAL;
1010 }
1011 if (pwm->chip == NULL) {
1012 pr_err("No pwm_chip\n");
1013 return -ENODEV;
1014 }
Jay Chokshi57656862011-09-07 12:02:22 -07001015
1016 if (pwm->chip->is_lpg_supported == 0) {
1017 pr_err("LPG module isn't supported\n");
1018 return -EINVAL;
1019 }
1020
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001021 if (idx_len >= PM_PWM_LUT_SIZE && start_idx) {
1022 pr_err("Wrong LUT size or index\n");
1023 return -EINVAL;
1024 }
1025 if ((start_idx + idx_len) > PM_PWM_LUT_SIZE) {
1026 pr_err("Exceed LUT limit\n");
1027 return -EINVAL;
1028 }
1029 if ((unsigned)period_us > PM8XXX_PWM_PERIOD_MAX ||
1030 (unsigned)period_us < PM8XXX_PWM_PERIOD_MIN) {
1031 pr_err("Period out of range\n");
1032 return -EINVAL;
1033 }
1034
Willie Ruan719c6762011-08-25 11:03:14 -07001035 period = &pwm->period;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001036 mutex_lock(&pwm->chip->pwm_mutex);
1037
Prasad Sodagudi5af87922013-06-05 17:33:52 +05301038 if (flags & PM_PWM_BANK_HI)
1039 pwm->banks = PM_PWM_BANK_HI;
1040
1041 if (flags & PM_PWM_BANK_LO)
1042 pwm->banks |= PM_PWM_BANK_LO;
1043
1044 /*Enable both banks if banks information is not shared.*/
1045 if (!pwm->banks)
1046 pwm->banks |= (PM_PWM_BANK_LO | PM_PWM_BANK_HI);
1047
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001048 if (!pwm->in_use) {
1049 pr_err("pwm_id: %d: stale handle?\n", pwm->pwm_id);
1050 rc = -EINVAL;
1051 goto out_unlock;
1052 }
1053
Willie Ruan8de2f382011-08-25 11:04:50 -07001054 if (pwm->pwm_period != period_us) {
1055 pm8xxx_pwm_calc_period(period_us, period);
1056 pm8xxx_pwm_save_period(pwm);
1057 pwm->pwm_period = period_us;
1058 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001059
1060 len = (idx_len > PM_PWM_LUT_SIZE) ? PM_PWM_LUT_SIZE : idx_len;
1061
1062 if (flags & PM_PWM_LUT_NO_TABLE)
1063 goto after_table_write;
1064
Willie Ruan8de2f382011-08-25 11:04:50 -07001065 rc = pm8xxx_pwm_change_table(pwm, duty_pct, start_idx, len, 0);
1066 if (rc) {
1067 pr_err("pm8xxx_pwm_change_table: rc=%d\n", rc);
1068 goto out_unlock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001069 }
1070
1071after_table_write:
Willie Ruan719c6762011-08-25 11:03:14 -07001072 lut.lut_duty_ms = duty_time_ms;
1073 lut.lut_lo_index = start_idx;
1074 lut.lut_hi_index = start_idx + len - 1;
1075 lut.lut_pause_lo = pause_lo;
1076 lut.lut_pause_hi = pause_hi;
1077 lut.flags = flags;
1078 pwm->bypass_lut = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001079
Willie Ruan8de2f382011-08-25 11:04:50 -07001080 rc = pm8xxx_pwm_change_lut(pwm, &lut);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001081
1082out_unlock:
1083 mutex_unlock(&pwm->chip->pwm_mutex);
1084 return rc;
1085}
1086EXPORT_SYMBOL_GPL(pm8xxx_pwm_lut_config);
1087
1088/**
1089 * pm8xxx_pwm_lut_enable - control a PWM device to start/stop LUT ramp
1090 * @pwm: the PWM device
1091 * @start: to start (1), or stop (0)
1092 */
1093int pm8xxx_pwm_lut_enable(struct pwm_device *pwm, int start)
1094{
1095 if (pwm == NULL || IS_ERR(pwm)) {
1096 pr_err("Invalid pwm handle\n");
1097 return -EINVAL;
1098 }
1099 if (pwm->chip == NULL) {
1100 pr_err("No pwm_chip\n");
1101 return -ENODEV;
1102 }
Jay Chokshi57656862011-09-07 12:02:22 -07001103 if (pwm->chip->is_lpg_supported == 0) {
1104 pr_err("LPG module isn't supported\n");
1105 return -EINVAL;
1106 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001107
1108 mutex_lock(&pwm->chip->pwm_mutex);
1109 if (start) {
Jay Chokshib0a0fa52012-02-23 16:18:44 -08001110 if (pwm->dtest_mode_supported)
1111 pm8xxx_pwm_set_dtest(pwm, 1);
1112
Jay Chokshif3db97a2012-08-08 19:32:15 -07001113 pm8xxx_pwm_bank_sel(pwm);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001114 pm8xxx_pwm_bank_enable(pwm, 1);
1115
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001116 pm8xxx_pwm_start(pwm, 1, 1);
1117 } else {
Jay Chokshib0a0fa52012-02-23 16:18:44 -08001118 if (pwm->dtest_mode_supported)
1119 pm8xxx_pwm_set_dtest(pwm, 0);
1120
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001121 pm8xxx_pwm_bank_sel(pwm);
1122 pm8xxx_pwm_start(pwm, 0, 0);
1123
1124 pm8xxx_pwm_bank_enable(pwm, 0);
1125 }
1126 mutex_unlock(&pwm->chip->pwm_mutex);
1127 return 0;
1128}
1129EXPORT_SYMBOL_GPL(pm8xxx_pwm_lut_enable);
1130
1131#if defined(CONFIG_DEBUG_FS)
1132
1133struct pm8xxx_pwm_dbg_device;
1134
1135struct pm8xxx_pwm_user {
1136 int pwm_id;
1137 struct pwm_device *pwm;
1138 int period;
1139 int duty_cycle;
1140 int enable;
1141 struct pm8xxx_pwm_dbg_device *dbgdev;
1142};
1143
1144struct pm8xxx_pwm_dbg_device {
1145 struct mutex dbg_mutex;
1146 struct device *dev;
1147 struct dentry *dent;
1148
Jay Chokshi57656862011-09-07 12:02:22 -07001149 struct pm8xxx_pwm_user *user;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001150};
1151
1152static struct pm8xxx_pwm_dbg_device *pmic_dbg_device;
1153
1154static int dbg_pwm_check_period(int period)
1155{
1156 if (period < PM8XXX_PWM_PERIOD_MIN || period > PM8XXX_PWM_PERIOD_MAX) {
1157 pr_err("period is invalid: %d\n", period);
1158 return -EINVAL;
1159 }
1160 return 0;
1161}
1162
1163static int dbg_pwm_check_duty_cycle(int duty_cycle, const char *func_name)
1164{
1165 if (duty_cycle <= 0 || duty_cycle > 100) {
1166 pr_err("%s: duty_cycle is invalid: %d\n",
1167 func_name, duty_cycle);
1168 return -EINVAL;
1169 }
1170 return 0;
1171}
1172
1173static void dbg_pwm_check_handle(struct pm8xxx_pwm_user *puser)
1174{
1175 struct pwm_device *tmp;
1176
1177 if (puser->pwm == NULL) {
1178 tmp = pwm_request(puser->pwm_id, "pwm-dbg");
1179 if (PTR_ERR(puser->pwm)) {
1180 pr_err("pwm_request: err=%ld\n", PTR_ERR(puser->pwm));
1181 puser->pwm = NULL;
Willie Ruan10976ea2011-08-25 11:01:15 -07001182 } else {
1183 pr_debug("[id=%d] pwm_request ok\n", puser->pwm_id);
1184 puser->pwm = tmp;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001185 }
1186 }
1187}
1188
1189static int dbg_pwm_enable_set(void *data, u64 val)
1190{
1191 struct pm8xxx_pwm_user *puser = data;
1192 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1193 int rc;
1194
1195 mutex_lock(&dbgdev->dbg_mutex);
1196 rc = dbg_pwm_check_duty_cycle(puser->duty_cycle, __func__);
1197 if (!rc) {
1198 puser->enable = val;
1199 dbg_pwm_check_handle(puser);
1200 if (puser->pwm) {
1201 if (puser->enable)
1202 pwm_enable(puser->pwm);
1203 else
1204 pwm_disable(puser->pwm);
1205 }
1206 }
1207 mutex_unlock(&dbgdev->dbg_mutex);
1208 return 0;
1209}
1210
1211static int dbg_pwm_enable_get(void *data, u64 *val)
1212{
1213 struct pm8xxx_pwm_user *puser = data;
1214 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1215
1216 mutex_lock(&dbgdev->dbg_mutex);
1217 *val = puser->enable;
1218 mutex_unlock(&dbgdev->dbg_mutex);
1219 return 0;
1220}
1221
1222DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_enable_fops,
1223 dbg_pwm_enable_get, dbg_pwm_enable_set,
1224 "%lld\n");
1225
1226static int dbg_pwm_duty_cycle_set(void *data, u64 val)
1227{
1228 struct pm8xxx_pwm_user *puser = data;
1229 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1230 int rc;
1231
1232 mutex_lock(&dbgdev->dbg_mutex);
1233 rc = dbg_pwm_check_duty_cycle(val, __func__);
1234 if (!rc) {
1235 puser->duty_cycle = val;
1236 dbg_pwm_check_handle(puser);
1237 if (puser->pwm) {
1238 int duty_us;
1239
Willie Ruan10976ea2011-08-25 11:01:15 -07001240 duty_us = puser->duty_cycle * puser->period / 100;
1241 pwm_config(puser->pwm, duty_us, puser->period);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001242 }
1243 }
1244 mutex_unlock(&dbgdev->dbg_mutex);
1245 return 0;
1246}
1247
1248static int dbg_pwm_duty_cycle_get(void *data, u64 *val)
1249{
1250 struct pm8xxx_pwm_user *puser = data;
1251 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1252
1253 mutex_lock(&dbgdev->dbg_mutex);
1254 *val = puser->duty_cycle;
1255 mutex_unlock(&dbgdev->dbg_mutex);
1256 return 0;
1257}
1258
1259DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_duty_cycle_fops,
1260 dbg_pwm_duty_cycle_get, dbg_pwm_duty_cycle_set,
1261 "%lld\n");
1262
1263static int dbg_pwm_period_set(void *data, u64 val)
1264{
1265 struct pm8xxx_pwm_user *puser = data;
1266 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1267 int rc;
1268
1269 mutex_lock(&dbgdev->dbg_mutex);
1270 rc = dbg_pwm_check_period(val);
1271 if (!rc)
1272 puser->period = val;
1273 mutex_unlock(&dbgdev->dbg_mutex);
1274 return 0;
1275}
1276
1277static int dbg_pwm_period_get(void *data, u64 *val)
1278{
1279 struct pm8xxx_pwm_user *puser = data;
1280 struct pm8xxx_pwm_dbg_device *dbgdev = puser->dbgdev;
1281
1282 mutex_lock(&dbgdev->dbg_mutex);
1283 *val = puser->period;
1284 mutex_unlock(&dbgdev->dbg_mutex);
1285 return 0;
1286}
1287
1288DEFINE_SIMPLE_ATTRIBUTE(dbg_pwm_period_fops,
1289 dbg_pwm_period_get, dbg_pwm_period_set, "%lld\n");
1290
1291static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)
1292{
1293 struct pm8xxx_pwm_dbg_device *dbgdev;
1294 struct dentry *dent;
1295 struct dentry *temp;
1296 struct pm8xxx_pwm_user *puser;
1297 int i;
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001298 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001299
1300 if (dev == NULL) {
1301 pr_err("no parent data passed in.\n");
1302 return -EINVAL;
1303 }
1304
1305 dbgdev = kzalloc(sizeof *dbgdev, GFP_KERNEL);
1306 if (dbgdev == NULL) {
1307 pr_err("kzalloc() failed.\n");
1308 return -ENOMEM;
1309 }
1310
Jay Chokshi57656862011-09-07 12:02:22 -07001311 dbgdev->user = kcalloc(pwm_chip->pwm_channels,
1312 sizeof(struct pm8xxx_pwm_user), GFP_KERNEL);
1313 if (dbgdev->user == NULL) {
1314 pr_err("kcalloc() failed.\n");
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001315 rc = -ENOMEM;
1316 goto user_error;
Jay Chokshi57656862011-09-07 12:02:22 -07001317 }
1318
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001319 mutex_init(&dbgdev->dbg_mutex);
1320
1321 dbgdev->dev = dev;
1322
1323 dent = debugfs_create_dir("pm8xxx-pwm-dbg", NULL);
1324 if (dent == NULL || IS_ERR(dent)) {
1325 pr_err("ERR debugfs_create_dir: dent=%p\n", dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001326 rc = -ENOMEM;
1327 goto dir_error;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001328 }
1329
1330 dbgdev->dent = dent;
1331
Jay Chokshi57656862011-09-07 12:02:22 -07001332 for (i = 0; i < pwm_chip->pwm_channels; i++) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001333 char pwm_ch[] = "0";
1334
1335 pwm_ch[0] = '0' + i;
1336 dent = debugfs_create_dir(pwm_ch, dbgdev->dent);
1337 if (dent == NULL || IS_ERR(dent)) {
1338 pr_err("ERR: pwm=%d: dir: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001339 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001340 goto debug_error;
1341 }
1342
1343 puser = &dbgdev->user[i];
1344 puser->dbgdev = dbgdev;
1345 puser->pwm_id = i;
1346 temp = debugfs_create_file("period", S_IRUGO | S_IWUSR,
1347 dent, puser, &dbg_pwm_period_fops);
1348 if (temp == NULL || IS_ERR(temp)) {
1349 pr_err("ERR: pwm=%d: period: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001350 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001351 goto debug_error;
1352 }
1353
1354 temp = debugfs_create_file("duty-cycle", S_IRUGO | S_IWUSR,
1355 dent, puser, &dbg_pwm_duty_cycle_fops);
1356 if (temp == NULL || IS_ERR(temp)) {
1357 pr_err("ERR: pwm=%d: duty-cycle: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001358 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001359 goto debug_error;
1360 }
1361
1362 temp = debugfs_create_file("enable", S_IRUGO | S_IWUSR,
1363 dent, puser, &dbg_pwm_enable_fops);
1364 if (temp == NULL || IS_ERR(temp)) {
1365 pr_err("ERR: pwm=%d: enable: dent=%p\n", i, dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001366 rc = -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001367 goto debug_error;
1368 }
1369 }
1370
1371 pmic_dbg_device = dbgdev;
1372
1373 return 0;
1374
1375debug_error:
1376 debugfs_remove_recursive(dbgdev->dent);
Jay Chokshi00c07cc2011-10-27 15:50:03 -07001377dir_error:
1378 kfree(dbgdev->user);
1379user_error:
1380 kfree(dbgdev);
1381 return rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001382}
1383
1384static int __devexit pm8xxx_pwm_dbg_remove(void)
1385{
1386 if (pmic_dbg_device) {
Jay Chokshi57656862011-09-07 12:02:22 -07001387 kfree(pmic_dbg_device->user);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001388 debugfs_remove_recursive(pmic_dbg_device->dent);
1389 kfree(pmic_dbg_device);
1390 }
1391 return 0;
1392}
1393
1394#else
1395
1396static int __devinit pm8xxx_pwm_dbg_probe(struct device *dev)
1397{
1398 return 0;
1399}
1400
1401static int __devexit pm8xxx_pwm_dbg_remove(void)
1402{
1403 return 0;
1404}
1405
1406#endif
1407
1408static int __devinit pm8xxx_pwm_probe(struct platform_device *pdev)
1409{
Jay Chokshib0a0fa52012-02-23 16:18:44 -08001410 const struct pm8xxx_pwm_platform_data *pdata = pdev->dev.platform_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001411 struct pm8xxx_pwm_chip *chip;
Jay Chokshid9e4bc22012-03-02 15:13:44 -08001412 int i, dtest_channel;
Jay Chokshi57656862011-09-07 12:02:22 -07001413 enum pm8xxx_version version;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001414
1415 chip = kzalloc(sizeof *chip, GFP_KERNEL);
1416 if (chip == NULL) {
1417 pr_err("kzalloc() failed.\n");
1418 return -ENOMEM;
1419 }
1420
Jay Chokshid9e4bc22012-03-02 15:13:44 -08001421 if (pdata != NULL)
1422 dtest_channel = pdata->dtest_channel;
1423 else
1424 dtest_channel = -1;
1425
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001426 mutex_init(&chip->pwm_mutex);
1427
1428 chip->dev = &pdev->dev;
1429 pwm_chip = chip;
Jay Chokshi57656862011-09-07 12:02:22 -07001430
1431 version = pm8xxx_get_version(chip->dev->parent);
1432
1433 if (version == PM8XXX_VERSION_8921 ||
David Collins999480d2011-11-16 08:52:30 -08001434 version == PM8XXX_VERSION_8058 ||
Jay Chokshi0c220cd2011-12-09 17:18:20 -08001435 version == PM8XXX_VERSION_8922 ||
1436 version == PM8XXX_VERSION_8038) {
Jay Chokshi57656862011-09-07 12:02:22 -07001437 chip->is_lpg_supported = 1;
1438 }
Jay Chokshi90485222012-08-07 20:22:39 -07001439
1440 if (version == PM8XXX_VERSION_8038)
1441 chip->is_pwm_enable_sync_workaround_needed = 1;
1442 else
1443 chip->is_pwm_enable_sync_workaround_needed = 0;
1444
Jay Chokshi57656862011-09-07 12:02:22 -07001445 if (chip->is_lpg_supported) {
Jay Chokshi0c220cd2011-12-09 17:18:20 -08001446 if (version == PM8XXX_VERSION_8922 ||
1447 version == PM8XXX_VERSION_8038) {
1448 for (i = 0; i < NUM_CLOCKS; i++)
1449 pt_t[0][i] /= PRE_DIVIDE_2;
1450 chip->pwm_channels = PM8XXX_LPG_V1_PWM_CHANNELS;
1451 } else {
1452 chip->pwm_channels = PM8XXX_LPG_V0_PWM_CHANNELS;
1453 }
Jay Chokshi57656862011-09-07 12:02:22 -07001454 chip->pwm_total_pre_divs = NUM_LPG_PRE_DIVIDE;
1455 } else {
1456 chip->pwm_channels = PM8XXX_PWM_CHANNELS;
1457 chip->pwm_total_pre_divs = NUM_PWM_PRE_DIVIDE;
1458 }
1459
1460 chip->pwm_dev = kcalloc(chip->pwm_channels, sizeof(struct pwm_device),
1461 GFP_KERNEL);
1462 if (chip->pwm_dev == NULL) {
1463 pr_err("kcalloc() failed.\n");
1464 mutex_destroy(&chip->pwm_mutex);
1465 kfree(chip);
1466 return -ENOMEM;
1467 }
1468
1469 for (i = 0; i < chip->pwm_channels; i++) {
1470 chip->pwm_dev[i].pwm_id = i;
1471 chip->pwm_dev[i].chip = chip;
Jay Chokshid9e4bc22012-03-02 15:13:44 -08001472 if (i == dtest_channel)
Jay Chokshib0a0fa52012-02-23 16:18:44 -08001473 chip->pwm_dev[i].dtest_mode_supported = 1;
Jay Chokshi57656862011-09-07 12:02:22 -07001474 }
1475
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001476 platform_set_drvdata(pdev, chip);
1477
1478 if (pm8xxx_pwm_dbg_probe(&pdev->dev) < 0)
1479 pr_err("could not set up debugfs\n");
1480
1481 pr_notice("OK\n");
1482 return 0;
1483}
1484
1485static int __devexit pm8xxx_pwm_remove(struct platform_device *pdev)
1486{
1487 struct pm8xxx_pwm_chip *chip = dev_get_drvdata(pdev->dev.parent);
1488
1489 pm8xxx_pwm_dbg_remove();
Jay Chokshi57656862011-09-07 12:02:22 -07001490 kfree(chip->pwm_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001491 mutex_destroy(&chip->pwm_mutex);
1492 platform_set_drvdata(pdev, NULL);
1493 kfree(chip);
1494 return 0;
1495}
1496
1497static struct platform_driver pm8xxx_pwm_driver = {
1498 .probe = pm8xxx_pwm_probe,
1499 .remove = __devexit_p(pm8xxx_pwm_remove),
1500 .driver = {
1501 .name = PM8XXX_PWM_DEV_NAME,
1502 .owner = THIS_MODULE,
1503 },
1504};
1505
1506static int __init pm8xxx_pwm_init(void)
1507{
1508 return platform_driver_register(&pm8xxx_pwm_driver);
1509}
1510
1511static void __exit pm8xxx_pwm_exit(void)
1512{
1513 platform_driver_unregister(&pm8xxx_pwm_driver);
1514}
1515
1516subsys_initcall(pm8xxx_pwm_init);
1517module_exit(pm8xxx_pwm_exit);
1518
1519MODULE_LICENSE("GPL v2");
1520MODULE_DESCRIPTION("PM8XXX PWM driver");
1521MODULE_VERSION("1.0");
1522MODULE_ALIAS("platform:" PM8XXX_PWM_DEV_NAME);