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
  12/*
  13 *  CR0014114 SPI protocol descrtiption:
  14 *  +----+-----------------------------------+----+
  15 *  | CMD|             BRIGHTNESS            |CRC |
  16 *  +----+-----------------------------------+----+
  17 *  |    | LED0| LED1| LED2| LED3| LED4| LED5|    |
  18 *  |    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |
  19 *  |    |R|G|B|R|G|B|R|G|B|R|G|B|R|G|B|R|G|B|    |
  20 *  | 1  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1  |
  21 *  |    |1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|    |
  22 *  |    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |
  23 *  |    |               18                  |    |
  24 *  +----+-----------------------------------+----+
  25 *  |                    20                       |
  26 *  +---------------------------------------------+
  27 *
  28 *  PS: Boards can be connected to the chain:
  29 *      SPI -> board0 -> board1 -> board2 ..
  30 */
  31
  32/* CR0014114 SPI commands */
  33#define CR_SET_BRIGHTNESS       0x80
  34#define CR_INIT_REENUMERATE     0x81
  35#define CR_NEXT_REENUMERATE     0x82
  36
  37/* CR0014114 default settings */
  38#define CR_MAX_BRIGHTNESS       GENMASK(6, 0)
  39#define CR_FW_DELAY_MSEC        10
  40#define CR_RECOUNT_DELAY        (HZ * 3600)
  41
  42#define CR_DEV_NAME             "cr0014114"
  43
  44struct cr0014114_led {
  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 to %d\n", brightness);
 171
 172        mutex_lock(&led->priv->lock);
 173        led->brightness = (u8)brightness;
 174        ret = cr0014114_sync(led->priv);
 175        mutex_unlock(&led->priv->lock);
 176
 177        return ret;
 178}
 179
 180static int cr0014114_probe_dt(struct cr0014114 *priv)
 181{
 182        size_t                  i = 0;
 183        struct cr0014114_led    *led;
 184        struct fwnode_handle    *child;
 185        struct led_init_data    init_data = {};
 186        int                     ret;
 187
 188        device_for_each_child_node(priv->dev, child) {
 189                led = &priv->leds[i];
 190
 191                led->priv                         = priv;
 192                led->ldev.max_brightness          = CR_MAX_BRIGHTNESS;
 193                led->ldev.brightness_set_blocking = cr0014114_set_sync;
 194
 195                init_data.fwnode = child;
 196                init_data.devicename = CR_DEV_NAME;
 197                init_data.default_label = ":";
 198
 199                ret = devm_led_classdev_register_ext(priv->dev, &led->ldev,
 200                                                     &init_data);
 201                if (ret) {
 202                        dev_err(priv->dev,
 203                                "failed to register LED device, err %d", ret);
 204                        fwnode_handle_put(child);
 205                        return ret;
 206                }
 207
 208                i++;
 209        }
 210
 211        return 0;
 212}
 213
 214static int cr0014114_probe(struct spi_device *spi)
 215{
 216        struct cr0014114        *priv;
 217        size_t                  count;
 218        int                     ret;
 219
 220        count = device_get_child_node_count(&spi->dev);
 221        if (!count) {
 222                dev_err(&spi->dev, "LEDs are not defined in device tree!");
 223                return -ENODEV;
 224        }
 225
 226        priv = devm_kzalloc(&spi->dev, struct_size(priv, leds, count),
 227                            GFP_KERNEL);
 228        if (!priv)
 229                return -ENOMEM;
 230
 231        priv->buf = devm_kzalloc(&spi->dev, count + 2, GFP_KERNEL);
 232        if (!priv->buf)
 233                return -ENOMEM;
 234
 235        mutex_init(&priv->lock);
 236        INIT_DELAYED_WORK(&priv->work, cr0014114_recount_work);
 237        priv->count     = count;
 238        priv->dev       = &spi->dev;
 239        priv->spi       = spi;
 240        priv->delay     = jiffies -
 241                          msecs_to_jiffies(CR_FW_DELAY_MSEC);
 242
 243        priv->do_recount = true;
 244        ret = cr0014114_sync(priv);
 245        if (ret) {
 246                dev_err(priv->dev, "first recount failed %d\n", ret);
 247                return ret;
 248        }
 249
 250        priv->do_recount = true;
 251        ret = cr0014114_sync(priv);
 252        if (ret) {
 253                dev_err(priv->dev, "second recount failed %d\n", ret);
 254                return ret;
 255        }
 256
 257        ret = cr0014114_probe_dt(priv);
 258        if (ret)
 259                return ret;
 260
 261        /* setup recount work to workaround buggy firmware */
 262        schedule_delayed_work(&priv->work, CR_RECOUNT_DELAY);
 263
 264        spi_set_drvdata(spi, priv);
 265
 266        return 0;
 267}
 268
 269static int cr0014114_remove(struct spi_device *spi)
 270{
 271        struct cr0014114 *priv = spi_get_drvdata(spi);
 272
 273        cancel_delayed_work_sync(&priv->work);
 274        mutex_destroy(&priv->lock);
 275
 276        return 0;
 277}
 278
 279static const struct of_device_id cr0014114_dt_ids[] = {
 280        { .compatible = "crane,cr0014114", },
 281        {},
 282};
 283
 284MODULE_DEVICE_TABLE(of, cr0014114_dt_ids);
 285
 286static struct spi_driver cr0014114_driver = {
 287        .probe          = cr0014114_probe,
 288        .remove         = cr0014114_remove,
 289        .driver = {
 290                .name           = KBUILD_MODNAME,
 291                .of_match_table = cr0014114_dt_ids,
 292        },
 293};
 294
 295module_spi_driver(cr0014114_driver);
 296
 297MODULE_AUTHOR("Oleh Kravchenko <oleg@kaa.org.ua>");
 298MODULE_DESCRIPTION("cr0014114 LED driver");
 299MODULE_LICENSE("GPL v2");
 300MODULE_ALIAS("spi:cr0014114");
 301