diff options
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/Makefile | 4 | ||||
-rw-r--r-- | drivers/leds/led-class-3g.c | 281 | ||||
-rw-r--r-- | drivers/leds/led-class.c | 24 | ||||
-rw-r--r-- | drivers/leds/leds-apollo3g.c | 365 | ||||
-rw-r--r-- | drivers/leds/leds-gpio.c | 4 | ||||
-rw-r--r-- | drivers/leds/leds.h | 19 |
6 files changed, 685 insertions, 12 deletions
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 46d72704d60..6b2691c49d2 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -30,6 +30,10 @@ obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o obj-$(CONFIG_LEDS_PWM) += leds-pwm.o + +obj-y += led-class-3g.o +obj-y += leds-apollo3g.o + # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/led-class-3g.c b/drivers/leds/led-class-3g.c new file mode 100644 index 00000000000..c0c1c11cbdb --- /dev/null +++ b/drivers/leds/led-class-3g.c @@ -0,0 +1,281 @@ +/* + * 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_color(struct led_classdev *led_cdev) +{ + if (led_cdev->color_get) + led_cdev->color = led_cdev->color_get(led_cdev); +} + +static ssize_t led_color_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + unsigned long state = 9; + + switch (buf[0]) { + case 'r': /* red */ + state = 1; + break; + case 'g': /* green */ + state = 2; + break; + case 'b': /* blue */ + state = 3; + break; + case 'y': /* yellow */ + state = 4; + break; + case 'w': /* white */ + state = 5; + break; + case 'o': /* off */ + state = 0; + break; + default: + break; + } + + led_set_color(led_cdev, state); + + return (ssize_t)size; +} + +static ssize_t led_color_show(struct device *dev, + struct device_attribute *attr, char *buf) { + + struct led_classdev *led_cdev = dev_get_drvdata(dev); + char * readbuf[] = {"off", "red", "green", "blue", "yellow", "white"} ; + /* no lock needed for this */ + led_update_color(led_cdev); + + return sprintf(buf, "%s\n", readbuf[led_cdev->color]); +} + +static ssize_t led_blink_show(struct device *dev, struct device_attribute *attr, + char *buf) { + char *blinkStr = "no"; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + if( led_cdev->blink == 0 ){ + blinkStr = "no"; + } + else if (led_cdev->blink == 1 ){ + blinkStr = "yes"; + } + return sprintf(buf, "%s\n", blinkStr); +} + +static ssize_t led_blink_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) { + int val = 0; + struct led_classdev * led_cdev = dev_get_drvdata(dev); + + if( buf[0] == 'y' ) { + val = 1; + } + else if( buf[0] == 'n' ) { + val = 0; + } + else if( buf[0] == 'f' ) { + val = 2; + } + led_set_blink( led_cdev, val ); + + return (ssize_t)size; +} + +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(color, 0644, led_color_show, led_color_store); +static DEVICE_ATTR(blink, 0644, led_blink_show, led_blink_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->color_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->color_set(led_cdev, led_cdev->color); + 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_color); + if (rc) + goto err_out; + + rc = device_create_file(led_cdev->dev, &dev_attr_blink); + 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_color(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_color); + 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_color); +#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"); diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index f2cc13d7681..b1e6be1ea63 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -30,17 +30,6 @@ static void led_update_brightness(struct led_classdev *led_cdev) 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) { @@ -52,18 +41,29 @@ static ssize_t led_brightness_store(struct device *dev, 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_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_max_brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/leds/leds-apollo3g.c b/drivers/leds/leds-apollo3g.c new file mode 100644 index 00000000000..6e796dc7c24 --- /dev/null +++ b/drivers/leds/leds-apollo3g.c @@ -0,0 +1,365 @@ +/* + * LED Platform driver for Apollo3G board. + * + * © 2010 Western Digital Technologies, Inc. All rights reserved. + * + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/completion.h> +#include <linux/leds.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/ioport.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include <linux/suspend.h> +#include <linux/kthread.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/signal.h> +#include <linux/freezer.h> + + + +#define _3G_BIT_LED_RED 0x10 +#define _3G_BIT_LED_GREEN 0x20 +#define _3G_BIT_LED_BLUE 0x40 +#define _3G_BIT_LED_YELLOW (_3G_BIT_LED_GREEN | _3G_BIT_LED_RED) +#define _3G_BIT_LED_ALL 0x70 +#define _3G_BIT_LED_OFF 0x00 + +#define _3G_LED_OFF 0 +#define _3G_LED_RED 1 +#define _3G_LED_GREEN 2 +#define _3G_LED_BLUE 3 +#define _3G_LED_YELLOW 4 +#define _3G_LED_ALL 5 +#define _3G_LED_WHITE 5 /* save as _ALL */ + +#define _BLINK_YES 1 +#define _BLINK_NO 0 + +#define HDD_BLINK_RATE 250 + +static DEFINE_SPINLOCK(led_lock); +struct task_struct * threadptr; + +wait_queue_head_t ts_wait; +int blink_flag = 0; + + +void __iomem *led_port = NULL; + +typedef struct led_state_s { + int cur_color; + int cur_action; +} led_state_t; + +led_state_t led_state = { .cur_color = _3G_LED_YELLOW, + .cur_action = _BLINK_NO + }; + +EXPORT_SYMBOL(led_port); + +/****************************************************/ +/* read value from 3g led_port */ + u8 read_3gled( void ) { + return readb(led_port); +} + +/****************************************************/ +/* read-modify-write 3g led port */ +u8 write_3gled( u8 mask ,u8 value ) { + u8 regval; + + regval = read_3gled(); + regval &= ~mask; + regval |= (value & mask); + writeb( regval, led_port); + return regval; +} + +/****************************************************/ +/* return 3g led color */ +static enum led_brightness a3g_led_get( struct led_classdev * led_cdev ) { + unsigned char readval; + + readval = readb(led_port); + if( (readval & _3G_BIT_LED_ALL) == _3G_BIT_LED_RED ) { + readval = _3G_LED_RED; + } + else if( (readval & _3G_BIT_LED_ALL) == _3G_BIT_LED_GREEN ) { + readval = _3G_LED_GREEN; + } + else if( (readval & _3G_BIT_LED_ALL) == _3G_BIT_LED_BLUE ) { + readval = _3G_LED_BLUE; + } + else if( (readval & _3G_BIT_LED_ALL) == _3G_BIT_LED_YELLOW ) { + readval = _3G_LED_YELLOW; + } + else if( (readval & _3G_BIT_LED_ALL) == _3G_BIT_LED_ALL ) { + readval = _3G_LED_ALL; + } + else if( (readval & _3G_BIT_LED_ALL) == _3G_BIT_LED_OFF ) { + readval = _3G_LED_OFF; + } + + return readval; +} + +/****************************************************/ +/* set 3g led color */ +static void a3g_led_set(struct led_classdev *led_cdev, enum led_brightness value) { + + unsigned long flags; + + spin_lock_irqsave(&led_lock, flags); + + switch (value) { + case _3G_LED_RED: + write_3gled( _3G_BIT_LED_ALL, _3G_BIT_LED_RED); + break; + case _3G_LED_GREEN: + write_3gled( _3G_BIT_LED_ALL, _3G_BIT_LED_GREEN); + break; + case _3G_LED_BLUE: + write_3gled( _3G_BIT_LED_ALL, _3G_BIT_LED_BLUE); + break; + case _3G_LED_OFF: + write_3gled( _3G_BIT_LED_ALL, _3G_BIT_LED_OFF); + break; + case _3G_LED_ALL: + write_3gled( _3G_BIT_LED_ALL, _3G_BIT_LED_ALL); + break; + case _3G_LED_YELLOW: + write_3gled(_3G_BIT_LED_ALL, _3G_BIT_LED_YELLOW); + break; + default: + break; /* should never be here */ + } + led_state.cur_color = value; + + spin_unlock_irqrestore(&led_lock, flags); + +} + +/****************************************************/ +/* set 3g led blinking */ +static int a3g_led_blink(struct led_classdev *led_cdev, int value) { + + /* + * if forced blink, don't set blink_flag + */ + if( blink_flag == 2 ) { + return 0; + } + + /*spin_lock_irqsave(&led_lock, flags);*/ + /* user wants to blink led */ + if( value == 1 ) { + wake_up(&ts_wait); + blink_flag = 1; + + } + else if( value == 0) { + blink_flag = 0; + } + else if( value == 2 ) { + wake_up(&ts_wait); + blink_flag = 2; + } + /* spin_unlock_irqrestore(&led_lock, flags);*/ + + return 0; +} + +/****************************************************/ +/* + * flag = blink or not + * color = blinking color + */ +void signal_hdd_led(int flag, int color) { + + /* + * if forced blinking was set, keep it blinking forever + */ + if( blink_flag == 2 ) { + return; + } + + if( flag && /* blink or not */ + (led_state.cur_color == _3G_LED_GREEN) +#if 0 + (led_state.cur_color != _3G_LED_WHITE) && /* don't touch fw update led */ + (led_state.cur_color != _3G_LED_RED) && /* don't touch system error led */ + !((led_state.cur_color == _3G_LED_BLUE) && (led_state.cur_action == _BLINK_YES)) && /* leave identity alone */ + (color != _3G_LED_RED) +#endif + ) { + if( color == _3G_LED_RED ) { + a3g_led_set( NULL, _3G_LED_RED); + } + blink_flag = 1; + wake_up(&ts_wait); + } + else { + blink_flag = 0; + } +} + +static struct led_classdev a3g_led_dev = { + .name = "a3g_led", + .color_set = a3g_led_set, + .color_get = a3g_led_get, + .blink_set_3g = a3g_led_blink, +}; + +/****************************************************/ +static int __init a3g_led_probe(struct platform_device *pdev ) { + + /* Not used */ + return 0; +} + +/****************************************************/ +static int __devexit a3g_led_remove(struct platform_device *pdev){ + + led_classdev_unregister(&a3g_led_dev); + if( led_port ){ + iounmap(led_port); + led_port = NULL; + } + return 0; +} +static struct platform_driver a3g_led_driver = { + .probe = a3g_led_probe, + .remove = __devexit_p(a3g_led_remove), + .driver = { + .name = "a3g-leds", + .owner = THIS_MODULE, + }, +}; + +#if 0 +struct platform_device { + const char * name; + int id; + struct device dev; + u32 num_resources; + struct resource * resource; + + struct platform_device_id *id_entry; + + /* arch specific additions */ + struct pdev_archdata archdata; +}; +#endif + +static struct resource a3g_res = { + .name = "cs1", + .start = 0x4e0000000ULL, + .end = 0x4e0000300ULL, + /*.flags = IORESOURCE_IO,*/ + .flags = IORESOURCE_MEM, + }; + + +/****************************************************/ +static int a3g_led_blink_thread( void * data ) { + unsigned char readval, color; + + struct task_struct * tsk = current; + struct sched_param param = { .sched_priority = 1}; + + init_waitqueue_head(&ts_wait); + + sched_setscheduler(tsk, SCHED_FIFO, ¶m); + set_freezable(); + + while( !kthread_should_stop() ) { + + led_state.cur_action = _BLINK_NO; + /* always set current color before blinking */ + a3g_led_set( NULL, led_state.cur_color); + wait_event_freezable_timeout(ts_wait, blink_flag || kthread_should_stop(), MAX_SCHEDULE_TIMEOUT); + if( led_port ) { + readval = readb(led_port); + color = readval & _3G_BIT_LED_ALL; + write_3gled( _3G_BIT_LED_ALL, _3G_BIT_LED_OFF); + msleep(HDD_BLINK_RATE); + write_3gled( _3G_BIT_LED_ALL, color); + msleep(HDD_BLINK_RATE); + led_state.cur_action = _BLINK_YES; + } + } + + return 0; +} + + +/****************************************************/ +static int __init a3g_led_init(void) { + + resource_size_t res_size; + struct resource *phys_res = &a3g_res; + int retval; + + res_size = resource_size(phys_res); + + if( !request_mem_region(phys_res->start, res_size, phys_res->name) ) { + printk(KERN_DEBUG "**** error request_mem_region()\n"); + return -1; + } + + led_port = ioremap(phys_res->start, res_size); + if (led_port == NULL) { + release_mem_region(phys_res->start, res_size); + printk(KERN_DEBUG "*** Error ioremap()"); + return -1; + } + else { + retval = led_classdev_register(NULL, &a3g_led_dev); + if (retval) { + led_classdev_unregister(&a3g_led_dev); + iounmap(led_port); + led_port = NULL; + release_mem_region(phys_res->start, res_size); + return -1; + } + + threadptr = kthread_run( a3g_led_blink_thread, NULL, "a3gblink_t"); + + + return platform_driver_register(&a3g_led_driver); + } +} + +/****************************************************/ +static void __exit a3g_led_exit(void) { + + platform_driver_unregister(&a3g_led_driver); + if( led_port ){ + led_classdev_unregister(&a3g_led_dev); + iounmap(led_port); + led_port = NULL; + if( threadptr ){ + kthread_stop(threadptr); + } + release_mem_region(a3g_res.start, (a3g_res.end - a3g_res.start + 1)); + } +} + + +module_init(a3g_led_init); +module_exit(a3g_led_exit); + +MODULE_AUTHOR("Hai Le <hai.le@wdc.com>"); +MODULE_DESCRIPTION("Apollo3G LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 0823e2622e8..33927c2a0d6 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -18,6 +18,9 @@ #include <asm/gpio.h> + +#define CONFIG_LEDS_GPIO_PLATFORM + struct gpio_led_data { struct led_classdev cdev; unsigned gpio; @@ -141,6 +144,7 @@ static int __devinit gpio_led_probe(struct platform_device *pdev) struct gpio_led_data *leds_data; int i, ret = 0; +printk(KERN_INFO ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 1\n"); if (!pdata) return -EBUSY; diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index 2dd8ecbfdc3..f4b75500b60 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h @@ -17,6 +17,25 @@ #include <linux/rwsem.h> #include <linux/leds.h> +static inline void led_set_color(struct led_classdev *led_cdev, + enum led_brightness value) +{ + led_cdev->color = value; + if (!(led_cdev->flags & LED_SUSPENDED)) + led_cdev->color_set(led_cdev, value); +} + +static inline int led_get_color(struct led_classdev *led_cdev) +{ + return led_cdev->color; +} +static inline void led_set_blink(struct led_classdev *led_cdev, + enum led_brightness value) +{ + led_cdev->blink = value; + led_cdev->blink_set_3g(led_cdev, value); +} + static inline void led_set_brightness(struct led_classdev *led_cdev, enum led_brightness value) { |