fusee: get uSD working, fix bad no_iopower, and abstract card differences

This commit is contained in:
Kate J. Temkin 2018-05-04 03:23:16 -06:00
parent 99f749ef82
commit 0120b9ce52
3 changed files with 257 additions and 98 deletions

View file

@ -202,7 +202,10 @@ enum tegra_pinmux_constants {
PINMUX_SELECT_FUNCTION3 = 3,
/* Drive */
PINMUX_DRIVE_1X = (0x0 << 13),
PINMUX_DRIVE_2X = (0x1 << 13),
PINMUX_DRIVE_3X = (0x2 << 13),
PINMUX_DRIVE_4X = (0x3 << 13),
};

View file

@ -195,6 +195,23 @@ enum sdmmc_register_bits {
/* Software reset */
MMC_SOFT_RESET_FULL = (1 << 0),
/* Vendor clock control */
MMC_CLOCK_TAP_MASK = (0xFF << 16),
MMC_CLOCK_TAP_SDMMC1 = (0x04 << 16),
MMC_CLOCK_TAP_SDMMC4 = (0x00 << 16),
MMC_CLOCK_TRIM_MASK = (0xFF << 24),
MMC_CLOCK_TRIM_SDMMC1 = (0x02 << 24),
MMC_CLOCK_TRIM_SDMMC4 = (0x08 << 24),
/* Auto cal configuration */
MMC_AUTOCAL_PDPU_CONFIG_MASK = 0x7f7f,
MMC_AUTOCAL_PDPU_SDMMC1_1V8 = 0x7b7b,
MMC_AUTOCAL_PDPU_SDMMC1_3V3 = 0x7d00,
MMC_AUTOCAL_PDPU_SDMMC4_1V8 = 0x0505,
};
@ -206,6 +223,7 @@ enum sdmmc_command {
CMD_SEND_OPERATING_CONDITIONS = 1,
CMD_ALL_SEND_CID = 2,
CMD_SET_RELATIVE_ADDR = 3,
CMD_GET_RELATIVE_ADDR = 3,
CMD_SET_DSR = 4,
CMD_TOGGLE_SLEEP_AWAKE = 5,
CMD_SWITCH_MODE = 6,
@ -305,6 +323,8 @@ enum sdmmc_command_magic {
MMC_EMMC_OPERATING_READINESS_MASK = (0x0f << 28),
MMC_SD_OPERATING_COND_READY = (1 << 31),
MMC_SD_OPERATING_COND_HIGH_CAPACITY = (1 << 30),
MMC_SD_OPERATING_COND_ACCEPTS_3V3 = (1 << 20),
/* READ_STATUS responses */
MMC_STATUS_MASK = (0xf << 9),
@ -527,8 +547,11 @@ static int sdmmc1_enable_supplies(struct mmc *mmc)
volatile struct tegra_pmc *pmc = pmc_get_regs();
volatile struct tegra_pinmux *pinmux = pinmux_get_regs();
// Set PAD_E_INPUT_OR_E_PWRD (relevant for eMMC only)
mmc->regs->sdmemcomppadctrl |= 0x80000000;
// Ensure the PMC is prepared for the SDMMC card to recieve power.
pmc->no_iopower |= PMC_CONTROL_SDMMC1;
pmc->no_iopower &= ~PMC_CONTROL_SDMMC1;
pmc->pwr_det_val |= PMC_CONTROL_SDMMC1;
// Configure the enable line for the SD card power.
@ -537,10 +560,6 @@ static int sdmmc1_enable_supplies(struct mmc *mmc)
gpio_configure_direction(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_DIRECTION_OUTPUT);
gpio_write(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_LEVEL_HIGH);
// Set up SD card voltages.
udelay(1000);
supply_enable(SUPPLY_MICROSD);
udelay(1000);
return 0;
}
@ -565,10 +584,6 @@ static int sdmmc1_hardware_init(struct mmc *mmc)
pinmux->sdmmc1_dat1 = PINMUX_DRIVE_2X | PINMUX_PARKED | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT | PINMUX_PULL_UP;
pinmux->sdmmc1_dat0 = PINMUX_DRIVE_2X | PINMUX_PARKED | PINMUX_SELECT_FUNCTION0 | PINMUX_INPUT | PINMUX_PULL_UP;
// Set up the SDMMC write protect.
// TODO: should this be an output, that we control?
pinmux->pz4 = PINMUX_SELECT_FUNCTION0 | PINMUX_PULL_UP;
// Set up the card detect pin as a GPIO input.
pinmux->pz1 = PINMUX_TRISTATE | PINMUX_SELECT_FUNCTION1 | PINMUX_PULL_UP | PINMUX_INPUT;
gpio_configure_mode(GPIO_MICROSD_CARD_DETECT, GPIO_MODE_GPIO);
@ -592,6 +607,11 @@ static int sdmmc1_hardware_init(struct mmc *mmc)
car->clk_enb_l_set |= CAR_CONTROL_SDMMC1;
car->clk_enb_y_set |= CAR_CONTROL_SDMMC_LEGACY;
// Set up SD card voltages.
udelay(1000);
supply_enable(SUPPLY_MICROSD);
udelay(1000);
// host_clk_delay(0x64, clk_freq) -> Delay 100 host clock cycles
udelay(5000);
@ -648,6 +668,45 @@ static int sdmmc_enable_supplies(struct mmc *mmc)
}
/**
* Configures clocking parameters for a given controller.
*
* @param mmc The MMC controller to set up.
*/
static int sdmmc_set_up_clocking_parameters(struct mmc *mmc)
{
// TODO: timing for HS400/HS667 modes
// TODO: timing for tuanble modes (SDR50/104/200)
// Clear the I/O conditioning constants.
mmc->regs->vendor_clock_cntrl &= ~(MMC_CLOCK_TRIM_MASK | MMC_CLOCK_TAP_MASK);
mmc->regs->auto_cal_config &= ~MMC_AUTOCAL_PDPU_CONFIG_MASK;
// Set up the I/O conditioning constants used to ensure we have a reliable clock.
// Constants above and procedure below from the TRM.
switch (mmc->controller) {
case SWITCH_EMMC:
mmc->regs->vendor_clock_cntrl |= (MMC_CLOCK_TRIM_SDMMC4 | MMC_CLOCK_TAP_SDMMC4);
mmc->regs->auto_cal_config |= MMC_AUTOCAL_PDPU_SDMMC4_1V8;
break;
case SWITCH_MICROSD:
mmc->regs->vendor_clock_cntrl |= (MMC_CLOCK_TRIM_SDMMC1 | MMC_CLOCK_TAP_SDMMC1);
mmc->regs->auto_cal_config |= MMC_AUTOCAL_PDPU_SDMMC1_3V3;
break;
default:
printk("ERROR: initialization not yet writen for SDMMC%d", mmc->controller);
return ENODEV;
}
return 0;
}
/**
* Initialize the low-level SDMMC hardware.
* Thanks to hexkyz for this init code.
@ -677,27 +736,33 @@ static int sdmmc_hardware_init(struct mmc *mmc)
return rc;
}
// Turn on the card's power supplies...
rc = sdmmc_enable_supplies(mmc);
if (rc) {
mmc_print(mmc, "ERROR: could power on the card!");
return rc;
}
// Set IO_SPARE[19] (one cycle delay)
regs->io_spare |= 0x80000;
// Clear SEL_VREG
regs->vendor_io_trim_cntrl &= ~(0x04);
// Set trimmer value to 0x08 (SDMMC4)
regs->vendor_clock_cntrl &= ~(0x1F000000);
regs->vendor_clock_cntrl |= 0x08000000;
// The bootrom sets TAP_VAL to be 4.
// We'll do that too. FIXME: should we?
regs->vendor_clock_cntrl |= 0x40000;
// Set SDMMC2TMC_CFG_SDMEMCOMP_VREF_SEL to 0x07
regs->sdmemcomppadctrl &= ~(0x0F);
regs->sdmemcomppadctrl |= 0x07;
// Set auto-calibration PD/PU offsets
regs->auto_cal_config = ((regs->auto_cal_config & ~(0x7f)) | 0x05);
regs->auto_cal_config = ((regs->auto_cal_config & ~(0x7f00)) | 0x05);
/*
*/
// Set ourselves up to have a stable.
rc = sdmmc_set_up_clocking_parameters(mmc);
if (rc) {
mmc_print(mmc, "WARNING: could not optimize card clocking parameters. (%d)", rc);
}
// Set PAD_E_INPUT_OR_E_PWRD (relevant for eMMC only)
regs->sdmemcomppadctrl |= 0x80000000;
@ -715,7 +780,6 @@ static int sdmmc_hardware_init(struct mmc *mmc)
timebase = get_time();
// Wait for AUTO_CAL_ACTIVE to be cleared
mmc_print(mmc, "initialing autocal...");
while ((regs->auto_cal_status & 0x80000000) && !is_timeout) {
// Keep checking if timeout expired
is_timeout = get_time_since(timebase) > 10000;
@ -727,21 +791,6 @@ static int sdmmc_hardware_init(struct mmc *mmc)
return ETIMEDOUT;
}
//
//if (is_timeout)
//{
// mmc_print(mmc, "autocal timed out!");
// // Set CFG2TMC_EMMC4_PAD_DRVUP_COMP and CFG2TMC_EMMC4_PAD_DRVDN_COMP
// APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 = ((APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 & ~(0x3F00)) | 0x1000);
// APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 = ((APB_MISC_GP_EMMC4_PAD_CFGPADCTRL_0 & ~(0xFC)) | 0x40);
//
// // Clear AUTO_CAL_ENABLE
// regs->auto_cal_config &= ~(0x20000000);
//}
mmc_print(mmc, "autocal complete.");
// Clear PAD_E_INPUT_OR_E_PWRD (relevant for eMMC only)
regs->sdmemcomppadctrl &= ~(0x80000000);
@ -753,7 +802,6 @@ static int sdmmc_hardware_init(struct mmc *mmc)
is_timeout = false;
// Wait for SDHCI_CLOCK_INT_STABLE to be set
mmc_print(mmc, "waiting for internal clock to stabalize...");
while (!(regs->clock_control & 0x02) && !is_timeout) {
// Keep checking if timeout expired
is_timeout = get_time_since(timebase) > 2000000;
@ -763,12 +811,10 @@ static int sdmmc_hardware_init(struct mmc *mmc)
if (is_timeout) {
mmc_print(mmc, "clock never stabalized!");
return -1;
} else {
mmc_print(mmc, "clock stabalized.");
}
// Clear upper 17 bits
regs->host_control2 &= ~(0xFFFF8000);
// FIXME: replace this to support better clocks
regs->host_control2 = 0;
// Clear SDHCI_PROG_CLOCK_MODE
regs->clock_control &= ~(0x20);
@ -784,18 +830,16 @@ static int sdmmc_hardware_init(struct mmc *mmc)
regs->host_control &= 0xFD;
regs->host_control &= 0xDF;
// Set SDHCI_POWER_180
// Set up the SD card's voltage.
regs->power_control &= 0xF1;
regs->power_control |= 0x0A;
regs->power_control |= mmc->operating_voltage << 1;
// Mark the power as on.
regs->power_control |= 0x01;
// Clear TAP_VAL_UPDATED_BY_HW
regs->vendor_tuning_cntrl0 &= ~(0x20000);
// Set TAP_VAL
regs->vendor_clock_cntrl &= ~(0xFF0000);
// Clear SDHCI_CTRL_HISPD
regs->host_control &= 0xFB;
@ -805,28 +849,20 @@ static int sdmmc_hardware_init(struct mmc *mmc)
// Set SDHCI_DIVIDER and SDHCI_DIVIDER_HI
// FIXME: divider SD if necessary
regs->clock_control &= ~(0xFFC0);
regs->clock_control |= (0x18 << 8); // 400kHz, for now
regs->clock_control |= (0x30 << 8); // 200kHz, initially
// Set SDHCI_CLOCK_CARD_EN
regs->clock_control |= 0x04;
// Ensure we're using System DMA (SDMA) mode for DMA.
// Ensure we're using Single-operation DMA (SDMA) mode for DMA.
regs->host_control &= ~MMC_DMA_SELECT_MASK;
// Turn on the card's power supplies...
rc = sdmmc_enable_supplies(mmc);
if (rc) {
mmc_print(mmc, "ERROR: could power on the card!");
return rc;
}
// ... and verify that the card is there.
if (!sdmmc_card_present(mmc)) {
mmc_print(mmc, "ERROR: no card detected!");
return ENODEV;
}
mmc_print(mmc, "initialized.");
return 0;
}
@ -1636,7 +1672,77 @@ static int sdmmc_set_up_partitions(struct mmc *mmc)
}
/**
* Requests that an MMC target use the card's current relative address.
*
* @param mmc The SDMMC controller to work with.
* @return 0 on success, or an error code on failure.
*/
static int sdmmc_set_relative_address(struct mmc *mmc)
{
int rc;
// Set up the card's relative address.
rc = sdmmc_send_simple_command(mmc, CMD_SET_RELATIVE_ADDR, MMC_RESPONSE_LEN48, mmc->relative_address << 16, NULL);
if (rc) {
mmc_print(mmc, "could not set the card's relative address! (%d)", rc);
return rc;
}
return 0;
}
/**
* Requests that an SD target report a relative address for us to use
* to communicate with it.
*
* @param mmc The SDMMC controller to work with.
* @return 0 on success, or an error code on failure.
*/
static int sdmmc_get_relative_address(struct mmc *mmc)
{
int rc;
uint32_t response;
//uint32_t timebase = get_time();
// TODO: do we need to repeatedly retry this? other codebases do
// Set up the card's relative address.
rc = sdmmc_send_simple_command(mmc, CMD_GET_RELATIVE_ADDR, MMC_RESPONSE_LEN48, 0, &response);
if (rc) {
mmc_print(mmc, "could not get the card's relative address! (%d)", rc);
return rc;
}
// Apply the fetched relative address.
mmc->relative_address = response >> 16;
return 0;
}
/**
* Establishes a relative address that can be used to communicate with a
* given card-- either by using or replacing mmc->relative_address.
*
* @param mmc The MMC controller to work with.
* @return 0 on success, or an error code on failure.
*/
static int sdmmc_get_or_set_relative_address(struct mmc *mmc)
{
// The SD and MMC specifications handle relative address assignemnt
// differently-- delegate accordingly.
switch (mmc->card_type) {
case MMC_CARD_EMMC:
case MMC_CARD_MMC:
return sdmmc_set_relative_address(mmc);
case MMC_CARD_SD:
return sdmmc_get_relative_address(mmc);
default:
mmc_print(mmc, "cannot figure out how to set up an relative address for TYPE%d devices", mmc->card_type);
return ENODEV;
}
}
/**
@ -1652,38 +1758,38 @@ static int sdmmc_card_init(struct mmc *mmc)
rc = sdmmc_send_simple_command(mmc, CMD_ALL_SEND_CID, MMC_RESPONSE_LEN136, 0, response);
if (rc) {
mmc_print(mmc, "could not fetch the CID");
return ENODEV;
return rc;
}
// Store the card ID for later.
memcpy(mmc->cid, response, sizeof(mmc->cid));
// Set up the card's relative address.
rc = sdmmc_send_simple_command(mmc, CMD_SET_RELATIVE_ADDR, MMC_RESPONSE_LEN48, mmc->relative_address << 16, response);
// Establish a relative address to communicate with
rc = sdmmc_get_or_set_relative_address(mmc);
if (rc) {
mmc_print(mmc, "could not set the card's relative address! (%d)", rc);
return EPIPE;
mmc_print(mmc, "could not establish a relative address! (%d)", rc);
return rc;
}
// Read and handle card's Card Specific Data (CSD).
rc = sdmmc_read_and_parse_csd(mmc);
if (rc) {
mmc_print(mmc, "could not populate CSD attributes! (%d)", rc);
return EPIPE;
return rc;
}
// Select our eMMC card, so it knows we're talking to it.
rc = sdmmc_send_simple_command(mmc, CMD_TOGGLE_CARD_SELECT, MMC_RESPONSE_LEN48, mmc->relative_address << 16, response);
if (rc) {
mmc_print(mmc, "could not select the active card for use! (%d)", rc);
return EPIPE;
return rc;
}
// Determine the block size we want to work with, and then set up the size accordingly.
rc = sdmmc_set_up_block_transfer_size(mmc);
if (rc) {
mmc_print(mmc, "could not set up block transfer sizes! (%d)", rc);
return EPIPE;
return rc;
}
return 0;
@ -1739,14 +1845,17 @@ static int sdmmc_mmc_wait_for_card_readiness(struct mmc *mmc)
static int sdmmc_sd_wait_for_card_readiness(struct mmc *mmc, uint32_t *response)
{
int rc;
uint32_t argument = MMC_SD_OPERATING_COND_ACCEPTS_3V3;
// TODO: populate this correctly per version
uint32_t argument = 0;
// If this is a SDv2 or higher card, check for an SDHC card.
if (mmc->spec_version >= SD_VERSION_2) {
argument |= MMC_SD_OPERATING_COND_HIGH_CAPACITY;
}
while (true) {
// Ask the SD card to identify its state. It will respond with readiness and a capacity magic.
rc = sdmmc_send_simple_app_command(mmc, CMD_APP_SEND_OP_COND,
MMC_RESPONSE_LEN136, MMC_CHECKS_NONE, argument, response);
MMC_RESPONSE_LEN48, MMC_CHECKS_NONE, argument, response);
if (rc) {
mmc_print(mmc, "ERROR: could not read the card's operating conditions!");
return rc;
@ -1755,6 +1864,9 @@ static int sdmmc_sd_wait_for_card_readiness(struct mmc *mmc, uint32_t *response)
// If the device has just become ready, we're done!
if (response[0] & MMC_SD_OPERATING_COND_READY)
return 0;
// Wait a delay so we're not spamming the card incessantly.
udelay(1000);
}
}
@ -1769,11 +1881,6 @@ static int sdmmc_mmc_card_init(struct mmc *mmc)
mmc_print(mmc, "setting up card as MMC");
// We only support Switch eMMC addressing, which is alawys block-based.
mmc->uses_block_addressing = true;
udelay(10000000);
// Bring the bus out of its idle state.
rc = sdmmc_send_simple_command(mmc, CMD_GO_IDLE_OR_INIT, MMC_RESPONSE_NONE, 0, NULL);
if (rc) {
@ -1842,7 +1949,7 @@ static bool sdmmc_check_pattern_present(uint32_t response)
static int sdmmc_sd_card_init(struct mmc *mmc)
{
int rc;
uint32_t response[4];
uint32_t ocr, response;
mmc_print(mmc, "setting up card as SD");
@ -1853,23 +1960,35 @@ static int sdmmc_sd_card_init(struct mmc *mmc)
return rc;
}
udelay(1000);
// Validate that the card can handle working with the voltages we can provide.
rc = sdmmc_send_simple_command(mmc, CMD_SEND_IF_COND, MMC_RESPONSE_LEN48, MMC_IF_VOLTAGE_3V3 | MMC_IF_CHECK_PATTERN, &response[0]);
if (rc || !sdmmc_check_pattern_present(response[0])) {
// TODO: Maybe don't assume we _need_ 3V3 interfacing?
mmc_print(mmc, "v1 or MMC card detected");
} else {
mmc_print(mmc, "this card is a v2 or greater card!");
rc = sdmmc_send_simple_command(mmc, CMD_SEND_IF_COND, MMC_RESPONSE_LEN48, MMC_IF_VOLTAGE_3V3 | MMC_IF_CHECK_PATTERN, &response);
if (rc || !sdmmc_check_pattern_present(response)) {
// TODO: This is either a broken, SDv1 or MMC card.
// Handle the latter two cases as best we can.
mmc_print(mmc, "ERROR: this card isn't an SDHC card!");
mmc_print(mmc, " we don't yet support low-capacity cards. :(");
return rc;
}
// If this responded, indicate that this is a v2 card.
else {
// store that this is a v2 card
mmc->spec_version = SD_VERSION_2;
}
// Wait for the card to finish being busy.
rc = sdmmc_sd_wait_for_card_readiness(mmc, response);
rc = sdmmc_sd_wait_for_card_readiness(mmc, &ocr);
if (rc) {
mmc_print(mmc, "card failed to come up! (%d)", rc);
return rc;
}
// FIXME: parse the response
// If the response indicated this was a high capacity card,
// always use block addressing.
mmc->uses_block_addressing = !!(ocr & MMC_SD_OPERATING_COND_HIGH_CAPACITY);
// Run the common core card initialization.
rc = sdmmc_card_init(mmc);
@ -2034,12 +2153,21 @@ static void sdmmc_initialize_defaults(struct mmc *mmc)
mmc->name = "eMMC";
mmc->card_type = MMC_CARD_EMMC;
mmc->max_bus_width = MMC_BUS_WIDTH_8BIT;
mmc->operating_voltage = MMC_VOLTAGE_1V8;
// The Switch's eMMC always uses block addressin.g
mmc->uses_block_addressing = true;
break;
case SWITCH_MICROSD:
mmc->name = "uSD";
mmc->card_type = MMC_CARD_SD;
mmc->max_bus_width = MMC_BUS_WIDTH_4BIT;
mmc->operating_voltage = MMC_VOLTAGE_3V3;
// Start off assuming byte addressing; we'll detect and correct this
// later, if necessary.
mmc->uses_block_addressing = false;
break;
default:

View file

@ -24,11 +24,22 @@ enum sdmmc_bus_width {
};
/**
* Describes the voltages that the host will use to drive the SD card.
* CAUTION: getting these wrong can damage (especially embedded) cards!
*/
enum sdmmc_bus_voltage {
MMC_VOLTAGE_3V3 = 0b111,
MMC_VOLTAGE_3V0 = 0b110,
MMC_VOLTAGE_1V8 = 0b101,
};
/**
* Represents the different types of devices an MMC object
* can represent.
*/
enum mmc_card_type {
enum sdmmc_card_type {
MMC_CARD_EMMC,
MMC_CARD_MMC,
MMC_CARD_SD,
@ -36,6 +47,22 @@ enum mmc_card_type {
};
/**
* Specification versions for SD/MMC cards.
*/
enum sdmmc_spec_version {
/* MMC card versions */
MMC_VERSION_4 = 0,
/* SD card versions */
SD_VERSION_1 = 1,
SD_VERSION_2 = 2,
};
/**
* SDMMC controllers
*/
@ -54,15 +81,16 @@ struct mmc {
/* Controller properties */
char *name;
unsigned int timeout;
enum mmc_card_type card_type;
enum sdmmc_card_type card_type;
bool use_dma;
/* Card properties */
uint8_t cid[15];
uint32_t relative_address;
uint8_t partitioned;
enum sdmmc_spec_version spec_version;
enum sdmmc_bus_width max_bus_width;
enum sdmmc_bus_voltage operating_voltage;
uint8_t partition_support;
uint8_t partition_config;