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                fwnode_property_read_string(child, "linux,default-trigger",
 192                                            &led->ldev.default_trigger);
 193
 194                led->priv                         = priv;
 195                led->ldev.max_brightness          = CR_MAX_BRIGHTNESS;
 196                led->ldev.brightness_set_blocking = cr0014114_set_sync;
 197
 198                init_data.fwnode = child;
 199                init_data.devicename = CR_DEV_NAME;
 200                init_data.default_label = ":";
 201
 202                ret = devm_led_classdev_register_ext(priv->dev, &led->ldev,
 203                                                     &init_data);
 204                if (ret) {
 205                        dev_err(priv->dev,
 206                                "failed to register LED device, err %d", ret);
 207                        fwnode_handle_put(child);
 208                        return ret;
 209                }
 210
 211                i++;
 212        }
 213
 214        return 0;
 215}
 216
 217static int cr0014114_probe(struct spi_device *spi)
 218{
 219        struct cr0014114        *priv;
 220        size_t                  count;
 221        int                     ret;
 222
 223        count = device_get_child_node_count(&spi->dev);
 224        if (!count) {
 225                dev_err(&spi->dev, "LEDs are not defined in device tree!");
 226                return -ENODEV;
 227        }
 228
 229        priv = devm_kzalloc(&spi->dev, struct_size(priv, leds, count),
 230                            GFP_KERNEL);
 231        if (!priv)
 232                return -ENOMEM;
 233
 234        priv->buf = devm_kzalloc(&spi->dev, count + 2, GFP_KERNEL);
 235        if (!priv->buf)
 236                return -ENOMEM;
 237
 238        mutex_init(&priv->lock);
 239        INIT_DELAYED_WORK(&priv->work, cr0014114_recount_work);
 240        priv->count     = count;
 241        priv->dev       = &spi->dev;
 242        priv->spi       = spi;
 243        priv->delay     = jiffies -
 244                          msecs_to_jiffies(CR_FW_DELAY_MSEC);
 245
 246        priv->do_recount = true;
 247        ret = cr0014114_sync(priv);
 248        if (ret) {
 249                dev_err(priv->dev, "first recount failed %d\n", ret);
 250                return ret;
 251        }
 252
 253        priv->do_recount = true;
 254        ret = cr0014114_sync(priv);
 255        if (ret) {
 256                dev_err(priv->dev, "second recount failed %d\n", ret);
 257                return ret;
 258        }
 259
 260        ret = cr0014114_probe_dt(priv);
 261        if (ret)
 262                return ret;
 263
 264        /* setup recount work to workaround buggy firmware */
 265        schedule_delayed_work(&priv->work, CR_RECOUNT_DELAY);
 266
 267        spi_set_drvdata(spi, priv);
 268
 269        return 0;
 270}
 271
 272static int cr0014114_remove(struct spi_device *spi)
 273{
 274        struct cr0014114 *priv = spi_get_drvdata(spi);
 275
 276        cancel_delayed_work_sync(&priv->work);
 277        mutex_destroy(&priv->lock);
 278
 279        return 0;
 280}
 281
 282static const struct of_device_id cr0014114_dt_ids[] = {
 283        { .compatible = "crane,cr0014114", },
 284        {},
 285};
 286
 287MODULE_DEVICE_TABLE(of, cr0014114_dt_ids);
 288
 289static struct spi_driver cr0014114_driver = {
 290        .probe          = cr0014114_probe,
 291        .remove         = cr0014114_remove,
 292        .driver = {
 293                .name           = KBUILD_MODNAME,
 294                .of_match_table = cr0014114_dt_ids,
 295        },
 296};
 297
 298module_spi_driver(cr0014114_driver);
 299
 300MODULE_AUTHOR("Oleh Kravchenko <oleg@kaa.org.ua>");
 301MODULE_DESCRIPTION("cr0014114 LED driver");
 302MODULE_LICENSE("GPL v2");
 303MODULE_ALIAS("spi:cr0014114");
 304