1
2
3
4#include <linux/leds.h>
5#include <linux/module.h>
6#include <linux/of.h>
7#include <linux/platform_device.h>
8#include <linux/regmap.h>
9#include <uapi/linux/uleds.h>
10
11
12#define SC27XX_MODULE_EN0 0xc08
13#define SC27XX_CLK_EN0 0xc18
14#define SC27XX_RGB_CTRL 0xebc
15
16#define SC27XX_BLTC_EN BIT(9)
17#define SC27XX_RTC_EN BIT(7)
18#define SC27XX_RGB_PD BIT(0)
19
20
21#define SC27XX_LEDS_CTRL 0x00
22#define SC27XX_LEDS_PRESCALE 0x04
23#define SC27XX_LEDS_DUTY 0x08
24#define SC27XX_LEDS_CURVE0 0x0c
25#define SC27XX_LEDS_CURVE1 0x10
26
27#define SC27XX_CTRL_SHIFT 4
28#define SC27XX_LED_RUN BIT(0)
29#define SC27XX_LED_TYPE BIT(1)
30
31#define SC27XX_DUTY_SHIFT 8
32#define SC27XX_DUTY_MASK GENMASK(15, 0)
33#define SC27XX_MOD_MASK GENMASK(7, 0)
34
35#define SC27XX_LEDS_OFFSET 0x10
36#define SC27XX_LEDS_MAX 3
37
38struct sc27xx_led {
39 char name[LED_MAX_NAME_SIZE];
40 struct led_classdev ldev;
41 struct sc27xx_led_priv *priv;
42 u8 line;
43 bool active;
44};
45
46struct sc27xx_led_priv {
47 struct sc27xx_led leds[SC27XX_LEDS_MAX];
48 struct regmap *regmap;
49 struct mutex lock;
50 u32 base;
51};
52
53#define to_sc27xx_led(ldev) \
54 container_of(ldev, struct sc27xx_led, ldev)
55
56static int sc27xx_led_init(struct regmap *regmap)
57{
58 int err;
59
60 err = regmap_update_bits(regmap, SC27XX_MODULE_EN0, SC27XX_BLTC_EN,
61 SC27XX_BLTC_EN);
62 if (err)
63 return err;
64
65 err = regmap_update_bits(regmap, SC27XX_CLK_EN0, SC27XX_RTC_EN,
66 SC27XX_RTC_EN);
67 if (err)
68 return err;
69
70 return regmap_update_bits(regmap, SC27XX_RGB_CTRL, SC27XX_RGB_PD, 0);
71}
72
73static u32 sc27xx_led_get_offset(struct sc27xx_led *leds)
74{
75 return leds->priv->base + SC27XX_LEDS_OFFSET * leds->line;
76}
77
78static int sc27xx_led_enable(struct sc27xx_led *leds, enum led_brightness value)
79{
80 u32 base = sc27xx_led_get_offset(leds);
81 u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
82 u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
83 struct regmap *regmap = leds->priv->regmap;
84 int err;
85
86 err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
87 SC27XX_DUTY_MASK,
88 (value << SC27XX_DUTY_SHIFT) |
89 SC27XX_MOD_MASK);
90 if (err)
91 return err;
92
93 return regmap_update_bits(regmap, ctrl_base,
94 (SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift,
95 (SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift);
96}
97
98static int sc27xx_led_disable(struct sc27xx_led *leds)
99{
100 struct regmap *regmap = leds->priv->regmap;
101 u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
102 u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
103
104 return regmap_update_bits(regmap, ctrl_base,
105 (SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
106}
107
108static int sc27xx_led_set(struct led_classdev *ldev, enum led_brightness value)
109{
110 struct sc27xx_led *leds = to_sc27xx_led(ldev);
111 int err;
112
113 mutex_lock(&leds->priv->lock);
114
115 if (value == LED_OFF)
116 err = sc27xx_led_disable(leds);
117 else
118 err = sc27xx_led_enable(leds, value);
119
120 mutex_unlock(&leds->priv->lock);
121
122 return err;
123}
124
125static int sc27xx_led_register(struct device *dev, struct sc27xx_led_priv *priv)
126{
127 int i, err;
128
129 err = sc27xx_led_init(priv->regmap);
130 if (err)
131 return err;
132
133 for (i = 0; i < SC27XX_LEDS_MAX; i++) {
134 struct sc27xx_led *led = &priv->leds[i];
135
136 if (!led->active)
137 continue;
138
139 led->line = i;
140 led->priv = priv;
141 led->ldev.name = led->name;
142 led->ldev.brightness_set_blocking = sc27xx_led_set;
143
144 err = devm_led_classdev_register(dev, &led->ldev);
145 if (err)
146 return err;
147 }
148
149 return 0;
150}
151
152static int sc27xx_led_probe(struct platform_device *pdev)
153{
154 struct device *dev = &pdev->dev;
155 struct device_node *np = dev->of_node, *child;
156 struct sc27xx_led_priv *priv;
157 const char *str;
158 u32 base, count, reg;
159 int err;
160
161 count = of_get_child_count(np);
162 if (!count || count > SC27XX_LEDS_MAX)
163 return -EINVAL;
164
165 err = of_property_read_u32(np, "reg", &base);
166 if (err) {
167 dev_err(dev, "fail to get reg of property\n");
168 return err;
169 }
170
171 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
172 if (!priv)
173 return -ENOMEM;
174
175 platform_set_drvdata(pdev, priv);
176 mutex_init(&priv->lock);
177 priv->base = base;
178 priv->regmap = dev_get_regmap(dev->parent, NULL);
179 if (!priv->regmap) {
180 err = -ENODEV;
181 dev_err(dev, "failed to get regmap: %d\n", err);
182 return err;
183 }
184
185 for_each_child_of_node(np, child) {
186 err = of_property_read_u32(child, "reg", ®);
187 if (err) {
188 of_node_put(child);
189 mutex_destroy(&priv->lock);
190 return err;
191 }
192
193 if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active) {
194 of_node_put(child);
195 mutex_destroy(&priv->lock);
196 return -EINVAL;
197 }
198
199 priv->leds[reg].active = true;
200
201 err = of_property_read_string(child, "label", &str);
202 if (err)
203 snprintf(priv->leds[reg].name, LED_MAX_NAME_SIZE,
204 "sc27xx::");
205 else
206 snprintf(priv->leds[reg].name, LED_MAX_NAME_SIZE,
207 "sc27xx:%s", str);
208 }
209
210 err = sc27xx_led_register(dev, priv);
211 if (err)
212 mutex_destroy(&priv->lock);
213
214 return err;
215}
216
217static int sc27xx_led_remove(struct platform_device *pdev)
218{
219 struct sc27xx_led_priv *priv = platform_get_drvdata(pdev);
220
221 mutex_destroy(&priv->lock);
222 return 0;
223}
224
225static const struct of_device_id sc27xx_led_of_match[] = {
226 { .compatible = "sprd,sc2731-bltc", },
227 { }
228};
229MODULE_DEVICE_TABLE(of, sc27xx_led_of_match);
230
231static struct platform_driver sc27xx_led_driver = {
232 .driver = {
233 .name = "sprd-bltc",
234 .of_match_table = sc27xx_led_of_match,
235 },
236 .probe = sc27xx_led_probe,
237 .remove = sc27xx_led_remove,
238};
239
240module_platform_driver(sc27xx_led_driver);
241
242MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
243MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
244MODULE_LICENSE("GPL v2");
245