diff options
-rw-r--r-- | sys/dev/fdt/sdhc_fdt.c | 248 |
1 files changed, 245 insertions, 3 deletions
diff --git a/sys/dev/fdt/sdhc_fdt.c b/sys/dev/fdt/sdhc_fdt.c index 43452b65d50..1c9335178bd 100644 --- a/sys/dev/fdt/sdhc_fdt.c +++ b/sys/dev/fdt/sdhc_fdt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdhc_fdt.c,v 1.10 2020/05/22 09:52:27 patrick Exp $ */ +/* $OpenBSD: sdhc_fdt.c,v 1.11 2020/05/22 10:26:00 patrick Exp $ */ /* * Copyright (c) 2017 Mark Kettenis * @@ -28,6 +28,7 @@ #include <dev/ofw/ofw_gpio.h> #include <dev/ofw/ofw_misc.h> #include <dev/ofw/ofw_pinctrl.h> +#include <dev/ofw/ofw_regulator.h> #include <dev/ofw/fdt.h> #include <dev/sdmmc/sdhcreg.h> @@ -42,14 +43,65 @@ #define GRF_EMMCCORE_CON11_CLOCKMULT_CLR (0xff << 16) #define GRF_EMMCCORE_CON11_CLOCKMULT_VAL(x) (((x) & 0xff) << 0) +/* Marvell Xenon */ +#define XENON_SYS_OP_CTRL 0x108 +#define XENON_SYS_OP_CTRL_SLOT_ENABLE(x) (1 << (x)) +#define XENON_SYS_OP_CTRL_SDCLK_IDLEOFF_ENABLE(x) (1 << ((x) + 8)) +#define XENON_SYS_OP_CTRL_AUTO_CLKGATE_DISABLE (1 << 20) +#define XENON_SYS_EXT_OP_CTRL 0x10c +#define XENON_SYS_EXT_OP_CTRL_PARALLEL_TRAN(x) (1 << (x)) +#define XENON_SYS_EXT_OP_CTRL_MASK_CMD_CONFLICT_ERR (1 << 8) +#define XENON_SLOT_EMMC_CTRL 0x130 +#define XENON_SLOT_EMMC_CTRL_ENABLE_DATA_STROBE (1 << 24) +#define XENON_SLOT_EMMC_CTRL_ENABLE_RESP_STROBE (1 << 25) +#define XENON_EMMC_PHY_TIMING_ADJUST 0x170 +#define XENON_EMMC_PHY_TIMING_ADJUST_SAMPL_INV_QSP_PHASE_SELECT (1 << 18) +#define XENON_EMMC_PHY_TIMING_ADJUST_SDIO_MODE (1 << 28) +#define XENON_EMMC_PHY_TIMING_ADJUST_SLOW_MODE (1 << 29) +#define XENON_EMMC_PHY_TIMING_ADJUST_INIT (1U << 31) +#define XENON_EMMC_PHY_FUNC_CONTROL 0x174 +#define XENON_EMMC_PHY_FUNC_CONTROL_DQ_ASYNC_MODE (1 << 4) +#define XENON_EMMC_PHY_FUNC_CONTROL_DQ_DDR_MODE (0xff << 8) +#define XENON_EMMC_PHY_FUNC_CONTROL_CMD_DDR_MODE (1 << 16) +#define XENON_EMMC_PHY_PAD_CONTROL 0x178 +#define XENON_EMMC_PHY_PAD_CONTROL_FC_DQ_RECEN (1 << 24) +#define XENON_EMMC_PHY_PAD_CONTROL_FC_CMD_RECEN (1 << 25) +#define XENON_EMMC_PHY_PAD_CONTROL_FC_QSP_RECEN (1 << 26) +#define XENON_EMMC_PHY_PAD_CONTROL_FC_QSN_RECEN (1 << 27) +#define XENON_EMMC_PHY_PAD_CONTROL_FC_ALL_CMOS_RECVR 0xf000 +#define XENON_EMMC_PHY_PAD_CONTROL1 0x17c +#define XENON_EMMC_PHY_PAD_CONTROL1_FC_CMD_PD (1 << 8) +#define XENON_EMMC_PHY_PAD_CONTROL1_FC_QSP_PD (1 << 9) +#define XENON_EMMC_PHY_PAD_CONTROL1_FC_CMD_PU (1 << 24) +#define XENON_EMMC_PHY_PAD_CONTROL1_FC_QSP_PU (1 << 25) +#define XENON_EMMC_PHY_PAD_CONTROL1_FC_DQ_PD 0xff +#define XENON_EMMC_PHY_PAD_CONTROL1_FC_DQ_PU (0xff << 16) +#define XENON_EMMC_PHY_PAD_CONTROL2 0x180 +#define XENON_EMMC_PHY_PAD_CONTROL2_ZPR_SHIFT 0 +#define XENON_EMMC_PHY_PAD_CONTROL2_ZPR_MASK 0x1f +#define XENON_EMMC_PHY_PAD_CONTROL2_ZNR_SHIFT 8 +#define XENON_EMMC_PHY_PAD_CONTROL2_ZNR_MASK 0x1f + +#define ARMADA_3700_SOC_PAD_CTL 0 +#define ARMADA_3700_SOC_PAD_CTL_3_3V 0 +#define ARMADA_3700_SOC_PAD_CTL_1_8V 1 + struct sdhc_fdt_softc { struct sdhc_softc sc; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; + bus_space_handle_t sc_pad_ioh; bus_size_t sc_size; void *sc_ih; int sc_node; uint32_t sc_gpio[3]; + uint32_t sc_vqmmc; + + /* Marvell Xenon */ + int sc_sdhc_id; + int sc_slow_mode; + uint32_t sc_znr; + uint32_t sc_zpr; struct sdhc_host *sc_host; struct clock_device sc_cd; @@ -66,6 +118,8 @@ int sdhc_fdt_card_detect(struct sdhc_softc *); int sdhc_fdt_signal_voltage(struct sdhc_softc *, int); uint32_t sdhc_fdt_get_frequency(void *, uint32_t *); +void sdhc_fdt_xenon_bus_clock_post(struct sdhc_softc *, int, int); + int sdhc_fdt_match(struct device *parent, void *match, void *aux) { @@ -73,7 +127,10 @@ sdhc_fdt_match(struct device *parent, void *match, void *aux) return (OF_is_compatible(faa->fa_node, "arasan,sdhci-5.1") || OF_is_compatible(faa->fa_node, "brcm,bcm2711-emmc2") || - OF_is_compatible(faa->fa_node, "brcm,bcm2835-sdhci")); + OF_is_compatible(faa->fa_node, "brcm,bcm2835-sdhci") || + OF_is_compatible(faa->fa_node, "marvell,armada-3700-sdhci") || + OF_is_compatible(faa->fa_node, "marvell,armada-ap806-sdhci") || + OF_is_compatible(faa->fa_node, "marvell,armada-cp110-sdhci")); } void @@ -82,7 +139,8 @@ sdhc_fdt_attach(struct device *parent, struct device *self, void *aux) struct sdhc_fdt_softc *sc = (struct sdhc_fdt_softc *)self; struct fdt_attach_args *faa = aux; struct regmap *rm = NULL; - uint32_t phandle, freq, cap = 0; + uint32_t reg, phandle, freq, cap = 0; + char pad_type[16] = { 0 }; if (faa->fa_nreg < 1) { printf(": no registers\n"); @@ -120,6 +178,8 @@ sdhc_fdt_attach(struct device *parent, struct device *self, void *aux) sc->sc.sc_card_detect = sdhc_fdt_card_detect; } + sc->sc_vqmmc = OF_getpropint(sc->sc_node, "vqmmc-supply", 0); + printf("\n"); sc->sc.sc_host = &sc->sc_host; @@ -185,6 +245,73 @@ sdhc_fdt_attach(struct device *parent, struct device *self, void *aux) sc->sc.sc_flags |= SDHC_F_32BIT_ACCESS; } + if (OF_is_compatible(faa->fa_node, "marvell,armada-3700-sdhci") || + OF_is_compatible(faa->fa_node, "marvell,armada-ap806-sdhci") || + OF_is_compatible(faa->fa_node, "marvell,armada-cp110-sdhci")) { + if (OF_is_compatible(faa->fa_node, + "marvell,armada-3700-sdhci")) { + KASSERT(faa->fa_nreg > 1); + if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, + faa->fa_reg[1].size, 0, &sc->sc_pad_ioh)) { + printf("%s: can't map registers\n", + sc->sc.sc_dev.dv_xname); + return; + } + OF_getprop(faa->fa_node, "marvell,pad-type", + pad_type, sizeof(pad_type)); + if (!strcmp(pad_type, "fixed-1-8v")) { + bus_space_write_4(sc->sc_iot, sc->sc_pad_ioh, + ARMADA_3700_SOC_PAD_CTL, + ARMADA_3700_SOC_PAD_CTL_1_8V); + } else { + bus_space_write_4(sc->sc_iot, sc->sc_pad_ioh, + ARMADA_3700_SOC_PAD_CTL, + ARMADA_3700_SOC_PAD_CTL_3_3V); + regulator_set_voltage(sc->sc_vqmmc, 3300000); + } + } + + cap = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + SDHC_CAPABILITIES); + if (OF_getpropint(faa->fa_node, "bus-width", 1) != 8) + cap &= ~SDHC_8BIT_MODE_SUPP; + if (OF_getproplen(faa->fa_node, "no-1-8-v") == 0) + cap &= ~SDHC_VOLTAGE_SUPP_1_8V; + if (OF_getproplen(faa->fa_node, + "marvell,xenon-phy-slow-mode") == 0) + sc->sc_slow_mode = 1; + + sc->sc_znr = OF_getpropint(faa->fa_node, + "marvell,xenon-phy-znr", 0xf); + sc->sc_znr &= XENON_EMMC_PHY_PAD_CONTROL2_ZNR_MASK; + sc->sc_zpr = OF_getpropint(faa->fa_node, + "marvell,xenon-phy-zpr", 0xf); + sc->sc_zpr &= XENON_EMMC_PHY_PAD_CONTROL2_ZPR_MASK; + sc->sc_sdhc_id = OF_getpropint(faa->fa_node, + "marvell,xenon-sdhc-id", 0); + + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + XENON_SYS_OP_CTRL); + reg |= XENON_SYS_OP_CTRL_SLOT_ENABLE(sc->sc_sdhc_id); + reg &= ~XENON_SYS_OP_CTRL_SDCLK_IDLEOFF_ENABLE(sc->sc_sdhc_id); + reg &= ~XENON_SYS_OP_CTRL_AUTO_CLKGATE_DISABLE; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + XENON_SYS_OP_CTRL, reg); + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + XENON_SYS_EXT_OP_CTRL); + reg |= XENON_SYS_EXT_OP_CTRL_PARALLEL_TRAN(sc->sc_sdhc_id); + reg |= XENON_SYS_EXT_OP_CTRL_MASK_CMD_CONFLICT_ERR; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + XENON_SYS_EXT_OP_CTRL, reg); + + freq = clock_get_frequency(faa->fa_node, NULL); + sc->sc.sc_clkbase = freq / 1000; + sc->sc.sc_bus_clock_post = sdhc_fdt_xenon_bus_clock_post; + + /* XXX: not yet implemented */ + sc->sc.sc_flags |= SDHC_F_NODDR50; + } + sdhc_host_found(&sc->sc, sc->sc_iot, sc->sc_ioh, sc->sc_size, 1, cap); return; @@ -220,3 +347,118 @@ sdhc_fdt_get_frequency(void *cookie, uint32_t *cells) struct sdhc_fdt_softc *sc = cookie; return clock_get_frequency(sc->sc_cd.cd_node, "clk_xin"); } + +/* Marvell Xenon */ +void +sdhc_fdt_xenon_bus_clock_post(struct sdhc_softc *ssc, int freq, int timing) +{ + struct sdhc_fdt_softc *sc = (struct sdhc_fdt_softc *)ssc; + uint32_t reg; + int i; + + if (freq == 0) + return; + + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_PAD_CONTROL); + reg |= (XENON_EMMC_PHY_PAD_CONTROL_FC_DQ_RECEN | + XENON_EMMC_PHY_PAD_CONTROL_FC_CMD_RECEN | + XENON_EMMC_PHY_PAD_CONTROL_FC_QSP_RECEN | + XENON_EMMC_PHY_PAD_CONTROL_FC_QSN_RECEN | + XENON_EMMC_PHY_PAD_CONTROL_FC_ALL_CMOS_RECVR); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_PAD_CONTROL, reg); + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_PAD_CONTROL1); + reg &= ~(XENON_EMMC_PHY_PAD_CONTROL1_FC_CMD_PD | + XENON_EMMC_PHY_PAD_CONTROL1_FC_DQ_PD); + reg |= (XENON_EMMC_PHY_PAD_CONTROL1_FC_CMD_PU | + XENON_EMMC_PHY_PAD_CONTROL1_FC_DQ_PU); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_PAD_CONTROL1, reg); + + if (timing == SDMMC_TIMING_LEGACY) + goto phy_init; + + /* TODO: check for SMF_IO_MODE and set flag */ + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_TIMING_ADJUST); + reg &= ~XENON_EMMC_PHY_TIMING_ADJUST_SDIO_MODE; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_TIMING_ADJUST, reg); + + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_PAD_CONTROL2); + reg &= ~(XENON_EMMC_PHY_PAD_CONTROL2_ZPR_MASK << + XENON_EMMC_PHY_PAD_CONTROL2_ZPR_SHIFT | + XENON_EMMC_PHY_PAD_CONTROL2_ZNR_MASK << + XENON_EMMC_PHY_PAD_CONTROL2_ZNR_SHIFT); + reg |= sc->sc_zpr << XENON_EMMC_PHY_PAD_CONTROL2_ZPR_SHIFT | + sc->sc_znr << XENON_EMMC_PHY_PAD_CONTROL2_ZNR_SHIFT; + + reg = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SDHC_CLOCK_CTL); + reg &= ~SDHC_SDCLK_ENABLE; + bus_space_write_2(sc->sc_iot, sc->sc_ioh, SDHC_CLOCK_CTL, reg); + + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_FUNC_CONTROL); + reg &= ~(XENON_EMMC_PHY_FUNC_CONTROL_DQ_DDR_MODE | + XENON_EMMC_PHY_FUNC_CONTROL_CMD_DDR_MODE); + reg |= XENON_EMMC_PHY_FUNC_CONTROL_DQ_ASYNC_MODE; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_FUNC_CONTROL, reg); + + reg = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SDHC_CLOCK_CTL); + reg |= SDHC_SDCLK_ENABLE; + bus_space_write_2(sc->sc_iot, sc->sc_ioh, SDHC_CLOCK_CTL, reg); + + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + XENON_SLOT_EMMC_CTRL); + reg &= ~(XENON_SLOT_EMMC_CTRL_ENABLE_DATA_STROBE | + XENON_SLOT_EMMC_CTRL_ENABLE_RESP_STROBE); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + XENON_SLOT_EMMC_CTRL, reg); + + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_PAD_CONTROL1); + reg &= ~(XENON_EMMC_PHY_PAD_CONTROL1_FC_QSP_PD | + XENON_EMMC_PHY_PAD_CONTROL1_FC_QSP_PU); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_PAD_CONTROL1, reg); + +phy_init: + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_TIMING_ADJUST); + reg |= XENON_EMMC_PHY_TIMING_ADJUST_SAMPL_INV_QSP_PHASE_SELECT; + reg &= ~XENON_EMMC_PHY_TIMING_ADJUST_SLOW_MODE; + if (timing == SDMMC_TIMING_LEGACY || + timing == SDMMC_TIMING_HIGHSPEED || sc->sc_slow_mode) + reg |= XENON_EMMC_PHY_TIMING_ADJUST_SLOW_MODE; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_TIMING_ADJUST, reg); + + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_TIMING_ADJUST); + reg |= XENON_EMMC_PHY_TIMING_ADJUST_INIT; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_TIMING_ADJUST, reg); + + for (i = 1000; i > 0; i--) { + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + XENON_EMMC_PHY_TIMING_ADJUST); + if (!(reg & XENON_EMMC_PHY_TIMING_ADJUST_INIT)) + break; + delay(10); + } + if (i == 0) + printf("%s: phy initialization timeout\n", + sc->sc.sc_dev.dv_xname); + + if (freq > SDMMC_SDCLK_400KHZ) { + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, + XENON_SYS_OP_CTRL); + reg |= XENON_SYS_OP_CTRL_SDCLK_IDLEOFF_ENABLE(sc->sc_sdhc_id); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + XENON_SYS_OP_CTRL, reg); + } +} |