Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -229,6 +229,12 @@ config LEDS_PWM
	help
	  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
	tristate "LED driver for BD2802 RGB LED"
	depends on LEDS_CLASS && I2C
@@ -236,6 +242,33 @@ config LEDS_BD2802
	  This option enables support for BD2802GU RGB LED driver chips
	  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"

config LEDS_TRIGGERS
+4 −0
Original line number Diff line number Diff line
@@ -29,6 +29,10 @@ obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
obj-$(CONFIG_LEDS_WM831X_STATUS)	+= leds-wm831x-status.o
obj-$(CONFIG_LEDS_WM8350)		+= leds-wm8350.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
obj-$(CONFIG_LEDS_DAC124S085)		+= leds-dac124s085.o
+230 −0
Original line number Diff line number Diff line
/*
 * 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");
+84 −31
Original line number Diff line number Diff line
@@ -11,11 +11,24 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/pci.h>

static int force = 0;
module_param(force, bool, 0444);
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 led_classdev cdev;
	unsigned short port;
@@ -30,9 +43,9 @@ static void alix_led_set(struct led_classdev *led_cdev,
		container_of(led_cdev, struct alix_led, cdev);

	if (brightness)
		outl(led_dev->on_value, led_dev->port);
		outl(led_dev->on_value, gpio_base + led_dev->port);
	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[] = {
@@ -41,7 +54,7 @@ static struct alix_led alix_leds[] = {
			.name = "alix:1",
			.brightness_set = alix_led_set,
		},
		.port = 0x6100,
		.port = 0x00,
		.on_value = 1 << 22,
		.off_value = 1 << 6,
	},
@@ -50,7 +63,7 @@ static struct alix_led alix_leds[] = {
			.name = "alix:2",
			.brightness_set = alix_led_set,
		},
		.port = 0x6180,
		.port = 0x80,
		.on_value = 1 << 25,
		.off_value = 1 << 9,
	},
@@ -59,7 +72,7 @@ static struct alix_led alix_leds[] = {
			.name = "alix:3",
			.brightness_set = alix_led_set,
		},
		.port = 0x6180,
		.port = 0x80,
		.on_value = 1 << 27,
		.off_value = 1 << 11,
	},
@@ -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 char alix_sig[] = "PC Engines ALIX.";
	const size_t alix_sig_len = sizeof(alix_sig) - 1;

	const char *bios_virt;
	const char *scan_end;
	const char *p;
	int ret = 0;
	char name[64];

	if (force) {
		printk(KERN_NOTICE "%s: forced to skip BIOS test, "
		       "assume system has ALIX.2 style LEDs\n",
		       KBUILD_MODNAME);
		ret = 1;
		goto out;
		return 1;
	}

	bios_virt = phys_to_virt(bios_phys);
	scan_end = bios_virt + bios_len - (alix_sig_len + 2);
	for (p = bios_virt; p < scan_end; p++) {
		const char *tail;
		char *a;

		if (memcmp(p, alix_sig, alix_sig_len) != 0) {
		if (memcmp(p, alix_sig, alix_sig_len) != 0)
			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;
		if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') {
		if ((tail[0] == '2' || tail[0] == '3')) {
			printk(KERN_INFO
			       "%s: system is recognized as \"%s\"\n",
			       KBUILD_MODNAME, p);
			ret = 1;
			break;
			       KBUILD_MODNAME, name);
			return 1;
		}
	}

out:
	return ret;
	return 0;
}

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()) {
		ret = -ENODEV;
		goto out;
	if (pci_dev_present(divil_pci) == 0) {
		printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n");
		return -ENODEV;
	}

	/* enable output on GPIO for LED 1,2,3 */
	outl(1 << 6, 0x6104);
	outl(1 << 9, 0x6184);
	outl(1 << 11, 0x6184);
	/* Grab the GPIO I/O range */
	rdmsr(MSR_LBAR_GPIO, low, hi);

	/* 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);
	if (!IS_ERR(pdev)) {
@@ -168,7 +221,6 @@ static int __init alix_led_init(void)
	} else
		ret = PTR_ERR(pdev);

out:
	return ret;
}

@@ -176,6 +228,7 @@ static void __exit alix_led_exit(void)
{
	platform_device_unregister(pdev);
	platform_driver_unregister(&alix_led_driver);
	release_region(gpio_base, CS5535_GPIO_SIZE);
}

module_init(alix_led_init);
+2 −2
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ static struct led_classdev qube_front_led = {
	.name			= "qube::front",
	.brightness		= LED_FULL,
	.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)
@@ -43,7 +43,7 @@ static int __devinit cobalt_qube_led_probe(struct platform_device *pdev)
	if (!res)
		return -EBUSY;

	led_port = ioremap(res->start, res->end - res->start + 1);
	led_port = ioremap(res->start, resource_size(res));
	if (!led_port)
		return -ENOMEM;

Loading