add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

314
kernel/drivers/leds/Kconfig Normal file
View File

@@ -0,0 +1,314 @@
menuconfig NEW_LEDS
bool "LED Support"
help
Say Y to enable Linux LED support. This allows control of supported
LEDs from both userspace and optionally, by kernel events (triggers).
This is not related to standard keyboard LEDs which are controlled
via the input system.
if NEW_LEDS
config LEDS_CLASS
tristate "LED Class Support"
help
This option enables the led sysfs class in /sys/class/leds. You'll
need this to do anything useful with LEDs. If unsure, say N.
comment "LED drivers"
config LEDS_ATMEL_PWM
tristate "LED Support using Atmel PWM outputs"
depends on LEDS_CLASS && ATMEL_PWM
help
This option enables support for LEDs driven using outputs
of the dedicated PWM controller found on newer Atmel SOCs.
config LEDS_LOCOMO
tristate "LED Support for Locomo device"
depends on LEDS_CLASS && SHARP_LOCOMO
help
This option enables support for the LEDs on Sharp Locomo.
Zaurus models SL-5500 and SL-5600.
config LEDS_MIKROTIK_RB532
tristate "LED Support for Mikrotik Routerboard 532"
depends on LEDS_CLASS && MIKROTIK_RB532
help
This option enables support for the so called "User LED" of
Mikrotik's Routerboard 532.
config LEDS_S3C24XX
tristate "LED Support for Samsung S3C24XX GPIO LEDs"
depends on LEDS_CLASS && ARCH_S3C2410
help
This option enables support for LEDs connected to GPIO lines
on Samsung S3C24XX series CPUs, such as the S3C2410 and S3C2440.
config LEDS_AMS_DELTA
tristate "LED Support for the Amstrad Delta (E3)"
depends on LEDS_CLASS && MACH_AMS_DELTA
help
This option enables support for the LEDs on Amstrad Delta (E3).
config LEDS_NET48XX
tristate "LED Support for Soekris net48xx series Error LED"
depends on LEDS_CLASS && SCx200_GPIO
help
This option enables support for the Soekris net4801 and net4826 error
LED.
config LEDS_FSG
tristate "LED Support for the Freecom FSG-3"
depends on LEDS_CLASS && MACH_FSG
help
This option enables support for the LEDs on the Freecom FSG-3.
config LEDS_WRAP
tristate "LED Support for the WRAP series LEDs"
depends on LEDS_CLASS && SCx200_GPIO
help
This option enables support for the PCEngines WRAP programmable LEDs.
config LEDS_ALIX2
tristate "LED Support for ALIX.2 and ALIX.3 series"
depends on LEDS_CLASS && X86 && EXPERIMENTAL
help
This option enables support for the PCEngines ALIX.2 and ALIX.3 LEDs.
You have to set leds-alix2.force=1 for boards with Award BIOS.
config LEDS_H1940
tristate "LED Support for iPAQ H1940 device"
depends on LEDS_CLASS && ARCH_H1940
help
This option enables support for the LEDs on the h1940.
config LEDS_COBALT_QUBE
tristate "LED Support for the Cobalt Qube series front LED"
depends on LEDS_CLASS && MIPS_COBALT
help
This option enables support for the front LED on Cobalt Qube series
config LEDS_COBALT_RAQ
bool "LED Support for the Cobalt Raq series"
depends on LEDS_CLASS=y && MIPS_COBALT
select LEDS_TRIGGERS
help
This option enables support for the Cobalt Raq series LEDs.
config LEDS_SUNFIRE
tristate "LED support for SunFire servers."
depends on LEDS_CLASS && SPARC64
select LEDS_TRIGGERS
help
This option enables support for the Left, Middle, and Right
LEDs on the I/O and CPU boards of SunFire UltraSPARC servers.
config LEDS_HP6XX
tristate "LED Support for the HP Jornada 6xx"
depends on LEDS_CLASS && SH_HP6XX
help
This option enables LED support for the handheld
HP Jornada 620/660/680/690.
config LEDS_PCA9532
tristate "LED driver for PCA9532 dimmer"
depends on LEDS_CLASS && I2C && INPUT && EXPERIMENTAL
help
This option enables support for NXP pca9532
LED controller. It is generally only useful
as a platform driver
config LEDS_GPIO
tristate "LED Support for GPIO connected LEDs"
depends on LEDS_CLASS && GENERIC_GPIO
help
This option enables support for the LEDs connected to GPIO
outputs. To be useful the particular board must have LEDs
and they must be connected to the GPIO lines. The LEDs must be
defined as platform devices and/or OpenFirmware platform devices.
The code to use these bindings can be selected below.
config LEDS_GPIO_PLATFORM
bool "Platform device bindings for GPIO LEDs"
depends on LEDS_GPIO
default y
help
Let the leds-gpio driver drive LEDs which have been defined as
platform devices. If you don't know what this means, say yes.
config LEDS_GPIO_OF
bool "OpenFirmware platform device bindings for GPIO LEDs"
depends on LEDS_GPIO && OF_DEVICE
default y
help
Let the leds-gpio driver drive LEDs which have been defined as
of_platform devices. For instance, LEDs which are listed in a "dts"
file.
config LEDS_LP3944
tristate "LED Support for N.S. LP3944 (Fun Light) I2C chip"
depends on LEDS_CLASS && I2C
help
This option enables support for LEDs connected to the National
Semiconductor LP3944 Lighting Management Unit (LMU) also known as
Fun Light Chip.
To compile this driver as a module, choose M here: the
module will be called leds-lp3944.
config LEDS_CLEVO_MAIL
tristate "Mail LED on Clevo notebook"
depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI
help
This driver makes the mail LED accessible from userspace
programs through the leds subsystem. This LED have three
known mode: off, blink at 0.5Hz and blink at 1Hz.
The driver supports two kinds of interface: using ledtrig-timer
or through /sys/class/leds/clevo::mail/brightness. As this LED
cannot change it's brightness it blinks instead. The brightness
value 0 means off, 1..127 means blink at 0.5Hz and 128..255 means
blink at 1Hz.
This module can drive the mail LED for the following notebooks:
Clevo D400P
Clevo D410J
Clevo D410V
Clevo D400V/D470V (not tested, but might work)
Clevo M540N
Clevo M5x0N (not tested, but might work)
Positivo Mobile (Clevo M5x0V)
If your model is not listed here you can try the "nodetect"
module parameter.
To compile this driver as a module, choose M here: the
module will be called leds-clevo-mail.
config LEDS_PCA955X
tristate "LED Support for PCA955x I2C chips"
depends on LEDS_CLASS && I2C
help
This option enables support for LEDs connected to PCA955x
LED driver chips accessed via the I2C bus. Supported
devices include PCA9550, PCA9551, PCA9552, and PCA9553.
config LEDS_WM831X_STATUS
tristate "LED support for status LEDs on WM831x PMICs"
depends on LEDS_CLASS && MFD_WM831X
help
This option enables support for the status LEDs of the WM831x
series of PMICs.
config LEDS_WM8350
tristate "LED Support for WM8350 AudioPlus PMIC"
depends on LEDS_CLASS && MFD_WM8350
help
This option enables support for LEDs driven by the Wolfson
Microelectronics WM8350 AudioPlus PMIC.
config LEDS_DA903X
tristate "LED Support for DA9030/DA9034 PMIC"
depends on LEDS_CLASS && PMIC_DA903X
help
This option enables support for on-chip LED drivers found
on Dialog Semiconductor DA9030/DA9034 PMICs.
config LEDS_DAC124S085
tristate "LED Support for DAC124S085 SPI DAC"
depends on LEDS_CLASS && SPI
help
This option enables support for DAC124S085 SPI DAC from NatSemi,
which can be used to control up to four LEDs.
config LEDS_PWM
tristate "PWM driven LED Support"
depends on LEDS_CLASS && HAVE_PWM
help
This option enables support for pwm driven LEDs
config LEDS_BD2802
tristate "LED driver for BD2802 RGB LED"
depends on LEDS_CLASS && I2C
help
This option enables support for BD2802GU RGB LED driver chips
accessed via the I2C bus.
config LEDS_MB374
tristate "LED Support for STMicroelectronics mb374 LEDs"
depends on LEDS_CLASS && SH_ST_MB374
help
This option enables support for the LEDs on the STMicroelectronics
mb374 (ST40RA Starter board).
comment "LED Triggers"
config LEDS_TRIGGERS
bool "LED Trigger support"
help
This option enables trigger support for the leds class.
These triggers allow kernel events to drive the LEDs and can
be configured via sysfs. If unsure, say Y.
config LEDS_TRIGGER_TIMER
tristate "LED Timer Trigger"
depends on LEDS_TRIGGERS
help
This allows LEDs to be controlled by a programmable timer
via sysfs. Some LED hardware can be programmed to start
blinking the LED without any further software interaction.
For more details read Documentation/leds-class.txt.
If unsure, say Y.
config LEDS_TRIGGER_IDE_DISK
bool "LED IDE Disk Trigger"
depends on LEDS_TRIGGERS && IDE_GD_ATA
help
This allows LEDs to be controlled by IDE disk activity.
If unsure, say Y.
config LEDS_TRIGGER_HEARTBEAT
tristate "LED Heartbeat Trigger"
depends on LEDS_TRIGGERS
help
This allows LEDs to be controlled by a CPU load average.
The flash frequency is a hyperbolic function of the 1-minute
load average.
If unsure, say Y.
config LEDS_TRIGGER_BACKLIGHT
tristate "LED backlight Trigger"
depends on LEDS_TRIGGERS
help
This allows LEDs to be controlled as a backlight device: they
turn off and on when the display is blanked and unblanked.
If unsure, say N.
config LEDS_TRIGGER_GPIO
tristate "LED GPIO Trigger"
depends on LEDS_TRIGGERS
depends on GPIOLIB
help
This allows LEDs to be controlled by gpio events. It's good
when using gpios as switches and triggering the needed LEDs
from there. One use case is n810's keypad LEDs that could
be triggered by this trigger when user slides up to show
keypad.
If unsure, say N.
config LEDS_TRIGGER_DEFAULT_ON
tristate "LED Default ON Trigger"
depends on LEDS_TRIGGERS
help
This allows LEDs to be initialised in the ON state.
If unsure, say Y.
comment "iptables trigger is under Netfilter config (LED target)"
depends on LEDS_TRIGGERS
endif # NEW_LEDS

View File

@@ -0,0 +1,45 @@
# LED Core
obj-$(CONFIG_NEW_LEDS) += led-core.o
obj-$(CONFIG_LEDS_CLASS) += led-class.o
obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
# LED Platform Drivers
obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o
obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
obj-$(CONFIG_LEDS_ALIX2) += leds-alix2.o
obj-$(CONFIG_LEDS_H1940) += leds-h1940.o
obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
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_MB374) += leds-mb374.o
obj-$(CONFIG_SH_ST_MB411) += leds-mb411.o
obj-$(CONFIG_SH_ST_MB628) += leds-mb628.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
# LED Triggers
obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o
obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o
obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o
obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o

View File

@@ -0,0 +1,231 @@
/*
* LED Class Core
*
* Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
* Copyright (C) 2005-2007 Richard Purdie <rpurdie@openedhand.com>
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/sysdev.h>
#include <linux/timer.h>
#include <linux/err.h>
#include <linux/ctype.h>
#include <linux/leds.h>
#include "leds.h"
static struct class *leds_class;
static void led_update_brightness(struct led_classdev *led_cdev)
{
if (led_cdev->brightness_get)
led_cdev->brightness = led_cdev->brightness_get(led_cdev);
}
static ssize_t led_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
/* no lock needed for this */
led_update_brightness(led_cdev);
return sprintf(buf, "%u\n", led_cdev->brightness);
}
static ssize_t led_brightness_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
ssize_t ret = -EINVAL;
char *after;
unsigned long state = simple_strtoul(buf, &after, 10);
size_t count = after - buf;
if (*after && isspace(*after))
count++;
if (count == size) {
ret = count;
if (state == LED_OFF)
led_trigger_remove(led_cdev);
led_set_brightness(led_cdev, state);
}
return ret;
}
static ssize_t led_max_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", led_cdev->max_brightness);
}
static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);
static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
#ifdef CONFIG_LEDS_TRIGGERS
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
#endif
/**
* led_classdev_suspend - suspend an led_classdev.
* @led_cdev: the led_classdev to suspend.
*/
void led_classdev_suspend(struct led_classdev *led_cdev)
{
led_cdev->flags |= LED_SUSPENDED;
led_cdev->brightness_set(led_cdev, 0);
}
EXPORT_SYMBOL_GPL(led_classdev_suspend);
/**
* led_classdev_resume - resume an led_classdev.
* @led_cdev: the led_classdev to resume.
*/
void led_classdev_resume(struct led_classdev *led_cdev)
{
led_cdev->brightness_set(led_cdev, led_cdev->brightness);
led_cdev->flags &= ~LED_SUSPENDED;
}
EXPORT_SYMBOL_GPL(led_classdev_resume);
static int led_suspend(struct device *dev, pm_message_t state)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
led_classdev_suspend(led_cdev);
return 0;
}
static int led_resume(struct device *dev)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
led_classdev_resume(led_cdev);
return 0;
}
/**
* led_classdev_register - register a new object of led_classdev class.
* @parent: The device to register.
* @led_cdev: the led_classdev structure for this device.
*/
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
int rc;
led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
"%s", led_cdev->name);
if (IS_ERR(led_cdev->dev))
return PTR_ERR(led_cdev->dev);
/* register the attributes */
rc = device_create_file(led_cdev->dev, &dev_attr_brightness);
if (rc)
goto err_out;
#ifdef CONFIG_LEDS_TRIGGERS
init_rwsem(&led_cdev->trigger_lock);
#endif
/* add to the list of leds */
down_write(&leds_list_lock);
list_add_tail(&led_cdev->node, &leds_list);
up_write(&leds_list_lock);
if (!led_cdev->max_brightness)
led_cdev->max_brightness = LED_FULL;
rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness);
if (rc)
goto err_out_attr_max;
led_update_brightness(led_cdev);
#ifdef CONFIG_LEDS_TRIGGERS
rc = device_create_file(led_cdev->dev, &dev_attr_trigger);
if (rc)
goto err_out_led_list;
led_trigger_set_default(led_cdev);
#endif
printk(KERN_INFO "Registered led device: %s\n",
led_cdev->name);
return 0;
#ifdef CONFIG_LEDS_TRIGGERS
err_out_led_list:
device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
#endif
err_out_attr_max:
device_remove_file(led_cdev->dev, &dev_attr_brightness);
list_del(&led_cdev->node);
err_out:
device_unregister(led_cdev->dev);
return rc;
}
EXPORT_SYMBOL_GPL(led_classdev_register);
/**
* led_classdev_unregister - unregisters a object of led_properties class.
* @led_cdev: the led device to unregister
*
* Unregisters a previously registered via led_classdev_register object.
*/
void led_classdev_unregister(struct led_classdev *led_cdev)
{
device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
device_remove_file(led_cdev->dev, &dev_attr_brightness);
#ifdef CONFIG_LEDS_TRIGGERS
device_remove_file(led_cdev->dev, &dev_attr_trigger);
down_write(&led_cdev->trigger_lock);
if (led_cdev->trigger)
led_trigger_set(led_cdev, NULL);
up_write(&led_cdev->trigger_lock);
#endif
device_unregister(led_cdev->dev);
down_write(&leds_list_lock);
list_del(&led_cdev->node);
up_write(&leds_list_lock);
}
EXPORT_SYMBOL_GPL(led_classdev_unregister);
static int __init leds_init(void)
{
leds_class = class_create(THIS_MODULE, "leds");
if (IS_ERR(leds_class))
return PTR_ERR(leds_class);
leds_class->suspend = led_suspend;
leds_class->resume = led_resume;
return 0;
}
static void __exit leds_exit(void)
{
class_destroy(leds_class);
}
subsys_initcall(leds_init);
module_exit(leds_exit);
MODULE_AUTHOR("John Lenz, Richard Purdie");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LED Class Interface");

View File

@@ -0,0 +1,25 @@
/*
* LED Class Core
*
* Copyright 2005-2006 Openedhand Ltd.
*
* Author: Richard Purdie <rpurdie@openedhand.com>
*
* 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/list.h>
#include <linux/module.h>
#include <linux/rwsem.h>
#include <linux/leds.h>
#include "leds.h"
DECLARE_RWSEM(leds_list_lock);
EXPORT_SYMBOL_GPL(leds_list_lock);
LIST_HEAD(leds_list);
EXPORT_SYMBOL_GPL(leds_list);

View File

@@ -0,0 +1,264 @@
/*
* LED Triggers Core
*
* Copyright 2005-2007 Openedhand Ltd.
*
* Author: Richard Purdie <rpurdie@openedhand.com>
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/sysdev.h>
#include <linux/timer.h>
#include <linux/rwsem.h>
#include <linux/leds.h>
#include "leds.h"
/*
* Nests outside led_cdev->trigger_lock
*/
static DECLARE_RWSEM(triggers_list_lock);
static LIST_HEAD(trigger_list);
/* Used by LED Class */
ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
char trigger_name[TRIG_NAME_MAX];
struct led_trigger *trig;
size_t len;
trigger_name[sizeof(trigger_name) - 1] = '\0';
strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
len = strlen(trigger_name);
if (len && trigger_name[len - 1] == '\n')
trigger_name[len - 1] = '\0';
if (!strcmp(trigger_name, "none")) {
led_trigger_remove(led_cdev);
return count;
}
down_read(&triggers_list_lock);
list_for_each_entry(trig, &trigger_list, next_trig) {
if (!strcmp(trigger_name, trig->name)) {
down_write(&led_cdev->trigger_lock);
led_trigger_set(led_cdev, trig);
up_write(&led_cdev->trigger_lock);
up_read(&triggers_list_lock);
return count;
}
}
up_read(&triggers_list_lock);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(led_trigger_store);
ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct led_trigger *trig;
int len = 0;
down_read(&triggers_list_lock);
down_read(&led_cdev->trigger_lock);
if (!led_cdev->trigger)
len += sprintf(buf+len, "[none] ");
else
len += sprintf(buf+len, "none ");
list_for_each_entry(trig, &trigger_list, next_trig) {
if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
trig->name))
len += sprintf(buf+len, "[%s] ", trig->name);
else
len += sprintf(buf+len, "%s ", trig->name);
}
up_read(&led_cdev->trigger_lock);
up_read(&triggers_list_lock);
len += sprintf(len+buf, "\n");
return len;
}
EXPORT_SYMBOL_GPL(led_trigger_show);
/* Caller must ensure led_cdev->trigger_lock held */
void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
{
unsigned long flags;
/* Remove any existing trigger */
if (led_cdev->trigger) {
write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
list_del(&led_cdev->trig_list);
write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
flags);
if (led_cdev->trigger->deactivate)
led_cdev->trigger->deactivate(led_cdev);
led_cdev->trigger = NULL;
led_set_brightness(led_cdev, LED_OFF);
}
if (trigger) {
write_lock_irqsave(&trigger->leddev_list_lock, flags);
list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
led_cdev->trigger = trigger;
if (trigger->activate)
trigger->activate(led_cdev);
}
}
EXPORT_SYMBOL_GPL(led_trigger_set);
void led_trigger_remove(struct led_classdev *led_cdev)
{
down_write(&led_cdev->trigger_lock);
led_trigger_set(led_cdev, NULL);
up_write(&led_cdev->trigger_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_remove);
void led_trigger_set_default(struct led_classdev *led_cdev)
{
struct led_trigger *trig;
if (!led_cdev->default_trigger)
return;
down_read(&triggers_list_lock);
down_write(&led_cdev->trigger_lock);
list_for_each_entry(trig, &trigger_list, next_trig) {
if (!strcmp(led_cdev->default_trigger, trig->name))
led_trigger_set(led_cdev, trig);
}
up_write(&led_cdev->trigger_lock);
up_read(&triggers_list_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_set_default);
/* LED Trigger Interface */
int led_trigger_register(struct led_trigger *trigger)
{
struct led_classdev *led_cdev;
struct led_trigger *trig;
rwlock_init(&trigger->leddev_list_lock);
INIT_LIST_HEAD(&trigger->led_cdevs);
down_write(&triggers_list_lock);
/* Make sure the trigger's name isn't already in use */
list_for_each_entry(trig, &trigger_list, next_trig) {
if (!strcmp(trig->name, trigger->name)) {
up_write(&triggers_list_lock);
return -EEXIST;
}
}
/* Add to the list of led triggers */
list_add_tail(&trigger->next_trig, &trigger_list);
up_write(&triggers_list_lock);
/* Register with any LEDs that have this as a default trigger */
down_read(&leds_list_lock);
list_for_each_entry(led_cdev, &leds_list, node) {
down_write(&led_cdev->trigger_lock);
if (!led_cdev->trigger && led_cdev->default_trigger &&
!strcmp(led_cdev->default_trigger, trigger->name))
led_trigger_set(led_cdev, trigger);
up_write(&led_cdev->trigger_lock);
}
up_read(&leds_list_lock);
return 0;
}
EXPORT_SYMBOL_GPL(led_trigger_register);
void led_trigger_unregister(struct led_trigger *trigger)
{
struct led_classdev *led_cdev;
/* Remove from the list of led triggers */
down_write(&triggers_list_lock);
list_del(&trigger->next_trig);
up_write(&triggers_list_lock);
/* Remove anyone actively using this trigger */
down_read(&leds_list_lock);
list_for_each_entry(led_cdev, &leds_list, node) {
down_write(&led_cdev->trigger_lock);
if (led_cdev->trigger == trigger)
led_trigger_set(led_cdev, NULL);
up_write(&led_cdev->trigger_lock);
}
up_read(&leds_list_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_unregister);
/* Simple LED Tigger Interface */
void led_trigger_event(struct led_trigger *trigger,
enum led_brightness brightness)
{
struct list_head *entry;
if (!trigger)
return;
read_lock(&trigger->leddev_list_lock);
list_for_each(entry, &trigger->led_cdevs) {
struct led_classdev *led_cdev;
led_cdev = list_entry(entry, struct led_classdev, trig_list);
led_set_brightness(led_cdev, brightness);
}
read_unlock(&trigger->leddev_list_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_event);
void led_trigger_register_simple(const char *name, struct led_trigger **tp)
{
struct led_trigger *trigger;
int err;
trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
if (trigger) {
trigger->name = name;
err = led_trigger_register(trigger);
if (err < 0)
printk(KERN_WARNING "LED trigger %s failed to register"
" (%d)\n", name, err);
} else
printk(KERN_WARNING "LED trigger %s failed to register"
" (no memory)\n", name);
*tp = trigger;
}
EXPORT_SYMBOL_GPL(led_trigger_register_simple);
void led_trigger_unregister_simple(struct led_trigger *trigger)
{
if (trigger)
led_trigger_unregister(trigger);
kfree(trigger);
}
EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
MODULE_AUTHOR("Richard Purdie");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LED Triggers Core");

View File

@@ -0,0 +1,186 @@
/*
* LEDs driver for PCEngines ALIX.2 and ALIX.3
*
* Copyright (C) 2008 Constantin Baranov <const@mimas.ru>
*/
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/string.h>
static int force = 0;
module_param(force, bool, 0444);
MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs");
struct alix_led {
struct led_classdev cdev;
unsigned short port;
unsigned int on_value;
unsigned int off_value;
};
static void alix_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct alix_led *led_dev =
container_of(led_cdev, struct alix_led, cdev);
if (brightness)
outl(led_dev->on_value, led_dev->port);
else
outl(led_dev->off_value, led_dev->port);
}
static struct alix_led alix_leds[] = {
{
.cdev = {
.name = "alix:1",
.brightness_set = alix_led_set,
},
.port = 0x6100,
.on_value = 1 << 22,
.off_value = 1 << 6,
},
{
.cdev = {
.name = "alix:2",
.brightness_set = alix_led_set,
},
.port = 0x6180,
.on_value = 1 << 25,
.off_value = 1 << 9,
},
{
.cdev = {
.name = "alix:3",
.brightness_set = alix_led_set,
},
.port = 0x6180,
.on_value = 1 << 27,
.off_value = 1 << 11,
},
};
static int __init alix_led_probe(struct platform_device *pdev)
{
int i;
int ret;
for (i = 0; i < ARRAY_SIZE(alix_leds); i++) {
alix_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME;
ret = led_classdev_register(&pdev->dev, &alix_leds[i].cdev);
if (ret < 0)
goto fail;
}
return 0;
fail:
while (--i >= 0)
led_classdev_unregister(&alix_leds[i].cdev);
return ret;
}
static int alix_led_remove(struct platform_device *pdev)
{
int i;
for (i = 0; i < ARRAY_SIZE(alix_leds); i++)
led_classdev_unregister(&alix_leds[i].cdev);
return 0;
}
static struct platform_driver alix_led_driver = {
.remove = alix_led_remove,
.driver = {
.name = KBUILD_MODNAME,
.owner = THIS_MODULE,
},
};
static int __init alix_present(void)
{
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;
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;
}
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;
if (memcmp(p, alix_sig, alix_sig_len) != 0) {
continue;
}
tail = p + alix_sig_len;
if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') {
printk(KERN_INFO
"%s: system is recognized as \"%s\"\n",
KBUILD_MODNAME, p);
ret = 1;
break;
}
}
out:
return ret;
}
static struct platform_device *pdev;
static int __init alix_led_init(void)
{
int ret;
if (!alix_present()) {
ret = -ENODEV;
goto out;
}
/* enable output on GPIO for LED 1,2,3 */
outl(1 << 6, 0x6104);
outl(1 << 9, 0x6184);
outl(1 << 11, 0x6184);
pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
if (!IS_ERR(pdev)) {
ret = platform_driver_probe(&alix_led_driver, alix_led_probe);
if (ret)
platform_device_unregister(pdev);
} else
ret = PTR_ERR(pdev);
out:
return ret;
}
static void __exit alix_led_exit(void)
{
platform_device_unregister(pdev);
platform_driver_unregister(&alix_led_driver);
}
module_init(alix_led_init);
module_exit(alix_led_exit);
MODULE_AUTHOR("Constantin Baranov <const@mimas.ru>");
MODULE_DESCRIPTION("PCEngines ALIX.2 and ALIX.3 LED driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,136 @@
/*
* LEDs driver for Amstrad Delta (E3)
*
* Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
*
* 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 <mach/board-ams-delta.h>
/*
* Our context
*/
struct ams_delta_led {
struct led_classdev cdev;
u8 bitmask;
};
static void ams_delta_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct ams_delta_led *led_dev =
container_of(led_cdev, struct ams_delta_led, cdev);
if (value)
ams_delta_latch1_write(led_dev->bitmask, led_dev->bitmask);
else
ams_delta_latch1_write(led_dev->bitmask, 0);
}
static struct ams_delta_led ams_delta_leds[] = {
{
.cdev = {
.name = "ams-delta::camera",
.brightness_set = ams_delta_led_set,
},
.bitmask = AMS_DELTA_LATCH1_LED_CAMERA,
},
{
.cdev = {
.name = "ams-delta::advert",
.brightness_set = ams_delta_led_set,
},
.bitmask = AMS_DELTA_LATCH1_LED_ADVERT,
},
{
.cdev = {
.name = "ams-delta::email",
.brightness_set = ams_delta_led_set,
},
.bitmask = AMS_DELTA_LATCH1_LED_EMAIL,
},
{
.cdev = {
.name = "ams-delta::handsfree",
.brightness_set = ams_delta_led_set,
},
.bitmask = AMS_DELTA_LATCH1_LED_HANDSFREE,
},
{
.cdev = {
.name = "ams-delta::voicemail",
.brightness_set = ams_delta_led_set,
},
.bitmask = AMS_DELTA_LATCH1_LED_VOICEMAIL,
},
{
.cdev = {
.name = "ams-delta::voice",
.brightness_set = ams_delta_led_set,
},
.bitmask = AMS_DELTA_LATCH1_LED_VOICE,
},
};
static int ams_delta_led_probe(struct platform_device *pdev)
{
int i, ret;
for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) {
ams_delta_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME;
ret = led_classdev_register(&pdev->dev,
&ams_delta_leds[i].cdev);
if (ret < 0)
goto fail;
}
return 0;
fail:
while (--i >= 0)
led_classdev_unregister(&ams_delta_leds[i].cdev);
return ret;
}
static int ams_delta_led_remove(struct platform_device *pdev)
{
int i;
for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++)
led_classdev_unregister(&ams_delta_leds[i].cdev);
return 0;
}
static struct platform_driver ams_delta_led_driver = {
.probe = ams_delta_led_probe,
.remove = ams_delta_led_remove,
.driver = {
.name = "ams-delta-led",
.owner = THIS_MODULE,
},
};
static int __init ams_delta_led_init(void)
{
return platform_driver_register(&ams_delta_led_driver);
}
static void __exit ams_delta_led_exit(void)
{
platform_driver_unregister(&ams_delta_led_driver);
}
module_init(ams_delta_led_init);
module_exit(ams_delta_led_exit);
MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
MODULE_DESCRIPTION("Amstrad Delta LED driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ams-delta-led");

