]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/pcmcia/yenta_socket.c
/home/lenb/src/to-linus branch 'acpi-2.6.12'
[mirror_ubuntu-bionic-kernel.git] / drivers / pcmcia / yenta_socket.c
index 0e7aa8176692f1aa60f5dc874af8a6afde17d740..744e469a9eda6cca3e0e45457706442e99f3f687 100644 (file)
@@ -527,98 +527,144 @@ static int yenta_sock_suspend(struct pcmcia_socket *sock)
  * Use an adaptive allocation for the memory resource,
  * sometimes the memory behind pci bridges is limited:
  * 1/8 of the size of the io window of the parent.
- * max 4 MB, min 16 kB.
+ * max 4 MB, min 16 kB. We try very hard to not get below
+ * the "ACC" values, though.
  */
 #define BRIDGE_MEM_MAX 4*1024*1024
+#define BRIDGE_MEM_ACC 128*1024
 #define BRIDGE_MEM_MIN 16*1024
 
-#define BRIDGE_IO_MAX 256
+#define BRIDGE_IO_MAX 512
+#define BRIDGE_IO_ACC 256
 #define BRIDGE_IO_MIN 32
 
 #ifndef PCIBIOS_MIN_CARDBUS_IO
 #define PCIBIOS_MIN_CARDBUS_IO PCIBIOS_MIN_IO
 #endif
 
