xref: /openbmc/linux/drivers/leds/leds-mt6323.c (revision 5a1ea477)
1 /*
2  * LED driver for Mediatek MT6323 PMIC
3  *
4  * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  */
16 #include <linux/kernel.h>
17 #include <linux/leds.h>
18 #include <linux/mfd/mt6323/registers.h>
19 #include <linux/mfd/mt6397/core.h>
20 #include <linux/module.h>
21 #include <linux/of.h>
22 #include <linux/platform_device.h>
23 #include <linux/regmap.h>
24 
25 /*
26  * Register field for MT6323_TOP_CKPDN0 to enable
27  * 32K clock common for LED device.
28  */
29 #define MT6323_RG_DRV_32K_CK_PDN	BIT(11)
30 #define MT6323_RG_DRV_32K_CK_PDN_MASK	BIT(11)
31 
32 /*
33  * Register field for MT6323_TOP_CKPDN2 to enable
34  * individual clock for LED device.
35  */
36 #define MT6323_RG_ISINK_CK_PDN(i)	BIT(i)
37 #define MT6323_RG_ISINK_CK_PDN_MASK(i)	BIT(i)
38 
39 /*
40  * Register field for MT6323_TOP_CKCON1 to select
41  * clock source.
42  */
43 #define MT6323_RG_ISINK_CK_SEL_MASK(i)	(BIT(10) << (i))
44 
45 /*
46  * Register for MT6323_ISINK_CON0 to setup the
47  * duty cycle of the blink.
48  */
49 #define MT6323_ISINK_CON0(i)		(MT6323_ISINK0_CON0 + 0x8 * (i))
50 #define MT6323_ISINK_DIM_DUTY_MASK	(0x1f << 8)
51 #define MT6323_ISINK_DIM_DUTY(i)	(((i) << 8) & \
52 					MT6323_ISINK_DIM_DUTY_MASK)
53 
54 /* Register to setup the period of the blink. */
55 #define MT6323_ISINK_CON1(i)		(MT6323_ISINK0_CON1 + 0x8 * (i))
56 #define MT6323_ISINK_DIM_FSEL_MASK	(0xffff)
57 #define MT6323_ISINK_DIM_FSEL(i)	((i) & MT6323_ISINK_DIM_FSEL_MASK)
58 
59 /* Register to control the brightness. */
60 #define MT6323_ISINK_CON2(i)		(MT6323_ISINK0_CON2 + 0x8 * (i))
61 #define MT6323_ISINK_CH_STEP_SHIFT	12
62 #define MT6323_ISINK_CH_STEP_MASK	(0x7 << 12)
63 #define MT6323_ISINK_CH_STEP(i)		(((i) << 12) & \
64 					MT6323_ISINK_CH_STEP_MASK)
65 #define MT6323_ISINK_SFSTR0_TC_MASK	(0x3 << 1)
66 #define MT6323_ISINK_SFSTR0_TC(i)	(((i) << 1) & \
67 					MT6323_ISINK_SFSTR0_TC_MASK)
68 #define MT6323_ISINK_SFSTR0_EN_MASK	BIT(0)
69 #define MT6323_ISINK_SFSTR0_EN		BIT(0)
70 
71 /* Register to LED channel enablement. */
72 #define MT6323_ISINK_CH_EN_MASK(i)	BIT(i)
73 #define MT6323_ISINK_CH_EN(i)		BIT(i)
74 
75 #define MT6323_MAX_PERIOD		10000
76 #define MT6323_MAX_LEDS			4
77 #define MT6323_MAX_BRIGHTNESS		6
78 #define MT6323_UNIT_DUTY		3125
79 #define MT6323_CAL_HW_DUTY(o, p)	DIV_ROUND_CLOSEST((o) * 100000ul,\
80 					(p) * MT6323_UNIT_DUTY)
81 
82 struct mt6323_leds;
83 
84 /**
85  * struct mt6323_led - state container for the LED device
86  * @id:			the identifier in MT6323 LED device
87  * @parent:		the pointer to MT6323 LED controller
88  * @cdev:		LED class device for this LED device
89  * @current_brightness: current state of the LED device
90  */
91 struct mt6323_led {
92 	int			id;
93 	struct mt6323_leds	*parent;
94 	struct led_classdev	cdev;
95 	enum led_brightness	current_brightness;
96 };
97 
98 /**
99  * struct mt6323_leds -	state container for holding LED controller
100  *			of the driver
101  * @dev:		the device pointer
102  * @hw:			the underlying hardware providing shared
103  *			bus for the register operations
104  * @lock:		the lock among process context
105  * @led:		the array that contains the state of individual
106  *			LED device
107  */
108 struct mt6323_leds {
109 	struct device		*dev;
110 	struct mt6397_chip	*hw;
111 	/* protect among process context */
112 	struct mutex		lock;
113 	struct mt6323_led	*led[MT6323_MAX_LEDS];
114 };
115 
116 static int mt6323_led_hw_brightness(struct led_classdev *cdev,
117 				    enum led_brightness brightness)
118 {
119 	struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
120 	struct mt6323_leds *leds = led->parent;
121 	struct regmap *regmap = leds->hw->regmap;
122 	u32 con2_mask = 0, con2_val = 0;
123 	int ret;
124 
125 	/*
126 	 * Setup current output for the corresponding
127 	 * brightness level.
128 	 */
129 	con2_mask |= MT6323_ISINK_CH_STEP_MASK |
130 		     MT6323_ISINK_SFSTR0_TC_MASK |
131 		     MT6323_ISINK_SFSTR0_EN_MASK;
132 	con2_val |=  MT6323_ISINK_CH_STEP(brightness - 1) |
133 		     MT6323_ISINK_SFSTR0_TC(2) |
134 		     MT6323_ISINK_SFSTR0_EN;
135 
136 	ret = regmap_update_bits(regmap, MT6323_ISINK_CON2(led->id),
137 				 con2_mask, con2_val);
138 	return ret;
139 }
140 
141 static int mt6323_led_hw_off(struct led_classdev *cdev)
142 {
143 	struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
144 	struct mt6323_leds *leds = led->parent;
145 	struct regmap *regmap = leds->hw->regmap;
146 	unsigned int status;
147 	int ret;
148 
149 	status = MT6323_ISINK_CH_EN(led->id);
150 	ret = regmap_update_bits(regmap, MT6323_ISINK_EN_CTRL,
151 				 MT6323_ISINK_CH_EN_MASK(led->id), ~status);
152 	if (ret < 0)
153 		return ret;
154 
155 	usleep_range(100, 300);
156 	ret = regmap_update_bits(regmap, MT6323_TOP_CKPDN2,
157 				 MT6323_RG_ISINK_CK_PDN_MASK(led->id),
158 				 MT6323_RG_ISINK_CK_PDN(led->id));
159 	if (ret < 0)
160 		return ret;
161 
162 	return 0;
163 }
164 
165 static enum led_brightness
166 mt6323_get_led_hw_brightness(struct led_classdev *cdev)
167 {
168 	struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
169 	struct mt6323_leds *leds = led->parent;
170 	struct regmap *regmap = leds->hw->regmap;
171 	unsigned int status;
172 	int ret;
173 
174 	ret = regmap_read(regmap, MT6323_TOP_CKPDN2, &status);
175 	if (ret < 0)
176 		return ret;
177 
178 	if (status & MT6323_RG_ISINK_CK_PDN_MASK(led->id))
179 		return 0;
180 
181 	ret = regmap_read(regmap, MT6323_ISINK_EN_CTRL, &status);
182 	if (ret < 0)
183 		return ret;
184 
185 	if (!(status & MT6323_ISINK_CH_EN(led->id)))
186 		return 0;
187 
188 	ret = regmap_read(regmap, MT6323_ISINK_CON2(led->id), &status);
189 	if (ret < 0)
190 		return ret;
191 
192 	return  ((status & MT6323_ISINK_CH_STEP_MASK)
193 		  >> MT6323_ISINK_CH_STEP_SHIFT) + 1;
194 }
195 
196 static int mt6323_led_hw_on(struct led_classdev *cdev,
197 			    enum led_brightness brightness)
198 {
199 	struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
200 	struct mt6323_leds *leds = led->parent;
201 	struct regmap *regmap = leds->hw->regmap;
202 	unsigned int status;
203 	int ret;
204 
205 	/*
206 	 * Setup required clock source, enable the corresponding
207 	 * clock and channel and let work with continuous blink as
208 	 * the default.
209 	 */
210 	ret = regmap_update_bits(regmap, MT6323_TOP_CKCON1,
211 				 MT6323_RG_ISINK_CK_SEL_MASK(led->id), 0);
212 	if (ret < 0)
213 		return ret;
214 
215 	status = MT6323_RG_ISINK_CK_PDN(led->id);
216 	ret = regmap_update_bits(regmap, MT6323_TOP_CKPDN2,
217 				 MT6323_RG_ISINK_CK_PDN_MASK(led->id),
218 				 ~status);
219 	if (ret < 0)
220 		return ret;
221 
222 	usleep_range(100, 300);
223 
224 	ret = regmap_update_bits(regmap, MT6323_ISINK_EN_CTRL,
225 				 MT6323_ISINK_CH_EN_MASK(led->id),
226 				 MT6323_ISINK_CH_EN(led->id));
227 	if (ret < 0)
228 		return ret;
229 
230 	ret = mt6323_led_hw_brightness(cdev, brightness);
231 	if (ret < 0)
232 		return ret;
233 
234 	ret = regmap_update_bits(regmap, MT6323_ISINK_CON0(led->id),
235 				 MT6323_ISINK_DIM_DUTY_MASK,
236 				 MT6323_ISINK_DIM_DUTY(31));
237 	if (ret < 0)
238 		return ret;
239 
240 	ret = regmap_update_bits(regmap, MT6323_ISINK_CON1(led->id),
241 				 MT6323_ISINK_DIM_FSEL_MASK,
242 				 MT6323_ISINK_DIM_FSEL(1000));
243 	if (ret < 0)
244 		return ret;
245 
246 	return 0;
247 }
248 
249 static int mt6323_led_set_blink(struct led_classdev *cdev,
250 				unsigned long *delay_on,
251 				unsigned long *delay_off)
252 {
253 	struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
254 	struct mt6323_leds *leds = led->parent;
255 	struct regmap *regmap = leds->hw->regmap;
256 	unsigned long period;
257 	u8 duty_hw;
258 	int ret;
259 
260 	/*
261 	 * Units are in ms, if over the hardware able
262 	 * to support, fallback into software blink
263 	 */
264 	period = *delay_on + *delay_off;
265 
266 	if (period > MT6323_MAX_PERIOD)
267 		return -EINVAL;
268 
269 	/*
270 	 * LED subsystem requires a default user
271 	 * friendly blink pattern for the LED so using
272 	 * 1Hz duty cycle 50% here if without specific
273 	 * value delay_on and delay off being assigned.
274 	 */
275 	if (!*delay_on && !*delay_off) {
276 		*delay_on = 500;
277 		*delay_off = 500;
278 	}
279 
280 	/*
281 	 * Calculate duty_hw based on the percentage of period during
282 	 * which the led is ON.
283 	 */
284 	duty_hw = MT6323_CAL_HW_DUTY(*delay_on, period);
285 
286 	/* hardware doesn't support zero duty cycle. */
287 	if (!duty_hw)
288 		return -EINVAL;
289 
290 	mutex_lock(&leds->lock);
291 	/*
292 	 * Set max_brightness as the software blink behavior
293 	 * when no blink brightness.
294 	 */
295 	if (!led->current_brightness) {
296 		ret = mt6323_led_hw_on(cdev, cdev->max_brightness);
297 		if (ret < 0)
298 			goto out;
299 		led->current_brightness = cdev->max_brightness;
300 	}
301 
302 	ret = regmap_update_bits(regmap, MT6323_ISINK_CON0(led->id),
303 				 MT6323_ISINK_DIM_DUTY_MASK,
304 				 MT6323_ISINK_DIM_DUTY(duty_hw - 1));
305 	if (ret < 0)
306 		goto out;
307 
308 	ret = regmap_update_bits(regmap, MT6323_ISINK_CON1(led->id),
309 				 MT6323_ISINK_DIM_FSEL_MASK,
310 				 MT6323_ISINK_DIM_FSEL(period - 1));
311 out:
312 	mutex_unlock(&leds->lock);
313 
314 	return ret;
315 }
316 
317 static int mt6323_led_set_brightness(struct led_classdev *cdev,
318 				     enum led_brightness brightness)
319 {
320 	struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
321 	struct mt6323_leds *leds = led->parent;
322 	int ret;
323 
324 	mutex_lock(&leds->lock);
325 
326 	if (!led->current_brightness && brightness) {
327 		ret = mt6323_led_hw_on(cdev, brightness);
328 		if (ret < 0)
329 			goto out;
330 	} else if (brightness) {
331 		ret = mt6323_led_hw_brightness(cdev, brightness);
332 		if (ret < 0)
333 			goto out;
334 	} else {
335 		ret = mt6323_led_hw_off(cdev);
336 		if (ret < 0)
337 			goto out;
338 	}
339 
340 	led->current_brightness = brightness;
341 out:
342 	mutex_unlock(&leds->lock);
343 
344 	return ret;
345 }
346 
347 static int mt6323_led_set_dt_default(struct led_classdev *cdev,
348 				     struct device_node *np)
349 {
350 	struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev);
351 	const char *state;
352 	int ret = 0;
353 
354 	led->cdev.name = of_get_property(np, "label", NULL) ? : np->name;
355 	led->cdev.default_trigger = of_get_property(np,
356 						    "linux,default-trigger",
357 						    NULL);
358 
359 	state = of_get_property(np, "default-state", NULL);
360 	if (state) {
361 		if (!strcmp(state, "keep")) {
362 			ret = mt6323_get_led_hw_brightness(cdev);
363 			if (ret < 0)
364 				return ret;
365 			led->current_brightness = ret;
366 			ret = 0;
367 		} else if (!strcmp(state, "on")) {
368 			ret =
369 			mt6323_led_set_brightness(cdev, cdev->max_brightness);
370 		} else  {
371 			ret = mt6323_led_set_brightness(cdev, LED_OFF);
372 		}
373 	}
374 
375 	return ret;
376 }
377 
378 static int mt6323_led_probe(struct platform_device *pdev)
379 {
380 	struct device *dev = &pdev->dev;
381 	struct device_node *np = pdev->dev.of_node;
382 	struct device_node *child;
383 	struct mt6397_chip *hw = dev_get_drvdata(pdev->dev.parent);
384 	struct mt6323_leds *leds;
385 	struct mt6323_led *led;
386 	int ret;
387 	unsigned int status;
388 	u32 reg;
389 
390 	leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL);
391 	if (!leds)
392 		return -ENOMEM;
393 
394 	platform_set_drvdata(pdev, leds);
395 	leds->dev = dev;
396 
397 	/*
398 	 * leds->hw points to the underlying bus for the register
399 	 * controlled.
400 	 */
401 	leds->hw = hw;
402 	mutex_init(&leds->lock);
403 
404 	status = MT6323_RG_DRV_32K_CK_PDN;
405 	ret = regmap_update_bits(leds->hw->regmap, MT6323_TOP_CKPDN0,
406 				 MT6323_RG_DRV_32K_CK_PDN_MASK, ~status);
407 	if (ret < 0) {
408 		dev_err(leds->dev,
409 			"Failed to update MT6323_TOP_CKPDN0 Register\n");
410 		return ret;
411 	}
412 
413 	for_each_available_child_of_node(np, child) {
414 		ret = of_property_read_u32(child, "reg", &reg);
415 		if (ret) {
416 			dev_err(dev, "Failed to read led 'reg' property\n");
417 			goto put_child_node;
418 		}
419 
420 		if (reg >= MT6323_MAX_LEDS || leds->led[reg]) {
421 			dev_err(dev, "Invalid led reg %u\n", reg);
422 			ret = -EINVAL;
423 			goto put_child_node;
424 		}
425 
426 		led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
427 		if (!led) {
428 			ret = -ENOMEM;
429 			goto put_child_node;
430 		}
431 
432 		leds->led[reg] = led;
433 		leds->led[reg]->id = reg;
434 		leds->led[reg]->cdev.max_brightness = MT6323_MAX_BRIGHTNESS;
435 		leds->led[reg]->cdev.brightness_set_blocking =
436 					mt6323_led_set_brightness;
437 		leds->led[reg]->cdev.blink_set = mt6323_led_set_blink;
438 		leds->led[reg]->cdev.brightness_get =
439 					mt6323_get_led_hw_brightness;
440 		leds->led[reg]->parent = leds;
441 
442 		ret = mt6323_led_set_dt_default(&leds->led[reg]->cdev, child);
443 		if (ret < 0) {
444 			dev_err(leds->dev,
445 				"Failed to LED set default from devicetree\n");
446 			goto put_child_node;
447 		}
448 
449 		ret = devm_led_classdev_register(dev, &leds->led[reg]->cdev);
450 		if (ret) {
451 			dev_err(&pdev->dev, "Failed to register LED: %d\n",
452 				ret);
453 			goto put_child_node;
454 		}
455 		leds->led[reg]->cdev.dev->of_node = child;
456 	}
457 
458 	return 0;
459 
460 put_child_node:
461 	of_node_put(child);
462 	return ret;
463 }
464 
465 static int mt6323_led_remove(struct platform_device *pdev)
466 {
467 	struct mt6323_leds *leds = platform_get_drvdata(pdev);
468 	int i;
469 
470 	/* Turn the LEDs off on driver removal. */
471 	for (i = 0 ; leds->led[i] ; i++)
472 		mt6323_led_hw_off(&leds->led[i]->cdev);
473 
474 	regmap_update_bits(leds->hw->regmap, MT6323_TOP_CKPDN0,
475 			   MT6323_RG_DRV_32K_CK_PDN_MASK,
476 			   MT6323_RG_DRV_32K_CK_PDN);
477 
478 	mutex_destroy(&leds->lock);
479 
480 	return 0;
481 }
482 
483 static const struct of_device_id mt6323_led_dt_match[] = {
484 	{ .compatible = "mediatek,mt6323-led" },
485 	{},
486 };
487 MODULE_DEVICE_TABLE(of, mt6323_led_dt_match);
488 
489 static struct platform_driver mt6323_led_driver = {
490 	.probe		= mt6323_led_probe,
491 	.remove		= mt6323_led_remove,
492 	.driver		= {
493 		.name	= "mt6323-led",
494 		.of_match_table = mt6323_led_dt_match,
495 	},
496 };
497 
498 module_platform_driver(mt6323_led_driver);
499 
500 MODULE_DESCRIPTION("LED driver for Mediatek MT6323 PMIC");
501 MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
502 MODULE_LICENSE("GPL");
503