1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // TI LM3697 LED chip family driver |
3 | // Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ |
4 | |
5 | #include <linux/bits.h> |
6 | #include <linux/gpio/consumer.h> |
7 | #include <linux/i2c.h> |
8 | #include <linux/mod_devicetable.h> |
9 | #include <linux/module.h> |
10 | #include <linux/property.h> |
11 | #include <linux/regmap.h> |
12 | #include <linux/regulator/consumer.h> |
13 | #include <linux/types.h> |
14 | |
15 | #include <linux/leds-ti-lmu-common.h> |
16 | |
17 | #define LM3697_REV 0x0 |
18 | #define LM3697_RESET 0x1 |
19 | #define LM3697_OUTPUT_CONFIG 0x10 |
20 | #define LM3697_CTRL_A_RAMP 0x11 |
21 | #define LM3697_CTRL_B_RAMP 0x12 |
22 | #define LM3697_CTRL_A_B_RT_RAMP 0x13 |
23 | #define LM3697_CTRL_A_B_RAMP_CFG 0x14 |
24 | #define LM3697_CTRL_A_B_BRT_CFG 0x16 |
25 | #define LM3697_CTRL_A_FS_CURR_CFG 0x17 |
26 | #define LM3697_CTRL_B_FS_CURR_CFG 0x18 |
27 | #define LM3697_PWM_CFG 0x1c |
28 | #define LM3697_CTRL_A_BRT_LSB 0x20 |
29 | #define LM3697_CTRL_A_BRT_MSB 0x21 |
30 | #define LM3697_CTRL_B_BRT_LSB 0x22 |
31 | #define LM3697_CTRL_B_BRT_MSB 0x23 |
32 | #define LM3697_CTRL_ENABLE 0x24 |
33 | |
34 | #define LM3697_SW_RESET BIT(0) |
35 | |
36 | #define LM3697_CTRL_A_EN BIT(0) |
37 | #define LM3697_CTRL_B_EN BIT(1) |
38 | #define LM3697_CTRL_A_B_EN (LM3697_CTRL_A_EN | LM3697_CTRL_B_EN) |
39 | |
40 | #define LM3697_MAX_LED_STRINGS 3 |
41 | |
42 | #define LM3697_CONTROL_A 0 |
43 | #define LM3697_CONTROL_B 1 |
44 | #define LM3697_MAX_CONTROL_BANKS 2 |
45 | |
46 | /** |
47 | * struct lm3697_led - |
48 | * @hvled_strings: Array of LED strings associated with a control bank |
49 | * @label: LED label |
50 | * @led_dev: LED class device |
51 | * @priv: Pointer to the device struct |
52 | * @lmu_data: Register and setting values for common code |
53 | * @control_bank: Control bank the LED is associated to. 0 is control bank A |
54 | * 1 is control bank B |
55 | * @enabled: LED brightness level (or LED_OFF) |
56 | * @num_leds: Number of LEDs available |
57 | */ |
58 | struct lm3697_led { |
59 | u32 hvled_strings[LM3697_MAX_LED_STRINGS]; |
60 | char label[LED_MAX_NAME_SIZE]; |
61 | struct led_classdev led_dev; |
62 | struct lm3697 *priv; |
63 | struct ti_lmu_bank lmu_data; |
64 | int control_bank; |
65 | int enabled; |
66 | int num_leds; |
67 | }; |
68 | |
69 | /** |
70 | * struct lm3697 - |
71 | * @enable_gpio: Hardware enable gpio |
72 | * @regulator: LED supply regulator pointer |
73 | * @client: Pointer to the I2C client |
74 | * @regmap: Devices register map |
75 | * @dev: Pointer to the devices device struct |
76 | * @lock: Lock for reading/writing the device |
77 | * @leds: Array of LED strings |
78 | * @bank_cfg: OUTPUT_CONFIG register values |
79 | * @num_banks: Number of control banks |
80 | */ |
81 | struct lm3697 { |
82 | struct gpio_desc *enable_gpio; |
83 | struct regulator *regulator; |
84 | struct i2c_client *client; |
85 | struct regmap *regmap; |
86 | struct device *dev; |
87 | struct mutex lock; |
88 | |
89 | int bank_cfg; |
90 | int num_banks; |
91 | |
92 | struct lm3697_led leds[] __counted_by(num_banks); |
93 | }; |
94 | |
95 | static const struct reg_default lm3697_reg_defs[] = { |
96 | {LM3697_OUTPUT_CONFIG, 0x6}, |
97 | {LM3697_CTRL_A_RAMP, 0x0}, |
98 | {LM3697_CTRL_B_RAMP, 0x0}, |
99 | {LM3697_CTRL_A_B_RT_RAMP, 0x0}, |
100 | {LM3697_CTRL_A_B_RAMP_CFG, 0x0}, |
101 | {LM3697_CTRL_A_B_BRT_CFG, 0x0}, |
102 | {LM3697_CTRL_A_FS_CURR_CFG, 0x13}, |
103 | {LM3697_CTRL_B_FS_CURR_CFG, 0x13}, |
104 | {LM3697_PWM_CFG, 0xc}, |
105 | {LM3697_CTRL_A_BRT_LSB, 0x0}, |
106 | {LM3697_CTRL_A_BRT_MSB, 0x0}, |
107 | {LM3697_CTRL_B_BRT_LSB, 0x0}, |
108 | {LM3697_CTRL_B_BRT_MSB, 0x0}, |
109 | {LM3697_CTRL_ENABLE, 0x0}, |
110 | }; |
111 | |
112 | static const struct regmap_config lm3697_regmap_config = { |
113 | .reg_bits = 8, |
114 | .val_bits = 8, |
115 | |
116 | .max_register = LM3697_CTRL_ENABLE, |
117 | .reg_defaults = lm3697_reg_defs, |
118 | .num_reg_defaults = ARRAY_SIZE(lm3697_reg_defs), |
119 | .cache_type = REGCACHE_FLAT, |
120 | }; |
121 | |
122 | static int lm3697_brightness_set(struct led_classdev *led_cdev, |
123 | enum led_brightness brt_val) |
124 | { |
125 | struct lm3697_led *led = container_of(led_cdev, struct lm3697_led, |
126 | led_dev); |
127 | int ctrl_en_val = (1 << led->control_bank); |
128 | struct device *dev = led->priv->dev; |
129 | int ret; |
130 | |
131 | mutex_lock(&led->priv->lock); |
132 | |
133 | if (brt_val == LED_OFF) { |
134 | ret = regmap_update_bits(map: led->priv->regmap, LM3697_CTRL_ENABLE, |
135 | mask: ctrl_en_val, val: ~ctrl_en_val); |
136 | if (ret) { |
137 | dev_err(dev, "Cannot write ctrl register\n" ); |
138 | goto brightness_out; |
139 | } |
140 | |
141 | led->enabled = LED_OFF; |
142 | } else { |
143 | ret = ti_lmu_common_set_brightness(lmu_bank: &led->lmu_data, brightness: brt_val); |
144 | if (ret) { |
145 | dev_err(dev, "Cannot write brightness\n" ); |
146 | goto brightness_out; |
147 | } |
148 | |
149 | if (!led->enabled) { |
150 | ret = regmap_update_bits(map: led->priv->regmap, |
151 | LM3697_CTRL_ENABLE, |
152 | mask: ctrl_en_val, val: ctrl_en_val); |
153 | if (ret) { |
154 | dev_err(dev, "Cannot enable the device\n" ); |
155 | goto brightness_out; |
156 | } |
157 | |
158 | led->enabled = brt_val; |
159 | } |
160 | } |
161 | |
162 | brightness_out: |
163 | mutex_unlock(lock: &led->priv->lock); |
164 | return ret; |
165 | } |
166 | |
167 | static int lm3697_init(struct lm3697 *priv) |
168 | { |
169 | struct device *dev = priv->dev; |
170 | struct lm3697_led *led; |
171 | int i, ret; |
172 | |
173 | if (priv->enable_gpio) { |
174 | gpiod_direction_output(desc: priv->enable_gpio, value: 1); |
175 | } else { |
176 | ret = regmap_write(map: priv->regmap, LM3697_RESET, LM3697_SW_RESET); |
177 | if (ret) { |
178 | dev_err(dev, "Cannot reset the device\n" ); |
179 | goto out; |
180 | } |
181 | } |
182 | |
183 | ret = regmap_write(map: priv->regmap, LM3697_CTRL_ENABLE, val: 0x0); |
184 | if (ret) { |
185 | dev_err(dev, "Cannot write ctrl enable\n" ); |
186 | goto out; |
187 | } |
188 | |
189 | ret = regmap_write(map: priv->regmap, LM3697_OUTPUT_CONFIG, val: priv->bank_cfg); |
190 | if (ret) |
191 | dev_err(dev, "Cannot write OUTPUT config\n" ); |
192 | |
193 | for (i = 0; i < priv->num_banks; i++) { |
194 | led = &priv->leds[i]; |
195 | ret = ti_lmu_common_set_ramp(lmu_bank: &led->lmu_data); |
196 | if (ret) |
197 | dev_err(dev, "Setting the ramp rate failed\n" ); |
198 | } |
199 | out: |
200 | return ret; |
201 | } |
202 | |
203 | static int lm3697_probe_dt(struct lm3697 *priv) |
204 | { |
205 | struct fwnode_handle *child = NULL; |
206 | struct device *dev = priv->dev; |
207 | struct lm3697_led *led; |
208 | int ret = -EINVAL; |
209 | int control_bank; |
210 | size_t i = 0; |
211 | int j; |
212 | |
213 | priv->enable_gpio = devm_gpiod_get_optional(dev, con_id: "enable" , |
214 | flags: GPIOD_OUT_LOW); |
215 | if (IS_ERR(ptr: priv->enable_gpio)) |
216 | return dev_err_probe(dev, err: PTR_ERR(ptr: priv->enable_gpio), |
217 | fmt: "Failed to get enable GPIO\n" ); |
218 | |
219 | priv->regulator = devm_regulator_get(dev, id: "vled" ); |
220 | if (IS_ERR(ptr: priv->regulator)) |
221 | priv->regulator = NULL; |
222 | |
223 | device_for_each_child_node(dev, child) { |
224 | struct led_init_data init_data = {}; |
225 | |
226 | ret = fwnode_property_read_u32(fwnode: child, propname: "reg" , val: &control_bank); |
227 | if (ret) { |
228 | dev_err(dev, "reg property missing\n" ); |
229 | goto child_out; |
230 | } |
231 | |
232 | if (control_bank > LM3697_CONTROL_B) { |
233 | dev_err(dev, "reg property is invalid\n" ); |
234 | ret = -EINVAL; |
235 | goto child_out; |
236 | } |
237 | |
238 | led = &priv->leds[i]; |
239 | |
240 | ret = ti_lmu_common_get_brt_res(dev, child, lmu_data: &led->lmu_data); |
241 | if (ret) |
242 | dev_warn(dev, |
243 | "brightness resolution property missing\n" ); |
244 | |
245 | led->control_bank = control_bank; |
246 | led->lmu_data.regmap = priv->regmap; |
247 | led->lmu_data.runtime_ramp_reg = LM3697_CTRL_A_RAMP + |
248 | control_bank; |
249 | led->lmu_data.msb_brightness_reg = LM3697_CTRL_A_BRT_MSB + |
250 | led->control_bank * 2; |
251 | led->lmu_data.lsb_brightness_reg = LM3697_CTRL_A_BRT_LSB + |
252 | led->control_bank * 2; |
253 | |
254 | led->num_leds = fwnode_property_count_u32(fwnode: child, propname: "led-sources" ); |
255 | if (led->num_leds > LM3697_MAX_LED_STRINGS) { |
256 | dev_err(dev, "Too many LED strings defined\n" ); |
257 | continue; |
258 | } |
259 | |
260 | ret = fwnode_property_read_u32_array(fwnode: child, propname: "led-sources" , |
261 | val: led->hvled_strings, |
262 | nval: led->num_leds); |
263 | if (ret) { |
264 | dev_err(dev, "led-sources property missing\n" ); |
265 | goto child_out; |
266 | } |
267 | |
268 | for (j = 0; j < led->num_leds; j++) |
269 | priv->bank_cfg |= |
270 | (led->control_bank << led->hvled_strings[j]); |
271 | |
272 | ret = ti_lmu_common_get_ramp_params(dev, child, lmu_data: &led->lmu_data); |
273 | if (ret) |
274 | dev_warn(dev, "runtime-ramp properties missing\n" ); |
275 | |
276 | init_data.fwnode = child; |
277 | init_data.devicename = priv->client->name; |
278 | /* for backwards compatibility if `label` is not present */ |
279 | init_data.default_label = ":" ; |
280 | |
281 | led->priv = priv; |
282 | led->led_dev.max_brightness = led->lmu_data.max_brightness; |
283 | led->led_dev.brightness_set_blocking = lm3697_brightness_set; |
284 | |
285 | ret = devm_led_classdev_register_ext(parent: dev, led_cdev: &led->led_dev, |
286 | init_data: &init_data); |
287 | if (ret) { |
288 | dev_err(dev, "led register err: %d\n" , ret); |
289 | goto child_out; |
290 | } |
291 | |
292 | i++; |
293 | } |
294 | |
295 | return ret; |
296 | |
297 | child_out: |
298 | fwnode_handle_put(fwnode: child); |
299 | return ret; |
300 | } |
301 | |
302 | static int lm3697_probe(struct i2c_client *client) |
303 | { |
304 | struct device *dev = &client->dev; |
305 | struct lm3697 *led; |
306 | int count; |
307 | int ret; |
308 | |
309 | count = device_get_child_node_count(dev); |
310 | if (!count || count > LM3697_MAX_CONTROL_BANKS) { |
311 | dev_err(dev, "Strange device tree!" ); |
312 | return -ENODEV; |
313 | } |
314 | |
315 | led = devm_kzalloc(dev, struct_size(led, leds, count), GFP_KERNEL); |
316 | if (!led) |
317 | return -ENOMEM; |
318 | |
319 | mutex_init(&led->lock); |
320 | i2c_set_clientdata(client, data: led); |
321 | |
322 | led->client = client; |
323 | led->dev = dev; |
324 | led->num_banks = count; |
325 | led->regmap = devm_regmap_init_i2c(client, &lm3697_regmap_config); |
326 | if (IS_ERR(ptr: led->regmap)) { |
327 | ret = PTR_ERR(ptr: led->regmap); |
328 | dev_err(dev, "Failed to allocate register map: %d\n" , ret); |
329 | return ret; |
330 | } |
331 | |
332 | ret = lm3697_probe_dt(priv: led); |
333 | if (ret) |
334 | return ret; |
335 | |
336 | return lm3697_init(priv: led); |
337 | } |
338 | |
339 | static void lm3697_remove(struct i2c_client *client) |
340 | { |
341 | struct lm3697 *led = i2c_get_clientdata(client); |
342 | struct device *dev = &led->client->dev; |
343 | int ret; |
344 | |
345 | ret = regmap_update_bits(map: led->regmap, LM3697_CTRL_ENABLE, |
346 | LM3697_CTRL_A_B_EN, val: 0); |
347 | if (ret) |
348 | dev_err(dev, "Failed to disable the device\n" ); |
349 | |
350 | if (led->enable_gpio) |
351 | gpiod_direction_output(desc: led->enable_gpio, value: 0); |
352 | |
353 | if (led->regulator) { |
354 | ret = regulator_disable(regulator: led->regulator); |
355 | if (ret) |
356 | dev_err(dev, "Failed to disable regulator\n" ); |
357 | } |
358 | |
359 | mutex_destroy(lock: &led->lock); |
360 | } |
361 | |
362 | static const struct i2c_device_id lm3697_id[] = { |
363 | { "lm3697" , 0 }, |
364 | { } |
365 | }; |
366 | MODULE_DEVICE_TABLE(i2c, lm3697_id); |
367 | |
368 | static const struct of_device_id of_lm3697_leds_match[] = { |
369 | { .compatible = "ti,lm3697" , }, |
370 | {}, |
371 | }; |
372 | MODULE_DEVICE_TABLE(of, of_lm3697_leds_match); |
373 | |
374 | static struct i2c_driver lm3697_driver = { |
375 | .driver = { |
376 | .name = "lm3697" , |
377 | .of_match_table = of_lm3697_leds_match, |
378 | }, |
379 | .probe = lm3697_probe, |
380 | .remove = lm3697_remove, |
381 | .id_table = lm3697_id, |
382 | }; |
383 | module_i2c_driver(lm3697_driver); |
384 | |
385 | MODULE_DESCRIPTION("Texas Instruments LM3697 LED driver" ); |
386 | MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>" ); |
387 | MODULE_LICENSE("GPL v2" ); |
388 | |