linux/drivers/leds/leds-cr0014114.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (c) 2018 Crane Merchandising Systems. All rights reserved.
   3// Copyright (C) 2018 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#include <linux/workqueue.h>
  11#include <uapi/linux/uleds.h>
  12
  13/*
  14 *  CR0014114 SPI protocol descrtiption:
  15 *  +----+-----------------------------------+----+
  16 *  | CMD|             BRIGHTNESS            |CRC |
  17 *  +----+-----------------------------------+----+
  18 *  |    | LED0| LED1| LED2| LED3| LED4| LED5|    |
  19 *  |    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |
  20 *  |    |R|G|B|R|G|B|R|G|B|R|G|B|R|G|B|R|G|B|    |
  21 *  | 1  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1  |
  22 *  |    |1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|    |
  23 *  |    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |
  24 *  |    |               18                  |    |
  25 *  +----+-----------------------------------+----+
  26 *  |                    20                       |
  27 *  +---------------------------------------------+
  28 *
  29 *  PS: Boards can be connected to the chain:
  30 *      SPI -> board0 -> board1 -> board2 ..
  31 */
  32
  33/* CR0014114 SPI commands */
  34#define CR_SET_BRIGHTNESS       0x80
  35#define CR_INIT_REENUMERATE     0x81
  36#define CR_NEXT_REENUMERATE     0x82
  37
  38/* CR0014114 default settings */
  39#define CR_MAX_BRIGHTNESS       GENMASK(6, 0)
  40#define CR_FW_DELAY_MSEC        10
  41#define CR_RECOUNT_DELAY        (HZ * 3600)
  42
  43struct cr0014114_led {
  44        char                    name[LED_MAX_NAME_SIZE];
  45        struct cr0014114        *priv;
  46        struct led_classdev     ldev;
  47        u8                      brightness;
  48};
  49
  50struct cr0014114 {
  51        bool                    do_recount;
  52        size_t                  count;
  53        struct delayed_work     work;
  54        struct device           *dev;
  55        struct mutex            lock;
  56        struct spi_device       *spi;
  57        u8                      *buf;
  58        unsigned long           delay;
  59        struct cr0014114_led    leds[];
  60};
  61
  62static void cr0014114_calc_crc(u8 *buf, const size_t len)
  63{
  64        size_t  i;
  65        u8      crc;
  66
  67        for (i = 1, crc = 1; i < len - 1; i++)
  68                crc += buf[i];
  69        crc |= BIT(7);
  70
  71        /* special case when CRC matches the SPI commands */
  72        if (crc == CR_SET_BRIGHTNESS ||
  73            crc == CR_INIT_REENUMERATE ||
  74            crc == CR_NEXT_REENUMERATE)
  75                crc = 0xfe;
  76
  77        buf[len - 1] = crc;
  78}
  79
  80static int cr0014114_recount(struct cr0014114 *priv)
  81{
  82        int     ret;
  83        size_t  i;
  84        u8      cmd;
  85
  86        dev_dbg(priv->dev, "LEDs recount is started\n");
  87
  88        cmd = CR_INIT_REENUMERATE;
  89        ret = spi_write(priv->spi, &cmd, sizeof(cmd));
  90        if (ret)
  91                goto err;
  92
  93        cmd = CR_NEXT_REENUMERATE;
  94        for (i = 0; i < priv->count; i++) {
  95                msleep(CR_FW_DELAY_MSEC);
  96
  97                ret = spi_write(priv->spi, &cmd, sizeof(cmd));
  98                if (ret)
  99                        goto err;
 100        }
 101
 102err:
 103        dev_dbg(priv->dev, "LEDs recount is finished\n");
 104
 105        if (ret)
 106                dev_err(priv->dev, "with error %d", ret);
 107
 108        return ret;
 109}
 110
 111static int cr0014114_sync(struct cr0014114 *priv)
 112{
 113        int             ret;
 114        size_t          i;
 115        unsigned long   udelay, now = jiffies;
 116
 117        /* to avoid SPI mistiming with firmware we should wait some time */
 118        if (time_after(priv->delay, now)) {
 119                udelay = jiffies_to_usecs(priv->delay - now);
 120                usleep_range(udelay, udelay + 1);
 121        }
 122
 123        if (unlikely(priv->do_recount)) {
 124                ret = cr0014114_recount(priv);
 125                if (ret)
 126                        goto err;
 127
 128                priv->do_recount = false;
 129                msleep(CR_FW_DELAY_MSEC);
 130        }
 131
 132        priv->buf[0] = CR_SET_BRIGHTNESS;
 133        for (i = 0; i < priv->count; i++)
 134                priv->buf[i + 1] = priv->leds[i].brightness;
 135        cr0014114_calc_crc(priv->buf, priv->count + 2);
 136        ret = spi_write(priv->spi, priv->buf, priv->count + 2);
 137
 138err:
 139        priv->delay = jiffies + msecs_to_jiffies(CR_FW_DELAY_MSEC);
 140
 141        return ret;
 142}
 143
 144static void cr0014114_recount_work(struct work_struct *work)
 145{
 146        int                     ret;
 147        struct cr0014114        *priv = container_of(work,
 148                                                     struct cr0014114,
 149                                                     work.work);
 150
 151        mutex_lock(&priv->lock);
 152        priv->do_recount = true;
 153        ret = cr0014114_sync(priv);
 154        mutex_unlock(&priv->lock);
 155
 156        if (ret)
 157                dev_warn(priv->dev, "sync of LEDs failed %d\n", ret);
 158
 159        schedule_delayed_work(&priv->work, CR_RECOUNT_DELAY);
 160}
 161
 162static int cr0014114_set_sync(struct led_classdev *ldev,
 163                              enum led_brightness brightness)
 164{
 165        int                     ret;
 166        struct cr0014114_led    *led = container_of(ldev,
 167                                                    struct cr0014114_led,
 168                                                    ldev);
 169
 170        dev_dbg(led->priv->dev, "Set brightness of %s to %d\n",
 171                led->name, brightness);
 172
 173        mutex_lock(&led->priv->lock);
 174        led->brightness = (u8)brightness;
 175        ret = cr0014114_sync(led->priv);
 176        mutex_unlock(&led->priv->lock);
 177
 178        return ret;
 179}
 180
 181static int cr0014114_probe_dt(struct cr0014114 *priv)
 182{
 183        size_t                  i = 0;
 184        struct cr0014114_led    *led;
 185        struct fwnode_handle    *child;
 186        struct device_node      *np;
 187        int                     ret;
 188        const char              *str;
 189
 190        device_for_each_child_node(priv->dev, child) {
 191                np = to_of_node(child);
 192                led = &priv->leds[i];
 193
 194                ret = fwnode_property_read_string(child, "label", &str);
 195                if (ret)
 196                        snprintf(led->name, sizeof(led->name),
 197                                 "cr0014114::");
 198                else
 199                        snprintf(led->name, sizeof(led->name),
 200                                 "cr0014114:%s", str);
 201
 202                fwnode_property_read_string(child, "linux,default-trigger",
 203                                            &led->ldev.default_trigger);
 204
 205                led->priv                         = priv;
 206                led->ldev.name                    = led->name;
 207                led->ldev.max_brightness          = CR_MAX_BRIGHTNESS;
 208                led->ldev.brightness_set_blocking = cr0014114_set_sync;
 209
 210                ret = devm_of_led_classdev_register(priv->dev, np,
 211                                                    &led->ldev);
 212                if (ret) {
 213                        dev_err(priv->dev,
 214                                "failed to register LED device %s, err %d",
 215                                led->name, ret);
 216                        fwnode_handle_put(child);
 217                        return ret;
 218                }
 219
 220                led->ldev.dev->of_node = np;
 221
 222                i++;
 223        }
 224
 225        return 0;
 226}
 227
 228static int cr0014114_probe(struct spi_device *spi)
 229{
 230        struct cr0014114        *priv;
 231        size_t                  count;
 232        int                     ret;
 233
 234        count = device_get_child_node_count(&spi->dev);
 235        if (!count) {
 236                dev_err(&spi->dev, "LEDs are not defined in device tree!");
 237                return -ENODEV;
 238        }
 239
 240        priv = devm_kzalloc(&spi->dev, struct_size(priv, leds, count),
 241                            GFP_KERNEL);
 242        if (!priv)
 243                return -ENOMEM;
 244
 245        priv->buf = devm_kzalloc(&spi->dev, count + 2, GFP_KERNEL);
 246        if (!priv->buf)
 247                return -ENOMEM;
 248
 249        mutex_init(&priv->lock);
 250        INIT_DELAYED_WORK(&priv->work, cr0014114_recount_work);
 251        priv->count     = count;
 252        priv->dev       = &spi->dev;
 253        priv->spi       = spi;
 254        priv->delay     = jiffies -
 255                          msecs_to_jiffies(CR_FW_DELAY_MSEC);
 256
 257        priv->do_recount = true;
 258        ret = cr0014114_sync(priv);
 259        if (ret) {
 260                dev_err(priv->dev, "first recount failed %d\n", ret);
 261                return ret;
 262        }
 263
 264        priv->do_recount = true;
 265        ret = cr0014114_sync(priv);
 266        if (ret) {
 267                dev_err(priv->dev, "second recount failed %d\n", ret);
 268                return ret;
 269        }
 270
 271        ret = cr0014114_probe_dt(priv);
 272        if (ret)
 273                return ret;
 274
 275        /* setup recount work to workaround buggy firmware */
 276        schedule_delayed_work(&priv->work, CR_RECOUNT_DELAY);
 277
 278        spi_set_drvdata(spi, priv);
 279
 280        return 0;
 281}
 282
 283static int cr0014114_remove(struct spi_device *spi)
 284{
 285        struct cr0014114 *priv = spi_get_drvdata(spi);
 286
 287        cancel_delayed_work_sync(&priv->work);
 288        mutex_destroy(&priv->lock);
 289
 290        return 0;
 291}
 292
 293static const struct of_device_id cr0014114_dt_ids[] = {
 294        { .compatible = "crane,cr0014114", },
 295        {},
 296};
 297
 298MODULE_DEVICE_TABLE(of, cr0014114_dt_ids);
 299
 300static struct spi_driver cr0014114_driver = {
 301        .probe          = cr0014114_probe,
 302        .remove         = cr0014114_remove,
 303        .driver = {
 304                .name           = KBUILD_MODNAME,
 305                .of_match_table = cr0014114_dt_ids,
 306        },
 307};
 308
 309module_spi_driver(cr0014114_driver);
 310
 311MODULE_AUTHOR("Oleh Kravchenko <oleg@kaa.org.ua>");
 312MODULE_DESCRIPTION("cr0014114 LED driver");
 313MODULE_LICENSE("GPL v2");
 314MODULE_ALIAS("spi:cr0014114");
 315