WIP FPC-III support
[linux/fpc-iii.git] / drivers / char / tb0219.c
blob1f36be14978ff32112dc4f17b247eca53de55798
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Driver for TANBAC TB0219 base board.
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>
13 #include <asm/io.h>
14 #include <asm/reboot.h>
15 #include <asm/vr41xx/giu.h>
16 #include <asm/vr41xx/tb0219.h>
18 MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
19 MODULE_DESCRIPTION("TANBAC TB0219 base board driver");
20 MODULE_LICENSE("GPL");
22 static int major; /* default is dynamic major device number */
23 module_param(major, int, 0);
24 MODULE_PARM_DESC(major, "Major device number");
26 static void (*old_machine_restart)(char *command);
27 static void __iomem *tb0219_base;
28 static DEFINE_SPINLOCK(tb0219_lock);
30 #define tb0219_read(offset) readw(tb0219_base + (offset))
31 #define tb0219_write(offset, value) writew((value), tb0219_base + (offset))
33 #define TB0219_START 0x0a000000UL
34 #define TB0219_SIZE 0x20UL
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
46 typedef enum {
47 TYPE_LED,
48 TYPE_GPIO_OUTPUT,
49 } tb0219_type_t;
52 * Minor device number
53 * 0 = 7 segment LED
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
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
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
83 static inline char get_led(void)
85 return (char)tb0219_read(TB0219_LED);
88 static inline char get_gpio_input_pin(unsigned int pin)
90 uint16_t values;
92 values = tb0219_read(TB0219_GPIO_INPUT);
93 if (values & (1 << pin))
94 return '1';
96 return '0';
99 static inline char get_gpio_output_pin(unsigned int pin)
101 uint16_t values;
103 values = tb0219_read(TB0219_GPIO_OUTPUT);
104 if (values & (1 << pin))
105 return '1';
107 return '0';
110 static inline char get_dip_switch(unsigned int pin)
112 uint16_t values;
114 values = tb0219_read(TB0219_DIP_SWITCH);
115 if (values & (1 << pin))
116 return '1';
118 return '0';
121 static inline int set_led(char command)
123 tb0219_write(TB0219_LED, command);
125 return 0;
128 static inline int set_gpio_output_pin(unsigned int pin, char command)
130 unsigned long flags;
131 uint16_t value;
133 if (command != '0' && command != '1')
134 return -EINVAL;
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);
145 return 0;
149 static ssize_t tanbac_tb0219_read(struct file *file, char __user *buf, size_t len,
150 loff_t *ppos)
152 unsigned int minor;
153 char value;
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;
173 if (len <= 0)
174 return -EFAULT;
176 if (put_user(value, buf))
177 return -EFAULT;
179 return 1;
182 static ssize_t tanbac_tb0219_write(struct file *file, const char __user *data,
183 size_t len, loff_t *ppos)
185 unsigned int minor;
186 tb0219_type_t type;
187 size_t i;
188 int retval = 0;
189 char c;
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;
203 for (i = 0; i < len; i++) {
204 if (get_user(c, data + i))
205 return -EFAULT;
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;
216 if (retval < 0)
217 break;
220 return i;
223 static int tanbac_tb0219_open(struct inode *inode, struct file *file)
225 unsigned int minor;
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;
238 return -EBADF;
241 static int tanbac_tb0219_release(struct inode *inode, struct file *file)
243 return 0;
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,
255 static void tb0219_restart(char *command)
257 tb0219_write(TB0219_RESET, 0);
260 static void tb0219_pci_irq_init(void)
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);
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);
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);
275 static int tb0219_probe(struct platform_device *dev)
277 int retval;
279 if (request_mem_region(TB0219_START, TB0219_SIZE, "TB0219") == NULL)
280 return -EBUSY;
282 tb0219_base = ioremap(TB0219_START, TB0219_SIZE);
283 if (tb0219_base == NULL) {
284 release_mem_region(TB0219_START, TB0219_SIZE);
285 return -ENOMEM;
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;
296 old_machine_restart = _machine_restart;
297 _machine_restart = tb0219_restart;
299 tb0219_pci_irq_init();
301 if (major == 0) {
302 major = retval;
303 printk(KERN_INFO "TB0219: major number %d\n", major);
306 return 0;
309 static int tb0219_remove(struct platform_device *dev)
311 _machine_restart = old_machine_restart;
313 iounmap(tb0219_base);
314 tb0219_base = NULL;
316 release_mem_region(TB0219_START, TB0219_SIZE);
318 return 0;
321 static struct platform_device *tb0219_platform_device;
323 static struct platform_driver tb0219_device_driver = {
324 .probe = tb0219_probe,
325 .remove = tb0219_remove,
326 .driver = {
327 .name = "TB0219",
331 static int __init tanbac_tb0219_init(void)
333 int retval;
335 tb0219_platform_device = platform_device_alloc("TB0219", -1);
336 if (!tb0219_platform_device)
337 return -ENOMEM;
339 retval = platform_device_add(tb0219_platform_device);
340 if (retval < 0) {
341 platform_device_put(tb0219_platform_device);
342 return retval;
345 retval = platform_driver_register(&tb0219_device_driver);
346 if (retval < 0)
347 platform_device_unregister(tb0219_platform_device);
349 return retval;
352 static void __exit tanbac_tb0219_exit(void)
354 platform_driver_unregister(&tb0219_device_driver);
355 platform_device_unregister(tb0219_platform_device);
358 module_init(tanbac_tb0219_init);
359 module_exit(tanbac_tb0219_exit);