xref: /linux/drivers/leds/leds-ltc3220.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1*07e028d9SEdelweise Escala // SPDX-License-Identifier: GPL-2.0
2*07e028d9SEdelweise Escala /*
3*07e028d9SEdelweise Escala  * LTC3220 18-Channel LED Driver
4*07e028d9SEdelweise Escala  *
5*07e028d9SEdelweise Escala  * Copyright 2026 Analog Devices Inc.
6*07e028d9SEdelweise Escala  *
7*07e028d9SEdelweise Escala  * Author: Edelweise Escala <edelweise.escala@analog.com>
8*07e028d9SEdelweise Escala  */
9*07e028d9SEdelweise Escala 
10*07e028d9SEdelweise Escala #include <linux/bitfield.h>
11*07e028d9SEdelweise Escala #include <linux/delay.h>
12*07e028d9SEdelweise Escala #include <linux/device.h>
13*07e028d9SEdelweise Escala #include <linux/gpio/consumer.h>
14*07e028d9SEdelweise Escala #include <linux/i2c.h>
15*07e028d9SEdelweise Escala #include <linux/leds.h>
16*07e028d9SEdelweise Escala #include <linux/module.h>
17*07e028d9SEdelweise Escala #include <linux/property.h>
18*07e028d9SEdelweise Escala #include <linux/regmap.h>
19*07e028d9SEdelweise Escala #include <linux/types.h>
20*07e028d9SEdelweise Escala 
21*07e028d9SEdelweise Escala /* LTC3220 Registers */
22*07e028d9SEdelweise Escala #define LTC3220_COMMAND_REG				0x00
23*07e028d9SEdelweise Escala #define   LTC3220_QUICK_WRITE_MASK			BIT(0)
24*07e028d9SEdelweise Escala #define   LTC3220_SHUTDOWN_MASK				BIT(3)
25*07e028d9SEdelweise Escala 
26*07e028d9SEdelweise Escala #define LTC3220_ULED_REG(x)				(0x01 + (x))
27*07e028d9SEdelweise Escala #define   LTC3220_LED_CURRENT_MASK			GENMASK(5, 0)
28*07e028d9SEdelweise Escala #define   LTC3220_LED_MODE_MASK				GENMASK(7, 6)
29*07e028d9SEdelweise Escala 
30*07e028d9SEdelweise Escala #define LTC3220_GRAD_BLINK_REG				0x13
31*07e028d9SEdelweise Escala #define   LTC3220_GRADATION_MASK			GENMASK(2, 0)
32*07e028d9SEdelweise Escala #define   LTC3220_GRADATION_DIRECTION_MASK		BIT(0)
33*07e028d9SEdelweise Escala #define   LTC3220_GRADATION_PERIOD_MASK			GENMASK(2, 1)
34*07e028d9SEdelweise Escala #define   LTC3220_BLINK_MASK				GENMASK(4, 3)
35*07e028d9SEdelweise Escala 
36*07e028d9SEdelweise Escala #define LTC3220_NUM_LEDS				18
37*07e028d9SEdelweise Escala #define LTC3220_MAX_BRIGHTNESS				63
38*07e028d9SEdelweise Escala 
39*07e028d9SEdelweise Escala #define LTC3220_GRADATION_RAMP_TIME_240MS		240
40*07e028d9SEdelweise Escala #define LTC3220_GRADATION_RAMP_TIME_480MS		480
41*07e028d9SEdelweise Escala 
42*07e028d9SEdelweise Escala #define LTC3220_BLINK_ON_156MS				156
43*07e028d9SEdelweise Escala #define LTC3220_BLINK_ON_625MS				625
44*07e028d9SEdelweise Escala #define LTC3220_BLINK_PERIOD_1250MS			1250
45*07e028d9SEdelweise Escala #define LTC3220_BLINK_PERIOD_2500MS			2500
46*07e028d9SEdelweise Escala 
47*07e028d9SEdelweise Escala #define LTC3220_BLINK_SHORT_ON_TIME			BIT(0)
48*07e028d9SEdelweise Escala #define LTC3220_BLINK_LONG_PERIOD			BIT(1)
49*07e028d9SEdelweise Escala 
50*07e028d9SEdelweise Escala enum ltc3220_led_mode {
51*07e028d9SEdelweise Escala 	LTC3220_NORMAL_MODE,
52*07e028d9SEdelweise Escala 	LTC3220_BLINK_MODE,
53*07e028d9SEdelweise Escala 	LTC3220_GRADATION_MODE,
54*07e028d9SEdelweise Escala };
55*07e028d9SEdelweise Escala 
56*07e028d9SEdelweise Escala enum ltc3220_blink_mode {
57*07e028d9SEdelweise Escala 	LTC3220_BLINK_MODE_625MS_1250MS,
58*07e028d9SEdelweise Escala 	LTC3220_BLINK_MODE_156MS_1250MS,
59*07e028d9SEdelweise Escala 	LTC3220_BLINK_MODE_625MS_2500MS,
60*07e028d9SEdelweise Escala 	LTC3220_BLINK_MODE_156MS_2500MS
61*07e028d9SEdelweise Escala };
62*07e028d9SEdelweise Escala 
63*07e028d9SEdelweise Escala enum ltc3220_gradation_mode {
64*07e028d9SEdelweise Escala 	LTC3220_GRADATION_MODE_DISABLED,
65*07e028d9SEdelweise Escala 	LTC3220_GRADATION_MODE_240MS_RAMP_TIME,
66*07e028d9SEdelweise Escala 	LTC3220_GRADATION_MODE_480MS_RAMP_TIME,
67*07e028d9SEdelweise Escala 	LTC3220_GRADATION_MODE_960MS_RAMP_TIME
68*07e028d9SEdelweise Escala };
69*07e028d9SEdelweise Escala 
70*07e028d9SEdelweise Escala static const struct regmap_config ltc3220_regmap_config = {
71*07e028d9SEdelweise Escala 	.reg_bits = 8,
72*07e028d9SEdelweise Escala 	.val_bits = 8,
73*07e028d9SEdelweise Escala 	.max_register = LTC3220_GRAD_BLINK_REG,
74*07e028d9SEdelweise Escala 	.cache_type = REGCACHE_FLAT_S,
75*07e028d9SEdelweise Escala };
76*07e028d9SEdelweise Escala 
77*07e028d9SEdelweise Escala struct ltc3220_uled_cfg {
78*07e028d9SEdelweise Escala 	struct led_classdev led_cdev;
79*07e028d9SEdelweise Escala 	u8 reg_value;
80*07e028d9SEdelweise Escala 	u8 led_index;
81*07e028d9SEdelweise Escala 	bool registered;
82*07e028d9SEdelweise Escala };
83*07e028d9SEdelweise Escala 
84*07e028d9SEdelweise Escala struct ltc3220 {
85*07e028d9SEdelweise Escala 	struct ltc3220_uled_cfg uled_cfg[LTC3220_NUM_LEDS];
86*07e028d9SEdelweise Escala 	struct regmap *regmap;
87*07e028d9SEdelweise Escala 	struct mutex lock;
88*07e028d9SEdelweise Escala };
89*07e028d9SEdelweise Escala 
90*07e028d9SEdelweise Escala /*
91*07e028d9SEdelweise Escala  * Set LED brightness. Hardware supports 0-63 brightness levels.
92*07e028d9SEdelweise Escala  * Mode switching (blink/gradation) is handled through dedicated callbacks.
93*07e028d9SEdelweise Escala  *
94*07e028d9SEdelweise Escala  * In aggregated mode only a single LED (reg = 1) is registered and the
95*07e028d9SEdelweise Escala  * hardware quick-write feature propagates the write to all 18 channels, so
96*07e028d9SEdelweise Escala  * there is no need to update the other registers explicitly.
97*07e028d9SEdelweise Escala  */
98*07e028d9SEdelweise Escala static int __ltc3220_set_led_data(struct ltc3220 *ltc3220,
99*07e028d9SEdelweise Escala 				  struct ltc3220_uled_cfg *uled_cfg,
100*07e028d9SEdelweise Escala 				  enum led_brightness brightness)
101*07e028d9SEdelweise Escala {
102*07e028d9SEdelweise Escala 	int ret;
103*07e028d9SEdelweise Escala 
104*07e028d9SEdelweise Escala 	brightness &= LTC3220_LED_CURRENT_MASK;
105*07e028d9SEdelweise Escala 
106*07e028d9SEdelweise Escala 	ret = regmap_write(ltc3220->regmap, LTC3220_ULED_REG(uled_cfg->led_index),
107*07e028d9SEdelweise Escala 			   brightness);
108*07e028d9SEdelweise Escala 	if (ret)
109*07e028d9SEdelweise Escala 		return ret;
110*07e028d9SEdelweise Escala 
111*07e028d9SEdelweise Escala 	uled_cfg->reg_value = brightness;
112*07e028d9SEdelweise Escala 
113*07e028d9SEdelweise Escala 	return 0;
114*07e028d9SEdelweise Escala }
115*07e028d9SEdelweise Escala 
116*07e028d9SEdelweise Escala static int ltc3220_set_led_data(struct led_classdev *led_cdev,
117*07e028d9SEdelweise Escala 				enum led_brightness brightness)
118*07e028d9SEdelweise Escala {
119*07e028d9SEdelweise Escala 	struct ltc3220_uled_cfg *uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg,
120*07e028d9SEdelweise Escala 							 led_cdev);
121*07e028d9SEdelweise Escala 	struct ltc3220 *ltc3220 = container_of(uled_cfg - uled_cfg->led_index, struct ltc3220,
122*07e028d9SEdelweise Escala 					       uled_cfg[0]);
123*07e028d9SEdelweise Escala 	int ret;
124*07e028d9SEdelweise Escala 
125*07e028d9SEdelweise Escala 	mutex_lock(&ltc3220->lock);
126*07e028d9SEdelweise Escala 	ret = __ltc3220_set_led_data(ltc3220, uled_cfg, brightness);
127*07e028d9SEdelweise Escala 	mutex_unlock(&ltc3220->lock);
128*07e028d9SEdelweise Escala 
129*07e028d9SEdelweise Escala 	return ret;
130*07e028d9SEdelweise Escala }
131*07e028d9SEdelweise Escala 
132*07e028d9SEdelweise Escala static enum led_brightness ltc3220_get_led_data(struct led_classdev *led_cdev)
133*07e028d9SEdelweise Escala {
134*07e028d9SEdelweise Escala 	struct ltc3220_uled_cfg *uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg,
135*07e028d9SEdelweise Escala 							 led_cdev);
136*07e028d9SEdelweise Escala 
137*07e028d9SEdelweise Escala 	return uled_cfg->reg_value;
138*07e028d9SEdelweise Escala }
139*07e028d9SEdelweise Escala 
140*07e028d9SEdelweise Escala /*
141*07e028d9SEdelweise Escala  * LTC3220 pattern support for hardware-assisted breathing/gradation.
142*07e028d9SEdelweise Escala  * The hardware supports 3 gradation ramp times (240ms, 480ms, 960ms)
143*07e028d9SEdelweise Escala  * and can ramp up or down. The gradation period and direction are chip-global
144*07e028d9SEdelweise Escala  * registers (LTC3220_GRAD_BLINK_REG), affecting all 18 channels simultaneously.
145*07e028d9SEdelweise Escala  * This is a hardware limitation, not a driver bug.
146*07e028d9SEdelweise Escala  *
147*07e028d9SEdelweise Escala  * Pattern array interpretation:
148*07e028d9SEdelweise Escala  *   pattern[0].brightness = start brightness (0-63)
149*07e028d9SEdelweise Escala  *   pattern[0].delta_t = ramp time in milliseconds
150*07e028d9SEdelweise Escala  *   pattern[1].brightness = end brightness (0-63)
151*07e028d9SEdelweise Escala  *   pattern[1].delta_t = (optional, can be 0 or same as pattern[0].delta_t)
152*07e028d9SEdelweise Escala  */
153*07e028d9SEdelweise Escala static int ltc3220_pattern_set(struct led_classdev *led_cdev,
154*07e028d9SEdelweise Escala 			       struct led_pattern *pattern,
155*07e028d9SEdelweise Escala 			       u32 len, int repeat)
156*07e028d9SEdelweise Escala {
157*07e028d9SEdelweise Escala 	struct ltc3220_uled_cfg *uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg,
158*07e028d9SEdelweise Escala 							 led_cdev);
159*07e028d9SEdelweise Escala 	struct ltc3220 *ltc3220 = container_of(uled_cfg - uled_cfg->led_index, struct ltc3220,
160*07e028d9SEdelweise Escala 					       uled_cfg[0]);
161*07e028d9SEdelweise Escala 	u8 gradation_period;
162*07e028d9SEdelweise Escala 	u8 start_brightness;
163*07e028d9SEdelweise Escala 	u8 end_brightness;
164*07e028d9SEdelweise Escala 	u8 gradation_val;
165*07e028d9SEdelweise Escala 	u8 led_mode;
166*07e028d9SEdelweise Escala 	bool is_increasing;
167*07e028d9SEdelweise Escala 	int ret;
168*07e028d9SEdelweise Escala 
169*07e028d9SEdelweise Escala 	if (len != 2)
170*07e028d9SEdelweise Escala 		return -EINVAL;
171*07e028d9SEdelweise Escala 
172*07e028d9SEdelweise Escala 	start_brightness = clamp_val(pattern[0].brightness, 0, LTC3220_LED_CURRENT_MASK);
173*07e028d9SEdelweise Escala 	end_brightness = clamp_val(pattern[1].brightness, 0, LTC3220_LED_CURRENT_MASK);
174*07e028d9SEdelweise Escala 
175*07e028d9SEdelweise Escala 	is_increasing = end_brightness > start_brightness;
176*07e028d9SEdelweise Escala 
177*07e028d9SEdelweise Escala 	if (pattern[0].delta_t == 0)
178*07e028d9SEdelweise Escala 		gradation_period = LTC3220_GRADATION_MODE_DISABLED;
179*07e028d9SEdelweise Escala 	else if (pattern[0].delta_t <= LTC3220_GRADATION_RAMP_TIME_240MS)
180*07e028d9SEdelweise Escala 		gradation_period = LTC3220_GRADATION_MODE_240MS_RAMP_TIME;
181*07e028d9SEdelweise Escala 	else if (pattern[0].delta_t <= LTC3220_GRADATION_RAMP_TIME_480MS)
182*07e028d9SEdelweise Escala 		gradation_period = LTC3220_GRADATION_MODE_480MS_RAMP_TIME;
183*07e028d9SEdelweise Escala 	else
184*07e028d9SEdelweise Escala 		gradation_period = LTC3220_GRADATION_MODE_960MS_RAMP_TIME;
185*07e028d9SEdelweise Escala 
186*07e028d9SEdelweise Escala 	gradation_val = FIELD_PREP(LTC3220_GRADATION_PERIOD_MASK, gradation_period);
187*07e028d9SEdelweise Escala 	gradation_val |= FIELD_PREP(LTC3220_GRADATION_DIRECTION_MASK, is_increasing);
188*07e028d9SEdelweise Escala 
189*07e028d9SEdelweise Escala 	/*
190*07e028d9SEdelweise Escala 	 * With the ramp disabled (delta_t == 0) there is no gradation to run,
191*07e028d9SEdelweise Escala 	 * so apply the end brightness directly in NORMAL mode instead of
192*07e028d9SEdelweise Escala 	 * leaving the channel in gradation mode with a disabled ramp.
193*07e028d9SEdelweise Escala 	 */
194*07e028d9SEdelweise Escala 	led_mode = gradation_period == LTC3220_GRADATION_MODE_DISABLED ?
195*07e028d9SEdelweise Escala 		   LTC3220_NORMAL_MODE : LTC3220_GRADATION_MODE;
196*07e028d9SEdelweise Escala 
197*07e028d9SEdelweise Escala 	mutex_lock(&ltc3220->lock);
198*07e028d9SEdelweise Escala 
199*07e028d9SEdelweise Escala 	ret = regmap_update_bits(ltc3220->regmap, LTC3220_GRAD_BLINK_REG,
200*07e028d9SEdelweise Escala 				 LTC3220_GRADATION_MASK, gradation_val);
201*07e028d9SEdelweise Escala 	if (ret)
202*07e028d9SEdelweise Escala 		goto unlock;
203*07e028d9SEdelweise Escala 
204*07e028d9SEdelweise Escala 	if (led_mode == LTC3220_GRADATION_MODE) {
205*07e028d9SEdelweise Escala 		ret = regmap_write(ltc3220->regmap, LTC3220_ULED_REG(uled_cfg->led_index),
206*07e028d9SEdelweise Escala 				   start_brightness & LTC3220_LED_CURRENT_MASK);
207*07e028d9SEdelweise Escala 		if (ret)
208*07e028d9SEdelweise Escala 			goto unlock;
209*07e028d9SEdelweise Escala 
210*07e028d9SEdelweise Escala 		ret = regmap_write(ltc3220->regmap, LTC3220_ULED_REG(uled_cfg->led_index),
211*07e028d9SEdelweise Escala 				   FIELD_PREP(LTC3220_LED_MODE_MASK, led_mode) |
212*07e028d9SEdelweise Escala 				   (end_brightness & LTC3220_LED_CURRENT_MASK));
213*07e028d9SEdelweise Escala 		if (ret)
214*07e028d9SEdelweise Escala 			goto unlock;
215*07e028d9SEdelweise Escala 
216*07e028d9SEdelweise Escala 		uled_cfg->reg_value = end_brightness;
217*07e028d9SEdelweise Escala 	} else {
218*07e028d9SEdelweise Escala 		ret = __ltc3220_set_led_data(ltc3220, uled_cfg, end_brightness);
219*07e028d9SEdelweise Escala 		if (ret)
220*07e028d9SEdelweise Escala 			goto unlock;
221*07e028d9SEdelweise Escala 	}
222*07e028d9SEdelweise Escala 
223*07e028d9SEdelweise Escala unlock:
224*07e028d9SEdelweise Escala 	mutex_unlock(&ltc3220->lock);
225*07e028d9SEdelweise Escala 	return ret;
226*07e028d9SEdelweise Escala }
227*07e028d9SEdelweise Escala 
228*07e028d9SEdelweise Escala static int ltc3220_pattern_clear(struct led_classdev *led_cdev)
229*07e028d9SEdelweise Escala {
230*07e028d9SEdelweise Escala 	struct ltc3220_uled_cfg *uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg,
231*07e028d9SEdelweise Escala 							 led_cdev);
232*07e028d9SEdelweise Escala 	struct ltc3220 *ltc3220 = container_of(uled_cfg - uled_cfg->led_index, struct ltc3220,
233*07e028d9SEdelweise Escala 					       uled_cfg[0]);
234*07e028d9SEdelweise Escala 	int ret;
235*07e028d9SEdelweise Escala 
236*07e028d9SEdelweise Escala 	mutex_lock(&ltc3220->lock);
237*07e028d9SEdelweise Escala 
238*07e028d9SEdelweise Escala 	ret = regmap_update_bits(ltc3220->regmap, LTC3220_ULED_REG(uled_cfg->led_index),
239*07e028d9SEdelweise Escala 				 LTC3220_LED_MODE_MASK, LTC3220_NORMAL_MODE);
240*07e028d9SEdelweise Escala 	if (ret)
241*07e028d9SEdelweise Escala 		goto unlock;
242*07e028d9SEdelweise Escala 
243*07e028d9SEdelweise Escala 	ret = __ltc3220_set_led_data(ltc3220, uled_cfg, LED_OFF);
244*07e028d9SEdelweise Escala 
245*07e028d9SEdelweise Escala unlock:
246*07e028d9SEdelweise Escala 	mutex_unlock(&ltc3220->lock);
247*07e028d9SEdelweise Escala 	return ret;
248*07e028d9SEdelweise Escala }
249*07e028d9SEdelweise Escala 
250*07e028d9SEdelweise Escala /*
251*07e028d9SEdelweise Escala  * LTC3220 has a global blink configuration that affects all LEDs.
252*07e028d9SEdelweise Escala  * This implementation allows per-LED blink requests via sysfs, but setting
253*07e028d9SEdelweise Escala  * blink on any LED reprograms the timing for all 18 channels simultaneously.
254*07e028d9SEdelweise Escala  * The delay values are mapped to the hardware's discrete blink rates.
255*07e028d9SEdelweise Escala  *
256*07e028d9SEdelweise Escala  * HARDWARE LIMITATION: This is not a driver bug. Per-LED blink timing control
257*07e028d9SEdelweise Escala  * is not possible with this hardware due to the global blink register.
258*07e028d9SEdelweise Escala  */
259*07e028d9SEdelweise Escala static int ltc3220_blink_set(struct led_classdev *led_cdev,
260*07e028d9SEdelweise Escala 			     unsigned long *delay_on,
261*07e028d9SEdelweise Escala 			     unsigned long *delay_off)
262*07e028d9SEdelweise Escala {
263*07e028d9SEdelweise Escala 	struct ltc3220_uled_cfg *uled_cfg = container_of(led_cdev, struct ltc3220_uled_cfg,
264*07e028d9SEdelweise Escala 							 led_cdev);
265*07e028d9SEdelweise Escala 	struct ltc3220 *ltc3220 = container_of(uled_cfg - uled_cfg->led_index, struct ltc3220,
266*07e028d9SEdelweise Escala 					       uled_cfg[0]);
267*07e028d9SEdelweise Escala 	u8 blink_brightness;
268*07e028d9SEdelweise Escala 	u8 blink_mode = 0;
269*07e028d9SEdelweise Escala 	int ret;
270*07e028d9SEdelweise Escala 
271*07e028d9SEdelweise Escala 	if (*delay_on <= LTC3220_BLINK_ON_156MS)
272*07e028d9SEdelweise Escala 		blink_mode = LTC3220_BLINK_SHORT_ON_TIME;
273*07e028d9SEdelweise Escala 
274*07e028d9SEdelweise Escala 	if (*delay_on + *delay_off > LTC3220_BLINK_PERIOD_1250MS)
275*07e028d9SEdelweise Escala 		blink_mode |= LTC3220_BLINK_LONG_PERIOD;
276*07e028d9SEdelweise Escala 
277*07e028d9SEdelweise Escala 	switch (blink_mode) {
278*07e028d9SEdelweise Escala 	case LTC3220_BLINK_MODE_625MS_1250MS:
279*07e028d9SEdelweise Escala 		*delay_on = LTC3220_BLINK_ON_625MS;
280*07e028d9SEdelweise Escala 		*delay_off = LTC3220_BLINK_PERIOD_1250MS - LTC3220_BLINK_ON_625MS;
281*07e028d9SEdelweise Escala 		break;
282*07e028d9SEdelweise Escala 	case LTC3220_BLINK_MODE_156MS_1250MS:
283*07e028d9SEdelweise Escala 		*delay_on = LTC3220_BLINK_ON_156MS;
284*07e028d9SEdelweise Escala 		*delay_off = LTC3220_BLINK_PERIOD_1250MS - LTC3220_BLINK_ON_156MS;
285*07e028d9SEdelweise Escala 		break;
286*07e028d9SEdelweise Escala 	case LTC3220_BLINK_MODE_625MS_2500MS:
287*07e028d9SEdelweise Escala 		*delay_on = LTC3220_BLINK_ON_625MS;
288*07e028d9SEdelweise Escala 		*delay_off = LTC3220_BLINK_PERIOD_2500MS - LTC3220_BLINK_ON_625MS;
289*07e028d9SEdelweise Escala 		break;
290*07e028d9SEdelweise Escala 	case LTC3220_BLINK_MODE_156MS_2500MS:
291*07e028d9SEdelweise Escala 		*delay_on = LTC3220_BLINK_ON_156MS;
292*07e028d9SEdelweise Escala 		*delay_off = LTC3220_BLINK_PERIOD_2500MS - LTC3220_BLINK_ON_156MS;
293*07e028d9SEdelweise Escala 		break;
294*07e028d9SEdelweise Escala 	}
295*07e028d9SEdelweise Escala 
296*07e028d9SEdelweise Escala 	mutex_lock(&ltc3220->lock);
297*07e028d9SEdelweise Escala 
298*07e028d9SEdelweise Escala 	ret = regmap_update_bits(ltc3220->regmap, LTC3220_GRAD_BLINK_REG,
299*07e028d9SEdelweise Escala 				 LTC3220_BLINK_MASK, FIELD_PREP(LTC3220_BLINK_MASK, blink_mode));
300*07e028d9SEdelweise Escala 	if (ret)
301*07e028d9SEdelweise Escala 		goto unlock;
302*07e028d9SEdelweise Escala 
303*07e028d9SEdelweise Escala 	blink_brightness = uled_cfg->reg_value ? : led_cdev->max_brightness;
304*07e028d9SEdelweise Escala 
305*07e028d9SEdelweise Escala 	ret = regmap_write(ltc3220->regmap, LTC3220_ULED_REG(uled_cfg->led_index),
306*07e028d9SEdelweise Escala 			    FIELD_PREP(LTC3220_LED_MODE_MASK, LTC3220_BLINK_MODE) |
307*07e028d9SEdelweise Escala 			    (blink_brightness & LTC3220_LED_CURRENT_MASK));
308*07e028d9SEdelweise Escala 	if (ret)
309*07e028d9SEdelweise Escala 		goto unlock;
310*07e028d9SEdelweise Escala 
311*07e028d9SEdelweise Escala 	uled_cfg->reg_value = blink_brightness;
312*07e028d9SEdelweise Escala 
313*07e028d9SEdelweise Escala unlock:
314*07e028d9SEdelweise Escala 	mutex_unlock(&ltc3220->lock);
315*07e028d9SEdelweise Escala 	return ret;
316*07e028d9SEdelweise Escala }
317*07e028d9SEdelweise Escala 
318*07e028d9SEdelweise Escala static void ltc3220_reset_gpio_action(void *data)
319*07e028d9SEdelweise Escala {
320*07e028d9SEdelweise Escala 	struct gpio_desc *reset_gpio = data;
321*07e028d9SEdelweise Escala 
322*07e028d9SEdelweise Escala 	gpiod_set_value_cansleep(reset_gpio, 1);
323*07e028d9SEdelweise Escala }
324*07e028d9SEdelweise Escala 
325*07e028d9SEdelweise Escala static int ltc3220_reset(struct ltc3220 *ltc3220, struct i2c_client *client)
326*07e028d9SEdelweise Escala {
327*07e028d9SEdelweise Escala 	struct gpio_desc *reset_gpio;
328*07e028d9SEdelweise Escala 	int ret;
329*07e028d9SEdelweise Escala 
330*07e028d9SEdelweise Escala 	reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
331*07e028d9SEdelweise Escala 	if (IS_ERR(reset_gpio))
332*07e028d9SEdelweise Escala 		return dev_err_probe(&client->dev, PTR_ERR(reset_gpio), "Failed on reset GPIO\n");
333*07e028d9SEdelweise Escala 
334*07e028d9SEdelweise Escala 	if (reset_gpio) {
335*07e028d9SEdelweise Escala 		usleep_range(10000, 12000);
336*07e028d9SEdelweise Escala 		gpiod_set_value_cansleep(reset_gpio, 0);
337*07e028d9SEdelweise Escala 		usleep_range(10000, 12000);
338*07e028d9SEdelweise Escala 
339*07e028d9SEdelweise Escala 		ret = devm_add_action_or_reset(&client->dev, ltc3220_reset_gpio_action,
340*07e028d9SEdelweise Escala 					       reset_gpio);
341*07e028d9SEdelweise Escala 		if (ret)
342*07e028d9SEdelweise Escala 			return ret;
343*07e028d9SEdelweise Escala 	}
344*07e028d9SEdelweise Escala 
345*07e028d9SEdelweise Escala 	ret = regmap_write(ltc3220->regmap, LTC3220_COMMAND_REG, 0);
346*07e028d9SEdelweise Escala 	if (ret)
347*07e028d9SEdelweise Escala 		return ret;
348*07e028d9SEdelweise Escala 
349*07e028d9SEdelweise Escala 	for (int i = 0; i < LTC3220_NUM_LEDS; i++) {
350*07e028d9SEdelweise Escala 		ret = regmap_write(ltc3220->regmap, LTC3220_ULED_REG(i), 0);
351*07e028d9SEdelweise Escala 		if (ret)
352*07e028d9SEdelweise Escala 			return ret;
353*07e028d9SEdelweise Escala 	}
354*07e028d9SEdelweise Escala 
355*07e028d9SEdelweise Escala 	return regmap_write(ltc3220->regmap, LTC3220_GRAD_BLINK_REG, 0);
356*07e028d9SEdelweise Escala }
357*07e028d9SEdelweise Escala 
358*07e028d9SEdelweise Escala static int ltc3220_suspend(struct device *dev)
359*07e028d9SEdelweise Escala {
360*07e028d9SEdelweise Escala 	struct ltc3220 *ltc3220 = i2c_get_clientdata(to_i2c_client(dev));
361*07e028d9SEdelweise Escala 	int ret;
362*07e028d9SEdelweise Escala 
363*07e028d9SEdelweise Escala 	ret = regmap_update_bits(ltc3220->regmap, LTC3220_COMMAND_REG,
364*07e028d9SEdelweise Escala 				 LTC3220_SHUTDOWN_MASK, LTC3220_SHUTDOWN_MASK);
365*07e028d9SEdelweise Escala 	if (ret)
366*07e028d9SEdelweise Escala 		return ret;
367*07e028d9SEdelweise Escala 
368*07e028d9SEdelweise Escala 	regcache_mark_dirty(ltc3220->regmap);
369*07e028d9SEdelweise Escala 
370*07e028d9SEdelweise Escala 	return 0;
371*07e028d9SEdelweise Escala }
372*07e028d9SEdelweise Escala 
373*07e028d9SEdelweise Escala static int ltc3220_resume(struct device *dev)
374*07e028d9SEdelweise Escala {
375*07e028d9SEdelweise Escala 	struct ltc3220 *ltc3220 = i2c_get_clientdata(to_i2c_client(dev));
376*07e028d9SEdelweise Escala 	bool quick_write_enabled;
377*07e028d9SEdelweise Escala 	unsigned int command_reg;
378*07e028d9SEdelweise Escala 	int ret;
379*07e028d9SEdelweise Escala 
380*07e028d9SEdelweise Escala 	ret = regmap_read(ltc3220->regmap, LTC3220_COMMAND_REG, &command_reg);
381*07e028d9SEdelweise Escala 	if (ret)
382*07e028d9SEdelweise Escala 		return ret;
383*07e028d9SEdelweise Escala 
384*07e028d9SEdelweise Escala 	quick_write_enabled = command_reg & LTC3220_QUICK_WRITE_MASK;
385*07e028d9SEdelweise Escala 
386*07e028d9SEdelweise Escala 	if (quick_write_enabled) {
387*07e028d9SEdelweise Escala 		ret = regmap_update_bits(ltc3220->regmap, LTC3220_COMMAND_REG,
388*07e028d9SEdelweise Escala 					 LTC3220_QUICK_WRITE_MASK, 0);
389*07e028d9SEdelweise Escala 		if (ret)
390*07e028d9SEdelweise Escala 			return ret;
391*07e028d9SEdelweise Escala 	}
392*07e028d9SEdelweise Escala 
393*07e028d9SEdelweise Escala 	ret = regmap_update_bits(ltc3220->regmap, LTC3220_COMMAND_REG,
394*07e028d9SEdelweise Escala 				 LTC3220_SHUTDOWN_MASK, 0);
395*07e028d9SEdelweise Escala 	if (ret)
396*07e028d9SEdelweise Escala 		return ret;
397*07e028d9SEdelweise Escala 
398*07e028d9SEdelweise Escala 	usleep_range(10000, 12000);
399*07e028d9SEdelweise Escala 
400*07e028d9SEdelweise Escala 	ret = regcache_sync(ltc3220->regmap);
401*07e028d9SEdelweise Escala 	if (ret)
402*07e028d9SEdelweise Escala 		return ret;
403*07e028d9SEdelweise Escala 
404*07e028d9SEdelweise Escala 	if (quick_write_enabled) {
405*07e028d9SEdelweise Escala 		ret = regmap_update_bits(ltc3220->regmap, LTC3220_COMMAND_REG,
406*07e028d9SEdelweise Escala 					 LTC3220_QUICK_WRITE_MASK,
407*07e028d9SEdelweise Escala 					 LTC3220_QUICK_WRITE_MASK);
408*07e028d9SEdelweise Escala 		if (ret)
409*07e028d9SEdelweise Escala 			return ret;
410*07e028d9SEdelweise Escala 	}
411*07e028d9SEdelweise Escala 
412*07e028d9SEdelweise Escala 	return 0;
413*07e028d9SEdelweise Escala }
414*07e028d9SEdelweise Escala 
415*07e028d9SEdelweise Escala static DEFINE_SIMPLE_DEV_PM_OPS(ltc3220_pm_ops, ltc3220_suspend, ltc3220_resume);
416*07e028d9SEdelweise Escala 
417*07e028d9SEdelweise Escala static int ltc3220_probe(struct i2c_client *client)
418*07e028d9SEdelweise Escala {
419*07e028d9SEdelweise Escala 	struct ltc3220 *ltc3220;
420*07e028d9SEdelweise Escala 	bool aggregated_led_found = false;
421*07e028d9SEdelweise Escala 	int num_leds = 0;
422*07e028d9SEdelweise Escala 	u8 led_index = 0;
423*07e028d9SEdelweise Escala 	int ret;
424*07e028d9SEdelweise Escala 
425*07e028d9SEdelweise Escala 	ltc3220 = devm_kzalloc(&client->dev, sizeof(*ltc3220), GFP_KERNEL);
426*07e028d9SEdelweise Escala 	if (!ltc3220)
427*07e028d9SEdelweise Escala 		return -ENOMEM;
428*07e028d9SEdelweise Escala 
429*07e028d9SEdelweise Escala 	ltc3220->regmap = devm_regmap_init_i2c(client, &ltc3220_regmap_config);
430*07e028d9SEdelweise Escala 	if (IS_ERR(ltc3220->regmap))
431*07e028d9SEdelweise Escala 		return dev_err_probe(&client->dev, PTR_ERR(ltc3220->regmap),
432*07e028d9SEdelweise Escala 				     "Failed to initialize regmap\n");
433*07e028d9SEdelweise Escala 
434*07e028d9SEdelweise Escala 	ret = devm_mutex_init(&client->dev, &ltc3220->lock);
435*07e028d9SEdelweise Escala 	if (ret)
436*07e028d9SEdelweise Escala 		return ret;
437*07e028d9SEdelweise Escala 
438*07e028d9SEdelweise Escala 	i2c_set_clientdata(client, ltc3220);
439*07e028d9SEdelweise Escala 
440*07e028d9SEdelweise Escala 	ret = ltc3220_reset(ltc3220, client);
441*07e028d9SEdelweise Escala 	if (ret)
442*07e028d9SEdelweise Escala 		return dev_err_probe(&client->dev, ret, "Failed to reset device\n");
443*07e028d9SEdelweise Escala 
444*07e028d9SEdelweise Escala 	/* First pass: validate configuration and set up LED structures */
445*07e028d9SEdelweise Escala 	device_for_each_child_node_scoped(&client->dev, child) {
446*07e028d9SEdelweise Escala 		struct ltc3220_uled_cfg *led;
447*07e028d9SEdelweise Escala 		u32 source;
448*07e028d9SEdelweise Escala 
449*07e028d9SEdelweise Escala 		ret = fwnode_property_read_u32(child, "reg", &source);
450*07e028d9SEdelweise Escala 		if (ret)
451*07e028d9SEdelweise Escala 			return dev_err_probe(&client->dev, ret, "Couldn't read LED address\n");
452*07e028d9SEdelweise Escala 
453*07e028d9SEdelweise Escala 		if (!source || source > LTC3220_NUM_LEDS)
454*07e028d9SEdelweise Escala 			return dev_err_probe(&client->dev, -EINVAL, "LED address out of range\n");
455*07e028d9SEdelweise Escala 
456*07e028d9SEdelweise Escala 		if (fwnode_property_present(child, "led-sources")) {
457*07e028d9SEdelweise Escala 			u32 led_sources[LTC3220_NUM_LEDS];
458*07e028d9SEdelweise Escala 			int count;
459*07e028d9SEdelweise Escala 
460*07e028d9SEdelweise Escala 			if (source != 1)
461*07e028d9SEdelweise Escala 				return dev_err_probe(&client->dev, -EINVAL,
462*07e028d9SEdelweise Escala 						     "Aggregated LED out of range\n");
463*07e028d9SEdelweise Escala 
464*07e028d9SEdelweise Escala 			if (aggregated_led_found)
465*07e028d9SEdelweise Escala 				return dev_err_probe(&client->dev, -EINVAL,
466*07e028d9SEdelweise Escala 						     "One Aggregated LED only\n");
467*07e028d9SEdelweise Escala 
468*07e028d9SEdelweise Escala 			count = fwnode_property_count_u32(child, "led-sources");
469*07e028d9SEdelweise Escala 			if (count != LTC3220_NUM_LEDS)
470*07e028d9SEdelweise Escala 				return dev_err_probe(&client->dev, -EINVAL,
471*07e028d9SEdelweise Escala 						     "Aggregated mode requires all %d outputs in led-sources, got %d\n",
472*07e028d9SEdelweise Escala 						     LTC3220_NUM_LEDS, count);
473*07e028d9SEdelweise Escala 
474*07e028d9SEdelweise Escala 			ret = fwnode_property_read_u32_array(child, "led-sources",
475*07e028d9SEdelweise Escala 							     led_sources, LTC3220_NUM_LEDS);
476*07e028d9SEdelweise Escala 			if (ret)
477*07e028d9SEdelweise Escala 				return dev_err_probe(&client->dev, ret,
478*07e028d9SEdelweise Escala 						     "Failed to read led-sources array\n");
479*07e028d9SEdelweise Escala 
480*07e028d9SEdelweise Escala 			/*
481*07e028d9SEdelweise Escala 			 * Validate array contents for DT correctness. The hardware
482*07e028d9SEdelweise Escala 			 * quick-write broadcasts to all 18 channels regardless of
483*07e028d9SEdelweise Escala 			 * array contents, but checking helps catch DT mistakes.
484*07e028d9SEdelweise Escala 			 */
485*07e028d9SEdelweise Escala 			for (int i = 0; i < LTC3220_NUM_LEDS; i++) {
486*07e028d9SEdelweise Escala 				if (led_sources[i] < 1 || led_sources[i] > LTC3220_NUM_LEDS)
487*07e028d9SEdelweise Escala 					return dev_err_probe(&client->dev, -EINVAL,
488*07e028d9SEdelweise Escala 							     "Invalid output %u in led-sources\n",
489*07e028d9SEdelweise Escala 							     led_sources[i]);
490*07e028d9SEdelweise Escala 			}
491*07e028d9SEdelweise Escala 
492*07e028d9SEdelweise Escala 			aggregated_led_found = true;
493*07e028d9SEdelweise Escala 		}
494*07e028d9SEdelweise Escala 
495*07e028d9SEdelweise Escala 		num_leds++;
496*07e028d9SEdelweise Escala 
497*07e028d9SEdelweise Escala 		/* LED node reg/index/address goes from 1 to 18 */
498*07e028d9SEdelweise Escala 		led_index = source - 1;
499*07e028d9SEdelweise Escala 		led = &ltc3220->uled_cfg[led_index];
500*07e028d9SEdelweise Escala 
501*07e028d9SEdelweise Escala 		if (led->registered)
502*07e028d9SEdelweise Escala 			return dev_err_probe(&client->dev, -EINVAL,
503*07e028d9SEdelweise Escala 					     "Duplicate LED reg %u found\n", source);
504*07e028d9SEdelweise Escala 
505*07e028d9SEdelweise Escala 		led->registered = true;
506*07e028d9SEdelweise Escala 		led->led_index = led_index;
507*07e028d9SEdelweise Escala 		led->reg_value = 0;
508*07e028d9SEdelweise Escala 		led->led_cdev.brightness_set_blocking = ltc3220_set_led_data;
509*07e028d9SEdelweise Escala 		led->led_cdev.brightness_get = ltc3220_get_led_data;
510*07e028d9SEdelweise Escala 		led->led_cdev.max_brightness = LTC3220_MAX_BRIGHTNESS;
511*07e028d9SEdelweise Escala 		led->led_cdev.blink_set = ltc3220_blink_set;
512*07e028d9SEdelweise Escala 		led->led_cdev.pattern_set = ltc3220_pattern_set;
513*07e028d9SEdelweise Escala 		led->led_cdev.pattern_clear = ltc3220_pattern_clear;
514*07e028d9SEdelweise Escala 	}
515*07e028d9SEdelweise Escala 
516*07e028d9SEdelweise Escala 	/*
517*07e028d9SEdelweise Escala 	 * Aggregated LED mode uses hardware quick-write to control all 18 LEDs
518*07e028d9SEdelweise Escala 	 * simultaneously. This is mutually exclusive with individual LED control.
519*07e028d9SEdelweise Escala 	 * See Documentation/devicetree/bindings/leds/adi,ltc3220.yaml for details
520*07e028d9SEdelweise Escala 	 * on how to configure aggregated LED mode.
521*07e028d9SEdelweise Escala 	 */
522*07e028d9SEdelweise Escala 	if (aggregated_led_found && num_leds > 1)
523*07e028d9SEdelweise Escala 		return dev_err_probe(&client->dev, -EINVAL,
524*07e028d9SEdelweise Escala 				     "Aggregated LED must be the only LED node\n");
525*07e028d9SEdelweise Escala 
526*07e028d9SEdelweise Escala 	if (num_leds == 0)
527*07e028d9SEdelweise Escala 		return dev_err_probe(&client->dev, -EINVAL,
528*07e028d9SEdelweise Escala 				     "No LED nodes found in device tree\n");
529*07e028d9SEdelweise Escala 
530*07e028d9SEdelweise Escala 	if (aggregated_led_found) {
531*07e028d9SEdelweise Escala 		ret = regmap_update_bits(ltc3220->regmap,
532*07e028d9SEdelweise Escala 						LTC3220_COMMAND_REG,
533*07e028d9SEdelweise Escala 						LTC3220_QUICK_WRITE_MASK,
534*07e028d9SEdelweise Escala 						LTC3220_QUICK_WRITE_MASK);
535*07e028d9SEdelweise Escala 		if (ret)
536*07e028d9SEdelweise Escala 			return dev_err_probe(&client->dev, ret,
537*07e028d9SEdelweise Escala 						"Failed to set quick write mode\n");
538*07e028d9SEdelweise Escala 	}
539*07e028d9SEdelweise Escala 
540*07e028d9SEdelweise Escala 	/* Second pass: register LEDs after validation */
541*07e028d9SEdelweise Escala 	device_for_each_child_node_scoped(&client->dev, child) {
542*07e028d9SEdelweise Escala 		struct led_init_data init_data = {};
543*07e028d9SEdelweise Escala 		struct ltc3220_uled_cfg *led;
544*07e028d9SEdelweise Escala 		u32 source;
545*07e028d9SEdelweise Escala 
546*07e028d9SEdelweise Escala 		ret = fwnode_property_read_u32(child, "reg", &source);
547*07e028d9SEdelweise Escala 		if (ret)
548*07e028d9SEdelweise Escala 			return ret;
549*07e028d9SEdelweise Escala 
550*07e028d9SEdelweise Escala 		if (!source || source > LTC3220_NUM_LEDS)
551*07e028d9SEdelweise Escala 			return dev_err_probe(&client->dev, -EINVAL,
552*07e028d9SEdelweise Escala 					     "LED address out of range in second pass\n");
553*07e028d9SEdelweise Escala 
554*07e028d9SEdelweise Escala 		init_data.fwnode = child;
555*07e028d9SEdelweise Escala 		init_data.devicename = "ltc3220";
556*07e028d9SEdelweise Escala 
557*07e028d9SEdelweise Escala 		led_index = source - 1;
558*07e028d9SEdelweise Escala 		led = &ltc3220->uled_cfg[led_index];
559*07e028d9SEdelweise Escala 
560*07e028d9SEdelweise Escala 		ret = devm_led_classdev_register_ext(&client->dev, &led->led_cdev, &init_data);
561*07e028d9SEdelweise Escala 		if (ret)
562*07e028d9SEdelweise Escala 			return dev_err_probe(&client->dev, ret, "Failed to register LED class\n");
563*07e028d9SEdelweise Escala 	}
564*07e028d9SEdelweise Escala 
565*07e028d9SEdelweise Escala 	return 0;
566*07e028d9SEdelweise Escala }
567*07e028d9SEdelweise Escala 
568*07e028d9SEdelweise Escala static const struct of_device_id ltc3220_of_match[] = {
569*07e028d9SEdelweise Escala 	{ .compatible = "adi,ltc3220" },
570*07e028d9SEdelweise Escala 	{ }
571*07e028d9SEdelweise Escala };
572*07e028d9SEdelweise Escala MODULE_DEVICE_TABLE(of, ltc3220_of_match);
573*07e028d9SEdelweise Escala 
574*07e028d9SEdelweise Escala static struct i2c_driver ltc3220_led_driver = {
575*07e028d9SEdelweise Escala 	.driver = {
576*07e028d9SEdelweise Escala 		.name = "ltc3220",
577*07e028d9SEdelweise Escala 		.of_match_table = ltc3220_of_match,
578*07e028d9SEdelweise Escala 		.pm = pm_sleep_ptr(&ltc3220_pm_ops),
579*07e028d9SEdelweise Escala 	},
580*07e028d9SEdelweise Escala 	.probe = ltc3220_probe,
581*07e028d9SEdelweise Escala };
582*07e028d9SEdelweise Escala module_i2c_driver(ltc3220_led_driver);
583*07e028d9SEdelweise Escala 
584*07e028d9SEdelweise Escala MODULE_AUTHOR("Edelweise Escala <edelweise.escala@analog.com>");
585*07e028d9SEdelweise Escala MODULE_DESCRIPTION("LED driver for LTC3220 controllers");
586*07e028d9SEdelweise Escala MODULE_LICENSE("GPL");
587