Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / net / wireless / atmel_cs.c
index a4ed28d..26bf112 100644 (file)
@@ -43,7 +43,6 @@
 #include <linux/moduleparam.h>
 #include <linux/device.h>
 
-#include <pcmcia/version.h>
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
 #include <pcmcia/cistpl.h>
@@ -64,6 +63,7 @@
    be present but disabled -- but it can then be enabled for specific
    modules at load time with a 'pc_debug=#' option to insmod.
 */
+
 #ifdef PCMCIA_DEBUG
 static int pc_debug = PCMCIA_DEBUG;
 module_param(pc_debug, int, 0);
@@ -91,10 +91,8 @@ MODULE_SUPPORTED_DEVICE("Atmel at76c50x PCMCIA cards");
    event handler. 
 */
 
-static void atmel_config(dev_link_t *link);
-static void atmel_release(dev_link_t *link);
-static int atmel_event(event_t event, int priority,
-                      event_callback_args_t *args);
+static int atmel_config(struct pcmcia_device *link);
+static void atmel_release(struct pcmcia_device *link);
 
 /*
    The attach() and detach() entry points are used to create and destroy
@@ -102,8 +100,7 @@ static int atmel_event(event_t event, int priority,
    needed to manage one actual PCMCIA card.
 */
 
-static dev_link_t *atmel_attach(void);
-static void atmel_detach(dev_link_t *);
+static void atmel_detach(struct pcmcia_device *p_dev);
 
 /*
    You'll also need to prototype all the functions that will actually
@@ -112,39 +109,23 @@ static void atmel_detach(dev_link_t *);
    less on other parts of the kernel.
 */
 
-/*
-   The dev_info variable is the "key" that is used to match up this
-   device driver with appropriate cards, through the card configuration
-   database.
-*/
-
-static dev_info_t dev_info = "atmel_cs";
-
 /*
    A linked list of "instances" of the  atmelnet device.  Each actual
    PCMCIA card corresponds to one device instance, and is described
-   by one dev_link_t structure (defined in ds.h).
+   by one struct pcmcia_device structure (defined in ds.h).
 
    You may not want to use a linked list for this -- for example, the
-   memory card driver uses an array of dev_link_t pointers, where minor
+   memory card driver uses an array of struct pcmcia_device pointers, where minor
    device numbers are used to derive the corresponding array index.
 */
 
-static dev_link_t *dev_list = NULL;
-
 /*
-   A dev_link_t structure has fields for most things that are needed
-   to keep track of a socket, but there will usually be some device
-   specific information that also needs to be kept track of.  The
-   'priv' pointer in a dev_link_t structure can be used to point to
-   a device-specific private data structure, like this.
-
    A driver needs to provide a dev_node_t structure for each device
    on a card.  In some cases, there is only one device per card (for
    example, ethernet cards, modems).  In other cases, there may be
    many actual or logical devices (SCSI adapters, memory cards with
    multiple partitions).  The dev_node_t structures need to be kept
-   in a linked list starting at the 'dev' field of a dev_link_t
+   in a linked list starting at the 'dev' field of a struct pcmcia_device
    structure.  We allocate them in the card's private data structure,
    because they generally shouldn't be allocated dynamically.
 
@@ -171,28 +152,17 @@ typedef struct local_info_t {
   
   ======================================================================*/
 
