xref: /openbmc/linux/drivers/leds/leds-lm3532.c (revision 070a0eed)
1bc1b8492SDan Murphy // SPDX-License-Identifier: GPL-2.0
2bc1b8492SDan Murphy // TI LM3532 LED driver
3bc1b8492SDan Murphy // Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
4bc1b8492SDan Murphy 
5bc1b8492SDan Murphy #include <linux/i2c.h>
6bc1b8492SDan Murphy #include <linux/leds.h>
7bc1b8492SDan Murphy #include <linux/slab.h>
8bc1b8492SDan Murphy #include <linux/regmap.h>
9bc1b8492SDan Murphy #include <linux/types.h>
10bc1b8492SDan Murphy #include <linux/regulator/consumer.h>
11bc1b8492SDan Murphy #include <linux/module.h>
12bc1b8492SDan Murphy #include <uapi/linux/uleds.h>
13bc1b8492SDan Murphy #include <linux/gpio/consumer.h>
14bc1b8492SDan Murphy 
15bc1b8492SDan Murphy #define LM3532_NAME "lm3532-led"
16bc1b8492SDan Murphy #define LM3532_BL_MODE_MANUAL	0x00
17bc1b8492SDan Murphy #define LM3532_BL_MODE_ALS	0x01
18bc1b8492SDan Murphy 
19bc1b8492SDan Murphy #define LM3532_REG_OUTPUT_CFG	0x10
20bc1b8492SDan Murphy #define LM3532_REG_STARTSHUT_RAMP	0x11
21bc1b8492SDan Murphy #define LM3532_REG_RT_RAMP	0x12
22bc1b8492SDan Murphy #define LM3532_REG_PWM_A_CFG	0x13
23bc1b8492SDan Murphy #define LM3532_REG_PWM_B_CFG	0x14
24bc1b8492SDan Murphy #define LM3532_REG_PWM_C_CFG	0x15
25bc1b8492SDan Murphy #define LM3532_REG_ZONE_CFG_A	0x16
264c905450SDan Murphy #define LM3532_REG_CTRL_A_FS_CURR	0x17
27bc1b8492SDan Murphy #define LM3532_REG_ZONE_CFG_B	0x18
284c905450SDan Murphy #define LM3532_REG_CTRL_B_FS_CURR	0x19
29bc1b8492SDan Murphy #define LM3532_REG_ZONE_CFG_C	0x1a
304c905450SDan Murphy #define LM3532_REG_CTRL_C_FS_CURR	0x1b
31bc1b8492SDan Murphy #define LM3532_REG_ENABLE	0x1d
32bc1b8492SDan Murphy #define LM3532_ALS_CONFIG	0x23
33bc1b8492SDan Murphy #define LM3532_REG_ZN_0_HI	0x60
34bc1b8492SDan Murphy #define LM3532_REG_ZN_0_LO	0x61
35bc1b8492SDan Murphy #define LM3532_REG_ZN_1_HI	0x62
36bc1b8492SDan Murphy #define LM3532_REG_ZN_1_LO	0x63
37bc1b8492SDan Murphy #define LM3532_REG_ZN_2_HI	0x64
38bc1b8492SDan Murphy #define LM3532_REG_ZN_2_LO	0x65
39bc1b8492SDan Murphy #define LM3532_REG_ZN_3_HI	0x66
40bc1b8492SDan Murphy #define LM3532_REG_ZN_3_LO	0x67
4113123940SDan Murphy #define LM3532_REG_ZONE_TRGT_A	0x70
4213123940SDan Murphy #define LM3532_REG_ZONE_TRGT_B	0x75
4313123940SDan Murphy #define LM3532_REG_ZONE_TRGT_C	0x7a
44bc1b8492SDan Murphy #define LM3532_REG_MAX		0x7e
45bc1b8492SDan Murphy 
466559ac32SDan Murphy /* Control Enable */
47bc1b8492SDan Murphy #define LM3532_CTRL_A_ENABLE	BIT(0)
48bc1b8492SDan Murphy #define LM3532_CTRL_B_ENABLE	BIT(1)
49bc1b8492SDan Murphy #define LM3532_CTRL_C_ENABLE	BIT(2)
50bc1b8492SDan Murphy 
51bc1b8492SDan Murphy /* PWM Zone Control */
52bc1b8492SDan Murphy #define LM3532_PWM_ZONE_MASK	0x7c
53bc1b8492SDan Murphy #define LM3532_PWM_ZONE_0_EN	BIT(2)
54bc1b8492SDan Murphy #define LM3532_PWM_ZONE_1_EN	BIT(3)
55bc1b8492SDan Murphy #define LM3532_PWM_ZONE_2_EN	BIT(4)
56bc1b8492SDan Murphy #define LM3532_PWM_ZONE_3_EN	BIT(5)
57bc1b8492SDan Murphy #define LM3532_PWM_ZONE_4_EN	BIT(6)
58bc1b8492SDan Murphy 
59bc1b8492SDan Murphy /* Brightness Configuration */
60bc1b8492SDan Murphy #define LM3532_I2C_CTRL		BIT(0)
61bc1b8492SDan Murphy #define LM3532_ALS_CTRL		0
62bc1b8492SDan Murphy #define LM3532_LINEAR_MAP	BIT(1)
63bc1b8492SDan Murphy #define LM3532_ZONE_MASK	(BIT(2) | BIT(3) | BIT(4))
64bc1b8492SDan Murphy #define LM3532_ZONE_0		0
65bc1b8492SDan Murphy #define LM3532_ZONE_1		BIT(2)
66bc1b8492SDan Murphy #define LM3532_ZONE_2		BIT(3)
67bc1b8492SDan Murphy #define LM3532_ZONE_3		(BIT(2) | BIT(3))
68bc1b8492SDan Murphy #define LM3532_ZONE_4		BIT(4)
69bc1b8492SDan Murphy 
70bc1b8492SDan Murphy #define LM3532_ENABLE_ALS	BIT(3)
71bc1b8492SDan Murphy #define LM3532_ALS_SEL_SHIFT	6
72bc1b8492SDan Murphy 
73bc1b8492SDan Murphy /* Zone Boundary Register */
74bc1b8492SDan Murphy #define LM3532_ALS_WINDOW_mV	2000
75bc1b8492SDan Murphy #define LM3532_ALS_ZB_MAX	4
76bc1b8492SDan Murphy #define LM3532_ALS_OFFSET_mV	2
77bc1b8492SDan Murphy 
78bc1b8492SDan Murphy #define LM3532_CONTROL_A	0
79bc1b8492SDan Murphy #define LM3532_CONTROL_B	1
80bc1b8492SDan Murphy #define LM3532_CONTROL_C	2
81bc1b8492SDan Murphy #define LM3532_MAX_CONTROL_BANKS 3
82bc1b8492SDan Murphy #define LM3532_MAX_LED_STRINGS	3
83bc1b8492SDan Murphy 
84bc1b8492SDan Murphy #define LM3532_OUTPUT_CFG_MASK	0x3
85bc1b8492SDan Murphy #define LM3532_BRT_VAL_ADJUST	8
86bc1b8492SDan Murphy #define LM3532_RAMP_DOWN_SHIFT	3
87bc1b8492SDan Murphy 
88bc1b8492SDan Murphy #define LM3532_NUM_RAMP_VALS	8
89bc1b8492SDan Murphy #define LM3532_NUM_AVG_VALS	8
90bc1b8492SDan Murphy #define LM3532_NUM_IMP_VALS	32
91bc1b8492SDan Murphy 
92517ea49aSDan Murphy #define LM3532_FS_CURR_MIN	5000
93517ea49aSDan Murphy #define LM3532_FS_CURR_MAX	29800
94517ea49aSDan Murphy #define LM3532_FS_CURR_STEP	800
95517ea49aSDan Murphy 
96bc1b8492SDan Murphy /*
97bc1b8492SDan Murphy  * struct lm3532_als_data
98bc1b8492SDan Murphy  * @config - value of ALS configuration register
99bc1b8492SDan Murphy  * @als1_imp_sel - value of ALS1 resistor select register
100bc1b8492SDan Murphy  * @als2_imp_sel - value of ALS2 resistor select register
101bc1b8492SDan Murphy  * @als_avrg_time - ALS averaging time
102bc1b8492SDan Murphy  * @als_input_mode - ALS input mode for brightness control
103bc1b8492SDan Murphy  * @als_vmin - Minimum ALS voltage
104bc1b8492SDan Murphy  * @als_vmax - Maximum ALS voltage
105bc1b8492SDan Murphy  * @zone_lo - values of ALS lo ZB(Zone Boundary) registers
106bc1b8492SDan Murphy  * @zone_hi - values of ALS hi ZB(Zone Boundary) registers
107bc1b8492SDan Murphy  */
108bc1b8492SDan Murphy struct lm3532_als_data {
109bc1b8492SDan Murphy 	u8 config;
110bc1b8492SDan Murphy 	u8 als1_imp_sel;
111bc1b8492SDan Murphy 	u8 als2_imp_sel;
112bc1b8492SDan Murphy 	u8 als_avrg_time;
113bc1b8492SDan Murphy 	u8 als_input_mode;
114bc1b8492SDan Murphy 	u32 als_vmin;
115bc1b8492SDan Murphy 	u32 als_vmax;
116bc1b8492SDan Murphy 	u8 zones_lo[LM3532_ALS_ZB_MAX];
117bc1b8492SDan Murphy 	u8 zones_hi[LM3532_ALS_ZB_MAX];
118bc1b8492SDan Murphy };
119bc1b8492SDan Murphy 
120bc1b8492SDan Murphy /**
121bc1b8492SDan Murphy  * struct lm3532_led
122bc1b8492SDan Murphy  * @led_dev: led class device
123bc1b8492SDan Murphy  * @priv - Pointer the device data structure
124bc1b8492SDan Murphy  * @control_bank - Control bank the LED is associated to
125bc1b8492SDan Murphy  * @mode - Mode of the LED string
12613123940SDan Murphy  * @ctrl_brt_pointer - Zone target register that controls the sink
127bc1b8492SDan Murphy  * @num_leds - Number of LED strings are supported in this array
128517ea49aSDan Murphy  * @full_scale_current - The full-scale current setting for the current sink.
129bc1b8492SDan Murphy  * @led_strings - The LED strings supported in this array
130070a0eedSTony Lindgren  * @enabled - Enabled status
131bc1b8492SDan Murphy  * @label - LED label
132bc1b8492SDan Murphy  */
133bc1b8492SDan Murphy struct lm3532_led {
134bc1b8492SDan Murphy 	struct led_classdev led_dev;
135bc1b8492SDan Murphy 	struct lm3532_data *priv;
136bc1b8492SDan Murphy 
137bc1b8492SDan Murphy 	int control_bank;
138bc1b8492SDan Murphy 	int mode;
13913123940SDan Murphy 	int ctrl_brt_pointer;
140bc1b8492SDan Murphy 	int num_leds;
141517ea49aSDan Murphy 	int full_scale_current;
142070a0eedSTony Lindgren 	int enabled:1;
143bc1b8492SDan Murphy 	u32 led_strings[LM3532_MAX_CONTROL_BANKS];
144bc1b8492SDan Murphy 	char label[LED_MAX_NAME_SIZE];
145bc1b8492SDan Murphy };
146bc1b8492SDan Murphy 
147bc1b8492SDan Murphy /**
148bc1b8492SDan Murphy  * struct lm3532_data
149bc1b8492SDan Murphy  * @enable_gpio - Hardware enable gpio
150bc1b8492SDan Murphy  * @regulator: regulator
151bc1b8492SDan Murphy  * @client: i2c client
152bc1b8492SDan Murphy  * @regmap - Devices register map
153bc1b8492SDan Murphy  * @dev - Pointer to the devices device struct
154bc1b8492SDan Murphy  * @lock - Lock for reading/writing the device
155bc1b8492SDan Murphy  * @als_data - Pointer to the als data struct
156bc1b8492SDan Murphy  * @runtime_ramp_up - Runtime ramp up setting
157bc1b8492SDan Murphy  * @runtime_ramp_down - Runtime ramp down setting
158bc1b8492SDan Murphy  * @leds - Array of LED strings
159bc1b8492SDan Murphy  */
160bc1b8492SDan Murphy struct lm3532_data {
161bc1b8492SDan Murphy 	struct gpio_desc *enable_gpio;
162bc1b8492SDan Murphy 	struct regulator *regulator;
163bc1b8492SDan Murphy 	struct i2c_client *client;
164bc1b8492SDan Murphy 	struct regmap *regmap;
165bc1b8492SDan Murphy 	struct device *dev;
166bc1b8492SDan Murphy 	struct mutex lock;
167bc1b8492SDan Murphy 
168bc1b8492SDan Murphy 	struct lm3532_als_data *als_data;
169bc1b8492SDan Murphy 
170bc1b8492SDan Murphy 	u32 runtime_ramp_up;
171bc1b8492SDan Murphy 	u32 runtime_ramp_down;
172bc1b8492SDan Murphy 
173bc1b8492SDan Murphy 	struct lm3532_led leds[];
174bc1b8492SDan Murphy };
175bc1b8492SDan Murphy 
176bc1b8492SDan Murphy static const struct reg_default lm3532_reg_defs[] = {
177bc1b8492SDan Murphy 	{LM3532_REG_OUTPUT_CFG, 0xe4},
178bc1b8492SDan Murphy 	{LM3532_REG_STARTSHUT_RAMP, 0xc0},
179bc1b8492SDan Murphy 	{LM3532_REG_RT_RAMP, 0xc0},
180bc1b8492SDan Murphy 	{LM3532_REG_PWM_A_CFG, 0x82},
181bc1b8492SDan Murphy 	{LM3532_REG_PWM_B_CFG, 0x82},
182bc1b8492SDan Murphy 	{LM3532_REG_PWM_C_CFG, 0x82},
183bc1b8492SDan Murphy 	{LM3532_REG_ZONE_CFG_A, 0xf1},
1844c905450SDan Murphy 	{LM3532_REG_CTRL_A_FS_CURR, 0xf3},
185bc1b8492SDan Murphy 	{LM3532_REG_ZONE_CFG_B, 0xf1},
1864c905450SDan Murphy 	{LM3532_REG_CTRL_B_FS_CURR, 0xf3},
187bc1b8492SDan Murphy 	{LM3532_REG_ZONE_CFG_C, 0xf1},
1884c905450SDan Murphy 	{LM3532_REG_CTRL_C_FS_CURR, 0xf3},
189bc1b8492SDan Murphy 	{LM3532_REG_ENABLE, 0xf8},
190bc1b8492SDan Murphy 	{LM3532_ALS_CONFIG, 0x44},
191bc1b8492SDan Murphy 	{LM3532_REG_ZN_0_HI, 0x35},
192bc1b8492SDan Murphy 	{LM3532_REG_ZN_0_LO, 0x33},
193bc1b8492SDan Murphy 	{LM3532_REG_ZN_1_HI, 0x6a},
194bc1b8492SDan Murphy 	{LM3532_REG_ZN_1_LO, 0x66},
195bc1b8492SDan Murphy 	{LM3532_REG_ZN_2_HI, 0xa1},
196bc1b8492SDan Murphy 	{LM3532_REG_ZN_2_LO, 0x99},
197bc1b8492SDan Murphy 	{LM3532_REG_ZN_3_HI, 0xdc},
198bc1b8492SDan Murphy 	{LM3532_REG_ZN_3_LO, 0xcc},
199bc1b8492SDan Murphy };
200bc1b8492SDan Murphy 
201bc1b8492SDan Murphy static const struct regmap_config lm3532_regmap_config = {
202bc1b8492SDan Murphy 	.reg_bits = 8,
203bc1b8492SDan Murphy 	.val_bits = 8,
204bc1b8492SDan Murphy 
205bc1b8492SDan Murphy 	.max_register = LM3532_REG_MAX,
206bc1b8492SDan Murphy 	.reg_defaults = lm3532_reg_defs,
207bc1b8492SDan Murphy 	.num_reg_defaults = ARRAY_SIZE(lm3532_reg_defs),
208bc1b8492SDan Murphy 	.cache_type = REGCACHE_FLAT,
209bc1b8492SDan Murphy };
210bc1b8492SDan Murphy 
211bc1b8492SDan Murphy const static int als_imp_table[LM3532_NUM_IMP_VALS] = {37000, 18500, 12330,
212bc1b8492SDan Murphy 						       92500, 7400, 6170, 5290,
213bc1b8492SDan Murphy 						       4630, 4110, 3700, 3360,
214bc1b8492SDan Murphy 						       3080, 2850, 2640, 2440,
215bc1b8492SDan Murphy 						       2310, 2180, 2060, 1950,
216bc1b8492SDan Murphy 						       1850, 1760, 1680, 1610,
217bc1b8492SDan Murphy 						       1540, 1480, 1420, 1370,
218bc1b8492SDan Murphy 						       1320, 1280, 1230, 1190};
219bc1b8492SDan Murphy static int lm3532_get_als_imp_index(int als_imped)
220bc1b8492SDan Murphy {
221bc1b8492SDan Murphy 	int i;
222bc1b8492SDan Murphy 
223bc1b8492SDan Murphy 	if (als_imped > als_imp_table[1])
224bc1b8492SDan Murphy 		return 0;
225bc1b8492SDan Murphy 
226bc1b8492SDan Murphy 	if (als_imped < als_imp_table[LM3532_NUM_IMP_VALS - 1])
227bc1b8492SDan Murphy 		return LM3532_NUM_IMP_VALS - 1;
228bc1b8492SDan Murphy 
229bc1b8492SDan Murphy 	for (i = 1; i < LM3532_NUM_IMP_VALS; i++) {
230bc1b8492SDan Murphy 		if (als_imped == als_imp_table[i])
231bc1b8492SDan Murphy 			return i;
232bc1b8492SDan Murphy 
233bc1b8492SDan Murphy 		/* Find an approximate index by looking up the table */
234bc1b8492SDan Murphy 		if (als_imped < als_imp_table[i - 1] &&
235bc1b8492SDan Murphy 		    als_imped > als_imp_table[i]) {
236bc1b8492SDan Murphy 			if (als_imped - als_imp_table[i - 1] <
237bc1b8492SDan Murphy 			    als_imp_table[i] - als_imped)
238bc1b8492SDan Murphy 				return i + 1;
239bc1b8492SDan Murphy 			else
240bc1b8492SDan Murphy 				return i;
241bc1b8492SDan Murphy 		}
242bc1b8492SDan Murphy 	}
243bc1b8492SDan Murphy 
244bc1b8492SDan Murphy 	return -EINVAL;
245bc1b8492SDan Murphy }
246bc1b8492SDan Murphy 
247bc1b8492SDan Murphy static int lm3532_get_index(const int table[], int size, int value)
248bc1b8492SDan Murphy {
249bc1b8492SDan Murphy 	int i;
250bc1b8492SDan Murphy 
251bc1b8492SDan Murphy 	for (i = 1; i < size; i++) {
252bc1b8492SDan Murphy 		if (value == table[i])
253bc1b8492SDan Murphy 			return i;
254bc1b8492SDan Murphy 
255bc1b8492SDan Murphy 		/* Find an approximate index by looking up the table */
256bc1b8492SDan Murphy 		if (value > table[i - 1] &&
257bc1b8492SDan Murphy 		    value < table[i]) {
258bc1b8492SDan Murphy 			if (value - table[i - 1] < table[i] - value)
259bc1b8492SDan Murphy 				return i - 1;
260bc1b8492SDan Murphy 			else
261bc1b8492SDan Murphy 				return i;
262bc1b8492SDan Murphy 		}
263bc1b8492SDan Murphy 	}
264bc1b8492SDan Murphy 
265bc1b8492SDan Murphy 	return -EINVAL;
266bc1b8492SDan Murphy }
267bc1b8492SDan Murphy 
268bc1b8492SDan Murphy const static int als_avrg_table[LM3532_NUM_AVG_VALS] = {17920, 35840, 71680,
269bc1b8492SDan Murphy 							1433360, 286720, 573440,
270bc1b8492SDan Murphy 							1146880, 2293760};
271bc1b8492SDan Murphy static int lm3532_get_als_avg_index(int avg_time)
272bc1b8492SDan Murphy {
273bc1b8492SDan Murphy 	if (avg_time <= als_avrg_table[0])
274bc1b8492SDan Murphy 		return 0;
275bc1b8492SDan Murphy 
276bc1b8492SDan Murphy 	if (avg_time > als_avrg_table[LM3532_NUM_AVG_VALS - 1])
277bc1b8492SDan Murphy 		return LM3532_NUM_AVG_VALS - 1;
278bc1b8492SDan Murphy 
279bc1b8492SDan Murphy 	return lm3532_get_index(&als_avrg_table[0], LM3532_NUM_AVG_VALS,
280bc1b8492SDan Murphy 				avg_time);
281bc1b8492SDan Murphy }
282bc1b8492SDan Murphy 
283bc1b8492SDan Murphy const static int ramp_table[LM3532_NUM_RAMP_VALS] = { 8, 1024, 2048, 4096, 8192,
284bc1b8492SDan Murphy 						     16384, 32768, 65536};
285bc1b8492SDan Murphy static int lm3532_get_ramp_index(int ramp_time)
286bc1b8492SDan Murphy {
287bc1b8492SDan Murphy 	if (ramp_time <= ramp_table[0])
288bc1b8492SDan Murphy 		return 0;
289bc1b8492SDan Murphy 
290bc1b8492SDan Murphy 	if (ramp_time > ramp_table[LM3532_NUM_RAMP_VALS - 1])
291bc1b8492SDan Murphy 		return LM3532_NUM_RAMP_VALS - 1;
292bc1b8492SDan Murphy 
293bc1b8492SDan Murphy 	return lm3532_get_index(&ramp_table[0], LM3532_NUM_RAMP_VALS,
294bc1b8492SDan Murphy 				ramp_time);
295bc1b8492SDan Murphy }
296bc1b8492SDan Murphy 
297070a0eedSTony Lindgren /* Caller must take care of locking */
298bc1b8492SDan Murphy static int lm3532_led_enable(struct lm3532_led *led_data)
299bc1b8492SDan Murphy {
300bc1b8492SDan Murphy 	int ctrl_en_val = BIT(led_data->control_bank);
301bc1b8492SDan Murphy 	int ret;
302bc1b8492SDan Murphy 
303070a0eedSTony Lindgren 	if (led_data->enabled)
304070a0eedSTony Lindgren 		return 0;
305070a0eedSTony Lindgren 
306bc1b8492SDan Murphy 	ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE,
307bc1b8492SDan Murphy 					 ctrl_en_val, ctrl_en_val);
308bc1b8492SDan Murphy 	if (ret) {
309bc1b8492SDan Murphy 		dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret);
310bc1b8492SDan Murphy 		return ret;
311bc1b8492SDan Murphy 	}
312bc1b8492SDan Murphy 
313070a0eedSTony Lindgren 	ret = regulator_enable(led_data->priv->regulator);
314070a0eedSTony Lindgren 	if (ret < 0)
315070a0eedSTony Lindgren 		return ret;
316070a0eedSTony Lindgren 
317070a0eedSTony Lindgren 	led_data->enabled = 1;
318070a0eedSTony Lindgren 
319070a0eedSTony Lindgren 	return 0;
320bc1b8492SDan Murphy }
321bc1b8492SDan Murphy 
322070a0eedSTony Lindgren /* Caller must take care of locking */
323bc1b8492SDan Murphy static int lm3532_led_disable(struct lm3532_led *led_data)
324bc1b8492SDan Murphy {
325bc1b8492SDan Murphy 	int ctrl_en_val = BIT(led_data->control_bank);
326bc1b8492SDan Murphy 	int ret;
327bc1b8492SDan Murphy 
328070a0eedSTony Lindgren 	if (!led_data->enabled)
329070a0eedSTony Lindgren 		return 0;
330070a0eedSTony Lindgren 
331bc1b8492SDan Murphy 	ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE,
3326559ac32SDan Murphy 					 ctrl_en_val, 0);
333bc1b8492SDan Murphy 	if (ret) {
334bc1b8492SDan Murphy 		dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret);
335bc1b8492SDan Murphy 		return ret;
336bc1b8492SDan Murphy 	}
337bc1b8492SDan Murphy 
338070a0eedSTony Lindgren 	ret = regulator_disable(led_data->priv->regulator);
339070a0eedSTony Lindgren 	if (ret < 0)
340070a0eedSTony Lindgren 		return ret;
341070a0eedSTony Lindgren 
342070a0eedSTony Lindgren 	led_data->enabled = 0;
343070a0eedSTony Lindgren 
344070a0eedSTony Lindgren 	return 0;
345bc1b8492SDan Murphy }
346bc1b8492SDan Murphy 
347bc1b8492SDan Murphy static int lm3532_brightness_set(struct led_classdev *led_cdev,
348bc1b8492SDan Murphy 				 enum led_brightness brt_val)
349bc1b8492SDan Murphy {
350bc1b8492SDan Murphy 	struct lm3532_led *led =
351bc1b8492SDan Murphy 			container_of(led_cdev, struct lm3532_led, led_dev);
352bc1b8492SDan Murphy 	u8 brightness_reg;
353bc1b8492SDan Murphy 	int ret;
354bc1b8492SDan Murphy 
355bc1b8492SDan Murphy 	mutex_lock(&led->priv->lock);
356bc1b8492SDan Murphy 
3576559ac32SDan Murphy 	if (led->mode == LM3532_ALS_CTRL) {
358bc1b8492SDan Murphy 		if (brt_val > LED_OFF)
359bc1b8492SDan Murphy 			ret = lm3532_led_enable(led);
360bc1b8492SDan Murphy 		else
361bc1b8492SDan Murphy 			ret = lm3532_led_disable(led);
362bc1b8492SDan Murphy 
363bc1b8492SDan Murphy 		goto unlock;
364bc1b8492SDan Murphy 	}
365bc1b8492SDan Murphy 
366bc1b8492SDan Murphy 	if (brt_val == LED_OFF) {
367bc1b8492SDan Murphy 		ret = lm3532_led_disable(led);
368bc1b8492SDan Murphy 		goto unlock;
369bc1b8492SDan Murphy 	}
370bc1b8492SDan Murphy 
371bc1b8492SDan Murphy 	ret = lm3532_led_enable(led);
372bc1b8492SDan Murphy 	if (ret)
373bc1b8492SDan Murphy 		goto unlock;
374bc1b8492SDan Murphy 
37513123940SDan Murphy 	brightness_reg = LM3532_REG_ZONE_TRGT_A + led->control_bank * 5 +
37613123940SDan Murphy 			 (led->ctrl_brt_pointer >> 2);
377bc1b8492SDan Murphy 
378bc1b8492SDan Murphy 	ret = regmap_write(led->priv->regmap, brightness_reg, brt_val);
379bc1b8492SDan Murphy 
380bc1b8492SDan Murphy unlock:
381bc1b8492SDan Murphy 	mutex_unlock(&led->priv->lock);
382bc1b8492SDan Murphy 	return ret;
383bc1b8492SDan Murphy }
384bc1b8492SDan Murphy 
385bc1b8492SDan Murphy static int lm3532_init_registers(struct lm3532_led *led)
386bc1b8492SDan Murphy {
387bc1b8492SDan Murphy 	struct lm3532_data *drvdata = led->priv;
388bc1b8492SDan Murphy 	unsigned int runtime_ramp_val;
389bc1b8492SDan Murphy 	unsigned int output_cfg_val = 0;
390bc1b8492SDan Murphy 	unsigned int output_cfg_shift = 0;
391bc1b8492SDan Murphy 	unsigned int output_cfg_mask = 0;
39213123940SDan Murphy 	unsigned int brightness_config_reg;
39313123940SDan Murphy 	unsigned int brightness_config_val;
394517ea49aSDan Murphy 	int fs_current_reg;
395517ea49aSDan Murphy 	int fs_current_val;
396bc1b8492SDan Murphy 	int ret, i;
397bc1b8492SDan Murphy 
39813123940SDan Murphy 	if (drvdata->enable_gpio)
39913123940SDan Murphy 		gpiod_direction_output(drvdata->enable_gpio, 1);
40013123940SDan Murphy 
40113123940SDan Murphy 	brightness_config_reg = LM3532_REG_ZONE_CFG_A + led->control_bank * 2;
40213123940SDan Murphy 	/*
40313123940SDan Murphy 	 * This could be hard coded to the default value but the control
40413123940SDan Murphy 	 * brightness register may have changed during boot.
40513123940SDan Murphy 	 */
40613123940SDan Murphy 	ret = regmap_read(drvdata->regmap, brightness_config_reg,
40713123940SDan Murphy 			  &led->ctrl_brt_pointer);
40813123940SDan Murphy 	if (ret)
40913123940SDan Murphy 		return ret;
41013123940SDan Murphy 
41113123940SDan Murphy 	led->ctrl_brt_pointer &= LM3532_ZONE_MASK;
41213123940SDan Murphy 	brightness_config_val = led->ctrl_brt_pointer | led->mode;
41313123940SDan Murphy 	ret = regmap_write(drvdata->regmap, brightness_config_reg,
41413123940SDan Murphy 			   brightness_config_val);
41513123940SDan Murphy 	if (ret)
41613123940SDan Murphy 		return ret;
41713123940SDan Murphy 
418517ea49aSDan Murphy 	if (led->full_scale_current) {
419517ea49aSDan Murphy 		fs_current_reg = LM3532_REG_CTRL_A_FS_CURR + led->control_bank * 2;
420517ea49aSDan Murphy 		fs_current_val = (led->full_scale_current - LM3532_FS_CURR_MIN) /
421517ea49aSDan Murphy 				 LM3532_FS_CURR_STEP;
422517ea49aSDan Murphy 
423517ea49aSDan Murphy 		ret = regmap_write(drvdata->regmap, fs_current_reg,
424517ea49aSDan Murphy 				   fs_current_val);
425517ea49aSDan Murphy 		if (ret)
426517ea49aSDan Murphy 			return ret;
427517ea49aSDan Murphy 	}
428517ea49aSDan Murphy 
429bc1b8492SDan Murphy 	for (i = 0; i < led->num_leds; i++) {
430bc1b8492SDan Murphy 		output_cfg_shift = led->led_strings[i] * 2;
431bc1b8492SDan Murphy 		output_cfg_val |= (led->control_bank << output_cfg_shift);
432bc1b8492SDan Murphy 		output_cfg_mask |= LM3532_OUTPUT_CFG_MASK << output_cfg_shift;
433bc1b8492SDan Murphy 	}
434bc1b8492SDan Murphy 
435bc1b8492SDan Murphy 	ret = regmap_update_bits(drvdata->regmap, LM3532_REG_OUTPUT_CFG,
436bc1b8492SDan Murphy 				 output_cfg_mask, output_cfg_val);
437bc1b8492SDan Murphy 	if (ret)
438bc1b8492SDan Murphy 		return ret;
439bc1b8492SDan Murphy 
440bc1b8492SDan Murphy 	runtime_ramp_val = drvdata->runtime_ramp_up |
441bc1b8492SDan Murphy 			 (drvdata->runtime_ramp_down << LM3532_RAMP_DOWN_SHIFT);
442bc1b8492SDan Murphy 
443bc1b8492SDan Murphy 	return regmap_write(drvdata->regmap, LM3532_REG_RT_RAMP,
444bc1b8492SDan Murphy 			    runtime_ramp_val);
445bc1b8492SDan Murphy }
446bc1b8492SDan Murphy 
447bc1b8492SDan Murphy static int lm3532_als_configure(struct lm3532_data *priv,
448bc1b8492SDan Murphy 				struct lm3532_led *led)
449bc1b8492SDan Murphy {
450bc1b8492SDan Murphy 	struct lm3532_als_data *als = priv->als_data;
451bc1b8492SDan Murphy 	u32 als_vmin, als_vmax, als_vstep;
452bc1b8492SDan Murphy 	int zone_reg = LM3532_REG_ZN_0_HI;
453bc1b8492SDan Murphy 	int ret;
454bc1b8492SDan Murphy 	int i;
455bc1b8492SDan Murphy 
456bc1b8492SDan Murphy 	als_vmin = als->als_vmin;
457bc1b8492SDan Murphy 	als_vmax = als->als_vmax;
458bc1b8492SDan Murphy 
459bc1b8492SDan Murphy 	als_vstep = (als_vmax - als_vmin) / ((LM3532_ALS_ZB_MAX + 1) * 2);
460bc1b8492SDan Murphy 
461bc1b8492SDan Murphy 	for (i = 0; i < LM3532_ALS_ZB_MAX; i++) {
462bc1b8492SDan Murphy 		als->zones_lo[i] = ((als_vmin + als_vstep + (i * als_vstep)) *
463bc1b8492SDan Murphy 				LED_FULL) / 1000;
464bc1b8492SDan Murphy 		als->zones_hi[i] = ((als_vmin + LM3532_ALS_OFFSET_mV +
465bc1b8492SDan Murphy 				als_vstep + (i * als_vstep)) * LED_FULL) / 1000;
466bc1b8492SDan Murphy 
467bc1b8492SDan Murphy 		zone_reg = LM3532_REG_ZN_0_HI + i * 2;
468bc1b8492SDan Murphy 		ret = regmap_write(priv->regmap, zone_reg, als->zones_lo[i]);
469bc1b8492SDan Murphy 		if (ret)
470bc1b8492SDan Murphy 			return ret;
471bc1b8492SDan Murphy 
472bc1b8492SDan Murphy 		zone_reg += 1;
473bc1b8492SDan Murphy 		ret = regmap_write(priv->regmap, zone_reg, als->zones_hi[i]);
474bc1b8492SDan Murphy 		if (ret)
475bc1b8492SDan Murphy 			return ret;
476bc1b8492SDan Murphy 	}
477bc1b8492SDan Murphy 
478bc1b8492SDan Murphy 	als->config = (als->als_avrg_time | (LM3532_ENABLE_ALS) |
479bc1b8492SDan Murphy 		(als->als_input_mode << LM3532_ALS_SEL_SHIFT));
480bc1b8492SDan Murphy 
48113123940SDan Murphy 	return regmap_write(priv->regmap, LM3532_ALS_CONFIG, als->config);
482bc1b8492SDan Murphy }
483bc1b8492SDan Murphy 
484bc1b8492SDan Murphy static int lm3532_parse_als(struct lm3532_data *priv)
485bc1b8492SDan Murphy {
486bc1b8492SDan Murphy 	struct lm3532_als_data *als;
487bc1b8492SDan Murphy 	int als_avg_time;
488bc1b8492SDan Murphy 	int als_impedance;
489bc1b8492SDan Murphy 	int ret;
490bc1b8492SDan Murphy 
491bc1b8492SDan Murphy 	als = devm_kzalloc(priv->dev, sizeof(*als), GFP_KERNEL);
492bc1b8492SDan Murphy 	if (als == NULL)
493bc1b8492SDan Murphy 		return -ENOMEM;
494bc1b8492SDan Murphy 
495bc1b8492SDan Murphy 	ret = device_property_read_u32(&priv->client->dev, "ti,als-vmin",
496bc1b8492SDan Murphy 				       &als->als_vmin);
497bc1b8492SDan Murphy 	if (ret)
498bc1b8492SDan Murphy 		als->als_vmin = 0;
499bc1b8492SDan Murphy 
500bc1b8492SDan Murphy 	ret = device_property_read_u32(&priv->client->dev, "ti,als-vmax",
501bc1b8492SDan Murphy 				       &als->als_vmax);
502bc1b8492SDan Murphy 	if (ret)
503bc1b8492SDan Murphy 		als->als_vmax = LM3532_ALS_WINDOW_mV;
504bc1b8492SDan Murphy 
505bc1b8492SDan Murphy 	if (als->als_vmax > LM3532_ALS_WINDOW_mV) {
506bc1b8492SDan Murphy 		ret = -EINVAL;
507bc1b8492SDan Murphy 		return ret;
508bc1b8492SDan Murphy 	}
509bc1b8492SDan Murphy 
510bc1b8492SDan Murphy 	ret = device_property_read_u32(&priv->client->dev, "ti,als1-imp-sel",
511bc1b8492SDan Murphy 				      &als_impedance);
512bc1b8492SDan Murphy 	if (ret)
513bc1b8492SDan Murphy 		als->als1_imp_sel = 0;
514bc1b8492SDan Murphy 	else
515bc1b8492SDan Murphy 		als->als1_imp_sel = lm3532_get_als_imp_index(als_impedance);
516bc1b8492SDan Murphy 
517bc1b8492SDan Murphy 	ret = device_property_read_u32(&priv->client->dev, "ti,als2-imp-sel",
518bc1b8492SDan Murphy 				      &als_impedance);
519bc1b8492SDan Murphy 	if (ret)
520bc1b8492SDan Murphy 		als->als2_imp_sel = 0;
521bc1b8492SDan Murphy 	else
522bc1b8492SDan Murphy 		als->als2_imp_sel = lm3532_get_als_imp_index(als_impedance);
523bc1b8492SDan Murphy 
524bc1b8492SDan Murphy 	ret = device_property_read_u32(&priv->client->dev, "ti,als-avrg-time-us",
525bc1b8492SDan Murphy 				      &als_avg_time);
526bc1b8492SDan Murphy 	if (ret)
527bc1b8492SDan Murphy 		als->als_avrg_time = 0;
528bc1b8492SDan Murphy 	else
529bc1b8492SDan Murphy 		als->als_avrg_time = lm3532_get_als_avg_index(als_avg_time);
530bc1b8492SDan Murphy 
531bc1b8492SDan Murphy 	ret = device_property_read_u8(&priv->client->dev, "ti,als-input-mode",
532bc1b8492SDan Murphy 				      &als->als_input_mode);
533bc1b8492SDan Murphy 	if (ret)
534bc1b8492SDan Murphy 		als->als_input_mode = 0;
535bc1b8492SDan Murphy 
536bc1b8492SDan Murphy 	if (als->als_input_mode > LM3532_BL_MODE_ALS) {
537bc1b8492SDan Murphy 		ret = -EINVAL;
538bc1b8492SDan Murphy 		return ret;
539bc1b8492SDan Murphy 	}
540bc1b8492SDan Murphy 
541bc1b8492SDan Murphy 	priv->als_data = als;
542bc1b8492SDan Murphy 
543bc1b8492SDan Murphy 	return ret;
544bc1b8492SDan Murphy }
545bc1b8492SDan Murphy 
546bc1b8492SDan Murphy static int lm3532_parse_node(struct lm3532_data *priv)
547bc1b8492SDan Murphy {
548bc1b8492SDan Murphy 	struct fwnode_handle *child = NULL;
549bc1b8492SDan Murphy 	struct lm3532_led *led;
550bc1b8492SDan Murphy 	const char *name;
551bc1b8492SDan Murphy 	int control_bank;
552bc1b8492SDan Murphy 	u32 ramp_time;
553bc1b8492SDan Murphy 	size_t i = 0;
554bc1b8492SDan Murphy 	int ret;
555bc1b8492SDan Murphy 
556bc1b8492SDan Murphy 	priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev,
557bc1b8492SDan Murphy 						   "enable", GPIOD_OUT_LOW);
558bc1b8492SDan Murphy 	if (IS_ERR(priv->enable_gpio))
559bc1b8492SDan Murphy 		priv->enable_gpio = NULL;
560bc1b8492SDan Murphy 
561bc1b8492SDan Murphy 	priv->regulator = devm_regulator_get(&priv->client->dev, "vin");
562bc1b8492SDan Murphy 	if (IS_ERR(priv->regulator))
563bc1b8492SDan Murphy 		priv->regulator = NULL;
564bc1b8492SDan Murphy 
565bc1b8492SDan Murphy 	ret = device_property_read_u32(&priv->client->dev, "ramp-up-us",
566bc1b8492SDan Murphy 				       &ramp_time);
567bc1b8492SDan Murphy 	if (ret)
568bc1b8492SDan Murphy 		dev_info(&priv->client->dev, "ramp-up-ms property missing\n");
569bc1b8492SDan Murphy 	else
570bc1b8492SDan Murphy 		priv->runtime_ramp_up = lm3532_get_ramp_index(ramp_time);
571bc1b8492SDan Murphy 
572bc1b8492SDan Murphy 	ret = device_property_read_u32(&priv->client->dev, "ramp-down-us",
573bc1b8492SDan Murphy 				       &ramp_time);
574bc1b8492SDan Murphy 	if (ret)
575bc1b8492SDan Murphy 		dev_info(&priv->client->dev, "ramp-down-ms property missing\n");
576bc1b8492SDan Murphy 	else
577bc1b8492SDan Murphy 		priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time);
578bc1b8492SDan Murphy 
579bc1b8492SDan Murphy 	device_for_each_child_node(priv->dev, child) {
580bc1b8492SDan Murphy 		led = &priv->leds[i];
581bc1b8492SDan Murphy 
582bc1b8492SDan Murphy 		ret = fwnode_property_read_u32(child, "reg", &control_bank);
583bc1b8492SDan Murphy 		if (ret) {
584bc1b8492SDan Murphy 			dev_err(&priv->client->dev, "reg property missing\n");
585bc1b8492SDan Murphy 			fwnode_handle_put(child);
586bc1b8492SDan Murphy 			goto child_out;
587bc1b8492SDan Murphy 		}
588bc1b8492SDan Murphy 
589bc1b8492SDan Murphy 		if (control_bank > LM3532_CONTROL_C) {
590bc1b8492SDan Murphy 			dev_err(&priv->client->dev, "Control bank invalid\n");
591bc1b8492SDan Murphy 			continue;
592bc1b8492SDan Murphy 		}
593bc1b8492SDan Murphy 
594bc1b8492SDan Murphy 		led->control_bank = control_bank;
595bc1b8492SDan Murphy 
596bc1b8492SDan Murphy 		ret = fwnode_property_read_u32(child, "ti,led-mode",
597bc1b8492SDan Murphy 					       &led->mode);
598bc1b8492SDan Murphy 		if (ret) {
599bc1b8492SDan Murphy 			dev_err(&priv->client->dev, "ti,led-mode property missing\n");
600bc1b8492SDan Murphy 			fwnode_handle_put(child);
601bc1b8492SDan Murphy 			goto child_out;
602bc1b8492SDan Murphy 		}
603bc1b8492SDan Murphy 
604517ea49aSDan Murphy 		ret = fwnode_property_read_u32(child, "led-max-microamp",
605517ea49aSDan Murphy 					       &led->full_scale_current);
606517ea49aSDan Murphy 
607517ea49aSDan Murphy 		if (led->full_scale_current > LM3532_FS_CURR_MAX)
608517ea49aSDan Murphy 			led->full_scale_current = LM3532_FS_CURR_MAX;
609517ea49aSDan Murphy 
610bc1b8492SDan Murphy 		if (led->mode == LM3532_BL_MODE_ALS) {
6116559ac32SDan Murphy 			led->mode = LM3532_ALS_CTRL;
612bc1b8492SDan Murphy 			ret = lm3532_parse_als(priv);
613bc1b8492SDan Murphy 			if (ret)
614bc1b8492SDan Murphy 				dev_err(&priv->client->dev, "Failed to parse als\n");
615bc1b8492SDan Murphy 			else
616bc1b8492SDan Murphy 				lm3532_als_configure(priv, led);
6176559ac32SDan Murphy 		} else {
6186559ac32SDan Murphy 			led->mode = LM3532_I2C_CTRL;
619bc1b8492SDan Murphy 		}
620bc1b8492SDan Murphy 
621cc93c863SAndy Shevchenko 		led->num_leds = fwnode_property_count_u32(child, "led-sources");
622bc1b8492SDan Murphy 		if (led->num_leds > LM3532_MAX_LED_STRINGS) {
623bc1b8492SDan Murphy 			dev_err(&priv->client->dev, "To many LED string defined\n");
624bc1b8492SDan Murphy 			continue;
625bc1b8492SDan Murphy 		}
626bc1b8492SDan Murphy 
627bc1b8492SDan Murphy 		ret = fwnode_property_read_u32_array(child, "led-sources",
628bc1b8492SDan Murphy 						    led->led_strings,
629bc1b8492SDan Murphy 						    led->num_leds);
630bc1b8492SDan Murphy 		if (ret) {
631bc1b8492SDan Murphy 			dev_err(&priv->client->dev, "led-sources property missing\n");
632bc1b8492SDan Murphy 			fwnode_handle_put(child);
633bc1b8492SDan Murphy 			goto child_out;
634bc1b8492SDan Murphy 		}
635bc1b8492SDan Murphy 
636bc1b8492SDan Murphy 		fwnode_property_read_string(child, "linux,default-trigger",
637bc1b8492SDan Murphy 					    &led->led_dev.default_trigger);
638bc1b8492SDan Murphy 
639bc1b8492SDan Murphy 		ret = fwnode_property_read_string(child, "label", &name);
640bc1b8492SDan Murphy 		if (ret)
641bc1b8492SDan Murphy 			snprintf(led->label, sizeof(led->label),
642bc1b8492SDan Murphy 				"%s::", priv->client->name);
643bc1b8492SDan Murphy 		else
644bc1b8492SDan Murphy 			snprintf(led->label, sizeof(led->label),
645bc1b8492SDan Murphy 				 "%s:%s", priv->client->name, name);
646bc1b8492SDan Murphy 
647bc1b8492SDan Murphy 		led->priv = priv;
648bc1b8492SDan Murphy 		led->led_dev.name = led->label;
649bc1b8492SDan Murphy 		led->led_dev.brightness_set_blocking = lm3532_brightness_set;
650bc1b8492SDan Murphy 
651bc1b8492SDan Murphy 		ret = devm_led_classdev_register(priv->dev, &led->led_dev);
652bc1b8492SDan Murphy 		if (ret) {
653bc1b8492SDan Murphy 			dev_err(&priv->client->dev, "led register err: %d\n",
654bc1b8492SDan Murphy 				ret);
655bc1b8492SDan Murphy 			fwnode_handle_put(child);
656bc1b8492SDan Murphy 			goto child_out;
657bc1b8492SDan Murphy 		}
658bc1b8492SDan Murphy 
6596559ac32SDan Murphy 		ret = lm3532_init_registers(led);
6606559ac32SDan Murphy 		if (ret) {
6616559ac32SDan Murphy 			dev_err(&priv->client->dev, "register init err: %d\n",
6626559ac32SDan Murphy 				ret);
6636559ac32SDan Murphy 			fwnode_handle_put(child);
6646559ac32SDan Murphy 			goto child_out;
6656559ac32SDan Murphy 		}
666bc1b8492SDan Murphy 
667bc1b8492SDan Murphy 		i++;
668bc1b8492SDan Murphy 	}
669bc1b8492SDan Murphy 
670bc1b8492SDan Murphy child_out:
671bc1b8492SDan Murphy 	return ret;
672bc1b8492SDan Murphy }
673bc1b8492SDan Murphy 
674bc1b8492SDan Murphy static int lm3532_probe(struct i2c_client *client,
675bc1b8492SDan Murphy 			   const struct i2c_device_id *id)
676bc1b8492SDan Murphy {
677bc1b8492SDan Murphy 	struct lm3532_data *drvdata;
678bc1b8492SDan Murphy 	int ret = 0;
679bc1b8492SDan Murphy 	int count;
680bc1b8492SDan Murphy 
681bc1b8492SDan Murphy 	count = device_get_child_node_count(&client->dev);
682bc1b8492SDan Murphy 	if (!count) {
683bc1b8492SDan Murphy 		dev_err(&client->dev, "LEDs are not defined in device tree!");
684bc1b8492SDan Murphy 		return -ENODEV;
685bc1b8492SDan Murphy 	}
686bc1b8492SDan Murphy 
687bc1b8492SDan Murphy 	drvdata = devm_kzalloc(&client->dev, struct_size(drvdata, leds, count),
688bc1b8492SDan Murphy 			   GFP_KERNEL);
689bc1b8492SDan Murphy 	if (drvdata == NULL)
690bc1b8492SDan Murphy 		return -ENOMEM;
691bc1b8492SDan Murphy 
692bc1b8492SDan Murphy 	drvdata->client = client;
693bc1b8492SDan Murphy 	drvdata->dev = &client->dev;
694bc1b8492SDan Murphy 
695bc1b8492SDan Murphy 	drvdata->regmap = devm_regmap_init_i2c(client, &lm3532_regmap_config);
696bc1b8492SDan Murphy 	if (IS_ERR(drvdata->regmap)) {
697bc1b8492SDan Murphy 		ret = PTR_ERR(drvdata->regmap);
698bc1b8492SDan Murphy 		dev_err(&client->dev, "Failed to allocate register map: %d\n",
699bc1b8492SDan Murphy 			ret);
700bc1b8492SDan Murphy 		return ret;
701bc1b8492SDan Murphy 	}
702bc1b8492SDan Murphy 
703bc1b8492SDan Murphy 	mutex_init(&drvdata->lock);
704bc1b8492SDan Murphy 	i2c_set_clientdata(client, drvdata);
705bc1b8492SDan Murphy 
706bc1b8492SDan Murphy 	ret = lm3532_parse_node(drvdata);
707bc1b8492SDan Murphy 	if (ret) {
708bc1b8492SDan Murphy 		dev_err(&client->dev, "Failed to parse node\n");
709bc1b8492SDan Murphy 		return ret;
710bc1b8492SDan Murphy 	}
711bc1b8492SDan Murphy 
712bc1b8492SDan Murphy 	return ret;
713bc1b8492SDan Murphy }
714bc1b8492SDan Murphy 
715bc1b8492SDan Murphy static int lm3532_remove(struct i2c_client *client)
716bc1b8492SDan Murphy {
717bc1b8492SDan Murphy 	struct lm3532_data *drvdata = i2c_get_clientdata(client);
718bc1b8492SDan Murphy 
719bc1b8492SDan Murphy 	mutex_destroy(&drvdata->lock);
720bc1b8492SDan Murphy 
721bc1b8492SDan Murphy 	if (drvdata->enable_gpio)
722bc1b8492SDan Murphy 		gpiod_direction_output(drvdata->enable_gpio, 0);
723bc1b8492SDan Murphy 
724bc1b8492SDan Murphy 	return 0;
725bc1b8492SDan Murphy }
726bc1b8492SDan Murphy 
727bc1b8492SDan Murphy static const struct of_device_id of_lm3532_leds_match[] = {
728bc1b8492SDan Murphy 	{ .compatible = "ti,lm3532", },
729bc1b8492SDan Murphy 	{},
730bc1b8492SDan Murphy };
731bc1b8492SDan Murphy MODULE_DEVICE_TABLE(of, of_lm3532_leds_match);
732bc1b8492SDan Murphy 
733bc1b8492SDan Murphy static const struct i2c_device_id lm3532_id[] = {
734bc1b8492SDan Murphy 	{LM3532_NAME, 0},
735bc1b8492SDan Murphy 	{}
736bc1b8492SDan Murphy };
737bc1b8492SDan Murphy MODULE_DEVICE_TABLE(i2c, lm3532_id);
738bc1b8492SDan Murphy 
739bc1b8492SDan Murphy static struct i2c_driver lm3532_i2c_driver = {
740bc1b8492SDan Murphy 	.probe = lm3532_probe,
741bc1b8492SDan Murphy 	.remove = lm3532_remove,
742bc1b8492SDan Murphy 	.id_table = lm3532_id,
743bc1b8492SDan Murphy 	.driver = {
744bc1b8492SDan Murphy 		.name = LM3532_NAME,
745bc1b8492SDan Murphy 		.of_match_table = of_lm3532_leds_match,
746bc1b8492SDan Murphy 	},
747bc1b8492SDan Murphy };
748bc1b8492SDan Murphy module_i2c_driver(lm3532_i2c_driver);
749bc1b8492SDan Murphy 
750bc1b8492SDan Murphy MODULE_DESCRIPTION("Back Light driver for LM3532");
751bc1b8492SDan Murphy MODULE_LICENSE("GPL v2");
752bc1b8492SDan Murphy MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
753