View File

@@ -0,0 +1,160 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/io.h>
#include <linux/atmel_pwm.h>
struct pwmled {
struct led_classdev cdev;
struct pwm_channel pwmc;
struct gpio_led *desc;
u32 mult;
u8 active_low;
};
/*
* For simplicity, we use "brightness" as if it were a linear function
* of PWM duty cycle. However, a logarithmic function of duty cycle is
* probably a better match for perceived brightness: two is half as bright
* as four, four is half as bright as eight, etc
*/
static void pwmled_brightness(struct led_classdev *cdev, enum led_brightness b)
{
struct pwmled *led;
/* update the duty cycle for the *next* period */
led = container_of(cdev, struct pwmled, cdev);
pwm_channel_writel(&led->pwmc, PWM_CUPD, led->mult * (unsigned) b);
}
/*
* NOTE: we reuse the platform_data structure of GPIO leds,
* but repurpose its "gpio" number as a PWM channel number.
*/
static int __init pwmled_probe(struct platform_device *pdev)
{
const struct gpio_led_platform_data *pdata;
struct pwmled *leds;
int i;
int status;
pdata = pdev->dev.platform_data;
if (!pdata || pdata->num_leds < 1)
return -ENODEV;
leds = kcalloc(pdata->num_leds, sizeof(*leds), GFP_KERNEL);
if (!leds)
return -ENOMEM;
for (i = 0; i < pdata->num_leds; i++) {
struct pwmled *led = leds + i;
const struct gpio_led *dat = pdata->leds + i;
u32 tmp;
led->cdev.name = dat->name;
led->cdev.brightness = LED_OFF;
led->cdev.brightness_set = pwmled_brightness;
led->cdev.default_trigger = dat->default_trigger;
led->active_low = dat->active_low;
status = pwm_channel_alloc(dat->gpio, &led->pwmc);
if (status < 0)
goto err;
/*
* Prescale clock by 2^x, so PWM counts in low MHz.
* Start each cycle with the LED active, so increasing
* the duty cycle gives us more time on (== brighter).
*/
tmp = 5;
if (!led->active_low)
tmp |= PWM_CPR_CPOL;
pwm_channel_writel(&led->pwmc, PWM_CMR, tmp);
/*
* Pick a period so PWM cycles at 100+ Hz; and a multiplier
* for scaling duty cycle: brightness * mult.
*/
tmp = (led->pwmc.mck / (1 << 5)) / 100;
tmp /= 255;
led->mult = tmp;
pwm_channel_writel(&led->pwmc, PWM_CDTY,
led->cdev.brightness * 255);
pwm_channel_writel(&led->pwmc, PWM_CPRD,
LED_FULL * tmp);
pwm_channel_enable(&led->pwmc);
/* Hand it over to the LED framework */
status = led_classdev_register(&pdev->dev, &led->cdev);
if (status < 0) {
pwm_channel_free(&led->pwmc);
goto err;
}
}
platform_set_drvdata(pdev, leds);
return 0;
err:
if (i > 0) {
for (i = i - 1; i >= 0; i--) {
led_classdev_unregister(&leds[i].cdev);
pwm_channel_free(&leds[i].pwmc);
}
}
kfree(leds);
return status;
}
static int __exit pwmled_remove(struct platform_device *pdev)
{
const struct gpio_led_platform_data *pdata;
struct pwmled *leds;
unsigned i;
pdata = pdev->dev.platform_data;
leds = platform_get_drvdata(pdev);
for (i = 0; i < pdata->num_leds; i++) {
struct pwmled *led = leds + i;
led_classdev_unregister(&led->cdev);
pwm_channel_free(&led->pwmc);
}
kfree(leds);
platform_set_drvdata(pdev, NULL);
return 0;
}
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:leds-atmel-pwm");
static struct platform_driver pwmled_driver = {
.driver = {
.name = "leds-atmel-pwm",
.owner = THIS_MODULE,
},
/* REVISIT add suspend() and resume() methods */
.remove = __exit_p(pwmled_remove),
};
static int __init modinit(void)
{
return platform_driver_probe(&pwmled_driver, pwmled_probe);
}
module_init(modinit);
static void __exit modexit(void)
{
platform_driver_unregister(&pwmled_driver);
}
module_exit(modexit);
MODULE_DESCRIPTION("Driver for LEDs with PWM-controlled brightness");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,819 @@
/*
* leds-bd2802.c - RGB LED Driver
*
* Copyright (C) 2009 Samsung Electronics
* Kim Kyuwon <q1.kim@samsung.com>
*
* 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.
*
* Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf
*
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/leds.h>
#include <linux/leds-bd2802.h>
#define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0))
#define BD2802_LED_OFFSET 0xa
#define BD2802_COLOR_OFFSET 0x3
#define BD2802_REG_CLKSETUP 0x00
#define BD2802_REG_CONTROL 0x01
#define BD2802_REG_HOURSETUP 0x02
#define BD2802_REG_CURRENT1SETUP 0x03
#define BD2802_REG_CURRENT2SETUP 0x04
#define BD2802_REG_WAVEPATTERN 0x05
#define BD2802_CURRENT_032 0x10 /* 3.2mA */
#define BD2802_CURRENT_000 0x00 /* 0.0mA */
#define BD2802_PATTERN_FULL 0x07
#define BD2802_PATTERN_HALF 0x03
enum led_ids {
LED1,
LED2,
LED_NUM,
};
enum led_colors {
RED,
GREEN,
BLUE,
};
enum led_bits {
BD2802_OFF,
BD2802_BLINK,
BD2802_ON,
};
/*
* State '0' : 'off'
* State '1' : 'blink'
* State '2' : 'on'.
*/
struct led_state {
unsigned r:2;
unsigned g:2;
unsigned b:2;
};
struct bd2802_led {
struct bd2802_led_platform_data *pdata;
struct i2c_client *client;
struct rw_semaphore rwsem;
struct work_struct work;
struct led_state led[2];
/*
* Making led_classdev as array is not recommended, because array
* members prevent using 'container_of' macro. So repetitive works
* are needed.
*/
struct led_classdev cdev_led1r;
struct led_classdev cdev_led1g;
struct led_classdev cdev_led1b;
struct led_classdev cdev_led2r;
struct led_classdev cdev_led2g;
struct led_classdev cdev_led2b;
/*
* Advanced Configuration Function(ADF) mode:
* In ADF mode, user can set registers of BD2802GU directly,
* therefore BD2802GU doesn't enter reset state.
*/
int adf_on;
enum led_ids led_id;
enum led_colors color;
enum led_bits state;
/* General attributes of RGB LEDs */
int wave_pattern;
int rgb_current;
};
/*--------------------------------------------------------------*/
/* BD2802GU helper functions */
/*--------------------------------------------------------------*/
static inline int bd2802_is_rgb_off(struct bd2802_led *led, enum led_ids id,
enum led_colors color)
{
switch (color) {
case RED:
return !led->led[id].r;
case GREEN:
return !led->led[id].g;
case BLUE:
return !led->led[id].b;
default:
dev_err(&led->client->dev, "%s: Invalid color\n", __func__);
return -EINVAL;
}
}
static inline int bd2802_is_led_off(struct bd2802_led *led, enum led_ids id)
{
if (led->led[id].r || led->led[id].g || led->led[id].b)
return 0;
return 1;
}
static inline int bd2802_is_all_off(struct bd2802_led *led)
{
int i;
for (i = 0; i < LED_NUM; i++)
if (!bd2802_is_led_off(led, i))
return 0;
return 1;
}
static inline u8 bd2802_get_base_offset(enum led_ids id, enum led_colors color)
{
return id * BD2802_LED_OFFSET + color * BD2802_COLOR_OFFSET;
}
static inline u8 bd2802_get_reg_addr(enum led_ids id, enum led_colors color,
u8 reg_offset)
{
return reg_offset + bd2802_get_base_offset(id, color);
}
/*--------------------------------------------------------------*/
/* BD2802GU core functions */
/*--------------------------------------------------------------*/
static int bd2802_write_byte(struct i2c_client *client, u8 reg, u8 val)
{
int ret = i2c_smbus_write_byte_data(client, reg, val);
if (ret >= 0)
return 0;
dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
__func__, reg, val, ret);
return ret;
}
static void bd2802_update_state(struct bd2802_led *led, enum led_ids id,
enum led_colors color, enum led_bits led_bit)
{
int i;
u8 value;
for (i = 0; i < LED_NUM; i++) {
if (i == id) {
switch (color) {
case RED:
led->led[i].r = led_bit;
break;
case GREEN:
led->led[i].g = led_bit;
break;
case BLUE:
led->led[i].b = led_bit;
break;
default:
dev_err(&led->client->dev,
"%s: Invalid color\n", __func__);
return;
}
}
}
if (led_bit == BD2802_BLINK || led_bit == BD2802_ON)
return;
if (!bd2802_is_led_off(led, id))
return;
if (bd2802_is_all_off(led) && !led->adf_on) {
gpio_set_value(led->pdata->reset_gpio, 0);
return;
}
/*
* In this case, other led is turned on, and current led is turned
* off. So set RGB LED Control register to stop the current RGB LED
*/
value = (id == LED1) ? LED_CTL(1, 0) : LED_CTL(0, 1);
bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
}
static void bd2802_configure(struct bd2802_led *led)
{
struct bd2802_led_platform_data *pdata = led->pdata;
u8 reg;
reg = bd2802_get_reg_addr(LED1, RED, BD2802_REG_HOURSETUP);
bd2802_write_byte(led->client, reg, pdata->rgb_time);
reg = bd2802_get_reg_addr(LED2, RED, BD2802_REG_HOURSETUP);
bd2802_write_byte(led->client, reg, pdata->rgb_time);
}
static void bd2802_reset_cancel(struct bd2802_led *led)
{
gpio_set_value(led->pdata->reset_gpio, 1);
udelay(100);
bd2802_configure(led);
}
static void bd2802_enable(struct bd2802_led *led, enum led_ids id)
{
enum led_ids other_led = (id == LED1) ? LED2 : LED1;
u8 value, other_led_on;
other_led_on = !bd2802_is_led_off(led, other_led);
if (id == LED1)
value = LED_CTL(other_led_on, 1);
else
value = LED_CTL(1 , other_led_on);
bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
}
static void bd2802_set_on(struct bd2802_led *led, enum led_ids id,
enum led_colors color)
{
u8 reg;
if (bd2802_is_all_off(led) && !led->adf_on)
bd2802_reset_cancel(led);
reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
bd2802_write_byte(led->client, reg, led->rgb_current);
reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
bd2802_write_byte(led->client, reg, BD2802_PATTERN_FULL);
bd2802_enable(led, id);
bd2802_update_state(led, id, color, BD2802_ON);
}
static void bd2802_set_blink(struct bd2802_led *led, enum led_ids id,
enum led_colors color)
{
u8 reg;
if (bd2802_is_all_off(led) && !led->adf_on)
bd2802_reset_cancel(led);
reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
bd2802_write_byte(led->client, reg, led->rgb_current);
reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
bd2802_write_byte(led->client, reg, led->wave_pattern);
bd2802_enable(led, id);
bd2802_update_state(led, id, color, BD2802_BLINK);
}
static void bd2802_turn_on(struct bd2802_led *led, enum led_ids id,
enum led_colors color, enum led_bits led_bit)
{
if (led_bit == BD2802_OFF) {
dev_err(&led->client->dev,
"Only 'blink' and 'on' are allowed\n");
return;
}
if (led_bit == BD2802_BLINK)
bd2802_set_blink(led, id, color);
else
bd2802_set_on(led, id, color);
}
static void bd2802_turn_off(struct bd2802_led *led, enum led_ids id,
enum led_colors color)
{
u8 reg;
if (bd2802_is_rgb_off(led, id, color))
return;
reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
bd2802_update_state(led, id, color, BD2802_OFF);
}
static void bd2802_restore_state(struct bd2802_led *led)
{
int i;
for (i = 0; i < LED_NUM; i++) {
if (led->led[i].r)
bd2802_turn_on(led, i, RED, led->led[i].r);
if (led->led[i].g)
bd2802_turn_on(led, i, GREEN, led->led[i].g);
if (led->led[i].b)
bd2802_turn_on(led, i, BLUE, led->led[i].b);
}
}
#define BD2802_SET_REGISTER(reg_addr, reg_name) \
static ssize_t bd2802_store_reg##reg_addr(struct device *dev, \
struct device_attribute *attr, const char *buf, size_t count) \
{ \
struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
unsigned long val; \
int ret; \
if (!count) \
return -EINVAL; \
ret = strict_strtoul(buf, 16, &val); \
if (ret) \
return ret; \
down_write(&led->rwsem); \
bd2802_write_byte(led->client, reg_addr, (u8) val); \
up_write(&led->rwsem); \
return count; \
} \
static struct device_attribute bd2802_reg##reg_addr##_attr = { \
.attr = {.name = reg_name, .mode = 0644, .owner = THIS_MODULE}, \
.store = bd2802_store_reg##reg_addr, \
};
BD2802_SET_REGISTER(0x00, "0x00");
BD2802_SET_REGISTER(0x01, "0x01");
BD2802_SET_REGISTER(0x02, "0x02");
BD2802_SET_REGISTER(0x03, "0x03");
BD2802_SET_REGISTER(0x04, "0x04");
BD2802_SET_REGISTER(0x05, "0x05");
BD2802_SET_REGISTER(0x06, "0x06");
BD2802_SET_REGISTER(0x07, "0x07");
BD2802_SET_REGISTER(0x08, "0x08");
BD2802_SET_REGISTER(0x09, "0x09");
BD2802_SET_REGISTER(0x0a, "0x0a");
BD2802_SET_REGISTER(0x0b, "0x0b");
BD2802_SET_REGISTER(0x0c, "0x0c");
BD2802_SET_REGISTER(0x0d, "0x0d");
BD2802_SET_REGISTER(0x0e, "0x0e");
BD2802_SET_REGISTER(0x0f, "0x0f");
BD2802_SET_REGISTER(0x10, "0x10");
BD2802_SET_REGISTER(0x11, "0x11");
BD2802_SET_REGISTER(0x12, "0x12");
BD2802_SET_REGISTER(0x13, "0x13");
BD2802_SET_REGISTER(0x14, "0x14");
BD2802_SET_REGISTER(0x15, "0x15");
static struct device_attribute *bd2802_addr_attributes[] = {
&bd2802_reg0x00_attr,
&bd2802_reg0x01_attr,
&bd2802_reg0x02_attr,
&bd2802_reg0x03_attr,
&bd2802_reg0x04_attr,
&bd2802_reg0x05_attr,
&bd2802_reg0x06_attr,
&bd2802_reg0x07_attr,
&bd2802_reg0x08_attr,
&bd2802_reg0x09_attr,
&bd2802_reg0x0a_attr,
&bd2802_reg0x0b_attr,
&bd2802_reg0x0c_attr,
&bd2802_reg0x0d_attr,
&bd2802_reg0x0e_attr,
&bd2802_reg0x0f_attr,
&bd2802_reg0x10_attr,
&bd2802_reg0x11_attr,
&bd2802_reg0x12_attr,
&bd2802_reg0x13_attr,
&bd2802_reg0x14_attr,
&bd2802_reg0x15_attr,
};
static void bd2802_enable_adv_conf(struct bd2802_led *led)
{
int i, ret;
for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) {
ret = device_create_file(&led->client->dev,
bd2802_addr_attributes[i]);
if (ret) {
dev_err(&led->client->dev, "failed: sysfs file %s\n",
bd2802_addr_attributes[i]->attr.name);
goto failed_remove_files;
}
}
if (bd2802_is_all_off(led))
bd2802_reset_cancel(led);
led->adf_on = 1;
return;
failed_remove_files:
for (i--; i >= 0; i--)
device_remove_file(&led->client->dev,
bd2802_addr_attributes[i]);
}
static void bd2802_disable_adv_conf(struct bd2802_led *led)
{
int i;
for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++)
device_remove_file(&led->client->dev,
bd2802_addr_attributes[i]);
if (bd2802_is_all_off(led))
gpio_set_value(led->pdata->reset_gpio, 0);
led->adf_on = 0;
}
static ssize_t bd2802_show_adv_conf(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
ssize_t ret;
down_read(&led->rwsem);
if (led->adf_on)
ret = sprintf(buf, "on\n");
else
ret = sprintf(buf, "off\n");
up_read(&led->rwsem);
return ret;
}
static ssize_t bd2802_store_adv_conf(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
if (!count)
return -EINVAL;
down_write(&led->rwsem);
if (!led->adf_on && !strncmp(buf, "on", 2))
bd2802_enable_adv_conf(led);
else if (led->adf_on && !strncmp(buf, "off", 3))
bd2802_disable_adv_conf(led);
up_write(&led->rwsem);
return count;
}
static struct device_attribute bd2802_adv_conf_attr = {
.attr = {
.name = "advanced_configuration",
.mode = 0644,
.owner = THIS_MODULE
},
.show = bd2802_show_adv_conf,
.store = bd2802_store_adv_conf,
};
#define BD2802_CONTROL_ATTR(attr_name, name_str) \
static ssize_t bd2802_show_##attr_name(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
ssize_t ret; \
down_read(&led->rwsem); \
ret = sprintf(buf, "0x%02x\n", led->attr_name); \
up_read(&led->rwsem); \
return ret; \
} \
static ssize_t bd2802_store_##attr_name(struct device *dev, \
struct device_attribute *attr, const char *buf, size_t count) \
{ \
struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
unsigned long val; \
int ret; \
if (!count) \
return -EINVAL; \
ret = strict_strtoul(buf, 16, &val); \
if (ret) \
return ret; \
down_write(&led->rwsem); \
led->attr_name = val; \
up_write(&led->rwsem); \
return count; \
} \
static struct device_attribute bd2802_##attr_name##_attr = { \
.attr = { \
.name = name_str, \
.mode = 0644, \
.owner = THIS_MODULE \
}, \
.show = bd2802_show_##attr_name, \
.store = bd2802_store_##attr_name, \
};
BD2802_CONTROL_ATTR(wave_pattern, "wave_pattern");
BD2802_CONTROL_ATTR(rgb_current, "rgb_current");
static struct device_attribute *bd2802_attributes[] = {
&bd2802_adv_conf_attr,
&bd2802_wave_pattern_attr,
&bd2802_rgb_current_attr,
};
static void bd2802_led_work(struct work_struct *work)
{
struct bd2802_led *led = container_of(work, struct bd2802_led, work);
if (led->state)
bd2802_turn_on(led, led->led_id, led->color, led->state);
else
bd2802_turn_off(led, led->led_id, led->color);
}
#define BD2802_CONTROL_RGBS(name, id, clr) \
static void bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\
enum led_brightness value) \
{ \
struct bd2802_led *led = \
container_of(led_cdev, struct bd2802_led, cdev_##name); \
led->led_id = id; \
led->color = clr; \
if (value == LED_OFF) \
led->state = BD2802_OFF; \
else \
led->state = BD2802_ON; \
schedule_work(&led->work); \
} \
static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \
unsigned long *delay_on, unsigned long *delay_off) \
{ \
struct bd2802_led *led = \
container_of(led_cdev, struct bd2802_led, cdev_##name); \
if (*delay_on == 0 || *delay_off == 0) \
return -EINVAL; \
led->led_id = id; \
led->color = clr; \
led->state = BD2802_BLINK; \
schedule_work(&led->work); \
return 0; \
}
BD2802_CONTROL_RGBS(led1r, LED1, RED);
BD2802_CONTROL_RGBS(led1g, LED1, GREEN);
BD2802_CONTROL_RGBS(led1b, LED1, BLUE);
BD2802_CONTROL_RGBS(led2r, LED2, RED);
BD2802_CONTROL_RGBS(led2g, LED2, GREEN);
BD2802_CONTROL_RGBS(led2b, LED2, BLUE);
static int bd2802_register_led_classdev(struct bd2802_led *led)
{
int ret;
INIT_WORK(&led->work, bd2802_led_work);
led->cdev_led1r.name = "led1_R";
led->cdev_led1r.brightness = LED_OFF;
led->cdev_led1r.brightness_set = bd2802_set_led1r_brightness;
led->cdev_led1r.blink_set = bd2802_set_led1r_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led1r);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led1r.name);
goto failed_unregister_led1_R;
}
led->cdev_led1g.name = "led1_G";
led->cdev_led1g.brightness = LED_OFF;
led->cdev_led1g.brightness_set = bd2802_set_led1g_brightness;
led->cdev_led1g.blink_set = bd2802_set_led1g_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led1g);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led1g.name);
goto failed_unregister_led1_G;
}
led->cdev_led1b.name = "led1_B";
led->cdev_led1b.brightness = LED_OFF;
led->cdev_led1b.brightness_set = bd2802_set_led1b_brightness;
led->cdev_led1b.blink_set = bd2802_set_led1b_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led1b);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led1b.name);
goto failed_unregister_led1_B;
}
led->cdev_led2r.name = "led2_R";
led->cdev_led2r.brightness = LED_OFF;
led->cdev_led2r.brightness_set = bd2802_set_led2r_brightness;
led->cdev_led2r.blink_set = bd2802_set_led2r_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led2r);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led2r.name);
goto failed_unregister_led2_R;
}
led->cdev_led2g.name = "led2_G";
led->cdev_led2g.brightness = LED_OFF;
led->cdev_led2g.brightness_set = bd2802_set_led2g_brightness;
led->cdev_led2g.blink_set = bd2802_set_led2g_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led2g);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led2g.name);
goto failed_unregister_led2_G;
}
led->cdev_led2b.name = "led2_B";
led->cdev_led2b.brightness = LED_OFF;
led->cdev_led2b.brightness_set = bd2802_set_led2b_brightness;
led->cdev_led2b.blink_set = bd2802_set_led2b_blink;
led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME;
ret = led_classdev_register(&led->client->dev, &led->cdev_led2b);
if (ret < 0) {
dev_err(&led->client->dev, "couldn't register LED %s\n",
led->cdev_led2b.name);
goto failed_unregister_led2_B;
}
return 0;
failed_unregister_led2_B:
led_classdev_unregister(&led->cdev_led2g);
failed_unregister_led2_G:
led_classdev_unregister(&led->cdev_led2r);
failed_unregister_led2_R:
led_classdev_unregister(&led->cdev_led1b);
failed_unregister_led1_B:
led_classdev_unregister(&led->cdev_led1g);
failed_unregister_led1_G:
led_classdev_unregister(&led->cdev_led1r);
failed_unregister_led1_R:
return ret;
}
static void bd2802_unregister_led_classdev(struct bd2802_led *led)
{
cancel_work_sync(&led->work);
led_classdev_unregister(&led->cdev_led1r);
}
static int __devinit bd2802_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct bd2802_led *led;
struct bd2802_led_platform_data *pdata;
int ret, i;
led = kzalloc(sizeof(struct bd2802_led), GFP_KERNEL);
if (!led) {
dev_err(&client->dev, "failed to allocate driver data\n");
return -ENOMEM;
}
led->client = client;
pdata = led->pdata = client->dev.platform_data;
i2c_set_clientdata(client, led);
/* Configure RESET GPIO (L: RESET, H: RESET cancel) */
gpio_request(pdata->reset_gpio, "RGB_RESETB");
gpio_direction_output(pdata->reset_gpio, 1);
/* Tacss = min 0.1ms */
udelay(100);
/* Detect BD2802GU */
ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00);
if (ret < 0) {
dev_err(&client->dev, "failed to detect device\n");
goto failed_free;
} else
dev_info(&client->dev, "return 0x%02x\n", ret);
/* To save the power, reset BD2802 after detecting */
gpio_set_value(led->pdata->reset_gpio, 0);
/* Default attributes */
led->wave_pattern = BD2802_PATTERN_HALF;
led->rgb_current = BD2802_CURRENT_032;
init_rwsem(&led->rwsem);
for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++) {
ret = device_create_file(&led->client->dev,
bd2802_attributes[i]);
if (ret) {
dev_err(&led->client->dev, "failed: sysfs file %s\n",
bd2802_attributes[i]->attr.name);
goto failed_unregister_dev_file;
}
}
ret = bd2802_register_led_classdev(led);
if (ret < 0)
goto failed_unregister_dev_file;
return 0;
failed_unregister_dev_file:
for (i--; i >= 0; i--)
device_remove_file(&led->client->dev, bd2802_attributes[i]);
failed_free:
i2c_set_clientdata(client, NULL);
kfree(led);
return ret;
}
static int __exit bd2802_remove(struct i2c_client *client)
{
struct bd2802_led *led = i2c_get_clientdata(client);
int i;
gpio_set_value(led->pdata->reset_gpio, 0);
bd2802_unregister_led_classdev(led);
if (led->adf_on)
bd2802_disable_adv_conf(led);
for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++)
device_remove_file(&led->client->dev, bd2802_attributes[i]);
i2c_set_clientdata(client, NULL);
kfree(led);
return 0;
}
static int bd2802_suspend(struct i2c_client *client, pm_message_t mesg)
{
struct bd2802_led *led = i2c_get_clientdata(client);
gpio_set_value(led->pdata->reset_gpio, 0);
return 0;
}
static int bd2802_resume(struct i2c_client *client)
{
struct bd2802_led *led = i2c_get_clientdata(client);
if (!bd2802_is_all_off(led) || led->adf_on) {
bd2802_reset_cancel(led);
bd2802_restore_state(led);
}
return 0;
}
static const struct i2c_device_id bd2802_id[] = {
{ "BD2802", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, bd2802_id);
static struct i2c_driver bd2802_i2c_driver = {
.driver = {
.name = "BD2802",
},
.probe = bd2802_probe,
.remove = __exit_p(bd2802_remove),
.suspend = bd2802_suspend,
.resume = bd2802_resume,
.id_table = bd2802_id,
};
static int __init bd2802_init(void)
{
return i2c_add_driver(&bd2802_i2c_driver);
}
module_init(bd2802_init);
static void __exit bd2802_exit(void)
{
i2c_del_driver(&bd2802_i2c_driver);
}
module_exit(bd2802_exit);
MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
MODULE_DESCRIPTION("BD2802 LED driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,219 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/leds.h>
#include <linux/io.h>
#include <linux/dmi.h>
#include <linux/i8042.h>
#define CLEVO_MAIL_LED_OFF 0x0084
#define CLEVO_MAIL_LED_BLINK_1HZ 0x008A
#define CLEVO_MAIL_LED_BLINK_0_5HZ 0x0083
MODULE_AUTHOR("Márton Németh <nm127@freemail.hu>");
MODULE_DESCRIPTION("Clevo mail LED driver");
MODULE_LICENSE("GPL");
static unsigned int __initdata nodetect;
module_param_named(nodetect, nodetect, bool, 0);
MODULE_PARM_DESC(nodetect, "Skip DMI hardware detection");
static struct platform_device *pdev;
static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id)
{
printk(KERN_INFO KBUILD_MODNAME ": '%s' found\n", id->ident);
return 1;
}
/*
* struct mail_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 mail_led_whitelist[] = {
{
.callback = clevo_mail_led_dmi_callback,
.ident = "Clevo D410J",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "VIA"),
DMI_MATCH(DMI_PRODUCT_NAME, "K8N800"),
DMI_MATCH(DMI_PRODUCT_VERSION, "VT8204B")
}
},
{
.callback = clevo_mail_led_dmi_callback,
.ident = "Clevo M5x0N",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."),
DMI_MATCH(DMI_PRODUCT_NAME, "M5x0N")
}
},
{
.callback = clevo_mail_led_dmi_callback,
.ident = "Positivo Mobile",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "CLEVO Co. "),
DMI_MATCH(DMI_BOARD_NAME, "M5X0V "),
DMI_MATCH(DMI_PRODUCT_NAME, "Positivo Mobile"),
DMI_MATCH(DMI_PRODUCT_VERSION, "VT6198")
}
},
{
.callback = clevo_mail_led_dmi_callback,
.ident = "Clevo D400P",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Clevo"),
DMI_MATCH(DMI_BOARD_NAME, "D400P"),
DMI_MATCH(DMI_BOARD_VERSION, "Rev.A"),
DMI_MATCH(DMI_PRODUCT_VERSION, "0106")
}
},
{
.callback = clevo_mail_led_dmi_callback,
.ident = "Clevo D410V",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Clevo, Co."),
DMI_MATCH(DMI_BOARD_NAME, "D400V/D470V"),
DMI_MATCH(DMI_BOARD_VERSION, "SS78B"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Rev. A1")
}
},
{ }
};
static void clevo_mail_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
i8042_lock_chip();
if (value == LED_OFF)
i8042_command(NULL, CLEVO_MAIL_LED_OFF);
else if (value <= LED_HALF)
i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
else
i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
i8042_unlock_chip();
}
static int clevo_mail_led_blink(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
int status = -EINVAL;
i8042_lock_chip();
if (*delay_on == 0 /* ms */ && *delay_off == 0 /* ms */) {
/* Special case: the leds subsystem requested us to
* chose one user friendly blinking of the LED, and
* start it. Let's blink the led slowly (0.5Hz).
*/
*delay_on = 1000; /* ms */
*delay_off = 1000; /* ms */
i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
status = 0;
} else if (*delay_on == 500 /* ms */ && *delay_off == 500 /* ms */) {
/* blink the led with 1Hz */
i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
status = 0;
} else if (*delay_on == 1000 /* ms */ && *delay_off == 1000 /* ms */) {
/* blink the led with 0.5Hz */
i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
status = 0;
} else {
printk(KERN_DEBUG KBUILD_MODNAME
": clevo_mail_led_blink(..., %lu, %lu),"
" returning -EINVAL (unsupported)\n",
*delay_on, *delay_off);
}
i8042_unlock_chip();
return status;
}
static struct led_classdev clevo_mail_led = {
.name = "clevo::mail",
.brightness_set = clevo_mail_led_set,
.blink_set = clevo_mail_led_blink,
.flags = LED_CORE_SUSPENDRESUME,
};
static int __devinit clevo_mail_led_probe(struct platform_device *pdev)
{
return led_classdev_register(&pdev->dev, &clevo_mail_led);
}
static int clevo_mail_led_remove(struct platform_device *pdev)
{
led_classdev_unregister(&clevo_mail_led);
return 0;
}
static struct platform_driver clevo_mail_led_driver = {
.probe = clevo_mail_led_probe,
.remove = clevo_mail_led_remove,
.driver = {
.name = KBUILD_MODNAME,
.owner = THIS_MODULE,
},
};
static int __init clevo_mail_led_init(void)
{
int error = 0;
int count = 0;
/* Check with the help of DMI if we are running on supported hardware */
if (!nodetect) {
count = dmi_check_system(mail_led_whitelist);
} else {
count = 1;
printk(KERN_ERR KBUILD_MODNAME ": Skipping DMI detection. "
"If the driver works on your hardware please "
"report model and the output of dmidecode in tracker "
"at http://sourceforge.net/projects/clevo-mailled/\n");
}
if (!count)
return -ENODEV;
pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
if (!IS_ERR(pdev)) {
error = platform_driver_probe(&clevo_mail_led_driver,
clevo_mail_led_probe);
if (error) {
printk(KERN_ERR KBUILD_MODNAME
": Can't probe platform driver\n");
platform_device_unregister(pdev);
}
} else
error = PTR_ERR(pdev);
return error;
}
static void __exit clevo_mail_led_exit(void)
{
platform_device_unregister(pdev);
platform_driver_unregister(&clevo_mail_led_driver);
clevo_mail_led_set(NULL, LED_OFF);
}
module_init(clevo_mail_led_init);
module_exit(clevo_mail_led_exit);

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2006 - Florian Fainelli <florian@openwrt.org>
*
* Control the Cobalt Qube/RaQ front LED
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#define LED_FRONT_LEFT 0x01
#define LED_FRONT_RIGHT 0x02
static void __iomem *led_port;
static u8 led_value;
static void qube_front_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
if (brightness)
led_value = LED_FRONT_LEFT | LED_FRONT_RIGHT;
else
led_value = ~(LED_FRONT_LEFT | LED_FRONT_RIGHT);
writeb(led_value, led_port);
}
static struct led_classdev qube_front_led = {
.name = "qube::front",
.brightness = LED_FULL,
.brightness_set = qube_front_led_set,
.default_trigger = "ide-disk",
};
static int __devinit cobalt_qube_led_probe(struct platform_device *pdev)
{
struct resource *res;
int retval;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EBUSY;
led_port = ioremap(res->start, res->end - res->start + 1);
if (!led_port)
return -ENOMEM;
led_value = LED_FRONT_LEFT | LED_FRONT_RIGHT;
writeb(led_value, led_port);
retval = led_classdev_register(&pdev->dev, &qube_front_led);
if (retval)
goto err_iounmap;
return 0;
err_iounmap:
iounmap(led_port);
led_port = NULL;
return retval;
}
static int __devexit cobalt_qube_led_remove(struct platform_device *pdev)
{
led_classdev_unregister(&qube_front_led);
if (led_port) {
iounmap(led_port);
led_port = NULL;
}
return 0;
}
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:cobalt-qube-leds");
static struct platform_driver cobalt_qube_led_driver = {
.probe = cobalt_qube_led_probe,
.remove = __devexit_p(cobalt_qube_led_remove),
.driver = {
.name = "cobalt-qube-leds",
.owner = THIS_MODULE,
},
};
static int __init cobalt_qube_led_init(void)
{
return platform_driver_register(&cobalt_qube_led_driver);
}
static void __exit cobalt_qube_led_exit(void)
{
platform_driver_unregister(&cobalt_qube_led_driver);
}
module_init(cobalt_qube_led_init);
module_exit(cobalt_qube_led_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Front LED support for Cobalt Server");
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");

View File

@@ -0,0 +1,138 @@
/*
* LEDs driver for the Cobalt Raq series.
*
* Copyright (C) 2007 Yoichi Yuasa <yuasa@linux-mips.org>
*
* 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.
*
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/leds.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#define LED_WEB 0x04
#define LED_POWER_OFF 0x08
static void __iomem *led_port;
static u8 led_value;
static DEFINE_SPINLOCK(led_value_lock);
static void raq_web_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
unsigned long flags;
spin_lock_irqsave(&led_value_lock, flags);
if (brightness)
led_value |= LED_WEB;
else
led_value &= ~LED_WEB;
writeb(led_value, led_port);
spin_unlock_irqrestore(&led_value_lock, flags);
}
static struct led_classdev raq_web_led = {
.name = "raq::web",
.brightness_set = raq_web_led_set,
};
static void raq_power_off_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
unsigned long flags;
spin_lock_irqsave(&led_value_lock, flags);
if (brightness)
led_value |= LED_POWER_OFF;
else
led_value &= ~LED_POWER_OFF;
writeb(led_value, led_port);
spin_unlock_irqrestore(&led_value_lock, flags);
}
static struct led_classdev raq_power_off_led = {
.name = "raq::power-off",
.brightness_set = raq_power_off_led_set,
.default_trigger = "power-off",
};
static int __devinit cobalt_raq_led_probe(struct platform_device *pdev)
{
struct resource *res;
int retval;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EBUSY;
led_port = ioremap(res->start, res->end - res->start + 1);
if (!led_port)
return -ENOMEM;
retval = led_classdev_register(&pdev->dev, &raq_power_off_led);
if (retval)
goto err_iounmap;
retval = led_classdev_register(&pdev->dev, &raq_web_led);
if (retval)
goto err_unregister;
return 0;
err_unregister:
led_classdev_unregister(&raq_power_off_led);
err_iounmap:
iounmap(led_port);
led_port = NULL;
return retval;
}
static int __devexit cobalt_raq_led_remove(struct platform_device *pdev)
{
led_classdev_unregister(&raq_power_off_led);
led_classdev_unregister(&raq_web_led);
if (led_port) {
iounmap(led_port);
led_port = NULL;
}
return 0;
}
static struct platform_driver cobalt_raq_led_driver = {
.probe = cobalt_raq_led_probe,
.remove = __devexit_p(cobalt_raq_led_remove),
.driver = {
.name = "cobalt-raq-leds",
.owner = THIS_MODULE,
},
};
static int __init cobalt_raq_led_init(void)
{
return platform_driver_register(&cobalt_raq_led_driver);
}
module_init(cobalt_raq_led_init);

View File

@@ -0,0 +1,176 @@
/*
* LEDs driver for Dialog Semiconductor DA9030/DA9034
*
* Copyright (C) 2008 Compulab, Ltd.
* Mike Rapoport <mike@compulab.co.il>
*
* Copyright (C) 2006-2008 Marvell International Ltd.
* Eric Miao <eric.miao@marvell.com>
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
#include <linux/mfd/da903x.h>
#define DA9030_LED1_CONTROL 0x20
#define DA9030_LED2_CONTROL 0x21
#define DA9030_LED3_CONTROL 0x22
#define DA9030_LED4_CONTROL 0x23
#define DA9030_LEDPC_CONTROL 0x24
#define DA9030_MISC_CONTROL_A 0x26 /* Vibrator Control */
#define DA9034_LED1_CONTROL 0x35
#define DA9034_LED2_CONTROL 0x36
#define DA9034_VIBRA 0x40
struct da903x_led {
struct led_classdev cdev;
struct work_struct work;
struct device *master;
enum led_brightness new_brightness;
int id;
int flags;
};
#define DA9030_LED_OFFSET(id) ((id) - DA9030_ID_LED_1)
#define DA9034_LED_OFFSET(id) ((id) - DA9034_ID_LED_1)
static void da903x_led_work(struct work_struct *work)
{
struct da903x_led *led = container_of(work, struct da903x_led, work);
uint8_t val;
int offset;
switch (led->id) {
case DA9030_ID_LED_1:
case DA9030_ID_LED_2:
case DA9030_ID_LED_3:
case DA9030_ID_LED_4:
case DA9030_ID_LED_PC:
offset = DA9030_LED_OFFSET(led->id);
val = led->flags & ~0x87;
val |= (led->new_brightness) ? 0x80 : 0; /* EN bit */
val |= (0x7 - (led->new_brightness >> 5)) & 0x7; /* PWM<2:0> */
da903x_write(led->master, DA9030_LED1_CONTROL + offset, val);
break;
case DA9030_ID_VIBRA:
val = led->flags & ~0x80;
val |= (led->new_brightness) ? 0x80 : 0; /* EN bit */
da903x_write(led->master, DA9030_MISC_CONTROL_A, val);
break;
case DA9034_ID_LED_1:
case DA9034_ID_LED_2:
offset = DA9034_LED_OFFSET(led->id);
val = (led->new_brightness * 0x5f / LED_FULL) & 0x7f;
val |= (led->flags & DA9034_LED_RAMP) ? 0x80 : 0;
da903x_write(led->master, DA9034_LED1_CONTROL + offset, val);
break;
case DA9034_ID_VIBRA:
val = led->new_brightness & 0xfe;
da903x_write(led->master, DA9034_VIBRA, val);
break;
}
}
static void da903x_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct da903x_led *led;
led = container_of(led_cdev, struct da903x_led, cdev);
led->new_brightness = value;
schedule_work(&led->work);
}
static int __devinit da903x_led_probe(struct platform_device *pdev)
{
struct led_info *pdata = pdev->dev.platform_data;
struct da903x_led *led;
int id, ret;
if (pdata == NULL)
return 0;
id = pdev->id;
if (!((id >= DA9030_ID_LED_1 && id <= DA9030_ID_VIBRA) ||
(id >= DA9034_ID_LED_1 && id <= DA9034_ID_VIBRA))) {
dev_err(&pdev->dev, "invalid LED ID (%d) specified\n", id);
return -EINVAL;
}
led = kzalloc(sizeof(struct da903x_led), GFP_KERNEL);
if (led == NULL) {
dev_err(&pdev->dev, "failed to alloc memory for LED%d\n", id);
return -ENOMEM;
}
led->cdev.name = pdata->name;
led->cdev.default_trigger = pdata->default_trigger;
led->cdev.brightness_set = da903x_led_set;
led->cdev.brightness = LED_OFF;
led->id = id;
led->flags = pdata->flags;
led->master = pdev->dev.parent;
led->new_brightness = LED_OFF;
INIT_WORK(&led->work, da903x_led_work);
ret = led_classdev_register(led->master, &led->cdev);
if (ret) {
dev_err(&pdev->dev, "failed to register LED %d\n", id);
goto err;
}
platform_set_drvdata(pdev, led);
return 0;
err:
kfree(led);
return ret;
}
static int __devexit da903x_led_remove(struct platform_device *pdev)
{
struct da903x_led *led = platform_get_drvdata(pdev);
led_classdev_unregister(&led->cdev);
kfree(led);
return 0;
}
static struct platform_driver da903x_led_driver = {
.driver = {
.name = "da903x-led",
.owner = THIS_MODULE,
},
.probe = da903x_led_probe,
.remove = __devexit_p(da903x_led_remove),
};
static int __init da903x_led_init(void)
{
return platform_driver_register(&da903x_led_driver);
}
module_init(da903x_led_init);
static void __exit da903x_led_exit(void)
{
platform_driver_unregister(&da903x_led_driver);
}
module_exit(da903x_led_exit);
MODULE_DESCRIPTION("LEDs driver for Dialog Semiconductor DA9030/DA9034");
MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"
"Mike Rapoport <mike@compulab.co.il>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:da903x-led");

