diff options
author | Daniel Ritz <daniel.ritz@gmx.ch> | 2005-06-23 09:10:12 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-23 18:45:31 +0200 |
commit | fa912bcb06d5dc9525d8912a145db2bf4b7668c5 (patch) | |
tree | 5c62d9a1ef416c83d3e149ca94c4e646eb29daf2 /drivers/pcmcia/ti113x.h | |
parent | [PATCH] Add offset.h to dontdiff (diff) | |
download | linux-fa912bcb06d5dc9525d8912a145db2bf4b7668c5.tar.xz linux-fa912bcb06d5dc9525d8912a145db2bf4b7668c5.zip |
[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/pcmcia/ti113x.h')
-rw-r--r-- | drivers/pcmcia/ti113x.h | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/drivers/pcmcia/ti113x.h b/drivers/pcmcia/ti113x.h index a8a1d104524a..c7ba99871aca 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); } |