diff options
-rw-r--r-- | sys/dev/exca/excareg.h | 7 | ||||
-rw-r--r-- | sys/dev/pccbb/pccbb.c | 158 | ||||
-rw-r--r-- | sys/dev/pccbb/pccbbvar.h | 1 |
3 files changed, 152 insertions, 14 deletions
diff --git a/sys/dev/exca/excareg.h b/sys/dev/exca/excareg.h index bb03ace..e2f092a 100644 --- a/sys/dev/exca/excareg.h +++ b/sys/dev/exca/excareg.h @@ -395,6 +395,13 @@ #define EXCA_RID_296 0x32 #define EXCA_RID_396 0xb2 +/* + * o2 micro specific registers + */ +#define EXCA_O2MICRO_CTRL_C 0x3a +#define EXCA_O2CC_IREQ_INTC 0x80 +#define EXCA_O2CC_STSCHG_INTC 0x20 + /* Plug and play */ #define EXCA_PNP_ACTIONTEC 0x1802A904 /* AEI0218 */ #define EXCA_PNP_IBM3765 0x65374d24 /* IBM3765 */ diff --git a/sys/dev/pccbb/pccbb.c b/sys/dev/pccbb/pccbb.c index 78c8f56..541e5de 100644 --- a/sys/dev/pccbb/pccbb.c +++ b/sys/dev/pccbb/pccbb.c @@ -172,12 +172,12 @@ struct yenta_chipinfo { {PCIC_ID_CLPD6834, "CLPD6834 PCI-CardBus Bridge", CB_CIRRUS}, /* 02Micro */ - {PCIC_ID_OZ6832, "O2Micro OZ6832/6833 PCI-CardBus Bridge", CB_CIRRUS}, - {PCIC_ID_OZ6860, "O2Micro OZ6836/6860 PCI-CardBus Bridge", CB_CIRRUS}, - {PCIC_ID_OZ6872, "O2Micro OZ6812/6872 PCI-CardBus Bridge", CB_CIRRUS}, - {PCIC_ID_OZ6912, "O2Micro OZ6912/6972 PCI-CardBus Bridge", CB_CIRRUS}, - {PCIC_ID_OZ6922, "O2Micro OZ6922 PCI-CardBus Bridge", CB_CIRRUS}, - {PCIC_ID_OZ6933, "O2Micro OZ6933 PCI-CardBus Bridge", CB_CIRRUS}, + {PCIC_ID_OZ6832, "O2Micro OZ6832/6833 PCI-CardBus Bridge", CB_O2MICRO}, + {PCIC_ID_OZ6860, "O2Micro OZ6836/6860 PCI-CardBus Bridge", CB_O2MICRO}, + {PCIC_ID_OZ6872, "O2Micro OZ6812/6872 PCI-CardBus Bridge", CB_O2MICRO}, + {PCIC_ID_OZ6912, "O2Micro OZ6912/6972 PCI-CardBus Bridge", CB_O2MICRO}, + {PCIC_ID_OZ6922, "O2Micro OZ6922 PCI-CardBus Bridge", CB_O2MICRO}, + {PCIC_ID_OZ6933, "O2Micro OZ6933 PCI-CardBus Bridge", CB_O2MICRO}, /* sentinel */ {0 /* null id */, "unknown", CB_UNKNOWN}, @@ -402,7 +402,7 @@ cbb_probe(device_t brdev) static void cbb_chipinit(struct cbb_softc *sc) { - uint32_t mux, sysctrl; + uint32_t mux, sysctrl, reg; /* Set CardBus latency timer */ if (pci_read_config(sc->dev, PCIR_SECLAT_1, 1) < 0x20) @@ -487,6 +487,36 @@ cbb_chipinit(struct cbb_softc *sc) */ pci_write_config(sc->dev, CBBR_MMCTRL, 0, 4); break; + case CB_O2MICRO: + /* + * Issue #1: INT# generated at the same time as + * selected ISA IRQ. When IREQ# or STSCHG# is active, + * in addition to the ISA IRQ being generated, INT# + * will also be generated at the same time. + * + * Some of our older controllers have an issue in + * which the slot's PCI INT# will be asserted whenever + * IREQ# or STSCGH# is asserted even if ExCA registers + * 03h or 05h have an ISA IRQ selected. + * + * The fix for this issue, which will work for any + * controller (old or new), is to set ExCA registers + * 3Ah (slot 0) & 7Ah (slot 1) bits 7:4 = 1010b. + * These bits are undocumented. By setting this + * register (of each slot) to '1010xxxxb' a routing of + * IREQ# to INTC# and STSCHG# to INTC# is selected. + * Since INTC# isn't connected there will be no + * unexpected PCI INT when IREQ# or STSCHG# is active. + * However, INTA# (slot 0) or INTB# (slot 1) will + * still be correctly generated if NO ISA IRQ is + * selected (ExCA regs 03h or 05h are cleared). + */ + reg = exca_getb(&sc->exca, EXCA_O2MICRO_CTRL_C); + reg = (reg & 0x0f) | + EXCA_O2CC_IREQ_INTC | EXCA_O2CC_STSCHG_INTC; + exca_putb(&sc->exca, EXCA_O2MICRO_CTRL_C, reg); + + break; case CB_TOPIC97: /* * Disable Zoom Video, ToPIC 97, 100. @@ -555,6 +585,45 @@ cbb_chipinit(struct cbb_softc *sc) pci_write_config(sc->dev, CBBR_IOLIMIT1, 0, 4); } +static void +cbb_powerstate_d0(device_t dev) +{ + u_int32_t membase, irq; + + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + /* Save important PCI config data. */ + membase = pci_read_config(dev, CBBR_SOCKBASE, 4); + irq = pci_read_config(dev, PCIR_INTLINE, 4); + + /* Reset the power state. */ + device_printf(dev, "chip is in D%d power mode " + "-- setting to D0\n", pci_get_powerstate(dev)); + + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + + /* Restore PCI config data. */ + pci_write_config(dev, CBBR_SOCKBASE, membase, 4); + pci_write_config(dev, PCIR_INTLINE, irq, 4); + } +} + +/* + * Print out the config space + */ +static void +cbb_print_config(device_t dev) +{ + int i; + + device_printf(dev, "PCI Configuration space:"); + for (i = 0; i < 256; i += 4) { + if (i % 16 == 0) + printf("\n 0x%02x: ", i); + printf("0x%08x ", pci_read_config(dev, i, 4)); + } + printf("\n"); +} + static int cbb_attach(device_t brdev) { @@ -572,6 +641,8 @@ cbb_attach(device_t brdev) SLIST_INIT(&sc->rl); STAILQ_INIT(&sc->intr_handlers); + cbb_powerstate_d0(brdev); + #ifndef BURN_THE_BOATS /* * The PCI bus code should assign us memory in the absense @@ -621,8 +692,11 @@ cbb_attach(device_t brdev) DEVPRINTF((brdev, "PCI Memory allocated: %08lx\n", rman_get_start(sc->base_res))); #endif + } else { + DEVPRINTF((brdev, "Found memory at %08lx\n", + rman_get_start(sc->base_res))); } - + sc->bst = rman_get_bustag(sc->base_res); sc->bsh = rman_get_bushandle(sc->base_res); exca_init(&sc->exca, brdev, sc->bst, sc->bsh, CBB_EXCA_OFFSET); @@ -674,11 +748,14 @@ cbb_attach(device_t brdev) /* reset interrupt */ cbb_set(sc, CBB_SOCKET_EVENT, cbb_get(sc, CBB_SOCKET_EVENT)); + if (bootverbose) + cbb_print_config(brdev); + /* Start the thread */ if (kthread_create(cbb_event_thread, sc, &sc->event_thread, 0, 0, - "%s%d", device_get_name(sc->dev), device_get_unit(sc->dev))) { - device_printf (sc->dev, "unable to create event thread.\n"); - panic ("cbb_create_event_thread"); + "%s%d", device_get_name(brdev), device_get_unit(brdev))) { + device_printf(brdev, "unable to create event thread.\n"); + panic("cbb_create_event_thread"); } return (0); @@ -1041,13 +1118,60 @@ cbb_detect_voltage(device_t brdev) return (vol); } +static uint8_t +cbb_o2micro_power_hack(struct cbb_softc *sc) +{ + uint8_t reg; + + /* + * Issue #2: INT# not qualified with IRQ Routing Bit. An + * unexpected PCI INT# may be generated during PC-Card + * initialization even with the IRQ Routing Bit Set with some + * PC-Cards. + * + * This is a two part issue. The first part is that some of + * our older controllers have an issue in which the slot's PCI + * INT# is NOT qualified by the IRQ routing bit (PCI reg. 3Eh + * bit 7). Regardless of the IRQ routing bit, if NO ISA IRQ + * is selected (ExCA register 03h bits 3:0, of the slot, are + * cleared) we will generate INT# if IREQ# is asserted. The + * second part is because some PC-Cards prematurally assert + * IREQ# before the ExCA registers are fully programmed. This + * in turn asserts INT# because ExCA register 03h bits 3:0 + * (ISA IRQ Select) are not yet programmed. + * + * The fix for this issue, which will work for any controller + * (old or new), is to set ExCA register 03h bits 3:0 = 0001b + * (select IRQ1), of the slot, before turning on slot power. + * Selecting IRQ1 will result in INT# NOT being asserted + * (because IRQ1 is selected), and IRQ1 won't be asserted + * because our controllers don't generate IRQ1. + */ + reg = exca_getb(&sc->exca, EXCA_INTR); + exca_putb(&sc->exca, EXCA_INTR, (reg & 0xf0) | 1); + return (reg); +} + +/* + * Restore the damage that cbb_o2micro_power_hack does to EXCA_INTR so + * we don't have an interrupt storm on power on. This has the efect of + * disabling card status change interrupts for the duration of poweron. + */ +static void +cbb_o2micro_power_hack2(struct cbb_softc *sc, uint8_t reg) +{ + exca_putb(&sc->exca, EXCA_INTR, reg); +} + static int cbb_power(device_t brdev, int volts) { uint32_t status, sock_ctrl; struct cbb_softc *sc = device_get_softc(brdev); int timeout; + int retval = 0; uint32_t sockevent; + uint8_t reg = 0; DEVPRINTF((sc->dev, "cbb_power: %s and %s [%x]\n", (volts & CARD_VCCMASK) == CARD_VCC_UC ? "CARD_VCC_UC" : @@ -1113,6 +1237,8 @@ cbb_power(device_t brdev, int volts) if (cbb_get(sc, CBB_SOCKET_CONTROL) == sock_ctrl) return (1); /* no change necessary */ + if (volts != 0 && sc->chipset == CB_O2MICRO) + reg = cbb_o2micro_power_hack(sc); cbb_set(sc, CBB_SOCKET_CONTROL, sock_ctrl); status = cbb_get(sc, CBB_SOCKET_STATE); @@ -1133,7 +1259,7 @@ cbb_power(device_t brdev, int volts) cbb_set(sc, CBB_SOCKET_EVENT, sockevent); if (timeout < 0) { printf ("VCC supply failed.\n"); - return (0); + goto done; } /* XXX @@ -1162,9 +1288,13 @@ cbb_power(device_t brdev, int volts) (volts & CARD_VPPMASK) == CARD_VPP_0V ? "CARD_VPP_0V" : "VPP-UNKNOWN", volts); - return (0); + goto done; } - return (1); /* power changed correctly */ + retval = 1; +done:; + if (volts != 0 && sc->chipset == CB_O2MICRO) + cbb_o2micro_power_hack2(sc, reg); + return (retval); } /* diff --git a/sys/dev/pccbb/pccbbvar.h b/sys/dev/pccbb/pccbbvar.h index 8e6d696..c310f79 100644 --- a/sys/dev/pccbb/pccbbvar.h +++ b/sys/dev/pccbb/pccbbvar.h @@ -80,6 +80,7 @@ struct cbb_softc { #define CB_CIRRUS 6 /* Cirrus Logic CLPD683x */ #define CB_TOPIC95 7 /* Toshiba ToPIC95 */ #define CB_TOPIC97 8 /* Toshiba ToPIC97/100 */ +#define CB_O2MICRO 9 /* O2Micro chips */ SLIST_HEAD(, cbb_reslist) rl; STAILQ_HEAD(, cbb_intrhand) intr_handlers; |