2 * Driver for TWL4030/6030 Pulse Width Modulator used as LED driver
4 * Copyright (C) 2012 Texas Instruments
5 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
7 * This driver is a complete rewrite of the former pwm-twl6030.c authorded by:
8 * Hemanth V <hemanthv@ti.com>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License version 2 as published by
12 * the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <linux/module.h>
24 #include <linux/platform_device.h>
25 #include <linux/pwm.h>
26 #include <linux/i2c/twl.h>
27 #include <linux/slab.h>
30 * This driver handles the PWM driven LED terminals of TWL4030 and TWL6030.
31 * To generate the signal on TWL4030:
34 * TWL6030 has one LED pin with dedicated LEDPWM
37 #define TWL4030_LED_MAX 0x7f
38 #define TWL6030_LED_MAX 0xff
40 /* Registers, bits and macro for TWL4030 */
41 #define TWL4030_LEDEN_REG 0x00
42 #define TWL4030_PWMA_REG 0x01
44 #define TWL4030_LEDXON (1 << 0)
45 #define TWL4030_LEDXPWM (1 << 4)
46 #define TWL4030_LED_PINS (TWL4030_LEDXON | TWL4030_LEDXPWM)
47 #define TWL4030_LED_TOGGLE(led, x) ((x) << (led))
49 /* Register, bits and macro for TWL6030 */
50 #define TWL6030_LED_PWM_CTRL1 0xf4
51 #define TWL6030_LED_PWM_CTRL2 0xf5
53 #define TWL6040_LED_MODE_HW 0x00
54 #define TWL6040_LED_MODE_ON 0x01
55 #define TWL6040_LED_MODE_OFF 0x02
56 #define TWL6040_LED_MODE_MASK 0x03
58 struct twl_pwmled_chip
{
63 static inline struct twl_pwmled_chip
*to_twl(struct pwm_chip
*chip
)
65 return container_of(chip
, struct twl_pwmled_chip
, chip
);
68 static int twl4030_pwmled_config(struct pwm_chip
*chip
, struct pwm_device
*pwm
,
69 int duty_ns
, int period_ns
)
71 int duty_cycle
= DIV_ROUND_UP(duty_ns
* TWL4030_LED_MAX
, period_ns
) + 1;
72 u8 pwm_config
[2] = { 1, 0 };
76 * To configure the duty period:
77 * On-cycle is set to 1 (the minimum allowed value)
78 * The off time of 0 is not configurable, so the mapping is:
82 * 126 - > off cycle 127,
84 * When on cycle == off cycle the PWM will be always on
88 else if (duty_cycle
> TWL4030_LED_MAX
)
91 base
= pwm
->hwpwm
* 2 + TWL4030_PWMA_REG
;
93 pwm_config
[1] = duty_cycle
;
95 ret
= twl_i2c_write(TWL4030_MODULE_LED
, pwm_config
, base
, 2);
97 dev_err(chip
->dev
, "%s: Failed to configure PWM\n", pwm
->label
);
102 static int twl4030_pwmled_enable(struct pwm_chip
*chip
, struct pwm_device
*pwm
)
104 struct twl_pwmled_chip
*twl
= to_twl(chip
);
108 mutex_lock(&twl
->mutex
);
109 ret
= twl_i2c_read_u8(TWL4030_MODULE_LED
, &val
, TWL4030_LEDEN_REG
);
111 dev_err(chip
->dev
, "%s: Failed to read LEDEN\n", pwm
->label
);
115 val
|= TWL4030_LED_TOGGLE(pwm
->hwpwm
, TWL4030_LED_PINS
);
117 ret
= twl_i2c_write_u8(TWL4030_MODULE_LED
, val
, TWL4030_LEDEN_REG
);
119 dev_err(chip
->dev
, "%s: Failed to enable PWM\n", pwm
->label
);
122 mutex_unlock(&twl
->mutex
);
126 static void twl4030_pwmled_disable(struct pwm_chip
*chip
,
127 struct pwm_device
*pwm
)
129 struct twl_pwmled_chip
*twl
= to_twl(chip
);
133 mutex_lock(&twl
->mutex
);
134 ret
= twl_i2c_read_u8(TWL4030_MODULE_LED
, &val
, TWL4030_LEDEN_REG
);
136 dev_err(chip
->dev
, "%s: Failed to read LEDEN\n", pwm
->label
);
140 val
&= ~TWL4030_LED_TOGGLE(pwm
->hwpwm
, TWL4030_LED_PINS
);
142 ret
= twl_i2c_write_u8(TWL4030_MODULE_LED
, val
, TWL4030_LEDEN_REG
);
144 dev_err(chip
->dev
, "%s: Failed to disable PWM\n", pwm
->label
);
147 mutex_unlock(&twl
->mutex
);
150 static int twl6030_pwmled_config(struct pwm_chip
*chip
, struct pwm_device
*pwm
,
151 int duty_ns
, int period_ns
)
153 int duty_cycle
= (duty_ns
* TWL6030_LED_MAX
) / period_ns
;
157 on_time
= duty_cycle
& 0xff;
159 ret
= twl_i2c_write_u8(TWL6030_MODULE_ID1
, on_time
,
160 TWL6030_LED_PWM_CTRL1
);
162 dev_err(chip
->dev
, "%s: Failed to configure PWM\n", pwm
->label
);
167 static int twl6030_pwmled_enable(struct pwm_chip
*chip
, struct pwm_device
*pwm
)
169 struct twl_pwmled_chip
*twl
= to_twl(chip
);
173 mutex_lock(&twl
->mutex
);
174 ret
= twl_i2c_read_u8(TWL6030_MODULE_ID1
, &val
, TWL6030_LED_PWM_CTRL2
);
176 dev_err(chip
->dev
, "%s: Failed to read PWM_CTRL2\n",
181 val
&= ~TWL6040_LED_MODE_MASK
;
182 val
|= TWL6040_LED_MODE_ON
;
184 ret
= twl_i2c_write_u8(TWL6030_MODULE_ID1
, val
, TWL6030_LED_PWM_CTRL2
);
186 dev_err(chip
->dev
, "%s: Failed to enable PWM\n", pwm
->label
);
189 mutex_unlock(&twl
->mutex
);
193 static void twl6030_pwmled_disable(struct pwm_chip
*chip
,
194 struct pwm_device
*pwm
)
196 struct twl_pwmled_chip
*twl
= to_twl(chip
);
200 mutex_lock(&twl
->mutex
);
201 ret
= twl_i2c_read_u8(TWL6030_MODULE_ID1
, &val
, TWL6030_LED_PWM_CTRL2
);
203 dev_err(chip
->dev
, "%s: Failed to read PWM_CTRL2\n",
208 val
&= ~TWL6040_LED_MODE_MASK
;
209 val
|= TWL6040_LED_MODE_OFF
;
211 ret
= twl_i2c_write_u8(TWL6030_MODULE_ID1
, val
, TWL6030_LED_PWM_CTRL2
);
213 dev_err(chip
->dev
, "%s: Failed to disable PWM\n", pwm
->label
);
216 mutex_unlock(&twl
->mutex
);
219 static int twl6030_pwmled_request(struct pwm_chip
*chip
, struct pwm_device
*pwm
)
221 struct twl_pwmled_chip
*twl
= to_twl(chip
);
225 mutex_lock(&twl
->mutex
);
226 ret
= twl_i2c_read_u8(TWL6030_MODULE_ID1
, &val
, TWL6030_LED_PWM_CTRL2
);
228 dev_err(chip
->dev
, "%s: Failed to read PWM_CTRL2\n",
233 val
&= ~TWL6040_LED_MODE_MASK
;
234 val
|= TWL6040_LED_MODE_OFF
;
236 ret
= twl_i2c_write_u8(TWL6030_MODULE_ID1
, val
, TWL6030_LED_PWM_CTRL2
);
238 dev_err(chip
->dev
, "%s: Failed to request PWM\n", pwm
->label
);
241 mutex_unlock(&twl
->mutex
);
245 static void twl6030_pwmled_free(struct pwm_chip
*chip
, struct pwm_device
*pwm
)
247 struct twl_pwmled_chip
*twl
= to_twl(chip
);
251 mutex_lock(&twl
->mutex
);
252 ret
= twl_i2c_read_u8(TWL6030_MODULE_ID1
, &val
, TWL6030_LED_PWM_CTRL2
);
254 dev_err(chip
->dev
, "%s: Failed to read PWM_CTRL2\n",
259 val
&= ~TWL6040_LED_MODE_MASK
;
260 val
|= TWL6040_LED_MODE_HW
;
262 ret
= twl_i2c_write_u8(TWL6030_MODULE_ID1
, val
, TWL6030_LED_PWM_CTRL2
);
264 dev_err(chip
->dev
, "%s: Failed to free PWM\n", pwm
->label
);
267 mutex_unlock(&twl
->mutex
);
270 static const struct pwm_ops twl4030_pwmled_ops
= {
271 .enable
= twl4030_pwmled_enable
,
272 .disable
= twl4030_pwmled_disable
,
273 .config
= twl4030_pwmled_config
,
274 .owner
= THIS_MODULE
,
277 static const struct pwm_ops twl6030_pwmled_ops
= {
278 .enable
= twl6030_pwmled_enable
,
279 .disable
= twl6030_pwmled_disable
,
280 .config
= twl6030_pwmled_config
,
281 .request
= twl6030_pwmled_request
,
282 .free
= twl6030_pwmled_free
,
283 .owner
= THIS_MODULE
,
286 static int twl_pwmled_probe(struct platform_device
*pdev
)
288 struct twl_pwmled_chip
*twl
;
291 twl
= devm_kzalloc(&pdev
->dev
, sizeof(*twl
), GFP_KERNEL
);
295 if (twl_class_is_4030()) {
296 twl
->chip
.ops
= &twl4030_pwmled_ops
;
299 twl
->chip
.ops
= &twl6030_pwmled_ops
;
303 twl
->chip
.dev
= &pdev
->dev
;
305 twl
->chip
.can_sleep
= true;
307 mutex_init(&twl
->mutex
);
309 ret
= pwmchip_add(&twl
->chip
);
313 platform_set_drvdata(pdev
, twl
);
318 static int twl_pwmled_remove(struct platform_device
*pdev
)
320 struct twl_pwmled_chip
*twl
= platform_get_drvdata(pdev
);
322 return pwmchip_remove(&twl
->chip
);
326 static const struct of_device_id twl_pwmled_of_match
[] = {
327 { .compatible
= "ti,twl4030-pwmled" },
328 { .compatible
= "ti,twl6030-pwmled" },
331 MODULE_DEVICE_TABLE(of
, twl_pwmled_of_match
);
334 static struct platform_driver twl_pwmled_driver
= {
336 .name
= "twl-pwmled",
337 .of_match_table
= of_match_ptr(twl_pwmled_of_match
),
339 .probe
= twl_pwmled_probe
,
340 .remove
= twl_pwmled_remove
,
342 module_platform_driver(twl_pwmled_driver
);
344 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
345 MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030 LED outputs");
346 MODULE_ALIAS("platform:twl-pwmled");
347 MODULE_LICENSE("GPL");