2 * LED driver for WM831x status LEDs
4 * Copyright(C) 2009 Wolfson Microelectronics PLC.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
12 #include <linux/kernel.h>
13 #include <linux/init.h>
14 #include <linux/platform_device.h>
15 #include <linux/slab.h>
16 #include <linux/leds.h>
17 #include <linux/err.h>
18 #include <linux/mfd/wm831x/core.h>
19 #include <linux/mfd/wm831x/pdata.h>
20 #include <linux/mfd/wm831x/status.h>
23 struct wm831x_status
{
24 struct led_classdev cdev
;
25 struct wm831x
*wm831x
;
26 struct work_struct work
;
29 spinlock_t value_lock
;
30 int reg
; /* Control register */
31 int reg_val
; /* Control register value */
37 enum led_brightness brightness
;
40 #define to_wm831x_status(led_cdev) \
41 container_of(led_cdev, struct wm831x_status, cdev)
43 static void wm831x_status_work(struct work_struct
*work
)
45 struct wm831x_status
*led
= container_of(work
, struct wm831x_status
,
49 mutex_lock(&led
->mutex
);
51 led
->reg_val
&= ~(WM831X_LED_SRC_MASK
| WM831X_LED_MODE_MASK
|
52 WM831X_LED_DUTY_CYC_MASK
| WM831X_LED_DUR_MASK
);
54 spin_lock_irqsave(&led
->value_lock
, flags
);
56 led
->reg_val
|= led
->src
<< WM831X_LED_SRC_SHIFT
;
58 led
->reg_val
|= 2 << WM831X_LED_MODE_SHIFT
;
59 led
->reg_val
|= led
->blink_time
<< WM831X_LED_DUR_SHIFT
;
60 led
->reg_val
|= led
->blink_cyc
;
62 if (led
->brightness
!= LED_OFF
)
63 led
->reg_val
|= 1 << WM831X_LED_MODE_SHIFT
;
66 spin_unlock_irqrestore(&led
->value_lock
, flags
);
68 wm831x_reg_write(led
->wm831x
, led
->reg
, led
->reg_val
);
70 mutex_unlock(&led
->mutex
);
73 static void wm831x_status_set(struct led_classdev
*led_cdev
,
74 enum led_brightness value
)
76 struct wm831x_status
*led
= to_wm831x_status(led_cdev
);
79 spin_lock_irqsave(&led
->value_lock
, flags
);
80 led
->brightness
= value
;
83 schedule_work(&led
->work
);
84 spin_unlock_irqrestore(&led
->value_lock
, flags
);
87 static int wm831x_status_blink_set(struct led_classdev
*led_cdev
,
88 unsigned long *delay_on
,
89 unsigned long *delay_off
)
91 struct wm831x_status
*led
= to_wm831x_status(led_cdev
);
95 /* Pick some defaults if we've not been given times */
96 if (*delay_on
== 0 && *delay_off
== 0) {
101 spin_lock_irqsave(&led
->value_lock
, flags
);
103 /* We only have a limited selection of settings, see if we can
104 * support the configuration we're being given */
117 /* Actually 62.5ms */
126 switch (*delay_off
/ *delay_on
) {
150 /* Always update; if we fail turn off blinking since we expect
151 * a software fallback. */
152 schedule_work(&led
->work
);
154 spin_unlock_irqrestore(&led
->value_lock
, flags
);
159 static const char *led_src_texts
[] = {
166 static ssize_t
wm831x_status_src_show(struct device
*dev
,
167 struct device_attribute
*attr
, char *buf
)
169 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
170 struct wm831x_status
*led
= to_wm831x_status(led_cdev
);
174 mutex_lock(&led
->mutex
);
176 for (i
= 0; i
< ARRAY_SIZE(led_src_texts
); i
++)
178 ret
+= sprintf(&buf
[ret
], "[%s] ", led_src_texts
[i
]);
180 ret
+= sprintf(&buf
[ret
], "%s ", led_src_texts
[i
]);
182 mutex_unlock(&led
->mutex
);
184 ret
+= sprintf(&buf
[ret
], "\n");
189 static ssize_t
wm831x_status_src_store(struct device
*dev
,
190 struct device_attribute
*attr
,
191 const char *buf
, size_t size
)
193 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
194 struct wm831x_status
*led
= to_wm831x_status(led_cdev
);
199 name
[sizeof(name
) - 1] = '\0';
200 strncpy(name
, buf
, sizeof(name
) - 1);
203 if (len
&& name
[len
- 1] == '\n')
204 name
[len
- 1] = '\0';
206 for (i
= 0; i
< ARRAY_SIZE(led_src_texts
); i
++) {
207 if (!strcmp(name
, led_src_texts
[i
])) {
208 mutex_lock(&led
->mutex
);
211 schedule_work(&led
->work
);
213 mutex_unlock(&led
->mutex
);
220 static DEVICE_ATTR(src
, 0644, wm831x_status_src_show
, wm831x_status_src_store
);
222 static int wm831x_status_probe(struct platform_device
*pdev
)
224 struct wm831x
*wm831x
= dev_get_drvdata(pdev
->dev
.parent
);
225 struct wm831x_pdata
*chip_pdata
;
226 struct wm831x_status_pdata pdata
;
227 struct wm831x_status
*drvdata
;
228 struct resource
*res
;
229 int id
= pdev
->id
% ARRAY_SIZE(chip_pdata
->status
);
232 res
= platform_get_resource(pdev
, IORESOURCE_IO
, 0);
234 dev_err(&pdev
->dev
, "No I/O resource\n");
239 drvdata
= kzalloc(sizeof(struct wm831x_status
), GFP_KERNEL
);
242 dev_set_drvdata(&pdev
->dev
, drvdata
);
244 drvdata
->wm831x
= wm831x
;
245 drvdata
->reg
= res
->start
;
247 if (wm831x
->dev
->platform_data
)
248 chip_pdata
= wm831x
->dev
->platform_data
;
252 memset(&pdata
, 0, sizeof(pdata
));
253 if (chip_pdata
&& chip_pdata
->status
[id
])
254 memcpy(&pdata
, chip_pdata
->status
[id
], sizeof(pdata
));
256 pdata
.name
= dev_name(&pdev
->dev
);
258 mutex_init(&drvdata
->mutex
);
259 INIT_WORK(&drvdata
->work
, wm831x_status_work
);
260 spin_lock_init(&drvdata
->value_lock
);
262 /* We cache the configuration register and read startup values
264 drvdata
->reg_val
= wm831x_reg_read(wm831x
, drvdata
->reg
);
266 if (drvdata
->reg_val
& WM831X_LED_MODE_MASK
)
267 drvdata
->brightness
= LED_FULL
;
269 drvdata
->brightness
= LED_OFF
;
271 /* Set a default source if configured, otherwise leave the
272 * current hardware setting.
274 if (pdata
.default_src
== WM831X_STATUS_PRESERVE
) {
275 drvdata
->src
= drvdata
->reg_val
;
276 drvdata
->src
&= WM831X_LED_SRC_MASK
;
277 drvdata
->src
>>= WM831X_LED_SRC_SHIFT
;
279 drvdata
->src
= pdata
.default_src
- 1;
282 drvdata
->cdev
.name
= pdata
.name
;
283 drvdata
->cdev
.default_trigger
= pdata
.default_trigger
;
284 drvdata
->cdev
.brightness_set
= wm831x_status_set
;
285 drvdata
->cdev
.blink_set
= wm831x_status_blink_set
;
287 ret
= led_classdev_register(wm831x
->dev
, &drvdata
->cdev
);
289 dev_err(&pdev
->dev
, "Failed to register LED: %d\n", ret
);
293 ret
= device_create_file(drvdata
->cdev
.dev
, &dev_attr_src
);
296 "No source control for LED: %d\n", ret
);
301 led_classdev_unregister(&drvdata
->cdev
);
307 static int wm831x_status_remove(struct platform_device
*pdev
)
309 struct wm831x_status
*drvdata
= platform_get_drvdata(pdev
);
311 device_remove_file(drvdata
->cdev
.dev
, &dev_attr_src
);
312 led_classdev_unregister(&drvdata
->cdev
);
318 static struct platform_driver wm831x_status_driver
= {
320 .name
= "wm831x-status",
321 .owner
= THIS_MODULE
,
323 .probe
= wm831x_status_probe
,
324 .remove
= wm831x_status_remove
,
327 static int __devinit
wm831x_status_init(void)
329 return platform_driver_register(&wm831x_status_driver
);
331 module_init(wm831x_status_init
);
333 static void wm831x_status_exit(void)
335 platform_driver_unregister(&wm831x_status_driver
);
337 module_exit(wm831x_status_exit
);
339 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
340 MODULE_DESCRIPTION("WM831x status LED driver");
341 MODULE_LICENSE("GPL");
342 MODULE_ALIAS("platform:wm831x-status");