Index: head/sys/arm/freescale/imx/files.imx6 =================================================================== --- head/sys/arm/freescale/imx/files.imx6 (revision 320075) +++ head/sys/arm/freescale/imx/files.imx6 (revision 320076) @@ -1,71 +1,72 @@ # $FreeBSD$ # # Standard ARM support. # kern/kern_clocksource.c standard # # Standard imx6 devices and support. # arm/freescale/fsl_ocotp.c standard arm/freescale/imx/imx6_anatop.c standard arm/freescale/imx/imx6_ccm.c standard arm/freescale/imx/imx6_machdep.c standard arm/freescale/imx/imx6_mp.c optional smp arm/freescale/imx/imx6_pl310.c standard arm/freescale/imx/imx6_src.c standard +arm/freescale/imx/imx_epit.c standard arm/freescale/imx/imx_iomux.c standard arm/freescale/imx/imx_machdep.c standard -arm/freescale/imx/imx_gpt.c standard +arm/freescale/imx/imx_gpt.c optional imx_gpt arm/freescale/imx/imx_gpio.c optional gpio arm/freescale/imx/imx_i2c.c optional fsliic arm/freescale/imx/imx6_sdma.c optional sdma arm/freescale/imx/imx6_audmux.c optional sound arm/freescale/imx/imx6_ssi.c optional sound dev/hdmi/hdmi_if.m optional hdmi dev/hdmi/dwc_hdmi.c optional hdmi arm/freescale/imx/imx6_hdmi.c optional hdmi arm/freescale/imx/imx6_ipu.c optional vt # # Optional devices. # dev/sdhci/fsl_sdhci.c optional sdhci arm/freescale/imx/imx_wdog.c optional imxwdt dev/ffec/if_ffec.c optional ffec dev/uart/uart_dev_imx.c optional uart dev/usb/controller/ehci_imx.c optional ehci arm/freescale/imx/imx6_usbphy.c optional ehci # # Low-level serial console for debugging early kernel startup. # #arm/freescale/imx/imx_console.c standard # # Not ready yet... # #arm/freescale/imx/imx51_ipuv3.c optional sc # SDMA firmware sdma_fw.c optional sdma_fw \ compile-with "${AWK} -f $S/tools/fw_stub.awk sdma-imx6q-to1.bin:sdma_fw -msdma -c${.TARGET}" \ no-implicit-rule before-depend local \ clean "sdma_fw.c" sdma-imx6q-to1.fwo optional sdma_fw \ dependency "sdma-imx6q-to1.bin" \ compile-with "${LD} -b binary -d -warn-common -r -d -o ${.TARGET} sdma-imx6q-to1.bin" \ no-implicit-rule \ clean "sdma-imx6q-to1.fwo" sdma-imx6q-to1.bin optional sdma_fw \ dependency "$S/contrib/dev/imx/sdma-imx6q-to1.bin.uu" \ compile-with "uudecode < $S/contrib/dev/imx/sdma-imx6q-to1.bin.uu" \ no-obj no-implicit-rule \ clean "sdma-imx6q-to1.bin" Index: head/sys/arm/freescale/imx/imx6_ccm.c =================================================================== --- head/sys/arm/freescale/imx/imx6_ccm.c (revision 320075) +++ head/sys/arm/freescale/imx/imx6_ccm.c (revision 320076) @@ -1,421 +1,421 @@ /*- * Copyright (c) 2013 Ian Lepore * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 __FBSDID("$FreeBSD$"); /* * Clocks and power control driver for Freescale i.MX6 family of SoCs. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef CCGR_CLK_MODE_ALWAYS #define CCGR_CLK_MODE_OFF 0 #define CCGR_CLK_MODE_RUNMODE 1 #define CCGR_CLK_MODE_ALWAYS 3 #endif struct ccm_softc { device_t dev; struct resource *mem_res; }; static struct ccm_softc *ccm_sc; static inline uint32_t RD4(struct ccm_softc *sc, bus_size_t off) { return (bus_read_4(sc->mem_res, off)); } static inline void WR4(struct ccm_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->mem_res, off, val); } /* * Until we have a fully functional ccm driver which implements the fdt_clock * interface, use the age-old workaround of unconditionally enabling the clocks * for devices we might need to use. The SoC defaults to most clocks enabled, * but the rom boot code and u-boot disable a few of them. We turn on only * what's needed to run the chip plus devices we have drivers for, and turn off * devices we don't yet have drivers for. (Note that USB is not turned on here * because that is one we do when the driver asks for it.) */ static void ccm_init_gates(struct ccm_softc *sc) { uint32_t reg; /* ahpbdma, aipstz 1 & 2 buses */ reg = CCGR0_AIPS_TZ1 | CCGR0_AIPS_TZ2 | CCGR0_ABPHDMA; WR4(sc, CCM_CCGR0, reg); - /* gpt, enet */ - reg = CCGR1_ENET | CCGR1_GPT; + /* enet, epit, gpt */ + reg = CCGR1_ENET | CCGR1_EPIT1 | CCGR1_GPT; WR4(sc, CCM_CCGR1, reg); /* ipmux & ipsync (bridges), iomux, i2c */ reg = CCGR2_I2C1 | CCGR2_I2C2 | CCGR2_I2C3 | CCGR2_IIM | CCGR2_IOMUX_IPT | CCGR2_IPMUX1 | CCGR2_IPMUX2 | CCGR2_IPMUX3 | CCGR2_IPSYNC_IP2APB_TZASC1 | CCGR2_IPSYNC_IP2APB_TZASC2 | CCGR2_IPSYNC_VDOA; WR4(sc, CCM_CCGR2, reg); /* DDR memory controller */ reg = CCGR3_OCRAM | CCGR3_MMDC_CORE_IPG | CCGR3_MMDC_CORE_ACLK_FAST | CCGR3_CG11 | CCGR3_CG13; WR4(sc, CCM_CCGR3, reg); /* pl301 bus crossbar */ reg = CCGR4_PL301_MX6QFAST1_S133 | CCGR4_PL301_MX6QPER1_BCH | CCGR4_PL301_MX6QPER2_MAIN; WR4(sc, CCM_CCGR4, reg); /* uarts, ssi, sdma */ reg = CCGR5_SDMA | CCGR5_SSI1 | CCGR5_SSI2 | CCGR5_SSI3 | CCGR5_UART | CCGR5_UART_SERIAL; WR4(sc, CCM_CCGR5, reg); /* usdhc 1-4, usboh3 */ reg = CCGR6_USBOH3 | CCGR6_USDHC1 | CCGR6_USDHC2 | CCGR6_USDHC3 | CCGR6_USDHC4; WR4(sc, CCM_CCGR6, reg); } static int ccm_detach(device_t dev) { struct ccm_softc *sc; sc = device_get_softc(dev); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (0); } static int ccm_attach(device_t dev) { struct ccm_softc *sc; int err, rid; uint32_t reg; sc = device_get_softc(dev); err = 0; /* Allocate bus_space resources. */ rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); err = ENXIO; goto out; } ccm_sc = sc; /* * Configure the Low Power Mode setting to leave the ARM core power on * when a WFI instruction is executed. This lets the MPCore timers and * GIC continue to run, which is helpful when the only thing that can * wake you up is an MPCore Private Timer interrupt delivered via GIC. * * XXX Based on the docs, setting CCM_CGPR_INT_MEM_CLK_LPM shouldn't be * required when the LPM bits are set to LPM_RUN. But experimentally * I've experienced a fairly rare lockup when not setting it. I was * unable to prove conclusively that the lockup was related to power * management or that this definitively fixes it. Revisit this. */ reg = RD4(sc, CCM_CGPR); reg |= CCM_CGPR_INT_MEM_CLK_LPM; WR4(sc, CCM_CGPR, reg); reg = RD4(sc, CCM_CLPCR); reg = (reg & ~CCM_CLPCR_LPM_MASK) | CCM_CLPCR_LPM_RUN; WR4(sc, CCM_CLPCR, reg); ccm_init_gates(sc); err = 0; out: if (err != 0) ccm_detach(dev); return (err); } static int ccm_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "fsl,imx6q-ccm") == 0) return (ENXIO); device_set_desc(dev, "Freescale i.MX6 Clock Control Module"); return (BUS_PROBE_DEFAULT); } void imx_ccm_ssi_configure(device_t _ssidev) { struct ccm_softc *sc; uint32_t reg; sc = ccm_sc; /* * Select PLL4 (Audio PLL) clock multiplexer as source. * PLL output frequency = Fref * (DIV_SELECT + NUM/DENOM). */ reg = RD4(sc, CCM_CSCMR1); reg &= ~(SSI_CLK_SEL_M << SSI1_CLK_SEL_S); reg |= (SSI_CLK_SEL_PLL4 << SSI1_CLK_SEL_S); reg &= ~(SSI_CLK_SEL_M << SSI2_CLK_SEL_S); reg |= (SSI_CLK_SEL_PLL4 << SSI2_CLK_SEL_S); reg &= ~(SSI_CLK_SEL_M << SSI3_CLK_SEL_S); reg |= (SSI_CLK_SEL_PLL4 << SSI3_CLK_SEL_S); WR4(sc, CCM_CSCMR1, reg); /* * Ensure we have set hardware-default values * for pre and post dividers. */ /* SSI1 and SSI3 */ reg = RD4(sc, CCM_CS1CDR); /* Divide by 2 */ reg &= ~(SSI_CLK_PODF_MASK << SSI1_CLK_PODF_SHIFT); reg &= ~(SSI_CLK_PODF_MASK << SSI3_CLK_PODF_SHIFT); reg |= (0x1 << SSI1_CLK_PODF_SHIFT); reg |= (0x1 << SSI3_CLK_PODF_SHIFT); /* Divide by 4 */ reg &= ~(SSI_CLK_PRED_MASK << SSI1_CLK_PRED_SHIFT); reg &= ~(SSI_CLK_PRED_MASK << SSI3_CLK_PRED_SHIFT); reg |= (0x3 << SSI1_CLK_PRED_SHIFT); reg |= (0x3 << SSI3_CLK_PRED_SHIFT); WR4(sc, CCM_CS1CDR, reg); /* SSI2 */ reg = RD4(sc, CCM_CS2CDR); /* Divide by 2 */ reg &= ~(SSI_CLK_PODF_MASK << SSI2_CLK_PODF_SHIFT); reg |= (0x1 << SSI2_CLK_PODF_SHIFT); /* Divide by 4 */ reg &= ~(SSI_CLK_PRED_MASK << SSI2_CLK_PRED_SHIFT); reg |= (0x3 << SSI2_CLK_PRED_SHIFT); WR4(sc, CCM_CS2CDR, reg); } void imx_ccm_usb_enable(device_t _usbdev) { /* * For imx6, the USBOH3 clock gate is bits 0-1 of CCGR6, so no need for * shifting and masking here, just set the low-order two bits to ALWAYS. */ WR4(ccm_sc, CCM_CCGR6, RD4(ccm_sc, CCM_CCGR6) | CCGR_CLK_MODE_ALWAYS); } void imx_ccm_usbphy_enable(device_t _phydev) { /* * XXX Which unit? * Right now it's not clear how to figure from fdt data which phy unit * we're supposed to operate on. Until this is worked out, just enable * both PHYs. */ #if 0 int phy_num, regoff; phy_num = 0; /* XXX */ switch (phy_num) { case 0: regoff = 0; break; case 1: regoff = 0x10; break; default: device_printf(ccm_sc->dev, "Bad PHY number %u,\n", phy_num); return; } imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + regoff, IMX6_ANALOG_CCM_PLL_USB_ENABLE | IMX6_ANALOG_CCM_PLL_USB_POWER | IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS); #else imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + 0, IMX6_ANALOG_CCM_PLL_USB_ENABLE | IMX6_ANALOG_CCM_PLL_USB_POWER | IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS); imx6_anatop_write_4(IMX6_ANALOG_CCM_PLL_USB1 + 0x10, IMX6_ANALOG_CCM_PLL_USB_ENABLE | IMX6_ANALOG_CCM_PLL_USB_POWER | IMX6_ANALOG_CCM_PLL_USB_EN_USB_CLKS); #endif } uint32_t imx_ccm_ipg_hz(void) { return (66000000); } uint32_t imx_ccm_perclk_hz(void) { return (66000000); } uint32_t imx_ccm_sdhci_hz(void) { return (200000000); } uint32_t imx_ccm_uart_hz(void) { return (80000000); } uint32_t imx_ccm_ahb_hz(void) { return (132000000); } void imx_ccm_ipu_enable(int ipu) { struct ccm_softc *sc; uint32_t reg; sc = ccm_sc; reg = RD4(sc, CCM_CCGR3); if (ipu == 1) reg |= CCGR3_IPU1_IPU | CCGR3_IPU1_DI0; else reg |= CCGR3_IPU2_IPU | CCGR3_IPU2_DI0; WR4(sc, CCM_CCGR3, reg); } void imx_ccm_hdmi_enable(void) { struct ccm_softc *sc; uint32_t reg; sc = ccm_sc; reg = RD4(sc, CCM_CCGR2); reg |= CCGR2_HDMI_TX | CCGR2_HDMI_TX_ISFR; WR4(sc, CCM_CCGR2, reg); /* Set HDMI clock to 280MHz */ reg = RD4(sc, CCM_CHSCCDR); reg &= ~(CHSCCDR_IPU1_DI0_PRE_CLK_SEL_MASK | CHSCCDR_IPU1_DI0_PODF_MASK | CHSCCDR_IPU1_DI0_CLK_SEL_MASK); reg |= (CHSCCDR_PODF_DIVIDE_BY_3 << CHSCCDR_IPU1_DI0_PODF_SHIFT); reg |= (CHSCCDR_IPU_PRE_CLK_540M_PFD << CHSCCDR_IPU1_DI0_PRE_CLK_SEL_SHIFT); WR4(sc, CCM_CHSCCDR, reg); reg |= (CHSCCDR_CLK_SEL_LDB_DI0 << CHSCCDR_IPU1_DI0_CLK_SEL_SHIFT); WR4(sc, CCM_CHSCCDR, reg); } uint32_t imx_ccm_get_cacrr(void) { return (RD4(ccm_sc, CCM_CACCR)); } void imx_ccm_set_cacrr(uint32_t divisor) { WR4(ccm_sc, CCM_CACCR, divisor); } static device_method_t ccm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ccm_probe), DEVMETHOD(device_attach, ccm_attach), DEVMETHOD(device_detach, ccm_detach), DEVMETHOD_END }; static driver_t ccm_driver = { "ccm", ccm_methods, sizeof(struct ccm_softc) }; static devclass_t ccm_devclass; EARLY_DRIVER_MODULE(ccm, simplebus, ccm_driver, ccm_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_EARLY); Index: head/sys/arm/freescale/imx/imx6_ccmreg.h =================================================================== --- head/sys/arm/freescale/imx/imx6_ccmreg.h (revision 320075) +++ head/sys/arm/freescale/imx/imx6_ccmreg.h (revision 320076) @@ -1,130 +1,133 @@ /*- * Copyright (c) 2013 Ian Lepore * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #ifndef IMX6_CCMREG_H #define IMX6_CCMREG_H #define CCM_CACCR 0x010 #define CCM_CBCDR 0x014 #define CBCDR_MMDC_CH1_AXI_PODF_SHIFT 3 #define CBCDR_MMDC_CH1_AXI_PODF_MASK (7 << 3) #define CCM_CSCMR1 0x01C #define SSI1_CLK_SEL_S 10 #define SSI2_CLK_SEL_S 12 #define SSI3_CLK_SEL_S 14 #define SSI_CLK_SEL_M 0x3 #define SSI_CLK_SEL_508_PFD 0 #define SSI_CLK_SEL_454_PFD 1 #define SSI_CLK_SEL_PLL4 2 #define CCM_CSCMR2 0x020 #define CSCMR2_LDB_DI0_IPU_DIV_SHIFT 10 #define CCM_CS1CDR 0x028 #define SSI1_CLK_PODF_SHIFT 0 #define SSI1_CLK_PRED_SHIFT 6 #define SSI3_CLK_PODF_SHIFT 16 #define SSI3_CLK_PRED_SHIFT 22 #define SSI_CLK_PODF_MASK 0x3f #define SSI_CLK_PRED_MASK 0x7 #define CCM_CS2CDR 0x02C #define SSI2_CLK_PODF_SHIFT 0 #define SSI2_CLK_PRED_SHIFT 6 #define LDB_DI0_CLK_SEL_SHIFT 9 #define LDB_DI0_CLK_SEL_MASK (3 << LDB_DI0_CLK_SEL_SHIFT) #define CCM_CHSCCDR 0x034 #define CHSCCDR_IPU1_DI0_PRE_CLK_SEL_MASK (0x7 << 6) #define CHSCCDR_IPU1_DI0_PRE_CLK_SEL_SHIFT 6 #define CHSCCDR_IPU1_DI0_PODF_MASK (0x7 << 3) #define CHSCCDR_IPU1_DI0_PODF_SHIFT 3 #define CHSCCDR_IPU1_DI0_CLK_SEL_MASK (0x7) #define CHSCCDR_IPU1_DI0_CLK_SEL_SHIFT 0 #define CHSCCDR_CLK_SEL_LDB_DI0 3 #define CHSCCDR_PODF_DIVIDE_BY_3 2 #define CHSCCDR_IPU_PRE_CLK_540M_PFD 5 #define CCM_CSCDR2 0x038 #define CCM_CLPCR 0x054 #define CCM_CLPCR_LPM_MASK 0x03 #define CCM_CLPCR_LPM_RUN 0x00 #define CCM_CLPCR_LPM_WAIT 0x01 #define CCM_CLPCR_LPM_STOP 0x02 #define CCM_CGPR 0x064 #define CCM_CGPR_INT_MEM_CLK_LPM (1 << 17) #define CCM_CCGR0 0x068 #define CCGR0_AIPS_TZ1 (0x3 << 0) #define CCGR0_AIPS_TZ2 (0x3 << 2) #define CCGR0_ABPHDMA (0x3 << 4) #define CCM_CCGR1 0x06C #define CCGR1_ENET (0x3 << 10) +#define CCGR1_EPIT1 (0x3 << 12) +#define CCGR1_EPIT2 (0x3 << 14) #define CCGR1_GPT (0x3 << 20) +#define CCGR1_GPT_SERIAL (0x3 << 22) #define CCM_CCGR2 0x070 #define CCGR2_HDMI_TX (0x3 << 0) #define CCGR2_HDMI_TX_ISFR (0x3 << 4) #define CCGR2_I2C1 (0x3 << 6) #define CCGR2_I2C2 (0x3 << 8) #define CCGR2_I2C3 (0x3 << 10) #define CCGR2_IIM (0x3 << 12) #define CCGR2_IOMUX_IPT (0x3 << 14) #define CCGR2_IPMUX1 (0x3 << 16) #define CCGR2_IPMUX2 (0x3 << 18) #define CCGR2_IPMUX3 (0x3 << 20) #define CCGR2_IPSYNC_IP2APB_TZASC1 (0x3 << 22) #define CCGR2_IPSYNC_IP2APB_TZASC2 (0x3 << 24) #define CCGR2_IPSYNC_VDOA (0x3 << 26) #define CCM_CCGR3 0x074 #define CCGR3_IPU1_IPU (0x3 << 0) #define CCGR3_IPU1_DI0 (0x3 << 2) #define CCGR3_IPU1_DI1 (0x3 << 4) #define CCGR3_IPU2_IPU (0x3 << 6) #define CCGR3_IPU2_DI0 (0x3 << 8) #define CCGR3_IPU2_DI1 (0x3 << 10) #define CCGR3_LDB_DI0 (0x3 << 12) #define CCGR3_LDB_DI1 (0x3 << 14) #define CCGR3_MMDC_CORE_ACLK_FAST (0x3 << 20) #define CCGR3_CG11 (0x3 << 22) #define CCGR3_MMDC_CORE_IPG (0x3 << 24) #define CCGR3_CG13 (0x3 << 26) #define CCGR3_OCRAM (0x3 << 28) #define CCM_CCGR4 0x078 #define CCGR4_PL301_MX6QFAST1_S133 (0x3 << 8) #define CCGR4_PL301_MX6QPER1_BCH (0x3 << 12) #define CCGR4_PL301_MX6QPER2_MAIN (0x3 << 14) #define CCM_CCGR5 0x07C #define CCGR5_SDMA (0x3 << 6) #define CCGR5_SSI1 (0x3 << 18) #define CCGR5_SSI2 (0x3 << 20) #define CCGR5_SSI3 (0x3 << 22) #define CCGR5_UART (0x3 << 24) #define CCGR5_UART_SERIAL (0x3 << 26) #define CCM_CCGR6 0x080 #define CCGR6_USBOH3 (0x3 << 0) #define CCGR6_USDHC1 (0x3 << 2) #define CCGR6_USDHC2 (0x3 << 4) #define CCGR6_USDHC3 (0x3 << 6) #define CCGR6_USDHC4 (0x3 << 8) #define CCM_CMEOR 0x088 #endif Index: head/sys/arm/freescale/imx/imx_epit.c =================================================================== --- head/sys/arm/freescale/imx/imx_epit.c (nonexistent) +++ head/sys/arm/freescale/imx/imx_epit.c (revision 320076) @@ -0,0 +1,528 @@ +/*- + * Copyright (c) 2017 Ian Lepore + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 +__FBSDID("$FreeBSD$"); + +/* + * Driver for imx Enhanced Programmable Interval Timer, a simple free-running + * counter device that can be used as the system timecounter. On imx5 a second + * instance of the device is used as the system eventtimer. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define EPIT_CR 0x00 /* Control register */ +#define EPIT_CR_CLKSRC_SHIFT 24 +#define EPIT_CR_CLKSRC_OFF 0 +#define EPIT_CR_CLKSRC_IPG 1 +#define EPIT_CR_CLKSRC_HFCLK 2 +#define EPIT_CR_CLKSRC_LFCLK 3 +#define EPIT_CR_STOPEN (1u << 21) +#define EPIT_CR_WAITEN (1u << 19) +#define EPIT_CR_DBGEN (1u << 18) +#define EPIT_CR_IOVW (1u << 17) +#define EPIT_CR_SWR (1u << 16) +#define EPIT_CR_RLD (1u << 3) +#define EPIT_CR_OCIEN (1u << 2) +#define EPIT_CR_ENMOD (1u << 1) +#define EPIT_CR_EN (1u << 0) + +#define EPIT_SR 0x04 /* Status register */ +#define EPIT_SR_OCIF (1u << 0) + +#define EPIT_LR 0x08 /* Load register */ +#define EPIT_CMPR 0x0c /* Compare register */ +#define EPIT_CNR 0x10 /* Counter register */ + +/* + * Define event timer limits. + * + * In theory our minimum period is 1 tick, because to setup a oneshot we don't + * need a read-modify-write sequence to calculate and set a compare register + * value while the counter is running. In practice the waveform diagrams in the + * manual make it appear that a setting of 1 might cause it to miss the event, + * so I'm setting the lower limit to 2 ticks. + */ +#define ET_MIN_TICKS 2 +#define ET_MAX_TICKS 0xfffffffe + +static u_int epit_tc_get_timecount(struct timecounter *tc); + +struct epit_softc { + device_t dev; + struct resource * memres; + struct resource * intres; + void * inthandle; + uint32_t clkfreq; + uint32_t ctlreg; + uint32_t period; + struct timecounter tc; + struct eventtimer et; + bool oneshot; +}; + +#ifndef MULTIDELAY +/* Global softc pointer for use in DELAY(). */ +static struct epit_softc *epit_sc; +#endif + +/* + * Probe data. For some reason, the standard linux dts files don't have + * compatible properties on the epit devices (other properties are missing too, + * like clocks, but we don't care as much about that). So our probe routine + * uses the name of the node (must contain "epit") and the address of the + * registers as identifying marks. + */ +static const uint32_t imx51_epit_ioaddr[2] = {0x73fac000, 0x73fb0000}; +static const uint32_t imx53_epit_ioaddr[2] = {0x53fac000, 0x53fb0000}; +static const uint32_t imx6_epit_ioaddr[2] = {0x020d0000, 0x020d4000}; + +/* ocd_data is number of units to instantiate on the platform */ +static struct ofw_compat_data compat_data[] = { + {"fsl,imx6ul-epit", 1}, + {"fsl,imx6sx-epit", 1}, + {"fsl,imx6q-epit", 1}, + {"fsl,imx6dl-epit", 1}, + {"fsl,imx53-epit", 2}, + {"fsl,imx51-epit", 2}, + {"fsl,imx31-epit", 2}, + {"fsl,imx27-epit", 2}, + {"fsl,imx25-epit", 2}, + {NULL, 0} +}; + +static inline uint32_t +RD4(struct epit_softc *sc, bus_size_t offset) +{ + + return (bus_read_4(sc->memres, offset)); +} + +static inline void +WR4(struct epit_softc *sc, bus_size_t offset, uint32_t value) +{ + + bus_write_4(sc->memres, offset, value); +} + +static inline void +WR4B(struct epit_softc *sc, bus_size_t offset, uint32_t value) +{ + + bus_write_4(sc->memres, offset, value); + bus_barrier(sc->memres, offset, 4, BUS_SPACE_BARRIER_WRITE); +} + +static u_int +epit_read_counter(struct epit_softc *sc) +{ + + /* + * Hardware is a downcounter, adjust to look like it counts up for use + * with timecounter and DELAY. + */ + return (0xffffffff - RD4(sc, EPIT_CNR)); +} + +static void +epit_do_delay(int usec, void *arg) +{ + struct epit_softc *sc = arg; + uint64_t curcnt, endcnt, startcnt, ticks; + + /* + * Calculate the tick count with 64-bit values so that it works for any + * clock frequency. Loop until the hardware count reaches start+ticks. + * If the 32-bit hardware count rolls over while we're looping, just + * manually do a carry into the high bits after each read; don't worry + * that doing this on each loop iteration is inefficient -- we're trying + * to waste time here. + */ + ticks = 1 + ((uint64_t)usec * sc->clkfreq) / 1000000; + curcnt = startcnt = epit_read_counter(sc); + endcnt = startcnt + ticks; + while (curcnt < endcnt) { + curcnt = epit_read_counter(sc); + if (curcnt < startcnt) + curcnt += 1ULL << 32; + } +} + +static u_int +epit_tc_get_timecount(struct timecounter *tc) +{ + + return (epit_read_counter(tc->tc_priv)); +} + +static int +epit_tc_attach(struct epit_softc *sc) +{ + + /* When the counter hits zero, reload with 0xffffffff. Start it. */ + WR4(sc, EPIT_LR, 0xffffffff); + WR4(sc, EPIT_CR, sc->ctlreg | EPIT_CR_EN); + + /* Register as a timecounter. */ + sc->tc.tc_name = "EPIT"; + sc->tc.tc_quality = 1000; + sc->tc.tc_frequency = sc->clkfreq; + sc->tc.tc_counter_mask = 0xffffffff; + sc->tc.tc_get_timecount = epit_tc_get_timecount; + sc->tc.tc_priv = sc; + tc_init(&sc->tc); + + /* We are the DELAY() implementation. */ +#ifdef MULTIDELAY + arm_set_delay(epit_do_delay, sc); +#else + epit_sc = sc; +#endif + return (0); +} + +static int +epit_et_start(struct eventtimer *et, sbintime_t first, sbintime_t period) +{ + struct epit_softc *sc; + uint32_t ticks; + + sc = (struct epit_softc *)et->et_priv; + + /* + * Disable the timer and clear any pending status. The timer may be + * running or may have just expired if we're called to reschedule the + * next event before the previous event time arrives. + */ + WR4(sc, EPIT_CR, sc->ctlreg); + WR4(sc, EPIT_SR, EPIT_SR_OCIF); + if (period != 0) { + sc->oneshot = false; + ticks = ((uint32_t)et->et_frequency * period) >> 32; + } else if (first != 0) { + sc->oneshot = true; + ticks = ((uint32_t)et->et_frequency * first) >> 32; + } else { + return (EINVAL); + } + + /* Set the countdown load register and start the timer. */ + WR4(sc, EPIT_LR, ticks); + WR4B(sc, EPIT_CR, sc->ctlreg | EPIT_CR_EN); + + return (0); +} + +static int +epit_et_stop(struct eventtimer *et) +{ + struct epit_softc *sc; + + sc = (struct epit_softc *)et->et_priv; + + /* Disable the timer and clear any pending status. */ + WR4(sc, EPIT_CR, sc->ctlreg); + WR4B(sc, EPIT_SR, EPIT_SR_OCIF); + + return (0); +} + +static int +epit_intr(void *arg) +{ + struct epit_softc *sc; + uint32_t status; + + sc = arg; + + /* + * Disable a one-shot timer until a new event is scheduled so that the + * counter doesn't wrap and fire again. Do this before clearing the + * status since a short period would make it fire again really soon. + * + * Clear interrupt status before invoking event callbacks. The callback + * often sets up a new one-shot timer event and if the interval is short + * enough it can fire before we get out of this function. If we cleared + * at the bottom we'd miss the interrupt and hang until the clock wraps. + */ + if (sc->oneshot) + WR4(sc, EPIT_CR, sc->ctlreg); + + status = RD4(sc, EPIT_SR); + WR4B(sc, EPIT_SR, status); + + if ((status & EPIT_SR_OCIF) == 0) + return (FILTER_STRAY); + + if (sc->et.et_active) + sc->et.et_event_cb(&sc->et, sc->et.et_arg); + + return (FILTER_HANDLED); +} + +static int +epit_et_attach(struct epit_softc *sc) +{ + int err, rid; + + rid = 0; + sc->intres = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->intres == NULL) { + device_printf(sc->dev, "could not allocate interrupt\n"); + return (ENXIO); + } + + err = bus_setup_intr(sc->dev, sc->intres, INTR_TYPE_CLK | INTR_MPSAFE, + epit_intr, NULL, sc, &sc->inthandle); + if (err != 0) { + device_printf(sc->dev, "unable to setup the irq handler\n"); + return (err); + } + + /* To be an eventtimer, we need interrupts enabled. */ + sc->ctlreg |= EPIT_CR_OCIEN; + + /* Register as an eventtimer. */ + sc->et.et_name = "EPIT"; + sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERIODIC; + sc->et.et_quality = 1000; + sc->et.et_frequency = sc->clkfreq; + sc->et.et_min_period = ((uint64_t)ET_MIN_TICKS << 32) / sc->clkfreq; + sc->et.et_max_period = ((uint64_t)ET_MAX_TICKS << 32) / sc->clkfreq; + sc->et.et_start = epit_et_start; + sc->et.et_stop = epit_et_stop; + sc->et.et_priv = sc; + et_register(&sc->et); + + return (0); +} + +static int +epit_probe(device_t dev) +{ + struct resource *memres; + rman_res_t ioaddr; + int num_units, rid, unit; + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + /* + * The FDT data for imx5 and imx6 EPIT hardware is missing or broken, + * but it may get fixed some day, so first just do a normal check. We + * return success if the compatible string matches and we haven't + * already instantiated the number of units needed on this platform. + */ + unit = device_get_unit(dev); + num_units = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + if (unit < num_units) { + device_set_desc(dev, "i.MX EPIT timer"); + return (BUS_PROBE_DEFAULT); + } + + /* + * No compat string match, but for imx6 all the data we need is in the + * node except the compat string, so do our own compatibility check + * using the device name of the node and the register block address. + */ + if (strstr(ofw_bus_get_name(dev), "epit") == NULL) + return (ENXIO); + + rid = 0; + memres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_UNMAPPED); + if (memres == NULL) + return (ENXIO); + ioaddr = rman_get_start(memres); + bus_free_resource(dev, SYS_RES_MEMORY, memres); + + if (imx_soc_family() == 6) { + if (unit > 0) + return (ENXIO); + if (ioaddr != imx6_epit_ioaddr[unit]) + return (ENXIO); + } else { + if (unit > 1) + return (ENXIO); + switch (imx_soc_type()) { + case IMXSOC_51: + if (ioaddr != imx51_epit_ioaddr[unit]) + return (ENXIO); + break; + case IMXSOC_53: + if (ioaddr != imx53_epit_ioaddr[unit]) + return (ENXIO); + break; + default: + return (ENXIO); + } + /* + * XXX Right now we have no way to handle the fact that the + * entire EPIT node is missing, which means no interrupt data. + */ + return (ENXIO); + } + + device_set_desc(dev, "i.MX EPIT timer"); + return (BUS_PROBE_DEFAULT); +} + +static int +epit_attach(device_t dev) +{ + struct epit_softc *sc; + int err, rid; + uint32_t clksrc; + + sc = device_get_softc(dev); + sc->dev = dev; + + rid = 0; + sc->memres = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->memres == NULL) { + device_printf(sc->dev, "could not allocate registers\n"); + return (ENXIO); + } + + /* + * For now, use ipg (66 MHz). Some day we should get this from fdt. + */ + clksrc = EPIT_CR_CLKSRC_IPG; + + switch (clksrc) { + default: + device_printf(dev, + "Unsupported clock source '%d', using IPG\n", clksrc); + /* FALLTHROUGH */ + case EPIT_CR_CLKSRC_IPG: + sc->clkfreq = imx_ccm_ipg_hz(); + break; + case EPIT_CR_CLKSRC_HFCLK: + sc->clkfreq = imx_ccm_perclk_hz(); + break; + case EPIT_CR_CLKSRC_LFCLK: + sc->clkfreq = 32768; + break; + } + + /* + * Init: stop operations and clear all options, then set up options and + * clock source, then do a soft-reset and wait for it to complete. + */ + WR4(sc, EPIT_CR, 0); + + sc->ctlreg = + (clksrc << EPIT_CR_CLKSRC_SHIFT) | /* Use selected clock */ + EPIT_CR_ENMOD | /* Reload counter on enable */ + EPIT_CR_RLD | /* Reload counter from LR */ + EPIT_CR_STOPEN | /* Run in STOP mode */ + EPIT_CR_WAITEN | /* Run in WAIT mode */ + EPIT_CR_DBGEN; /* Run in DEBUG mode */ + + WR4B(sc, EPIT_CR, sc->ctlreg | EPIT_CR_SWR); + while (RD4(sc, EPIT_CR) & EPIT_CR_SWR) + continue; + + /* + * Unit 0 is the timecounter, 1 (if instantiated) is the eventtimer. + */ + if (device_get_unit(sc->dev) == 0) + err = epit_tc_attach(sc); + else + err = epit_et_attach(sc); + + return (err); +} + +static device_method_t epit_methods[] = { + DEVMETHOD(device_probe, epit_probe), + DEVMETHOD(device_attach, epit_attach), + + DEVMETHOD_END +}; + +static driver_t epit_driver = { + "imx_epit", + epit_methods, + sizeof(struct epit_softc), +}; + +static devclass_t epit_devclass; + +EARLY_DRIVER_MODULE(imx_epit, simplebus, epit_driver, epit_devclass, 0, + 0, BUS_PASS_TIMER); + +#ifndef MULTIDELAY + +/* + * Hand-calibrated delay-loop counter. This was calibrated on an i.MX6 running + * at 792mhz. It will delay a bit too long on slower processors -- that's + * better than not delaying long enough. In practice this is unlikely to get + * used much since the clock driver is one of the first to start up, and once + * we're attached the delay loop switches to using the timer hardware. + */ +static const int epit_delay_count = 78; + +void +DELAY(int usec) +{ + uint64_t ticks; + + /* If the timer hardware is not accessible, just use a loop. */ + if (epit_sc == NULL) { + while (usec-- > 0) + for (ticks = 0; ticks < epit_delay_count; ++ticks) + cpufunc_nullop(); + return; + } else { + epit_do_delay(usec, epit_sc); + } +} + +#endif Property changes on: head/sys/arm/freescale/imx/imx_epit.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property