1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * TI LP8864/LP8866 4/6 Channel LED Driver 4 * 5 * Copyright (C) 2024 Siemens AG 6 * 7 * Based on LP8860 driver by Dan Murphy <dmurphy@ti.com> 8 */ 9 10 #include <linux/gpio/consumer.h> 11 #include <linux/i2c.h> 12 #include <linux/init.h> 13 #include <linux/leds.h> 14 #include <linux/module.h> 15 #include <linux/mutex.h> 16 #include <linux/of.h> 17 #include <linux/regmap.h> 18 #include <linux/regulator/consumer.h> 19 #include <linux/slab.h> 20 21 #define LP8864_BRT_CONTROL 0x00 22 #define LP8864_USER_CONFIG1 0x04 23 #define LP8864_BRT_MODE_MASK GENMASK(9, 8) 24 #define LP8864_BRT_MODE_REG BIT(9) /* Brightness control by DISPLAY_BRT reg */ 25 #define LP8864_SUPPLY_STATUS 0x0e 26 #define LP8864_BOOST_STATUS 0x10 27 #define LP8864_LED_STATUS 0x12 28 #define LP8864_LED_STATUS_WR_MASK GENMASK(14, 9) /* Writeable bits in the LED_STATUS reg */ 29 30 /* Textual meaning for status bits, starting from bit 1 */ 31 static const char *const lp8864_supply_status_msg[] = { 32 "Vin under-voltage fault", 33 "Vin over-voltage fault", 34 "Vdd under-voltage fault", 35 "Vin over-current fault", 36 "Missing charge pump fault", 37 "Charge pump fault", 38 "Missing boost sync fault", 39 "CRC error fault ", 40 }; 41 42 /* Textual meaning for status bits, starting from bit 1 */ 43 static const char *const lp8864_boost_status_msg[] = { 44 "Boost OVP low fault", 45 "Boost OVP high fault", 46 "Boost over-current fault", 47 "Missing boost FSET resistor fault", 48 "Missing MODE SEL resistor fault", 49 "Missing LED resistor fault", 50 "ISET resistor short to ground fault", 51 "Thermal shutdown fault", 52 }; 53 54 /* Textual meaning for every register bit */ 55 static const char *const lp8864_led_status_msg[] = { 56 "LED 1 fault", 57 "LED 2 fault", 58 "LED 3 fault", 59 "LED 4 fault", 60 "LED 5 fault", 61 "LED 6 fault", 62 "LED open fault", 63 "LED internal short fault", 64 "LED short to GND fault", 65 NULL, NULL, NULL, 66 "Invalid string configuration fault", 67 NULL, 68 "I2C time out fault", 69 }; 70 71 /** 72 * struct lp8864_led 73 * @client: Pointer to the I2C client 74 * @led_dev: led class device pointer 75 * @regmap: Devices register map 76 * @led_status_mask: Helps to report LED fault only once 77 */ 78 struct lp8864_led { 79 struct i2c_client *client; 80 struct led_classdev led_dev; 81 struct regmap *regmap; 82 u16 led_status_mask; 83 }; 84 85 static int lp8864_fault_check(struct lp8864_led *led) 86 { 87 int ret, i; 88 unsigned int val; 89 90 ret = regmap_read(led->regmap, LP8864_SUPPLY_STATUS, &val); 91 if (ret) 92 goto err; 93 94 /* Odd bits are status bits, even bits are clear bits */ 95 for (i = 0; i < ARRAY_SIZE(lp8864_supply_status_msg); i++) 96 if (val & BIT(i * 2 + 1)) 97 dev_warn(&led->client->dev, "%s\n", lp8864_supply_status_msg[i]); 98 99 /* 100 * Clear bits have an index preceding the corresponding Status bits; 101 * both have to be written "1" simultaneously to clear the corresponding 102 * Status bit. 103 */ 104 if (val) 105 ret = regmap_write(led->regmap, LP8864_SUPPLY_STATUS, val >> 1 | val); 106 if (ret) 107 goto err; 108 109 ret = regmap_read(led->regmap, LP8864_BOOST_STATUS, &val); 110 if (ret) 111 goto err; 112 113 /* Odd bits are status bits, even bits are clear bits */ 114 for (i = 0; i < ARRAY_SIZE(lp8864_boost_status_msg); i++) 115 if (val & BIT(i * 2 + 1)) 116 dev_warn(&led->client->dev, "%s\n", lp8864_boost_status_msg[i]); 117 118 if (val) 119 ret = regmap_write(led->regmap, LP8864_BOOST_STATUS, val >> 1 | val); 120 if (ret) 121 goto err; 122 123 ret = regmap_read(led->regmap, LP8864_LED_STATUS, &val); 124 if (ret) 125 goto err; 126 127 /* 128 * Clear already reported faults that maintain their value until device 129 * power-down 130 */ 131 val &= ~led->led_status_mask; 132 133 for (i = 0; i < ARRAY_SIZE(lp8864_led_status_msg); i++) 134 if (lp8864_led_status_msg[i] && val & BIT(i)) 135 dev_warn(&led->client->dev, "%s\n", lp8864_led_status_msg[i]); 136 137 /* 138 * Mark those which maintain their value until device power-down as 139 * "already reported" 140 */ 141 led->led_status_mask |= val & ~LP8864_LED_STATUS_WR_MASK; 142 143 /* 144 * Only bits 14, 12, 10 have to be cleared here, but others are RO, 145 * we don't care what we write to them. 146 */ 147 if (val & LP8864_LED_STATUS_WR_MASK) 148 ret = regmap_write(led->regmap, LP8864_LED_STATUS, val >> 1 | val); 149 if (ret) 150 goto err; 151 152 return 0; 153 154 err: 155 dev_err(&led->client->dev, "Failed to read/clear faults (%pe)\n", ERR_PTR(ret)); 156 157 return ret; 158 } 159 160 static int lp8864_brightness_set(struct led_classdev *led_cdev, 161 enum led_brightness brt_val) 162 { 163 struct lp8864_led *led = container_of(led_cdev, struct lp8864_led, led_dev); 164 /* Scale 0..LED_FULL into 16-bit HW brightness */ 165 unsigned int val = brt_val * 0xffff / LED_FULL; 166 int ret; 167 168 ret = lp8864_fault_check(led); 169 if (ret) 170 return ret; 171 172 ret = regmap_write(led->regmap, LP8864_BRT_CONTROL, val); 173 if (ret) 174 dev_err(&led->client->dev, "Failed to write brightness value\n"); 175 176 return ret; 177 } 178 179 static enum led_brightness lp8864_brightness_get(struct led_classdev *led_cdev) 180 { 181 struct lp8864_led *led = container_of(led_cdev, struct lp8864_led, led_dev); 182 unsigned int val; 183 int ret; 184 185 ret = regmap_read(led->regmap, LP8864_BRT_CONTROL, &val); 186 if (ret) { 187 dev_err(&led->client->dev, "Failed to read brightness value\n"); 188 return ret; 189 } 190 191 /* Scale 16-bit HW brightness into 0..LED_FULL */ 192 return val * LED_FULL / 0xffff; 193 } 194 195 static const struct regmap_config lp8864_regmap_config = { 196 .reg_bits = 8, 197 .val_bits = 16, 198 .val_format_endian = REGMAP_ENDIAN_LITTLE, 199 }; 200 201 static void lp8864_disable_gpio(void *data) 202 { 203 struct gpio_desc *gpio = data; 204 205 gpiod_set_value(gpio, 0); 206 } 207 208 static int lp8864_probe(struct i2c_client *client) 209 { 210 int ret; 211 struct lp8864_led *led; 212 struct device_node *np = dev_of_node(&client->dev); 213 struct device_node *child_node; 214 struct led_init_data init_data = {}; 215 struct gpio_desc *enable_gpio; 216 217 led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL); 218 if (!led) 219 return -ENOMEM; 220 221 child_node = of_get_next_available_child(np, NULL); 222 if (!child_node) { 223 dev_err(&client->dev, "No LED function defined\n"); 224 return -EINVAL; 225 } 226 227 ret = devm_regulator_get_enable_optional(&client->dev, "vled"); 228 if (ret && ret != -ENODEV) 229 return dev_err_probe(&client->dev, ret, "Failed to enable vled regulator\n"); 230 231 enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_HIGH); 232 if (IS_ERR(enable_gpio)) 233 return dev_err_probe(&client->dev, PTR_ERR(enable_gpio), 234 "Failed to get enable GPIO\n"); 235 236 ret = devm_add_action_or_reset(&client->dev, lp8864_disable_gpio, enable_gpio); 237 if (ret) 238 return ret; 239 240 led->client = client; 241 led->led_dev.brightness_set_blocking = lp8864_brightness_set; 242 led->led_dev.brightness_get = lp8864_brightness_get; 243 244 led->regmap = devm_regmap_init_i2c(client, &lp8864_regmap_config); 245 if (IS_ERR(led->regmap)) 246 return dev_err_probe(&client->dev, PTR_ERR(led->regmap), 247 "Failed to allocate regmap\n"); 248 249 /* Control brightness by DISPLAY_BRT register */ 250 ret = regmap_update_bits(led->regmap, LP8864_USER_CONFIG1, LP8864_BRT_MODE_MASK, 251 LP8864_BRT_MODE_REG); 252 if (ret) { 253 dev_err(&led->client->dev, "Failed to set brightness control mode\n"); 254 return ret; 255 } 256 257 ret = lp8864_fault_check(led); 258 if (ret) 259 return ret; 260 261 init_data.fwnode = of_fwnode_handle(child_node); 262 init_data.devicename = "lp8864"; 263 init_data.default_label = ":display_cluster"; 264 265 ret = devm_led_classdev_register_ext(&client->dev, &led->led_dev, &init_data); 266 if (ret) 267 dev_err(&client->dev, "Failed to register LED device (%pe)\n", ERR_PTR(ret)); 268 269 return ret; 270 } 271 272 static const struct i2c_device_id lp8864_id[] = { 273 { "lp8864" }, 274 {} 275 }; 276 MODULE_DEVICE_TABLE(i2c, lp8864_id); 277 278 static const struct of_device_id of_lp8864_leds_match[] = { 279 { .compatible = "ti,lp8864" }, 280 {} 281 }; 282 MODULE_DEVICE_TABLE(of, of_lp8864_leds_match); 283 284 static struct i2c_driver lp8864_driver = { 285 .driver = { 286 .name = "lp8864", 287 .of_match_table = of_lp8864_leds_match, 288 }, 289 .probe = lp8864_probe, 290 .id_table = lp8864_id, 291 }; 292 module_i2c_driver(lp8864_driver); 293 294 MODULE_DESCRIPTION("Texas Instruments LP8864/LP8866 LED driver"); 295 MODULE_AUTHOR("Alexander Sverdlin <alexander.sverdlin@siemens.com>"); 296 MODULE_LICENSE("GPL"); 297