Commit f2d816ef authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://bk.arm.linux.org.uk/linux-2.6-pcmcia

into ppc970.osdl.org:/home/torvalds/v2.6/linux
parents 5566c10d 7347d800
...@@ -135,5 +135,6 @@ void pci_enable_bridges(struct pci_bus *bus) ...@@ -135,5 +135,6 @@ void pci_enable_bridges(struct pci_bus *bus)
} }
} }
EXPORT_SYMBOL(pci_bus_alloc_resource);
EXPORT_SYMBOL(pci_bus_add_devices); EXPORT_SYMBOL(pci_bus_add_devices);
EXPORT_SYMBOL(pci_enable_bridges); EXPORT_SYMBOL(pci_enable_bridges);
...@@ -2151,9 +2151,8 @@ static int __init init_pcmcia_cs(void) ...@@ -2151,9 +2151,8 @@ static int __init init_pcmcia_cs(void)
{ {
printk(KERN_INFO "%s\n", release); printk(KERN_INFO "%s\n", release);
printk(KERN_INFO " %s\n", options); printk(KERN_INFO " %s\n", options);
class_register(&pcmcia_socket_class);
return 0; return class_register(&pcmcia_socket_class);
} }
static void __exit exit_pcmcia_cs(void) static void __exit exit_pcmcia_cs(void)
......
...@@ -181,7 +181,7 @@ int copy_memory(memory_handle_t handle, copy_op_t *req); ...@@ -181,7 +181,7 @@ int copy_memory(memory_handle_t handle, copy_op_t *req);
/* In rsrc_mgr */ /* In rsrc_mgr */
void validate_mem(struct pcmcia_socket *s); void validate_mem(struct pcmcia_socket *s);
int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align, int find_io_region(ioaddr_t *base, ioaddr_t num, unsigned long align,
char *name, struct pcmcia_socket *s); char *name, struct pcmcia_socket *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 low, char *name, struct pcmcia_socket *s); int low, char *name, struct pcmcia_socket *s);
......
...@@ -107,44 +107,8 @@ static irq_info_t irq_table[NR_IRQS]; ...@@ -107,44 +107,8 @@ static irq_info_t irq_table[NR_IRQS];
======================================================================*/ ======================================================================*/
static struct resource *resource_parent(unsigned long b, unsigned long n, static struct resource *
int flags, struct pci_dev *dev) make_resource(unsigned long b, unsigned long n, int flags, char *name)
{
#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;
}
/* FIXME: Fundamentally racy. */
static inline int check_io_resource(unsigned long b, unsigned long n,
struct pci_dev *dev)
{
struct resource *region;
region = __request_region(resource_parent(b, n, IORESOURCE_IO, dev),
b, n, "check_io_resource");
if (!region)
return -EBUSY;
release_resource(region);
kfree(region);
return 0;
}
static struct resource *make_resource(unsigned long b, unsigned long n,
int flags, char *name)
{ {
struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL); struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
...@@ -158,34 +122,34 @@ static struct resource *make_resource(unsigned long b, unsigned long n, ...@@ -158,34 +122,34 @@ static struct resource *make_resource(unsigned long b, unsigned long n,
return res; return res;
} }
static int request_io_resource(unsigned long b, unsigned long n, static struct resource *
char *name, struct pci_dev *dev) claim_region(struct pcmcia_socket *s, unsigned long base, unsigned long size,
int type, char *name)
{ {
struct resource *res = make_resource(b, n, IORESOURCE_IO, name); struct resource *res, *parent;
struct resource *pr = resource_parent(b, n, IORESOURCE_IO, dev);
int err = -ENOMEM; parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource;
res = make_resource(base, size, type | IORESOURCE_BUSY, name);
if (res) { if (res) {
err = request_resource(pr, res); #ifdef CONFIG_PCI
if (err) if (s && s->cb_dev)
parent = pci_find_parent_resource(s->cb_dev, res);
#endif
if (!parent || request_resource(parent, res)) {
kfree(res); kfree(res);
res = NULL;
}
} }
return err; return res;
} }
static int request_mem_resource(unsigned long b, unsigned long n, static void free_region(struct resource *res)
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) { if (res) {
err = request_resource(pr, res); release_resource(res);
if (err) kfree(res);
kfree(res);
} }
return err;
} }
/*====================================================================== /*======================================================================
...@@ -261,7 +225,7 @@ static int sub_interval(resource_map_t *map, u_long base, u_long num) ...@@ -261,7 +225,7 @@ static int sub_interval(resource_map_t *map, u_long base, u_long num)
#ifdef CONFIG_PCMCIA_PROBE #ifdef CONFIG_PCMCIA_PROBE
static void do_io_probe(ioaddr_t base, ioaddr_t num) static void do_io_probe(ioaddr_t base, ioaddr_t num)
{ {
struct resource *res;
ioaddr_t i, j, bad, any; ioaddr_t i, j, bad, any;
u_char *b, hole, most; u_char *b, hole, most;
...@@ -276,11 +240,13 @@ static void do_io_probe(ioaddr_t base, ioaddr_t num) ...@@ -276,11 +240,13 @@ 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, NULL)) res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe");
if (!res)
continue; continue;
hole = inb(i); hole = inb(i);
for (j = 1; j < 8; j++) for (j = 1; j < 8; j++)
if (inb(i+j) != hole) break; if (inb(i+j) != hole) break;
free_region(res);
if ((j == 8) && (++b[hole] > b[most])) if ((j == 8) && (++b[hole] > b[most]))
most = hole; most = hole;
if (b[most] == 127) break; if (b[most] == 127) break;
...@@ -289,10 +255,12 @@ static void do_io_probe(ioaddr_t base, ioaddr_t num) ...@@ -289,10 +255,12 @@ 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, NULL)) res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe");
if (!res)
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;
free_region(res);
if (j < 8) { if (j < 8) {
if (!any) if (!any)
printk(" excluding"); printk(" excluding");
...@@ -389,18 +357,16 @@ cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size) ...@@ -389,18 +357,16 @@ cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size)
cisinfo_t info1, info2; cisinfo_t info1, info2;
int ret = 0; int ret = 0;
res1 = request_mem_region(base, size/2, "cs memory probe"); res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe");
res2 = request_mem_region(base + size/2, size/2, "cs memory probe"); res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "cs memory probe");
if (res1 && res2) { if (res1 && res2) {
ret = readable(s, res1, &info1); ret = readable(s, res1, &info1);
ret += readable(s, res2, &info2); ret += readable(s, res2, &info2);
} }
if (res2) free_region(res2);
release_resource(res2); free_region(res1);
if (res1)
release_resource(res1);
return (ret == 2) && (info1.Chains == info2.Chains); return (ret == 2) && (info1.Chains == info2.Chains);
} }
...@@ -411,18 +377,16 @@ checksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size) ...@@ -411,18 +377,16 @@ checksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size)
struct resource *res1, *res2; struct resource *res1, *res2;
int a = -1, b = -1; int a = -1, b = -1;
res1 = request_mem_region(base, size/2, "cs memory probe"); res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe");
res2 = request_mem_region(base + size/2, size/2, "cs memory probe"); res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "cs memory probe");
if (res1 && res2) { if (res1 && res2) {
a = checksum(s, res1); a = checksum(s, res1);
b = checksum(s, res2); b = checksum(s, res2);
} }
if (res2) free_region(res2);
release_resource(res2); free_region(res1);
if (res1)
release_resource(res1);
return (a == b) && (a >= 0); return (a == b) && (a >= 0);
} }
...@@ -553,6 +517,68 @@ void validate_mem(struct pcmcia_socket *s) ...@@ -553,6 +517,68 @@ void validate_mem(struct pcmcia_socket *s)
#endif /* CONFIG_PCMCIA_PROBE */ #endif /* CONFIG_PCMCIA_PROBE */
struct pcmcia_align_data {
unsigned long mask;
unsigned long offset;
resource_map_t *map;
};
static void
pcmcia_common_align(void *align_data, struct resource *res,
unsigned long size, unsigned long align)
{
struct pcmcia_align_data *data = align_data;
unsigned long start;
/*
* Ensure that we have the correct start address
*/
start = (res->start & ~data->mask) + data->offset;
if (start < res->start)
start += data->mask + 1;
res->start = start;
}
static void
pcmcia_align(void *align_data, struct resource *res,
unsigned long size, unsigned long align)
{
struct pcmcia_align_data *data = align_data;
resource_map_t *m;
pcmcia_common_align(data, res, size, align);
for (m = data->map->next; m != data->map; m = m->next) {
unsigned long start = m->base;
unsigned long end = m->base + m->num;
/*
* If the lower resources are not available, try aligning
* to this entry of the resource database to see if it'll
* fit here.
*/
if (res->start < start) {
res->start = start;
pcmcia_common_align(data, res, size, align);
}
/*
* If we're above the area which was passed in, there's
* no point proceeding.
*/
if (res->start >= res->end)
break;
if ((res->start + size) <= end)
break;
}
/*
* If we failed to find something suitable, ensure we fail.
*/
if (m == data->map)
res->start = res->end;
}
/*====================================================================== /*======================================================================
These find ranges of I/O ports or memory addresses that are not These find ranges of I/O ports or memory addresses that are not
...@@ -566,69 +592,89 @@ void validate_mem(struct pcmcia_socket *s) ...@@ -566,69 +592,89 @@ void validate_mem(struct pcmcia_socket *s)
======================================================================*/ ======================================================================*/
int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align, int find_io_region(ioaddr_t *base, ioaddr_t num, unsigned long align,
char *name, struct pcmcia_socket *s) char *name, struct pcmcia_socket *s)
{ {
ioaddr_t try; struct resource *res = make_resource(0, num, IORESOURCE_IO, name);
resource_map_t *m; struct pcmcia_align_data data;
int ret = -1; unsigned long min = *base;
int ret;
down(&rsrc_sem); if (align == 0)
for (m = io_db.next; m != &io_db; m = m->next) { align = 0x10000UL;
try = (m->base & ~(align-1)) + *base;
for (try = (try >= m->base) ? try : try+align; data.mask = align - 1;
(try >= m->base) && (try+num <= m->base+m->num); data.offset = *base & data.mask;
try += align) { data.map = &io_db;
if (request_io_resource(try, num, name, s->cb_dev) == 0) {
*base = try; #ifdef CONFIG_PCI
ret = 0; if (s->cb_dev) {
goto out; ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
} min, 0, pcmcia_align, &data);
if (!align) } else
break; #endif
{
down(&rsrc_sem);
ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, 0,
pcmcia_align, &data);
up(&rsrc_sem);
} }
}
out: if (ret != 0) {
up(&rsrc_sem); kfree(res);
return ret; } else {
*base = res->start;
}
return ret;
} }
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 low, char *name, struct pcmcia_socket *s) int low, char *name, struct pcmcia_socket *s)
{ {
u_long try; struct resource *res = make_resource(0, num, IORESOURCE_MEM, name);
resource_map_t *m; struct pcmcia_align_data data;
int ret = -1; unsigned long min, max;
int ret, i;
low = low || !(s->features & SS_CAP_PAGE_REGS); low = low || !(s->features & SS_CAP_PAGE_REGS);
down(&rsrc_sem); data.mask = align - 1;
while (1) { data.offset = *base & data.mask;
for (m = mem_db.next; m != &mem_db; m = m->next) { data.map = &mem_db;
/* first pass >1MB, second pass <1MB */
if ((low != 0) ^ (m->base < 0x100000)) for (i = 0; i < 2; i++) {
continue; if (low) {
max = 0x100000UL;
try = (m->base & ~(align-1)) + *base; min = *base < max ? *base : 0;
for (try = (try >= m->base) ? try : try+align; } else {
(try >= m->base) && (try+num <= m->base+m->num); max = ~0UL;
try += align) { min = 0x100000UL + *base;
if (request_mem_resource(try, num, name, s->cb_dev) == 0) {
*base = try;
ret = 0;
goto out;
} }
if (!align)
break; #ifdef CONFIG_PCI
} if (s->cb_dev) {
ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num,
1, min, 0,
pcmcia_align, &data);
} else
#endif
{
down(&rsrc_sem);
ret = allocate_resource(&iomem_resource, res, num, min,
max, 0, pcmcia_align, &data);
up(&rsrc_sem);
}
if (ret == 0 || low)
break;
low = 1;
} }
if (low)
break; if (ret != 0) {
low++; kfree(res);
} } else {
out: *base = res->start;
up(&rsrc_sem); }
return ret; return ret;
} }
/*====================================================================== /*======================================================================
......
...@@ -676,6 +676,12 @@ int pci_request_region(struct pci_dev *, int, char *); ...@@ -676,6 +676,12 @@ int pci_request_region(struct pci_dev *, int, char *);
void pci_release_region(struct pci_dev *, int); void pci_release_region(struct pci_dev *, int);
/* drivers/pci/bus.c */ /* drivers/pci/bus.c */
int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
unsigned long size, unsigned long align,
unsigned long min, unsigned int type_mask,
void (*alignf)(void *, struct resource *,
unsigned long, unsigned long),
void *alignf_data);
void pci_enable_bridges(struct pci_bus *bus); void pci_enable_bridges(struct pci_bus *bus);
/* New-style probing supporting hot-pluggable devices */ /* New-style probing supporting hot-pluggable devices */
......
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