0001
0002
0003
0004
0005
0006 #include <linux/acpi.h>
0007 #include <linux/leds.h>
0008 #include <linux/module.h>
0009 #include <linux/platform_device.h>
0010 #include <linux/spinlock.h>
0011
0012 #define NIC78BX_USER1_LED_MASK 0x3
0013 #define NIC78BX_USER1_GREEN_LED BIT(0)
0014 #define NIC78BX_USER1_YELLOW_LED BIT(1)
0015
0016 #define NIC78BX_USER2_LED_MASK 0xC
0017 #define NIC78BX_USER2_GREEN_LED BIT(2)
0018 #define NIC78BX_USER2_YELLOW_LED BIT(3)
0019
0020 #define NIC78BX_LOCK_REG_OFFSET 1
0021 #define NIC78BX_LOCK_VALUE 0xA5
0022 #define NIC78BX_UNLOCK_VALUE 0x5A
0023
0024 #define NIC78BX_USER_LED_IO_SIZE 2
0025
0026 struct nic78bx_led_data {
0027 u16 io_base;
0028 spinlock_t lock;
0029 struct platform_device *pdev;
0030 };
0031
0032 struct nic78bx_led {
0033 u8 bit;
0034 u8 mask;
0035 struct nic78bx_led_data *data;
0036 struct led_classdev cdev;
0037 };
0038
0039 static inline struct nic78bx_led *to_nic78bx_led(struct led_classdev *cdev)
0040 {
0041 return container_of(cdev, struct nic78bx_led, cdev);
0042 }
0043
0044 static void nic78bx_brightness_set(struct led_classdev *cdev,
0045 enum led_brightness brightness)
0046 {
0047 struct nic78bx_led *nled = to_nic78bx_led(cdev);
0048 unsigned long flags;
0049 u8 value;
0050
0051 spin_lock_irqsave(&nled->data->lock, flags);
0052 value = inb(nled->data->io_base);
0053
0054 if (brightness) {
0055 value &= ~nled->mask;
0056 value |= nled->bit;
0057 } else {
0058 value &= ~nled->bit;
0059 }
0060
0061 outb(value, nled->data->io_base);
0062 spin_unlock_irqrestore(&nled->data->lock, flags);
0063 }
0064
0065 static enum led_brightness nic78bx_brightness_get(struct led_classdev *cdev)
0066 {
0067 struct nic78bx_led *nled = to_nic78bx_led(cdev);
0068 unsigned long flags;
0069 u8 value;
0070
0071 spin_lock_irqsave(&nled->data->lock, flags);
0072 value = inb(nled->data->io_base);
0073 spin_unlock_irqrestore(&nled->data->lock, flags);
0074
0075 return (value & nled->bit) ? 1 : LED_OFF;
0076 }
0077
0078 static struct nic78bx_led nic78bx_leds[] = {
0079 {
0080 .bit = NIC78BX_USER1_GREEN_LED,
0081 .mask = NIC78BX_USER1_LED_MASK,
0082 .cdev = {
0083 .name = "nilrt:green:user1",
0084 .max_brightness = 1,
0085 .brightness_set = nic78bx_brightness_set,
0086 .brightness_get = nic78bx_brightness_get,
0087 }
0088 },
0089 {
0090 .bit = NIC78BX_USER1_YELLOW_LED,
0091 .mask = NIC78BX_USER1_LED_MASK,
0092 .cdev = {
0093 .name = "nilrt:yellow:user1",
0094 .max_brightness = 1,
0095 .brightness_set = nic78bx_brightness_set,
0096 .brightness_get = nic78bx_brightness_get,
0097 }
0098 },
0099 {
0100 .bit = NIC78BX_USER2_GREEN_LED,
0101 .mask = NIC78BX_USER2_LED_MASK,
0102 .cdev = {
0103 .name = "nilrt:green:user2",
0104 .max_brightness = 1,
0105 .brightness_set = nic78bx_brightness_set,
0106 .brightness_get = nic78bx_brightness_get,
0107 }
0108 },
0109 {
0110 .bit = NIC78BX_USER2_YELLOW_LED,
0111 .mask = NIC78BX_USER2_LED_MASK,
0112 .cdev = {
0113 .name = "nilrt:yellow:user2",
0114 .max_brightness = 1,
0115 .brightness_set = nic78bx_brightness_set,
0116 .brightness_get = nic78bx_brightness_get,
0117 }
0118 }
0119 };
0120
0121 static int nic78bx_probe(struct platform_device *pdev)
0122 {
0123 struct device *dev = &pdev->dev;
0124 struct nic78bx_led_data *led_data;
0125 struct resource *io_rc;
0126 int ret, i;
0127
0128 led_data = devm_kzalloc(dev, sizeof(*led_data), GFP_KERNEL);
0129 if (!led_data)
0130 return -ENOMEM;
0131
0132 led_data->pdev = pdev;
0133 platform_set_drvdata(pdev, led_data);
0134
0135 io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0);
0136 if (!io_rc) {
0137 dev_err(dev, "missing IO resources\n");
0138 return -EINVAL;
0139 }
0140
0141 if (resource_size(io_rc) < NIC78BX_USER_LED_IO_SIZE) {
0142 dev_err(dev, "IO region too small\n");
0143 return -EINVAL;
0144 }
0145
0146 if (!devm_request_region(dev, io_rc->start, resource_size(io_rc),
0147 KBUILD_MODNAME)) {
0148 dev_err(dev, "failed to get IO region\n");
0149 return -EBUSY;
0150 }
0151
0152 led_data->io_base = io_rc->start;
0153 spin_lock_init(&led_data->lock);
0154
0155 for (i = 0; i < ARRAY_SIZE(nic78bx_leds); i++) {
0156 nic78bx_leds[i].data = led_data;
0157
0158 ret = devm_led_classdev_register(dev, &nic78bx_leds[i].cdev);
0159 if (ret)
0160 return ret;
0161 }
0162
0163
0164 outb(NIC78BX_UNLOCK_VALUE,
0165 led_data->io_base + NIC78BX_LOCK_REG_OFFSET);
0166
0167 return ret;
0168 }
0169
0170 static int nic78bx_remove(struct platform_device *pdev)
0171 {
0172 struct nic78bx_led_data *led_data = platform_get_drvdata(pdev);
0173
0174
0175 outb(NIC78BX_LOCK_VALUE,
0176 led_data->io_base + NIC78BX_LOCK_REG_OFFSET);
0177
0178 return 0;
0179 }
0180
0181 static const struct acpi_device_id led_device_ids[] = {
0182 {"NIC78B3", 0},
0183 {"", 0},
0184 };
0185 MODULE_DEVICE_TABLE(acpi, led_device_ids);
0186
0187 static struct platform_driver led_driver = {
0188 .probe = nic78bx_probe,
0189 .remove = nic78bx_remove,
0190 .driver = {
0191 .name = KBUILD_MODNAME,
0192 .acpi_match_table = ACPI_PTR(led_device_ids),
0193 },
0194 };
0195
0196 module_platform_driver(led_driver);
0197
0198 MODULE_DESCRIPTION("National Instruments PXI User LEDs driver");
0199 MODULE_AUTHOR("Hui Chun Ong <hui.chun.ong@ni.com>");
0200 MODULE_LICENSE("GPL");