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)
(s->cis_mem.sys_start == 0)) {
int low = !(s->cap.features & SS_CAP_PAGE_REGS);
vs = s;
validate_mem(cis_readable, checksum_match, low);
validate_mem(cis_readable, checksum_match, low, s);
s->cis_mem.sys_start = 0;
vs = NULL;
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");
return CS_OUT_OF_RESOURCE;
}
......
......@@ -809,7 +809,7 @@ static int alloc_io_space(socket_info_t *s, u_int attr, ioaddr_t *base,
return 1;
for (i = 0; i < MAX_IO_WIN; i++) {
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].BasePort = *base;
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,
/* Try to extend top of window */
try = s->io[i].BasePort + s->io[i].NumPorts;
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;
s->io[i].NumPorts += 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,
/* Try to extend bottom of window */
try = s->io[i].BasePort - num;
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].NumPorts += num;
s->io[i].InUse += num;
......@@ -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,
(req->Attributes & WIN_MAP_BELOW_1MB) ||
!(s->cap.features & SS_CAP_PAGE_REGS),
(*handle)->dev_info))
(*handle)->dev_info, s))
return CS_IN_USE;
(*handle)->state |= CLIENT_WIN_REQ(w);
......
......@@ -238,11 +238,11 @@ int copy_memory(memory_handle_t handle, copy_op_t *req);
/* In rsrc_mgr */
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,
char *name);
char *name, socket_info_t *s);
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);
void undo_irq(u_int Attributes, int irq);
int adjust_resource_info(client_handle_t handle, adjust_t *adj);
......
......@@ -44,6 +44,7 @@
#include <linux/ioport.h>
#include <linux/timer.h>
#include <linux/proc_fs.h>
#include <linux/pci.h>
#include <asm/irq.h>
#include <asm/io.h>
......@@ -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))
#define check_mem_resource(b,n) check_resource(&iomem_resource, (b), (n))
static struct resource *resource_parent(unsigned long b, unsigned long 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)
}
memset(b, 0, 256);
for (i = base, most = 0; i < base+num; i += 8) {
if (check_io_resource(i, 8))
if (check_io_resource(i, 8, NULL))
continue;
hole = inb(i);
for (j = 1; j < 8; j++)
......@@ -207,7 +282,7 @@ static void do_io_probe(ioaddr_t base, ioaddr_t num)
bad = any = 0;
for (i = base; i < base+num; i += 8) {
if (check_io_resource(i, 8))
if (check_io_resource(i, 8, NULL))
continue;
for (j = 0; j < 8; j++)
if (inb(i+j) != most) break;
......@@ -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,
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;
......@@ -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) {
if (!fail) {
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;
fail = ((i == base) && (j == base+num));
}
if (fail) {
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))
break;
}
......@@ -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),
int (*do_cksum)(u_long),
resource_map_t *m)
resource_map_t *m, socket_info_t *s)
{
u_long ok;
if (m == &mem_db)
return 0;
ok = inv_probe(is_valid, do_cksum, m->next);
ok = inv_probe(is_valid, do_cksum, m->next, s);
if (ok) {
if (m->base >= 0x100000)
sub_interval(&mem_db, m->base, m->num);
......@@ -296,11 +373,11 @@ static u_long inv_probe(int (*is_valid)(u_long),
}
if (m->base < 0x100000)
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),
int force_low)
int force_low, socket_info_t *s)
{
resource_map_t *m, *n;
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),
if (!probe_mem) return;
/* We do up to four passes through the list */
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;
printk(KERN_NOTICE "cs: warning: no high memory space "
"available!\n");
......@@ -321,7 +398,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
/* Only probe < 1 MB */
if (m->base >= 0x100000) continue;
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;
}
/* Special probe for 64K-aligned block */
......@@ -331,7 +408,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
if (ok >= mem_limit)
sub_interval(&mem_db, b, 0x10000);
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),
#else /* CONFIG_ISA */
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;
static int done = 0;
......@@ -348,7 +425,7 @@ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
if (!probe_mem || done++)
return;
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;
}
......@@ -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,
char *name)
char *name, socket_info_t *s)
{
ioaddr_t try;
resource_map_t *m;
......@@ -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;
(try >= m->base) && (try+num <= m->base+m->num);
try += align) {
if (check_io_resource(try, num) == 0) {
if (request_io_resource(try, num, name, s->cap.cb_dev) == 0) {
*base = try;
request_region(try, num, name);
return 0;
}
if (!align) break;
......@@ -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 force_low, char *name)
int force_low, char *name, socket_info_t *s)
{
u_long try;
resource_map_t *m;
......@@ -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;
(try >= m->base) && (try+num <= m->base+m->num);
try += align) {
if (check_mem_resource(try, num) == 0) {
request_mem_region(try, num, name);
if (request_mem_resource(try, num, name, s->cap.cb_dev) == 0) {
*base = try;
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