Changeset View
Changeset View
Standalone View
Standalone View
sys/arm/allwinner/a10_mmc.c
Show First 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | |||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#include <dev/mmc/bridge.h> | #include <dev/mmc/bridge.h> | ||||
#include <dev/mmc/mmcreg.h> | #include <dev/mmc/mmcreg.h> | ||||
#include <dev/mmc/mmcbrvar.h> | #include <dev/mmc/mmcbrvar.h> | ||||
#include <arm/allwinner/allwinner_machdep.h> | #include <arm/allwinner/allwinner_machdep.h> | ||||
#include <arm/allwinner/a10_clk.h> | |||||
#include <arm/allwinner/a10_mmc.h> | #include <arm/allwinner/a10_mmc.h> | ||||
#include <arm/allwinner/a31/a31_clk.h> | #include <dev/extres/clk/clk.h> | ||||
#include <dev/extres/hwreset/hwreset.h> | |||||
#define A10_MMC_MEMRES 0 | #define A10_MMC_MEMRES 0 | ||||
#define A10_MMC_IRQRES 1 | #define A10_MMC_IRQRES 1 | ||||
#define A10_MMC_RESSZ 2 | #define A10_MMC_RESSZ 2 | ||||
#define A10_MMC_DMA_SEGS 16 | #define A10_MMC_DMA_SEGS 16 | ||||
#define A10_MMC_DMA_MAX_SIZE 0x2000 | #define A10_MMC_DMA_MAX_SIZE 0x2000 | ||||
#define A10_MMC_DMA_FTRGLEVEL 0x20070008 | #define A10_MMC_DMA_FTRGLEVEL 0x20070008 | ||||
#define CARD_ID_FREQUENCY 400000 | |||||
static int a10_mmc_pio_mode = 0; | static int a10_mmc_pio_mode = 0; | ||||
TUNABLE_INT("hw.a10.mmc.pio_mode", &a10_mmc_pio_mode); | TUNABLE_INT("hw.a10.mmc.pio_mode", &a10_mmc_pio_mode); | ||||
static struct ofw_compat_data compat_data[] = { | static struct ofw_compat_data compat_data[] = { | ||||
{"allwinner,sun4i-a10-mmc", 1}, | {"allwinner,sun4i-a10-mmc", 1}, | ||||
{"allwinner,sun5i-a13-mmc", 1}, | {"allwinner,sun5i-a13-mmc", 1}, | ||||
{NULL, 0} | {NULL, 0} | ||||
}; | }; | ||||
struct a10_mmc_softc { | struct a10_mmc_softc { | ||||
bus_space_handle_t a10_bsh; | bus_space_handle_t a10_bsh; | ||||
bus_space_tag_t a10_bst; | bus_space_tag_t a10_bst; | ||||
device_t a10_dev; | device_t a10_dev; | ||||
clk_t a10_clk_ahb; | |||||
clk_t a10_clk_mmc; | |||||
hwreset_t a10_rst_ahb; | |||||
int a10_bus_busy; | int a10_bus_busy; | ||||
int a10_id; | int a10_id; | ||||
int a10_resid; | int a10_resid; | ||||
int a10_timeout; | int a10_timeout; | ||||
struct callout a10_timeoutc; | struct callout a10_timeoutc; | ||||
struct mmc_host a10_host; | struct mmc_host a10_host; | ||||
struct mmc_request * a10_req; | struct mmc_request * a10_req; | ||||
struct mtx a10_mtx; | struct mtx a10_mtx; | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
a10_mmc_attach(device_t dev) | a10_mmc_attach(device_t dev) | ||||
{ | { | ||||
device_t child; | device_t child; | ||||
struct a10_mmc_softc *sc; | struct a10_mmc_softc *sc; | ||||
struct sysctl_ctx_list *ctx; | struct sysctl_ctx_list *ctx; | ||||
struct sysctl_oid_list *tree; | struct sysctl_oid_list *tree; | ||||
int clk; | int error; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
sc->a10_dev = dev; | sc->a10_dev = dev; | ||||
sc->a10_req = NULL; | sc->a10_req = NULL; | ||||
sc->a10_id = device_get_unit(dev); | sc->a10_id = device_get_unit(dev); | ||||
if (sc->a10_id > 3) { | if (sc->a10_id > 3) { | ||||
device_printf(dev, "only 4 hosts are supported (0-3)\n"); | device_printf(dev, "only 4 hosts are supported (0-3)\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
if (bus_alloc_resources(dev, a10_mmc_res_spec, sc->a10_res) != 0) { | if (bus_alloc_resources(dev, a10_mmc_res_spec, sc->a10_res) != 0) { | ||||
device_printf(dev, "cannot allocate device resources\n"); | device_printf(dev, "cannot allocate device resources\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
sc->a10_bst = rman_get_bustag(sc->a10_res[A10_MMC_MEMRES]); | sc->a10_bst = rman_get_bustag(sc->a10_res[A10_MMC_MEMRES]); | ||||
sc->a10_bsh = rman_get_bushandle(sc->a10_res[A10_MMC_MEMRES]); | sc->a10_bsh = rman_get_bushandle(sc->a10_res[A10_MMC_MEMRES]); | ||||
if (bus_setup_intr(dev, sc->a10_res[A10_MMC_IRQRES], | if (bus_setup_intr(dev, sc->a10_res[A10_MMC_IRQRES], | ||||
INTR_TYPE_MISC | INTR_MPSAFE, NULL, a10_mmc_intr, sc, | INTR_TYPE_MISC | INTR_MPSAFE, NULL, a10_mmc_intr, sc, | ||||
&sc->a10_intrhand)) { | &sc->a10_intrhand)) { | ||||
bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res); | bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res); | ||||
device_printf(dev, "cannot setup interrupt handler\n"); | device_printf(dev, "cannot setup interrupt handler\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
mtx_init(&sc->a10_mtx, device_get_nameunit(sc->a10_dev), "a10_mmc", | |||||
MTX_DEF); | |||||
callout_init_mtx(&sc->a10_timeoutc, &sc->a10_mtx, 0); | |||||
/* | /* | ||||
* Later chips use a different FIFO offset. Unfortunately the FDT | * Later chips use a different FIFO offset. Unfortunately the FDT | ||||
* uses the same compatible string for old and new implementations. | * uses the same compatible string for old and new implementations. | ||||
*/ | */ | ||||
switch (allwinner_soc_family()) { | switch (allwinner_soc_family()) { | ||||
case ALLWINNERSOC_SUN4I: | case ALLWINNERSOC_SUN4I: | ||||
case ALLWINNERSOC_SUN5I: | case ALLWINNERSOC_SUN5I: | ||||
case ALLWINNERSOC_SUN7I: | case ALLWINNERSOC_SUN7I: | ||||
sc->a10_fifo_reg = A10_MMC_FIFO; | sc->a10_fifo_reg = A10_MMC_FIFO; | ||||
break; | break; | ||||
default: | default: | ||||
sc->a10_fifo_reg = A31_MMC_FIFO; | sc->a10_fifo_reg = A31_MMC_FIFO; | ||||
break; | break; | ||||
} | } | ||||
/* De-assert reset */ | |||||
if (hwreset_get_by_ofw_name(dev, "ahb", &sc->a10_rst_ahb) == 0) { | |||||
error = hwreset_deassert(sc->a10_rst_ahb); | |||||
if (error != 0) { | |||||
device_printf(dev, "cannot de-assert reset\n"); | |||||
return (error); | |||||
} | |||||
} | |||||
/* Activate the module clock. */ | /* Activate the module clock. */ | ||||
switch (allwinner_soc_type()) { | error = clk_get_by_ofw_name(dev, "ahb", &sc->a10_clk_ahb); | ||||
#if defined(SOC_ALLWINNER_A10) || defined(SOC_ALLWINNER_A20) | if (error != 0) { | ||||
case ALLWINNERSOC_A10: | device_printf(dev, "cannot get ahb clock\n"); | ||||
case ALLWINNERSOC_A10S: | goto fail; | ||||
case ALLWINNERSOC_A20: | |||||
clk = a10_clk_mmc_activate(sc->a10_id); | |||||
break; | |||||
#endif | |||||
#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S) | |||||
case ALLWINNERSOC_A31: | |||||
case ALLWINNERSOC_A31S: | |||||
clk = a31_clk_mmc_activate(sc->a10_id); | |||||
break; | |||||
#endif | |||||
default: | |||||
clk = -1; | |||||
} | } | ||||
if (clk != 0) { | error = clk_enable(sc->a10_clk_ahb); | ||||
bus_teardown_intr(dev, sc->a10_res[A10_MMC_IRQRES], | if (error != 0) { | ||||
sc->a10_intrhand); | device_printf(dev, "cannot enable ahb clock\n"); | ||||
bus_release_resources(dev, a10_mmc_res_spec, sc->a10_res); | goto fail; | ||||
device_printf(dev, "cannot activate mmc clock\n"); | |||||
return (ENXIO); | |||||
} | } | ||||
error = clk_get_by_ofw_name(dev, "mmc", &sc->a10_clk_mmc); | |||||
if (error != 0) { | |||||
device_printf(dev, "cannot get mmc clock\n"); | |||||
goto fail; | |||||
} | |||||
error = clk_set_freq(sc->a10_clk_mmc, CARD_ID_FREQUENCY, | |||||
CLK_SET_ROUND_DOWN); | |||||
if (error != 0) { | |||||
device_printf(dev, "cannot init mmc clock\n"); | |||||
goto fail; | |||||
} | |||||
error = clk_enable(sc->a10_clk_mmc); | |||||
if (error != 0) { | |||||
device_printf(dev, "cannot enable mmc clock\n"); | |||||
goto fail; | |||||
} | |||||
sc->a10_timeout = 10; | sc->a10_timeout = 10; | ||||
ctx = device_get_sysctl_ctx(dev); | ctx = device_get_sysctl_ctx(dev); | ||||
tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); | tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); | ||||
SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "req_timeout", CTLFLAG_RW, | SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "req_timeout", CTLFLAG_RW, | ||||
&sc->a10_timeout, 0, "Request timeout in seconds"); | &sc->a10_timeout, 0, "Request timeout in seconds"); | ||||
mtx_init(&sc->a10_mtx, device_get_nameunit(sc->a10_dev), "a10_mmc", | |||||
MTX_DEF); | |||||
callout_init_mtx(&sc->a10_timeoutc, &sc->a10_mtx, 0); | |||||
/* Reset controller. */ | /* Reset controller. */ | ||||
if (a10_mmc_reset(sc) != 0) { | if (a10_mmc_reset(sc) != 0) { | ||||
device_printf(dev, "cannot reset the controller\n"); | device_printf(dev, "cannot reset the controller\n"); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
if (a10_mmc_pio_mode == 0 && a10_mmc_setup_dma(sc) != 0) { | if (a10_mmc_pio_mode == 0 && a10_mmc_setup_dma(sc) != 0) { | ||||
▲ Show 20 Lines • Show All 590 Lines • ▼ Show 20 Lines | if (ios->clock) { | ||||
/* Reset the divider. */ | /* Reset the divider. */ | ||||
clkcr &= ~A10_MMC_CLKCR_DIV; | clkcr &= ~A10_MMC_CLKCR_DIV; | ||||
A10_MMC_WRITE_4(sc, A10_MMC_CLKCR, clkcr); | A10_MMC_WRITE_4(sc, A10_MMC_CLKCR, clkcr); | ||||
error = a10_mmc_update_clock(sc); | error = a10_mmc_update_clock(sc); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
/* Set the MMC clock. */ | /* Set the MMC clock. */ | ||||
switch (allwinner_soc_type()) { | error = clk_set_freq(sc->a10_clk_mmc, ios->clock, | ||||
#if defined(SOC_ALLWINNER_A10) || defined(SOC_ALLWINNER_A20) | CLK_SET_ROUND_DOWN); | ||||
case ALLWINNERSOC_A10: | if (error != 0) { | ||||
case ALLWINNERSOC_A10S: | device_printf(sc->a10_dev, | ||||
case ALLWINNERSOC_A20: | "failed to set frequency to %u Hz: %d\n", | ||||
error = a10_clk_mmc_cfg(sc->a10_id, ios->clock); | ios->clock, error); | ||||
break; | |||||
#endif | |||||
#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S) | |||||
case ALLWINNERSOC_A31: | |||||
case ALLWINNERSOC_A31S: | |||||
error = a31_clk_mmc_cfg(sc->a10_id, ios->clock); | |||||
break; | |||||
#endif | |||||
default: | |||||
error = ENXIO; | |||||
} | |||||
if (error != 0) | |||||
return (error); | return (error); | ||||
} | |||||
/* Enable clock. */ | /* Enable clock. */ | ||||
clkcr |= A10_MMC_CARD_CLK_ON; | clkcr |= A10_MMC_CARD_CLK_ON; | ||||
A10_MMC_WRITE_4(sc, A10_MMC_CLKCR, clkcr); | A10_MMC_WRITE_4(sc, A10_MMC_CLKCR, clkcr); | ||||
error = a10_mmc_update_clock(sc); | error = a10_mmc_update_clock(sc); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 91 Lines • Show Last 20 Lines |