1 // SPDX-License-Identifier: GPL-2.0-only
3 * LED driver for WM831x status LEDs
5 * Copyright(C) 2009 Wolfson Microelectronics PLC.
8 #include <linux/kernel.h>
9 #include <linux/platform_device.h>
10 #include <linux/slab.h>
11 #include <linux/leds.h>
12 #include <linux/err.h>
13 #include <linux/mfd/wm831x/core.h>
14 #include <linux/mfd/wm831x/pdata.h>
15 #include <linux/mfd/wm831x/status.h>
16 #include <linux/module.h>
19 struct wm831x_status
{
20 struct led_classdev cdev
;
21 struct wm831x
*wm831x
;
24 spinlock_t value_lock
;
25 int reg
; /* Control register */
26 int reg_val
; /* Control register value */
32 enum led_brightness brightness
;
35 #define to_wm831x_status(led_cdev) \
36 container_of(led_cdev, struct wm831x_status, cdev)
38 static void wm831x_status_set(struct wm831x_status
*led
)
42 mutex_lock(&led
->mutex
);
44 led
->reg_val
&= ~(WM831X_LED_SRC_MASK
| WM831X_LED_MODE_MASK
|
45 WM831X_LED_DUTY_CYC_MASK
| WM831X_LED_DUR_MASK
);
47 spin_lock_irqsave(&led
->value_lock
, flags
);
49 led
->reg_val
|= led
->src
<< WM831X_LED_SRC_SHIFT
;
51 led
->reg_val
|= 2 << WM831X_LED_MODE_SHIFT
;
52 led
->reg_val
|= led
->blink_time
<< WM831X_LED_DUR_SHIFT
;
53 led
->reg_val
|= led
->blink_cyc
;
55 if (led
->brightness
!= LED_OFF
)
56 led
->reg_val
|= 1 << WM831X_LED_MODE_SHIFT
;
59 spin_unlock_irqrestore(&led
->value_lock
, flags
);
61 wm831x_reg_write(led
->wm831x
, led
->reg
, led
->reg_val
);
63 mutex_unlock(&led
->mutex
);
66 static int wm831x_status_brightness_set(struct led_classdev
*led_cdev
,
67 enum led_brightness value
)
69 struct wm831x_status
*led
= to_wm831x_status(led_cdev
);
72 spin_lock_irqsave(&led
->value_lock
, flags
);
73 led
->brightness
= value
;
76 spin_unlock_irqrestore(&led
->value_lock
, flags
);
77 wm831x_status_set(led
);
82 static int wm831x_status_blink_set(struct led_classdev
*led_cdev
,
83 unsigned long *delay_on
,
84 unsigned long *delay_off
)
86 struct wm831x_status
*led
= to_wm831x_status(led_cdev
);
90 /* Pick some defaults if we've not been given times */
91 if (*delay_on
== 0 && *delay_off
== 0) {
96 spin_lock_irqsave(&led
->value_lock
, flags
);
98 /* We only have a limited selection of settings, see if we can
99 * support the configuration we're being given */
112 /* Actually 62.5ms */
121 switch (*delay_off
/ *delay_on
) {
145 spin_unlock_irqrestore(&led
->value_lock
, flags
);
146 wm831x_status_set(led
);
151 static const char * const led_src_texts
[] = {
158 static ssize_t
wm831x_status_src_show(struct device
*dev
,
159 struct device_attribute
*attr
, char *buf
)
161 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
162 struct wm831x_status
*led
= to_wm831x_status(led_cdev
);
166 mutex_lock(&led
->mutex
);
168 for (i
= 0; i
< ARRAY_SIZE(led_src_texts
); i
++)
170 ret
+= sprintf(&buf
[ret
], "[%s] ", led_src_texts
[i
]);
172 ret
+= sprintf(&buf
[ret
], "%s ", led_src_texts
[i
]);
174 mutex_unlock(&led
->mutex
);
176 ret
+= sprintf(&buf
[ret
], "\n");
181 static ssize_t
wm831x_status_src_store(struct device
*dev
,
182 struct device_attribute
*attr
,
183 const char *buf
, size_t size
)
185 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
186 struct wm831x_status
*led
= to_wm831x_status(led_cdev
);
189 i
= sysfs_match_string(led_src_texts
, buf
);
191 mutex_lock(&led
->mutex
);
193 mutex_unlock(&led
->mutex
);
194 wm831x_status_set(led
);
200 static DEVICE_ATTR(src
, 0644, wm831x_status_src_show
, wm831x_status_src_store
);
202 static struct attribute
*wm831x_status_attrs
[] = {
206 ATTRIBUTE_GROUPS(wm831x_status
);
208 static int wm831x_status_probe(struct platform_device
*pdev
)
210 struct wm831x
*wm831x
= dev_get_drvdata(pdev
->dev
.parent
);
211 struct wm831x_pdata
*chip_pdata
;
212 struct wm831x_status_pdata pdata
;
213 struct wm831x_status
*drvdata
;
214 struct resource
*res
;
215 int id
= pdev
->id
% ARRAY_SIZE(chip_pdata
->status
);
218 res
= platform_get_resource(pdev
, IORESOURCE_REG
, 0);
220 dev_err(&pdev
->dev
, "No register resource\n");
224 drvdata
= devm_kzalloc(&pdev
->dev
, sizeof(struct wm831x_status
),
229 drvdata
->wm831x
= wm831x
;
230 drvdata
->reg
= res
->start
;
232 if (dev_get_platdata(wm831x
->dev
))
233 chip_pdata
= dev_get_platdata(wm831x
->dev
);
237 memset(&pdata
, 0, sizeof(pdata
));
238 if (chip_pdata
&& chip_pdata
->status
[id
])
239 memcpy(&pdata
, chip_pdata
->status
[id
], sizeof(pdata
));
241 pdata
.name
= dev_name(&pdev
->dev
);
243 mutex_init(&drvdata
->mutex
);
244 spin_lock_init(&drvdata
->value_lock
);
246 /* We cache the configuration register and read startup values
248 drvdata
->reg_val
= wm831x_reg_read(wm831x
, drvdata
->reg
);
250 if (drvdata
->reg_val
& WM831X_LED_MODE_MASK
)
251 drvdata
->brightness
= LED_FULL
;
253 drvdata
->brightness
= LED_OFF
;
255 /* Set a default source if configured, otherwise leave the
256 * current hardware setting.
258 if (pdata
.default_src
== WM831X_STATUS_PRESERVE
) {
259 drvdata
->src
= drvdata
->reg_val
;
260 drvdata
->src
&= WM831X_LED_SRC_MASK
;
261 drvdata
->src
>>= WM831X_LED_SRC_SHIFT
;
263 drvdata
->src
= pdata
.default_src
- 1;
266 drvdata
->cdev
.name
= pdata
.name
;
267 drvdata
->cdev
.default_trigger
= pdata
.default_trigger
;
268 drvdata
->cdev
.brightness_set_blocking
= wm831x_status_brightness_set
;
269 drvdata
->cdev
.blink_set
= wm831x_status_blink_set
;
270 drvdata
->cdev
.groups
= wm831x_status_groups
;
272 ret
= led_classdev_register(wm831x
->dev
, &drvdata
->cdev
);
274 dev_err(&pdev
->dev
, "Failed to register LED: %d\n", ret
);
278 platform_set_drvdata(pdev
, drvdata
);
283 static int wm831x_status_remove(struct platform_device
*pdev
)
285 struct wm831x_status
*drvdata
= platform_get_drvdata(pdev
);
287 led_classdev_unregister(&drvdata
->cdev
);
292 static struct platform_driver wm831x_status_driver
= {
294 .name
= "wm831x-status",
296 .probe
= wm831x_status_probe
,
297 .remove
= wm831x_status_remove
,
300 module_platform_driver(wm831x_status_driver
);
302 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
303 MODULE_DESCRIPTION("WM831x status LED driver");
304 MODULE_LICENSE("GPL");
305 MODULE_ALIAS("platform:wm831x-status");