1891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto/*
2891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto * LED Heartbeat Trigger
3891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto *
4891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
5891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto *
6891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto * Based on Richard Purdie's ledtrig-timer.c and some arch's
7891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto * CONFIG_HEARTBEAT code.
8891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto *
9891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto * This program is free software; you can redistribute it and/or modify
10891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto * it under the terms of the GNU General Public License version 2 as
11891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto * published by the Free Software Foundation.
12891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto *
13891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto */
14891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto#include <linux/module.h>
15891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto#include <linux/kernel.h>
16891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto#include <linux/init.h>
175a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
18891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto#include <linux/timer.h>
19891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto#include <linux/sched.h>
20891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto#include <linux/leds.h>
21891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto#include "leds.h"
22891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto
23891c668b90ded38cec36f0852c4983573597170dAtsushi Nemotostruct heartbeat_trig_data {
24891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	unsigned int phase;
25891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	unsigned int period;
26891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	struct timer_list timer;
27891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto};
28891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto
29891c668b90ded38cec36f0852c4983573597170dAtsushi Nemotostatic void led_heartbeat_function(unsigned long data)
30891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto{
31891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	struct led_classdev *led_cdev = (struct led_classdev *) data;
32891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
33891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	unsigned long brightness = LED_OFF;
34891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	unsigned long delay = 0;
35891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto
36891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	/* acts like an actual heart beat -- ie thump-thump-pause... */
37891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	switch (heartbeat_data->phase) {
38891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	case 0:
39891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		/*
40891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		 * The hyperbolic function below modifies the
41891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		 * heartbeat period length in dependency of the
42891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		 * current (1min) load. It goes through the points
43891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		 * f(0)=1260, f(1)=860, f(5)=510, f(inf)->300.
44891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		 */
45891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		heartbeat_data->period = 300 +
46891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto			(6720 << FSHIFT) / (5 * avenrun[0] + (7 << FSHIFT));
47891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		heartbeat_data->period =
48891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto			msecs_to_jiffies(heartbeat_data->period);
49891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		delay = msecs_to_jiffies(70);
50891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		heartbeat_data->phase++;
511bd465e6b0e2b559db47420fea686507a01cfab0Guennadi Liakhovetski		brightness = led_cdev->max_brightness;
52891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		break;
53891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	case 1:
54891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
55891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		heartbeat_data->phase++;
56891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		break;
57891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	case 2:
58891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		delay = msecs_to_jiffies(70);
59891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		heartbeat_data->phase++;
601bd465e6b0e2b559db47420fea686507a01cfab0Guennadi Liakhovetski		brightness = led_cdev->max_brightness;
61891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		break;
62891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	default:
63891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		delay = heartbeat_data->period - heartbeat_data->period / 4 -
64891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto			msecs_to_jiffies(70);
65891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		heartbeat_data->phase = 0;
66891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		break;
67891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	}
68891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto
69891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	led_set_brightness(led_cdev, brightness);
70891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	mod_timer(&heartbeat_data->timer, jiffies + delay);
71891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto}
72891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto
73891c668b90ded38cec36f0852c4983573597170dAtsushi Nemotostatic void heartbeat_trig_activate(struct led_classdev *led_cdev)
74891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto{
75891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	struct heartbeat_trig_data *heartbeat_data;
76891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto
77891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL);
78891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	if (!heartbeat_data)
79891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		return;
80891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto
81891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	led_cdev->trigger_data = heartbeat_data;
82891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	setup_timer(&heartbeat_data->timer,
83891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		    led_heartbeat_function, (unsigned long) led_cdev);
84891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	heartbeat_data->phase = 0;
85891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	led_heartbeat_function(heartbeat_data->timer.data);
86891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto}
87891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto
88891c668b90ded38cec36f0852c4983573597170dAtsushi Nemotostatic void heartbeat_trig_deactivate(struct led_classdev *led_cdev)
89891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto{
90891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
91891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto
92891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	if (heartbeat_data) {
93891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		del_timer_sync(&heartbeat_data->timer);
94891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto		kfree(heartbeat_data);
95891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	}
96891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto}
97891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto
98891c668b90ded38cec36f0852c4983573597170dAtsushi Nemotostatic struct led_trigger heartbeat_led_trigger = {
99891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	.name     = "heartbeat",
100891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	.activate = heartbeat_trig_activate,
101891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	.deactivate = heartbeat_trig_deactivate,
102891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto};
103891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto
104891c668b90ded38cec36f0852c4983573597170dAtsushi Nemotostatic int __init heartbeat_trig_init(void)
105891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto{
106891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	return led_trigger_register(&heartbeat_led_trigger);
107891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto}
108891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto
109891c668b90ded38cec36f0852c4983573597170dAtsushi Nemotostatic void __exit heartbeat_trig_exit(void)
110891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto{
111891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto	led_trigger_unregister(&heartbeat_led_trigger);
112891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto}
113891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto
114891c668b90ded38cec36f0852c4983573597170dAtsushi Nemotomodule_init(heartbeat_trig_init);
115891c668b90ded38cec36f0852c4983573597170dAtsushi Nemotomodule_exit(heartbeat_trig_exit);
116891c668b90ded38cec36f0852c4983573597170dAtsushi Nemoto
117891c668b90ded38cec36f0852c4983573597170dAtsushi NemotoMODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
118891c668b90ded38cec36f0852c4983573597170dAtsushi NemotoMODULE_DESCRIPTION("Heartbeat LED trigger");
119891c668b90ded38cec36f0852c4983573597170dAtsushi NemotoMODULE_LICENSE("GPL");
120