1 /* 2 * Linux ARCnet driver - COM20020 PCMCIA support 3 * 4 * Written 1994-1999 by Avery Pennarun, 5 * based on an ISA version by David Woodhouse. 6 * Derived from ibmtr_cs.c by Steve Kipisz (pcmcia-cs 3.1.4) 7 * which was derived from pcnet_cs.c by David Hinds. 8 * Some additional portions derived from skeleton.c by Donald Becker. 9 * 10 * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com) 11 * for sponsoring the further development of this driver. 12 * 13 * ********************** 14 * 15 * The original copyright of skeleton.c was as follows: 16 * 17 * skeleton.c Written 1993 by Donald Becker. 18 * Copyright 1993 United States Government as represented by the 19 * Director, National Security Agency. This software may only be used 20 * and distributed according to the terms of the GNU General Public License as 21 * modified by SRC, incorporated herein by reference. 22 * 23 * ********************** 24 * Changes: 25 * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/08/2000 26 * - reorganize kmallocs in com20020_attach, checking all for failure 27 * and releasing the previous allocations if one fails 28 * ********************** 29 * 30 * For more details, see drivers/net/arcnet.c 31 * 32 * ********************** 33 */ 34 #include <linux/kernel.h> 35 #include <linux/init.h> 36 #include <linux/ptrace.h> 37 #include <linux/slab.h> 38 #include <linux/string.h> 39 #include <linux/timer.h> 40 #include <linux/delay.h> 41 #include <linux/module.h> 42 #include <linux/netdevice.h> 43 #include <linux/arcdevice.h> 44 #include <linux/com20020.h> 45 46 #include <pcmcia/cistpl.h> 47 #include <pcmcia/ds.h> 48 49 #include <asm/io.h> 50 #include <asm/system.h> 51 52 #define VERSION "arcnet: COM20020 PCMCIA support loaded.\n" 53 54 55 static void regdump(struct net_device *dev) 56 { 57 #ifdef DEBUG 58 int ioaddr = dev->base_addr; 59 int count; 60 61 netdev_dbg(dev, "register dump:\n"); 62 for (count = ioaddr; count < ioaddr + 16; count++) 63 { 64 if (!(count % 16)) 65 pr_cont("%04X:", count); 66 pr_cont(" %02X", inb(count)); 67 } 68 pr_cont("\n"); 69 70 netdev_dbg(dev, "buffer0 dump:\n"); 71 /* set up the address register */ 72 count = 0; 73 outb((count >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI); 74 outb(count & 0xff, _ADDR_LO); 75 76 for (count = 0; count < 256+32; count++) 77 { 78 if (!(count % 16)) 79 pr_cont("%04X:", count); 80 81 /* copy the data */ 82 pr_cont(" %02X", inb(_MEMDATA)); 83 } 84 pr_cont("\n"); 85 #endif 86 } 87 88 89 90 /*====================================================================*/ 91 92 /* Parameters that can be set with 'insmod' */ 93 94 static int node; 95 static int timeout = 3; 96 static int backplane; 97 static int clockp; 98 static int clockm; 99 100 module_param(node, int, 0); 101 module_param(timeout, int, 0); 102 module_param(backplane, int, 0); 103 module_param(clockp, int, 0); 104 module_param(clockm, int, 0); 105 106 MODULE_LICENSE("GPL"); 107 108 /*====================================================================*/ 109 110 static int com20020_config(struct pcmcia_device *link); 111 static void com20020_release(struct pcmcia_device *link); 112 113 static void com20020_detach(struct pcmcia_device *p_dev); 114 115 /*====================================================================*/ 116 117 typedef struct com20020_dev_t { 118 struct net_device *dev; 119 } com20020_dev_t; 120 121 static int com20020_probe(struct pcmcia_device *p_dev) 122 { 123 com20020_dev_t *info; 124 struct net_device *dev; 125 struct arcnet_local *lp; 126 127 dev_dbg(&p_dev->dev, "com20020_attach()\n"); 128 129 /* Create new network device */ 130 info = kzalloc(sizeof(struct com20020_dev_t), GFP_KERNEL); 131 if (!info) 132 goto fail_alloc_info; 133 134 dev = alloc_arcdev(""); 135 if (!dev) 136 goto fail_alloc_dev; 137 138 lp = netdev_priv(dev); 139 lp->timeout = timeout; 140 lp->backplane = backplane; 141 lp->clockp = clockp; 142 lp->clockm = clockm & 3; 143 lp->hw.owner = THIS_MODULE; 144 145 /* fill in our module parameters as defaults */ 146 dev->dev_addr[0] = node; 147 148 p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; 149 p_dev->resource[0]->end = 16; 150 p_dev->config_flags |= CONF_ENABLE_IRQ; 151 152 info->dev = dev; 153 p_dev->priv = info; 154 155 return com20020_config(p_dev); 156 157 fail_alloc_dev: 158 kfree(info); 159 fail_alloc_info: 160 return -ENOMEM; 161 } /* com20020_attach */ 162 163 static void com20020_detach(struct pcmcia_device *link) 164 { 165 struct com20020_dev_t *info = link->priv; 166 struct net_device *dev = info->dev; 167 168 dev_dbg(&link->dev, "detach...\n"); 169 170 dev_dbg(&link->dev, "com20020_detach\n"); 171 172 dev_dbg(&link->dev, "unregister...\n"); 173 174 unregister_netdev(dev); 175 176 /* 177 * this is necessary because we register our IRQ separately 178 * from card services. 179 */ 180 if (dev->irq) 181 free_irq(dev->irq, dev); 182 183 com20020_release(link); 184 185 /* Unlink device structure, free bits */ 186 dev_dbg(&link->dev, "unlinking...\n"); 187 if (link->priv) 188 { 189 dev = info->dev; 190 if (dev) 191 { 192 dev_dbg(&link->dev, "kfree...\n"); 193 free_netdev(dev); 194 } 195 dev_dbg(&link->dev, "kfree2...\n"); 196 kfree(info); 197 } 198 199 } /* com20020_detach */ 200 201 static int com20020_config(struct pcmcia_device *link) 202 { 203 struct arcnet_local *lp; 204 com20020_dev_t *info; 205 struct net_device *dev; 206 int i, ret; 207 int ioaddr; 208 209 info = link->priv; 210 dev = info->dev; 211 212 dev_dbg(&link->dev, "config...\n"); 213 214 dev_dbg(&link->dev, "com20020_config\n"); 215 216 dev_dbg(&link->dev, "baseport1 is %Xh\n", 217 (unsigned int) link->resource[0]->start); 218 219 i = -ENODEV; 220 link->io_lines = 16; 221 222 if (!link->resource[0]->start) 223 { 224 for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10) 225 { 226 link->resource[0]->start = ioaddr; 227 i = pcmcia_request_io(link); 228 if (i == 0) 229 break; 230 } 231 } 232 else 233 i = pcmcia_request_io(link); 234 235 if (i != 0) 236 { 237 dev_dbg(&link->dev, "requestIO failed totally!\n"); 238 goto failed; 239 } 240 241 ioaddr = dev->base_addr = link->resource[0]->start; 242 dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr); 243 244 dev_dbg(&link->dev, "request IRQ %d\n", 245 link->irq); 246 if (!link->irq) 247 { 248 dev_dbg(&link->dev, "requestIRQ failed totally!\n"); 249 goto failed; 250 } 251 252 dev->irq = link->irq; 253 254 ret = pcmcia_enable_device(link); 255 if (ret) 256 goto failed; 257 258 if (com20020_check(dev)) 259 { 260 regdump(dev); 261 goto failed; 262 } 263 264 lp = netdev_priv(dev); 265 lp->card_name = "PCMCIA COM20020"; 266 lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */ 267 268 SET_NETDEV_DEV(dev, &link->dev); 269 270 i = com20020_found(dev, 0); /* calls register_netdev */ 271 272 if (i != 0) { 273 dev_notice(&link->dev, 274 "com20020_found() failed\n"); 275 goto failed; 276 } 277 278 netdev_dbg(dev, "port %#3lx, irq %d\n", 279 dev->base_addr, dev->irq); 280 return 0; 281 282 failed: 283 dev_dbg(&link->dev, "com20020_config failed...\n"); 284 com20020_release(link); 285 return -ENODEV; 286 } /* com20020_config */ 287 288 static void com20020_release(struct pcmcia_device *link) 289 { 290 dev_dbg(&link->dev, "com20020_release\n"); 291 pcmcia_disable_device(link); 292 } 293 294 static int com20020_suspend(struct pcmcia_device *link) 295 { 296 com20020_dev_t *info = link->priv; 297 struct net_device *dev = info->dev; 298 299 if (link->open) 300 netif_device_detach(dev); 301 302 return 0; 303 } 304 305 static int com20020_resume(struct pcmcia_device *link) 306 { 307 com20020_dev_t *info = link->priv; 308 struct net_device *dev = info->dev; 309 310 if (link->open) { 311 int ioaddr = dev->base_addr; 312 struct arcnet_local *lp = netdev_priv(dev); 313 ARCRESET; 314 } 315 316 return 0; 317 } 318 319 static const struct pcmcia_device_id com20020_ids[] = { 320 PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.", 321 "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf), 322 PCMCIA_DEVICE_PROD_ID12("SoHard AG", 323 "SH ARC PCMCIA", 0xf8991729, 0x69dff0c7), 324 PCMCIA_DEVICE_NULL 325 }; 326 MODULE_DEVICE_TABLE(pcmcia, com20020_ids); 327 328 static struct pcmcia_driver com20020_cs_driver = { 329 .owner = THIS_MODULE, 330 .name = "com20020_cs", 331 .probe = com20020_probe, 332 .remove = com20020_detach, 333 .id_table = com20020_ids, 334 .suspend = com20020_suspend, 335 .resume = com20020_resume, 336 }; 337 338 static int __init init_com20020_cs(void) 339 { 340 return pcmcia_register_driver(&com20020_cs_driver); 341 } 342 343 static void __exit exit_com20020_cs(void) 344 { 345 pcmcia_unregister_driver(&com20020_cs_driver); 346 } 347 348 module_init(init_com20020_cs); 349 module_exit(exit_com20020_cs); 350