View File

@@ -0,0 +1,151 @@
/*
* Copyright 2008
* Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
*
* LED driver for the DAC124S085 SPI DAC
*/
#include <linux/gfp.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/spi/spi.h>
struct dac124s085_led {
struct led_classdev ldev;
struct spi_device *spi;
int id;
int brightness;
char name[sizeof("dac124s085-3")];
struct mutex mutex;
struct work_struct work;
spinlock_t lock;
};
struct dac124s085 {
struct dac124s085_led leds[4];
};
#define REG_WRITE (0 << 12)
#define REG_WRITE_UPDATE (1 << 12)
#define ALL_WRITE_UPDATE (2 << 12)
#define POWER_DOWN_OUTPUT (3 << 12)
static void dac124s085_led_work(struct work_struct *work)
{
struct dac124s085_led *led = container_of(work, struct dac124s085_led,
work);
u16 word;
mutex_lock(&led->mutex);
word = cpu_to_le16(((led->id) << 14) | REG_WRITE_UPDATE |
(led->brightness & 0xfff));
spi_write(led->spi, (const u8 *)&word, sizeof(word));
mutex_unlock(&led->mutex);
}
static void dac124s085_set_brightness(struct led_classdev *ldev,
enum led_brightness brightness)
{
struct dac124s085_led *led = container_of(ldev, struct dac124s085_led,
ldev);
spin_lock(&led->lock);
led->brightness = brightness;
schedule_work(&led->work);
spin_unlock(&led->lock);
}
static int dac124s085_probe(struct spi_device *spi)
{
struct dac124s085 *dac;
struct dac124s085_led *led;
int i, ret;
dac = kzalloc(sizeof(*dac), GFP_KERNEL);
if (!dac)
return -ENOMEM;
spi->bits_per_word = 16;
for (i = 0; i < ARRAY_SIZE(dac->leds); i++) {
led = dac->leds + i;
led->id = i;
led->brightness = LED_OFF;
led->spi = spi;
snprintf(led->name, sizeof(led->name), "dac124s085-%d", i);
spin_lock_init(&led->lock);
INIT_WORK(&led->work, dac124s085_led_work);
mutex_init(&led->mutex);
led->ldev.name = led->name;
led->ldev.brightness = LED_OFF;
led->ldev.max_brightness = 0xfff;
led->ldev.brightness_set = dac124s085_set_brightness;
ret = led_classdev_register(&spi->dev, &led->ldev);
if (ret < 0)
goto eledcr;
}
spi_set_drvdata(spi, dac);
return 0;
eledcr:
while (i--)
led_classdev_unregister(&dac->leds[i].ldev);
spi_set_drvdata(spi, NULL);
kfree(dac);
return ret;
}
static int dac124s085_remove(struct spi_device *spi)
{
struct dac124s085 *dac = spi_get_drvdata(spi);
int i;
for (i = 0; i < ARRAY_SIZE(dac->leds); i++) {
led_classdev_unregister(&dac->leds[i].ldev);
cancel_work_sync(&dac->leds[i].work);
}
spi_set_drvdata(spi, NULL);
kfree(dac);
return 0;
}
static struct spi_driver dac124s085_driver = {
.probe = dac124s085_probe,
.remove = dac124s085_remove,
.driver = {
.name = "dac124s085",
.owner = THIS_MODULE,
},
};
static int __init dac124s085_leds_init(void)
{
return spi_register_driver(&dac124s085_driver);
}
static void __exit dac124s085_leds_exit(void)
{
spi_unregister_driver(&dac124s085_driver);
}
module_init(dac124s085_leds_init);
module_exit(dac124s085_leds_exit);
MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
MODULE_DESCRIPTION("DAC124S085 LED driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("spi:dac124s085");

View File

@@ -0,0 +1,236 @@
/*
* LED Driver for the Freecom FSG-3
*
* Copyright (c) 2008 Rod Whitby <rod@whitby.id.au>
*
* Author: Rod Whitby <rod@whitby.id.au>
*
* Based on leds-spitz.c
* Copyright 2005-2006 Openedhand Ltd.
* Author: Richard Purdie <rpurdie@openedhand.com>
*
* 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 <mach/hardware.h>
#include <asm/io.h>
static short __iomem *latch_address;
static unsigned short latch_value;
static void fsg_led_wlan_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (value) {
latch_value &= ~(1 << FSG_LED_WLAN_BIT);
*latch_address = latch_value;
} else {
latch_value |= (1 << FSG_LED_WLAN_BIT);
*latch_address = latch_value;
}
}
static void fsg_led_wan_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (value) {
latch_value &= ~(1 << FSG_LED_WAN_BIT);
*latch_address = latch_value;
} else {
latch_value |= (1 << FSG_LED_WAN_BIT);
*latch_address = latch_value;
}
}
static void fsg_led_sata_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (value) {
latch_value &= ~(1 << FSG_LED_SATA_BIT);
*latch_address = latch_value;
} else {
latch_value |= (1 << FSG_LED_SATA_BIT);
*latch_address = latch_value;
}
}
static void fsg_led_usb_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (value) {
latch_value &= ~(1 << FSG_LED_USB_BIT);
*latch_address = latch_value;
} else {
latch_value |= (1 << FSG_LED_USB_BIT);
*latch_address = latch_value;
}
}
static void fsg_led_sync_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (value) {
latch_value &= ~(1 << FSG_LED_SYNC_BIT);
*latch_address = latch_value;
} else {
latch_value |= (1 << FSG_LED_SYNC_BIT);
*latch_address = latch_value;
}
}
static void fsg_led_ring_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (value) {
latch_value &= ~(1 << FSG_LED_RING_BIT);
*latch_address = latch_value;
} else {
latch_value |= (1 << FSG_LED_RING_BIT);
*latch_address = latch_value;
}
}
static struct led_classdev fsg_wlan_led = {
.name = "fsg:blue:wlan",
.brightness_set = fsg_led_wlan_set,
.flags = LED_CORE_SUSPENDRESUME,
};
static struct led_classdev fsg_wan_led = {
.name = "fsg:blue:wan",
.brightness_set = fsg_led_wan_set,
.flags = LED_CORE_SUSPENDRESUME,
};
static struct led_classdev fsg_sata_led = {
.name = "fsg:blue:sata",
.brightness_set = fsg_led_sata_set,
.flags = LED_CORE_SUSPENDRESUME,
};
static struct led_classdev fsg_usb_led = {
.name = "fsg:blue:usb",
.brightness_set = fsg_led_usb_set,
.flags = LED_CORE_SUSPENDRESUME,
};
static struct led_classdev fsg_sync_led = {
.name = "fsg:blue:sync",
.brightness_set = fsg_led_sync_set,
.flags = LED_CORE_SUSPENDRESUME,
};
static struct led_classdev fsg_ring_led = {
.name = "fsg:blue:ring",
.brightness_set = fsg_led_ring_set,
.flags = LED_CORE_SUSPENDRESUME,
};
static int fsg_led_probe(struct platform_device *pdev)
{
int ret;
/* Map the LED chip select address space */
latch_address = (unsigned short *) ioremap(IXP4XX_EXP_BUS_BASE(2), 512);
if (!latch_address) {
ret = -ENOMEM;
goto failremap;
}
latch_value = 0xffff;
*latch_address = latch_value;
ret = led_classdev_register(&pdev->dev, &fsg_wlan_led);
if (ret < 0)
goto failwlan;
ret = led_classdev_register(&pdev->dev, &fsg_wan_led);
if (ret < 0)
goto failwan;
ret = led_classdev_register(&pdev->dev, &fsg_sata_led);
if (ret < 0)
goto failsata;
ret = led_classdev_register(&pdev->dev, &fsg_usb_led);
if (ret < 0)
goto failusb;
ret = led_classdev_register(&pdev->dev, &fsg_sync_led);
if (ret < 0)
goto failsync;
ret = led_classdev_register(&pdev->dev, &fsg_ring_led);
if (ret < 0)
goto failring;
return ret;
failring:
led_classdev_unregister(&fsg_sync_led);
failsync:
led_classdev_unregister(&fsg_usb_led);
failusb:
led_classdev_unregister(&fsg_sata_led);
failsata:
led_classdev_unregister(&fsg_wan_led);
failwan:
led_classdev_unregister(&fsg_wlan_led);
failwlan:
iounmap(latch_address);
failremap:
return ret;
}
static int fsg_led_remove(struct platform_device *pdev)
{
led_classdev_unregister(&fsg_wlan_led);
led_classdev_unregister(&fsg_wan_led);
led_classdev_unregister(&fsg_sata_led);
led_classdev_unregister(&fsg_usb_led);
led_classdev_unregister(&fsg_sync_led);
led_classdev_unregister(&fsg_ring_led);
iounmap(latch_address);
return 0;
}
static struct platform_driver fsg_led_driver = {
.probe = fsg_led_probe,
.remove = fsg_led_remove,
.driver = {
.name = "fsg-led",
},
};
static int __init fsg_led_init(void)
{
return platform_driver_register(&fsg_led_driver);
}
static void __exit fsg_led_exit(void)
{
platform_driver_unregister(&fsg_led_driver);
}
module_init(fsg_led_init);
module_exit(fsg_led_exit);
MODULE_AUTHOR("Rod Whitby <rod@whitby.id.au>");
MODULE_DESCRIPTION("Freecom FSG-3 LED driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,335 @@
/*
* LEDs driver for GPIOs
*
* 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 <asm/gpio.h>
struct gpio_led_data {
struct led_classdev cdev;
unsigned gpio;
struct work_struct work;
u8 new_level;
u8 can_sleep;
u8 active_low;
int (*platform_gpio_blink_set)(unsigned gpio,
unsigned long *delay_on, unsigned long *delay_off);
};
static void gpio_led_work(struct work_struct *work)
{
struct gpio_led_data *led_dat =
container_of(work, struct gpio_led_data, work);
gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level);
}
static void gpio_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct gpio_led_data *led_dat =
container_of(led_cdev, struct gpio_led_data, cdev);
int level;
if (value == LED_OFF)
level = 0;
else
level = 1;
if (led_dat->active_low)
level = !level;
/* Setting GPIOs with I2C/etc requires a task context, and we don't
* seem to have a reliable way to know if we're already in one; so
* let's just assume the worst.
*/
if (led_dat->can_sleep) {
led_dat->new_level = level;
schedule_work(&led_dat->work);
} else
gpio_set_value(led_dat->gpio, level);
}
static int gpio_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_off)
{
struct gpio_led_data *led_dat =
container_of(led_cdev, struct gpio_led_data, cdev);
return led_dat->platform_gpio_blink_set(led_dat->gpio, delay_on, delay_off);
}
static int __devinit create_gpio_led(const struct gpio_led *template,
struct gpio_led_data *led_dat, struct device *parent,
int (*blink_set)(unsigned, unsigned long *, unsigned long *))
{
int ret, state;
led_dat->gpio = -1;
/* skip leds that aren't available */
if (!gpio_is_valid(template->gpio)) {
printk(KERN_INFO "Skipping unavailable LED gpio %d (%s)\n",
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->can_sleep = gpio_cansleep(template->gpio);
led_dat->active_low = template->active_low;
if (blink_set) {
led_dat->platform_gpio_blink_set = blink_set;
led_dat->cdev.blink_set = gpio_blink_set;
}
led_dat->cdev.brightness_set = gpio_led_set;
if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
state = !!gpio_get_value(led_dat->gpio) ^ led_dat->active_low;
else
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, led_dat->active_low ^ state);
if (ret < 0)
goto err;
INIT_WORK(&led_dat->work, gpio_led_work);
ret = led_classdev_register(parent, &led_dat->cdev);
if (ret < 0)
goto err;
return 0;
err:
gpio_free(led_dat->gpio);
return ret;
}
static void delete_gpio_led(struct gpio_led_data *led)
{
if (!gpio_is_valid(led->gpio))
return;
led_classdev_unregister(&led->cdev);
cancel_work_sync(&led->work);
gpio_free(led->gpio);
}
#ifdef CONFIG_LEDS_GPIO_PLATFORM
static int __devinit gpio_led_probe(struct platform_device *pdev)
{
struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
struct gpio_led_data *leds_data;
int i, ret = 0;
if (!pdata)
return -EBUSY;
leds_data = kzalloc(sizeof(struct gpio_led_data) * pdata->num_leds,
GFP_KERNEL);
if (!leds_data)
return -ENOMEM;
for (i = 0; i < pdata->num_leds; i++) {
ret = create_gpio_led(&pdata->leds[i], &leds_data[i],
&pdev->dev, pdata->gpio_blink_set);
if (ret < 0)
goto err;
}
platform_set_drvdata(pdev, leds_data);
return 0;
err:
for (i = i - 1; i >= 0; i--)
delete_gpio_led(&leds_data[i]);
kfree(leds_data);
return ret;
}
static int __devexit gpio_led_remove(struct platform_device *pdev)
{
int i;
struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
struct gpio_led_data *leds_data;
leds_data = platform_get_drvdata(pdev);
for (i = 0; i < pdata->num_leds; i++)
delete_gpio_led(&leds_data[i]);
kfree(leds_data);
return 0;
}
static struct platform_driver gpio_led_driver = {
.probe = gpio_led_probe,
.remove = __devexit_p(gpio_led_remove),
.driver = {
.name = "leds-gpio",
.owner = THIS_MODULE,
},
};
MODULE_ALIAS("platform:leds-gpio");
#endif /* CONFIG_LEDS_GPIO_PLATFORM */
/* Code to create from OpenFirmware platform devices */
#ifdef CONFIG_LEDS_GPIO_OF
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
struct gpio_led_of_platform_data {
int num_leds;
struct gpio_led_data led_data[];
};
static int __devinit of_gpio_leds_probe(struct of_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->node, *child;
struct gpio_led_of_platform_data *pdata;
int count = 0, ret;
/* count LEDs defined by this device, so we know how much to allocate */
for_each_child_of_node(np, child)
count++;
if (!count)
return 0; /* or ENODEV? */
pdata = kzalloc(sizeof(*pdata) + sizeof(struct gpio_led_data) * count,
GFP_KERNEL);
if (!pdata)
return -ENOMEM;
for_each_child_of_node(np, child) {
struct gpio_led led = {};
enum of_gpio_flags flags;
const char *state;
led.gpio = of_get_gpio_flags(child, 0, &flags);
led.active_low = flags & OF_GPIO_ACTIVE_LOW;
led.name = of_get_property(child, "label", NULL) ? : child->name;
led.default_trigger =
of_get_property(child, "linux,default-trigger", NULL);
state = of_get_property(child, "default-state", NULL);
if (state) {
if (!strcmp(state, "keep"))
led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
else if(!strcmp(state, "on"))
led.default_state = LEDS_GPIO_DEFSTATE_ON;
else
led.default_state = LEDS_GPIO_DEFSTATE_OFF;
}
ret = create_gpio_led(&led, &pdata->led_data[pdata->num_leds++],
&ofdev->dev, NULL);
if (ret < 0) {
of_node_put(child);
goto err;
}
}
dev_set_drvdata(&ofdev->dev, pdata);
return 0;
err:
for (count = pdata->num_leds - 2; count >= 0; count--)
delete_gpio_led(&pdata->led_data[count]);
kfree(pdata);
return ret;
}
static int __devexit of_gpio_leds_remove(struct of_device *ofdev)
{
struct gpio_led_of_platform_data *pdata = dev_get_drvdata(&ofdev->dev);
int i;
for (i = 0; i < pdata->num_leds; i++)
delete_gpio_led(&pdata->led_data[i]);
kfree(pdata);
dev_set_drvdata(&ofdev->dev, NULL);
return 0;
}
static const struct of_device_id of_gpio_leds_match[] = {
{ .compatible = "gpio-leds", },
{},
};
static struct of_platform_driver of_gpio_leds_driver = {
.driver = {
.name = "of_gpio_leds",
.owner = THIS_MODULE,
},
.match_table = of_gpio_leds_match,
.probe = of_gpio_leds_probe,
.remove = __devexit_p(of_gpio_leds_remove),
};
#endif
static int __init gpio_led_init(void)
{
int ret;
#ifdef CONFIG_LEDS_GPIO_PLATFORM
ret = platform_driver_register(&gpio_led_driver);
if (ret)
return ret;
#endif
#ifdef CONFIG_LEDS_GPIO_OF
ret = of_register_platform_driver(&of_gpio_leds_driver);
#endif
#ifdef CONFIG_LEDS_GPIO_PLATFORM
if (ret)
platform_driver_unregister(&gpio_led_driver);
#endif
return ret;
}
static void __exit gpio_led_exit(void)
{
#ifdef CONFIG_LEDS_GPIO_PLATFORM
platform_driver_unregister(&gpio_led_driver);
#endif
#ifdef CONFIG_LEDS_GPIO_OF
of_unregister_platform_driver(&of_gpio_leds_driver);
#endif
}
module_init(gpio_led_init);
module_exit(gpio_led_exit);
MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
MODULE_DESCRIPTION("GPIO LED driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,170 @@
/*
* drivers/leds/leds-h1940.c
* Copyright (c) Arnaud Patard <arnaud.patard@rtp-net.org>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*
* H1940 leds driver
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/leds.h>
#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <mach/h1940-latch.h>
/*
* Green led.
*/
static void h1940_greenled_set(struct led_classdev *led_dev,
enum led_brightness value)
{
switch (value) {
case LED_HALF:
h1940_latch_control(0, H1940_LATCH_LED_FLASH);
s3c2410_gpio_setpin(S3C2410_GPA7, 1);
break;
case LED_FULL:
h1940_latch_control(0, H1940_LATCH_LED_GREEN);
s3c2410_gpio_setpin(S3C2410_GPA7, 1);
break;
default:
case LED_OFF:
h1940_latch_control(H1940_LATCH_LED_FLASH, 0);
h1940_latch_control(H1940_LATCH_LED_GREEN, 0);
s3c2410_gpio_setpin(S3C2410_GPA7, 0);
break;
}
}
static struct led_classdev h1940_greenled = {
.name = "h1940:green",
.brightness_set = h1940_greenled_set,
.default_trigger = "h1940-charger",
};
/*
* Red led.
*/
static void h1940_redled_set(struct led_classdev *led_dev,
enum led_brightness value)
{
switch (value) {
case LED_HALF:
h1940_latch_control(0, H1940_LATCH_LED_FLASH);
s3c2410_gpio_setpin(S3C2410_GPA1, 1);
break;
case LED_FULL:
h1940_latch_control(0, H1940_LATCH_LED_RED);
s3c2410_gpio_setpin(S3C2410_GPA1, 1);
break;
default:
case LED_OFF:
h1940_latch_control(H1940_LATCH_LED_FLASH, 0);
h1940_latch_control(H1940_LATCH_LED_RED, 0);
s3c2410_gpio_setpin(S3C2410_GPA1, 0);
break;
}
}
static struct led_classdev h1940_redled = {
.name = "h1940:red",
.brightness_set = h1940_redled_set,
.default_trigger = "h1940-charger",
};
/*
* Blue led.
* (it can only be blue flashing led)
*/
static void h1940_blueled_set(struct led_classdev *led_dev,
enum led_brightness value)
{
if (value) {
/* flashing Blue */
h1940_latch_control(0, H1940_LATCH_LED_FLASH);
s3c2410_gpio_setpin(S3C2410_GPA3, 1);
} else {
h1940_latch_control(H1940_LATCH_LED_FLASH, 0);
s3c2410_gpio_setpin(S3C2410_GPA3, 0);
}
}
static struct led_classdev h1940_blueled = {
.name = "h1940:blue",
.brightness_set = h1940_blueled_set,
.default_trigger = "h1940-bluetooth",
};
static int __devinit h1940leds_probe(struct platform_device *pdev)
{
int ret;
ret = led_classdev_register(&pdev->dev, &h1940_greenled);
if (ret)
goto err_green;
ret = led_classdev_register(&pdev->dev, &h1940_redled);
if (ret)
goto err_red;
ret = led_classdev_register(&pdev->dev, &h1940_blueled);
if (ret)
goto err_blue;
return 0;
err_blue:
led_classdev_unregister(&h1940_redled);
err_red:
led_classdev_unregister(&h1940_greenled);
err_green:
return ret;
}
static int h1940leds_remove(struct platform_device *pdev)
{
led_classdev_unregister(&h1940_greenled);
led_classdev_unregister(&h1940_redled);
led_classdev_unregister(&h1940_blueled);
return 0;
}
static struct platform_driver h1940leds_driver = {
.driver = {
.name = "h1940-leds",
.owner = THIS_MODULE,
},
.probe = h1940leds_probe,
.remove = h1940leds_remove,
};
static int __init h1940leds_init(void)
{
return platform_driver_register(&h1940leds_driver);
}
static void __exit h1940leds_exit(void)
{
platform_driver_unregister(&h1940leds_driver);
}
module_init(h1940leds_init);
module_exit(h1940leds_exit);
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("LED driver for the iPAQ H1940");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:h1940-leds");

View File

@@ -0,0 +1,108 @@
/*
* LED Triggers Core
* For the HP Jornada 620/660/680/690 handhelds
*
* Copyright 2008 Kristoffer Ericson <kristoffer.ericson@gmail.com>
* this driver is based on leds-spitz.c by Richard Purdie.
*
* 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 <asm/hd64461.h>
#include <mach/hp6xx.h>
static void hp6xxled_green_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
u8 v8;
v8 = inb(PKDR);
if (value)
outb(v8 & (~PKDR_LED_GREEN), PKDR);
else
outb(v8 | PKDR_LED_GREEN, PKDR);
}
static void hp6xxled_red_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
u16 v16;
v16 = inw(HD64461_GPBDR);
if (value)
outw(v16 & (~HD64461_GPBDR_LED_RED), HD64461_GPBDR);
else
outw(v16 | HD64461_GPBDR_LED_RED, HD64461_GPBDR);
}
static struct led_classdev hp6xx_red_led = {
.name = "hp6xx:red",
.default_trigger = "hp6xx-charge",
.brightness_set = hp6xxled_red_set,
.flags = LED_CORE_SUSPENDRESUME,
};
static struct led_classdev hp6xx_green_led = {
.name = "hp6xx:green",
.default_trigger = "ide-disk",
.brightness_set = hp6xxled_green_set,
.flags = LED_CORE_SUSPENDRESUME,
};
static int hp6xxled_probe(struct platform_device *pdev)
{
int ret;
ret = led_classdev_register(&pdev->dev, &hp6xx_red_led);
if (ret < 0)
return ret;
ret = led_classdev_register(&pdev->dev, &hp6xx_green_led);
if (ret < 0)
led_classdev_unregister(&hp6xx_red_led);
return ret;
}
static int hp6xxled_remove(struct platform_device *pdev)
{
led_classdev_unregister(&hp6xx_red_led);
led_classdev_unregister(&hp6xx_green_led);
return 0;
}
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:hp6xx-led");
static struct platform_driver hp6xxled_driver = {
.probe = hp6xxled_probe,
.remove = hp6xxled_remove,
.driver = {
.name = "hp6xx-led",
.owner = THIS_MODULE,
},
};
static int __init hp6xxled_init(void)
{
return platform_driver_register(&hp6xxled_driver);
}
static void __exit hp6xxled_exit(void)
{
platform_driver_unregister(&hp6xxled_driver);
}
module_init(hp6xxled_init);
module_exit(hp6xxled_exit);
MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
MODULE_DESCRIPTION("HP Jornada 6xx LED driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,96 @@
/*
* linux/drivers/leds/leds-locomo.c
*
* Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
*
* 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/device.h>
#include <linux/leds.h>
#include <mach/hardware.h>
#include <asm/hardware/locomo.h>
static void locomoled_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value, int offset)
{
struct locomo_dev *locomo_dev = LOCOMO_DEV(led_cdev->dev->parent);
unsigned long flags;
local_irq_save(flags);
if (value)
locomo_writel(LOCOMO_LPT_TOFH, locomo_dev->mapbase + offset);
else
locomo_writel(LOCOMO_LPT_TOFL, locomo_dev->mapbase + offset);
local_irq_restore(flags);
}
static void locomoled_brightness_set0(struct led_classdev *led_cdev,
enum led_brightness value)
{
locomoled_brightness_set(led_cdev, value, LOCOMO_LPT0);
}
static void locomoled_brightness_set1(struct led_classdev *led_cdev,
enum led_brightness value)
{
locomoled_brightness_set(led_cdev, value, LOCOMO_LPT1);
}
static struct led_classdev locomo_led0 = {
.name = "locomo:amber:charge",
.default_trigger = "sharpsl-charge",
.brightness_set = locomoled_brightness_set0,
};
static struct led_classdev locomo_led1 = {
.name = "locomo:green:mail",
.default_trigger = "nand-disk",
.brightness_set = locomoled_brightness_set1,
};
static int locomoled_probe(struct locomo_dev *ldev)
{
int ret;
ret = led_classdev_register(&ldev->dev, &locomo_led0);
if (ret < 0)
return ret;
ret = led_classdev_register(&ldev->dev, &locomo_led1);
if (ret < 0)
led_classdev_unregister(&locomo_led0);
return ret;
}
static int locomoled_remove(struct locomo_dev *dev)
{
led_classdev_unregister(&locomo_led0);
led_classdev_unregister(&locomo_led1);
return 0;
}
static struct locomo_driver locomoled_driver = {
.drv = {
.name = "locomoled"
},
.devid = LOCOMO_DEVID_LED,
.probe = locomoled_probe,
.remove = locomoled_remove,
};
static int __init locomoled_init(void)
{
return locomo_driver_register(&locomoled_driver);
}
module_init(locomoled_init);
MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
MODULE_DESCRIPTION("Locomo LED driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,466 @@
/*
* leds-lp3944.c - driver for National Semiconductor LP3944 Funlight Chip
*
* 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.
*
*/
/*
* I2C driver for National Semiconductor LP3944 Funlight Chip
* http://www.national.com/pf/LP/LP3944.html
*
* This helper chip can drive up to 8 leds, with two programmable DIM modes;
* it could even be used as a gpio expander but this driver assumes it is used
* as a led controller.
*
* The DIM modes are used to set _blink_ patterns for leds, the pattern is
* specified supplying two parameters:
* - period: from 0s to 1.6s
* - duty cycle: percentage of the period the led is on, from 0 to 100
*
* LP3944 can be found on Motorola A910 smartphone, where it drives the rgb
* leds, the camera flash light and the displays backlights.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/leds.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/leds-lp3944.h>
/* Read Only Registers */
#define LP3944_REG_INPUT1 0x00 /* LEDs 0-7 InputRegister (Read Only) */
#define LP3944_REG_REGISTER1 0x01 /* None (Read Only) */
#define LP3944_REG_PSC0 0x02 /* Frequency Prescaler 0 (R/W) */
#define LP3944_REG_PWM0 0x03 /* PWM Register 0 (R/W) */
#define LP3944_REG_PSC1 0x04 /* Frequency Prescaler 1 (R/W) */
#define LP3944_REG_PWM1 0x05 /* PWM Register 1 (R/W) */
#define LP3944_REG_LS0 0x06 /* LEDs 0-3 Selector (R/W) */
#define LP3944_REG_LS1 0x07 /* LEDs 4-7 Selector (R/W) */
/* These registers are not used to control leds in LP3944, they can store
* arbitrary values which the chip will ignore.
*/
#define LP3944_REG_REGISTER8 0x08
#define LP3944_REG_REGISTER9 0x09
#define LP3944_DIM0 0
#define LP3944_DIM1 1
/* period in ms */
#define LP3944_PERIOD_MIN 0
#define LP3944_PERIOD_MAX 1600
/* duty cycle is a percentage */
#define LP3944_DUTY_CYCLE_MIN 0
#define LP3944_DUTY_CYCLE_MAX 100
#define ldev_to_led(c) container_of(c, struct lp3944_led_data, ldev)
/* Saved data */
struct lp3944_led_data {
u8 id;
enum lp3944_type type;
enum lp3944_status status;
struct led_classdev ldev;
struct i2c_client *client;
struct work_struct work;
};
struct lp3944_data {
struct mutex lock;
struct i2c_client *client;
struct lp3944_led_data leds[LP3944_LEDS_MAX];
};
static int lp3944_reg_read(struct i2c_client *client, u8 reg, u8 *value)
{
int tmp;
tmp = i2c_smbus_read_byte_data(client, reg);
if (tmp < 0)
return -EINVAL;
*value = tmp;
return 0;
}
static int lp3944_reg_write(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
/**
* Set the period for DIM status
*
* @client: the i2c client
* @dim: either LP3944_DIM0 or LP3944_DIM1
* @period: period of a blink, that is a on/off cycle, expressed in ms.
*/
static int lp3944_dim_set_period(struct i2c_client *client, u8 dim, u16 period)
{
u8 psc_reg;
u8 psc_value;
int err;
if (dim == LP3944_DIM0)
psc_reg = LP3944_REG_PSC0;
else if (dim == LP3944_DIM1)
psc_reg = LP3944_REG_PSC1;
else
return -EINVAL;
/* Convert period to Prescaler value */
if (period > LP3944_PERIOD_MAX)
return -EINVAL;
psc_value = (period * 255) / LP3944_PERIOD_MAX;
err = lp3944_reg_write(client, psc_reg, psc_value);
return err;
}
/**
* Set the duty cycle for DIM status
*
* @client: the i2c client
* @dim: either LP3944_DIM0 or LP3944_DIM1
* @duty_cycle: percentage of a period during which a led is ON
*/
static int lp3944_dim_set_dutycycle(struct i2c_client *client, u8 dim,
u8 duty_cycle)
{
u8 pwm_reg;
u8 pwm_value;
int err;
if (dim == LP3944_DIM0)
pwm_reg = LP3944_REG_PWM0;
else if (dim == LP3944_DIM1)
pwm_reg = LP3944_REG_PWM1;
else
return -EINVAL;
/* Convert duty cycle to PWM value */
if (duty_cycle > LP3944_DUTY_CYCLE_MAX)
return -EINVAL;
pwm_value = (duty_cycle * 255) / LP3944_DUTY_CYCLE_MAX;
err = lp3944_reg_write(client, pwm_reg, pwm_value);
return err;
}
/**
* Set the led status
*
* @led: a lp3944_led_data structure
* @status: one of LP3944_LED_STATUS_OFF
* LP3944_LED_STATUS_ON
* LP3944_LED_STATUS_DIM0
* LP3944_LED_STATUS_DIM1
*/
static int lp3944_led_set(struct lp3944_led_data *led, u8 status)
{
struct lp3944_data *data = i2c_get_clientdata(led->client);
u8 id = led->id;
u8 reg;
u8 val = 0;
int err;
dev_dbg(&led->client->dev, "%s: %s, status before normalization:%d\n",
__func__, led->ldev.name, status);
switch (id) {
case LP3944_LED0:
case LP3944_LED1:
case LP3944_LED2:
case LP3944_LED3:
reg = LP3944_REG_LS0;
break;
case LP3944_LED4:
case LP3944_LED5:
case LP3944_LED6:
case LP3944_LED7:
id -= LP3944_LED4;
reg = LP3944_REG_LS1;
break;
default:
return -EINVAL;
}
if (status > LP3944_LED_STATUS_DIM1)
return -EINVAL;
/* invert only 0 and 1, leave unchanged the other values,
* remember we are abusing status to set blink patterns
*/
if (led->type == LP3944_LED_TYPE_LED_INVERTED && status < 2)
status = 1 - status;
mutex_lock(&data->lock);
lp3944_reg_read(led->client, reg, &val);
val &= ~(LP3944_LED_STATUS_MASK << (id << 1));
val |= (status << (id << 1));
dev_dbg(&led->client->dev, "%s: %s, reg:%d id:%d status:%d val:%#x\n",
__func__, led->ldev.name, reg, id, status, val);
/* set led status */
err = lp3944_reg_write(led->client, reg, val);
mutex_unlock(&data->lock);
return err;
}
static int lp3944_led_set_blink(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
struct lp3944_led_data *led = ldev_to_led(led_cdev);
u16 period;
u8 duty_cycle;
int err;
/* units are in ms */
if (*delay_on + *delay_off > LP3944_PERIOD_MAX)
return -EINVAL;
if (*delay_on == 0 && *delay_off == 0) {
/* Special case: the leds subsystem requires a default user
* friendly blink pattern for the LED. Let's blink the led
* slowly (1Hz).
*/
*delay_on = 500;
*delay_off = 500;
}
period = (*delay_on) + (*delay_off);
/* duty_cycle is the percentage of period during which the led is ON */
duty_cycle = 100 * (*delay_on) / period;
/* invert duty cycle for inverted leds, this has the same effect of
* swapping delay_on and delay_off
*/
if (led->type == LP3944_LED_TYPE_LED_INVERTED)
duty_cycle = 100 - duty_cycle;
/* NOTE: using always the first DIM mode, this means that all leds
* will have the same blinking pattern.
*
* We could find a way later to have two leds blinking in hardware
* with different patterns at the same time, falling back to software
* control for the other ones.
*/
err = lp3944_dim_set_period(led->client, LP3944_DIM0, period);
if (err)
return err;
err = lp3944_dim_set_dutycycle(led->client, LP3944_DIM0, duty_cycle);
if (err)
return err;
dev_dbg(&led->client->dev, "%s: OK hardware accelerated blink!\n",
__func__);
led->status = LP3944_LED_STATUS_DIM0;
schedule_work(&led->work);
return 0;
}
static void lp3944_led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct lp3944_led_data *led = ldev_to_led(led_cdev);
dev_dbg(&led->client->dev, "%s: %s, %d\n",
__func__, led_cdev->name, brightness);
led->status = brightness;
schedule_work(&led->work);
}
static void lp3944_led_work(struct work_struct *work)
{
struct lp3944_led_data *led;
led = container_of(work, struct lp3944_led_data, work);
lp3944_led_set(led, led->status);
}
static int lp3944_configure(struct i2c_client *client,
struct lp3944_data *data,
struct lp3944_platform_data *pdata)
{
int i, err = 0;
for (i = 0; i < pdata->leds_size; i++) {
struct lp3944_led *pled = &pdata->leds[i];
struct lp3944_led_data *led = &data->leds[i];
led->client = client;
led->id = i;
switch (pled->type) {
case LP3944_LED_TYPE_LED:
case LP3944_LED_TYPE_LED_INVERTED:
led->type = pled->type;
led->status = pled->status;
led->ldev.name = pled->name;
led->ldev.max_brightness = 1;
led->ldev.brightness_set = lp3944_led_set_brightness;
led->ldev.blink_set = lp3944_led_set_blink;
led->ldev.flags = LED_CORE_SUSPENDRESUME;
INIT_WORK(&led->work, lp3944_led_work);
err = led_classdev_register(&client->dev, &led->ldev);
if (err < 0) {
dev_err(&client->dev,
"couldn't register LED %s\n",
led->ldev.name);
goto exit;
}
/* to expose the default value to userspace */
led->ldev.brightness = led->status;
/* Set the default led status */
err = lp3944_led_set(led, led->status);
if (err < 0) {
dev_err(&client->dev,
"%s couldn't set STATUS %d\n",
led->ldev.name, led->status);
goto exit;
}
break;
case LP3944_LED_TYPE_NONE:
default:
break;
}
}
return 0;
exit:
if (i > 0)
for (i = i - 1; i >= 0; i--)
switch (pdata->leds[i].type) {
case LP3944_LED_TYPE_LED:
case LP3944_LED_TYPE_LED_INVERTED:
led_classdev_unregister(&data->leds[i].ldev);
cancel_work_sync(&data->leds[i].work);
break;
case LP3944_LED_TYPE_NONE:
default:
break;
}
return err;
}
static int __devinit lp3944_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct lp3944_platform_data *lp3944_pdata = client->dev.platform_data;
struct lp3944_data *data;
if (lp3944_pdata == NULL) {
dev_err(&client->dev, "no platform data\n");
return -EINVAL;
}
/* Let's see whether this adapter can support what we need. */
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&client->dev, "insufficient functionality!\n");
return -ENODEV;
}
data = kzalloc(sizeof(struct lp3944_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
i2c_set_clientdata(client, data);
mutex_init(&data->lock);
dev_info(&client->dev, "lp3944 enabled\n");
lp3944_configure(client, data, lp3944_pdata);
return 0;
}
static int __devexit lp3944_remove(struct i2c_client *client)
{
struct lp3944_platform_data *pdata = client->dev.platform_data;
struct lp3944_data *data = i2c_get_clientdata(client);
int i;
for (i = 0; i < pdata->leds_size; i++)
switch (data->leds[i].type) {
case LP3944_LED_TYPE_LED:
case LP3944_LED_TYPE_LED_INVERTED:
led_classdev_unregister(&data->leds[i].ldev);
cancel_work_sync(&data->leds[i].work);
break;
case LP3944_LED_TYPE_NONE:
default:
break;
}
kfree(data);
i2c_set_clientdata(client, NULL);
return 0;
}
/* lp3944 i2c driver struct */
static const struct i2c_device_id lp3944_id[] = {
{"lp3944", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, lp3944_id);
static struct i2c_driver lp3944_driver = {
.driver = {
.name = "lp3944",
},
.probe = lp3944_probe,
.remove = __devexit_p(lp3944_remove),
.id_table = lp3944_id,
};
static int __init lp3944_module_init(void)
{
return i2c_add_driver(&lp3944_driver);
}
static void __exit lp3944_module_exit(void)
{
i2c_del_driver(&lp3944_driver);
}
module_init(lp3944_module_init);
module_exit(lp3944_module_exit);
MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
MODULE_DESCRIPTION("LP3944 Fun Light Chip");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,98 @@
/*
* linux/drivers/leds/leds-mb374.c
*
* Copyright (C) 2007 STMicroelectronics Limited
* Author: Stuart Menefy <stuart.menefy@st.com>
*
* 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/types.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/leds.h>
#include <linux/stm/pio.h>
#include <asm/io.h>
#include <asm/mb374/epld.h>
struct mb374_led {
struct led_classdev cdev;
union {
struct stpio_pin *pio;
int bit;
} u;
};
static void mb374_led_pio_set(struct led_classdev *led_cdev, enum led_brightness brightness)
{
struct mb374_led *led_dev =
container_of(led_cdev, struct mb374_led, cdev);
stpio_set_pin(led_dev->u.pio, brightness);
}
static void mb374_led_epld_set(struct led_classdev *led_cdev, enum led_brightness brightness)
{
struct mb374_led *led_dev =
container_of(led_cdev, struct mb374_led, cdev);
ctrl_outl(1 << led_dev->u.bit, brightness ? EPLD_LED_SET : EPLD_LED_CLR);
}
#define MB374_EPLD_LED(_n, _bit) \
{ \
.cdev = { \
.name = "mb374-led" #_n, \
.brightness_set = mb374_led_epld_set, \
}, \
.u.bit = _bit \
}
#define MB374_PIO_LED(_n) \
{ \
.cdev = { \
.name = "mb374-led" #_n, \
.brightness_set = mb374_led_pio_set, \
}, \
}
static struct mb374_led mb374_leds[8] = {
MB374_EPLD_LED(0,0),
MB374_EPLD_LED(1,1),
MB374_EPLD_LED(2,2),
MB374_EPLD_LED(3,3),
MB374_EPLD_LED(4,4),
MB374_EPLD_LED(5,5),
MB374_PIO_LED(6),
MB374_PIO_LED(7)
};
static int __init mb374_led_init(void)
{
int i;
mb374_leds[6].u.pio = stpio_request_pin(0,3, "LED", STPIO_OUT);
mb374_leds[7].u.pio = stpio_request_pin(0,0, "LED", STPIO_OUT);
mb374_leds[0].cdev.default_trigger = "heartbeat";
for (i=0; i<8; i++) {
led_classdev_register(NULL, &mb374_leds[i].cdev);
}
}
static void __exit mb374_led_exit(void)
{
int i;
for (i=0; i<8; i++) {
led_classdev_unregister(&mb374_leds[i].cdev);
}
}
module_init(mb374_led_init);
module_exit(mb374_led_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Front LED support for Mb374 Server");
MODULE_AUTHOR("Stuart Menefy <stuart.menefy@st.com>");

View File

@@ -0,0 +1,46 @@
/*
* linux/drivers/leds/leds-mb411.c
*
* Copyright (C) 2007 STMicroelectronics Limited
* Author: Stuart Menefy <stuart.menefy@st.com>
*
* 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/types.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/leds.h>
#include <asm/io.h>
#include <asm/mb411/epld.h>
static void mb411_led_set(struct led_classdev *led_cdev, enum led_brightness brightness)
{
ctrl_outl(EPLD_LED, brightness ? EPLD_LED_ON : EPLD_LED_OFF);
}
static struct led_classdev mb411_led = {
.name = "mb411-led",
.brightness_set = mb411_led_set,
.default_trigger = "heartbeat";
};
static int __init mb411_led_init(void)
{
led_classdev_register(NULL, &mb411_led);
}
static void __exit mb411_led_exit(void)
{
led_classdev_unregister(&mb411_led);
}
module_init(mb411_led_init);
module_exit(mb411_led_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LED support for STMicroelectronics mb411");
MODULE_AUTHOR("Stuart Menefy <stuart.menefy@st.com>");

View File

@@ -0,0 +1,56 @@
/*
* linux/drivers/leds/leds-mb628.c
*
* Copyright (C) 2008 STMicroelectronics Limited
* Author: Stuart Menefy <stuart.menefy@st.com>
*
* 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/types.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/leds.h>
#include <linux/io.h>
#include <mach/epld.h>
#include <mach/common.h>
static void mb628_led_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
u8 reg;
/* Locking required here */
reg = epld_read(EPLD_ENABLE);
if (brightness)
reg |= EPLD_ENABLE_HBEAT;
else
reg &= ~EPLD_ENABLE_HBEAT;
epld_write(reg, EPLD_ENABLE);
}
static struct led_classdev mb628_led = {
.name = "mb628-led",
.brightness_set = mb628_led_set,
.default_trigger = "heartbeat"
};
static int __init mb628_led_init(void)
{
return led_classdev_register(NULL, &mb628_led);
}
static void __exit mb628_led_exit(void)
{
led_classdev_unregister(&mb628_led);
}
module_init(mb628_led_init);
module_exit(mb628_led_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LED support for STMicroelectronics mb628");
MODULE_AUTHOR("Stuart Menefy <stuart.menefy@st.com>");

View File

@@ -0,0 +1,96 @@
/*
* LEDs driver for Soekris net48xx
*
* Copyright (C) 2006 Chris Boot <bootc@bootc.net>
*
* Based on leds-ams-delta.c
*
* 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/err.h>
#include <asm/io.h>
#include <linux/nsc_gpio.h>
#include <linux/scx200_gpio.h>
#define DRVNAME "net48xx-led"
#define NET48XX_ERROR_LED_GPIO 20
static struct platform_device *pdev;
static void net48xx_error_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
scx200_gpio_ops.gpio_set(NET48XX_ERROR_LED_GPIO, value ? 1 : 0);
}
static struct led_classdev net48xx_error_led = {
.name = "net48xx::error",
.brightness_set = net48xx_error_led_set,
.flags = LED_CORE_SUSPENDRESUME,
};
static int net48xx_led_probe(struct platform_device *pdev)
{
return led_classdev_register(&pdev->dev, &net48xx_error_led);
}
static int net48xx_led_remove(struct platform_device *pdev)
{
led_classdev_unregister(&net48xx_error_led);
return 0;
}
static struct platform_driver net48xx_led_driver = {
.probe = net48xx_led_probe,
.remove = net48xx_led_remove,
.driver = {
.name = DRVNAME,
.owner = THIS_MODULE,
},
};
static int __init net48xx_led_init(void)
{
int ret;
/* small hack, but scx200_gpio doesn't set .dev if the probe fails */
if (!scx200_gpio_ops.dev) {
ret = -ENODEV;
goto out;
}
ret = platform_driver_register(&net48xx_led_driver);
if (ret < 0)
goto out;
pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0);
if (IS_ERR(pdev)) {
ret = PTR_ERR(pdev);
platform_driver_unregister(&net48xx_led_driver);
goto out;
}
out:
return ret;
}
static void __exit net48xx_led_exit(void)
{
platform_device_unregister(pdev);
platform_driver_unregister(&net48xx_led_driver);
}
module_init(net48xx_led_init);
module_exit(net48xx_led_exit);
MODULE_AUTHOR("Chris Boot <bootc@bootc.net>");
MODULE_DESCRIPTION("Soekris net48xx LED driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,373 @@
/*
* pca9532.c - 16-bit Led dimmer
*
* Copyright (C) 2008 Riku Voipio
*
* 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; version 2 of the License.
*
* Datasheet: http://www.nxp.com/acrobat/datasheets/PCA9532_3.pdf
*
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/leds.h>
#include <linux/input.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/leds-pca9532.h>
#define PCA9532_REG_PSC(i) (0x2+(i)*2)
#define PCA9532_REG_PWM(i) (0x3+(i)*2)
#define PCA9532_REG_LS0 0x6
#define LED_REG(led) ((led>>2)+PCA9532_REG_LS0)
#define LED_NUM(led) (led & 0x3)
#define ldev_to_led(c) container_of(c, struct pca9532_led, ldev)
struct pca9532_data {
struct i2c_client *client;
struct pca9532_led leds[16];
struct mutex update_lock;
struct input_dev *idev;
struct work_struct work;
u8 pwm[2];
u8 psc[2];
};
static int pca9532_probe(struct i2c_client *client,
const struct i2c_device_id *id);
static int pca9532_remove(struct i2c_client *client);
static const struct i2c_device_id pca9532_id[] = {
{ "pca9532", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pca9532_id);
static struct i2c_driver pca9532_driver = {
.driver = {
.name = "pca9532",
},
.probe = pca9532_probe,
.remove = pca9532_remove,
.id_table = pca9532_id,
};
/* We have two pwm/blinkers, but 16 possible leds to drive. Additionaly,
* the clever Thecus people are using one pwm to drive the beeper. So,
* as a compromise we average one pwm to the values requested by all
* leds that are not ON/OFF.
* */
static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink,
enum led_brightness value)
{
int a = 0, b = 0, i = 0;
struct pca9532_data *data = i2c_get_clientdata(client);
for (i = 0; i < 16; i++) {
if (data->leds[i].type == PCA9532_TYPE_LED &&
data->leds[i].state == PCA9532_PWM0+pwm) {
a++;
b += data->leds[i].ldev.brightness;
}
}
if (a == 0) {
dev_err(&client->dev,
"fear of division by zero %d/%d, wanted %d\n",
b, a, value);
return -EINVAL;
}
b = b/a;
if (b > 0xFF)
return -EINVAL;
data->pwm[pwm] = b;
data->psc[pwm] = blink;
return 0;
}
static int pca9532_setpwm(struct i2c_client *client, int pwm)
{
struct pca9532_data *data = i2c_get_clientdata(client);
mutex_lock(&data->update_lock);
i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm),
data->pwm[pwm]);
i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm),
data->psc[pwm]);
mutex_unlock(&data->update_lock);
return 0;
}
/* Set LED routing */
static void pca9532_setled(struct pca9532_led *led)
{
struct i2c_client *client = led->client;
struct pca9532_data *data = i2c_get_clientdata(client);
char reg;
mutex_lock(&data->update_lock);
reg = i2c_smbus_read_byte_data(client, LED_REG(led->id));
/* zero led bits */
reg = reg & ~(0x3<<LED_NUM(led->id)*2);
/* set the new value */
reg = reg | (led->state << LED_NUM(led->id)*2);
i2c_smbus_write_byte_data(client, LED_REG(led->id), reg);
mutex_unlock(&data->update_lock);
}
static void pca9532_set_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
int err = 0;
struct pca9532_led *led = ldev_to_led(led_cdev);
if (value == LED_OFF)
led->state = PCA9532_OFF;
else if (value == LED_FULL)
led->state = PCA9532_ON;
else {
led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */
err = pca9532_calcpwm(led->client, 0, 0, value);
if (err)
return; /* XXX: led api doesn't allow error code? */
}
schedule_work(&led->work);
}
static int pca9532_set_blink(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_off)
{
struct pca9532_led *led = ldev_to_led(led_cdev);
struct i2c_client *client = led->client;
int psc;
int err = 0;
if (*delay_on == 0 && *delay_off == 0) {
/* led subsystem ask us for a blink rate */
*delay_on = 1000;
*delay_off = 1000;
}
if (*delay_on != *delay_off || *delay_on > 1690 || *delay_on < 6)
return -EINVAL;
/* Thecus specific: only use PSC/PWM 0 */
psc = (*delay_on * 152-1)/1000;
err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness);
if (err)
return err;
schedule_work(&led->work);
return 0;
}
static int pca9532_event(struct input_dev *dev, unsigned int type,
unsigned int code, int value)
{
struct pca9532_data *data = input_get_drvdata(dev);
if (!(type == EV_SND && (code == SND_BELL || code == SND_TONE)))
return -1;
/* XXX: allow different kind of beeps with psc/pwm modifications */
if (value > 1 && value < 32767)
data->pwm[1] = 127;
else
data->pwm[1] = 0;
schedule_work(&data->work);
return 0;
}
static void pca9532_input_work(struct work_struct *work)
{
struct pca9532_data *data;
data = container_of(work, struct pca9532_data, work);
mutex_lock(&data->update_lock);
i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1),
data->pwm[1]);
mutex_unlock(&data->update_lock);
}
static void pca9532_led_work(struct work_struct *work)
{
struct pca9532_led *led;
led = container_of(work, struct pca9532_led, work);
if (led->state == PCA9532_PWM0)
pca9532_setpwm(led->client, 0);
pca9532_setled(led);
}
static int pca9532_configure(struct i2c_client *client,
struct pca9532_data *data, struct pca9532_platform_data *pdata)
{
int i, err = 0;
for (i = 0; i < 2; i++) {
data->pwm[i] = pdata->pwm[i];
data->psc[i] = pdata->psc[i];
i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(i),
data->pwm[i]);
i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(i),
data->psc[i]);
}
for (i = 0; i < 16; i++) {
struct pca9532_led *led = &data->leds[i];
struct pca9532_led *pled = &pdata->leds[i];
led->client = client;
led->id = i;
led->type = pled->type;
switch (led->type) {
case PCA9532_TYPE_NONE:
break;
case PCA9532_TYPE_LED:
led->state = pled->state;
led->name = pled->name;
led->ldev.name = led->name;
led->ldev.brightness = LED_OFF;
led->ldev.brightness_set = pca9532_set_brightness;
led->ldev.blink_set = pca9532_set_blink;
INIT_WORK(&led->work, pca9532_led_work);
err = led_classdev_register(&client->dev, &led->ldev);
if (err < 0) {
dev_err(&client->dev,
"couldn't register LED %s\n",
led->name);
goto exit;
}
pca9532_setled(led);
break;
case PCA9532_TYPE_N2100_BEEP:
BUG_ON(data->idev);
led->state = PCA9532_PWM1;
pca9532_setled(led);
data->idev = input_allocate_device();
if (data->idev == NULL) {
err = -ENOMEM;
goto exit;
}
data->idev->name = pled->name;
data->idev->phys = "i2c/pca9532";
data->idev->id.bustype = BUS_HOST;
data->idev->id.vendor = 0x001f;
data->idev->id.product = 0x0001;
data->idev->id.version = 0x0100;
data->idev->evbit[0] = BIT_MASK(EV_SND);
data->idev->sndbit[0] = BIT_MASK(SND_BELL) |
BIT_MASK(SND_TONE);
data->idev->event = pca9532_event;
input_set_drvdata(data->idev, data);
INIT_WORK(&data->work, pca9532_input_work);
err = input_register_device(data->idev);
if (err) {
input_free_device(data->idev);
cancel_work_sync(&data->work);
data->idev = NULL;
goto exit;
}
break;
}
}
return 0;
exit:
if (i > 0)
for (i = i - 1; i >= 0; i--)
switch (data->leds[i].type) {
case PCA9532_TYPE_NONE:
break;
case PCA9532_TYPE_LED:
led_classdev_unregister(&data->leds[i].ldev);
cancel_work_sync(&data->leds[i].work);
break;
case PCA9532_TYPE_N2100_BEEP:
if (data->idev != NULL) {
input_unregister_device(data->idev);
input_free_device(data->idev);
cancel_work_sync(&data->work);
data->idev = NULL;
}
break;
}
return err;
}
static int pca9532_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pca9532_data *data = i2c_get_clientdata(client);
struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data;
int err;
if (!pca9532_pdata)
return -EIO;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
dev_info(&client->dev, "setting platform data\n");
i2c_set_clientdata(client, data);
data->client = client;
mutex_init(&data->update_lock);
err = pca9532_configure(client, data, pca9532_pdata);
if (err) {
kfree(data);
i2c_set_clientdata(client, NULL);
}
return err;
}
static int pca9532_remove(struct i2c_client *client)
{
struct pca9532_data *data = i2c_get_clientdata(client);
int i;
for (i = 0; i < 16; i++)
switch (data->leds[i].type) {
case PCA9532_TYPE_NONE:
break;
case PCA9532_TYPE_LED:
led_classdev_unregister(&data->leds[i].ldev);
cancel_work_sync(&data->leds[i].work);
break;
case PCA9532_TYPE_N2100_BEEP:
if (data->idev != NULL) {
input_unregister_device(data->idev);
input_free_device(data->idev);
cancel_work_sync(&data->work);
data->idev = NULL;
}
break;
}
kfree(data);
i2c_set_clientdata(client, NULL);
return 0;
}
static int __init pca9532_init(void)
{
return i2c_add_driver(&pca9532_driver);
}
static void __exit pca9532_exit(void)
{
i2c_del_driver(&pca9532_driver);
}
MODULE_AUTHOR("Riku Voipio");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PCA 9532 LED dimmer");
module_init(pca9532_init);
module_exit(pca9532_exit);

