diff options
Diffstat (limited to 'target/linux/ramips/patches-3.8/0206-owrt-MIPS-ralink-add-pseudo-pwm-led-trigger-based-on.patch')
-rw-r--r-- | target/linux/ramips/patches-3.8/0206-owrt-MIPS-ralink-add-pseudo-pwm-led-trigger-based-on.patch | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/target/linux/ramips/patches-3.8/0206-owrt-MIPS-ralink-add-pseudo-pwm-led-trigger-based-on.patch b/target/linux/ramips/patches-3.8/0206-owrt-MIPS-ralink-add-pseudo-pwm-led-trigger-based-on.patch new file mode 100644 index 000000000..cbf934583 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0206-owrt-MIPS-ralink-add-pseudo-pwm-led-trigger-based-on.patch @@ -0,0 +1,306 @@ +From b35a0a294d39316c20f85004335c02f33a70ab68 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Sun, 24 Mar 2013 17:17:17 +0100 +Subject: [PATCH 206/208] owrt: MIPS: ralink: add pseudo pwm led trigger based + on timer0 + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + arch/mips/ralink/timer.c | 213 ++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 197 insertions(+), 16 deletions(-) + +diff --git a/arch/mips/ralink/timer.c b/arch/mips/ralink/timer.c +index 9a943e8..33b2a04 100644 +--- a/arch/mips/ralink/timer.c ++++ b/arch/mips/ralink/timer.c +@@ -12,6 +12,8 @@ + #include <linux/timer.h> + #include <linux/of_gpio.h> + #include <linux/clk.h> ++#include <linux/leds.h> ++#include <linux/slab.h> + + #include <asm/mach-ralink/ralink_regs.h> + +@@ -23,16 +25,34 @@ + + #define TMR0CTL_ENABLE BIT(7) + #define TMR0CTL_MODE_PERIODIC BIT(4) +-#define TMR0CTL_PRESCALER 1 ++#define TMR0CTL_PRESCALER 2 + #define TMR0CTL_PRESCALE_VAL (0xf - TMR0CTL_PRESCALER) + #define TMR0CTL_PRESCALE_DIV (65536 / BIT(TMR0CTL_PRESCALER)) + ++struct rt_timer_gpio { ++ struct list_head list; ++ struct led_classdev *led; ++}; ++ + struct rt_timer { +- struct device *dev; +- void __iomem *membase; +- int irq; +- unsigned long timer_freq; +- unsigned long timer_div; ++ struct device *dev; ++ void __iomem *membase; ++ int irq; ++ ++ unsigned long timer_freq; ++ unsigned long timer_div; ++ ++ struct list_head gpios; ++ struct led_trigger led_trigger; ++ unsigned int duty_cycle; ++ unsigned int duty; ++ ++ unsigned int fade; ++ unsigned int fade_min; ++ unsigned int fade_max; ++ unsigned int fade_speed; ++ unsigned int fade_dir; ++ unsigned int fade_count; + }; + + static inline void rt_timer_w32(struct rt_timer *rt, u8 reg, u32 val) +@@ -48,18 +68,46 @@ static inline u32 rt_timer_r32(struct rt_timer *rt, u8 reg) + static irqreturn_t rt_timer_irq(int irq, void *_rt) + { + struct rt_timer *rt = (struct rt_timer *) _rt; ++ struct rt_timer_gpio *gpio; ++ unsigned int val; + +- rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div); ++ if (rt->fade && (rt->fade_count++ > rt->fade_speed)) { ++ rt->fade_count = 0; ++ if (rt->duty_cycle <= rt->fade_min) ++ rt->fade_dir = 1; ++ else if (rt->duty_cycle >= rt->fade_max) ++ rt->fade_dir = 0; ++ ++ if (rt->fade_dir) ++ rt->duty_cycle += 1; ++ else ++ rt->duty_cycle -= 1; ++ ++ } ++ ++ val = rt->timer_freq / rt->timer_div; ++ if (rt->duty) ++ val *= rt->duty_cycle; ++ else ++ val *= (100 - rt->duty_cycle); ++ val /= 100; ++ ++ if (!list_empty(&rt->gpios)) ++ list_for_each_entry(gpio, &rt->gpios, list) ++ led_set_brightness(gpio->led, !!rt->duty); ++ ++ rt->duty = !rt->duty; ++ ++ rt_timer_w32(rt, TIMER_REG_TMR0LOAD, val + 1); + rt_timer_w32(rt, TIMER_REG_TMRSTAT, TMRSTAT_TMR0INT); + + return IRQ_HANDLED; + } + +- + static int rt_timer_request(struct rt_timer *rt) + { +- int err = request_irq(rt->irq, rt_timer_irq, IRQF_DISABLED, +- dev_name(rt->dev), rt); ++ int err = devm_request_irq(rt->dev, rt->irq, rt_timer_irq, ++ IRQF_DISABLED, dev_name(rt->dev), rt); + if (err) { + dev_err(rt->dev, "failed to request irq\n"); + } else { +@@ -81,8 +129,6 @@ static int rt_timer_config(struct rt_timer *rt, unsigned long divisor) + else + rt->timer_div = divisor; + +- rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div); +- + return 0; + } + +@@ -108,11 +154,128 @@ static void rt_timer_disable(struct rt_timer *rt) + rt_timer_w32(rt, TIMER_REG_TMR0CTL, t); + } + ++static ssize_t led_fade_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct led_classdev *led_cdev = dev_get_drvdata(dev); ++ struct rt_timer *rt = container_of(led_cdev->trigger, struct rt_timer, led_trigger); ++ ++ return sprintf(buf, "speed: %d, min: %d, max: %d\n", rt->fade_speed, rt->fade_min, rt->fade_max); ++} ++ ++static ssize_t led_fade_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct led_classdev *led_cdev = dev_get_drvdata(dev); ++ struct rt_timer *rt = container_of(led_cdev->trigger, struct rt_timer, led_trigger); ++ unsigned int speed = 0, min = 0, max = 0; ++ ssize_t ret = -EINVAL; ++ ++ ret = sscanf(buf, "%u %u %u", &speed, &min, &max); ++ ++ if (ret == 3) { ++ rt->fade_speed = speed; ++ rt->fade_min = min; ++ rt->fade_max = max; ++ rt->fade = 1; ++ } else { ++ rt->fade = 0; ++ } ++ ++ return size; ++} ++ ++static DEVICE_ATTR(fade, 0644, led_fade_show, led_fade_store); ++ ++static ssize_t led_duty_cycle_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct led_classdev *led_cdev = dev_get_drvdata(dev); ++ struct rt_timer *rt = container_of(led_cdev->trigger, struct rt_timer, led_trigger); ++ ++ return sprintf(buf, "%u\n", rt->duty_cycle); ++} ++ ++static ssize_t led_duty_cycle_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct led_classdev *led_cdev = dev_get_drvdata(dev); ++ struct rt_timer *rt = container_of(led_cdev->trigger, struct rt_timer, led_trigger); ++ unsigned long state; ++ ssize_t ret = -EINVAL; ++ ++ ret = kstrtoul(buf, 10, &state); ++ if (ret) ++ return ret; ++ ++ if (state <= 100) ++ rt->duty_cycle = state; ++ else ++ rt->duty_cycle = 100; ++ ++ rt->fade = 0; ++ ++ return size; ++} ++ ++static DEVICE_ATTR(duty_cycle, 0644, led_duty_cycle_show, led_duty_cycle_store); ++ ++static void rt_timer_trig_activate(struct led_classdev *led_cdev) ++{ ++ struct rt_timer *rt = container_of(led_cdev->trigger, struct rt_timer, led_trigger); ++ struct rt_timer_gpio *gpio_data; ++ int rc; ++ ++ led_cdev->trigger_data = NULL; ++ gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL); ++ if (!gpio_data) ++ return; ++ ++ rc = device_create_file(led_cdev->dev, &dev_attr_duty_cycle); ++ if (rc) ++ goto err_gpio; ++ rc = device_create_file(led_cdev->dev, &dev_attr_fade); ++ if (rc) ++ goto err_out_duty_cycle; ++ ++ led_cdev->activated = true; ++ led_cdev->trigger_data = gpio_data; ++ gpio_data->led = led_cdev; ++ list_add(&gpio_data->list, &rt->gpios); ++ led_cdev->trigger_data = gpio_data; ++ rt_timer_enable(rt); ++ return; ++ ++err_out_duty_cycle: ++ device_remove_file(led_cdev->dev, &dev_attr_duty_cycle); ++ ++err_gpio: ++ kfree(gpio_data); ++} ++ ++static void rt_timer_trig_deactivate(struct led_classdev *led_cdev) ++{ ++ struct rt_timer *rt = container_of(led_cdev->trigger, struct rt_timer, led_trigger); ++ struct rt_timer_gpio *gpio_data = (struct rt_timer_gpio*) led_cdev->trigger_data; ++ ++ if (led_cdev->activated) { ++ device_remove_file(led_cdev->dev, &dev_attr_duty_cycle); ++ device_remove_file(led_cdev->dev, &dev_attr_fade); ++ led_cdev->activated = false; ++ } ++ ++ list_del(&gpio_data->list); ++ rt_timer_disable(rt); ++ led_set_brightness(led_cdev, LED_OFF); ++} ++ + static int rt_timer_probe(struct platform_device *pdev) + { + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ const __be32 *divisor; + struct rt_timer *rt; + struct clk *clk; ++ int ret; + + if (!res) { + dev_err(&pdev->dev, "no memory resource found\n"); +@@ -147,12 +310,29 @@ static int rt_timer_probe(struct platform_device *pdev) + if (!rt->timer_freq) + return -EINVAL; + ++ rt->duty_cycle = 100; + rt->dev = &pdev->dev; + platform_set_drvdata(pdev, rt); + +- rt_timer_request(rt); +- rt_timer_config(rt, 2); +- rt_timer_enable(rt); ++ ret = rt_timer_request(rt); ++ if (ret) ++ return ret; ++ ++ divisor = of_get_property(pdev->dev.of_node, "ralink,divisor", NULL); ++ if (divisor) ++ rt_timer_config(rt, be32_to_cpu(*divisor)); ++ else ++ rt_timer_config(rt, 200); ++ ++ rt->led_trigger.name = "pwmtimer", ++ rt->led_trigger.activate = rt_timer_trig_activate, ++ rt->led_trigger.deactivate = rt_timer_trig_deactivate, ++ ++ ret = led_trigger_register(&rt->led_trigger); ++ if (ret) ++ return ret; ++ ++ INIT_LIST_HEAD(&rt->gpios); + + dev_info(&pdev->dev, "maximum frequncy is %luHz\n", rt->timer_freq); + +@@ -163,6 +343,7 @@ static int rt_timer_remove(struct platform_device *pdev) + { + struct rt_timer *rt = platform_get_drvdata(pdev); + ++ led_trigger_unregister(&rt->led_trigger); + rt_timer_disable(rt); + rt_timer_free(rt); + +@@ -187,6 +368,6 @@ static struct platform_driver rt_timer_driver = { + + module_platform_driver(rt_timer_driver); + +-MODULE_DESCRIPTION("Ralink RT2880 timer"); ++MODULE_DESCRIPTION("Ralink RT2880 timer / pseudo pwm"); + MODULE_AUTHOR("John Crispin <blogic@openwrt.org"); + MODULE_LICENSE("GPL"); +-- +1.7.10.4 + |