xref: /openbmc/linux/drivers/leds/leds-lp8860.c (revision 7a8685accb95801bb29ab85d5b370999d3fb8e32)
1*7a8685acSDan Murphy /*
2*7a8685acSDan Murphy  * TI LP8860 4-Channel LED Driver
3*7a8685acSDan Murphy  *
4*7a8685acSDan Murphy  * Copyright (C) 2014 Texas Instruments
5*7a8685acSDan Murphy  *
6*7a8685acSDan Murphy  * Author: Dan Murphy <dmurphy@ti.com>
7*7a8685acSDan Murphy  *
8*7a8685acSDan Murphy  * This program is free software; you can redistribute it and/or
9*7a8685acSDan Murphy  * modify it under the terms of the GNU General Public License
10*7a8685acSDan Murphy  * version 2 as published by the Free Software Foundation.
11*7a8685acSDan Murphy  *
12*7a8685acSDan Murphy  */
13*7a8685acSDan Murphy 
14*7a8685acSDan Murphy #include <linux/i2c.h>
15*7a8685acSDan Murphy #include <linux/init.h>
16*7a8685acSDan Murphy #include <linux/leds.h>
17*7a8685acSDan Murphy #include <linux/regmap.h>
18*7a8685acSDan Murphy #include <linux/regulator/consumer.h>
19*7a8685acSDan Murphy #include <linux/module.h>
20*7a8685acSDan Murphy #include <linux/mutex.h>
21*7a8685acSDan Murphy #include <linux/of.h>
22*7a8685acSDan Murphy #include <linux/of_gpio.h>
23*7a8685acSDan Murphy #include <linux/gpio/consumer.h>
24*7a8685acSDan Murphy #include <linux/slab.h>
25*7a8685acSDan Murphy 
26*7a8685acSDan Murphy #define LP8860_DISP_CL1_BRT_MSB		0x00
27*7a8685acSDan Murphy #define LP8860_DISP_CL1_BRT_LSB		0x01
28*7a8685acSDan Murphy #define LP8860_DISP_CL1_CURR_MSB	0x02
29*7a8685acSDan Murphy #define LP8860_DISP_CL1_CURR_LSB	0x03
30*7a8685acSDan Murphy #define LP8860_CL2_BRT_MSB		0x04
31*7a8685acSDan Murphy #define LP8860_CL2_BRT_LSB		0x05
32*7a8685acSDan Murphy #define LP8860_CL2_CURRENT		0x06
33*7a8685acSDan Murphy #define LP8860_CL3_BRT_MSB		0x07
34*7a8685acSDan Murphy #define LP8860_CL3_BRT_LSB		0x08
35*7a8685acSDan Murphy #define LP8860_CL3_CURRENT		0x09
36*7a8685acSDan Murphy #define LP8860_CL4_BRT_MSB		0x0a
37*7a8685acSDan Murphy #define LP8860_CL4_BRT_LSB		0x0b
38*7a8685acSDan Murphy #define LP8860_CL4_CURRENT		0x0c
39*7a8685acSDan Murphy #define LP8860_CONFIG			0x0d
40*7a8685acSDan Murphy #define LP8860_STATUS			0x0e
41*7a8685acSDan Murphy #define LP8860_FAULT			0x0f
42*7a8685acSDan Murphy #define LP8860_LED_FAULT		0x10
43*7a8685acSDan Murphy #define LP8860_FAULT_CLEAR		0x11
44*7a8685acSDan Murphy #define LP8860_ID			0x12
45*7a8685acSDan Murphy #define LP8860_TEMP_MSB			0x13
46*7a8685acSDan Murphy #define LP8860_TEMP_LSB			0x14
47*7a8685acSDan Murphy #define LP8860_DISP_LED_CURR_MSB	0x15
48*7a8685acSDan Murphy #define LP8860_DISP_LED_CURR_LSB	0x16
49*7a8685acSDan Murphy #define LP8860_DISP_LED_PWM_MSB		0x17
50*7a8685acSDan Murphy #define LP8860_DISP_LED_PWM_LSB		0x18
51*7a8685acSDan Murphy #define LP8860_EEPROM_CNTRL		0x19
52*7a8685acSDan Murphy #define LP8860_EEPROM_UNLOCK		0x1a
53*7a8685acSDan Murphy 
54*7a8685acSDan Murphy #define LP8860_EEPROM_REG_0		0x60
55*7a8685acSDan Murphy #define LP8860_EEPROM_REG_1		0x61
56*7a8685acSDan Murphy #define LP8860_EEPROM_REG_2		0x62
57*7a8685acSDan Murphy #define LP8860_EEPROM_REG_3		0x63
58*7a8685acSDan Murphy #define LP8860_EEPROM_REG_4		0x64
59*7a8685acSDan Murphy #define LP8860_EEPROM_REG_5		0x65
60*7a8685acSDan Murphy #define LP8860_EEPROM_REG_6		0x66
61*7a8685acSDan Murphy #define LP8860_EEPROM_REG_7		0x67
62*7a8685acSDan Murphy #define LP8860_EEPROM_REG_8		0x68
63*7a8685acSDan Murphy #define LP8860_EEPROM_REG_9		0x69
64*7a8685acSDan Murphy #define LP8860_EEPROM_REG_10		0x6a
65*7a8685acSDan Murphy #define LP8860_EEPROM_REG_11		0x6b
66*7a8685acSDan Murphy #define LP8860_EEPROM_REG_12		0x6c
67*7a8685acSDan Murphy #define LP8860_EEPROM_REG_13		0x6d
68*7a8685acSDan Murphy #define LP8860_EEPROM_REG_14		0x6e
69*7a8685acSDan Murphy #define LP8860_EEPROM_REG_15		0x6f
70*7a8685acSDan Murphy #define LP8860_EEPROM_REG_16		0x70
71*7a8685acSDan Murphy #define LP8860_EEPROM_REG_17		0x71
72*7a8685acSDan Murphy #define LP8860_EEPROM_REG_18		0x72
73*7a8685acSDan Murphy #define LP8860_EEPROM_REG_19		0x73
74*7a8685acSDan Murphy #define LP8860_EEPROM_REG_20		0x74
75*7a8685acSDan Murphy #define LP8860_EEPROM_REG_21		0x75
76*7a8685acSDan Murphy #define LP8860_EEPROM_REG_22		0x76
77*7a8685acSDan Murphy #define LP8860_EEPROM_REG_23		0x77
78*7a8685acSDan Murphy #define LP8860_EEPROM_REG_24		0x78
79*7a8685acSDan Murphy 
80*7a8685acSDan Murphy #define LP8860_LOCK_EEPROM		0x00
81*7a8685acSDan Murphy #define LP8860_UNLOCK_EEPROM		0x01
82*7a8685acSDan Murphy #define LP8860_PROGRAM_EEPROM		0x02
83*7a8685acSDan Murphy #define LP8860_EEPROM_CODE_1		0x08
84*7a8685acSDan Murphy #define LP8860_EEPROM_CODE_2		0xba
85*7a8685acSDan Murphy #define LP8860_EEPROM_CODE_3		0xef
86*7a8685acSDan Murphy 
87*7a8685acSDan Murphy #define LP8860_CLEAR_FAULTS		0x01
88*7a8685acSDan Murphy 
89*7a8685acSDan Murphy #define LP8860_DISP_LED_NAME		"display_cluster"
90*7a8685acSDan Murphy 
91*7a8685acSDan Murphy /**
92*7a8685acSDan Murphy  * struct lp8860_led -
93*7a8685acSDan Murphy  * @lock - Lock for reading/writing the device
94*7a8685acSDan Murphy  * @work - Work item used to off load the brightness register writes
95*7a8685acSDan Murphy  * @client - Pointer to the I2C client
96*7a8685acSDan Murphy  * @led_dev - led class device pointer
97*7a8685acSDan Murphy  * @regmap - Devices register map
98*7a8685acSDan Murphy  * @eeprom_regmap - EEPROM register map
99*7a8685acSDan Murphy  * @enable_gpio - VDDIO/EN gpio to enable communication interface
100*7a8685acSDan Murphy  * @regulator - LED supply regulator pointer
101*7a8685acSDan Murphy  * @brightness - Current brightness value requested
102*7a8685acSDan Murphy  * @label - LED label
103*7a8685acSDan Murphy **/
104*7a8685acSDan Murphy struct lp8860_led {
105*7a8685acSDan Murphy 	struct mutex lock;
106*7a8685acSDan Murphy 	struct work_struct work;
107*7a8685acSDan Murphy 	struct i2c_client *client;
108*7a8685acSDan Murphy 	struct led_classdev led_dev;
109*7a8685acSDan Murphy 	struct regmap *regmap;
110*7a8685acSDan Murphy 	struct regmap *eeprom_regmap;
111*7a8685acSDan Murphy 	struct gpio_desc *enable_gpio;
112*7a8685acSDan Murphy 	struct regulator *regulator;
113*7a8685acSDan Murphy 	enum led_brightness brightness;
114*7a8685acSDan Murphy 	const char *label;
115*7a8685acSDan Murphy };
116*7a8685acSDan Murphy 
117*7a8685acSDan Murphy struct lp8860_eeprom_reg {
118*7a8685acSDan Murphy 	uint8_t reg;
119*7a8685acSDan Murphy 	uint8_t value;
120*7a8685acSDan Murphy };
121*7a8685acSDan Murphy 
122*7a8685acSDan Murphy static struct lp8860_eeprom_reg lp8860_eeprom_disp_regs[] = {
123*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_0, 0xed },
124*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_1, 0xdf },
125*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_2, 0xdc },
126*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_3, 0xf0 },
127*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_4, 0xdf },
128*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_5, 0xe5 },
129*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_6, 0xf2 },
130*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_7, 0x77 },
131*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_8, 0x77 },
132*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_9, 0x71 },
133*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_10, 0x3f },
134*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_11, 0xb7 },
135*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_12, 0x17 },
136*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_13, 0xef },
137*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_14, 0xb0 },
138*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_15, 0x87 },
139*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_16, 0xce },
140*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_17, 0x72 },
141*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_18, 0xe5 },
142*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_19, 0xdf },
143*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_20, 0x35 },
144*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_21, 0x06 },
145*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_22, 0xdc },
146*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_23, 0x88 },
147*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_24, 0x3E },
148*7a8685acSDan Murphy };
149*7a8685acSDan Murphy 
150*7a8685acSDan Murphy static int lp8860_unlock_eeprom(struct lp8860_led *led, int lock)
151*7a8685acSDan Murphy {
152*7a8685acSDan Murphy 	int ret;
153*7a8685acSDan Murphy 
154*7a8685acSDan Murphy 	mutex_lock(&led->lock);
155*7a8685acSDan Murphy 
156*7a8685acSDan Murphy 	if (lock == LP8860_UNLOCK_EEPROM) {
157*7a8685acSDan Murphy 		ret = regmap_write(led->regmap,
158*7a8685acSDan Murphy 			LP8860_EEPROM_UNLOCK,
159*7a8685acSDan Murphy 			LP8860_EEPROM_CODE_1);
160*7a8685acSDan Murphy 		if (ret) {
161*7a8685acSDan Murphy 			dev_err(&led->client->dev, "EEPROM Unlock failed\n");
162*7a8685acSDan Murphy 			goto out;
163*7a8685acSDan Murphy 		}
164*7a8685acSDan Murphy 
165*7a8685acSDan Murphy 		ret = regmap_write(led->regmap,
166*7a8685acSDan Murphy 			LP8860_EEPROM_UNLOCK,
167*7a8685acSDan Murphy 			LP8860_EEPROM_CODE_2);
168*7a8685acSDan Murphy 		if (ret) {
169*7a8685acSDan Murphy 			dev_err(&led->client->dev, "EEPROM Unlock failed\n");
170*7a8685acSDan Murphy 			goto out;
171*7a8685acSDan Murphy 		}
172*7a8685acSDan Murphy 		ret = regmap_write(led->regmap,
173*7a8685acSDan Murphy 			LP8860_EEPROM_UNLOCK,
174*7a8685acSDan Murphy 			LP8860_EEPROM_CODE_3);
175*7a8685acSDan Murphy 		if (ret) {
176*7a8685acSDan Murphy 			dev_err(&led->client->dev, "EEPROM Unlock failed\n");
177*7a8685acSDan Murphy 			goto out;
178*7a8685acSDan Murphy 		}
179*7a8685acSDan Murphy 	} else {
180*7a8685acSDan Murphy 		ret = regmap_write(led->regmap,
181*7a8685acSDan Murphy 			LP8860_EEPROM_UNLOCK,
182*7a8685acSDan Murphy 			LP8860_LOCK_EEPROM);
183*7a8685acSDan Murphy 	}
184*7a8685acSDan Murphy 
185*7a8685acSDan Murphy out:
186*7a8685acSDan Murphy 	mutex_unlock(&led->lock);
187*7a8685acSDan Murphy 	return ret;
188*7a8685acSDan Murphy }
189*7a8685acSDan Murphy 
190*7a8685acSDan Murphy static int lp8860_fault_check(struct lp8860_led *led)
191*7a8685acSDan Murphy {
192*7a8685acSDan Murphy 	int ret, fault;
193*7a8685acSDan Murphy 	unsigned int read_buf;
194*7a8685acSDan Murphy 
195*7a8685acSDan Murphy 	ret = regmap_read(led->regmap, LP8860_LED_FAULT, &read_buf);
196*7a8685acSDan Murphy 	if (ret)
197*7a8685acSDan Murphy 		goto out;
198*7a8685acSDan Murphy 
199*7a8685acSDan Murphy 	fault = read_buf;
200*7a8685acSDan Murphy 
201*7a8685acSDan Murphy 	ret = regmap_read(led->regmap, LP8860_FAULT, &read_buf);
202*7a8685acSDan Murphy 	if (ret)
203*7a8685acSDan Murphy 		goto out;
204*7a8685acSDan Murphy 
205*7a8685acSDan Murphy 	fault |= read_buf;
206*7a8685acSDan Murphy 
207*7a8685acSDan Murphy 	/* Attempt to clear any faults */
208*7a8685acSDan Murphy 	if (fault)
209*7a8685acSDan Murphy 		ret = regmap_write(led->regmap, LP8860_FAULT_CLEAR,
210*7a8685acSDan Murphy 			LP8860_CLEAR_FAULTS);
211*7a8685acSDan Murphy out:
212*7a8685acSDan Murphy 	return ret;
213*7a8685acSDan Murphy }
214*7a8685acSDan Murphy 
215*7a8685acSDan Murphy static void lp8860_led_brightness_work(struct work_struct *work)
216*7a8685acSDan Murphy {
217*7a8685acSDan Murphy 	struct lp8860_led *led = container_of(work, struct lp8860_led, work);
218*7a8685acSDan Murphy 	int ret;
219*7a8685acSDan Murphy 	int disp_brightness = led->brightness * 255;
220*7a8685acSDan Murphy 
221*7a8685acSDan Murphy 	mutex_lock(&led->lock);
222*7a8685acSDan Murphy 
223*7a8685acSDan Murphy 	ret = lp8860_fault_check(led);
224*7a8685acSDan Murphy 	if (ret) {
225*7a8685acSDan Murphy 		dev_err(&led->client->dev, "Cannot read/clear faults\n");
226*7a8685acSDan Murphy 		goto out;
227*7a8685acSDan Murphy 	}
228*7a8685acSDan Murphy 
229*7a8685acSDan Murphy 	ret = regmap_write(led->regmap, LP8860_DISP_CL1_BRT_MSB,
230*7a8685acSDan Murphy 			(disp_brightness & 0xff00) >> 8);
231*7a8685acSDan Murphy 	if (ret) {
232*7a8685acSDan Murphy 		dev_err(&led->client->dev, "Cannot write CL1 MSB\n");
233*7a8685acSDan Murphy 		goto out;
234*7a8685acSDan Murphy 	}
235*7a8685acSDan Murphy 
236*7a8685acSDan Murphy 	ret = regmap_write(led->regmap, LP8860_DISP_CL1_BRT_LSB,
237*7a8685acSDan Murphy 			disp_brightness & 0xff);
238*7a8685acSDan Murphy 	if (ret) {
239*7a8685acSDan Murphy 		dev_err(&led->client->dev, "Cannot write CL1 LSB\n");
240*7a8685acSDan Murphy 		goto out;
241*7a8685acSDan Murphy 	}
242*7a8685acSDan Murphy out:
243*7a8685acSDan Murphy 	mutex_unlock(&led->lock);
244*7a8685acSDan Murphy }
245*7a8685acSDan Murphy 
246*7a8685acSDan Murphy static void lp8860_brightness_set(struct led_classdev *led_cdev,
247*7a8685acSDan Murphy 				enum led_brightness brt_val)
248*7a8685acSDan Murphy {
249*7a8685acSDan Murphy 	struct lp8860_led *led =
250*7a8685acSDan Murphy 			container_of(led_cdev, struct lp8860_led, led_dev);
251*7a8685acSDan Murphy 
252*7a8685acSDan Murphy 	led->brightness = brt_val;
253*7a8685acSDan Murphy 	schedule_work(&led->work);
254*7a8685acSDan Murphy }
255*7a8685acSDan Murphy 
256*7a8685acSDan Murphy static int lp8860_init(struct lp8860_led *led)
257*7a8685acSDan Murphy {
258*7a8685acSDan Murphy 	unsigned int read_buf;
259*7a8685acSDan Murphy 	int ret, i, reg_count;
260*7a8685acSDan Murphy 
261*7a8685acSDan Murphy 	if (led->enable_gpio)
262*7a8685acSDan Murphy 		gpiod_direction_output(led->enable_gpio, 1);
263*7a8685acSDan Murphy 
264*7a8685acSDan Murphy 	ret = lp8860_fault_check(led);
265*7a8685acSDan Murphy 	if (ret)
266*7a8685acSDan Murphy 		goto out;
267*7a8685acSDan Murphy 
268*7a8685acSDan Murphy 	ret = regmap_read(led->regmap, LP8860_STATUS, &read_buf);
269*7a8685acSDan Murphy 	if (ret)
270*7a8685acSDan Murphy 		goto out;
271*7a8685acSDan Murphy 
272*7a8685acSDan Murphy 	ret = lp8860_unlock_eeprom(led, LP8860_UNLOCK_EEPROM);
273*7a8685acSDan Murphy 	if (ret) {
274*7a8685acSDan Murphy 		dev_err(&led->client->dev, "Failed unlocking EEPROM\n");
275*7a8685acSDan Murphy 		goto out;
276*7a8685acSDan Murphy 	}
277*7a8685acSDan Murphy 
278*7a8685acSDan Murphy 	reg_count = ARRAY_SIZE(lp8860_eeprom_disp_regs) / sizeof(lp8860_eeprom_disp_regs[0]);
279*7a8685acSDan Murphy 	for (i = 0; i < reg_count; i++) {
280*7a8685acSDan Murphy 		ret = regmap_write(led->eeprom_regmap,
281*7a8685acSDan Murphy 				lp8860_eeprom_disp_regs[i].reg,
282*7a8685acSDan Murphy 				lp8860_eeprom_disp_regs[i].value);
283*7a8685acSDan Murphy 		if (ret) {
284*7a8685acSDan Murphy 			dev_err(&led->client->dev, "Failed writing EEPROM\n");
285*7a8685acSDan Murphy 			goto out;
286*7a8685acSDan Murphy 		}
287*7a8685acSDan Murphy 	}
288*7a8685acSDan Murphy 
289*7a8685acSDan Murphy 	ret = lp8860_unlock_eeprom(led, LP8860_LOCK_EEPROM);
290*7a8685acSDan Murphy 	if (ret)
291*7a8685acSDan Murphy 		goto out;
292*7a8685acSDan Murphy 
293*7a8685acSDan Murphy 	ret = regmap_write(led->regmap,
294*7a8685acSDan Murphy 			LP8860_EEPROM_CNTRL,
295*7a8685acSDan Murphy 			LP8860_PROGRAM_EEPROM);
296*7a8685acSDan Murphy 	if (ret)
297*7a8685acSDan Murphy 		dev_err(&led->client->dev, "Failed programming EEPROM\n");
298*7a8685acSDan Murphy out:
299*7a8685acSDan Murphy 	if (ret)
300*7a8685acSDan Murphy 		if (led->enable_gpio)
301*7a8685acSDan Murphy 			gpiod_direction_output(led->enable_gpio, 0);
302*7a8685acSDan Murphy 	return ret;
303*7a8685acSDan Murphy }
304*7a8685acSDan Murphy 
305*7a8685acSDan Murphy static struct reg_default lp8860_reg_defs[] = {
306*7a8685acSDan Murphy 	{ LP8860_DISP_CL1_BRT_MSB, 0x00},
307*7a8685acSDan Murphy 	{ LP8860_DISP_CL1_BRT_LSB, 0x00},
308*7a8685acSDan Murphy 	{ LP8860_DISP_CL1_CURR_MSB, 0x00},
309*7a8685acSDan Murphy 	{ LP8860_DISP_CL1_CURR_LSB, 0x00},
310*7a8685acSDan Murphy 	{ LP8860_CL2_BRT_MSB, 0x00},
311*7a8685acSDan Murphy 	{ LP8860_CL2_BRT_LSB, 0x00},
312*7a8685acSDan Murphy 	{ LP8860_CL2_CURRENT, 0x00},
313*7a8685acSDan Murphy 	{ LP8860_CL3_BRT_MSB, 0x00},
314*7a8685acSDan Murphy 	{ LP8860_CL3_BRT_LSB, 0x00},
315*7a8685acSDan Murphy 	{ LP8860_CL3_CURRENT, 0x00},
316*7a8685acSDan Murphy 	{ LP8860_CL4_BRT_MSB, 0x00},
317*7a8685acSDan Murphy 	{ LP8860_CL4_BRT_LSB, 0x00},
318*7a8685acSDan Murphy 	{ LP8860_CL4_CURRENT, 0x00},
319*7a8685acSDan Murphy 	{ LP8860_CONFIG, 0x00},
320*7a8685acSDan Murphy 	{ LP8860_FAULT_CLEAR, 0x00},
321*7a8685acSDan Murphy 	{ LP8860_EEPROM_CNTRL, 0x80},
322*7a8685acSDan Murphy 	{ LP8860_EEPROM_UNLOCK, 0x00},
323*7a8685acSDan Murphy };
324*7a8685acSDan Murphy 
325*7a8685acSDan Murphy static const struct regmap_config lp8860_regmap_config = {
326*7a8685acSDan Murphy 	.reg_bits = 8,
327*7a8685acSDan Murphy 	.val_bits = 8,
328*7a8685acSDan Murphy 
329*7a8685acSDan Murphy 	.max_register = LP8860_EEPROM_UNLOCK,
330*7a8685acSDan Murphy 	.reg_defaults = lp8860_reg_defs,
331*7a8685acSDan Murphy 	.num_reg_defaults = ARRAY_SIZE(lp8860_reg_defs),
332*7a8685acSDan Murphy 	.cache_type = REGCACHE_NONE,
333*7a8685acSDan Murphy };
334*7a8685acSDan Murphy 
335*7a8685acSDan Murphy static struct reg_default lp8860_eeprom_defs[] = {
336*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_0, 0x00 },
337*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_1, 0x00 },
338*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_2, 0x00 },
339*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_3, 0x00 },
340*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_4, 0x00 },
341*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_5, 0x00 },
342*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_6, 0x00 },
343*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_7, 0x00 },
344*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_8, 0x00 },
345*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_9, 0x00 },
346*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_10, 0x00 },
347*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_11, 0x00 },
348*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_12, 0x00 },
349*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_13, 0x00 },
350*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_14, 0x00 },
351*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_15, 0x00 },
352*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_16, 0x00 },
353*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_17, 0x00 },
354*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_18, 0x00 },
355*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_19, 0x00 },
356*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_20, 0x00 },
357*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_21, 0x00 },
358*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_22, 0x00 },
359*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_23, 0x00 },
360*7a8685acSDan Murphy 	{ LP8860_EEPROM_REG_24, 0x00 },
361*7a8685acSDan Murphy };
362*7a8685acSDan Murphy 
363*7a8685acSDan Murphy static const struct regmap_config lp8860_eeprom_regmap_config = {
364*7a8685acSDan Murphy 	.reg_bits = 8,
365*7a8685acSDan Murphy 	.val_bits = 8,
366*7a8685acSDan Murphy 
367*7a8685acSDan Murphy 	.max_register = LP8860_EEPROM_REG_24,
368*7a8685acSDan Murphy 	.reg_defaults = lp8860_eeprom_defs,
369*7a8685acSDan Murphy 	.num_reg_defaults = ARRAY_SIZE(lp8860_eeprom_defs),
370*7a8685acSDan Murphy 	.cache_type = REGCACHE_NONE,
371*7a8685acSDan Murphy };
372*7a8685acSDan Murphy 
373*7a8685acSDan Murphy static int lp8860_probe(struct i2c_client *client,
374*7a8685acSDan Murphy 			const struct i2c_device_id *id)
375*7a8685acSDan Murphy {
376*7a8685acSDan Murphy 	int ret;
377*7a8685acSDan Murphy 	struct lp8860_led *led;
378*7a8685acSDan Murphy 	struct device_node *np = client->dev.of_node;
379*7a8685acSDan Murphy 
380*7a8685acSDan Murphy 	led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL);
381*7a8685acSDan Murphy 	if (!led)
382*7a8685acSDan Murphy 		return -ENOMEM;
383*7a8685acSDan Murphy 
384*7a8685acSDan Murphy 	led->label = LP8860_DISP_LED_NAME;
385*7a8685acSDan Murphy 
386*7a8685acSDan Murphy 	if (client->dev.of_node) {
387*7a8685acSDan Murphy 		ret = of_property_read_string(np, "label", &led->label);
388*7a8685acSDan Murphy 		if (ret) {
389*7a8685acSDan Murphy 			dev_err(&client->dev, "Missing label in dt\n");
390*7a8685acSDan Murphy 			return -EINVAL;
391*7a8685acSDan Murphy 		}
392*7a8685acSDan Murphy 	}
393*7a8685acSDan Murphy 
394*7a8685acSDan Murphy 	led->enable_gpio = devm_gpiod_get(&client->dev, "enable");
395*7a8685acSDan Murphy 	if (IS_ERR(led->enable_gpio))
396*7a8685acSDan Murphy 		led->enable_gpio = NULL;
397*7a8685acSDan Murphy 	else
398*7a8685acSDan Murphy 		gpiod_direction_output(led->enable_gpio, 0);
399*7a8685acSDan Murphy 
400*7a8685acSDan Murphy 	led->regulator = devm_regulator_get(&client->dev, "vled");
401*7a8685acSDan Murphy 	if (IS_ERR(led->regulator))
402*7a8685acSDan Murphy 		led->regulator = NULL;
403*7a8685acSDan Murphy 
404*7a8685acSDan Murphy 	led->client = client;
405*7a8685acSDan Murphy 	led->led_dev.name = led->label;
406*7a8685acSDan Murphy 	led->led_dev.max_brightness = LED_FULL;
407*7a8685acSDan Murphy 	led->led_dev.brightness_set = lp8860_brightness_set;
408*7a8685acSDan Murphy 
409*7a8685acSDan Murphy 	mutex_init(&led->lock);
410*7a8685acSDan Murphy 	INIT_WORK(&led->work, lp8860_led_brightness_work);
411*7a8685acSDan Murphy 
412*7a8685acSDan Murphy 	i2c_set_clientdata(client, led);
413*7a8685acSDan Murphy 
414*7a8685acSDan Murphy 	led->regmap = devm_regmap_init_i2c(client, &lp8860_regmap_config);
415*7a8685acSDan Murphy 	if (IS_ERR(led->regmap)) {
416*7a8685acSDan Murphy 		ret = PTR_ERR(led->regmap);
417*7a8685acSDan Murphy 		dev_err(&client->dev, "Failed to allocate register map: %d\n",
418*7a8685acSDan Murphy 			ret);
419*7a8685acSDan Murphy 		return ret;
420*7a8685acSDan Murphy 	}
421*7a8685acSDan Murphy 
422*7a8685acSDan Murphy 	led->eeprom_regmap = devm_regmap_init_i2c(client, &lp8860_eeprom_regmap_config);
423*7a8685acSDan Murphy 	if (IS_ERR(led->eeprom_regmap)) {
424*7a8685acSDan Murphy 		ret = PTR_ERR(led->eeprom_regmap);
425*7a8685acSDan Murphy 		dev_err(&client->dev, "Failed to allocate register map: %d\n",
426*7a8685acSDan Murphy 			ret);
427*7a8685acSDan Murphy 		return ret;
428*7a8685acSDan Murphy 	}
429*7a8685acSDan Murphy 
430*7a8685acSDan Murphy 	ret = lp8860_init(led);
431*7a8685acSDan Murphy 	if (ret)
432*7a8685acSDan Murphy 		return ret;
433*7a8685acSDan Murphy 
434*7a8685acSDan Murphy 	ret = led_classdev_register(&client->dev, &led->led_dev);
435*7a8685acSDan Murphy 	if (ret) {
436*7a8685acSDan Murphy 		dev_err(&client->dev, "led register err: %d\n", ret);
437*7a8685acSDan Murphy 		return ret;
438*7a8685acSDan Murphy 	}
439*7a8685acSDan Murphy 
440*7a8685acSDan Murphy 	return 0;
441*7a8685acSDan Murphy }
442*7a8685acSDan Murphy 
443*7a8685acSDan Murphy static int lp8860_remove(struct i2c_client *client)
444*7a8685acSDan Murphy {
445*7a8685acSDan Murphy 	struct lp8860_led *led = i2c_get_clientdata(client);
446*7a8685acSDan Murphy 	int ret;
447*7a8685acSDan Murphy 
448*7a8685acSDan Murphy 	led_classdev_unregister(&led->led_dev);
449*7a8685acSDan Murphy 	cancel_work_sync(&led->work);
450*7a8685acSDan Murphy 
451*7a8685acSDan Murphy 	if (led->enable_gpio)
452*7a8685acSDan Murphy 		gpiod_direction_output(led->enable_gpio, 0);
453*7a8685acSDan Murphy 
454*7a8685acSDan Murphy 	if (led->regulator) {
455*7a8685acSDan Murphy 		ret = regulator_disable(led->regulator);
456*7a8685acSDan Murphy 		if (ret)
457*7a8685acSDan Murphy 			dev_err(&led->client->dev,
458*7a8685acSDan Murphy 				"Failed to disable regulator\n");
459*7a8685acSDan Murphy 	}
460*7a8685acSDan Murphy 
461*7a8685acSDan Murphy 	return 0;
462*7a8685acSDan Murphy }
463*7a8685acSDan Murphy 
464*7a8685acSDan Murphy static const struct i2c_device_id lp8860_id[] = {
465*7a8685acSDan Murphy 	{ "lp8860", 0 },
466*7a8685acSDan Murphy 	{ }
467*7a8685acSDan Murphy };
468*7a8685acSDan Murphy MODULE_DEVICE_TABLE(i2c, lp8860_id);
469*7a8685acSDan Murphy 
470*7a8685acSDan Murphy #ifdef CONFIG_OF
471*7a8685acSDan Murphy static const struct of_device_id of_lp8860_leds_match[] = {
472*7a8685acSDan Murphy 	{ .compatible = "ti,lp8860", },
473*7a8685acSDan Murphy 	{},
474*7a8685acSDan Murphy };
475*7a8685acSDan Murphy MODULE_DEVICE_TABLE(of, of_lp8860_leds_match);
476*7a8685acSDan Murphy #endif
477*7a8685acSDan Murphy 
478*7a8685acSDan Murphy static struct i2c_driver lp8860_driver = {
479*7a8685acSDan Murphy 	.driver = {
480*7a8685acSDan Murphy 		.name	= "lp8860",
481*7a8685acSDan Murphy 		.of_match_table = of_match_ptr(of_lp8860_leds_match),
482*7a8685acSDan Murphy 	},
483*7a8685acSDan Murphy 	.probe		= lp8860_probe,
484*7a8685acSDan Murphy 	.remove		= lp8860_remove,
485*7a8685acSDan Murphy 	.id_table	= lp8860_id,
486*7a8685acSDan Murphy };
487*7a8685acSDan Murphy module_i2c_driver(lp8860_driver);
488*7a8685acSDan Murphy 
489*7a8685acSDan Murphy MODULE_DESCRIPTION("Texas Instruments LP8860 LED drvier");
490*7a8685acSDan Murphy MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
491*7a8685acSDan Murphy MODULE_LICENSE("GPL");
492