blob: 7564b99534089bd3f7b54b2ebe8e617d8341add6 [file] [log] [blame]
Thomas Gleixner2b27bdc2019-05-29 16:57:50 -07001// SPDX-License-Identifier: GPL-2.0-only
Samu Onkalo500fe142010-11-11 14:05:22 -08002/*
3 * LP5521 LED chip driver.
4 *
5 * Copyright (C) 2010 Nokia Corporation
Milo(Woogyom) Kima2387cb2013-02-05 19:28:00 +09006 * Copyright (C) 2012 Texas Instruments
Samu Onkalo500fe142010-11-11 14:05:22 -08007 *
8 * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
Milo(Woogyom) Kima2387cb2013-02-05 19:28:00 +09009 * Milo(Woogyom) Kim <milo.kim@ti.com>
Samu Onkalo500fe142010-11-11 14:05:22 -080010 */
11
Christian Marangi4137d942024-06-27 00:15:13 +020012#include <linux/cleanup.h>
Samu Onkalo500fe142010-11-11 14:05:22 -080013#include <linux/delay.h>
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +090014#include <linux/firmware.h>
Milo(Woogyom) Kim79bcc102013-02-05 19:26:14 +090015#include <linux/i2c.h>
Milo(Woogyom) Kim79bcc102013-02-05 19:26:14 +090016#include <linux/leds.h>
17#include <linux/module.h>
18#include <linux/mutex.h>
19#include <linux/platform_data/leds-lp55xx.h>
20#include <linux/slab.h>
Linus Walleij7542a04b2013-04-23 04:52:59 -070021#include <linux/of.h>
Milo(Woogyom) Kim6a0c9a42013-02-05 18:03:08 +090022
23#include "leds-lp55xx-common.h"
Samu Onkalo500fe142010-11-11 14:05:22 -080024
Milo(Woogyom) Kim12f022d2013-02-05 19:25:26 +090025#define LP5521_MAX_LEDS 3
26#define LP5521_CMD_DIRECT 0x3F
Samu Onkalo500fe142010-11-11 14:05:22 -080027
28/* Registers */
29#define LP5521_REG_ENABLE 0x00
30#define LP5521_REG_OP_MODE 0x01
31#define LP5521_REG_R_PWM 0x02
32#define LP5521_REG_G_PWM 0x03
33#define LP5521_REG_B_PWM 0x04
34#define LP5521_REG_R_CURRENT 0x05
35#define LP5521_REG_G_CURRENT 0x06
36#define LP5521_REG_B_CURRENT 0x07
37#define LP5521_REG_CONFIG 0x08
Samu Onkalo500fe142010-11-11 14:05:22 -080038#define LP5521_REG_STATUS 0x0C
39#define LP5521_REG_RESET 0x0D
Samu Onkalo500fe142010-11-11 14:05:22 -080040#define LP5521_REG_R_PROG_MEM 0x10
41#define LP5521_REG_G_PROG_MEM 0x30
42#define LP5521_REG_B_PROG_MEM 0x50
43
Samu Onkalo500fe142010-11-11 14:05:22 -080044/* Base register to set LED current */
45#define LP5521_REG_LED_CURRENT_BASE LP5521_REG_R_CURRENT
Samu Onkalo500fe142010-11-11 14:05:22 -080046/* Base register to set the brightness */
47#define LP5521_REG_LED_PWM_BASE LP5521_REG_R_PWM
48
49/* Bits in ENABLE register */
50#define LP5521_MASTER_ENABLE 0x40 /* Chip master enable */
51#define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */
52#define LP5521_EXEC_RUN 0x2A
Kim, Milo32a2f742012-03-23 15:02:09 -070053#define LP5521_ENABLE_DEFAULT \
54 (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM)
55#define LP5521_ENABLE_RUN_PROGRAM \
56 (LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN)
Samu Onkalo500fe142010-11-11 14:05:22 -080057
Kim, Milo81f2a5b42013-03-20 17:37:04 -070058/* CONFIG register */
59#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */
60#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */
Maarten Zanders54a7bef2023-04-21 09:53:05 +020061#define LP5521_CP_MODE_MASK 0x18 /* Charge pump mode */
62#define LP5521_CP_MODE_SHIFT 3
Kim, Milo81f2a5b42013-03-20 17:37:04 -070063#define LP5521_R_TO_BATT 0x04 /* R out: 0 = CP, 1 = Vbat */
64#define LP5521_CLK_INT 0x01 /* Internal clock */
Maarten Zanders54a7bef2023-04-21 09:53:05 +020065#define LP5521_DEFAULT_CFG (LP5521_PWM_HF | LP5521_PWRSAVE_EN)
Kim, Milo81f2a5b42013-03-20 17:37:04 -070066
Samu Onkalo500fe142010-11-11 14:05:22 -080067/* Status */
68#define LP5521_EXT_CLK_USED 0x08
69
Srinidhi KASAGARb3c49c02011-10-31 17:12:24 -070070/* default R channel current register value */
71#define LP5521_REG_R_CURR_DEFAULT 0xAF
72
Milo(Woogyom) Kim48068d52013-02-05 18:08:49 +090073/* Reset register value */
74#define LP5521_RESET 0xFF
75
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +090076static inline void lp5521_wait_opmode_done(void)
77{
78 /* operation mode change needs to be longer than 153 us */
79 usleep_range(200, 300);
80}
81
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +090082static inline void lp5521_wait_enable_done(void)
83{
84 /* it takes more 488 us to update ENABLE register */
85 usleep_range(500, 600);
86}
87
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +090088static void lp5521_run_engine(struct lp55xx_chip *chip, bool start)
89{
Samu Onkalo500fe142010-11-11 14:05:22 -080090 int ret;
Samu Onkalo500fe142010-11-11 14:05:22 -080091
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +090092 /* stop engine */
93 if (!start) {
Christian Marangi43e91e52024-06-26 18:00:19 +020094 lp55xx_stop_engine(chip);
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +090095 lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
96 lp5521_wait_opmode_done();
97 return;
98 }
99
Christian Marangi42a9eaa2024-06-26 18:00:12 +0200100 ret = lp55xx_run_engine_common(chip);
101 if (!ret)
102 lp5521_wait_enable_done();
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900103}
104
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900105static int lp5521_post_init_device(struct lp55xx_chip *chip)
Samu Onkalo500fe142010-11-11 14:05:22 -0800106{
Samu Onkalo500fe142010-11-11 14:05:22 -0800107 int ret;
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900108 u8 val;
Samu Onkalo500fe142010-11-11 14:05:22 -0800109
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900110 /*
111 * Make sure that the chip is reset by reading back the r channel
112 * current reg. This is dummy read is required on some platforms -
113 * otherwise further access to the R G B channels in the
114 * LP5521_REG_ENABLE register will not have any effect - strange!
115 */
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900116 ret = lp55xx_read(chip, LP5521_REG_R_CURRENT, &val);
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900117 if (ret) {
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900118 dev_err(&chip->cl->dev, "error in resetting chip\n");
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900119 return ret;
120 }
121 if (val != LP5521_REG_R_CURR_DEFAULT) {
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900122 dev_err(&chip->cl->dev,
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900123 "unexpected data in register (expected 0x%x got 0x%x)\n",
124 LP5521_REG_R_CURR_DEFAULT, val);
125 ret = -EINVAL;
126 return ret;
127 }
128 usleep_range(10000, 20000);
Samu Onkalo500fe142010-11-11 14:05:22 -0800129
Samu Onkalo500fe142010-11-11 14:05:22 -0800130 /* Set all PWMs to direct control mode */
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900131 ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
Su Huib9604be2023-10-20 17:19:31 +0800132 if (ret)
133 return ret;
Samu Onkalo500fe142010-11-11 14:05:22 -0800134
Kim, Milo81f2a5b42013-03-20 17:37:04 -0700135 /* Update configuration for the clock setting */
136 val = LP5521_DEFAULT_CFG;
137 if (!lp55xx_is_extclk_used(chip))
138 val |= LP5521_CLK_INT;
139
Maarten Zanders54a7bef2023-04-21 09:53:05 +0200140 val |= (chip->pdata->charge_pump_mode << LP5521_CP_MODE_SHIFT) & LP5521_CP_MODE_MASK;
141
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900142 ret = lp55xx_write(chip, LP5521_REG_CONFIG, val);
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900143 if (ret)
144 return ret;
Samu Onkalo500fe142010-11-11 14:05:22 -0800145
146 /* Initialize all channels PWM to zero -> leds off */
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900147 lp55xx_write(chip, LP5521_REG_R_PWM, 0);
148 lp55xx_write(chip, LP5521_REG_G_PWM, 0);
149 lp55xx_write(chip, LP5521_REG_B_PWM, 0);
Samu Onkalo500fe142010-11-11 14:05:22 -0800150
151 /* Set engines are set to run state when OP_MODE enables engines */
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900152 ret = lp55xx_write(chip, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM);
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900153 if (ret)
154 return ret;
Samu Onkalo500fe142010-11-11 14:05:22 -0800155
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900156 lp5521_wait_enable_done();
157
158 return 0;
Samu Onkalo500fe142010-11-11 14:05:22 -0800159}
160
Milo(Woogyom) Kim9ca3bd82013-02-05 19:21:43 +0900161static int lp5521_run_selftest(struct lp55xx_chip *chip, char *buf)
Samu Onkalo500fe142010-11-11 14:05:22 -0800162{
Milo(Woogyom) Kim9ca3bd82013-02-05 19:21:43 +0900163 struct lp55xx_platform_data *pdata = chip->pdata;
Samu Onkalo500fe142010-11-11 14:05:22 -0800164 int ret;
165 u8 status;
166
Milo(Woogyom) Kim9ca3bd82013-02-05 19:21:43 +0900167 ret = lp55xx_read(chip, LP5521_REG_STATUS, &status);
Samu Onkalo500fe142010-11-11 14:05:22 -0800168 if (ret < 0)
169 return ret;
170
Milo(Woogyom) Kim9ca3bd82013-02-05 19:21:43 +0900171 if (pdata->clock_mode != LP55XX_CLOCK_EXT)
172 return 0;
173
Samu Onkalo500fe142010-11-11 14:05:22 -0800174 /* Check that ext clock is really in use if requested */
Milo(Woogyom) Kim9ca3bd82013-02-05 19:21:43 +0900175 if ((status & LP5521_EXT_CLK_USED) == 0)
176 return -EIO;
177
Samu Onkalo500fe142010-11-11 14:05:22 -0800178 return 0;
179}
180
Samu Onkalo500fe142010-11-11 14:05:22 -0800181static ssize_t lp5521_selftest(struct device *dev,
182 struct device_attribute *attr,
183 char *buf)
184{
Milo(Woogyom) Kim9ca3bd82013-02-05 19:21:43 +0900185 struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
186 struct lp55xx_chip *chip = led->chip;
Samu Onkalo500fe142010-11-11 14:05:22 -0800187 int ret;
188
Christian Marangi4137d942024-06-27 00:15:13 +0200189 guard(mutex)(&chip->lock);
190
Samu Onkalo500fe142010-11-11 14:05:22 -0800191 ret = lp5521_run_selftest(chip, buf);
Kim, Milo24d32122013-03-14 17:19:36 -0700192
ye xingchen3f6fb1c2022-12-01 16:11:24 +0800193 return sysfs_emit(buf, "%s\n", ret ? "FAIL" : "OK");
Samu Onkalo500fe142010-11-11 14:05:22 -0800194}
195
Samu Onkalo500fe142010-11-11 14:05:22 -0800196/* device attributes */
Christian Marangi082a4d32024-06-26 18:00:20 +0200197LP55XX_DEV_ATTR_ENGINE_MODE(1);
198LP55XX_DEV_ATTR_ENGINE_MODE(2);
199LP55XX_DEV_ATTR_ENGINE_MODE(3);
200LP55XX_DEV_ATTR_ENGINE_LOAD(1);
201LP55XX_DEV_ATTR_ENGINE_LOAD(2);
202LP55XX_DEV_ATTR_ENGINE_LOAD(3);
Milo Kimc0e5e9b2013-08-08 13:41:40 +0900203static LP55XX_DEV_ATTR_RO(selftest, lp5521_selftest);
Samu Onkalo500fe142010-11-11 14:05:22 -0800204
205static struct attribute *lp5521_attributes[] = {
Milo Kimc0e5e9b2013-08-08 13:41:40 +0900206 &dev_attr_engine1_mode.attr,
207 &dev_attr_engine2_mode.attr,
208 &dev_attr_engine3_mode.attr,
209 &dev_attr_engine1_load.attr,
210 &dev_attr_engine2_load.attr,
211 &dev_attr_engine3_load.attr,
Samu Onkalo500fe142010-11-11 14:05:22 -0800212 &dev_attr_selftest.attr,
Samu Onkalo500fe142010-11-11 14:05:22 -0800213 NULL
214};
215
216static const struct attribute_group lp5521_group = {
217 .attrs = lp5521_attributes,
218};
219
Milo(Woogyom) Kim48068d52013-02-05 18:08:49 +0900220/* Chip specific configurations */
221static struct lp55xx_device_config lp5521_cfg = {
Christian Marangia9b202b2024-06-26 18:00:08 +0200222 .reg_op_mode = {
223 .addr = LP5521_REG_OP_MODE,
224 },
Christian Marangi42a9eaa2024-06-26 18:00:12 +0200225 .reg_exec = {
226 .addr = LP5521_REG_ENABLE,
227 },
Milo(Woogyom) Kim48068d52013-02-05 18:08:49 +0900228 .reset = {
229 .addr = LP5521_REG_RESET,
230 .val = LP5521_RESET,
231 },
Milo(Woogyom) Kime3a700d2013-02-05 18:09:56 +0900232 .enable = {
233 .addr = LP5521_REG_ENABLE,
234 .val = LP5521_ENABLE_DEFAULT,
235 },
Christian Marangi31379a52024-06-26 18:00:13 +0200236 .prog_mem_base = {
237 .addr = LP5521_REG_R_PROG_MEM,
238 },
Christian Marangic63580b2024-06-26 18:00:15 +0200239 .reg_led_pwm_base = {
240 .addr = LP5521_REG_LED_PWM_BASE,
241 },
Christian Marangi01e02902024-06-26 18:00:17 +0200242 .reg_led_current_base = {
243 .addr = LP5521_REG_LED_CURRENT_BASE,
244 },
Milo(Woogyom) Kim0e202342013-02-05 19:07:34 +0900245 .max_channel = LP5521_MAX_LEDS,
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900246 .post_init_device = lp5521_post_init_device,
Christian Marangic63580b2024-06-26 18:00:15 +0200247 .brightness_fn = lp55xx_led_brightness,
Christian Marangi794826b2024-06-26 18:00:16 +0200248 .multicolor_brightness_fn = lp55xx_multicolor_brightness,
Christian Marangi01e02902024-06-26 18:00:17 +0200249 .set_led_current = lp55xx_set_led_current,
Christian Marangia3df1902024-06-26 18:00:14 +0200250 .firmware_cb = lp55xx_firmware_loaded_cb,
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900251 .run_engine = lp5521_run_engine,
Milo(Woogyom) Kime73c0ce2013-02-05 19:20:45 +0900252 .dev_attr_group = &lp5521_group,
Milo(Woogyom) Kim48068d52013-02-05 18:08:49 +0900253};
254
Samu Onkalo500fe142010-11-11 14:05:22 -0800255static const struct i2c_device_id lp5521_id[] = {
Christian Marangidb30c282024-06-26 18:00:09 +0200256 { "lp5521", .driver_data = (kernel_ulong_t)&lp5521_cfg, }, /* Three channel chip */
Samu Onkalo500fe142010-11-11 14:05:22 -0800257 { }
258};
259MODULE_DEVICE_TABLE(i2c, lp5521_id);
260
Axel Linb548a342013-05-08 21:48:07 -0700261static const struct of_device_id of_lp5521_leds_match[] = {
Christian Marangidb30c282024-06-26 18:00:09 +0200262 { .compatible = "national,lp5521", .data = &lp5521_cfg, },
Axel Linb548a342013-05-08 21:48:07 -0700263 {},
264};
265
266MODULE_DEVICE_TABLE(of, of_lp5521_leds_match);
Zhu Wang3d590af2023-08-08 19:11:08 +0800267
Samu Onkalo500fe142010-11-11 14:05:22 -0800268static struct i2c_driver lp5521_driver = {
269 .driver = {
270 .name = "lp5521",
Zhu Wang3d590af2023-08-08 19:11:08 +0800271 .of_match_table = of_lp5521_leds_match,
Samu Onkalo500fe142010-11-11 14:05:22 -0800272 },
Christian Marangidb30c282024-06-26 18:00:09 +0200273 .probe = lp55xx_probe,
274 .remove = lp55xx_remove,
Samu Onkalo500fe142010-11-11 14:05:22 -0800275 .id_table = lp5521_id,
276};
277
Axel Lin09a0d182012-01-10 15:09:27 -0800278module_i2c_driver(lp5521_driver);
Samu Onkalo500fe142010-11-11 14:05:22 -0800279
280MODULE_AUTHOR("Mathias Nyman, Yuri Zaporozhets, Samu Onkalo");
Milo(Woogyom) Kima2387cb2013-02-05 19:28:00 +0900281MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
Samu Onkalo500fe142010-11-11 14:05:22 -0800282MODULE_DESCRIPTION("LP5521 LED engine");
283MODULE_LICENSE("GPL v2");