pcmcia: introduce autoconfiguration feature
[linux-2.6.git] / drivers / isdn / hisax / sedlbauer_cs.c
index 5745eb1..b69eccf 100644 (file)
@@ -38,7 +38,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
-#include <linux/sched.h>
 #include <linux/ptrace.h>
 #include <linux/slab.h>
 #include <linux/string.h>
@@ -47,8 +46,6 @@
 #include <asm/io.h>
 #include <asm/system.h>
 
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
 #include <pcmcia/cistpl.h>
 #include <pcmcia/cisreg.h>
 #include <pcmcia/ds.h>
@@ -58,24 +55,6 @@ MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Sedlbauer cards");
 MODULE_AUTHOR("Marcus Niemann");
 MODULE_LICENSE("Dual MPL/GPL");
 
-/*
-   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
-   you do not define PCMCIA_DEBUG at all, all the debug code will be
-   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
-   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);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args); 
-static char *version =
-"sedlbauer_cs.c 1.1a 2001/01/28 15:04:04 (M.Niemann)";
-#else
-#define DEBUG(n, args...)
-#endif
-
 
 /*====================================================================*/
 
@@ -95,8 +74,8 @@ module_param(protocol, int, 0);
    event handler. 
 */
 
-static void sedlbauer_config(dev_link_t *link);
-static void sedlbauer_release(dev_link_t *link);
+static int sedlbauer_config(struct pcmcia_device *link) __devinit ;
+static void sedlbauer_release(struct pcmcia_device *link);
 
 /*
    The attach() and detach() entry points are used to create and destroy
@@ -104,34 +83,10 @@ static void sedlbauer_release(dev_link_t *link);
    needed to manage one actual PCMCIA card.
 */
 
-static void sedlbauer_detach(struct pcmcia_device *p_dev);
-
-/*
-   You'll also need to prototype all the functions that will actually
-   be used to talk to your device.  See 'memory_cs' for a good example
-   of a fully self-sufficient driver; the other drivers rely more or
-   less on other parts of the kernel.
-*/
+static void sedlbauer_detach(struct pcmcia_device *p_dev) __devexit;
 
-/*
-   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
-   structure.  We allocate them in the card's private data structure,
-   because they generally shouldn't be allocated dynamically.
-
-   In this case, we also provide a flag to indicate if a device is
-   "stopped" due to a power management event, or card ejection.  The
-   device IO routines can use a flag like this to throttle IO to a
-   card that is not ready to accept it.
-*/
-   
 typedef struct local_info_t {
-    dev_link_t         link;
-    dev_node_t         node;
+       struct pcmcia_device    *p_dev;
     int                        stop;
     int                        cardnr;
 } local_info_t;
