File Index
Files and directories
Sign in
FILE

leds-nic78bx.c

File preview with syntax highlighting.

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