1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2018 Spreadtrum Communications Inc.
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 /* PMIC global control register definition */
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 /* Breathing light controller register definition */
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 
38 struct 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 
46 struct 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 
56 static 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 
73 static u32 sc27xx_led_get_offset(struct sc27xx_led *leds)
74 {
75 	return leds->priv->base + SC27XX_LEDS_OFFSET * leds->line;
76 }
77 
78 static 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 
98 static 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 
108 static 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 
125 static 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 
152 static 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", &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 
217 static 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 
225 static const struct of_device_id sc27xx_led_of_match[] = {
226 	{ .compatible = "sprd,sc2731-bltc", },
227 	{ }
228 };
229 MODULE_DEVICE_TABLE(of, sc27xx_led_of_match);
230 
231 static 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 
240 module_platform_driver(sc27xx_led_driver);
241 
242 MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
243 MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
244 MODULE_LICENSE("GPL v2");
245