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