linux/drivers/leds/leds-nic78bx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2016 National Instruments Corp.
   4 */
   5
   6#include <linux/acpi.h>
   7#include <linux/leds.h>
   8#include <linux/module.h>
   9#include <linux/platform_device.h>
  10#include <linux/spinlock.h>
  11
  12#define NIC78BX_USER1_LED_MASK          0x3
  13#define NIC78BX_USER1_GREEN_LED         BIT(0)
  14#define NIC78BX_USER1_YELLOW_LED        BIT(1)
  15
  16#define NIC78BX_USER2_LED_MASK          0xC
  17#define NIC78BX_USER2_GREEN_LED         BIT(2)
  18#define NIC78BX_USER2_YELLOW_LED        BIT(3)
  19
  20#define NIC78BX_LOCK_REG_OFFSET         1
  21#define NIC78BX_LOCK_VALUE              0xA5
  22#define NIC78BX_UNLOCK_VALUE            0x5A
  23
  24#define NIC78BX_USER_LED_IO_SIZE        2
  25
  26struct nic78bx_led_data {
  27        u16 io_base;
  28        spinlock_t lock;
  29        struct platform_device *pdev;
  30};
  31
  32struct nic78bx_led {
  33        u8 bit;
  34        u8 mask;
  35        struct nic78bx_led_data *data;
  36        struct led_classdev cdev;
  37};
  38
  39static inline struct nic78bx_led *to_nic78bx_led(struct led_classdev *cdev)
  40{
  41        return container_of(cdev, struct nic78bx_led, cdev);
  42}
  43
  44static void nic78bx_brightness_set(struct led_classdev *cdev,
  45                                  enum led_brightness brightness)
  46{
  47        struct nic78bx_led *nled = to_nic78bx_led(cdev);
  48        unsigned long flags;
  49        u8 value;
  50
  51        spin_lock_irqsave(&nled->data->lock, flags);
  52        value = inb(nled->data->io_base);
  53
  54        if (brightness) {
  55                value &= ~nled->mask;
  56                value |= nled->bit;
  57        } else {
  58                value &= ~nled->bit;
  59        }
  60
  61        outb(value, nled->data->io_base);
  62        spin_unlock_irqrestore(&nled->data->lock, flags);
  63}
  64
  65static enum led_brightness nic78bx_brightness_get(struct led_classdev *cdev)
  66{
  67        struct nic78bx_led *nled = to_nic78bx_led(cdev);
  68        unsigned long flags;
  69        u8 value;
  70
  71        spin_lock_irqsave(&nled->data->lock, flags);
  72        value = inb(nled->data->io_base);
  73        spin_unlock_irqrestore(&nled->data->lock, flags);
  74
  75        return (value & nled->bit) ? 1 : LED_OFF;
  76}
  77
  78static struct nic78bx_led nic78bx_leds[] = {
  79        {
  80                .bit = NIC78BX_USER1_GREEN_LED,
  81                .mask = NIC78BX_USER1_LED_MASK,
  82                .cdev = {
  83                        .name = "nilrt:green:user1",
  84                        .max_brightness = 1,
  85                        .brightness_set = nic78bx_brightness_set,
  86                        .brightness_get = nic78bx_brightness_get,
  87                }
  88        },
  89        {
  90                .bit = NIC78BX_USER1_YELLOW_LED,
  91                .mask = NIC78BX_USER1_LED_MASK,
  92                .cdev = {
  93                        .name = "nilrt:yellow:user1",
  94                        .max_brightness = 1,
  95                        .brightness_set = nic78bx_brightness_set,
  96                        .brightness_get = nic78bx_brightness_get,
  97                }
  98        },
  99        {
 100                .bit = NIC78BX_USER2_GREEN_LED,
 101                .mask = NIC78BX_USER2_LED_MASK,
 102                .cdev = {
 103                        .name = "nilrt:green:user2",
 104                        .max_brightness = 1,
 105                        .brightness_set = nic78bx_brightness_set,
 106                        .brightness_get = nic78bx_brightness_get,
 107                }
 108        },
 109        {
 110                .bit = NIC78BX_USER2_YELLOW_LED,
 111                .mask = NIC78BX_USER2_LED_MASK,
 112                .cdev = {
 113                        .name = "nilrt:yellow:user2",
 114                        .max_brightness = 1,
 115                        .brightness_set = nic78bx_brightness_set,
 116                        .brightness_get = nic78bx_brightness_get,
 117                }
 118        }
 119};
 120
 121static int nic78bx_probe(struct platform_device *pdev)
 122{
 123        struct device *dev = &pdev->dev;
 124        struct nic78bx_led_data *led_data;
 125        struct resource *io_rc;
 126        int ret, i;
 127
 128        led_data = devm_kzalloc(dev, sizeof(*led_data), GFP_KERNEL);
 129        if (!led_data)
 130                return -ENOMEM;
 131
 132        led_data->pdev = pdev;
 133        platform_set_drvdata(pdev, led_data);
 134
 135        io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0);
 136        if (!io_rc) {
 137                dev_err(dev, "missing IO resources\n");
 138                return -EINVAL;
 139        }
 140
 141        if (resource_size(io_rc) < NIC78BX_USER_LED_IO_SIZE) {
 142                dev_err(dev, "IO region too small\n");
 143                return -EINVAL;
 144        }
 145
 146        if (!devm_request_region(dev, io_rc->start, resource_size(io_rc),
 147                                 KBUILD_MODNAME)) {
 148                dev_err(dev, "failed to get IO region\n");
 149                return -EBUSY;
 150        }
 151
 152        led_data->io_base = io_rc->start;
 153        spin_lock_init(&led_data->lock);
 154
 155        for (i = 0; i < ARRAY_SIZE(nic78bx_leds); i++) {
 156                nic78bx_leds[i].data = led_data;
 157
 158                ret = devm_led_classdev_register(dev, &nic78bx_leds[i].cdev);
 159                if (ret)
 160                        return ret;
 161        }
 162
 163        /* Unlock LED register */
 164        outb(NIC78BX_UNLOCK_VALUE,
 165             led_data->io_base + NIC78BX_LOCK_REG_OFFSET);
 166
 167        return ret;
 168}
 169
 170static int nic78bx_remove(struct platform_device *pdev)
 171{
 172        struct nic78bx_led_data *led_data = platform_get_drvdata(pdev);
 173
 174        /* Lock LED register */
 175        outb(NIC78BX_LOCK_VALUE,
 176             led_data->io_base + NIC78BX_LOCK_REG_OFFSET);
 177
 178        return 0;
 179}
 180
 181static const struct acpi_device_id led_device_ids[] = {
 182        {"NIC78B3", 0},
 183        {"", 0},
 184};
 185MODULE_DEVICE_TABLE(acpi, led_device_ids);
 186
 187static struct platform_driver led_driver = {
 188        .probe = nic78bx_probe,
 189        .remove = nic78bx_remove,
 190        .driver = {
 191                .name = KBUILD_MODNAME,
 192                .acpi_match_table = ACPI_PTR(led_device_ids),
 193        },
 194};
 195
 196module_platform_driver(led_driver);
 197
 198MODULE_DESCRIPTION("National Instruments PXI User LEDs driver");
 199MODULE_AUTHOR("Hui Chun Ong <hui.chun.ong@ni.com>");
 200MODULE_LICENSE("GPL");
 201