From: Felipe Balbi <[EMAIL PROTECTED]>
Signed-off-by: Felipe Balbi <[EMAIL PROTECTED]>
---
drivers/leds/Kconfig | 21 +++
drivers/leds/Makefile | 2 +
drivers/leds/leds-omap-pwm.c | 376 ++++++++++++++++++++++++++++++++++++++++++
drivers/leds/leds-omap.c | 135 +++++++++++++++
4 files changed, 534 insertions(+), 0 deletions(-)
create mode 100644 drivers/leds/leds-omap-pwm.c
create mode 100644 drivers/leds/leds-omap.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 9556262..6a2f441 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -77,6 +77,27 @@ config LEDS_WRAP
help
This option enables support for the PCEngines WRAP programmable LEDs.
+config LEDS_OMAP_DEBUG
+ boolean "LED Support for OMAP debug board LEDs"
+ depends on LEDS_CLASS=y && ARCH_OMAP
+ help
+ Enables support for the LEDs on the debug board used with OMAP
+ reference boards like H2/H3/H4 and Perseus2. Up to six of these
+ may be claimed by the original ARM debug LED API.
+
+config LEDS_OMAP
+ tristate "LED Support for OMAP GPIO LEDs"
+ depends on LEDS_CLASS && ARCH_OMAP
+ help
+ This option enables support for the LEDs on OMAP processors.
+
+config LEDS_OMAP_PWM
+ tristate "LED Support for OMAP PWM-controlled LEDs"
+ depends on LEDS_CLASS && ARCH_OMAP && OMAP_DM_TIMER
+ help
+ This options enables support for LEDs connected to GPIO lines
+ controlled by a PWM timer on OMAP CPUs.
+
config LEDS_H1940
tristate "LED Support for iPAQ H1940 device"
depends on LEDS_CLASS && ARCH_H1940
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index ff7982b..fd6316e 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -13,6 +13,8 @@ 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_OMAP) += leds-omap.o
+obj-$(CONFIG_LEDS_OMAP_PWM) += leds-omap-pwm.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
diff --git a/drivers/leds/leds-omap-pwm.c b/drivers/leds/leds-omap-pwm.c
new file mode 100644
index 0000000..57eb383
--- /dev/null
+++ b/drivers/leds/leds-omap-pwm.c
@@ -0,0 +1,376 @@
+/* drivers/leds/leds-omap_pwm.c
+ *
+ * Driver to blink LEDs using OMAP PWM timers
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Timo Teras
+ *
+ * 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/err.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/ctype.h>
+#include <linux/sched.h>
+#include <asm/delay.h>
+#include <mach/board.h>
+#include <mach/dmtimer.h>
+
+struct omap_pwm_led {
+ struct led_classdev cdev;
+ struct work_struct work;
+ struct omap_pwm_led_platform_data *pdata;
+ struct omap_dm_timer *intensity_timer;
+ struct omap_dm_timer *blink_timer;
+ int powered;
+ unsigned int on_period, off_period;
+ enum led_brightness brightness;
+};
+
+static inline struct omap_pwm_led *pdev_to_omap_pwm_led(struct platform_device
*pdev)
+{
+ return platform_get_drvdata(pdev);
+}
+
+static inline struct omap_pwm_led *cdev_to_omap_pwm_led(struct led_classdev
*led_cdev)
+{
+ return container_of(led_cdev, struct omap_pwm_led, cdev);
+}
+
+static inline struct omap_pwm_led *work_to_omap_pwm_led(struct work_struct
*work)
+{
+ return container_of(work, struct omap_pwm_led, work);
+}
+
+static void omap_pwm_led_set_blink(struct omap_pwm_led *led)
+{
+ if (!led->powered)
+ return;
+
+ if (led->on_period != 0 && led->off_period != 0) {
+ unsigned long load_reg, cmp_reg;
+
+ load_reg = 32768 * (led->on_period + led->off_period) / 1000;
+ cmp_reg = 32768 * led->on_period / 1000;
+
+ omap_dm_timer_stop(led->blink_timer);
+ omap_dm_timer_set_load(led->blink_timer, 1, -load_reg);
+ omap_dm_timer_set_match(led->blink_timer, 1, -cmp_reg);
+ omap_dm_timer_set_pwm(led->blink_timer, 1, 1,
+ OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+ omap_dm_timer_write_counter(led->blink_timer, -2);
+ omap_dm_timer_start(led->blink_timer);
+ } else {
+ omap_dm_timer_set_pwm(led->blink_timer, 1, 1,
+ OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+ omap_dm_timer_stop(led->blink_timer);
+ }
+}
+
+static void omap_pwm_led_power_on(struct omap_pwm_led *led)
+{
+ if (led->powered)
+ return;
+ led->powered = 1;
+
+ /* Select clock */
+ omap_dm_timer_enable(led->intensity_timer);
+ omap_dm_timer_set_source(led->intensity_timer, OMAP_TIMER_SRC_32_KHZ);
+
+ /* Turn voltage on */
+ if (led->pdata->set_power != NULL)
+ led->pdata->set_power(led->pdata, 1);
+
+ /* Enable PWM timers */
+ if (led->blink_timer != NULL) {
+ omap_dm_timer_enable(led->blink_timer);
+ omap_dm_timer_set_source(led->blink_timer,
+ OMAP_TIMER_SRC_32_KHZ);
+ omap_pwm_led_set_blink(led);
+ }
+
+ omap_dm_timer_set_load(led->intensity_timer, 1, 0xffffff00);
+}
+
+static void omap_pwm_led_power_off(struct omap_pwm_led *led)
+{
+ if (!led->powered)
+ return;
+ led->powered = 0;
+
+ /* Everything off */
+ omap_dm_timer_stop(led->intensity_timer);
+ omap_dm_timer_disable(led->intensity_timer);
+
+ if (led->blink_timer != NULL) {
+ omap_dm_timer_stop(led->blink_timer);
+ omap_dm_timer_disable(led->blink_timer);
+ }
+
+ if (led->pdata->set_power != NULL)
+ led->pdata->set_power(led->pdata, 0);
+}
+
+static void omap_pwm_led_set_pwm_cycle(struct omap_pwm_led *led, int cycle)
+{
+ int n;
+
+ if (cycle == 0)
+ n = 0xff;
+ else n = cycle - 1;
+
+ if (cycle == LED_FULL) {
+ omap_dm_timer_set_pwm(led->intensity_timer, 1, 1,
+ OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+ omap_dm_timer_stop(led->intensity_timer);
+ } else {
+ omap_dm_timer_set_pwm(led->intensity_timer, 0, 1,
+ OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+ omap_dm_timer_set_match(led->intensity_timer, 1,
+ (0xffffff00) | cycle);
+ omap_dm_timer_start(led->intensity_timer);
+ }
+}
+
+static void omap_pwm_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+
+ led->brightness = value;
+ schedule_work(&led->work);
+}
+
+static void omap_pwm_led_work(struct work_struct *work)
+{
+ struct omap_pwm_led *led = work_to_omap_pwm_led(work);
+
+ if (led->brightness != LED_OFF) {
+ omap_pwm_led_power_on(led);
+ omap_pwm_led_set_pwm_cycle(led, led->brightness);
+ } else {
+ omap_pwm_led_power_off(led);
+ }
+}
+
+static ssize_t omap_pwm_led_on_period_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+
+ return sprintf(buf, "%u\n", led->on_period) + 1;
+}
+
+static ssize_t omap_pwm_led_on_period_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+ int ret = -EINVAL;
+ unsigned long val;
+ char *after;
+ size_t count;
+
+ val = simple_strtoul(buf, &after, 10);
+ count = after - buf;
+ if (*after && isspace(*after))
+ count++;
+
+ if (count == size) {
+ led->on_period = val;
+ omap_pwm_led_set_blink(led);
+ ret = count;
+ }
+
+ return ret;
+}
+
+static ssize_t omap_pwm_led_off_period_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+
+ return sprintf(buf, "%u\n", led->off_period) + 1;
+}
+
+static ssize_t omap_pwm_led_off_period_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+ int ret = -EINVAL;
+ unsigned long val;
+ char *after;
+ size_t count;
+
+ val = simple_strtoul(buf, &after, 10);
+ count = after - buf;
+ if (*after && isspace(*after))
+ count++;
+
+ if (count == size) {
+ led->off_period = val;
+ omap_pwm_led_set_blink(led);
+ ret = count;
+ }
+
+ return ret;
+}
+
+static DEVICE_ATTR(on_period, 0644, omap_pwm_led_on_period_show,
+ omap_pwm_led_on_period_store);
+static DEVICE_ATTR(off_period, 0644, omap_pwm_led_off_period_show,
+ omap_pwm_led_off_period_store);
+
+static int omap_pwm_led_probe(struct platform_device *pdev)
+{
+ struct omap_pwm_led_platform_data *pdata = pdev->dev.platform_data;
+ struct omap_pwm_led *led;
+ int ret;
+
+ led = kzalloc(sizeof(struct omap_pwm_led), GFP_KERNEL);
+ if (led == NULL) {
+ dev_err(&pdev->dev, "No memory for device\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, led);
+ led->cdev.brightness_set = omap_pwm_led_set;
+ led->cdev.default_trigger = NULL;
+ led->cdev.name = pdata->name;
+ led->pdata = pdata;
+ led->brightness = LED_OFF;
+ INIT_WORK(&led->work, omap_pwm_led_work);
+
+ dev_info(&pdev->dev, "OMAP PWM LED (%s) at GP timer %d/%d\n",
+ pdata->name, pdata->intensity_timer, pdata->blink_timer);
+
+ /* register our new led device */
+ ret = led_classdev_register(&pdev->dev, &led->cdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "led_classdev_register failed\n");
+ goto error_classdev;
+ }
+
+ /* get related dm timers */
+ led->intensity_timer =
omap_dm_timer_request_specific(pdata->intensity_timer);
+ if (led->intensity_timer == NULL) {
+ dev_err(&pdev->dev, "failed to request intensity pwm timer\n");
+ ret = -ENODEV;
+ goto error_intensity;
+ }
+ omap_dm_timer_disable(led->intensity_timer);
+
+ if (pdata->blink_timer != 0) {
+ led->blink_timer =
omap_dm_timer_request_specific(pdata->blink_timer);
+ if (led->blink_timer == NULL) {
+ dev_err(&pdev->dev, "failed to request blinking pwm
timer\n");
+ ret = -ENODEV;
+ goto error_blink1;
+ }
+ omap_dm_timer_disable(led->blink_timer);
+
+ ret = device_create_file(led->cdev.dev,
+ &dev_attr_on_period);
+ if(ret)
+ goto error_blink2;
+
+ ret = device_create_file(led->cdev.dev,
+ &dev_attr_off_period);
+ if(ret)
+ goto error_blink3;
+
+ }
+
+ return 0;
+
+error_blink3:
+ device_remove_file(led->cdev.dev,
+ &dev_attr_on_period);
+error_blink2:
+ dev_err(&pdev->dev, "failed to create device file(s)\n");
+error_blink1:
+ omap_dm_timer_free(led->intensity_timer);
+error_intensity:
+ led_classdev_unregister(&led->cdev);
+error_classdev:
+ kfree(led);
+ return ret;
+}
+
+static int omap_pwm_led_remove(struct platform_device *pdev)
+{
+ struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
+
+ device_remove_file(led->cdev.dev,
+ &dev_attr_on_period);
+ device_remove_file(led->cdev.dev,
+ &dev_attr_off_period);
+ led_classdev_unregister(&led->cdev);
+
+ omap_pwm_led_set(&led->cdev, LED_OFF);
+ if (led->blink_timer != NULL)
+ omap_dm_timer_free(led->blink_timer);
+ omap_dm_timer_free(led->intensity_timer);
+ kfree(led);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int omap_pwm_led_suspend(struct platform_device *pdev, pm_message_t
state)
+{
+ struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
+
+ led_classdev_suspend(&led->cdev);
+ return 0;
+}
+
+static int omap_pwm_led_resume(struct platform_device *pdev)
+{
+ struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
+
+ led_classdev_resume(&led->cdev);
+ return 0;
+}
+#else
+#define omap_pwm_led_suspend NULL
+#define omap_pwm_led_resume NULL
+#endif
+
+static struct platform_driver omap_pwm_led_driver = {
+ .probe = omap_pwm_led_probe,
+ .remove = omap_pwm_led_remove,
+ .suspend = omap_pwm_led_suspend,
+ .resume = omap_pwm_led_resume,
+ .driver = {
+ .name = "omap_pwm_led",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap_pwm_led_init(void)
+{
+ return platform_driver_register(&omap_pwm_led_driver);
+}
+
+static void __exit omap_pwm_led_exit(void)
+{
+ platform_driver_unregister(&omap_pwm_led_driver);
+}
+
+module_init(omap_pwm_led_init);
+module_exit(omap_pwm_led_exit);
+
+MODULE_AUTHOR("Timo Teras");
+MODULE_DESCRIPTION("OMAP PWM LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-omap.c b/drivers/leds/leds-omap.c
new file mode 100644
index 0000000..5c14c4b
--- /dev/null
+++ b/drivers/leds/leds-omap.c
@@ -0,0 +1,135 @@
+/* drivers/leds/leds-omap.c
+ *
+ * (C) 2006 Samsung Electronics
+ * Kyungmin Park <[EMAIL PROTECTED]>
+ *
+ * OMAP - 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 <mach/gpio.h>
+#include <mach/hardware.h>
+#include <mach/led.h>
+
+/* our context */
+
+static void omap_set_led_gpio(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct omap_led_config *led_dev;
+
+ led_dev = container_of(led_cdev, struct omap_led_config, cdev);
+
+ if (value)
+ omap_set_gpio_dataout(led_dev->gpio, 1);
+ else
+ omap_set_gpio_dataout(led_dev->gpio, 0);
+}
+
+static void omap_configure_led_gpio(int gpio)
+{
+ if (omap_request_gpio(gpio) < 0) {
+ printk(KERN_ERR "Failed to request GPIO%d for LEDs\n", gpio);
+ return;
+ }
+ omap_set_gpio_direction(gpio, 0); /* OUT */
+}
+
+static int omap_led_probe(struct platform_device *dev)
+{
+ struct omap_led_platform_data *pdata = dev->dev.platform_data;
+ struct omap_led_config *leds = pdata->leds;
+ int i, ret = 0;
+
+ for (i = 0; ret >= 0 && i < pdata->nr_leds; i++) {
+ omap_configure_led_gpio(leds[i].gpio);
+ if (!leds[i].cdev.brightness_set)
+ leds[i].cdev.brightness_set = omap_set_led_gpio;
+
+ ret = led_classdev_register(&dev->dev, &leds[i].cdev);
+ }
+
+ if (ret < 0 && i > 1) {
+ for (i = i - 2; i >= 0; i--)
+ led_classdev_unregister(&leds[i].cdev);
+ }
+
+ return ret;
+}
+
+static int omap_led_remove(struct platform_device *dev)
+{
+ struct omap_led_platform_data *pdata = dev->dev.platform_data;
+ struct omap_led_config *leds = pdata->leds;
+ int i;
+
+ for (i = 0; i < pdata->nr_leds; i++)
+ led_classdev_unregister(&leds[i].cdev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int omap_led_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct omap_led_platform_data *pdata = dev->dev.platform_data;
+ struct omap_led_config *leds = pdata->leds;
+ int i;
+
+ for (i = 0; i < pdata->nr_leds; i++)
+ led_classdev_suspend(&leds[i].cdev);
+
+ return 0;
+}
+
+static int omap_led_resume(struct platform_device *dev)
+{
+ struct omap_led_platform_data *pdata = dev->dev.platform_data;
+ struct omap_led_config *leds = pdata->leds;
+ int i;
+
+ for (i = 0; i < pdata->nr_leds; i++)
+ led_classdev_resume(&leds[i].cdev);
+
+ return 0;
+}
+#else
+#define omap_led_suspend NULL
+#define omap_led_resume NULL
+#endif
+
+static struct platform_driver omap_led_driver = {
+ .probe = omap_led_probe,
+ .remove = omap_led_remove,
+ .suspend = omap_led_suspend,
+ .resume = omap_led_resume,
+ .driver = {
+ .name = "omap-led",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap_led_init(void)
+{
+ return platform_driver_register(&omap_led_driver);
+}
+
+static void __exit omap_led_exit(void)
+{
+ platform_driver_unregister(&omap_led_driver);
+}
+
+module_init(omap_led_init);
+module_exit(omap_led_exit);
+
+MODULE_AUTHOR("Kyungmin Park<[EMAIL PROTECTED]>");
+MODULE_DESCRIPTION("OMAP LED driver");
+MODULE_LICENSE("GPL");
--
1.6.0.1.141.g445ca
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html