1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 1999-2001 Vojtech Pavlik 4 * Copyright (c) 1999 Brian Gerst 5 */ 6 7 /* 8 * NS558 based standard IBM game port driver for Linux 9 */ 10 11 #include <asm/io.h> 12 13 #include <linux/module.h> 14 #include <linux/ioport.h> 15 #include <linux/init.h> 16 #include <linux/delay.h> 17 #include <linux/gameport.h> 18 #include <linux/slab.h> 19 #include <linux/pnp.h> 20 21 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 22 MODULE_DESCRIPTION("Classic gameport (ISA/PnP) driver"); 23 MODULE_LICENSE("GPL"); 24 25 static int ns558_isa_portlist[] = { 0x201, 0x200, 0x202, 0x203, 0x204, 0x205, 0x207, 0x209, 26 0x20b, 0x20c, 0x20e, 0x20f, 0x211, 0x219, 0x101, 0 }; 27 28 struct ns558 { 29 int type; 30 int io; 31 int size; 32 struct pnp_dev *dev; 33 struct gameport *gameport; 34 struct list_head node; 35 }; 36 37 static LIST_HEAD(ns558_list); 38 39 /* 40 * ns558_isa_probe() tries to find an isa gameport at the 41 * specified address, and also checks for mirrors. 42 * A joystick must be attached for this to work. 43 */ 44 45 static int ns558_isa_probe(int io) 46 { 47 int i, j, b; 48 unsigned char c, u, v; 49 struct ns558 *ns558; 50 struct gameport *port; 51 52 /* 53 * No one should be using this address. 54 */ 55 56 if (!request_region(io, 1, "ns558-isa")) 57 return -EBUSY; 58 59 /* 60 * We must not be able to write arbitrary values to the port. 61 * The lower two axis bits must be 1 after a write. 62 */ 63 64 c = inb(io); 65 outb(~c & ~3, io); 66 if (~(u = v = inb(io)) & 3) { 67 outb(c, io); 68 release_region(io, 1); 69 return -ENODEV; 70 } 71 /* 72 * After a trigger, there must be at least some bits changing. 73 */ 74 75 for (i = 0; i < 1000; i++) v &= inb(io); 76 77 if (u == v) { 78 outb(c, io); 79 release_region(io, 1); 80 return -ENODEV; 81 } 82 msleep(3); 83 /* 84 * After some time (4ms) the axes shouldn't change anymore. 85 */ 86 87 u = inb(io); 88 for (i = 0; i < 1000; i++) 89 if ((u ^ inb(io)) & 0xf) { 90 outb(c, io); 91 release_region(io, 1); 92 return -ENODEV; 93 } 94 /* 95 * And now find the number of mirrors of the port. 96 */ 97 98 for (i = 1; i < 5; i++) { 99 100 release_region(io & (-1 << (i - 1)), (1 << (i - 1))); 101 102 if (!request_region(io & (-1 << i), (1 << i), "ns558-isa")) 103 break; /* Don't disturb anyone */ 104 105 outb(0xff, io & (-1 << i)); 106 for (j = b = 0; j < 1000; j++) 107 if (inb(io & (-1 << i)) != inb((io & (-1 << i)) + (1 << i) - 1)) b++; 108 msleep(3); 109 110 if (b > 300) { /* We allow 30% difference */ 111 release_region(io & (-1 << i), (1 << i)); 112 break; 113 } 114 } 115 116 i--; 117 118 if (i != 4) { 119 if (!request_region(io & (-1 << i), (1 << i), "ns558-isa")) 120 return -EBUSY; 121 } 122 123 ns558 = kzalloc(sizeof(struct ns558), GFP_KERNEL); 124 port = gameport_allocate_port(); 125 if (!ns558 || !port) { 126 printk(KERN_ERR "ns558: Memory allocation failed.\n"); 127 release_region(io & (-1 << i), (1 << i)); 128 kfree(ns558); 129 gameport_free_port(port); 130 return -ENOMEM; 131 } 132 133 ns558->io = io; 134 ns558->size = 1 << i; 135 ns558->gameport = port; 136 137 port->io = io; 138 gameport_set_name(port, "NS558 ISA Gameport"); 139 gameport_set_phys(port, "isa%04x/gameport0", io & (-1 << i)); 140 141 gameport_register_port(port); 142 143 list_add(&ns558->node, &ns558_list); 144 145 return 0; 146 } 147 148 #ifdef CONFIG_PNP 149 150 static const struct pnp_device_id pnp_devids[] = { 151 { .id = "@P@0001", .driver_data = 0 }, /* ALS 100 */ 152 { .id = "@P@0020", .driver_data = 0 }, /* ALS 200 */ 153 { .id = "@P@1001", .driver_data = 0 }, /* ALS 100+ */ 154 { .id = "@P@2001", .driver_data = 0 }, /* ALS 120 */ 155 { .id = "ASB16fd", .driver_data = 0 }, /* AdLib NSC16 */ 156 { .id = "AZT3001", .driver_data = 0 }, /* AZT1008 */ 157 { .id = "CDC0001", .driver_data = 0 }, /* Opl3-SAx */ 158 { .id = "CSC0001", .driver_data = 0 }, /* CS4232 */ 159 { .id = "CSC000f", .driver_data = 0 }, /* CS4236 */ 160 { .id = "CSC0101", .driver_data = 0 }, /* CS4327 */ 161 { .id = "CTL7001", .driver_data = 0 }, /* SB16 */ 162 { .id = "CTL7002", .driver_data = 0 }, /* AWE64 */ 163 { .id = "CTL7005", .driver_data = 0 }, /* Vibra16 */ 164 { .id = "ENS2020", .driver_data = 0 }, /* SoundscapeVIVO */ 165 { .id = "ESS0001", .driver_data = 0 }, /* ES1869 */ 166 { .id = "ESS0005", .driver_data = 0 }, /* ES1878 */ 167 { .id = "ESS6880", .driver_data = 0 }, /* ES688 */ 168 { .id = "IBM0012", .driver_data = 0 }, /* CS4232 */ 169 { .id = "OPT0001", .driver_data = 0 }, /* OPTi Audio16 */ 170 { .id = "YMH0006", .driver_data = 0 }, /* Opl3-SA */ 171 { .id = "YMH0022", .driver_data = 0 }, /* Opl3-SAx */ 172 { .id = "PNPb02f", .driver_data = 0 }, /* Generic */ 173 { .id = "", }, 174 }; 175 176 MODULE_DEVICE_TABLE(pnp, pnp_devids); 177 178 static int ns558_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *did) 179 { 180 int ioport, iolen; 181 struct ns558 *ns558; 182 struct gameport *port; 183 184 if (!pnp_port_valid(dev, 0)) { 185 printk(KERN_WARNING "ns558: No i/o ports on a gameport? Weird\n"); 186 return -ENODEV; 187 } 188 189 ioport = pnp_port_start(dev, 0); 190 iolen = pnp_port_len(dev, 0); 191 192 if (!request_region(ioport, iolen, "ns558-pnp")) 193 return -EBUSY; 194 195 ns558 = kzalloc(sizeof(struct ns558), GFP_KERNEL); 196 port = gameport_allocate_port(); 197 if (!ns558 || !port) { 198 printk(KERN_ERR "ns558: Memory allocation failed\n"); 199 kfree(ns558); 200 gameport_free_port(port); 201 return -ENOMEM; 202 } 203 204 ns558->io = ioport; 205 ns558->size = iolen; 206 ns558->dev = dev; 207 ns558->gameport = port; 208 209 gameport_set_name(port, "NS558 PnP Gameport"); 210 gameport_set_phys(port, "pnp%s/gameport0", dev_name(&dev->dev)); 211 port->dev.parent = &dev->dev; 212 port->io = ioport; 213 214 gameport_register_port(port); 215 216 list_add_tail(&ns558->node, &ns558_list); 217 return 0; 218 } 219 220 static struct pnp_driver ns558_pnp_driver = { 221 .name = "ns558", 222 .id_table = pnp_devids, 223 .probe = ns558_pnp_probe, 224 }; 225 226 #else 227 228 static struct pnp_driver ns558_pnp_driver; 229 230 #endif 231 232 static int __init ns558_init(void) 233 { 234 int i = 0; 235 int error; 236 237 error = pnp_register_driver(&ns558_pnp_driver); 238 if (error && error != -ENODEV) /* should be ENOSYS really */ 239 return error; 240 241 /* 242 * Probe ISA ports after PnP, so that PnP ports that are already 243 * enabled get detected as PnP. This may be suboptimal in multi-device 244 * configurations, but saves hassle with simple setups. 245 */ 246 247 while (ns558_isa_portlist[i]) 248 ns558_isa_probe(ns558_isa_portlist[i++]); 249 250 return list_empty(&ns558_list) && error ? -ENODEV : 0; 251 } 252 253 static void __exit ns558_exit(void) 254 { 255 struct ns558 *ns558, *safe; 256 257 list_for_each_entry_safe(ns558, safe, &ns558_list, node) { 258 gameport_unregister_port(ns558->gameport); 259 release_region(ns558->io & ~(ns558->size - 1), ns558->size); 260 kfree(ns558); 261 } 262 263 pnp_unregister_driver(&ns558_pnp_driver); 264 } 265 266 module_init(ns558_init); 267 module_exit(ns558_exit); 268