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