blob: d8418295bfeb47d856e9c315f7bc13c57fb39e0d [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001From ecbd9c87f073f097d9fe56390353e64e963e866a Mon Sep 17 00:00:00 2001
2From: John Crispin <john@phrozen.org>
3Date: Tue, 6 Mar 2018 10:03:03 +0100
4Subject: [PATCH 03/27] leds: add reset-controller based driver
5
6Signed-off-by: John Crispin <john@phrozen.org>
7---
8 drivers/leds/Kconfig | 11 ++++
9 drivers/leds/Makefile | 1 +
10 drivers/leds/leds-reset.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++
11 3 files changed, 149 insertions(+)
12 create mode 100644 drivers/leds/leds-reset.c
13
14--- a/drivers/leds/Kconfig
15+++ b/drivers/leds/Kconfig
16@@ -826,6 +826,17 @@ config LEDS_LM36274
17
18 source "drivers/leds/blink/Kconfig"
19
20+config LEDS_RESET
21+ tristate "LED support for reset-controller API"
22+ depends on LEDS_CLASS
23+ depends on RESET_CONTROLLER
24+ help
25+ This option enables support for LEDs connected to pins driven by reset
26+ controllers. Yes, DNI actual built HW like that.
27+
28+ To compile this driver as a module, choose M here: the module
29+ will be called leds-reset.
30+
31 comment "LED Triggers"
32 source "drivers/leds/trigger/Kconfig"
33
34--- /dev/null
35+++ b/drivers/leds/leds-reset.c
36@@ -0,0 +1,140 @@
37+/*
38+ * Copyright (C) 2018 John Crispin <john@phrozen.org>
39+ *
40+ * This program is free software; you can redistribute it and/or modify
41+ * it under the terms of the GNU General Public License version 2 as
42+ * published by the Free Software Foundation.
43+ *
44+ */
45+#include <linux/err.h>
46+#include <linux/reset.h>
47+#include <linux/kernel.h>
48+#include <linux/leds.h>
49+#include <linux/module.h>
50+#include <linux/of.h>
51+#include <linux/platform_device.h>
52+#include <linux/reset.h>
53+
54+struct reset_led_data {
55+ struct led_classdev cdev;
56+ struct reset_control *rst;
57+};
58+
59+static inline struct reset_led_data *
60+ cdev_to_reset_led_data(struct led_classdev *led_cdev)
61+{
62+ return container_of(led_cdev, struct reset_led_data, cdev);
63+}
64+
65+static void reset_led_set(struct led_classdev *led_cdev,
66+ enum led_brightness value)
67+{
68+ struct reset_led_data *led_dat = cdev_to_reset_led_data(led_cdev);
69+
70+ if (value == LED_OFF)
71+ reset_control_assert(led_dat->rst);
72+ else
73+ reset_control_deassert(led_dat->rst);
74+}
75+
76+struct reset_leds_priv {
77+ int num_leds;
78+ struct reset_led_data leds[];
79+};
80+
81+static inline int sizeof_reset_leds_priv(int num_leds)
82+{
83+ return sizeof(struct reset_leds_priv) +
84+ (sizeof(struct reset_led_data) * num_leds);
85+}
86+
87+static struct reset_leds_priv *reset_leds_create(struct platform_device *pdev)
88+{
89+ struct device *dev = &pdev->dev;
90+ struct fwnode_handle *child;
91+ struct reset_leds_priv *priv;
92+ int count, ret;
93+
94+ count = device_get_child_node_count(dev);
95+ if (!count)
96+ return ERR_PTR(-ENODEV);
97+
98+ priv = devm_kzalloc(dev, sizeof_reset_leds_priv(count), GFP_KERNEL);
99+ if (!priv)
100+ return ERR_PTR(-ENOMEM);
101+
102+ device_for_each_child_node(dev, child) {
103+ struct reset_led_data *led = &priv->leds[priv->num_leds];
104+ struct device_node *np = to_of_node(child);
105+
106+ ret = fwnode_property_read_string(child, "label", &led->cdev.name);
107+ if (!led->cdev.name) {
108+ fwnode_handle_put(child);
109+ return ERR_PTR(-EINVAL);
110+ }
111+ led->rst = __of_reset_control_get(np, NULL, 0, 0, 0, true);
112+ if (IS_ERR(led->rst))
113+ return ERR_PTR(-EINVAL);
114+
115+ fwnode_property_read_string(child, "linux,default-trigger",
116+ &led->cdev.default_trigger);
117+
118+ led->cdev.brightness_set = reset_led_set;
119+ ret = devm_led_classdev_register(&pdev->dev, &led->cdev);
120+ if (ret < 0)
121+ return ERR_PTR(ret);
122+ led->cdev.dev->of_node = np;
123+ priv->num_leds++;
124+ }
125+
126+ return priv;
127+}
128+
129+static const struct of_device_id of_reset_leds_match[] = {
130+ { .compatible = "reset-leds", },
131+ {},
132+};
133+
134+MODULE_DEVICE_TABLE(of, of_reset_leds_match);
135+
136+static int reset_led_probe(struct platform_device *pdev)
137+{
138+ struct reset_leds_priv *priv;
139+
140+ priv = reset_leds_create(pdev);
141+ if (IS_ERR(priv))
142+ return PTR_ERR(priv);
143+
144+ platform_set_drvdata(pdev, priv);
145+
146+ return 0;
147+}
148+
149+static void reset_led_shutdown(struct platform_device *pdev)
150+{
151+ struct reset_leds_priv *priv = platform_get_drvdata(pdev);
152+ int i;
153+
154+ for (i = 0; i < priv->num_leds; i++) {
155+ struct reset_led_data *led = &priv->leds[i];
156+
157+ if (!(led->cdev.flags & LED_RETAIN_AT_SHUTDOWN))
158+ reset_led_set(&led->cdev, LED_OFF);
159+ }
160+}
161+
162+static struct platform_driver reset_led_driver = {
163+ .probe = reset_led_probe,
164+ .shutdown = reset_led_shutdown,
165+ .driver = {
166+ .name = "leds-reset",
167+ .of_match_table = of_reset_leds_match,
168+ },
169+};
170+
171+module_platform_driver(reset_led_driver);
172+
173+MODULE_AUTHOR("John Crispin <john@phrozen.org>");
174+MODULE_DESCRIPTION("reset controller LED driver");
175+MODULE_LICENSE("GPL");
176+MODULE_ALIAS("platform:leds-reset");
177--- a/drivers/leds/Makefile
178+++ b/drivers/leds/Makefile
179@@ -85,6 +85,7 @@ obj-$(CONFIG_LEDS_LM3601X) += leds-lm36
180 obj-$(CONFIG_LEDS_TI_LMU_COMMON) += leds-ti-lmu-common.o
181 obj-$(CONFIG_LEDS_LM3697) += leds-lm3697.o
182 obj-$(CONFIG_LEDS_LM36274) += leds-lm36274.o
183+obj-$(CONFIG_LEDS_RESET) += leds-reset.o
184
185 # LED SPI Drivers
186 obj-$(CONFIG_LEDS_CR0014114) += leds-cr0014114.o