@@ -148,24 +103,19 @@ typedef struct local_info_t {
     
 ======================================================================*/
 
-static int sedlbauer_attach(struct pcmcia_device *p_dev)
+static int __devinit sedlbauer_probe(struct pcmcia_device *link)
 {
     local_info_t *local;
-    dev_link_t *link;
-    
-    DEBUG(0, "sedlbauer_attach()\n");
+
+    dev_dbg(&link->dev, "sedlbauer_attach()\n");
 
     /* 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) return -ENOMEM;
-    memset(local, 0, sizeof(local_info_t));
     local->cardnr = -1;
-    link = &local->link; link->priv = local;
-    
-    /* Interrupt setup */
-    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
-    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
-    link->irq.Handler = NULL;
+
+    local->p_dev = link;
+    link->priv = local;
 
     /*
       General socket configuration defaults can go here.  In this
@@ -178,22 +128,10 @@ static int sedlbauer_attach(struct pcmcia_device *p_dev)
     /* from old sedl_cs 
     */
     /* The io structure describes IO port mapping */
-    link->io.NumPorts1 = 8;
-    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
-    link->io.IOAddrLines = 3;
+    link->resource[0]->end = 8;
+    link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
 
-
-    link->conf.Attributes = 0;
-    link->conf.Vcc = 50;
-    link->conf.IntType = INT_MEMORY_AND_IO;
-
-    link->handle = p_dev;
-    p_dev->instance = link;
-
-    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-    sedlbauer_config(link);
-
-    return 0;
+    return sedlbauer_config(link);
 } /* sedlbauer_attach */
 
 /*======================================================================
@@ -205,19 +143,15 @@ static int sedlbauer_attach(struct pcmcia_device *p_dev)
 
 ======================================================================*/
 
-static void sedlbauer_detach(struct pcmcia_device *p_dev)
+static void __devexit sedlbauer_detach(struct pcmcia_device *link)
 {
-    dev_link_t *link = dev_to_instance(p_dev);
-
-    DEBUG(0, "sedlbauer_detach(0x%p)\n", link);
+       dev_dbg(&link->dev, "sedlbauer_detach(0x%p)\n", link);
 
-    if (link->state & DEV_CONFIG) {
-           ((local_info_t *)link->priv)->stop = 1;
-           sedlbauer_release(link);
-    }
+       ((local_info_t *)link->priv)->stop = 1;
+       sedlbauer_release(link);
 
-    /* This points to the parent local_info_t struct */
-    kfree(link->priv);
+       /* This points to the parent local_info_t struct */
+       kfree(link->priv);
 } /* sedlbauer_detach */
 
 /*======================================================================
@@ -227,45 +161,48 @@ static void sedlbauer_detach(struct pcmcia_device *p_dev)
     device available to the system.
     
 ======================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+static int sedlbauer_config_check(struct pcmcia_device *p_dev,
+                                 cistpl_cftable_entry_t *cfg,
+                                 cistpl_cftable_entry_t *dflt,
+                                 void *priv_data)
+{
+       if (cfg->index == 0)
+               return -ENODEV;
+
+       /* IO window settings */
+       p_dev->resource[0]->end = p_dev->resource[1]->end = 0;
+       if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+               cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+               p_dev->resource[0]->start = io->win[0].base;
+               p_dev->resource[0]->end = io->win[0].len;
+               p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+               p_dev->resource[0]->flags |=
+                                       pcmcia_io_cfg_data_width(io->flags);
+               if (io->nwin > 1) {
+                       p_dev->resource[1]->flags = p_dev->resource[0]->flags;
+                       p_dev->resource[1]->start = io->win[1].base;
+                       p_dev->resource[1]->end = io->win[1].len;
+               }
+               /* This reserves IO space but doesn't actually enable it */
+               p_dev->io_lines = 3;
+               if (pcmcia_request_io(p_dev) != 0)
+                       return -ENODEV;
+       }
+
+       return 0;
+}
+
+
 
