Commit 45ecad2e authored by Paul Mackerras's avatar Paul Mackerras Committed by Paul Mackerras

[PATCH] pcmcia resource allocation fix

The patch below is a forward-port from 2.4 of a fix that went in to
the 2.4.x PCMCIA code some time back.  It makes sure that that we
request I/O and memory regions from the correct resource (the parent
of the PCMCIA bridge chip, for PCMCIA bridges connected to a PCI bus)
rather than always requesting them from the top-level ioport_resource
or iomem_resource.
parent d036a4e7
...@@ -264,11 +264,11 @@ static int setup_cis_mem(socket_info_t *s) ...@@ -264,11 +264,11 @@ static int setup_cis_mem(socket_info_t *s)
(s->cis_mem.sys_start == 0)) { (s->cis_mem.sys_start == 0)) {
int low = !(s->cap.features & SS_CAP_PAGE_REGS); int low = !(s->cap.features & SS_CAP_PAGE_REGS);
vs = s; vs = s;
validate_mem(cis_readable, checksum_match, low); validate_mem(cis_readable, checksum_match, low, s);
s->cis_mem.sys_start = 0; s->cis_mem.sys_start = 0;
vs = NULL; vs = NULL;
if (find_mem_region(&s->cis_mem.sys_start, s->cap.map_size, if (find_mem_region(&s->cis_mem.sys_start, s->cap.map_size,
s->cap.map_size, low, "card services")) { s->cap.map_size, low, "card services", s)) {
printk(KERN_NOTICE "cs: unable to map card memory!\n"); printk(KERN_NOTICE "cs: unable to map card memory!\n");
return CS_OUT_OF_RESOURCE; return CS_OUT_OF_RESOURCE;
} }
......
...@@ -809,7 +809,7 @@ static int alloc_io_space(socket_info_t *s, u_int attr, ioaddr_t *base, ...@@ -809,7 +809,7 @@ static int alloc_io_space(socket_info_t *s, u_int attr, ioaddr_t *base,
return 1; return 1;
for (i = 0; i < MAX_IO_WIN; i++) { for (i = 0; i < MAX_IO_WIN; i++) {
if (s->io[i].NumPorts == 0) { if (s->io[i].NumPorts == 0) {
if (find_io_region(base, num, align, name) == 0) { if (find_io_region(base, num, align, name, s) == 0) {
s->io[i].Attributes = attr; s->io[i].Attributes = attr;
s->io[i].BasePort = *base; s->io[i].BasePort = *base;
s->io[i].NumPorts = s->io[i].InUse = num; s->io[i].NumPorts = s->io[i].InUse = num;
...@@ -821,7 +821,7 @@ static int alloc_io_space(socket_info_t *s, u_int attr, ioaddr_t *base, ...@@ -821,7 +821,7 @@ static int alloc_io_space(socket_info_t *s, u_int attr, ioaddr_t *base,
/* Try to extend top of window */ /* Try to extend top of window */
try = s->io[i].BasePort + s->io[i].NumPorts; try = s->io[i].BasePort + s->io[i].NumPorts;
if ((*base == 0) || (*base == try)) if ((*base == 0) || (*base == try))
if (find_io_region(&try, num, 0, name) == 0) { if (find_io_region(&try, num, 0, name, s) == 0) {
*base = try; *base = try;
s->io[i].NumPorts += num; s->io[i].NumPorts += num;
s->io[i].InUse += num; s->io[i].InUse += num;
...@@ -830,7 +830,7 @@ static int alloc_io_space(socket_info_t *s, u_int attr, ioaddr_t *base, ...@@ -830,7 +830,7 @@ static int alloc_io_space(socket_info_t *s, u_int attr, ioaddr_t *base,
/* Try to extend bottom of window */ /* Try to extend bottom of window */
try = s->io[i].BasePort - num; try = s->io[i].BasePort - num;
if ((*base == 0) || (*base == try)) if ((*base == 0) || (*base == try))
if (find_io_region(&try, num, 0, name) == 0) { if (find_io_region(&try, num, 0, name, s) == 0) {
s->io[i].BasePort = *base = try; s->io[i].BasePort = *base = try;
s->io[i].NumPorts += num; s->io[i].NumPorts += num;
s->io[i].InUse += num; s->io[i].InUse += num;
...@@ -1974,7 +1974,7 @@ int pcmcia_request_window(client_handle_t *handle, win_req_t *req, window_handle ...@@ -1974,7 +1974,7 @@ int pcmcia_request_window(client_handle_t *handle, win_req_t *req, window_handle
find_mem_region(&win->base, win->size, align, find_mem_region(&win->base, win->size, align,
(req->Attributes & WIN_MAP_BELOW_1MB) || (req->Attributes & WIN_MAP_BELOW_1MB) ||
!(s->cap.features & SS_CAP_PAGE_REGS), !(s->cap.features & SS_CAP_PAGE_REGS),
(*handle)->dev_info)) (*handle)->dev_info, s))
return CS_IN_USE; return CS_IN_USE;
(*handle)->state |= CLIENT_WIN_REQ(w); (*handle)->state |= CLIENT_WIN_REQ(w);
......
...@@ -238,11 +238,11 @@ int copy_memory(memory_handle_t handle, copy_op_t *req); ...@@ -238,11 +238,11 @@ int copy_memory(memory_handle_t handle, copy_op_t *req);
/* In rsrc_mgr */ /* In rsrc_mgr */
void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long), void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
int force_low); int force_low, socket_info_t *s);
int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align, int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align,
char *name); char *name, socket_info_t *s);
int find_mem_region(u_long *base, u_long num, u_long align, int find_mem_region(u_long *base, u_long num, u_long align,
int force_low, char *name); int force_low, char *name, socket_info_t *s);
int try_irq(u_int Attributes, int irq, int specific); int try_irq(u_int Attributes, int irq, int specific);
void undo_irq(u_int Attributes, int irq); void undo_irq(u_int Attributes, int irq);
int adjust_resource_info(client_handle_t handle, adjust_t *adj); int adjust_resource_info(client_handle_t handle, adjust_t *adj);
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/pci.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -103,8 +104,82 @@ static irq_info_t irq_table[NR_IRQS] = { { 0, 0, 0 }, /* etc */ }; ...@@ -103,8 +104,82 @@ static irq_info_t irq_table[NR_IRQS] = { { 0, 0, 0 }, /* etc */ };
======================================================================*/ ======================================================================*/
#define check_io_resource(b,n) check_resource(&ioport_resource, (b), (n)) static struct resource *resource_parent(unsigned long b, unsigned long n,
#define check_mem_resource(b,n) check_resource(&iomem_resource, (b), (n)) int flags, struct pci_dev *dev)
{
#ifdef CONFIG_PCI
struct resource res, *pr;
if (dev != NULL) {
res.start = b;
res.end = b + n - 1;
res.flags = flags;
pr = pci_find_parent_resource(dev, &res);
if (pr)
return pr;
}
#endif /* CONFIG_PCI */
if (flags & IORESOURCE_MEM)
return &iomem_resource;
return &ioport_resource;
}
static inline int check_io_resource(unsigned long b, unsigned long n,
struct pci_dev *dev)
{
return check_resource(resource_parent(b, n, IORESOURCE_IO, dev), b, n);
}
static inline int check_mem_resource(unsigned long b, unsigned long n,
struct pci_dev *dev)
{
return check_resource(resource_parent(b, n, IORESOURCE_MEM, dev), b, n);
}
static struct resource *make_resource(unsigned long b, unsigned long n,
int flags, char *name)
{
struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
if (res) {
memset(res, 0, sizeof(*res));
res->name = name;
res->start = b;
res->end = b + n - 1;
res->flags = flags | IORESOURCE_BUSY;
}
return res;
}
static int request_io_resource(unsigned long b, unsigned long n,
char *name, struct pci_dev *dev)
{
struct resource *res = make_resource(b, n, IORESOURCE_IO, name);
struct resource *pr = resource_parent(b, n, IORESOURCE_IO, dev);
int err = -ENOMEM;
if (res) {
err = request_resource(pr, res);
if (err)
kfree(res);
}
return err;
}
static int request_mem_resource(unsigned long b, unsigned long n,
char *name, struct pci_dev *dev)
{
struct resource *res = make_resource(b, n, IORESOURCE_MEM, name);
struct resource *pr = resource_parent(b, n, IORESOURCE_MEM, dev);
int err = -ENOMEM;
if (res) {
err = request_resource(pr, res);
if (err)
kfree(res);
}
return err;
}
/*====================================================================== /*======================================================================
...@@ -194,7 +269,7 @@ static void do_io_probe(ioaddr_t base, ioaddr_t num) ...@@ -194,7 +269,7 @@ static void do_io_probe(ioaddr_t base, ioaddr_t num)
} }
memset(b, 0, 256); memset(b, 0, 256);
for (i = base, most = 0; i < base+num; i += 8) { for (i = base, most = 0; i < base+num; i += 8) {
if (check_io_resource(i, 8)) if (check_io_resource(i, 8, NULL))
continue; continue;
hole = inb(i); hole = inb(i);
for (j = 1; j < 8; j++) for (j = 1; j < 8; j++)
...@@ -207,7 +282,7 @@ static void do_io_probe(ioaddr_t base, ioaddr_t num) ...@@ -207,7 +282,7 @@ static void do_io_probe(ioaddr_t base, ioaddr_t num)
bad = any = 0; bad = any = 0;
for (i = base; i < base+num; i += 8) { for (i = base; i < base+num; i += 8) {
if (check_io_resource(i, 8)) if (check_io_resource(i, 8, NULL))
continue; continue;
for (j = 0; j < 8; j++) for (j = 0; j < 8; j++)
if (inb(i+j) != most) break; if (inb(i+j) != most) break;
...@@ -247,7 +322,8 @@ static void do_io_probe(ioaddr_t base, ioaddr_t num) ...@@ -247,7 +322,8 @@ static void do_io_probe(ioaddr_t base, ioaddr_t num)
======================================================================*/ ======================================================================*/
static int do_mem_probe(u_long base, u_long num, static int do_mem_probe(u_long base, u_long num,
int (*is_valid)(u_long), int (*do_cksum)(u_long)) int (*is_valid)(u_long), int (*do_cksum)(u_long),
socket_info_t *s)
{ {
u_long i, j, bad, fail, step; u_long i, j, bad, fail, step;
...@@ -258,13 +334,14 @@ static int do_mem_probe(u_long base, u_long num, ...@@ -258,13 +334,14 @@ static int do_mem_probe(u_long base, u_long num,
for (i = j = base; i < base+num; i = j + step) { for (i = j = base; i < base+num; i = j + step) {
if (!fail) { if (!fail) {
for (j = i; j < base+num; j += step) for (j = i; j < base+num; j += step)
if ((check_mem_resource(j, step) == 0) && is_valid(j)) if ((check_mem_resource(j, step, s->cap.cb_dev) == 0) &&
is_valid(j))
break; break;
fail = ((i == base) && (j == base+num)); fail = ((i == base) && (j == base+num));
} }
if (fail) { if (fail) {
for (j = i; j < base+num; j += 2*step) for (j = i; j < base+num; j += 2*step)
if ((check_mem_resource(j, 2*step) == 0) && if ((check_mem_resource(j, 2*step, s->cap.cb_dev) == 0) &&
do_cksum(j) && do_cksum(j+step)) do_cksum(j) && do_cksum(j+step))
break; break;
} }
...@@ -283,12 +360,12 @@ static int do_mem_probe(u_long base, u_long num, ...@@ -283,12 +360,12 @@ static int do_mem_probe(u_long base, u_long num,
static u_long inv_probe(int (*is_valid)(u_long), static u_long inv_probe(int (*is_valid)(u_long),
int (*do_cksum)(u_long), int (*do_cksum)(u_long),
resource_map_t *m) resource_map_t *m, socket_info_t *s)
{ {
u_long ok; u_long ok;
if (m == &mem_db) if (m == &mem_db)
return 0; return 0;
ok = inv_probe(is_valid, do_cksum, m->next); ok = inv_probe(is_valid, do_cksum, m->next, s);
if (ok) { if (ok) {
if (m->base >= 0x100000) if (m->base >= 0x100000)
sub_interval(&mem_db, m->base, m->num); sub_interval(&mem_db, m->base, m->num);
...@@ -296,11 +373,11 @@ static u_long inv_probe(int (*is_valid)(u_long), ...@@ -296,11 +373,11 @@ static u_long inv_probe(int (*is_valid)(u_long),
} }
if (m->base < 0x100000) if (m->base < 0x100000)
return 0; return 0;
return do_mem_probe(m->base, m->num, is_valid, do_cksum); return do_mem_probe(m->base, m->num, is_valid, do_cksum, s);
} }
void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long), void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
int force_low) int force_low, socket_info_t *s)
{ {
resource_map_t *m, *n; resource_map_t *m, *n;
static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 }; static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
...@@ -310,7 +387,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long), ...@@ -310,7 +387,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
if (!probe_mem) return; if (!probe_mem) return;
/* We do up to four passes through the list */ /* We do up to four passes through the list */
if (!force_low) { if (!force_low) {
if (hi++ || (inv_probe(is_valid, do_cksum, mem_db.next) > 0)) if (hi++ || (inv_probe(is_valid, do_cksum, mem_db.next, s) > 0))
return; return;
printk(KERN_NOTICE "cs: warning: no high memory space " printk(KERN_NOTICE "cs: warning: no high memory space "
"available!\n"); "available!\n");
...@@ -321,7 +398,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long), ...@@ -321,7 +398,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
/* Only probe < 1 MB */ /* Only probe < 1 MB */
if (m->base >= 0x100000) continue; if (m->base >= 0x100000) continue;
if ((m->base | m->num) & 0xffff) { if ((m->base | m->num) & 0xffff) {
ok += do_mem_probe(m->base, m->num, is_valid, do_cksum); ok += do_mem_probe(m->base, m->num, is_valid, do_cksum, s);
continue; continue;
} }
/* Special probe for 64K-aligned block */ /* Special probe for 64K-aligned block */
...@@ -331,7 +408,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long), ...@@ -331,7 +408,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
if (ok >= mem_limit) if (ok >= mem_limit)
sub_interval(&mem_db, b, 0x10000); sub_interval(&mem_db, b, 0x10000);
else else
ok += do_mem_probe(b, 0x10000, is_valid, do_cksum); ok += do_mem_probe(b, 0x10000, is_valid, do_cksum, s);
} }
} }
} }
...@@ -340,7 +417,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long), ...@@ -340,7 +417,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
#else /* CONFIG_ISA */ #else /* CONFIG_ISA */
void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long), void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
int force_low) int force_low, socket_info_t *s)
{ {
resource_map_t *m; resource_map_t *m;
static int done = 0; static int done = 0;
...@@ -348,7 +425,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long), ...@@ -348,7 +425,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
if (!probe_mem || done++) if (!probe_mem || done++)
return; return;
for (m = mem_db.next; m != &mem_db; m = m->next) for (m = mem_db.next; m != &mem_db; m = m->next)
if (do_mem_probe(m->base, m->num, is_valid, do_cksum)) if (do_mem_probe(m->base, m->num, is_valid, do_cksum, s))
return; return;
} }
...@@ -368,7 +445,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long), ...@@ -368,7 +445,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
======================================================================*/ ======================================================================*/
int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align, int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align,
char *name) char *name, socket_info_t *s)
{ {
ioaddr_t try; ioaddr_t try;
resource_map_t *m; resource_map_t *m;
...@@ -378,9 +455,8 @@ int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align, ...@@ -378,9 +455,8 @@ int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align,
for (try = (try >= m->base) ? try : try+align; for (try = (try >= m->base) ? try : try+align;
(try >= m->base) && (try+num <= m->base+m->num); (try >= m->base) && (try+num <= m->base+m->num);
try += align) { try += align) {
if (check_io_resource(try, num) == 0) { if (request_io_resource(try, num, name, s->cap.cb_dev) == 0) {
*base = try; *base = try;
request_region(try, num, name);
return 0; return 0;
} }
if (!align) break; if (!align) break;
...@@ -390,7 +466,7 @@ int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align, ...@@ -390,7 +466,7 @@ int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align,
} }
int find_mem_region(u_long *base, u_long num, u_long align, int find_mem_region(u_long *base, u_long num, u_long align,
int force_low, char *name) int force_low, char *name, socket_info_t *s)
{ {
u_long try; u_long try;
resource_map_t *m; resource_map_t *m;
...@@ -403,8 +479,7 @@ int find_mem_region(u_long *base, u_long num, u_long align, ...@@ -403,8 +479,7 @@ int find_mem_region(u_long *base, u_long num, u_long align,
for (try = (try >= m->base) ? try : try+align; for (try = (try >= m->base) ? try : try+align;
(try >= m->base) && (try+num <= m->base+m->num); (try >= m->base) && (try+num <= m->base+m->num);
try += align) { try += align) {
if (check_mem_resource(try, num) == 0) { if (request_mem_resource(try, num, name, s->cap.cb_dev) == 0) {
request_mem_region(try, num, name);
*base = try; *base = try;
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment