2 * LEDs driver for Freescale MC13783/MC13892
4 * Copyright (C) 2010 Philippe Rétornaz
6 * Based on leds-da903x:
7 * Copyright (C) 2008 Compulab, Ltd.
8 * Mike Rapoport <mike@compulab.co.il>
10 * Copyright (C) 2006-2008 Marvell International Ltd.
11 * Eric Miao <eric.miao@marvell.com>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2 as
15 * published by the Free Software Foundation.
18 #include <linux/module.h>
19 #include <linux/kernel.h>
20 #include <linux/init.h>
21 #include <linux/platform_device.h>
22 #include <linux/leds.h>
23 #include <linux/workqueue.h>
24 #include <linux/mfd/mc13xxx.h>
26 #define MC13XXX_REG_LED_CONTROL(x) (51 + (x))
28 struct mc13xxx_led_devtype
{
35 struct led_classdev cdev
;
36 struct work_struct work
;
37 struct mc13xxx
*master
;
38 enum led_brightness new_brightness
;
43 struct mc13xxx_led_devtype
*devtype
;
45 struct mc13xxx_led led
[0];
48 static void mc13xxx_led_work(struct work_struct
*work
)
50 struct mc13xxx_led
*led
= container_of(work
, struct mc13xxx_led
, work
);
51 int reg
, mask
, value
, bank
, off
, shift
;
55 reg
= MC13XXX_REG_LED_CONTROL(2);
58 value
= led
->new_brightness
>> 4;
61 reg
= MC13XXX_REG_LED_CONTROL(2);
64 value
= led
->new_brightness
>> 4;
67 reg
= MC13XXX_REG_LED_CONTROL(2);
70 value
= led
->new_brightness
>> 4;
81 off
= led
->id
- MC13783_LED_R1
;
83 reg
= MC13XXX_REG_LED_CONTROL(3) + bank
;
84 shift
= (off
- bank
* 3) * 5 + 6;
85 value
= led
->new_brightness
>> 3;
89 reg
= MC13XXX_REG_LED_CONTROL(0);
92 value
= led
->new_brightness
>> 2;
95 reg
= MC13XXX_REG_LED_CONTROL(0);
98 value
= led
->new_brightness
>> 2;
101 reg
= MC13XXX_REG_LED_CONTROL(1);
104 value
= led
->new_brightness
>> 2;
109 off
= led
->id
- MC13892_LED_R
;
111 reg
= MC13XXX_REG_LED_CONTROL(2) + bank
;
112 shift
= (off
- bank
* 2) * 12 + 3;
113 value
= led
->new_brightness
>> 2;
120 mc13xxx_lock(led
->master
);
121 mc13xxx_reg_rmw(led
->master
, reg
, mask
<< shift
, value
<< shift
);
122 mc13xxx_unlock(led
->master
);
125 static void mc13xxx_led_set(struct led_classdev
*led_cdev
,
126 enum led_brightness value
)
128 struct mc13xxx_led
*led
=
129 container_of(led_cdev
, struct mc13xxx_led
, cdev
);
131 led
->new_brightness
= value
;
132 schedule_work(&led
->work
);
135 static int __init
mc13xxx_led_setup(struct mc13xxx_led
*led
, int max_current
)
137 int shift
, mask
, reg
, ret
, bank
;
141 reg
= MC13XXX_REG_LED_CONTROL(2);
146 reg
= MC13XXX_REG_LED_CONTROL(2);
151 reg
= MC13XXX_REG_LED_CONTROL(2);
164 bank
= (led
->id
- MC13783_LED_R1
) / 3;
165 reg
= MC13XXX_REG_LED_CONTROL(3) + bank
;
166 shift
= ((led
->id
- MC13783_LED_R1
) - bank
* 3) * 2;
170 reg
= MC13XXX_REG_LED_CONTROL(0);
175 reg
= MC13XXX_REG_LED_CONTROL(0);
180 reg
= MC13XXX_REG_LED_CONTROL(1);
187 bank
= (led
->id
- MC13892_LED_R
) / 2;
188 reg
= MC13XXX_REG_LED_CONTROL(2) + bank
;
189 shift
= ((led
->id
- MC13892_LED_R
) - bank
* 2) * 12 + 9;
196 mc13xxx_lock(led
->master
);
197 ret
= mc13xxx_reg_rmw(led
->master
, reg
, mask
<< shift
,
198 max_current
<< shift
);
199 mc13xxx_unlock(led
->master
);
204 static int __init
mc13xxx_led_probe(struct platform_device
*pdev
)
206 struct mc13xxx_leds_platform_data
*pdata
= dev_get_platdata(&pdev
->dev
);
207 struct mc13xxx
*mcdev
= dev_get_drvdata(pdev
->dev
.parent
);
208 struct mc13xxx_led_devtype
*devtype
=
209 (struct mc13xxx_led_devtype
*)pdev
->id_entry
->driver_data
;
210 struct mc13xxx_leds
*leds
;
211 int i
, id
, num_leds
, ret
= -ENODATA
;
212 u32 reg
, init_led
= 0;
215 dev_err(&pdev
->dev
, "Missing platform data\n");
219 num_leds
= pdata
->num_leds
;
221 if ((num_leds
< 1) ||
222 (num_leds
> (devtype
->led_max
- devtype
->led_min
+ 1))) {
223 dev_err(&pdev
->dev
, "Invalid LED count %d\n", num_leds
);
227 leds
= devm_kzalloc(&pdev
->dev
, num_leds
* sizeof(struct mc13xxx_led
) +
228 sizeof(struct mc13xxx_leds
), GFP_KERNEL
);
232 leds
->devtype
= devtype
;
233 leds
->num_leds
= num_leds
;
234 platform_set_drvdata(pdev
, leds
);
237 for (i
= 0; i
< devtype
->num_regs
; i
++) {
238 reg
= pdata
->led_control
[i
];
239 WARN_ON(reg
>= (1 << 24));
240 ret
= mc13xxx_reg_write(mcdev
, MC13XXX_REG_LED_CONTROL(i
), reg
);
244 mc13xxx_unlock(mcdev
);
247 dev_err(&pdev
->dev
, "Unable to init LED driver\n");
251 for (i
= 0; i
< num_leds
; i
++) {
252 const char *name
, *trig
;
257 id
= pdata
->led
[i
].id
;
258 name
= pdata
->led
[i
].name
;
259 trig
= pdata
->led
[i
].default_trigger
;
260 max_current
= pdata
->led
[i
].max_current
;
262 if ((id
> devtype
->led_max
) || (id
< devtype
->led_min
)) {
263 dev_err(&pdev
->dev
, "Invalid ID %i\n", id
);
267 if (init_led
& (1 << id
)) {
269 "LED %i already initialized\n", id
);
274 leds
->led
[i
].id
= id
;
275 leds
->led
[i
].master
= mcdev
;
276 leds
->led
[i
].cdev
.name
= name
;
277 leds
->led
[i
].cdev
.default_trigger
= trig
;
278 leds
->led
[i
].cdev
.brightness_set
= mc13xxx_led_set
;
279 leds
->led
[i
].cdev
.brightness
= LED_OFF
;
281 INIT_WORK(&leds
->led
[i
].work
, mc13xxx_led_work
);
283 ret
= mc13xxx_led_setup(&leds
->led
[i
], max_current
);
285 dev_err(&pdev
->dev
, "Unable to setup LED %i\n", id
);
288 ret
= led_classdev_register(pdev
->dev
.parent
,
291 dev_err(&pdev
->dev
, "Failed to register LED %i\n", id
);
298 led_classdev_unregister(&leds
->led
[i
].cdev
);
299 cancel_work_sync(&leds
->led
[i
].work
);
305 static int mc13xxx_led_remove(struct platform_device
*pdev
)
307 struct mc13xxx
*mcdev
= dev_get_drvdata(pdev
->dev
.parent
);
308 struct mc13xxx_leds
*leds
= platform_get_drvdata(pdev
);
311 for (i
= 0; i
< leds
->num_leds
; i
++) {
312 led_classdev_unregister(&leds
->led
[i
].cdev
);
313 cancel_work_sync(&leds
->led
[i
].work
);
317 for (i
= 0; i
< leds
->devtype
->num_regs
; i
++)
318 mc13xxx_reg_write(mcdev
, MC13XXX_REG_LED_CONTROL(i
), 0);
319 mc13xxx_unlock(mcdev
);
324 static const struct mc13xxx_led_devtype mc13783_led_devtype
= {
325 .led_min
= MC13783_LED_MD
,
326 .led_max
= MC13783_LED_B3
,
330 static const struct mc13xxx_led_devtype mc13892_led_devtype
= {
331 .led_min
= MC13892_LED_MD
,
332 .led_max
= MC13892_LED_B
,
336 static const struct platform_device_id mc13xxx_led_id_table
[] = {
337 { "mc13783-led", (kernel_ulong_t
)&mc13783_led_devtype
, },
338 { "mc13892-led", (kernel_ulong_t
)&mc13892_led_devtype
, },
341 MODULE_DEVICE_TABLE(platform
, mc13xxx_led_id_table
);
343 static struct platform_driver mc13xxx_led_driver
= {
345 .name
= "mc13xxx-led",
346 .owner
= THIS_MODULE
,
348 .remove
= mc13xxx_led_remove
,
349 .id_table
= mc13xxx_led_id_table
,
351 module_platform_driver_probe(mc13xxx_led_driver
, mc13xxx_led_probe
);
353 MODULE_DESCRIPTION("LEDs driver for Freescale MC13XXX PMIC");
354 MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>");
355 MODULE_LICENSE("GPL");