View File

@@ -0,0 +1,390 @@
/*
* Copyright 2007-2008 Extreme Engineering Solutions, Inc.
*
* Author: Nate Case <ncase@xes-inc.com>
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
*
* LED driver for various PCA955x I2C LED drivers
*
* Supported devices:
*
* Device Description 7-bit slave address
* ------ ----------- -------------------
* PCA9550 2-bit driver 0x60 .. 0x61
* PCA9551 8-bit driver 0x60 .. 0x67
* PCA9552 16-bit driver 0x60 .. 0x67
* PCA9553/01 4-bit driver 0x62
* PCA9553/02 4-bit driver 0x63
*
* Philips PCA955x LED driver chips follow a register map as shown below:
*
* Control Register Description
* ---------------- -----------
* 0x0 Input register 0
* ..
* NUM_INPUT_REGS - 1 Last Input register X
*
* NUM_INPUT_REGS Frequency prescaler 0
* NUM_INPUT_REGS + 1 PWM register 0
* NUM_INPUT_REGS + 2 Frequency prescaler 1
* NUM_INPUT_REGS + 3 PWM register 1
*
* NUM_INPUT_REGS + 4 LED selector 0
* NUM_INPUT_REGS + 4
* + NUM_LED_REGS - 1 Last LED selector
*
* where NUM_INPUT_REGS and NUM_LED_REGS vary depending on how many
* bits the chip supports.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/leds.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/workqueue.h>
/* LED select registers determine the source that drives LED outputs */
#define PCA955X_LS_LED_ON 0x0 /* Output LOW */
#define PCA955X_LS_LED_OFF 0x1 /* Output HI-Z */
#define PCA955X_LS_BLINK0 0x2 /* Blink at PWM0 rate */
#define PCA955X_LS_BLINK1 0x3 /* Blink at PWM1 rate */
enum pca955x_type {
pca9550,
pca9551,
pca9552,
pca9553,
};
struct pca955x_chipdef {
int bits;
u8 slv_addr; /* 7-bit slave address mask */
int slv_addr_shift; /* Number of bits to ignore */
};
static struct pca955x_chipdef pca955x_chipdefs[] = {
[pca9550] = {
.bits = 2,
.slv_addr = /* 110000x */ 0x60,
.slv_addr_shift = 1,
},
[pca9551] = {
.bits = 8,
.slv_addr = /* 1100xxx */ 0x60,
.slv_addr_shift = 3,
},
[pca9552] = {
.bits = 16,
.slv_addr = /* 1100xxx */ 0x60,
.slv_addr_shift = 3,
},
[pca9553] = {
.bits = 4,
.slv_addr = /* 110001x */ 0x62,
.slv_addr_shift = 1,
},
};
static const struct i2c_device_id pca955x_id[] = {
{ "pca9550", pca9550 },
{ "pca9551", pca9551 },
{ "pca9552", pca9552 },
{ "pca9553", pca9553 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pca955x_id);
struct pca955x_led {
struct pca955x_chipdef *chipdef;
struct i2c_client *client;
struct work_struct work;
spinlock_t lock;
enum led_brightness brightness;
struct led_classdev led_cdev;
int led_num; /* 0 .. 15 potentially */
char name[32];
};
/* 8 bits per input register */
static inline int pca95xx_num_input_regs(int bits)
{
return (bits + 7) / 8;
}
/* 4 bits per LED selector register */
static inline int pca95xx_num_led_regs(int bits)
{
return (bits + 3) / 4;
}
/*
* Return an LED selector register value based on an existing one, with
* the appropriate 2-bit state value set for the given LED number (0-3).
*/
static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state)
{
return (oldval & (~(0x3 << (led_num << 1)))) |
((state & 0x3) << (led_num << 1));
}
/*
* Write to frequency prescaler register, used to program the
* period of the PWM output. period = (PSCx + 1) / 38
*/
static void pca955x_write_psc(struct i2c_client *client, int n, u8 val)
{
struct pca955x_led *pca955x = i2c_get_clientdata(client);
i2c_smbus_write_byte_data(client,
pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n,
val);
}
/*
* Write to PWM register, which determines the duty cycle of the
* output. LED is OFF when the count is less than the value of this
* register, and ON when it is greater. If PWMx == 0, LED is always OFF.
*
* Duty cycle is (256 - PWMx) / 256
*/
static void pca955x_write_pwm(struct i2c_client *client, int n, u8 val)
{
struct pca955x_led *pca955x = i2c_get_clientdata(client);
i2c_smbus_write_byte_data(client,
pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n,
val);
}
/*
* Write to LED selector register, which determines the source that
* drives the LED output.
*/
static void pca955x_write_ls(struct i2c_client *client, int n, u8 val)
{
struct pca955x_led *pca955x = i2c_get_clientdata(client);
i2c_smbus_write_byte_data(client,
pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n,
val);
}
/*
* Read the LED selector register, which determines the source that
* drives the LED output.
*/
static u8 pca955x_read_ls(struct i2c_client *client, int n)
{
struct pca955x_led *pca955x = i2c_get_clientdata(client);
return (u8) i2c_smbus_read_byte_data(client,
pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n);
}
static void pca955x_led_work(struct work_struct *work)
{
struct pca955x_led *pca955x;
u8 ls;
int chip_ls; /* which LSx to use (0-3 potentially) */
int ls_led; /* which set of bits within LSx to use (0-3) */
pca955x = container_of(work, struct pca955x_led, work);
chip_ls = pca955x->led_num / 4;
ls_led = pca955x->led_num % 4;
ls = pca955x_read_ls(pca955x->client, chip_ls);
switch (pca955x->brightness) {
case LED_FULL:
ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON);
break;
case LED_OFF:
ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_OFF);
break;
case LED_HALF:
ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK0);
break;
default:
/*
* Use PWM1 for all other values. This has the unwanted
* side effect of making all LEDs on the chip share the
* same brightness level if set to a value other than
* OFF, HALF, or FULL. But, this is probably better than
* just turning off for all other values.
*/
pca955x_write_pwm(pca955x->client, 1, 255-pca955x->brightness);
ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1);
break;
}
pca955x_write_ls(pca955x->client, chip_ls, ls);
}
static void pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness value)
{
struct pca955x_led *pca955x;
pca955x = container_of(led_cdev, struct pca955x_led, led_cdev);
spin_lock(&pca955x->lock);
pca955x->brightness = value;
/*
* Must use workqueue for the actual I/O since I2C operations
* can sleep.
*/
schedule_work(&pca955x->work);
spin_unlock(&pca955x->lock);
}
static int __devinit pca955x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pca955x_led *pca955x;
struct pca955x_chipdef *chip;
struct i2c_adapter *adapter;
struct led_platform_data *pdata;
int i, err;
chip = &pca955x_chipdefs[id->driver_data];
adapter = to_i2c_adapter(client->dev.parent);
pdata = client->dev.platform_data;
/* Make sure the slave address / chip type combo given is possible */
if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=
chip->slv_addr) {
dev_err(&client->dev, "invalid slave address %02x\n",
client->addr);
return -ENODEV;
}
printk(KERN_INFO "leds-pca955x: Using %s %d-bit LED driver at "
"slave address 0x%02x\n",
id->name, chip->bits, client->addr);
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
return -EIO;
if (pdata) {
if (pdata->num_leds != chip->bits) {
dev_err(&client->dev, "board info claims %d LEDs"
" on a %d-bit chip\n",
pdata->num_leds, chip->bits);
return -ENODEV;
}
}
pca955x = kzalloc(sizeof(*pca955x) * chip->bits, GFP_KERNEL);
if (!pca955x)
return -ENOMEM;
i2c_set_clientdata(client, pca955x);
for (i = 0; i < chip->bits; i++) {
pca955x[i].chipdef = chip;
pca955x[i].client = client;
pca955x[i].led_num = i;
/* Platform data can specify LED names and default triggers */
if (pdata) {
if (pdata->leds[i].name)
snprintf(pca955x[i].name,
sizeof(pca955x[i].name), "pca955x:%s",
pdata->leds[i].name);
if (pdata->leds[i].default_trigger)
pca955x[i].led_cdev.default_trigger =
pdata->leds[i].default_trigger;
} else {
snprintf(pca955x[i].name, sizeof(pca955x[i].name),
"pca955x:%d", i);
}
spin_lock_init(&pca955x[i].lock);
pca955x[i].led_cdev.name = pca955x[i].name;
pca955x[i].led_cdev.brightness_set = pca955x_led_set;
INIT_WORK(&pca955x[i].work, pca955x_led_work);
err = led_classdev_register(&client->dev, &pca955x[i].led_cdev);
if (err < 0)
goto exit;
}
/* Turn off LEDs */
for (i = 0; i < pca95xx_num_led_regs(chip->bits); i++)
pca955x_write_ls(client, i, 0x55);
/* PWM0 is used for half brightness or 50% duty cycle */
pca955x_write_pwm(client, 0, 255-LED_HALF);
/* PWM1 is used for variable brightness, default to OFF */
pca955x_write_pwm(client, 1, 0);
/* Set to fast frequency so we do not see flashing */
pca955x_write_psc(client, 0, 0);
pca955x_write_psc(client, 1, 0);
return 0;
exit:
while (i--) {
led_classdev_unregister(&pca955x[i].led_cdev);
cancel_work_sync(&pca955x[i].work);
}
kfree(pca955x);
i2c_set_clientdata(client, NULL);
return err;
}
static int __devexit pca955x_remove(struct i2c_client *client)
{
struct pca955x_led *pca955x = i2c_get_clientdata(client);
int i;
for (i = 0; i < pca955x->chipdef->bits; i++) {
led_classdev_unregister(&pca955x[i].led_cdev);
cancel_work_sync(&pca955x[i].work);
}
kfree(pca955x);
i2c_set_clientdata(client, NULL);
return 0;
}
static struct i2c_driver pca955x_driver = {
.driver = {
.name = "leds-pca955x",
.owner = THIS_MODULE,
},
.probe = pca955x_probe,
.remove = __devexit_p(pca955x_remove),
.id_table = pca955x_id,
};
static int __init pca955x_leds_init(void)
{
return i2c_add_driver(&pca955x_driver);
}
static void __exit pca955x_leds_exit(void)
{
i2c_del_driver(&pca955x_driver);
}
module_init(pca955x_leds_init);
module_exit(pca955x_leds_exit);
MODULE_AUTHOR("Nate Case <ncase@xes-inc.com>");
MODULE_DESCRIPTION("PCA955x LED driver");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,153 @@
/*
* linux/drivers/leds-pwm.c
*
* simple PWM based LED control
*
* Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de)
*
* based on leds-gpio.c by Raphael Assenat <raph@8d.com>
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/leds.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/leds_pwm.h>
struct led_pwm_data {
struct led_classdev cdev;
struct pwm_device *pwm;
unsigned int active_low;
unsigned int period;
unsigned int max_brightness;
};
static void led_pwm_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct led_pwm_data *led_dat =
container_of(led_cdev, struct led_pwm_data, cdev);
unsigned int max = led_dat->max_brightness;
unsigned int period = led_dat->period;
if (brightness == 0) {
pwm_config(led_dat->pwm, 0, period);
pwm_disable(led_dat->pwm);
} else {
pwm_config(led_dat->pwm, brightness * period / max, period);
pwm_enable(led_dat->pwm);
}
}
static int led_pwm_probe(struct platform_device *pdev)
{
struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
struct led_pwm *cur_led;
struct led_pwm_data *leds_data, *led_dat;
int i, ret = 0;
if (!pdata)
return -EBUSY;
leds_data = kzalloc(sizeof(struct led_pwm_data) * pdata->num_leds,
GFP_KERNEL);
if (!leds_data)
return -ENOMEM;
for (i = 0; i < pdata->num_leds; i++) {
cur_led = &pdata->leds[i];
led_dat = &leds_data[i];
led_dat->pwm = pwm_request(cur_led->pwm_id,
cur_led->name);
if (IS_ERR(led_dat->pwm)) {
dev_err(&pdev->dev, "unable to request PWM %d\n",
cur_led->pwm_id);
goto err;
}
led_dat->cdev.name = cur_led->name;
led_dat->cdev.default_trigger = cur_led->default_trigger;
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->cdev.brightness_set = led_pwm_set;
led_dat->cdev.brightness = LED_OFF;
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
if (ret < 0) {
pwm_free(led_dat->pwm);
goto err;
}
}
platform_set_drvdata(pdev, leds_data);
return 0;
err:
if (i > 0) {
for (i = i - 1; i >= 0; i--) {
led_classdev_unregister(&leds_data[i].cdev);
pwm_free(leds_data[i].pwm);
}
}
kfree(leds_data);
return ret;
}
static int __devexit led_pwm_remove(struct platform_device *pdev)
{
int i;
struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
struct led_pwm_data *leds_data;
leds_data = platform_get_drvdata(pdev);
for (i = 0; i < pdata->num_leds; i++) {
led_classdev_unregister(&leds_data[i].cdev);
pwm_free(leds_data[i].pwm);
}
kfree(leds_data);
return 0;
}
static struct platform_driver led_pwm_driver = {
.probe = led_pwm_probe,
.remove = __devexit_p(led_pwm_remove),
.driver = {
.name = "leds_pwm",
.owner = THIS_MODULE,
},
};
static int __init led_pwm_init(void)
{
return platform_driver_register(&led_pwm_driver);
}
static void __exit led_pwm_exit(void)
{
platform_driver_unregister(&led_pwm_driver);
}
module_init(led_pwm_init);
module_exit(led_pwm_exit);
MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
MODULE_DESCRIPTION("PWM LED driver for PXA");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:leds-pwm");

View File

@@ -0,0 +1,77 @@
/*
* LEDs driver for the "User LED" on Routerboard532
*
* Copyright (C) 2009 Phil Sutter <n0-1@freewrt.org>
*
* Based on leds-cobalt-qube.c by Florian Fainelly and
* rb-diag.c (my own standalone driver for both LED and
* button of Routerboard532).
*/
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/mach-rc32434/gpio.h>
#include <asm/mach-rc32434/rb.h>
static void rb532_led_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
if (brightness)
set_latch_u5(LO_ULED, 0);
else
set_latch_u5(0, LO_ULED);
}
static enum led_brightness rb532_led_get(struct led_classdev *cdev)
{
return (get_latch_u5() & LO_ULED) ? LED_FULL : LED_OFF;
}
static struct led_classdev rb532_uled = {
.name = "uled",
.brightness_set = rb532_led_set,
.brightness_get = rb532_led_get,
.default_trigger = "nand-disk",
};
static int __devinit rb532_led_probe(struct platform_device *pdev)
{
return led_classdev_register(&pdev->dev, &rb532_uled);
}
static int __devexit rb532_led_remove(struct platform_device *pdev)
{
led_classdev_unregister(&rb532_uled);
return 0;
}
static struct platform_driver rb532_led_driver = {
.probe = rb532_led_probe,
.remove = __devexit_p(rb532_led_remove),
.driver = {
.name = "rb532-led",
.owner = THIS_MODULE,
},
};
static int __init rb532_led_init(void)
{
return platform_driver_register(&rb532_led_driver);
}
static void __exit rb532_led_exit(void)
{
platform_driver_unregister(&rb532_led_driver);
}
module_init(rb532_led_init);
module_exit(rb532_led_exit);
MODULE_ALIAS("platform:rb532-led");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("User LED support for Routerboard532");
MODULE_AUTHOR("Phil Sutter <n0-1@freewrt.org>");

