xref: /openbmc/linux/arch/sh/drivers/heartbeat.c (revision 643d1f7f)
1 /*
2  * Generic heartbeat driver for regular LED banks
3  *
4  * Copyright (C) 2007  Paul Mundt
5  *
6  * Most SH reference boards include a number of individual LEDs that can
7  * be independently controlled (either via a pre-defined hardware
8  * function or via the LED class, if desired -- the hardware tends to
9  * encapsulate some of the same "triggers" that the LED class supports,
10  * so there's not too much value in it).
11  *
12  * Additionally, most of these boards also have a LED bank that we've
13  * traditionally used for strobing the load average. This use case is
14  * handled by this driver, rather than giving each LED bit position its
15  * own struct device.
16  *
17  * This file is subject to the terms and conditions of the GNU General Public
18  * License.  See the file "COPYING" in the main directory of this archive
19  * for more details.
20  */
21 #include <linux/init.h>
22 #include <linux/module.h>
23 #include <linux/platform_device.h>
24 #include <linux/sched.h>
25 #include <linux/timer.h>
26 #include <linux/io.h>
27 #include <asm/heartbeat.h>
28 
29 #define DRV_NAME "heartbeat"
30 #define DRV_VERSION "0.1.1"
31 
32 static unsigned char default_bit_pos[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
33 
34 static inline void heartbeat_toggle_bit(struct heartbeat_data *hd,
35 					unsigned bit, unsigned int inverted)
36 {
37 	unsigned int new;
38 
39 	new = (1 << hd->bit_pos[bit]);
40 	if (inverted)
41 		new = ~new;
42 
43 	switch (hd->regsize) {
44 	case 32:
45 		iowrite32(new, hd->base);
46 		break;
47 	case 16:
48 		iowrite16(new, hd->base);
49 		break;
50 	default:
51 		iowrite8(new, hd->base);
52 		break;
53 	}
54 }
55 
56 static void heartbeat_timer(unsigned long data)
57 {
58 	struct heartbeat_data *hd = (struct heartbeat_data *)data;
59 	static unsigned bit = 0, up = 1;
60 
61 	heartbeat_toggle_bit(hd, bit, hd->flags & HEARTBEAT_INVERTED);
62 
63 	bit += up;
64 	if ((bit == 0) || (bit == (hd->nr_bits)-1))
65 		up = -up;
66 
67 	mod_timer(&hd->timer, jiffies + (110 - ((300 << FSHIFT) /
68 			((avenrun[0] / 5) + (3 << FSHIFT)))));
69 }
70 
71 static int heartbeat_drv_probe(struct platform_device *pdev)
72 {
73 	struct resource *res;
74 	struct heartbeat_data *hd;
75 
76 	if (unlikely(pdev->num_resources != 1)) {
77 		dev_err(&pdev->dev, "invalid number of resources\n");
78 		return -EINVAL;
79 	}
80 
81 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
82 	if (unlikely(res == NULL)) {
83 		dev_err(&pdev->dev, "invalid resource\n");
84 		return -EINVAL;
85 	}
86 
87 	if (pdev->dev.platform_data) {
88 		hd = pdev->dev.platform_data;
89 	} else {
90 		hd = kzalloc(sizeof(struct heartbeat_data), GFP_KERNEL);
91 		if (unlikely(!hd))
92 			return -ENOMEM;
93 	}
94 
95 	hd->base = ioremap_nocache(res->start, res->end - res->start + 1);
96 	if (!unlikely(hd->base)) {
97 		dev_err(&pdev->dev, "ioremap failed\n");
98 
99 		if (!pdev->dev.platform_data)
100 			kfree(hd);
101 
102 		return -ENXIO;
103 	}
104 
105 	if (!hd->nr_bits) {
106 		hd->bit_pos = default_bit_pos;
107 		hd->nr_bits = ARRAY_SIZE(default_bit_pos);
108 	}
109 
110 	if (!hd->regsize)
111 		hd->regsize = 8;	/* default access size */
112 
113 	setup_timer(&hd->timer, heartbeat_timer, (unsigned long)hd);
114 	platform_set_drvdata(pdev, hd);
115 
116 	return mod_timer(&hd->timer, jiffies + 1);
117 }
118 
119 static int heartbeat_drv_remove(struct platform_device *pdev)
120 {
121 	struct heartbeat_data *hd = platform_get_drvdata(pdev);
122 
123 	del_timer_sync(&hd->timer);
124 	iounmap(hd->base);
125 
126 	platform_set_drvdata(pdev, NULL);
127 
128 	if (!pdev->dev.platform_data)
129 		kfree(hd);
130 
131 	return 0;
132 }
133 
134 static struct platform_driver heartbeat_driver = {
135 	.probe		= heartbeat_drv_probe,
136 	.remove		= heartbeat_drv_remove,
137 	.driver		= {
138 		.name	= DRV_NAME,
139 	},
140 };
141 
142 static int __init heartbeat_init(void)
143 {
144 	printk(KERN_NOTICE DRV_NAME ": version %s loaded\n", DRV_VERSION);
145 	return platform_driver_register(&heartbeat_driver);
146 }
147 
148 static void __exit heartbeat_exit(void)
149 {
150 	platform_driver_unregister(&heartbeat_driver);
151 }
152 module_init(heartbeat_init);
153 module_exit(heartbeat_exit);
154 
155 MODULE_VERSION(DRV_VERSION);
156 MODULE_AUTHOR("Paul Mundt");
157 MODULE_LICENSE("GPLv2");
158