Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

leds: Add user LED driver for NIC78bx device

Add the driver to support User LEDs on PXI Embedded Controller.

Signed-off-by: Hui Chun Ong <hui.chun.ong@ni.com>
Signed-off-by: Brad Mouring <brad.mouring@ni.com>
Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>

authored by

Hui Chun Ong and committed by
Jacek Anaszewski
8338eab5 dca897e2

+221
+11
drivers/leds/Kconfig
··· 667 667 support in kernel. To compile this driver as a module, choose 'm' here: 668 668 the module will be called uleds. 669 669 670 + config LEDS_NIC78BX 671 + tristate "LED support for NI PXI NIC78bx devices" 672 + depends on LEDS_CLASS 673 + depends on X86 && ACPI 674 + help 675 + This option enables support for the User1 and User2 LEDs on NI 676 + PXI NIC78bx devices. 677 + 678 + To compile this driver as a module, choose M here: the module 679 + will be called leds-nic78bx. 680 + 670 681 comment "LED Triggers" 671 682 source "drivers/leds/trigger/Kconfig" 672 683
+1
drivers/leds/Makefile
··· 71 71 obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o 72 72 obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o 73 73 obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o 74 + obj-$(CONFIG_LEDS_NIC78BX) += leds-nic78bx.o 74 75 75 76 # LED SPI Drivers 76 77 obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
+209
drivers/leds/leds-nic78bx.c
··· 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 + 35 + struct nic78bx_led_data { 36 + u16 io_base; 37 + spinlock_t lock; 38 + struct platform_device *pdev; 39 + }; 40 + 41 + struct nic78bx_led { 42 + u8 bit; 43 + u8 mask; 44 + struct nic78bx_led_data *data; 45 + struct led_classdev cdev; 46 + }; 47 + 48 + static inline struct nic78bx_led *to_nic78bx_led(struct led_classdev *cdev) 49 + { 50 + return container_of(cdev, struct nic78bx_led, cdev); 51 + } 52 + 53 + static 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 + 74 + static 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 + 87 + static 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 + 130 + static 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 + 179 + static 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 + 190 + static const struct acpi_device_id led_device_ids[] = { 191 + {"NIC78B3", 0}, 192 + {"", 0}, 193 + }; 194 + MODULE_DEVICE_TABLE(acpi, led_device_ids); 195 + 196 + static 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 + 205 + module_platform_driver(led_driver); 206 + 207 + MODULE_DESCRIPTION("National Instruments PXI User LEDs driver"); 208 + MODULE_AUTHOR("Hui Chun Ong <hui.chun.ong@ni.com>"); 209 + MODULE_LICENSE("GPL");