View File

@@ -0,0 +1,138 @@
/* drivers/leds/leds-s3c24xx.c
*
* (c) 2006 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
* S3C24XX - LEDs GPIO 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/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/gpio.h>
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
#include <mach/leds-gpio.h>
/* our context */
struct s3c24xx_gpio_led {
struct led_classdev cdev;
struct s3c24xx_led_platdata *pdata;
};
static inline struct s3c24xx_gpio_led *pdev_to_gpio(struct platform_device *dev)
{
return platform_get_drvdata(dev);
}
static inline struct s3c24xx_gpio_led *to_gpio(struct led_classdev *led_cdev)
{
return container_of(led_cdev, struct s3c24xx_gpio_led, cdev);
}
static void s3c24xx_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct s3c24xx_gpio_led *led = to_gpio(led_cdev);
struct s3c24xx_led_platdata *pd = led->pdata;
/* there will be a short delay between setting the output and
* going from output to input when using tristate. */
s3c2410_gpio_setpin(pd->gpio, (value ? 1 : 0) ^
(pd->flags & S3C24XX_LEDF_ACTLOW));
if (pd->flags & S3C24XX_LEDF_TRISTATE)
s3c2410_gpio_cfgpin(pd->gpio,
value ? S3C2410_GPIO_OUTPUT : S3C2410_GPIO_INPUT);
}
static int s3c24xx_led_remove(struct platform_device *dev)
{
struct s3c24xx_gpio_led *led = pdev_to_gpio(dev);
led_classdev_unregister(&led->cdev);
kfree(led);
return 0;
}
static int s3c24xx_led_probe(struct platform_device *dev)
{
struct s3c24xx_led_platdata *pdata = dev->dev.platform_data;
struct s3c24xx_gpio_led *led;
int ret;
led = kzalloc(sizeof(struct s3c24xx_gpio_led), GFP_KERNEL);
if (led == NULL) {
dev_err(&dev->dev, "No memory for device\n");
return -ENOMEM;
}
platform_set_drvdata(dev, led);
led->cdev.brightness_set = s3c24xx_led_set;
led->cdev.default_trigger = pdata->def_trigger;
led->cdev.name = pdata->name;
led->cdev.flags |= LED_CORE_SUSPENDRESUME;
led->pdata = pdata;
/* no point in having a pull-up if we are always driving */
if (pdata->flags & S3C24XX_LEDF_TRISTATE) {
s3c2410_gpio_setpin(pdata->gpio, 0);
s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_INPUT);
} else {
s3c2410_gpio_pullup(pdata->gpio, 0);
s3c2410_gpio_setpin(pdata->gpio, 0);
s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_OUTPUT);
}
/* register our new led device */
ret = led_classdev_register(&dev->dev, &led->cdev);
if (ret < 0) {
dev_err(&dev->dev, "led_classdev_register failed\n");
kfree(led);
return ret;
}
return 0;
}
static struct platform_driver s3c24xx_led_driver = {
.probe = s3c24xx_led_probe,
.remove = s3c24xx_led_remove,
.driver = {
.name = "s3c24xx_led",
.owner = THIS_MODULE,
},
};
static int __init s3c24xx_led_init(void)
{
return platform_driver_register(&s3c24xx_led_driver);
}
static void __exit s3c24xx_led_exit(void)
{
platform_driver_unregister(&s3c24xx_led_driver);
}
module_init(s3c24xx_led_init);
module_exit(s3c24xx_led_exit);
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
MODULE_DESCRIPTION("S3C24XX LED driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:s3c24xx_led");

