1 // SPDX-License-Identifier: GPL-2.0-only
3 * Driver for TWL4030/6030 Pulse Width Modulator used as LED driver
5 * Copyright (C) 2012 Texas Instruments
6 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
8 * This driver is a complete rewrite of the former pwm-twl6030.c authorded by:
9 * Hemanth V <hemanthv@ti.com>
12 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/pwm.h>
16 #include <linux/mfd/twl.h>
17 #include <linux/slab.h>
20 * This driver handles the PWM driven LED terminals of TWL4030 and TWL6030.
21 * To generate the signal on TWL4030:
24 * TWL6030 has one LED pin with dedicated LEDPWM
27 #define TWL4030_LED_MAX 0x7f
28 #define TWL6030_LED_MAX 0xff
30 /* Registers, bits and macro for TWL4030 */
31 #define TWL4030_LEDEN_REG 0x00
32 #define TWL4030_PWMA_REG 0x01
34 #define TWL4030_LEDXON (1 << 0)
35 #define TWL4030_LEDXPWM (1 << 4)
36 #define TWL4030_LED_PINS (TWL4030_LEDXON | TWL4030_LEDXPWM)
37 #define TWL4030_LED_TOGGLE(led, x) ((x) << (led))
39 /* Register, bits and macro for TWL6030 */
40 #define TWL6030_LED_PWM_CTRL1 0xf4
41 #define TWL6030_LED_PWM_CTRL2 0xf5
43 #define TWL6040_LED_MODE_HW 0x00
44 #define TWL6040_LED_MODE_ON 0x01
45 #define TWL6040_LED_MODE_OFF 0x02
46 #define TWL6040_LED_MODE_MASK 0x03
48 struct twl_pwmled_chip
{
53 static inline struct twl_pwmled_chip
*to_twl(struct pwm_chip
*chip
)
55 return container_of(chip
, struct twl_pwmled_chip
, chip
);
58 static int twl4030_pwmled_config(struct pwm_chip
*chip
, struct pwm_device
*pwm
,
59 int duty_ns
, int period_ns
)
61 int duty_cycle
= DIV_ROUND_UP(duty_ns
* TWL4030_LED_MAX
, period_ns
) + 1;
62 u8 pwm_config
[2] = { 1, 0 };
66 * To configure the duty period:
67 * On-cycle is set to 1 (the minimum allowed value)
68 * The off time of 0 is not configurable, so the mapping is:
72 * 126 - > off cycle 127,
74 * When on cycle == off cycle the PWM will be always on
78 else if (duty_cycle
> TWL4030_LED_MAX
)
81 base
= pwm
->hwpwm
* 2 + TWL4030_PWMA_REG
;
83 pwm_config
[1] = duty_cycle
;
85 ret
= twl_i2c_write(TWL4030_MODULE_LED
, pwm_config
, base
, 2);
87 dev_err(chip
->dev
, "%s: Failed to configure PWM\n", pwm
->label
);
92 static int twl4030_pwmled_enable(struct pwm_chip
*chip
, struct pwm_device
*pwm
)
94 struct twl_pwmled_chip
*twl
= to_twl(chip
);
98 mutex_lock(&twl
->mutex
);
99 ret
= twl_i2c_read_u8(TWL4030_MODULE_LED
, &val
, TWL4030_LEDEN_REG
);
101 dev_err(chip
->dev
, "%s: Failed to read LEDEN\n", pwm
->label
);
105 val
|= TWL4030_LED_TOGGLE(pwm
->hwpwm
, TWL4030_LED_PINS
);
107 ret
= twl_i2c_write_u8(TWL4030_MODULE_LED
, val
, TWL4030_LEDEN_REG
);
109 dev_err(chip
->dev
, "%s: Failed to enable PWM\n", pwm
->label
);
112 mutex_unlock(&twl
->mutex
);
116 static void twl4030_pwmled_disable(struct pwm_chip
*chip
,
117 struct pwm_device
*pwm
)
119 struct twl_pwmled_chip
*twl
= to_twl(chip
);
123 mutex_lock(&twl
->mutex
);
124 ret
= twl_i2c_read_u8(TWL4030_MODULE_LED
, &val
, TWL4030_LEDEN_REG
);
126 dev_err(chip
->dev
, "%s: Failed to read LEDEN\n", pwm
->label
);
130 val
&= ~TWL4030_LED_TOGGLE(pwm
->hwpwm
, TWL4030_LED_PINS
);
132 ret
= twl_i2c_write_u8(TWL4030_MODULE_LED
, val
, TWL4030_LEDEN_REG
);
134 dev_err(chip
->dev
, "%s: Failed to disable PWM\n", pwm
->label
);
137 mutex_unlock(&twl
->mutex
);
140 static int twl6030_pwmled_config(struct pwm_chip
*chip
, struct pwm_device
*pwm
,
141 int duty_ns
, int period_ns
)
143 int duty_cycle
= (duty_ns
* TWL6030_LED_MAX
) / period_ns
;
147 on_time
= duty_cycle
& 0xff;
149 ret
= twl_i2c_write_u8(TWL6030_MODULE_ID1
, on_time
,
150 TWL6030_LED_PWM_CTRL1
);
152 dev_err(chip
->dev
, "%s: Failed to configure PWM\n", pwm
->label
);
157 static int twl6030_pwmled_enable(struct pwm_chip
*chip
, struct pwm_device
*pwm
)
159 struct twl_pwmled_chip
*twl
= to_twl(chip
);
163 mutex_lock(&twl
->mutex
);
164 ret
= twl_i2c_read_u8(TWL6030_MODULE_ID1
, &val
, TWL6030_LED_PWM_CTRL2
);
166 dev_err(chip
->dev
, "%s: Failed to read PWM_CTRL2\n",
171 val
&= ~TWL6040_LED_MODE_MASK
;
172 val
|= TWL6040_LED_MODE_ON
;
174 ret
= twl_i2c_write_u8(TWL6030_MODULE_ID1
, val
, TWL6030_LED_PWM_CTRL2
);
176 dev_err(chip
->dev
, "%s: Failed to enable PWM\n", pwm
->label
);
179 mutex_unlock(&twl
->mutex
);
183 static void twl6030_pwmled_disable(struct pwm_chip
*chip
,
184 struct pwm_device
*pwm
)
186 struct twl_pwmled_chip
*twl
= to_twl(chip
);
190 mutex_lock(&twl
->mutex
);
191 ret
= twl_i2c_read_u8(TWL6030_MODULE_ID1
, &val
, TWL6030_LED_PWM_CTRL2
);
193 dev_err(chip
->dev
, "%s: Failed to read PWM_CTRL2\n",
198 val
&= ~TWL6040_LED_MODE_MASK
;
199 val
|= TWL6040_LED_MODE_OFF
;
201 ret
= twl_i2c_write_u8(TWL6030_MODULE_ID1
, val
, TWL6030_LED_PWM_CTRL2
);
203 dev_err(chip
->dev
, "%s: Failed to disable PWM\n", pwm
->label
);
206 mutex_unlock(&twl
->mutex
);
209 static int twl6030_pwmled_request(struct pwm_chip
*chip
, struct pwm_device
*pwm
)
211 struct twl_pwmled_chip
*twl
= to_twl(chip
);
215 mutex_lock(&twl
->mutex
);
216 ret
= twl_i2c_read_u8(TWL6030_MODULE_ID1
, &val
, TWL6030_LED_PWM_CTRL2
);
218 dev_err(chip
->dev
, "%s: Failed to read PWM_CTRL2\n",
223 val
&= ~TWL6040_LED_MODE_MASK
;
224 val
|= TWL6040_LED_MODE_OFF
;
226 ret
= twl_i2c_write_u8(TWL6030_MODULE_ID1
, val
, TWL6030_LED_PWM_CTRL2
);
228 dev_err(chip
->dev
, "%s: Failed to request PWM\n", pwm
->label
);
231 mutex_unlock(&twl
->mutex
);
235 static void twl6030_pwmled_free(struct pwm_chip
*chip
, struct pwm_device
*pwm
)
237 struct twl_pwmled_chip
*twl
= to_twl(chip
);
241 mutex_lock(&twl
->mutex
);
242 ret
= twl_i2c_read_u8(TWL6030_MODULE_ID1
, &val
, TWL6030_LED_PWM_CTRL2
);
244 dev_err(chip
->dev
, "%s: Failed to read PWM_CTRL2\n",
249 val
&= ~TWL6040_LED_MODE_MASK
;
250 val
|= TWL6040_LED_MODE_HW
;
252 ret
= twl_i2c_write_u8(TWL6030_MODULE_ID1
, val
, TWL6030_LED_PWM_CTRL2
);
254 dev_err(chip
->dev
, "%s: Failed to free PWM\n", pwm
->label
);
257 mutex_unlock(&twl
->mutex
);
260 static const struct pwm_ops twl4030_pwmled_ops
= {
261 .enable
= twl4030_pwmled_enable
,
262 .disable
= twl4030_pwmled_disable
,
263 .config
= twl4030_pwmled_config
,
264 .owner
= THIS_MODULE
,
267 static const struct pwm_ops twl6030_pwmled_ops
= {
268 .enable
= twl6030_pwmled_enable
,
269 .disable
= twl6030_pwmled_disable
,
270 .config
= twl6030_pwmled_config
,
271 .request
= twl6030_pwmled_request
,
272 .free
= twl6030_pwmled_free
,
273 .owner
= THIS_MODULE
,
276 static int twl_pwmled_probe(struct platform_device
*pdev
)
278 struct twl_pwmled_chip
*twl
;
281 twl
= devm_kzalloc(&pdev
->dev
, sizeof(*twl
), GFP_KERNEL
);
285 if (twl_class_is_4030()) {
286 twl
->chip
.ops
= &twl4030_pwmled_ops
;
289 twl
->chip
.ops
= &twl6030_pwmled_ops
;
293 twl
->chip
.dev
= &pdev
->dev
;
296 mutex_init(&twl
->mutex
);
298 ret
= pwmchip_add(&twl
->chip
);
302 platform_set_drvdata(pdev
, twl
);
307 static int twl_pwmled_remove(struct platform_device
*pdev
)
309 struct twl_pwmled_chip
*twl
= platform_get_drvdata(pdev
);
311 return pwmchip_remove(&twl
->chip
);
315 static const struct of_device_id twl_pwmled_of_match
[] = {
316 { .compatible
= "ti,twl4030-pwmled" },
317 { .compatible
= "ti,twl6030-pwmled" },
320 MODULE_DEVICE_TABLE(of
, twl_pwmled_of_match
);
323 static struct platform_driver twl_pwmled_driver
= {
325 .name
= "twl-pwmled",
326 .of_match_table
= of_match_ptr(twl_pwmled_of_match
),
328 .probe
= twl_pwmled_probe
,
329 .remove
= twl_pwmled_remove
,
331 module_platform_driver(twl_pwmled_driver
);
333 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
334 MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030 LED outputs");
335 MODULE_ALIAS("platform:twl-pwmled");
336 MODULE_LICENSE("GPL");