realtek: add system LED peripheral driver
[openwrt/staging/svanheule.git] / target / linux / realtek / files-5.10 / drivers / leds / realtek / rtl-switch-sys-leds.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <linux/mfd/syscon.h>
4 #include <linux/mod_devicetable.h>
5 #include <linux/module.h>
6 #include <linux/of.h>
7 #include <linux/platform_device.h>
8 #include <linux/property.h>
9 #include <linux/regmap.h>
10
11 #include "led-regfield.h"
12
13 /*
14 * Realtek hardware system LED
15 *
16 * The switch SoC supports one hardware managed direct LED output
17 * to manage a system LED, with two supported blinking rates.
18 */
19
20 #define RTL838X_REG_LED_GLB_CTRL 0xa000
21 #define RTL839X_REG_LED_GLB_CTRL 0x00e4
22 #define RTL930X_REG_LED_GLB_CTRL 0xcc00
23 #define RTL931X_REG_LED_GLB_CTRL 0x0600
24
25 static const struct reg_field rtl838x_sys_led_field = REG_FIELD(RTL838X_REG_LED_GLB_CTRL, 16, 17);
26 static const struct reg_field rtl839x_sys_led_field = REG_FIELD(RTL839X_REG_LED_GLB_CTRL, 15, 16);
27 static const struct reg_field rtl930x_sys_led_field = REG_FIELD(RTL930X_REG_LED_GLB_CTRL, 13, 14);
28 static const struct reg_field rtl931x_sys_led_field = REG_FIELD(RTL931X_REG_LED_GLB_CTRL, 12, 13);
29
30 static const struct regfield_led_modes rtl_sys_led_modes = {
31 .off = 0,
32 .on = 3,
33 .blink = {
34 {64, 1},
35 {1024, 2},
36 { /* sentinel */ }
37 },
38 };
39
40 static const struct of_device_id of_rtl_sys_led_match[] = {
41 {
42 .compatible = "realtek,maple-sys-led",
43 .data = &rtl838x_sys_led_field,
44 },
45 {
46 .compatible = "realtek,cypress-sys-led",
47 .data = &rtl839x_sys_led_field,
48 },
49 {
50 .compatible = "realtek,longan-sys-led",
51 .data = &rtl930x_sys_led_field,
52 },
53 {
54 .compatible = "realtek,mango-sys-led",
55 .data = &rtl931x_sys_led_field,
56 },
57 { /* sentinel */ }
58 };
59 MODULE_DEVICE_TABLE(of, of_rtl_sys_led_match);
60
61 static int rtl_sys_led_probe(struct platform_device *pdev)
62 {
63 struct device *dev = &pdev->dev;
64 struct device_node *np = dev->of_node;
65 struct led_init_data init_data = {};
66 const struct reg_field *field_data;
67 struct regmap_field *field;
68 struct regfield_led *led;
69 struct regmap *map;
70 int err;
71
72 map = syscon_node_to_regmap(of_get_parent(dev->of_node));
73 if (!map)
74 return dev_err_probe(dev, -ENXIO, "failed to get regmap\n");
75
76 led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
77 if (!led)
78 return -ENOMEM;
79
80 field_data = device_get_match_data(dev);
81 field = devm_regmap_field_alloc(dev, map, *field_data);
82 if (IS_ERR(field))
83 return dev_err_probe(dev, PTR_ERR(field), "register field allocation failed\n");
84
85 err = regfield_led_init(led, field, of_fwnode_handle(np), &rtl_sys_led_modes);
86 if (err)
87 dev_err_probe(dev, err, "regfield_led initialisation failed\n");
88
89 init_data.fwnode = of_fwnode_handle(np);
90
91 return devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
92 }
93
94 static struct platform_driver rtl_sys_led_driver = {
95 .probe = rtl_sys_led_probe,
96 .driver = {
97 .name = "realtek-sys-led",
98 .of_match_table = of_rtl_sys_led_match,
99 },
100 };
101 module_platform_driver(rtl_sys_led_driver);
102
103 MODULE_AUTHOR("Sander Vanheule <sander@svanheule.net>");
104 MODULE_DESCRIPTION("Realtek switch SoC system LED driver");
105 MODULE_LICENSE("GPL v2");