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