summaryrefslogtreecommitdiffstats
path: root/drivers/pcmcia/yenta_socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pcmcia/yenta_socket.c')
-rw-r--r--drivers/pcmcia/yenta_socket.c160
1 files changed, 105 insertions, 55 deletions
diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c
index 15f716a7968..6837491f021 100644
--- a/drivers/pcmcia/yenta_socket.c
+++ b/drivers/pcmcia/yenta_socket.c
@@ -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);
}