Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/sdhci/sdhci.c
Context not available. | |||||
static void sdhci_read_block_pio(struct sdhci_slot *slot); | static void sdhci_read_block_pio(struct sdhci_slot *slot); | ||||
static void sdhci_req_done(struct sdhci_slot *slot); | static void sdhci_req_done(struct sdhci_slot *slot); | ||||
static void sdhci_req_wakeup(struct mmc_request *req); | static void sdhci_req_wakeup(struct mmc_request *req); | ||||
static void sdhci_reset(struct sdhci_slot *slot, uint8_t mask); | |||||
static void sdhci_retune(void *arg); | static void sdhci_retune(void *arg); | ||||
static void sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock); | static void sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock); | ||||
static void sdhci_set_power(struct sdhci_slot *slot, u_char power); | static void sdhci_set_power(struct sdhci_slot *slot, u_char power); | ||||
Context not available. | |||||
return (0); | return (0); | ||||
} | } | ||||
static void | |||||
sdhci_reset(struct sdhci_slot *slot, uint8_t mask) | |||||
{ | |||||
int timeout; | |||||
uint32_t clock; | |||||
if (slot->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { | |||||
if (!SDHCI_GET_CARD_PRESENT(slot->bus, slot)) | |||||
return; | |||||
} | |||||
/* Some controllers need this kick or reset won't work. */ | |||||
if ((mask & SDHCI_RESET_ALL) == 0 && | |||||
(slot->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)) { | |||||
/* This is to force an update */ | |||||
clock = slot->clock; | |||||
slot->clock = 0; | |||||
sdhci_set_clock(slot, clock); | |||||
} | |||||
if (mask & SDHCI_RESET_ALL) { | |||||
slot->clock = 0; | |||||
slot->power = 0; | |||||
} | |||||
WR1(slot, SDHCI_SOFTWARE_RESET, mask); | |||||
if (slot->quirks & SDHCI_QUIRK_WAITFOR_RESET_ASSERTED) { | |||||
/* | |||||
* Resets on TI OMAPs and AM335x are incompatible with SDHCI | |||||
* specification. The reset bit has internal propagation delay, | |||||
* so a fast read after write returns 0 even if reset process is | |||||
* in progress. The workaround is to poll for 1 before polling | |||||
* for 0. In the worst case, if we miss seeing it asserted the | |||||
* time we spent waiting is enough to ensure the reset finishes. | |||||
*/ | |||||
timeout = 10000; | |||||
while ((RD1(slot, SDHCI_SOFTWARE_RESET) & mask) != mask) { | |||||
if (timeout <= 0) | |||||
break; | |||||
timeout--; | |||||
DELAY(1); | |||||
} | |||||
} | |||||
/* Wait max 100 ms */ | |||||
timeout = 10000; | |||||
/* Controller clears the bits when it's done */ | |||||
while (RD1(slot, SDHCI_SOFTWARE_RESET) & mask) { | |||||
if (timeout <= 0) { | |||||
slot_printf(slot, "Reset 0x%x never completed.\n", | |||||
mask); | |||||
sdhci_dumpregs(slot); | |||||
return; | |||||
} | |||||
timeout--; | |||||
DELAY(10); | |||||
} | |||||
} | |||||
static uint32_t | static uint32_t | ||||
sdhci_tuning_intmask(const struct sdhci_slot *slot) | sdhci_tuning_intmask(const struct sdhci_slot *slot) | ||||
{ | { | ||||
Context not available. | |||||
sdhci_init(struct sdhci_slot *slot) | sdhci_init(struct sdhci_slot *slot) | ||||
{ | { | ||||
sdhci_reset(slot, SDHCI_RESET_ALL); | SDHCI_RESET(slot->bus, slot, SDHCI_RESET_ALL); | ||||
/* Enable interrupts. */ | /* Enable interrupts. */ | ||||
slot->intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | | slot->intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | | ||||
Context not available. | |||||
device_delete_child(slot->bus, d); | device_delete_child(slot->bus, d); | ||||
SDHCI_LOCK(slot); | SDHCI_LOCK(slot); | ||||
sdhci_reset(slot, SDHCI_RESET_ALL); | SDHCI_RESET(slot->bus, slot, SDHCI_RESET_ALL); | ||||
SDHCI_UNLOCK(slot); | SDHCI_UNLOCK(slot); | ||||
if (slot->opt & SDHCI_HAVE_DMA) | if (slot->opt & SDHCI_HAVE_DMA) | ||||
sdhci_dma_free(slot); | sdhci_dma_free(slot); | ||||
Context not available. | |||||
callout_drain(&slot->retune_callout); | callout_drain(&slot->retune_callout); | ||||
SDHCI_LOCK(slot); | SDHCI_LOCK(slot); | ||||
slot->opt &= ~SDHCI_TUNING_ENABLED; | slot->opt &= ~SDHCI_TUNING_ENABLED; | ||||
sdhci_reset(slot, SDHCI_RESET_ALL); | SDHCI_RESET(slot->bus, slot, SDHCI_RESET_ALL); | ||||
SDHCI_UNLOCK(slot); | SDHCI_UNLOCK(slot); | ||||
return (0); | return (0); | ||||
Context not available. | |||||
return (0); | return (0); | ||||
} | } | ||||
void | |||||
sdhci_generic_reset(device_t brdev __unused, struct sdhci_slot *slot, | |||||
uint8_t mask) | |||||
{ | |||||
int timeout; | |||||
uint32_t clock; | |||||
if (slot->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { | |||||
if (!SDHCI_GET_CARD_PRESENT(slot->bus, slot)) | |||||
return; | |||||
} | |||||
/* Some controllers need this kick or reset won't work. */ | |||||
if ((mask & SDHCI_RESET_ALL) == 0 && | |||||
(slot->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)) { | |||||
/* This is to force an update */ | |||||
clock = slot->clock; | |||||
slot->clock = 0; | |||||
sdhci_set_clock(slot, clock); | |||||
} | |||||
if (mask & SDHCI_RESET_ALL) { | |||||
slot->clock = 0; | |||||
slot->power = 0; | |||||
} | |||||
WR1(slot, SDHCI_SOFTWARE_RESET, mask); | |||||
if (slot->quirks & SDHCI_QUIRK_WAITFOR_RESET_ASSERTED) { | |||||
/* | |||||
* Resets on TI OMAPs and AM335x are incompatible with SDHCI | |||||
* specification. The reset bit has internal propagation delay, | |||||
* so a fast read after write returns 0 even if reset process is | |||||
* in progress. The workaround is to poll for 1 before polling | |||||
* for 0. In the worst case, if we miss seeing it asserted the | |||||
* time we spent waiting is enough to ensure the reset finishes. | |||||
*/ | |||||
timeout = 10000; | |||||
while ((RD1(slot, SDHCI_SOFTWARE_RESET) & mask) != mask) { | |||||
if (timeout <= 0) | |||||
break; | |||||
timeout--; | |||||
DELAY(1); | |||||
} | |||||
} | |||||
/* Wait max 100 ms */ | |||||
timeout = 10000; | |||||
/* Controller clears the bits when it's done */ | |||||
while (RD1(slot, SDHCI_SOFTWARE_RESET) & mask) { | |||||
if (timeout <= 0) { | |||||
slot_printf(slot, "Reset 0x%x never completed.\n", | |||||
mask); | |||||
sdhci_dumpregs(slot); | |||||
return; | |||||
} | |||||
timeout--; | |||||
DELAY(10); | |||||
} | |||||
} | |||||
uint32_t | uint32_t | ||||
sdhci_generic_min_freq(device_t brdev __unused, struct sdhci_slot *slot) | sdhci_generic_min_freq(device_t brdev __unused, struct sdhci_slot *slot) | ||||
{ | { | ||||
Context not available. | |||||
SDHCI_SET_UHS_TIMING(brdev, slot); | SDHCI_SET_UHS_TIMING(brdev, slot); | ||||
/* Some controllers like reset after bus changes. */ | /* Some controllers like reset after bus changes. */ | ||||
if (slot->quirks & SDHCI_QUIRK_RESET_ON_IOS) | if (slot->quirks & SDHCI_QUIRK_RESET_ON_IOS) | ||||
sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA); | SDHCI_RESET(slot->bus, slot, | ||||
SDHCI_RESET_CMD | SDHCI_RESET_DATA); | |||||
SDHCI_UNLOCK(slot); | SDHCI_UNLOCK(slot); | ||||
return (0); | return (0); | ||||
Context not available. | |||||
slot_printf(slot, "Tuning failed, using fixed sampling clock\n"); | slot_printf(slot, "Tuning failed, using fixed sampling clock\n"); | ||||
WR2(slot, SDHCI_HOST_CONTROL2, hostctrl2 & ~(SDHCI_CTRL2_EXEC_TUNING | | WR2(slot, SDHCI_HOST_CONTROL2, hostctrl2 & ~(SDHCI_CTRL2_EXEC_TUNING | | ||||
SDHCI_CTRL2_SAMPLING_CLOCK)); | SDHCI_CTRL2_SAMPLING_CLOCK)); | ||||
sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA); | SDHCI_RESET(slot->bus, slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA); | ||||
return (EIO); | return (EIO); | ||||
} | } | ||||
Context not available. | |||||
if (slot->curcmd != NULL) { | if (slot->curcmd != NULL) { | ||||
slot_printf(slot, "Controller timeout\n"); | slot_printf(slot, "Controller timeout\n"); | ||||
sdhci_dumpregs(slot); | sdhci_dumpregs(slot); | ||||
sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA); | SDHCI_RESET(slot->bus, slot, | ||||
SDHCI_RESET_CMD | SDHCI_RESET_DATA); | |||||
slot->curcmd->error = MMC_ERR_TIMEOUT; | slot->curcmd->error = MMC_ERR_TIMEOUT; | ||||
sdhci_req_done(slot); | sdhci_req_done(slot); | ||||
} else { | } else { | ||||
Context not available. | |||||
if (slot->curcmd->error) { | if (slot->curcmd->error) { | ||||
if (slot->curcmd->error == MMC_ERR_BADCRC) | if (slot->curcmd->error == MMC_ERR_BADCRC) | ||||
slot->retune_req |= SDHCI_RETUNE_REQ_RESET; | slot->retune_req |= SDHCI_RETUNE_REQ_RESET; | ||||
sdhci_reset(slot, SDHCI_RESET_CMD); | SDHCI_RESET(slot->bus, slot, SDHCI_RESET_CMD); | ||||
sdhci_reset(slot, SDHCI_RESET_DATA); | SDHCI_RESET(slot->bus, slot, SDHCI_RESET_DATA); | ||||
sdhci_start(slot); | sdhci_start(slot); | ||||
return; | return; | ||||
} | } | ||||
Context not available. | |||||
if (slot->curcmd->error) { | if (slot->curcmd->error) { | ||||
if (slot->curcmd->error == MMC_ERR_BADCRC) | if (slot->curcmd->error == MMC_ERR_BADCRC) | ||||
slot->retune_req |= SDHCI_RETUNE_REQ_RESET; | slot->retune_req |= SDHCI_RETUNE_REQ_RESET; | ||||
sdhci_reset(slot, SDHCI_RESET_CMD); | SDHCI_RESET(slot->bus, slot, SDHCI_RESET_CMD); | ||||
sdhci_reset(slot, SDHCI_RESET_DATA); | SDHCI_RESET(slot->bus, slot, SDHCI_RESET_DATA); | ||||
sdhci_start(slot); | sdhci_start(slot); | ||||
return; | return; | ||||
} | } | ||||
Context not available. | |||||
slot_printf(slot, "result: %d\n", mmcio->cmd.error); | slot_printf(slot, "result: %d\n", mmcio->cmd.error); | ||||
if (mmcio->cmd.error == 0 && | if (mmcio->cmd.error == 0 && | ||||
(slot->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) { | (slot->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) { | ||||
sdhci_reset(slot, SDHCI_RESET_CMD); | SDHCI_RESET(slot->bus, slot, SDHCI_RESET_CMD); | ||||
sdhci_reset(slot, SDHCI_RESET_DATA); | SDHCI_RESET(slot->bus, slot, SDHCI_RESET_DATA); | ||||
} | } | ||||
sdhci_req_done(slot); | sdhci_req_done(slot); | ||||
Context not available. | |||||
((slot->curcmd == req->stop && | ((slot->curcmd == req->stop && | ||||
(slot->quirks & SDHCI_QUIRK_BROKEN_AUTO_STOP)) || | (slot->quirks & SDHCI_QUIRK_BROKEN_AUTO_STOP)) || | ||||
(slot->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) { | (slot->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) { | ||||
sdhci_reset(slot, SDHCI_RESET_CMD); | SDHCI_RESET(slot->bus, slot, SDHCI_RESET_CMD); | ||||
sdhci_reset(slot, SDHCI_RESET_DATA); | SDHCI_RESET(slot->bus, slot, SDHCI_RESET_DATA); | ||||
} | } | ||||
sdhci_req_done(slot); | sdhci_req_done(slot); | ||||
Context not available. | |||||
return; | return; | ||||
} | } | ||||
slot_printf(slot, "Got AutoCMD12 error 0x%04x\n", acmd_err); | slot_printf(slot, "Got AutoCMD12 error 0x%04x\n", acmd_err); | ||||
sdhci_reset(slot, SDHCI_RESET_CMD); | SDHCI_RESET(slot->bus, slot, SDHCI_RESET_CMD); | ||||
} | } | ||||
void | void | ||||
Context not available. | |||||
WR1(slot, SDHCI_HOST_CONTROL, slot->hostctrl); | WR1(slot, SDHCI_HOST_CONTROL, slot->hostctrl); | ||||
/* Some controllers like reset after bus changes. */ | /* Some controllers like reset after bus changes. */ | ||||
if(slot->quirks & SDHCI_QUIRK_RESET_ON_IOS) | if(slot->quirks & SDHCI_QUIRK_RESET_ON_IOS) | ||||
sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA); | SDHCI_RESET(slot->bus, slot, | ||||
SDHCI_RESET_CMD | SDHCI_RESET_DATA); | |||||
SDHCI_UNLOCK(slot); | SDHCI_UNLOCK(slot); | ||||
return (0); | return (0); | ||||
Context not available. |