cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

leds-el15203000.c (8855B)


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