-static void sedlbauer_config(dev_link_t *link)
+static int __devinit sedlbauer_config(struct pcmcia_device *link)
 {
-    client_handle_t handle = link->handle;
-    local_info_t *dev = link->priv;
-    tuple_t tuple;
-    cisparse_t parse;
-    int last_fn, last_ret;
-    u8 buf[64];
-    config_info_t conf;
-    win_req_t req;
-    memreq_t map;
+    int ret;
     IsdnCard_t  icard;
 
-    DEBUG(0, "sedlbauer_config(0x%p)\n", link);
+    dev_dbg(&link->dev, "sedlbauer_config(0x%p)\n", link);
 
-    /*
-       This reads the card's CONFIG tuple to find its configuration
-       registers.
-    */
-    tuple.DesiredTuple = CISTPL_CONFIG;
-    tuple.Attributes = 0;
-    tuple.TupleData = buf;
-    tuple.TupleDataMax = sizeof(buf);
-    tuple.TupleOffset = 0;
-    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));
-    link->conf.ConfigBase = parse.config.base;
-    link->conf.Present = parse.config.rmask[0];
-    
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
-    /* Look up the current Vcc */
-    CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
-    link->conf.Vcc = conf.Vcc;
+    link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_CHECK_VCC |
+           CONF_AUTO_SET_VPP | CONF_AUTO_AUDIO;
 
     /*
       In this loop, we scan the CIS for configuration table entries,
@@ -279,168 +216,51 @@ static void sedlbauer_config(dev_link_t *link)
       these things without consulting the CIS, and most client drivers
       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));
-    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)
-           goto next_entry;
-
-       if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
-       if (cfg->index == 0) goto next_entry;
-       link->conf.ConfigIndex = cfg->index;
-       
-       /* Does this card need audio output? */
-       if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
-           link->conf.Attributes |= CONF_ENABLE_SPKR;
-           link->conf.Status = CCSR_AUDIO_ENA;
-       }
-       
-       /* 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)) {
-           if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000)
-               goto next_entry;
-       } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
-           if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000)
-               goto next_entry;
-       }
-           
-       if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
-           link->conf.Vpp1 = link->conf.Vpp2 =
-               cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
-       else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
-           link->conf.Vpp1 = link->conf.Vpp2 =
-               dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
-       
-       /* Do we need to allocate an interrupt? */
-       if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
-           link->conf.Attributes |= CONF_ENABLE_IRQ;
-       
-       /* IO window settings */
-       link->io.NumPorts1 = link->io.NumPorts2 = 0;
-       if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
-           cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
-           link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
-           if (!(io->flags & CISTPL_IO_8BIT))
-               link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
-           if (!(io->flags & CISTPL_IO_16BIT))
-               link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
-/* new in dummy.cs 2001/01/28 MN 
-            link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
-*/
-           link->io.BasePort1 = io->win[0].base;
-           link->io.NumPorts1 = io->win[0].len;
-           if (io->nwin > 1) {
-               link->io.Attributes2 = link->io.Attributes1;
-               link->io.BasePort2 = io->win[1].base;
-               link->io.NumPorts2 = io->win[1].len;
-           }
-           /* This reserves IO space but doesn't actually enable it */
-           if (pcmcia_request_io(link->handle, &link->io) != 0)
-               goto next_entry;
-       }
+    ret = pcmcia_loop_config(link, sedlbauer_config_check, NULL);
+    if (ret)
+           goto failed;
 
-       /*
-         Now set up a common memory window, if needed.  There is room
-         in the dev_link_t structure for one memory window handle,
-         but if the base addresses need to be saved, or if multiple
-         windows are needed, the info should go in the private data
-         structure for this device.
-
-         Note that the memory window base is a physical address, and
-         needs to be mapped to virtual space with ioremap() before it
-         is used.
-       */
-       if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
-           cistpl_mem_t *mem =
-               (cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
-           req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM;
-           req.Attributes |= WIN_ENABLE;
-           req.Base = mem->win[0].host_addr;
-           req.Size = mem->win[0].len;
-/* new in dummy.cs 2001/01/28 MN 
-            if (req.Size < 0x1000)
-                req.Size = 0x1000;
-*/
-           req.AccessSpeed = 0;
-           if (pcmcia_request_window(&link->handle, &req, &link->win) != 0)
-               goto next_entry;
-           map.Page = 0; map.CardOffset = mem->win[0].card_addr;
-           if (pcmcia_map_mem_page(link->win, &map) != 0)
-               goto next_entry;
-       }
-       /* If we got this far, we're cool! */
-       break;
-
-    next_entry:
-       CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
-    }
-
-    /*
-       Allocate an interrupt line.  Note that this does not assign a
-       handler to the interrupt, unless the 'Handler' member of the
-       irq structure is initialized.
-    */
-    if (link->conf.Attributes & CONF_ENABLE_IRQ)
-       CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &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));
-
-    /*
-      At this point, the dev_node_t structure(s) need to be
-      initialized and arranged in a linked list at link->dev.
-    */
-    sprintf(dev->node.dev_name, "sedlbauer");
-    dev->node.major = dev->node.minor = 0;
-    link->dev = &dev->node;
+    ret = pcmcia_enable_device(link);
+    if (ret)
+           goto failed;
 
     /* Finally, report what we've done */
