1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Driver for TANBAC TB0219 base board.
4 *
5 * Copyright (C) 2005 Yoichi Yuasa <yuasa@linux-mips.org>
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
18 MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
19 MODULE_DESCRIPTION("TANBAC TB0219 base board driver");
20 MODULE_LICENSE("GPL");
21
22 static int major; /* default is dynamic major device number */
23 module_param(major, int, 0);
24 MODULE_PARM_DESC(major, "Major device number");
25
26 static void (*old_machine_restart)(char *command);
27 static void __iomem *tb0219_base;
28 static 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
46 typedef enum {
47 TYPE_LED,
48 TYPE_GPIO_OUTPUT,
49 } tb0219_type_t;
50
51 /*
52 * Minor device number
53 * 0 = 7 segment LED
54 *
55 * 16 = GPIO IN 0
56 * 17 = GPIO IN 1
57 * 18 = GPIO IN 2
58 * 19 = GPIO IN 3
59 * 20 = GPIO IN 4
60 * 21 = GPIO IN 5
61 * 22 = GPIO IN 6
62 * 23 = GPIO IN 7
63 *
64 * 32 = GPIO OUT 0
65 * 33 = GPIO OUT 1
66 * 34 = GPIO OUT 2
67 * 35 = GPIO OUT 3
68 * 36 = GPIO OUT 4
69 * 37 = GPIO OUT 5
70 * 38 = GPIO OUT 6
71 * 39 = GPIO OUT 7
72 *
73 * 48 = DIP switch 1
74 * 49 = DIP switch 2
75 * 50 = DIP switch 3
76 * 51 = DIP switch 4
77 * 52 = DIP switch 5
78 * 53 = DIP switch 6
79 * 54 = DIP switch 7
80 * 55 = DIP switch 8
81 */
82
get_led(void)83 static inline char get_led(void)
84 {
85 return (char)tb0219_read(TB0219_LED);
86 }
87
get_gpio_input_pin(unsigned int pin)88 static 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
get_gpio_output_pin(unsigned int pin)99 static 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
get_dip_switch(unsigned int pin)110 static 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
set_led(char command)121 static inline int set_led(char command)
122 {
123 tb0219_write(TB0219_LED, command);
124
125 return 0;
126 }
127
set_gpio_output_pin(unsigned int pin,char command)128 static 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
tanbac_tb0219_read(struct file * file,char __user * buf,size_t len,loff_t * ppos)149 static 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
tanbac_tb0219_write(struct file * file,const char __user * data,size_t len,loff_t * ppos)182 static 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
tanbac_tb0219_open(struct inode * inode,struct file * file)223 static 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
tanbac_tb0219_release(struct inode * inode,struct file * file)241 static int tanbac_tb0219_release(struct inode *inode, struct file *file)
242 {
243 return 0;
244 }
245
246 static 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
tb0219_restart(char * command)255 static void tb0219_restart(char *command)
256 {
257 tb0219_write(TB0219_RESET, 0);
258 }
259
tb0219_pci_irq_init(void)260 static void tb0219_pci_irq_init(void)
261 {
262 /* PCI Slot 1 */
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 /* PCI Slot 2 */
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 /* PCI Slot 3 */
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
tb0219_probe(struct platform_device * dev)275 static 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
tb0219_remove(struct platform_device * dev)309 static 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
321 static struct platform_device *tb0219_platform_device;
322
323 static struct platform_driver tb0219_device_driver = {
324 .probe = tb0219_probe,
325 .remove = tb0219_remove,
326 .driver = {
327 .name = "TB0219",
328 },
329 };
330
tanbac_tb0219_init(void)331 static 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
tanbac_tb0219_exit(void)352 static void __exit tanbac_tb0219_exit(void)
353 {
354 platform_driver_unregister(&tb0219_device_driver);
355 platform_device_unregister(tb0219_platform_device);
356 }
357
358 module_init(tanbac_tb0219_init);
359 module_exit(tanbac_tb0219_exit);
360