sh: Subnormal double to float conversion
[linux-2.6] / arch / sh / drivers / heartbeat.c
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("GPL v2");