linux/drivers/net/arcnet/com20020_cs.c
<<
>>
Prefs
   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/ptrace.h>
  36#include <linux/slab.h>
  37#include <linux/string.h>
  38#include <linux/timer.h>
  39#include <linux/delay.h>
  40#include <linux/module.h>
  41#include <linux/netdevice.h>
  42#include <linux/arcdevice.h>
  43#include <linux/com20020.h>
  44
  45#include <pcmcia/cistpl.h>
  46#include <pcmcia/ds.h>
  47
  48#include <asm/io.h>
  49
  50#define VERSION "arcnet: COM20020 PCMCIA support loaded.\n"
  51
  52
  53static void regdump(struct net_device *dev)
  54{
  55#ifdef DEBUG
  56    int ioaddr = dev->base_addr;
  57    int count;
  58    
  59    netdev_dbg(dev, "register dump:\n");
  60    for (count = ioaddr; count < ioaddr + 16; count++)
  61    {
  62        if (!(count % 16))
  63            pr_cont("%04X:", count);
  64        pr_cont(" %02X", inb(count));
  65    }
  66    pr_cont("\n");
  67    
  68    netdev_dbg(dev, "buffer0 dump:\n");
  69        /* set up the address register */
  70        count = 0;
  71        outb((count >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI);
  72        outb(count & 0xff, _ADDR_LO);
  73    
  74    for (count = 0; count < 256+32; count++)
  75    {
  76        if (!(count % 16))
  77            pr_cont("%04X:", count);
  78        
  79        /* copy the data */
  80        pr_cont(" %02X", inb(_MEMDATA));
  81    }
  82    pr_cont("\n");
  83#endif
  84}
  85
  86
  87
  88/*====================================================================*/
  89
  90/* Parameters that can be set with 'insmod' */
  91
  92static int node;
  93static int timeout = 3;
  94static int backplane;
  95static int clockp;
  96static int clockm;
  97
  98module_param(node, int, 0);
  99module_param(timeout, int, 0);
 100module_param(backplane, int, 0);
 101module_param(clockp, int, 0);
 102module_param(clockm, int, 0);
 103
 104MODULE_LICENSE("GPL");
 105
 106/*====================================================================*/
 107
 108static int com20020_config(struct pcmcia_device *link);
 109static void com20020_release(struct pcmcia_device *link);
 110
 111static void com20020_detach(struct pcmcia_device *p_dev);
 112
 113/*====================================================================*/
 114
 115static int com20020_probe(struct pcmcia_device *p_dev)
 116{
 117    struct com20020_dev *info;
 118    struct net_device *dev;
 119    struct arcnet_local *lp;
 120
 121    dev_dbg(&p_dev->dev, "com20020_attach()\n");
 122
 123    /* Create new network device */
 124    info = kzalloc(sizeof(*info), GFP_KERNEL);
 125    if (!info)
 126        goto fail_alloc_info;
 127
 128    dev = alloc_arcdev("");
 129    if (!dev)
 130        goto fail_alloc_dev;
 131
 132    lp = netdev_priv(dev);
 133    lp->timeout = timeout;
 134    lp->backplane = backplane;
 135    lp->clockp = clockp;
 136    lp->clockm = clockm & 3;
 137    lp->hw.owner = THIS_MODULE;
 138
 139    /* fill in our module parameters as defaults */
 140    dev->dev_addr[0] = node;
 141
 142    p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
 143    p_dev->resource[0]->end = 16;
 144    p_dev->config_flags |= CONF_ENABLE_IRQ;
 145
 146    info->dev = dev;
 147    p_dev->priv = info;
 148
 149    return com20020_config(p_dev);
 150
 151fail_alloc_dev:
 152    kfree(info);
 153fail_alloc_info:
 154    return -ENOMEM;
 155} /* com20020_attach */
 156
 157static void com20020_detach(struct pcmcia_device *link)
 158{
 159    struct com20020_dev *info = link->priv;
 160    struct net_device *dev = info->dev;
 161
 162    dev_dbg(&link->dev, "detach...\n");
 163
 164    dev_dbg(&link->dev, "com20020_detach\n");
 165
 166    dev_dbg(&link->dev, "unregister...\n");
 167
 168    unregister_netdev(dev);
 169
 170    /*
 171     * this is necessary because we register our IRQ separately
 172     * from card services.
 173     */
 174    if (dev->irq)
 175            free_irq(dev->irq, dev);
 176
 177    com20020_release(link);
 178
 179    /* Unlink device structure, free bits */
 180    dev_dbg(&link->dev, "unlinking...\n");
 181    if (link->priv)
 182    {
 183        dev = info->dev;
 184        if (dev)
 185        {
 186            dev_dbg(&link->dev, "kfree...\n");
 187            free_netdev(dev);
 188        }
 189        dev_dbg(&link->dev, "kfree2...\n");
 190        kfree(info);
 191    }
 192
 193} /* com20020_detach */
 194
 195static int com20020_config(struct pcmcia_device *link)
 196{
 197    struct arcnet_local *lp;
 198    struct com20020_dev *info;
 199    struct net_device *dev;
 200    int i, ret;
 201    int ioaddr;
 202
 203    info = link->priv;
 204    dev = info->dev;
 205
 206    dev_dbg(&link->dev, "config...\n");
 207
 208    dev_dbg(&link->dev, "com20020_config\n");
 209
 210    dev_dbg(&link->dev, "baseport1 is %Xh\n",
 211            (unsigned int) link->resource[0]->start);
 212
 213    i = -ENODEV;
 214    link->io_lines = 16;
 215
 216    if (!link->resource[0]->start)
 217    {
 218        for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
 219        {
 220            link->resource[0]->start = ioaddr;
 221            i = pcmcia_request_io(link);
 222            if (i == 0)
 223                break;
 224        }
 225    }
 226    else
 227        i = pcmcia_request_io(link);
 228    
 229    if (i != 0)
 230    {
 231        dev_dbg(&link->dev, "requestIO failed totally!\n");
 232        goto failed;
 233    }
 234        
 235    ioaddr = dev->base_addr = link->resource[0]->start;
 236    dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr);
 237
 238    dev_dbg(&link->dev, "request IRQ %d\n",
 239            link->irq);
 240    if (!link->irq)
 241    {
 242        dev_dbg(&link->dev, "requestIRQ failed totally!\n");
 243        goto failed;
 244    }
 245
 246    dev->irq = link->irq;
 247
 248    ret = pcmcia_enable_device(link);
 249    if (ret)
 250            goto failed;
 251
 252    if (com20020_check(dev))
 253    {
 254        regdump(dev);
 255        goto failed;
 256    }
 257    
 258    lp = netdev_priv(dev);
 259    lp->card_name = "PCMCIA COM20020";
 260    lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
 261
 262    SET_NETDEV_DEV(dev, &link->dev);
 263
 264    i = com20020_found(dev, 0); /* calls register_netdev */
 265    
 266    if (i != 0) {
 267        dev_notice(&link->dev,
 268                   "com20020_found() failed\n");
 269        goto failed;
 270    }
 271
 272    netdev_dbg(dev, "port %#3lx, irq %d\n",
 273               dev->base_addr, dev->irq);
 274    return 0;
 275
 276failed:
 277    dev_dbg(&link->dev, "com20020_config failed...\n");
 278    com20020_release(link);
 279    return -ENODEV;
 280} /* com20020_config */
 281
 282static void com20020_release(struct pcmcia_device *link)
 283{
 284        dev_dbg(&link->dev, "com20020_release\n");
 285        pcmcia_disable_device(link);
 286}
 287
 288static int com20020_suspend(struct pcmcia_device *link)
 289{
 290        struct com20020_dev *info = link->priv;
 291        struct net_device *dev = info->dev;
 292
 293        if (link->open)
 294                netif_device_detach(dev);
 295
 296        return 0;
 297}
 298
 299static int com20020_resume(struct pcmcia_device *link)
 300{
 301        struct com20020_dev *info = link->priv;
 302        struct net_device *dev = info->dev;
 303
 304        if (link->open) {
 305                int ioaddr = dev->base_addr;
 306                struct arcnet_local *lp = netdev_priv(dev);
 307                ARCRESET;
 308        }
 309
 310        return 0;
 311}
 312
 313static const struct pcmcia_device_id com20020_ids[] = {
 314        PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.",
 315                        "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf),
 316        PCMCIA_DEVICE_PROD_ID12("SoHard AG",
 317                        "SH ARC PCMCIA", 0xf8991729, 0x69dff0c7),
 318        PCMCIA_DEVICE_NULL
 319};
 320MODULE_DEVICE_TABLE(pcmcia, com20020_ids);
 321
 322static struct pcmcia_driver com20020_cs_driver = {
 323        .owner          = THIS_MODULE,
 324        .name           = "com20020_cs",
 325        .probe          = com20020_probe,
 326        .remove         = com20020_detach,
 327        .id_table       = com20020_ids,
 328        .suspend        = com20020_suspend,
 329        .resume         = com20020_resume,
 330};
 331module_pcmcia_driver(com20020_cs_driver);
 332