Module Name: src Committed By: cliff Date: Fri Jan 29 00:23:54 UTC 2010
Modified Files: src/sys/arch/mips/rmi [matt-nb5-mips64]: rmixl_pcie.c Log Message: - rmixl_cache_err_dis, rmixl_cache_err_restore, rmixl_cache_err_check inlines moved from here ro rmixlvar.h - add a layer of interrupt dispatch to allow sharing link interrupts, - enable and handle pcie link error interrupts - initialize the PCIe INT and MSI config regs, make sure MSI ints are disabled! - improve display names for link configurations - be more thorough about 'mips_cpu_id' based variations To generate a diff of this commit: cvs rdiff -u -r1.1.2.7 -r1.1.2.8 src/sys/arch/mips/rmi/rmixl_pcie.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/mips/rmi/rmixl_pcie.c diff -u src/sys/arch/mips/rmi/rmixl_pcie.c:1.1.2.7 src/sys/arch/mips/rmi/rmixl_pcie.c:1.1.2.8 --- src/sys/arch/mips/rmi/rmixl_pcie.c:1.1.2.7 Wed Jan 20 09:04:35 2010 +++ src/sys/arch/mips/rmi/rmixl_pcie.c Fri Jan 29 00:23:54 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: rmixl_pcie.c,v 1.1.2.7 2010/01/20 09:04:35 matt Exp $ */ +/* $NetBSD: rmixl_pcie.c,v 1.1.2.8 2010/01/29 00:23:54 cliff Exp $ */ /* * Copyright (c) 2001 Wasabi Systems, Inc. @@ -40,7 +40,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: rmixl_pcie.c,v 1.1.2.7 2010/01/20 09:04:35 matt Exp $"); +__KERNEL_RCSID(0, "$NetBSD: rmixl_pcie.c,v 1.1.2.8 2010/01/29 00:23:54 cliff Exp $"); #include "opt_pci.h" #include "pci.h" @@ -125,6 +125,38 @@ #define PCIE_ECFG_ERRS_OFFTAB_NENTRIES \ (sizeof(pcie_ecfg_errs_tab)/sizeof(pcie_ecfg_errs_tab[0])) +typedef struct rmixl_pcie_int_csr { + uint r0; + uint r1; +} rmixl_pcie_int_csr_t; + +static const rmixl_pcie_int_csr_t int_enb_offset[4] = { + { RMIXL_PCIE_LINK0_INT_ENABLE0, RMIXL_PCIE_LINK0_INT_ENABLE1 }, + { RMIXL_PCIE_LINK1_INT_ENABLE0, RMIXL_PCIE_LINK1_INT_ENABLE1 }, + { RMIXL_PCIE_LINK2_INT_ENABLE0, RMIXL_PCIE_LINK2_INT_ENABLE1 }, + { RMIXL_PCIE_LINK3_INT_ENABLE0, RMIXL_PCIE_LINK3_INT_ENABLE1 }, +}; + +static const rmixl_pcie_int_csr_t int_sts_offset[4] = { + { RMIXL_PCIE_LINK0_INT_STATUS0, RMIXL_PCIE_LINK0_INT_STATUS1 }, + { RMIXL_PCIE_LINK1_INT_STATUS0, RMIXL_PCIE_LINK1_INT_STATUS1 }, + { RMIXL_PCIE_LINK2_INT_STATUS0, RMIXL_PCIE_LINK2_INT_STATUS1 }, + { RMIXL_PCIE_LINK3_INT_STATUS0, RMIXL_PCIE_LINK3_INT_STATUS1 }, +}; + +static const u_int msi_enb_offset[4] = { + RMIXL_PCIE_LINK0_MSI_ENABLE, + RMIXL_PCIE_LINK1_MSI_ENABLE, + RMIXL_PCIE_LINK2_MSI_ENABLE, + RMIXL_PCIE_LINK3_MSI_ENABLE +}; + +#define RMIXL_PCIE_LINK_STATUS0_ERRORS __BITS(6,4) +#define RMIXL_PCIE_LINK_STATUS1_ERRORS __BITS(10,0) +#define RMIXL_PCIE_LINK_STATUS_ERRORS \ + ((((uint64_t)RMIXL_PCIE_LINK_STATUS1_ERRORS) << 32) | \ + (uint64_t)RMIXL_PCIE_LINK_STATUS0_ERRORS) + static int rmixl_pcie_match(device_t, cfdata_t, void *); static void rmixl_pcie_attach(device_t, device_t, void *); static void rmixl_pcie_init(struct rmixl_pcie_softc *); @@ -136,6 +168,7 @@ static void rmixl_pcie_lnkcfg_2xx(rmixl_pcie_lnktab_t *, uint32_t); static void rmixl_pcie_lnkcfg_1xx(rmixl_pcie_lnktab_t *, uint32_t); static void rmixl_pcie_lnkcfg(struct rmixl_pcie_softc *); +static void rmixl_pcie_intcfg(struct rmixl_pcie_softc *); static void rmixl_pcie_errata(struct rmixl_pcie_softc *); static void rmixl_conf_interrupt(void *, int, int, int, int, int *); static int rmixl_pcie_bus_maxdevs(void *, int); @@ -155,9 +188,14 @@ rmixl_pcie_intr_string(void *, pci_intr_handle_t); static const struct evcnt * rmixl_pcie_intr_evcnt(void *, pci_intr_handle_t); +static pci_intr_handle_t + rmixl_pcie_make_pih(u_int, u_int, u_int); +static void rmixl_pcie_decompose_pih(pci_intr_handle_t, u_int *, u_int *, u_int *); +static void rmixl_pcie_intr_disestablish(void *, void *); static void *rmixl_pcie_intr_establish(void *, pci_intr_handle_t, int, int (*)(void *), void *); -static void rmixl_pcie_intr_disestablish(void *, void *); +static int rmixl_pcie_intr(void *); +static void rmixl_pcie_link_error_intr(u_int, uint32_t, uint32_t); #if defined(DEBUG) || defined(DDB) int rmixl_pcie_error_check(void); #endif @@ -207,44 +245,6 @@ static int rmixl_pcie_found; -/* - * rmixl_cache_err_dis: - * - disable Cache, Data ECC, Snoop Tag Parity, Tag Parity errors - * - clear the cache error log - * - return previous value from RMIXL_PCR_L1D_CONFIG0 - */ -static inline uint64_t -rmixl_cache_err_dis(void) -{ - uint64_t r; - - r = rmixl_mfcr(RMIXL_PCR_L1D_CONFIG0); - rmixl_mtcr(RMIXL_PCR_L1D_CONFIG0, r & ~0x2e); - rmixl_mtcr(RMIXL_PCR_L1D_CACHE_ERROR_LOG, 0); - return r; -} - -/* - * rmixl_cache_err_restore: - * - clear the cache error log, cache error overflow log, - * and cache interrupt registers - * - restore previous value to RMIXL_PCR_L1D_CONFIG0 - */ -static inline void -rmixl_cache_err_restore(uint64_t r) -{ - rmixl_mtcr(RMIXL_PCR_L1D_CACHE_ERROR_LOG, 0); - rmixl_mtcr(RMIXL_PCR_L1D_CACHE_ERROR_OVF_LO, 0); - rmixl_mtcr(RMIXL_PCR_L1D_CACHE_INTERRUPT, 0); - rmixl_mtcr(RMIXL_PCR_L1D_CONFIG0, r); -} - -static inline uint64_t -rmixl_cache_err_check(void) -{ - return rmixl_mfcr(RMIXL_PCR_L1D_CACHE_ERROR_LOG); -} - static int rmixl_pcie_match(device_t parent, cfdata_t cf, void *aux) { @@ -258,7 +258,6 @@ return 0; /* read GPIO Reset Configuration register */ - /* XXX FIXME define the offset */ r = RMIXL_IOREG_READ(RMIXL_IO_DEV_GPIO + RMIXL_GPIO_RESET_CFG); r >>= 26; r &= 3; @@ -284,6 +283,8 @@ rmixl_pcie_lnkcfg(sc); + rmixl_pcie_intcfg(sc); + rmixl_pcie_errata(sc); sc->sc_29bit_dmat = obio->obio_29bit_dmat; @@ -402,10 +403,10 @@ {{ LCFG_RC, 1}, {LCFG_RC, 1}, {LCFG_RC, 1}, {LCFG_RC, 1}}, }; static const char *lnkstr_4xx[4] = { - "EP 1x4", - "RC 1x4", - "EP 1x1, RC 4x1", - "RC 4x1" + "1EPx4", + "1RCx4", + "1EPx1, 3RCx1", + "4RCx1" }; index = (grcr >> 20) & 3; ltp->ncfgs = 4; @@ -429,10 +430,10 @@ {{ LCFG_RC, 1}, {LCFG_RC, 1}}, }; static const char *lnkstr_408Lite[4] = { - "EP 1x4", - "RC 1x4", - "EP 1x1, RC 1x1", - "RC 2x1" + "4EPx4", + "1RCx4", + "1EPx1, 1RCx1", + "2RCx1" }; index = (grcr >> 20) & 3; @@ -455,8 +456,8 @@ {{ LCFG_RC, 1}, {LCFG_RC, 1}, {LCFG_RC, 1}, {LCFG_RC, 1}} }; static const char *lnkstr_2xx[2] = { - "EP 1x1, RC 3x1", - "RC 4x1", + "1EPx1, 3RCx1", + "4RCx1", }; index = (grcr >> 20) & 1; @@ -479,8 +480,8 @@ {{ LCFG_RC, 1}, {LCFG_RC, 1}} }; static const char *lnkstr_1xx[2] = { - "EP 1x1, RC 1x1", - "RC 2x1", + "1EPx1, 1RCx1", + "2RCx1", }; index = (grcr >> 20) & 1; @@ -530,6 +531,30 @@ device_xname(sc->sc_dev), sc->sc_pcie_lnktab.str); } +/* + * rmixl_pcie_intcfg - init PCIe Link interrupt enables + */ +static void +rmixl_pcie_intcfg(struct rmixl_pcie_softc *sc) +{ + rmixl_pcie_link_intr_t *lip; + int link; + + DPRINTF(("%s: disable all link interrupts\n", __func__)); + for (link=0; link < sc->sc_pcie_lnktab.ncfgs; link++) { + RMIXL_IOREG_WRITE(RMIXL_IO_DEV_PCIE_LE + int_enb_offset[link].r0, + RMIXL_PCIE_LINK_STATUS0_ERRORS); + RMIXL_IOREG_WRITE(RMIXL_IO_DEV_PCIE_LE + int_enb_offset[link].r1, + RMIXL_PCIE_LINK_STATUS1_ERRORS); + RMIXL_IOREG_WRITE(RMIXL_IO_DEV_PCIE_LE + msi_enb_offset[link], 0); + lip = &sc->sc_link_intr[link]; + LIST_INIT(&lip->dispatch); + lip->ih = NULL; + lip->link = link; + lip->enabled = false; + } +} + static void rmixl_pcie_errata(struct rmixl_pcie_softc *sc) { @@ -735,17 +760,24 @@ r = (pcireg_t)(~PCIE_ECFG_RECR_RESV); rmixl_pcie_conf_write(v, tag, RMIXL_PCIE_ECFG_RECR, r); - - if (MIPS_PRID_IMPL(mips_options.mips_cpu_id) == MIPS_XLS408LITE) { - /* - * establish ISR for PCIE Fatal Error interrupt - * XXX for XLS408Lite, XLS2xx, XLS1xx only - * tested on XLS408Lite only - */ - (void)rmixl_intr_establish(29, IPL_HIGH, - RMIXL_INTR_LEVEL, RMIXL_INTR_HIGH, - rmixl_pcie_error_intr, v); + /* + * establish ISR for PCIE Fatal Error interrupt + * - for XLS4xxLite, XLS2xx, XLS1xx only + */ + switch (MIPS_PRID_IMPL(mips_options.mips_cpu_id)) { + case MIPS_XLS104: + case MIPS_XLS108: + case MIPS_XLS204: + case MIPS_XLS208: + case MIPS_XLS404LITE: + case MIPS_XLS408LITE: + sc->sc_fatal_ih = rmixl_intr_establish(29, IPL_HIGH, RMIXL_INTR_LEVEL, + RMIXL_INTR_HIGH, rmixl_pcie_error_intr, v); + break; + default: + break; } + #if defined(DEBUG) || defined(DDB) rmixl_pcie_v = v; #endif @@ -972,6 +1004,7 @@ int rmixl_pcie_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *pih) { + u_int link; u_int irq; #ifdef DEBUG @@ -982,34 +1015,48 @@ #endif /* - * XXX cpu implementation specific + * PCIe Link INT irq assignment is cpu implementation specific */ switch (MIPS_PRID_IMPL(mips_options.mips_cpu_id)) { + case MIPS_XLS104: + case MIPS_XLS108: + case MIPS_XLS204: + case MIPS_XLS208: + case MIPS_XLS404LITE: case MIPS_XLS408LITE: switch (pa->pa_bus) { case 1: + link = 0; irq = 26; break; case 2: + link = 1; irq = 27; break; default: panic("%s: bad bus %d\n", __func__, pa->pa_bus); } break; + case MIPS_XLS404: + case MIPS_XLS408: case MIPS_XLS416: + case MIPS_XLS608: case MIPS_XLS616: switch (pa->pa_bus) { case 1: + link = 0; irq = 26; break; case 2: + link = 1; irq = 27; break; case 3: + link = 2; irq = 28; break; case 4: + link = 3; irq = 29; break; default: @@ -1021,7 +1068,10 @@ __func__, MIPS_PRID_IMPL(mips_options.mips_cpu_id)); } - *pih = irq; + if (pa->pa_intrpin != PCI_INTERRUPT_PIN_NONE) + *pih = rmixl_pcie_make_pih(link, pa->pa_intrpin - 1, irq); + else + *pih = ~0; return 0; } @@ -1033,6 +1083,11 @@ int irq = (int)pih; switch (MIPS_PRID_IMPL(mips_options.mips_cpu_id)) { + case MIPS_XLS104: + case MIPS_XLS108: + case MIPS_XLS204: + case MIPS_XLS208: + case MIPS_XLS404LITE: case MIPS_XLS408LITE: switch (irq) { case 26: @@ -1041,6 +1096,10 @@ break; } break; + case MIPS_XLS404: + case MIPS_XLS408: + case MIPS_XLS416: + case MIPS_XLS608: case MIPS_XLS616: switch (irq) { case 26: @@ -1051,6 +1110,9 @@ break; } break; + default: + panic("%s: cpu IMPL %#x not supported\n", + __func__, MIPS_PRID_IMPL(mips_options.mips_cpu_id)); } return name; @@ -1062,17 +1124,44 @@ return NULL; } -static int -rmixl_pcie_irq(pci_intr_handle_t pih) +static pci_intr_handle_t +rmixl_pcie_make_pih(u_int link, u_int bitno, u_int irq) { - return (int)pih; + pci_intr_handle_t pih; + + KASSERT((link >= 0) && (link < RMIXL_PCIE_NLINKS_MAX)); + KASSERT((bitno >= 0) && (bitno < 64)); + KASSERT((irq >= 0) && (irq < 31)); + + pih = (irq << 10); + pih |= (bitno << 4); + pih |= link; + + return pih; } +static void +rmixl_pcie_decompose_pih(pci_intr_handle_t pih, u_int *link, u_int *bitno, u_int *irq) +{ + *link = (u_int)(pih & 0xf); + *bitno = (u_int)((pih >> 4) & 0x3f); + *irq = (u_int)(pih >> 10); + + KASSERT((*link >= 0) && (*link < RMIXL_PCIE_NLINKS_MAX)); + KASSERT((*bitno >= 0) && (*bitno < 64)); + KASSERT((*irq >= 0) && (*irq < 31)); +} + +#if 0 + static void * rmixl_pcie_intr_establish(void *v, pci_intr_handle_t pih, int ipl, int (*func)(void *), void *arg) { - return rmixl_intr_establish(rmixl_pcie_irq((int)pih), ipl, + u_int link, bitno, irq; + + rmixl_pcie_decompose_pih(pih, &link, &bitno, &irq); + return rmixl_intr_establish(irq, ipl, RMIXL_INTR_LEVEL, RMIXL_INTR_HIGH, func, arg); } @@ -1082,6 +1171,202 @@ rmixl_intr_disestablish(ih); } +#else /* 0 */ + +static void +rmixl_pcie_intr_disestablish(void *v, void *ih) +{ + rmixl_pcie_softc_t *sc = v; + rmixl_pcie_link_dispatch_t *dip = ih; + rmixl_pcie_link_intr_t *lip = &sc->sc_link_intr[dip->link];; + uint32_t r; + uint32_t bit; + u_int offset; + u_int other; + + DPRINTF(("%s: link=%d bitno=%d irq=%d\n", __func__, dip->link, dip->bitno, dip->irq)); + LIST_REMOVE(dip, next); + + rmixl_intr_disestablish(lip->ih); + + if (dip->bitno < 32) { + bit = 1 << dip->bitno; + offset = int_enb_offset[dip->link].r0; + other = int_enb_offset[dip->link].r1; + } else { + bit = 1 << (dip->bitno - 32); + offset = int_enb_offset[dip->link].r1; + other = int_enb_offset[dip->link].r1; + } + + /* disable this interrupt in the PCIe bridge */ + r = RMIXL_IOREG_READ(RMIXL_IO_DEV_PCIE_LE + offset); + r &= ~bit; + RMIXL_IOREG_WRITE(RMIXL_IO_DEV_PCIE_LE + offset, r); + + /* + * if both STATUS0 and STATUS1 are 0 + * mark the link interrupt disabled + */ + if (r == 0) { + /* check the other reg */ + if (RMIXL_IOREG_READ(RMIXL_IO_DEV_PCIE_LE + other) == 0) { + lip->enabled = false; + DPRINTF(("%s: disabled link %d\n", __func__, lip->link)); + } + } + + evcnt_detach(&dip->count); + + free(dip, M_DEVBUF); + +} + +static void * +rmixl_pcie_intr_establish(void *v, pci_intr_handle_t pih, int ipl, + int (*func)(void *), void *arg) +{ + rmixl_pcie_softc_t *sc = v; + u_int link, bitno, irq; + uint32_t r; + rmixl_pcie_link_intr_t *lip; + rmixl_pcie_link_dispatch_t *dip; + uint32_t bit; + u_int offset; + int s; + char strbuf[32]; + + if (pih == ~0) { + DPRINTF(("%s: bad pih=%#lx, implies PCI_INTERRUPT_PIN_NONE\n", + __func__, pih)); + return NULL; + } + + rmixl_pcie_decompose_pih(pih, &link, &bitno, &irq); + DPRINTF(("%s: link=%d bitno=%d irq=%d\n", __func__, link, bitno, irq)); + + lip = &sc->sc_link_intr[link]; + + s = splhigh(); + +#ifdef DEBUG + LIST_FOREACH(dip, &lip->dispatch, next) { + if (dip->bitno == bitno) + panic("%s: bitno %d alread on dispatch list", __func__, bitno); + } +#endif + + /* + * all intrs on a link get same ipl and sc + * first intr established sets the standard + */ + if (lip->enabled == true) { + KASSERT(sc = lip->sc); + if (sc != lip->sc) { + printf("%s: sc %p mismatch\n", __func__, sc); + goto out; + } + KASSERT(ipl = lip->ipl); + if (ipl != lip->ipl) { + printf("%s: ipl %d mismatch\n", __func__, ipl); + goto out; + } + } + + /* + * allocate and initialize a dispatch handle + */ + dip = malloc(sizeof(*dip), M_DEVBUF, M_NOWAIT); + if (dip == NULL) { + printf("%s: cannot malloc dispatch handle\n", __func__); + goto out; + } + + dip->link = link; + dip->bitno = bitno; + dip->irq = irq; + dip->func = func; + dip->arg = arg; + snprintf(strbuf, sizeof(strbuf), "link %d, bitno %d", link, bitno); + evcnt_attach_dynamic(&dip->count, EVCNT_TYPE_INTR, NULL, + "rmixl_pcie", strbuf); + + if (bitno < 32) { + offset = int_enb_offset[link].r0; + bit = 1 << bitno; + } else { + offset = int_enb_offset[link].r1; + bit = 1 << (bitno - 32); + } + + /* enable this interrupt in the PCIe bridge */ + r = RMIXL_IOREG_READ(RMIXL_IO_DEV_PCIE_LE + offset); + r |= bit; + RMIXL_IOREG_WRITE(RMIXL_IO_DEV_PCIE_LE + offset, r); + + if (lip->enabled == false) { + lip->ih = rmixl_intr_establish(irq, ipl, + RMIXL_INTR_LEVEL, RMIXL_INTR_HIGH, rmixl_pcie_intr, lip); + if (lip->ih == NULL) + panic("%s: cannot establish irq %d", __func__, link); + + lip->sc = sc; + lip->ipl = ipl; + lip->enabled = true; + DPRINTF(("%s: enabled link %d\n", __func__, link)); + } + LIST_INSERT_HEAD(&lip->dispatch, dip, next); + + out: + splx(s); + return dip; +} + +static int +rmixl_pcie_intr(void *arg) +{ + rmixl_pcie_link_intr_t *lip = arg; + u_int link = lip->link; + int rv = 0; + + uint32_t status0 = RMIXL_IOREG_READ(RMIXL_IO_DEV_PCIE_LE + int_sts_offset[link].r0); + uint32_t status1 = RMIXL_IOREG_READ(RMIXL_IO_DEV_PCIE_LE + int_sts_offset[link].r1); + uint64_t status = ((uint64_t)status1 << 32) | status0; + DPRINTF(("%s: %d:%#"PRIx64"\n", __func__, link, status)); + + if (status != 0) { + rmixl_pcie_link_dispatch_t *dip; + + if (status & RMIXL_PCIE_LINK_STATUS_ERRORS) + rmixl_pcie_link_error_intr(link, status0, status1); + + LIST_FOREACH(dip, &lip->dispatch, next) { + uint64_t bit = 1 << dip->bitno; + if ((status & bit) != 0) { + (void)(*dip->func)(dip->arg); + dip->count.ev_count++; + rv = 1; + } + } + } + + return rv; +} + +static void +rmixl_pcie_link_error_intr(u_int link, uint32_t status0, uint32_t status1) +{ + printf("%s: mask %#"PRIx64"\n", + __func__, RMIXL_PCIE_LINK_STATUS_ERRORS); + printf("%s: PCIe Link Error: link=%d status0=%#x status1=%#x\n", + __func__, link, status0, status1); +#if defined(DDB) && defined(DEBUG) + Debugger(); +#endif +} + +#endif /* 0 */ + #if defined(DEBUG) || defined(DDB) /* this function exists to facilitate call from ddb */ int