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