1
2
3
4
5
6
7#include <linux/platform_device.h>
8#include <linux/fs.h>
9#include <linux/init.h>
10#include <linux/module.h>
11#include <linux/uaccess.h>
12
13#include <asm/io.h>
14#include <asm/reboot.h>
15#include <asm/vr41xx/giu.h>
16#include <asm/vr41xx/tb0219.h>
17
18MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
19MODULE_DESCRIPTION("TANBAC TB0219 base board driver");
20MODULE_LICENSE("GPL");
21
22static int major;
23module_param(major, int, 0);
24MODULE_PARM_DESC(major, "Major device number");
25
26static void (*old_machine_restart)(char *command);
27static void __iomem *tb0219_base;
28static DEFINE_SPINLOCK(tb0219_lock);
29
30#define tb0219_read(offset) readw(tb0219_base + (offset))
31#define tb0219_write(offset, value) writew((value), tb0219_base + (offset))
32
33#define TB0219_START 0x0a000000UL
34#define TB0219_SIZE 0x20UL
35
36#define TB0219_LED 0x00
37#define TB0219_GPIO_INPUT 0x02
38#define TB0219_GPIO_OUTPUT 0x04
39#define TB0219_DIP_SWITCH 0x06
40#define TB0219_MISC 0x08
41#define TB0219_RESET 0x0e
42#define TB0219_PCI_SLOT1_IRQ_STATUS 0x10
43#define TB0219_PCI_SLOT2_IRQ_STATUS 0x12
44#define TB0219_PCI_SLOT3_IRQ_STATUS 0x14
45
46typedef enum {
47 TYPE_LED,
48 TYPE_GPIO_OUTPUT,
49} tb0219_type_t;
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83static inline char get_led(void)
84{
85 return (char)tb0219_read(TB0219_LED);
86}
87
88static inline char get_gpio_input_pin(unsigned int pin)
89{
90 uint16_t values;
91
92 values = tb0219_read(TB0219_GPIO_INPUT);
93 if (values & (1 << pin))
94 return '1';
95
96 return '0';
97}
98
99static inline char get_gpio_output_pin(unsigned int pin)
100{
101 uint16_t values;
102
103 values = tb0219_read(TB0219_GPIO_OUTPUT);
104 if (values & (1 << pin))
105 return '1';
106
107 return '0';
108}
109
110static inline char get_dip_switch(unsigned int pin)
111{
112 uint16_t values;
113
114 values = tb0219_read(TB0219_DIP_SWITCH);
115 if (values & (1 << pin))
116 return '1';
117
118 return '0';
119}
120
121static inline int set_led(char command)
122{
123 tb0219_write(TB0219_LED, command);
124
125 return 0;
126}
127
128static inline int set_gpio_output_pin(unsigned int pin, char command)
129{
130 unsigned long flags;
131 uint16_t value;
132
133 if (command != '0' && command != '1')
134 return -EINVAL;
135
136 spin_lock_irqsave(&tb0219_lock, flags);
137 value = tb0219_read(TB0219_GPIO_OUTPUT);
138 if (command == '0')
139 value &= ~(1 << pin);
140 else
141 value |= 1 << pin;
142 tb0219_write(TB0219_GPIO_OUTPUT, value);
143 spin_unlock_irqrestore(&tb0219_lock, flags);
144
145 return 0;
146
147}
148
149static ssize_t tanbac_tb0219_read(struct file *file, char __user *buf, size_t len,
150 loff_t *ppos)
151{
152 unsigned int minor;
153 char value;
154
155 minor = iminor(file_inode(file));
156 switch (minor) {
157 case 0:
158 value = get_led();
159 break;
160 case 16 ... 23:
161 value = get_gpio_input_pin(minor - 16);
162 break;
163 case 32 ... 39:
164 value = get_gpio_output_pin(minor - 32);
165 break;
166 case 48 ... 55:
167 value = get_dip_switch(minor - 48);
168 break;
169 default:
170 return -EBADF;
171 }
172
173 if (len <= 0)
174 return -EFAULT;
175
176 if (put_user(value, buf))
177 return -EFAULT;
178
179 return 1;
180}
181
182static ssize_t tanbac_tb0219_write(struct file *file, const char __user *data,
183 size_t len, loff_t *ppos)
184{
185 unsigned int minor;
186 tb0219_type_t type;
187 size_t i;
188 int retval = 0;
189 char c;
190
191 minor = iminor(file_inode(file));
192 switch (minor) {
193 case 0:
194 type = TYPE_LED;
195 break;
196 case 32 ... 39:
197 type = TYPE_GPIO_OUTPUT;
198 break;
199 default:
200 return -EBADF;
201 }
202
203 for (i = 0; i < len; i++) {
204 if (get_user(c, data + i))
205 return -EFAULT;
206
207 switch (type) {
208 case TYPE_LED:
209 retval = set_led(c);
210 break;
211 case TYPE_GPIO_OUTPUT:
212 retval = set_gpio_output_pin(minor - 32, c);
213 break;
214 }
215
216 if (retval < 0)
217 break;
218 }
219
220 return i;
221}
222
223static int tanbac_tb0219_open(struct inode *inode, struct file *file)
224{
225 unsigned int minor;
226
227 minor = iminor(inode);
228 switch (minor) {
229 case 0:
230 case 16 ... 23:
231 case 32 ... 39:
232 case 48 ... 55:
233 return stream_open(inode, file);
234 default:
235 break;
236 }
237
238 return -EBADF;
239}
240
241static int tanbac_tb0219_release(struct inode *inode, struct file *file)
242{
243 return 0;
244}
245
246static const struct file_operations tb0219_fops = {
247 .owner = THIS_MODULE,
248 .read = tanbac_tb0219_read,
249 .write = tanbac_tb0219_write,
250 .open = tanbac_tb0219_open,
251 .release = tanbac_tb0219_release,
252 .llseek = no_llseek,
253};
254
255static void tb0219_restart(char *command)
256{
257 tb0219_write(TB0219_RESET, 0);
258}
259
260static void tb0219_pci_irq_init(void)
261{
262
263 vr41xx_set_irq_trigger(TB0219_PCI_SLOT1_PIN, IRQ_TRIGGER_LEVEL, IRQ_SIGNAL_THROUGH);
264 vr41xx_set_irq_level(TB0219_PCI_SLOT1_PIN, IRQ_LEVEL_LOW);
265
266
267 vr41xx_set_irq_trigger(TB0219_PCI_SLOT2_PIN, IRQ_TRIGGER_LEVEL, IRQ_SIGNAL_THROUGH);
268 vr41xx_set_irq_level(TB0219_PCI_SLOT2_PIN, IRQ_LEVEL_LOW);
269
270
271 vr41xx_set_irq_trigger(TB0219_PCI_SLOT3_PIN, IRQ_TRIGGER_LEVEL, IRQ_SIGNAL_THROUGH);
272 vr41xx_set_irq_level(TB0219_PCI_SLOT3_PIN, IRQ_LEVEL_LOW);
273}
274
275static int tb0219_probe(struct platform_device *dev)
276{
277 int retval;
278
279 if (request_mem_region(TB0219_START, TB0219_SIZE, "TB0219") == NULL)
280 return -EBUSY;
281
282 tb0219_base = ioremap(TB0219_START, TB0219_SIZE);
283 if (tb0219_base == NULL) {
284 release_mem_region(TB0219_START, TB0219_SIZE);
285 return -ENOMEM;
286 }
287
288 retval = register_chrdev(major, "TB0219", &tb0219_fops);
289 if (retval < 0) {
290 iounmap(tb0219_base);
291 tb0219_base = NULL;
292 release_mem_region(TB0219_START, TB0219_SIZE);
293 return retval;
294 }
295
296 old_machine_restart = _machine_restart;
297 _machine_restart = tb0219_restart;
298
299 tb0219_pci_irq_init();
300
301 if (major == 0) {
302 major = retval;
303 printk(KERN_INFO "TB0219: major number %d\n", major);
304 }
305
306 return 0;
307}
308
309static int tb0219_remove(struct platform_device *dev)
310{
311 _machine_restart = old_machine_restart;
312
313 iounmap(tb0219_base);
314 tb0219_base = NULL;
315
316 release_mem_region(TB0219_START, TB0219_SIZE);
317
318 return 0;
319}
320
321static struct platform_device *tb0219_platform_device;
322
323static struct platform_driver tb0219_device_driver = {
324 .probe = tb0219_probe,
325 .remove = tb0219_remove,
326 .driver = {
327 .name = "TB0219",
328 },
329};
330
331static int __init tanbac_tb0219_init(void)
332{
333 int retval;
334
335 tb0219_platform_device = platform_device_alloc("TB0219", -1);
336 if (!tb0219_platform_device)
337 return -ENOMEM;
338
339 retval = platform_device_add(tb0219_platform_device);
340 if (retval < 0) {
341 platform_device_put(tb0219_platform_device);
342 return retval;
343 }
344
345 retval = platform_driver_register(&tb0219_device_driver);
346 if (retval < 0)
347 platform_device_unregister(tb0219_platform_device);
348
349 return retval;
350}
351
352static void __exit tanbac_tb0219_exit(void)
353{
354 platform_driver_unregister(&tb0219_device_driver);
355 platform_device_unregister(tb0219_platform_device);
356}
357
358module_init(tanbac_tb0219_init);
359module_exit(tanbac_tb0219_exit);
360