summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/fdt/sdhc_fdt.c248
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);
+ }
+}