Re: [PATCH] leds: Add user LED driver for NIC78bx device

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Friday, October 21, 2016 04:06:04 PM Mika Westerberg wrote:
> On Fri, Oct 21, 2016 at 02:56:15PM +0200, Jacek Anaszewski wrote:
> > Hi Hui,
> > 
> > Thanks for the patch. I have few comments in the code below.
> > 
> > On 10/21/2016 08:33 AM, Hui Chun Ong wrote:
> > > Add the driver to support User LEDs on PXI Embedded Controller.
> > > 
> > > Signed-off-by: Hui Chun Ong <hui.chun.ong@xxxxxx>
> > > Signed-off-by: Brad Mouring <brad.mouring@xxxxxx>
> > > ---
> > >  drivers/leds/Kconfig       |  11 +++
> > >  drivers/leds/Makefile      |   1 +
> > >  drivers/leds/leds-ni78bx.c | 210 +++++++++++++++++++++++++++++++++++++++++++++
> > >  3 files changed, 222 insertions(+)
> > >  create mode 100644 drivers/leds/leds-ni78bx.c
> > > 
> > > diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> > > index 7a628c6..5540795 100644
> > > --- a/drivers/leds/Kconfig
> > > +++ b/drivers/leds/Kconfig
> > > @@ -659,6 +659,17 @@ config LEDS_MLXCPLD
> > >  	  This option enabled support for the LEDs on the Mellanox
> > >  	  boards. Say Y to enabled these.
> > > 
> > > +config LEDS_NI78BX
> > > +	tristate "LED support for NI PXI NIC78bx devices"
> > > +	depends on LEDS_CLASS
> > > +	depends on X86 && ACPI
> > > +	help
> > > +	  This option enables support for the User1 and User2 LEDs on NI
> > > +	  PXI NIC78bx devices.
> > > +
> > > +	  To compile this driver as a module, choose M here: the module
> > > +	  will be called leds-ni78bx.
> > > +
> > >  comment "LED Triggers"
> > >  source "drivers/leds/trigger/Kconfig"
> > > 
> > > diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> > > index 3965070..9758d1e 100644
> > > --- a/drivers/leds/Makefile
> > > +++ b/drivers/leds/Makefile
> > > @@ -71,6 +71,7 @@ obj-$(CONFIG_LEDS_IS31FL319X)		+= leds-is31fl319x.o
> > >  obj-$(CONFIG_LEDS_IS31FL32XX)		+= leds-is31fl32xx.o
> > >  obj-$(CONFIG_LEDS_PM8058)		+= leds-pm8058.o
> > >  obj-$(CONFIG_LEDS_MLXCPLD)		+= leds-mlxcpld.o
> > > +obj-$(CONFIG_LEDS_NI78BX)		+= leds-ni78bx.o
> > > 
> > >  # LED SPI Drivers
> > >  obj-$(CONFIG_LEDS_DAC124S085)		+= leds-dac124s085.o
> > > diff --git a/drivers/leds/leds-ni78bx.c b/drivers/leds/leds-ni78bx.c
> > > new file mode 100644
> > > index 0000000..3da4675
> > > --- /dev/null
> > > +++ b/drivers/leds/leds-ni78bx.c
> > > @@ -0,0 +1,210 @@
> > > +/*
> > > + * Copyright (C) 2016 National Instruments Corp.
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify
> > > + * it under the terms of the GNU General Public License as published by
> > > + * the Free Software Foundation; either version 2 of the License, or
> > > + * (at your option) any later version.
> > > + *
> > > + * This program is distributed in the hope that it will be useful,
> > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > > + * GNU General Public License for more details.
> > > + */
> > > +
> > > +#include <linux/acpi.h>
> > > +#include <linux/leds.h>
> > > +#include <linux/module.h>
> > > +#include <linux/mutex.h>
> > > +
> > > +#define USER1_LED_MASK		0x3
> > > +#define USER1_GREEN_LED		BIT(0)
> > > +#define USER1_YELLOW_LED	BIT(1)
> > > +
> > > +#define USER2_LED_MASK		0xC
> > > +#define USER2_GREEN_LED		BIT(2)
> > > +#define USER2_YELLOW_LED	BIT(3)
> > > +
> > > +#define LOCK_REG_OFFSET		1
> > > +#define LOCK_VALUE		0xA5
> > > +#define UNLOCK_VALUE		0x5A
> > 
> > Please add NIC78BX namespacing prefix to these macros.
> > 
> > > +
> > > +#define USER_LED_IO_SIZE	2
> > > +
> > > +static u16 io_base;
> > > +static DEFINE_MUTEX(led_lock);
> > 
> > Don't define static variables but instead create a struct that
> > will aggregate them and allocate it dynamically.
> > 
> > > +
> > > +struct ni78bx_led {
> > > +	u8 bit;
> > > +	u8 mask;
> > > +	struct led_classdev cdev;
> > > +};
> > > +
> > > +static inline struct ni78bx_led *to_ni78bx_led(struct led_classdev *cdev)
> > > +{
> > > +	return container_of(cdev, struct ni78bx_led, cdev);
> > > +}
> > > +
> > > +static void ni78bx_brightness_set(struct led_classdev *cdev,
> > > +				  enum led_brightness brightness)
> > > +{
> > > +	struct ni78bx_led *nled = to_ni78bx_led(cdev);
> > > +	u8 value;
> > > +
> > > +	mutex_lock(&led_lock);
> > 
> > You can use spin_lock_irqsave() instead, since we are not going to sleep
> > while accessing memory mapped registers.
> > 
> > Note that in the opposite case you would have to use
> > brightness_set_blocking op.
> > 
> > > +
> > > +	value = inb(io_base);
> > > +
> > > +	if (brightness) {
> > > +		value &= ~nled->mask;
> > > +		value |= nled->bit;
> > > +	} else {
> > > +		value &= ~nled->bit;
> > > +	}
> > > +
> > > +	outb(value, io_base);
> > > +	mutex_unlock(&led_lock);
> > > +}
> > > +
> > > +static enum led_brightness ni78bx_brightness_get(struct led_classdev *cdev)
> > > +{
> > > +	struct ni78bx_led *nled = to_ni78bx_led(cdev);
> > > +	u8 value;
> > > +
> > > +	mutex_lock(&led_lock);
> > > +	value = inb(io_base);
> > > +	mutex_unlock(&led_lock);
> > > +
> > > +	return (value & nled->bit) ? 1 : LED_OFF;
> > > +}
> > > +
> > > +static struct ni78bx_led ni78bx_leds[] = {
> > > +	{
> > > +		.bit = USER1_GREEN_LED,
> > > +		.mask = USER1_LED_MASK,
> > > +		.cdev = {
> > > +			.name = "nilrt:green:user1",
> > > +			.max_brightness = 1,
> > > +			.brightness_set = ni78bx_brightness_set,
> > > +			.brightness_get = ni78bx_brightness_get,
> > > +		}
> > > +	},
> > > +	{
> > > +		.bit = USER1_YELLOW_LED,
> > > +		.mask = USER1_LED_MASK,
> > > +		.cdev = {
> > > +			.name = "nilrt:yellow:user1",
> > > +			.max_brightness = 1,
> > > +			.brightness_set = ni78bx_brightness_set,
> > > +			.brightness_get = ni78bx_brightness_get,
> > > +		}
> > > +	},
> > > +	{
> > > +		.bit = USER2_GREEN_LED,
> > > +		.mask = USER2_LED_MASK,
> > > +		.cdev = {
> > > +			.name = "nilrt:green:user2",
> > > +			.max_brightness = 1,
> > > +			.brightness_set = ni78bx_brightness_set,
> > > +			.brightness_get = ni78bx_brightness_get,
> > > +		}
> > > +	},
> > > +	{
> > > +		.bit = USER2_YELLOW_LED,
> > > +		.mask = USER2_LED_MASK,
> > > +		.cdev = {
> > > +			.name = "nilrt:yellow:user2",
> > > +			.max_brightness = 1,
> > > +			.brightness_set = ni78bx_brightness_set,
> > > +			.brightness_get = ni78bx_brightness_get,
> > > +		}
> > > +	}
> > > +};
> > > +
> > > +static acpi_status
> > > +ni78bx_resources(struct acpi_resource *res, void *data)
> > > +{
> > > +	struct acpi_device *led = data;
> > > +	u16 io_size;
> > > +
> > > +	switch (res->type) {
> > > +	case ACPI_RESOURCE_TYPE_IO:
> > > +		if (io_base != 0) {
> > > +			dev_err(&led->dev, "too many IO resources\n");
> > > +			return AE_ERROR;
> > > +		}
> > > +
> > > +		io_base = res->data.io.minimum;
> > > +		io_size = res->data.io.address_length;
> > > +
> > > +		if (io_size < USER_LED_IO_SIZE) {
> > > +			dev_err(&led->dev, "memory region too small\n");
> > > +			return AE_ERROR;
> > > +		}
> > > +
> > > +		if (!devm_request_region(&led->dev, io_base, io_size,
> > > +					 KBUILD_MODNAME)) {
> > > +			dev_err(&led->dev, "failed to get memory region\n");
> > > +			return AE_ERROR;
> > > +		}
> > > +
> > > +		return AE_OK;
> > > +
> > > +	default:
> > > +		/* Ignore unsupported resources */
> > > +		return AE_OK;
> > > +	}
> > > +}
> > > +
> > > +static int ni78bx_remove(struct acpi_device *pdev)
> > > +{
> > > +	/* Lock LED register */
> > > +	outb(LOCK_VALUE, io_base + LOCK_REG_OFFSET);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int ni78bx_add(struct acpi_device *pdev)
> > > +{
> > > +	int ret, i;
> > > +	acpi_status status;
> > > +
> > > +	status = acpi_walk_resources(pdev->handle, METHOD_NAME__CRS,
> > > +				     ni78bx_resources, pdev);
> > > +
> > > +	if (ACPI_FAILURE(status) || io_base == 0)
> > > +		return -ENODEV;
> > > +
> > > +	/* Unlock LED register */
> > > +	outb(UNLOCK_VALUE, io_base + LOCK_REG_OFFSET);
> > > +
> > > +	for (i = 0; i < ARRAY_SIZE(ni78bx_leds); i++) {
> > > +		ret = devm_led_classdev_register(&pdev->dev,
> > > +						 &ni78bx_leds[i].cdev);
> > > +		if (ret)
> > > +			return ret;
> > > +	}
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static const struct acpi_device_id led_device_ids[] = {
> > > +	{"NIC78B3", 0},
> > > +	{"", 0},
> > 
> > CC also ACPI maintainers.
> > 
> > > +};
> > > +MODULE_DEVICE_TABLE(acpi, led_device_ids);
> > > +
> > > +static struct acpi_driver led_acpi_driver = {
> 
> I think you want to have a platform driver here instead.

Ah, thanks for the heads-up Mika!

As a rule, struct acpi_driver can only be used by things in drivers/acpi/.

If you think you need it *anyway*, the patch must be CCed to linux-acpi and
it is unlikely to be approved unless you have a very good reason to use
struct acpi_driver and very convincing arguments to support that.

Thanks,
Rafael

--
To unsubscribe from this list: send the line "unsubscribe linux-leds" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux