Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 14 Jan 2018 22:05:29 +0000 (UTC)
From:      Emmanuel Vadot <manu@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r327979 - head/sys/arm/allwinner
Message-ID:  <201801142205.w0EM5TRa075355@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: manu
Date: Sun Jan 14 22:05:29 2018
New Revision: 327979
URL: https://svnweb.freebsd.org/changeset/base/327979

Log:
  allwinner: mmc: Multiple improvement
  
    - Add a per compatible configuration struct
    - Not all SoC uses the same size for DMA transfert, add this into the
      configuration data
    - Use new timing mode for some SoC (A64 mmc)
    - Auto calibrate clock for A64 mmc/emmc
    - A64 mmc controller need masking of data0
    - Add support for vmmc/vqmmc regulator
    - Add more capabilities, r/w speed is better for eMMC
    - MMC_CAP_SIGNALING_180 gives weird result so do not enable it for now.
    - Add new register documented in H3/A64 user manual
  
  Tested-On: Pine64-LTS (A64), eMMC still doesn't work
  Tested-On: A64-Olinuxino (A64), sd and eMMC are working
  Tested-On: NanoPi Neo Plus2 (H5), sd and eMMC are working
  Tested-On: OrangePi PC2 (H5), sd only (no eMMC)
  Tested-On: OrangePi One (H3), sd only (no eMMC)
  Tested-On: BananaPi M2 (A31s), sd only (no eMMC)

Modified:
  head/sys/arm/allwinner/aw_mmc.c
  head/sys/arm/allwinner/aw_mmc.h

Modified: head/sys/arm/allwinner/aw_mmc.c
==============================================================================
--- head/sys/arm/allwinner/aw_mmc.c	Sun Jan 14 21:21:10 2018	(r327978)
+++ head/sys/arm/allwinner/aw_mmc.c	Sun Jan 14 22:05:29 2018	(r327979)
@@ -50,22 +50,50 @@ __FBSDID("$FreeBSD$");
 #include <arm/allwinner/aw_mmc.h>
 #include <dev/extres/clk/clk.h>
 #include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/regulator/regulator.h>
 
 #define	AW_MMC_MEMRES		0
 #define	AW_MMC_IRQRES		1
 #define	AW_MMC_RESSZ		2
 #define	AW_MMC_DMA_SEGS		((MAXPHYS / PAGE_SIZE) + 1)
-#define	AW_MMC_DMA_MAX_SIZE	0x2000
 #define	AW_MMC_DMA_FTRGLEVEL	0x20070008
 #define	AW_MMC_RESET_RETRY	1000
 
 #define	CARD_ID_FREQUENCY	400000
 
+struct aw_mmc_conf {
+	uint32_t	dma_xferlen;
+	bool		mask_data0;
+	bool		can_calibrate;
+	bool		new_timing;
+};
+
+static const struct aw_mmc_conf a10_mmc_conf = {
+	.dma_xferlen = 0x2000,
+};
+
+static const struct aw_mmc_conf a13_mmc_conf = {
+	.dma_xferlen = 0x10000,
+};
+
+static const struct aw_mmc_conf a64_mmc_conf = {
+	.dma_xferlen = 0x10000,
+	.mask_data0 = true,
+	.can_calibrate = true,
+	.new_timing = true,
+};
+
+static const struct aw_mmc_conf a64_emmc_conf = {
+	.dma_xferlen = 0x2000,
+	.can_calibrate = true,
+};
+
 static struct ofw_compat_data compat_data[] = {
-	{"allwinner,sun4i-a10-mmc", 1},
-	{"allwinner,sun5i-a13-mmc", 1},
-	{"allwinner,sun7i-a20-mmc", 1},
-	{"allwinner,sun50i-a64-mmc", 1},
+	{"allwinner,sun4i-a10-mmc", (uintptr_t)&a10_mmc_conf},
+	{"allwinner,sun5i-a13-mmc", (uintptr_t)&a13_mmc_conf},
+	{"allwinner,sun7i-a20-mmc", (uintptr_t)&a13_mmc_conf},
+	{"allwinner,sun50i-a64-mmc", (uintptr_t)&a64_mmc_conf},
+	{"allwinner,sun50i-a64-emmc", (uintptr_t)&a64_emmc_conf},
 	{NULL,             0}
 };
 