-    printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
-          dev->node.dev_name, 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);
-    if (link->win)
-       printk(", mem 0x%06lx-0x%06lx", req.Base,
-              req.Base+req.Size-1);
+    dev_info(&link->dev, "index 0x%02x:",
+          link->config_index);
+    if (link->vpp)
+       printk(", Vpp %d.%d", link->vpp/10, link->vpp%10);
+    printk(", irq %d", link->irq);
+    if (link->resource[0])
+       printk(" & %pR", link->resource[0]);
+    if (link->resource[1])
+       printk(" & %pR", link->resource[1]);
     printk("\n");
-    
-    link->state &= ~DEV_CONFIG_PENDING;
 
-    icard.para[0] = link->irq.AssignedIRQ;
-    icard.para[1] = link->io.BasePort1;
+    icard.para[0] = link->irq;
+    icard.para[1] = link->resource[0]->start;
     icard.protocol = protocol;
     icard.typ = ISDN_CTYPE_SEDLBAUER_PCMCIA;
     
-    last_ret = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->stop), &icard);
-    if (last_ret < 0) {
-       printk(KERN_ERR "sedlbauer_cs: failed to initialize SEDLBAUER PCMCIA %d at i/o %#x\n",
-               last_ret, link->io.BasePort1);
+    ret = hisax_init_pcmcia(link, 
+                           &(((local_info_t *)link->priv)->stop), &icard);
+    if (ret < 0) {
+       printk(KERN_ERR "sedlbauer_cs: failed to initialize SEDLBAUER PCMCIA %d with %pR\n",
+               ret, link->resource[0]);
        sedlbauer_release(link);
+       return -ENODEV;
     } else
-       ((local_info_t*)link->priv)->cardnr = last_ret;
+       ((local_info_t *)link->priv)->cardnr = ret;
 
-    return;
+    return 0;
 
-cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
+failed:
     sedlbauer_release(link);
+    return -ENODEV;
 
 } /* sedlbauer_config */
 
@@ -452,10 +272,10 @@ cs_failed:
     
 ======================================================================*/
 
-static void sedlbauer_release(dev_link_t *link)
+static void sedlbauer_release(struct pcmcia_device *link)
 {
     local_info_t *local = link->priv;
-    DEBUG(0, "sedlbauer_release(0x%p)\n", link);
+    dev_dbg(&link->dev, "sedlbauer_release(0x%p)\n", link);
 
     if (local) {
        if (local->cardnr >= 0) {
@@ -464,12 +284,11 @@ static void sedlbauer_release(dev_link_t *link)
        }
     }
 
-    pcmcia_disable_device(link->handle);
+    pcmcia_disable_device(link);
 } /* sedlbauer_release */
 
-static int sedlbauer_suspend(struct pcmcia_device *p_dev)
+static int sedlbauer_suspend(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        local_info_t *dev = link->priv;
 
        dev->stop = 1;
@@ -477,9 +296,8 @@ static int sedlbauer_suspend(struct pcmcia_device *p_dev)
        return 0;
 }
 
-static int sedlbauer_resume(struct pcmcia_device *p_dev)
+static int sedlbauer_resume(struct pcmcia_device *link)
 {
-       dev_link_t *link = dev_to_instance(p_dev);
        local_info_t *dev = link->priv;
 
        dev->stop = 0;
@@ -505,8 +323,8 @@ static struct pcmcia_driver sedlbauer_driver = {
        .drv            = {
                .name   = "sedlbauer_cs",
        },
-       .probe          = sedlbauer_attach,
-       .remove         = sedlbauer_detach,
+       .probe          = sedlbauer_probe,
+       .remove         = __devexit_p(sedlbauer_detach),
        .id_table       = sedlbauer_ids,
        .suspend        = sedlbauer_suspend,
        .resume         = sedlbauer_resume,