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