tb0219.c (7285B)
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 18MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>"); 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; 28static 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 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 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 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 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 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: 233 return stream_open(inode, file); 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 246static 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 255static void tb0219_restart(char *command) 256{ 257 tb0219_write(TB0219_RESET, 0); 258} 259 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 275static 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 309static 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 321static struct platform_device *tb0219_platform_device; 322 323static struct platform_driver tb0219_device_driver = { 324 .probe = tb0219_probe, 325 .remove = tb0219_remove, 326 .driver = { 327 .name = "TB0219", 328 }, 329}; 330 331static 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 352static void __exit tanbac_tb0219_exit(void) 353{ 354 platform_driver_unregister(&tb0219_device_driver); 355 platform_device_unregister(tb0219_platform_device); 356} 357 358module_init(tanbac_tb0219_init); 359module_exit(tanbac_tb0219_exit);