aboutsummaryrefslogtreecommitdiffstats
generated by cgit v1.2.3 (git 2.25.1) at 2025-02-21 14:37:37 +0000 > 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
From 9de00286e20a5f5edc419698373010f1cb6ff0ce Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Sun, 27 Jul 2014 09:25:02 +0100
Subject: [PATCH 19/57] 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(-)

--- 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,8 +68,37 @@ static inline u32 rt_timer_r32(struct rt
 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;
@@ -58,8 +107,8 @@ static irqreturn_t rt_timer_irq(int irq,
 
 static int rt_timer_request(struct rt_timer *rt)
 {
-	int err = request_irq(rt->irq, rt_timer_irq, 0,
-						dev_name(rt->dev), rt);
+	int err = devm_request_irq(rt->dev, rt->irq, rt_timer_irq,
+					0, dev_name(rt->dev), rt);
 	if (err) {
 		dev_err(rt->dev, "failed to request irq\n");
 	} else {
@@ -81,8 +130,6 @@ static int rt_timer_config(struct rt_tim
 	else
 		rt->timer_div = divisor;
 
-	rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div);
-
 	return 0;
 }
 
@@ -108,11 +155,128 @@ static void rt_timer_disable(struct rt_t
 	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;
 
 	rt = devm_kzalloc(&pdev->dev, sizeof(*rt), GFP_KERNEL);
 	if (!rt) {
@@ -140,12 +304,29 @@ static int rt_timer_probe(struct platfor
 	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 frequency is %luHz\n", rt->timer_freq);
 
@@ -156,6 +337,7 @@ static int rt_timer_remove(struct platfo
 {
 	struct rt_timer *rt = platform_get_drvdata(pdev);
 
+	led_trigger_unregister(&rt->led_trigger);
 	rt_timer_disable(rt);
 	rt_timer_free(rt);
 
@@ -180,6 +362,6 @@ static struct platform_driver rt_timer_d
 
 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");