View File

@@ -0,0 +1,273 @@
/* leds-sunfire.c: SUNW,Ultra-Enterprise LED driver.
*
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/leds.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <asm/fhc.h>
#include <asm/upa.h>
#define DRIVER_NAME "leds-sunfire"
#define PFX DRIVER_NAME ": "
MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
MODULE_DESCRIPTION("Sun Fire LED driver");
MODULE_LICENSE("GPL");
struct sunfire_led {
struct led_classdev led_cdev;
void __iomem *reg;
};
#define to_sunfire_led(d) container_of(d, struct sunfire_led, led_cdev)
static void __clockboard_set(struct led_classdev *led_cdev,
enum led_brightness led_val, u8 bit)
{
struct sunfire_led *p = to_sunfire_led(led_cdev);
u8 reg = upa_readb(p->reg);
switch (bit) {
case CLOCK_CTRL_LLED:
if (led_val)
reg &= ~bit;
else
reg |= bit;
break;
default:
if (led_val)
reg |= bit;
else
reg &= ~bit;
break;
}
upa_writeb(reg, p->reg);
}
static void clockboard_left_set(struct led_classdev *led_cdev,
enum led_brightness led_val)
{
__clockboard_set(led_cdev, led_val, CLOCK_CTRL_LLED);
}
static void clockboard_middle_set(struct led_classdev *led_cdev,
enum led_brightness led_val)
{
__clockboard_set(led_cdev, led_val, CLOCK_CTRL_MLED);
}
static void clockboard_right_set(struct led_classdev *led_cdev,
enum led_brightness led_val)
{
__clockboard_set(led_cdev, led_val, CLOCK_CTRL_RLED);
}
static void __fhc_set(struct led_classdev *led_cdev,
enum led_brightness led_val, u32 bit)
{
struct sunfire_led *p = to_sunfire_led(led_cdev);
u32 reg = upa_readl(p->reg);
switch (bit) {
case FHC_CONTROL_LLED:
if (led_val)
reg &= ~bit;
else
reg |= bit;
break;
default:
if (led_val)
reg |= bit;
else
reg &= ~bit;
break;
}
upa_writel(reg, p->reg);
}
static void fhc_left_set(struct led_classdev *led_cdev,
enum led_brightness led_val)
{
__fhc_set(led_cdev, led_val, FHC_CONTROL_LLED);
}
static void fhc_middle_set(struct led_classdev *led_cdev,
enum led_brightness led_val)
{
__fhc_set(led_cdev, led_val, FHC_CONTROL_MLED);
}
static void fhc_right_set(struct led_classdev *led_cdev,
enum led_brightness led_val)
{
__fhc_set(led_cdev, led_val, FHC_CONTROL_RLED);
}
typedef void (*set_handler)(struct led_classdev *, enum led_brightness);
struct led_type {
const char *name;
set_handler handler;
const char *default_trigger;
};
#define NUM_LEDS_PER_BOARD 3
struct sunfire_drvdata {
struct sunfire_led leds[NUM_LEDS_PER_BOARD];
};
static int __devinit sunfire_led_generic_probe(struct platform_device *pdev,
struct led_type *types)
{
struct sunfire_drvdata *p;
int i, err = -EINVAL;
if (pdev->num_resources != 1) {
printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n",
pdev->num_resources);
goto out;
}
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (!p) {
printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n");
goto out;
}
for (i = 0; i < NUM_LEDS_PER_BOARD; i++) {
struct led_classdev *lp = &p->leds[i].led_cdev;
p->leds[i].reg = (void __iomem *) pdev->resource[0].start;
lp->name = types[i].name;
lp->brightness = LED_FULL;
lp->brightness_set = types[i].handler;
lp->default_trigger = types[i].default_trigger;
err = led_classdev_register(&pdev->dev, lp);
if (err) {
printk(KERN_ERR PFX "Could not register %s LED\n",
lp->name);
goto out_unregister_led_cdevs;
}
}
dev_set_drvdata(&pdev->dev, p);
err = 0;
out:
return err;
out_unregister_led_cdevs:
for (i--; i >= 0; i--)
led_classdev_unregister(&p->leds[i].led_cdev);
goto out;
}
static int __devexit sunfire_led_generic_remove(struct platform_device *pdev)
{
struct sunfire_drvdata *p = dev_get_drvdata(&pdev->dev);
int i;
for (i = 0; i < NUM_LEDS_PER_BOARD; i++)
led_classdev_unregister(&p->leds[i].led_cdev);
kfree(p);
return 0;
}
static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = {
{
.name = "clockboard-left",
.handler = clockboard_left_set,
},
{
.name = "clockboard-middle",
.handler = clockboard_middle_set,
},
{
.name = "clockboard-right",
.handler = clockboard_right_set,
.default_trigger= "heartbeat",
},
};
static int __devinit sunfire_clockboard_led_probe(struct platform_device *pdev)
{
return sunfire_led_generic_probe(pdev, clockboard_led_types);
}
static struct led_type fhc_led_types[NUM_LEDS_PER_BOARD] = {
{
.name = "fhc-left",
.handler = fhc_left_set,
},
{
.name = "fhc-middle",
.handler = fhc_middle_set,
},
{
.name = "fhc-right",
.handler = fhc_right_set,
.default_trigger= "heartbeat",
},
};
static int __devinit sunfire_fhc_led_probe(struct platform_device *pdev)
{
return sunfire_led_generic_probe(pdev, fhc_led_types);
}
MODULE_ALIAS("platform:sunfire-clockboard-leds");
MODULE_ALIAS("platform:sunfire-fhc-leds");
static struct platform_driver sunfire_clockboard_led_driver = {
.probe = sunfire_clockboard_led_probe,
.remove = __devexit_p(sunfire_led_generic_remove),
.driver = {
.name = "sunfire-clockboard-leds",
.owner = THIS_MODULE,
},
};
static struct platform_driver sunfire_fhc_led_driver = {
.probe = sunfire_fhc_led_probe,
.remove = __devexit_p(sunfire_led_generic_remove),
.driver = {
.name = "sunfire-fhc-leds",
.owner = THIS_MODULE,
},
};
static int __init sunfire_leds_init(void)
{
int err = platform_driver_register(&sunfire_clockboard_led_driver);
if (err) {
printk(KERN_ERR PFX "Could not register clock board LED driver\n");
return err;
}
err = platform_driver_register(&sunfire_fhc_led_driver);
if (err) {
printk(KERN_ERR PFX "Could not register FHC LED driver\n");
platform_driver_unregister(&sunfire_clockboard_led_driver);
}
return err;
}
static void __exit sunfire_leds_exit(void)
{
platform_driver_unregister(&sunfire_clockboard_led_driver);
platform_driver_unregister(&sunfire_fhc_led_driver);
}
module_init(sunfire_leds_init);
module_exit(sunfire_leds_exit);

View File

@@ -0,0 +1,341 @@
/*
* LED driver for WM831x status LEDs
*
* Copyright(C) 2009 Wolfson Microelectronics PLC.
*
* 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/err.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
#include <linux/mfd/wm831x/status.h>
struct wm831x_status {
struct led_classdev cdev;
struct wm831x *wm831x;
struct work_struct work;
struct mutex mutex;
spinlock_t value_lock;
int reg; /* Control register */
int reg_val; /* Control register value */
int blink;
int blink_time;
int blink_cyc;
int src;
enum led_brightness brightness;
};
#define to_wm831x_status(led_cdev) \
container_of(led_cdev, struct wm831x_status, cdev)
static void wm831x_status_work(struct work_struct *work)
{
struct wm831x_status *led = container_of(work, struct wm831x_status,
work);
unsigned long flags;
mutex_lock(&led->mutex);
led->reg_val &= ~(WM831X_LED_SRC_MASK | WM831X_LED_MODE_MASK |
WM831X_LED_DUTY_CYC_MASK | WM831X_LED_DUR_MASK);
spin_lock_irqsave(&led->value_lock, flags);
led->reg_val |= led->src << WM831X_LED_SRC_SHIFT;
if (led->blink) {
led->reg_val |= 2 << WM831X_LED_MODE_SHIFT;
led->reg_val |= led->blink_time << WM831X_LED_DUR_SHIFT;
led->reg_val |= led->blink_cyc;
} else {
if (led->brightness != LED_OFF)
led->reg_val |= 1 << WM831X_LED_MODE_SHIFT;
}
spin_unlock_irqrestore(&led->value_lock, flags);
wm831x_reg_write(led->wm831x, led->reg, led->reg_val);
mutex_unlock(&led->mutex);
}
static void wm831x_status_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct wm831x_status *led = to_wm831x_status(led_cdev);
unsigned long flags;
spin_lock_irqsave(&led->value_lock, flags);
led->brightness = value;
if (value == LED_OFF)
led->blink = 0;
schedule_work(&led->work);
spin_unlock_irqrestore(&led->value_lock, flags);
}
static int wm831x_status_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
struct wm831x_status *led = to_wm831x_status(led_cdev);
unsigned long flags;
int ret = 0;
/* Pick some defaults if we've not been given times */
if (*delay_on == 0 && *delay_off == 0) {
*delay_on = 250;
*delay_off = 250;
}
spin_lock_irqsave(&led->value_lock, flags);
/* We only have a limited selection of settings, see if we can
* support the configuration we're being given */
switch (*delay_on) {
case 1000:
led->blink_time = 0;
break;
case 250:
led->blink_time = 1;
break;
case 125:
led->blink_time = 2;
break;
case 62:
case 63:
/* Actually 62.5ms */
led->blink_time = 3;
break;
default:
ret = -EINVAL;
break;
}
if (ret == 0) {
switch (*delay_off / *delay_on) {
case 1:
led->blink_cyc = 0;
break;
case 3:
led->blink_cyc = 1;
break;
case 4:
led->blink_cyc = 2;
break;
case 8:
led->blink_cyc = 3;
break;
default:
ret = -EINVAL;
break;
}
}
if (ret == 0)
led->blink = 1;
else
led->blink = 0;
/* Always update; if we fail turn off blinking since we expect
* a software fallback. */
schedule_work(&led->work);
spin_unlock_irqrestore(&led->value_lock, flags);
return ret;
}
static const char *led_src_texts[] = {
"otp",
"power",
"charger",
"soft",
};
static ssize_t wm831x_status_src_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct wm831x_status *led = to_wm831x_status(led_cdev);
int i;
ssize_t ret = 0;
mutex_lock(&led->mutex);
for (i = 0; i < ARRAY_SIZE(led_src_texts); i++)
if (i == led->src)
ret += sprintf(&buf[ret], "[%s] ", led_src_texts[i]);
else
ret += sprintf(&buf[ret], "%s ", led_src_texts[i]);
mutex_unlock(&led->mutex);
ret += sprintf(&buf[ret], "\n");
return ret;
}
static ssize_t wm831x_status_src_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct wm831x_status *led = to_wm831x_status(led_cdev);
char name[20];
int i;
size_t len;
name[sizeof(name) - 1] = '\0';
strncpy(name, buf, sizeof(name) - 1);
len = strlen(name);
if (len && name[len - 1] == '\n')
name[len - 1] = '\0';
for (i = 0; i < ARRAY_SIZE(led_src_texts); i++) {
if (!strcmp(name, led_src_texts[i])) {
mutex_lock(&led->mutex);
led->src = i;
schedule_work(&led->work);
mutex_unlock(&led->mutex);
}
}
return size;
}
static DEVICE_ATTR(src, 0644, wm831x_status_src_show, wm831x_status_src_store);
static int wm831x_status_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *chip_pdata;
struct wm831x_status_pdata pdata;
struct wm831x_status *drvdata;
struct resource *res;
int id = pdev->id % ARRAY_SIZE(chip_pdata->status);
int ret;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (res == NULL) {
dev_err(&pdev->dev, "No I/O resource\n");
ret = -EINVAL;
goto err;
}
drvdata = kzalloc(sizeof(struct wm831x_status), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
dev_set_drvdata(&pdev->dev, drvdata);
drvdata->wm831x = wm831x;
drvdata->reg = res->start;
if (wm831x->dev->platform_data)
chip_pdata = wm831x->dev->platform_data;
else
chip_pdata = NULL;
memset(&pdata, 0, sizeof(pdata));
if (chip_pdata && chip_pdata->status[id])
memcpy(&pdata, chip_pdata->status[id], sizeof(pdata));
else
pdata.name = dev_name(&pdev->dev);
mutex_init(&drvdata->mutex);
INIT_WORK(&drvdata->work, wm831x_status_work);
spin_lock_init(&drvdata->value_lock);
/* We cache the configuration register and read startup values
* from it. */
drvdata->reg_val = wm831x_reg_read(wm831x, drvdata->reg);
if (drvdata->reg_val & WM831X_LED_MODE_MASK)
drvdata->brightness = LED_FULL;
else
drvdata->brightness = LED_OFF;
/* Set a default source if configured, otherwise leave the
* current hardware setting.
*/
if (pdata.default_src == WM831X_STATUS_PRESERVE) {
drvdata->src = drvdata->reg_val;
drvdata->src &= WM831X_LED_SRC_MASK;
drvdata->src >>= WM831X_LED_SRC_SHIFT;
} else {
drvdata->src = pdata.default_src - 1;
}
drvdata->cdev.name = pdata.name;
drvdata->cdev.default_trigger = pdata.default_trigger;
drvdata->cdev.brightness_set = wm831x_status_set;
drvdata->cdev.blink_set = wm831x_status_blink_set;
ret = led_classdev_register(wm831x->dev, &drvdata->cdev);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register LED: %d\n", ret);
goto err_led;
}
ret = device_create_file(drvdata->cdev.dev, &dev_attr_src);
if (ret != 0)
dev_err(&pdev->dev,
"No source control for LED: %d\n", ret);
return 0;
err_led:
led_classdev_unregister(&drvdata->cdev);
kfree(drvdata);
err:
return ret;
}
static int wm831x_status_remove(struct platform_device *pdev)
{
struct wm831x_status *drvdata = platform_get_drvdata(pdev);
device_remove_file(drvdata->cdev.dev, &dev_attr_src);
led_classdev_unregister(&drvdata->cdev);
kfree(drvdata);
return 0;
}
static struct platform_driver wm831x_status_driver = {
.driver = {
.name = "wm831x-status",
.owner = THIS_MODULE,
},
.probe = wm831x_status_probe,
.remove = wm831x_status_remove,
};
static int __devinit wm831x_status_init(void)
{
return platform_driver_register(&wm831x_status_driver);
}
module_init(wm831x_status_init);
static void wm831x_status_exit(void)
{
platform_driver_unregister(&wm831x_status_driver);
}
module_exit(wm831x_status_exit);
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_DESCRIPTION("WM831x status LED driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:wm831x-status");

