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:
parent
512f29d141
commit
ce0618bea2
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=327979
@ -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 which,
|
||||
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 which,
|
||||
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,33 +814,83 @@ aw_mmc_write_ivar(device_t bus, device_t child, int which,
|
||||
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
|
||||
@ -796,7 +899,8 @@ 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,28 +919,67 @@ 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);
|
||||
if (error != 0)
|
||||
|
@ -47,13 +47,22 @@
|
||||
#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)
|
||||
@ -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)
|
||||
@ -184,6 +197,12 @@
|
||||
#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 {
|
||||
uint32_t config;
|
||||
|
Loading…
Reference in New Issue
Block a user