Index: sys/arm/freescale/imx/files.imx6 =================================================================== --- sys/arm/freescale/imx/files.imx6 +++ sys/arm/freescale/imx/files.imx6 @@ -23,6 +23,7 @@ arm/freescale/imx/imx6_sdma.c optional sdma arm/freescale/imx/imx6_audmux.c optional sound arm/freescale/imx/imx6_ssi.c optional sound +arm/freescale/imx/imx6_ahci.c optional ahci arm/arm/hdmi_if.m optional hdmi arm/freescale/imx/imx6_hdmi.c optional hdmi Index: sys/arm/freescale/imx/imx6_ahci.c =================================================================== --- /dev/null +++ sys/arm/freescale/imx/imx6_ahci.c @@ -0,0 +1,345 @@ +/*- + * Copyright (c) 2017 Rogiel Sulzbach + * 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$"); + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define SATA_P0PHYCR 0x00000178 +#define SATA_P0PHYCR_CR_READ (1 << 19) +#define SATA_P0PHYCR_CR_WRITE (1 << 18) +#define SATA_P0PHYCR_CR_CAP_DATA (1 << 17) +#define SATA_P0PHYCR_CR_CAP_ADDR (1 << 16) +#define SATA_P0PHYCR_CR_DATA_IN(v) ((v) & 0xffff) +#define SATA_P0PHYSR 0x0000017c +#define SATA_P0PHYSR_CR_ACK (1 << 18) +#define SATA_P0PHYSR_CR_DATA_OUT(v) ((v) & 0xffff) + +#define SATA_TIMER1MS 0x000000e0 + +/* phy registers */ +#define SATA_PHY_CLOCK_RESET 0x7f3f +#define SATA_PHY_CLOCK_RESET_RST (1 << 0) + +#define SATA_PHY_LANE0_OUT_STAT 0x2003 +#define SATA_PHY_LANE0_OUT_STAT_RX_PLL_STATE (1 << 1) + +static int +imx6_ahci_phy_ctrl(struct ahci_controller* sc, uint32_t bitmask, int on) +{ + uint32_t v; + int timeout; + + v = ATA_INL(sc->r_mem, SATA_P0PHYCR); + if(on) { + v |= bitmask; + } else { + v &= ~bitmask; + } + ATA_OUTL(sc->r_mem, SATA_P0PHYCR, v); + + for(timeout = 5000; timeout > 0; --timeout) { + v = ATA_INL(sc->r_mem, SATA_P0PHYSR); + if(!!(v & SATA_P0PHYSR_CR_ACK) == !!on) { + break; + } + DELAY(100); + } + + if(timeout > 0) { + return 0; + } + + return ETIMEDOUT; +} + +static int +imx6_ahci_phy_addr(struct ahci_controller* sc, uint32_t addr) +{ + int error; + DELAY(100); + + ATA_OUTL(sc->r_mem, SATA_P0PHYCR, addr); + + error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_ADDR, 1); + if(error != 0) { + if(bootverbose) { + device_printf(sc->dev, "%s: timeout on SATA_P0PHYCR_CR_CAP_ADDR=1\n", __FUNCTION__); + } + return error; + } + + error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_ADDR, 0); + if(error != 0) { + if(bootverbose) { + device_printf(sc->dev, "%s: timeout on SATA_P0PHYCR_CR_CAP_ADDR=0\n", __FUNCTION__); + } + return error; + } + + return 0; +} + +static int +imx6_ahci_phy_write(struct ahci_controller* sc, uint32_t addr, + uint16_t data) +{ + int error; + + error = imx6_ahci_phy_addr(sc, addr); + if(error != 0) { + if(bootverbose) { device_printf(sc->dev, "%s: error on imx6_ahci_phy_addr\n", __FUNCTION__); } + return error; + } + + ATA_OUTL(sc->r_mem, SATA_P0PHYCR, data); + + error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_DATA, 1); + if(error != 0) { + if(bootverbose) { + device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_CAP_DATA=1\n", __FUNCTION__); + } + return error; + } + if(imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_DATA, 0) != 0) { + if(bootverbose) { + device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_CAP_DATA=0\n", __FUNCTION__); + } + return error; + } + + if((addr == SATA_PHY_CLOCK_RESET) && data) { + /* we can't check ACK after RESET */ + ATA_OUTL(sc->r_mem, SATA_P0PHYCR, + SATA_P0PHYCR_CR_DATA_IN(data) | SATA_P0PHYCR_CR_WRITE); + return 0; + } + + error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_WRITE, 1); + if(error != 0) { + if(bootverbose) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_WRITE=1\n", __FUNCTION__); } + return error; + } + + error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_WRITE, 0); + if(error != 0) { + if(bootverbose) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_WRITE=0\n", __FUNCTION__); } + return error; + } + + return 0; +} + +static int +imx6_ahci_phy_read(struct ahci_controller* sc, uint32_t addr) +{ + int error; + uint32_t v; + + error = imx6_ahci_phy_addr(sc, addr); + if(error != 0) { + if(bootverbose) { device_printf(sc->dev, "%s: error on imx6_ahci_phy_addr\n", __FUNCTION__); } + return error; + } + + error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_READ, 1); + if(error != 0) { + if(bootverbose) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_READ=1\n", __FUNCTION__); } + return error; + } + + v = ATA_INL(sc->r_mem, SATA_P0PHYSR); + + error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_READ, 0); + if(error != 0) { + if(bootverbose) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_READ=0\n", __FUNCTION__); } + return error; + } + + return SATA_P0PHYSR_CR_DATA_OUT(v); +} + +static int +imx6_ahci_probe(device_t dev) +{ + if(!ofw_bus_is_compatible(dev, "fsl,imx6q-ahci")) { + return (ENXIO); + } + device_set_desc(dev, "i.MX6 Integrated AHCI controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +imx6_ahci_attach(device_t dev) +{ + struct ahci_controller* ctlr; + uint32_t v; + int timeout; + int pllstat; + int error; + + /* + * Init the device PHY + */ + ctlr = device_get_softc(dev); + + error = imx6_ccm_sataphy_enable(); + if(error != 0) { + device_printf(dev, "time out resetting AHCI PHY\n"); + return error; + } + + ctlr->vendorid = 0; + ctlr->deviceid = 0; + ctlr->subvendorid = 0; + ctlr->subdeviceid = 0; + ctlr->numirqs = 1; + ctlr->r_rid = 0; + if(!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &ctlr->r_rid, RF_ACTIVE))) { + return (ENXIO); + } + + v = imx_iomux_gpr_get(IOMUX_GPR13); + v &= ~(IOMUX_GPR13_SATA_PHY_8(7) | + IOMUX_GPR13_SATA_PHY_7(0x1f) | + IOMUX_GPR13_SATA_PHY_6(7) | + IOMUX_GPR13_SATA_SPEED(1) | + IOMUX_GPR13_SATA_PHY_5(1) | + IOMUX_GPR13_SATA_PHY_4(7) | + IOMUX_GPR13_SATA_PHY_3(0xf) | + IOMUX_GPR13_SATA_PHY_2(0x1f) | + IOMUX_GPR13_SATA_PHY_1(1) | + IOMUX_GPR13_SATA_PHY_0(1)); + /* setting */ + v |= IOMUX_GPR13_SATA_PHY_8(5) | /* Rx 3.0db */ + IOMUX_GPR13_SATA_PHY_7(0x12) | /* Rx SATA2m */ + IOMUX_GPR13_SATA_PHY_6(3) | /* Rx DPLL mode */ + IOMUX_GPR13_SATA_SPEED(1) | /* 3.0GHz */ + IOMUX_GPR13_SATA_PHY_5(0) | /* SpreadSpectram */ + IOMUX_GPR13_SATA_PHY_4(4) | /* Tx Attenuation 9/16 */ + IOMUX_GPR13_SATA_PHY_3(0) | /* Tx Boost 0db */ + IOMUX_GPR13_SATA_PHY_2(0x11) | /* Tx Level 1.104V */ + IOMUX_GPR13_SATA_PHY_1(1); /* PLL clock enable */ + imx_iomux_gpr_set(IOMUX_GPR13, v); + + /* phy reset */ + error = imx6_ahci_phy_write(ctlr, SATA_PHY_CLOCK_RESET, + SATA_PHY_CLOCK_RESET_RST); + if(error != 0) { + device_printf(dev, "cannot reset PHY\n"); + goto fail; + } + + for(timeout = 50; timeout > 0; --timeout) { + DELAY(100); + pllstat = imx6_ahci_phy_read(ctlr, SATA_PHY_LANE0_OUT_STAT); + if(pllstat < 0) { + device_printf(dev, "cannot read LANE0 status\n"); + break; + } + if(pllstat & SATA_PHY_LANE0_OUT_STAT_RX_PLL_STATE) { + break; + } + } + if(timeout <= 0) { + device_printf(dev, "time out reading LANE0 status\n"); + error = ETIMEDOUT; + goto fail; + } + + /* Support Staggered Spin-up */ + v = ATA_INL(ctlr->r_mem, AHCI_CAP); + ATA_OUTL(ctlr->r_mem, AHCI_CAP, v | AHCI_CAP_SSS); + + /* Ports Implemented. must set 1 */ + v = ATA_INL(ctlr->r_mem, AHCI_PI); + ATA_OUTL(ctlr->r_mem, AHCI_PI, v | (1 << 0)); + + /* set 1ms-timer = AHB clock / 1000 */ + ATA_OUTL(ctlr->r_mem, SATA_TIMER1MS, + imx_ccm_ahb_hz() / 1000); + + /* + * Note: ahci_attach will release ctlr->r_mem on errors automatically + */ + return (ahci_attach(dev)); + + fail: + bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); + return error; +} + +static int +imx6_ahci_detach(device_t dev) +{ + return (ahci_detach(dev)); +} + +devclass_t ahci_devclass; + +static device_method_t imx6_ahci_ata_methods[] = { + /* device probe, attach and detach methods */ + DEVMETHOD(device_probe, imx6_ahci_probe), + DEVMETHOD(device_attach, imx6_ahci_attach), + DEVMETHOD(device_detach, imx6_ahci_detach), + + /* ahci bus methods */ + DEVMETHOD(bus_print_child, ahci_print_child), + DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), + DEVMETHOD(bus_release_resource, ahci_release_resource), + DEVMETHOD(bus_setup_intr, ahci_setup_intr), + DEVMETHOD(bus_teardown_intr, ahci_teardown_intr), + DEVMETHOD(bus_child_location_str, ahci_child_location_str), + + DEVMETHOD_END +}; + +static driver_t ahci_ata_driver = { + "ahci", + imx6_ahci_ata_methods, + sizeof(struct ahci_controller) +}; + +DRIVER_MODULE(ahci, simplebus, ahci_ata_driver, ahci_devclass, 0, 0); Index: sys/arm/freescale/imx/imx6_ccm.c =================================================================== --- sys/arm/freescale/imx/imx6_ccm.c +++ sys/arm/freescale/imx/imx6_ccm.c @@ -117,7 +117,7 @@ /* uarts, ssi, sdma */ reg = CCGR5_SDMA | CCGR5_SSI1 | CCGR5_SSI2 | CCGR5_SSI3 | - CCGR5_UART | CCGR5_UART_SERIAL; + CCGR5_UART | CCGR5_UART_SERIAL | CCGR5_SATA; WR4(sc, CCM_CCGR5, reg); /* usdhc 1-4, usboh3 */ @@ -314,6 +314,36 @@ #endif } +int +imx6_ccm_sataphy_enable(void) +{ + uint32_t v; + int timeout; + + v = RD4(ccm_sc, CCM_ANALOG_PLL_ENET); + v &= ~CCM_ANALOG_PLL_ENET_POWERDOWN; + WR4(ccm_sc, CCM_ANALOG_PLL_ENET, v); + + for(timeout = 100000; timeout > 0; timeout--) { + if(RD4(ccm_sc, CCM_ANALOG_PLL_ENET) & + CCM_ANALOG_PLL_ENET_LOCK) { + break; + } + } + if(timeout <= 0) { + return ETIMEDOUT; + } + + v |= CCM_ANALOG_PLL_ENET_ENABLE; + v &= ~CCM_ANALOG_PLL_ENET_BYPASS; + WR4(ccm_sc, CCM_ANALOG_PLL_ENET, v); + + v |= CCM_ANALOG_PLL_ENET_ENABLE_100M; + WR4(ccm_sc, CCM_ANALOG_PLL_ENET, v); + + return 0; +} + uint32_t imx_ccm_ipg_hz(void) { Index: sys/arm/freescale/imx/imx6_ccmreg.h =================================================================== --- sys/arm/freescale/imx/imx6_ccmreg.h +++ sys/arm/freescale/imx/imx6_ccmreg.h @@ -113,6 +113,7 @@ #define CCGR4_PL301_MX6QPER1_BCH (0x3 << 12) #define CCGR4_PL301_MX6QPER2_MAIN (0x3 << 14) #define CCM_CCGR5 0x07C +#define CCGR5_SATA (0x3 << 4) #define CCGR5_SDMA (0x3 << 6) #define CCGR5_SSI1 (0x3 << 18) #define CCGR5_SSI2 (0x3 << 20) @@ -127,4 +128,11 @@ #define CCGR6_USDHC4 (0x3 << 8) #define CCM_CMEOR 0x088 +#define CCM_ANALOG_PLL_ENET 0x000040e0 +#define CCM_ANALOG_PLL_ENET_LOCK (1 << 31) +#define CCM_ANALOG_PLL_ENET_ENABLE_100M (1 << 20) /* SATA */ +#define CCM_ANALOG_PLL_ENET_BYPASS (1 << 16) +#define CCM_ANALOG_PLL_ENET_ENABLE (1 << 13) /* Ether */ +#define CCM_ANALOG_PLL_ENET_POWERDOWN (1 << 12) + #endif Index: sys/arm/freescale/imx/imx6_ccmvar.h =================================================================== --- /dev/null +++ sys/arm/freescale/imx/imx6_ccmvar.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2017 Rogiel Sulzbach + * 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. + */ + +#ifndef IMX6_CCMVAR_H +#define IMX6_CCMVAR_H + +int imx6_ccm_sataphy_enable(void); + +#endif Index: sys/arm/freescale/imx/imx_iomuxreg.h =================================================================== --- sys/arm/freescale/imx/imx_iomuxreg.h +++ sys/arm/freescale/imx/imx_iomuxreg.h @@ -29,6 +29,13 @@ #ifndef IMX_IOMUXREG_H #define IMX_IOMUXREG_H +#define IMX_IOMUXREG_LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask)) +#define IMX_IOMUXREG_SHIFTIN(__x, __mask) ((__x) * IMX_IOMUXREG_LOWEST_SET_BIT(__mask)) + +#define IMX_IOMUXREG_BIT(n) (1 << (n)) +#define IMX_IOMUXREG_BITS(__m, __n) \ + ((IMX_IOMUXREG_BIT(MAX((__m), (__n)) + 1) - 1) ^ (IMX_IOMUXREG_BIT(MIN((__m), (__n))) - 1)) + #define IOMUXC_GPR0 0x00 #define IOMUXC_GPR1 0x04 #define IOMUXC_GPR2 0x08 @@ -39,4 +46,16 @@ #define IOMUXC_GPR3_HDMI_IPU2_DI0 (2 << 2) #define IOMUXC_GPR3_HDMI_IPU2_DI1 (3 << 2) +#define IOMUX_GPR13 0x34 +#define IOMUX_GPR13_SATA_PHY_8(n) IMX_IOMUXREG_SHIFTIN(n, IMX_IOMUXREG_BITS(26, 24)) +#define IOMUX_GPR13_SATA_PHY_7(n) IMX_IOMUXREG_SHIFTIN(n, IMX_IOMUXREG_BITS(23, 19)) +#define IOMUX_GPR13_SATA_PHY_6(n) IMX_IOMUXREG_SHIFTIN(n, IMX_IOMUXREG_BITS(18, 16)) +#define IOMUX_GPR13_SATA_SPEED(n) IMX_IOMUXREG_SHIFTIN(n, (1 << 15)) +#define IOMUX_GPR13_SATA_PHY_5(n) IMX_IOMUXREG_SHIFTIN(n, (1 << 14)) +#define IOMUX_GPR13_SATA_PHY_4(n) IMX_IOMUXREG_SHIFTIN(n, IMX_IOMUXREG_BITS(13, 11)) +#define IOMUX_GPR13_SATA_PHY_3(n) IMX_IOMUXREG_SHIFTIN(n, IMX_IOMUXREG_BITS(10, 7)) +#define IOMUX_GPR13_SATA_PHY_2(n) IMX_IOMUXREG_SHIFTIN(n, IMX_IOMUXREG_BITS(6, 2)) +#define IOMUX_GPR13_SATA_PHY_1(n) IMX_IOMUXREG_SHIFTIN(n, (1 << 1)) +#define IOMUX_GPR13_SATA_PHY_0(n) IMX_IOMUXREG_SHIFTIN(n, (1 << 0)) + #endif Index: sys/arm/freescale/imx/std.imx6 =================================================================== --- sys/arm/freescale/imx/std.imx6 +++ sys/arm/freescale/imx/std.imx6 @@ -10,6 +10,7 @@ options IPI_IRQ_END=15 device fdt_pinctrl +device ahci files "../freescale/imx/files.imx6"