leds-mt6323.c (12608B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * LED driver for Mediatek MT6323 PMIC 4 * 5 * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com> 6 */ 7#include <linux/kernel.h> 8#include <linux/leds.h> 9#include <linux/mfd/mt6323/registers.h> 10#include <linux/mfd/mt6397/core.h> 11#include <linux/module.h> 12#include <linux/of.h> 13#include <linux/platform_device.h> 14#include <linux/regmap.h> 15 16/* 17 * Register field for MT6323_TOP_CKPDN0 to enable 18 * 32K clock common for LED device. 19 */ 20#define MT6323_RG_DRV_32K_CK_PDN BIT(11) 21#define MT6323_RG_DRV_32K_CK_PDN_MASK BIT(11) 22 23/* 24 * Register field for MT6323_TOP_CKPDN2 to enable 25 * individual clock for LED device. 26 */ 27#define MT6323_RG_ISINK_CK_PDN(i) BIT(i) 28#define MT6323_RG_ISINK_CK_PDN_MASK(i) BIT(i) 29 30/* 31 * Register field for MT6323_TOP_CKCON1 to select 32 * clock source. 33 */ 34#define MT6323_RG_ISINK_CK_SEL_MASK(i) (BIT(10) << (i)) 35 36/* 37 * Register for MT6323_ISINK_CON0 to setup the 38 * duty cycle of the blink. 39 */ 40#define MT6323_ISINK_CON0(i) (MT6323_ISINK0_CON0 + 0x8 * (i)) 41#define MT6323_ISINK_DIM_DUTY_MASK (0x1f << 8) 42#define MT6323_ISINK_DIM_DUTY(i) (((i) << 8) & \ 43 MT6323_ISINK_DIM_DUTY_MASK) 44 45/* Register to setup the period of the blink. */ 46#define MT6323_ISINK_CON1(i) (MT6323_ISINK0_CON1 + 0x8 * (i)) 47#define MT6323_ISINK_DIM_FSEL_MASK (0xffff) 48#define MT6323_ISINK_DIM_FSEL(i) ((i) & MT6323_ISINK_DIM_FSEL_MASK) 49 50/* Register to control the brightness. */ 51#define MT6323_ISINK_CON2(i) (MT6323_ISINK0_CON2 + 0x8 * (i)) 52#define MT6323_ISINK_CH_STEP_SHIFT 12 53#define MT6323_ISINK_CH_STEP_MASK (0x7 << 12) 54#define MT6323_ISINK_CH_STEP(i) (((i) << 12) & \ 55 MT6323_ISINK_CH_STEP_MASK) 56#define MT6323_ISINK_SFSTR0_TC_MASK (0x3 << 1) 57#define MT6323_ISINK_SFSTR0_TC(i) (((i) << 1) & \ 58 MT6323_ISINK_SFSTR0_TC_MASK) 59#define MT6323_ISINK_SFSTR0_EN_MASK BIT(0) 60#define MT6323_ISINK_SFSTR0_EN BIT(0) 61 62/* Register to LED channel enablement. */ 63#define MT6323_ISINK_CH_EN_MASK(i) BIT(i) 64#define MT6323_ISINK_CH_EN(i) BIT(i) 65 66#define MT6323_MAX_PERIOD 10000 67#define MT6323_MAX_LEDS 4 68#define MT6323_MAX_BRIGHTNESS 6 69#define MT6323_UNIT_DUTY 3125 70#define MT6323_CAL_HW_DUTY(o, p) DIV_ROUND_CLOSEST((o) * 100000ul,\ 71 (p) * MT6323_UNIT_DUTY) 72 73struct mt6323_leds; 74 75/** 76 * struct mt6323_led - state container for the LED device 77 * @id: the identifier in MT6323 LED device 78 * @parent: the pointer to MT6323 LED controller 79 * @cdev: LED class device for this LED device 80 * @current_brightness: current state of the LED device 81 */ 82struct mt6323_led { 83 int id; 84 struct mt6323_leds *parent; 85 struct led_classdev cdev; 86 enum led_brightness current_brightness; 87}; 88 89/** 90 * struct mt6323_leds - state container for holding LED controller 91 * of the driver 92 * @dev: the device pointer 93 * @hw: the underlying hardware providing shared 94 * bus for the register operations 95 * @lock: the lock among process context 96 * @led: the array that contains the state of individual 97 * LED device 98 */ 99struct mt6323_leds { 100 struct device *dev; 101 struct mt6397_chip *hw; 102 /* protect among process context */ 103 struct mutex lock; 104 struct mt6323_led *led[MT6323_MAX_LEDS]; 105}; 106 107static int mt6323_led_hw_brightness(struct led_classdev *cdev, 108 enum led_brightness brightness) 109{ 110 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 111 struct mt6323_leds *leds = led->parent; 112 struct regmap *regmap = leds->hw->regmap; 113 u32 con2_mask = 0, con2_val = 0; 114 int ret; 115 116 /* 117 * Setup current output for the corresponding 118 * brightness level. 119 */ 120 con2_mask |= MT6323_ISINK_CH_STEP_MASK | 121 MT6323_ISINK_SFSTR0_TC_MASK | 122 MT6323_ISINK_SFSTR0_EN_MASK; 123 con2_val |= MT6323_ISINK_CH_STEP(brightness - 1) | 124 MT6323_ISINK_SFSTR0_TC(2) | 125 MT6323_ISINK_SFSTR0_EN; 126 127 ret = regmap_update_bits(regmap, MT6323_ISINK_CON2(led->id), 128 con2_mask, con2_val); 129 return ret; 130} 131 132static int mt6323_led_hw_off(struct led_classdev *cdev) 133{ 134 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 135 struct mt6323_leds *leds = led->parent; 136 struct regmap *regmap = leds->hw->regmap; 137 unsigned int status; 138 int ret; 139 140 status = MT6323_ISINK_CH_EN(led->id); 141 ret = regmap_update_bits(regmap, MT6323_ISINK_EN_CTRL, 142 MT6323_ISINK_CH_EN_MASK(led->id), ~status); 143 if (ret < 0) 144 return ret; 145 146 usleep_range(100, 300); 147 ret = regmap_update_bits(regmap, MT6323_TOP_CKPDN2, 148 MT6323_RG_ISINK_CK_PDN_MASK(led->id), 149 MT6323_RG_ISINK_CK_PDN(led->id)); 150 if (ret < 0) 151 return ret; 152 153 return 0; 154} 155 156static enum led_brightness 157mt6323_get_led_hw_brightness(struct led_classdev *cdev) 158{ 159 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 160 struct mt6323_leds *leds = led->parent; 161 struct regmap *regmap = leds->hw->regmap; 162 unsigned int status; 163 int ret; 164 165 ret = regmap_read(regmap, MT6323_TOP_CKPDN2, &status); 166 if (ret < 0) 167 return ret; 168 169 if (status & MT6323_RG_ISINK_CK_PDN_MASK(led->id)) 170 return 0; 171 172 ret = regmap_read(regmap, MT6323_ISINK_EN_CTRL, &status); 173 if (ret < 0) 174 return ret; 175 176 if (!(status & MT6323_ISINK_CH_EN(led->id))) 177 return 0; 178 179 ret = regmap_read(regmap, MT6323_ISINK_CON2(led->id), &status); 180 if (ret < 0) 181 return ret; 182 183 return ((status & MT6323_ISINK_CH_STEP_MASK) 184 >> MT6323_ISINK_CH_STEP_SHIFT) + 1; 185} 186 187static int mt6323_led_hw_on(struct led_classdev *cdev, 188 enum led_brightness brightness) 189{ 190 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 191 struct mt6323_leds *leds = led->parent; 192 struct regmap *regmap = leds->hw->regmap; 193 unsigned int status; 194 int ret; 195 196 /* 197 * Setup required clock source, enable the corresponding 198 * clock and channel and let work with continuous blink as 199 * the default. 200 */ 201 ret = regmap_update_bits(regmap, MT6323_TOP_CKCON1, 202 MT6323_RG_ISINK_CK_SEL_MASK(led->id), 0); 203 if (ret < 0) 204 return ret; 205 206 status = MT6323_RG_ISINK_CK_PDN(led->id); 207 ret = regmap_update_bits(regmap, MT6323_TOP_CKPDN2, 208 MT6323_RG_ISINK_CK_PDN_MASK(led->id), 209 ~status); 210 if (ret < 0) 211 return ret; 212 213 usleep_range(100, 300); 214 215 ret = regmap_update_bits(regmap, MT6323_ISINK_EN_CTRL, 216 MT6323_ISINK_CH_EN_MASK(led->id), 217 MT6323_ISINK_CH_EN(led->id)); 218 if (ret < 0) 219 return ret; 220 221 ret = mt6323_led_hw_brightness(cdev, brightness); 222 if (ret < 0) 223 return ret; 224 225 ret = regmap_update_bits(regmap, MT6323_ISINK_CON0(led->id), 226 MT6323_ISINK_DIM_DUTY_MASK, 227 MT6323_ISINK_DIM_DUTY(31)); 228 if (ret < 0) 229 return ret; 230 231 ret = regmap_update_bits(regmap, MT6323_ISINK_CON1(led->id), 232 MT6323_ISINK_DIM_FSEL_MASK, 233 MT6323_ISINK_DIM_FSEL(1000)); 234 if (ret < 0) 235 return ret; 236 237 return 0; 238} 239 240static int mt6323_led_set_blink(struct led_classdev *cdev, 241 unsigned long *delay_on, 242 unsigned long *delay_off) 243{ 244 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 245 struct mt6323_leds *leds = led->parent; 246 struct regmap *regmap = leds->hw->regmap; 247 unsigned long period; 248 u8 duty_hw; 249 int ret; 250 251 /* 252 * LED subsystem requires a default user 253 * friendly blink pattern for the LED so using 254 * 1Hz duty cycle 50% here if without specific 255 * value delay_on and delay off being assigned. 256 */ 257 if (!*delay_on && !*delay_off) { 258 *delay_on = 500; 259 *delay_off = 500; 260 } 261 262 /* 263 * Units are in ms, if over the hardware able 264 * to support, fallback into software blink 265 */ 266 period = *delay_on + *delay_off; 267 268 if (period > MT6323_MAX_PERIOD) 269 return -EINVAL; 270 271 /* 272 * Calculate duty_hw based on the percentage of period during 273 * which the led is ON. 274 */ 275 duty_hw = MT6323_CAL_HW_DUTY(*delay_on, period); 276 277 /* hardware doesn't support zero duty cycle. */ 278 if (!duty_hw) 279 return -EINVAL; 280 281 mutex_lock(&leds->lock); 282 /* 283 * Set max_brightness as the software blink behavior 284 * when no blink brightness. 285 */ 286 if (!led->current_brightness) { 287 ret = mt6323_led_hw_on(cdev, cdev->max_brightness); 288 if (ret < 0) 289 goto out; 290 led->current_brightness = cdev->max_brightness; 291 } 292 293 ret = regmap_update_bits(regmap, MT6323_ISINK_CON0(led->id), 294 MT6323_ISINK_DIM_DUTY_MASK, 295 MT6323_ISINK_DIM_DUTY(duty_hw - 1)); 296 if (ret < 0) 297 goto out; 298 299 ret = regmap_update_bits(regmap, MT6323_ISINK_CON1(led->id), 300 MT6323_ISINK_DIM_FSEL_MASK, 301 MT6323_ISINK_DIM_FSEL(period - 1)); 302out: 303 mutex_unlock(&leds->lock); 304 305 return ret; 306} 307 308static int mt6323_led_set_brightness(struct led_classdev *cdev, 309 enum led_brightness brightness) 310{ 311 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 312 struct mt6323_leds *leds = led->parent; 313 int ret; 314 315 mutex_lock(&leds->lock); 316 317 if (!led->current_brightness && brightness) { 318 ret = mt6323_led_hw_on(cdev, brightness); 319 if (ret < 0) 320 goto out; 321 } else if (brightness) { 322 ret = mt6323_led_hw_brightness(cdev, brightness); 323 if (ret < 0) 324 goto out; 325 } else { 326 ret = mt6323_led_hw_off(cdev); 327 if (ret < 0) 328 goto out; 329 } 330 331 led->current_brightness = brightness; 332out: 333 mutex_unlock(&leds->lock); 334 335 return ret; 336} 337 338static int mt6323_led_set_dt_default(struct led_classdev *cdev, 339 struct device_node *np) 340{ 341 struct mt6323_led *led = container_of(cdev, struct mt6323_led, cdev); 342 const char *state; 343 int ret = 0; 344 345 state = of_get_property(np, "default-state", NULL); 346 if (state) { 347 if (!strcmp(state, "keep")) { 348 ret = mt6323_get_led_hw_brightness(cdev); 349 if (ret < 0) 350 return ret; 351 led->current_brightness = ret; 352 ret = 0; 353 } else if (!strcmp(state, "on")) { 354 ret = 355 mt6323_led_set_brightness(cdev, cdev->max_brightness); 356 } else { 357 ret = mt6323_led_set_brightness(cdev, LED_OFF); 358 } 359 } 360 361 return ret; 362} 363 364static int mt6323_led_probe(struct platform_device *pdev) 365{ 366 struct device *dev = &pdev->dev; 367 struct device_node *np = dev_of_node(dev); 368 struct device_node *child; 369 struct mt6397_chip *hw = dev_get_drvdata(dev->parent); 370 struct mt6323_leds *leds; 371 struct mt6323_led *led; 372 int ret; 373 unsigned int status; 374 u32 reg; 375 376 leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL); 377 if (!leds) 378 return -ENOMEM; 379 380 platform_set_drvdata(pdev, leds); 381 leds->dev = dev; 382 383 /* 384 * leds->hw points to the underlying bus for the register 385 * controlled. 386 */ 387 leds->hw = hw; 388 mutex_init(&leds->lock); 389 390 status = MT6323_RG_DRV_32K_CK_PDN; 391 ret = regmap_update_bits(leds->hw->regmap, MT6323_TOP_CKPDN0, 392 MT6323_RG_DRV_32K_CK_PDN_MASK, ~status); 393 if (ret < 0) { 394 dev_err(leds->dev, 395 "Failed to update MT6323_TOP_CKPDN0 Register\n"); 396 return ret; 397 } 398 399 for_each_available_child_of_node(np, child) { 400 struct led_init_data init_data = {}; 401 402 ret = of_property_read_u32(child, "reg", ®); 403 if (ret) { 404 dev_err(dev, "Failed to read led 'reg' property\n"); 405 goto put_child_node; 406 } 407 408 if (reg >= MT6323_MAX_LEDS || leds->led[reg]) { 409 dev_err(dev, "Invalid led reg %u\n", reg); 410 ret = -EINVAL; 411 goto put_child_node; 412 } 413 414 led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); 415 if (!led) { 416 ret = -ENOMEM; 417 goto put_child_node; 418 } 419 420 leds->led[reg] = led; 421 leds->led[reg]->id = reg; 422 leds->led[reg]->cdev.max_brightness = MT6323_MAX_BRIGHTNESS; 423 leds->led[reg]->cdev.brightness_set_blocking = 424 mt6323_led_set_brightness; 425 leds->led[reg]->cdev.blink_set = mt6323_led_set_blink; 426 leds->led[reg]->cdev.brightness_get = 427 mt6323_get_led_hw_brightness; 428 leds->led[reg]->parent = leds; 429 430 ret = mt6323_led_set_dt_default(&leds->led[reg]->cdev, child); 431 if (ret < 0) { 432 dev_err(leds->dev, 433 "Failed to LED set default from devicetree\n"); 434 goto put_child_node; 435 } 436 437 init_data.fwnode = of_fwnode_handle(child); 438 439 ret = devm_led_classdev_register_ext(dev, &leds->led[reg]->cdev, 440 &init_data); 441 if (ret) { 442 dev_err(dev, "Failed to register LED: %d\n", ret); 443 goto put_child_node; 444 } 445 } 446 447 return 0; 448 449put_child_node: 450 of_node_put(child); 451 return ret; 452} 453 454static int mt6323_led_remove(struct platform_device *pdev) 455{ 456 struct mt6323_leds *leds = platform_get_drvdata(pdev); 457 int i; 458 459 /* Turn the LEDs off on driver removal. */ 460 for (i = 0 ; leds->led[i] ; i++) 461 mt6323_led_hw_off(&leds->led[i]->cdev); 462 463 regmap_update_bits(leds->hw->regmap, MT6323_TOP_CKPDN0, 464 MT6323_RG_DRV_32K_CK_PDN_MASK, 465 MT6323_RG_DRV_32K_CK_PDN); 466 467 mutex_destroy(&leds->lock); 468 469 return 0; 470} 471 472static const struct of_device_id mt6323_led_dt_match[] = { 473 { .compatible = "mediatek,mt6323-led" }, 474 {}, 475}; 476MODULE_DEVICE_TABLE(of, mt6323_led_dt_match); 477 478static struct platform_driver mt6323_led_driver = { 479 .probe = mt6323_led_probe, 480 .remove = mt6323_led_remove, 481 .driver = { 482 .name = "mt6323-led", 483 .of_match_table = mt6323_led_dt_match, 484 }, 485}; 486 487module_platform_driver(mt6323_led_driver); 488 489MODULE_DESCRIPTION("LED driver for Mediatek MT6323 PMIC"); 490MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); 491MODULE_LICENSE("GPL");