Module Name: src Committed By: jmcneill Date: Mon Jul 17 23:31:05 UTC 2017
Modified Files: src/sys/arch/arm/sunxi: sunxi_mmc.c sunxi_mmc.h Log Message: Add support for eMMC DDR52 transfer mode. To generate a diff of this commit: cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/sunxi/sunxi_mmc.c cvs rdiff -u -r1.1 -r1.2 src/sys/arch/arm/sunxi/sunxi_mmc.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/arm/sunxi/sunxi_mmc.c diff -u src/sys/arch/arm/sunxi/sunxi_mmc.c:1.2 src/sys/arch/arm/sunxi/sunxi_mmc.c:1.3 --- src/sys/arch/arm/sunxi/sunxi_mmc.c:1.2 Sun Jul 16 17:12:18 2017 +++ src/sys/arch/arm/sunxi/sunxi_mmc.c Mon Jul 17 23:31:05 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_mmc.c,v 1.2 2017/07/16 17:12:18 jmcneill Exp $ */ +/* $NetBSD: sunxi_mmc.c,v 1.3 2017/07/17 23:31:05 jmcneill Exp $ */ /*- * Copyright (c) 2014-2017 Jared McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sunxi_mmc.c,v 1.2 2017/07/16 17:12:18 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sunxi_mmc.c,v 1.3 2017/07/17 23:31:05 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -45,6 +45,27 @@ __KERNEL_RCSID(0, "$NetBSD: sunxi_mmc.c, #include <arm/sunxi/sunxi_mmc.h> +enum sunxi_mmc_timing { + SUNXI_MMC_TIMING_400K, + SUNXI_MMC_TIMING_25M, + SUNXI_MMC_TIMING_50M, + SUNXI_MMC_TIMING_50M_DDR, + SUNXI_MMC_TIMING_50M_DDR_8BIT, +}; + +struct sunxi_mmc_delay { + u_int output_phase; + u_int sample_phase; +}; + +static const struct sunxi_mmc_delay sunxi_mmc_delays[] = { + [SUNXI_MMC_TIMING_400K] = { 180, 180 }, + [SUNXI_MMC_TIMING_25M] = { 180, 75 }, + [SUNXI_MMC_TIMING_50M] = { 90, 120 }, + [SUNXI_MMC_TIMING_50M_DDR] = { 60, 120 }, + [SUNXI_MMC_TIMING_50M_DDR_8BIT] = { 90, 180 }, +}; + #define SUNXI_MMC_NDESC 16 #define SUNXI_MMC_DMA_XFERLEN 0x10000 #define SUNXI_MMC_DMA_FTRGLEVEL 0x20070008 @@ -64,9 +85,10 @@ static int sunxi_mmc_host_maxblklen(sdmm static int sunxi_mmc_card_detect(sdmmc_chipset_handle_t); static int sunxi_mmc_write_protect(sdmmc_chipset_handle_t); static int sunxi_mmc_bus_power(sdmmc_chipset_handle_t, uint32_t); -static int sunxi_mmc_bus_clock(sdmmc_chipset_handle_t, int); +static int sunxi_mmc_bus_clock(sdmmc_chipset_handle_t, int, bool); static int sunxi_mmc_bus_width(sdmmc_chipset_handle_t, int); static int sunxi_mmc_bus_rod(sdmmc_chipset_handle_t, int); +static int sunxi_mmc_signal_voltage(sdmmc_chipset_handle_t, int); static void sunxi_mmc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); static void sunxi_mmc_card_enable_intr(sdmmc_chipset_handle_t, int); @@ -79,9 +101,10 @@ static struct sdmmc_chip_functions sunxi .card_detect = sunxi_mmc_card_detect, .write_protect = sunxi_mmc_write_protect, .bus_power = sunxi_mmc_bus_power, - .bus_clock = sunxi_mmc_bus_clock, + .bus_clock_ddr = sunxi_mmc_bus_clock, .bus_width = sunxi_mmc_bus_width, .bus_rod = sunxi_mmc_bus_rod, + .signal_voltage = sunxi_mmc_signal_voltage, .exec_command = sunxi_mmc_exec_command, .card_enable_intr = sunxi_mmc_card_enable_intr, .card_intr_ack = sunxi_mmc_card_intr_ack, @@ -129,6 +152,8 @@ struct sunxi_mmc_softc { int sc_gpio_cd_inverted; struct fdtbus_gpio_pin *sc_gpio_wp; int sc_gpio_wp_inverted; + + struct fdtbus_regulator *sc_reg_vqmmc; }; CFATTACH_DECL_NEW(sunxi_mmc, sizeof(struct sunxi_mmc_softc), @@ -188,6 +213,8 @@ sunxi_mmc_attach(device_t parent, device return; } + sc->sc_reg_vqmmc = fdtbus_regulator_acquire(phandle, "vqmmc-supply"); + if (clk_enable(sc->sc_clk_ahb) != 0 || clk_enable(sc->sc_clk_mmc) != 0) { aprint_error(": couldn't enable clocks\n"); @@ -287,9 +314,44 @@ free: } static int -sunxi_mmc_set_clock(struct sunxi_mmc_softc *sc, u_int freq) +sunxi_mmc_set_clock(struct sunxi_mmc_softc *sc, u_int freq, bool ddr) { - return clk_set_rate(sc->sc_clk_mmc, freq * 1000); + const struct sunxi_mmc_delay *delays; + int error, timing; + + if (freq <= 400) { + timing = SUNXI_MMC_TIMING_400K; + } else if (freq <= 25000) { + timing = SUNXI_MMC_TIMING_25M; + } else if (freq <= 52000) { + if (ddr) { + timing = sc->sc_mmc_width == 8 ? + SUNXI_MMC_TIMING_50M_DDR_8BIT : + SUNXI_MMC_TIMING_50M_DDR; + } else { + timing = SUNXI_MMC_TIMING_50M; + } + } else + return EINVAL; + + delays = &sunxi_mmc_delays[timing]; + + error = clk_set_rate(sc->sc_clk_mmc, (freq * 1000) << ddr); + if (error != 0) + return error; + + if (sc->sc_clk_sample) { + error = clk_set_rate(sc->sc_clk_sample, delays->sample_phase); + if (error != 0) + return error; + } + if (sc->sc_clk_output) { + error = clk_set_rate(sc->sc_clk_output, delays->output_phase); + if (error != 0) + return error; + } + + return 0; } static void @@ -301,7 +363,7 @@ sunxi_mmc_attach_i(device_t self) sunxi_mmc_host_reset(sc); sunxi_mmc_bus_width(sc, 1); - sunxi_mmc_set_clock(sc, 400); + sunxi_mmc_set_clock(sc, 400, false); if (of_getprop_uint32(sc->sc_phandle, "bus-width", &width) != 0) width = 4; @@ -318,6 +380,7 @@ sunxi_mmc_attach_i(device_t self) SMC_CAPS_AUTO_STOP | SMC_CAPS_SD_HIGHSPEED | SMC_CAPS_MMC_HIGHSPEED | + SMC_CAPS_MMC_DDR52 | SMC_CAPS_POLLING; if (width == 4) saa.saa_caps |= SMC_CAPS_4BIT_MODE; @@ -535,10 +598,10 @@ sunxi_mmc_update_clock(struct sunxi_mmc_ } static int -sunxi_mmc_bus_clock(sdmmc_chipset_handle_t sch, int freq) +sunxi_mmc_bus_clock(sdmmc_chipset_handle_t sch, int freq, bool ddr) { struct sunxi_mmc_softc *sc = sch; - uint32_t clkcr; + uint32_t clkcr, gctrl; clkcr = MMC_READ(sc, SUNXI_MMC_CLKCR); if (clkcr & SUNXI_MMC_CLKCR_CARDCLKON) { @@ -551,11 +614,19 @@ sunxi_mmc_bus_clock(sdmmc_chipset_handle if (freq) { clkcr &= ~SUNXI_MMC_CLKCR_DIV; + clkcr |= __SHIFTIN(ddr, SUNXI_MMC_CLKCR_DIV); MMC_WRITE(sc, SUNXI_MMC_CLKCR, clkcr); if (sunxi_mmc_update_clock(sc) != 0) return 1; - if (sunxi_mmc_set_clock(sc, freq) != 0) + gctrl = MMC_READ(sc, SUNXI_MMC_GCTRL); + if (ddr) + gctrl |= SUNXI_MMC_GCTRL_DDR_MODE; + else + gctrl &= ~SUNXI_MMC_GCTRL_DDR_MODE; + MMC_WRITE(sc, SUNXI_MMC_GCTRL, gctrl); + + if (sunxi_mmc_set_clock(sc, freq, ddr) != 0) return 1; clkcr |= SUNXI_MMC_CLKCR_CARDCLKON; @@ -602,6 +673,34 @@ sunxi_mmc_bus_rod(sdmmc_chipset_handle_t } static int +sunxi_mmc_signal_voltage(sdmmc_chipset_handle_t sch, int signal_voltage) +{ + struct sunxi_mmc_softc *sc = sch; + u_int uvol; + int error; + + if (sc->sc_reg_vqmmc == NULL) + return 0; + + switch (signal_voltage) { + case SDMMC_SIGNAL_VOLTAGE_330: + uvol = 3300000; + break; + case SDMMC_SIGNAL_VOLTAGE_180: + uvol = 1800000; + break; + default: + return EINVAL; + } + + error = fdtbus_regulator_set_voltage(sc->sc_reg_vqmmc, uvol, uvol); + if (error != 0) + return error; + + return fdtbus_regulator_enable(sc->sc_reg_vqmmc); +} + +static int sunxi_mmc_dma_prepare(struct sunxi_mmc_softc *sc, struct sdmmc_command *cmd) { struct sunxi_mmc_idma_descriptor *dma = sc->sc_idma_desc; Index: src/sys/arch/arm/sunxi/sunxi_mmc.h diff -u src/sys/arch/arm/sunxi/sunxi_mmc.h:1.1 src/sys/arch/arm/sunxi/sunxi_mmc.h:1.2 --- src/sys/arch/arm/sunxi/sunxi_mmc.h:1.1 Thu Jun 29 09:26:06 2017 +++ src/sys/arch/arm/sunxi/sunxi_mmc.h Mon Jul 17 23:31:05 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_mmc.h,v 1.1 2017/06/29 09:26:06 jmcneill Exp $ */ +/* $NetBSD: sunxi_mmc.h,v 1.2 2017/07/17 23:31:05 jmcneill Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -51,6 +51,7 @@ #define SUNXI_MMC_BBCR 0x004C #define SUNXI_MMC_DBGC 0x0050 #define SUNXI_MMC_A12A 0x0058 /* A80 */ +#define SUNXI_MMC_NTSR 0x005C #define SUNXI_MMC_HWRST 0x0078 /* A80 */ #define SUNXI_MMC_DMAC 0x0080 #define SUNXI_MMC_DLBA 0x0084 @@ -58,7 +59,7 @@ #define SUNXI_MMC_IDIE 0x008C #define SUNXI_MMC_CHDA 0x0090 #define SUNXI_MMC_CBDA 0x0094 -#define SUNXI_MMC_FIFO 0x0100 + #define SUNXI_MMC_GCTRL_ACCESS_BY_AHB __BIT(31) #define SUNXI_MMC_GCTRL_WAIT_MEM_ACCESS_DONE __BIT(30) #define SUNXI_MMC_GCTRL_DDR_MODE __BIT(10) @@ -73,7 +74,7 @@ SUNXI_MMC_GCTRL_DMARESET) #define SUNXI_MMC_CLKCR_LOWPOWERON __BIT(17) #define SUNXI_MMC_CLKCR_CARDCLKON __BIT(16) -#define SUNXI_MMC_CLKCR_DIV __BITS(15,0) +#define SUNXI_MMC_CLKCR_DIV __BITS(7,0) #define SUNXI_MMC_WIDTH_1 0 #define SUNXI_MMC_WIDTH_4 1 #define SUNXI_MMC_WIDTH_8 2 @@ -137,6 +138,9 @@ #define SUNXI_MMC_FUNCSEL_ABT_RD_DATA __BIT(2) #define SUNXI_MMC_FUNCSEL_SDIO_RD_WAIT __BIT(1) #define SUNXI_MMC_FUNCSEL_SEND_IRQ_RSP __BIT(0) +#define SUNXI_MMC_NTSR_MODE_SELECT __BIT(31) +#define SUNXI_MMC_NTSR_SAMPLE_PHASE __BITS(30,6) +#define SUNXI_MMC_NTSR_OUTPUT_PHASE __BITS(1,0) #define SUNXI_MMC_DMAC_REFETCH_DES __BIT(31) #define SUNXI_MMC_DMAC_IDMA_ON __BIT(7) #define SUNXI_MMC_DMAC_FIX_BURST __BIT(1)