View File

@@ -0,0 +1,311 @@
/*
* LED driver for WM8350 driven LEDS.
*
* Copyright(C) 2007, 2008 Wolfson Microelectronics PLC.
*
* 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/err.h>
#include <linux/mfd/wm8350/pmic.h>
#include <linux/regulator/consumer.h>
/* Microamps */
static const int isink_cur[] = {
4,
5,
6,
7,
8,
10,
11,
14,
16,
19,
23,
27,
32,
39,
46,
54,
65,
77,
92,
109,
130,
154,
183,
218,
259,
308,
367,
436,
518,
616,
733,
872,
1037,
1233,
1466,
1744,
2073,
2466,
2933,
3487,
4147,
4932,
5865,
6975,
8294,
9864,
11730,
13949,
16589,
19728,
23460,
27899,
33178,
39455,
46920,
55798,
66355,
78910,
93840,
111596,
132710,
157820,
187681,
223191
};
#define to_wm8350_led(led_cdev) \
container_of(led_cdev, struct wm8350_led, cdev)
static void wm8350_led_enable(struct wm8350_led *led)
{
int ret;
if (led->enabled)
return;
ret = regulator_enable(led->isink);
if (ret != 0) {
dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret);
return;
}
ret = regulator_enable(led->dcdc);
if (ret != 0) {
dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret);
regulator_disable(led->isink);
return;
}
led->enabled = 1;
}
static void wm8350_led_disable(struct wm8350_led *led)
{
int ret;
if (!led->enabled)
return;
ret = regulator_disable(led->dcdc);
if (ret != 0) {
dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret);
return;
}
ret = regulator_disable(led->isink);
if (ret != 0) {
dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret);
regulator_enable(led->dcdc);
return;
}
led->enabled = 0;
}
static void led_work(struct work_struct *work)
{
struct wm8350_led *led = container_of(work, struct wm8350_led, work);
int ret;
int uA;
unsigned long flags;
mutex_lock(&led->mutex);
spin_lock_irqsave(&led->value_lock, flags);
if (led->value == LED_OFF) {
spin_unlock_irqrestore(&led->value_lock, flags);
wm8350_led_disable(led);
goto out;
}
/* This scales linearly into the index of valid current
* settings which results in a linear scaling of perceived
* brightness due to the non-linear current settings provided
* by the hardware.
*/
uA = (led->max_uA_index * led->value) / LED_FULL;
spin_unlock_irqrestore(&led->value_lock, flags);
BUG_ON(uA >= ARRAY_SIZE(isink_cur));
ret = regulator_set_current_limit(led->isink, isink_cur[uA],
isink_cur[uA]);
if (ret != 0)
dev_err(led->cdev.dev, "Failed to set %duA: %d\n",
isink_cur[uA], ret);
wm8350_led_enable(led);
out:
mutex_unlock(&led->mutex);
}
static void wm8350_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct wm8350_led *led = to_wm8350_led(led_cdev);
unsigned long flags;
spin_lock_irqsave(&led->value_lock, flags);
led->value = value;
schedule_work(&led->work);
spin_unlock_irqrestore(&led->value_lock, flags);
}
static void wm8350_led_shutdown(struct platform_device *pdev)
{
struct wm8350_led *led = platform_get_drvdata(pdev);
mutex_lock(&led->mutex);
led->value = LED_OFF;
wm8350_led_disable(led);
mutex_unlock(&led->mutex);
}
static int wm8350_led_probe(struct platform_device *pdev)
{
struct regulator *isink, *dcdc;
struct wm8350_led *led;
struct wm8350_led_platform_data *pdata = pdev->dev.platform_data;
int ret, i;
if (pdata == NULL) {
dev_err(&pdev->dev, "no platform data\n");
return -ENODEV;
}
if (pdata->max_uA < isink_cur[0]) {
dev_err(&pdev->dev, "Invalid maximum current %duA\n",
pdata->max_uA);
return -EINVAL;
}
isink = regulator_get(&pdev->dev, "led_isink");
if (IS_ERR(isink)) {
printk(KERN_ERR "%s: cant get ISINK\n", __func__);
return PTR_ERR(isink);
}
dcdc = regulator_get(&pdev->dev, "led_vcc");
if (IS_ERR(dcdc)) {
printk(KERN_ERR "%s: cant get DCDC\n", __func__);
ret = PTR_ERR(dcdc);
goto err_isink;
}
led = kzalloc(sizeof(*led), GFP_KERNEL);
if (led == NULL) {
ret = -ENOMEM;
goto err_dcdc;
}
led->cdev.brightness_set = wm8350_led_set;
led->cdev.default_trigger = pdata->default_trigger;
led->cdev.name = pdata->name;
led->cdev.flags |= LED_CORE_SUSPENDRESUME;
led->enabled = regulator_is_enabled(isink);
led->isink = isink;
led->dcdc = dcdc;
for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++)
if (isink_cur[i] >= pdata->max_uA)
break;
led->max_uA_index = i;
if (pdata->max_uA != isink_cur[i])
dev_warn(&pdev->dev,
"Maximum current %duA is not directly supported,"
" check platform data\n",
pdata->max_uA);
spin_lock_init(&led->value_lock);
mutex_init(&led->mutex);
INIT_WORK(&led->work, led_work);
led->value = LED_OFF;
platform_set_drvdata(pdev, led);
ret = led_classdev_register(&pdev->dev, &led->cdev);
if (ret < 0)
goto err_led;
return 0;
err_led:
kfree(led);
err_dcdc:
regulator_put(dcdc);
err_isink:
regulator_put(isink);
return ret;
}
static int wm8350_led_remove(struct platform_device *pdev)
{
struct wm8350_led *led = platform_get_drvdata(pdev);
led_classdev_unregister(&led->cdev);
flush_scheduled_work();
wm8350_led_disable(led);
regulator_put(led->dcdc);
regulator_put(led->isink);
kfree(led);
return 0;
}
static struct platform_driver wm8350_led_driver = {
.driver = {
.name = "wm8350-led",
.owner = THIS_MODULE,
},
.probe = wm8350_led_probe,
.remove = wm8350_led_remove,
.shutdown = wm8350_led_shutdown,
};
static int __devinit wm8350_led_init(void)
{
return platform_driver_register(&wm8350_led_driver);
}
module_init(wm8350_led_init);
static void wm8350_led_exit(void)
{
platform_driver_unregister(&wm8350_led_driver);
}
module_exit(wm8350_led_exit);
MODULE_AUTHOR("Mark Brown");
MODULE_DESCRIPTION("WM8350 LED driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:wm8350-led");

View File

