diff options
author | Daniel Ritz <daniel.ritz@gmx.ch> | 2005-06-23 00:10:12 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-23 09:45:31 -0700 |
commit | fa912bcb06d5dc9525d8912a145db2bf4b7668c5 (patch) | |
tree | 5c62d9a1ef416c83d3e149ca94c4e646eb29daf2 /drivers | |
parent | 7007cab53176dc906e67b4efa62f20ff1432805a (diff) |
[PATCH] yenta TI: turn off interrupts during card power-on #2
- make boot-up card recognition more reliable (ie. redo interrogation
always if there is no valid 'card inserted' state) (and yes, i saw it
happening on an o2micro controller that both CB_CBARD and CB_16BITCARD
bits were set at the same time)
- also redo interrogation before probing the ISA interrupts. it's safer
to do the probing with the socket in a clean state.
- make card insert detect more reliable. yenta_get_status() now returns
SS_PENDING as long as the card is not completley inserted and one of the
voltage bits is set. also !CB_CBARD doesn't mean CB_16BITCARD. there is
CB_NOTACARD as well, so make an explicit check for CB_16BITCARD.
- for TI bridges: disable IRQs during power-on. in all-serial and tied
interrupt mode the interrupts are always disabled for single-slot
controllers. for two-slot contollers the disabling is only done when the
other slot is empty. to force disabling there is a new module parameter
now: pwr_irqs_off=Y (which is a regression for working setups. that's
why it's an option, only use when required)
- modparm to disable ISA interrupt probing (isa_probe, defaults to on)
- remove unneeded code/cleanups (ie. merge yenta_events() into
yenta_interrupts())
Signed-off-by: Daniel Ritz <daniel.ritz@gmx.ch>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pcmcia/cs.c | 11 | ||||
-rw-r--r-- | drivers/pcmcia/ti113x.h | 167 | ||||
-rw-r--r-- | drivers/pcmcia/yenta_socket.c | 60 |
3 files changed, 215 insertions, 23 deletions
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 03fc885db1c..d136b3c8fac 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -508,6 +508,10 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay) cs_err(skt, "unsupported voltage key.\n"); return CS_BAD_TYPE; } + + if (skt->power_hook) + skt->power_hook(skt, HOOK_POWER_PRE); + skt->socket.flags = 0; skt->ops->set_socket(skt, &skt->socket); @@ -522,7 +526,12 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay) return CS_BAD_TYPE; } - return socket_reset(skt); + status = socket_reset(skt); + + if (skt->power_hook) + skt->power_hook(skt, HOOK_POWER_POST); + + return status; } /* diff --git a/drivers/pcmcia/ti113x.h b/drivers/pcmcia/ti113x.h index a8a1d104524..c7ba99871ac 100644 --- a/drivers/pcmcia/ti113x.h +++ b/drivers/pcmcia/ti113x.h @@ -611,6 +611,170 @@ out: } } + +/* Returns true value if the second slot of a two-slot controller is empty */ +static int ti12xx_2nd_slot_empty(struct yenta_socket *socket) +{ + struct pci_dev *func; + struct yenta_socket *slot2; + int devfn; + unsigned int state; + int ret = 1; + + /* catch the two-slot controllers */ + switch (socket->dev->device) { + case PCI_DEVICE_ID_TI_1220: + case PCI_DEVICE_ID_TI_1221: + case PCI_DEVICE_ID_TI_1225: + case PCI_DEVICE_ID_TI_1251A: + case PCI_DEVICE_ID_TI_1251B: + case PCI_DEVICE_ID_TI_1420: + case PCI_DEVICE_ID_TI_1450: + case PCI_DEVICE_ID_TI_1451A: + case PCI_DEVICE_ID_TI_1520: + case PCI_DEVICE_ID_TI_1620: + case PCI_DEVICE_ID_TI_4520: + case PCI_DEVICE_ID_TI_4450: + case PCI_DEVICE_ID_TI_4451: + /* + * there are way more, but they need to be added in yenta_socket.c + * and pci_ids.h first anyway. + */ + break; + + /* single-slot controllers have the 2nd slot empty always :) */ + default: + return 1; + } + + /* get other slot */ + devfn = socket->dev->devfn & ~0x07; + func = pci_get_slot(socket->dev->bus, + (socket->dev->devfn & 0x07) ? devfn : devfn | 0x01); + if (!func) + return 1; + + slot2 = pci_get_drvdata(func); + if (!slot2) + goto out; + + /* check state */ + yenta_get_status(&socket->socket, &state); + if (state & SS_DETECT) { + ret = 0; + goto out; + } + +out: + pci_dev_put(func); + return ret; +} + +/* + * TI specifiy parts for the power hook. + * + * some TI's with some CB's produces interrupt storm on power on. it has been + * seen with atheros wlan cards on TI1225 and TI1410. solution is simply to + * disable any CB interrupts during this time. + */ +static int ti12xx_power_hook(struct pcmcia_socket *sock, int operation) +{ + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + u32 mfunc, devctl, sysctl; + u8 gpio3; + + /* only POWER_PRE and POWER_POST are interesting */ + if ((operation != HOOK_POWER_PRE) && (operation != HOOK_POWER_POST)) + return 0; + + devctl = config_readb(socket, TI113X_DEVICE_CONTROL); + sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL); + mfunc = config_readl(socket, TI122X_MFUNC); + + /* + * all serial/tied: only disable when modparm set. always doing it + * would mean a regression for working setups 'cos it disables the + * interrupts for both both slots on 2-slot controllers + * (and users of single slot controllers where it's save have to + * live with setting the modparm, most don't have to anyway) + */ + if (((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) && + (pwr_irqs_off || ti12xx_2nd_slot_empty(socket))) { + switch (socket->dev->device) { + case PCI_DEVICE_ID_TI_1250: + case PCI_DEVICE_ID_TI_1251A: + case PCI_DEVICE_ID_TI_1251B: + case PCI_DEVICE_ID_TI_1450: + case PCI_DEVICE_ID_TI_1451A: + case PCI_DEVICE_ID_TI_4450: + case PCI_DEVICE_ID_TI_4451: + /* these chips have no IRQSER setting in MFUNC3 */ + break; + + default: + if (operation == HOOK_POWER_PRE) + mfunc = (mfunc & ~TI122X_MFUNC3_MASK); + else + mfunc = (mfunc & ~TI122X_MFUNC3_MASK) | TI122X_MFUNC3_IRQSER; + } + + return 0; + } + + /* do the job differently for func0/1 */ + if ((PCI_FUNC(socket->dev->devfn) == 0) || + ((sysctl & TI122X_SCR_INTRTIE) && + (pwr_irqs_off || ti12xx_2nd_slot_empty(socket)))) { + /* some bridges are different */ + switch (socket->dev->device) { + case PCI_DEVICE_ID_TI_1250: + case PCI_DEVICE_ID_TI_1251A: + case PCI_DEVICE_ID_TI_1251B: + case PCI_DEVICE_ID_TI_1450: + /* those oldies use gpio3 for INTA */ + gpio3 = config_readb(socket, TI1250_GPIO3_CONTROL); + if (operation == HOOK_POWER_PRE) + gpio3 = (gpio3 & ~TI1250_GPIO_MODE_MASK) | 0x40; + else + gpio3 &= ~TI1250_GPIO_MODE_MASK; + config_writeb(socket, TI1250_GPIO3_CONTROL, gpio3); + break; + + default: + /* all new bridges are the same */ + if (operation == HOOK_POWER_PRE) + mfunc &= ~TI122X_MFUNC0_MASK; + else + mfunc |= TI122X_MFUNC0_INTA; + config_writel(socket, TI122X_MFUNC, mfunc); + } + } else { + switch (socket->dev->device) { + case PCI_DEVICE_ID_TI_1251A: + case PCI_DEVICE_ID_TI_1251B: + case PCI_DEVICE_ID_TI_1450: + /* those have INTA elsewhere and INTB in MFUNC0 */ + if (operation == HOOK_POWER_PRE) + mfunc &= ~TI122X_MFUNC0_MASK; + else + mfunc |= TI125X_MFUNC0_INTB; + config_writel(socket, TI122X_MFUNC, mfunc); + + break; + + default: + /* all new bridges are the same */ + if (operation == HOOK_POWER_PRE) + mfunc &= ~TI122X_MFUNC1_MASK; + else + mfunc |= TI122X_MFUNC1_INTB; + config_writel(socket, TI122X_MFUNC, mfunc); + } + } + + return 0; +} + static int ti12xx_override(struct yenta_socket *socket) { u32 val, val_orig; @@ -654,6 +818,9 @@ static int ti12xx_override(struct yenta_socket *socket) else ti12xx_irqroute_func1(socket); + /* install power hook */ + socket->socket.power_hook = ti12xx_power_hook; + return ti_override(socket); } diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 6404d97a12e..bee05362fd2 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -32,6 +32,14 @@ static int disable_clkrun; module_param(disable_clkrun, bool, 0444); MODULE_PARM_DESC(disable_clkrun, "If PC card doesn't function properly, please try this option"); +static int isa_probe = 1; +module_param(isa_probe, bool, 0444); +MODULE_PARM_DESC(isa_probe, "If set ISA interrupts are probed (default). Set to N to disable probing"); + +static int pwr_irqs_off; +module_param(pwr_irqs_off, bool, 0644); +MODULE_PARM_DESC(pwr_irqs_off, "Force IRQs off during power-on of slot. Use only when seeing IRQ storms!"); + #if 0 #define debug(x,args...) printk(KERN_DEBUG "%s: " x, __func__ , ##args) #else @@ -150,15 +158,16 @@ static int yenta_get_status(struct pcmcia_socket *sock, unsigned int *value) val = (state & CB_3VCARD) ? SS_3VCARD : 0; val |= (state & CB_XVCARD) ? SS_XVCARD : 0; - val |= (state & (CB_CDETECT1 | CB_CDETECT2 | CB_5VCARD | CB_3VCARD - | CB_XVCARD | CB_YVCARD)) ? 0 : SS_PENDING; + val |= (state & (CB_5VCARD | CB_3VCARD | CB_XVCARD | CB_YVCARD)) ? 0 : SS_PENDING; + val |= (state & (CB_CDETECT1 | CB_CDETECT2)) ? SS_PENDING : 0; + if (state & CB_CBCARD) { val |= SS_CARDBUS; val |= (state & CB_CARDSTS) ? SS_STSCHG : 0; val |= (state & (CB_CDETECT1 | CB_CDETECT2)) ? 0 : SS_DETECT; val |= (state & CB_PWRCYCLE) ? SS_POWERON | SS_READY : 0; - } else { + } else if (state & CB_16BITCARD) { u8 status = exca_readb(socket, I365_STATUS); val |= ((status & I365_CS_DETECT) == I365_CS_DETECT) ? SS_DETECT : 0; if (exca_readb(socket, I365_INTCTL) & I365_PC_IOCARD) { @@ -405,11 +414,13 @@ static int yenta_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map * } -static unsigned int yenta_events(struct yenta_socket *socket) + +static irqreturn_t yenta_interrupt(int irq, void *dev_id, struct pt_regs *regs) { + unsigned int events; + struct yenta_socket *socket = (struct yenta_socket *) dev_id; u8 csc; u32 cb_event; - unsigned int events; /* Clear interrupt status for the event */ cb_event = cb_readl(socket, CB_SOCKET_EVENT); @@ -426,20 +437,13 @@ static unsigned int yenta_events(struct yenta_socket *socket) events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0; events |= (csc & I365_CSC_READY) ? SS_READY : 0; } - return events; -} - - -static irqreturn_t yenta_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - unsigned int events; - struct yenta_socket *socket = (struct yenta_socket *) dev_id; - events = yenta_events(socket); - if (events) { + if (events) pcmcia_parse_events(&socket->socket, events); + + if (cb_event || csc) return IRQ_HANDLED; - } + return IRQ_NONE; } @@ -470,11 +474,22 @@ static void yenta_clear_maps(struct yenta_socket *socket) } } +/* redoes voltage interrogation if required */ +static void yenta_interrogate(struct yenta_socket *socket) +{ + u32 state; + + state = cb_readl(socket, CB_SOCKET_STATE); + if (!(state & (CB_5VCARD | CB_3VCARD | CB_XVCARD | CB_YVCARD)) || + (state & (CB_CDETECT1 | CB_CDETECT2 | CB_NOTACARD | CB_BADVCCREQ)) || + ((state & (CB_16BITCARD | CB_CBCARD)) == (CB_16BITCARD | CB_CBCARD))) + cb_writel(socket, CB_SOCKET_FORCE, CB_CVSTEST); +} + /* Called at resume and initialization events */ static int yenta_sock_init(struct pcmcia_socket *sock) { struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); - u32 state; u16 bridge; bridge = config_readw(socket, CB_BRIDGE_CONTROL) & ~CB_BRIDGE_INTR; @@ -486,10 +501,7 @@ static int yenta_sock_init(struct pcmcia_socket *sock) exca_writeb(socket, I365_GENCTL, 0x00); /* Redo card voltage interrogation */ - state = cb_readl(socket, CB_SOCKET_STATE); - if (!(state & (CB_CDETECT1 | CB_CDETECT2 | CB_5VCARD | - CB_3VCARD | CB_XVCARD | CB_YVCARD))) - cb_writel(socket, CB_SOCKET_FORCE, CB_CVSTEST); + yenta_interrogate(socket); yenta_clear_maps(socket); @@ -856,7 +868,10 @@ static void yenta_get_socket_capabilities(struct yenta_socket *socket, u32 isa_i socket->socket.features |= SS_CAP_PAGE_REGS | SS_CAP_PCCARD | SS_CAP_CARDBUS; socket->socket.map_size = 0x1000; socket->socket.pci_irq = socket->cb_irq; - socket->socket.irq_mask = yenta_probe_irq(socket, isa_irq_mask); + if (isa_probe) + socket->socket.irq_mask = yenta_probe_irq(socket, isa_irq_mask); + else + socket->socket.irq_mask = 0; socket->socket.cb_dev = socket->dev; printk(KERN_INFO "Yenta: ISA IRQ mask 0x%04x, PCI irq %d\n", @@ -996,6 +1011,7 @@ static int __devinit yenta_probe (struct pci_dev *dev, const struct pci_device_i } /* Figure out what the dang thing can do for the PCMCIA layer... */ + yenta_interrogate(socket); yenta_get_socket_capabilities(socket, isa_interrupts); printk(KERN_INFO "Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE)); |