dwmmc: Add clock support and other improvements
* If compiled with EXT_RESOURCES look up the "biu" and "ciu" clocks in the DT * Don't use custom property "bus-frequency" but the standard one "clock-frequency" * Use the DT property max-frequency and fall back to 200Mhz if it don't exists * Add more mmc caps suported by the controller * Always ack all interrupts * Subclassed driver can supply an update_ios so they can handle update the clocks accordingly * Take care of the DDR bit in update_ios (no functional change since we do not support voltage change for now) * Make use of the FDT bus-width property
This commit is contained in:
parent
2a3d5e3364
commit
dd198e868a
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=330037
@ -56,6 +56,10 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <machine/cpu.h>
|
#include <machine/cpu.h>
|
||||||
#include <machine/intr.h>
|
#include <machine/intr.h>
|
||||||
|
|
||||||
|
#ifdef EXT_RESOURCES
|
||||||
|
#include <dev/extres/clk/clk.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <dev/mmc/host/dwmmc_reg.h>
|
#include <dev/mmc/host/dwmmc_reg.h>
|
||||||
#include <dev/mmc/host/dwmmc_var.h>
|
#include <dev/mmc/host/dwmmc_var.h>
|
||||||
|
|
||||||
@ -341,14 +345,12 @@ dwmmc_intr(void *arg)
|
|||||||
dprintf("%s 0x%08x\n", __func__, reg);
|
dprintf("%s 0x%08x\n", __func__, reg);
|
||||||
|
|
||||||
if (reg & DWMMC_CMD_ERR_FLAGS) {
|
if (reg & DWMMC_CMD_ERR_FLAGS) {
|
||||||
WRITE4(sc, SDMMC_RINTSTS, DWMMC_CMD_ERR_FLAGS);
|
|
||||||
dprintf("cmd err 0x%08x cmd 0x%08x\n",
|
dprintf("cmd err 0x%08x cmd 0x%08x\n",
|
||||||
reg, cmd->opcode);
|
reg, cmd->opcode);
|
||||||
cmd->error = MMC_ERR_TIMEOUT;
|
cmd->error = MMC_ERR_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reg & DWMMC_DATA_ERR_FLAGS) {
|
if (reg & DWMMC_DATA_ERR_FLAGS) {
|
||||||
WRITE4(sc, SDMMC_RINTSTS, DWMMC_DATA_ERR_FLAGS);
|
|
||||||
dprintf("data err 0x%08x cmd 0x%08x\n",
|
dprintf("data err 0x%08x cmd 0x%08x\n",
|
||||||
reg, cmd->opcode);
|
reg, cmd->opcode);
|
||||||
cmd->error = MMC_ERR_FAILED;
|
cmd->error = MMC_ERR_FAILED;
|
||||||
@ -361,25 +363,22 @@ dwmmc_intr(void *arg)
|
|||||||
if (reg & SDMMC_INTMASK_CMD_DONE) {
|
if (reg & SDMMC_INTMASK_CMD_DONE) {
|
||||||
dwmmc_cmd_done(sc);
|
dwmmc_cmd_done(sc);
|
||||||
sc->cmd_done = 1;
|
sc->cmd_done = 1;
|
||||||
WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_CMD_DONE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reg & SDMMC_INTMASK_ACD) {
|
if (reg & SDMMC_INTMASK_ACD)
|
||||||
sc->acd_rcvd = 1;
|
sc->acd_rcvd = 1;
|
||||||
WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_ACD);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reg & SDMMC_INTMASK_DTO) {
|
if (reg & SDMMC_INTMASK_DTO)
|
||||||
sc->dto_rcvd = 1;
|
sc->dto_rcvd = 1;
|
||||||
WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_DTO);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reg & SDMMC_INTMASK_CD) {
|
if (reg & SDMMC_INTMASK_CD) {
|
||||||
/* XXX: Handle card detect */
|
/* XXX: Handle card detect */
|
||||||
WRITE4(sc, SDMMC_RINTSTS, SDMMC_INTMASK_CD);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ack interrupts */
|
||||||
|
WRITE4(sc, SDMMC_RINTSTS, reg);
|
||||||
|
|
||||||
if (sc->use_pio) {
|
if (sc->use_pio) {
|
||||||
if (reg & (SDMMC_INTMASK_RXDR|SDMMC_INTMASK_DTO)) {
|
if (reg & (SDMMC_INTMASK_RXDR|SDMMC_INTMASK_DTO)) {
|
||||||
pio_read(sc, cmd);
|
pio_read(sc, cmd);
|
||||||
@ -411,37 +410,88 @@ parse_fdt(struct dwmmc_softc *sc)
|
|||||||
{
|
{
|
||||||
pcell_t dts_value[3];
|
pcell_t dts_value[3];
|
||||||
phandle_t node;
|
phandle_t node;
|
||||||
|
uint32_t bus_hz = 0, bus_width;
|
||||||
int len;
|
int len;
|
||||||
|
#ifdef EXT_RESOURCES
|
||||||
|
int error;
|
||||||
|
#endif
|
||||||
|
|
||||||
if ((node = ofw_bus_get_node(sc->dev)) == -1)
|
if ((node = ofw_bus_get_node(sc->dev)) == -1)
|
||||||
return (ENXIO);
|
return (ENXIO);
|
||||||
|
|
||||||
|
/* bus-width */
|
||||||
|
if (OF_getencprop(node, "bus-width", &bus_width, sizeof(uint32_t)) <= 0)
|
||||||
|
bus_width = 4;
|
||||||
|
if (bus_width >= 4)
|
||||||
|
sc->host.caps |= MMC_CAP_4_BIT_DATA;
|
||||||
|
if (bus_width >= 8)
|
||||||
|
sc->host.caps |= MMC_CAP_8_BIT_DATA;
|
||||||
|
|
||||||
|
/* max-frequency */
|
||||||
|
if (OF_getencprop(node, "max-frequency", &sc->max_hz, sizeof(uint32_t)) <= 0)
|
||||||
|
sc->max_hz = 200000000;
|
||||||
|
|
||||||
/* fifo-depth */
|
/* fifo-depth */
|
||||||
if ((len = OF_getproplen(node, "fifo-depth")) > 0) {
|
if ((len = OF_getproplen(node, "fifo-depth")) > 0) {
|
||||||
OF_getencprop(node, "fifo-depth", dts_value, len);
|
OF_getencprop(node, "fifo-depth", dts_value, len);
|
||||||
sc->fifo_depth = dts_value[0];
|
sc->fifo_depth = dts_value[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* num-slots */
|
/* num-slots (Deprecated) */
|
||||||
sc->num_slots = 1;
|
sc->num_slots = 1;
|
||||||
if ((len = OF_getproplen(node, "num-slots")) > 0) {
|
if ((len = OF_getproplen(node, "num-slots")) > 0) {
|
||||||
|
device_printf(sc->dev, "num-slots property is deprecated\n");
|
||||||
OF_getencprop(node, "num-slots", dts_value, len);
|
OF_getencprop(node, "num-slots", dts_value, len);
|
||||||
sc->num_slots = dts_value[0];
|
sc->num_slots = dts_value[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* clock-frequency */
|
||||||
|
if ((len = OF_getproplen(node, "clock-frequency")) > 0) {
|
||||||
|
OF_getencprop(node, "clock-frequency", dts_value, len);
|
||||||
|
bus_hz = dts_value[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef EXT_RESOURCES
|
||||||
|
/* BIU (Bus Interface Unit clock) is optional */
|
||||||
|
error = clk_get_by_ofw_name(sc->dev, 0, "biu", &sc->biu);
|
||||||
|
if (sc->biu) {
|
||||||
|
error = clk_enable(sc->biu);
|
||||||
|
if (error != 0) {
|
||||||
|
device_printf(sc->dev, "cannot enable biu clock\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need some platform-specific code to know
|
* CIU (Controller Interface Unit clock) is mandatory
|
||||||
* what the clock is supplied for our device.
|
* if no clock-frequency property is given
|
||||||
* For now rely on the value specified in FDT.
|
|
||||||
*/
|
*/
|
||||||
|
error = clk_get_by_ofw_name(sc->dev, 0, "ciu", &sc->ciu);
|
||||||
|
if (sc->ciu) {
|
||||||
|
error = clk_enable(sc->ciu);
|
||||||
|
if (error != 0) {
|
||||||
|
device_printf(sc->dev, "cannot enable ciu clock\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (bus_hz != 0) {
|
||||||
|
error = clk_set_freq(sc->ciu, bus_hz, 0);
|
||||||
|
if (error != 0)
|
||||||
|
device_printf(sc->dev,
|
||||||
|
"cannot set ciu clock to %u\n", bus_hz);
|
||||||
|
}
|
||||||
|
clk_get_freq(sc->ciu, &sc->bus_hz);
|
||||||
|
}
|
||||||
|
#endif /* EXT_RESOURCES */
|
||||||
|
|
||||||
if (sc->bus_hz == 0) {
|
if (sc->bus_hz == 0) {
|
||||||
if ((len = OF_getproplen(node, "bus-frequency")) <= 0)
|
device_printf(sc->dev, "No bus speed provided\n");
|
||||||
return (ENXIO);
|
goto fail;
|
||||||
OF_getencprop(node, "bus-frequency", dts_value, len);
|
|
||||||
sc->bus_hz = dts_value[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return (ENXIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -541,9 +591,10 @@ dwmmc_attach(device_t dev)
|
|||||||
WRITE4(sc, SDMMC_CTRL, SDMMC_CTRL_INT_ENABLE);
|
WRITE4(sc, SDMMC_CTRL, SDMMC_CTRL_INT_ENABLE);
|
||||||
|
|
||||||
sc->host.f_min = 400000;
|
sc->host.f_min = 400000;
|
||||||
sc->host.f_max = min(200000000, sc->bus_hz);
|
sc->host.f_max = sc->max_hz;
|
||||||
sc->host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
|
sc->host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
|
||||||
sc->host.caps = MMC_CAP_4_BIT_DATA;
|
sc->host.caps |= MMC_CAP_HSPEED;
|
||||||
|
sc->host.caps |= MMC_CAP_SIGNALING_330;
|
||||||
|
|
||||||
device_add_child(dev, "mmc", -1);
|
device_add_child(dev, "mmc", -1);
|
||||||
return (bus_generic_attach(dev));
|
return (bus_generic_attach(dev));
|
||||||
@ -608,6 +659,8 @@ dwmmc_update_ios(device_t brdev, device_t reqdev)
|
|||||||
{
|
{
|
||||||
struct dwmmc_softc *sc;
|
struct dwmmc_softc *sc;
|
||||||
struct mmc_ios *ios;
|
struct mmc_ios *ios;
|
||||||
|
uint32_t reg;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
sc = device_get_softc(brdev);
|
sc = device_get_softc(brdev);
|
||||||
ios = &sc->host.ios;
|
ios = &sc->host.ios;
|
||||||
@ -615,8 +668,6 @@ dwmmc_update_ios(device_t brdev, device_t reqdev)
|
|||||||
dprintf("Setting up clk %u bus_width %d\n",
|
dprintf("Setting up clk %u bus_width %d\n",
|
||||||
ios->clock, ios->bus_width);
|
ios->clock, ios->bus_width);
|
||||||
|
|
||||||
dwmmc_setup_bus(sc, ios->clock);
|
|
||||||
|
|
||||||
if (ios->bus_width == bus_width_8)
|
if (ios->bus_width == bus_width_8)
|
||||||
WRITE4(sc, SDMMC_CTYPE, SDMMC_CTYPE_8BIT);
|
WRITE4(sc, SDMMC_CTYPE, SDMMC_CTYPE_8BIT);
|
||||||
else if (ios->bus_width == bus_width_4)
|
else if (ios->bus_width == bus_width_4)
|
||||||
@ -629,15 +680,22 @@ dwmmc_update_ios(device_t brdev, device_t reqdev)
|
|||||||
WRITE4(sc, SDMMC_CLKSEL, sc->sdr_timing);
|
WRITE4(sc, SDMMC_CLKSEL, sc->sdr_timing);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Set DDR mode */
|
||||||
* XXX: take care about DDR bit
|
reg = READ4(sc, SDMMC_UHS_REG);
|
||||||
*
|
if (ios->timing == bus_timing_uhs_ddr50 ||
|
||||||
* reg = READ4(sc, SDMMC_UHS_REG);
|
ios->timing == bus_timing_mmc_ddr52 ||
|
||||||
* reg |= (SDMMC_UHS_REG_DDR);
|
ios->timing == bus_timing_mmc_hs400)
|
||||||
* WRITE4(sc, SDMMC_UHS_REG, reg);
|
reg |= (SDMMC_UHS_REG_DDR);
|
||||||
*/
|
else
|
||||||
|
reg &= ~(SDMMC_UHS_REG_DDR);
|
||||||
|
WRITE4(sc, SDMMC_UHS_REG, reg);
|
||||||
|
|
||||||
return (0);
|
if (sc->update_ios)
|
||||||
|
ret = sc->update_ios(sc, ios);
|
||||||
|
|
||||||
|
dwmmc_setup_bus(sc, ios->clock);
|
||||||
|
|
||||||
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -1032,7 +1090,6 @@ dwmmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
|
|||||||
*(int *)result = sc->host.ios.vdd;
|
*(int *)result = sc->host.ios.vdd;
|
||||||
break;
|
break;
|
||||||
case MMCBR_IVAR_CAPS:
|
case MMCBR_IVAR_CAPS:
|
||||||
sc->host.caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
|
|
||||||
*(int *)result = sc->host.caps;
|
*(int *)result = sc->host.caps;
|
||||||
break;
|
break;
|
||||||
case MMCBR_IVAR_MAX_DATA:
|
case MMCBR_IVAR_MAX_DATA:
|
||||||
|
@ -33,6 +33,10 @@
|
|||||||
#ifndef DEV_MMC_HOST_DWMMC_VAR_H
|
#ifndef DEV_MMC_HOST_DWMMC_VAR_H
|
||||||
#define DEV_MMC_HOST_DWMMC_VAR_H
|
#define DEV_MMC_HOST_DWMMC_VAR_H
|
||||||
|
|
||||||
|
#ifdef EXT_RESOURCES
|
||||||
|
#include <dev/extres/clk/clk.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
HWTYPE_NONE,
|
HWTYPE_NONE,
|
||||||
HWTYPE_ALTERA,
|
HWTYPE_ALTERA,
|
||||||
@ -56,6 +60,8 @@ struct dwmmc_softc {
|
|||||||
uint32_t pwren_inverted;
|
uint32_t pwren_inverted;
|
||||||
u_int desc_count;
|
u_int desc_count;
|
||||||
|
|
||||||
|
int (*update_ios)(struct dwmmc_softc *sc, struct mmc_ios *ios);
|
||||||
|
|
||||||
bus_dma_tag_t desc_tag;
|
bus_dma_tag_t desc_tag;
|
||||||
bus_dmamap_t desc_map;
|
bus_dmamap_t desc_map;
|
||||||
struct idmac_desc *desc_ring;
|
struct idmac_desc *desc_ring;
|
||||||
@ -67,11 +73,17 @@ struct dwmmc_softc {
|
|||||||
uint32_t dto_rcvd;
|
uint32_t dto_rcvd;
|
||||||
uint32_t acd_rcvd;
|
uint32_t acd_rcvd;
|
||||||
uint32_t cmd_done;
|
uint32_t cmd_done;
|
||||||
uint32_t bus_hz;
|
uint64_t bus_hz;
|
||||||
|
uint32_t max_hz;
|
||||||
uint32_t fifo_depth;
|
uint32_t fifo_depth;
|
||||||
uint32_t num_slots;
|
uint32_t num_slots;
|
||||||
uint32_t sdr_timing;
|
uint32_t sdr_timing;
|
||||||
uint32_t ddr_timing;
|
uint32_t ddr_timing;
|
||||||
|
|
||||||
|
#ifdef EXT_RESOURCES
|
||||||
|
clk_t biu;
|
||||||
|
clk_t ciu;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
DECLARE_CLASS(dwmmc_driver);
|
DECLARE_CLASS(dwmmc_driver);
|
||||||
|
Loading…
Reference in New Issue
Block a user