Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/sdhci/sdhci_fsl_fdt.c
Show First 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | |||||
#define SDHCI_FSL_CLK_PRESCALE_MASK 0x0000ff00 | #define SDHCI_FSL_CLK_PRESCALE_MASK 0x0000ff00 | ||||
#define SDHCI_FSL_CLK_PRESCALE_SHIFT 8 | #define SDHCI_FSL_CLK_PRESCALE_SHIFT 8 | ||||
#define SDHCI_FSL_WTMK_LVL 0x44 | #define SDHCI_FSL_WTMK_LVL 0x44 | ||||
#define SDHCI_FSL_WTMK_RD_512B (0 << 0) | #define SDHCI_FSL_WTMK_RD_512B (0 << 0) | ||||
#define SDHCI_FSL_WTMK_WR_512B (0 << 15) | #define SDHCI_FSL_WTMK_WR_512B (0 << 15) | ||||
#define SDHCI_FSL_HOST_VERSION 0xfc | #define SDHCI_FSL_HOST_VERSION 0xfc | ||||
#define SDHCI_FSL_VENDOR_V23 0x13 | |||||
#define SDHCI_FSL_CAPABILITIES2 0x114 | #define SDHCI_FSL_CAPABILITIES2 0x114 | ||||
#define SDHCI_FSL_TBCTL 0x120 | #define SDHCI_FSL_TBCTL 0x120 | ||||
#define SDHCI_FSL_TBCTL_TBEN (1 << 2) | #define SDHCI_FSL_TBCTL_TBEN (1 << 2) | ||||
#define SDHCI_FSL_ESDHC_CTRL 0x40c | #define SDHCI_FSL_ESDHC_CTRL 0x40c | ||||
#define SDHCI_FSL_ESDHC_CTRL_SNOOP (1 << 6) | #define SDHCI_FSL_ESDHC_CTRL_SNOOP (1 << 6) | ||||
#define SDHCI_FSL_ESDHC_CTRL_CLK_DIV2 (1 << 19) | #define SDHCI_FSL_ESDHC_CTRL_CLK_DIV2 (1 << 19) | ||||
Show All 10 Lines | struct sdhci_fsl_fdt_softc { | ||||
uint32_t baseclk_hz; | uint32_t baseclk_hz; | ||||
uint32_t maxclk_hz; | uint32_t maxclk_hz; | ||||
struct sdhci_fdt_gpio *gpio; | struct sdhci_fdt_gpio *gpio; | ||||
struct sdhci_slot slot; | struct sdhci_slot slot; | ||||
bool slot_init_done; | bool slot_init_done; | ||||
uint32_t cmd_and_mode; | uint32_t cmd_and_mode; | ||||
uint16_t sdclk_bits; | uint16_t sdclk_bits; | ||||
struct mmc_helper fdt_helper; | struct mmc_helper fdt_helper; | ||||
uint8_t vendor_ver; | |||||
uint32_t (* read)(struct sdhci_fsl_fdt_softc *, bus_size_t); | uint32_t (* read)(struct sdhci_fsl_fdt_softc *, bus_size_t); | ||||
void (* write)(struct sdhci_fsl_fdt_softc *, bus_size_t, uint32_t); | void (* write)(struct sdhci_fsl_fdt_softc *, bus_size_t, uint32_t); | ||||
}; | }; | ||||
struct sdhci_fsl_fdt_soc_data { | struct sdhci_fsl_fdt_soc_data { | ||||
int quirks; | int quirks; | ||||
int baseclk_div; | int baseclk_div; | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | sdhci_fsl_fdt_get_clock(struct sdhci_fsl_fdt_softc *sc) | ||||
if (RD4(sc, SDHCI_FSL_PRES_STATE) & SDHCI_FSL_PRES_SDSTB) | if (RD4(sc, SDHCI_FSL_PRES_STATE) & SDHCI_FSL_PRES_SDSTB) | ||||
val |= SDHCI_CLOCK_INT_STABLE; | val |= SDHCI_CLOCK_INT_STABLE; | ||||
if (RD4(sc, SDHCI_FSL_SYS_CTRL) & SDHCI_FSL_CLK_SDCLKEN) | if (RD4(sc, SDHCI_FSL_SYS_CTRL) & SDHCI_FSL_CLK_SDCLKEN) | ||||
val |= SDHCI_CLOCK_CARD_EN; | val |= SDHCI_CLOCK_CARD_EN; | ||||
return (val); | return (val); | ||||
} | } | ||||
/* | |||||
* Calculate clock prescaler and divisor values based on the following formula: | |||||
* `frequency = base clock / (prescaler * divisor)`. | |||||
*/ | |||||
#define SDHCI_FSL_FDT_CLK_DIV(sc, base, freq, pre, div) \ | |||||
do { \ | |||||
(pre) = (sc)->vendor_ver < SDHCI_FSL_VENDOR_V23 ? 2 : 1;\ | |||||
while ((freq) < (base) / ((pre) * 16) && (pre) < 256) \ | |||||
(pre) <<= 1; \ | |||||
/* div/pre can't both be set to 1, according to PM. */ \ | |||||
(div) = ((pre) == 1 ? 2 : 1); \ | |||||
while ((freq) < (base) / ((pre) * (div)) && (div) < 16) \ | |||||
++(div); \ | |||||
} while (0) | |||||
static void | static void | ||||
fsl_sdhc_fdt_set_clock(struct sdhci_fsl_fdt_softc *sc, uint16_t val) | fsl_sdhc_fdt_set_clock(struct sdhci_fsl_fdt_softc *sc, struct sdhci_slot *slot, | ||||
uint16_t val) | |||||
{ | { | ||||
uint32_t div, freq, prescale, val32; | uint32_t prescale, div, val32; | ||||
sc->sdclk_bits = val & SDHCI_DIVIDERS_MASK; | sc->sdclk_bits = val & SDHCI_DIVIDERS_MASK; | ||||
val32 = RD4(sc, SDHCI_CLOCK_CONTROL); | val32 = RD4(sc, SDHCI_CLOCK_CONTROL); | ||||
if ((val & SDHCI_CLOCK_CARD_EN) == 0) { | if ((val & SDHCI_CLOCK_CARD_EN) == 0) { | ||||
WR4(sc, SDHCI_CLOCK_CONTROL, val32 & ~SDHCI_FSL_CLK_SDCLKEN); | WR4(sc, SDHCI_CLOCK_CONTROL, val32 & ~SDHCI_FSL_CLK_SDCLKEN); | ||||
return; | return; | ||||
} | } | ||||
div = ((val >> SDHCI_DIVIDER_SHIFT) & SDHCI_DIVIDER_MASK) | | /* | ||||
((val >> SDHCI_DIVIDER_HI_SHIFT) & SDHCI_DIVIDER_HI_MASK) << | * Ignore dividers provided by core in `sdhci_set_clock` and calculate | ||||
SDHCI_DIVIDER_MASK_LEN; | * them anew with higher accuracy. | ||||
if (div == 0) | */ | ||||
freq = sc->maxclk_hz; | SDHCI_FSL_FDT_CLK_DIV(sc, sc->baseclk_hz, slot->clock, prescale, div); | ||||
else | |||||
freq = sc->maxclk_hz / (2 * div); | |||||
for (prescale = 2; freq < sc->baseclk_hz / (prescale * 16); ) | |||||
prescale <<= 1; | |||||
for (div = 1; freq < sc->baseclk_hz / (prescale * div); ) | |||||
++div; | |||||
#ifdef DEBUG | #ifdef DEBUG | ||||
device_printf(sc->dev, | device_printf(sc->dev, | ||||
"Desired SD/MMC freq: %d, actual: %d; base %d prescale %d divisor %d\n", | "Desired SD/MMC freq: %d, actual: %d; base %d prescale %d divisor %d\n", | ||||
freq, sc->baseclk_hz / (prescale * div), | slot->clock, sc->baseclk_hz / (prescale * div), | ||||
sc->baseclk_hz, prescale, div); | sc->baseclk_hz, prescale, div); | ||||
#endif | #endif | ||||
prescale >>= 1; | prescale >>= 1; | ||||
div -= 1; | div -= 1; | ||||
val32 &= ~(SDHCI_FSL_CLK_DIVIDER_MASK | SDHCI_FSL_CLK_PRESCALE_MASK); | val32 &= ~(SDHCI_FSL_CLK_DIVIDER_MASK | SDHCI_FSL_CLK_PRESCALE_MASK); | ||||
val32 |= div << SDHCI_FSL_CLK_DIVIDER_SHIFT; | val32 |= div << SDHCI_FSL_CLK_DIVIDER_SHIFT; | ||||
▲ Show 20 Lines • Show All 138 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
struct sdhci_fsl_fdt_softc *sc; | struct sdhci_fsl_fdt_softc *sc; | ||||
uint32_t val32; | uint32_t val32; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
switch (off) { | switch (off) { | ||||
case SDHCI_CLOCK_CONTROL: | case SDHCI_CLOCK_CONTROL: | ||||
fsl_sdhc_fdt_set_clock(sc, val); | fsl_sdhc_fdt_set_clock(sc, slot, val); | ||||
return; | return; | ||||
/* | /* | ||||
* eSDHC hardware combines command and mode into a single | * eSDHC hardware combines command and mode into a single | ||||
* register. Cache it here, so that command isn't written | * register. Cache it here, so that command isn't written | ||||
* until after mode. | * until after mode. | ||||
*/ | */ | ||||
case SDHCI_TRANSFER_MODE: | case SDHCI_TRANSFER_MODE: | ||||
sc->cmd_and_mode = val; | sc->cmd_and_mode = val; | ||||
▲ Show 20 Lines • Show All 302 Lines • ▼ Show 20 Lines | if (OF_hasprop(node, "little-endian")) { | ||||
sc->write = write_le; | sc->write = write_le; | ||||
buf_order = SDHCI_FSL_PROT_CTRL_BYTE_NATIVE; | buf_order = SDHCI_FSL_PROT_CTRL_BYTE_NATIVE; | ||||
} else { | } else { | ||||
sc->read = read_be; | sc->read = read_be; | ||||
sc->write = write_be; | sc->write = write_be; | ||||
buf_order = SDHCI_FSL_PROT_CTRL_BYTE_SWAP; | buf_order = SDHCI_FSL_PROT_CTRL_BYTE_SWAP; | ||||
} | } | ||||
sc->vendor_ver = (RD4(sc, SDHCI_FSL_HOST_VERSION) & | |||||
SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; | |||||
sdhci_fsl_fdt_of_parse(dev); | sdhci_fsl_fdt_of_parse(dev); | ||||
sc->maxclk_hz = host->f_max ? host->f_max : sc->baseclk_hz; | sc->maxclk_hz = host->f_max ? host->f_max : sc->baseclk_hz; | ||||
/* | /* | ||||
* Setting this register affects byte order in SDHCI_BUFFER only. | * Setting this register affects byte order in SDHCI_BUFFER only. | ||||
* If the eSDHC block is connected over a big-endian bus, the data | * If the eSDHC block is connected over a big-endian bus, the data | ||||
* read from/written to the buffer will be already byte swapped. | * read from/written to the buffer will be already byte swapped. | ||||
* In such a case, setting SDHCI_FSL_PROT_CTRL_BYTE_SWAP will convert | * In such a case, setting SDHCI_FSL_PROT_CTRL_BYTE_SWAP will convert | ||||
▲ Show 20 Lines • Show All 98 Lines • ▼ Show 20 Lines | if (which == MMCBR_IVAR_MAX_DATA && (slot->opt & SDHCI_HAVE_DMA)) { | ||||
* to bounce buffer size. | * to bounce buffer size. | ||||
*/ | */ | ||||
*result = howmany(slot->sdma_bbufsz, 512); | *result = howmany(slot->sdma_bbufsz, 512); | ||||
return (0); | return (0); | ||||
} | } | ||||
return (sdhci_generic_read_ivar(bus, child, which, result)); | return (sdhci_generic_read_ivar(bus, child, which, result)); | ||||
} | } | ||||
static int | |||||
sdhci_fsl_fdt_write_ivar(device_t bus, device_t child, int which, | |||||
uintptr_t value) | |||||
{ | |||||
struct sdhci_fsl_fdt_softc *sc; | |||||
struct sdhci_slot *slot = device_get_ivars(child); | |||||
uint32_t prescale, div; | |||||
/* Don't depend on clock resolution limits from sdhci core. */ | |||||
if (which == MMCBR_IVAR_CLOCK) { | |||||
if (value == 0) { | |||||
slot->host.ios.clock = 0; | |||||
return (0); | |||||
} | |||||
sc = device_get_softc(bus); | |||||
SDHCI_FSL_FDT_CLK_DIV(sc, sc->baseclk_hz, value, prescale, div); | |||||
slot->host.ios.clock = sc->baseclk_hz / (prescale * div); | |||||
return (0); | |||||
} | |||||
return (sdhci_generic_write_ivar(bus, child, which, value)); | |||||
} | |||||
static void | static void | ||||
sdhci_fsl_fdt_reset(device_t dev, struct sdhci_slot *slot, uint8_t mask) | sdhci_fsl_fdt_reset(device_t dev, struct sdhci_slot *slot, uint8_t mask) | ||||
{ | { | ||||
struct sdhci_fsl_fdt_softc *sc; | struct sdhci_fsl_fdt_softc *sc; | ||||
uint32_t val; | uint32_t val; | ||||
sdhci_generic_reset(dev, slot, mask); | sdhci_generic_reset(dev, slot, mask); | ||||
Show All 13 Lines | |||||
static const device_method_t sdhci_fsl_fdt_methods[] = { | static const device_method_t sdhci_fsl_fdt_methods[] = { | ||||
/* Device interface. */ | /* Device interface. */ | ||||
DEVMETHOD(device_probe, sdhci_fsl_fdt_probe), | DEVMETHOD(device_probe, sdhci_fsl_fdt_probe), | ||||
DEVMETHOD(device_attach, sdhci_fsl_fdt_attach), | DEVMETHOD(device_attach, sdhci_fsl_fdt_attach), | ||||
DEVMETHOD(device_detach, sdhci_fsl_fdt_detach), | DEVMETHOD(device_detach, sdhci_fsl_fdt_detach), | ||||
/* Bus interface. */ | /* Bus interface. */ | ||||
DEVMETHOD(bus_read_ivar, sdhci_fsl_fdt_read_ivar), | DEVMETHOD(bus_read_ivar, sdhci_fsl_fdt_read_ivar), | ||||
DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar), | DEVMETHOD(bus_write_ivar, sdhci_fsl_fdt_write_ivar), | ||||
/* MMC bridge interface. */ | /* MMC bridge interface. */ | ||||
DEVMETHOD(mmcbr_request, sdhci_generic_request), | DEVMETHOD(mmcbr_request, sdhci_generic_request), | ||||
DEVMETHOD(mmcbr_get_ro, sdhci_fsl_fdt_get_ro), | DEVMETHOD(mmcbr_get_ro, sdhci_fsl_fdt_get_ro), | ||||
DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host), | DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host), | ||||
DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host), | DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host), | ||||
DEVMETHOD(mmcbr_switch_vccq, sdhci_fsl_fdt_switch_vccq), | DEVMETHOD(mmcbr_switch_vccq, sdhci_fsl_fdt_switch_vccq), | ||||
DEVMETHOD(mmcbr_update_ios, sdhci_fsl_fdt_update_ios), | DEVMETHOD(mmcbr_update_ios, sdhci_fsl_fdt_update_ios), | ||||
Show All 29 Lines |