| .. | .. |
|---|
| 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; |
|---|