]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blame - drivers/char/tb0219.c
Merge tag 'mmc-v5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
[mirror_ubuntu-focal-kernel.git] / drivers / char / tb0219.c
CommitLineData
1a59d1b8 1// SPDX-License-Identifier: GPL-2.0-or-later
1da177e4
LT
2/*
3 * Driver for TANBAC TB0219 base board.
4 *
ada8e951 5 * Copyright (C) 2005 Yoichi Yuasa <yuasa@linux-mips.org>
1da177e4 6 */
d052d1be 7#include <linux/platform_device.h>
1da177e4
LT
8#include <linux/fs.h>
9#include <linux/init.h>
10#include <linux/module.h>
29abfbd9 11#include <linux/uaccess.h>
1da177e4
LT
12
13#include <asm/io.h>
14#include <asm/reboot.h>
6e498c10
YY
15#include <asm/vr41xx/giu.h>
16#include <asm/vr41xx/tb0219.h>
1da177e4 17
ada8e951 18MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
1da177e4
LT
19MODULE_DESCRIPTION("TANBAC TB0219 base board driver");
20MODULE_LICENSE("GPL");
21
22static int major; /* default is dynamic major device number */
23module_param(major, int, 0);
24MODULE_PARM_DESC(major, "Major device number");
25
26static void (*old_machine_restart)(char *command);
27static void __iomem *tb0219_base;
d2a7be0b 28static DEFINE_SPINLOCK(tb0219_lock);
1da177e4
LT
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
46typedef 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
83static inline char get_led(void)
84{
85 return (char)tb0219_read(TB0219_LED);
86}
87
88static 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
99static 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
110static 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
121static inline int set_led(char command)
122{
123 tb0219_write(TB0219_LED, command);
124
125 return 0;
126}
127
128static 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
149static 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
496ad9aa 155 minor = iminor(file_inode(file));
1da177e4
LT
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
182static 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
496ad9aa 191 minor = iminor(file_inode(file));
1da177e4
LT
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
223static 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:
c5bf68fe 233 return stream_open(inode, file);
1da177e4
LT
234 default:
235 break;
236 }
237
238 return -EBADF;
239}
240
241static int tanbac_tb0219_release(struct inode *inode, struct file *file)
242{
243 return 0;
244}
245
62322d25 246static const struct file_operations tb0219_fops = {
1da177e4
LT
247 .owner = THIS_MODULE,
248 .read = tanbac_tb0219_read,
249 .write = tanbac_tb0219_write,
250 .open = tanbac_tb0219_open,
251 .release = tanbac_tb0219_release,
6038f373 252 .llseek = no_llseek,
1da177e4
LT
253};
254
255static void tb0219_restart(char *command)
256{
257 tb0219_write(TB0219_RESET, 0);
258}
259
6e498c10
YY
260static 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
2223cbec 275static int tb0219_probe(struct platform_device *dev)
1da177e4
LT
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
1da177e4
LT
296 old_machine_restart = _machine_restart;
297 _machine_restart = tb0219_restart;
298
6e498c10
YY
299 tb0219_pci_irq_init();
300
1da177e4
LT
301 if (major == 0) {
302 major = retval;
303 printk(KERN_INFO "TB0219: major number %d\n", major);
304 }
305
306 return 0;
307}
308
39af33fc 309static int tb0219_remove(struct platform_device *dev)
1da177e4
LT
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
321static struct platform_device *tb0219_platform_device;
322
3ae5eaec 323static struct platform_driver tb0219_device_driver = {
1da177e4 324 .probe = tb0219_probe,
4dde2d2f 325 .remove = tb0219_remove,
3ae5eaec
RK
326 .driver = {
327 .name = "TB0219",
328 },
1da177e4
LT
329};
330
d0497614 331static int __init tanbac_tb0219_init(void)
1da177e4
LT
332{
333 int retval;
334
d0497614
DT
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 }
1da177e4 344
3ae5eaec 345 retval = platform_driver_register(&tb0219_device_driver);
1da177e4
LT
346 if (retval < 0)
347 platform_device_unregister(tb0219_platform_device);
348
349 return retval;
350}
351
d0497614 352static void __exit tanbac_tb0219_exit(void)
1da177e4 353{
3ae5eaec 354 platform_driver_unregister(&tb0219_device_driver);
1da177e4
LT
355 platform_device_unregister(tb0219_platform_device);
356}
357
358module_init(tanbac_tb0219_init);
359module_exit(tanbac_tb0219_exit);