@@ -82,9 +110,13 @@ struct aw_mmc_softc {
 	struct mmc_request *	aw_req;
 	struct mtx		aw_mtx;
 	struct resource *	aw_res[AW_MMC_RESSZ];
+	struct aw_mmc_conf *	aw_mmc_conf;
 	uint32_t		aw_intr;
 	uint32_t		aw_intr_wait;
 	void *			aw_intrhand;
+	int32_t			aw_vdd;
+	regulator_t		aw_reg_vmmc;
+	regulator_t		aw_reg_vqmmc;
 
 	/* Fields required for DMA access. */
 	bus_addr_t	  	aw_dma_desc_phys;
@@ -151,6 +183,9 @@ aw_mmc_attach(device_t dev)
 	node = ofw_bus_get_node(dev);
 	sc = device_get_softc(dev);
 	sc->aw_dev = dev;
+
+	sc->aw_mmc_conf = (struct aw_mmc_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
 	sc->aw_req = NULL;
 	if (bus_alloc_resources(dev, aw_mmc_res_spec, sc->aw_res) != 0) {
 		device_printf(dev, "cannot allocate device resources\n");
@@ -230,11 +265,22 @@ aw_mmc_attach(device_t dev)
 	if (OF_getencprop(node, "bus-width", &bus_width, sizeof(uint32_t)) <= 0)
 		bus_width = 4;
 
+	if (regulator_get_by_ofw_property(dev, 0, "vmmc-supply",
+	    &sc->aw_reg_vmmc) == 0 && bootverbose)
+		device_printf(dev, "vmmc-supply regulator found\n");
+	if (regulator_get_by_ofw_property(dev, 0, "vqmmc-supply",
+	    &sc->aw_reg_vqmmc) == 0 && bootverbose)
+		device_printf(dev, "vqmmc-supply regulator found\n");
+
 	sc->aw_host.f_min = 400000;
 	sc->aw_host.f_max = 52000000;
 	sc->aw_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
-	sc->aw_host.mode = mode_sd;
-	sc->aw_host.caps = MMC_CAP_HSPEED;
+	sc->aw_host.caps = MMC_CAP_HSPEED | MMC_CAP_UHS_SDR12 |
+			   MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
+			   MMC_CAP_UHS_DDR50 | MMC_CAP_MMC_DDR52;
+
+	sc->aw_host.caps |= MMC_CAP_SIGNALING_330 /* | MMC_CAP_SIGNALING_180 */;
+
 	if (bus_width >= 4)
 		sc->aw_host.caps |= MMC_CAP_4_BIT_DATA;
 	if (bus_width >= 8)
@@ -311,8 +357,8 @@ aw_mmc_setup_dma(struct aw_mmc_softc *sc)
 	error = bus_dma_tag_create(bus_get_dma_tag(sc->aw_dev),
 	    AW_MMC_DMA_ALIGN, 0,
 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
-	    AW_MMC_DMA_MAX_SIZE * AW_MMC_DMA_SEGS, AW_MMC_DMA_SEGS,
-	    AW_MMC_DMA_MAX_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL,
+	    sc->aw_mmc_conf->dma_xferlen * AW_MMC_DMA_SEGS, AW_MMC_DMA_SEGS,
+	    sc->aw_mmc_conf->dma_xferlen, BUS_DMA_ALLOCNOW, NULL, NULL,
 	    &sc->aw_dma_buf_tag);
 	if (error)
 		return (error);
@@ -366,7 +412,7 @@ aw_mmc_prepare_dma(struct aw_mmc_softc *sc)
 	uint32_t val;
 
 	cmd = sc->aw_req->cmd;
-	if (cmd->data->len > AW_MMC_DMA_MAX_SIZE * AW_MMC_DMA_SEGS)
+	if (cmd->data->len > (sc->aw_mmc_conf->dma_xferlen * AW_MMC_DMA_SEGS))
 		return (EFBIG);
 	error = bus_dmamap_load(sc->aw_dma_buf_tag, sc->aw_dma_buf_map,
 	    cmd->data->data, cmd->data->len, aw_dma_cb, sc, 0);
@@ -562,7 +608,8 @@ aw_mmc_intr(void *arg)
 		goto end;
 	}
 	if (rint & AW_MMC_INT_ERR_BIT) {
-		device_printf(sc->aw_dev, "error rint: 0x%08X\n", rint);
+		if (bootverbose)
+			device_printf(sc->aw_dev, "error rint: 0x%08X\n", rint);
 		if (rint & AW_MMC_INT_RESP_TIMEOUT)
 			sc->aw_req->cmd->error = MMC_ERR_TIMEOUT;
 		else
@@ -704,6 +751,9 @@ aw_mmc_read_ivar(device_t bus, device_t child, int whi
 	case MMCBR_IVAR_CAPS:
 		*(int *)result = sc->aw_host.caps;
 		break;
+	case MMCBR_IVAR_TIMING:
+		*(int *)result = sc->aw_host.ios.timing;
+		break;
 	case MMCBR_IVAR_MAX_DATA:
 		*(int *)result = 65535;
 		break;
@@ -746,6 +796,9 @@ aw_mmc_write_ivar(device_t bus, device_t child, int wh
 	case MMCBR_IVAR_VDD:
 		sc->aw_host.ios.vdd = value;
 		break;
+	case MMCBR_IVAR_TIMING:
+		sc->aw_host.ios.timing = value;
+		break;
 	/* These are read-only */
 	case MMCBR_IVAR_CAPS:
 	case MMCBR_IVAR_HOST_OCR:
@@ -761,42 +814,93 @@ aw_mmc_write_ivar(device_t bus, device_t child, int wh
 static int
 aw_mmc_update_clock(struct aw_mmc_softc *sc, uint32_t clkon)
 {
-	uint32_t cmdreg;
+	uint32_t reg;
 	int retry;
-	uint32_t ckcr;
 
-	ckcr = AW_MMC_READ_4(sc, AW_MMC_CKCR);
-	ckcr &= ~(AW_MMC_CKCR_CCLK_ENB | AW_MMC_CKCR_CCLK_CTRL);
+	reg = AW_MMC_READ_4(sc, AW_MMC_CKCR);
+	reg &= ~(AW_MMC_CKCR_CCLK_ENB | AW_MMC_CKCR_CCLK_CTRL |
+	    AW_MMC_CKCR_CCLK_MASK_DATA0);
 
 	if (clkon)
-		ckcr |= AW_MMC_CKCR_CCLK_ENB;
+		reg |= AW_MMC_CKCR_CCLK_ENB;
+	if (sc->aw_mmc_conf->mask_data0)
+		reg |= AW_MMC_CKCR_CCLK_MASK_DATA0;
 
-	AW_MMC_WRITE_4(sc, AW_MMC_CKCR, ckcr);
+	AW_MMC_WRITE_4(sc, AW_MMC_CKCR, reg);
 
-	cmdreg = AW_MMC_CMDR_LOAD | AW_MMC_CMDR_PRG_CLK |
+	reg = AW_MMC_CMDR_LOAD | AW_MMC_CMDR_PRG_CLK |
 	    AW_MMC_CMDR_WAIT_PRE_OVER;
-	AW_MMC_WRITE_4(sc, AW_MMC_CMDR, cmdreg);
+	AW_MMC_WRITE_4(sc, AW_MMC_CMDR, reg);
 	retry = 0xfffff;
-	while (--retry > 0) {
-		if ((AW_MMC_READ_4(sc, AW_MMC_CMDR) & AW_MMC_CMDR_LOAD) == 0) {
-			AW_MMC_WRITE_4(sc, AW_MMC_RISR, 0xffffffff);
-			return (0);
-		}
+
+	while (reg & AW_MMC_CMDR_LOAD && --retry > 0) {
+		reg = AW_MMC_READ_4(sc, AW_MMC_CMDR);
 		DELAY(10);
 	}
 	AW_MMC_WRITE_4(sc, AW_MMC_RISR, 0xffffffff);
-	device_printf(sc->aw_dev, "timeout updating clock\n");
 
-	return (ETIMEDOUT);
+	if (reg & AW_MMC_CMDR_LOAD) {
+		device_printf(sc->aw_dev, "timeout updating clock\n");
+		return (ETIMEDOUT);
+	}
+
+	if (sc->aw_mmc_conf->mask_data0) {
+		reg = AW_MMC_READ_4(sc, AW_MMC_CKCR);
+		reg &= ~AW_MMC_CKCR_CCLK_MASK_DATA0;
+		AW_MMC_WRITE_4(sc, AW_MMC_CKCR, reg);
+	}
+
+	return (0);
 }
 
+static void
+aw_mmc_set_power(struct aw_mmc_softc *sc, int32_t vdd)
+{
+	int min_uvolt, max_uvolt;
+
+	sc->aw_vdd = vdd;
+
+	if (sc->aw_reg_vmmc == NULL && sc->aw_reg_vqmmc == NULL)
+		return;
+
+	switch (1 << vdd) {
+	case MMC_OCR_LOW_VOLTAGE:
+		min_uvolt = max_uvolt = 1800000;
+		break;
+	case MMC_OCR_320_330:
+		min_uvolt = 3200000;
+		max_uvolt = 3300000;
+		break;
+	case MMC_OCR_330_340:
+		min_uvolt = 3300000;
+		max_uvolt = 3400000;
+		break;
+	}
+
+	if (sc->aw_reg_vmmc)
+		if (regulator_set_voltage(sc->aw_reg_vmmc,
+		    min_uvolt, max_uvolt) != 0)
+			device_printf(sc->aw_dev,
+			    "Cannot set vmmc to %d<->%d\n",
+			    min_uvolt,
+			    max_uvolt);
+	if (sc->aw_reg_vqmmc)
+		if (regulator_set_voltage(sc->aw_reg_vqmmc,
+		    min_uvolt, max_uvolt) != 0)
+			device_printf(sc->aw_dev,
+			    "Cannot set vqmmc to %d<->%d\n",
+			    min_uvolt,
+			    max_uvolt);
+}
+
 static int
 aw_mmc_update_ios(device_t bus, device_t child)
 {
 	int error;
 	struct aw_mmc_softc *sc;
 	struct mmc_ios *ios;
-	uint32_t ckcr;
+	unsigned int clock;
+	uint32_t reg, div = 1;
 
 	sc = device_get_softc(bus);
 
@@ -815,27 +919,66 @@ aw_mmc_update_ios(device_t bus, device_t child)
 		break;
 	}
 
+	/* Set the voltage */
+	if (ios->power_mode == power_off) {
+		if (bootverbose)
+			device_printf(sc->aw_dev, "Powering down sd/mmc\n");
+		if (sc->aw_reg_vmmc)
+			regulator_disable(sc->aw_reg_vmmc);
+		if (sc->aw_reg_vqmmc)
+			regulator_disable(sc->aw_reg_vqmmc);
+	} else if (sc->aw_vdd != ios->vdd)
+		aw_mmc_set_power(sc, ios->vdd);
+
+	/* Enable ddr mode if needed */
+	reg = AW_MMC_READ_4(sc, AW_MMC_GCTL);
+	if (ios->timing == bus_timing_uhs_ddr50 ||
+	  ios->timing == bus_timing_mmc_ddr52)
+		reg |= AW_MMC_CTRL_DDR_MOD_SEL;
+	else
+		reg &= ~AW_MMC_CTRL_DDR_MOD_SEL;
+	AW_MMC_WRITE_4(sc, AW_MMC_GCTL, reg);
+
 	if (ios->clock) {
+		clock = ios->clock;
 
 		/* Disable clock */
 		error = aw_mmc_update_clock(sc, 0);
 		if (error != 0)
 			return (error);
 
+		if (ios->timing == bus_timing_mmc_ddr52 &&
+		    (sc->aw_mmc_conf->new_timing ||
+		    ios->bus_width == bus_width_8)) {
+			div = 2;
+			clock <<= 1;
+		}
+
 		/* Reset the divider. */
-		ckcr = AW_MMC_READ_4(sc, AW_MMC_CKCR);
-		ckcr &= ~AW_MMC_CKCR_CCLK_DIV;
-		AW_MMC_WRITE_4(sc, AW_MMC_CKCR, ckcr);
+		reg = AW_MMC_READ_4(sc, AW_MMC_CKCR);
+		reg &= ~AW_MMC_CKCR_CCLK_DIV;
+		reg |= div - 1;
+		AW_MMC_WRITE_4(sc, AW_MMC_CKCR, reg);
 
+		/* New timing mode if needed */
+		if (sc->aw_mmc_conf->new_timing) {
+			reg = AW_MMC_READ_4(sc, AW_MMC_NTSR);
+			reg |= AW_MMC_NTSR_MODE_SELECT;
+			AW_MMC_WRITE_4(sc, AW_MMC_NTSR, reg);
+		}
+
 		/* Set the MMC clock. */
-		error = clk_set_freq(sc->aw_clk_mmc, ios->clock,
+		error = clk_set_freq(sc->aw_clk_mmc, clock,
 		    CLK_SET_ROUND_DOWN);
 		if (error != 0) {
 			device_printf(sc->aw_dev,
 			    "failed to set frequency to %u Hz: %d\n",
-			    ios->clock, error);
+			    clock, error);
 			return (error);
 		}
+
+		if (sc->aw_mmc_conf->can_calibrate)
+			AW_MMC_WRITE_4(sc, AW_MMC_SAMP_DL, AW_MMC_SAMP_DL_SW_EN);
 
 		/* Enable clock. */
 		error = aw_mmc_update_clock(sc, 1);

Modified: head/sys/arm/allwinner/aw_mmc.h
==============================================================================
--- head/sys/arm/allwinner/aw_mmc.h	Sun Jan 14 21:21:10 2018	(r327978)
+++ head/sys/arm/allwinner/aw_mmc.h	Sun Jan 14 22:05:29 2018	(r327979)
@@ -47,14 +47,23 @@
 #define	AW_MMC_STAR		0x3C	/* Status Register */
 #define	AW_MMC_FWLR		0x40	/* FIFO Threshold Watermark Register */
 #define	AW_MMC_FUNS		0x44	/* Function Select Register */
-#define	AW_MMC_HWRST		0x78	/* Hardware reset (not documented) */
+#define	AW_MMC_CSDC		0x54	/* CRC status detect controler register (A64 smhc2 only) */
+#define	AW_MMC_A12A		0x58	/* Auto command 12 argument register */
+#define	AW_MMC_NTSR		0x5C	/* SD new timing register (H3, A64 smhc0/1 only) */
+#define	AW_MMC_HWRST		0x78	/* Hardware reset */
 #define	AW_MMC_DMAC		0x80	/* IDMAC Control Register */
 #define	AW_MMC_DLBA		0x84	/* IDMAC Desc List Base Address Reg */
 #define	AW_MMC_IDST		0x88	/* IDMAC Status Register */
 #define	AW_MMC_IDIE		0x8C	/* IDMAC Interrupt Enable Register */
-#define	AW_MMC_FIFO		0x100   /* FIFO Access Address (A10/A20) */
-#define	A31_MMC_FIFO		0x200   /* FIFO Access Address (A31) */
 
+#define	AW_MMC_DDR_SBIT_DET	0x10C	/* eMMC4.5 DDR Start Bit Detection control register */
+#define	AW_MMC_DRV_DL		0x140	/* Drive Delay control register */
+#define	AW_MMC_SAMP_DL		0x144	/* Sample Delay controle register */
+#define	AW_MMC_DS_DL		0x148	/* Data strobe delay control register */
+
+#define	AW_MMC_FIFO		0x100	/* FIFO Access Address (A10/A20) */
+#define	A31_MMC_FIFO		0x200	/* FIFO Access Address (A31) */
+
 /* AW_MMC_GCTL */
 #define	AW_MMC_CTRL_SOFT_RST		(1U << 0)
 #define	AW_MMC_CTRL_FIFO_RST		(1U << 1)
@@ -70,6 +79,7 @@
 /* AW_MMC_CKCR */
 #define	AW_MMC_CKCR_CCLK_ENB		(1U << 16)
 #define	AW_MMC_CKCR_CCLK_CTRL		(1U << 17)
+#define	AW_MMC_CKCR_CCLK_MASK_DATA0	(1U << 31)
 #define	AW_MMC_CKCR_CCLK_DIV		0xff
 
 /* AW_MMC_TMOR */
@@ -153,6 +163,9 @@
 #define	AW_MMC_SEND_AUTOSTOP_CC_SD	(1U << 9)
 #define	AW_MMC_CE_ATA_DEV_INT_ENB	(1U << 10)
 
+/* AW_MMC_NTSR */
+#define	AW_MMC_NTSR_MODE_SELECT		(1U << 31)
+
 /* IDMA CONTROLLER BUS MOD BIT FIELD */
 #define	AW_MMC_DMAC_IDMAC_SOFT_RST	(1U << 0)
 #define	AW_MMC_DMAC_IDMAC_FIX_BURST	(1U << 1)
@@ -183,6 +196,12 @@
 	 AW_MMC_IDST_DES_UNAVL_INT | AW_MMC_IDST_ABN_INT_SUM)
 #define	AW_MMC_IDST_COMPLETE				\
 	(AW_MMC_IDST_TX_INT | AW_MMC_IDST_RX_INT)
+
+/* AW_MMC_DDR_SBIT_DET */
+#define	AW_MMC_DDR_SBIT_HS_MD_EN	(1U << 31)
+
+/* AW_MMC_SAMP */
+#define	AW_MMC_SAMP_DL_SW_EN		(1U << 7)
 
 /* The DMA descriptor table. */
 struct aw_mmc_dma_desc {



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201801142205.w0EM5TRa075355>