pcmcia: use autoconfiguration feature for ioports and iomem
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / pcmcia / pcmcia_cis.c
index 160da0697335b59d928c28d51c80c952fd96f577..e2c92415b8924a89c27e6759652b4a7e5726139b 100644 (file)
@@ -6,7 +6,7 @@
  * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
  *
  * Copyright (C) 1999       David A. Hinds
- * Copyright (C) 2004-2009   Dominik Brodowski
+ * Copyright (C) 2004-2010   Dominik Brodowski
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -125,13 +125,24 @@ next_entry:
        return ret;
 }
 
+
+/**
+ * pcmcia_io_cfg_data_width() - convert cfgtable to data path width parameter
+ */
+static int pcmcia_io_cfg_data_width(unsigned int flags)
+{
+       if (!(flags & CISTPL_IO_8BIT))
+               return IO_DATA_PATH_WIDTH_16;
+       if (!(flags & CISTPL_IO_16BIT))
+               return IO_DATA_PATH_WIDTH_8;
+       return IO_DATA_PATH_WIDTH_AUTO;
+}
+
+
 struct pcmcia_cfg_mem {
        struct pcmcia_device *p_dev;
+       int (*conf_check) (struct pcmcia_device *p_dev, void *priv_data);
        void *priv_data;
-       int (*conf_check) (struct pcmcia_device *p_dev,
-                          cistpl_cftable_entry_t *cfg,
-                          cistpl_cftable_entry_t *dflt,
-                          void *priv_data);
        cisparse_t parse;
        cistpl_cftable_entry_t dflt;
 };
@@ -184,16 +195,63 @@ static int pcmcia_do_loop_config(tuple_t *tuple, cisparse_t *parse, void *priv)
        if ((flags & CONF_AUTO_AUDIO) && (cfg->flags & CISTPL_CFTABLE_AUDIO))
                p_dev->config_flags |= CONF_ENABLE_SPKR;
 
-       return cfg_mem->conf_check(cfg_mem->p_dev, cfg, &cfg_mem->dflt,
-                                  cfg_mem->priv_data);
+
+       /* IO window settings? */
+       if (flags & CONF_AUTO_SET_IO) {
+               cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+               int i = 0;
+
+               p_dev->resource[0]->start = p_dev->resource[0]->end = 0;
+               p_dev->resource[1]->start = p_dev->resource[1]->end = 0;
+               if (io->nwin == 0)
+                       return -ENODEV;
+
+               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) {
+                       /* For multifunction cards, by convention, we
+                        * configure the network function with window 0,
+                        * and serial with window 1 */
+                       i = (io->win[1].len > io->win[0].len);
+                       p_dev->resource[1]->flags = p_dev->resource[0]->flags;
+                       p_dev->resource[1]->start = io->win[1-i].base;
+                       p_dev->resource[1]->end = io->win[1-i].len;
+               }
+               p_dev->resource[0]->start = io->win[i].base;
+               p_dev->resource[0]->end = io->win[i].len;
+               p_dev->io_lines = io->flags & CISTPL_IO_LINES_MASK;
+       }
+
+       /* MEM window settings? */
+       if (flags & CONF_AUTO_SET_IOMEM) {
+               /* so far, we only set one memory window */
+               cistpl_mem_t *mem = (cfg->mem.nwin) ? &cfg->mem : &dflt->mem;
+
+               p_dev->resource[2]->start = p_dev->resource[2]->end = 0;
+               if (mem->nwin == 0)
+                       return -ENODEV;
+
+               p_dev->resource[2]->start = mem->win[0].host_addr;
+               p_dev->resource[2]->end = mem->win[0].len;
+               if (p_dev->resource[2]->end < 0x1000)
+                       p_dev->resource[2]->end = 0x1000;
+               p_dev->card_addr = mem->win[0].card_addr;
+       }
+
+       dev_dbg(&p_dev->dev,
+               "checking configuration %x: %pr %pr %pr (%d lines)\n",
+               p_dev->config_index, p_dev->resource[0], p_dev->resource[1],
+               p_dev->resource[2], p_dev->io_lines);
+
+       return cfg_mem->conf_check(p_dev, cfg_mem->priv_data);
 }
 
 /**
  * pcmcia_loop_config() - loop over configuration options
  * @p_dev:     the struct pcmcia_device which we need to loop for.
  * @conf_check:        function to call for each configuration option.
- *             It gets passed the struct pcmcia_device, the CIS data
- *             describing the configuration option, and private data
+ *             It gets passed the struct pcmcia_device and private data
  *             being passed to pcmcia_loop_config()
  * @priv_data: private data to be passed to the conf_check function.
  *
@@ -203,8 +261,6 @@ static int pcmcia_do_loop_config(tuple_t *tuple, cisparse_t *parse, void *priv)
  */
 int pcmcia_loop_config(struct pcmcia_device *p_dev,
                       int      (*conf_check)   (struct pcmcia_device *p_dev,
-                                                cistpl_cftable_entry_t *cfg,
-                                                cistpl_cftable_entry_t *dflt,
                                                 void *priv_data),
                       void *priv_data)
 {