494 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Driver for ISSI IS31FL32xx family of I2C LED controllers4 *5 * Copyright 2015 Allworx Corp.6 *7 * Datasheets:8 * http://www.issi.com/US/product-analog-fxled-driver.shtml9 * http://www.si-en.com/product.asp?parentid=89010 */11 12#include <linux/device.h>13#include <linux/i2c.h>14#include <linux/kernel.h>15#include <linux/leds.h>16#include <linux/module.h>17#include <linux/of.h>18 19/* Used to indicate a device has no such register */20#define IS31FL32XX_REG_NONE 0xFF21 22/* Software Shutdown bit in Shutdown Register */23#define IS31FL32XX_SHUTDOWN_SSD_ENABLE 024#define IS31FL32XX_SHUTDOWN_SSD_DISABLE BIT(0)25 26/* IS31FL3216 has a number of unique registers */27#define IS31FL3216_CONFIG_REG 0x0028#define IS31FL3216_LIGHTING_EFFECT_REG 0x0329#define IS31FL3216_CHANNEL_CONFIG_REG 0x0430 31/* Software Shutdown bit in 3216 Config Register */32#define IS31FL3216_CONFIG_SSD_ENABLE BIT(7)33#define IS31FL3216_CONFIG_SSD_DISABLE 034 35struct is31fl32xx_priv;36struct is31fl32xx_led_data {37 struct led_classdev cdev;38 u8 channel; /* 1-based, max priv->cdef->channels */39 struct is31fl32xx_priv *priv;40};41 42struct is31fl32xx_priv {43 const struct is31fl32xx_chipdef *cdef;44 struct i2c_client *client;45 unsigned int num_leds;46 struct is31fl32xx_led_data leds[];47};48 49/**50 * struct is31fl32xx_chipdef - chip-specific attributes51 * @channels : Number of LED channels52 * @shutdown_reg : address of Shutdown register (optional)53 * @pwm_update_reg : address of PWM Update register54 * @global_control_reg : address of Global Control register (optional)55 * @reset_reg : address of Reset register (optional)56 * @pwm_register_base : address of first PWM register57 * @pwm_registers_reversed: : true if PWM registers count down instead of up58 * @led_control_register_base : address of first LED control register (optional)59 * @enable_bits_per_led_control_register: number of LEDs enable bits in each60 * @reset_func : pointer to reset function61 * @sw_shutdown_func : pointer to software shutdown function62 *63 * For all optional register addresses, the sentinel value %IS31FL32XX_REG_NONE64 * indicates that this chip has no such register.65 *66 * If non-NULL, @reset_func will be called during probing to set all67 * necessary registers to a known initialization state. This is needed68 * for chips that do not have a @reset_reg.69 *70 * @enable_bits_per_led_control_register must be >=1 if71 * @led_control_register_base != %IS31FL32XX_REG_NONE.72 */73struct is31fl32xx_chipdef {74 u8 channels;75 u8 shutdown_reg;76 u8 pwm_update_reg;77 u8 global_control_reg;78 u8 reset_reg;79 u8 pwm_register_base;80 bool pwm_registers_reversed;81 u8 led_control_register_base;82 u8 enable_bits_per_led_control_register;83 int (*reset_func)(struct is31fl32xx_priv *priv);84 int (*sw_shutdown_func)(struct is31fl32xx_priv *priv, bool enable);85};86 87static const struct is31fl32xx_chipdef is31fl3236_cdef = {88 .channels = 36,89 .shutdown_reg = 0x00,90 .pwm_update_reg = 0x25,91 .global_control_reg = 0x4a,92 .reset_reg = 0x4f,93 .pwm_register_base = 0x01,94 .led_control_register_base = 0x26,95 .enable_bits_per_led_control_register = 1,96};97 98static const struct is31fl32xx_chipdef is31fl3235_cdef = {99 .channels = 28,100 .shutdown_reg = 0x00,101 .pwm_update_reg = 0x25,102 .global_control_reg = 0x4a,103 .reset_reg = 0x4f,104 .pwm_register_base = 0x05,105 .led_control_register_base = 0x2a,106 .enable_bits_per_led_control_register = 1,107};108 109static const struct is31fl32xx_chipdef is31fl3218_cdef = {110 .channels = 18,111 .shutdown_reg = 0x00,112 .pwm_update_reg = 0x16,113 .global_control_reg = IS31FL32XX_REG_NONE,114 .reset_reg = 0x17,115 .pwm_register_base = 0x01,116 .led_control_register_base = 0x13,117 .enable_bits_per_led_control_register = 6,118};119 120static int is31fl3216_reset(struct is31fl32xx_priv *priv);121static int is31fl3216_software_shutdown(struct is31fl32xx_priv *priv,122 bool enable);123static const struct is31fl32xx_chipdef is31fl3216_cdef = {124 .channels = 16,125 .shutdown_reg = IS31FL32XX_REG_NONE,126 .pwm_update_reg = 0xB0,127 .global_control_reg = IS31FL32XX_REG_NONE,128 .reset_reg = IS31FL32XX_REG_NONE,129 .pwm_register_base = 0x10,130 .pwm_registers_reversed = true,131 .led_control_register_base = 0x01,132 .enable_bits_per_led_control_register = 8,133 .reset_func = is31fl3216_reset,134 .sw_shutdown_func = is31fl3216_software_shutdown,135};136 137static int is31fl32xx_write(struct is31fl32xx_priv *priv, u8 reg, u8 val)138{139 int ret;140 141 dev_dbg(&priv->client->dev, "writing register 0x%02X=0x%02X", reg, val);142 143 ret = i2c_smbus_write_byte_data(priv->client, reg, val);144 if (ret) {145 dev_err(&priv->client->dev,146 "register write to 0x%02X failed (error %d)",147 reg, ret);148 }149 return ret;150}151 152/*153 * Custom reset function for IS31FL3216 because it does not have a RESET154 * register the way that the other IS31FL32xx chips do. We don't bother155 * writing the GPIO and animation registers, because the registers we156 * do write ensure those will have no effect.157 */158static int is31fl3216_reset(struct is31fl32xx_priv *priv)159{160 unsigned int i;161 int ret;162 163 ret = is31fl32xx_write(priv, IS31FL3216_CONFIG_REG,164 IS31FL3216_CONFIG_SSD_ENABLE);165 if (ret)166 return ret;167 for (i = 0; i < priv->cdef->channels; i++) {168 ret = is31fl32xx_write(priv, priv->cdef->pwm_register_base+i,169 0x00);170 if (ret)171 return ret;172 }173 ret = is31fl32xx_write(priv, priv->cdef->pwm_update_reg, 0);174 if (ret)175 return ret;176 ret = is31fl32xx_write(priv, IS31FL3216_LIGHTING_EFFECT_REG, 0x00);177 if (ret)178 return ret;179 ret = is31fl32xx_write(priv, IS31FL3216_CHANNEL_CONFIG_REG, 0x00);180 if (ret)181 return ret;182 183 return 0;184}185 186/*187 * Custom Software-Shutdown function for IS31FL3216 because it does not have188 * a SHUTDOWN register the way that the other IS31FL32xx chips do.189 * We don't bother doing a read/modify/write on the CONFIG register because190 * we only ever use a value of '0' for the other fields in that register.191 */192static int is31fl3216_software_shutdown(struct is31fl32xx_priv *priv,193 bool enable)194{195 u8 value = enable ? IS31FL3216_CONFIG_SSD_ENABLE :196 IS31FL3216_CONFIG_SSD_DISABLE;197 198 return is31fl32xx_write(priv, IS31FL3216_CONFIG_REG, value);199}200 201/*202 * NOTE: A mutex is not needed in this function because:203 * - All referenced data is read-only after probe()204 * - The I2C core has a mutex on to protect the bus205 * - There are no read/modify/write operations206 * - Intervening operations between the write of the PWM register207 * and the Update register are harmless.208 *209 * Example:210 * PWM_REG_1 write 16211 * UPDATE_REG write 0212 * PWM_REG_2 write 128213 * UPDATE_REG write 0214 * vs:215 * PWM_REG_1 write 16216 * PWM_REG_2 write 128217 * UPDATE_REG write 0218 * UPDATE_REG write 0219 * are equivalent. Poking the Update register merely applies all PWM220 * register writes up to that point.221 */222static int is31fl32xx_brightness_set(struct led_classdev *led_cdev,223 enum led_brightness brightness)224{225 const struct is31fl32xx_led_data *led_data =226 container_of(led_cdev, struct is31fl32xx_led_data, cdev);227 const struct is31fl32xx_chipdef *cdef = led_data->priv->cdef;228 u8 pwm_register_offset;229 int ret;230 231 dev_dbg(led_cdev->dev, "%s: %d\n", __func__, brightness);232 233 /* NOTE: led_data->channel is 1-based */234 if (cdef->pwm_registers_reversed)235 pwm_register_offset = cdef->channels - led_data->channel;236 else237 pwm_register_offset = led_data->channel - 1;238 239 ret = is31fl32xx_write(led_data->priv,240 cdef->pwm_register_base + pwm_register_offset,241 brightness);242 if (ret)243 return ret;244 245 return is31fl32xx_write(led_data->priv, cdef->pwm_update_reg, 0);246}247 248static int is31fl32xx_reset_regs(struct is31fl32xx_priv *priv)249{250 const struct is31fl32xx_chipdef *cdef = priv->cdef;251 int ret;252 253 if (cdef->reset_reg != IS31FL32XX_REG_NONE) {254 ret = is31fl32xx_write(priv, cdef->reset_reg, 0);255 if (ret)256 return ret;257 }258 259 if (cdef->reset_func)260 return cdef->reset_func(priv);261 262 return 0;263}264 265static int is31fl32xx_software_shutdown(struct is31fl32xx_priv *priv,266 bool enable)267{268 const struct is31fl32xx_chipdef *cdef = priv->cdef;269 int ret;270 271 if (cdef->shutdown_reg != IS31FL32XX_REG_NONE) {272 u8 value = enable ? IS31FL32XX_SHUTDOWN_SSD_ENABLE :273 IS31FL32XX_SHUTDOWN_SSD_DISABLE;274 ret = is31fl32xx_write(priv, cdef->shutdown_reg, value);275 if (ret)276 return ret;277 }278 279 if (cdef->sw_shutdown_func)280 return cdef->sw_shutdown_func(priv, enable);281 282 return 0;283}284 285static int is31fl32xx_init_regs(struct is31fl32xx_priv *priv)286{287 const struct is31fl32xx_chipdef *cdef = priv->cdef;288 int ret;289 290 ret = is31fl32xx_reset_regs(priv);291 if (ret)292 return ret;293 294 /*295 * Set enable bit for all channels.296 * We will control state with PWM registers alone.297 */298 if (cdef->led_control_register_base != IS31FL32XX_REG_NONE) {299 u8 value =300 GENMASK(cdef->enable_bits_per_led_control_register-1, 0);301 u8 num_regs = cdef->channels /302 cdef->enable_bits_per_led_control_register;303 int i;304 305 for (i = 0; i < num_regs; i++) {306 ret = is31fl32xx_write(priv,307 cdef->led_control_register_base+i,308 value);309 if (ret)310 return ret;311 }312 }313 314 ret = is31fl32xx_software_shutdown(priv, false);315 if (ret)316 return ret;317 318 if (cdef->global_control_reg != IS31FL32XX_REG_NONE) {319 ret = is31fl32xx_write(priv, cdef->global_control_reg, 0x00);320 if (ret)321 return ret;322 }323 324 return 0;325}326 327static int is31fl32xx_parse_child_dt(const struct device *dev,328 const struct device_node *child,329 struct is31fl32xx_led_data *led_data)330{331 struct led_classdev *cdev = &led_data->cdev;332 int ret = 0;333 u32 reg;334 335 ret = of_property_read_u32(child, "reg", ®);336 if (ret || reg < 1 || reg > led_data->priv->cdef->channels) {337 dev_err(dev,338 "Child node %pOF does not have a valid reg property\n",339 child);340 return -EINVAL;341 }342 led_data->channel = reg;343 344 cdev->brightness_set_blocking = is31fl32xx_brightness_set;345 346 return 0;347}348 349static struct is31fl32xx_led_data *is31fl32xx_find_led_data(350 struct is31fl32xx_priv *priv,351 u8 channel)352{353 size_t i;354 355 for (i = 0; i < priv->num_leds; i++) {356 if (priv->leds[i].channel == channel)357 return &priv->leds[i];358 }359 360 return NULL;361}362 363static int is31fl32xx_parse_dt(struct device *dev,364 struct is31fl32xx_priv *priv)365{366 int ret = 0;367 368 for_each_available_child_of_node_scoped(dev_of_node(dev), child) {369 struct led_init_data init_data = {};370 struct is31fl32xx_led_data *led_data =371 &priv->leds[priv->num_leds];372 const struct is31fl32xx_led_data *other_led_data;373 374 led_data->priv = priv;375 376 ret = is31fl32xx_parse_child_dt(dev, child, led_data);377 if (ret)378 return ret;379 380 /* Detect if channel is already in use by another child */381 other_led_data = is31fl32xx_find_led_data(priv,382 led_data->channel);383 if (other_led_data) {384 dev_err(dev,385 "Node %pOF 'reg' conflicts with another LED\n",386 child);387 return -EINVAL;388 }389 390 init_data.fwnode = of_fwnode_handle(child);391 392 ret = devm_led_classdev_register_ext(dev, &led_data->cdev,393 &init_data);394 if (ret) {395 dev_err(dev, "Failed to register LED for %pOF: %d\n",396 child, ret);397 return ret;398 }399 400 priv->num_leds++;401 }402 403 return 0;404}405 406static const struct of_device_id of_is31fl32xx_match[] = {407 { .compatible = "issi,is31fl3236", .data = &is31fl3236_cdef, },408 { .compatible = "issi,is31fl3235", .data = &is31fl3235_cdef, },409 { .compatible = "issi,is31fl3218", .data = &is31fl3218_cdef, },410 { .compatible = "si-en,sn3218", .data = &is31fl3218_cdef, },411 { .compatible = "issi,is31fl3216", .data = &is31fl3216_cdef, },412 { .compatible = "si-en,sn3216", .data = &is31fl3216_cdef, },413 {},414};415 416MODULE_DEVICE_TABLE(of, of_is31fl32xx_match);417 418static int is31fl32xx_probe(struct i2c_client *client)419{420 const struct is31fl32xx_chipdef *cdef;421 struct device *dev = &client->dev;422 struct is31fl32xx_priv *priv;423 int count;424 int ret = 0;425 426 cdef = device_get_match_data(dev);427 428 count = of_get_available_child_count(dev_of_node(dev));429 if (!count)430 return -EINVAL;431 432 priv = devm_kzalloc(dev, struct_size(priv, leds, count),433 GFP_KERNEL);434 if (!priv)435 return -ENOMEM;436 437 priv->client = client;438 priv->cdef = cdef;439 i2c_set_clientdata(client, priv);440 441 ret = is31fl32xx_init_regs(priv);442 if (ret)443 return ret;444 445 ret = is31fl32xx_parse_dt(dev, priv);446 if (ret)447 return ret;448 449 return 0;450}451 452static void is31fl32xx_remove(struct i2c_client *client)453{454 struct is31fl32xx_priv *priv = i2c_get_clientdata(client);455 int ret;456 457 ret = is31fl32xx_reset_regs(priv);458 if (ret)459 dev_err(&client->dev, "Failed to reset registers on removal (%pe)\n",460 ERR_PTR(ret));461}462 463/*464 * i2c-core (and modalias) requires that id_table be properly filled,465 * even though it is not used for DeviceTree based instantiation.466 */467static const struct i2c_device_id is31fl32xx_id[] = {468 { "is31fl3236" },469 { "is31fl3235" },470 { "is31fl3218" },471 { "sn3218" },472 { "is31fl3216" },473 { "sn3216" },474 {},475};476 477MODULE_DEVICE_TABLE(i2c, is31fl32xx_id);478 479static struct i2c_driver is31fl32xx_driver = {480 .driver = {481 .name = "is31fl32xx",482 .of_match_table = of_is31fl32xx_match,483 },484 .probe = is31fl32xx_probe,485 .remove = is31fl32xx_remove,486 .id_table = is31fl32xx_id,487};488 489module_i2c_driver(is31fl32xx_driver);490 491MODULE_AUTHOR("David Rivshin <drivshin@allworx.com>");492MODULE_DESCRIPTION("ISSI IS31FL32xx LED driver");493MODULE_LICENSE("GPL v2");494