From 18c71d48755ee4a8b5f51d112dda24612a38c521 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski <linux@dominikbrodowski.de> Date: Tue, 11 Jan 2005 03:26:09 -0800 Subject: [PATCH] [PATCH] pcmcia: pd6729: isa_irq handling From: Komuro <komurojun-mbn@nifty.com> 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 <linux@brodo.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org> --- drivers/pcmcia/pd6729.c | 176 ++++++++++++++++++++++++++++++++-------- drivers/pcmcia/pd6729.h | 6 +- 2 files changed, 147 insertions(+), 35 deletions(-) diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c index 6b01d6a40724..ded56ce22d17 100644 --- a/drivers/pcmcia/pd6729.c +++ b/drivers/pcmcia/pd6729.c @@ -39,6 +39,34 @@ MODULE_AUTHOR("Jun Komuro <komurojun-mbn@nifty.com>"); */ #define to_cycles(ns) ((ns)/120) +#ifndef NO_IRQ +#define NO_IRQ ((unsigned int)(0)) +#endif + +/* + * PARAMETERS + * irq_mode=n + * Specifies the interrupt delivery mode. The default (1) is to use PCI + * interrupts; a value of 0 selects ISA interrupts. This must be set for + * correct operation of PCI card readers. + * + * irq_list=i,j,... + * This list limits the set of interrupts that can be used by PCMCIA + * cards. + * The default list is 3,4,5,7,9,10,11. + * (irq_list parameter is not used, if irq_mode = 1) + */ + +static int irq_mode = 1; /* 0 = ISA interrupt, 1 = PCI interrupt */ +static int irq_list[16]; +static int irq_list_count = 0; + +module_param(irq_mode, int, 0444); +module_param_array(irq_list, int, &irq_list_count, 0444); +MODULE_PARM_DESC(irq_mode, + "interrupt delivery mode. 0 = ISA, 1 = PCI. default is 1"); +MODULE_PARM_DESC(irq_list, "interrupts that can be used by PCMCIA cards"); + static spinlock_t port_lock = SPIN_LOCK_UNLOCKED; /* basic value read/write functions */ @@ -218,12 +246,12 @@ static irqreturn_t pd6729_interrupt(int irq, void *dev, struct pt_regs *regs) /* socket functions */ -static void set_bridge_state(struct pd6729_socket *socket) +static void pd6729_interrupt_wrapper(unsigned long data) { - indirect_write(socket, I365_GBLCTL, 0x00); - indirect_write(socket, I365_GENCTL, 0x00); + struct pd6729_socket *socket = (struct pd6729_socket *) data; - indirect_setbit(socket, I365_INTCTL, 0x08); + pd6729_interrupt(0, (void *)socket, NULL); + mod_timer(&socket->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 pcmcia_socket *sock, socket_state_t *state) 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 pcmcia_socket *sock, socket_state_t *state) 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 pcmcia_socket *sock, socket_state_t *state) 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 pcmcia_socket *sock, socket_state_t *state) { 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 pcmcia_socket *sock, socket_state_t *state) 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 pcmcia_socket *sock, socket_state_t *state) 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_operations = { .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_list[i]); + + mask0 &= PD67_MASK; + + /* just find interrupts that aren't in use */ + for (i = 0; i < 16; i++) + if ((mask0 & (1 << i)) && (pd6729_check_irq(i, 0) == 0)) + mask |= (1 << i); + + printk(KERN_INFO "pd6729: ISA irqs = "); + for (i = 0; i < 16; i++) + if (mask & (1<<i)) + printk("%s%d", ((mask & ((1<<i)-1)) ? "," : ""), i); + + if (mask == 0) printk("none!"); + + printk(" polling status changes.\n"); + + return mask; +} + static int __devinit pd6729_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { int i, j, ret; + u_int mask; char configbyte; struct pd6729_socket *socket; @@ -659,11 +739,20 @@ static int __devinit pd6729_pci_probe(struct pci_dev *dev, goto err_out_disable; } + if (dev->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(struct pci_dev *dev, } 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(struct pci_dev *dev, 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(struct pci_dev *dev) 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 --git a/drivers/pcmcia/pd6729.h b/drivers/pcmcia/pd6729.h index 9e90520ef6cc..f392e458cdfd 100644 --- a/drivers/pcmcia/pd6729.h +++ b/drivers/pcmcia/pd6729.h @@ -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 -- 2.30.9