-static dev_link_t *atmel_attach(void)
+static int atmel_probe(struct pcmcia_device *p_dev)
 {
-       client_reg_t client_reg;
-       dev_link_t *link;
        local_info_t *local;
-       int ret;
-       
+
        DEBUG(0, "atmel_attach()\n");
 
-       /* Initialize the dev_link_t structure */
-       link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
-       if (!link) {
-               printk(KERN_ERR "atmel_cs: no memory for new device\n");
-               return NULL;
-       }
-       memset(link, 0, sizeof(struct dev_link_t));
-       
        /* Interrupt setup */
-       link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
-       link->irq.IRQInfo1 = IRQ_LEVEL_ID;
-       link->irq.Handler = NULL;
-       
+       p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+       p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+       p_dev->irq.Handler = NULL;
+
        /*
          General socket configuration defaults can go here.  In this
          client, we assume very little, and rely on the CIS for almost
@@ -200,39 +170,18 @@ static dev_link_t *atmel_attach(void)
          and attributes of IO windows) are fixed by the nature of the
          device, and can be hard-wired here.
        */
-       link->conf.Attributes = 0;
-       link->conf.Vcc = 50;
-       link->conf.IntType = INT_MEMORY_AND_IO;
-       
+       p_dev->conf.Attributes = 0;
+       p_dev->conf.IntType = INT_MEMORY_AND_IO;
+
        /* Allocate space for private device-specific data */
-       local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+       local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
        if (!local) {
                printk(KERN_ERR "atmel_cs: no memory for new device\n");
-               kfree (link);
-               return NULL;
+               return -ENOMEM;
        }
-       memset(local, 0, sizeof(local_info_t));
-       link->priv = local;
-       
-       /* Register with Card Services */
-       link->next = dev_list;
-       dev_list = link;
-       client_reg.dev_info = &dev_info;
-       client_reg.EventMask =
-               CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
-               CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
-               CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
-       client_reg.event_handler = &atmel_event;
-       client_reg.Version = 0x0210;
-       client_reg.event_callback_args.client_data = link;
-       ret = pcmcia_register_client(&link->handle, &client_reg);
-       if (ret != 0) {
-               cs_error(link->handle, RegisterClient, ret);
-               atmel_detach(link);
-               return NULL;
-       }
-       
-       return link;
+       p_dev->priv = local;
+
+       return atmel_config(p_dev);
 } /* atmel_attach */
 
 /*======================================================================
@@ -244,30 +193,13 @@ static dev_link_t *atmel_attach(void)
   
   ======================================================================*/
 
-static void atmel_detach(dev_link_t *link)
+static void atmel_detach(struct pcmcia_device *link)
 {
-       dev_link_t **linkp;
-       
        DEBUG(0, "atmel_detach(0x%p)\n", link);
-       
-       /* Locate device structure */
-       for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
-               if (*linkp == link) break;
-       if (*linkp == NULL)
-               return;
-
-       if (link->state & DEV_CONFIG)
-               atmel_release(link);
-               
-       /* Break the link with Card Services */
-       if (link->handle)
-               pcmcia_deregister_client(link->handle);
-
-       /* Unlink device structure, free pieces */
-       *linkp = link->next;
-       if (link->priv)
-               kfree(link->priv);
-       kfree(link);
+
+       atmel_release(link);
+
+       kfree(link->priv);
 }
 
 /*======================================================================
@@ -284,63 +216,26 @@ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 /* Call-back function to interrogate PCMCIA-specific information
    about the current existance of the card */
 static int card_present(void *arg)
