root/drivers/leds/leds-spi-byte.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. spi_byte_brightness_set_blocking
  2. spi_byte_probe
  3. spi_byte_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 // Copyright (c) 2019 Christian Mauderer <oss@c-mauderer.de>
   3 
   4 /*
   5  * The driver supports controllers with a very simple SPI protocol:
   6  * - one LED is controlled by a single byte on MOSI
   7  * - the value of the byte gives the brightness between two values (lowest to
   8  *   highest)
   9  * - no return value is necessary (no MISO signal)
  10  *
  11  * The value for minimum and maximum brightness depends on the device
  12  * (compatible string).
  13  *
  14  * Supported devices:
  15  * - "ubnt,acb-spi-led": Microcontroller (SONiX 8F26E611LA) based device used
  16  *   for example in Ubiquiti airCube ISP. Reverse engineered protocol for this
  17  *   controller:
  18  *   * Higher two bits set a mode. Lower six bits are a parameter.
  19  *   * Mode: 00 -> set brightness between 0x00 (min) and 0x3F (max)
  20  *   * Mode: 01 -> pulsing pattern (min -> max -> min) with an interval. From
  21  *     some tests, the period is about (50ms + 102ms * parameter). There is a
  22  *     slightly different pattern starting from 0x10 (longer gap between the
  23  *     pulses) but the time still follows that calculation.
  24  *   * Mode: 10 -> same as 01 but with only a ramp from min to max. Again a
  25  *     slight jump in the pattern at 0x10.
  26  *   * Mode: 11 -> blinking (off -> 25% -> off -> 25% -> ...) with a period of
  27  *     (105ms * parameter)
  28  *   NOTE: This driver currently only supports mode 00.
  29  */
  30 
  31 #include <linux/leds.h>
  32 #include <linux/module.h>
  33 #include <linux/of_device.h>
  34 #include <linux/spi/spi.h>
  35 #include <linux/mutex.h>
  36 #include <uapi/linux/uleds.h>
  37 
  38 struct spi_byte_chipdef {
  39         /* SPI byte that will be send to switch the LED off */
  40         u8      off_value;
  41         /* SPI byte that will be send to switch the LED to maximum brightness */
  42         u8      max_value;
  43 };
  44 
  45 struct spi_byte_led {
  46         struct led_classdev             ldev;
  47         struct spi_device               *spi;
  48         char                            name[LED_MAX_NAME_SIZE];
  49         struct mutex                    mutex;
  50         const struct spi_byte_chipdef   *cdef;
  51 };
  52 
  53 static const struct spi_byte_chipdef ubnt_acb_spi_led_cdef = {
  54         .off_value = 0x0,
  55         .max_value = 0x3F,
  56 };
  57 
  58 static const struct of_device_id spi_byte_dt_ids[] = {
  59         { .compatible = "ubnt,acb-spi-led", .data = &ubnt_acb_spi_led_cdef },
  60         {},
  61 };
  62 
  63 MODULE_DEVICE_TABLE(of, spi_byte_dt_ids);
  64 
  65 static int spi_byte_brightness_set_blocking(struct led_classdev *dev,
  66                                             enum led_brightness brightness)
  67 {
  68         struct spi_byte_led *led = container_of(dev, struct spi_byte_led, ldev);
  69         u8 value;
  70         int ret;
  71 
  72         value = (u8) brightness + led->cdef->off_value;
  73 
  74         mutex_lock(&led->mutex);
  75         ret = spi_write(led->spi, &value, sizeof(value));
  76         mutex_unlock(&led->mutex);
  77 
  78         return ret;
  79 }
  80 
  81 static int spi_byte_probe(struct spi_device *spi)
  82 {
  83         const struct of_device_id *of_dev_id;
  84         struct device_node *child;
  85         struct device *dev = &spi->dev;
  86         struct spi_byte_led *led;
  87         const char *name = "leds-spi-byte::";
  88         const char *state;
  89         int ret;
  90 
  91         of_dev_id = of_match_device(spi_byte_dt_ids, dev);
  92         if (!of_dev_id)
  93                 return -EINVAL;
  94 
  95         if (of_get_child_count(dev->of_node) != 1) {
  96                 dev_err(dev, "Device must have exactly one LED sub-node.");
  97                 return -EINVAL;
  98         }
  99         child = of_get_next_child(dev->of_node, NULL);
 100 
 101         led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
 102         if (!led)
 103                 return -ENOMEM;
 104 
 105         of_property_read_string(child, "label", &name);
 106         strlcpy(led->name, name, sizeof(led->name));
 107         led->spi = spi;
 108         mutex_init(&led->mutex);
 109         led->cdef = of_dev_id->data;
 110         led->ldev.name = led->name;
 111         led->ldev.brightness = LED_OFF;
 112         led->ldev.max_brightness = led->cdef->max_value - led->cdef->off_value;
 113         led->ldev.brightness_set_blocking = spi_byte_brightness_set_blocking;
 114 
 115         state = of_get_property(child, "default-state", NULL);
 116         if (state) {
 117                 if (!strcmp(state, "on")) {
 118                         led->ldev.brightness = led->ldev.max_brightness;
 119                 } else if (strcmp(state, "off")) {
 120                         /* all other cases except "off" */
 121                         dev_err(dev, "default-state can only be 'on' or 'off'");
 122                         return -EINVAL;
 123                 }
 124         }
 125         spi_byte_brightness_set_blocking(&led->ldev,
 126                                          led->ldev.brightness);
 127 
 128         ret = devm_led_classdev_register(&spi->dev, &led->ldev);
 129         if (ret) {
 130                 mutex_destroy(&led->mutex);
 131                 return ret;
 132         }
 133         spi_set_drvdata(spi, led);
 134 
 135         return 0;
 136 }
 137 
 138 static int spi_byte_remove(struct spi_device *spi)
 139 {
 140         struct spi_byte_led     *led = spi_get_drvdata(spi);
 141 
 142         mutex_destroy(&led->mutex);
 143 
 144         return 0;
 145 }
 146 
 147 static struct spi_driver spi_byte_driver = {
 148         .probe          = spi_byte_probe,
 149         .remove         = spi_byte_remove,
 150         .driver = {
 151                 .name           = KBUILD_MODNAME,
 152                 .of_match_table = spi_byte_dt_ids,
 153         },
 154 };
 155 
 156 module_spi_driver(spi_byte_driver);
 157 
 158 MODULE_AUTHOR("Christian Mauderer <oss@c-mauderer.de>");
 159 MODULE_DESCRIPTION("single byte SPI LED driver");
 160 MODULE_LICENSE("GPL v2");
 161 MODULE_ALIAS("spi:leds-spi-byte");

/* [<][>][^][v][top][bottom][index][help] */