/* * Intel PCIC or compatible Controller driver *------------------------------------------------------------------------- * * Copyright (c) 2001 M. Warner Losh. All rights reserved. * Copyright (c) 1995 Andrew McRae. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include /* Get pnp IDs */ #include #include #include #include "card_if.h" /* * Prototypes for interrupt handler. */ static int pcic_ioctl(struct slot *, int, caddr_t); static int pcic_power(struct slot *); static void pcic_mapirq(struct slot *, int); static timeout_t pcic_reset; static void pcic_resume(struct slot *); static void pcic_disable(struct slot *); static int pcic_memory(struct slot *, int); static int pcic_io(struct slot *, int); devclass_t pcic_devclass; static struct slot_ctrl pcic_cinfo = { pcic_mapirq, pcic_memory, pcic_io, pcic_reset, pcic_disable, pcic_power, pcic_ioctl, pcic_resume, PCIC_MEM_WIN, PCIC_IO_WIN }; /* sysctl vars */ SYSCTL_NODE(_hw, OID_AUTO, pcic, CTLFLAG_RD, 0, "PCIC parameters"); int pcic_override_irq = 0; TUNABLE_INT("machdep.pccard.pcic_irq", &pcic_override_irq); TUNABLE_INT("hw.pcic.irq", &pcic_override_irq); SYSCTL_INT(_hw_pcic, OID_AUTO, irq, CTLFLAG_RD, &pcic_override_irq, 0, "Override the IRQ configured by the config system for all pcic devices"); int pcic_boot_deactivated = 0; TUNABLE_INT("hw.pcic.boot_deactivated", &pcic_boot_deactivated); SYSCTL_INT(_hw_pcic, OID_AUTO, boot_deactivated, CTLFLAG_RD, &pcic_boot_deactivated, 0, "Override the automatic powering up of pccards at boot."); /* * Read a register from the PCIC. */ unsigned char pcic_getb_io(struct pcic_slot *sp, int reg) { bus_space_write_1(sp->bst, sp->bsh, PCIC_INDEX, sp->offset + reg); return (bus_space_read_1(sp->bst, sp->bsh, PCIC_DATA)); } /* * Write a register on the PCIC */ void pcic_putb_io(struct pcic_slot *sp, int reg, unsigned char val) { /* * Many datasheets recommend using outw rather than outb to save * a microsecond. Maybe we should do this, but we'd likely only * save 20-30us on card activation. */ bus_space_write_1(sp->bst, sp->bsh, PCIC_INDEX, sp->offset + reg); bus_space_write_1(sp->bst, sp->bsh, PCIC_DATA, val); } /* * Clear bit(s) of a register. */ __inline void pcic_clrb(struct pcic_slot *sp, int reg, unsigned char mask) { sp->putb(sp, reg, sp->getb(sp, reg) & ~mask); } /* * Set bit(s) of a register */ __inline void pcic_setb(struct pcic_slot *sp, int reg, unsigned char mask) { sp->putb(sp, reg, sp->getb(sp, reg) | mask); } /* * Write a 16 bit value to 2 adjacent PCIC registers */ static __inline void pcic_putw(struct pcic_slot *sp, int reg, unsigned short word) { sp->putb(sp, reg, word & 0xFF); sp->putb(sp, reg + 1, (word >> 8) & 0xff); } /* * pc98 cbus cards introduce a slight wrinkle here. They route the irq7 pin * from the pcic chip to INT 2 on the cbus. INT 2 is normally mapped to * irq 6 on the pc98 architecture, so if we get a request for irq 6 * lie to the hardware and say it is 7. All the other usual mappings for * cbus INT into irq space are the same as the rest of the system. */ static __inline int host_irq_to_pcic(int irq) { #ifdef PC98 if (irq == 6) irq = 7; #endif return (irq); } /* * Free up resources allocated so far. */ void pcic_dealloc(device_t dev) { struct pcic_softc *sc; sc = (struct pcic_softc *) device_get_softc(dev); if (sc->slot_poll) untimeout(sc->slot_poll, sc, sc->timeout_ch); if (sc->iores) bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->iores); if (sc->memres) bus_release_resource(dev, SYS_RES_MEMORY, sc->memrid, sc->memres); if (sc->ih) bus_teardown_intr(dev, sc->irqres, sc->ih); if (sc->irqres) bus_release_resource(dev, SYS_RES_IRQ, sc->irqrid, sc->irqres); } /* * entry point from main code to map/unmap memory context. */ static int pcic_memory(struct slot *slt, int win) { struct pcic_slot *sp = slt->cdata; struct mem_desc *mp = &slt->mem[win]; int reg = win * PCIC_MEMSIZE + PCIC_MEMBASE; if (win < 0 || win >= slt->ctrl->maxmem) { printf("Illegal PCIC MEMORY window request %d\n", win); return (ENXIO); } if (mp->flags & MDF_ACTIVE) { unsigned long sys_addr = (uintptr_t)(void *)mp->start >> 12; /* * Write the addresses, card offsets and length. * The values are all stored as the upper 12 bits of the * 24 bit address i.e everything is allocated as 4 Kb chunks. */ pcic_putw(sp, reg, sys_addr & 0xFFF); pcic_putw(sp, reg+2, (sys_addr + (mp->size >> 12) - 1) & 0xFFF); pcic_putw(sp, reg+4, ((mp->card >> 12) - sys_addr) & 0x3FFF); /* * Each 16 bit register has some flags in the upper bits. */ if (mp->flags & MDF_16BITS) pcic_setb(sp, reg+1, PCIC_DATA16); if (mp->flags & MDF_ZEROWS) pcic_setb(sp, reg+1, PCIC_ZEROWS); if (mp->flags & MDF_WS0) pcic_setb(sp, reg+3, PCIC_MW0); if (mp->flags & MDF_WS1) pcic_setb(sp, reg+3, PCIC_MW1); if (mp->flags & MDF_ATTR) pcic_setb(sp, reg+5, PCIC_REG); if (mp->flags & MDF_WP) pcic_setb(sp, reg+5, PCIC_WP); /* * Enable the memory window. By experiment, we need a delay. */ pcic_setb(sp, PCIC_ADDRWINE, (1<cdata; struct io_desc *ip = &slt->io[win]; if (bootverbose) { printf("pcic: I/O win %d flags %x %x-%x\n", win, ip->flags, ip->start, ip->start + ip->size - 1); } switch (win) { case 0: mask = PCIC_IO0_EN; reg = PCIC_IO0; break; case 1: mask = PCIC_IO1_EN; reg = PCIC_IO1; break; default: printf("Illegal PCIC I/O window request %d\n", win); return (ENXIO); } if (ip->flags & IODF_ACTIVE) { unsigned char x, ioctlv; pcic_putw(sp, reg, ip->start); pcic_putw(sp, reg+2, ip->start + ip->size - 1); x = 0; if (ip->flags & IODF_ZEROWS) x |= PCIC_IO_0WS; if (ip->flags & IODF_WS) x |= PCIC_IO_WS; if (ip->flags & IODF_CS16) x |= PCIC_IO_CS16; if (ip->flags & IODF_16BIT) x |= PCIC_IO_16BIT; /* * Extract the current flags and merge with new flags. * Flags for window 0 in lower nybble, and in upper nybble * for window 1. */ ioctlv = sp->getb(sp, PCIC_IOCTL); DELAY(100); switch (win) { case 0: sp->putb(sp, PCIC_IOCTL, x | (ioctlv & 0xf0)); break; case 1: sp->putb(sp, PCIC_IOCTL, (x << 4) | (ioctlv & 0xf)); break; } DELAY(100); pcic_setb(sp, PCIC_ADDRWINE, mask); DELAY(100); } else { pcic_clrb(sp, PCIC_ADDRWINE, mask); DELAY(100); pcic_putw(sp, reg, 0); pcic_putw(sp, reg + 2, 0); } return (0); } static void pcic_do_mgt_irq(struct pcic_slot *sp, int irq) { u_int32_t reg; if (sp->sc->csc_route == pcic_iw_pci) { /* Do the PCI side of things: Enable the Card Change int */ reg = CB_SM_CD; bus_space_write_4(sp->bst, sp->bsh, CB_SOCKET_MASK, reg); /* * TI Chips need us to set the following. We tell the * controller to route things via PCI interrupts. Also * we clear the interrupt number in the STAT_INT register * as well. The TI-12xx and newer chips require one or the * other of these to happen, depending on what is set in the * diagnostic register. I do both on the theory that other * chips might need one or the other and that no harm will * come from it. If there is harm, then I'll make it a bit * in the tables. */ pcic_setb(sp, PCIC_INT_GEN, PCIC_INTR_ENA); pcic_clrb(sp, PCIC_STAT_INT, PCIC_CSCSELECT); } else { /* Management IRQ changes */ /* * The PCIC_INTR_ENA bit means either "tie the function * and csc interrupts together" or "Route csc interrupts * via PCI" or "Reserved". In any case, we want to clear * it since we're using ISA interrupts. */ pcic_clrb(sp, PCIC_INT_GEN, PCIC_INTR_ENA); irq = host_irq_to_pcic(irq); sp->putb(sp, PCIC_STAT_INT, (irq << PCIC_SI_IRQ_SHIFT) | PCIC_CDEN); } } int pcic_attach(device_t dev) { int i; device_t kid; struct pcic_softc *sc; struct slot *slt; struct pcic_slot *sp; sc = (struct pcic_softc *) device_get_softc(dev); callout_handle_init(&sc->timeout_ch); sp = &sc->slots[0]; for (i = 0; i < PCIC_CARD_SLOTS; i++, sp++) { if (!sp->slt) continue; sp->slt = 0; kid = device_add_child(dev, NULL, -1); if (kid == NULL) { device_printf(dev, "Can't add pccard bus slot %d", i); return (ENXIO); } device_probe_and_attach(kid); slt = pccard_init_slot(kid, &pcic_cinfo); if (slt == 0) { device_printf(dev, "Can't get pccard info slot %d", i); return (ENXIO); } sc->slotmask |= (1 << i); slt->cdata = sp; sp->slt = slt; sp->sc = sc; } sp = &sc->slots[0]; for (i = 0; i < PCIC_CARD_SLOTS; i++, sp++) { if (sp->slt == NULL) continue; pcic_do_mgt_irq(sp, sc->irq); sp->slt->irq = sc->irq; /* Check for changes */ pcic_setb(sp, PCIC_POWER, PCIC_PCPWRE | PCIC_DISRST); sp->slt->laststate = sp->slt->state = empty; if (pcic_boot_deactivated) { if ((sp->getb(sp, PCIC_STATUS) & PCIC_CD) == PCIC_CD) { sp->slt->state = inactive; pccard_event(sp->slt, card_deactivated); } } else { pcic_do_stat_delta(sp); } } return (bus_generic_attach(dev)); } static int pcic_sresource(struct slot *slt, caddr_t data) { struct pccard_resource *pr; struct resource *r; int flags; int rid = 0; device_t bridgedev = slt->dev; struct pcic_slot *sp = slt->cdata; pr = (struct pccard_resource *)data; pr->resource_addr = ~0ul; /* * If we're using PCI interrupt routing, then force the IRQ to * use and to heck with what the user requested. If they want * to be able to request IRQs, they must use ISA interrupt * routing. If we don't give them an irq, and it is the * pccardd 0,0 case, then just return (giving the "bad resource" * return in pr->resource_addr). */ if (pr->type == SYS_RES_IRQ) { if (sp->sc->func_route >= pcic_iw_pci) { pr->resource_addr = sp->sc->irq; return (0); } if (pr->min == 0 && pr->max == 0) return (0); } /* * Make sure we grok this type. */ switch(pr->type) { default: return (EINVAL); case SYS_RES_MEMORY: case SYS_RES_IRQ: case SYS_RES_IOPORT: break; } /* * Allocate the resource, and align it to the most natural * size. If we get it, then tell userland what we actually got * in the range they requested. */ flags = rman_make_alignment_flags(pr->size); r = bus_alloc_resource(bridgedev, pr->type, &rid, pr->min, pr->max, pr->size, flags); if (r != NULL) { pr->resource_addr = (u_long)rman_get_start(r); bus_release_resource(bridgedev, pr->type, rid, r); } return (0); } /* * ioctl calls - Controller specific ioctls */ static int pcic_ioctl(struct slot *slt, int cmd, caddr_t data) { struct pcic_slot *sp = slt->cdata; struct pcic_reg *preg = (struct pcic_reg *) data; switch(cmd) { default: return (ENOTTY); case PIOCGREG: /* Get pcic register */ preg->value = sp->getb(sp, preg->reg); break; /* Set pcic register */ case PIOCSREG: sp->putb(sp, preg->reg, preg->value); break; case PIOCSRESOURCE: /* Can I use this resource? */ pcic_sresource(slt, data); break; } return (0); } /* * pcic_cardbus_power * * Power the card up, as specified, using the cardbus power * registers to control power. Microsoft recommends that cardbus * vendors support powering the card via cardbus registers because * there is no standard for 3.3V cards. Since at least a few of the * cardbus bridges have minor issues with power via the ExCA registers, * go ahead and do it all via cardbus registers. * * An expamination of the code will show the relative ease that we do * Vpp in comparison to the ExCA case (which may be partially broken). * * Too bad it appears to not work. When used we seem to be unable to * read the card's CIS. */ static int pcic_cardbus_power(struct pcic_slot *sp, struct slot *slt) { uint32_t power; uint32_t state; /* * Preserve the clock stop bit of the socket power register. */ power = bus_space_read_4(sp->bst, sp->bsh, CB_SOCKET_POWER); state = bus_space_read_4(sp->bst, sp->bsh, CB_SOCKET_STATE); printf("old value 0x%x\n", power); power &= ~CB_SP_CLKSTOP; /* * vcc == -1 means automatically detect the voltage of the card. * Do so and apply the right amount of power. */ if (slt->pwr.vcc == -1) { if (state & CB_SS_5VCARD) slt->pwr.vcc = 50; else if (state & CB_SS_3VCARD) slt->pwr.vcc = 33; else if (state & CB_SS_XVCARD) slt->pwr.vcc = 22; else if (state & CB_SS_YVCARD) slt->pwr.vcc = 11; } switch(slt->pwr.vcc) { default: return (EINVAL); case 0: power |= CB_SP_VCC_0V; break; case 11: power |= CB_SP_VCC_YV; break; case 22: power |= CB_SP_VCC_XV; break; case 33: power |= CB_SP_VCC_3V; break; case 50: power |= CB_SP_VCC_5V; break; } /* * vpp == -1 means use vcc voltage. */ if (slt->pwr.vpp == -1) slt->pwr.vpp = slt->pwr.vcc; switch(slt->pwr.vpp) { default: return (EINVAL); case 0: power |= CB_SP_VPP_0V; break; case 11: power |= CB_SP_VPP_YV; break; case 22: power |= CB_SP_VPP_XV; break; case 33: power |= CB_SP_VPP_3V; break; case 50: power |= CB_SP_VPP_5V; break; case 120: power |= CB_SP_VPP_12V; break; } printf("Setting power reg to 0x%x", power); bus_space_write_4(sp->bst, sp->bsh, CB_SOCKET_POWER, power); return (EIO); } /* * pcic_power - Enable the power of the slot according to * the parameters in the power structure(s). */ static int pcic_power(struct slot *slt) { unsigned char c; unsigned char reg = PCIC_DISRST | PCIC_PCPWRE; struct pcic_slot *sp = slt->cdata; struct pcic_slot *sp2; struct pcic_softc *sc = sp->sc; int dodefault = 0; /* * Cardbus power registers are completely different. */ if (sc->flags & PCIC_CARDBUS_POWER) return (pcic_cardbus_power(sp, slt)); if (bootverbose) device_printf(sc->dev, "Power: Vcc=%d Vpp=%d\n", slt->pwr.vcc, slt->pwr.vpp); /* * If we're automatically detecting what voltage to use, then we need * to ask the bridge what type (voltage-wise) the card is. */ if (slt->pwr.vcc == -1) { if (sc->flags & PCIC_DF_POWER) { /* * Look at the VS[12]# bits on the card. If VS1 is * clear then the card needs 3.3V instead of 5.0V. */ c = sp->getb(sp, PCIC_CDGC); if ((c & PCIC_VS1STAT) == 0) slt->pwr.vcc = 33; else slt->pwr.vcc = 50; } if (sc->flags & PCIC_PD_POWER) { /* * The 6710 does it one way, and the '22 and '29 do it * another. And it appears that the '32 and '33 yet * another way (which I don't know). The '22 can also * do it the same way as a '10 does it, despite what * the datasheets say. Some laptops with '22 don't * seem to have the signals wired right for the '29 * method to work, so we always use the '10 method for * the '22. The laptops that don't work hang solid * when the pccard memory is accessed. */ switch (sp->controller) { case PCIC_PD6710: case PCIC_PD6722: c = sp->getb(sp, PCIC_MISC1); if ((c & PCIC_MISC1_5V_DETECT) == 0) slt->pwr.vcc = 33; else slt->pwr.vcc = 50; break; case PCIC_PD6729: /* * VS[12] signals are in slot1's * extended reg 0xa for both slots. */ sp2 = &sc->slots[1]; sp2->putb(sp2, PCIC_EXT_IND, PCIC_EXT_DATA); c = sp2->getb(sp2, PCIC_EXTENDED); if (sp == sp2) /* slot 1 */ c >>= 2; if ((c & PCIC_VS1A) == 0) slt->pwr.vcc = 33; else slt->pwr.vcc = 50; break; default: /* I have no idea how do do this for others */ break; } /* * Regardless of the above, setting the Auto Power * Switch Enable appears to help. */ reg |= PCIC_APSENA; } /* Other bridges here */ if (bootverbose && slt->pwr.vcc != -1) device_printf(sc->dev, "Autodetected %d.%dV card\n", slt->pwr.vcc / 10, slt->pwr.vcc %10); } if (slt->pwr.vcc == -1) { if (bootverbose) device_printf(sc->dev, "Couldn't autodetect voltage, assuming 5.0V\n"); dodefault = 1; slt->pwr.vcc = 50; } /* * XXX Note: The Vpp controls varies quit a bit between bridge chips * and the following might not be right in all cases. The Linux * code and wildboar code bases are more complex. However, most * applications want vpp == vcc and the following code does appear * to do that for all bridge sets. */ if (slt->pwr.vpp == -1) slt->pwr.vpp = slt->pwr.vcc; switch(slt->pwr.vpp) { default: return (EINVAL); case 0: break; case 50: case 33: reg |= PCIC_VPP_5V; break; case 120: reg |= PCIC_VPP_12V; break; } if (slt->pwr.vcc) reg |= PCIC_VCC_ON; /* Turn on Vcc */ switch(slt->pwr.vcc) { default: return (EINVAL); case 0: break; case 33: /* * The wildboar code has comments that state that * the IBM KING controller doesn't support 3.3V * on the "IBM Smart PC card drive". The code * intemates that's the only place they have seen * it used and that there's a boatload of issues * with it. I'm not even sure this is right because * the only docs I've been able to find say this is for * 5V power. Of course, this "doc" is just code comments * so who knows for sure. */ if (sc->flags & PCIC_KING_POWER) { reg |= PCIC_VCC_5V_KING; break; } if (sc->flags & PCIC_VG_POWER) { pcic_setb(sp, PCIC_CVSR, PCIC_CVSR_VS); break; } if (sc->flags & PCIC_PD_POWER) { pcic_setb(sp, PCIC_MISC1, PCIC_MISC1_VCC_33); break; } if (sc->flags & PCIC_RICOH_POWER) { pcic_setb(sp, PCIC_RICOH_MCR2, PCIC_MCR2_VCC_33); break; } if (sc->flags & PCIC_DF_POWER) reg |= PCIC_VCC_3V; break; case 50: if (sc->flags & PCIC_KING_POWER) reg |= PCIC_VCC_5V_KING; /* * For all of the variant power schemes for 3.3V go * ahead and turn off the 3.3V enable bit. For all * bridges, the setting the Vcc on bit does the rest. * Note that we don't have to turn off the 3.3V bit * for the '365 step D since with the reg assigments * to this point it doesn't get turned on. */ if (sc->flags & PCIC_VG_POWER) pcic_clrb(sp, PCIC_CVSR, PCIC_CVSR_VS); if (sc->flags & PCIC_PD_POWER) pcic_clrb(sp, PCIC_MISC1, PCIC_MISC1_VCC_33); if (sc->flags & PCIC_RICOH_POWER) pcic_clrb(sp, PCIC_RICOH_MCR2, PCIC_MCR2_VCC_33); break; } sp->putb(sp, PCIC_POWER, reg); if (bootverbose) device_printf(sc->dev, "Power applied\n"); DELAY(300*1000); if (slt->pwr.vcc) { reg |= PCIC_OUTENA; sp->putb(sp, PCIC_POWER, reg); if (bootverbose) device_printf(sc->dev, "Output enabled\n"); DELAY(100*1000); if (bootverbose) device_printf(sc->dev, "Settling complete\n"); } /* * Some chipsets will attempt to preclude us from supplying * 5.0V to cards that only handle 3.3V. We seem to need to * try 3.3V to paper over some power handling issues in other * parts of the system. Maybe the proper detection of 3.3V cards * now obviates the need for this hack, so put a printf in to * warn the world about it. */ if (!(sp->getb(sp, PCIC_STATUS) & PCIC_POW) && dodefault) { slt->pwr.vcc = 33; slt->pwr.vpp = 0; device_printf(sc->dev, "Failed at 5.0V. Trying 3.3V. Please report message to mobile@freebsd.org\n"); return (pcic_power(slt)); } if (bootverbose) printf("Power complete.\n"); return (0); } /* * tell the PCIC which irq we want to use. only the following are legal: * 3, 4, 5, 7, 9, 10, 11, 12, 14, 15. We require the callers of this * routine to do the check for legality. */ static void pcic_mapirq(struct slot *slt, int irq) { struct pcic_slot *sp = slt->cdata; sp->sc->chip->map_irq(sp, irq); } /* * pcic_reset - Reset the card and enable initial power. This may * need to be interrupt driven in the future. We should likely turn * the reset on, DELAY for a period of time < 250ms, turn it off and * tsleep for a while and check it when we're woken up. I think that * we're running afoul of the card status interrupt glitching, causing * an interrupt storm because the card doesn't seem to be able to * clear this pin while in reset. */ static void pcic_reset(void *chan) { struct slot *slt = chan; struct pcic_slot *sp = slt->cdata; if (bootverbose) device_printf(sp->sc->dev, "reset %d ", slt->insert_seq); switch (slt->insert_seq) { case 0: /* Something funny happended on the way to the pub... */ if (bootverbose) printf("\n"); return; case 1: /* Assert reset */ pcic_clrb(sp, PCIC_INT_GEN, PCIC_CARDRESET); if (bootverbose) printf("int is %x stat is %x\n", sp->getb(sp, PCIC_INT_GEN), sp->getb(sp, PCIC_STATUS)); slt->insert_seq = 2; timeout(pcic_reset, (void *)slt, hz/4); return; case 2: /* Deassert it again */ pcic_setb(sp, PCIC_INT_GEN, PCIC_CARDRESET | PCIC_IOCARD); if (bootverbose) printf("int is %x stat is %x\n", sp->getb(sp, PCIC_INT_GEN), sp->getb(sp, PCIC_STATUS)); slt->insert_seq = 3; timeout(pcic_reset, (void *)slt, hz/4); return; case 3: /* Wait if card needs more time */ if (bootverbose) printf("int is %x stat is %x\n", sp->getb(sp, PCIC_INT_GEN), sp->getb(sp, PCIC_STATUS)); if (!sp->getb(sp, PCIC_STATUS) & PCIC_READY) { timeout(pcic_reset, (void *)slt, hz/10); return; } } slt->insert_seq = 0; if (sp->controller == PCIC_PD6722 || sp->controller == PCIC_PD6710) { sp->putb(sp, PCIC_TIME_SETUP0, 0x1); sp->putb(sp, PCIC_TIME_CMD0, 0x6); sp->putb(sp, PCIC_TIME_RECOV0, 0x0); sp->putb(sp, PCIC_TIME_SETUP1, 1); sp->putb(sp, PCIC_TIME_CMD1, 0xf); sp->putb(sp, PCIC_TIME_RECOV1, 0); } selwakeup(&slt->selp); } /* * pcic_disable - Disable the slot. I wonder if these operations can * cause an interrupt we need to acknowledge? XXX */ static void pcic_disable(struct slot *slt) { struct pcic_slot *sp = slt->cdata; pcic_clrb(sp, PCIC_INT_GEN, PCIC_CARDTYPE | PCIC_CARDRESET); pcic_mapirq(slt, 0); sp->putb(sp, PCIC_POWER, 0); } /* * pcic_resume - Suspend/resume support for PCIC */ static void pcic_resume(struct slot *slt) { struct pcic_slot *sp = slt->cdata; pcic_do_mgt_irq(sp, slt->irq); if (sp->controller == PCIC_PD6722) { pcic_setb(sp, PCIC_MISC1, PCIC_MISC1_SPEAKER); pcic_setb(sp, PCIC_MISC2, PCIC_LPDM_EN); } if (sp->slt->state != inactive) pcic_do_stat_delta(sp); } int pcic_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct pccard_devinfo *devi = device_get_ivars(child); int err; if (dev != device_get_parent(device_get_parent(child)) || devi == NULL) return (bus_generic_activate_resource(dev, child, type, rid, r)); switch (type) { case SYS_RES_IOPORT: { struct io_desc *ip; ip = &devi->slt->io[rid]; if (ip->flags == 0) { if (rid == 0) ip->flags = IODF_WS | IODF_16BIT | IODF_CS16; else ip->flags = devi->slt->io[0].flags; } ip->flags |= IODF_ACTIVE; ip->start = rman_get_start(r); ip->size = rman_get_end(r) - rman_get_start(r) + 1; err = pcic_io(devi->slt, rid); if (err) return (err); break; } case SYS_RES_IRQ: /* * We actually defer the activation of the IRQ resource * until the interrupt is registered to avoid stray * interrupt messages. */ break; case SYS_RES_MEMORY: { struct mem_desc *mp; if (rid >= NUM_MEM_WINDOWS) return (EINVAL); mp = &devi->slt->mem[rid]; mp->flags |= MDF_ACTIVE; mp->start = (caddr_t) rman_get_start(r); mp->size = rman_get_end(r) - rman_get_start(r) + 1; err = pcic_memory(devi->slt, rid); if (err) return (err); break; } default: break; } err = bus_generic_activate_resource(dev, child, type, rid, r); return (err); } int pcic_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { struct pccard_devinfo *devi = device_get_ivars(child); int err; if (dev != device_get_parent(device_get_parent(child)) || devi == NULL) return (bus_generic_deactivate_resource(dev, child, type, rid, r)); switch (type) { case SYS_RES_IOPORT: { struct io_desc *ip = &devi->slt->io[rid]; ip->flags &= ~IODF_ACTIVE; err = pcic_io(devi->slt, rid); if (err) return (err); break; } case SYS_RES_IRQ: break; case SYS_RES_MEMORY: { struct mem_desc *mp = &devi->slt->mem[rid]; mp->flags &= ~(MDF_ACTIVE | MDF_ATTR); err = pcic_memory(devi->slt, rid); if (err) return (err); break; } default: break; } err = bus_generic_deactivate_resource(dev, child, type, rid, r); return (err); } int pcic_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { struct pccard_devinfo *devi = device_get_ivars(child); int err; if (((1 << rman_get_start(irq)) & PCIC_INT_MASK_ALLOWED) == 0) { device_printf(dev, "Hardware does not support irq %ld.\n", rman_get_start(irq)); return (EINVAL); } err = bus_generic_setup_intr(dev, child, irq, flags, intr, arg, cookiep); if (err == 0) pcic_mapirq(devi->slt, rman_get_start(irq)); else device_printf(dev, "Error %d irq %ld\n", err, rman_get_start(irq)); return (err); } int pcic_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { struct pccard_devinfo *devi = device_get_ivars(child); pcic_mapirq(devi->slt, 0); return (bus_generic_teardown_intr(dev, child, irq, cookie)); } int pcic_set_res_flags(device_t bus, device_t child, int restype, int rid, u_long value) { struct pccard_devinfo *devi = device_get_ivars(child); int err = 0; switch (restype) { case SYS_RES_MEMORY: { struct mem_desc *mp = &devi->slt->mem[rid]; switch (value) { case PCCARD_A_MEM_COM: mp->flags &= ~MDF_ATTR; break; case PCCARD_A_MEM_ATTR: mp->flags |= MDF_ATTR; break; case PCCARD_A_MEM_8BIT: mp->flags &= ~MDF_16BITS; break; case PCCARD_A_MEM_16BIT: mp->flags |= MDF_16BITS; break; } err = pcic_memory(devi->slt, rid); break; } default: err = EOPNOTSUPP; } return (err); } int pcic_get_res_flags(device_t bus, device_t child, int restype, int rid, u_long *value) { struct pccard_devinfo *devi = device_get_ivars(child); int err = 0; if (value == 0) return (ENOMEM); switch (restype) { case SYS_RES_IOPORT: { struct io_desc *ip = &devi->slt->io[rid]; *value = ip->flags; break; } case SYS_RES_MEMORY: { struct mem_desc *mp = &devi->slt->mem[rid]; *value = mp->flags; break; } default: err = EOPNOTSUPP; } return (err); } int pcic_set_memory_offset(device_t bus, device_t child, int rid, u_int32_t offset #if __FreeBSD_version >= 500000 ,u_int32_t *deltap #endif ) { struct pccard_devinfo *devi = device_get_ivars(child); struct mem_desc *mp = &devi->slt->mem[rid]; mp->card = offset; #if __FreeBSD_version >= 500000 if (deltap) *deltap = 0; /* XXX BAD XXX */ #endif return (pcic_memory(devi->slt, rid)); } int pcic_get_memory_offset(device_t bus, device_t child, int rid, u_int32_t *offset) { struct pccard_devinfo *devi = device_get_ivars(child); struct mem_desc *mp = &devi->slt->mem[rid]; if (offset == 0) return (ENOMEM); *offset = mp->card; return (0); } struct resource * pcic_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct pcic_softc *sc = device_get_softc(dev); /* * If we're routing via pci, we can share. */ if (sc->func_route == pcic_iw_pci && type == SYS_RES_IRQ) { if (bootverbose) device_printf(child, "Forcing IRQ to %d\n", sc->irq); start = end = sc->irq; flags |= RF_SHAREABLE; } return (bus_generic_alloc_resource(dev, child, type, rid, start, end, count, flags)); } void pcic_do_stat_delta(struct pcic_slot *sp) { if ((sp->getb(sp, PCIC_STATUS) & PCIC_CD) != PCIC_CD) pccard_event(sp->slt, card_removed); else pccard_event(sp->slt, card_inserted); } /* * Wrapper function for pcicintr so that signatures match. */ void pcic_isa_intr(void *arg) { pcic_isa_intr1(arg); } /* * PCIC timer. If the controller doesn't have a free IRQ to use * or if interrupt steering doesn't work, poll the controller for * insertion/removal events. */ void pcic_timeout(void *chan) { struct pcic_softc *sc = (struct pcic_softc *) chan; if (pcic_isa_intr1(chan) != 0) { device_printf(sc->dev, "Static bug detected, ignoring hardware."); sc->slot_poll = 0; return; } sc->timeout_ch = timeout(sc->slot_poll, chan, hz/2); } /* * PCIC Interrupt handler. * Check each slot in turn, and read the card status change * register. If this is non-zero, then a change has occurred * on this card, so send an event to the main code. */ int pcic_isa_intr1(void *arg) { int slot, s; u_int8_t chg; struct pcic_softc *sc = (struct pcic_softc *) arg; struct pcic_slot *sp = &sc->slots[0]; s = splhigh(); for (slot = 0; slot < PCIC_CARD_SLOTS; slot++, sp++) { if (sp->slt == NULL) continue; if ((chg = sp->getb(sp, PCIC_STAT_CHG)) != 0) { /* * if chg is 0xff, then we know that we've hit * the famous "static bug" for some desktop * pcmcia cards. This is caused by static * discharge frying the poor card's mind and * it starts return 0xff forever. We return * an error and stop polling the card. When * we're interrupt based, we never see this. * The card just goes away silently. */ if (chg == 0xff) { splx(s); return (EIO); } if (chg & PCIC_CDTCH) pcic_do_stat_delta(sp); } } splx(s); return (0); } int pcic_isa_mapirq(struct pcic_slot *sp, int irq) { irq = host_irq_to_pcic(irq); sp->sc->chip->func_intr_way(sp, pcic_iw_isa); if (irq == 0) pcic_clrb(sp, PCIC_INT_GEN, 0xF); else sp->putb(sp, PCIC_INT_GEN, (sp->getb(sp, PCIC_INT_GEN) & 0xF0) | irq); return (0); }