diff options
Diffstat (limited to 'drivers/pcmcia/yenta_socket.c')
| -rw-r--r-- | drivers/pcmcia/yenta_socket.c | 169 |
1 files changed, 111 insertions, 58 deletions
diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 0e7aa8176692..62fd705203fb 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -527,64 +527,29 @@ 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) { - 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; - - /* 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); + u32 align, size, start, end; - if (!root) - return; - - start = config_readl(socket, offset) & mask; - end = config_readl(socket, offset+4) | ~mask; - if (start && end > start && !override_bios) { - res->start = start; - res->end = end; - if (request_resource(root, res) == 0) - return; - printk(KERN_INFO "yenta %s: Preassigned resource %d busy, reconfiguring...\n", - pci_name(socket->dev), nr); - res->start = res->end = 0; - } - - if (type & IORESOURCE_IO) { + if (res->flags & IORESOURCE_IO) { align = 1024; size = BRIDGE_IO_MAX; - min = BRIDGE_IO_MIN; start = PCIBIOS_MIN_CARDBUS_IO; end = ~0U; } else { @@ -599,26 +564,107 @@ static void yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned typ i++; size = 1 << i; } - if (size < BRIDGE_MEM_MIN) - size = BRIDGE_MEM_MIN; - min = BRIDGE_MEM_MIN; + 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) { - config_writel(socket, offset, res->start); - config_writel(socket, offset+4, res->end); - return; + 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 resource *root, *res; + struct pci_bus_region region; + unsigned mask; + + res = socket->dev->resource + PCI_BRIDGE_RESOURCES + nr; + /* Already allocated? */ + if (res->parent) + return; + + /* The granularity of the memory limit is 4kB, on IO it's 4 bytes */ + mask = ~0xfff; + if (type & IORESOURCE_IO) + mask = ~3; + + res->name = socket->dev->subordinate->name; + res->flags = type; + + region.start = config_readl(socket, addr_start) & mask; + region.end = config_readl(socket, addr_end) | ~mask; + if (region.start && region.end > region.start && !override_bios) { + pcibios_bus_to_resource(socket->dev, res, ®ion); + 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 or not available, reconfiguring...\n", + pci_name(socket->dev), nr); + } + + if (type & IORESOURCE_IO) { + 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); + return; + } + } else { + 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); + return; + } + /* Approximating prefetchable by non-prefetchable */ + res->flags = IORESOURCE_MEM; + } + 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); + return; + } + } + 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,7 @@ 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); /* * Some laptops (IBM T22) do not like us putting the Cardbus @@ -1078,6 +1129,8 @@ 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->type && socket->type->restore_state) socket->type->restore_state(socket); |
