From: Daniel Ritz Some TI cardbus bridges found in notebooks and PCI add-on cards are uninitialized. this means the interrupt mode and the interrupt routing is wrong in most cases, ending up in non working PCI interrupts. this patch probes the PCI interrupt and adjust the interrupt setting if no interrupts are delivered. it's done in a safe way, that doesn't hurt working setups. also function 1 on two slot devices is handled different that function 0 since both share the settings. btw. pcmcia-cs i82365 driver always probed the PCI interrupt with no negative effect acording to dave hinds. --- 25-akpm/drivers/pcmcia/ti113x.h | 340 ++++++++++++++++++++++++++++------ 25-akpm/drivers/pcmcia/yenta_socket.c | 72 +++++++ 25-akpm/drivers/pcmcia/yenta_socket.h | 3 25-akpm/include/linux/pci_ids.h | 2 4 files changed, 364 insertions(+), 53 deletions(-) diff -puN drivers/pcmcia/ti113x.h~yenta-TI-irq-routing-fix drivers/pcmcia/ti113x.h --- 25/drivers/pcmcia/ti113x.h~yenta-TI-irq-routing-fix 2004-04-05 18:45:51.271417128 -0700 +++ 25-akpm/drivers/pcmcia/ti113x.h 2004-04-05 18:45:51.280415760 -0700 @@ -76,7 +76,23 @@ #define TI1250_GPIO1_CONTROL 0x0089 /* 8 bit */ #define TI1250_GPIO2_CONTROL 0x008a /* 8 bit */ #define TI1250_GPIO3_CONTROL 0x008b /* 8 bit */ -#define TI122X_IRQMUX 0x008c /* 32 bit */ +#define TI1250_GPIO_MODE_MASK 0xc0 + +/* IRQMUX/MFUNC Register */ +#define TI122X_MFUNC 0x008c /* 32 bit */ +#define TI122X_MFUNC0_MASK 0x0000000f +#define TI122X_MFUNC1_MASK 0x000000f0 +#define TI122X_MFUNC2_MASK 0x00000f00 +#define TI122X_MFUNC3_MASK 0x0000f000 +#define TI122X_MFUNC4_MASK 0x000f0000 +#define TI122X_MFUNC5_MASK 0x00f00000 +#define TI122X_MFUNC6_MASK 0x0f000000 + +#define TI122X_MFUNC0_INTA 0x00000002 +#define TI125X_MFUNC0_INTB 0x00000001 +#define TI122X_MFUNC1_INTB 0x00000020 +#define TI122X_MFUNC3_IRQSER 0x00001000 + /* Retry Status Register */ #define TI113X_RETRY_STATUS 0x0090 /* 8 bit */ @@ -151,7 +167,7 @@ static void ti_save_state(struct yenta_socket *socket) { ti_sysctl(socket) = config_readl(socket, TI113X_SYSTEM_CONTROL); - ti_irqmux(socket) = config_readl(socket, TI122X_IRQMUX); + ti_irqmux(socket) = config_readl(socket, TI122X_MFUNC); ti_cardctl(socket) = config_readb(socket, TI113X_CARD_CONTROL); ti_devctl(socket) = config_readb(socket, TI113X_DEVICE_CONTROL); ti_diag(socket) = config_readb(socket, TI1250_DIAGNOSTIC); @@ -160,7 +176,7 @@ static void ti_save_state(struct yenta_s static void ti_restore_state(struct yenta_socket *socket) { config_writel(socket, TI113X_SYSTEM_CONTROL, ti_sysctl(socket)); - config_writel(socket, TI122X_IRQMUX, ti_irqmux(socket)); + config_writel(socket, TI122X_MFUNC, ti_irqmux(socket)); config_writeb(socket, TI113X_CARD_CONTROL, ti_cardctl(socket)); config_writeb(socket, TI113X_DEVICE_CONTROL, ti_devctl(socket)); config_writeb(socket, TI1250_DIAGNOSTIC, ti_diag(socket)); @@ -252,7 +268,7 @@ static void ti_set_zv(struct yenta_socke * INTCTL register that sets the PCI CSC interrupt. * Make sure we set it correctly at open and init * time - * - open: disable the PCI CSC interrupt. This makes + * - override: disable the PCI CSC interrupt. This makes * it possible to use the CSC interrupt to probe the * ISA interrupts. * - init: set the interrupt to match our PCI state. @@ -281,33 +297,6 @@ static int ti_override(struct yenta_sock ti_set_zv(socket); -#if 0 - /* - * If ISA interrupts don't work, then fall back to routing card - * interrupts to the PCI interrupt of the socket. - * - * Tweaking this when we are using serial PCI IRQs causes hangs - * --rmk - */ - if (!socket->socket.irq_mask) { - u8 irqmux, devctl; - - devctl = config_readb(socket, TI113X_DEVICE_CONTROL); - if ((devctl & TI113X_DCR_IMODE_MASK) != TI12XX_DCR_IMODE_ALL_SERIAL) { - printk (KERN_INFO "ti113x: Routing card interrupts to PCI\n"); - - devctl &= ~TI113X_DCR_IMODE_MASK; - - irqmux = config_readl(socket, TI122X_IRQMUX); - irqmux = (irqmux & ~0x0f) | 0x02; /* route INTA */ - irqmux = (irqmux & ~0xf0) | 0x20; /* route INTB */ - - config_writel(socket, TI122X_IRQMUX, irqmux); - config_writeb(socket, TI113X_DEVICE_CONTROL, devctl); - } - } -#endif - return 0; } @@ -325,6 +314,269 @@ static int ti113x_override(struct yenta_ } +/* irqrouting for func0, probes PCI interrupt and ISA interrupts */ +static void ti12xx_irqroute_func0(struct yenta_socket *socket) +{ + u32 mfunc, mfunc_old, devctl; + u8 gpio3, gpio3_old; + int pci_irq_status; + + mfunc = mfunc_old = config_readl(socket, TI122X_MFUNC); + devctl = config_readb(socket, TI113X_DEVICE_CONTROL); + printk(KERN_INFO "Yenta TI: socket %s, mfunc 0x%08x, devctl 0x%02x\n", + pci_name(socket->dev), mfunc, devctl); + + /* make sure PCI interrupts are enabled before probing */ + ti_init(socket); + + /* test PCI interrupts first. only try fixing if return value is 0! */ + pci_irq_status = yenta_probe_cb_irq(socket); + if (pci_irq_status) + goto out; + + /* + * We're here which means PCI interrupts are _not_ delivered. try to + * find the right setting (all serial or parallel) + */ + printk(KERN_INFO "Yenta TI: socket %s probing PCI interrupt failed, trying to fix\n", + pci_name(socket->dev)); + + /* for serial PCI make sure MFUNC3 is set to IRQSER */ + if ((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) { + 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: + mfunc = (mfunc & ~TI122X_MFUNC3_MASK) | TI122X_MFUNC3_IRQSER; + + /* write down if changed, probe */ + if (mfunc != mfunc_old) { + config_writel(socket, TI122X_MFUNC, mfunc); + + pci_irq_status = yenta_probe_cb_irq(socket); + if (pci_irq_status == 1) { + printk(KERN_INFO "Yenta TI: socket %s all-serial interrupts ok\n", + pci_name(socket->dev)); + mfunc_old = mfunc; + goto out; + } + + /* not working, back to old value */ + mfunc = mfunc_old; + config_writel(socket, TI122X_MFUNC, mfunc); + + if (pci_irq_status == -1) + goto out; + } + } + + /* serial PCI interrupts not working fall back to parallel */ + printk(KERN_INFO "Yenta TI: socket %s falling back to parallel PCI interrupts\n", + pci_name(socket->dev)); + devctl &= ~TI113X_DCR_IMODE_MASK; + devctl |= TI113X_DCR_IMODE_SERIAL; /* serial ISA could be right */ + config_writeb(socket, TI113X_DEVICE_CONTROL, devctl); + } + + /* parallel PCI interrupts: route INTA */ + 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: + /* make sure GPIO3 is set to INTA */ + gpio3 = gpio3_old = config_readb(socket, TI1250_GPIO3_CONTROL); + gpio3 &= ~TI1250_GPIO_MODE_MASK; + if (gpio3 != gpio3_old) + config_writeb(socket, TI1250_GPIO3_CONTROL, gpio3); + break; + + default: + gpio3 = gpio3_old = 0; + + mfunc = (mfunc & ~TI122X_MFUNC0_MASK) | TI122X_MFUNC0_INTA; + if (mfunc != mfunc_old) + config_writel(socket, TI122X_MFUNC, mfunc); + } + + /* time to probe again */ + pci_irq_status = yenta_probe_cb_irq(socket); + if (pci_irq_status == 1) { + mfunc_old = mfunc; + printk(KERN_INFO "Yenta TI: socket %s parallel PCI interrupts ok\n", + pci_name(socket->dev)); + } else { + /* not working, back to old value */ + mfunc = mfunc_old; + config_writel(socket, TI122X_MFUNC, mfunc); + if (gpio3 != gpio3_old) + config_writeb(socket, TI1250_GPIO3_CONTROL, gpio3_old); + } + +out: + if (pci_irq_status < 1) { + socket->cb_irq = 0; + printk(KERN_INFO "Yenta TI: socket %s no PCI interrupts. Fish. Please report.\n", + pci_name(socket->dev)); + } +} + + +/* + * ties INTA and INTB together. also changes the devices irq to that of + * the function 0 device. call from func1 only. + * returns 1 if INTRTIE changed, 0 otherwise. + */ +static int ti12xx_tie_interrupts(struct yenta_socket *socket, int *old_irq) +{ + struct pci_dev *func0; + u32 sysctl; + + sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL); + if (sysctl & TI122X_SCR_INTRTIE) + return 0; + + /* find func0 device */ + func0 = pci_find_slot(socket->dev->bus->number, socket->dev->devfn & ~0x07); + if (!func0) + return 0; + + /* change the interrupt to match func0, tie 'em up */ + *old_irq = socket->cb_irq; + socket->cb_irq = socket->dev->irq = func0->irq; + sysctl |= TI122X_SCR_INTRTIE; + config_writel(socket, TI113X_SYSTEM_CONTROL, sysctl); + + return 1; +} + +/* undo what ti12xx_tie_interrupts() did */ +static void ti12xx_untie_interrupts(struct yenta_socket *socket, int old_irq) +{ + u32 sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL); + sysctl &= ~TI122X_SCR_INTRTIE; + config_writel(socket, TI113X_SYSTEM_CONTROL, sysctl); + + socket->cb_irq = socket->dev->irq = old_irq; +} + +/* + * irqrouting for func1, plays with INTB routing + * only touches MFUNC for INTB routing. all other bits are taken + * care of in func0 already. + */ +static void ti12xx_irqroute_func1(struct yenta_socket *socket) +{ + u32 mfunc, mfunc_old, devctl; + int pci_irq_status; + + mfunc = mfunc_old = config_readl(socket, TI122X_MFUNC); + devctl = config_readb(socket, TI113X_DEVICE_CONTROL); + printk(KERN_INFO "Yenta TI: socket %s, mfunc 0x%08x, devctl 0x%02x\n", + pci_name(socket->dev), mfunc, devctl); + + /* make sure PCI interrupts are enabled before probing */ + ti_init(socket); + + /* test PCI interrupts first. only try fixing if return value is 0! */ + pci_irq_status = yenta_probe_cb_irq(socket); + if (pci_irq_status) + goto out; + + /* + * We're here which means PCI interrupts are _not_ delivered. try to + * find the right setting + */ + printk(KERN_INFO "Yenta TI: socket %s probing PCI interrupt failed, trying to fix\n", + pci_name(socket->dev)); + + + /* if all serial: set INTRTIE, probe again */ + if ((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) { + int old_irq; + + if (ti12xx_tie_interrupts(socket, &old_irq)) { + pci_irq_status = yenta_probe_cb_irq(socket); + if (pci_irq_status == 1) { + printk(KERN_INFO "Yenta TI: socket %s all-serial interrupts, tied ok\n", + pci_name(socket->dev)); + goto out; + } + + ti12xx_untie_interrupts(socket, old_irq); + } + } + /* parallel PCI: route INTB, probe again */ + else { + int old_irq; + + switch (socket->dev->device) { + case PCI_DEVICE_ID_TI_1250: + /* the 1250 has one pin for IRQSER/INTB depending on devctl */ + break; + + case PCI_DEVICE_ID_TI_1251A: + case PCI_DEVICE_ID_TI_1251B: + case PCI_DEVICE_ID_TI_1450: + /* + * those have a pin for IRQSER/INTB plus INTB in MFUNC0 + * we alread probed the shared pin, now go for MFUNC0 + */ + mfunc = (mfunc & ~TI122X_MFUNC0_MASK) | TI125X_MFUNC0_INTB; + break; + + default: + mfunc = (mfunc & ~TI122X_MFUNC1_MASK) | TI122X_MFUNC1_INTB; + break; + } + + /* write, probe */ + if (mfunc != mfunc_old) { + config_writel(socket, TI122X_MFUNC, mfunc); + + pci_irq_status = yenta_probe_cb_irq(socket); + if (pci_irq_status == 1) { + printk(KERN_INFO "Yenta TI: socket %s parallel PCI interrupts ok\n", + pci_name(socket->dev)); + goto out; + } + + mfunc = mfunc_old; + config_writel(socket, TI122X_MFUNC, mfunc); + + if (pci_irq_status == -1) + goto out; + } + + /* still nothing: set INTRTIE */ + if (ti12xx_tie_interrupts(socket, &old_irq)) { + pci_irq_status = yenta_probe_cb_irq(socket); + if (pci_irq_status == 1) { + printk(KERN_INFO "Yenta TI: socket %s parallel PCI interrupts, tied ok\n", + pci_name(socket->dev)); + goto out; + } + + ti12xx_untie_interrupts(socket, old_irq); + } + } + +out: + if (pci_irq_status < 1) { + socket->cb_irq = 0; + printk(KERN_INFO "Yenta TI: socket %s no PCI interrupts. Fish. Please report.\n", + pci_name(socket->dev)); + } +} + static int ti12xx_override(struct yenta_socket *socket) { u32 val; @@ -347,6 +599,12 @@ static int ti12xx_override(struct yenta_ printk(KERN_INFO "Yenta: Routing CardBus interrupts to %s\n", (val & TI1250_DIAG_PCI_IREQ) ? "PCI" : "ISA"); + /* do irqrouting, depending on function */ + if (PCI_FUNC(socket->dev->devfn) == 0) + ti12xx_irqroute_func0(socket); + else + ti12xx_irqroute_func1(socket); + return ti_override(socket); } @@ -366,26 +624,6 @@ static int ti1250_override(struct yenta_ config_writeb(socket, TI1250_DIAGNOSTIC, diag); } -#if 0 - /* - * This is highly machine specific, and we should NOT touch - * this register - we have no knowledge how the hardware - * is actually wired. - * - * If we're going to do this, we should probably look into - * using the subsystem IDs. - * - * On ThinkPad 380XD, this changes MFUNC0 from the ISA IRQ3 - * output (which it is) to IRQ2. We also change MFUNC1 - * from ISA IRQ4 to IRQ6. - */ - irqmux = config_readl(socket, TI122X_IRQMUX); - irqmux = (irqmux & ~0x0f) | 0x02; /* route INTA */ - if (!(ti_sysctl(socket) & TI122X_SCR_INTRTIE)) - irqmux = (irqmux & ~0xf0) | 0x20; /* route INTB */ - config_writel(socket, TI122X_IRQMUX, irqmux); -#endif - return ti12xx_override(socket); } diff -puN drivers/pcmcia/yenta_socket.c~yenta-TI-irq-routing-fix drivers/pcmcia/yenta_socket.c --- 25/drivers/pcmcia/yenta_socket.c~yenta-TI-irq-routing-fix 2004-04-05 18:45:51.272416976 -0700 +++ 25-akpm/drivers/pcmcia/yenta_socket.c 2004-04-05 18:45:51.282415456 -0700 @@ -39,6 +39,9 @@ #define to_cycles(ns) ((ns)/120) #define to_ns(cycles) ((cycles)*120) +static int yenta_probe_cb_irq(struct yenta_socket *socket); + + /* * Generate easy-to-use ways of reading a cardbus sockets * regular memory space ("cb_xxx"), configuration space @@ -768,6 +771,69 @@ static unsigned int yenta_probe_irq(stru return mask; } + +/* interrupt handler, only used during probing */ +static irqreturn_t yenta_probe_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + struct yenta_socket *socket = (struct yenta_socket *) dev_id; + u8 csc; + u32 cb_event; + + /* Clear interrupt status for the event */ + cb_event = cb_readl(socket, CB_SOCKET_EVENT); + cb_writel(socket, CB_SOCKET_EVENT, -1); + csc = exca_readb(socket, I365_CSC); + + if (cb_event || csc) { + socket->probe_status = 1; + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/* probes the PCI interrupt, use only on override functions */ +static int yenta_probe_cb_irq(struct yenta_socket *socket) +{ + u16 bridge_ctrl; + + if (!socket->cb_irq) + return -1; + + socket->probe_status = 0; + + /* disable ISA interrupts */ + bridge_ctrl = config_readw(socket, CB_BRIDGE_CONTROL); + bridge_ctrl &= ~CB_BRIDGE_INTR; + config_writew(socket, CB_BRIDGE_CONTROL, bridge_ctrl); + + if (request_irq(socket->cb_irq, yenta_probe_handler, SA_SHIRQ, "yenta", socket)) { + printk(KERN_WARNING "Yenta: request_irq() in yenta_probe_cb_irq() failed!\n"); + return -1; + } + + /* generate interrupt, wait */ + exca_writeb(socket, I365_CSCINT, I365_CSC_STSCHG); + cb_writel(socket, CB_SOCKET_EVENT, -1); + cb_writel(socket, CB_SOCKET_MASK, CB_CSTSMASK); + cb_writel(socket, CB_SOCKET_FORCE, CB_FCARDSTS); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/10); + + /* disable interrupts */ + cb_writel(socket, CB_SOCKET_MASK, 0); + exca_writeb(socket, I365_CSCINT, 0); + cb_writel(socket, CB_SOCKET_EVENT, -1); + exca_readb(socket, I365_CSC); + + free_irq(socket->cb_irq, socket); + + return (int) socket->probe_status; +} + + + /* * Set static data that doesn't need re-initializing.. */ @@ -997,7 +1063,6 @@ static struct pci_device_id yenta_table * data sheets for these devices. --rmk) */ CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1210, TI), - CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1251B, TI), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1130, TI113X), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1131, TI113X), @@ -1007,11 +1072,14 @@ static struct pci_device_id yenta_table CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1221, TI12XX), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1225, TI12XX), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1251A, TI12XX), + CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1251B, TI12XX), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1420, TI12XX), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1450, TI12XX), + CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1451A, TI12XX), + CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1510, TI12XX), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1520, TI12XX), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_4410, TI12XX), -// CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_4450, TI12XX), + CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_4450, TI12XX), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_4451, TI12XX), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1250, TI1250), diff -puN drivers/pcmcia/yenta_socket.h~yenta-TI-irq-routing-fix drivers/pcmcia/yenta_socket.h --- 25/drivers/pcmcia/yenta_socket.h~yenta-TI-irq-routing-fix 2004-04-05 18:45:51.274416672 -0700 +++ 25-akpm/drivers/pcmcia/yenta_socket.h 2004-04-05 18:45:51.283415304 -0700 @@ -113,6 +113,9 @@ struct yenta_socket { struct pcmcia_socket socket; struct cardbus_type *type; + /* for PCI interrupt probing */ + unsigned int probe_status; + /* A few words of private data for special stuff of overrides... */ unsigned int private[8]; diff -puN include/linux/pci_ids.h~yenta-TI-irq-routing-fix include/linux/pci_ids.h --- 25/include/linux/pci_ids.h~yenta-TI-irq-routing-fix 2004-04-05 18:45:51.275416520 -0700 +++ 25-akpm/include/linux/pci_ids.h 2004-04-05 18:45:51.285415000 -0700 @@ -708,6 +708,7 @@ #define PCI_VENDOR_ID_TI 0x104c #define PCI_DEVICE_ID_TI_TVP4010 0x3d04 #define PCI_DEVICE_ID_TI_TVP4020 0x3d07 +#define PCI_DEVICE_ID_TI_4450 0x8011 #define PCI_DEVICE_ID_TI_1130 0xac12 #define PCI_DEVICE_ID_TI_1031 0xac13 #define PCI_DEVICE_ID_TI_1131 0xac15 @@ -724,6 +725,7 @@ #define PCI_DEVICE_ID_TI_4451 0xac42 #define PCI_DEVICE_ID_TI_1410 0xac50 #define PCI_DEVICE_ID_TI_1420 0xac51 +#define PCI_DEVICE_ID_TI_1451A 0xac52 #define PCI_DEVICE_ID_TI_1520 0xac55 #define PCI_DEVICE_ID_TI_1510 0xac56 _