mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 03:01:39 -04:00
Merge remote branch 'origin' into secretlab/next-devicetree
Merging in current state of Linus' tree to deal with merge conflicts and build failures in vio.c after merge. Conflicts: drivers/i2c/busses/i2c-cpm.c drivers/i2c/busses/i2c-mpc.c drivers/net/gianfar.c Also fixed up one line in arch/powerpc/kernel/vio.c to use the correct node pointer. Signed-off-by: Grant Likely <[email protected]>
This commit is contained in:
commit
cf9b59e9d3
6983 changed files with 473508 additions and 213620 deletions
|
|
@ -49,26 +49,6 @@ config PCMCIA_LOAD_CIS
|
|||
|
||||
If unsure, say Y.
|
||||
|
||||
config PCMCIA_IOCTL
|
||||
bool "PCMCIA control ioctl (obsolete)"
|
||||
depends on PCMCIA && ARM && !SMP && !PREEMPT
|
||||
default y
|
||||
help
|
||||
If you say Y here, the deprecated ioctl interface to the PCMCIA
|
||||
subsystem will be built. It is needed by the deprecated pcmcia-cs
|
||||
tools (cardmgr, cardctl) to function properly.
|
||||
|
||||
You should use the new pcmciautils package instead (see
|
||||
<file:Documentation/Changes> for location and details).
|
||||
|
||||
This config option will most likely be removed from kernel 2.6.35,
|
||||
the associated code from kernel 2.6.36.
|
||||
|
||||
As the PCMCIA ioctl is not locking safe, it depends on !SMP and
|
||||
!PREEMPT.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config CARDBUS
|
||||
bool "32-bit CardBus support"
|
||||
depends on PCI
|
||||
|
|
@ -234,7 +214,8 @@ config PCMCIA_PXA2XX
|
|||
depends on ARM && ARCH_PXA && PCMCIA
|
||||
depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \
|
||||
|| MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \
|
||||
|| ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2)
|
||||
|| ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \
|
||||
|| MACH_VPAC270)
|
||||
select PCMCIA_SOC_COMMON
|
||||
help
|
||||
Say Y here to include support for the PXA2xx PCMCIA controller
|
||||
|
|
@ -317,7 +298,7 @@ config ELECTRA_CF
|
|||
PA Semi Electra eval board.
|
||||
|
||||
config PCCARD_NONSTATIC
|
||||
tristate
|
||||
bool
|
||||
|
||||
config PCCARD_IODYN
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -2,15 +2,18 @@
|
|||
# Makefile for the kernel pcmcia subsystem (c/o David Hinds)
|
||||
#
|
||||
|
||||
pcmcia_core-y += cs.o rsrc_mgr.o socket_sysfs.o
|
||||
pcmcia_core-y += cs.o socket_sysfs.o
|
||||
pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o
|
||||
obj-$(CONFIG_PCCARD) += pcmcia_core.o
|
||||
|
||||
pcmcia-y += ds.o pcmcia_resource.o cistpl.o
|
||||
pcmcia-y += ds.o pcmcia_resource.o cistpl.o pcmcia_cis.o
|
||||
pcmcia-$(CONFIG_PCMCIA_IOCTL) += pcmcia_ioctl.o
|
||||
obj-$(CONFIG_PCMCIA) += pcmcia.o
|
||||
|
||||
obj-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o
|
||||
pcmcia_rsrc-y += rsrc_mgr.o
|
||||
pcmcia_rsrc-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o
|
||||
pcmcia_rsrc-$(CONFIG_PCCARD_IODYN) += rsrc_iodyn.o
|
||||
obj-$(CONFIG_PCCARD) += pcmcia_rsrc.o
|
||||
|
||||
|
||||
# socket drivers
|
||||
|
|
@ -66,6 +69,7 @@ pxa2xx-obj-$(CONFIG_MACH_PALMTC) += pxa2xx_palmtc.o
|
|||
pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o
|
||||
pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o
|
||||
pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o
|
||||
pxa2xx-obj-$(CONFIG_MACH_VPAC270) += pxa2xx_vpac270.o
|
||||
|
||||
obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y)
|
||||
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ static int bfin_cf_get_status(struct pcmcia_socket *s, u_int *sp)
|
|||
|
||||
if (bfin_cf_present(cf->cd_pfx)) {
|
||||
*sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
|
||||
s->irq.AssignedIRQ = 0;
|
||||
s->pcmcia_irq = 0;
|
||||
s->pci_irq = cf->irq;
|
||||
|
||||
} else
|
||||
|
|
|
|||
|
|
@ -94,7 +94,6 @@ int __ref cb_alloc(struct pcmcia_socket *s)
|
|||
pci_enable_bridges(bus);
|
||||
pci_bus_add_devices(bus);
|
||||
|
||||
s->irq.AssignedIRQ = s->pci_irq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,8 @@ static void __iomem *set_cis_map(struct pcmcia_socket *s,
|
|||
|
||||
/**
|
||||
* pcmcia_read_cis_mem() - low-level function to read CIS memory
|
||||
*
|
||||
* must be called with ops_mutex held
|
||||
*/
|
||||
int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
||||
u_int len, void *ptr)
|
||||
|
|
@ -138,7 +140,6 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|||
|
||||
dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
|
||||
|
||||
mutex_lock(&s->ops_mutex);
|
||||
if (attr & IS_INDIRECT) {
|
||||
/* Indirect accesses use a bunch of special registers at fixed
|
||||
locations in common memory */
|
||||
|
|
@ -153,7 +154,6 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|||
if (!sys) {
|
||||
dev_dbg(&s->dev, "could not map memory\n");
|
||||
memset(ptr, 0xff, len);
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -184,7 +184,6 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|||
if (!sys) {
|
||||
dev_dbg(&s->dev, "could not map memory\n");
|
||||
memset(ptr, 0xff, len);
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
return -1;
|
||||
}
|
||||
end = sys + s->map_size;
|
||||
|
|
@ -198,7 +197,6 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|||
addr = 0;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
dev_dbg(&s->dev, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
|
||||
*(u_char *)(ptr+0), *(u_char *)(ptr+1),
|
||||
*(u_char *)(ptr+2), *(u_char *)(ptr+3));
|
||||
|
|
@ -209,7 +207,8 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|||
/**
|
||||
* pcmcia_write_cis_mem() - low-level function to write CIS memory
|
||||
*
|
||||
* Probably only useful for writing one-byte registers.
|
||||
* Probably only useful for writing one-byte registers. Must be called
|
||||
* with ops_mutex held.
|
||||
*/
|
||||
void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
||||
u_int len, void *ptr)
|
||||
|
|
@ -220,7 +219,6 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|||
dev_dbg(&s->dev,
|
||||
"pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
|
||||
|
||||
mutex_lock(&s->ops_mutex);
|
||||
if (attr & IS_INDIRECT) {
|
||||
/* Indirect accesses use a bunch of special registers at fixed
|
||||
locations in common memory */
|
||||
|
|
@ -234,7 +232,6 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|||
((cis_width) ? MAP_16BIT : 0));
|
||||
if (!sys) {
|
||||
dev_dbg(&s->dev, "could not map memory\n");
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
return; /* FIXME: Error */
|
||||
}
|
||||
|
||||
|
|
@ -260,7 +257,6 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|||
sys = set_cis_map(s, card_offset, flags);
|
||||
if (!sys) {
|
||||
dev_dbg(&s->dev, "could not map memory\n");
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
return; /* FIXME: error */
|
||||
}
|
||||
|
||||
|
|
@ -275,7 +271,6 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
|
|||
addr = 0;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -314,7 +309,6 @@ static int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
|
||||
ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr);
|
||||
|
||||
|
|
@ -326,11 +320,11 @@ static int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
|
|||
cis->len = len;
|
||||
cis->attr = attr;
|
||||
memcpy(cis->cache, ptr, len);
|
||||
mutex_lock(&s->ops_mutex);
|
||||
list_add(&cis->node, &s->cis_cache);
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -386,6 +380,7 @@ int verify_cis_cache(struct pcmcia_socket *s)
|
|||
"no memory for verifying CIS\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
mutex_lock(&s->ops_mutex);
|
||||
list_for_each_entry(cis, &s->cis_cache, node) {
|
||||
int len = cis->len;
|
||||
|
||||
|
|
@ -395,10 +390,12 @@ int verify_cis_cache(struct pcmcia_socket *s)
|
|||
ret = pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf);
|
||||
if (ret || memcmp(buf, cis->cache, len) != 0) {
|
||||
kfree(buf);
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
kfree(buf);
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1361,106 +1358,6 @@ int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse)
|
|||
EXPORT_SYMBOL(pcmcia_parse_tuple);
|
||||
|
||||
|
||||
/**
|
||||
* pccard_read_tuple() - internal CIS tuple access
|
||||
* @s: the struct pcmcia_socket where the card is inserted
|
||||
* @function: the device function we loop for
|
||||
* @code: which CIS code shall we look for?
|
||||
* @parse: buffer where the tuple shall be parsed (or NULL, if no parse)
|
||||
*
|
||||
* pccard_read_tuple() reads out one tuple and attempts to parse it
|
||||
*/
|
||||
int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function,
|
||||
cisdata_t code, void *parse)
|
||||
{
|
||||
tuple_t tuple;
|
||||
cisdata_t *buf;
|
||||
int ret;
|
||||
|
||||
buf = kmalloc(256, GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
tuple.DesiredTuple = code;
|
||||
tuple.Attributes = 0;
|
||||
if (function == BIND_FN_ALL)
|
||||
tuple.Attributes = TUPLE_RETURN_COMMON;
|
||||
ret = pccard_get_first_tuple(s, function, &tuple);
|
||||
if (ret != 0)
|
||||
goto done;
|
||||
tuple.TupleData = buf;
|
||||
tuple.TupleOffset = 0;
|
||||
tuple.TupleDataMax = 255;
|
||||
ret = pccard_get_tuple_data(s, &tuple);
|
||||
if (ret != 0)
|
||||
goto done;
|
||||
ret = pcmcia_parse_tuple(&tuple, parse);
|
||||
done:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pccard_loop_tuple() - loop over tuples in the CIS
|
||||
* @s: the struct pcmcia_socket where the card is inserted
|
||||
* @function: the device function we loop for
|
||||
* @code: which CIS code shall we look for?
|
||||
* @parse: buffer where the tuple shall be parsed (or NULL, if no parse)
|
||||
* @priv_data: private data to be passed to the loop_tuple function.
|
||||
* @loop_tuple: function to call for each CIS entry of type @function. IT
|
||||
* gets passed the raw tuple, the paresed tuple (if @parse is
|
||||
* set) and @priv_data.
|
||||
*
|
||||
* pccard_loop_tuple() loops over all CIS entries of type @function, and
|
||||
* calls the @loop_tuple function for each entry. If the call to @loop_tuple
|
||||
* returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
|
||||
*/
|
||||
int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function,
|
||||
cisdata_t code, cisparse_t *parse, void *priv_data,
|
||||
int (*loop_tuple) (tuple_t *tuple,
|
||||
cisparse_t *parse,
|
||||
void *priv_data))
|
||||
{
|
||||
tuple_t tuple;
|
||||
cisdata_t *buf;
|
||||
int ret;
|
||||
|
||||
buf = kzalloc(256, GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tuple.TupleData = buf;
|
||||
tuple.TupleDataMax = 255;
|
||||
tuple.TupleOffset = 0;
|
||||
tuple.DesiredTuple = code;
|
||||
tuple.Attributes = 0;
|
||||
|
||||
ret = pccard_get_first_tuple(s, function, &tuple);
|
||||
while (!ret) {
|
||||
if (pccard_get_tuple_data(s, &tuple))
|
||||
goto next_entry;
|
||||
|
||||
if (parse)
|
||||
if (pcmcia_parse_tuple(&tuple, parse))
|
||||
goto next_entry;
|
||||
|
||||
ret = loop_tuple(&tuple, parse, priv_data);
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
next_entry:
|
||||
ret = pccard_get_next_tuple(s, function, &tuple);
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pccard_validate_cis() - check whether card has a sensible CIS
|
||||
* @s: the struct pcmcia_socket we are to check
|
||||
|
|
@ -1484,6 +1381,11 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info)
|
|||
if (!s)
|
||||
return -EINVAL;
|
||||
|
||||
if (s->functions) {
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* We do not want to validate the CIS cache... */
|
||||
mutex_lock(&s->ops_mutex);
|
||||
destroy_cis_cache(s);
|
||||
|
|
@ -1629,7 +1531,7 @@ static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf,
|
|||
}
|
||||
|
||||
|
||||
static ssize_t pccard_show_cis(struct kobject *kobj,
|
||||
static ssize_t pccard_show_cis(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
|
|
@ -1639,7 +1541,7 @@ static ssize_t pccard_show_cis(struct kobject *kobj,
|
|||
count = 0;
|
||||
else {
|
||||
struct pcmcia_socket *s;
|
||||
unsigned int chains;
|
||||
unsigned int chains = 1;
|
||||
|
||||
if (off + count > size)
|
||||
count = size - off;
|
||||
|
|
@ -1648,7 +1550,7 @@ static ssize_t pccard_show_cis(struct kobject *kobj,
|
|||
|
||||
if (!(s->state & SOCKET_PRESENT))
|
||||
return -ENODEV;
|
||||
if (pccard_validate_cis(s, &chains))
|
||||
if (!s->functions && pccard_validate_cis(s, &chains))
|
||||
return -EIO;
|
||||
if (!chains)
|
||||
return -ENODATA;
|
||||
|
|
@ -1660,7 +1562,7 @@ static ssize_t pccard_show_cis(struct kobject *kobj,
|
|||
}
|
||||
|
||||
|
||||
static ssize_t pccard_store_cis(struct kobject *kobj,
|
||||
static ssize_t pccard_store_cis(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -337,7 +337,6 @@ static void socket_shutdown(struct pcmcia_socket *s)
|
|||
s->socket = dead_socket;
|
||||
s->ops->init(s);
|
||||
s->ops->set_socket(s, &s->socket);
|
||||
s->irq.AssignedIRQ = s->irq.Config = 0;
|
||||
s->lock_count = 0;
|
||||
kfree(s->fake_cis);
|
||||
s->fake_cis = NULL;
|
||||
|
|
@ -671,20 +670,22 @@ static int pccardd(void *__skt)
|
|||
socket_remove(skt);
|
||||
if (sysfs_events & PCMCIA_UEVENT_INSERT)
|
||||
socket_insert(skt);
|
||||
if ((sysfs_events & PCMCIA_UEVENT_RESUME) &&
|
||||
!(skt->state & SOCKET_CARDBUS)) {
|
||||
ret = socket_resume(skt);
|
||||
if (!ret && skt->callback)
|
||||
skt->callback->resume(skt);
|
||||
}
|
||||
if ((sysfs_events & PCMCIA_UEVENT_SUSPEND) &&
|
||||
!(skt->state & SOCKET_CARDBUS)) {
|
||||
if (skt->callback)
|
||||
ret = skt->callback->suspend(skt);
|
||||
else
|
||||
ret = 0;
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
socket_suspend(skt);
|
||||
msleep(100);
|
||||
}
|
||||
}
|
||||
if ((sysfs_events & PCMCIA_UEVENT_RESUME) &&
|
||||
!(skt->state & SOCKET_CARDBUS)) {
|
||||
ret = socket_resume(skt);
|
||||
if (!ret && skt->callback)
|
||||
skt->callback->resume(skt);
|
||||
}
|
||||
if ((sysfs_events & PCMCIA_UEVENT_REQUERY) &&
|
||||
!(skt->state & SOCKET_CARDBUS)) {
|
||||
|
|
|
|||
|
|
@ -52,13 +52,11 @@ struct cis_cache_entry {
|
|||
|
||||
struct pccard_resource_ops {
|
||||
int (*validate_mem) (struct pcmcia_socket *s);
|
||||
int (*adjust_io_region) (struct resource *res,
|
||||
unsigned long r_start,
|
||||
unsigned long r_end,
|
||||
struct pcmcia_socket *s);
|
||||
struct resource* (*find_io) (unsigned long base, int num,
|
||||
unsigned long align,
|
||||
struct pcmcia_socket *s);
|
||||
int (*find_io) (struct pcmcia_socket *s,
|
||||
unsigned int attr,
|
||||
unsigned int *base,
|
||||
unsigned int num,
|
||||
unsigned int align);
|
||||
struct resource* (*find_mem) (unsigned long base, unsigned long num,
|
||||
unsigned long align, int low,
|
||||
struct pcmcia_socket *s);
|
||||
|
|
@ -88,6 +86,14 @@ struct pccard_resource_ops {
|
|||
#define SOCKET_CARDBUS_CONFIG 0x10000
|
||||
|
||||
|
||||
/*
|
||||
* Stuff internal to module "pcmcia_rsrc":
|
||||
*/
|
||||
extern int static_init(struct pcmcia_socket *s);
|
||||
extern struct resource *pcmcia_make_resource(unsigned long start,
|
||||
unsigned long end,
|
||||
int flags, const char *name);
|
||||
|
||||
/*
|
||||
* Stuff internal to module "pcmcia_core":
|
||||
*/
|
||||
|
|
@ -149,6 +155,8 @@ extern struct resource *pcmcia_find_mem_region(u_long base,
|
|||
int low,
|
||||
struct pcmcia_socket *s);
|
||||
|
||||
void pcmcia_cleanup_irq(struct pcmcia_socket *s);
|
||||
int pcmcia_setup_irq(struct pcmcia_device *p_dev);
|
||||
|
||||
/* cistpl.c */
|
||||
extern struct bin_attribute pccard_cis_attr;
|
||||
|
|
|
|||
|
|
@ -146,7 +146,6 @@ static irqreturn_t db1200_pcmcia_cdirq(int irq, void *data)
|
|||
static int db1x_pcmcia_setup_irqs(struct db1x_pcmcia_sock *sock)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
if (sock->stschg_irq != -1) {
|
||||
ret = request_irq(sock->stschg_irq, db1000_pcmcia_stschgirq,
|
||||
|
|
@ -162,8 +161,6 @@ static int db1x_pcmcia_setup_irqs(struct db1x_pcmcia_sock *sock)
|
|||
* active one disabled.
|
||||
*/
|
||||
if (sock->board_type == BOARD_TYPE_DB1200) {
|
||||
local_irq_save(flags);
|
||||
|
||||
ret = request_irq(sock->insert_irq, db1200_pcmcia_cdirq,
|
||||
IRQF_DISABLED, "pcmcia_insert", sock);
|
||||
if (ret)
|
||||
|
|
@ -173,17 +170,14 @@ static int db1x_pcmcia_setup_irqs(struct db1x_pcmcia_sock *sock)
|
|||
IRQF_DISABLED, "pcmcia_eject", sock);
|
||||
if (ret) {
|
||||
free_irq(sock->insert_irq, sock);
|
||||
local_irq_restore(flags);
|
||||
goto out1;
|
||||
}
|
||||
|
||||
/* disable the currently active one */
|
||||
/* enable the currently silent one */
|
||||
if (db1200_card_inserted(sock))
|
||||
disable_irq_nosync(sock->insert_irq);
|
||||
enable_irq(sock->eject_irq);
|
||||
else
|
||||
disable_irq_nosync(sock->eject_irq);
|
||||
|
||||
local_irq_restore(flags);
|
||||
enable_irq(sock->insert_irq);
|
||||
} else {
|
||||
/* all other (older) Db1x00 boards use a GPIO to show
|
||||
* card detection status: use both-edge triggers.
|
||||
|
|
|
|||
|
|
@ -335,7 +335,6 @@ static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *le
|
|||
|
||||
mutex_lock(&s->ops_mutex);
|
||||
list_del(&p_dev->socket_device_list);
|
||||
p_dev->_removed = 1;
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
|
||||
dev_dbg(&p_dev->dev, "unregistering device\n");
|
||||
|
|
@ -372,8 +371,6 @@ static int pcmcia_device_remove(struct device *dev)
|
|||
if (p_drv->remove)
|
||||
p_drv->remove(p_dev);
|
||||
|
||||
p_dev->dev_node = NULL;
|
||||
|
||||
/* check for proper unloading */
|
||||
if (p_dev->_irq || p_dev->_io || p_dev->_locked)
|
||||
dev_printk(KERN_INFO, dev,
|
||||
|
|
@ -480,15 +477,6 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
|
|||
}
|
||||
|
||||
|
||||
/* device_add_lock is needed to avoid double registration by cardmgr and kernel.
|
||||
* Serializes pcmcia_device_add; will most likely be removed in future.
|
||||
*
|
||||
* While it has the caveat that adding new PCMCIA devices inside(!) device_register()
|
||||
* won't work, this doesn't matter much at the moment: the driver core doesn't
|
||||
* support it either.
|
||||
*/
|
||||
static DEFINE_MUTEX(device_add_lock);
|
||||
|
||||
struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int function)
|
||||
{
|
||||
struct pcmcia_device *p_dev, *tmp_dev;
|
||||
|
|
@ -498,8 +486,6 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu
|
|||
if (!s)
|
||||
return NULL;
|
||||
|
||||
mutex_lock(&device_add_lock);
|
||||
|
||||
pr_debug("adding device to %d, function %d\n", s->sock, function);
|
||||
|
||||
p_dev = kzalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
|
||||
|
|
@ -539,8 +525,8 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu
|
|||
|
||||
/*
|
||||
* p_dev->function_config must be the same for all card functions.
|
||||
* Note that this is serialized by the device_add_lock, so that
|
||||
* only one such struct will be created.
|
||||
* Note that this is serialized by ops_mutex, so that only one
|
||||
* such struct will be created.
|
||||
*/
|
||||
list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list)
|
||||
if (p_dev->func == tmp_dev->func) {
|
||||
|
|
@ -553,28 +539,31 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu
|
|||
/* Add to the list in pcmcia_bus_socket */
|
||||
list_add(&p_dev->socket_device_list, &s->devices_list);
|
||||
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
if (pcmcia_setup_irq(p_dev))
|
||||
dev_warn(&p_dev->dev,
|
||||
"IRQ setup failed -- device might not work\n");
|
||||
|
||||
if (!p_dev->function_config) {
|
||||
dev_dbg(&p_dev->dev, "creating config_t\n");
|
||||
p_dev->function_config = kzalloc(sizeof(struct config_t),
|
||||
GFP_KERNEL);
|
||||
if (!p_dev->function_config)
|
||||
if (!p_dev->function_config) {
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
goto err_unreg;
|
||||
}
|
||||
kref_init(&p_dev->function_config->ref);
|
||||
}
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
|
||||
dev_printk(KERN_NOTICE, &p_dev->dev,
|
||||
"pcmcia: registering new device %s\n",
|
||||
p_dev->devname);
|
||||
"pcmcia: registering new device %s (IRQ: %d)\n",
|
||||
p_dev->devname, p_dev->irq);
|
||||
|
||||
pcmcia_device_query(p_dev);
|
||||
|
||||
if (device_register(&p_dev->dev))
|
||||
goto err_unreg;
|
||||
|
||||
mutex_unlock(&device_add_lock);
|
||||
|
||||
return p_dev;
|
||||
|
||||
err_unreg:
|
||||
|
|
@ -592,7 +581,6 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu
|
|||
kfree(p_dev->devname);
|
||||
kfree(p_dev);
|
||||
err_put:
|
||||
mutex_unlock(&device_add_lock);
|
||||
pcmcia_put_socket(s);
|
||||
|
||||
return NULL;
|
||||
|
|
@ -654,14 +642,7 @@ static int pcmcia_requery_callback(struct device *dev, void * _data)
|
|||
|
||||
static void pcmcia_requery(struct pcmcia_socket *s)
|
||||
{
|
||||
int present, has_pfc;
|
||||
|
||||
mutex_lock(&s->ops_mutex);
|
||||
present = s->pcmcia_state.present;
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
|
||||
if (!present)
|
||||
return;
|
||||
int has_pfc;
|
||||
|
||||
if (s->functions == 0) {
|
||||
pcmcia_card_add(s);
|
||||
|
|
@ -687,12 +668,10 @@ static void pcmcia_requery(struct pcmcia_socket *s)
|
|||
new_funcs = mfc.nfn;
|
||||
else
|
||||
new_funcs = 1;
|
||||
if (old_funcs > new_funcs) {
|
||||
if (old_funcs != new_funcs) {
|
||||
/* we need to re-start */
|
||||
pcmcia_card_remove(s, NULL);
|
||||
pcmcia_card_add(s);
|
||||
} else if (new_funcs > old_funcs) {
|
||||
s->functions = new_funcs;
|
||||
pcmcia_device_add(s, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -728,6 +707,8 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
|
|||
struct pcmcia_socket *s = dev->socket;
|
||||
const struct firmware *fw;
|
||||
int ret = -ENOMEM;
|
||||
cistpl_longlink_mfc_t mfc;
|
||||
int old_funcs, new_funcs = 1;
|
||||
|
||||
if (!filename)
|
||||
return -EINVAL;
|
||||
|
|
@ -750,6 +731,14 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
|
|||
goto release;
|
||||
}
|
||||
|
||||
/* we need to re-start if the number of functions changed */
|
||||
old_funcs = s->functions;
|
||||
if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC,
|
||||
&mfc))
|
||||
new_funcs = mfc.nfn;
|
||||
|
||||
if (old_funcs != new_funcs)
|
||||
ret = -EBUSY;
|
||||
|
||||
/* update information */
|
||||
pcmcia_device_query(dev);
|
||||
|
|
@ -820,11 +809,12 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev,
|
|||
}
|
||||
|
||||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) {
|
||||
if (dev->device_no != did->device_no)
|
||||
return 0;
|
||||
dev_dbg(&dev->dev, "this is a pseudo-multi-function device\n");
|
||||
mutex_lock(&dev->socket->ops_mutex);
|
||||
dev->socket->pcmcia_state.has_pfc = 1;
|
||||
mutex_unlock(&dev->socket->ops_mutex);
|
||||
if (dev->device_no != did->device_no)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) {
|
||||
|
|
@ -835,7 +825,7 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev,
|
|||
|
||||
/* if this is a pseudo-multi-function device,
|
||||
* we need explicit matches */
|
||||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO)
|
||||
if (dev->socket->pcmcia_state.has_pfc)
|
||||
return 0;
|
||||
if (dev->device_no)
|
||||
return 0;
|
||||
|
|
@ -858,10 +848,8 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev,
|
|||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) {
|
||||
dev_dbg(&dev->dev, "device needs a fake CIS\n");
|
||||
if (!dev->socket->fake_cis)
|
||||
pcmcia_load_firmware(dev, did->cisfile);
|
||||
|
||||
if (!dev->socket->fake_cis)
|
||||
return 0;
|
||||
if (pcmcia_load_firmware(dev, did->cisfile))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (did->match_flags & PCMCIA_DEV_ID_MATCH_ANONYMOUS) {
|
||||
|
|
@ -1254,20 +1242,19 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
|
|||
|
||||
switch (event) {
|
||||
case CS_EVENT_CARD_REMOVAL:
|
||||
mutex_lock(&s->ops_mutex);
|
||||
s->pcmcia_state.present = 0;
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
atomic_set(&skt->present, 0);
|
||||
pcmcia_card_remove(skt, NULL);
|
||||
handle_event(skt, event);
|
||||
mutex_lock(&s->ops_mutex);
|
||||
destroy_cis_cache(s);
|
||||
pcmcia_cleanup_irq(s);
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
break;
|
||||
|
||||
case CS_EVENT_CARD_INSERTION:
|
||||
atomic_set(&skt->present, 1);
|
||||
mutex_lock(&s->ops_mutex);
|
||||
s->pcmcia_state.has_pfc = 0;
|
||||
s->pcmcia_state.present = 1;
|
||||
destroy_cis_cache(s); /* to be on the safe side... */
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
pcmcia_card_add(skt);
|
||||
|
|
@ -1286,6 +1273,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
|
|||
destroy_cis_cache(skt);
|
||||
kfree(skt->fake_cis);
|
||||
skt->fake_cis = NULL;
|
||||
s->functions = 0;
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
/* now, add the new card */
|
||||
ds_event(skt, CS_EVENT_CARD_INSERTION,
|
||||
|
|
@ -1307,7 +1295,13 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
|
|||
return 0;
|
||||
} /* ds_event */
|
||||
|
||||
|
||||
/*
|
||||
* NOTE: This is racy. There's no guarantee the card will still be
|
||||
* physically present, even if the call to this function returns
|
||||
* non-NULL. Furthermore, the device driver most likely is unbound
|
||||
* almost immediately, so the timeframe where pcmcia_dev_present
|
||||
* returns NULL is probably really really small.
|
||||
*/
|
||||
struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev)
|
||||
{
|
||||
struct pcmcia_device *p_dev;
|
||||
|
|
@ -1317,22 +1311,9 @@ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev)
|
|||
if (!p_dev)
|
||||
return NULL;
|
||||
|
||||
mutex_lock(&p_dev->socket->ops_mutex);
|
||||
if (!p_dev->socket->pcmcia_state.present)
|
||||
goto out;
|
||||
if (atomic_read(&p_dev->socket->present) != 0)
|
||||
ret = p_dev;
|
||||
|
||||
if (p_dev->socket->pcmcia_state.dead)
|
||||
goto out;
|
||||
|
||||
if (p_dev->_removed)
|
||||
goto out;
|
||||
|
||||
if (p_dev->suspended)
|
||||
goto out;
|
||||
|
||||
ret = p_dev;
|
||||
out:
|
||||
mutex_unlock(&p_dev->socket->ops_mutex);
|
||||
pcmcia_put_dev(p_dev);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1382,6 +1363,8 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
atomic_set(&socket->present, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1393,10 +1376,6 @@ static void pcmcia_bus_remove_socket(struct device *dev,
|
|||
if (!socket)
|
||||
return;
|
||||
|
||||
mutex_lock(&socket->ops_mutex);
|
||||
socket->pcmcia_state.dead = 1;
|
||||
mutex_unlock(&socket->ops_mutex);
|
||||
|
||||
pccard_register_pcmcia(socket, NULL);
|
||||
|
||||
/* unregister any unbound devices */
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ static int omap_cf_get_status(struct pcmcia_socket *s, u_int *sp)
|
|||
|
||||
*sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
|
||||
cf = container_of(s, struct omap_cf_socket, socket);
|
||||
s->irq.AssignedIRQ = 0;
|
||||
s->pcmcia_irq = 0;
|
||||
s->pci_irq = cf->irq;
|
||||
} else
|
||||
*sp = 0;
|
||||
|
|
|
|||
356
drivers/pcmcia/pcmcia_cis.c
Normal file
356
drivers/pcmcia/pcmcia_cis.c
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
/*
|
||||
* PCMCIA high-level CIS access functions
|
||||
*
|
||||
* The initial developer of the original code is David A. Hinds
|
||||
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
|
||||
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
|
||||
*
|
||||
* Copyright (C) 1999 David A. Hinds
|
||||
* Copyright (C) 2004-2009 Dominik Brodowski
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include <pcmcia/cs_types.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/ss.h>
|
||||
#include <pcmcia/cs.h>
|
||||
#include <pcmcia/ds.h>
|
||||
#include "cs_internal.h"
|
||||
|
||||
|
||||
/**
|
||||
* pccard_read_tuple() - internal CIS tuple access
|
||||
* @s: the struct pcmcia_socket where the card is inserted
|
||||
* @function: the device function we loop for
|
||||
* @code: which CIS code shall we look for?
|
||||
* @parse: buffer where the tuple shall be parsed (or NULL, if no parse)
|
||||
*
|
||||
* pccard_read_tuple() reads out one tuple and attempts to parse it
|
||||
*/
|
||||
int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function,
|
||||
cisdata_t code, void *parse)
|
||||
{
|
||||
tuple_t tuple;
|
||||
cisdata_t *buf;
|
||||
int ret;
|
||||
|
||||
buf = kmalloc(256, GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
tuple.DesiredTuple = code;
|
||||
tuple.Attributes = 0;
|
||||
if (function == BIND_FN_ALL)
|
||||
tuple.Attributes = TUPLE_RETURN_COMMON;
|
||||
ret = pccard_get_first_tuple(s, function, &tuple);
|
||||
if (ret != 0)
|
||||
goto done;
|
||||
tuple.TupleData = buf;
|
||||
tuple.TupleOffset = 0;
|
||||
tuple.TupleDataMax = 255;
|
||||
ret = pccard_get_tuple_data(s, &tuple);
|
||||
if (ret != 0)
|
||||
goto done;
|
||||
ret = pcmcia_parse_tuple(&tuple, parse);
|
||||
done:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pccard_loop_tuple() - loop over tuples in the CIS
|
||||
* @s: the struct pcmcia_socket where the card is inserted
|
||||
* @function: the device function we loop for
|
||||
* @code: which CIS code shall we look for?
|
||||
* @parse: buffer where the tuple shall be parsed (or NULL, if no parse)
|
||||
* @priv_data: private data to be passed to the loop_tuple function.
|
||||
* @loop_tuple: function to call for each CIS entry of type @function. IT
|
||||
* gets passed the raw tuple, the paresed tuple (if @parse is
|
||||
* set) and @priv_data.
|
||||
*
|
||||
* pccard_loop_tuple() loops over all CIS entries of type @function, and
|
||||
* calls the @loop_tuple function for each entry. If the call to @loop_tuple
|
||||
* returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
|
||||
*/
|
||||
int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function,
|
||||
cisdata_t code, cisparse_t *parse, void *priv_data,
|
||||
int (*loop_tuple) (tuple_t *tuple,
|
||||
cisparse_t *parse,
|
||||
void *priv_data))
|
||||
{
|
||||
tuple_t tuple;
|
||||
cisdata_t *buf;
|
||||
int ret;
|
||||
|
||||
buf = kzalloc(256, GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tuple.TupleData = buf;
|
||||
tuple.TupleDataMax = 255;
|
||||
tuple.TupleOffset = 0;
|
||||
tuple.DesiredTuple = code;
|
||||
tuple.Attributes = 0;
|
||||
|
||||
ret = pccard_get_first_tuple(s, function, &tuple);
|
||||
while (!ret) {
|
||||
if (pccard_get_tuple_data(s, &tuple))
|
||||
goto next_entry;
|
||||
|
||||
if (parse)
|
||||
if (pcmcia_parse_tuple(&tuple, parse))
|
||||
goto next_entry;
|
||||
|
||||
ret = loop_tuple(&tuple, parse, priv_data);
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
next_entry:
|
||||
ret = pccard_get_next_tuple(s, function, &tuple);
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct pcmcia_cfg_mem {
|
||||
struct pcmcia_device *p_dev;
|
||||
void *priv_data;
|
||||
int (*conf_check) (struct pcmcia_device *p_dev,
|
||||
cistpl_cftable_entry_t *cfg,
|
||||
cistpl_cftable_entry_t *dflt,
|
||||
unsigned int vcc,
|
||||
void *priv_data);
|
||||
cisparse_t parse;
|
||||
cistpl_cftable_entry_t dflt;
|
||||
};
|
||||
|
||||
/**
|
||||
* pcmcia_do_loop_config() - internal helper for pcmcia_loop_config()
|
||||
*
|
||||
* pcmcia_do_loop_config() is the internal callback for the call from
|
||||
* pcmcia_loop_config() to pccard_loop_tuple(). Data is transferred
|
||||
* by a struct pcmcia_cfg_mem.
|
||||
*/
|
||||
static int pcmcia_do_loop_config(tuple_t *tuple, cisparse_t *parse, void *priv)
|
||||
{
|
||||
cistpl_cftable_entry_t *cfg = &parse->cftable_entry;
|
||||
struct pcmcia_cfg_mem *cfg_mem = priv;
|
||||
|
||||
/* default values */
|
||||
cfg_mem->p_dev->conf.ConfigIndex = cfg->index;
|
||||
if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
|
||||
cfg_mem->dflt = *cfg;
|
||||
|
||||
return cfg_mem->conf_check(cfg_mem->p_dev, cfg, &cfg_mem->dflt,
|
||||
cfg_mem->p_dev->socket->socket.Vcc,
|
||||
cfg_mem->priv_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* pcmcia_loop_config() - loop over configuration options
|
||||
* @p_dev: the struct pcmcia_device which we need to loop for.
|
||||
* @conf_check: function to call for each configuration option.
|
||||
* It gets passed the struct pcmcia_device, the CIS data
|
||||
* describing the configuration option, and private data
|
||||
* being passed to pcmcia_loop_config()
|
||||
* @priv_data: private data to be passed to the conf_check function.
|
||||
*
|
||||
* pcmcia_loop_config() loops over all configuration options, and calls
|
||||
* the driver-specific conf_check() for each one, checking whether
|
||||
* it is a valid one. Returns 0 on success or errorcode otherwise.
|
||||
*/
|
||||
int pcmcia_loop_config(struct pcmcia_device *p_dev,
|
||||
int (*conf_check) (struct pcmcia_device *p_dev,
|
||||
cistpl_cftable_entry_t *cfg,
|
||||
cistpl_cftable_entry_t *dflt,
|
||||
unsigned int vcc,
|
||||
void *priv_data),
|
||||
void *priv_data)
|
||||
{
|
||||
struct pcmcia_cfg_mem *cfg_mem;
|
||||
int ret;
|
||||
|
||||
cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL);
|
||||
if (cfg_mem == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
cfg_mem->p_dev = p_dev;
|
||||
cfg_mem->conf_check = conf_check;
|
||||
cfg_mem->priv_data = priv_data;
|
||||
|
||||
ret = pccard_loop_tuple(p_dev->socket, p_dev->func,
|
||||
CISTPL_CFTABLE_ENTRY, &cfg_mem->parse,
|
||||
cfg_mem, pcmcia_do_loop_config);
|
||||
|
||||
kfree(cfg_mem);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(pcmcia_loop_config);
|
||||
|
||||
|
||||
struct pcmcia_loop_mem {
|
||||
struct pcmcia_device *p_dev;
|
||||
void *priv_data;
|
||||
int (*loop_tuple) (struct pcmcia_device *p_dev,
|
||||
tuple_t *tuple,
|
||||
void *priv_data);
|
||||
};
|
||||
|
||||
/**
|
||||
* pcmcia_do_loop_tuple() - internal helper for pcmcia_loop_config()
|
||||
*
|
||||
* pcmcia_do_loop_tuple() is the internal callback for the call from
|
||||
* pcmcia_loop_tuple() to pccard_loop_tuple(). Data is transferred
|
||||
* by a struct pcmcia_cfg_mem.
|
||||
*/
|
||||
static int pcmcia_do_loop_tuple(tuple_t *tuple, cisparse_t *parse, void *priv)
|
||||
{
|
||||
struct pcmcia_loop_mem *loop = priv;
|
||||
|
||||
return loop->loop_tuple(loop->p_dev, tuple, loop->priv_data);
|
||||
};
|
||||
|
||||
/**
|
||||
* pcmcia_loop_tuple() - loop over tuples in the CIS
|
||||
* @p_dev: the struct pcmcia_device which we need to loop for.
|
||||
* @code: which CIS code shall we look for?
|
||||
* @priv_data: private data to be passed to the loop_tuple function.
|
||||
* @loop_tuple: function to call for each CIS entry of type @function. IT
|
||||
* gets passed the raw tuple and @priv_data.
|
||||
*
|
||||
* pcmcia_loop_tuple() loops over all CIS entries of type @function, and
|
||||
* calls the @loop_tuple function for each entry. If the call to @loop_tuple
|
||||
* returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
|
||||
*/
|
||||
int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code,
|
||||
int (*loop_tuple) (struct pcmcia_device *p_dev,
|
||||
tuple_t *tuple,
|
||||
void *priv_data),
|
||||
void *priv_data)
|
||||
{
|
||||
struct pcmcia_loop_mem loop = {
|
||||
.p_dev = p_dev,
|
||||
.loop_tuple = loop_tuple,
|
||||
.priv_data = priv_data};
|
||||
|
||||
return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL,
|
||||
&loop, pcmcia_do_loop_tuple);
|
||||
}
|
||||
EXPORT_SYMBOL(pcmcia_loop_tuple);
|
||||
|
||||
|
||||
struct pcmcia_loop_get {
|
||||
size_t len;
|
||||
cisdata_t **buf;
|
||||
};
|
||||
|
||||
/**
|
||||
* pcmcia_do_get_tuple() - internal helper for pcmcia_get_tuple()
|
||||
*
|
||||
* pcmcia_do_get_tuple() is the internal callback for the call from
|
||||
* pcmcia_get_tuple() to pcmcia_loop_tuple(). As we're only interested in
|
||||
* the first tuple, return 0 unconditionally. Create a memory buffer large
|
||||
* enough to hold the content of the tuple, and fill it with the tuple data.
|
||||
* The caller is responsible to free the buffer.
|
||||
*/
|
||||
static int pcmcia_do_get_tuple(struct pcmcia_device *p_dev, tuple_t *tuple,
|
||||
void *priv)
|
||||
{
|
||||
struct pcmcia_loop_get *get = priv;
|
||||
|
||||
*get->buf = kzalloc(tuple->TupleDataLen, GFP_KERNEL);
|
||||
if (*get->buf) {
|
||||
get->len = tuple->TupleDataLen;
|
||||
memcpy(*get->buf, tuple->TupleData, tuple->TupleDataLen);
|
||||
} else
|
||||
dev_dbg(&p_dev->dev, "do_get_tuple: out of memory\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcmcia_get_tuple() - get first tuple from CIS
|
||||
* @p_dev: the struct pcmcia_device which we need to loop for.
|
||||
* @code: which CIS code shall we look for?
|
||||
* @buf: pointer to store the buffer to.
|
||||
*
|
||||
* pcmcia_get_tuple() gets the content of the first CIS entry of type @code.
|
||||
* It returns the buffer length (or zero). The caller is responsible to free
|
||||
* the buffer passed in @buf.
|
||||
*/
|
||||
size_t pcmcia_get_tuple(struct pcmcia_device *p_dev, cisdata_t code,
|
||||
unsigned char **buf)
|
||||
{
|
||||
struct pcmcia_loop_get get = {
|
||||
.len = 0,
|
||||
.buf = buf,
|
||||
};
|
||||
|
||||
*get.buf = NULL;
|
||||
pcmcia_loop_tuple(p_dev, code, pcmcia_do_get_tuple, &get);
|
||||
|
||||
return get.len;
|
||||
}
|
||||
EXPORT_SYMBOL(pcmcia_get_tuple);
|
||||
|
||||
|
||||
/**
|
||||
* pcmcia_do_get_mac() - internal helper for pcmcia_get_mac_from_cis()
|
||||
*
|
||||
* pcmcia_do_get_mac() is the internal callback for the call from
|
||||
* pcmcia_get_mac_from_cis() to pcmcia_loop_tuple(). We check whether the
|
||||
* tuple contains a proper LAN_NODE_ID of length 6, and copy the data
|
||||
* to struct net_device->dev_addr[i].
|
||||
*/
|
||||
static int pcmcia_do_get_mac(struct pcmcia_device *p_dev, tuple_t *tuple,
|
||||
void *priv)
|
||||
{
|
||||
struct net_device *dev = priv;
|
||||
int i;
|
||||
|
||||
if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID)
|
||||
return -EINVAL;
|
||||
if (tuple->TupleDataLen < ETH_ALEN + 2) {
|
||||
dev_warn(&p_dev->dev, "Invalid CIS tuple length for "
|
||||
"LAN_NODE_ID\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (tuple->TupleData[1] != ETH_ALEN) {
|
||||
dev_warn(&p_dev->dev, "Invalid header for LAN_NODE_ID\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; i < 6; i++)
|
||||
dev->dev_addr[i] = tuple->TupleData[i+2];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcmcia_get_mac_from_cis() - read out MAC address from CISTPL_FUNCE
|
||||
* @p_dev: the struct pcmcia_device for which we want the address.
|
||||
* @dev: a properly prepared struct net_device to store the info to.
|
||||
*
|
||||
* pcmcia_get_mac_from_cis() reads out the hardware MAC address from
|
||||
* CISTPL_FUNCE and stores it into struct net_device *dev->dev_addr which
|
||||
* must be set up properly by the driver (see examples!).
|
||||
*/
|
||||
int pcmcia_get_mac_from_cis(struct pcmcia_device *p_dev, struct net_device *dev)
|
||||
{
|
||||
return pcmcia_loop_tuple(p_dev, CISTPL_FUNCE, pcmcia_do_get_mac, dev);
|
||||
}
|
||||
EXPORT_SYMBOL(pcmcia_get_mac_from_cis);
|
||||
|
||||
|
|
@ -301,7 +301,9 @@ static int pccard_get_status(struct pcmcia_socket *s,
|
|||
(c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) {
|
||||
u_char reg;
|
||||
if (c->CardValues & PRESENT_PIN_REPLACE) {
|
||||
mutex_lock(&s->ops_mutex);
|
||||
pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, ®);
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
status->CardState |=
|
||||
(reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0;
|
||||
status->CardState |=
|
||||
|
|
@ -315,7 +317,9 @@ static int pccard_get_status(struct pcmcia_socket *s,
|
|||
status->CardState |= CS_EVENT_READY_CHANGE;
|
||||
}
|
||||
if (c->CardValues & PRESENT_EXT_STATUS) {
|
||||
mutex_lock(&s->ops_mutex);
|
||||
pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, ®);
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
status->CardState |=
|
||||
(reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0;
|
||||
}
|
||||
|
|
@ -351,7 +355,7 @@ static int pccard_get_configuration_info(struct pcmcia_socket *s,
|
|||
if (s->state & SOCKET_CARDBUS_CONFIG) {
|
||||
config->Attributes = CONF_VALID_CLIENT;
|
||||
config->IntType = INT_CARDBUS;
|
||||
config->AssignedIRQ = s->irq.AssignedIRQ;
|
||||
config->AssignedIRQ = s->pcmcia_irq;
|
||||
if (config->AssignedIRQ)
|
||||
config->Attributes |= CONF_ENABLE_IRQ;
|
||||
if (s->io[0].res) {
|
||||
|
|
@ -391,7 +395,7 @@ static int pccard_get_configuration_info(struct pcmcia_socket *s,
|
|||
config->ExtStatus = c->ExtStatus;
|
||||
config->Present = config->CardValues = c->CardValues;
|
||||
config->IRQAttributes = c->irq.Attributes;
|
||||
config->AssignedIRQ = s->irq.AssignedIRQ;
|
||||
config->AssignedIRQ = s->pcmcia_irq;
|
||||
config->BasePort1 = c->io.BasePort1;
|
||||
config->NumPorts1 = c->io.NumPorts1;
|
||||
config->Attributes1 = c->io.Attributes1;
|
||||
|
|
@ -571,7 +575,6 @@ static struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s)
|
|||
|
||||
static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int first)
|
||||
{
|
||||
dev_node_t *node;
|
||||
struct pcmcia_device *p_dev;
|
||||
struct pcmcia_driver *p_drv;
|
||||
int ret = 0;
|
||||
|
|
@ -633,21 +636,13 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int
|
|||
goto err_put;
|
||||
}
|
||||
|
||||
if (first)
|
||||
node = p_dev->dev_node;
|
||||
else
|
||||
for (node = p_dev->dev_node; node; node = node->next)
|
||||
if (node == bind_info->next)
|
||||
break;
|
||||
if (!node) {
|
||||
if (!first) {
|
||||
ret = -ENODEV;
|
||||
goto err_put;
|
||||
}
|
||||
|
||||
strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN);
|
||||
bind_info->major = node->major;
|
||||
bind_info->minor = node->minor;
|
||||
bind_info->next = node->next;
|
||||
strlcpy(bind_info->name, dev_name(&p_dev->dev), DEV_NAME_LEN);
|
||||
bind_info->next = NULL;
|
||||
|
||||
err_put:
|
||||
pcmcia_put_dev(p_dev);
|
||||
|
|
@ -711,7 +706,7 @@ static int ds_open(struct inode *inode, struct file *file)
|
|||
warning_printed = 1;
|
||||
}
|
||||
|
||||
if (s->pcmcia_state.present)
|
||||
if (atomic_read(&s->present))
|
||||
queue_event(user, CS_EVENT_CARD_INSERTION);
|
||||
out:
|
||||
unlock_kernel();
|
||||
|
|
@ -770,9 +765,6 @@ static ssize_t ds_read(struct file *file, char __user *buf,
|
|||
return -EIO;
|
||||
|
||||
s = user->socket;
|
||||
if (s->pcmcia_state.dead)
|
||||
return -EIO;
|
||||
|
||||
ret = wait_event_interruptible(s->queue, !queue_empty(user));
|
||||
if (ret == 0)
|
||||
ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4;
|
||||
|
|
@ -838,8 +830,6 @@ static int ds_ioctl(struct inode *inode, struct file *file,
|
|||
return -EIO;
|
||||
|
||||
s = user->socket;
|
||||
if (s->pcmcia_state.dead)
|
||||
return -EIO;
|
||||
|
||||
size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
|
||||
if (size > sizeof(ds_ioctl_arg_t))
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
#include <linux/netdevice.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
|
||||
#include <pcmcia/cs_types.h>
|
||||
#include <pcmcia/ss.h>
|
||||
#include <pcmcia/cs.h>
|
||||
|
|
@ -38,29 +40,6 @@ static int io_speed;
|
|||
module_param(io_speed, int, 0444);
|
||||
|
||||
|
||||
#ifdef CONFIG_PCMCIA_PROBE
|
||||
#include <asm/irq.h>
|
||||
/* mask of IRQs already reserved by other cards, we should avoid using them */
|
||||
static u8 pcmcia_used_irq[NR_IRQS];
|
||||
#endif
|
||||
|
||||
static int pcmcia_adjust_io_region(struct resource *res, unsigned long start,
|
||||
unsigned long end, struct pcmcia_socket *s)
|
||||
{
|
||||
if (s->resource_ops->adjust_io_region)
|
||||
return s->resource_ops->adjust_io_region(res, start, end, s);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static struct resource *pcmcia_find_io_region(unsigned long base, int num,
|
||||
unsigned long align,
|
||||
struct pcmcia_socket *s)
|
||||
{
|
||||
if (s->resource_ops->find_io)
|
||||
return s->resource_ops->find_io(base, num, align, s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int pcmcia_validate_mem(struct pcmcia_socket *s)
|
||||
{
|
||||
if (s->resource_ops->validate_mem)
|
||||
|
|
@ -86,8 +65,7 @@ struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align,
|
|||
static int alloc_io_space(struct pcmcia_socket *s, u_int attr,
|
||||
unsigned int *base, unsigned int num, u_int lines)
|
||||
{
|
||||
int i;
|
||||
unsigned int try, align;
|
||||
unsigned int align;
|
||||
|
||||
align = (*base) ? (lines ? 1<<lines : 0) : 1;
|
||||
if (align && (align < num)) {
|
||||
|
|
@ -104,50 +82,8 @@ static int alloc_io_space(struct pcmcia_socket *s, u_int attr,
|
|||
*base, align);
|
||||
align = 0;
|
||||
}
|
||||
if ((s->features & SS_CAP_STATIC_MAP) && s->io_offset) {
|
||||
*base = s->io_offset | (*base & 0x0fff);
|
||||
return 0;
|
||||
}
|
||||
/* Check for an already-allocated window that must conflict with
|
||||
* what was asked for. It is a hack because it does not catch all
|
||||
* potential conflicts, just the most obvious ones.
|
||||
*/
|
||||
for (i = 0; i < MAX_IO_WIN; i++)
|
||||
if ((s->io[i].res) && *base &&
|
||||
((s->io[i].res->start & (align-1)) == *base))
|
||||
return 1;
|
||||
for (i = 0; i < MAX_IO_WIN; i++) {
|
||||
if (!s->io[i].res) {
|
||||
s->io[i].res = pcmcia_find_io_region(*base, num, align, s);
|
||||
if (s->io[i].res) {
|
||||
*base = s->io[i].res->start;
|
||||
s->io[i].res->flags = (s->io[i].res->flags & ~IORESOURCE_BITS) | (attr & IORESOURCE_BITS);
|
||||
s->io[i].InUse = num;
|
||||
break;
|
||||
} else
|
||||
return 1;
|
||||
} else if ((s->io[i].res->flags & IORESOURCE_BITS) != (attr & IORESOURCE_BITS))
|
||||
continue;
|
||||
/* Try to extend top of window */
|
||||
try = s->io[i].res->end + 1;
|
||||
if ((*base == 0) || (*base == try))
|
||||
if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start,
|
||||
s->io[i].res->end + num, s) == 0) {
|
||||
*base = try;
|
||||
s->io[i].InUse += num;
|
||||
break;
|
||||
}
|
||||
/* Try to extend bottom of window */
|
||||
try = s->io[i].res->start - num;
|
||||
if ((*base == 0) || (*base == try))
|
||||
if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start - num,
|
||||
s->io[i].res->end, s) == 0) {
|
||||
*base = try;
|
||||
s->io[i].InUse += num;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (i == MAX_IO_WIN);
|
||||
|
||||
return s->resource_ops->find_io(s, attr, base, num, align);
|
||||
} /* alloc_io_space */
|
||||
|
||||
|
||||
|
|
@ -187,6 +123,7 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev,
|
|||
config_t *c;
|
||||
int addr;
|
||||
u_char val;
|
||||
int ret = 0;
|
||||
|
||||
if (!p_dev || !p_dev->function_config)
|
||||
return -EINVAL;
|
||||
|
|
@ -203,11 +140,10 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev,
|
|||
}
|
||||
|
||||
addr = (c->ConfigBase + reg->Offset) >> 1;
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
|
||||
switch (reg->Action) {
|
||||
case CS_READ:
|
||||
pcmcia_read_cis_mem(s, 1, addr, 1, &val);
|
||||
ret = pcmcia_read_cis_mem(s, 1, addr, 1, &val);
|
||||
reg->Value = val;
|
||||
break;
|
||||
case CS_WRITE:
|
||||
|
|
@ -216,10 +152,11 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev,
|
|||
break;
|
||||
default:
|
||||
dev_dbg(&s->dev, "Invalid conf register request\n");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
return ret;
|
||||
} /* pcmcia_access_configuration_register */
|
||||
EXPORT_SYMBOL(pcmcia_access_configuration_register);
|
||||
|
||||
|
|
@ -275,19 +212,9 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
if (mod->Attributes & CONF_IRQ_CHANGE_VALID) {
|
||||
if (mod->Attributes & CONF_ENABLE_IRQ) {
|
||||
c->Attributes |= CONF_ENABLE_IRQ;
|
||||
s->socket.io_irq = s->irq.AssignedIRQ;
|
||||
} else {
|
||||
c->Attributes &= ~CONF_ENABLE_IRQ;
|
||||
s->socket.io_irq = 0;
|
||||
}
|
||||
s->ops->set_socket(s, &s->socket);
|
||||
}
|
||||
|
||||
if (mod->Attributes & CONF_VCC_CHANGE_VALID) {
|
||||
dev_dbg(&s->dev, "changing Vcc is not allowed at this time\n");
|
||||
if (mod->Attributes & (CONF_IRQ_CHANGE_VALID | CONF_VCC_CHANGE_VALID)) {
|
||||
dev_dbg(&s->dev,
|
||||
"changing Vcc or IRQ is not allowed at this time\n");
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
|
@ -422,52 +349,6 @@ out:
|
|||
} /* pcmcia_release_io */
|
||||
|
||||
|
||||
static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
|
||||
{
|
||||
struct pcmcia_socket *s = p_dev->socket;
|
||||
config_t *c;
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&s->ops_mutex);
|
||||
|
||||
c = p_dev->function_config;
|
||||
|
||||
if (!p_dev->_irq)
|
||||
goto out;
|
||||
|
||||
p_dev->_irq = 0;
|
||||
|
||||
if (c->state & CONFIG_LOCKED)
|
||||
goto out;
|
||||
|
||||
if (c->irq.Attributes != req->Attributes) {
|
||||
dev_dbg(&s->dev, "IRQ attributes must match assigned ones\n");
|
||||
goto out;
|
||||
}
|
||||
if (s->irq.AssignedIRQ != req->AssignedIRQ) {
|
||||
dev_dbg(&s->dev, "IRQ must match assigned one\n");
|
||||
goto out;
|
||||
}
|
||||
if (--s->irq.Config == 0) {
|
||||
c->state &= ~CONFIG_IRQ_REQ;
|
||||
s->irq.AssignedIRQ = 0;
|
||||
}
|
||||
|
||||
if (req->Handler)
|
||||
free_irq(req->AssignedIRQ, p_dev->priv);
|
||||
|
||||
#ifdef CONFIG_PCMCIA_PROBE
|
||||
pcmcia_used_irq[req->AssignedIRQ]--;
|
||||
#endif
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
|
||||
return ret;
|
||||
} /* pcmcia_release_irq */
|
||||
|
||||
|
||||
int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh)
|
||||
{
|
||||
struct pcmcia_socket *s = p_dev->socket;
|
||||
|
|
@ -551,12 +432,11 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
|
|||
if (req->Attributes & CONF_ENABLE_SPKR)
|
||||
s->socket.flags |= SS_SPKR_ENA;
|
||||
if (req->Attributes & CONF_ENABLE_IRQ)
|
||||
s->socket.io_irq = s->irq.AssignedIRQ;
|
||||
s->socket.io_irq = s->pcmcia_irq;
|
||||
else
|
||||
s->socket.io_irq = 0;
|
||||
s->ops->set_socket(s, &s->socket);
|
||||
s->lock_count++;
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
|
||||
/* Set up CIS configuration registers */
|
||||
base = c->ConfigBase = req->ConfigBase;
|
||||
|
|
@ -574,9 +454,9 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
|
|||
if (req->Present & PRESENT_IOBASE_0)
|
||||
c->Option |= COR_ADDR_DECODE;
|
||||
}
|
||||
if (c->state & CONFIG_IRQ_REQ)
|
||||
if (!(c->irq.Attributes & IRQ_FORCED_PULSE))
|
||||
c->Option |= COR_LEVEL_REQ;
|
||||
if ((req->Attributes & CONF_ENABLE_IRQ) &&
|
||||
!(req->Attributes & CONF_ENABLE_PULSE_IRQ))
|
||||
c->Option |= COR_LEVEL_REQ;
|
||||
pcmcia_write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &c->Option);
|
||||
mdelay(40);
|
||||
}
|
||||
|
|
@ -605,7 +485,6 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
|
|||
|
||||
/* Configure I/O windows */
|
||||
if (c->state & CONFIG_IO_REQ) {
|
||||
mutex_lock(&s->ops_mutex);
|
||||
iomap.speed = io_speed;
|
||||
for (i = 0; i < MAX_IO_WIN; i++)
|
||||
if (s->io[i].res) {
|
||||
|
|
@ -624,11 +503,11 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
|
|||
s->ops->set_io_map(s, &iomap);
|
||||
s->io[i].Config++;
|
||||
}
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
}
|
||||
|
||||
c->state |= CONFIG_LOCKED;
|
||||
p_dev->_locked = 1;
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
return 0;
|
||||
} /* pcmcia_request_configuration */
|
||||
EXPORT_SYMBOL(pcmcia_request_configuration);
|
||||
|
|
@ -706,137 +585,176 @@ out:
|
|||
EXPORT_SYMBOL(pcmcia_request_io);
|
||||
|
||||
|
||||
/** pcmcia_request_irq
|
||||
/**
|
||||
* pcmcia_request_irq() - attempt to request a IRQ for a PCMCIA device
|
||||
*
|
||||
* Request_irq() reserves an irq for this client.
|
||||
*
|
||||
* Also, since Linux only reserves irq's when they are actually
|
||||
* hooked, we don't guarantee that an irq will still be available
|
||||
* when the configuration is locked. Now that I think about it,
|
||||
* there might be a way to fix this using a dummy handler.
|
||||
* pcmcia_request_irq() is a wrapper around request_irq which will allow
|
||||
* the PCMCIA core to clean up the registration in pcmcia_disable_device().
|
||||
* Drivers are free to use request_irq() directly, but then they need to
|
||||
* call free_irq themselfves, too. Also, only IRQF_SHARED capable IRQ
|
||||
* handlers are allowed.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PCMCIA_PROBE
|
||||
static irqreturn_t test_action(int cpl, void *dev_id)
|
||||
int __must_check pcmcia_request_irq(struct pcmcia_device *p_dev,
|
||||
irq_handler_t handler)
|
||||
{
|
||||
return IRQ_NONE;
|
||||
int ret;
|
||||
|
||||
if (!p_dev->irq)
|
||||
return -EINVAL;
|
||||
|
||||
ret = request_irq(p_dev->irq, handler, IRQF_SHARED,
|
||||
p_dev->devname, p_dev->priv);
|
||||
if (!ret)
|
||||
p_dev->_irq = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
EXPORT_SYMBOL(pcmcia_request_irq);
|
||||
|
||||
int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
|
||||
|
||||
/**
|
||||
* pcmcia_request_exclusive_irq() - attempt to request an exclusive IRQ first
|
||||
*
|
||||
* pcmcia_request_exclusive_irq() is a wrapper around request_irq which
|
||||
* attempts first to request an exclusive IRQ. If it fails, it also accepts
|
||||
* a shared IRQ, but prints out a warning. PCMCIA drivers should allow for
|
||||
* IRQ sharing and either use request_irq directly (then they need to call
|
||||
* free_irq themselves, too), or the pcmcia_request_irq() function.
|
||||
*/
|
||||
int __must_check
|
||||
__pcmcia_request_exclusive_irq(struct pcmcia_device *p_dev,
|
||||
irq_handler_t handler)
|
||||
{
|
||||
struct pcmcia_socket *s = p_dev->socket;
|
||||
config_t *c;
|
||||
int ret = -EINVAL, irq = 0;
|
||||
int type;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&s->ops_mutex);
|
||||
if (!p_dev->irq)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(s->state & SOCKET_PRESENT)) {
|
||||
dev_dbg(&s->dev, "No card present\n");
|
||||
goto out;
|
||||
}
|
||||
c = p_dev->function_config;
|
||||
if (c->state & CONFIG_LOCKED) {
|
||||
dev_dbg(&s->dev, "Configuration is locked\n");
|
||||
goto out;
|
||||
}
|
||||
if (c->state & CONFIG_IRQ_REQ) {
|
||||
dev_dbg(&s->dev, "IRQ already configured\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Decide what type of interrupt we are registering */
|
||||
type = 0;
|
||||
if (s->functions > 1) /* All of this ought to be handled higher up */
|
||||
type = IRQF_SHARED;
|
||||
else if (req->Attributes & IRQ_TYPE_DYNAMIC_SHARING)
|
||||
type = IRQF_SHARED;
|
||||
else
|
||||
printk(KERN_WARNING "pcmcia: Driver needs updating to support IRQ sharing.\n");
|
||||
|
||||
#ifdef CONFIG_PCMCIA_PROBE
|
||||
|
||||
if (s->irq.AssignedIRQ != 0) {
|
||||
/* If the interrupt is already assigned, it must be the same */
|
||||
irq = s->irq.AssignedIRQ;
|
||||
} else {
|
||||
int try;
|
||||
u32 mask = s->irq_mask;
|
||||
void *data = p_dev; /* something unique to this device */
|
||||
|
||||
for (try = 0; try < 64; try++) {
|
||||
irq = try % 32;
|
||||
|
||||
/* marked as available by driver, and not blocked by userspace? */
|
||||
if (!((mask >> irq) & 1))
|
||||
continue;
|
||||
|
||||
/* avoid an IRQ which is already used by a PCMCIA card */
|
||||
if ((try < 32) && pcmcia_used_irq[irq])
|
||||
continue;
|
||||
|
||||
/* register the correct driver, if possible, of check whether
|
||||
* registering a dummy handle works, i.e. if the IRQ isn't
|
||||
* marked as used by the kernel resource management core */
|
||||
ret = request_irq(irq,
|
||||
(req->Handler) ? req->Handler : test_action,
|
||||
type,
|
||||
p_dev->devname,
|
||||
(req->Handler) ? p_dev->priv : data);
|
||||
if (!ret) {
|
||||
if (!req->Handler)
|
||||
free_irq(irq, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* only assign PCI irq if no IRQ already assigned */
|
||||
if (ret && !s->irq.AssignedIRQ) {
|
||||
if (!s->pci_irq) {
|
||||
dev_printk(KERN_INFO, &s->dev, "no IRQ found\n");
|
||||
goto out;
|
||||
}
|
||||
type = IRQF_SHARED;
|
||||
irq = s->pci_irq;
|
||||
}
|
||||
|
||||
if (ret && req->Handler) {
|
||||
ret = request_irq(irq, req->Handler, type,
|
||||
p_dev->devname, p_dev->priv);
|
||||
if (ret) {
|
||||
dev_printk(KERN_INFO, &s->dev,
|
||||
"request_irq() failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure the fact the request type was overridden is passed back */
|
||||
if (type == IRQF_SHARED && !(req->Attributes & IRQ_TYPE_DYNAMIC_SHARING)) {
|
||||
req->Attributes |= IRQ_TYPE_DYNAMIC_SHARING;
|
||||
ret = request_irq(p_dev->irq, handler, 0, p_dev->devname, p_dev->priv);
|
||||
if (ret) {
|
||||
ret = pcmcia_request_irq(p_dev, handler);
|
||||
dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: "
|
||||
"request for exclusive IRQ could not be fulfilled.\n");
|
||||
dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: the driver "
|
||||
"needs updating to supported shared IRQ lines.\n");
|
||||
}
|
||||
c->irq.Attributes = req->Attributes;
|
||||
s->irq.AssignedIRQ = req->AssignedIRQ = irq;
|
||||
s->irq.Config++;
|
||||
if (ret)
|
||||
dev_printk(KERN_INFO, &p_dev->dev, "request_irq() failed\n");
|
||||
else
|
||||
p_dev->_irq = 1;
|
||||
|
||||
return ret;
|
||||
} /* pcmcia_request_exclusive_irq */
|
||||
EXPORT_SYMBOL(__pcmcia_request_exclusive_irq);
|
||||
|
||||
c->state |= CONFIG_IRQ_REQ;
|
||||
p_dev->_irq = 1;
|
||||
|
||||
#ifdef CONFIG_PCMCIA_PROBE
|
||||
pcmcia_used_irq[irq]++;
|
||||
#endif
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
mutex_unlock(&s->ops_mutex);
|
||||
/* mask of IRQs already reserved by other cards, we should avoid using them */
|
||||
static u8 pcmcia_used_irq[NR_IRQS];
|
||||
|
||||
static irqreturn_t test_action(int cpl, void *dev_id)
|
||||
{
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcmcia_setup_isa_irq() - determine whether an ISA IRQ can be used
|
||||
* @p_dev - the associated PCMCIA device
|
||||
*
|
||||
* locking note: must be called with ops_mutex locked.
|
||||
*/
|
||||
static int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type)
|
||||
{
|
||||
struct pcmcia_socket *s = p_dev->socket;
|
||||
unsigned int try, irq;
|
||||
u32 mask = s->irq_mask;
|
||||
int ret = -ENODEV;
|
||||
|
||||
for (try = 0; try < 64; try++) {
|
||||
irq = try % 32;
|
||||
|
||||
/* marked as available by driver, not blocked by userspace? */
|
||||
if (!((mask >> irq) & 1))
|
||||
continue;
|
||||
|
||||
/* avoid an IRQ which is already used by another PCMCIA card */
|
||||
if ((try < 32) && pcmcia_used_irq[irq])
|
||||
continue;
|
||||
|
||||
/* register the correct driver, if possible, to check whether
|
||||
* registering a dummy handle works, i.e. if the IRQ isn't
|
||||
* marked as used by the kernel resource management core */
|
||||
ret = request_irq(irq, test_action, type, p_dev->devname,
|
||||
p_dev);
|
||||
if (!ret) {
|
||||
free_irq(irq, p_dev);
|
||||
p_dev->irq = s->pcmcia_irq = irq;
|
||||
pcmcia_used_irq[irq]++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
} /* pcmcia_request_irq */
|
||||
EXPORT_SYMBOL(pcmcia_request_irq);
|
||||
}
|
||||
|
||||
void pcmcia_cleanup_irq(struct pcmcia_socket *s)
|
||||
{
|
||||
pcmcia_used_irq[s->pcmcia_irq]--;
|
||||
s->pcmcia_irq = 0;
|
||||
}
|
||||
|
||||
#else /* CONFIG_PCMCIA_PROBE */
|
||||
|
||||
static int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void pcmcia_cleanup_irq(struct pcmcia_socket *s)
|
||||
{
|
||||
s->pcmcia_irq = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PCMCIA_PROBE */
|
||||
|
||||
|
||||
/**
|
||||
* pcmcia_setup_irq() - determine IRQ to be used for device
|
||||
* @p_dev - the associated PCMCIA device
|
||||
*
|
||||
* locking note: must be called with ops_mutex locked.
|
||||
*/
|
||||
int pcmcia_setup_irq(struct pcmcia_device *p_dev)
|
||||
{
|
||||
struct pcmcia_socket *s = p_dev->socket;
|
||||
|
||||
if (p_dev->irq)
|
||||
return 0;
|
||||
|
||||
/* already assigned? */
|
||||
if (s->pcmcia_irq) {
|
||||
p_dev->irq = s->pcmcia_irq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* prefer an exclusive ISA irq */
|
||||
if (!pcmcia_setup_isa_irq(p_dev, 0))
|
||||
return 0;
|
||||
|
||||
/* but accept a shared ISA irq */
|
||||
if (!pcmcia_setup_isa_irq(p_dev, IRQF_SHARED))
|
||||
return 0;
|
||||
|
||||
/* but use the PCI irq otherwise */
|
||||
if (s->pci_irq) {
|
||||
p_dev->irq = s->pcmcia_irq = s->pci_irq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
/** pcmcia_request_window
|
||||
|
|
@ -939,237 +857,9 @@ void pcmcia_disable_device(struct pcmcia_device *p_dev)
|
|||
{
|
||||
pcmcia_release_configuration(p_dev);
|
||||
pcmcia_release_io(p_dev, &p_dev->io);
|
||||
pcmcia_release_irq(p_dev, &p_dev->irq);
|
||||
if (p_dev->_irq)
|
||||
free_irq(p_dev->irq, p_dev->priv);
|
||||
if (p_dev->win)
|
||||
pcmcia_release_window(p_dev, p_dev->win);
|
||||
}
|
||||
EXPORT_SYMBOL(pcmcia_disable_device);
|
||||
|
||||
|
||||
struct pcmcia_cfg_mem {
|
||||
struct pcmcia_device *p_dev;
|
||||
void *priv_data;
|
||||
int (*conf_check) (struct pcmcia_device *p_dev,
|
||||
cistpl_cftable_entry_t *cfg,
|
||||
cistpl_cftable_entry_t *dflt,
|
||||
unsigned int vcc,
|
||||
void *priv_data);
|
||||
cisparse_t parse;
|
||||
cistpl_cftable_entry_t dflt;
|
||||
};
|
||||
|
||||
/**
|
||||
* pcmcia_do_loop_config() - internal helper for pcmcia_loop_config()
|
||||
*
|
||||
* pcmcia_do_loop_config() is the internal callback for the call from
|
||||
* pcmcia_loop_config() to pccard_loop_tuple(). Data is transferred
|
||||
* by a struct pcmcia_cfg_mem.
|
||||
*/
|
||||
static int pcmcia_do_loop_config(tuple_t *tuple, cisparse_t *parse, void *priv)
|
||||
{
|
||||
cistpl_cftable_entry_t *cfg = &parse->cftable_entry;
|
||||
struct pcmcia_cfg_mem *cfg_mem = priv;
|
||||
|
||||
/* default values */
|
||||
cfg_mem->p_dev->conf.ConfigIndex = cfg->index;
|
||||
if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
|
||||
cfg_mem->dflt = *cfg;
|
||||
|
||||
return cfg_mem->conf_check(cfg_mem->p_dev, cfg, &cfg_mem->dflt,
|
||||
cfg_mem->p_dev->socket->socket.Vcc,
|
||||
cfg_mem->priv_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* pcmcia_loop_config() - loop over configuration options
|
||||
* @p_dev: the struct pcmcia_device which we need to loop for.
|
||||
* @conf_check: function to call for each configuration option.
|
||||
* It gets passed the struct pcmcia_device, the CIS data
|
||||
* describing the configuration option, and private data
|
||||
* being passed to pcmcia_loop_config()
|
||||
* @priv_data: private data to be passed to the conf_check function.
|
||||
*
|
||||
* pcmcia_loop_config() loops over all configuration options, and calls
|
||||
* the driver-specific conf_check() for each one, checking whether
|
||||
* it is a valid one. Returns 0 on success or errorcode otherwise.
|
||||
*/
|
||||
int pcmcia_loop_config(struct pcmcia_device *p_dev,
|
||||
int (*conf_check) (struct pcmcia_device *p_dev,
|
||||
cistpl_cftable_entry_t *cfg,
|
||||
cistpl_cftable_entry_t *dflt,
|
||||
unsigned int vcc,
|
||||
void *priv_data),
|
||||
void *priv_data)
|
||||
{
|
||||
struct pcmcia_cfg_mem *cfg_mem;
|
||||
int ret;
|
||||
|
||||
cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL);
|
||||
if (cfg_mem == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
cfg_mem->p_dev = p_dev;
|
||||
cfg_mem->conf_check = conf_check;
|
||||
cfg_mem->priv_data = priv_data;
|
||||
|
||||
ret = pccard_loop_tuple(p_dev->socket, p_dev->func,
|
||||
CISTPL_CFTABLE_ENTRY, &cfg_mem->parse,
|
||||
cfg_mem, pcmcia_do_loop_config);
|
||||
|
||||
kfree(cfg_mem);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(pcmcia_loop_config);
|
||||
|
||||
|
||||
struct pcmcia_loop_mem {
|
||||
struct pcmcia_device *p_dev;
|
||||
void *priv_data;
|
||||
int (*loop_tuple) (struct pcmcia_device *p_dev,
|
||||
tuple_t *tuple,
|
||||
void *priv_data);
|
||||
};
|
||||
|
||||
/**
|
||||
* pcmcia_do_loop_tuple() - internal helper for pcmcia_loop_config()
|
||||
*
|
||||
* pcmcia_do_loop_tuple() is the internal callback for the call from
|
||||
* pcmcia_loop_tuple() to pccard_loop_tuple(). Data is transferred
|
||||
* by a struct pcmcia_cfg_mem.
|
||||
*/
|
||||
static int pcmcia_do_loop_tuple(tuple_t *tuple, cisparse_t *parse, void *priv)
|
||||
{
|
||||
struct pcmcia_loop_mem *loop = priv;
|
||||
|
||||
return loop->loop_tuple(loop->p_dev, tuple, loop->priv_data);
|
||||
};
|
||||
|
||||
/**
|
||||
* pcmcia_loop_tuple() - loop over tuples in the CIS
|
||||
* @p_dev: the struct pcmcia_device which we need to loop for.
|
||||
* @code: which CIS code shall we look for?
|
||||
* @priv_data: private data to be passed to the loop_tuple function.
|
||||
* @loop_tuple: function to call for each CIS entry of type @function. IT
|
||||
* gets passed the raw tuple and @priv_data.
|
||||
*
|
||||
* pcmcia_loop_tuple() loops over all CIS entries of type @function, and
|
||||
* calls the @loop_tuple function for each entry. If the call to @loop_tuple
|
||||
* returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
|
||||
*/
|
||||
int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code,
|
||||
int (*loop_tuple) (struct pcmcia_device *p_dev,
|
||||
tuple_t *tuple,
|
||||
void *priv_data),
|
||||
void *priv_data)
|
||||
{
|
||||
struct pcmcia_loop_mem loop = {
|
||||
.p_dev = p_dev,
|
||||
.loop_tuple = loop_tuple,
|
||||
.priv_data = priv_data};
|
||||
|
||||
return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL,
|
||||
&loop, pcmcia_do_loop_tuple);
|
||||
}
|
||||
EXPORT_SYMBOL(pcmcia_loop_tuple);
|
||||
|
||||
|
||||
struct pcmcia_loop_get {
|
||||
size_t len;
|
||||
cisdata_t **buf;
|
||||
};
|
||||
|
||||
/**
|
||||
* pcmcia_do_get_tuple() - internal helper for pcmcia_get_tuple()
|
||||
*
|
||||
* pcmcia_do_get_tuple() is the internal callback for the call from
|
||||
* pcmcia_get_tuple() to pcmcia_loop_tuple(). As we're only interested in
|
||||
* the first tuple, return 0 unconditionally. Create a memory buffer large
|
||||
* enough to hold the content of the tuple, and fill it with the tuple data.
|
||||
* The caller is responsible to free the buffer.
|
||||
*/
|
||||
static int pcmcia_do_get_tuple(struct pcmcia_device *p_dev, tuple_t *tuple,
|
||||
void *priv)
|
||||
{
|
||||
struct pcmcia_loop_get *get = priv;
|
||||
|
||||
*get->buf = kzalloc(tuple->TupleDataLen, GFP_KERNEL);
|
||||
if (*get->buf) {
|
||||
get->len = tuple->TupleDataLen;
|
||||
memcpy(*get->buf, tuple->TupleData, tuple->TupleDataLen);
|
||||
} else
|
||||
dev_dbg(&p_dev->dev, "do_get_tuple: out of memory\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcmcia_get_tuple() - get first tuple from CIS
|
||||
* @p_dev: the struct pcmcia_device which we need to loop for.
|
||||
* @code: which CIS code shall we look for?
|
||||
* @buf: pointer to store the buffer to.
|
||||
*
|
||||
* pcmcia_get_tuple() gets the content of the first CIS entry of type @code.
|
||||
* It returns the buffer length (or zero). The caller is responsible to free
|
||||
* the buffer passed in @buf.
|
||||
*/
|
||||
size_t pcmcia_get_tuple(struct pcmcia_device *p_dev, cisdata_t code,
|
||||
unsigned char **buf)
|
||||
{
|
||||
struct pcmcia_loop_get get = {
|
||||
.len = 0,
|
||||
.buf = buf,
|
||||
};
|
||||
|
||||
*get.buf = NULL;
|
||||
pcmcia_loop_tuple(p_dev, code, pcmcia_do_get_tuple, &get);
|
||||
|
||||
return get.len;
|
||||
}
|
||||
EXPORT_SYMBOL(pcmcia_get_tuple);
|
||||
|
||||
|
||||
/**
|
||||
* pcmcia_do_get_mac() - internal helper for pcmcia_get_mac_from_cis()
|
||||
*
|
||||
* pcmcia_do_get_mac() is the internal callback for the call from
|
||||
* pcmcia_get_mac_from_cis() to pcmcia_loop_tuple(). We check whether the
|
||||
* tuple contains a proper LAN_NODE_ID of length 6, and copy the data
|
||||
* to struct net_device->dev_addr[i].
|
||||
*/
|
||||
static int pcmcia_do_get_mac(struct pcmcia_device *p_dev, tuple_t *tuple,
|
||||
void *priv)
|
||||
{
|
||||
struct net_device *dev = priv;
|
||||
int i;
|
||||
|
||||
if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID)
|
||||
return -EINVAL;
|
||||
if (tuple->TupleDataLen < ETH_ALEN + 2) {
|
||||
dev_warn(&p_dev->dev, "Invalid CIS tuple length for "
|
||||
"LAN_NODE_ID\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (tuple->TupleData[1] != ETH_ALEN) {
|
||||
dev_warn(&p_dev->dev, "Invalid header for LAN_NODE_ID\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; i < 6; i++)
|
||||
dev->dev_addr[i] = tuple->TupleData[i+2];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcmcia_get_mac_from_cis() - read out MAC address from CISTPL_FUNCE
|
||||
* @p_dev: the struct pcmcia_device for which we want the address.
|
||||
* @dev: a properly prepared struct net_device to store the info to.
|
||||
*
|
||||
* pcmcia_get_mac_from_cis() reads out the hardware MAC address from
|
||||
* CISTPL_FUNCE and stores it into struct net_device *dev->dev_addr which
|
||||
* must be set up properly by the driver (see examples!).
|
||||
*/
|
||||
int pcmcia_get_mac_from_cis(struct pcmcia_device *p_dev, struct net_device *dev)
|
||||
{
|
||||
return pcmcia_loop_tuple(p_dev, CISTPL_FUNCE, pcmcia_do_get_mac, dev);
|
||||
}
|
||||
EXPORT_SYMBOL(pcmcia_get_mac_from_cis);
|
||||
|
||||
|
|
|
|||
229
drivers/pcmcia/pxa2xx_vpac270.c
Normal file
229
drivers/pcmcia/pxa2xx_vpac270.c
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* linux/drivers/pcmcia/pxa2xx_vpac270.c
|
||||
*
|
||||
* Driver for Voipac PXA270 PCMCIA and CF sockets
|
||||
*
|
||||
* Copyright (C) 2010
|
||||
* Marek Vasut <marek.vasut@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
#include <mach/gpio.h>
|
||||
#include <mach/vpac270.h>
|
||||
|
||||
#include "soc_common.h"
|
||||
|
||||
static struct pcmcia_irqs cd_irqs[] = {
|
||||
{
|
||||
.sock = 0,
|
||||
.irq = IRQ_GPIO(GPIO84_VPAC270_PCMCIA_CD),
|
||||
.str = "PCMCIA CD"
|
||||
},
|
||||
{
|
||||
.sock = 1,
|
||||
.irq = IRQ_GPIO(GPIO17_VPAC270_CF_CD),
|
||||
.str = "CF CD"
|
||||
},
|
||||
};
|
||||
|
||||
static int vpac270_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (skt->nr == 0) {
|
||||
ret = gpio_request(GPIO84_VPAC270_PCMCIA_CD, "PCMCIA CD");
|
||||
if (ret)
|
||||
goto err1;
|
||||
ret = gpio_direction_input(GPIO84_VPAC270_PCMCIA_CD);
|
||||
if (ret)
|
||||
goto err2;
|
||||
|
||||
ret = gpio_request(GPIO35_VPAC270_PCMCIA_RDY, "PCMCIA RDY");
|
||||
if (ret)
|
||||
goto err2;
|
||||
ret = gpio_direction_input(GPIO35_VPAC270_PCMCIA_RDY);
|
||||
if (ret)
|
||||
goto err3;
|
||||
|
||||
ret = gpio_request(GPIO107_VPAC270_PCMCIA_PPEN, "PCMCIA PPEN");
|
||||
if (ret)
|
||||
goto err3;
|
||||
ret = gpio_direction_output(GPIO107_VPAC270_PCMCIA_PPEN, 0);
|
||||
if (ret)
|
||||
goto err4;
|
||||
|
||||
ret = gpio_request(GPIO11_VPAC270_PCMCIA_RESET, "PCMCIA RESET");
|
||||
if (ret)
|
||||
goto err4;
|
||||
ret = gpio_direction_output(GPIO11_VPAC270_PCMCIA_RESET, 0);
|
||||
if (ret)
|
||||
goto err5;
|
||||
|
||||
skt->socket.pci_irq = gpio_to_irq(GPIO35_VPAC270_PCMCIA_RDY);
|
||||
|
||||
return soc_pcmcia_request_irqs(skt, &cd_irqs[0], 1);
|
||||
|
||||
err5:
|
||||
gpio_free(GPIO11_VPAC270_PCMCIA_RESET);
|
||||
err4:
|
||||
gpio_free(GPIO107_VPAC270_PCMCIA_PPEN);
|
||||
err3:
|
||||
gpio_free(GPIO35_VPAC270_PCMCIA_RDY);
|
||||
err2:
|
||||
gpio_free(GPIO84_VPAC270_PCMCIA_CD);
|
||||
err1:
|
||||
return ret;
|
||||
|
||||
} else {
|
||||
ret = gpio_request(GPIO17_VPAC270_CF_CD, "CF CD");
|
||||
if (ret)
|
||||
goto err6;
|
||||
ret = gpio_direction_input(GPIO17_VPAC270_CF_CD);
|
||||
if (ret)
|
||||
goto err7;
|
||||
|
||||
ret = gpio_request(GPIO12_VPAC270_CF_RDY, "CF RDY");
|
||||
if (ret)
|
||||
goto err7;
|
||||
ret = gpio_direction_input(GPIO12_VPAC270_CF_RDY);
|
||||
if (ret)
|
||||
goto err8;
|
||||
|
||||
ret = gpio_request(GPIO16_VPAC270_CF_RESET, "CF RESET");
|
||||
if (ret)
|
||||
goto err8;
|
||||
ret = gpio_direction_output(GPIO16_VPAC270_CF_RESET, 0);
|
||||
if (ret)
|
||||
goto err9;
|
||||
|
||||
skt->socket.pci_irq = gpio_to_irq(GPIO12_VPAC270_CF_RDY);
|
||||
|
||||
return soc_pcmcia_request_irqs(skt, &cd_irqs[1], 1);
|
||||
|
||||
err9:
|
||||
gpio_free(GPIO16_VPAC270_CF_RESET);
|
||||
err8:
|
||||
gpio_free(GPIO12_VPAC270_CF_RDY);
|
||||
err7:
|
||||
gpio_free(GPIO17_VPAC270_CF_CD);
|
||||
err6:
|
||||
return ret;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void vpac270_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
|
||||
{
|
||||
gpio_free(GPIO11_VPAC270_PCMCIA_RESET);
|
||||
gpio_free(GPIO107_VPAC270_PCMCIA_PPEN);
|
||||
gpio_free(GPIO35_VPAC270_PCMCIA_RDY);
|
||||
gpio_free(GPIO84_VPAC270_PCMCIA_CD);
|
||||
gpio_free(GPIO16_VPAC270_CF_RESET);
|
||||
gpio_free(GPIO12_VPAC270_CF_RDY);
|
||||
gpio_free(GPIO17_VPAC270_CF_CD);
|
||||
}
|
||||
|
||||
static void vpac270_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
|
||||
struct pcmcia_state *state)
|
||||
{
|
||||
if (skt->nr == 0) {
|
||||
state->detect = !gpio_get_value(GPIO84_VPAC270_PCMCIA_CD);
|
||||
state->ready = !!gpio_get_value(GPIO35_VPAC270_PCMCIA_RDY);
|
||||
} else {
|
||||
state->detect = !gpio_get_value(GPIO17_VPAC270_CF_CD);
|
||||
state->ready = !!gpio_get_value(GPIO12_VPAC270_CF_RDY);
|
||||
}
|
||||
state->bvd1 = 1;
|
||||
state->bvd2 = 1;
|
||||
state->wrprot = 0;
|
||||
state->vs_3v = 1;
|
||||
state->vs_Xv = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vpac270_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
|
||||
const socket_state_t *state)
|
||||
{
|
||||
if (skt->nr == 0) {
|
||||
gpio_set_value(GPIO11_VPAC270_PCMCIA_RESET,
|
||||
(state->flags & SS_RESET));
|
||||
gpio_set_value(GPIO107_VPAC270_PCMCIA_PPEN,
|
||||
!(state->Vcc == 33 || state->Vcc == 50));
|
||||
} else {
|
||||
gpio_set_value(GPIO16_VPAC270_CF_RESET,
|
||||
(state->flags & SS_RESET));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vpac270_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
|
||||
{
|
||||
}
|
||||
|
||||
static void vpac270_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
|
||||
{
|
||||
}
|
||||
|
||||
static struct pcmcia_low_level vpac270_pcmcia_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
|
||||
.first = 0,
|
||||
.nr = 2,
|
||||
|
||||
.hw_init = vpac270_pcmcia_hw_init,
|
||||
.hw_shutdown = vpac270_pcmcia_hw_shutdown,
|
||||
|
||||
.socket_state = vpac270_pcmcia_socket_state,
|
||||
.configure_socket = vpac270_pcmcia_configure_socket,
|
||||
|
||||
.socket_init = vpac270_pcmcia_socket_init,
|
||||
.socket_suspend = vpac270_pcmcia_socket_suspend,
|
||||
};
|
||||
|
||||
static struct platform_device *vpac270_pcmcia_device;
|
||||
|
||||
static int __init vpac270_pcmcia_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!machine_is_vpac270())
|
||||
return -ENODEV;
|
||||
|
||||
vpac270_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
|
||||
if (!vpac270_pcmcia_device)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = platform_device_add_data(vpac270_pcmcia_device,
|
||||
&vpac270_pcmcia_ops, sizeof(vpac270_pcmcia_ops));
|
||||
|
||||
if (!ret)
|
||||
ret = platform_device_add(vpac270_pcmcia_device);
|
||||
|
||||
if (ret)
|
||||
platform_device_put(vpac270_pcmcia_device);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit vpac270_pcmcia_exit(void)
|
||||
{
|
||||
platform_device_unregister(vpac270_pcmcia_device);
|
||||
}
|
||||
|
||||
module_init(vpac270_pcmcia_init);
|
||||
module_exit(vpac270_pcmcia_exit);
|
||||
|
||||
MODULE_AUTHOR("Marek Vasut <[email protected]>");
|
||||
MODULE_DESCRIPTION("PCMCIA support for Voipac PXA270");
|
||||
MODULE_ALIAS("platform:pxa2xx-pcmcia");
|
||||
MODULE_LICENSE("GPL");
|
||||
172
drivers/pcmcia/rsrc_iodyn.c
Normal file
172
drivers/pcmcia/rsrc_iodyn.c
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* rsrc_iodyn.c -- Resource management routines for MEM-static sockets.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* The initial developer of the original code is David A. Hinds
|
||||
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
|
||||
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
|
||||
*
|
||||
* (C) 1999 David A. Hinds
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <pcmcia/cs_types.h>
|
||||
#include <pcmcia/ss.h>
|
||||
#include <pcmcia/cs.h>
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include "cs_internal.h"
|
||||
|
||||
|
||||
struct pcmcia_align_data {
|
||||
unsigned long mask;
|
||||
unsigned long offset;
|
||||
};
|
||||
|
||||
static resource_size_t pcmcia_align(void *align_data,
|
||||
const struct resource *res,
|
||||
resource_size_t size, resource_size_t align)
|
||||
{
|
||||
struct pcmcia_align_data *data = align_data;
|
||||
resource_size_t start;
|
||||
|
||||
start = (res->start & ~data->mask) + data->offset;
|
||||
if (start < res->start)
|
||||
start += data->mask + 1;
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
if (res->flags & IORESOURCE_IO) {
|
||||
if (start & 0x300)
|
||||
start = (start + 0x3ff) & ~0x3ff;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_M68K
|
||||
if (res->flags & IORESOURCE_IO) {
|
||||
if ((res->start + size - 1) >= 1024)
|
||||
start = res->end;
|
||||
}
|
||||
#endif
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
static struct resource *__iodyn_find_io_region(struct pcmcia_socket *s,
|
||||
unsigned long base, int num,
|
||||
unsigned long align)
|
||||
{
|
||||
struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO,
|
||||
dev_name(&s->dev));
|
||||
struct pcmcia_align_data data;
|
||||
unsigned long min = base;
|
||||
int ret;
|
||||
|
||||
data.mask = align - 1;
|
||||
data.offset = base & data.mask;
|
||||
|
||||
#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
|
||||
ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
|
||||
1, pcmcia_align, &data);
|
||||
|
||||
if (ret != 0) {
|
||||
kfree(res);
|
||||
res = NULL;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int iodyn_find_io(struct pcmcia_socket *s, unsigned int attr,
|
||||
unsigned int *base, unsigned int num,
|
||||
unsigned int align)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
/* Check for an already-allocated window that must conflict with
|
||||
* what was asked for. It is a hack because it does not catch all
|
||||
* potential conflicts, just the most obvious ones.
|
||||
*/
|
||||
for (i = 0; i < MAX_IO_WIN; i++) {
|
||||
if (!s->io[i].res)
|
||||
continue;
|
||||
|
||||
if (!*base)
|
||||
continue;
|
||||
|
||||
if ((s->io[i].res->start & (align-1)) == *base)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_IO_WIN; i++) {
|
||||
struct resource *res = s->io[i].res;
|
||||
unsigned int try;
|
||||
|
||||
if (res && (res->flags & IORESOURCE_BITS) !=
|
||||
(attr & IORESOURCE_BITS))
|
||||
continue;
|
||||
|
||||
if (!res) {
|
||||
if (align == 0)
|
||||
align = 0x10000;
|
||||
|
||||
res = s->io[i].res = __iodyn_find_io_region(s, *base,
|
||||
num, align);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
*base = res->start;
|
||||
s->io[i].res->flags =
|
||||
((res->flags & ~IORESOURCE_BITS) |
|
||||
(attr & IORESOURCE_BITS));
|
||||
s->io[i].InUse = num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to extend top of window */
|
||||
try = res->end + 1;
|
||||
if ((*base == 0) || (*base == try)) {
|
||||
if (adjust_resource(s->io[i].res, res->start,
|
||||
res->end - res->start + num + 1))
|
||||
continue;
|
||||
*base = try;
|
||||
s->io[i].InUse += num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to extend bottom of window */
|
||||
try = res->start - num;
|
||||
if ((*base == 0) || (*base == try)) {
|
||||
if (adjust_resource(s->io[i].res,
|
||||
res->start - num,
|
||||
res->end - res->start + num + 1))
|
||||
continue;
|
||||
*base = try;
|
||||
s->io[i].InUse += num;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
struct pccard_resource_ops pccard_iodyn_ops = {
|
||||
.validate_mem = NULL,
|
||||
.find_io = iodyn_find_io,
|
||||
.find_mem = NULL,
|
||||
.add_io = NULL,
|
||||
.add_mem = NULL,
|
||||
.init = static_init,
|
||||
.exit = NULL,
|
||||
};
|
||||
EXPORT_SYMBOL(pccard_iodyn_ops);
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
#include <pcmcia/cistpl.h>
|
||||
#include "cs_internal.h"
|
||||
|
||||
static int static_init(struct pcmcia_socket *s)
|
||||
int static_init(struct pcmcia_socket *s)
|
||||
{
|
||||
/* the good thing about SS_CAP_STATIC_MAP sockets is
|
||||
* that they don't need a resource database */
|
||||
|
|
@ -32,11 +32,35 @@ static int static_init(struct pcmcia_socket *s)
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct resource *pcmcia_make_resource(unsigned long start, unsigned long end,
|
||||
int flags, const char *name)
|
||||
{
|
||||
struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
|
||||
|
||||
if (res) {
|
||||
res->name = name;
|
||||
res->start = start;
|
||||
res->end = start + end - 1;
|
||||
res->flags = flags;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int static_find_io(struct pcmcia_socket *s, unsigned int attr,
|
||||
unsigned int *base, unsigned int num,
|
||||
unsigned int align)
|
||||
{
|
||||
if (!s->io_offset)
|
||||
return -EINVAL;
|
||||
*base = s->io_offset | (*base & 0x0fff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct pccard_resource_ops pccard_static_ops = {
|
||||
.validate_mem = NULL,
|
||||
.adjust_io_region = NULL,
|
||||
.find_io = NULL,
|
||||
.find_io = static_find_io,
|
||||
.find_mem = NULL,
|
||||
.add_io = NULL,
|
||||
.add_mem = NULL,
|
||||
|
|
@ -46,104 +70,6 @@ struct pccard_resource_ops pccard_static_ops = {
|
|||
EXPORT_SYMBOL(pccard_static_ops);
|
||||
|
||||
|
||||
#ifdef CONFIG_PCCARD_IODYN
|
||||
|
||||
static struct resource *
|
||||
make_resource(unsigned long b, unsigned long n, int flags, char *name)
|
||||
{
|
||||
struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
|
||||
|
||||
if (res) {
|
||||
res->name = name;
|
||||
res->start = b;
|
||||
res->end = b + n - 1;
|
||||
res->flags = flags;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
struct pcmcia_align_data {
|
||||
unsigned long mask;
|
||||
unsigned long offset;
|
||||
};
|
||||
|
||||
static resource_size_t pcmcia_align(void *align_data,
|
||||
const struct resource *res,
|
||||
resource_size_t size, resource_size_t align)
|
||||
{
|
||||
struct pcmcia_align_data *data = align_data;
|
||||
resource_size_t start;
|
||||
|
||||
start = (res->start & ~data->mask) + data->offset;
|
||||
if (start < res->start)
|
||||
start += data->mask + 1;
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
if (res->flags & IORESOURCE_IO) {
|
||||
if (start & 0x300)
|
||||
start = (start + 0x3ff) & ~0x3ff;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_M68K
|
||||
if (res->flags & IORESOURCE_IO) {
|
||||
if ((res->start + size - 1) >= 1024)
|
||||
start = res->end;
|
||||
}
|
||||
#endif
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
static int iodyn_adjust_io_region(struct resource *res, unsigned long r_start,
|
||||
unsigned long r_end, struct pcmcia_socket *s)
|
||||
{
|
||||
return adjust_resource(res, r_start, r_end - r_start + 1);
|
||||
}
|
||||
|
||||
|
||||
static struct resource *iodyn_find_io_region(unsigned long base, int num,
|
||||
unsigned long align, struct pcmcia_socket *s)
|
||||
{
|
||||
struct resource *res = make_resource(0, num, IORESOURCE_IO,
|
||||
dev_name(&s->dev));
|
||||
struct pcmcia_align_data data;
|
||||
unsigned long min = base;
|
||||
int ret;
|
||||
|
||||
if (align == 0)
|
||||
align = 0x10000;
|
||||
|
||||
data.mask = align - 1;
|
||||
data.offset = base & data.mask;
|
||||
|
||||
#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
|
||||
ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
|
||||
1, pcmcia_align, &data);
|
||||
|
||||
if (ret != 0) {
|
||||
kfree(res);
|
||||
res = NULL;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
struct pccard_resource_ops pccard_iodyn_ops = {
|
||||
.validate_mem = NULL,
|
||||
.adjust_io_region = iodyn_adjust_io_region,
|
||||
.find_io = iodyn_find_io_region,
|
||||
.find_mem = NULL,
|
||||
.add_io = NULL,
|
||||
.add_mem = NULL,
|
||||
.init = static_init,
|
||||
.exit = NULL,
|
||||
};
|
||||
EXPORT_SYMBOL(pccard_iodyn_ops);
|
||||
|
||||
#endif /* CONFIG_PCCARD_IODYN */
|
||||
MODULE_AUTHOR("David A. Hinds, Dominik Brodowski");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("rsrc_nonstatic");
|
||||
|
|
|
|||
|
|
@ -34,8 +34,10 @@
|
|||
#include <pcmcia/cistpl.h>
|
||||
#include "cs_internal.h"
|
||||
|
||||
/* moved to rsrc_mgr.c
|
||||
MODULE_AUTHOR("David A. Hinds, Dominik Brodowski");
|
||||
MODULE_LICENSE("GPL");
|
||||
*/
|
||||
|
||||
/* Parameters that can be set with 'insmod' */
|
||||
|
||||
|
|
@ -69,20 +71,6 @@ struct socket_data {
|
|||
|
||||
======================================================================*/
|
||||
|
||||
static struct resource *
|
||||
make_resource(resource_size_t b, resource_size_t n, int flags, const char *name)
|
||||
{
|
||||
struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
|
||||
|
||||
if (res) {
|
||||
res->name = name;
|
||||
res->start = b;
|
||||
res->end = b + n - 1;
|
||||
res->flags = flags;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct resource *
|
||||
claim_region(struct pcmcia_socket *s, resource_size_t base,
|
||||
resource_size_t size, int type, char *name)
|
||||
|
|
@ -90,7 +78,7 @@ claim_region(struct pcmcia_socket *s, resource_size_t base,
|
|||
struct resource *res, *parent;
|
||||
|
||||
parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource;
|
||||
res = make_resource(base, size, type | IORESOURCE_BUSY, name);
|
||||
res = pcmcia_make_resource(base, size, type | IORESOURCE_BUSY, name);
|
||||
|
||||
if (res) {
|
||||
#ifdef CONFIG_PCI
|
||||
|
|
@ -214,7 +202,7 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
|
|||
return;
|
||||
}
|
||||
for (i = base, most = 0; i < base+num; i += 8) {
|
||||
res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
|
||||
res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
|
||||
if (!res)
|
||||
continue;
|
||||
hole = inb(i);
|
||||
|
|
@ -231,9 +219,14 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
|
|||
|
||||
bad = any = 0;
|
||||
for (i = base; i < base+num; i += 8) {
|
||||
res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
|
||||
if (!res)
|
||||
res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
|
||||
if (!res) {
|
||||
if (!any)
|
||||
printk(" excluding");
|
||||
if (!bad)
|
||||
bad = any = i;
|
||||
continue;
|
||||
}
|
||||
for (j = 0; j < 8; j++)
|
||||
if (inb(i+j) != most)
|
||||
break;
|
||||
|
|
@ -253,6 +246,7 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
|
|||
}
|
||||
if (bad) {
|
||||
if ((num > 16) && (bad == base) && (i == base+num)) {
|
||||
sub_interval(&s_data->io_db, bad, i-bad);
|
||||
printk(" nothing: probe failed.\n");
|
||||
return;
|
||||
} else {
|
||||
|
|
@ -655,8 +649,9 @@ pcmcia_align(void *align_data, const struct resource *res,
|
|||
* Adjust an existing IO region allocation, but making sure that we don't
|
||||
* encroach outside the resources which the user supplied.
|
||||
*/
|
||||
static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_start,
|
||||
unsigned long r_end, struct pcmcia_socket *s)
|
||||
static int __nonstatic_adjust_io_region(struct pcmcia_socket *s,
|
||||
unsigned long r_start,
|
||||
unsigned long r_end)
|
||||
{
|
||||
struct resource_map *m;
|
||||
struct socket_data *s_data = s->resource_data;
|
||||
|
|
@ -669,8 +664,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star
|
|||
if (start > r_start || r_end > end)
|
||||
continue;
|
||||
|
||||
ret = adjust_resource(res, r_start, r_end - r_start + 1);
|
||||
break;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -689,18 +683,17 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star
|
|||
|
||||
======================================================================*/
|
||||
|
||||
static struct resource *nonstatic_find_io_region(unsigned long base, int num,
|
||||
unsigned long align, struct pcmcia_socket *s)
|
||||
static struct resource *__nonstatic_find_io_region(struct pcmcia_socket *s,
|
||||
unsigned long base, int num,
|
||||
unsigned long align)
|
||||
{
|
||||
struct resource *res = make_resource(0, num, IORESOURCE_IO, dev_name(&s->dev));
|
||||
struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO,
|
||||
dev_name(&s->dev));
|
||||
struct socket_data *s_data = s->resource_data;
|
||||
struct pcmcia_align_data data;
|
||||
unsigned long min = base;
|
||||
int ret;
|
||||
|
||||
if (align == 0)
|
||||
align = 0x10000;
|
||||
|
||||
data.mask = align - 1;
|
||||
data.offset = base & data.mask;
|
||||
data.map = &s_data->io_db;
|
||||
|
|
@ -721,10 +714,97 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num,
|
|||
return res;
|
||||
}
|
||||
|
||||
static int nonstatic_find_io(struct pcmcia_socket *s, unsigned int attr,
|
||||
unsigned int *base, unsigned int num,
|
||||
unsigned int align)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
/* Check for an already-allocated window that must conflict with
|
||||
* what was asked for. It is a hack because it does not catch all
|
||||
* potential conflicts, just the most obvious ones.
|
||||
*/
|
||||
for (i = 0; i < MAX_IO_WIN; i++) {
|
||||
if (!s->io[i].res)
|
||||
continue;
|
||||
|
||||
if (!*base)
|
||||
continue;
|
||||
|
||||
if ((s->io[i].res->start & (align-1)) == *base)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_IO_WIN; i++) {
|
||||
struct resource *res = s->io[i].res;
|
||||
unsigned int try;
|
||||
|
||||
if (res && (res->flags & IORESOURCE_BITS) !=
|
||||
(attr & IORESOURCE_BITS))
|
||||
continue;
|
||||
|
||||
if (!res) {
|
||||
if (align == 0)
|
||||
align = 0x10000;
|
||||
|
||||
res = s->io[i].res = __nonstatic_find_io_region(s,
|
||||
*base, num,
|
||||
align);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
*base = res->start;
|
||||
s->io[i].res->flags =
|
||||
((res->flags & ~IORESOURCE_BITS) |
|
||||
(attr & IORESOURCE_BITS));
|
||||
s->io[i].InUse = num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to extend top of window */
|
||||
try = res->end + 1;
|
||||
if ((*base == 0) || (*base == try)) {
|
||||
ret = __nonstatic_adjust_io_region(s, res->start,
|
||||
res->end + num);
|
||||
if (!ret) {
|
||||
ret = adjust_resource(s->io[i].res, res->start,
|
||||
res->end - res->start + num + 1);
|
||||
if (ret)
|
||||
continue;
|
||||
*base = try;
|
||||
s->io[i].InUse += num;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to extend bottom of window */
|
||||
try = res->start - num;
|
||||
if ((*base == 0) || (*base == try)) {
|
||||
ret = __nonstatic_adjust_io_region(s,
|
||||
res->start - num,
|
||||
res->end);
|
||||
if (!ret) {
|
||||
ret = adjust_resource(s->io[i].res,
|
||||
res->start - num,
|
||||
res->end - res->start + num + 1);
|
||||
if (ret)
|
||||
continue;
|
||||
*base = try;
|
||||
s->io[i].InUse += num;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
|
||||
u_long align, int low, struct pcmcia_socket *s)
|
||||
{
|
||||
struct resource *res = make_resource(0, num, IORESOURCE_MEM, dev_name(&s->dev));
|
||||
struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_MEM,
|
||||
dev_name(&s->dev));
|
||||
struct socket_data *s_data = s->resource_data;
|
||||
struct pcmcia_align_data data;
|
||||
unsigned long min, max;
|
||||
|
|
@ -804,7 +884,7 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned
|
|||
static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end)
|
||||
{
|
||||
struct socket_data *data = s->resource_data;
|
||||
unsigned long size = end - start + 1;
|
||||
unsigned long size;
|
||||
int ret = 0;
|
||||
|
||||
#if defined(CONFIG_X86)
|
||||
|
|
@ -814,6 +894,8 @@ static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long
|
|||
start = 0x100;
|
||||
#endif
|
||||
|
||||
size = end - start + 1;
|
||||
|
||||
if (end < start)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -853,23 +935,42 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
|
|||
return -ENODEV;
|
||||
|
||||
#if defined(CONFIG_X86)
|
||||
/* If this is the root bus, the risk of hitting
|
||||
* some strange system devices which aren't protected
|
||||
* by either ACPI resource tables or properly requested
|
||||
* resources is too big. Therefore, don't do auto-adding
|
||||
* of resources at the moment.
|
||||
/* If this is the root bus, the risk of hitting some strange
|
||||
* system devices is too high: If a driver isn't loaded, the
|
||||
* resources are not claimed; even if a driver is loaded, it
|
||||
* may not request all resources or even the wrong one. We
|
||||
* can neither trust the rest of the kernel nor ACPI/PNP and
|
||||
* CRS parsing to get it right. Therefore, use several
|
||||
* safeguards:
|
||||
*
|
||||
* - Do not auto-add resources if the CardBus bridge is on
|
||||
* the PCI root bus
|
||||
*
|
||||
* - Avoid any I/O ports < 0x100.
|
||||
*
|
||||
* - On PCI-PCI bridges, only use resources which are set up
|
||||
* exclusively for the secondary PCI bus: the risk of hitting
|
||||
* system devices is quite low, as they usually aren't
|
||||
* connected to the secondary PCI bus.
|
||||
*/
|
||||
if (s->cb_dev->bus->number == 0)
|
||||
return -EINVAL;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) {
|
||||
res = s->cb_dev->bus->resource[i];
|
||||
#else
|
||||
pci_bus_for_each_resource(s->cb_dev->bus, res, i) {
|
||||
#endif
|
||||
if (!res)
|
||||
continue;
|
||||
|
||||
if (res->flags & IORESOURCE_IO) {
|
||||
/* safeguard against the root resource, where the
|
||||
* risk of hitting any other device would be too
|
||||
* high */
|
||||
if (res == &ioport_resource)
|
||||
continue;
|
||||
|
||||
dev_printk(KERN_INFO, &s->cb_dev->dev,
|
||||
"pcmcia: parent PCI bridge window: %pR\n",
|
||||
res);
|
||||
|
|
@ -879,8 +980,12 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
|
|||
}
|
||||
|
||||
if (res->flags & IORESOURCE_MEM) {
|
||||
/* safeguard against the root resource, where the
|
||||
* risk of hitting any other device would be too
|
||||
* high */
|
||||
if (res == &iomem_resource)
|
||||
continue;
|
||||
|
||||
dev_printk(KERN_INFO, &s->cb_dev->dev,
|
||||
"pcmcia: parent PCI bridge window: %pR\n",
|
||||
res);
|
||||
|
|
@ -948,8 +1053,7 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s)
|
|||
|
||||
struct pccard_resource_ops pccard_nonstatic_ops = {
|
||||
.validate_mem = pcmcia_nonstatic_validate_mem,
|
||||
.adjust_io_region = nonstatic_adjust_io_region,
|
||||
.find_io = nonstatic_find_io_region,
|
||||
.find_io = nonstatic_find_io,
|
||||
.find_mem = nonstatic_find_mem_region,
|
||||
.add_io = adjust_io,
|
||||
.add_mem = adjust_memory,
|
||||
|
|
|
|||
|
|
@ -1303,13 +1303,6 @@ static int yenta_dev_suspend_noirq(struct device *dev)
|
|||
pci_read_config_dword(pdev, 17*4, &socket->saved_state[1]);
|
||||
pci_disable_device(pdev);
|
||||
|
||||
/*
|
||||
* Some laptops (IBM T22) do not like us putting the Cardbus
|
||||
* bridge into D3. At a guess, some other laptop will
|
||||
* probably require this, so leave it commented out for now.
|
||||
*/
|
||||
/* pci_set_power_state(dev, 3); */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue