xref: /openbmc/linux/arch/sh/drivers/heartbeat.c (revision e174d130)
1401e9093SPaul Mundt /*
2401e9093SPaul Mundt  * Generic heartbeat driver for regular LED banks
3401e9093SPaul Mundt  *
4401e9093SPaul Mundt  * Copyright (C) 2007  Paul Mundt
5401e9093SPaul Mundt  *
6401e9093SPaul Mundt  * Most SH reference boards include a number of individual LEDs that can
7401e9093SPaul Mundt  * be independently controlled (either via a pre-defined hardware
8401e9093SPaul Mundt  * function or via the LED class, if desired -- the hardware tends to
9401e9093SPaul Mundt  * encapsulate some of the same "triggers" that the LED class supports,
10401e9093SPaul Mundt  * so there's not too much value in it).
11401e9093SPaul Mundt  *
12401e9093SPaul Mundt  * Additionally, most of these boards also have a LED bank that we've
13401e9093SPaul Mundt  * traditionally used for strobing the load average. This use case is
14401e9093SPaul Mundt  * handled by this driver, rather than giving each LED bit position its
15401e9093SPaul Mundt  * own struct device.
16401e9093SPaul Mundt  *
17401e9093SPaul Mundt  * This file is subject to the terms and conditions of the GNU General Public
18401e9093SPaul Mundt  * License.  See the file "COPYING" in the main directory of this archive
19401e9093SPaul Mundt  * for more details.
20401e9093SPaul Mundt  */
21401e9093SPaul Mundt #include <linux/init.h>
22401e9093SPaul Mundt #include <linux/module.h>
23401e9093SPaul Mundt #include <linux/platform_device.h>
24401e9093SPaul Mundt #include <linux/sched.h>
25401e9093SPaul Mundt #include <linux/timer.h>
26401e9093SPaul Mundt #include <linux/io.h>
278786c952SPaul Mundt #include <asm/heartbeat.h>
28401e9093SPaul Mundt 
29401e9093SPaul Mundt #define DRV_NAME "heartbeat"
308786c952SPaul Mundt #define DRV_VERSION "0.1.1"
31401e9093SPaul Mundt 
328786c952SPaul Mundt static unsigned char default_bit_pos[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
338786c952SPaul Mundt 
348786c952SPaul Mundt static inline void heartbeat_toggle_bit(struct heartbeat_data *hd,
358786c952SPaul Mundt 					unsigned bit, unsigned int inverted)
368786c952SPaul Mundt {
378786c952SPaul Mundt 	unsigned int new;
388786c952SPaul Mundt 
398786c952SPaul Mundt 	new = (1 << hd->bit_pos[bit]);
408786c952SPaul Mundt 	if (inverted)
418786c952SPaul Mundt 		new = ~new;
428786c952SPaul Mundt 
43e174d130SKuninori Morimoto 	new &= hd->mask;
44e174d130SKuninori Morimoto 
458786c952SPaul Mundt 	switch (hd->regsize) {
468786c952SPaul Mundt 	case 32:
47e174d130SKuninori Morimoto 		new |= ioread32(hd->base) & ~hd->mask;
488786c952SPaul Mundt 		iowrite32(new, hd->base);
498786c952SPaul Mundt 		break;
508786c952SPaul Mundt 	case 16:
51e174d130SKuninori Morimoto 		new |= ioread16(hd->base) & ~hd->mask;
528786c952SPaul Mundt 		iowrite16(new, hd->base);
538786c952SPaul Mundt 		break;
548786c952SPaul Mundt 	default:
55e174d130SKuninori Morimoto 		new |= ioread8(hd->base) & ~hd->mask;
568786c952SPaul Mundt 		iowrite8(new, hd->base);
578786c952SPaul Mundt 		break;
588786c952SPaul Mundt 	}
598786c952SPaul Mundt }
60401e9093SPaul Mundt 
61401e9093SPaul Mundt static void heartbeat_timer(unsigned long data)
62401e9093SPaul Mundt {
63401e9093SPaul Mundt 	struct heartbeat_data *hd = (struct heartbeat_data *)data;
64401e9093SPaul Mundt 	static unsigned bit = 0, up = 1;
65401e9093SPaul Mundt 
668786c952SPaul Mundt 	heartbeat_toggle_bit(hd, bit, hd->flags & HEARTBEAT_INVERTED);
678786c952SPaul Mundt 
68f6072896STakashi YOSHII 	bit += up;
698786c952SPaul Mundt 	if ((bit == 0) || (bit == (hd->nr_bits)-1))
70f6072896STakashi YOSHII 		up = -up;
71401e9093SPaul Mundt 
72401e9093SPaul Mundt 	mod_timer(&hd->timer, jiffies + (110 - ((300 << FSHIFT) /
73401e9093SPaul Mundt 			((avenrun[0] / 5) + (3 << FSHIFT)))));
74401e9093SPaul Mundt }
75401e9093SPaul Mundt 
76401e9093SPaul Mundt static int heartbeat_drv_probe(struct platform_device *pdev)
77401e9093SPaul Mundt {
78401e9093SPaul Mundt 	struct resource *res;
79401e9093SPaul Mundt 	struct heartbeat_data *hd;
80e174d130SKuninori Morimoto 	int i;
81401e9093SPaul Mundt 
82401e9093SPaul Mundt 	if (unlikely(pdev->num_resources != 1)) {
83401e9093SPaul Mundt 		dev_err(&pdev->dev, "invalid number of resources\n");
84401e9093SPaul Mundt 		return -EINVAL;
85401e9093SPaul Mundt 	}
86401e9093SPaul Mundt 
87401e9093SPaul Mundt 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
88401e9093SPaul Mundt 	if (unlikely(res == NULL)) {
89401e9093SPaul Mundt 		dev_err(&pdev->dev, "invalid resource\n");
90401e9093SPaul Mundt 		return -EINVAL;
91401e9093SPaul Mundt 	}
92401e9093SPaul Mundt 
938786c952SPaul Mundt 	if (pdev->dev.platform_data) {
948786c952SPaul Mundt 		hd = pdev->dev.platform_data;
958786c952SPaul Mundt 	} else {
968786c952SPaul Mundt 		hd = kzalloc(sizeof(struct heartbeat_data), GFP_KERNEL);
97401e9093SPaul Mundt 		if (unlikely(!hd))
98401e9093SPaul Mundt 			return -ENOMEM;
99401e9093SPaul Mundt 	}
100401e9093SPaul Mundt 
1018786c952SPaul Mundt 	hd->base = ioremap_nocache(res->start, res->end - res->start + 1);
1021de83e94SRoel Kluin 	if (unlikely(!hd->base)) {
1038786c952SPaul Mundt 		dev_err(&pdev->dev, "ioremap failed\n");
1048786c952SPaul Mundt 
1058786c952SPaul Mundt 		if (!pdev->dev.platform_data)
1068786c952SPaul Mundt 			kfree(hd);
1078786c952SPaul Mundt 
1088786c952SPaul Mundt 		return -ENXIO;
1098786c952SPaul Mundt 	}
1108786c952SPaul Mundt 
1118786c952SPaul Mundt 	if (!hd->nr_bits) {
1128786c952SPaul Mundt 		hd->bit_pos = default_bit_pos;
1138786c952SPaul Mundt 		hd->nr_bits = ARRAY_SIZE(default_bit_pos);
1148786c952SPaul Mundt 	}
1158786c952SPaul Mundt 
116e174d130SKuninori Morimoto 	hd->mask = 0;
117e174d130SKuninori Morimoto 	for (i = 0; i < hd->nr_bits; i++)
118e174d130SKuninori Morimoto 		hd->mask |= (1 << hd->bit_pos[i]);
119e174d130SKuninori Morimoto 
1208786c952SPaul Mundt 	if (!hd->regsize)
1218786c952SPaul Mundt 		hd->regsize = 8;	/* default access size */
122401e9093SPaul Mundt 
123401e9093SPaul Mundt 	setup_timer(&hd->timer, heartbeat_timer, (unsigned long)hd);
124401e9093SPaul Mundt 	platform_set_drvdata(pdev, hd);
125401e9093SPaul Mundt 
126401e9093SPaul Mundt 	return mod_timer(&hd->timer, jiffies + 1);
127401e9093SPaul Mundt }
128401e9093SPaul Mundt 
129401e9093SPaul Mundt static int heartbeat_drv_remove(struct platform_device *pdev)
130401e9093SPaul Mundt {
131401e9093SPaul Mundt 	struct heartbeat_data *hd = platform_get_drvdata(pdev);
132401e9093SPaul Mundt 
133401e9093SPaul Mundt 	del_timer_sync(&hd->timer);
1348786c952SPaul Mundt 	iounmap(hd->base);
135401e9093SPaul Mundt 
136401e9093SPaul Mundt 	platform_set_drvdata(pdev, NULL);
137401e9093SPaul Mundt 
1388786c952SPaul Mundt 	if (!pdev->dev.platform_data)
139401e9093SPaul Mundt 		kfree(hd);
140401e9093SPaul Mundt 
141401e9093SPaul Mundt 	return 0;
142401e9093SPaul Mundt }
143401e9093SPaul Mundt 
144401e9093SPaul Mundt static struct platform_driver heartbeat_driver = {
145401e9093SPaul Mundt 	.probe		= heartbeat_drv_probe,
146401e9093SPaul Mundt 	.remove		= heartbeat_drv_remove,
147401e9093SPaul Mundt 	.driver		= {
148401e9093SPaul Mundt 		.name	= DRV_NAME,
149401e9093SPaul Mundt 	},
150401e9093SPaul Mundt };
151401e9093SPaul Mundt 
152401e9093SPaul Mundt static int __init heartbeat_init(void)
153401e9093SPaul Mundt {
154401e9093SPaul Mundt 	printk(KERN_NOTICE DRV_NAME ": version %s loaded\n", DRV_VERSION);
155401e9093SPaul Mundt 	return platform_driver_register(&heartbeat_driver);
156401e9093SPaul Mundt }
157401e9093SPaul Mundt 
158401e9093SPaul Mundt static void __exit heartbeat_exit(void)
159401e9093SPaul Mundt {
160401e9093SPaul Mundt 	platform_driver_unregister(&heartbeat_driver);
161401e9093SPaul Mundt }
162401e9093SPaul Mundt module_init(heartbeat_init);
163401e9093SPaul Mundt module_exit(heartbeat_exit);
164401e9093SPaul Mundt 
165401e9093SPaul Mundt MODULE_VERSION(DRV_VERSION);
166401e9093SPaul Mundt MODULE_AUTHOR("Paul Mundt");
167401e9093SPaul Mundt MODULE_LICENSE("GPL v2");
168