GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / char / tb0219.c
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
83 static inline char get_led(void)
84 {
85         return (char)tb0219_read(TB0219_LED);
86 }
87
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
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
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
121 static inline int set_led(char command)
122 {
123         tb0219_write(TB0219_LED, command);
124
125         return 0;
126 }
127
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
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
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
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
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
255 static void tb0219_restart(char *command)
256 {
257         tb0219_write(TB0219_RESET, 0);
258 }
259
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
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
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
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
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);