AUDIT: Honour audit_backlog_limit again.
[linux-2.6] / drivers / char / tb0219.c
1 /*
2  *  Driver for TANBAC TB0219 base board.
3  *
4  *  Copyright (C) 2005  Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <linux/device.h>
21 #include <linux/fs.h>
22 #include <linux/init.h>
23 #include <linux/module.h>
24
25 #include <asm/io.h>
26 #include <asm/reboot.h>
27
28 MODULE_AUTHOR("Yoichi Yuasa <yuasa@hh.iij4u.or.jp>");
29 MODULE_DESCRIPTION("TANBAC TB0219 base board driver");
30 MODULE_LICENSE("GPL");
31
32 static int major;       /* default is dynamic major device number */
33 module_param(major, int, 0);
34 MODULE_PARM_DESC(major, "Major device number");
35
36 static void (*old_machine_restart)(char *command);
37 static void __iomem *tb0219_base;
38 static spinlock_t tb0219_lock;
39
40 #define tb0219_read(offset)             readw(tb0219_base + (offset))
41 #define tb0219_write(offset, value)     writew((value), tb0219_base + (offset))
42
43 #define TB0219_START    0x0a000000UL
44 #define TB0219_SIZE     0x20UL
45
46 #define TB0219_LED                      0x00
47 #define TB0219_GPIO_INPUT               0x02
48 #define TB0219_GPIO_OUTPUT              0x04
49 #define TB0219_DIP_SWITCH               0x06
50 #define TB0219_MISC                     0x08
51 #define TB0219_RESET                    0x0e
52 #define TB0219_PCI_SLOT1_IRQ_STATUS     0x10
53 #define TB0219_PCI_SLOT2_IRQ_STATUS     0x12
54 #define TB0219_PCI_SLOT3_IRQ_STATUS     0x14
55
56 typedef enum {
57         TYPE_LED,
58         TYPE_GPIO_OUTPUT,
59 } tb0219_type_t;
60
61 /*
62  * Minor device number
63  *       0 = 7 segment LED
64  *
65  *      16 = GPIO IN 0
66  *      17 = GPIO IN 1
67  *      18 = GPIO IN 2
68  *      19 = GPIO IN 3
69  *      20 = GPIO IN 4
70  *      21 = GPIO IN 5
71  *      22 = GPIO IN 6
72  *      23 = GPIO IN 7
73  *
74  *      32 = GPIO OUT 0
75  *      33 = GPIO OUT 1
76  *      34 = GPIO OUT 2
77  *      35 = GPIO OUT 3
78  *      36 = GPIO OUT 4
79  *      37 = GPIO OUT 5
80  *      38 = GPIO OUT 6
81  *      39 = GPIO OUT 7
82  *
83  *      48 = DIP switch 1
84  *      49 = DIP switch 2
85  *      50 = DIP switch 3
86  *      51 = DIP switch 4
87  *      52 = DIP switch 5
88  *      53 = DIP switch 6
89  *      54 = DIP switch 7
90  *      55 = DIP switch 8
91  */
92
93 static inline char get_led(void)
94 {
95         return (char)tb0219_read(TB0219_LED);
96 }
97
98 static inline char get_gpio_input_pin(unsigned int pin)
99 {
100         uint16_t values;
101
102         values = tb0219_read(TB0219_GPIO_INPUT);
103         if (values & (1 << pin))
104                 return '1';
105
106         return '0';
107 }
108
109 static inline char get_gpio_output_pin(unsigned int pin)
110 {
111         uint16_t values;
112
113         values = tb0219_read(TB0219_GPIO_OUTPUT);
114         if (values & (1 << pin))
115                 return '1';
116
117         return '0';
118 }
119
120 static inline char get_dip_switch(unsigned int pin)
121 {
122         uint16_t values;
123
124         values = tb0219_read(TB0219_DIP_SWITCH);
125         if (values & (1 << pin))
126                 return '1';
127
128         return '0';
129 }
130
131 static inline int set_led(char command)
132 {
133         tb0219_write(TB0219_LED, command);
134
135         return 0;
136 }
137
138 static inline int set_gpio_output_pin(unsigned int pin, char command)
139 {
140         unsigned long flags;
141         uint16_t value;
142
143         if (command != '0' && command != '1')
144                 return -EINVAL;
145
146         spin_lock_irqsave(&tb0219_lock, flags);
147         value = tb0219_read(TB0219_GPIO_OUTPUT);
148         if (command == '0')
149                 value &= ~(1 << pin);
150         else
151                 value |= 1 << pin;
152         tb0219_write(TB0219_GPIO_OUTPUT, value);
153         spin_unlock_irqrestore(&tb0219_lock, flags);
154
155         return 0;
156
157 }
158
159 static ssize_t tanbac_tb0219_read(struct file *file, char __user *buf, size_t len,
160                                   loff_t *ppos)
161 {
162         unsigned int minor;
163         char value;
164
165         minor = iminor(file->f_dentry->d_inode);
166         switch (minor) {
167         case 0:
168                 value = get_led();
169                 break;
170         case 16 ... 23:
171                 value = get_gpio_input_pin(minor - 16);
172                 break;
173         case 32 ... 39:
174                 value = get_gpio_output_pin(minor - 32);
175                 break;
176         case 48 ... 55:
177                 value = get_dip_switch(minor - 48);
178                 break;
179         default:
180                 return -EBADF;
181         }
182
183         if (len <= 0)
184                 return -EFAULT;
185
186         if (put_user(value, buf))
187                 return -EFAULT;
188
189         return 1;
190 }
191
192 static ssize_t tanbac_tb0219_write(struct file *file, const char __user *data,
193                                    size_t len, loff_t *ppos)
194 {
195         unsigned int minor;
196         tb0219_type_t type;
197         size_t i;
198         int retval = 0;
199         char c;
200
201         minor = iminor(file->f_dentry->d_inode);
202         switch (minor) {
203         case 0:
204                 type = TYPE_LED;
205                 break;
206         case 32 ... 39:
207                 type = TYPE_GPIO_OUTPUT;
208                 break;
209         default:
210                 return -EBADF;
211         }
212
213         for (i = 0; i < len; i++) {
214                 if (get_user(c, data + i))
215                         return -EFAULT;
216
217                 switch (type) {
218                 case TYPE_LED:
219                         retval = set_led(c);
220                         break;
221                 case TYPE_GPIO_OUTPUT:
222                         retval = set_gpio_output_pin(minor - 32, c);
223                         break;
224                 }
225
226                 if (retval < 0)
227                         break;
228         }
229
230         return i;
231 }
232
233 static int tanbac_tb0219_open(struct inode *inode, struct file *file)
234 {
235         unsigned int minor;
236
237         minor = iminor(inode);
238         switch (minor) {
239         case 0:
240         case 16 ... 23:
241         case 32 ... 39:
242         case 48 ... 55:
243                 return nonseekable_open(inode, file);
244         default:
245                 break;
246         }
247
248         return -EBADF;
249 }
250
251 static int tanbac_tb0219_release(struct inode *inode, struct file *file)
252 {
253         return 0;
254 }
255
256 static struct file_operations tb0219_fops = {
257         .owner          = THIS_MODULE,
258         .read           = tanbac_tb0219_read,
259         .write          = tanbac_tb0219_write,
260         .open           = tanbac_tb0219_open,
261         .release        = tanbac_tb0219_release,
262 };
263
264 static void tb0219_restart(char *command)
265 {
266         tb0219_write(TB0219_RESET, 0);
267 }
268
269 static int tb0219_probe(struct device *dev)
270 {
271         int retval;
272
273         if (request_mem_region(TB0219_START, TB0219_SIZE, "TB0219") == NULL)
274                 return -EBUSY;
275
276         tb0219_base = ioremap(TB0219_START, TB0219_SIZE);
277         if (tb0219_base == NULL) {
278                 release_mem_region(TB0219_START, TB0219_SIZE);
279                 return -ENOMEM;
280         }
281
282         retval = register_chrdev(major, "TB0219", &tb0219_fops);
283         if (retval < 0) {
284                 iounmap(tb0219_base);
285                 tb0219_base = NULL;
286                 release_mem_region(TB0219_START, TB0219_SIZE);
287                 return retval;
288         }
289
290         spin_lock_init(&tb0219_lock);
291
292         old_machine_restart = _machine_restart;
293         _machine_restart = tb0219_restart;
294
295         if (major == 0) {
296                 major = retval;
297                 printk(KERN_INFO "TB0219: major number %d\n", major);
298         }
299
300         return 0;
301 }
302
303 static int tb0219_remove(struct device *dev)
304 {
305         _machine_restart = old_machine_restart;
306
307         iounmap(tb0219_base);
308         tb0219_base = NULL;
309
310         release_mem_region(TB0219_START, TB0219_SIZE);
311
312         return 0;
313 }
314
315 static struct platform_device *tb0219_platform_device;
316
317 static struct device_driver tb0219_device_driver = {
318         .name           = "TB0219",
319         .bus            = &platform_bus_type,
320         .probe          = tb0219_probe,
321         .remove         = tb0219_remove,
322 };
323
324 static int __devinit tanbac_tb0219_init(void)
325 {
326         int retval;
327
328         tb0219_platform_device = platform_device_register_simple("TB0219", -1, NULL, 0);
329         if (IS_ERR(tb0219_platform_device))
330                 return PTR_ERR(tb0219_platform_device);
331
332         retval = driver_register(&tb0219_device_driver);
333         if (retval < 0)
334                 platform_device_unregister(tb0219_platform_device);
335
336         return retval;
337 }
338
339 static void __devexit tanbac_tb0219_exit(void)
340 {
341         driver_unregister(&tb0219_device_driver);
342
343         platform_device_unregister(tb0219_platform_device);
344 }
345
346 module_init(tanbac_tb0219_init);
347 module_exit(tanbac_tb0219_exit);