-static void yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned type)
+static int yenta_search_one_res(struct resource *root, struct resource *res,
+                               u32 min)
+{
+       u32 align, size, start, end;
+
+       if (res->flags & IORESOURCE_IO) {
+               align = 1024;
+               size = BRIDGE_IO_MAX;
+               start = PCIBIOS_MIN_CARDBUS_IO;
+               end = ~0U;
+       } else {
+               unsigned long avail = root->end - root->start;
+               int i;
+               size = BRIDGE_MEM_MAX;
+               if (size > avail/8) {
+                       size=(avail+1)/8;
+                       /* round size down to next power of 2 */
+                       i = 0;
+                       while ((size /= 2) != 0)
+                               i++;
+                       size = 1 << i;
+               }
+               if (size < min)
+                       size = min;
+               align = size;
+               start = PCIBIOS_MIN_MEM;
+               end = ~0U;
+       }
+
+       do {
+               if (allocate_resource(root, res, size, start, end, align,
+                                     NULL, NULL)==0) {
+                       return 1;
+               }
+               size = size/2;
+               align = size;
+       } while (size >= min);
+
+       return 0;
+}
+
+
+static int yenta_search_res(struct yenta_socket *socket, struct resource *res,
+                           u32 min)
+{
+       int i;
+       for (i=0; i<PCI_BUS_NUM_RESOURCES; i++) {
+               struct resource * root = socket->dev->bus->resource[i];
+               if (!root)
+                       continue;
+
+               if ((res->flags ^ root->flags) &
+                   (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH))
+                       continue; /* Wrong type */
+
+               if (yenta_search_one_res(root, res, min))
+                       return 1;
+       }
+       return 0;
+}
+
+static void yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned type, int addr_start, int addr_end)
 {
        struct pci_bus *bus;
        struct resource *root, *res;
        u32 start, end;
-       u32 align, size, min;
-       unsigned offset;
        unsigned mask;
 
        res = socket->dev->resource + PCI_BRIDGE_RESOURCES + nr;
        /* Already allocated? */
        if (res->parent)
-               return 0;
+               return;
 
        /* The granularity of the memory limit is 4kB, on IO it's 4 bytes */
        mask = ~0xfff;
        if (type & IORESOURCE_IO)
                mask = ~3;
 
-       offset = 0x1c + 8*nr;
        bus = socket->dev->subordinate;
        res->name = bus->name;
        res->flags = type;
-       res->start = 0;
-       res->end = 0;
-       root = pci_find_parent_resource(socket->dev, res);
-
-       if (!root)
-               return;
 
-       start = config_readl(socket, offset) & mask;
-       end = config_readl(socket, offset+4) | ~mask;
+       start = config_readl(socket, addr_start) & mask;
+       end = config_readl(socket, addr_end) | ~mask;
        if (start && end > start && !override_bios) {
                res->start = start;
                res->end = end;
-               if (request_resource(root, res) == 0)
+               root = pci_find_parent_resource(socket->dev, res);
+               if (root && (request_resource(root, res) == 0))
                        return;
-               printk(KERN_INFO "yenta %s: Preassigned resource %d busy, reconfiguring...\n",
+               printk(KERN_INFO "yenta %s: Preassigned resource %d busy or not available, reconfiguring...\n",
                                pci_name(socket->dev), nr);
-               res->start = res->end = 0;
        }
 
        if (type & IORESOURCE_IO) {
-               align = 1024;
-               size = BRIDGE_IO_MAX;
-               min = BRIDGE_IO_MIN;
-               start = PCIBIOS_MIN_CARDBUS_IO;
-               end = ~0U;
+               if ((yenta_search_res(socket, res, BRIDGE_IO_MAX)) ||
+                   (yenta_search_res(socket, res, BRIDGE_IO_ACC)) ||
+                   (yenta_search_res(socket, res, BRIDGE_IO_MIN))) {
+                       config_writel(socket, addr_start, res->start);
+                       config_writel(socket, addr_end, res->end);
+               }
        } else {
-               unsigned long avail = root->end - root->start;
-               int i;
-               size = BRIDGE_MEM_MAX;
-               if (size > avail/8) {
-                       size=(avail+1)/8;
-                       /* round size down to next power of 2 */
-                       i = 0;
-                       while ((size /= 2) != 0)
-                               i++;
-                       size = 1 << i;
+               if (type & IORESOURCE_PREFETCH) {
+                       if ((yenta_search_res(socket, res, BRIDGE_MEM_MAX)) ||
+                           (yenta_search_res(socket, res, BRIDGE_MEM_ACC)) ||
+                           (yenta_search_res(socket, res, BRIDGE_MEM_MIN))) {
+                               config_writel(socket, addr_start, res->start);
+                               config_writel(socket, addr_end, res->end);
+                       }
+                       /* Approximating prefetchable by non-prefetchable */
+                       res->flags = IORESOURCE_MEM;
                }
-               if (size < BRIDGE_MEM_MIN)
-                       size = BRIDGE_MEM_MIN;
-               min = BRIDGE_MEM_MIN;
-               align = size;
-               start = PCIBIOS_MIN_MEM;
-               end = ~0U;
-       }
-       
-       do {
-               if (allocate_resource(root, res, size, start, end, align, NULL, NULL)==0) {
-                       config_writel(socket, offset, res->start);
-                       config_writel(socket, offset+4, res->end);
-                       return;
+               if ((yenta_search_res(socket, res, BRIDGE_MEM_MAX)) ||
+                   (yenta_search_res(socket, res, BRIDGE_MEM_ACC)) ||
+                   (yenta_search_res(socket, res, BRIDGE_MEM_MIN))) {
+                       config_writel(socket, addr_start, res->start);
+                       config_writel(socket, addr_end, res->end);
                }
-               size = size/2;
-               align = size;
-       } while (size >= min);
+       }
+
        printk(KERN_INFO "yenta %s: no resource of type %x available, trying to continue...\n",
-                       pci_name(socket->dev), type);
-       res->start = res->end = 0;
+              pci_name(socket->dev), type);
+       res->start = res->end = res->flags = 0;
 }
 
 /*
@@ -626,10 +672,14 @@ static void yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned typ
  */
 static void yenta_allocate_resources(struct yenta_socket *socket)
 {
-       yenta_allocate_res(socket, 0, IORESOURCE_MEM|IORESOURCE_PREFETCH);
-       yenta_allocate_res(socket, 1, IORESOURCE_MEM);
-       yenta_allocate_res(socket, 2, IORESOURCE_IO);
-       yenta_allocate_res(socket, 3, IORESOURCE_IO);   /* PCI isn't clever enough to use this one yet */
+       yenta_allocate_res(socket, 0, IORESOURCE_IO,
+                          PCI_CB_IO_BASE_0, PCI_CB_IO_LIMIT_0);
+       yenta_allocate_res(socket, 1, IORESOURCE_IO,
+                          PCI_CB_IO_BASE_1, PCI_CB_IO_LIMIT_1);
+       yenta_allocate_res(socket, 2, IORESOURCE_MEM|IORESOURCE_PREFETCH,
+                          PCI_CB_MEMORY_BASE_0, PCI_CB_MEMORY_LIMIT_0);
+       yenta_allocate_res(socket, 3, IORESOURCE_MEM,
+                          PCI_CB_MEMORY_BASE_1, PCI_CB_MEMORY_LIMIT_1);
 }
 
 
@@ -1055,6 +1105,9 @@ static int yenta_dev_suspend (struct pci_dev *dev, pm_message_t state)
                pci_save_state(dev);
                pci_read_config_dword(dev, 16*4, &socket->saved_state[0]);
                pci_read_config_dword(dev, 17*4, &socket->saved_state[1]);
+               pci_disable_device(dev);
+
+               free_irq(dev->irq, socket);
 
                /*
                 * Some laptops (IBM T22) do not like us putting the Cardbus
@@ -1078,6 +1131,15 @@ static int yenta_dev_resume (struct pci_dev *dev)
                pci_restore_state(dev);
                pci_write_config_dword(dev, 16*4, socket->saved_state[0]);
                pci_write_config_dword(dev, 17*4, socket->saved_state[1]);
+               pci_enable_device(dev);
+               pci_set_master(dev);
+
+               if (socket->cb_irq)
+                       if (request_irq(socket->cb_irq, yenta_interrupt,
+                                       SA_SHIRQ, "yenta", socket)) {
+                               printk(KERN_WARNING "Yenta: request_irq() failed on resume!\n");
+                               socket->cb_irq = 0;
+                       }
 
                if (socket->type && socket->type->restore_state)
                        socket->type->restore_state(socket);