linux/drivers/leds/leds-an30259a.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2//
   3// Driver for Panasonic AN30259A 3-channel LED driver
   4//
   5// Copyright (c) 2018 Simon Shields <simon@lineageos.org>
   6//
   7// Datasheet:
   8// https://www.alliedelec.com/m/d/a9d2b3ee87c2d1a535a41dd747b1c247.pdf
   9
  10#include <linux/i2c.h>
  11#include <linux/leds.h>
  12#include <linux/module.h>
  13#include <linux/mutex.h>
  14#include <linux/of.h>
  15#include <linux/regmap.h>
  16
  17#define AN30259A_MAX_LEDS 3
  18
  19#define AN30259A_REG_SRESET 0x00
  20#define AN30259A_LED_SRESET BIT(0)
  21
  22/* LED power registers */
  23#define AN30259A_REG_LED_ON 0x01
  24#define AN30259A_LED_EN(x) BIT((x) - 1)
  25#define AN30259A_LED_SLOPE(x) BIT(((x) - 1) + 4)
  26
  27#define AN30259A_REG_LEDCC(x) (0x03 + ((x) - 1))
  28
  29/* slope control registers */
  30#define AN30259A_REG_SLOPE(x) (0x06 + ((x) - 1))
  31#define AN30259A_LED_SLOPETIME1(x) (x)
  32#define AN30259A_LED_SLOPETIME2(x) ((x) << 4)
  33
  34#define AN30259A_REG_LEDCNT1(x) (0x09 + (4 * ((x) - 1)))
  35#define AN30259A_LED_DUTYMAX(x) ((x) << 4)
  36#define AN30259A_LED_DUTYMID(x) (x)
  37
  38#define AN30259A_REG_LEDCNT2(x) (0x0A + (4 * ((x) - 1)))
  39#define AN30259A_LED_DELAY(x) ((x) << 4)
  40#define AN30259A_LED_DUTYMIN(x) (x)
  41
  42/* detention time control (length of each slope step) */
  43#define AN30259A_REG_LEDCNT3(x) (0x0B + (4 * ((x) - 1)))
  44#define AN30259A_LED_DT1(x) (x)
  45#define AN30259A_LED_DT2(x) ((x) << 4)
  46
  47#define AN30259A_REG_LEDCNT4(x) (0x0C + (4 * ((x) - 1)))
  48#define AN30259A_LED_DT3(x) (x)
  49#define AN30259A_LED_DT4(x) ((x) << 4)
  50
  51#define AN30259A_REG_MAX 0x14
  52
  53#define AN30259A_BLINK_MAX_TIME 7500 /* ms */
  54#define AN30259A_SLOPE_RESOLUTION 500 /* ms */
  55
  56#define AN30259A_NAME "an30259a"
  57
  58#define STATE_OFF 0
  59#define STATE_KEEP 1
  60#define STATE_ON 2
  61
  62struct an30259a;
  63
  64struct an30259a_led {
  65        struct an30259a *chip;
  66        struct fwnode_handle *fwnode;
  67        struct led_classdev cdev;
  68        u32 num;
  69        u32 default_state;
  70        bool sloping;
  71};
  72
  73struct an30259a {
  74        struct mutex mutex; /* held when writing to registers */
  75        struct i2c_client *client;
  76        struct an30259a_led leds[AN30259A_MAX_LEDS];
  77        struct regmap *regmap;
  78        int num_leds;
  79};
  80
  81static int an30259a_brightness_set(struct led_classdev *cdev,
  82                                   enum led_brightness brightness)
  83{
  84        struct an30259a_led *led;
  85        int ret;
  86        unsigned int led_on;
  87
  88        led = container_of(cdev, struct an30259a_led, cdev);
  89        mutex_lock(&led->chip->mutex);
  90
  91        ret = regmap_read(led->chip->regmap, AN30259A_REG_LED_ON, &led_on);
  92        if (ret)
  93                goto error;
  94
  95        switch (brightness) {
  96        case LED_OFF:
  97                led_on &= ~AN30259A_LED_EN(led->num);
  98                led_on &= ~AN30259A_LED_SLOPE(led->num);
  99                led->sloping = false;
 100                break;
 101        default:
 102                led_on |= AN30259A_LED_EN(led->num);
 103                if (led->sloping)
 104                        led_on |= AN30259A_LED_SLOPE(led->num);
 105                ret = regmap_write(led->chip->regmap,
 106                                   AN30259A_REG_LEDCNT1(led->num),
 107                                   AN30259A_LED_DUTYMAX(0xf) |
 108                                   AN30259A_LED_DUTYMID(0xf));
 109                if (ret)
 110                        goto error;
 111                break;
 112        }
 113
 114        ret = regmap_write(led->chip->regmap, AN30259A_REG_LED_ON, led_on);
 115        if (ret)
 116                goto error;
 117
 118        ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCC(led->num),
 119                           brightness);
 120
 121error:
 122        mutex_unlock(&led->chip->mutex);
 123
 124        return ret;
 125}
 126
 127static int an30259a_blink_set(struct led_classdev *cdev,
 128                              unsigned long *delay_off, unsigned long *delay_on)
 129{
 130        struct an30259a_led *led;
 131        int ret, num;
 132        unsigned int led_on;
 133        unsigned long off = *delay_off, on = *delay_on;
 134
 135        led = container_of(cdev, struct an30259a_led, cdev);
 136
 137        mutex_lock(&led->chip->mutex);
 138        num = led->num;
 139
 140        /* slope time can only be a multiple of 500ms. */
 141        if (off % AN30259A_SLOPE_RESOLUTION || on % AN30259A_SLOPE_RESOLUTION) {
 142                ret = -EINVAL;
 143                goto error;
 144        }
 145
 146        /* up to a maximum of 7500ms. */
 147        if (off > AN30259A_BLINK_MAX_TIME || on > AN30259A_BLINK_MAX_TIME) {
 148                ret = -EINVAL;
 149                goto error;
 150        }
 151
 152        /* if no blink specified, default to 1 Hz. */
 153        if (!off && !on) {
 154                *delay_off = off = 500;
 155                *delay_on = on = 500;
 156        }
 157
 158        /* convert into values the HW will understand. */
 159        off /= AN30259A_SLOPE_RESOLUTION;
 160        on /= AN30259A_SLOPE_RESOLUTION;
 161
 162        /* duty min should be zero (=off), delay should be zero. */
 163        ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCNT2(num),
 164                           AN30259A_LED_DELAY(0) | AN30259A_LED_DUTYMIN(0));
 165        if (ret)
 166                goto error;
 167
 168        /* reset detention time (no "breathing" effect). */
 169        ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCNT3(num),
 170                           AN30259A_LED_DT1(0) | AN30259A_LED_DT2(0));
 171        if (ret)
 172                goto error;
 173        ret = regmap_write(led->chip->regmap, AN30259A_REG_LEDCNT4(num),
 174                           AN30259A_LED_DT3(0) | AN30259A_LED_DT4(0));
 175        if (ret)
 176                goto error;
 177
 178        /* slope time controls on/off cycle length. */
 179        ret = regmap_write(led->chip->regmap, AN30259A_REG_SLOPE(num),
 180                           AN30259A_LED_SLOPETIME1(off) |
 181                           AN30259A_LED_SLOPETIME2(on));
 182        if (ret)
 183                goto error;
 184
 185        /* Finally, enable slope mode. */
 186        ret = regmap_read(led->chip->regmap, AN30259A_REG_LED_ON, &led_on);
 187        if (ret)
 188                goto error;
 189
 190        led_on |= AN30259A_LED_SLOPE(num) | AN30259A_LED_EN(led->num);
 191
 192        ret = regmap_write(led->chip->regmap, AN30259A_REG_LED_ON, led_on);
 193
 194        if (!ret)
 195                led->sloping = true;
 196error:
 197        mutex_unlock(&led->chip->mutex);
 198
 199        return ret;
 200}
 201
 202static int an30259a_dt_init(struct i2c_client *client,
 203                            struct an30259a *chip)
 204{
 205        struct device_node *np = dev_of_node(&client->dev), *child;
 206        int count, ret;
 207        int i = 0;
 208        const char *str;
 209        struct an30259a_led *led;
 210
 211        count = of_get_available_child_count(np);
 212        if (!count || count > AN30259A_MAX_LEDS)
 213                return -EINVAL;
 214
 215        for_each_available_child_of_node(np, child) {
 216                u32 source;
 217
 218                ret = of_property_read_u32(child, "reg", &source);
 219                if (ret != 0 || !source || source > AN30259A_MAX_LEDS) {
 220                        dev_err(&client->dev, "Couldn't read LED address: %d\n",
 221                                ret);
 222                        count--;
 223                        continue;
 224                }
 225
 226                led = &chip->leds[i];
 227
 228                led->num = source;
 229                led->chip = chip;
 230                led->fwnode = of_fwnode_handle(child);
 231
 232                if (!of_property_read_string(child, "default-state", &str)) {
 233                        if (!strcmp(str, "on"))
 234                                led->default_state = STATE_ON;
 235                        else if (!strcmp(str, "keep"))
 236                                led->default_state = STATE_KEEP;
 237                        else
 238                                led->default_state = STATE_OFF;
 239                }
 240
 241                i++;
 242        }
 243
 244        if (!count)
 245                return -EINVAL;
 246
 247        chip->num_leds = i;
 248
 249        return 0;
 250}
 251
 252static const struct regmap_config an30259a_regmap_config = {
 253        .reg_bits = 8,
 254        .val_bits = 8,
 255        .max_register = AN30259A_REG_MAX,
 256};
 257
 258static void an30259a_init_default_state(struct an30259a_led *led)
 259{
 260        struct an30259a *chip = led->chip;
 261        int led_on, err;
 262
 263        switch (led->default_state) {
 264        case STATE_ON:
 265                led->cdev.brightness = LED_FULL;
 266                break;
 267        case STATE_KEEP:
 268                err = regmap_read(chip->regmap, AN30259A_REG_LED_ON, &led_on);
 269                if (err)
 270                        break;
 271
 272                if (!(led_on & AN30259A_LED_EN(led->num))) {
 273                        led->cdev.brightness = LED_OFF;
 274                        break;
 275                }
 276                regmap_read(chip->regmap, AN30259A_REG_LEDCC(led->num),
 277                            &led->cdev.brightness);
 278                break;
 279        default:
 280                led->cdev.brightness = LED_OFF;
 281        }
 282
 283        an30259a_brightness_set(&led->cdev, led->cdev.brightness);
 284}
 285
 286static int an30259a_probe(struct i2c_client *client)
 287{
 288        struct an30259a *chip;
 289        int i, err;
 290
 291        chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
 292        if (!chip)
 293                return -ENOMEM;
 294
 295        err = an30259a_dt_init(client, chip);
 296        if (err < 0)
 297                return err;
 298
 299        mutex_init(&chip->mutex);
 300        chip->client = client;
 301        i2c_set_clientdata(client, chip);
 302
 303        chip->regmap = devm_regmap_init_i2c(client, &an30259a_regmap_config);
 304
 305        if (IS_ERR(chip->regmap)) {
 306                err = PTR_ERR(chip->regmap);
 307                dev_err(&client->dev, "Failed to allocate register map: %d\n",
 308                        err);
 309                goto exit;
 310        }
 311
 312        for (i = 0; i < chip->num_leds; i++) {
 313                struct led_init_data init_data = {};
 314
 315                an30259a_init_default_state(&chip->leds[i]);
 316                chip->leds[i].cdev.brightness_set_blocking =
 317                        an30259a_brightness_set;
 318                chip->leds[i].cdev.blink_set = an30259a_blink_set;
 319
 320                init_data.fwnode = chip->leds[i].fwnode;
 321                init_data.devicename = AN30259A_NAME;
 322                init_data.default_label = ":";
 323
 324                err = devm_led_classdev_register_ext(&client->dev,
 325                                                 &chip->leds[i].cdev,
 326                                                 &init_data);
 327                if (err < 0)
 328                        goto exit;
 329        }
 330        return 0;
 331
 332exit:
 333        mutex_destroy(&chip->mutex);
 334        return err;
 335}
 336
 337static int an30259a_remove(struct i2c_client *client)
 338{
 339        struct an30259a *chip = i2c_get_clientdata(client);
 340
 341        mutex_destroy(&chip->mutex);
 342
 343        return 0;
 344}
 345
 346static const struct of_device_id an30259a_match_table[] = {
 347        { .compatible = "panasonic,an30259a", },
 348        { /* sentinel */ },
 349};
 350
 351MODULE_DEVICE_TABLE(of, an30259a_match_table);
 352
 353static const struct i2c_device_id an30259a_id[] = {
 354        { "an30259a", 0 },
 355        { /* sentinel */ },
 356};
 357MODULE_DEVICE_TABLE(i2c, an30259a_id);
 358
 359static struct i2c_driver an30259a_driver = {
 360        .driver = {
 361                .name = "leds-an30259a",
 362                .of_match_table = of_match_ptr(an30259a_match_table),
 363        },
 364        .probe_new = an30259a_probe,
 365        .remove = an30259a_remove,
 366        .id_table = an30259a_id,
 367};
 368
 369module_i2c_driver(an30259a_driver);
 370
 371MODULE_AUTHOR("Simon Shields <simon@lineageos.org>");
 372MODULE_DESCRIPTION("AN30259A LED driver");
 373MODULE_LICENSE("GPL v2");
 374