380 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * lp5523.c - LP5523, LP55231 LED Driver4 *5 * Copyright (C) 2010 Nokia Corporation6 * Copyright (C) 2012 Texas Instruments7 *8 * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>9 * Milo(Woogyom) Kim <milo.kim@ti.com>10 */11 12#include <linux/cleanup.h>13#include <linux/delay.h>14#include <linux/firmware.h>15#include <linux/i2c.h>16#include <linux/leds.h>17#include <linux/module.h>18#include <linux/mutex.h>19#include <linux/of.h>20#include <linux/platform_data/leds-lp55xx.h>21#include <linux/slab.h>22 23#include "leds-lp55xx-common.h"24 25/* Memory is used like this:26 * 0x00 engine 1 program27 * 0x10 engine 2 program28 * 0x20 engine 3 program29 * 0x30 engine 1 muxing info30 * 0x40 engine 2 muxing info31 * 0x50 engine 3 muxing info32 */33#define LP5523_PAGES_PER_ENGINE 134#define LP5523_MAX_LEDS 935 36/* Registers */37#define LP5523_REG_ENABLE 0x0038#define LP5523_REG_OP_MODE 0x0139#define LP5523_REG_ENABLE_LEDS_MSB 0x0440#define LP5523_REG_ENABLE_LEDS_LSB 0x0541#define LP5523_REG_LED_CTRL_BASE 0x0642#define LP5523_REG_LED_PWM_BASE 0x1643#define LP5523_REG_LED_CURRENT_BASE 0x2644#define LP5523_REG_CONFIG 0x3645 46#define LP5523_REG_STATUS 0x3A47#define LP5523_ENGINE_BUSY BIT(4)48 49#define LP5523_REG_RESET 0x3D50#define LP5523_REG_LED_TEST_CTRL 0x4151#define LP5523_REG_LED_TEST_ADC 0x4252#define LP5523_REG_MASTER_FADER_BASE 0x4853#define LP5523_REG_CH1_PROG_START 0x4C54#define LP5523_REG_CH2_PROG_START 0x4D55#define LP5523_REG_CH3_PROG_START 0x4E56#define LP5523_REG_PROG_PAGE_SEL 0x4F57#define LP5523_REG_PROG_MEM 0x5058 59/* Bit description in registers */60#define LP5523_ENABLE 0x4061#define LP5523_AUTO_INC 0x4062#define LP5523_PWR_SAVE 0x2063#define LP5523_PWM_PWR_SAVE 0x0464#define LP5523_CP_MODE_MASK 0x1865#define LP5523_CP_MODE_SHIFT 366#define LP5523_AUTO_CLK 0x0267#define LP5523_DEFAULT_CONFIG \68 (LP5523_AUTO_INC | LP5523_PWR_SAVE | LP5523_AUTO_CLK | LP5523_PWM_PWR_SAVE)69 70#define LP5523_EN_LEDTEST 0x8071#define LP5523_LEDTEST_DONE 0x8072#define LP5523_RESET 0xFF73#define LP5523_ADC_SHORTCIRC_LIM 8074#define LP5523_EXT_CLK_USED 0x0875#define LP5523_ENG_STATUS_MASK 0x0776 77static int lp5523_init_program_engine(struct lp55xx_chip *chip);78 79static int lp5523_post_init_device(struct lp55xx_chip *chip)80{81 int ret;82 int val;83 84 ret = lp55xx_write(chip, LP5523_REG_ENABLE, LP5523_ENABLE);85 if (ret)86 return ret;87 88 /* Chip startup time is 500 us, 1 - 2 ms gives some margin */89 usleep_range(1000, 2000);90 91 val = LP5523_DEFAULT_CONFIG;92 val |= (chip->pdata->charge_pump_mode << LP5523_CP_MODE_SHIFT) & LP5523_CP_MODE_MASK;93 94 ret = lp55xx_write(chip, LP5523_REG_CONFIG, val);95 if (ret)96 return ret;97 98 /* turn on all leds */99 ret = lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_MSB, 0x01);100 if (ret)101 return ret;102 103 ret = lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff);104 if (ret)105 return ret;106 107 return lp5523_init_program_engine(chip);108}109 110static void lp5523_run_engine(struct lp55xx_chip *chip, bool start)111{112 /* stop engine */113 if (!start) {114 lp55xx_stop_engine(chip);115 lp55xx_turn_off_channels(chip);116 return;117 }118 119 lp55xx_run_engine_common(chip);120}121 122static int lp5523_init_program_engine(struct lp55xx_chip *chip)123{124 int i;125 int j;126 int ret;127 u8 status;128 /* one pattern per engine setting LED MUX start and stop addresses */129 static const u8 pattern[][LP55xx_BYTES_PER_PAGE] = {130 { 0x9c, 0x30, 0x9c, 0xb0, 0x9d, 0x80, 0xd8, 0x00, 0},131 { 0x9c, 0x40, 0x9c, 0xc0, 0x9d, 0x80, 0xd8, 0x00, 0},132 { 0x9c, 0x50, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0},133 };134 135 /* hardcode 32 bytes of memory for each engine from program memory */136 ret = lp55xx_write(chip, LP5523_REG_CH1_PROG_START, 0x00);137 if (ret)138 return ret;139 140 ret = lp55xx_write(chip, LP5523_REG_CH2_PROG_START, 0x10);141 if (ret)142 return ret;143 144 ret = lp55xx_write(chip, LP5523_REG_CH3_PROG_START, 0x20);145 if (ret)146 return ret;147 148 /* write LED MUX address space for each engine */149 for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) {150 chip->engine_idx = i;151 lp55xx_load_engine(chip);152 153 for (j = 0; j < LP55xx_BYTES_PER_PAGE; j++) {154 ret = lp55xx_write(chip, LP5523_REG_PROG_MEM + j,155 pattern[i - 1][j]);156 if (ret)157 goto out;158 }159 }160 161 lp5523_run_engine(chip, true);162 163 /* Let the programs run for couple of ms and check the engine status */164 usleep_range(3000, 6000);165 ret = lp55xx_read(chip, LP5523_REG_STATUS, &status);166 if (ret)167 goto out;168 status &= LP5523_ENG_STATUS_MASK;169 170 if (status != LP5523_ENG_STATUS_MASK) {171 dev_err(&chip->cl->dev,172 "could not configure LED engine, status = 0x%.2x\n",173 status);174 ret = -1;175 }176 177out:178 lp55xx_stop_all_engine(chip);179 return ret;180}181 182static ssize_t lp5523_selftest(struct device *dev,183 struct device_attribute *attr,184 char *buf)185{186 struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));187 struct lp55xx_chip *chip = led->chip;188 struct lp55xx_platform_data *pdata = chip->pdata;189 int ret, pos = 0;190 u8 status, adc, vdd, i;191 192 guard(mutex)(&chip->lock);193 194 ret = lp55xx_read(chip, LP5523_REG_STATUS, &status);195 if (ret < 0)196 return sysfs_emit(buf, "FAIL\n");197 198 /* Check that ext clock is really in use if requested */199 if (pdata->clock_mode == LP55XX_CLOCK_EXT) {200 if ((status & LP5523_EXT_CLK_USED) == 0)201 return sysfs_emit(buf, "FAIL\n");202 }203 204 /* Measure VDD (i.e. VBAT) first (channel 16 corresponds to VDD) */205 lp55xx_write(chip, LP5523_REG_LED_TEST_CTRL, LP5523_EN_LEDTEST | 16);206 usleep_range(3000, 6000); /* ADC conversion time is typically 2.7 ms */207 ret = lp55xx_read(chip, LP5523_REG_STATUS, &status);208 if (ret < 0)209 return sysfs_emit(buf, "FAIL\n");210 211 if (!(status & LP5523_LEDTEST_DONE))212 usleep_range(3000, 6000); /* Was not ready. Wait little bit */213 214 ret = lp55xx_read(chip, LP5523_REG_LED_TEST_ADC, &vdd);215 if (ret < 0)216 return sysfs_emit(buf, "FAIL\n");217 218 vdd--; /* There may be some fluctuation in measurement */219 220 for (i = 0; i < pdata->num_channels; i++) {221 /* Skip disabled channels */222 if (pdata->led_config[i].led_current == 0)223 continue;224 225 /* Set default current */226 lp55xx_write(chip, LP5523_REG_LED_CURRENT_BASE + led->chan_nr,227 pdata->led_config[i].led_current);228 229 lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr,230 0xff);231 /* let current stabilize 2 - 4ms before measurements start */232 usleep_range(2000, 4000);233 lp55xx_write(chip, LP5523_REG_LED_TEST_CTRL,234 LP5523_EN_LEDTEST | led->chan_nr);235 /* ADC conversion time is 2.7 ms typically */236 usleep_range(3000, 6000);237 ret = lp55xx_read(chip, LP5523_REG_STATUS, &status);238 if (ret < 0)239 return sysfs_emit(buf, "FAIL\n");240 241 if (!(status & LP5523_LEDTEST_DONE))242 usleep_range(3000, 6000); /* Was not ready. Wait. */243 244 ret = lp55xx_read(chip, LP5523_REG_LED_TEST_ADC, &adc);245 if (ret < 0)246 return sysfs_emit(buf, "FAIL\n");247 248 if (adc >= vdd || adc < LP5523_ADC_SHORTCIRC_LIM)249 pos += sysfs_emit_at(buf, pos, "LED %d FAIL\n",250 led->chan_nr);251 252 lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr,253 0x00);254 255 /* Restore current */256 lp55xx_write(chip, LP5523_REG_LED_CURRENT_BASE + led->chan_nr,257 led->led_current);258 led++;259 }260 261 return pos == 0 ? sysfs_emit(buf, "OK\n") : pos;262}263 264LP55XX_DEV_ATTR_ENGINE_MODE(1);265LP55XX_DEV_ATTR_ENGINE_MODE(2);266LP55XX_DEV_ATTR_ENGINE_MODE(3);267LP55XX_DEV_ATTR_ENGINE_LEDS(1);268LP55XX_DEV_ATTR_ENGINE_LEDS(2);269LP55XX_DEV_ATTR_ENGINE_LEDS(3);270LP55XX_DEV_ATTR_ENGINE_LOAD(1);271LP55XX_DEV_ATTR_ENGINE_LOAD(2);272LP55XX_DEV_ATTR_ENGINE_LOAD(3);273static LP55XX_DEV_ATTR_RO(selftest, lp5523_selftest);274LP55XX_DEV_ATTR_MASTER_FADER(1);275LP55XX_DEV_ATTR_MASTER_FADER(2);276LP55XX_DEV_ATTR_MASTER_FADER(3);277static LP55XX_DEV_ATTR_RW(master_fader_leds, lp55xx_show_master_fader_leds,278 lp55xx_store_master_fader_leds);279 280static struct attribute *lp5523_attributes[] = {281 &dev_attr_engine1_mode.attr,282 &dev_attr_engine2_mode.attr,283 &dev_attr_engine3_mode.attr,284 &dev_attr_engine1_load.attr,285 &dev_attr_engine2_load.attr,286 &dev_attr_engine3_load.attr,287 &dev_attr_engine1_leds.attr,288 &dev_attr_engine2_leds.attr,289 &dev_attr_engine3_leds.attr,290 &dev_attr_selftest.attr,291 &dev_attr_master_fader1.attr,292 &dev_attr_master_fader2.attr,293 &dev_attr_master_fader3.attr,294 &dev_attr_master_fader_leds.attr,295 NULL,296};297 298static const struct attribute_group lp5523_group = {299 .attrs = lp5523_attributes,300};301 302/* Chip specific configurations */303static struct lp55xx_device_config lp5523_cfg = {304 .reg_op_mode = {305 .addr = LP5523_REG_OP_MODE,306 },307 .reg_exec = {308 .addr = LP5523_REG_ENABLE,309 },310 .engine_busy = {311 .addr = LP5523_REG_STATUS,312 .mask = LP5523_ENGINE_BUSY,313 },314 .reset = {315 .addr = LP5523_REG_RESET,316 .val = LP5523_RESET,317 },318 .enable = {319 .addr = LP5523_REG_ENABLE,320 .val = LP5523_ENABLE,321 },322 .prog_mem_base = {323 .addr = LP5523_REG_PROG_MEM,324 },325 .reg_led_pwm_base = {326 .addr = LP5523_REG_LED_PWM_BASE,327 },328 .reg_led_current_base = {329 .addr = LP5523_REG_LED_CURRENT_BASE,330 },331 .reg_master_fader_base = {332 .addr = LP5523_REG_MASTER_FADER_BASE,333 },334 .reg_led_ctrl_base = {335 .addr = LP5523_REG_LED_CTRL_BASE,336 },337 .pages_per_engine = LP5523_PAGES_PER_ENGINE,338 .max_channel = LP5523_MAX_LEDS,339 .post_init_device = lp5523_post_init_device,340 .brightness_fn = lp55xx_led_brightness,341 .multicolor_brightness_fn = lp55xx_multicolor_brightness,342 .set_led_current = lp55xx_set_led_current,343 .firmware_cb = lp55xx_firmware_loaded_cb,344 .run_engine = lp5523_run_engine,345 .dev_attr_group = &lp5523_group,346};347 348static const struct i2c_device_id lp5523_id[] = {349 { "lp5523", .driver_data = (kernel_ulong_t)&lp5523_cfg, },350 { "lp55231", .driver_data = (kernel_ulong_t)&lp5523_cfg, },351 { }352};353 354MODULE_DEVICE_TABLE(i2c, lp5523_id);355 356static const struct of_device_id of_lp5523_leds_match[] = {357 { .compatible = "national,lp5523", .data = &lp5523_cfg, },358 { .compatible = "ti,lp55231", .data = &lp5523_cfg, },359 {},360};361 362MODULE_DEVICE_TABLE(of, of_lp5523_leds_match);363 364static struct i2c_driver lp5523_driver = {365 .driver = {366 .name = "lp5523x",367 .of_match_table = of_lp5523_leds_match,368 },369 .probe = lp55xx_probe,370 .remove = lp55xx_remove,371 .id_table = lp5523_id,372};373 374module_i2c_driver(lp5523_driver);375 376MODULE_AUTHOR("Mathias Nyman <mathias.nyman@nokia.com>");377MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");378MODULE_DESCRIPTION("LP5523 LED engine");379MODULE_LICENSE("GPL");380