Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2016 National Instruments Corp.
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     /* Unlock LED register */
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     /* Lock LED register */
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");