brintos

brintos / linux-shallow public Read only

0
0
Text · 4.5 KiB · 282d9e4 Raw
202 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2016 National Instruments Corp.4 */5 6#include <linux/acpi.h>7#include <linux/leds.h>8#include <linux/module.h>9#include <linux/platform_device.h>10#include <linux/spinlock.h>11 12#define NIC78BX_USER1_LED_MASK		0x313#define NIC78BX_USER1_GREEN_LED		BIT(0)14#define NIC78BX_USER1_YELLOW_LED	BIT(1)15 16#define NIC78BX_USER2_LED_MASK		0xC17#define NIC78BX_USER2_GREEN_LED		BIT(2)18#define NIC78BX_USER2_YELLOW_LED	BIT(3)19 20#define NIC78BX_LOCK_REG_OFFSET		121#define NIC78BX_LOCK_VALUE		0xA522#define NIC78BX_UNLOCK_VALUE		0x5A23 24#define NIC78BX_USER_LED_IO_SIZE	225 26struct nic78bx_led_data {27	u16 io_base;28	spinlock_t lock;29	struct platform_device *pdev;30};31 32struct nic78bx_led {33	u8 bit;34	u8 mask;35	struct nic78bx_led_data *data;36	struct led_classdev cdev;37};38 39static inline struct nic78bx_led *to_nic78bx_led(struct led_classdev *cdev)40{41	return container_of(cdev, struct nic78bx_led, cdev);42}43 44static void nic78bx_brightness_set(struct led_classdev *cdev,45				  enum led_brightness brightness)46{47	struct nic78bx_led *nled = to_nic78bx_led(cdev);48	unsigned long flags;49	u8 value;50 51	spin_lock_irqsave(&nled->data->lock, flags);52	value = inb(nled->data->io_base);53 54	if (brightness) {55		value &= ~nled->mask;56		value |= nled->bit;57	} else {58		value &= ~nled->bit;59	}60 61	outb(value, nled->data->io_base);62	spin_unlock_irqrestore(&nled->data->lock, flags);63}64 65static enum led_brightness nic78bx_brightness_get(struct led_classdev *cdev)66{67	struct nic78bx_led *nled = to_nic78bx_led(cdev);68	unsigned long flags;69	u8 value;70 71	spin_lock_irqsave(&nled->data->lock, flags);72	value = inb(nled->data->io_base);73	spin_unlock_irqrestore(&nled->data->lock, flags);74 75	return (value & nled->bit) ? 1 : LED_OFF;76}77 78static struct nic78bx_led nic78bx_leds[] = {79	{80		.bit = NIC78BX_USER1_GREEN_LED,81		.mask = NIC78BX_USER1_LED_MASK,82		.cdev = {83			.name = "nilrt:green:user1",84			.max_brightness = 1,85			.brightness_set = nic78bx_brightness_set,86			.brightness_get = nic78bx_brightness_get,87		}88	},89	{90		.bit = NIC78BX_USER1_YELLOW_LED,91		.mask = NIC78BX_USER1_LED_MASK,92		.cdev = {93			.name = "nilrt:yellow:user1",94			.max_brightness = 1,95			.brightness_set = nic78bx_brightness_set,96			.brightness_get = nic78bx_brightness_get,97		}98	},99	{100		.bit = NIC78BX_USER2_GREEN_LED,101		.mask = NIC78BX_USER2_LED_MASK,102		.cdev = {103			.name = "nilrt:green:user2",104			.max_brightness = 1,105			.brightness_set = nic78bx_brightness_set,106			.brightness_get = nic78bx_brightness_get,107		}108	},109	{110		.bit = NIC78BX_USER2_YELLOW_LED,111		.mask = NIC78BX_USER2_LED_MASK,112		.cdev = {113			.name = "nilrt:yellow:user2",114			.max_brightness = 1,115			.brightness_set = nic78bx_brightness_set,116			.brightness_get = nic78bx_brightness_get,117		}118	}119};120 121static void lock_led_reg_action(void *data)122{123	struct nic78bx_led_data *led_data = data;124 125	/* Lock LED register */126	outb(NIC78BX_LOCK_VALUE,127	     led_data->io_base + NIC78BX_LOCK_REG_OFFSET);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	ret = devm_add_action(dev, lock_led_reg_action, led_data);165	if (ret)166		return ret;167 168	for (i = 0; i < ARRAY_SIZE(nic78bx_leds); i++) {169		nic78bx_leds[i].data = led_data;170 171		ret = devm_led_classdev_register(dev, &nic78bx_leds[i].cdev);172		if (ret)173			return ret;174	}175 176	/* Unlock LED register */177	outb(NIC78BX_UNLOCK_VALUE,178	     led_data->io_base + NIC78BX_LOCK_REG_OFFSET);179 180	return ret;181}182 183static const struct acpi_device_id led_device_ids[] = {184	{"NIC78B3", 0},185	{"", 0},186};187MODULE_DEVICE_TABLE(acpi, led_device_ids);188 189static struct platform_driver led_driver = {190	.probe = nic78bx_probe,191	.driver = {192		.name = KBUILD_MODNAME,193		.acpi_match_table = ACPI_PTR(led_device_ids),194	},195};196 197module_platform_driver(led_driver);198 199MODULE_DESCRIPTION("National Instruments PXI User LEDs driver");200MODULE_AUTHOR("Hui Chun Ong <hui.chun.ong@ni.com>");201MODULE_LICENSE("GPL");202