X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fpcmcia%2Fpd6729.c;h=05da3bf154ede186db37caa4b22f65cfb7fdeb39;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=4f00c9dda0754ba562d59d78e602b98598f17187;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c index 4f00c9dda..05da3bf15 100644 --- a/drivers/pcmcia/pd6729.c +++ b/drivers/pcmcia/pd6729.c @@ -29,7 +29,7 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Driver for the Cirrus PD6729 PCI-PCMCIA bridge"); -MODULE_AUTHOR("Jun Komuro "); +MODULE_AUTHOR("Jun Komuro "); #define MAX_SOCKETS 2 @@ -39,11 +39,40 @@ MODULE_AUTHOR("Jun Komuro "); */ #define to_cycles(ns) ((ns)/120) -static spinlock_t port_lock = SPIN_LOCK_UNLOCKED; +#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 DEFINE_SPINLOCK(port_lock); /* basic value read/write functions */ -static unsigned char indirect_read(struct pd6729_socket *socket, unsigned short reg) +static unsigned char indirect_read(struct pd6729_socket *socket, + unsigned short reg) { unsigned long port; unsigned char val; @@ -59,7 +88,8 @@ static unsigned char indirect_read(struct pd6729_socket *socket, unsigned short return val; } -static unsigned short indirect_read16(struct pd6729_socket *socket, unsigned short reg) +static unsigned short indirect_read16(struct pd6729_socket *socket, + unsigned short reg) { unsigned long port; unsigned short tmp; @@ -78,7 +108,8 @@ static unsigned short indirect_read16(struct pd6729_socket *socket, unsigned sho return tmp; } -static void indirect_write(struct pd6729_socket *socket, unsigned short reg, unsigned char value) +static void indirect_write(struct pd6729_socket *socket, unsigned short reg, + unsigned char value) { unsigned long port; unsigned long flags; @@ -91,7 +122,8 @@ static void indirect_write(struct pd6729_socket *socket, unsigned short reg, uns spin_unlock_irqrestore(&port_lock, flags); } -static void indirect_setbit(struct pd6729_socket *socket, unsigned short reg, unsigned char mask) +static void indirect_setbit(struct pd6729_socket *socket, unsigned short reg, + unsigned char mask) { unsigned long port; unsigned char val; @@ -108,7 +140,8 @@ static void indirect_setbit(struct pd6729_socket *socket, unsigned short reg, un spin_unlock_irqrestore(&port_lock, flags); } -static void indirect_resetbit(struct pd6729_socket *socket, unsigned short reg, unsigned char mask) +static void indirect_resetbit(struct pd6729_socket *socket, unsigned short reg, + unsigned char mask) { unsigned long port; unsigned char val; @@ -125,7 +158,8 @@ static void indirect_resetbit(struct pd6729_socket *socket, unsigned short reg, spin_unlock_irqrestore(&port_lock, flags); } -static void indirect_write16(struct pd6729_socket *socket, unsigned short reg, unsigned short value) +static void indirect_write16(struct pd6729_socket *socket, unsigned short reg, + unsigned short value) { unsigned long port; unsigned char val; @@ -160,7 +194,8 @@ static irqreturn_t pd6729_interrupt(int irq, void *dev, struct pt_regs *regs) while (1) { loopcount++; if (loopcount > 20) { - printk(KERN_ERR "pd6729: infinite eventloop in interrupt\n"); + printk(KERN_ERR "pd6729: infinite eventloop " + "in interrupt\n"); break; } @@ -182,14 +217,19 @@ static irqreturn_t pd6729_interrupt(int irq, void *dev, struct pt_regs *regs) dprintk("Card detected in socket %i!\n", i); } - if (indirect_read(&socket[i], I365_INTCTL) & I365_PC_IOCARD) { + if (indirect_read(&socket[i], I365_INTCTL) + & I365_PC_IOCARD) { /* For IO/CARDS, bit 0 means "read the card" */ - events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0; + events |= (csc & I365_CSC_STSCHG) + ? SS_STSCHG : 0; } else { /* Check for battery/ready events */ - events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0; - events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0; - events |= (csc & I365_CSC_READY) ? SS_READY : 0; + events |= (csc & I365_CSC_BVD1) + ? SS_BATDEAD : 0; + events |= (csc & I365_CSC_BVD2) + ? SS_BATWARN : 0; + events |= (csc & I365_CSC_READY) + ? SS_READY : 0; } if (events) { @@ -206,17 +246,18 @@ 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) { - struct pd6729_socket *socket = container_of(sock, struct pd6729_socket, socket); + struct pd6729_socket *socket + = container_of(sock, struct pd6729_socket, socket); unsigned int status; unsigned int data; struct pd6729_socket *t; @@ -265,7 +306,8 @@ static int pd6729_get_status(struct pcmcia_socket *sock, u_int *value) static int pd6729_get_socket(struct pcmcia_socket *sock, socket_state_t *state) { - struct pd6729_socket *socket = container_of(sock, struct pd6729_socket, socket); + struct pd6729_socket *socket + = container_of(sock, struct pd6729_socket, socket); unsigned char reg, vcc, vpp; state->flags = 0; @@ -274,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) @@ -302,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) @@ -314,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) @@ -342,21 +375,23 @@ static int pd6729_get_socket(struct pcmcia_socket *sock, socket_state_t *state) 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; + struct pd6729_socket *socket + = container_of(sock, struct pd6729_socket, socket); + 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 */ if (!(state->flags & SS_RESET)) - reg = reg | I365_PC_RESET; + reg |= I365_PC_RESET; if (state->flags & SS_IOCARD) - reg = reg | I365_PC_IOCARD; + reg |= I365_PC_IOCARD; /* IGENC, Interrupt and General Control Register */ indirect_write(socket, I365_INTCTL, reg); @@ -390,7 +425,8 @@ static int pd6729_set_socket(struct pcmcia_socket *sock, socket_state_t *state) indirect_resetbit(socket, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); break; default: - dprintk("pd6729: pd6729_set_socket called with invalid VCC power value: %i\n", + dprintk("pd6729: pd6729_set_socket called with " + "invalid VCC power value: %i\n", state->Vcc); return -EINVAL; } @@ -418,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 */ @@ -439,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 */ @@ -452,9 +497,11 @@ static int pd6729_set_socket(struct pcmcia_socket *sock, socket_state_t *state) return 0; } -static int pd6729_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) +static int pd6729_set_io_map(struct pcmcia_socket *sock, + struct pccard_io_map *io) { - struct pd6729_socket *socket = container_of(sock, struct pd6729_socket, socket); + struct pd6729_socket *socket + = container_of(sock, struct pd6729_socket, socket); unsigned char map, ioctl; map = io->map; @@ -469,7 +516,8 @@ static int pd6729_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *i if (indirect_read(socket, I365_ADDRWIN) & I365_ENA_IO(map)) indirect_resetbit(socket, I365_ADDRWIN, I365_ENA_IO(map)); -/* dprintk("set_io_map: Setting range to %x - %x\n", io->start, io->stop);*/ + /* dprintk("set_io_map: Setting range to %x - %x\n", + io->start, io->stop);*/ /* write the new values */ indirect_write16(socket, I365_IO(map)+I365_W_START, io->start); @@ -490,9 +538,11 @@ static int pd6729_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *i return 0; } -static int pd6729_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem) +static int pd6729_set_mem_map(struct pcmcia_socket *sock, + struct pccard_mem_map *mem) { - struct pd6729_socket *socket = container_of(sock, struct pd6729_socket, socket); + struct pd6729_socket *socket + = container_of(sock, struct pd6729_socket, socket); unsigned short base, i; unsigned char map; @@ -504,8 +554,6 @@ static int pd6729_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map if ((mem->res->start > mem->res->end) || (mem->speed > 1000)) { printk("pd6729_set_mem_map: invalid address / speed"); - /* printk("invalid mem map for socket %i : %lx to %lx with a start of %x\n", - sock, mem->res->start, mem->res->end, mem->card_start); */ return -EINVAL; } @@ -551,11 +599,11 @@ static int pd6729_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT; if (mem->flags & MAP_ATTRIB) { -/* dprintk("requesting attribute memory for socket %i\n", + /* dprintk("requesting attribute memory for socket %i\n", socket->number);*/ i |= I365_MEM_REG; } else { -/* dprintk("requesting normal memory for socket %i\n", + /* dprintk("requesting normal memory for socket %i\n", socket->number);*/ } indirect_write16(socket, base + I365_W_OFF, i); @@ -604,13 +652,66 @@ static struct pccard_operations pd6729_operations = { .set_mem_map = pd6729_set_mem_map, }; -static int __devinit pd6729_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +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); - printk(KERN_INFO "pd6729: configured as a %d socket device.\n", MAX_SOCKETS); + printk(KERN_INFO "pd6729: Cirrus PD6729 PCI to PCMCIA Bridge " + "at 0x%lx on irq %d\n", pci_resource_start(dev, 0), dev->irq); /* - * Since we have no memory BARs some firmware we may not - * have had PCI_COMMAND_MEM enabled, yet the device needs - * it. + * Since we have no memory BARs some firmware may not + * have had PCI_COMMAND_MEMORY enabled, yet the device needs it. */ pci_read_config_byte(dev, PCI_COMMAND, &configbyte); if (!(configbyte & PCI_COMMAND_MEMORY)) { @@ -640,33 +739,54 @@ static int __devinit pd6729_pci_probe(struct pci_dev *dev, const struct pci_devi 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; socket[i].number = i; socket[i].socket.ops = &pd6729_operations; + socket[i].socket.resource_ops = &pccard_nonstatic_ops; socket[i].socket.dev.dev = &dev->dev; socket[i].socket.driver_data = &socket[i]; } 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++) { ret = pcmcia_register_socket(&socket[i].socket); if (ret) { - printk(KERN_INFO "pd6729: pcmcia_register_socket failed.\n"); + printk(KERN_INFO "pd6729: pcmcia_register_socket " + "failed.\n"); for (j = 0; j < i ; j++) pcmcia_unregister_socket(&socket[j].socket); goto err_out_free_res2; @@ -676,7 +796,10 @@ static int __devinit pd6729_pci_probe(struct pci_dev *dev, const struct pci_devi 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: @@ -692,10 +815,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);