[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>
This commit is contained in:
parent
7007cab531
commit
fa912bcb06
4 changed files with 223 additions and 23 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
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));
|
||||
|
||||
|
|
|
@ -77,6 +77,11 @@ extern socket_state_t dead_socket;
|
|||
/* Use this just for bridge windows */
|
||||
#define MAP_IOSPACE 0x20
|
||||
|
||||
/* power hook operations */
|
||||
#define HOOK_POWER_PRE 0x01
|
||||
#define HOOK_POWER_POST 0x02
|
||||
|
||||
|
||||
typedef struct pccard_io_map {
|
||||
u_char map;
|
||||
u_char flags;
|
||||
|
@ -223,6 +228,9 @@ struct pcmcia_socket {
|
|||
this to _ops */
|
||||
void (*zoom_video)(struct pcmcia_socket *, int);
|
||||
|
||||
/* so is power hook */
|
||||
int (*power_hook)(struct pcmcia_socket *sock, int operation);
|
||||
|
||||
/* state thread */
|
||||
struct semaphore skt_sem; /* protects socket h/w state */
|
||||
|
||||
|
|
Loading…
Reference in a new issue