@@ -0,0 +1,153 @@
/*
* LEDs driver for PCEngines WRAP
*
* Copyright (C) 2006 Kristian Kielhofner <kris@krisk.org>
*
* Based on leds-net48xx.c
*
* 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/err.h>
#include <asm/io.h>
#include <linux/scx200_gpio.h>
#define DRVNAME "wrap-led"
#define WRAP_POWER_LED_GPIO 2
#define WRAP_ERROR_LED_GPIO 3
#define WRAP_EXTRA_LED_GPIO 18
static struct platform_device *pdev;
static void wrap_power_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (value)
scx200_gpio_set_low(WRAP_POWER_LED_GPIO);
else
scx200_gpio_set_high(WRAP_POWER_LED_GPIO);
}
static void wrap_error_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (value)
scx200_gpio_set_low(WRAP_ERROR_LED_GPIO);
else
scx200_gpio_set_high(WRAP_ERROR_LED_GPIO);
}
static void wrap_extra_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (value)
scx200_gpio_set_low(WRAP_EXTRA_LED_GPIO);
else
scx200_gpio_set_high(WRAP_EXTRA_LED_GPIO);
}
static struct led_classdev wrap_power_led = {
.name = "wrap::power",
.brightness_set = wrap_power_led_set,
.default_trigger = "default-on",
.flags = LED_CORE_SUSPENDRESUME,
};
static struct led_classdev wrap_error_led = {
.name = "wrap::error",
.brightness_set = wrap_error_led_set,
.flags = LED_CORE_SUSPENDRESUME,
};
static struct led_classdev wrap_extra_led = {
.name = "wrap::extra",
.brightness_set = wrap_extra_led_set,
.flags = LED_CORE_SUSPENDRESUME,
};
static int wrap_led_probe(struct platform_device *pdev)
{
int ret;
ret = led_classdev_register(&pdev->dev, &wrap_power_led);
if (ret < 0)
return ret;
ret = led_classdev_register(&pdev->dev, &wrap_error_led);
if (ret < 0)
goto err1;
ret = led_classdev_register(&pdev->dev, &wrap_extra_led);
if (ret < 0)
goto err2;
return ret;
err2:
led_classdev_unregister(&wrap_error_led);
err1:
led_classdev_unregister(&wrap_power_led);
return ret;
}
static int wrap_led_remove(struct platform_device *pdev)
{
led_classdev_unregister(&wrap_power_led);
led_classdev_unregister(&wrap_error_led);
led_classdev_unregister(&wrap_extra_led);
return 0;
}
static struct platform_driver wrap_led_driver = {
.probe = wrap_led_probe,
.remove = wrap_led_remove,
.driver = {
.name = DRVNAME,
.owner = THIS_MODULE,
},
};
static int __init wrap_led_init(void)
{
int ret;
if (!scx200_gpio_present()) {
ret = -ENODEV;
goto out;
}
ret = platform_driver_register(&wrap_led_driver);
if (ret < 0)
goto out;
pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0);
if (IS_ERR(pdev)) {
ret = PTR_ERR(pdev);
platform_driver_unregister(&wrap_led_driver);
goto out;
}
out:
return ret;
}
static void __exit wrap_led_exit(void)
{
platform_device_unregister(pdev);
platform_driver_unregister(&wrap_led_driver);
}
module_init(wrap_led_init);
module_exit(wrap_led_exit);
MODULE_AUTHOR("Kristian Kielhofner <kris@krisk.org>");
MODULE_DESCRIPTION("PCEngines WRAP LED driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,54 @@
/*
* LED Core
*
* Copyright 2005 Openedhand Ltd.
*
* Author: Richard Purdie <rpurdie@openedhand.com>
*
* 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 __LEDS_H_INCLUDED
#define __LEDS_H_INCLUDED
#include <linux/device.h>
#include <linux/rwsem.h>
#include <linux/leds.h>
static inline void led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (value > led_cdev->max_brightness)
value = led_cdev->max_brightness;
led_cdev->brightness = value;
if (!(led_cdev->flags & LED_SUSPENDED))
led_cdev->brightness_set(led_cdev, value);
}
static inline int led_get_brightness(struct led_classdev *led_cdev)
{
return led_cdev->brightness;
}
extern struct rw_semaphore leds_list_lock;
extern struct list_head leds_list;
#ifdef CONFIG_LEDS_TRIGGERS
void led_trigger_set_default(struct led_classdev *led_cdev);
void led_trigger_set(struct led_classdev *led_cdev,
struct led_trigger *trigger);
void led_trigger_remove(struct led_classdev *led_cdev);
#else
#define led_trigger_set_default(x) do {} while (0)
#define led_trigger_set(x, y) do {} while (0)
#define led_trigger_remove(x) do {} while (0)
#endif
ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
char *buf);
#endif /* __LEDS_H_INCLUDED */

View File

@@ -0,0 +1,110 @@
/*
* Backlight emulation LED trigger
*
* Copyright 2008 (C) Rodolfo Giometti <giometti@linux.it>
* Copyright 2008 (C) Eurotech S.p.A. <info@eurotech.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.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fb.h>
#include <linux/leds.h>
#include "leds.h"
#define BLANK 1
#define UNBLANK 0
struct bl_trig_notifier {
struct led_classdev *led;
int brightness;
int old_status;
struct notifier_block notifier;
};
static int fb_notifier_callback(struct notifier_block *p,
unsigned long event, void *data)
{
struct bl_trig_notifier *n = container_of(p,
struct bl_trig_notifier, notifier);
struct led_classdev *led = n->led;
struct fb_event *fb_event = data;
int *blank = fb_event->data;
switch (event) {
case FB_EVENT_BLANK :
if (*blank && n->old_status == UNBLANK) {
n->brightness = led->brightness;
led_set_brightness(led, LED_OFF);
n->old_status = BLANK;
} else if (!*blank && n->old_status == BLANK) {
led_set_brightness(led, n->brightness);
n->old_status = UNBLANK;
}
break;
}
return 0;
}
static void bl_trig_activate(struct led_classdev *led)
{
int ret;
struct bl_trig_notifier *n;
n = kzalloc(sizeof(struct bl_trig_notifier), GFP_KERNEL);
led->trigger_data = n;
if (!n) {
dev_err(led->dev, "unable to allocate backlight trigger\n");
return;
}
n->led = led;
n->brightness = led->brightness;
n->old_status = UNBLANK;
n->notifier.notifier_call = fb_notifier_callback;
ret = fb_register_client(&n->notifier);
if (ret)
dev_err(led->dev, "unable to register backlight trigger\n");
}
static void bl_trig_deactivate(struct led_classdev *led)
{
struct bl_trig_notifier *n =
(struct bl_trig_notifier *) led->trigger_data;
if (n) {
fb_unregister_client(&n->notifier);
kfree(n);
}
}
static struct led_trigger bl_led_trigger = {
.name = "backlight",
.activate = bl_trig_activate,
.deactivate = bl_trig_deactivate
};
static int __init bl_trig_init(void)
{
return led_trigger_register(&bl_led_trigger);
}
static void __exit bl_trig_exit(void)
{
led_trigger_unregister(&bl_led_trigger);
}
module_init(bl_trig_init);
module_exit(bl_trig_exit);
MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
MODULE_DESCRIPTION("Backlight emulation LED trigger");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,45 @@
/*
* LED Kernel Default ON Trigger
*
* Copyright 2008 Nick Forbes <nick.forbes@incepta.com>
*
* Based on Richard Purdie's ledtrig-timer.c.
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/leds.h>
#include "leds.h"
static void defon_trig_activate(struct led_classdev *led_cdev)
{
led_set_brightness(led_cdev, led_cdev->max_brightness);
}
static struct led_trigger defon_led_trigger = {
.name = "default-on",
.activate = defon_trig_activate,
};
static int __init defon_trig_init(void)
{
return led_trigger_register(&defon_led_trigger);
}
static void __exit defon_trig_exit(void)
{
led_trigger_unregister(&defon_led_trigger);
}
module_init(defon_trig_init);
module_exit(defon_trig_exit);
MODULE_AUTHOR("Nick Forbes <nick.forbes@incepta.com>");
MODULE_DESCRIPTION("Default-ON LED trigger");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,249 @@
/*
* ledtrig-gio.c - LED Trigger Based on GPIO events
*
* Copyright 2009 Felipe Balbi <me@felipebalbi.com>
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/leds.h>
#include "leds.h"
struct gpio_trig_data {
struct led_classdev *led;
struct work_struct work;
unsigned desired_brightness; /* desired brightness when led is on */
unsigned inverted; /* true when gpio is inverted */
unsigned gpio; /* gpio that triggers the leds */
};
static irqreturn_t gpio_trig_irq(int irq, void *_led)
{
struct led_classdev *led = _led;
struct gpio_trig_data *gpio_data = led->trigger_data;
/* just schedule_work since gpio_get_value can sleep */
schedule_work(&gpio_data->work);
return IRQ_HANDLED;
};
static void gpio_trig_work(struct work_struct *work)
{
struct gpio_trig_data *gpio_data = container_of(work,
struct gpio_trig_data, work);
int tmp;
if (!gpio_data->gpio)
return;
tmp = gpio_get_value(gpio_data->gpio);
if (gpio_data->inverted)
tmp = !tmp;
if (tmp) {
if (gpio_data->desired_brightness)
led_set_brightness(gpio_data->led,
gpio_data->desired_brightness);
else
led_set_brightness(gpio_data->led, LED_FULL);
} else {
led_set_brightness(gpio_data->led, LED_OFF);
}
}
static ssize_t gpio_trig_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led = dev_get_drvdata(dev);
struct gpio_trig_data *gpio_data = led->trigger_data;
return sprintf(buf, "%u\n", gpio_data->desired_brightness);
}
static ssize_t gpio_trig_brightness_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t n)
{
struct led_classdev *led = dev_get_drvdata(dev);
struct gpio_trig_data *gpio_data = led->trigger_data;
unsigned desired_brightness;
int ret;
ret = sscanf(buf, "%u", &desired_brightness);
if (ret < 1 || desired_brightness > 255) {
dev_err(dev, "invalid value\n");
return -EINVAL;
}
gpio_data->desired_brightness = desired_brightness;
return n;
}
static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show,
gpio_trig_brightness_store);
static ssize_t gpio_trig_inverted_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led = dev_get_drvdata(dev);
struct gpio_trig_data *gpio_data = led->trigger_data;
return sprintf(buf, "%s\n", gpio_data->inverted ? "yes" : "no");
}
static ssize_t gpio_trig_inverted_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t n)
{
struct led_classdev *led = dev_get_drvdata(dev);
struct gpio_trig_data *gpio_data = led->trigger_data;
unsigned inverted;
int ret;
ret = sscanf(buf, "%u", &inverted);
if (ret < 1) {
dev_err(dev, "invalid value\n");
return -EINVAL;
}
gpio_data->inverted = !!inverted;
/* After inverting, we need to update the LED. */
schedule_work(&gpio_data->work);
return n;
}
static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show,
gpio_trig_inverted_store);
static ssize_t gpio_trig_gpio_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led = dev_get_drvdata(dev);
struct gpio_trig_data *gpio_data = led->trigger_data;
return sprintf(buf, "%u\n", gpio_data->gpio);
}
static ssize_t gpio_trig_gpio_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t n)
{
struct led_classdev *led = dev_get_drvdata(dev);
struct gpio_trig_data *gpio_data = led->trigger_data;
unsigned gpio;
int ret;
ret = sscanf(buf, "%u", &gpio);
if (ret < 1) {
dev_err(dev, "couldn't read gpio number\n");
flush_work(&gpio_data->work);
return -EINVAL;
}
if (gpio_data->gpio == gpio)
return n;
if (!gpio) {
if (gpio_data->gpio != 0)
free_irq(gpio_to_irq(gpio_data->gpio), led);
gpio_data->gpio = 0;
return n;
}
ret = request_irq(gpio_to_irq(gpio), gpio_trig_irq,
IRQF_SHARED | IRQF_TRIGGER_RISING
| IRQF_TRIGGER_FALLING, "ledtrig-gpio", led);
if (ret) {
dev_err(dev, "request_irq failed with error %d\n", ret);
} else {
if (gpio_data->gpio != 0)
free_irq(gpio_to_irq(gpio_data->gpio), led);
gpio_data->gpio = gpio;
}
return ret ? ret : n;
}
static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store);
static void gpio_trig_activate(struct led_classdev *led)
{
struct gpio_trig_data *gpio_data;
int ret;
gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL);
if (!gpio_data)
return;
ret = device_create_file(led->dev, &dev_attr_gpio);
if (ret)
goto err_gpio;
ret = device_create_file(led->dev, &dev_attr_inverted);
if (ret)
goto err_inverted;
ret = device_create_file(led->dev, &dev_attr_desired_brightness);
if (ret)
goto err_brightness;
gpio_data->led = led;
led->trigger_data = gpio_data;
INIT_WORK(&gpio_data->work, gpio_trig_work);
return;
err_brightness:
device_remove_file(led->dev, &dev_attr_inverted);
err_inverted:
device_remove_file(led->dev, &dev_attr_gpio);
err_gpio:
kfree(gpio_data);
}
static void gpio_trig_deactivate(struct led_classdev *led)
{
struct gpio_trig_data *gpio_data = led->trigger_data;
if (gpio_data) {
device_remove_file(led->dev, &dev_attr_gpio);
device_remove_file(led->dev, &dev_attr_inverted);
device_remove_file(led->dev, &dev_attr_desired_brightness);
flush_work(&gpio_data->work);
if (gpio_data->gpio != 0)
free_irq(gpio_to_irq(gpio_data->gpio), led);
kfree(gpio_data);
}
}
static struct led_trigger gpio_led_trigger = {
.name = "gpio",
.activate = gpio_trig_activate,
.deactivate = gpio_trig_deactivate,
};
static int __init gpio_trig_init(void)
{
return led_trigger_register(&gpio_led_trigger);
}
module_init(gpio_trig_init);
static void __exit gpio_trig_exit(void)
{
led_trigger_unregister(&gpio_led_trigger);
}
module_exit(gpio_trig_exit);
MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>");
MODULE_DESCRIPTION("GPIO LED trigger");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,118 @@
/*
* LED Heartbeat Trigger
*
* Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
*
* Based on Richard Purdie's ledtrig-timer.c and some arch's
* CONFIG_HEARTBEAT code.
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/leds.h>
#include "leds.h"
struct heartbeat_trig_data {
unsigned int phase;
unsigned int period;
struct timer_list timer;
};
static void led_heartbeat_function(unsigned long data)
{
struct led_classdev *led_cdev = (struct led_classdev *) data;
struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
unsigned long brightness = LED_OFF;
unsigned long delay = 0;
/* acts like an actual heart beat -- ie thump-thump-pause... */
switch (heartbeat_data->phase) {
case 0:
/*
* The hyperbolic function below modifies the
* heartbeat period length in dependency of the
* current (1min) load. It goes through the points
* f(0)=1260, f(1)=860, f(5)=510, f(inf)->300.
*/
heartbeat_data->period = 300 +
(6720 << FSHIFT) / (5 * avenrun[0] + (7 << FSHIFT));
heartbeat_data->period =
msecs_to_jiffies(heartbeat_data->period);
delay = msecs_to_jiffies(70);
heartbeat_data->phase++;
brightness = led_cdev->max_brightness;
break;
case 1:
delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
heartbeat_data->phase++;
break;
case 2:
delay = msecs_to_jiffies(70);
heartbeat_data->phase++;
brightness = led_cdev->max_brightness;
break;
default:
delay = heartbeat_data->period - heartbeat_data->period / 4 -
msecs_to_jiffies(70);
heartbeat_data->phase = 0;
break;
}
led_set_brightness(led_cdev, brightness);
mod_timer(&heartbeat_data->timer, jiffies + delay);
}
static void heartbeat_trig_activate(struct led_classdev *led_cdev)
{
struct heartbeat_trig_data *heartbeat_data;
heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL);
if (!heartbeat_data)
return;
led_cdev->trigger_data = heartbeat_data;
setup_timer(&heartbeat_data->timer,
led_heartbeat_function, (unsigned long) led_cdev);
heartbeat_data->phase = 0;
led_heartbeat_function(heartbeat_data->timer.data);
}
static void heartbeat_trig_deactivate(struct led_classdev *led_cdev)
{
struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
if (heartbeat_data) {
del_timer_sync(&heartbeat_data->timer);
kfree(heartbeat_data);
}
}
static struct led_trigger heartbeat_led_trigger = {
.name = "heartbeat",
.activate = heartbeat_trig_activate,
.deactivate = heartbeat_trig_deactivate,
};
static int __init heartbeat_trig_init(void)
{
return led_trigger_register(&heartbeat_led_trigger);
}
static void __exit heartbeat_trig_exit(void)
{
led_trigger_unregister(&heartbeat_led_trigger);
}
module_init(heartbeat_trig_init);
module_exit(heartbeat_trig_exit);
MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
MODULE_DESCRIPTION("Heartbeat LED trigger");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,64 @@
/*
* LED IDE-Disk Activity Trigger
*
* Copyright 2006 Openedhand Ltd.
*
* Author: Richard Purdie <rpurdie@openedhand.com>
*
* 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/jiffies.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/leds.h>
static void ledtrig_ide_timerfunc(unsigned long data);
DEFINE_LED_TRIGGER(ledtrig_ide);
static DEFINE_TIMER(ledtrig_ide_timer, ledtrig_ide_timerfunc, 0, 0);
static int ide_activity;
static int ide_lastactivity;
void ledtrig_ide_activity(void)
{
ide_activity++;
if (!timer_pending(&ledtrig_ide_timer))
mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
}
EXPORT_SYMBOL(ledtrig_ide_activity);
static void ledtrig_ide_timerfunc(unsigned long data)
{
if (ide_lastactivity != ide_activity) {
ide_lastactivity = ide_activity;
/* INT_MAX will set each LED to its maximum brightness */
led_trigger_event(ledtrig_ide, INT_MAX);
mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
} else {
led_trigger_event(ledtrig_ide, LED_OFF);
}
}
static int __init ledtrig_ide_init(void)
{
led_trigger_register_simple("ide-disk", &ledtrig_ide);
return 0;
}
static void __exit ledtrig_ide_exit(void)
{
led_trigger_unregister_simple(ledtrig_ide);
}
module_init(ledtrig_ide_init);
module_exit(ledtrig_ide_exit);
MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
MODULE_DESCRIPTION("LED IDE Disk Activity Trigger");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,237 @@
/*
* LED Kernel Timer Trigger
*
* Copyright 2005-2006 Openedhand Ltd.
*
* Author: Richard Purdie <rpurdie@openedhand.com>
*
* 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/jiffies.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/sysdev.h>
#include <linux/timer.h>
#include <linux/ctype.h>
#include <linux/leds.h>
#include "leds.h"
struct timer_trig_data {
int brightness_on; /* LED brightness during "on" period.
* (LED_OFF < brightness_on <= LED_FULL)
*/
unsigned long delay_on; /* milliseconds on */
unsigned long delay_off; /* milliseconds off */
struct timer_list timer;
};
static void led_timer_function(unsigned long data)
{
struct led_classdev *led_cdev = (struct led_classdev *) data;
struct timer_trig_data *timer_data = led_cdev->trigger_data;
unsigned long brightness;
unsigned long delay;
if (!timer_data->delay_on || !timer_data->delay_off) {
led_set_brightness(led_cdev, LED_OFF);
return;
}
brightness = led_get_brightness(led_cdev);
if (!brightness) {
/* Time to switch the LED on. */
brightness = timer_data->brightness_on;
delay = timer_data->delay_on;
} else {
/* Store the current brightness value to be able
* to restore it when the delay_off period is over.
*/
timer_data->brightness_on = brightness;
brightness = LED_OFF;
delay = timer_data->delay_off;
}
led_set_brightness(led_cdev, brightness);
mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay));
}
static ssize_t led_delay_on_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct timer_trig_data *timer_data = led_cdev->trigger_data;
return sprintf(buf, "%lu\n", timer_data->delay_on);
}
static ssize_t led_delay_on_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct timer_trig_data *timer_data = led_cdev->trigger_data;
int ret = -EINVAL;
char *after;
unsigned long state = simple_strtoul(buf, &after, 10);
size_t count = after - buf;
if (*after && isspace(*after))
count++;
if (count == size) {
if (timer_data->delay_on != state) {
/* the new value differs from the previous */
timer_data->delay_on = state;
/* deactivate previous settings */
del_timer_sync(&timer_data->timer);
/* try to activate hardware acceleration, if any */
if (!led_cdev->blink_set ||
led_cdev->blink_set(led_cdev,
&timer_data->delay_on, &timer_data->delay_off)) {
/* no hardware acceleration, blink via timer */
mod_timer(&timer_data->timer, jiffies + 1);
}
}
ret = count;
}
return ret;
}
static ssize_t led_delay_off_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct timer_trig_data *timer_data = led_cdev->trigger_data;
return sprintf(buf, "%lu\n", timer_data->delay_off);
}
static ssize_t led_delay_off_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct timer_trig_data *timer_data = led_cdev->trigger_data;
int ret = -EINVAL;
char *after;
unsigned long state = simple_strtoul(buf, &after, 10);
size_t count = after - buf;
if (*after && isspace(*after))
count++;
if (count == size) {
if (timer_data->delay_off != state) {
/* the new value differs from the previous */
timer_data->delay_off = state;
/* deactivate previous settings */
del_timer_sync(&timer_data->timer);
/* try to activate hardware acceleration, if any */
if (!led_cdev->blink_set ||
led_cdev->blink_set(led_cdev,
&timer_data->delay_on, &timer_data->delay_off)) {
/* no hardware acceleration, blink via timer */
mod_timer(&timer_data->timer, jiffies + 1);
}
}
ret = count;
}
return ret;
}
static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
static void timer_trig_activate(struct led_classdev *led_cdev)
{
struct timer_trig_data *timer_data;
int rc;
timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL);
if (!timer_data)
return;
timer_data->brightness_on = led_get_brightness(led_cdev);
if (timer_data->brightness_on == LED_OFF)
timer_data->brightness_on = led_cdev->max_brightness;
led_cdev->trigger_data = timer_data;
init_timer(&timer_data->timer);
timer_data->timer.function = led_timer_function;
timer_data->timer.data = (unsigned long) led_cdev;
rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
if (rc)
goto err_out;
rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
if (rc)
goto err_out_delayon;
/* If there is hardware support for blinking, start one
* user friendly blink rate chosen by the driver.
*/
if (led_cdev->blink_set)
led_cdev->blink_set(led_cdev,
&timer_data->delay_on, &timer_data->delay_off);
return;
err_out_delayon:
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
err_out:
led_cdev->trigger_data = NULL;
kfree(timer_data);
}
static void timer_trig_deactivate(struct led_classdev *led_cdev)
{
struct timer_trig_data *timer_data = led_cdev->trigger_data;
unsigned long on = 0, off = 0;
if (timer_data) {
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
device_remove_file(led_cdev->dev, &dev_attr_delay_off);
del_timer_sync(&timer_data->timer);
kfree(timer_data);
}
/* If there is hardware support for blinking, stop it */
if (led_cdev->blink_set)
led_cdev->blink_set(led_cdev, &on, &off);
}
static struct led_trigger timer_led_trigger = {
.name = "timer",
.activate = timer_trig_activate,
.deactivate = timer_trig_deactivate,
};
static int __init timer_trig_init(void)
{
return led_trigger_register(&timer_led_trigger);
}
static void __exit timer_trig_exit(void)
{
led_trigger_unregister(&timer_led_trigger);
}
module_init(timer_trig_init);
module_exit(timer_trig_exit);
MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
MODULE_DESCRIPTION("Timer LED trigger");
MODULE_LICENSE("GPL");