-{ 
-       dev_link_t *link = (dev_link_t *)arg;
-       if (link->state & DEV_SUSPEND)
-               return 0;
-       else if (link->state & DEV_PRESENT)
+{
+       struct pcmcia_device *link = (struct pcmcia_device *)arg;
+
+       if (pcmcia_dev_present(link))
                return 1;
-       
+
        return 0;
 }
 
-/* list of cards we know about and their firmware requirements.
-   Go either by Manfid or version strings.
-   Cards not in this list will need a firmware parameter to the module
-   in all probability. Note that the SMC 2632 V2 and V3 have the same
-   manfids, so we ignore those and use the version1 strings. */
-
-static struct { 
-       int manf, card;
-       char *ver1;
-       AtmelFWType firmware;
-       char *name;
-} card_table[] = {
-       { 0, 0, "WLAN/802.11b PC CARD", ATMEL_FW_TYPE_502D, "Actiontec 802CAT1" },  
-       { 0, 0, "ATMEL/AT76C502AR", ATMEL_FW_TYPE_502, "NoName-RFMD" }, 
-       { 0, 0, "ATMEL/AT76C502AR_D", ATMEL_FW_TYPE_502D, "NoName-revD" }, 
-       { 0, 0, "ATMEL/AT76C502AR_E", ATMEL_FW_TYPE_502E, "NoName-revE" },
-       { 0, 0, "ATMEL/AT76C504", ATMEL_FW_TYPE_504, "NoName-504" },
-       { 0, 0, "ATMEL/AT76C504A", ATMEL_FW_TYPE_504A_2958, "NoName-504a-2958" },
-       { 0, 0, "ATMEL/AT76C504_R", ATMEL_FW_TYPE_504_2958, "NoName-504-2958" },
-       { MANFID_3COM, 0x0620, NULL, ATMEL_FW_TYPE_502_3COM, "3com 3CRWE62092B" }, 
-       { MANFID_3COM, 0x0696, NULL, ATMEL_FW_TYPE_502_3COM, "3com 3CRSHPW196" }, 
-       { 0, 0, "SMC/2632W-V2", ATMEL_FW_TYPE_502, "SMC 2632W-V2" },
-       { 0, 0, "SMC/2632W", ATMEL_FW_TYPE_502D, "SMC 2632W-V3" },
-       { 0xd601, 0x0007, NULL, ATMEL_FW_TYPE_502, "Sitecom WLAN-011" }, 
-       { 0x01bf, 0x3302, NULL, ATMEL_FW_TYPE_502E, "Belkin F5D6020-V2" }, 
-       { 0, 0, "BT/Voyager 1020 Laptop Adapter", ATMEL_FW_TYPE_502, "BT Voyager 1020" },
-       { 0, 0, "IEEE 802.11b/Wireless LAN PC Card", ATMEL_FW_TYPE_502, "Siemens Gigaset PC Card II" },
-       { 0, 0, "IEEE 802.11b/Wireless LAN Card S", ATMEL_FW_TYPE_504_2958, "Siemens Gigaset PC Card II" },
-       { 0, 0, "CNet/CNWLC 11Mbps Wireless PC Card V-5", ATMEL_FW_TYPE_502E, "CNet CNWLC-811ARL" },
-       { 0, 0, "Wireless/PC_CARD", ATMEL_FW_TYPE_502D, "Planet WL-3552" },
-       { 0, 0, "OEM/11Mbps Wireless LAN PC Card V-3", ATMEL_FW_TYPE_502, "OEM 11Mbps WLAN PCMCIA Card" },
-       { 0, 0, "11WAVE/11WP611AL-E", ATMEL_FW_TYPE_502E, "11WAVE WaveBuddy" },
-       { 0, 0, "LG/LW2100N", ATMEL_FW_TYPE_502E, "LG LW2100N 11Mbps WLAN PCMCIA Card" },
-};
-
-static void atmel_config(dev_link_t *link)
+static int atmel_config(struct pcmcia_device *link)
 {
-       client_handle_t handle;
        tuple_t tuple;
        cisparse_t parse;
        local_info_t *dev;
        int last_fn, last_ret;
        u_char buf[64];
-       int card_index = -1, done = 0;
-       
-       handle = link->handle;
+       struct pcmcia_device_id *did;
+
        dev = link->priv;
+       did = handle_to_dev(link).driver_data;
 
        DEBUG(0, "atmel_config(0x%p)\n", link);
        
@@ -349,73 +244,17 @@ static void atmel_config(dev_link_t *link)
        tuple.TupleDataMax = sizeof(buf);
        tuple.TupleOffset = 0;
        
-       tuple.DesiredTuple = CISTPL_MANFID;
-       if (pcmcia_get_first_tuple(handle, &tuple) == 0) {
-               int i;
-               cistpl_manfid_t *manfid;
-               CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-               CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
-               manfid = &(parse.manfid);
-               for (i = 0; i < sizeof(card_table)/sizeof(card_table[0]); i++) {
-                       if (!card_table[i].ver1 &&
-                           manfid->manf == card_table[i].manf &&
-                           manfid->card == card_table[i].card) {
-                               card_index = i;
-                               done = 1;
-                       }
-               }
-       }
-
-       tuple.DesiredTuple = CISTPL_VERS_1;
-       if (!done && (pcmcia_get_first_tuple(handle, &tuple) == 0)) {
-               int i, j, k;
-               cistpl_vers_1_t *ver1;
-               CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-               CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
-               ver1 = &(parse.version_1);
-               
-               for (i = 0; i < sizeof(card_table)/sizeof(card_table[0]); i++) {
-                       for (j = 0; j < ver1->ns; j++) {
-                               char *p = card_table[i].ver1;
-                               char *q = &ver1->str[ver1->ofs[j]];
-                               if (!p)
-                                       goto mismatch;
-                               for (k = 0; k < j; k++) {
-                                       while ((*p != '\0') && (*p != '/')) p++;
-                                       if (*p == '\0') {
-                                               if (*q != '\0')
-                                                       goto mismatch;
-                                       } else {
-                                               p++;
-                                       }
-                               }
-                               while((*q != '\0') && (*p != '\0') && 
-                                     (*p != '/') && (*p == *q)) p++, q++;
-                               if (((*p != '\0') && *p != '/') || *q != '\0')
-                                       goto mismatch;
-                       }
-                       card_index = i;
-                       break;  /* done */
-                       
-               mismatch:
-                       j = 0; /* dummy stmt to shut up compiler */
-               }
-       }               
-
        /*
          This reads the card's CONFIG tuple to find its configuration
          registers.
        */
        tuple.DesiredTuple = CISTPL_CONFIG;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
-       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
-       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
        link->conf.ConfigBase = parse.config.base;
        link->conf.Present = parse.config.rmask[0];
-       
-       /* Configure card */
-       link->state |= DEV_CONFIG;
-       
+
        /*
          In this loop, we scan the CIS for configuration table entries,
          each of which describes a valid card configuration, including
@@ -429,12 +268,12 @@ static void atmel_config(dev_link_t *link)
          will only use the CIS to fill in implementation-defined details.
        */
        tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
        while (1) {
                cistpl_cftable_entry_t dflt = { 0 };
                cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
-               if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
-                               pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+               if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
+                               pcmcia_parse_tuple(link, &tuple, &parse) != 0)
                        goto next_entry;
                
                if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
@@ -449,16 +288,11 @@ static void atmel_config(dev_link_t *link)
                
                /* Use power settings for Vcc and Vpp if present */
                /*  Note that the CIS values need to be rescaled */
-               if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM))
-                       link->conf.Vcc = cfg->vcc.param[CISTPL_POWER_VNOM]/10000;
-               else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM))
-                       link->conf.Vcc = dflt.vcc.param[CISTPL_POWER_VNOM]/10000;
-               
                if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                                cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
                else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
