linux/drivers/leds/leds-el15203000.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (c) 2019 Crane Merchandising Systems. All rights reserved.
   3// Copyright (C) 2019 Oleh Kravchenko <oleg@kaa.org.ua>
   4
   5#include <linux/delay.h>
   6#include <linux/leds.h>
   7#include <linux/mod_devicetable.h>
   8#include <linux/module.h>
   9#include <linux/property.h>
  10#include <linux/spi/spi.h>
  11
  12/*
  13 * EL15203000 SPI protocol description:
  14 * +-----+---------+
  15 * | LED | COMMAND |
  16 * +-----+---------+
  17 * |  1  |    1    |
  18 * +-----+---------+
  19 * (*) LEDs MCU board expects 20 msec delay per byte.
  20 *
  21 * LEDs:
  22 * +----------+--------------+-------------------------------------------+
  23 * |    ID    |     NAME     |         DESCRIPTION                       |
  24 * +----------+--------------+-------------------------------------------+
  25 * | 'P' 0x50 |     Pipe     | Consists from 5 LEDs, controlled by board |
  26 * +----------+--------------+-------------------------------------------+
  27 * | 'S' 0x53 | Screen frame | Light tube around the screen              |
  28 * +----------+--------------+-------------------------------------------+
  29 * | 'V' 0x56 | Vending area | Highlights a cup of coffee                |
  30 * +----------+--------------+-------------------------------------------+
  31 *
  32 * COMMAND:
  33 * +----------+-----------------+--------------+--------------+
  34 * |  VALUES  |       PIPE      | SCREEN FRAME | VENDING AREA |
  35 * +----------+-----------------+--------------+--------------+
  36 * | '0' 0x30 |                      Off                      |
  37 * +----------+-----------------------------------------------+
  38 * | '1' 0x31 |                      On                       |
  39 * +----------+-----------------+--------------+--------------+
  40 * | '2' 0x32 |     Cascade     |   Breathing  |
  41 * +----------+-----------------+--------------+
  42 * | '3' 0x33 | Inverse cascade |
  43 * +----------+-----------------+
  44 * | '4' 0x34 |     Bounce      |
  45 * +----------+-----------------+
  46 * | '5' 0x35 | Inverse bounce  |
  47 * +----------+-----------------+
  48 */
  49
  50/* EL15203000 default settings */
  51#define EL_FW_DELAY_USEC        20000ul
  52#define EL_PATTERN_DELAY_MSEC   800u
  53#define EL_PATTERN_LEN          10u
  54#define EL_PATTERN_HALF_LEN     (EL_PATTERN_LEN / 2)
  55
  56enum el15203000_command {
  57        /* for all LEDs */
  58        EL_OFF                  = '0',
  59        EL_ON                   = '1',
  60
  61        /* for Screen LED */
  62        EL_SCREEN_BREATHING     = '2',
  63
  64        /* for Pipe LED */
  65        EL_PIPE_CASCADE         = '2',
  66        EL_PIPE_INV_CASCADE     = '3',
  67        EL_PIPE_BOUNCE          = '4',
  68        EL_PIPE_INV_BOUNCE      = '5',
  69};
  70
  71struct el15203000_led {
  72        struct led_classdev     ldev;
  73        struct el15203000       *priv;
  74        u32                     reg;
  75};
  76
  77struct el15203000 {
  78        struct device           *dev;
  79        struct mutex            lock;
  80        struct spi_device       *spi;
  81        unsigned long           delay;
  82        size_t                  count;
  83        struct el15203000_led   leds[];
  84};
  85
  86#define to_el15203000_led(d)    container_of(d, struct el15203000_led, ldev)
  87
  88static int el15203000_cmd(struct el15203000_led *led, u8 brightness)
  89{
  90        int             ret;
  91        u8              cmd[2];
  92        size_t          i;
  93
  94        mutex_lock(&led->priv->lock);
  95
  96        dev_dbg(led->priv->dev, "Set brightness of 0x%02x(%c) to 0x%02x(%c)",
  97                led->reg, led->reg, brightness, brightness);
  98
  99        /* to avoid SPI mistiming with firmware we should wait some time */
 100        if (time_after(led->priv->delay, jiffies)) {
 101                dev_dbg(led->priv->dev, "Wait %luus to sync",
 102                        EL_FW_DELAY_USEC);
 103
 104                usleep_range(EL_FW_DELAY_USEC,
 105                             EL_FW_DELAY_USEC + 1);
 106        }
 107
 108        cmd[0] = led->reg;
 109        cmd[1] = brightness;
 110
 111        for (i = 0; i < ARRAY_SIZE(cmd); i++) {
 112                if (i)
 113                        usleep_range(EL_FW_DELAY_USEC,
 114                                     EL_FW_DELAY_USEC + 1);
 115
 116                ret = spi_write(led->priv->spi, &cmd[i], sizeof(cmd[i]));
 117                if (ret) {
 118                        dev_err(led->priv->dev,
 119                                "spi_write() error %d", ret);
 120                        break;
 121                }
 122        }
 123
 124        led->priv->delay = jiffies + usecs_to_jiffies(EL_FW_DELAY_USEC);
 125
 126        mutex_unlock(&led->priv->lock);
 127
 128        return ret;
 129}
 130
 131static int el15203000_set_blocking(struct led_classdev *ldev,
 132                                   enum led_brightness brightness)
 133{
 134        struct el15203000_led *led = to_el15203000_led(ldev);
 135
 136        return el15203000_cmd(led, brightness == LED_OFF ? EL_OFF : EL_ON);
 137}
 138
 139static int el15203000_pattern_set_S(struct led_classdev *ldev,
 140                                    struct led_pattern *pattern,
 141                                    u32 len, int repeat)
 142{
 143        struct el15203000_led *led = to_el15203000_led(ldev);
 144
 145        if (repeat > 0 || len != 2 ||
 146            pattern[0].delta_t != 4000 || pattern[0].brightness != 0 ||
 147            pattern[1].delta_t != 4000 || pattern[1].brightness != 1)
 148                return -EINVAL;
 149
 150        dev_dbg(led->priv->dev, "Breathing mode for 0x%02x(%c)",
 151                led->reg, led->reg);
 152
 153        return el15203000_cmd(led, EL_SCREEN_BREATHING);
 154}
 155
 156static bool is_cascade(const struct led_pattern *pattern, u32 len,
 157                       bool inv, bool right)
 158{
 159        int val, t;
 160        u32 i;
 161
 162        if (len != EL_PATTERN_HALF_LEN)
 163                return false;
 164
 165        val = right ? BIT(4) : BIT(0);
 166
 167        for (i = 0; i < len; i++) {
 168                t = inv ? ~val & GENMASK(4, 0) : val;
 169
 170                if (pattern[i].delta_t != EL_PATTERN_DELAY_MSEC ||
 171                    pattern[i].brightness != t)
 172                        return false;
 173
 174                val = right ? val >> 1 : val << 1;
 175        }
 176
 177        return true;
 178}
 179
 180static bool is_bounce(const struct led_pattern *pattern, u32 len, bool inv)
 181{
 182        if (len != EL_PATTERN_LEN)
 183                return false;
 184
 185        return is_cascade(pattern, EL_PATTERN_HALF_LEN, inv, false) &&
 186               is_cascade(pattern + EL_PATTERN_HALF_LEN,
 187                          EL_PATTERN_HALF_LEN, inv, true);
 188}
 189
 190static int el15203000_pattern_set_P(struct led_classdev *ldev,
 191                                    struct led_pattern *pattern,
 192                                    u32 len, int repeat)
 193{
 194        struct el15203000_led   *led = to_el15203000_led(ldev);
 195        u8                      cmd;
 196
 197        if (repeat > 0)
 198                return -EINVAL;
 199
 200        if (is_cascade(pattern, len, false, false)) {
 201                dev_dbg(led->priv->dev, "Cascade mode for 0x%02x(%c)",
 202                        led->reg, led->reg);
 203
 204                cmd = EL_PIPE_CASCADE;
 205        } else if (is_cascade(pattern, len, true, false)) {
 206                dev_dbg(led->priv->dev, "Inverse cascade mode for 0x%02x(%c)",
 207                        led->reg, led->reg);
 208
 209                cmd = EL_PIPE_INV_CASCADE;
 210        } else if (is_bounce(pattern, len, false)) {
 211                dev_dbg(led->priv->dev, "Bounce mode for 0x%02x(%c)",
 212                        led->reg, led->reg);
 213
 214                cmd = EL_PIPE_BOUNCE;
 215        } else if (is_bounce(pattern, len, true)) {
 216                dev_dbg(led->priv->dev, "Inverse bounce mode for 0x%02x(%c)",
 217                        led->reg, led->reg);
 218
 219                cmd = EL_PIPE_INV_BOUNCE;
 220        } else {
 221                dev_err(led->priv->dev, "Invalid hw_pattern for 0x%02x(%c)!",
 222                        led->reg, led->reg);
 223
 224                return -EINVAL;
 225        }
 226
 227        return el15203000_cmd(led, cmd);
 228}
 229
 230static int el15203000_pattern_clear(struct led_classdev *ldev)
 231{
 232        struct el15203000_led *led = to_el15203000_led(ldev);
 233
 234        return el15203000_cmd(led, EL_OFF);
 235}
 236
 237static int el15203000_probe_dt(struct el15203000 *priv)
 238{
 239        struct el15203000_led   *led = priv->leds;
 240        struct fwnode_handle    *child;
 241        int                     ret;
 242
 243        device_for_each_child_node(priv->dev, child) {
 244                struct led_init_data init_data = {};
 245
 246                ret = fwnode_property_read_u32(child, "reg", &led->reg);
 247                if (ret) {
 248                        dev_err(priv->dev, "LED without ID number");
 249                        goto err_child_out;
 250                }
 251
 252                if (led->reg > U8_MAX) {
 253                        dev_err(priv->dev, "LED value %d is invalid", led->reg);
 254                        ret = -EINVAL;
 255                        goto err_child_out;
 256                }
 257
 258                led->priv                         = priv;
 259                led->ldev.max_brightness          = LED_ON;
 260                led->ldev.brightness_set_blocking = el15203000_set_blocking;
 261
 262                if (led->reg == 'S') {
 263                        led->ldev.pattern_set   = el15203000_pattern_set_S;
 264                        led->ldev.pattern_clear = el15203000_pattern_clear;
 265                } else if (led->reg == 'P') {
 266                        led->ldev.pattern_set   = el15203000_pattern_set_P;
 267                        led->ldev.pattern_clear = el15203000_pattern_clear;
 268                }
 269
 270                init_data.fwnode = child;
 271                ret = devm_led_classdev_register_ext(priv->dev, &led->ldev,
 272                                                     &init_data);
 273                if (ret) {
 274                        dev_err(priv->dev,
 275                                "failed to register LED device %s, err %d",
 276                                led->ldev.name, ret);
 277                        goto err_child_out;
 278                }
 279
 280                led++;
 281        }
 282
 283        return 0;
 284
 285err_child_out:
 286        fwnode_handle_put(child);
 287        return ret;
 288}
 289
 290static int el15203000_probe(struct spi_device *spi)
 291{
 292        struct el15203000       *priv;
 293        size_t                  count;
 294
 295        count = device_get_child_node_count(&spi->dev);
 296        if (!count) {
 297                dev_err(&spi->dev, "LEDs are not defined in device tree!");
 298                return -ENODEV;
 299        }
 300
 301        priv = devm_kzalloc(&spi->dev, struct_size(priv, leds, count),
 302                            GFP_KERNEL);
 303        if (!priv)
 304                return -ENOMEM;
 305
 306        mutex_init(&priv->lock);
 307        priv->count     = count;
 308        priv->dev       = &spi->dev;
 309        priv->spi       = spi;
 310        priv->delay     = jiffies -
 311                          usecs_to_jiffies(EL_FW_DELAY_USEC);
 312
 313        spi_set_drvdata(spi, priv);
 314
 315        return el15203000_probe_dt(priv);
 316}
 317
 318static int el15203000_remove(struct spi_device *spi)
 319{
 320        struct el15203000 *priv = spi_get_drvdata(spi);
 321
 322        mutex_destroy(&priv->lock);
 323
 324        return 0;
 325}
 326
 327static const struct of_device_id el15203000_dt_ids[] = {
 328        { .compatible = "crane,el15203000", },
 329        {},
 330};
 331
 332MODULE_DEVICE_TABLE(of, el15203000_dt_ids);
 333
 334static struct spi_driver el15203000_driver = {
 335        .probe          = el15203000_probe,
 336        .remove         = el15203000_remove,
 337        .driver = {
 338                .name           = KBUILD_MODNAME,
 339                .of_match_table = el15203000_dt_ids,
 340        },
 341};
 342
 343module_spi_driver(el15203000_driver);
 344
 345MODULE_AUTHOR("Oleh Kravchenko <oleg@kaa.org.ua>");
 346MODULE_DESCRIPTION("el15203000 LED driver");
 347MODULE_LICENSE("GPL v2");
 348MODULE_ALIAS("spi:el15203000");
 349