1*c7dd343aSLukas Timmermann // SPDX-License-Identifier: GPL-2.0-or-later 2*c7dd343aSLukas Timmermann /* 3*c7dd343aSLukas Timmermann * Osram AMS AS3668 LED Driver IC 4*c7dd343aSLukas Timmermann * 5*c7dd343aSLukas Timmermann * Copyright (C) 2025 Lukas Timmermann <linux@timmermann.space> 6*c7dd343aSLukas Timmermann */ 7*c7dd343aSLukas Timmermann 8*c7dd343aSLukas Timmermann #include <linux/bitfield.h> 9*c7dd343aSLukas Timmermann #include <linux/i2c.h> 10*c7dd343aSLukas Timmermann #include <linux/leds.h> 11*c7dd343aSLukas Timmermann #include <linux/module.h> 12*c7dd343aSLukas Timmermann #include <linux/uleds.h> 13*c7dd343aSLukas Timmermann 14*c7dd343aSLukas Timmermann #define AS3668_MAX_LEDS 4 15*c7dd343aSLukas Timmermann 16*c7dd343aSLukas Timmermann /* Chip Ident */ 17*c7dd343aSLukas Timmermann 18*c7dd343aSLukas Timmermann #define AS3668_CHIP_ID1_REG 0x3e 19*c7dd343aSLukas Timmermann #define AS3668_CHIP_ID 0xa5 20*c7dd343aSLukas Timmermann 21*c7dd343aSLukas Timmermann /* Current Control */ 22*c7dd343aSLukas Timmermann 23*c7dd343aSLukas Timmermann #define AS3668_CURR_MODE_REG 0x01 24*c7dd343aSLukas Timmermann #define AS3668_CURR_MODE_OFF 0x0 25*c7dd343aSLukas Timmermann #define AS3668_CURR_MODE_ON 0x1 26*c7dd343aSLukas Timmermann #define AS3668_CURR1_MODE_MASK GENMASK(1, 0) 27*c7dd343aSLukas Timmermann #define AS3668_CURR2_MODE_MASK GENMASK(3, 2) 28*c7dd343aSLukas Timmermann #define AS3668_CURR3_MODE_MASK GENMASK(5, 4) 29*c7dd343aSLukas Timmermann #define AS3668_CURR4_MODE_MASK GENMASK(7, 6) 30*c7dd343aSLukas Timmermann #define AS3668_CURR1_REG 0x02 31*c7dd343aSLukas Timmermann #define AS3668_CURR2_REG 0x03 32*c7dd343aSLukas Timmermann #define AS3668_CURR3_REG 0x04 33*c7dd343aSLukas Timmermann #define AS3668_CURR4_REG 0x05 34*c7dd343aSLukas Timmermann 35*c7dd343aSLukas Timmermann #define AS3668_CURR_MODE_PACK(mode) (((mode) << 0) | \ 36*c7dd343aSLukas Timmermann ((mode) << 2) | \ 37*c7dd343aSLukas Timmermann ((mode) << 4) | \ 38*c7dd343aSLukas Timmermann ((mode) << 6)) 39*c7dd343aSLukas Timmermann 40*c7dd343aSLukas Timmermann struct as3668_led { 41*c7dd343aSLukas Timmermann struct led_classdev cdev; 42*c7dd343aSLukas Timmermann struct as3668 *chip; 43*c7dd343aSLukas Timmermann struct fwnode_handle *fwnode; 44*c7dd343aSLukas Timmermann u8 mode_mask; 45*c7dd343aSLukas Timmermann u8 current_reg; 46*c7dd343aSLukas Timmermann }; 47*c7dd343aSLukas Timmermann 48*c7dd343aSLukas Timmermann struct as3668 { 49*c7dd343aSLukas Timmermann struct i2c_client *client; 50*c7dd343aSLukas Timmermann struct as3668_led leds[AS3668_MAX_LEDS]; 51*c7dd343aSLukas Timmermann }; 52*c7dd343aSLukas Timmermann 53*c7dd343aSLukas Timmermann static int as3668_channel_mode_set(struct as3668_led *led, u8 mode) 54*c7dd343aSLukas Timmermann { 55*c7dd343aSLukas Timmermann int ret; 56*c7dd343aSLukas Timmermann u8 channel_modes; 57*c7dd343aSLukas Timmermann 58*c7dd343aSLukas Timmermann ret = i2c_smbus_read_byte_data(led->chip->client, AS3668_CURR_MODE_REG); 59*c7dd343aSLukas Timmermann if (ret < 0) { 60*c7dd343aSLukas Timmermann dev_err(led->cdev.dev, "failed to read channel modes\n"); 61*c7dd343aSLukas Timmermann return ret; 62*c7dd343aSLukas Timmermann } 63*c7dd343aSLukas Timmermann channel_modes = (u8)ret; 64*c7dd343aSLukas Timmermann 65*c7dd343aSLukas Timmermann channel_modes &= ~led->mode_mask; 66*c7dd343aSLukas Timmermann channel_modes |= led->mode_mask & (AS3668_CURR_MODE_PACK(mode)); 67*c7dd343aSLukas Timmermann 68*c7dd343aSLukas Timmermann return i2c_smbus_write_byte_data(led->chip->client, AS3668_CURR_MODE_REG, channel_modes); 69*c7dd343aSLukas Timmermann } 70*c7dd343aSLukas Timmermann 71*c7dd343aSLukas Timmermann static enum led_brightness as3668_brightness_get(struct led_classdev *cdev) 72*c7dd343aSLukas Timmermann { 73*c7dd343aSLukas Timmermann struct as3668_led *led = container_of(cdev, struct as3668_led, cdev); 74*c7dd343aSLukas Timmermann 75*c7dd343aSLukas Timmermann return i2c_smbus_read_byte_data(led->chip->client, led->current_reg); 76*c7dd343aSLukas Timmermann } 77*c7dd343aSLukas Timmermann 78*c7dd343aSLukas Timmermann static void as3668_brightness_set(struct led_classdev *cdev, enum led_brightness brightness) 79*c7dd343aSLukas Timmermann { 80*c7dd343aSLukas Timmermann struct as3668_led *led = container_of(cdev, struct as3668_led, cdev); 81*c7dd343aSLukas Timmermann int err; 82*c7dd343aSLukas Timmermann 83*c7dd343aSLukas Timmermann err = as3668_channel_mode_set(led, !!brightness); 84*c7dd343aSLukas Timmermann if (err) 85*c7dd343aSLukas Timmermann dev_err(cdev->dev, "failed to set channel mode: %d\n", err); 86*c7dd343aSLukas Timmermann 87*c7dd343aSLukas Timmermann err = i2c_smbus_write_byte_data(led->chip->client, led->current_reg, brightness); 88*c7dd343aSLukas Timmermann if (err) 89*c7dd343aSLukas Timmermann dev_err(cdev->dev, "failed to set brightness: %d\n", err); 90*c7dd343aSLukas Timmermann } 91*c7dd343aSLukas Timmermann 92*c7dd343aSLukas Timmermann static int as3668_dt_init(struct as3668 *as3668) 93*c7dd343aSLukas Timmermann { 94*c7dd343aSLukas Timmermann struct device *dev = &as3668->client->dev; 95*c7dd343aSLukas Timmermann struct as3668_led *led; 96*c7dd343aSLukas Timmermann struct led_init_data init_data = {}; 97*c7dd343aSLukas Timmermann int err; 98*c7dd343aSLukas Timmermann u32 reg; 99*c7dd343aSLukas Timmermann 100*c7dd343aSLukas Timmermann for_each_available_child_of_node_scoped(dev_of_node(dev), child) { 101*c7dd343aSLukas Timmermann err = of_property_read_u32(child, "reg", ®); 102*c7dd343aSLukas Timmermann if (err) 103*c7dd343aSLukas Timmermann return dev_err_probe(dev, err, "failed to read 'reg' property"); 104*c7dd343aSLukas Timmermann 105*c7dd343aSLukas Timmermann if (reg < 0 || reg >= AS3668_MAX_LEDS) 106*c7dd343aSLukas Timmermann return dev_err_probe(dev, -EINVAL, 107*c7dd343aSLukas Timmermann "unsupported LED: %d\n", reg); 108*c7dd343aSLukas Timmermann 109*c7dd343aSLukas Timmermann led = &as3668->leds[reg]; 110*c7dd343aSLukas Timmermann led->fwnode = of_fwnode_handle(child); 111*c7dd343aSLukas Timmermann 112*c7dd343aSLukas Timmermann led->current_reg = reg + AS3668_CURR1_REG; 113*c7dd343aSLukas Timmermann led->mode_mask = AS3668_CURR1_MODE_MASK << (reg * 2); 114*c7dd343aSLukas Timmermann led->chip = as3668; 115*c7dd343aSLukas Timmermann 116*c7dd343aSLukas Timmermann led->cdev.max_brightness = U8_MAX; 117*c7dd343aSLukas Timmermann led->cdev.brightness_get = as3668_brightness_get; 118*c7dd343aSLukas Timmermann led->cdev.brightness_set = as3668_brightness_set; 119*c7dd343aSLukas Timmermann 120*c7dd343aSLukas Timmermann init_data.fwnode = led->fwnode; 121*c7dd343aSLukas Timmermann init_data.default_label = ":"; 122*c7dd343aSLukas Timmermann 123*c7dd343aSLukas Timmermann err = devm_led_classdev_register_ext(dev, &led->cdev, &init_data); 124*c7dd343aSLukas Timmermann if (err) 125*c7dd343aSLukas Timmermann return dev_err_probe(dev, err, "failed to register LED %d\n", reg); 126*c7dd343aSLukas Timmermann } 127*c7dd343aSLukas Timmermann 128*c7dd343aSLukas Timmermann return 0; 129*c7dd343aSLukas Timmermann } 130*c7dd343aSLukas Timmermann 131*c7dd343aSLukas Timmermann static int as3668_probe(struct i2c_client *client) 132*c7dd343aSLukas Timmermann { 133*c7dd343aSLukas Timmermann struct as3668 *as3668; 134*c7dd343aSLukas Timmermann int err; 135*c7dd343aSLukas Timmermann u8 chip_id; 136*c7dd343aSLukas Timmermann 137*c7dd343aSLukas Timmermann chip_id = i2c_smbus_read_byte_data(client, AS3668_CHIP_ID1_REG); 138*c7dd343aSLukas Timmermann if (chip_id != AS3668_CHIP_ID) 139*c7dd343aSLukas Timmermann return dev_err_probe(&client->dev, -ENODEV, 140*c7dd343aSLukas Timmermann "expected chip ID 0x%02x, got 0x%02x\n", 141*c7dd343aSLukas Timmermann AS3668_CHIP_ID, chip_id); 142*c7dd343aSLukas Timmermann 143*c7dd343aSLukas Timmermann as3668 = devm_kzalloc(&client->dev, sizeof(*as3668), GFP_KERNEL); 144*c7dd343aSLukas Timmermann if (!as3668) 145*c7dd343aSLukas Timmermann return -ENOMEM; 146*c7dd343aSLukas Timmermann 147*c7dd343aSLukas Timmermann as3668->client = client; 148*c7dd343aSLukas Timmermann 149*c7dd343aSLukas Timmermann err = as3668_dt_init(as3668); 150*c7dd343aSLukas Timmermann if (err) 151*c7dd343aSLukas Timmermann return err; 152*c7dd343aSLukas Timmermann 153*c7dd343aSLukas Timmermann /* Set all four channel modes to 'off' */ 154*c7dd343aSLukas Timmermann err = i2c_smbus_write_byte_data(client, AS3668_CURR_MODE_REG, 155*c7dd343aSLukas Timmermann FIELD_PREP(AS3668_CURR1_MODE_MASK, AS3668_CURR_MODE_OFF) | 156*c7dd343aSLukas Timmermann FIELD_PREP(AS3668_CURR2_MODE_MASK, AS3668_CURR_MODE_OFF) | 157*c7dd343aSLukas Timmermann FIELD_PREP(AS3668_CURR3_MODE_MASK, AS3668_CURR_MODE_OFF) | 158*c7dd343aSLukas Timmermann FIELD_PREP(AS3668_CURR4_MODE_MASK, AS3668_CURR_MODE_OFF)); 159*c7dd343aSLukas Timmermann 160*c7dd343aSLukas Timmermann /* Set initial currents to 0mA */ 161*c7dd343aSLukas Timmermann err |= i2c_smbus_write_byte_data(client, AS3668_CURR1_REG, 0); 162*c7dd343aSLukas Timmermann err |= i2c_smbus_write_byte_data(client, AS3668_CURR2_REG, 0); 163*c7dd343aSLukas Timmermann err |= i2c_smbus_write_byte_data(client, AS3668_CURR3_REG, 0); 164*c7dd343aSLukas Timmermann err |= i2c_smbus_write_byte_data(client, AS3668_CURR4_REG, 0); 165*c7dd343aSLukas Timmermann 166*c7dd343aSLukas Timmermann if (err) 167*c7dd343aSLukas Timmermann return dev_err_probe(&client->dev, -EIO, "failed to set zero initial current levels\n"); 168*c7dd343aSLukas Timmermann 169*c7dd343aSLukas Timmermann return 0; 170*c7dd343aSLukas Timmermann } 171*c7dd343aSLukas Timmermann 172*c7dd343aSLukas Timmermann static void as3668_remove(struct i2c_client *client) 173*c7dd343aSLukas Timmermann { 174*c7dd343aSLukas Timmermann i2c_smbus_write_byte_data(client, AS3668_CURR_MODE_REG, 0); 175*c7dd343aSLukas Timmermann } 176*c7dd343aSLukas Timmermann 177*c7dd343aSLukas Timmermann static const struct i2c_device_id as3668_idtable[] = { 178*c7dd343aSLukas Timmermann { "as3668" }, 179*c7dd343aSLukas Timmermann { } 180*c7dd343aSLukas Timmermann }; 181*c7dd343aSLukas Timmermann MODULE_DEVICE_TABLE(i2c, as3668_idtable); 182*c7dd343aSLukas Timmermann 183*c7dd343aSLukas Timmermann static const struct of_device_id as3668_match_table[] = { 184*c7dd343aSLukas Timmermann { .compatible = "ams,as3668" }, 185*c7dd343aSLukas Timmermann { } 186*c7dd343aSLukas Timmermann }; 187*c7dd343aSLukas Timmermann MODULE_DEVICE_TABLE(of, as3668_match_table); 188*c7dd343aSLukas Timmermann 189*c7dd343aSLukas Timmermann static struct i2c_driver as3668_driver = { 190*c7dd343aSLukas Timmermann .driver = { 191*c7dd343aSLukas Timmermann .name = "leds_as3668", 192*c7dd343aSLukas Timmermann .of_match_table = as3668_match_table, 193*c7dd343aSLukas Timmermann }, 194*c7dd343aSLukas Timmermann .probe = as3668_probe, 195*c7dd343aSLukas Timmermann .remove = as3668_remove, 196*c7dd343aSLukas Timmermann .id_table = as3668_idtable, 197*c7dd343aSLukas Timmermann }; 198*c7dd343aSLukas Timmermann module_i2c_driver(as3668_driver); 199*c7dd343aSLukas Timmermann 200*c7dd343aSLukas Timmermann MODULE_AUTHOR("Lukas Timmermann <linux@timmermann.space>"); 201*c7dd343aSLukas Timmermann MODULE_DESCRIPTION("AS3668 LED driver"); 202*c7dd343aSLukas Timmermann MODULE_LICENSE("GPL"); 203