-                       link->conf.Vpp1 = link->conf.Vpp2 =
+                       link->conf.Vpp =
                                dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
                
                /* Do we need to allocate an interrupt? */
@@ -484,14 +318,14 @@ static void atmel_config(dev_link_t *link)
                }
                
                /* This reserves IO space but doesn't actually enable it */
-               if (pcmcia_request_io(link->handle, &link->io) != 0)
+               if (pcmcia_request_io(link, &link->io) != 0)
                        goto next_entry;
 
                /* If we got this far, we're cool! */
                break;
                
        next_entry:
-               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+               CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
        }
        
        /*
@@ -500,14 +334,14 @@ static void atmel_config(dev_link_t *link)
          irq structure is initialized.
        */
        if (link->conf.Attributes & CONF_ENABLE_IRQ)
-               CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+               CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
        
        /*
          This actually configures the PCMCIA socket -- setting up
          the I/O windows and the interrupt mapping, and putting the
          card and host interface into "Memory and IO" mode.
        */
-       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+       CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
        
        if (link->irq.AssignedIRQ == 0) {
                printk(KERN_ALERT 
@@ -518,46 +352,28 @@ static void atmel_config(dev_link_t *link)
        ((local_info_t*)link->priv)->eth_dev = 
                init_atmel_card(link->irq.AssignedIRQ,
                                link->io.BasePort1,
-                               card_index == -1 ? ATMEL_FW_TYPE_NONE :  card_table[card_index].firmware,
-                               &handle_to_dev(handle),
+                               did ? did->driver_info : ATMEL_FW_TYPE_NONE,
+                               &handle_to_dev(link),
                                card_present, 
                                link);
        if (!((local_info_t*)link->priv)->eth_dev) 
-               goto cs_failed;
+                       goto cs_failed;
+       
        
        /*
          At this point, the dev_node_t structure(s) need to be
-         initialized and arranged in a linked list at link->dev.
+         initialized and arranged in a linked list at link->dev_node.
        */
        strcpy(dev->node.dev_name, ((local_info_t*)link->priv)->eth_dev->name );
        dev->node.major = dev->node.minor = 0;
-       link->dev = &dev->node;
-       
-       /* Finally, report what we've done */
-       printk(KERN_INFO "%s: %s%sindex 0x%02x: Vcc %d.%d",
-              dev->node.dev_name,
-              card_index == -1 ? "" :  card_table[card_index].name,
-              card_index == -1 ? "" : " ",
-              link->conf.ConfigIndex,
-              link->conf.Vcc/10, link->conf.Vcc%10);
-       if (link->conf.Vpp1)
-               printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
-       if (link->conf.Attributes & CONF_ENABLE_IRQ)
-               printk(", irq %d", link->irq.AssignedIRQ);
-       if (link->io.NumPorts1)
-               printk(", io 0x%04x-0x%04x", link->io.BasePort1,
-                      link->io.BasePort1+link->io.NumPorts1-1);
-       if (link->io.NumPorts2)
-               printk(" & 0x%04x-0x%04x", link->io.BasePort2,
-                      link->io.BasePort2+link->io.NumPorts2-1);
-       printk("\n");
-       
-       link->state &= ~DEV_CONFIG_PENDING;
-       return;
-       
+       link->dev_node = &dev->node;
+
+       return 0;
+
  cs_failed:
-       cs_error(link->handle, last_fn, last_ret);
+       cs_error(link, last_fn, last_ret);
        atmel_release(link);
+       return -ENODEV;
 }
 
 /*======================================================================
@@ -568,91 +384,92 @@ static void atmel_config(dev_link_t *link)
   
   ======================================================================*/
 
-static void atmel_release(dev_link_t *link)
+static void atmel_release(struct pcmcia_device *link)
 {
        struct net_device *dev = ((local_info_t*)link->priv)->eth_dev;
-               
+
        DEBUG(0, "atmel_release(0x%p)\n", link);
-       
-       /* Unlink the device chain */
-       link->dev = NULL;
-       
-       if (dev) 
-               stop_atmel_card(dev, 0);
-       ((local_info_t*)link->priv)->eth_dev = NULL; 
-       
-       /* Don't bother checking to see if these succeed or not */
-       pcmcia_release_configuration(link->handle);
-       if (link->io.NumPorts1)
-               pcmcia_release_io(link->handle, &link->io);
-       if (link->irq.AssignedIRQ)
-               pcmcia_release_irq(link->handle, &link->irq);
-       link->state &= ~DEV_CONFIG;
+
+       if (dev)
+               stop_atmel_card(dev);
+       ((local_info_t*)link->priv)->eth_dev = NULL;
+
+       pcmcia_disable_device(link);
 }
 
-/*======================================================================
-  
-  The card status event handler.  Mostly, this schedules other
-  stuff to run after an event is received.
+static int atmel_suspend(struct pcmcia_device *link)
+{
+       local_info_t *local = link->priv;
 
-  When a CARD_REMOVAL event is received, we immediately set a
-  private flag to block future accesses to this device.  All the
-  functions that actually access the device should check this flag
-  to make sure the card is still present.
-  
-  ======================================================================*/
+       netif_device_detach(local->eth_dev);
+
+       return 0;
+}
 
