1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 29d7cffafSDavid Rivshin /* 39d7cffafSDavid Rivshin * Driver for ISSI IS31FL32xx family of I2C LED controllers 49d7cffafSDavid Rivshin * 59d7cffafSDavid Rivshin * Copyright 2015 Allworx Corp. 69d7cffafSDavid Rivshin * 7e0442d7dSDavid Rivshin * Datasheets: 8e0442d7dSDavid Rivshin * http://www.issi.com/US/product-analog-fxled-driver.shtml 9e0442d7dSDavid Rivshin * http://www.si-en.com/product.asp?parentid=890 109d7cffafSDavid Rivshin */ 119d7cffafSDavid Rivshin 129d7cffafSDavid Rivshin #include <linux/device.h> 139d7cffafSDavid Rivshin #include <linux/i2c.h> 149d7cffafSDavid Rivshin #include <linux/kernel.h> 159d7cffafSDavid Rivshin #include <linux/leds.h> 169d7cffafSDavid Rivshin #include <linux/module.h> 179d7cffafSDavid Rivshin #include <linux/of.h> 189d7cffafSDavid Rivshin #include <linux/of_device.h> 199d7cffafSDavid Rivshin 209d7cffafSDavid Rivshin /* Used to indicate a device has no such register */ 219d7cffafSDavid Rivshin #define IS31FL32XX_REG_NONE 0xFF 229d7cffafSDavid Rivshin 239d7cffafSDavid Rivshin /* Software Shutdown bit in Shutdown Register */ 249d7cffafSDavid Rivshin #define IS31FL32XX_SHUTDOWN_SSD_ENABLE 0 259d7cffafSDavid Rivshin #define IS31FL32XX_SHUTDOWN_SSD_DISABLE BIT(0) 269d7cffafSDavid Rivshin 279d7cffafSDavid Rivshin /* IS31FL3216 has a number of unique registers */ 289d7cffafSDavid Rivshin #define IS31FL3216_CONFIG_REG 0x00 299d7cffafSDavid Rivshin #define IS31FL3216_LIGHTING_EFFECT_REG 0x03 309d7cffafSDavid Rivshin #define IS31FL3216_CHANNEL_CONFIG_REG 0x04 319d7cffafSDavid Rivshin 329d7cffafSDavid Rivshin /* Software Shutdown bit in 3216 Config Register */ 339d7cffafSDavid Rivshin #define IS31FL3216_CONFIG_SSD_ENABLE BIT(7) 349d7cffafSDavid Rivshin #define IS31FL3216_CONFIG_SSD_DISABLE 0 359d7cffafSDavid Rivshin 369d7cffafSDavid Rivshin struct is31fl32xx_priv; 379d7cffafSDavid Rivshin struct is31fl32xx_led_data { 389d7cffafSDavid Rivshin struct led_classdev cdev; 399d7cffafSDavid Rivshin u8 channel; /* 1-based, max priv->cdef->channels */ 409d7cffafSDavid Rivshin struct is31fl32xx_priv *priv; 419d7cffafSDavid Rivshin }; 429d7cffafSDavid Rivshin 439d7cffafSDavid Rivshin struct is31fl32xx_priv { 449d7cffafSDavid Rivshin const struct is31fl32xx_chipdef *cdef; 459d7cffafSDavid Rivshin struct i2c_client *client; 469d7cffafSDavid Rivshin unsigned int num_leds; 477a630367SGustavo A. R. Silva struct is31fl32xx_led_data leds[]; 489d7cffafSDavid Rivshin }; 499d7cffafSDavid Rivshin 509d7cffafSDavid Rivshin /** 519d7cffafSDavid Rivshin * struct is31fl32xx_chipdef - chip-specific attributes 529d7cffafSDavid Rivshin * @channels : Number of LED channels 539d7cffafSDavid Rivshin * @shutdown_reg : address of Shutdown register (optional) 549d7cffafSDavid Rivshin * @pwm_update_reg : address of PWM Update register 559d7cffafSDavid Rivshin * @global_control_reg : address of Global Control register (optional) 569d7cffafSDavid Rivshin * @reset_reg : address of Reset register (optional) 579d7cffafSDavid Rivshin * @pwm_register_base : address of first PWM register 589d7cffafSDavid Rivshin * @pwm_registers_reversed: : true if PWM registers count down instead of up 599d7cffafSDavid Rivshin * @led_control_register_base : address of first LED control register (optional) 609d7cffafSDavid Rivshin * @enable_bits_per_led_control_register: number of LEDs enable bits in each 619d7cffafSDavid Rivshin * @reset_func: : pointer to reset function 629d7cffafSDavid Rivshin * 639d7cffafSDavid Rivshin * For all optional register addresses, the sentinel value %IS31FL32XX_REG_NONE 649d7cffafSDavid Rivshin * indicates that this chip has no such register. 659d7cffafSDavid Rivshin * 669d7cffafSDavid Rivshin * If non-NULL, @reset_func will be called during probing to set all 679d7cffafSDavid Rivshin * necessary registers to a known initialization state. This is needed 689d7cffafSDavid Rivshin * for chips that do not have a @reset_reg. 699d7cffafSDavid Rivshin * 709d7cffafSDavid Rivshin * @enable_bits_per_led_control_register must be >=1 if 719d7cffafSDavid Rivshin * @led_control_register_base != %IS31FL32XX_REG_NONE. 729d7cffafSDavid Rivshin */ 739d7cffafSDavid Rivshin struct is31fl32xx_chipdef { 749d7cffafSDavid Rivshin u8 channels; 759d7cffafSDavid Rivshin u8 shutdown_reg; 769d7cffafSDavid Rivshin u8 pwm_update_reg; 779d7cffafSDavid Rivshin u8 global_control_reg; 789d7cffafSDavid Rivshin u8 reset_reg; 799d7cffafSDavid Rivshin u8 pwm_register_base; 809d7cffafSDavid Rivshin bool pwm_registers_reversed; 819d7cffafSDavid Rivshin u8 led_control_register_base; 829d7cffafSDavid Rivshin u8 enable_bits_per_led_control_register; 839d7cffafSDavid Rivshin int (*reset_func)(struct is31fl32xx_priv *priv); 849d7cffafSDavid Rivshin int (*sw_shutdown_func)(struct is31fl32xx_priv *priv, bool enable); 859d7cffafSDavid Rivshin }; 869d7cffafSDavid Rivshin 879d7cffafSDavid Rivshin static const struct is31fl32xx_chipdef is31fl3236_cdef = { 889d7cffafSDavid Rivshin .channels = 36, 899d7cffafSDavid Rivshin .shutdown_reg = 0x00, 909d7cffafSDavid Rivshin .pwm_update_reg = 0x25, 919d7cffafSDavid Rivshin .global_control_reg = 0x4a, 929d7cffafSDavid Rivshin .reset_reg = 0x4f, 939d7cffafSDavid Rivshin .pwm_register_base = 0x01, 949d7cffafSDavid Rivshin .led_control_register_base = 0x26, 959d7cffafSDavid Rivshin .enable_bits_per_led_control_register = 1, 969d7cffafSDavid Rivshin }; 979d7cffafSDavid Rivshin 989d7cffafSDavid Rivshin static const struct is31fl32xx_chipdef is31fl3235_cdef = { 999d7cffafSDavid Rivshin .channels = 28, 1009d7cffafSDavid Rivshin .shutdown_reg = 0x00, 1019d7cffafSDavid Rivshin .pwm_update_reg = 0x25, 1029d7cffafSDavid Rivshin .global_control_reg = 0x4a, 1039d7cffafSDavid Rivshin .reset_reg = 0x4f, 1049d7cffafSDavid Rivshin .pwm_register_base = 0x05, 1059d7cffafSDavid Rivshin .led_control_register_base = 0x2a, 1069d7cffafSDavid Rivshin .enable_bits_per_led_control_register = 1, 1079d7cffafSDavid Rivshin }; 1089d7cffafSDavid Rivshin 1099d7cffafSDavid Rivshin static const struct is31fl32xx_chipdef is31fl3218_cdef = { 1109d7cffafSDavid Rivshin .channels = 18, 1119d7cffafSDavid Rivshin .shutdown_reg = 0x00, 1129d7cffafSDavid Rivshin .pwm_update_reg = 0x16, 1139d7cffafSDavid Rivshin .global_control_reg = IS31FL32XX_REG_NONE, 1149d7cffafSDavid Rivshin .reset_reg = 0x17, 1159d7cffafSDavid Rivshin .pwm_register_base = 0x01, 1169d7cffafSDavid Rivshin .led_control_register_base = 0x13, 1179d7cffafSDavid Rivshin .enable_bits_per_led_control_register = 6, 1189d7cffafSDavid Rivshin }; 1199d7cffafSDavid Rivshin 1209d7cffafSDavid Rivshin static int is31fl3216_reset(struct is31fl32xx_priv *priv); 1219d7cffafSDavid Rivshin static int is31fl3216_software_shutdown(struct is31fl32xx_priv *priv, 1229d7cffafSDavid Rivshin bool enable); 1239d7cffafSDavid Rivshin static const struct is31fl32xx_chipdef is31fl3216_cdef = { 1249d7cffafSDavid Rivshin .channels = 16, 1259d7cffafSDavid Rivshin .shutdown_reg = IS31FL32XX_REG_NONE, 1269d7cffafSDavid Rivshin .pwm_update_reg = 0xB0, 1279d7cffafSDavid Rivshin .global_control_reg = IS31FL32XX_REG_NONE, 1289d7cffafSDavid Rivshin .reset_reg = IS31FL32XX_REG_NONE, 1299d7cffafSDavid Rivshin .pwm_register_base = 0x10, 1309d7cffafSDavid Rivshin .pwm_registers_reversed = true, 1319d7cffafSDavid Rivshin .led_control_register_base = 0x01, 1329d7cffafSDavid Rivshin .enable_bits_per_led_control_register = 8, 1339d7cffafSDavid Rivshin .reset_func = is31fl3216_reset, 1349d7cffafSDavid Rivshin .sw_shutdown_func = is31fl3216_software_shutdown, 1359d7cffafSDavid Rivshin }; 1369d7cffafSDavid Rivshin 1379d7cffafSDavid Rivshin static int is31fl32xx_write(struct is31fl32xx_priv *priv, u8 reg, u8 val) 1389d7cffafSDavid Rivshin { 1399d7cffafSDavid Rivshin int ret; 1409d7cffafSDavid Rivshin 1419d7cffafSDavid Rivshin dev_dbg(&priv->client->dev, "writing register 0x%02X=0x%02X", reg, val); 1429d7cffafSDavid Rivshin 1439d7cffafSDavid Rivshin ret = i2c_smbus_write_byte_data(priv->client, reg, val); 1449d7cffafSDavid Rivshin if (ret) { 1459d7cffafSDavid Rivshin dev_err(&priv->client->dev, 1469d7cffafSDavid Rivshin "register write to 0x%02X failed (error %d)", 1479d7cffafSDavid Rivshin reg, ret); 1489d7cffafSDavid Rivshin } 1499d7cffafSDavid Rivshin return ret; 1509d7cffafSDavid Rivshin } 1519d7cffafSDavid Rivshin 1529d7cffafSDavid Rivshin /* 1539d7cffafSDavid Rivshin * Custom reset function for IS31FL3216 because it does not have a RESET 1549d7cffafSDavid Rivshin * register the way that the other IS31FL32xx chips do. We don't bother 1559d7cffafSDavid Rivshin * writing the GPIO and animation registers, because the registers we 1569d7cffafSDavid Rivshin * do write ensure those will have no effect. 1579d7cffafSDavid Rivshin */ 1589d7cffafSDavid Rivshin static int is31fl3216_reset(struct is31fl32xx_priv *priv) 1599d7cffafSDavid Rivshin { 1609d7cffafSDavid Rivshin unsigned int i; 1619d7cffafSDavid Rivshin int ret; 1629d7cffafSDavid Rivshin 1639d7cffafSDavid Rivshin ret = is31fl32xx_write(priv, IS31FL3216_CONFIG_REG, 1649d7cffafSDavid Rivshin IS31FL3216_CONFIG_SSD_ENABLE); 1659d7cffafSDavid Rivshin if (ret) 1669d7cffafSDavid Rivshin return ret; 1679d7cffafSDavid Rivshin for (i = 0; i < priv->cdef->channels; i++) { 1689d7cffafSDavid Rivshin ret = is31fl32xx_write(priv, priv->cdef->pwm_register_base+i, 1699d7cffafSDavid Rivshin 0x00); 1709d7cffafSDavid Rivshin if (ret) 1719d7cffafSDavid Rivshin return ret; 1729d7cffafSDavid Rivshin } 1739d7cffafSDavid Rivshin ret = is31fl32xx_write(priv, priv->cdef->pwm_update_reg, 0); 1749d7cffafSDavid Rivshin if (ret) 1759d7cffafSDavid Rivshin return ret; 1769d7cffafSDavid Rivshin ret = is31fl32xx_write(priv, IS31FL3216_LIGHTING_EFFECT_REG, 0x00); 1779d7cffafSDavid Rivshin if (ret) 1789d7cffafSDavid Rivshin return ret; 1799d7cffafSDavid Rivshin ret = is31fl32xx_write(priv, IS31FL3216_CHANNEL_CONFIG_REG, 0x00); 1809d7cffafSDavid Rivshin if (ret) 1819d7cffafSDavid Rivshin return ret; 1829d7cffafSDavid Rivshin 1839d7cffafSDavid Rivshin return 0; 1849d7cffafSDavid Rivshin } 1859d7cffafSDavid Rivshin 1869d7cffafSDavid Rivshin /* 1879d7cffafSDavid Rivshin * Custom Software-Shutdown function for IS31FL3216 because it does not have 1889d7cffafSDavid Rivshin * a SHUTDOWN register the way that the other IS31FL32xx chips do. 1899d7cffafSDavid Rivshin * We don't bother doing a read/modify/write on the CONFIG register because 1909d7cffafSDavid Rivshin * we only ever use a value of '0' for the other fields in that register. 1919d7cffafSDavid Rivshin */ 1929d7cffafSDavid Rivshin static int is31fl3216_software_shutdown(struct is31fl32xx_priv *priv, 1939d7cffafSDavid Rivshin bool enable) 1949d7cffafSDavid Rivshin { 1959d7cffafSDavid Rivshin u8 value = enable ? IS31FL3216_CONFIG_SSD_ENABLE : 1969d7cffafSDavid Rivshin IS31FL3216_CONFIG_SSD_DISABLE; 1979d7cffafSDavid Rivshin 1989d7cffafSDavid Rivshin return is31fl32xx_write(priv, IS31FL3216_CONFIG_REG, value); 1999d7cffafSDavid Rivshin } 2009d7cffafSDavid Rivshin 2019d7cffafSDavid Rivshin /* 2029d7cffafSDavid Rivshin * NOTE: A mutex is not needed in this function because: 2039d7cffafSDavid Rivshin * - All referenced data is read-only after probe() 2049d7cffafSDavid Rivshin * - The I2C core has a mutex on to protect the bus 2059d7cffafSDavid Rivshin * - There are no read/modify/write operations 2069d7cffafSDavid Rivshin * - Intervening operations between the write of the PWM register 2079d7cffafSDavid Rivshin * and the Update register are harmless. 2089d7cffafSDavid Rivshin * 2099d7cffafSDavid Rivshin * Example: 2109d7cffafSDavid Rivshin * PWM_REG_1 write 16 2119d7cffafSDavid Rivshin * UPDATE_REG write 0 2129d7cffafSDavid Rivshin * PWM_REG_2 write 128 2139d7cffafSDavid Rivshin * UPDATE_REG write 0 2149d7cffafSDavid Rivshin * vs: 2159d7cffafSDavid Rivshin * PWM_REG_1 write 16 2169d7cffafSDavid Rivshin * PWM_REG_2 write 128 2179d7cffafSDavid Rivshin * UPDATE_REG write 0 2189d7cffafSDavid Rivshin * UPDATE_REG write 0 2199d7cffafSDavid Rivshin * are equivalent. Poking the Update register merely applies all PWM 2209d7cffafSDavid Rivshin * register writes up to that point. 2219d7cffafSDavid Rivshin */ 2229d7cffafSDavid Rivshin static int is31fl32xx_brightness_set(struct led_classdev *led_cdev, 2239d7cffafSDavid Rivshin enum led_brightness brightness) 2249d7cffafSDavid Rivshin { 2259d7cffafSDavid Rivshin const struct is31fl32xx_led_data *led_data = 2269d7cffafSDavid Rivshin container_of(led_cdev, struct is31fl32xx_led_data, cdev); 2279d7cffafSDavid Rivshin const struct is31fl32xx_chipdef *cdef = led_data->priv->cdef; 2289d7cffafSDavid Rivshin u8 pwm_register_offset; 2299d7cffafSDavid Rivshin int ret; 2309d7cffafSDavid Rivshin 2319d7cffafSDavid Rivshin dev_dbg(led_cdev->dev, "%s: %d\n", __func__, brightness); 2329d7cffafSDavid Rivshin 2339d7cffafSDavid Rivshin /* NOTE: led_data->channel is 1-based */ 2349d7cffafSDavid Rivshin if (cdef->pwm_registers_reversed) 2359d7cffafSDavid Rivshin pwm_register_offset = cdef->channels - led_data->channel; 2369d7cffafSDavid Rivshin else 2379d7cffafSDavid Rivshin pwm_register_offset = led_data->channel - 1; 2389d7cffafSDavid Rivshin 2399d7cffafSDavid Rivshin ret = is31fl32xx_write(led_data->priv, 2409d7cffafSDavid Rivshin cdef->pwm_register_base + pwm_register_offset, 2419d7cffafSDavid Rivshin brightness); 2429d7cffafSDavid Rivshin if (ret) 2439d7cffafSDavid Rivshin return ret; 2449d7cffafSDavid Rivshin 2459d7cffafSDavid Rivshin return is31fl32xx_write(led_data->priv, cdef->pwm_update_reg, 0); 2469d7cffafSDavid Rivshin } 2479d7cffafSDavid Rivshin 2489d7cffafSDavid Rivshin static int is31fl32xx_reset_regs(struct is31fl32xx_priv *priv) 2499d7cffafSDavid Rivshin { 2509d7cffafSDavid Rivshin const struct is31fl32xx_chipdef *cdef = priv->cdef; 2519d7cffafSDavid Rivshin int ret; 2529d7cffafSDavid Rivshin 2539d7cffafSDavid Rivshin if (cdef->reset_reg != IS31FL32XX_REG_NONE) { 2549d7cffafSDavid Rivshin ret = is31fl32xx_write(priv, cdef->reset_reg, 0); 2559d7cffafSDavid Rivshin if (ret) 2569d7cffafSDavid Rivshin return ret; 2579d7cffafSDavid Rivshin } 2589d7cffafSDavid Rivshin 2599d7cffafSDavid Rivshin if (cdef->reset_func) 2609d7cffafSDavid Rivshin return cdef->reset_func(priv); 2619d7cffafSDavid Rivshin 2629d7cffafSDavid Rivshin return 0; 2639d7cffafSDavid Rivshin } 2649d7cffafSDavid Rivshin 2659d7cffafSDavid Rivshin static int is31fl32xx_software_shutdown(struct is31fl32xx_priv *priv, 2669d7cffafSDavid Rivshin bool enable) 2679d7cffafSDavid Rivshin { 2689d7cffafSDavid Rivshin const struct is31fl32xx_chipdef *cdef = priv->cdef; 2699d7cffafSDavid Rivshin int ret; 2709d7cffafSDavid Rivshin 2719d7cffafSDavid Rivshin if (cdef->shutdown_reg != IS31FL32XX_REG_NONE) { 2729d7cffafSDavid Rivshin u8 value = enable ? IS31FL32XX_SHUTDOWN_SSD_ENABLE : 2739d7cffafSDavid Rivshin IS31FL32XX_SHUTDOWN_SSD_DISABLE; 2749d7cffafSDavid Rivshin ret = is31fl32xx_write(priv, cdef->shutdown_reg, value); 2759d7cffafSDavid Rivshin if (ret) 2769d7cffafSDavid Rivshin return ret; 2779d7cffafSDavid Rivshin } 2789d7cffafSDavid Rivshin 2799d7cffafSDavid Rivshin if (cdef->sw_shutdown_func) 2809d7cffafSDavid Rivshin return cdef->sw_shutdown_func(priv, enable); 2819d7cffafSDavid Rivshin 2829d7cffafSDavid Rivshin return 0; 2839d7cffafSDavid Rivshin } 2849d7cffafSDavid Rivshin 2859d7cffafSDavid Rivshin static int is31fl32xx_init_regs(struct is31fl32xx_priv *priv) 2869d7cffafSDavid Rivshin { 2879d7cffafSDavid Rivshin const struct is31fl32xx_chipdef *cdef = priv->cdef; 2889d7cffafSDavid Rivshin int ret; 2899d7cffafSDavid Rivshin 2909d7cffafSDavid Rivshin ret = is31fl32xx_reset_regs(priv); 2919d7cffafSDavid Rivshin if (ret) 2929d7cffafSDavid Rivshin return ret; 2939d7cffafSDavid Rivshin 2949d7cffafSDavid Rivshin /* 2959d7cffafSDavid Rivshin * Set enable bit for all channels. 2969d7cffafSDavid Rivshin * We will control state with PWM registers alone. 2979d7cffafSDavid Rivshin */ 2989d7cffafSDavid Rivshin if (cdef->led_control_register_base != IS31FL32XX_REG_NONE) { 2999d7cffafSDavid Rivshin u8 value = 3009d7cffafSDavid Rivshin GENMASK(cdef->enable_bits_per_led_control_register-1, 0); 3019d7cffafSDavid Rivshin u8 num_regs = cdef->channels / 3029d7cffafSDavid Rivshin cdef->enable_bits_per_led_control_register; 3039d7cffafSDavid Rivshin int i; 3049d7cffafSDavid Rivshin 3059d7cffafSDavid Rivshin for (i = 0; i < num_regs; i++) { 3069d7cffafSDavid Rivshin ret = is31fl32xx_write(priv, 3079d7cffafSDavid Rivshin cdef->led_control_register_base+i, 3089d7cffafSDavid Rivshin value); 3099d7cffafSDavid Rivshin if (ret) 3109d7cffafSDavid Rivshin return ret; 3119d7cffafSDavid Rivshin } 3129d7cffafSDavid Rivshin } 3139d7cffafSDavid Rivshin 3149d7cffafSDavid Rivshin ret = is31fl32xx_software_shutdown(priv, false); 3159d7cffafSDavid Rivshin if (ret) 3169d7cffafSDavid Rivshin return ret; 3179d7cffafSDavid Rivshin 3189d7cffafSDavid Rivshin if (cdef->global_control_reg != IS31FL32XX_REG_NONE) { 3199d7cffafSDavid Rivshin ret = is31fl32xx_write(priv, cdef->global_control_reg, 0x00); 3209d7cffafSDavid Rivshin if (ret) 3219d7cffafSDavid Rivshin return ret; 3229d7cffafSDavid Rivshin } 3239d7cffafSDavid Rivshin 3249d7cffafSDavid Rivshin return 0; 3259d7cffafSDavid Rivshin } 3269d7cffafSDavid Rivshin 3279d7cffafSDavid Rivshin static int is31fl32xx_parse_child_dt(const struct device *dev, 3289d7cffafSDavid Rivshin const struct device_node *child, 3299d7cffafSDavid Rivshin struct is31fl32xx_led_data *led_data) 3309d7cffafSDavid Rivshin { 3319d7cffafSDavid Rivshin struct led_classdev *cdev = &led_data->cdev; 3329d7cffafSDavid Rivshin int ret = 0; 3339d7cffafSDavid Rivshin u32 reg; 3349d7cffafSDavid Rivshin 3359d7cffafSDavid Rivshin if (of_property_read_string(child, "label", &cdev->name)) 3369d7cffafSDavid Rivshin cdev->name = child->name; 3379d7cffafSDavid Rivshin 3389d7cffafSDavid Rivshin ret = of_property_read_u32(child, "reg", ®); 3399d7cffafSDavid Rivshin if (ret || reg < 1 || reg > led_data->priv->cdef->channels) { 3409d7cffafSDavid Rivshin dev_err(dev, 3410571753eSRob Herring "Child node %pOF does not have a valid reg property\n", 3420571753eSRob Herring child); 3439d7cffafSDavid Rivshin return -EINVAL; 3449d7cffafSDavid Rivshin } 3459d7cffafSDavid Rivshin led_data->channel = reg; 3469d7cffafSDavid Rivshin 3479d7cffafSDavid Rivshin of_property_read_string(child, "linux,default-trigger", 3489d7cffafSDavid Rivshin &cdev->default_trigger); 3499d7cffafSDavid Rivshin 3509d7cffafSDavid Rivshin cdev->brightness_set_blocking = is31fl32xx_brightness_set; 3519d7cffafSDavid Rivshin 3529d7cffafSDavid Rivshin return 0; 3539d7cffafSDavid Rivshin } 3549d7cffafSDavid Rivshin 3559d7cffafSDavid Rivshin static struct is31fl32xx_led_data *is31fl32xx_find_led_data( 3569d7cffafSDavid Rivshin struct is31fl32xx_priv *priv, 3579d7cffafSDavid Rivshin u8 channel) 3589d7cffafSDavid Rivshin { 3599d7cffafSDavid Rivshin size_t i; 3609d7cffafSDavid Rivshin 3619d7cffafSDavid Rivshin for (i = 0; i < priv->num_leds; i++) { 3629d7cffafSDavid Rivshin if (priv->leds[i].channel == channel) 3639d7cffafSDavid Rivshin return &priv->leds[i]; 3649d7cffafSDavid Rivshin } 3659d7cffafSDavid Rivshin 3669d7cffafSDavid Rivshin return NULL; 3679d7cffafSDavid Rivshin } 3689d7cffafSDavid Rivshin 3699d7cffafSDavid Rivshin static int is31fl32xx_parse_dt(struct device *dev, 3709d7cffafSDavid Rivshin struct is31fl32xx_priv *priv) 3719d7cffafSDavid Rivshin { 3729d7cffafSDavid Rivshin struct device_node *child; 3739d7cffafSDavid Rivshin int ret = 0; 3749d7cffafSDavid Rivshin 375*99a013c8SMarek Behún for_each_available_child_of_node(dev_of_node(dev), child) { 3769d7cffafSDavid Rivshin struct is31fl32xx_led_data *led_data = 3779d7cffafSDavid Rivshin &priv->leds[priv->num_leds]; 3789d7cffafSDavid Rivshin const struct is31fl32xx_led_data *other_led_data; 3799d7cffafSDavid Rivshin 3809d7cffafSDavid Rivshin led_data->priv = priv; 3819d7cffafSDavid Rivshin 3829d7cffafSDavid Rivshin ret = is31fl32xx_parse_child_dt(dev, child, led_data); 3839d7cffafSDavid Rivshin if (ret) 3849d7cffafSDavid Rivshin goto err; 3859d7cffafSDavid Rivshin 3869d7cffafSDavid Rivshin /* Detect if channel is already in use by another child */ 3879d7cffafSDavid Rivshin other_led_data = is31fl32xx_find_led_data(priv, 3889d7cffafSDavid Rivshin led_data->channel); 3899d7cffafSDavid Rivshin if (other_led_data) { 3909d7cffafSDavid Rivshin dev_err(dev, 3919d7cffafSDavid Rivshin "%s and %s both attempting to use channel %d\n", 3929d7cffafSDavid Rivshin led_data->cdev.name, 3939d7cffafSDavid Rivshin other_led_data->cdev.name, 3949d7cffafSDavid Rivshin led_data->channel); 3959d7cffafSDavid Rivshin goto err; 3969d7cffafSDavid Rivshin } 3979d7cffafSDavid Rivshin 3989d7cffafSDavid Rivshin ret = devm_led_classdev_register(dev, &led_data->cdev); 3999d7cffafSDavid Rivshin if (ret) { 4009d7cffafSDavid Rivshin dev_err(dev, "failed to register PWM led for %s: %d\n", 4019d7cffafSDavid Rivshin led_data->cdev.name, ret); 4029d7cffafSDavid Rivshin goto err; 4039d7cffafSDavid Rivshin } 4049d7cffafSDavid Rivshin 4059d7cffafSDavid Rivshin priv->num_leds++; 4069d7cffafSDavid Rivshin } 4079d7cffafSDavid Rivshin 4089d7cffafSDavid Rivshin return 0; 4099d7cffafSDavid Rivshin 4109d7cffafSDavid Rivshin err: 4119d7cffafSDavid Rivshin of_node_put(child); 4129d7cffafSDavid Rivshin return ret; 4139d7cffafSDavid Rivshin } 4149d7cffafSDavid Rivshin 4151d991b71SH. Nikolaus Schaller static const struct of_device_id of_is31fl32xx_match[] = { 4169d7cffafSDavid Rivshin { .compatible = "issi,is31fl3236", .data = &is31fl3236_cdef, }, 4179d7cffafSDavid Rivshin { .compatible = "issi,is31fl3235", .data = &is31fl3235_cdef, }, 4189d7cffafSDavid Rivshin { .compatible = "issi,is31fl3218", .data = &is31fl3218_cdef, }, 419e0442d7dSDavid Rivshin { .compatible = "si-en,sn3218", .data = &is31fl3218_cdef, }, 4209d7cffafSDavid Rivshin { .compatible = "issi,is31fl3216", .data = &is31fl3216_cdef, }, 421e0442d7dSDavid Rivshin { .compatible = "si-en,sn3216", .data = &is31fl3216_cdef, }, 4229d7cffafSDavid Rivshin {}, 4239d7cffafSDavid Rivshin }; 4249d7cffafSDavid Rivshin 4251d991b71SH. Nikolaus Schaller MODULE_DEVICE_TABLE(of, of_is31fl32xx_match); 4269d7cffafSDavid Rivshin 4279d7cffafSDavid Rivshin static int is31fl32xx_probe(struct i2c_client *client, 4289d7cffafSDavid Rivshin const struct i2c_device_id *id) 4299d7cffafSDavid Rivshin { 4309d7cffafSDavid Rivshin const struct is31fl32xx_chipdef *cdef; 4319d7cffafSDavid Rivshin struct device *dev = &client->dev; 4329d7cffafSDavid Rivshin struct is31fl32xx_priv *priv; 4339d7cffafSDavid Rivshin int count; 4349d7cffafSDavid Rivshin int ret = 0; 4359d7cffafSDavid Rivshin 4362779f472SMarek Behún cdef = device_get_match_data(dev); 4379d7cffafSDavid Rivshin 438*99a013c8SMarek Behún count = of_get_available_child_count(dev_of_node(dev)); 4399d7cffafSDavid Rivshin if (!count) 4409d7cffafSDavid Rivshin return -EINVAL; 4419d7cffafSDavid Rivshin 4421669ec78SGustavo A. R. Silva priv = devm_kzalloc(dev, struct_size(priv, leds, count), 4439d7cffafSDavid Rivshin GFP_KERNEL); 4449d7cffafSDavid Rivshin if (!priv) 4459d7cffafSDavid Rivshin return -ENOMEM; 4469d7cffafSDavid Rivshin 4479d7cffafSDavid Rivshin priv->client = client; 4489d7cffafSDavid Rivshin priv->cdef = cdef; 4499d7cffafSDavid Rivshin i2c_set_clientdata(client, priv); 4509d7cffafSDavid Rivshin 4519d7cffafSDavid Rivshin ret = is31fl32xx_init_regs(priv); 4529d7cffafSDavid Rivshin if (ret) 4539d7cffafSDavid Rivshin return ret; 4549d7cffafSDavid Rivshin 4559d7cffafSDavid Rivshin ret = is31fl32xx_parse_dt(dev, priv); 4569d7cffafSDavid Rivshin if (ret) 4579d7cffafSDavid Rivshin return ret; 4589d7cffafSDavid Rivshin 4599d7cffafSDavid Rivshin return 0; 4609d7cffafSDavid Rivshin } 4619d7cffafSDavid Rivshin 4629d7cffafSDavid Rivshin static int is31fl32xx_remove(struct i2c_client *client) 4639d7cffafSDavid Rivshin { 4649d7cffafSDavid Rivshin struct is31fl32xx_priv *priv = i2c_get_clientdata(client); 4659d7cffafSDavid Rivshin 4669d7cffafSDavid Rivshin return is31fl32xx_reset_regs(priv); 4679d7cffafSDavid Rivshin } 4689d7cffafSDavid Rivshin 4699d7cffafSDavid Rivshin /* 4705706c01fSH. Nikolaus Schaller * i2c-core (and modalias) requires that id_table be properly filled, 4715706c01fSH. Nikolaus Schaller * even though it is not used for DeviceTree based instantiation. 4729d7cffafSDavid Rivshin */ 4731d991b71SH. Nikolaus Schaller static const struct i2c_device_id is31fl32xx_id[] = { 4745706c01fSH. Nikolaus Schaller { "is31fl3236" }, 4755706c01fSH. Nikolaus Schaller { "is31fl3235" }, 4765706c01fSH. Nikolaus Schaller { "is31fl3218" }, 4775706c01fSH. Nikolaus Schaller { "sn3218" }, 4785706c01fSH. Nikolaus Schaller { "is31fl3216" }, 4795706c01fSH. Nikolaus Schaller { "sn3216" }, 4809d7cffafSDavid Rivshin {}, 4819d7cffafSDavid Rivshin }; 4829d7cffafSDavid Rivshin 4831d991b71SH. Nikolaus Schaller MODULE_DEVICE_TABLE(i2c, is31fl32xx_id); 4849d7cffafSDavid Rivshin 4859d7cffafSDavid Rivshin static struct i2c_driver is31fl32xx_driver = { 4869d7cffafSDavid Rivshin .driver = { 4879d7cffafSDavid Rivshin .name = "is31fl32xx", 4881d991b71SH. Nikolaus Schaller .of_match_table = of_is31fl32xx_match, 4899d7cffafSDavid Rivshin }, 4909d7cffafSDavid Rivshin .probe = is31fl32xx_probe, 4919d7cffafSDavid Rivshin .remove = is31fl32xx_remove, 4921d991b71SH. Nikolaus Schaller .id_table = is31fl32xx_id, 4939d7cffafSDavid Rivshin }; 4949d7cffafSDavid Rivshin 4959d7cffafSDavid Rivshin module_i2c_driver(is31fl32xx_driver); 4969d7cffafSDavid Rivshin 4979d7cffafSDavid Rivshin MODULE_AUTHOR("David Rivshin <drivshin@allworx.com>"); 4989d7cffafSDavid Rivshin MODULE_DESCRIPTION("ISSI IS31FL32xx LED driver"); 4999d7cffafSDavid Rivshin MODULE_LICENSE("GPL v2"); 500