Merge current mainline tree into linux-omap tree
[pandora-kernel.git] / drivers / cbus / retu-wdt.c
1 /**
2  * drivers/cbus/retu-wdt.c
3  *
4  * Driver for Retu watchdog
5  *
6  * Copyright (C) 2004, 2005 Nokia Corporation
7  *
8  * Written by Amit Kucheria <amit.kucheria@nokia.com>
9  *
10  * This file is subject to the terms and conditions of the GNU General
11  * Public License. See the file "COPYING" in the main directory of this
12  * archive for more details.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include <linux/kernel.h>
25 #include <linux/module.h>
26 #include <linux/device.h>
27 #include <linux/init.h>
28
29 #include <linux/completion.h>
30 #include <linux/errno.h>
31 #include <linux/moduleparam.h>
32 #include <linux/platform_device.h>
33
34 #include "cbus.h"
35 #include "retu.h"
36
37 /* Watchdog timeout in seconds */
38 #define RETU_WDT_MIN_TIMER 0
39 #define RETU_WDT_DEFAULT_TIMER 32
40 #define RETU_WDT_MAX_TIMER 63
41
42 static struct completion retu_wdt_completion;
43 static DEFINE_MUTEX(retu_wdt_mutex);
44
45 /* Current period of watchdog */
46 static unsigned int period_val = RETU_WDT_DEFAULT_TIMER;
47 static int counter_param = RETU_WDT_MAX_TIMER;
48
49 static int retu_modify_counter(unsigned int new)
50 {
51         int ret = 0;
52
53         if (new < RETU_WDT_MIN_TIMER || new > RETU_WDT_MAX_TIMER)
54                 return -EINVAL;
55
56         mutex_lock(&retu_wdt_mutex);
57
58         period_val = new;
59         retu_write_reg(RETU_REG_WATCHDOG, (u16)period_val);
60
61         mutex_unlock(&retu_wdt_mutex);
62         return ret;
63 }
64
65 static ssize_t retu_wdt_period_show(struct device *dev,
66                                 struct device_attribute *attr, char *buf)
67 {
68         /* Show current max counter */
69         return sprintf(buf, "%u\n", (u16)period_val);
70 }
71
72 static ssize_t retu_wdt_period_store(struct device *dev,
73                                 struct device_attribute *attr,
74                                 const char *buf, size_t count)
75 {
76         unsigned int new_period;
77         int ret;
78
79         if (sscanf(buf, "%u", &new_period) != 1) {
80                 printk(KERN_ALERT "retu_wdt_period_store: Invalid input\n");
81                 return -EINVAL;
82         }
83
84         ret = retu_modify_counter(new_period);
85         if (ret < 0)
86                 return ret;
87
88         return strnlen(buf, count);
89 }
90
91 static ssize_t retu_wdt_counter_show(struct device *dev,
92                                 struct device_attribute *attr, char *buf)
93 {
94         u16 counter;
95
96         /* Show current value in watchdog counter */
97         counter = retu_read_reg(RETU_REG_WATCHDOG);
98
99         /* Only the 5 LSB are important */
100         return snprintf(buf, PAGE_SIZE, "%u\n", (counter & 0x3F));
101 }
102
103 static DEVICE_ATTR(period, S_IRUGO | S_IWUSR, retu_wdt_period_show, \
104                         retu_wdt_period_store);
105 static DEVICE_ATTR(counter, S_IRUGO, retu_wdt_counter_show, NULL);
106
107 static int __devinit retu_wdt_probe(struct device *dev)
108 {
109         int ret;
110
111         ret = device_create_file(dev, &dev_attr_period);
112         if (ret) {
113                 printk(KERN_ERR "retu_wdt_probe: Error creating "
114                                         "sys device file: period\n");
115                 return ret;
116         }
117
118         ret = device_create_file(dev, &dev_attr_counter);
119         if (ret) {
120                 device_remove_file(dev, &dev_attr_period);
121                 printk(KERN_ERR "retu_wdt_probe: Error creating "
122                                         "sys device file: counter\n");
123         }
124
125         return ret;
126 }
127
128 static int __devexit retu_wdt_remove(struct device *dev)
129 {
130         device_remove_file(dev, &dev_attr_period);
131         device_remove_file(dev, &dev_attr_counter);
132         return 0;
133 }
134
135 static void retu_wdt_device_release(struct device *dev)
136 {
137         complete(&retu_wdt_completion);
138 }
139
140 static struct platform_device retu_wdt_device = {
141         .name = "retu-watchdog",
142         .id = -1,
143         .dev = {
144                 .release = retu_wdt_device_release,
145         },
146 };
147
148 static struct device_driver retu_wdt_driver = {
149         .name = "retu-watchdog",
150         .bus = &platform_bus_type,
151         .probe = retu_wdt_probe,
152         .remove = __devexit_p(retu_wdt_remove),
153 };
154
155 static int __init retu_wdt_init(void)
156 {
157         int ret;
158
159         init_completion(&retu_wdt_completion);
160
161         ret = driver_register(&retu_wdt_driver);
162         if (ret)
163                 return ret;
164
165         ret = platform_device_register(&retu_wdt_device);
166         if (ret)
167                 goto exit1;
168
169         /* passed as module parameter? */
170         ret = retu_modify_counter(counter_param);
171         if (ret == -EINVAL) {
172                 ret = retu_modify_counter(RETU_WDT_DEFAULT_TIMER);
173                 printk(KERN_INFO
174                        "retu_wdt_init: Intializing to default value\n");
175         }
176
177         printk(KERN_INFO "Retu watchdog driver initialized\n");
178         return ret;
179
180 exit1:
181         driver_unregister(&retu_wdt_driver);
182         wait_for_completion(&retu_wdt_completion);
183
184         return ret;
185 }
186
187 static void __exit retu_wdt_exit(void)
188 {
189         platform_device_unregister(&retu_wdt_device);
190         driver_unregister(&retu_wdt_driver);
191
192         wait_for_completion(&retu_wdt_completion);
193 }
194
195 module_init(retu_wdt_init);
196 module_exit(retu_wdt_exit);
197 module_param(counter_param, int, 0);
198
199 MODULE_DESCRIPTION("Retu WatchDog");
200 MODULE_AUTHOR("Amit Kucheria");
201 MODULE_LICENSE("GPL");
202