-static int atmel_event(event_t event, int priority,
-                     event_callback_args_t *args)
+static int atmel_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = args->client_data;
        local_info_t *local = link->priv;
-       
-       DEBUG(1, "atmel_event(0x%06x)\n", event);
-       
-       switch (event) {
-       case CS_EVENT_CARD_REMOVAL:
-               link->state &= ~DEV_PRESENT;
-               if (link->state & DEV_CONFIG) {
-                       netif_device_detach(local->eth_dev);
-                       atmel_release(link);
-               }
-               break;
-       case CS_EVENT_CARD_INSERTION:
-               link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-               atmel_config(link);
-               break;
-       case CS_EVENT_PM_SUSPEND:
-               link->state |= DEV_SUSPEND;
-               /* Fall through... */
-       case CS_EVENT_RESET_PHYSICAL:
-               if (link->state & DEV_CONFIG) {
-                       netif_device_detach(local->eth_dev);
-                       pcmcia_release_configuration(link->handle);
-               }
-               break;
-       case CS_EVENT_PM_RESUME:
-               link->state &= ~DEV_SUSPEND;
-               /* Fall through... */
-       case CS_EVENT_CARD_RESET:
-               if (link->state & DEV_CONFIG) {
-                       pcmcia_request_configuration(link->handle, &link->conf);
-                       atmel_open(local->eth_dev);
-                       netif_device_attach(local->eth_dev);
-               }
-               break;
-       }
+
+       atmel_open(local->eth_dev);
+       netif_device_attach(local->eth_dev);
+
        return 0;
-} /* atmel_event */
+}
 
 /*====================================================================*/
