2 * LEDs driver for Freescale MC13783/MC13892/MC34708
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>
24 #include <linux/workqueue.h>
25 #include <linux/mfd/mc13xxx.h>
27 struct mc13xxx_led_devtype
{
35 struct led_classdev cdev
;
36 struct work_struct work
;
37 enum led_brightness new_brightness
;
39 struct mc13xxx_leds
*leds
;
43 struct mc13xxx
*master
;
44 struct mc13xxx_led_devtype
*devtype
;
46 struct mc13xxx_led
*led
;
49 static unsigned int mc13xxx_max_brightness(int id
)
51 if (id
>= MC13783_LED_MD
&& id
<= MC13783_LED_KP
)
53 else if (id
>= MC13783_LED_R1
&& id
<= MC13783_LED_B3
)
59 static void mc13xxx_led_work(struct work_struct
*work
)
61 struct mc13xxx_led
*led
= container_of(work
, struct mc13xxx_led
, work
);
62 struct mc13xxx_leds
*leds
= led
->leds
;
63 unsigned int reg
, bank
, off
, shift
;
70 shift
= 9 + (led
->id
- MC13783_LED_MD
) * 4;
81 off
= led
->id
- MC13783_LED_R1
;
84 shift
= (off
- bank
* 3) * 5 + 6;
89 reg
= (led
->id
- MC13892_LED_MD
) / 2;
90 shift
= 3 + (led
->id
- MC13892_LED_MD
) * 12;
95 off
= led
->id
- MC13892_LED_R
;
98 shift
= (off
- bank
* 2) * 12 + 3;
103 shift
= 3 + (led
->id
- MC34708_LED_R
) * 12;
109 mc13xxx_reg_rmw(leds
->master
, leds
->devtype
->ledctrl_base
+ reg
,
110 mc13xxx_max_brightness(led
->id
) << shift
,
111 led
->new_brightness
<< shift
);
114 static void mc13xxx_led_set(struct led_classdev
*led_cdev
,
115 enum led_brightness value
)
117 struct mc13xxx_led
*led
=
118 container_of(led_cdev
, struct mc13xxx_led
, cdev
);
120 led
->new_brightness
= value
;
121 schedule_work(&led
->work
);
125 static struct mc13xxx_leds_platform_data __init
*mc13xxx_led_probe_dt(
126 struct platform_device
*pdev
)
128 struct mc13xxx_leds
*leds
= platform_get_drvdata(pdev
);
129 struct mc13xxx_leds_platform_data
*pdata
;
130 struct device_node
*parent
, *child
;
131 struct device
*dev
= &pdev
->dev
;
132 int i
= 0, ret
= -ENODATA
;
134 pdata
= devm_kzalloc(dev
, sizeof(*pdata
), GFP_KERNEL
);
136 return ERR_PTR(-ENOMEM
);
138 of_node_get(dev
->parent
->of_node
);
140 parent
= of_find_node_by_name(dev
->parent
->of_node
, "leds");
144 ret
= of_property_read_u32_array(parent
, "led-control",
146 leds
->devtype
->num_regs
);
150 pdata
->num_leds
= of_get_child_count(parent
);
152 pdata
->led
= devm_kzalloc(dev
, pdata
->num_leds
* sizeof(*pdata
->led
),
159 for_each_child_of_node(parent
, child
) {
163 if (of_property_read_u32(child
, "reg", &tmp
))
165 pdata
->led
[i
].id
= leds
->devtype
->led_min
+ tmp
;
167 if (!of_property_read_string(child
, "label", &str
))
168 pdata
->led
[i
].name
= str
;
169 if (!of_property_read_string(child
, "linux,default-trigger",
171 pdata
->led
[i
].default_trigger
= str
;
177 ret
= i
> 0 ? 0 : -ENODATA
;
182 return ret
? ERR_PTR(ret
) : pdata
;
185 static inline struct mc13xxx_leds_platform_data __init
*mc13xxx_led_probe_dt(
186 struct platform_device
*pdev
)
188 return ERR_PTR(-ENOSYS
);
192 static int __init
mc13xxx_led_probe(struct platform_device
*pdev
)
194 struct device
*dev
= &pdev
->dev
;
195 struct mc13xxx_leds_platform_data
*pdata
= dev_get_platdata(dev
);
196 struct mc13xxx
*mcdev
= dev_get_drvdata(dev
->parent
);
197 struct mc13xxx_led_devtype
*devtype
=
198 (struct mc13xxx_led_devtype
*)pdev
->id_entry
->driver_data
;
199 struct mc13xxx_leds
*leds
;
200 int i
, id
, ret
= -ENODATA
;
203 leds
= devm_kzalloc(dev
, sizeof(*leds
), GFP_KERNEL
);
207 leds
->devtype
= devtype
;
208 leds
->master
= mcdev
;
209 platform_set_drvdata(pdev
, leds
);
211 if (dev
->parent
->of_node
) {
212 pdata
= mc13xxx_led_probe_dt(pdev
);
214 return PTR_ERR(pdata
);
218 leds
->num_leds
= pdata
->num_leds
;
220 if ((leds
->num_leds
< 1) ||
221 (leds
->num_leds
> (devtype
->led_max
- devtype
->led_min
+ 1))) {
222 dev_err(dev
, "Invalid LED count %d\n", leds
->num_leds
);
226 leds
->led
= devm_kzalloc(dev
, leds
->num_leds
* sizeof(*leds
->led
),
231 for (i
= 0; i
< devtype
->num_regs
; i
++) {
232 ret
= mc13xxx_reg_write(mcdev
, leds
->devtype
->ledctrl_base
+ i
,
233 pdata
->led_control
[i
]);
238 for (i
= 0; i
< leds
->num_leds
; i
++) {
239 const char *name
, *trig
;
243 id
= pdata
->led
[i
].id
;
244 name
= pdata
->led
[i
].name
;
245 trig
= pdata
->led
[i
].default_trigger
;
247 if ((id
> devtype
->led_max
) || (id
< devtype
->led_min
)) {
248 dev_err(dev
, "Invalid ID %i\n", id
);
252 if (init_led
& (1 << id
)) {
253 dev_warn(dev
, "LED %i already initialized\n", id
);
258 leds
->led
[i
].id
= id
;
259 leds
->led
[i
].leds
= leds
;
260 leds
->led
[i
].cdev
.name
= name
;
261 leds
->led
[i
].cdev
.default_trigger
= trig
;
262 leds
->led
[i
].cdev
.flags
= LED_CORE_SUSPENDRESUME
;
263 leds
->led
[i
].cdev
.brightness_set
= mc13xxx_led_set
;
264 leds
->led
[i
].cdev
.max_brightness
= mc13xxx_max_brightness(id
);
266 INIT_WORK(&leds
->led
[i
].work
, mc13xxx_led_work
);
268 ret
= led_classdev_register(dev
->parent
, &leds
->led
[i
].cdev
);
270 dev_err(dev
, "Failed to register LED %i\n", id
);
277 led_classdev_unregister(&leds
->led
[i
].cdev
);
278 cancel_work_sync(&leds
->led
[i
].work
);
284 static int mc13xxx_led_remove(struct platform_device
*pdev
)
286 struct mc13xxx_leds
*leds
= platform_get_drvdata(pdev
);
289 for (i
= 0; i
< leds
->num_leds
; i
++) {
290 led_classdev_unregister(&leds
->led
[i
].cdev
);
291 cancel_work_sync(&leds
->led
[i
].work
);
297 static const struct mc13xxx_led_devtype mc13783_led_devtype
= {
298 .led_min
= MC13783_LED_MD
,
299 .led_max
= MC13783_LED_B3
,
304 static const struct mc13xxx_led_devtype mc13892_led_devtype
= {
305 .led_min
= MC13892_LED_MD
,
306 .led_max
= MC13892_LED_B
,
311 static const struct mc13xxx_led_devtype mc34708_led_devtype
= {
312 .led_min
= MC34708_LED_R
,
313 .led_max
= MC34708_LED_G
,
318 static const struct platform_device_id mc13xxx_led_id_table
[] = {
319 { "mc13783-led", (kernel_ulong_t
)&mc13783_led_devtype
, },
320 { "mc13892-led", (kernel_ulong_t
)&mc13892_led_devtype
, },
321 { "mc34708-led", (kernel_ulong_t
)&mc34708_led_devtype
, },
324 MODULE_DEVICE_TABLE(platform
, mc13xxx_led_id_table
);
326 static struct platform_driver mc13xxx_led_driver
= {
328 .name
= "mc13xxx-led",
329 .owner
= THIS_MODULE
,
331 .remove
= mc13xxx_led_remove
,
332 .id_table
= mc13xxx_led_id_table
,
334 module_platform_driver_probe(mc13xxx_led_driver
, mc13xxx_led_probe
);
336 MODULE_DESCRIPTION("LEDs driver for Freescale MC13XXX PMIC");
337 MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>");
338 MODULE_LICENSE("GPL");