Module Name: src Committed By: matt Date: Fri Dec 30 06:48:56 UTC 2011
Modified Files: src/sys/arch/mips/rmi [matt-nb5-mips64]: rmixl_intr.h rmixl_nand_pci.c rmixl_sdio.c rmixl_spi_pci.c rmixlreg.h Added Files: src/sys/arch/mips/rmi [matt-nb5-mips64]: rmixl_gpio_pci.c Log Message: Add GPIO support for XLP. Let NAND, MMC/SD, and SPI remove their pins from the GPIO available pin mask. To generate a diff of this commit: cvs rdiff -u -r0 -r1.1.2.1 src/sys/arch/mips/rmi/rmixl_gpio_pci.c cvs rdiff -u -r1.1.2.9 -r1.1.2.10 src/sys/arch/mips/rmi/rmixl_intr.h cvs rdiff -u -r1.1.2.1 -r1.1.2.2 src/sys/arch/mips/rmi/rmixl_nand_pci.c \ src/sys/arch/mips/rmi/rmixl_sdio.c src/sys/arch/mips/rmi/rmixl_spi_pci.c cvs rdiff -u -r1.1.2.15 -r1.1.2.16 src/sys/arch/mips/rmi/rmixlreg.h 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_intr.h diff -u src/sys/arch/mips/rmi/rmixl_intr.h:1.1.2.9 src/sys/arch/mips/rmi/rmixl_intr.h:1.1.2.10 --- src/sys/arch/mips/rmi/rmixl_intr.h:1.1.2.9 Sat Dec 24 01:57:54 2011 +++ src/sys/arch/mips/rmi/rmixl_intr.h Fri Dec 30 06:48:55 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: rmixl_intr.h,v 1.1.2.9 2011/12/24 01:57:54 matt Exp $ */ +/* $NetBSD: rmixl_intr.h,v 1.1.2.10 2011/12/30 06:48:55 matt Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. * All rights reserved. @@ -107,4 +107,8 @@ void rmixl_intr_init_clk(void); void rmixl_intr_init_ipi(void); #endif +void * gpio_intr_establish(size_t /* pin */, int /* ipl */, int /* ist */, + int (*)(void *), void *, bool); + +void gpio_intr_disestablish(void *); #endif /* _MIPS_RMI_RMIXL_INTR_H_ */ Index: src/sys/arch/mips/rmi/rmixl_nand_pci.c diff -u src/sys/arch/mips/rmi/rmixl_nand_pci.c:1.1.2.1 src/sys/arch/mips/rmi/rmixl_nand_pci.c:1.1.2.2 --- src/sys/arch/mips/rmi/rmixl_nand_pci.c:1.1.2.1 Tue Dec 27 19:58:19 2011 +++ src/sys/arch/mips/rmi/rmixl_nand_pci.c Fri Dec 30 06:48:56 2011 @@ -29,7 +29,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: rmixl_nand_pci.c,v 1.1.2.1 2011/12/27 19:58:19 matt Exp $"); +__KERNEL_RCSID(1, "$NetBSD: rmixl_nand_pci.c,v 1.1.2.2 2011/12/30 06:48:56 matt Exp $"); #include <sys/param.h> #include <sys/device.h> @@ -58,6 +58,18 @@ struct xlnand_softc { bus_space_handle_t sc_bsh; }; +static inline uint32_t +xlnand_read_4(struct xlnand_softc *sc, bus_size_t off) +{ + return bus_space_read_4(sc->sc_bst, sc->sc_bsh, off); +} + +static inline void +xlnand_write_4(struct xlnand_softc *sc, bus_size_t off, uint32_t v) +{ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, v); +} + CFATTACH_DECL_NEW(xlnand_pci, sizeof(struct xlnand_softc), xlnand_pci_match, xlnand_pci_attach, NULL, NULL); @@ -87,10 +99,22 @@ xlnand_pci_attach(device_t parent, devic * Why isn't this accessible via a BAR? */ if (bus_space_subregion(sc->sc_bst, rcp->rc_pci_ecfg_eb_memh, - pa->pa_tag | 0x100, 0, &sc->sc_bsh)) { + pa->pa_tag, 0, &sc->sc_bsh)) { aprint_error(": can't map registers\n"); return; } aprint_normal(": XLP NAND Controller\n"); + + /* + * If a NAND is using non-0 RDY/BSY signals, we need to take control + * of those from GPIO. + */ + uint32_t r = xlnand_read_4(sc, RMIXLP_NAND_RDYBSY_SEL); + for (r >>= 3; r != 0; r >>= 3) { + u_int rdybsy = r & 7; + if (rdybsy != 0) { + rcp->rc_gpio_available &= ~__BIT(33 + rdybsy); + } + } } Index: src/sys/arch/mips/rmi/rmixl_sdio.c diff -u src/sys/arch/mips/rmi/rmixl_sdio.c:1.1.2.1 src/sys/arch/mips/rmi/rmixl_sdio.c:1.1.2.2 --- src/sys/arch/mips/rmi/rmixl_sdio.c:1.1.2.1 Sat Dec 24 01:57:54 2011 +++ src/sys/arch/mips/rmi/rmixl_sdio.c Fri Dec 30 06:48:56 2011 @@ -29,7 +29,7 @@ #include <sys/param.h> -__KERNEL_RCSID(1, "$NetBSD: rmixl_sdio.c,v 1.1.2.1 2011/12/24 01:57:54 matt Exp $"); +__KERNEL_RCSID(1, "$NetBSD: rmixl_sdio.c,v 1.1.2.2 2011/12/30 06:48:56 matt Exp $"); #include <sys/device.h> #include <sys/bus.h> @@ -91,8 +91,10 @@ xlsdio_attach(device_t parent, device_t pci_conf_write(pa->pa_pc, pa->pa_tag, RMIXLP_MMC_SYSCTRL, r); DELAY(1000); r |= RMIXLP_MMC_SYSCTRL_CA; /* Cache Allocate */ +#if 0 r |= RMIXLP_MMC_SYSCTRL_EN0; /* Enable Slot 0 */ r |= RMIXLP_MMC_SYSCTRL_EN1; /* Enable Slot 1 */ +#endif r &= ~RMIXLP_MMC_SYSCTRL_CLK_DIS; /* Don't Disable Clock */ pci_conf_write(pa->pa_pc, pa->pa_tag, RMIXLP_MMC_SYSCTRL, r); @@ -113,7 +115,15 @@ xlsdio_attach(device_t parent, device_t pci_conf_write(pa->pa_pc, pa->pa_tag, offset + SDHC_NINTR_STATUS, 0xffffffff); - config_found(self, &xaa, xlsdio_print); + if (r & RMIXLP_MMC_SYSCTRL_EN(slot)) { + /* + * For any SDHC port we are using, we must remove + * the pins used by it from those that GPIO will + * offer to userland. + */ + rcp->rc_gpio_available &= ~RMIXLP_MMC_GPIO_PINS(slot); + config_found(self, &xaa, xlsdio_print); + } } } Index: src/sys/arch/mips/rmi/rmixl_spi_pci.c diff -u src/sys/arch/mips/rmi/rmixl_spi_pci.c:1.1.2.1 src/sys/arch/mips/rmi/rmixl_spi_pci.c:1.1.2.2 --- src/sys/arch/mips/rmi/rmixl_spi_pci.c:1.1.2.1 Tue Dec 27 19:58:19 2011 +++ src/sys/arch/mips/rmi/rmixl_spi_pci.c Fri Dec 30 06:48:56 2011 @@ -29,7 +29,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: rmixl_spi_pci.c,v 1.1.2.1 2011/12/27 19:58:19 matt Exp $"); +__KERNEL_RCSID(1, "$NetBSD: rmixl_spi_pci.c,v 1.1.2.2 2011/12/30 06:48:56 matt Exp $"); #include <sys/param.h> #include <sys/device.h> @@ -55,6 +55,7 @@ int xlspi_debug = 0; static int xlspi_pci_match(device_t, cfdata_t, void *); static void xlspi_pci_attach(device_t, device_t, void *); +static int xlspi_intr(void *); struct xlspi_softc { device_t sc_dev; @@ -82,7 +83,7 @@ xlspi_pci_attach(device_t parent, device struct rmixl_config * const rcp = &rmixl_configuration; struct pci_attach_args * const pa = aux; struct xlspi_softc * const sc = device_private(self); - // struct norbus_attach_args iba; + // struct spibus_attach_args iba; sc->sc_dev = self; sc->sc_bst = &rcp->rc_pci_ecfg_eb_memt; @@ -91,10 +92,41 @@ xlspi_pci_attach(device_t parent, device * Why isn't this accessible via a BAR? */ if (bus_space_subregion(sc->sc_bst, rcp->rc_pci_ecfg_eb_memh, - pa->pa_tag | 0x100, 0, &sc->sc_bsh)) { + pa->pa_tag, 0, &sc->sc_bsh)) { aprint_error(": can't map registers\n"); return; } - aprint_normal(": XLP SPI Controller\n"); + uint32_t r = bus_space_read_4(sc->sc_bst, sc->sc_bsh, + RMIXLP_SPI_SYSCNTRL); + + if ((r & RMIXLP_SPI_SYSCNTRL_PMEN) == 0) { + aprint_normal(": XLP SPI Controller%s\n", " (disabled)"); + return; + } + + /* + * Take away the SPI pins + */ + rcp->rc_gpio_available &= ~RMIXLP_SPI_GPIO_PINS; + + aprint_normal(": XLP SPI Controller%s\n", ""); + + pci_intr_handle_t pcih; + pci_intr_map(pa, &pcih); + + if (pci_intr_establish(pa->pa_pc, pcih, IPL_VM, xlspi_intr, sc) == NULL) { + aprint_error_dev(self, "failed to establish interrupt\n"); + } else { + const char * const intrstr = pci_intr_string(pa->pa_pc, pcih); + aprint_normal_dev(self, "interrupting at %s\n", intrstr); + } +} + +static int +xlspi_intr(void *v) +{ + struct xlspi_softc * const sc = v; + + panic("%s(%p)", device_xname(sc->sc_dev), v); } Index: src/sys/arch/mips/rmi/rmixlreg.h diff -u src/sys/arch/mips/rmi/rmixlreg.h:1.1.2.15 src/sys/arch/mips/rmi/rmixlreg.h:1.1.2.16 --- src/sys/arch/mips/rmi/rmixlreg.h:1.1.2.15 Wed Dec 28 05:36:11 2011 +++ src/sys/arch/mips/rmi/rmixlreg.h Fri Dec 30 06:48:56 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: rmixlreg.h,v 1.1.2.15 2011/12/28 05:36:11 matt Exp $ */ +/* $NetBSD: rmixlreg.h,v 1.1.2.16 2011/12/30 06:48:56 matt Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. @@ -900,6 +900,41 @@ */ #define RMIXL_GPIO_LOW_PWR_DIS_RESV __BITS(31,9) +#define RMIXLP_GPIO_PADOE(g) _RMIXL_OFFSET(0x40+(g)) // Pad Output Enable Register 0 +#define RMIXLP_GPIO_PADOE0 _RMIXL_OFFSET(0x40) // Pad Output Enable Register 0 +#define RMIXLP_GPIO_PADOE1 _RMIXL_OFFSET(0x41) // Pad Output Enable Register 1 +#define RMIXLP_GPIO_PADDRV(g) _RMIXL_OFFSET(0x42+(g)) // Pad Drive Register 0 +#define RMIXLP_GPIO_PADDRV0 _RMIXL_OFFSET(0x42) // Pad Drive Register 0 +#define RMIXLP_GPIO_PADDRV1 _RMIXL_OFFSET(0x43) // Pad Drive Register 1 +#define RMIXLP_GPIO_PADSAMPLE(g) _RMIXL_OFFSET(0x44+(g)) // Pad Sample Register 0 +#define RMIXLP_GPIO_PADSAMPLE0 _RMIXL_OFFSET(0x44) // Pad Sample Register 0 +#define RMIXLP_GPIO_PADSAMPLE1 _RMIXL_OFFSET(0x45) // Pad Sample Register 1 +#define RMIXLP_GPIO_INTEN(n,g) _RMIXL_OFFSET(0x46+2*(n)+(g)) // Interrupt 0 Enable Register 0 +#define RMIXLP_GPIO_INTEN0(n) _RMIXL_OFFSET(0x46+2*(n)) // Interrupt 0 Enable Register 0 +#define RMIXLP_GPIO_INTEN1(n) _RMIXL_OFFSET(0x47+2*(n)) // Interrupt 0 Enable Register 0 +#define RMIXLP_GPIO_INTEN00 _RMIXL_OFFSET(0x46) // Interrupt 0 Enable Register 0 +#define RMIXLP_GPIO_INTEN01 _RMIXL_OFFSET(0x47) // Interrupt 0 Enable Register 1 +#define RMIXLP_GPIO_INTEN10 _RMIXL_OFFSET(0x48) // Interrupt 1 Enable Register 0 +#define RMIXLP_GPIO_INTEN11 _RMIXL_OFFSET(0x49) // Interrupt 1 Enable Register 1 +#define RMIXLP_GPIO_INTEN20 _RMIXL_OFFSET(0x4A) // Interrupt 2 Enable Register 0 +#define RMIXLP_GPIO_INTEN21 _RMIXL_OFFSET(0x4B) // Interrupt 2 Enable Register 1 +#define RMIXLP_GPIO_INTEN30 _RMIXL_OFFSET(0x4C) // Interrupt 3 Enable Register 0 +#define RMIXLP_GPIO_INTEN31 _RMIXL_OFFSET(0x4D) // Interrupt 3 Enable Register 1 +#define RMIXLP_GPIO_INTPOL(g) _RMIXL_OFFSET((0x4E)+(g)) // Interrupt Polarity Register 0 +#define RMIXLP_GPIO_INTPOL0 _RMIXL_OFFSET(0x4E) // Interrupt Polarity Register 0 +#define RMIXLP_GPIO_INTPOL1 _RMIXL_OFFSET(0x4F) // Interrupt Polarity Register 1 +#define RMIXLP_GPIO_INTTYPE(g) _RMIXL_OFFSET(0x50+(g)) // Interrupt Type Register 0 +#define RMIXLP_GPIO_INTTYPE0 _RMIXL_OFFSET(0x50) // Interrupt Type Register 0 +#define RMIXLP_GPIO_INTTYPE1 _RMIXL_OFFSET(0x51) // Interrupt Type Register 1 +#define RMIXLP_GPIO_INTSTAT(g) _RMIXL_OFFSET(0x52+(g)) // Interrupt Status Register 0 +#define RMIXLP_GPIO_INTSTAT0 _RMIXL_OFFSET(0x52) // Interrupt Status Register 0 +#define RMIXLP_GPIO_INTSTAT1 _RMIXL_OFFSET(0x53) // Interrupt Status Register 1 + +#define RMIXLP_GPIO_8XX_MAXPINS 41 /* 41 GPIO pins */ +#define RMIXLP_GPIO_4XX_MAXPINS 41 /* 41 GPIO pins */ +#define RMIXLP_GPIO_3XX_MAXPINS 57 /* 41 GPIO pins */ +#define RMIXLP_GPIO_3XXL_MAXPINS 44 /* 44 GPIO pins */ + /* * Peripheral I/O bus (Flash/PCMCIA) controller registers */ @@ -1432,6 +1467,12 @@ #define PCI_RMIXLP_STATID _RMIXL_OFFSET(0x3c) #define PCI_RMIXLP_IRTINFO _RMIXL_OFFSET(0x3d) +#define PCI_RMIXLP_STATID_BASE(x) (((x) >> 0) & 0xfff) +#define PCI_RMIXLP_STATID_COUNT(x) (((x) >> 16) & 0xfff) + +#define PCI_RMIXLP_IRTINFO_BASE(x) (((x) >> 0) & 0xfff) +#define PCI_RMIXLP_IRTINFO_COUNT(x) (((x) >> 16) & 0xfff) + /* * XLP System Management Registers */ @@ -1608,14 +1649,78 @@ #define RMIXLP_NOR_STATUS_RDYBSY __BIT(0) #define RMIXLP_NAND_PCITAG _RMIXL_PCITAG(0,7,1) +#define RMIXLP_NAND_RDYBSY_SEL _RMIXL_OFFSET(0x81) + #define RMIXLP_SPI_PCITAG _RMIXL_PCITAG(0,7,2) +#define RMIXLP_SPI_CS_CONFIG(n) _RMIXL_OFFSET(0x40+0x10*(n)) +#define RMIXLP_SPI_CS_FDIV(n) _RMIXL_OFFSET(0x41+0x10*(n)) +#define RMIXLP_SPI_CS_CMD(n) _RMIXL_OFFSET(0x42+0x10*(n)) +#define RMIXLP_SPI_CS_STATUS(n) _RMIXL_OFFSET(0x43+0x10*(n)) +#define RMIXLP_SPI_CS_INTEN(n) _RMIXL_OFFSET(0x44+0x10*(n)) +#define RMIXLP_SPI_CS_FIFO_THRESH(n) _RMIXL_OFFSET(0x45+0x10*(n)) +#define RMIXLP_SPI_CS_FIFO_WCNT(n) _RMIXL_OFFSET(0x46+0x10*(n)) +#define RMIXLP_SPI_CS_TXDATA_FIFO(n) _RMIXL_OFFSET(0x47+0x10*(n)) +#define RMIXLP_SPI_CS_RXDATA_FIFO(n) _RMIXL_OFFSET(0x48+0x10*(n)) +#define RMIXLP_SPI_SYSCNTRL _RMIXL_OFFSET(0x80) + +#define RMIXLP_SPI_CS_CONFIG_RXCAP __BIT(11) // RX capture on phase drive +#define RMIXLP_SPI_CS_CONFIG_LSBFE __BIT(10) // LSB first enable +#define RMIXLP_SPI_CS_CONFIG_CSTODATA __BITS(9,8) // chip select polarity 1:high +#define RMIXLP_SPI_CS_CONFIG_SBPOL __BIT(6) // start bit polarity 1:high +#define RMIXLP_SPI_CS_CONFIG_SBE __BIT(6) // start bit enable 1:enabled +#define RMIXLP_SPI_CS_CONFIG_RXMISO __BIT(5) // 0: RX MOSI, 1: RX MISO +#define RMIXLP_SPI_CS_CONFIG_TXMOSI __BIT(4) // 1: TX enable for MOSI pin +#define RMIXLP_SPI_CS_CONFIG_TXMISO __BIT(3) // 1: TX enable for MISO pin +#define RMIXLP_SPI_CS_CONFIG_CSPOL __BIT(2) // chip select polarity 1:high +#define RMIXLP_SPI_CS_CONFIG_CPOL __BIT(1) // clock polarity 1:high +#define RMIXLP_SPI_CS_CONFIG_CPHA __BIT(0) // SPI_CLK clock phase select + +#define RMIXLP_SPI_CS_FDIV_CNT __BITS(15,0) // divisor (min of 16) + +#define RMIXLP_SPI_CS_CMD_XFERBITCNT __BITS(31,16) // total # of bits - 1 +#define RMIXLP_SPI_CS_CMD_CONT __BIT(4) +#define RMIXLP_SPI_CS_CMD_CMD __BITS(3,0) +#define RMIXLP_SPI_CS_CMD_IDLE 0 +#define RMIXLP_SPI_CS_CMD_TX 1 +#define RMIXLP_SPI_CS_CMD_RX 2 +#define RMIXLP_SPI_CS_CMD_TXRX 3 + +#define RMIXLP_SPI_CS_STATUS_RXOF __BIT(5) // RX FIFO overflow +#define RMIXLP_SPI_CS_STATUS_TXUF __BIT(4) // TX FIFO underflow +#define RMIXLP_SPI_CS_STATUS_RXTHRESH __BIT(3) // RX FIFO threshold crossed +#define RMIXLP_SPI_CS_STATUS_TXTHRESH __BIT(2) // TX FIFO threshold crossed +#define RMIXLP_SPI_CS_STATUS_XFERDONE __BIT(1) // Transfer is complete (RO) +#define RMIXLP_SPI_CS_STATUS_XFERPEND __BIT(1) // Transfer is pending (RO) + +#define RMIXLP_SPI_CS_INTEN_RXOF __BIT(4) // RX FIFO overflow +#define RMIXLP_SPI_CS_INTEN_TXUF __BIT(3) // TX FIFO underflow +#define RMIXLP_SPI_CS_INTEN_RXTHRESH __BIT(2) // RX FIFO threshold crossed +#define RMIXLP_SPI_CS_INTEN_TXTHRESH __BIT(1) // TX FIFO threshold crossed +#define RMIXLP_SPI_CS_INTEN_XFERDONE __BIT(0) // Transfer is complete + +#define RMIXLP_SPI_CS_FIFO_THRESH_TXFIFO __BITS(7,4) +#define RMIXLP_SPI_CS_FIFO_THRESH_RXFIFO __BITS(3,0) + +#define RMIXLP_SPI_CS_FIFO_WCNT_TXFIFO __BITS(7,4) +#define RMIXLP_SPI_CS_FIFO_WCNT_RXFIFO __BITS(3,0) + +#define RMIXLP_SPI_SYSCNTRL_PMEN __BIT(8) // Pin muxing enable +#define RMIXLP_SPI_SYSCNTRL_CLKDSI(n) __BIT(4+(n)) // Clock disable for chan +#define RMIXLP_SPI_SYSCNTRL_RESET(n) __BIT(0+(n)) // Reset SPI channel n + +#define RMIXLP_SPI_GPIO_PINS __BITS(28,22) + #define RMIXLP_MMC_PCITAG _RMIXL_PCITAG(0,7,3) #define RMIXLP_MMC_SLOTSIZE _RMIXL_OFFSET(0x40) #define RMIXLP_MMC_SLOT0 _RMIXL_OFFSET(0x40) #define RMIXLP_MMC_SLOT1 _RMIXL_OFFSET(0x80) #define RMIXLP_MMC_SYSCTRL _RMIXL_OFFSET(0xC0) +#define RMIXLP_MMC_CAPCFG0_S(n) _RMIXL_OFFSET(0xC1+5*(n)) +#define RMIXLP_MMC_CAPCFG1_S(n) _RMIXL_OFFSET(0xC2+5*(n)) +#define RMIXLP_MMC_INIT_PRESET_S(n) _RMIXL_OFFSET(0xC3+5*(n)) +#define RMIXLP_MMC_DEF_PRESET_S(n) _RMIXL_OFFSET(0xC4+5*(n)) #define RMIXLP_MMC_SYSCTRL_DELAY __BITS(21,19) #define RMIXLP_MMC_SYSCTRL_RT __BIT(8) @@ -1625,9 +1730,19 @@ #define RMIXLP_MMC_SYSCTRL_CA __BIT(4) #define RMIXLP_MMC_SYSCTRL_EN1 __BIT(3) #define RMIXLP_MMC_SYSCTRL_EN0 __BIT(2) +#define RMIXLP_MMC_SYSCTRL_EN(n) __BIT(2+(n)) #define RMIXLP_MMC_SYSCTRL_CLK_DIS __BIT(1) #define RMIXLP_MMC_SYSCTRL_RST __BIT(0) +#define RMIXLP_MMC_CAPCFG0_S_EMB __BIT(17) // Extended Media Bus + +#define RMIXLP_MMC_GPIO_PINS0 \ + (__BITS(10,0) | __BIT(29) | __BIT(31)) +#define RMIXLP_MMC_GPIO_PINS1 \ + (__BITS(21,11) | __BIT(28) | __BIT(30)) +#define RMIXLP_MMC_GPIO_PINS(slot) \ + (slot == 0 ? RMIXLP_MMC_GPIO_PINS0 : RMIXLP_MMC_GPIO_PINS1) + /* * FMN non-core station configuration registers */ Added files: Index: src/sys/arch/mips/rmi/rmixl_gpio_pci.c diff -u /dev/null src/sys/arch/mips/rmi/rmixl_gpio_pci.c:1.1.2.1 --- /dev/null Fri Dec 30 06:48:56 2011 +++ src/sys/arch/mips/rmi/rmixl_gpio_pci.c Fri Dec 30 06:48:55 2011 @@ -0,0 +1,581 @@ +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matt Thomas of 3am Software Foundry. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + +#include <sys/cdefs.h> + +__KERNEL_RCSID(1, "$NetBSD: rmixl_gpio_pci.c,v 1.1.2.1 2011/12/30 06:48:55 matt Exp $"); + +#include <sys/param.h> +#include <sys/atomic.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/gpio.h> + +#include "locators.h" +#include "gpio.h" + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> + +#include <dev/gpio/gpiovar.h> + +#include <mips/rmi/rmixlreg.h> +#include <mips/rmi/rmixlvar.h> +#include <mips/rmi/rmixl_intr.h> + +#ifdef DEBUG +int xlgpio_debug = 0; +#define DPRINTF(x, ...) do { if (xlgpio_debug) printf(x, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(x) +#endif + +static int xlgpio_pci_match(device_t, cfdata_t, void *); +static void xlgpio_pci_attach(device_t, device_t, void *); +static void xlgpio_defer(device_t); +static int xlgpio_high_intr(void *); +static int xlgpio_ddb_intr(void *); +static int xlgpio_sched_intr(void *); +static int xlgpio_vm_intr(void *); +static int xlgpio_stray_intr(void *v); + +static int xlgpio_pin_read(void *, int); +static void xlgpio_pin_write(void *, int, int); +static void xlgpio_pin_ctl(void *, int, int); + +static const uint8_t xlgpio_pincnt_by_variant[] = { + [RMIXLP_8XX] = RMIXLP_GPIO_8XX_MAXPINS, + [RMIXLP_4XX] = RMIXLP_GPIO_4XX_MAXPINS, + [RMIXLP_3XX] = RMIXLP_GPIO_3XX_MAXPINS, + [RMIXLP_3XXL] = RMIXLP_GPIO_3XXL_MAXPINS, + [RMIXLP_3XXH] = RMIXLP_GPIO_3XXL_MAXPINS, + [RMIXLP_3XXQ] = RMIXLP_GPIO_3XXL_MAXPINS, +}; + + +static int (* const xlgpio_intrs[])(void *) = { + [IPL_VM - IPL_VM] = xlgpio_vm_intr, + [IPL_SCHED - IPL_VM] = xlgpio_sched_intr, + [IPL_DDB - IPL_VM] = xlgpio_ddb_intr, + [IPL_HIGH - IPL_VM] = xlgpio_high_intr, +}; + +struct xlgpio_intrpin { + struct evcnt gip_ev; + int (*gip_func)(void *); + void *gip_arg; + uint8_t gip_ipl; + uint8_t gip_ist; + char gip_ev_name[sizeof("pin XX")]; +}; + +#define PINMASK 31 +#define PINGROUP (PINMASK+1) +#define PIN_GROUP(pin) ((pin) / PINGROUP) +#define PIN_SELECT(pin) ((pin) & PINMASK) +#define PIN_MASK(pin) (1 << ((pin) & PINMASK)) + +struct xlgpio_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + struct xlgpio_intrpin sc_pins[2*PINGROUP]; + struct xlgpio_group { + struct xlgpio_intrpin *gg_pins; + uint32_t gg_padoe; + uint32_t gg_paddrv; + uint32_t gg_inten[IPL_HIGH - IPL_VM + 1]; + uint32_t gg_intpol; + uint32_t gg_inttype; + uint16_t gg_r_padoe; + uint16_t gg_r_paddrv; + uint16_t gg_r_padsample; + uint16_t gg_r_inten[IPL_HIGH - IPL_VM + 1]; + uint16_t gg_r_intpol; + uint16_t gg_r_inttype; + uint32_t gg_r_intstat; + } sc_groups[2]; + kmutex_t *sc_pin_lock; + kmutex_t *sc_intr_lock; +#if NGPIO > 0 + uint8_t sc_pincnt; + struct gpio_chipset_tag sc_gpio_chipset; + gpio_pin_t sc_gpio_pins[64]; +#endif +}; + +static struct xlgpio_softc xlgpio_sc = { /* there can only be one */ + .sc_pins = { + [0 ... 2*PINGROUP-1] = { + .gip_ipl = IPL_NONE, + .gip_ist = IST_NONE, + .gip_func = xlgpio_stray_intr, + }, + }, + .sc_groups = { + [0] = { + .gg_pins = xlgpio_sc.sc_pins, + .gg_r_padoe = RMIXLP_GPIO_PADOE(0), + .gg_r_paddrv = RMIXLP_GPIO_PADOE(0), + .gg_r_padsample = RMIXLP_GPIO_PADOE(0), + .gg_r_inten = { + [0] = RMIXLP_GPIO_INTEN(0, 0), + [1] = RMIXLP_GPIO_INTEN(1, 0), + [2] = RMIXLP_GPIO_INTEN(2, 0), + [3] = RMIXLP_GPIO_INTEN(3, 0), + }, + .gg_r_intpol = RMIXLP_GPIO_INTPOL(0), + .gg_r_inttype = RMIXLP_GPIO_INTTYPE(0), + .gg_r_intstat = RMIXLP_GPIO_INTSTAT(0), + }, + [1] = { + .gg_pins = xlgpio_sc.sc_pins + PINGROUP, + .gg_r_padoe = RMIXLP_GPIO_PADOE(1), + .gg_r_paddrv = RMIXLP_GPIO_PADOE(1), + .gg_r_padsample = RMIXLP_GPIO_PADOE(1), + .gg_r_inten = { + [0] = RMIXLP_GPIO_INTEN(0, 1), + [1] = RMIXLP_GPIO_INTEN(1, 1), + [2] = RMIXLP_GPIO_INTEN(2, 1), + [3] = RMIXLP_GPIO_INTEN(3, 1), + }, + .gg_r_intpol = RMIXLP_GPIO_INTPOL(1), + .gg_r_inttype = RMIXLP_GPIO_INTTYPE(1), + .gg_r_intstat = RMIXLP_GPIO_INTSTAT(1), + }, + }, + .sc_gpio_chipset = { + .gp_cookie = &xlgpio_sc, +#if 0 + .gp_gc_open = xlgpio_gc_open, + .gp_gc_close = xlgpio_gc_close, +#endif + .gp_pin_read = xlgpio_pin_read, + .gp_pin_write = xlgpio_pin_write, + .gp_pin_ctl = xlgpio_pin_ctl, + }, +}; + +static inline uint32_t +xlgpio_read_4(struct xlgpio_softc *sc, bus_size_t off) +{ + return bus_space_read_4(sc->sc_bst, sc->sc_bsh, off); +} + +static inline void +xlgpio_write_4(struct xlgpio_softc *sc, bus_size_t off, uint32_t v) +{ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, v); +} + +CFATTACH_DECL_NEW(xlgpio_pci, 0, + xlgpio_pci_match, xlgpio_pci_attach, NULL, NULL); + +static int +xlgpio_pci_match(device_t parent, cfdata_t cf, void *aux) +{ + struct pci_attach_args * const pa = aux; + + if (xlgpio_sc.sc_dev != NULL) // there can only be one + return 0; + + if (pa->pa_id == PCI_ID_CODE(PCI_VENDOR_NETLOGIC, PCI_PRODUCT_NETLOGIC_XLP_GPIO)) + return 1; + + return 0; +} + +static void +xlgpio_pci_attach(device_t parent, device_t self, void *aux) +{ + struct rmixl_config * const rcp = &rmixl_configuration; + struct pci_attach_args * const pa = aux; + struct xlgpio_softc * const sc = &xlgpio_sc; + const char * const xname = device_xname(self); + + KASSERT(sc->sc_dev == NULL); + + self->dv_private = sc; + sc->sc_dev = self; + sc->sc_bst = &rcp->rc_pci_ecfg_eb_memt; + + /* + * Why isn't this accessible via a BAR? + */ + if (bus_space_subregion(sc->sc_bst, rcp->rc_pci_ecfg_eb_memh, + pa->pa_tag, 0, &sc->sc_bsh)) { + aprint_error(": can't map registers\n"); + return; + } + + aprint_normal(": XLP GPIO Controller\n"); + + sc->sc_intr_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_HIGH); + sc->sc_pin_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NONE); + + /* + * Of course, each XLP variant has a different number of pins. + */ + KASSERT(rmixl_xlp_variant < __arraycount(xlgpio_pincnt_by_variant)); + sc->sc_pincnt = xlgpio_pincnt_by_variant[rmixl_xlp_variant]; + rcp->rc_gpio_available &= __BITS(sc->sc_pincnt - 1, 0); + + /* + * Attach the evcnt for each pin, whether it's available or not. + */ + for (size_t pin = 0; pin < sc->sc_pincnt; pin++) { + struct xlgpio_intrpin * const gip = &sc->sc_pins[pin]; + snprintf(gip->gip_ev_name, sizeof(gip->gip_ev_name), + "pin %zu", pin); + + evcnt_attach_dynamic(&gip->gip_ev, EVCNT_TYPE_INTR, + NULL, xname, gip->gip_ev_name); + KASSERT(gip->gip_func == xlgpio_stray_intr); + } + + for (size_t group = 0; group < __arraycount(sc->sc_groups); group++) { + struct xlgpio_group * const gg = &sc->sc_groups[group]; + KASSERT(gg->gg_intpol == 0); + KASSERT(gg->gg_inttype == 0); + + /* + * Disable all interrupts for group. + * Get shadow copy of registers. + */ + + gg->gg_padoe = xlgpio_read_4(sc, gg->gg_r_padoe); + gg->gg_paddrv = xlgpio_read_4(sc, gg->gg_r_paddrv); + xlgpio_write_4(sc, gg->gg_r_intpol, gg->gg_intpol); + xlgpio_write_4(sc, gg->gg_r_inttype, gg->gg_inttype); + for (size_t irt = 0; irt < __arraycount(gg->gg_inten); irt++) { + KASSERT(gg->gg_inten[irt] == 0); + xlgpio_write_4(sc, gg->gg_r_inten[irt], + gg->gg_inten[irt]); + } + } + + /* + * GPIO has 4 interrupts which map 1:1 on IPL_VM to IPL_HIGH + */ + const pcireg_t irtinfo = xlgpio_read_4(sc, PCI_RMIXLP_IRTINFO); + + const size_t irtstart = PCI_RMIXLP_IRTINFO_BASE(irtinfo); + const size_t irtcount = PCI_RMIXLP_IRTINFO_COUNT(irtinfo); + + KASSERT(irtcount >= IPL_HIGH - IPL_VM + 1); + + for (size_t irt = 0; irt < irtcount; irt++) { + if (rmixl_intr_establish(irtstart + irt, IPL_VM + irt, + RMIXL_TRIG_LEVEL, RMIXL_POLR_HIGH, + xlgpio_intrs[irt], sc, true) == NULL) + panic("%s: failed to establish interrupt %zu", + __func__, irtstart + irt); + } + +#if NGPIO > 0 + config_defer(self, xlgpio_defer); +#endif +} + +static int +xlgpio_stray_intr(void *v) +{ + return 0; +} + +static int +xlgpio_group_intr(struct xlgpio_softc *sc, int ipl, size_t group) +{ + struct xlgpio_group * const gg = &sc->sc_groups[group]; + uint32_t sts = gg->gg_inten[ipl - IPL_VM]; + int rv = 0; + + if (sts == 0) + return rv; + + sts &= xlgpio_read_4(sc, gg->gg_r_intstat); + if (sts == 0) + return rv; + + /* First, ACK any edge type interrupts */ + if (sts & gg->gg_inttype) + xlgpio_write_4(sc, gg->gg_r_intstat, sts & gg->gg_inttype); + + /* narrow to level interrupts */ + uint32_t intlevel = (sts & ~gg->gg_inttype); + + while (sts != 0) { + const int bit = PINMASK - __builtin_clz(sts); + struct xlgpio_intrpin * const gip = &gg->gg_pins[bit]; + + KASSERT(gip->gip_ipl == ipl); + const int nrv = (*gip->gip_func)(gip->gip_arg); + if (nrv) + rv = nrv; + gip->gip_ev.ev_count++; + sts &= ~(1 << bit); + } + + /* Now ACK any level type interrupts */ + if (intlevel) + xlgpio_write_4(sc, gg->gg_r_intstat, intlevel); + + return rv; +} + +static inline int +xlgpio_intr(struct xlgpio_softc *sc, size_t ipl) +{ + int rv = 0; + for (size_t group = 0; group < __arraycount(sc->sc_groups); group++) { + const int nrv = xlgpio_group_intr(sc, ipl, group); + if (!rv) + rv = nrv; + } + + return rv; +} + +static int +xlgpio_high_intr(void *v) +{ + struct xlgpio_softc * const sc = v; + return xlgpio_intr(sc, IPL_HIGH); +} + +static int +xlgpio_ddb_intr(void *v) +{ + struct xlgpio_softc * const sc = v; + return xlgpio_intr(sc, IPL_DDB); +} + +static int +xlgpio_sched_intr(void *v) +{ + struct xlgpio_softc * const sc = v; + return xlgpio_intr(sc, IPL_SCHED); +} + +static int +xlgpio_vm_intr(void *v) +{ + struct xlgpio_softc * const sc = v; + return xlgpio_intr(sc, IPL_VM); +} + +void * +gpio_intr_establish(size_t pin, int ipl, int ist, + int (*func)(void *), void *arg, bool mpsafe) +{ + struct xlgpio_softc * const sc = &xlgpio_sc; + struct rmixl_config * const rcp = &rmixl_configuration; + struct xlgpio_intrpin * const gip = &sc->sc_pins[pin]; + size_t group = PIN_GROUP(pin); + struct xlgpio_group * const gg = &sc->sc_groups[group]; + const uint32_t mask = PIN_MASK(pin); + uint32_t * const inten_p = &gg->gg_inten[ipl - IPL_VM]; + + KASSERT(IPL_VM <= ipl); + KASSERT(ipl <= IPL_HIGH); + KASSERT(ist == IST_LEVEL_LOW || ist == IST_LEVEL_HIGH || ist == IST_EDGE); + KASSERT(func != NULL); + KASSERT(group < __arraycount(sc->sc_groups)); + + if (pin >= sc->sc_pincnt) + return NULL; + + if ((pin & rcp->rc_gpio_available) == 0) + return NULL; + + KASSERT((*inten_p & mask) == 0); + KASSERT(gip->gip_func != xlgpio_stray_intr); + KASSERT(gip->gip_ipl == IPL_NONE); + KASSERT(gip->gip_ist == IST_NONE); + + mutex_enter(sc->sc_intr_lock); + + gip->gip_ipl = ipl; + gip->gip_func = func; + gip->gip_arg = arg; + + if (ist == IST_EDGE) { + atomic_or_32(&gg->gg_inttype, mask); + atomic_and_32(&gg->gg_intpol, ~mask); + } else if (ist == IST_LEVEL_HIGH) { + atomic_and_32(&gg->gg_inttype, ~mask); + atomic_and_32(&gg->gg_intpol, ~mask); + } else { + atomic_and_32(&gg->gg_inttype, ~mask); + atomic_or_32(&gg->gg_intpol, mask); + } + + xlgpio_write_4(sc, gg->gg_r_inttype, gg->gg_inttype); + xlgpio_write_4(sc, gg->gg_r_intpol, gg->gg_intpol); + + /* + * Atomically update our shadow copy of inten and feed the new + * value to the register. + */ + + xlgpio_write_4(sc, gg->gg_r_intpol, atomic_or_32_nv(inten_p, mask)); + + mutex_exit(sc->sc_intr_lock); + + return gip; +} + +void +gpio_intr_disestablish(void *v) +{ + struct xlgpio_softc * const sc = &xlgpio_sc; + struct xlgpio_intrpin * const gip = v; + const size_t pin = gip - sc->sc_pins; + const size_t group = PIN_GROUP(pin); + const uint32_t mask = PIN_MASK(pin); + struct xlgpio_group * const gg = &sc->sc_groups[group]; + uint32_t * const inten_p = &gg->gg_inten[gip->gip_ipl - IPL_VM]; + + KASSERT(&sc->sc_pins[pin] == gip); + KASSERT(pin < __arraycount(sc->sc_pins)); + KASSERT(gip->gip_func != xlgpio_stray_intr); + + *inten_p &= ~mask; + xlgpio_write_4(sc, gg->gg_r_inten[gip->gip_ipl - IPL_VM], *inten_p); + xlgpio_write_4(sc, RMIXLP_GPIO_INTSTAT(group), mask); /* ACK it */ + + gip->gip_ipl = IPL_NONE; + gip->gip_ist = IST_NONE; +} + +#if NGPIO > 0 +static int +xlgpio_pin_read(void *arg, int pin) +{ + struct xlgpio_softc * const sc = arg; + struct xlgpio_group * const gg = &sc->sc_groups[PIN_GROUP(pin)]; + const uint32_t mask = PIN_MASK(pin); + + /* no locking needed */ + + return (xlgpio_read_4(sc, gg->gg_r_padsample) & mask) != 0; +} + +static void +xlgpio_pin_write(void *arg, int pin, int value) +{ + struct xlgpio_softc * const sc = arg; + struct xlgpio_group * const gg = &sc->sc_groups[PIN_GROUP(pin)]; + const uint32_t mask = 1 << (pin & PINMASK); + + mutex_enter(sc->sc_pin_lock); + + /* + * set new to 0 if no change, or mask since mask needs to be + * inverted. + */ + const uint32_t new = (gg->gg_paddrv & mask) ^ (value ? mask : 0); + + if (new) { + gg->gg_paddrv ^= new; + xlgpio_write_4(sc, gg->gg_r_paddrv, gg->gg_paddrv); + } + + mutex_exit(sc->sc_pin_lock); +} + +static void +xlgpio_pin_ctl(void *arg, int pin, int flags) +{ + struct xlgpio_softc * const sc = arg; + const bus_size_t r_padoe = RMIXLP_GPIO_PADOE(pin / PINGROUP); + const uint32_t mask = 1 << (pin & PINMASK); + + mutex_enter(sc->sc_pin_lock); + + KASSERT(pin < sc->sc_pincnt); + + const uint32_t old = xlgpio_read_4(sc, r_padoe); + uint32_t new; + + switch (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { + case GPIO_PIN_INPUT: new = old | mask; break; + case GPIO_PIN_OUTPUT: new = old & ~mask; break; + default: new = old; + } + if (old != new) + xlgpio_write_4(sc, r_padoe, new); + + mutex_exit(sc->sc_pin_lock); +} + +static void +xlgpio_defer(device_t self) +{ + struct xlgpio_softc * const sc = device_private(self); + struct gpio_chipset_tag * const gp = &sc->sc_gpio_chipset; + struct gpiobus_attach_args gba; + gpio_pin_t *pins = sc->sc_gpio_pins; + uint64_t mask, oe, valueout, valuein; + const uint64_t available_pins = rmixl_configuration.rc_gpio_available; + u_int pin; + + gba.gba_gc = gp; + gba.gba_pins = pins; + + oe = ((uint64_t)sc->sc_groups[0].gg_padoe << 0) + | ((uint64_t)sc->sc_groups[1].gg_padoe << 32); + valueout = ((uint64_t)sc->sc_groups[0].gg_paddrv << 0) + | ((uint64_t)sc->sc_groups[1].gg_paddrv << 32); + valuein = ((uint64_t)xlgpio_read_4(sc, RMIXLP_GPIO_PADSAMPLE0) << 0) + | ((uint64_t)xlgpio_read_4(sc, RMIXLP_GPIO_PADSAMPLE1) << 32); + + for (pin = 0, mask = 1; + available_pins >= mask && pin < sc->sc_pincnt; + mask <<= 1, pin++) { + if ((available_pins & mask) == 0) + continue; + + pins->pin_num = pin; + pins->pin_caps = GPIO_PIN_INPUT|GPIO_PIN_OUTPUT; + pins->pin_flags = + (oe & mask) ? GPIO_PIN_OUTPUT : GPIO_PIN_INPUT; + pins->pin_state = + (((oe & mask) ? valueout : valuein) & mask) + ? GPIO_PIN_HIGH + : GPIO_PIN_LOW; + + pins++; + } + gba.gba_npins = pins - sc->sc_gpio_pins; + + config_found_ia(self, "gpiobus", &gba, gpiobus_print); +} +#endif /* NGPIO > 0 */