+/* We use the driver_info field to store the correct firmware type for a card. */
+
+#define PCMCIA_DEVICE_MANF_CARD_INFO(manf, card, info) { \
+       .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
+                       PCMCIA_DEV_ID_MATCH_CARD_ID, \
+       .manf_id = (manf), \
+       .card_id = (card), \
+        .driver_info = (kernel_ulong_t)(info), }
+
+#define PCMCIA_DEVICE_PROD_ID12_INFO(v1, v2, vh1, vh2, info) { \
+       .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
+                       PCMCIA_DEV_ID_MATCH_PROD_ID2, \
+       .prod_id = { (v1), (v2), NULL, NULL }, \
+       .prod_id_hash = { (vh1), (vh2), 0, 0 }, \
+        .driver_info = (kernel_ulong_t)(info), }
+
+static struct pcmcia_device_id atmel_ids[] = {
+       PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0620, ATMEL_FW_TYPE_502_3COM),
+       PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0696, ATMEL_FW_TYPE_502_3COM),
+       PCMCIA_DEVICE_MANF_CARD_INFO(0x01bf, 0x3302, ATMEL_FW_TYPE_502E),
+       PCMCIA_DEVICE_MANF_CARD_INFO(0xd601, 0x0007, ATMEL_FW_TYPE_502),
+       PCMCIA_DEVICE_PROD_ID12_INFO("11WAVE", "11WP611AL-E", 0x9eb2da1f, 0xc9a0d3f9, ATMEL_FW_TYPE_502E),
+       PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR", 0xabda4164, 0x41b37e1f, ATMEL_FW_TYPE_502),
+       PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_D", 0xabda4164, 0x3675d704, ATMEL_FW_TYPE_502D),
+       PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_E", 0xabda4164, 0x4172e792, ATMEL_FW_TYPE_502E),
+       PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504_R", 0xabda4164, 0x917f3d72, ATMEL_FW_TYPE_504_2958),
+       PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504", 0xabda4164, 0x5040670a, ATMEL_FW_TYPE_504),
+       PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504A", 0xabda4164, 0xe15ed87f, ATMEL_FW_TYPE_504A_2958),
+       PCMCIA_DEVICE_PROD_ID12_INFO("BT", "Voyager 1020 Laptop Adapter", 0xae49b86a, 0x1e957cd5, ATMEL_FW_TYPE_502),
+       PCMCIA_DEVICE_PROD_ID12_INFO("CNet", "CNWLC 11Mbps Wireless PC Card V-5", 0xbc477dde, 0x502fae6b, ATMEL_FW_TYPE_502E),
+       PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN PC Card", 0x5b878724, 0x122f1df6, ATMEL_FW_TYPE_502),
+       PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN Card S", 0x5b878724, 0x5fba533a, ATMEL_FW_TYPE_504_2958),
+       PCMCIA_DEVICE_PROD_ID12_INFO("OEM", "11Mbps Wireless LAN PC Card V-3", 0xfea54c90, 0x1c5b0f68, ATMEL_FW_TYPE_502),
+       PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W", 0xc4f8b18b, 0x30f38774, ATMEL_FW_TYPE_502D),
+       PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W-V2", 0xc4f8b18b, 0x172d1377, ATMEL_FW_TYPE_502),
+       PCMCIA_DEVICE_PROD_ID12_INFO("Wireless", "PC_CARD", 0xa407ecdd, 0x119f6314, ATMEL_FW_TYPE_502D),
+       PCMCIA_DEVICE_PROD_ID12_INFO("WLAN", "802.11b PC CARD", 0x575c516c, 0xb1f6dbc4, ATMEL_FW_TYPE_502D),
+       PCMCIA_DEVICE_PROD_ID12_INFO("LG", "LW2100N", 0xb474d43a, 0x6b1fec94, ATMEL_FW_TYPE_502E),
+       PCMCIA_DEVICE_NULL
+};
+
+MODULE_DEVICE_TABLE(pcmcia, atmel_ids);
+
 static struct pcmcia_driver atmel_driver = {
-        .owner          = THIS_MODULE,
-        .drv            = {
-                .name   = "atmel_cs",
+       .owner          = THIS_MODULE,
+       .drv            = {
+               .name   = "atmel_cs",
         },
-        .attach         = atmel_attach,
-        .detach         = atmel_detach,
+       .probe          = atmel_probe,
+       .remove         = atmel_detach,
+       .id_table       = atmel_ids,
+       .suspend        = atmel_suspend,
+       .resume         = atmel_resume,
 };
 
 static int atmel_cs_init(void)
@@ -663,7 +480,6 @@ static int atmel_cs_init(void)
 static void atmel_cs_cleanup(void)
 {
         pcmcia_unregister_driver(&atmel_driver);
-       BUG_ON(dev_list != NULL);
 }
 
 /*