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