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)
This commit is contained in:
manu 2018-01-14 22:05:29 +00:00
parent 1c4853adf6
commit a09fcfef11
2 changed files with 197 additions and 35 deletions

View File

@ -50,22 +50,50 @@ __FBSDID("$FreeBSD$");
#include <arm/allwinner/aw_mmc.h> #include <arm/allwinner/aw_mmc.h>
#include <dev/extres/clk/clk.h> #include <dev/extres/clk/clk.h>
#include <dev/extres/hwreset/hwreset.h> #include <dev/extres/hwreset/hwreset.h>
#include <dev/extres/regulator/regulator.h>
#define AW_MMC_MEMRES 0 #define AW_MMC_MEMRES 0
#define AW_MMC_IRQRES 1 #define AW_MMC_IRQRES 1
#define AW_MMC_RESSZ 2 #define AW_MMC_RESSZ 2
#define AW_MMC_DMA_SEGS ((MAXPHYS / PAGE_SIZE) + 1) #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_DMA_FTRGLEVEL 0x20070008
#define AW_MMC_RESET_RETRY 1000 #define AW_MMC_RESET_RETRY 1000
#define CARD_ID_FREQUENCY 400000 #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[] = { static struct ofw_compat_data compat_data[] = {
{"allwinner,sun4i-a10-mmc", 1}, {"allwinner,sun4i-a10-mmc", (uintptr_t)&a10_mmc_conf},
{"allwinner,sun5i-a13-mmc", 1}, {"allwinner,sun5i-a13-mmc", (uintptr_t)&a13_mmc_conf},
{"allwinner,sun7i-a20-mmc", 1}, {"allwinner,sun7i-a20-mmc", (uintptr_t)&a13_mmc_conf},
{"allwinner,sun50i-a64-mmc", 1}, {"allwinner,sun50i-a64-mmc", (uintptr_t)&a64_mmc_conf},
{"allwinner,sun50i-a64-emmc", (uintptr_t)&a64_emmc_conf},
{NULL, 0} {NULL, 0}
}; };
@ -82,9 +110,13 @@ struct aw_mmc_softc {
struct mmc_request * aw_req; struct mmc_request * aw_req;
struct mtx aw_mtx; struct mtx aw_mtx;
struct resource * aw_res[AW_MMC_RESSZ]; struct resource * aw_res[AW_MMC_RESSZ];
struct aw_mmc_conf * aw_mmc_conf;
uint32_t aw_intr; uint32_t aw_intr;
uint32_t aw_intr_wait; uint32_t aw_intr_wait;
void * aw_intrhand; void * aw_intrhand;
int32_t aw_vdd;
regulator_t aw_reg_vmmc;
regulator_t aw_reg_vqmmc;
/* Fields required for DMA access. */ /* Fields required for DMA access. */
bus_addr_t aw_dma_desc_phys; bus_addr_t aw_dma_desc_phys;
@ -151,6 +183,9 @@ aw_mmc_attach(device_t dev)
node = ofw_bus_get_node(dev); node = ofw_bus_get_node(dev);
sc = device_get_softc(dev); sc = device_get_softc(dev);
sc->aw_dev = 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; sc->aw_req = NULL;
if (bus_alloc_resources(dev, aw_mmc_res_spec, sc->aw_res) != 0) { if (bus_alloc_resources(dev, aw_mmc_res_spec, sc->aw_res) != 0) {
device_printf(dev, "cannot allocate device resources\n"); 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) if (OF_getencprop(node, "bus-width", &bus_width, sizeof(uint32_t)) <= 0)
bus_width = 4; 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_min = 400000;
sc->aw_host.f_max = 52000000; sc->aw_host.f_max = 52000000;
sc->aw_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340; 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 | MMC_CAP_UHS_SDR12 |
sc->aw_host.caps = MMC_CAP_HSPEED; 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) if (bus_width >= 4)
sc->aw_host.caps |= MMC_CAP_4_BIT_DATA; sc->aw_host.caps |= MMC_CAP_4_BIT_DATA;
if (bus_width >= 8) 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), error = bus_dma_tag_create(bus_get_dma_tag(sc->aw_dev),
AW_MMC_DMA_ALIGN, 0, AW_MMC_DMA_ALIGN, 0,
BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
AW_MMC_DMA_MAX_SIZE * AW_MMC_DMA_SEGS, AW_MMC_DMA_SEGS, sc->aw_mmc_conf->dma_xferlen * AW_MMC_DMA_SEGS, AW_MMC_DMA_SEGS,
AW_MMC_DMA_MAX_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL, sc->aw_mmc_conf->dma_xferlen, BUS_DMA_ALLOCNOW, NULL, NULL,
&sc->aw_dma_buf_tag); &sc->aw_dma_buf_tag);
if (error) if (error)
return (error); return (error);
@ -366,7 +412,7 @@ aw_mmc_prepare_dma(struct aw_mmc_softc *sc)
uint32_t val; uint32_t val;
cmd = sc->aw_req->cmd; 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); return (EFBIG);
error = bus_dmamap_load(sc->aw_dma_buf_tag, sc->aw_dma_buf_map, 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); cmd->data->data, cmd->data->len, aw_dma_cb, sc, 0);
@ -562,7 +608,8 @@ aw_mmc_intr(void *arg)
goto end; goto end;
} }
if (rint & AW_MMC_INT_ERR_BIT) { 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) if (rint & AW_MMC_INT_RESP_TIMEOUT)
sc->aw_req->cmd->error = MMC_ERR_TIMEOUT; sc->aw_req->cmd->error = MMC_ERR_TIMEOUT;
else else
@ -704,6 +751,9 @@ aw_mmc_read_ivar(device_t bus, device_t child, int which,
case MMCBR_IVAR_CAPS: case MMCBR_IVAR_CAPS:
*(int *)result = sc->aw_host.caps; *(int *)result = sc->aw_host.caps;
break; break;
case MMCBR_IVAR_TIMING:
*(int *)result = sc->aw_host.ios.timing;
break;
case MMCBR_IVAR_MAX_DATA: case MMCBR_IVAR_MAX_DATA:
*(int *)result = 65535; *(int *)result = 65535;
break; break;
@ -746,6 +796,9 @@ aw_mmc_write_ivar(device_t bus, device_t child, int which,
case MMCBR_IVAR_VDD: case MMCBR_IVAR_VDD:
sc->aw_host.ios.vdd = value; sc->aw_host.ios.vdd = value;
break; break;
case MMCBR_IVAR_TIMING:
sc->aw_host.ios.timing = value;
break;
/* These are read-only */ /* These are read-only */
case MMCBR_IVAR_CAPS: case MMCBR_IVAR_CAPS:
case MMCBR_IVAR_HOST_OCR: case MMCBR_IVAR_HOST_OCR:
@ -761,33 +814,83 @@ aw_mmc_write_ivar(device_t bus, device_t child, int which,
static int static int
aw_mmc_update_clock(struct aw_mmc_softc *sc, uint32_t clkon) aw_mmc_update_clock(struct aw_mmc_softc *sc, uint32_t clkon)
{ {
uint32_t cmdreg; uint32_t reg;
int retry; int retry;
uint32_t ckcr;
ckcr = AW_MMC_READ_4(sc, AW_MMC_CKCR); reg = AW_MMC_READ_4(sc, AW_MMC_CKCR);
ckcr &= ~(AW_MMC_CKCR_CCLK_ENB | AW_MMC_CKCR_CCLK_CTRL); reg &= ~(AW_MMC_CKCR_CCLK_ENB | AW_MMC_CKCR_CCLK_CTRL |
AW_MMC_CKCR_CCLK_MASK_DATA0);
if (clkon) 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_CMDR_WAIT_PRE_OVER;
AW_MMC_WRITE_4(sc, AW_MMC_CMDR, cmdreg); AW_MMC_WRITE_4(sc, AW_MMC_CMDR, reg);
retry = 0xfffff; retry = 0xfffff;
while (--retry > 0) {
if ((AW_MMC_READ_4(sc, AW_MMC_CMDR) & AW_MMC_CMDR_LOAD) == 0) { while (reg & AW_MMC_CMDR_LOAD && --retry > 0) {
AW_MMC_WRITE_4(sc, AW_MMC_RISR, 0xffffffff); reg = AW_MMC_READ_4(sc, AW_MMC_CMDR);
return (0);
}
DELAY(10); DELAY(10);
} }
AW_MMC_WRITE_4(sc, AW_MMC_RISR, 0xffffffff); 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 static int
@ -796,7 +899,8 @@ aw_mmc_update_ios(device_t bus, device_t child)
int error; int error;
struct aw_mmc_softc *sc; struct aw_mmc_softc *sc;
struct mmc_ios *ios; struct mmc_ios *ios;
uint32_t ckcr; unsigned int clock;
uint32_t reg, div = 1;
sc = device_get_softc(bus); sc = device_get_softc(bus);
@ -815,28 +919,67 @@ aw_mmc_update_ios(device_t bus, device_t child)
break; 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) { if (ios->clock) {
clock = ios->clock;
/* Disable clock */ /* Disable clock */
error = aw_mmc_update_clock(sc, 0); error = aw_mmc_update_clock(sc, 0);
if (error != 0) if (error != 0)
return (error); 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. */ /* Reset the divider. */
ckcr = AW_MMC_READ_4(sc, AW_MMC_CKCR); reg = AW_MMC_READ_4(sc, AW_MMC_CKCR);
ckcr &= ~AW_MMC_CKCR_CCLK_DIV; reg &= ~AW_MMC_CKCR_CCLK_DIV;
AW_MMC_WRITE_4(sc, AW_MMC_CKCR, ckcr); 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. */ /* 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); CLK_SET_ROUND_DOWN);
if (error != 0) { if (error != 0) {
device_printf(sc->aw_dev, device_printf(sc->aw_dev,
"failed to set frequency to %u Hz: %d\n", "failed to set frequency to %u Hz: %d\n",
ios->clock, error); clock, error);
return (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. */ /* Enable clock. */
error = aw_mmc_update_clock(sc, 1); error = aw_mmc_update_clock(sc, 1);
if (error != 0) if (error != 0)

View File

@ -47,13 +47,22 @@
#define AW_MMC_STAR 0x3C /* Status Register */ #define AW_MMC_STAR 0x3C /* Status Register */
#define AW_MMC_FWLR 0x40 /* FIFO Threshold Watermark Register */ #define AW_MMC_FWLR 0x40 /* FIFO Threshold Watermark Register */
#define AW_MMC_FUNS 0x44 /* Function Select 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_DMAC 0x80 /* IDMAC Control Register */
#define AW_MMC_DLBA 0x84 /* IDMAC Desc List Base Address Reg */ #define AW_MMC_DLBA 0x84 /* IDMAC Desc List Base Address Reg */
#define AW_MMC_IDST 0x88 /* IDMAC Status Register */ #define AW_MMC_IDST 0x88 /* IDMAC Status Register */
#define AW_MMC_IDIE 0x8C /* IDMAC Interrupt Enable 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 */ /* AW_MMC_GCTL */
#define AW_MMC_CTRL_SOFT_RST (1U << 0) #define AW_MMC_CTRL_SOFT_RST (1U << 0)
@ -70,6 +79,7 @@
/* AW_MMC_CKCR */ /* AW_MMC_CKCR */
#define AW_MMC_CKCR_CCLK_ENB (1U << 16) #define AW_MMC_CKCR_CCLK_ENB (1U << 16)
#define AW_MMC_CKCR_CCLK_CTRL (1U << 17) #define AW_MMC_CKCR_CCLK_CTRL (1U << 17)
#define AW_MMC_CKCR_CCLK_MASK_DATA0 (1U << 31)
#define AW_MMC_CKCR_CCLK_DIV 0xff #define AW_MMC_CKCR_CCLK_DIV 0xff
/* AW_MMC_TMOR */ /* AW_MMC_TMOR */
@ -153,6 +163,9 @@
#define AW_MMC_SEND_AUTOSTOP_CC_SD (1U << 9) #define AW_MMC_SEND_AUTOSTOP_CC_SD (1U << 9)
#define AW_MMC_CE_ATA_DEV_INT_ENB (1U << 10) #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 */ /* IDMA CONTROLLER BUS MOD BIT FIELD */
#define AW_MMC_DMAC_IDMAC_SOFT_RST (1U << 0) #define AW_MMC_DMAC_IDMAC_SOFT_RST (1U << 0)
#define AW_MMC_DMAC_IDMAC_FIX_BURST (1U << 1) #define AW_MMC_DMAC_IDMAC_FIX_BURST (1U << 1)
@ -184,6 +197,12 @@
#define AW_MMC_IDST_COMPLETE \ #define AW_MMC_IDST_COMPLETE \
(AW_MMC_IDST_TX_INT | AW_MMC_IDST_RX_INT) (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. */ /* The DMA descriptor table. */
struct aw_mmc_dma_desc { struct aw_mmc_dma_desc {
uint32_t config; uint32_t config;