Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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