From: Dominik Brodowski From: Komuro Allow for ISA interrupt routing on pd6729 pcmcia sockets. This is necessary for correct operation of (certain?) PCI card readers. Signed-off-by: Dominik Brodowski Signed-off-by: Andrew Morton --- 25-akpm/drivers/pcmcia/pd6729.c | 176 ++++++++++++++++++++++++++++++++-------- 25-akpm/drivers/pcmcia/pd6729.h | 6 - 2 files changed, 147 insertions(+), 35 deletions(-) diff -puN drivers/pcmcia/pd6729.c~pcmcia-pd6729-isa_irq-handling drivers/pcmcia/pd6729.c --- 25/drivers/pcmcia/pd6729.c~pcmcia-pd6729-isa_irq-handling Fri Dec 17 15:24:57 2004 +++ 25-akpm/drivers/pcmcia/pd6729.c Fri Dec 17 15:24:57 2004 @@ -39,6 +39,34 @@ MODULE_AUTHOR("Jun Komuro poll_timer, jiffies + HZ); } static int pd6729_get_status(struct pcmcia_socket *sock, u_int *value) @@ -288,10 +316,7 @@ static int pd6729_get_socket(struct pcmc state->io_irq = 0; state->csc_mask = 0; - /* - * First the power status of the socket - * PCTRL - Power Control Register - */ + /* First the power status of the socket */ reg = indirect_read(socket, I365_POWER); if (reg & I365_PWR_AUTO) @@ -316,10 +341,7 @@ static int pd6729_get_socket(struct pcmc state->Vpp = 120; } - /* - * Now the IO card, RESET flags and IO interrupt - * IGENC, Interrupt and General Control - */ + /* Now the IO card, RESET flags and IO interrupt */ reg = indirect_read(socket, I365_INTCTL); if ((reg & I365_PC_RESET) == 0) @@ -328,12 +350,9 @@ static int pd6729_get_socket(struct pcmc state->flags |= SS_IOCARD; /* This is an IO card */ /* Set the IRQ number */ - state->io_irq = socket->socket.pci_irq; + state->io_irq = socket->card_irq; - /* - * Card status change - * CSCICR, Card Status Change Interrupt Configuration - */ + /* Card status change */ reg = indirect_read(socket, I365_CSCINT); if (reg & I365_CSC_DETECT) @@ -358,13 +377,14 @@ static int pd6729_set_socket(struct pcmc { struct pd6729_socket *socket = container_of(sock, struct pd6729_socket, socket); - unsigned char reg; + unsigned char reg, data; /* First, set the global controller options */ - - set_bridge_state(socket); + indirect_write(socket, I365_GBLCTL, 0x00); + indirect_write(socket, I365_GENCTL, 0x00); /* Values for the IGENC register */ + socket->card_irq = state->io_irq; reg = 0; /* The reset bit has "inverse" logic */ @@ -434,9 +454,14 @@ static int pd6729_set_socket(struct pcmc if (reg != indirect_read(socket, I365_POWER)) indirect_write(socket, I365_POWER, reg); - /* Now, specifiy that all interrupts are to be done as PCI interrupts */ + if (irq_mode == 1) { + /* all interrupts are to be done as PCI interrupts */ + data = PD67_EC1_INV_MGMT_IRQ | PD67_EC1_INV_CARD_IRQ; + } else + data = 0; + indirect_write(socket, PD67_EXT_INDEX, PD67_EXT_CTL_1); - indirect_write(socket, PD67_EXT_DATA, PD67_EC1_INV_MGMT_IRQ | PD67_EC1_INV_CARD_IRQ); + indirect_write(socket, PD67_EXT_DATA, data); /* Enable specific interrupt events */ @@ -455,11 +480,15 @@ static int pd6729_set_socket(struct pcmc if (state->csc_mask & SS_READY) reg |= I365_CSC_READY; } - reg |= 0x30; /* management IRQ: PCI INTA# = "irq 3" */ + if (irq_mode == 1) + reg |= 0x30; /* management IRQ: PCI INTA# = "irq 3" */ indirect_write(socket, I365_CSCINT, reg); reg = indirect_read(socket, I365_INTCTL); - reg |= 0x03; /* card IRQ: PCI INTA# = "irq 3" */ + if (irq_mode == 1) + reg |= 0x03; /* card IRQ: PCI INTA# = "irq 3" */ + else + reg |= socket->card_irq; indirect_write(socket, I365_INTCTL, reg); /* now clear the (probably bogus) pending stuff by doing a dummy read */ @@ -623,10 +652,61 @@ static struct pccard_operations pd6729_o .set_mem_map = pd6729_set_mem_map, }; +static irqreturn_t pd6729_test(int irq, void *dev, struct pt_regs *regs) +{ + dprintk("-> hit on irq %d\n", irq); + return IRQ_HANDLED; +} + +static int pd6729_check_irq(int irq, int flags) +{ + if (request_irq(irq, pd6729_test, flags, "x", pd6729_test) != 0) + return -1; + free_irq(irq, pd6729_test); + return 0; +} + +static u_int __init pd6729_isa_scan(void) +{ + u_int mask0, mask = 0; + int i; + + if (irq_mode == 1) { + printk(KERN_INFO "pd6729: PCI card interrupts, " + "PCI status changes\n"); + return 0; + } + + if (irq_list_count == 0) + mask0 = 0xffff; + else + for (i = mask0 = 0; i < irq_list_count; i++) + mask0 |= (1<irq == NO_IRQ) + irq_mode = 0; /* fall back to ISA interrupt mode */ + + mask = pd6729_isa_scan(); + if (irq_mode == 0 && mask == 0) { + printk(KERN_INFO "pd6729: no ISA interrupt is available.\n"); + goto err_out_free_res; + } + for (i = 0; i < MAX_SOCKETS; i++) { socket[i].io_base = pci_resource_start(dev, 0); socket[i].socket.features |= SS_CAP_PCCARD; socket[i].socket.map_size = 0x1000; - socket[i].socket.irq_mask = 0; + socket[i].socket.irq_mask = mask; socket[i].socket.pci_irq = dev->irq; socket[i].socket.owner = THIS_MODULE; @@ -675,11 +764,21 @@ static int __devinit pd6729_pci_probe(st } pci_set_drvdata(dev, socket); - - /* Register the interrupt handler */ - if ((ret = request_irq(dev->irq, pd6729_interrupt, SA_SHIRQ, "pd6729", socket))) { - printk(KERN_ERR "pd6729: Failed to register irq %d, aborting\n", dev->irq); - goto err_out_free_res; + if (irq_mode == 1) { + /* Register the interrupt handler */ + if ((ret = request_irq(dev->irq, pd6729_interrupt, SA_SHIRQ, + "pd6729", socket))) { + printk(KERN_ERR "pd6729: Failed to register irq %d, " + "aborting\n", dev->irq); + goto err_out_free_res; + } + } else { + /* poll Card status change */ + init_timer(&socket->poll_timer); + socket->poll_timer.function = pd6729_interrupt_wrapper; + socket->poll_timer.data = (unsigned long)socket; + socket->poll_timer.expires = jiffies + HZ; + add_timer(&socket->poll_timer); } for (i = 0; i < MAX_SOCKETS; i++) { @@ -696,7 +795,10 @@ static int __devinit pd6729_pci_probe(st return 0; err_out_free_res2: - free_irq(dev->irq, socket); + if (irq_mode == 1) + free_irq(dev->irq, socket); + else + del_timer_sync(&socket->poll_timer); err_out_free_res: pci_release_regions(dev); err_out_disable: @@ -712,10 +814,18 @@ static void __devexit pd6729_pci_remove( int i; struct pd6729_socket *socket = pci_get_drvdata(dev); - for (i = 0; i < MAX_SOCKETS; i++) + for (i = 0; i < MAX_SOCKETS; i++) { + /* Turn off all interrupt sources */ + indirect_write(&socket[i], I365_CSCINT, 0); + indirect_write(&socket[i], I365_INTCTL, 0); + pcmcia_unregister_socket(&socket[i].socket); + } - free_irq(dev->irq, socket); + if (irq_mode == 1) + free_irq(dev->irq, socket); + else + del_timer_sync(&socket->poll_timer); pci_release_regions(dev); pci_disable_device(dev); diff -puN drivers/pcmcia/pd6729.h~pcmcia-pd6729-isa_irq-handling drivers/pcmcia/pd6729.h --- 25/drivers/pcmcia/pd6729.h~pcmcia-pd6729-isa_irq-handling Fri Dec 17 15:24:57 2004 +++ 25-akpm/drivers/pcmcia/pd6729.h Fri Dec 17 15:24:57 2004 @@ -16,13 +16,15 @@ #define PD67_EXD_VS1(s) (0x01 << ((s) << 1)) #define PD67_EXD_VS2(s) (0x02 << ((s) << 1)) - - +/* Default ISA interrupt mask */ +#define PD67_MASK 0x0eb8 /* irq 11,10,9,7,5,4,3 */ struct pd6729_socket { int number; + int card_irq; unsigned long io_base; /* base io address of the socket */ struct pcmcia_socket socket; + struct timer_list poll_timer; }; #endif _