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