xref: /OK3568_Linux_fs/kernel/drivers/char/tb0219.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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