.. | .. |
---|
1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
---|
2 | 2 | // TI LM3692x LED chip family driver |
---|
3 | | -// Copyright (C) 2017-18 Texas Instruments Incorporated - http://www.ti.com/ |
---|
| 3 | +// Copyright (C) 2017-18 Texas Instruments Incorporated - https://www.ti.com/ |
---|
4 | 4 | |
---|
5 | 5 | #include <linux/gpio/consumer.h> |
---|
6 | 6 | #include <linux/i2c.h> |
---|
7 | 7 | #include <linux/init.h> |
---|
8 | 8 | #include <linux/leds.h> |
---|
| 9 | +#include <linux/log2.h> |
---|
9 | 10 | #include <linux/module.h> |
---|
10 | 11 | #include <linux/mutex.h> |
---|
11 | 12 | #include <linux/of.h> |
---|
.. | .. |
---|
13 | 14 | #include <linux/regmap.h> |
---|
14 | 15 | #include <linux/regulator/consumer.h> |
---|
15 | 16 | #include <linux/slab.h> |
---|
16 | | -#include <uapi/linux/uleds.h> |
---|
17 | 17 | |
---|
18 | 18 | #define LM36922_MODEL 0 |
---|
19 | 19 | #define LM36923_MODEL 1 |
---|
.. | .. |
---|
103 | 103 | * @regmap - Devices register map |
---|
104 | 104 | * @enable_gpio - VDDIO/EN gpio to enable communication interface |
---|
105 | 105 | * @regulator - LED supply regulator pointer |
---|
106 | | - * @label - LED label |
---|
107 | 106 | * @led_enable - LED sync to be enabled |
---|
108 | 107 | * @model_id - Current device model ID enumerated |
---|
109 | 108 | */ |
---|
.. | .. |
---|
114 | 113 | struct regmap *regmap; |
---|
115 | 114 | struct gpio_desc *enable_gpio; |
---|
116 | 115 | struct regulator *regulator; |
---|
117 | | - char label[LED_MAX_NAME_SIZE]; |
---|
118 | 116 | int led_enable; |
---|
119 | 117 | int model_id; |
---|
| 118 | + |
---|
| 119 | + u8 boost_ctrl, brightness_ctrl; |
---|
| 120 | + bool enabled; |
---|
120 | 121 | }; |
---|
121 | 122 | |
---|
122 | 123 | static const struct reg_default lm3692x_reg_defs[] = { |
---|
.. | .. |
---|
165 | 166 | return read_buf; |
---|
166 | 167 | } |
---|
167 | 168 | |
---|
168 | | -static int lm3692x_brightness_set(struct led_classdev *led_cdev, |
---|
169 | | - enum led_brightness brt_val) |
---|
170 | | -{ |
---|
171 | | - struct lm3692x_led *led = |
---|
172 | | - container_of(led_cdev, struct lm3692x_led, led_dev); |
---|
173 | | - int ret; |
---|
174 | | - int led_brightness_lsb = (brt_val >> 5); |
---|
175 | | - |
---|
176 | | - mutex_lock(&led->lock); |
---|
177 | | - |
---|
178 | | - ret = lm3692x_fault_check(led); |
---|
179 | | - if (ret) { |
---|
180 | | - dev_err(&led->client->dev, "Cannot read/clear faults\n"); |
---|
181 | | - goto out; |
---|
182 | | - } |
---|
183 | | - |
---|
184 | | - ret = regmap_write(led->regmap, LM3692X_BRT_MSB, brt_val); |
---|
185 | | - if (ret) { |
---|
186 | | - dev_err(&led->client->dev, "Cannot write MSB\n"); |
---|
187 | | - goto out; |
---|
188 | | - } |
---|
189 | | - |
---|
190 | | - ret = regmap_write(led->regmap, LM3692X_BRT_LSB, led_brightness_lsb); |
---|
191 | | - if (ret) { |
---|
192 | | - dev_err(&led->client->dev, "Cannot write LSB\n"); |
---|
193 | | - goto out; |
---|
194 | | - } |
---|
195 | | -out: |
---|
196 | | - mutex_unlock(&led->lock); |
---|
197 | | - return ret; |
---|
198 | | -} |
---|
199 | | - |
---|
200 | | -static int lm3692x_init(struct lm3692x_led *led) |
---|
| 169 | +static int lm3692x_leds_enable(struct lm3692x_led *led) |
---|
201 | 170 | { |
---|
202 | 171 | int enable_state; |
---|
203 | | - int ret; |
---|
| 172 | + int ret, reg_ret; |
---|
| 173 | + |
---|
| 174 | + if (led->enabled) |
---|
| 175 | + return 0; |
---|
204 | 176 | |
---|
205 | 177 | if (led->regulator) { |
---|
206 | 178 | ret = regulator_enable(led->regulator); |
---|
207 | 179 | if (ret) { |
---|
208 | 180 | dev_err(&led->client->dev, |
---|
209 | | - "Failed to enable regulator\n"); |
---|
| 181 | + "Failed to enable regulator: %d\n", ret); |
---|
210 | 182 | return ret; |
---|
211 | 183 | } |
---|
212 | 184 | } |
---|
.. | .. |
---|
216 | 188 | |
---|
217 | 189 | ret = lm3692x_fault_check(led); |
---|
218 | 190 | if (ret) { |
---|
219 | | - dev_err(&led->client->dev, "Cannot read/clear faults\n"); |
---|
| 191 | + dev_err(&led->client->dev, "Cannot read/clear faults: %d\n", |
---|
| 192 | + ret); |
---|
220 | 193 | goto out; |
---|
221 | 194 | } |
---|
222 | 195 | |
---|
.. | .. |
---|
250 | 223 | if (ret) |
---|
251 | 224 | goto out; |
---|
252 | 225 | |
---|
253 | | - ret = regmap_write(led->regmap, LM3692X_BOOST_CTRL, |
---|
254 | | - LM3692X_BRHT_MODE_RAMP_MULTI | |
---|
255 | | - LM3692X_BL_ADJ_POL | |
---|
256 | | - LM3692X_RAMP_RATE_250us); |
---|
| 226 | + ret = regmap_write(led->regmap, LM3692X_BOOST_CTRL, led->boost_ctrl); |
---|
257 | 227 | if (ret) |
---|
258 | 228 | goto out; |
---|
259 | 229 | |
---|
.. | .. |
---|
270 | 240 | goto out; |
---|
271 | 241 | |
---|
272 | 242 | ret = regmap_write(led->regmap, LM3692X_BRT_CTRL, |
---|
273 | | - LM3692X_BL_ADJ_POL | LM3692X_PWM_HYSTER_4LSB); |
---|
| 243 | + LM3692X_BL_ADJ_POL | LM3692X_RAMP_EN); |
---|
274 | 244 | if (ret) |
---|
275 | 245 | goto out; |
---|
276 | 246 | |
---|
.. | .. |
---|
306 | 276 | ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_ENABLE_MASK, |
---|
307 | 277 | enable_state | LM3692X_DEVICE_EN); |
---|
308 | 278 | |
---|
| 279 | + led->enabled = true; |
---|
309 | 280 | return ret; |
---|
310 | 281 | out: |
---|
311 | 282 | dev_err(&led->client->dev, "Fail writing initialization values\n"); |
---|
.. | .. |
---|
314 | 285 | gpiod_direction_output(led->enable_gpio, 0); |
---|
315 | 286 | |
---|
316 | 287 | if (led->regulator) { |
---|
317 | | - ret = regulator_disable(led->regulator); |
---|
318 | | - if (ret) |
---|
| 288 | + reg_ret = regulator_disable(led->regulator); |
---|
| 289 | + if (reg_ret) |
---|
319 | 290 | dev_err(&led->client->dev, |
---|
320 | | - "Failed to disable regulator\n"); |
---|
| 291 | + "Failed to disable regulator: %d\n", reg_ret); |
---|
321 | 292 | } |
---|
322 | 293 | |
---|
323 | 294 | return ret; |
---|
324 | 295 | } |
---|
| 296 | + |
---|
| 297 | +static int lm3692x_leds_disable(struct lm3692x_led *led) |
---|
| 298 | +{ |
---|
| 299 | + int ret; |
---|
| 300 | + |
---|
| 301 | + if (!led->enabled) |
---|
| 302 | + return 0; |
---|
| 303 | + |
---|
| 304 | + ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_DEVICE_EN, 0); |
---|
| 305 | + if (ret) { |
---|
| 306 | + dev_err(&led->client->dev, "Failed to disable regulator: %d\n", |
---|
| 307 | + ret); |
---|
| 308 | + return ret; |
---|
| 309 | + } |
---|
| 310 | + |
---|
| 311 | + if (led->enable_gpio) |
---|
| 312 | + gpiod_direction_output(led->enable_gpio, 0); |
---|
| 313 | + |
---|
| 314 | + if (led->regulator) { |
---|
| 315 | + ret = regulator_disable(led->regulator); |
---|
| 316 | + if (ret) |
---|
| 317 | + dev_err(&led->client->dev, |
---|
| 318 | + "Failed to disable regulator: %d\n", ret); |
---|
| 319 | + } |
---|
| 320 | + |
---|
| 321 | + led->enabled = false; |
---|
| 322 | + return ret; |
---|
| 323 | +} |
---|
| 324 | + |
---|
| 325 | +static int lm3692x_brightness_set(struct led_classdev *led_cdev, |
---|
| 326 | + enum led_brightness brt_val) |
---|
| 327 | +{ |
---|
| 328 | + struct lm3692x_led *led = |
---|
| 329 | + container_of(led_cdev, struct lm3692x_led, led_dev); |
---|
| 330 | + int ret; |
---|
| 331 | + int led_brightness_lsb = (brt_val >> 5); |
---|
| 332 | + |
---|
| 333 | + mutex_lock(&led->lock); |
---|
| 334 | + |
---|
| 335 | + if (brt_val == 0) { |
---|
| 336 | + ret = lm3692x_leds_disable(led); |
---|
| 337 | + goto out; |
---|
| 338 | + } else { |
---|
| 339 | + lm3692x_leds_enable(led); |
---|
| 340 | + } |
---|
| 341 | + |
---|
| 342 | + ret = lm3692x_fault_check(led); |
---|
| 343 | + if (ret) { |
---|
| 344 | + dev_err(&led->client->dev, "Cannot read/clear faults: %d\n", |
---|
| 345 | + ret); |
---|
| 346 | + goto out; |
---|
| 347 | + } |
---|
| 348 | + |
---|
| 349 | + ret = regmap_write(led->regmap, LM3692X_BRT_MSB, brt_val); |
---|
| 350 | + if (ret) { |
---|
| 351 | + dev_err(&led->client->dev, "Cannot write MSB: %d\n", ret); |
---|
| 352 | + goto out; |
---|
| 353 | + } |
---|
| 354 | + |
---|
| 355 | + ret = regmap_write(led->regmap, LM3692X_BRT_LSB, led_brightness_lsb); |
---|
| 356 | + if (ret) { |
---|
| 357 | + dev_err(&led->client->dev, "Cannot write LSB: %d\n", ret); |
---|
| 358 | + goto out; |
---|
| 359 | + } |
---|
| 360 | +out: |
---|
| 361 | + mutex_unlock(&led->lock); |
---|
| 362 | + return ret; |
---|
| 363 | +} |
---|
| 364 | + |
---|
| 365 | +static enum led_brightness lm3692x_max_brightness(struct lm3692x_led *led, |
---|
| 366 | + u32 max_cur) |
---|
| 367 | +{ |
---|
| 368 | + u32 max_code; |
---|
| 369 | + |
---|
| 370 | + /* see p.12 of LM36922 data sheet for brightness formula */ |
---|
| 371 | + max_code = ((max_cur * 1000) - 37806) / 12195; |
---|
| 372 | + if (max_code > 0x7FF) |
---|
| 373 | + max_code = 0x7FF; |
---|
| 374 | + |
---|
| 375 | + return max_code >> 3; |
---|
| 376 | +} |
---|
| 377 | + |
---|
325 | 378 | static int lm3692x_probe_dt(struct lm3692x_led *led) |
---|
326 | 379 | { |
---|
327 | 380 | struct fwnode_handle *child = NULL; |
---|
328 | | - const char *name; |
---|
| 381 | + struct led_init_data init_data = {}; |
---|
| 382 | + u32 ovp, max_cur; |
---|
329 | 383 | int ret; |
---|
330 | 384 | |
---|
331 | 385 | led->enable_gpio = devm_gpiod_get_optional(&led->client->dev, |
---|
.. | .. |
---|
340 | 394 | led->regulator = devm_regulator_get_optional(&led->client->dev, "vled"); |
---|
341 | 395 | if (IS_ERR(led->regulator)) { |
---|
342 | 396 | ret = PTR_ERR(led->regulator); |
---|
343 | | - if (ret != -ENODEV) { |
---|
344 | | - if (ret != -EPROBE_DEFER) |
---|
345 | | - dev_err(&led->client->dev, |
---|
346 | | - "Failed to get vled regulator: %d\n", |
---|
347 | | - ret); |
---|
348 | | - return ret; |
---|
349 | | - } |
---|
| 397 | + if (ret != -ENODEV) |
---|
| 398 | + return dev_err_probe(&led->client->dev, ret, |
---|
| 399 | + "Failed to get vled regulator\n"); |
---|
| 400 | + |
---|
350 | 401 | led->regulator = NULL; |
---|
| 402 | + } |
---|
| 403 | + |
---|
| 404 | + led->boost_ctrl = LM3692X_BOOST_SW_1MHZ | |
---|
| 405 | + LM3692X_BOOST_SW_NO_SHIFT | |
---|
| 406 | + LM3692X_OCP_PROT_1_5A; |
---|
| 407 | + ret = device_property_read_u32(&led->client->dev, |
---|
| 408 | + "ti,ovp-microvolt", &ovp); |
---|
| 409 | + if (ret) { |
---|
| 410 | + led->boost_ctrl |= LM3692X_OVP_29V; |
---|
| 411 | + } else { |
---|
| 412 | + switch (ovp) { |
---|
| 413 | + case 17000000: |
---|
| 414 | + break; |
---|
| 415 | + case 21000000: |
---|
| 416 | + led->boost_ctrl |= LM3692X_OVP_21V; |
---|
| 417 | + break; |
---|
| 418 | + case 25000000: |
---|
| 419 | + led->boost_ctrl |= LM3692X_OVP_25V; |
---|
| 420 | + break; |
---|
| 421 | + case 29000000: |
---|
| 422 | + led->boost_ctrl |= LM3692X_OVP_29V; |
---|
| 423 | + break; |
---|
| 424 | + default: |
---|
| 425 | + dev_err(&led->client->dev, "Invalid OVP %d\n", ovp); |
---|
| 426 | + return -EINVAL; |
---|
| 427 | + } |
---|
351 | 428 | } |
---|
352 | 429 | |
---|
353 | 430 | child = device_get_next_child_node(&led->client->dev, child); |
---|
.. | .. |
---|
356 | 433 | return -ENODEV; |
---|
357 | 434 | } |
---|
358 | 435 | |
---|
359 | | - fwnode_property_read_string(child, "linux,default-trigger", |
---|
360 | | - &led->led_dev.default_trigger); |
---|
361 | | - |
---|
362 | | - ret = fwnode_property_read_string(child, "label", &name); |
---|
363 | | - if (ret) |
---|
364 | | - snprintf(led->label, sizeof(led->label), |
---|
365 | | - "%s::", led->client->name); |
---|
366 | | - else |
---|
367 | | - snprintf(led->label, sizeof(led->label), |
---|
368 | | - "%s:%s", led->client->name, name); |
---|
369 | | - |
---|
370 | 436 | ret = fwnode_property_read_u32(child, "reg", &led->led_enable); |
---|
371 | 437 | if (ret) { |
---|
| 438 | + fwnode_handle_put(child); |
---|
372 | 439 | dev_err(&led->client->dev, "reg DT property missing\n"); |
---|
373 | 440 | return ret; |
---|
374 | 441 | } |
---|
375 | 442 | |
---|
376 | | - led->led_dev.name = led->label; |
---|
| 443 | + ret = fwnode_property_read_u32(child, "led-max-microamp", &max_cur); |
---|
| 444 | + led->led_dev.max_brightness = ret ? LED_FULL : |
---|
| 445 | + lm3692x_max_brightness(led, max_cur); |
---|
377 | 446 | |
---|
378 | | - ret = devm_led_classdev_register(&led->client->dev, &led->led_dev); |
---|
379 | | - if (ret) { |
---|
| 447 | + init_data.fwnode = child; |
---|
| 448 | + init_data.devicename = led->client->name; |
---|
| 449 | + init_data.default_label = ":"; |
---|
| 450 | + |
---|
| 451 | + ret = devm_led_classdev_register_ext(&led->client->dev, &led->led_dev, |
---|
| 452 | + &init_data); |
---|
| 453 | + if (ret) |
---|
380 | 454 | dev_err(&led->client->dev, "led register err: %d\n", ret); |
---|
381 | | - return ret; |
---|
382 | | - } |
---|
383 | 455 | |
---|
384 | | - led->led_dev.dev->of_node = to_of_node(child); |
---|
385 | | - |
---|
386 | | - return 0; |
---|
| 456 | + fwnode_handle_put(init_data.fwnode); |
---|
| 457 | + return ret; |
---|
387 | 458 | } |
---|
388 | 459 | |
---|
389 | 460 | static int lm3692x_probe(struct i2c_client *client, |
---|
.. | .. |
---|
414 | 485 | if (ret) |
---|
415 | 486 | return ret; |
---|
416 | 487 | |
---|
417 | | - ret = lm3692x_init(led); |
---|
| 488 | + ret = lm3692x_leds_enable(led); |
---|
418 | 489 | if (ret) |
---|
419 | 490 | return ret; |
---|
420 | 491 | |
---|
.. | .. |
---|
426 | 497 | struct lm3692x_led *led = i2c_get_clientdata(client); |
---|
427 | 498 | int ret; |
---|
428 | 499 | |
---|
429 | | - ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_DEVICE_EN, 0); |
---|
430 | | - if (ret) { |
---|
431 | | - dev_err(&led->client->dev, "Failed to disable regulator\n"); |
---|
| 500 | + ret = lm3692x_leds_disable(led); |
---|
| 501 | + if (ret) |
---|
432 | 502 | return ret; |
---|
433 | | - } |
---|
434 | | - |
---|
435 | | - if (led->enable_gpio) |
---|
436 | | - gpiod_direction_output(led->enable_gpio, 0); |
---|
437 | | - |
---|
438 | | - if (led->regulator) { |
---|
439 | | - ret = regulator_disable(led->regulator); |
---|
440 | | - if (ret) |
---|
441 | | - dev_err(&led->client->dev, |
---|
442 | | - "Failed to disable regulator\n"); |
---|
443 | | - } |
---|
444 | | - |
---|
445 | 503 | mutex_destroy(&led->lock); |
---|
446 | 504 | |
---|
447 | 505 | return 0; |
---|