Commit 9209e4bd authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds

* 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds:
  leds: leds-pwm: Set led_classdev max_brightness
  leds: leds-lp3944.h - remove unneeded includes
  leds: use default-on trigger for Cobalt Qube
  leds: drivers/leds/leds-ss4200.c: fix return statement
  leds: leds-pca9532.h- indent with tabs, not spaces
  leds: Add LED class driver for regulator driven LEDs.
  leds: leds-cobalt-qube.c: use resource_size()
  leds: leds-cobalt-raq.c - use resource_size()
  leds: Add driver for ADP5520/ADP5501 MFD PMICs
  leds: Add driver for LT3593 controlled LEDs
  leds-ss4200: Check pci_enable_device return
  leds: leds-alix2c - take port address from MSR
  leds: LED driver for Intel NAS SS4200 series (v5)
parents a695bc68 e4590620
...@@ -229,6 +229,12 @@ config LEDS_PWM ...@@ -229,6 +229,12 @@ config LEDS_PWM
help help
This option enables support for pwm driven LEDs This option enables support for pwm driven LEDs
config LEDS_REGULATOR
tristate "REGULATOR driven LED support"
depends on LEDS_CLASS && REGULATOR
help
This option enables support for regulator driven LEDs.
config LEDS_BD2802 config LEDS_BD2802
tristate "LED driver for BD2802 RGB LED" tristate "LED driver for BD2802 RGB LED"
depends on LEDS_CLASS && I2C depends on LEDS_CLASS && I2C
...@@ -236,6 +242,33 @@ config LEDS_BD2802 ...@@ -236,6 +242,33 @@ config LEDS_BD2802
This option enables support for BD2802GU RGB LED driver chips This option enables support for BD2802GU RGB LED driver chips
accessed via the I2C bus. accessed via the I2C bus.
config LEDS_INTEL_SS4200
tristate "LED driver for Intel NAS SS4200 series"
depends on LEDS_CLASS && PCI && DMI
help
This option enables support for the Intel SS4200 series of
Network Attached Storage servers. You may control the hard
drive or power LEDs on the front panel. Using this driver
can stop the front LED from blinking after startup.
config LEDS_LT3593
tristate "LED driver for LT3593 controllers"
depends on LEDS_CLASS && GENERIC_GPIO
help
This option enables support for LEDs driven by a Linear Technology
LT3593 controller. This controller uses a special one-wire pulse
coding protocol to set the brightness.
config LEDS_ADP5520
tristate "LED Support for ADP5520/ADP5501 PMIC"
depends on LEDS_CLASS && PMIC_ADP5520
help
This option enables support for on-chip LED drivers found
on Analog Devices ADP5520/ADP5501 PMICs.
To compile this driver as a module, choose M here: the module will
be called leds-adp5520.
comment "LED Triggers" comment "LED Triggers"
config LEDS_TRIGGERS config LEDS_TRIGGERS
......
...@@ -29,6 +29,10 @@ obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o ...@@ -29,6 +29,10 @@ obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
obj-$(CONFIG_LEDS_PWM) += leds-pwm.o obj-$(CONFIG_LEDS_PWM) += leds-pwm.o
obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o
obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o
obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o
obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o
# LED SPI Drivers # LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
......
/*
* LEDs driver for Analog Devices ADP5520/ADP5501 MFD PMICs
*
* Copyright 2009 Analog Devices Inc.
*
* Loosely derived from leds-da903x:
* Copyright (C) 2008 Compulab, Ltd.
* Mike Rapoport <mike@compulab.co.il>
*
* Copyright (C) 2006-2008 Marvell International Ltd.
* Eric Miao <eric.miao@marvell.com>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
#include <linux/mfd/adp5520.h>
struct adp5520_led {
struct led_classdev cdev;
struct work_struct work;
struct device *master;
enum led_brightness new_brightness;
int id;
int flags;
};
static void adp5520_led_work(struct work_struct *work)
{
struct adp5520_led *led = container_of(work, struct adp5520_led, work);
adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1,
led->new_brightness >> 2);
}
static void adp5520_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct adp5520_led *led;
led = container_of(led_cdev, struct adp5520_led, cdev);
led->new_brightness = value;
schedule_work(&led->work);
}
static int adp5520_led_setup(struct adp5520_led *led)
{
struct device *dev = led->master;
int flags = led->flags;
int ret = 0;
switch (led->id) {
case FLAG_ID_ADP5520_LED1_ADP5501_LED0:
ret |= adp5520_set_bits(dev, ADP5520_LED_TIME,
(flags >> ADP5520_FLAG_OFFT_SHIFT) &
ADP5520_FLAG_OFFT_MASK);
ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
ADP5520_LED1_EN);
break;
case FLAG_ID_ADP5520_LED2_ADP5501_LED1:
ret |= adp5520_set_bits(dev, ADP5520_LED_TIME,
((flags >> ADP5520_FLAG_OFFT_SHIFT) &
ADP5520_FLAG_OFFT_MASK) << 2);
ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL,
ADP5520_R3_MODE);
ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
ADP5520_LED2_EN);
break;
case FLAG_ID_ADP5520_LED3_ADP5501_LED2:
ret |= adp5520_set_bits(dev, ADP5520_LED_TIME,
((flags >> ADP5520_FLAG_OFFT_SHIFT) &
ADP5520_FLAG_OFFT_MASK) << 4);
ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL,
ADP5520_C3_MODE);
ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
ADP5520_LED3_EN);
break;
}
return ret;
}
static int __devinit adp5520_led_prepare(struct platform_device *pdev)
{
struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
struct device *dev = pdev->dev.parent;
int ret = 0;
ret |= adp5520_write(dev, ADP5520_LED1_CURRENT, 0);
ret |= adp5520_write(dev, ADP5520_LED2_CURRENT, 0);
ret |= adp5520_write(dev, ADP5520_LED3_CURRENT, 0);
ret |= adp5520_write(dev, ADP5520_LED_TIME, pdata->led_on_time << 6);
ret |= adp5520_write(dev, ADP5520_LED_FADE, FADE_VAL(pdata->fade_in,
pdata->fade_out));
return ret;
}
static int __devinit adp5520_led_probe(struct platform_device *pdev)
{
struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
struct adp5520_led *led, *led_dat;
struct led_info *cur_led;
int ret, i;
if (pdata == NULL) {
dev_err(&pdev->dev, "missing platform data\n");
return -ENODEV;
}
if (pdata->num_leds > ADP5520_01_MAXLEDS) {
dev_err(&pdev->dev, "can't handle more than %d LEDS\n",
ADP5520_01_MAXLEDS);
return -EFAULT;
}
led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL);
if (led == NULL) {
dev_err(&pdev->dev, "failed to alloc memory\n");
return -ENOMEM;
}
ret = adp5520_led_prepare(pdev);
if (ret) {
dev_err(&pdev->dev, "failed to write\n");
goto err_free;
}
for (i = 0; i < pdata->num_leds; ++i) {
cur_led = &pdata->leds[i];
led_dat = &led[i];
led_dat->cdev.name = cur_led->name;
led_dat->cdev.default_trigger = cur_led->default_trigger;
led_dat->cdev.brightness_set = adp5520_led_set;
led_dat->cdev.brightness = LED_OFF;
if (cur_led->flags & ADP5520_FLAG_LED_MASK)
led_dat->flags = cur_led->flags;
else
led_dat->flags = i + 1;
led_dat->id = led_dat->flags & ADP5520_FLAG_LED_MASK;
led_dat->master = pdev->dev.parent;
led_dat->new_brightness = LED_OFF;
INIT_WORK(&led_dat->work, adp5520_led_work);
ret = led_classdev_register(led_dat->master, &led_dat->cdev);
if (ret) {
dev_err(&pdev->dev, "failed to register LED %d\n",
led_dat->id);
goto err;
}
ret = adp5520_led_setup(led_dat);
if (ret) {
dev_err(&pdev->dev, "failed to write\n");
i++;
goto err;
}
}
platform_set_drvdata(pdev, led);
return 0;
err:
if (i > 0) {
for (i = i - 1; i >= 0; i--) {
led_classdev_unregister(&led[i].cdev);
cancel_work_sync(&led[i].work);
}
}
err_free:
kfree(led);
return ret;
}
static int __devexit adp5520_led_remove(struct platform_device *pdev)
{
struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
struct adp5520_led *led;
int i;
led = platform_get_drvdata(pdev);
adp5520_clr_bits(led->master, ADP5520_LED_CONTROL,
ADP5520_LED1_EN | ADP5520_LED2_EN | ADP5520_LED3_EN);
for (i = 0; i < pdata->num_leds; i++) {
led_classdev_unregister(&led[i].cdev);
cancel_work_sync(&led[i].work);
}
kfree(led);
return 0;
}
static struct platform_driver adp5520_led_driver = {
.driver = {
.name = "adp5520-led",
.owner = THIS_MODULE,
},
.probe = adp5520_led_probe,
.remove = __devexit_p(adp5520_led_remove),
};
static int __init adp5520_led_init(void)
{
return platform_driver_register(&adp5520_led_driver);
}
module_init(adp5520_led_init);
static void __exit adp5520_led_exit(void)
{
platform_driver_unregister(&adp5520_led_driver);
}
module_exit(adp5520_led_exit);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("LEDS ADP5520(01) Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:adp5520-led");
...@@ -11,11 +11,24 @@ ...@@ -11,11 +11,24 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/pci.h>
static int force = 0; static int force = 0;
module_param(force, bool, 0444); module_param(force, bool, 0444);
MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs"); MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs");
#define MSR_LBAR_GPIO 0x5140000C
#define CS5535_GPIO_SIZE 256
static u32 gpio_base;
static struct pci_device_id divil_pci[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
{ } /* NULL entry */
};
MODULE_DEVICE_TABLE(pci, divil_pci);
struct alix_led { struct alix_led {
struct led_classdev cdev; struct led_classdev cdev;
unsigned short port; unsigned short port;
...@@ -30,9 +43,9 @@ static void alix_led_set(struct led_classdev *led_cdev, ...@@ -30,9 +43,9 @@ static void alix_led_set(struct led_classdev *led_cdev,
container_of(led_cdev, struct alix_led, cdev); container_of(led_cdev, struct alix_led, cdev);
if (brightness) if (brightness)
outl(led_dev->on_value, led_dev->port); outl(led_dev->on_value, gpio_base + led_dev->port);
else else
outl(led_dev->off_value, led_dev->port); outl(led_dev->off_value, gpio_base + led_dev->port);
} }
static struct alix_led alix_leds[] = { static struct alix_led alix_leds[] = {
...@@ -41,7 +54,7 @@ static struct alix_led alix_leds[] = { ...@@ -41,7 +54,7 @@ static struct alix_led alix_leds[] = {
.name = "alix:1", .name = "alix:1",
.brightness_set = alix_led_set, .brightness_set = alix_led_set,
}, },
.port = 0x6100, .port = 0x00,
.on_value = 1 << 22, .on_value = 1 << 22,
.off_value = 1 << 6, .off_value = 1 << 6,
}, },
...@@ -50,7 +63,7 @@ static struct alix_led alix_leds[] = { ...@@ -50,7 +63,7 @@ static struct alix_led alix_leds[] = {
.name = "alix:2", .name = "alix:2",
.brightness_set = alix_led_set, .brightness_set = alix_led_set,
}, },
.port = 0x6180, .port = 0x80,
.on_value = 1 << 25, .on_value = 1 << 25,
.off_value = 1 << 9, .off_value = 1 << 9,
}, },
...@@ -59,7 +72,7 @@ static struct alix_led alix_leds[] = { ...@@ -59,7 +72,7 @@ static struct alix_led alix_leds[] = {
.name = "alix:3", .name = "alix:3",
.brightness_set = alix_led_set, .brightness_set = alix_led_set,
}, },
.port = 0x6180, .port = 0x80,
.on_value = 1 << 27, .on_value = 1 << 27,
.off_value = 1 << 11, .off_value = 1 << 11,
}, },
...@@ -101,64 +114,104 @@ static struct platform_driver alix_led_driver = { ...@@ -101,64 +114,104 @@ static struct platform_driver alix_led_driver = {
}, },
}; };
static int __init alix_present(void) static int __init alix_present(unsigned long bios_phys,
const char *alix_sig,
size_t alix_sig_len)
{ {
const unsigned long bios_phys = 0x000f0000;
const size_t bios_len = 0x00010000; const size_t bios_len = 0x00010000;
const char alix_sig[] = "PC Engines ALIX.";
const size_t alix_sig_len = sizeof(alix_sig) - 1;
const char *bios_virt; const char *bios_virt;
const char *scan_end; const char *scan_end;
const char *p; const char *p;
int ret = 0; char name[64];
if (force) { if (force) {
printk(KERN_NOTICE "%s: forced to skip BIOS test, " printk(KERN_NOTICE "%s: forced to skip BIOS test, "
"assume system has ALIX.2 style LEDs\n", "assume system has ALIX.2 style LEDs\n",
KBUILD_MODNAME); KBUILD_MODNAME);
ret = 1; return 1;
goto out;
} }
bios_virt = phys_to_virt(bios_phys); bios_virt = phys_to_virt(bios_phys);
scan_end = bios_virt + bios_len - (alix_sig_len + 2); scan_end = bios_virt + bios_len - (alix_sig_len + 2);
for (p = bios_virt; p < scan_end; p++) { for (p = bios_virt; p < scan_end; p++) {
const char *tail; const char *tail;
char *a;
if (memcmp(p, alix_sig, alix_sig_len) != 0) { if (memcmp(p, alix_sig, alix_sig_len) != 0)
continue; continue;
}
memcpy(name, p, sizeof(name));
/* remove the first \0 character from string */
a = strchr(name, '\0');
if (a)
*a = ' ';
/* cut the string at a newline */
a = strchr(name, '\r');
if (a)
*a = '\0';
tail = p + alix_sig_len; tail = p + alix_sig_len;
if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') { if ((tail[0] == '2' || tail[0] == '3')) {
printk(KERN_INFO printk(KERN_INFO
"%s: system is recognized as \"%s\"\n", "%s: system is recognized as \"%s\"\n",
KBUILD_MODNAME, p); KBUILD_MODNAME, name);
ret = 1; return 1;
break;
} }
} }
out: return 0;
return ret;
} }
static struct platform_device *pdev; static struct platform_device *pdev;
static int __init alix_led_init(void) static int __init alix_pci_led_init(void)
{ {
int ret; u32 low, hi;
if (!alix_present()) { if (pci_dev_present(divil_pci) == 0) {
ret = -ENODEV; printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n");
goto out; return -ENODEV;
} }
/* enable output on GPIO for LED 1,2,3 */ /* Grab the GPIO I/O range */
outl(1 << 6, 0x6104); rdmsr(MSR_LBAR_GPIO, low, hi);
outl(1 << 9, 0x6184);
outl(1 << 11, 0x6184); /* Check the mask and whether GPIO is enabled (sanity check) */
if (hi != 0x0000f001) {
printk(KERN_WARNING KBUILD_MODNAME": GPIO not enabled\n");
return -ENODEV;
}
/* Mask off the IO base address */
gpio_base = low & 0x0000ff00;
if (!request_region(gpio_base, CS5535_GPIO_SIZE, KBUILD_MODNAME)) {
printk(KERN_ERR KBUILD_MODNAME": can't allocate I/O for GPIO\n");
return -ENODEV;
}
/* Set GPIO function to output */
outl(1 << 6, gpio_base + 0x04);
outl(1 << 9, gpio_base + 0x84);
outl(1 << 11, gpio_base + 0x84);
return 0;
}
static int __init alix_led_init(void)
{
int ret = -ENODEV;
const char tinybios_sig[] = "PC Engines ALIX.";
const char coreboot_sig[] = "PC Engines\0ALIX.";
if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) ||
alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1))
ret = alix_pci_led_init();
if (ret < 0)
return ret;
pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
if (!IS_ERR(pdev)) { if (!IS_ERR(pdev)) {
...@@ -168,7 +221,6 @@ static int __init alix_led_init(void) ...@@ -168,7 +221,6 @@ static int __init alix_led_init(void)
} else } else
ret = PTR_ERR(pdev); ret = PTR_ERR(pdev);
out:
return ret; return ret;
} }
...@@ -176,6 +228,7 @@ static void __exit alix_led_exit(void) ...@@ -176,6 +228,7 @@ static void __exit alix_led_exit(void)
{ {
platform_device_unregister(pdev); platform_device_unregister(pdev);
platform_driver_unregister(&alix_led_driver); platform_driver_unregister(&alix_led_driver);
release_region(gpio_base, CS5535_GPIO_SIZE);
} }
module_init(alix_led_init); module_init(alix_led_init);
......
...@@ -31,7 +31,7 @@ static struct led_classdev qube_front_led = { ...@@ -31,7 +31,7 @@ static struct led_classdev qube_front_led = {
.name = "qube::front", .name = "qube::front",
.brightness = LED_FULL, .brightness = LED_FULL,
.brightness_set = qube_front_led_set, .brightness_set = qube_front_led_set,
.default_trigger = "ide-disk", .default_trigger = "default-on",
}; };
static int __devinit cobalt_qube_led_probe(struct platform_device *pdev) static int __devinit cobalt_qube_led_probe(struct platform_device *pdev)
...@@ -43,7 +43,7 @@ static int __devinit cobalt_qube_led_probe(struct platform_device *pdev) ...@@ -43,7 +43,7 @@ static int __devinit cobalt_qube_led_probe(struct platform_device *pdev)
if (!res) if (!res)
return -EBUSY; return -EBUSY;
led_port = ioremap(res->start, res->end - res->start + 1); led_port = ioremap(res->start, resource_size(res));
if (!led_port) if (!led_port)
return -ENOMEM; return -ENOMEM;
......
...@@ -84,7 +84,7 @@ static int __devinit cobalt_raq_led_probe(struct platform_device *pdev) ...@@ -84,7 +84,7 @@ static int __devinit cobalt_raq_led_probe(struct platform_device *pdev)
if (!res) if (!res)
return -EBUSY; return -EBUSY;
led_port = ioremap(res->start, res->end - res->start + 1); led_port = ioremap(res->start, resource_size(res));
if (!led_port) if (!led_port)
return -ENOMEM; return -ENOMEM;
......
/*
* LEDs driver for LT3593 controllers
*
* See the datasheet at http://cds.linear.com/docs/Datasheet/3593f.pdf
*
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
*
* Based on leds-gpio.c,
*
* Copyright (C) 2007 8D Technologies inc.
* Raphael Assenat <raph@8d.com>
* Copyright (C) 2008 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/gpio.h>
struct lt3593_led_data {
struct led_classdev cdev;
unsigned gpio;
struct work_struct work;
u8 new_level;
};
static void lt3593_led_work(struct work_struct *work)
{
int pulses;
struct lt3593_led_data *led_dat =
container_of(work, struct lt3593_led_data, work);
/*
* The LT3593 resets its internal current level register to the maximum
* level on the first falling edge on the control pin. Each following
* falling edge decreases the current level by 625uA. Up to 32 pulses
* can be sent, so the maximum power reduction is 20mA.
* After a timeout of 128us, the value is taken from the register and
* applied is to the output driver.
*/
if (led_dat->new_level == 0) {
gpio_set_value_cansleep(led_dat->gpio, 0);
return;
}
pulses = 32 - (led_dat->new_level * 32) / 255;
if (pulses == 0) {
gpio_set_value_cansleep(led_dat->gpio, 0);
mdelay(1);
gpio_set_value_cansleep(led_dat->gpio, 1);
return;
}
gpio_set_value_cansleep(led_dat->gpio, 1);
while (pulses--) {
gpio_set_value_cansleep(led_dat->gpio, 0);
udelay(1);
gpio_set_value_cansleep(led_dat->gpio, 1);
udelay(1);
}
}
static void lt3593_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct lt3593_led_data *led_dat =
container_of(led_cdev, struct lt3593_led_data, cdev);
led_dat->new_level = value;
schedule_work(&led_dat->work);
}
static int __devinit create_lt3593_led(const struct gpio_led *template,
struct lt3593_led_data *led_dat, struct device *parent)
{
int ret, state;
/* skip leds on GPIOs that aren't available */
if (!gpio_is_valid(template->gpio)) {
printk(KERN_INFO "%s: skipping unavailable LT3593 LED at gpio %d (%s)\n",
KBUILD_MODNAME, template->gpio, template->name);
return 0;
}
ret = gpio_request(template->gpio, template->name);
if (ret < 0)
return ret;
led_dat->cdev.name = template->name;
led_dat->cdev.default_trigger = template->default_trigger;
led_dat->gpio = template->gpio;
led_dat->cdev.brightness_set = lt3593_led_set;
state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
if (!template->retain_state_suspended)
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
ret = gpio_direction_output(led_dat->gpio, state);
if (ret < 0)
goto err;
INIT_WORK(&led_dat->work, lt3593_led_work);
ret = led_classdev_register(parent, &led_dat->cdev);
if (ret < 0)
goto err;
printk(KERN_INFO "%s: registered LT3593 LED '%s' at GPIO %d\n",
KBUILD_MODNAME, template->name, template->gpio);
return 0;
err:
gpio_free(led_dat->gpio);
return ret;
}
static void delete_lt3593_led(struct lt3593_led_data *led)
{
if (!gpio_is_valid(led->gpio))
return;
led_classdev_unregister(&led->cdev);
cancel_work_sync(&led->work);
gpio_free(led->gpio);
}
static int __devinit lt3593_led_probe(struct platform_device *pdev)
{
struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
struct lt3593_led_data *leds_data;
int i, ret = 0;
if (!pdata)
return -EBUSY;
leds_data = kzalloc(sizeof(struct lt3593_led_data) * pdata->num_leds,
GFP_KERNEL);
if (!leds_data)
return -ENOMEM;
for (i = 0; i < pdata->num_leds; i++) {
ret = create_lt3593_led(&pdata->leds[i], &leds_data[i],
&pdev->dev);
if (ret < 0)
goto err;
}
platform_set_drvdata(pdev, leds_data);
return 0;
err:
for (i = i - 1; i >= 0; i--)
delete_lt3593_led(&leds_data[i]);
kfree(leds_data);
return ret;
}
static int __devexit lt3593_led_remove(struct platform_device *pdev)
{
int i;
struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
struct lt3593_led_data *leds_data;
leds_data = platform_get_drvdata(pdev);
for (i = 0; i < pdata->num_leds; i++)
delete_lt3593_led(&leds_data[i]);
kfree(leds_data);
return 0;
}
static struct platform_driver lt3593_led_driver = {
.probe = lt3593_led_probe,
.remove = __devexit_p(lt3593_led_remove),
.driver = {
.name = "leds-lt3593",
.owner = THIS_MODULE,
},
};
MODULE_ALIAS("platform:leds-lt3593");
static int __init lt3593_led_init(void)
{
return platform_driver_register(&lt3593_led_driver);
}
static void __exit lt3593_led_exit(void)
{
platform_driver_unregister(&lt3593_led_driver);
}
module_init(lt3593_led_init);
module_exit(lt3593_led_exit);
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
MODULE_DESCRIPTION("LED driver for LT3593 controllers");
MODULE_LICENSE("GPL");
...@@ -27,7 +27,6 @@ struct led_pwm_data { ...@@ -27,7 +27,6 @@ struct led_pwm_data {
struct pwm_device *pwm; struct pwm_device *pwm;
unsigned int active_low; unsigned int active_low;
unsigned int period; unsigned int period;
unsigned int max_brightness;
}; };
static void led_pwm_set(struct led_classdev *led_cdev, static void led_pwm_set(struct led_classdev *led_cdev,
...@@ -35,7 +34,7 @@ static void led_pwm_set(struct led_classdev *led_cdev, ...@@ -35,7 +34,7 @@ static void led_pwm_set(struct led_classdev *led_cdev,
{ {
struct led_pwm_data *led_dat = struct led_pwm_data *led_dat =
container_of(led_cdev, struct led_pwm_data, cdev); container_of(led_cdev, struct led_pwm_data, cdev);
unsigned int max = led_dat->max_brightness; unsigned int max = led_dat->cdev.max_brightness;
unsigned int period = led_dat->period; unsigned int period = led_dat->period;
if (brightness == 0) { if (brightness == 0) {
...@@ -77,10 +76,10 @@ static int led_pwm_probe(struct platform_device *pdev) ...@@ -77,10 +76,10 @@ static int led_pwm_probe(struct platform_device *pdev)
led_dat->cdev.name = cur_led->name; led_dat->cdev.name = cur_led->name;
led_dat->cdev.default_trigger = cur_led->default_trigger; led_dat->cdev.default_trigger = cur_led->default_trigger;
led_dat->active_low = cur_led->active_low; led_dat->active_low = cur_led->active_low;
led_dat->max_brightness = cur_led->max_brightness;
led_dat->period = cur_led->pwm_period_ns; led_dat->period = cur_led->pwm_period_ns;
led_dat->cdev.brightness_set = led_pwm_set; led_dat->cdev.brightness_set = led_pwm_set;
led_dat->cdev.brightness = LED_OFF; led_dat->cdev.brightness = LED_OFF;
led_dat->cdev.max_brightness = cur_led->max_brightness;
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
ret = led_classdev_register(&pdev->dev, &led_dat->cdev); ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
......
/*
* leds-regulator.c - LED class driver for regulator driven LEDs.
*
* Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it>
*
* Inspired by leds-wm8350 driver.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/err.h>
#include <linux/workqueue.h>
#include <linux/leds.h>
#include <linux/leds-regulator.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#define to_regulator_led(led_cdev) \
container_of(led_cdev, struct regulator_led, cdev)
struct regulator_led {
struct led_classdev cdev;
enum led_brightness value;
int enabled;
struct mutex mutex;
struct work_struct work;
struct regulator *vcc;
};
static inline int led_regulator_get_max_brightness(struct regulator *supply)
{
int ret;
int voltage = regulator_list_voltage(supply, 0);
if (voltage <= 0)
return 1;
/* even if regulator can't change voltages,
* we still assume it can change status
* and the LED can be turned on and off.
*/
ret = regulator_set_voltage(supply, voltage, voltage);
if (ret < 0)
return 1;
return regulator_count_voltages(supply);
}
static int led_regulator_get_voltage(struct regulator *supply,
enum led_brightness brightness)
{
if (brightness == 0)
return -EINVAL;
return regulator_list_voltage(supply, brightness - 1);
}
static void regulator_led_enable(struct regulator_led *led)
{
int ret;
if (led->enabled)
return;
ret = regulator_enable(led->vcc);
if (ret != 0) {
dev_err(led->cdev.dev, "Failed to enable vcc: %d\n", ret);
return;
}
led->enabled = 1;
}
static void regulator_led_disable(struct regulator_led *led)
{
int ret;
if (!led->enabled)
return;
ret = regulator_disable(led->vcc);
if (ret != 0) {
dev_err(led->cdev.dev, "Failed to disable vcc: %d\n", ret);
return;
}
led->enabled = 0;
}
static void regulator_led_set_value(struct regulator_led *led)
{
int voltage;
int ret;
mutex_lock(&led->mutex);
if (led->value == LED_OFF) {
regulator_led_disable(led);
goto out;
}
if (led->cdev.max_brightness > 1) {
voltage = led_regulator_get_voltage(led->vcc, led->value);
dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n",
led->value, voltage);
ret = regulator_set_voltage(led->vcc, voltage, voltage);
if (ret != 0)
dev_err(led->cdev.dev, "Failed to set voltage %d: %d\n",
voltage, ret);
}
regulator_led_enable(led);
out:
mutex_unlock(&led->mutex);
}
static void led_work(struct work_struct *work)
{
struct regulator_led *led;
led = container_of(work, struct regulator_led, work);
regulator_led_set_value(led);
}
static void regulator_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct regulator_led *led = to_regulator_led(led_cdev);
led->value = value;
schedule_work(&led->work);
}
static int __devinit regulator_led_probe(struct platform_device *pdev)
{
struct led_regulator_platform_data *pdata = pdev->dev.platform_data;
struct regulator_led *led;
struct regulator *vcc;
int ret = 0;
if (pdata == NULL) {
dev_err(&pdev->dev, "no platform data\n");
return -ENODEV;
}
vcc = regulator_get_exclusive(&pdev->dev, "vled");
if (IS_ERR(vcc)) {
dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name);
return PTR_ERR(vcc);
}
led = kzalloc(sizeof(*led), GFP_KERNEL);
if (led == NULL) {
ret = -ENOMEM;
goto err_vcc;
}
led->cdev.max_brightness = led_regulator_get_max_brightness(vcc);
if (pdata->brightness > led->cdev.max_brightness) {
dev_err(&pdev->dev, "Invalid default brightness %d\n",
pdata->brightness);
ret = -EINVAL;
goto err_led;
}
led->value = pdata->brightness;
led->cdev.brightness_set = regulator_led_brightness_set;
led->cdev.name = pdata->name;
led->cdev.flags |= LED_CORE_SUSPENDRESUME;
led->vcc = vcc;
mutex_init(&led->mutex);
INIT_WORK(&led->work, led_work);
platform_set_drvdata(pdev, led);
ret = led_classdev_register(&pdev->dev, &led->cdev);
if (ret < 0) {
cancel_work_sync(&led->work);
goto err_led;
}
/* to expose the default value to userspace */
led->cdev.brightness = led->value;
/* Set the default led status */
regulator_led_set_value(led);
return 0;
err_led:
kfree(led);
err_vcc:
regulator_put(vcc);
return ret;
}
static int __devexit regulator_led_remove(struct platform_device *pdev)
{
struct regulator_led *led = platform_get_drvdata(pdev);
led_classdev_unregister(&led->cdev);
cancel_work_sync(&led->work);
regulator_led_disable(led);
regulator_put(led->vcc);
kfree(led);
return 0;
}
static struct platform_driver regulator_led_driver = {
.driver = {
.name = "leds-regulator",
.owner = THIS_MODULE,
},
.probe = regulator_led_probe,
.remove = __devexit_p(regulator_led_remove),
};
static int __init regulator_led_init(void)
{
return platform_driver_register(&regulator_led_driver);
}
module_init(regulator_led_init);
static void __exit regulator_led_exit(void)
{
platform_driver_unregister(&regulator_led_driver);
}
module_exit(regulator_led_exit);
MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
MODULE_DESCRIPTION("Regulator driven LED driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:leds-regulator");
/*
* SS4200-E Hardware API
* Copyright (c) 2009, Intel Corporation.
* Copyright IBM Corporation, 2009
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Author: Dave Hansen <dave@sr71.net>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/dmi.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/uaccess.h>
MODULE_AUTHOR("Rodney Girod <rgirod@confocus.com>, Dave Hansen <dave@sr71.net>");
MODULE_DESCRIPTION("Intel NAS/Home Server ICH7 GPIO Driver");
MODULE_LICENSE("GPL");
/*
* ICH7 LPC/GPIO PCI Config register offsets
*/
#define PMBASE 0x040
#define GPIO_BASE 0x048
#define GPIO_CTRL 0x04c
#define GPIO_EN 0x010
/*
* The ICH7 GPIO register block is 64 bytes in size.
*/
#define ICH7_GPIO_SIZE 64
/*
* Define register offsets within the ICH7 register block.
*/
#define GPIO_USE_SEL 0x000
#define GP_IO_SEL 0x004
#define GP_LVL 0x00c
#define GPO_BLINK 0x018
#define GPI_INV 0x030
#define GPIO_USE_SEL2 0x034
#define GP_IO_SEL2 0x038
#define GP_LVL2 0x03c
/*
* PCI ID of the Intel ICH7 LPC Device within which the GPIO block lives.
*/
static struct pci_device_id ich7_lpc_pci_id[] =
{
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_30) },
{ } /* NULL entry */
};
MODULE_DEVICE_TABLE(pci, ich7_lpc_pci_id);
static int __init ss4200_led_dmi_callback(const struct dmi_system_id *id)
{
pr_info("detected '%s'\n", id->ident);
return 1;
}
static unsigned int __initdata nodetect;
module_param_named(nodetect, nodetect, bool, 0);
MODULE_PARM_DESC(nodetect, "Skip DMI-based hardware detection");
/*
* struct nas_led_whitelist - List of known good models
*
* Contains the known good models this driver is compatible with.
* When adding a new model try to be as strict as possible. This
* makes it possible to keep the false positives (the model is
* detected as working, but in reality it is not) as low as
* possible.
*/
static struct dmi_system_id __initdata nas_led_whitelist[] = {
{
.callback = ss4200_led_dmi_callback,
.ident = "Intel SS4200-E",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel"),
DMI_MATCH(DMI_PRODUCT_NAME, "SS4200-E"),
DMI_MATCH(DMI_PRODUCT_VERSION, "1.00.00")
}
},
};
/*
* Base I/O address assigned to the Power Management register block
*/
static u32 g_pm_io_base;
/*
* Base I/O address assigned to the ICH7 GPIO register block
*/
static u32 nas_gpio_io_base;
/*
* When we successfully register a region, we are returned a resource.
* We use these to identify which regions we need to release on our way
* back out.
*/
static struct resource *gp_gpio_resource;
struct nasgpio_led {
char *name;
u32 gpio_bit;
struct led_classdev led_cdev;
};
/*
* gpio_bit(s) are the ICH7 GPIO bit assignments
*/
static struct nasgpio_led nasgpio_leds[] = {
{ .name = "hdd1:blue:sata", .gpio_bit = 0 },
{ .name = "hdd1:amber:sata", .gpio_bit = 1 },
{ .name = "hdd2:blue:sata", .gpio_bit = 2 },
{ .name = "hdd2:amber:sata", .gpio_bit = 3 },
{ .name = "hdd3:blue:sata", .gpio_bit = 4 },
{ .name = "hdd3:amber:sata", .gpio_bit = 5 },
{ .name = "hdd4:blue:sata", .gpio_bit = 6 },
{ .name = "hdd4:amber:sata", .gpio_bit = 7 },
{ .name = "power:blue:power", .gpio_bit = 27},
{ .name = "power:amber:power", .gpio_bit = 28},
};
#define NAS_RECOVERY 0x00000400 /* GPIO10 */
static struct nasgpio_led *
led_classdev_to_nasgpio_led(struct led_classdev *led_cdev)
{
return container_of(led_cdev, struct nasgpio_led, led_cdev);
}
static struct nasgpio_led *get_led_named(char *name)
{
int i;
for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) {
if (strcmp(nasgpio_leds[i].name, name))
continue;
return &nasgpio_leds[i];
}
return NULL;
}
/*
* This protects access to the gpio ports.
*/
static DEFINE_SPINLOCK(nasgpio_gpio_lock);
/*
* There are two gpio ports, one for blinking and the other
* for power. @port tells us if we're doing blinking or
* power control.
*
* Caller must hold nasgpio_gpio_lock
*/
static void __nasgpio_led_set_attr(struct led_classdev *led_cdev,
u32 port, u32 value)
{
struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev);
u32 gpio_out;
gpio_out = inl(nas_gpio_io_base + port);
if (value)
gpio_out |= (1<<led->gpio_bit);
else
gpio_out &= ~(1<<led->gpio_bit);
outl(gpio_out, nas_gpio_io_base + port);
}
static void nasgpio_led_set_attr(struct led_classdev *led_cdev,
u32 port, u32 value)
{
spin_lock(&nasgpio_gpio_lock);
__nasgpio_led_set_attr(led_cdev, port, value);
spin_unlock(&nasgpio_gpio_lock);
}
u32 nasgpio_led_get_attr(struct led_classdev *led_cdev, u32 port)
{
struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev);
u32 gpio_in;
spin_lock(&nasgpio_gpio_lock);
gpio_in = inl(nas_gpio_io_base + port);
spin_unlock(&nasgpio_gpio_lock);
if (gpio_in & (1<<led->gpio_bit))
return 1;
return 0;
}
/*
* There is actual brightness control in the hardware,
* but it is via smbus commands and not implemented
* in this driver.
*/
static void nasgpio_led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
u32 setting = 0;
if (brightness >= LED_HALF)
setting = 1;
/*
* Hold the lock across both operations. This ensures
* consistency so that both the "turn off blinking"
* and "turn light off" operations complete as a set.
*/
spin_lock(&nasgpio_gpio_lock);
/*
* LED class documentation asks that past blink state
* be disabled when brightness is turned to zero.
*/
if (brightness == 0)
__nasgpio_led_set_attr(led_cdev, GPO_BLINK, 0);
__nasgpio_led_set_attr(led_cdev, GP_LVL, setting);
spin_unlock(&nasgpio_gpio_lock);
}
static int nasgpio_led_set_blink(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
u32 setting = 1;
if (!(*delay_on == 0 && *delay_off == 0) &&
!(*delay_on == 500 && *delay_off == 500))
return -EINVAL;
/*
* These are very approximate.
*/
*delay_on = 500;
*delay_off = 500;
nasgpio_led_set_attr(led_cdev, GPO_BLINK, setting);
return 0;
}
/*
* Initialize the ICH7 GPIO registers for NAS usage. The BIOS should have
* already taken care of this, but we will do so in a non destructive manner
* so that we have what we need whether the BIOS did it or not.
*/
static int __devinit ich7_gpio_init(struct device *dev)
{
int i;
u32 config_data = 0;
u32 all_nas_led = 0;
for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++)
all_nas_led |= (1<<nasgpio_leds[i].gpio_bit);
spin_lock(&nasgpio_gpio_lock);
/*
* We need to enable all of the GPIO lines used by the NAS box,
* so we will read the current Use Selection and add our usage
* to it. This should be benign with regard to the original
* BIOS configuration.
*/
config_data = inl(nas_gpio_io_base + GPIO_USE_SEL);
dev_dbg(dev, ": Data read from GPIO_USE_SEL = 0x%08x\n", config_data);
config_data |= all_nas_led + NAS_RECOVERY;
outl(config_data, nas_gpio_io_base + GPIO_USE_SEL);
config_data = inl(nas_gpio_io_base + GPIO_USE_SEL);
dev_dbg(dev, ": GPIO_USE_SEL = 0x%08x\n\n", config_data);
/*
* The LED GPIO outputs need to be configured for output, so we
* will ensure that all LED lines are cleared for output and the
* RECOVERY line ready for input. This too should be benign with
* regard to BIOS configuration.
*/
config_data = inl(nas_gpio_io_base + GP_IO_SEL);
dev_dbg(dev, ": Data read from GP_IO_SEL = 0x%08x\n",
config_data);
config_data &= ~all_nas_led;
config_data |= NAS_RECOVERY;
outl(config_data, nas_gpio_io_base + GP_IO_SEL);
config_data = inl(nas_gpio_io_base + GP_IO_SEL);
dev_dbg(dev, ": GP_IO_SEL = 0x%08x\n", config_data);
/*
* In our final system, the BIOS will initialize the state of all
* of the LEDs. For now, we turn them all off (or Low).
*/
config_data = inl(nas_gpio_io_base + GP_LVL);
dev_dbg(dev, ": Data read from GP_LVL = 0x%08x\n", config_data);
/*
* In our final system, the BIOS will initialize the blink state of all
* of the LEDs. For now, we turn blink off for all of them.
*/
config_data = inl(nas_gpio_io_base + GPO_BLINK);
dev_dbg(dev, ": Data read from GPO_BLINK = 0x%08x\n", config_data);
/*
* At this moment, I am unsure if anything needs to happen with GPI_INV
*/
config_data = inl(nas_gpio_io_base + GPI_INV);
dev_dbg(dev, ": Data read from GPI_INV = 0x%08x\n", config_data);
spin_unlock(&nasgpio_gpio_lock);
return 0;
}
static void ich7_lpc_cleanup(struct device *dev)
{
/*
* If we were given exclusive use of the GPIO
* I/O Address range, we must return it.
*/
if (gp_gpio_resource) {
dev_dbg(dev, ": Releasing GPIO I/O addresses\n");
release_region(nas_gpio_io_base, ICH7_GPIO_SIZE);
gp_gpio_resource = NULL;
}
}
/*
* The OS has determined that the LPC of the Intel ICH7 Southbridge is present
* so we can retrive the required operational information and prepare the GPIO.
*/
static struct pci_dev *nas_gpio_pci_dev;
static int __devinit ich7_lpc_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
int status;
u32 gc = 0;
status = pci_enable_device(dev);
if (status) {
dev_err(&dev->dev, "pci_enable_device failed\n");
return -EIO;
}
nas_gpio_pci_dev = dev;
status = pci_read_config_dword(dev, PMBASE, &g_pm_io_base);
if (status)
goto out;
g_pm_io_base &= 0x00000ff80;
status = pci_read_config_dword(dev, GPIO_CTRL, &gc);
if (!(GPIO_EN & gc)) {
status = -EEXIST;
dev_info(&dev->dev,
"ERROR: The LPC GPIO Block has not been enabled.\n");
goto out;
}
status = pci_read_config_dword(dev, GPIO_BASE, &nas_gpio_io_base);
if (0 > status) {
dev_info(&dev->dev, "Unable to read GPIOBASE.\n");
goto out;
}
dev_dbg(&dev->dev, ": GPIOBASE = 0x%08x\n", nas_gpio_io_base);
nas_gpio_io_base &= 0x00000ffc0;
/*
* Insure that we have exclusive access to the GPIO I/O address range.
*/
gp_gpio_resource = request_region(nas_gpio_io_base, ICH7_GPIO_SIZE,
KBUILD_MODNAME);
if (NULL == gp_gpio_resource) {
dev_info(&dev->dev,
"ERROR Unable to register GPIO I/O addresses.\n");
status = -1;
goto out;
}
/*
* Initialize the GPIO for NAS/Home Server Use
*/
ich7_gpio_init(&dev->dev);
out:
if (status) {
ich7_lpc_cleanup(&dev->dev);
pci_disable_device(dev);
}
return status;
}
static void ich7_lpc_remove(struct pci_dev *dev)
{
ich7_lpc_cleanup(&dev->dev);
pci_disable_device(dev);
}
/*
* pci_driver structure passed to the PCI modules
*/
static struct pci_driver nas_gpio_pci_driver = {
.name = KBUILD_MODNAME,
.id_table = ich7_lpc_pci_id,
.probe = ich7_lpc_probe,
.remove = ich7_lpc_remove,
};
static struct led_classdev *get_classdev_for_led_nr(int nr)
{
struct nasgpio_led *nas_led = &nasgpio_leds[nr];
struct led_classdev *led = &nas_led->led_cdev;
return led;
}
static void set_power_light_amber_noblink(void)
{
struct nasgpio_led *amber = get_led_named("power:amber:power");
struct nasgpio_led *blue = get_led_named("power:blue:power");
if (!amber || !blue)
return;
/*
* LED_OFF implies disabling future blinking
*/
pr_debug("setting blue off and amber on\n");
nasgpio_led_set_brightness(&blue->led_cdev, LED_OFF);
nasgpio_led_set_brightness(&amber->led_cdev, LED_FULL);
}
static ssize_t nas_led_blink_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led = dev_get_drvdata(dev);
int blinking = 0;
if (nasgpio_led_get_attr(led, GPO_BLINK))
blinking = 1;
return sprintf(buf, "%u\n", blinking);
}
static ssize_t nas_led_blink_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
struct led_classdev *led = dev_get_drvdata(dev);
unsigned long blink_state;
ret = strict_strtoul(buf, 10, &blink_state);
if (ret)
return ret;
nasgpio_led_set_attr(led, GPO_BLINK, blink_state);
return size;
}
static DEVICE_ATTR(blink, 0644, nas_led_blink_show, nas_led_blink_store);
static int register_nasgpio_led(int led_nr)
{
int ret;
struct nasgpio_led *nas_led = &nasgpio_leds[led_nr];
struct led_classdev *led = get_classdev_for_led_nr(led_nr);
led->name = nas_led->name;
led->brightness = LED_OFF;
if (nasgpio_led_get_attr(led, GP_LVL))
led->brightness = LED_FULL;
led->brightness_set = nasgpio_led_set_brightness;
led->blink_set = nasgpio_led_set_blink;
ret = led_classdev_register(&nas_gpio_pci_dev->dev, led);
if (ret)
return ret;
ret = device_create_file(led->dev, &dev_attr_blink);
if (ret)
led_classdev_unregister(led);
return ret;
}
static void unregister_nasgpio_led(int led_nr)
{
struct led_classdev *led = get_classdev_for_led_nr(led_nr);
led_classdev_unregister(led);
device_remove_file(led->dev, &dev_attr_blink);
}
/*
* module load/initialization
*/
static int __init nas_gpio_init(void)
{
int i;
int ret = 0;
int nr_devices = 0;
nr_devices = dmi_check_system(nas_led_whitelist);
if (nodetect) {
pr_info("skipping hardware autodetection\n");
pr_info("Please send 'dmidecode' output to dave@sr71.net\n");
nr_devices++;
}
if (nr_devices <= 0) {
pr_info("no LED devices found\n");
return -ENODEV;
}
pr_info("registering PCI driver\n");
ret = pci_register_driver(&nas_gpio_pci_driver);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++) {
ret = register_nasgpio_led(i);
if (ret)
goto out_err;
}
/*
* When the system powers on, the BIOS leaves the power
* light blue and blinking. This will turn it solid
* amber once the driver is loaded.
*/
set_power_light_amber_noblink();
return 0;
out_err:
for (; i >= 0; i--)
unregister_nasgpio_led(i);
pci_unregister_driver(&nas_gpio_pci_driver);
return ret;
}
/*
* module unload
*/
static void __exit nas_gpio_exit(void)
{
int i;
pr_info("Unregistering driver\n");
for (i = 0; i < ARRAY_SIZE(nasgpio_leds); i++)
unregister_nasgpio_led(i);
pci_unregister_driver(&nas_gpio_pci_driver);
}
module_init(nas_gpio_init);
module_exit(nas_gpio_exit);
...@@ -12,9 +12,6 @@ ...@@ -12,9 +12,6 @@
#ifndef __LINUX_LEDS_LP3944_H #ifndef __LINUX_LEDS_LP3944_H
#define __LINUX_LEDS_LP3944_H #define __LINUX_LEDS_LP3944_H
#include <linux/leds.h>
#include <linux/workqueue.h>
#define LP3944_LED0 0 #define LP3944_LED0 0
#define LP3944_LED1 1 #define LP3944_LED1 1
#define LP3944_LED2 2 #define LP3944_LED2 2
......
/*
* leds-regulator.h - platform data structure for regulator driven LEDs.
*
* Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef __LINUX_LEDS_REGULATOR_H
#define __LINUX_LEDS_REGULATOR_H
/*
* Use "vled" as supply id when declaring the regulator consumer:
*
* static struct regulator_consumer_supply pcap_regulator_VVIB_consumers [] = {
* { .dev_name = "leds-regulator.0", supply = "vled" },
* };
*
* If you have several regulator driven LEDs, you can append a numerical id to
* .dev_name as done above, and use the same id when declaring the platform
* device:
*
* static struct led_regulator_platform_data a780_vibrator_data = {
* .name = "a780::vibrator",
* };
*
* static struct platform_device a780_vibrator = {
* .name = "leds-regulator",
* .id = 0,
* .dev = {
* .platform_data = &a780_vibrator_data,
* },
* };
*/
#include <linux/leds.h>
struct led_regulator_platform_data {
char *name; /* LED name as expected by LED class */
enum led_brightness brightness; /* initial brightness value */
};
#endif /* __LINUX_LEDS_REGULATOR_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment