mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-24 15:50:29 +01:00
Apply sdmmc stage1 changes to stage2
This commit is contained in:
parent
402b69c549
commit
ac9939b7a1
@ -206,7 +206,7 @@ static int rawmmcdev_close(struct _reent *r, void *fd) {
|
||||
}
|
||||
|
||||
/* Keep this <= the size of the DMA bounce buffer in sdmmc.c */
|
||||
static __attribute__((aligned(16))) uint8_t g_crypto_buffer[4096 * 4] = {0};
|
||||
static __attribute__((aligned(16))) uint8_t g_crypto_buffer[512] = {0};
|
||||
|
||||
static ssize_t rawmmcdev_write(struct _reent *r, void *fd, const char *ptr, size_t len) {
|
||||
rawmmcdev_file_t *f = (rawmmcdev_file_t *)fd;
|
||||
|
@ -187,6 +187,9 @@ enum sdmmc_register_bits {
|
||||
|
||||
MMC_STATUS_ERROR_MASK = (0xF << 16),
|
||||
|
||||
/* Clock control */
|
||||
MMC_CLOCK_CONTROL_CARD_CLOCK_ENABLE = (1 << 2),
|
||||
|
||||
/* Host control */
|
||||
MMC_DMA_SELECT_MASK = (0x3 << 3),
|
||||
MMC_DMA_SELECT_SDMA = (0x0 << 3),
|
||||
@ -211,7 +214,16 @@ enum sdmmc_register_bits {
|
||||
MMC_AUTOCAL_PDPU_SDMMC1_1V8 = 0x7b7b,
|
||||
MMC_AUTOCAL_PDPU_SDMMC1_3V3 = 0x7d00,
|
||||
MMC_AUTOCAL_PDPU_SDMMC4_1V8 = 0x0505,
|
||||
MMC_AUTOCAL_START = (1 << 31),
|
||||
MMC_AUTOCAL_ENABLE = (1 << 29),
|
||||
|
||||
/* Autocal status */
|
||||
MMC_AUTOCAL_ACTIVE = (1 << 31),
|
||||
|
||||
/* Power control */
|
||||
MMC_POWER_CONTROL_VOLTAGE_MASK = (0x3 << 1),
|
||||
MMC_POWER_CONTROL_VOLTAGE_SHIFT = 1,
|
||||
MMC_POWER_CONTROL_POWER_ENABLE = (1 << 0),
|
||||
|
||||
};
|
||||
|
||||
@ -228,11 +240,13 @@ enum sdmmc_command {
|
||||
CMD_SET_DSR = 4,
|
||||
CMD_TOGGLE_SLEEP_AWAKE = 5,
|
||||
CMD_SWITCH_MODE = 6,
|
||||
CMD_APP_SWITCH_WIDTH = 6,
|
||||
CMD_TOGGLE_CARD_SELECT = 7,
|
||||
CMD_SEND_EXT_CSD = 8,
|
||||
CMD_SEND_IF_COND = 8,
|
||||
CMD_SEND_CSD = 9,
|
||||
CMD_SEND_CID = 10,
|
||||
CMD_SWITCH_TO_LOW_VOLTAGE = 11,
|
||||
CMD_STOP_TRANSMISSION = 12,
|
||||
CMD_READ_STATUS = 13,
|
||||
CMD_BUS_TEST = 14,
|
||||
@ -244,6 +258,8 @@ enum sdmmc_command {
|
||||
CMD_WRITE_MULTIPLE_BLOCK = 25,
|
||||
|
||||
CMD_APP_SEND_OP_COND = 41,
|
||||
CMD_APP_SET_CARD_DETECT = 42,
|
||||
CMD_APP_SEND_SCR = 51,
|
||||
CMD_APP_COMMAND = 55,
|
||||
};
|
||||
|
||||
@ -263,7 +279,7 @@ static const char *sdmmc_command_string[] = {
|
||||
"CMD_SEND_EXT_CSD/CMD_SEND_IF_COND",
|
||||
"CMD_SEND_CSD",
|
||||
"CMD_SEND_CID ",
|
||||
"<unsupported>",
|
||||
"CMD_SWITCH_TO_LOW_VOLTAGE",
|
||||
"CMD_STOP_TRANSMISSION",
|
||||
"CMD_READ_STATUS",
|
||||
"CMD_BUS_TEST",
|
||||
@ -290,6 +306,7 @@ enum sdmmc_command_magic {
|
||||
|
||||
MMC_SD_OPERATING_COND_READY = (1 << 31),
|
||||
MMC_SD_OPERATING_COND_HIGH_CAPACITY = (1 << 30),
|
||||
MMC_SD_OPERATING_COND_ACCEPTS_1V8 = (1 << 24),
|
||||
MMC_SD_OPERATING_COND_ACCEPTS_3V3 = (1 << 20),
|
||||
|
||||
/* READ_STATUS responses */
|
||||
@ -301,6 +318,11 @@ enum sdmmc_command_magic {
|
||||
/* IF_COND components */
|
||||
MMC_IF_VOLTAGE_3V3 = (1 << 8),
|
||||
MMC_IF_CHECK_PATTERN = 0xAA,
|
||||
|
||||
/* Misc constants */
|
||||
MMC_DEFAULT_BLOCK_ORDER = 9,
|
||||
MMC_VOLTAGE_SWITCH_TIME = 5000, // 5mS
|
||||
MMC_POST_CLOCK_DELAY = 1000, // 1mS
|
||||
};
|
||||
|
||||
|
||||
@ -313,7 +335,6 @@ enum sdmmc_csd_versions {
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Positions of different fields in various CSDs.
|
||||
* May eventually be replaced with a bitfield struct, if we use enough of the CSDs.
|
||||
@ -330,6 +351,7 @@ enum sdmmc_csd_extents {
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Positions of the different fields in the Extended CSD.
|
||||
*/
|
||||
@ -358,6 +380,25 @@ enum sdmmc_ext_csd_extents {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Bitfield struct representing an SD SCR.
|
||||
*/
|
||||
struct PACKED sdmmc_scr {
|
||||
uint32_t reserved1;
|
||||
uint16_t reserved0;
|
||||
uint8_t supports_width_1bit : 1;
|
||||
uint8_t supports_width_reserved0 : 1;
|
||||
uint8_t supports_width_4bit : 1;
|
||||
uint8_t supports_width_reserved1 : 1;
|
||||
uint8_t security_support : 3;
|
||||
uint8_t data_after_erase : 1;
|
||||
uint8_t spec_version : 4;
|
||||
uint8_t scr_version : 4;
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
static int sdmmc_send_simple_command(struct mmc *mmc, enum sdmmc_command command,
|
||||
enum sdmmc_response_type response_type, uint32_t argument, void *response_buffer);
|
||||
|
||||
/* SDMMC debug enable */
|
||||
static int sdmmc_loglevel = 0;
|
||||
@ -434,6 +475,10 @@ static const char *sdmmc_get_command_string(enum sdmmc_command command)
|
||||
return "CMD_APP_COMMAND";
|
||||
case CMD_APP_SEND_OP_COND:
|
||||
return "CMD_APP_SEND_OP_COND";
|
||||
case CMD_APP_SET_CARD_DETECT:
|
||||
return "CMD_APP_SET_CARD_DETECT";
|
||||
case CMD_APP_SEND_SCR:
|
||||
return "CMD_APP_SEND_SCR";
|
||||
case CMD_WRITE_SINGLE_BLOCK:
|
||||
return "CMD_WRITE_SINGLE_BLOCK";
|
||||
case CMD_WRITE_MULTIPLE_BLOCK:
|
||||
@ -541,6 +586,25 @@ static int sdmmc4_set_up_clock_and_io(struct mmc *mmc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the voltage that the given SDMMC is currently working with.
|
||||
*
|
||||
* @param mmc The controller to affect.
|
||||
* @param voltage The voltage to apply.
|
||||
*/
|
||||
static void sdmmc_set_working_voltage(struct mmc *mmc, enum sdmmc_bus_voltage voltage)
|
||||
{
|
||||
// Apply the voltage...
|
||||
mmc->operating_voltage = voltage;
|
||||
|
||||
// Set up the SD card's voltage.
|
||||
mmc->regs->power_control &= ~MMC_POWER_CONTROL_VOLTAGE_MASK;
|
||||
mmc->regs->power_control |= voltage << MMC_POWER_CONTROL_VOLTAGE_SHIFT;
|
||||
|
||||
// Mark the power as on.
|
||||
mmc->regs->power_control |= MMC_POWER_CONTROL_POWER_ENABLE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enables power supplies for SDMMC4, used for eMMC.
|
||||
@ -548,6 +612,9 @@ static int sdmmc4_set_up_clock_and_io(struct mmc *mmc)
|
||||
static int sdmmc4_enable_supplies(struct mmc *mmc)
|
||||
{
|
||||
// As a booot device, SDMMC4's power supply is always on.
|
||||
// Modify the controller to know the voltage being applied to it,
|
||||
// and return success.
|
||||
sdmmc_set_working_voltage(mmc, MMC_VOLTAGE_1V8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -567,6 +634,14 @@ static int sdmmc1_enable_supplies(struct mmc *mmc)
|
||||
pmc->no_iopower &= ~PMC_CONTROL_SDMMC1;
|
||||
pmc->pwr_det_val |= PMC_CONTROL_SDMMC1;
|
||||
|
||||
// Set up SD card voltages.
|
||||
udelay(1000);
|
||||
supply_enable(SUPPLY_MICROSD, false);
|
||||
udelay(1000);
|
||||
|
||||
// Modify the controller to know the voltage being applied to it.
|
||||
sdmmc_set_working_voltage(mmc, MMC_VOLTAGE_3V3);
|
||||
|
||||
// Configure the enable line for the SD card power.
|
||||
pinmux->dmic3_clk = PINMUX_SELECT_FUNCTION0;
|
||||
gpio_configure_mode(GPIO_MICROSD_SUPPLY_ENABLE, GPIO_MODE_GPIO);
|
||||
@ -578,6 +653,175 @@ static int sdmmc1_enable_supplies(struct mmc *mmc)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configures clocking parameters for a given controller.
|
||||
*
|
||||
* @param mmc The MMC controller to set up.
|
||||
* @param operating_voltage The operating voltage for the bus, currently.
|
||||
*/
|
||||
static int sdmmc_set_up_clocking_parameters(struct mmc *mmc, enum sdmmc_bus_voltage operating_voltage)
|
||||
{
|
||||
// TODO: decide if these should be split into separate functions after seeing how much
|
||||
// is common to the tunable modes
|
||||
|
||||
// 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;
|
||||
|
||||
switch (operating_voltage) {
|
||||
case MMC_VOLTAGE_1V8:
|
||||
mmc->regs->auto_cal_config |= MMC_AUTOCAL_PDPU_SDMMC4_1V8;
|
||||
break;
|
||||
case MMC_VOLTAGE_3V3:
|
||||
mmc->regs->auto_cal_config |= MMC_AUTOCAL_PDPU_SDMMC1_3V3;
|
||||
break;
|
||||
default:
|
||||
printk("ERROR: currently no controllers support voltage %d", mmc->operating_voltage);
|
||||
return EINVAL;
|
||||
|
||||
}
|
||||
|
||||
// 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);
|
||||
break;
|
||||
|
||||
case SWITCH_MICROSD:
|
||||
mmc->regs->vendor_clock_cntrl |= (MMC_CLOCK_TRIM_SDMMC1 | MMC_CLOCK_TAP_SDMMC1);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk("ERROR: initialization not yet writen for SDMMC%d", mmc->controller);
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enables or disables delivering a clock to the downstream SD/MMC card.
|
||||
*
|
||||
* @param mmc The controller to be affected.
|
||||
* @param enabled True if the clock should be enabled; false to disable.
|
||||
*/
|
||||
void sdmmc_clock_enable(struct mmc *mmc, bool enabled)
|
||||
{
|
||||
// Set or clear the card clock enable bit according to the
|
||||
// controller paramter.
|
||||
if (enabled)
|
||||
mmc->regs->clock_control |= MMC_CLOCK_CONTROL_CARD_CLOCK_ENABLE;
|
||||
else
|
||||
mmc->regs->clock_control &= ~MMC_CLOCK_CONTROL_CARD_CLOCK_ENABLE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Runs SDMMC automatic calibration-- this tunes the parameters used for SDMMC
|
||||
* signal intergrity.
|
||||
*
|
||||
* @param mmc The controller whose card is to be tuned.
|
||||
* @param restart_sd_clock True iff the SD card should be started after calibration.
|
||||
*
|
||||
* @return 0 on success, or an error code on failure
|
||||
*/
|
||||
static int sdmmc_run_autocal(struct mmc *mmc, bool restart_sd_clock)
|
||||
{
|
||||
uint32_t timebase;
|
||||
|
||||
// Stop the SD card's clock, so our autocal sequence doesn't
|
||||
// confuse the target card.
|
||||
sdmmc_clock_enable(mmc, false);
|
||||
|
||||
// Start automatic calibration...
|
||||
mmc->regs->auto_cal_config |= (MMC_AUTOCAL_START | MMC_AUTOCAL_ENABLE);
|
||||
udelay(1000);
|
||||
|
||||
// ... and wait until the autocal is complete
|
||||
timebase = get_time();
|
||||
while ((mmc->regs->auto_cal_status & MMC_AUTOCAL_ACTIVE)) {
|
||||
|
||||
// Ensure we haven't timed out...
|
||||
if (get_time_since(timebase) > mmc->timeout) {
|
||||
mmc_print(mmc, "ERROR: autocal timed out!");
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
// If requested, enable the SD clock.
|
||||
if (restart_sd_clock)
|
||||
sdmmc_clock_enable(mmc, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Switches the Switch's microSD card into low-voltage mode.
|
||||
*
|
||||
* @param mmc The MMC controller via which to communicate.
|
||||
* @return 0 on success, or an error code on failure.
|
||||
*/
|
||||
static int sdmmc1_switch_to_low_voltage(struct mmc *mmc)
|
||||
{
|
||||
volatile struct tegra_pmc *pmc = pmc_get_regs();
|
||||
int rc;
|
||||
|
||||
// Let the SD card know we're about to switch into low-voltage mode.
|
||||
// Set up the card's relative address.
|
||||
rc = sdmmc_send_simple_command(mmc, CMD_SWITCH_TO_LOW_VOLTAGE, MMC_RESPONSE_LEN48, 0, NULL);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "card was not willling to switch to low voltage! (%d)", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Switch the MicroSD card supply into its low-voltage mode.
|
||||
supply_enable(SUPPLY_MICROSD, true);
|
||||
pmc->pwr_det_val &= ~PMC_CONTROL_SDMMC1;
|
||||
|
||||
// Apply our clocking parameters for low-voltage mode.
|
||||
rc = sdmmc_set_up_clocking_parameters(mmc, MMC_VOLTAGE_1V8);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "WARNING: could not optimize card clocking parameters. (%d)", rc);
|
||||
}
|
||||
|
||||
// Rerun the main clock calibration...
|
||||
rc = sdmmc_run_autocal(mmc, false);
|
||||
if (rc)
|
||||
mmc_print(mmc, "WARNING: failed to re-calibrate after voltage switch!");
|
||||
|
||||
// ... and ensure the host is set up to apply the relevant change.
|
||||
sdmmc_set_working_voltage(mmc, MMC_VOLTAGE_1V8);
|
||||
udelay(MMC_VOLTAGE_SWITCH_TIME);
|
||||
|
||||
// Enable the SD clock.
|
||||
sdmmc_clock_enable(mmc, true);
|
||||
udelay(MMC_POST_CLOCK_DELAY);
|
||||
|
||||
mmc_debug(mmc, "now running from 1V8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Low-voltage switching method for controllers that don't
|
||||
* support a low-voltage switch. Always fails.
|
||||
*
|
||||
* @param mmc The MMC controller via which to communicate.
|
||||
* @return ENOSYS, indicating failure, always
|
||||
*/
|
||||
static int sdmmc_always_fail(struct mmc *mmc)
|
||||
{
|
||||
// This card
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs low-level initialization for SDMMC1, used for the SD card slot.
|
||||
*/
|
||||
@ -620,11 +864,6 @@ static int sdmmc1_set_up_clock_and_io(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);
|
||||
|
||||
@ -638,45 +877,6 @@ static int sdmmc1_set_up_clock_and_io(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: decide if these should be split into separate functions after seeing how much
|
||||
// is common to the tunable modes
|
||||
|
||||
// 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.
|
||||
@ -724,41 +924,23 @@ static int sdmmc_hardware_init(struct mmc *mmc)
|
||||
regs->sdmemcomppadctrl &= ~(0x0F);
|
||||
regs->sdmemcomppadctrl |= 0x07;
|
||||
|
||||
// Set auto-calibration PD/PU offsets
|
||||
/*
|
||||
*/
|
||||
|
||||
// Set ourselves up to have a stable.
|
||||
rc = sdmmc_set_up_clocking_parameters(mmc);
|
||||
rc = sdmmc_set_up_clocking_parameters(mmc, mmc->operating_voltage);
|
||||
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)
|
||||
// Set PAD_E_INPUT_OR_E_PWRD
|
||||
regs->sdmemcomppadctrl |= 0x80000000;
|
||||
|
||||
// Wait one milisecond
|
||||
udelay(1000);
|
||||
|
||||
// Set AUTO_CAL_START and AUTO_CAL_ENABLE
|
||||
regs->auto_cal_config |= 0xA0000000;
|
||||
|
||||
udelay(1000);
|
||||
|
||||
// Program a timeout of 10ms
|
||||
is_timeout = false;
|
||||
timebase = get_time();
|
||||
|
||||
// Wait for AUTO_CAL_ACTIVE to be cleared
|
||||
while ((regs->auto_cal_status & 0x80000000) && !is_timeout) {
|
||||
// Keep checking if timeout expired
|
||||
is_timeout = get_time_since(timebase) > 10000;
|
||||
}
|
||||
|
||||
// AUTO_CAL_ACTIVE was not cleared in time
|
||||
if (is_timeout) {
|
||||
mmc_print(mmc, "autocal failed!");
|
||||
return ETIMEDOUT;
|
||||
// Run automatic calibration.
|
||||
rc = sdmmc_run_autocal(mmc, false);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "autocal failed! (%d)", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Clear PAD_E_INPUT_OR_E_PWRD (relevant for eMMC only)
|
||||
@ -800,12 +982,7 @@ static int sdmmc_hardware_init(struct mmc *mmc)
|
||||
regs->host_control &= 0xFD;
|
||||
regs->host_control &= 0xDF;
|
||||
|
||||
// Set up the SD card's voltage.
|
||||
regs->power_control &= 0xF1;
|
||||
regs->power_control |= mmc->operating_voltage << 1;
|
||||
|
||||
// Mark the power as on.
|
||||
regs->power_control |= 0x01;
|
||||
// TODO: move me into enable voltages, if applicable?
|
||||
|
||||
// Clear TAP_VAL_UPDATED_BY_HW
|
||||
regs->vendor_tuning_cntrl0 &= ~(0x20000);
|
||||
@ -819,10 +996,10 @@ 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 |= (0x30 << 8); // 200kHz, initially
|
||||
regs->clock_control |= (0x18 << 8); // 200kHz, initially
|
||||
|
||||
// Set SDHCI_CLOCK_CARD_EN
|
||||
regs->clock_control |= 0x04;
|
||||
// Start delivering the clock to the card.
|
||||
sdmmc_clock_enable(mmc, true);
|
||||
|
||||
// Ensure we're using Single-operation DMA (SDMA) mode for DMA.
|
||||
regs->host_control &= ~MMC_DMA_SELECT_MASK;
|
||||
@ -869,7 +1046,6 @@ static int sdmmc_wait_for_physical_state(struct mmc *mmc, uint32_t present_state
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Blocks until the SD driver is ready for a command,
|
||||
* or the MMC controller's timeout interval is met.
|
||||
@ -882,7 +1058,6 @@ static int sdmmc_wait_for_command_readiness(struct mmc *mmc)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Blocks until the SD driver is ready to transmit data,
|
||||
* or the MMC controller's timeout interval is met.
|
||||
@ -907,7 +1082,6 @@ static int sdmmc_wait_until_no_longer_busy(struct mmc *mmc)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Blocks until the SD driver has completed issuing a command.
|
||||
*
|
||||
@ -942,8 +1116,6 @@ static int sdmmc_wait_for_interrupt(struct mmc *mmc,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Blocks until the SD driver has completed issuing a command.
|
||||
*
|
||||
@ -966,6 +1138,21 @@ static int sdmmc_wait_for_transfer_completion(struct mmc *mmc)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the block order for a given operation on the MMC controller.
|
||||
*
|
||||
* @param mmc The MMC controller for which we're quierying block size.
|
||||
* @param is_write True iff the given operation is a write.
|
||||
*/
|
||||
static uint8_t sdmmc_get_block_order(struct mmc *mmc, bool is_write)
|
||||
{
|
||||
if (is_write)
|
||||
return mmc->write_block_order;
|
||||
else
|
||||
return mmc->read_block_order;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the block size for a given operation on the MMC controller.
|
||||
*
|
||||
@ -974,14 +1161,10 @@ static int sdmmc_wait_for_transfer_completion(struct mmc *mmc)
|
||||
*/
|
||||
static uint32_t sdmmc_get_block_size(struct mmc *mmc, bool is_write)
|
||||
{
|
||||
// FIXME: support write blocks?
|
||||
(void)is_write;
|
||||
|
||||
return (1 << mmc->read_block_order);
|
||||
return (1 << sdmmc_get_block_order(mmc, is_write));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Handles execution of a DATA stage using the CPU, rather than by using DMA.
|
||||
*
|
||||
@ -1232,7 +1415,6 @@ static int sdmmc_handle_command_response(struct mmc *mmc,
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sends a command to the SD card, and awaits a response.
|
||||
*
|
||||
@ -1359,7 +1541,6 @@ static int sdmmc_send_command(struct mmc *mmc, enum sdmmc_command command,
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convenience function that sends a simple SDMMC command
|
||||
* and awaits response. Wrapper around sdmmc_send_command.
|
||||
@ -1395,25 +1576,45 @@ static int sdmmc_send_simple_command(struct mmc *mmc, enum sdmmc_command command
|
||||
*
|
||||
* @returns 0 on success, an error number on failure
|
||||
*/
|
||||
static int sdmmc_send_simple_app_command(struct mmc *mmc, enum sdmmc_command command,
|
||||
static int sdmmc_send_app_command(struct mmc *mmc, enum sdmmc_command command,
|
||||
enum sdmmc_response_type response_type, enum sdmmc_response_checks checks,
|
||||
uint32_t argument, void *response_buffer)
|
||||
uint32_t argument, void *response_buffer, uint16_t blocks_to_transfer,
|
||||
bool auto_terminate, void *data_buffer)
|
||||
{
|
||||
int rc;
|
||||
|
||||
// First, send the application command.
|
||||
rc = sdmmc_send_simple_command(mmc, CMD_APP_COMMAND, MMC_RESPONSE_LEN48, 0, NULL);
|
||||
rc = sdmmc_send_simple_command(mmc, CMD_APP_COMMAND, MMC_RESPONSE_LEN48, mmc->relative_address << 16, NULL);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "failed to prepare application command! (%d)", rc);
|
||||
mmc_print(mmc, "failed to prepare application command %s! (%d)", sdmmc_get_command_string(command), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// And issue the body of the command.
|
||||
return sdmmc_send_command(mmc, command, response_type, checks, argument, response_buffer, 0, false, false, NULL);
|
||||
return sdmmc_send_command(mmc, command, response_type, checks, argument, response_buffer,
|
||||
blocks_to_transfer, false, auto_terminate, data_buffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sends an SDMMC application command.
|
||||
*
|
||||
* @param mmc The SDMMC device to be used to transmit the command.
|
||||
* @param response_type The type of response to expect-- mostly specifies the length.
|
||||
* @param checks Determines which sanity checks the host controller should run.
|
||||
* @param argument The argument to the SDMMC command.
|
||||
* @param response_buffer A buffer to store the response. Should be at uint32_t for a LEN48 command,
|
||||
* or 16 bytes for a LEN136 command.
|
||||
*
|
||||
* @returns 0 on success, an error number on failure
|
||||
*/
|
||||
static int sdmmc_send_simple_app_command(struct mmc *mmc, enum sdmmc_command command,
|
||||
enum sdmmc_response_type response_type, enum sdmmc_response_checks checks,
|
||||
uint32_t argument, void *response_buffer)
|
||||
{
|
||||
// Deletegate to the full app command function.
|
||||
return sdmmc_send_app_command(mmc, command, response_type, checks, argument, response_buffer, 0, false, NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -1486,6 +1687,79 @@ static int sdmmc_parse_csd_version1(struct mmc *mmc, uint32_t *csd)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decides on a block transfer sized based on the information observed,
|
||||
* and applies it to the card.
|
||||
*
|
||||
* @param mmc The controller to use to set the order
|
||||
* @param block_order The order (log-base-2) of the block size to be used.
|
||||
*/
|
||||
static int sdmmc_use_block_size(struct mmc *mmc, int block_order)
|
||||
{
|
||||
int rc;
|
||||
|
||||
// Inform the card of the block size we'll want to use.
|
||||
rc = sdmmc_send_simple_command(mmc, CMD_SET_BLKLEN, MMC_RESPONSE_LEN48, 1 << block_order, NULL);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "could not fetch the CID");
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
// On success, store the relevant block size.
|
||||
mmc->read_block_order = block_order;
|
||||
mmc->write_block_order = block_order;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads the active SD card's SD Configuration Register, and updates the object's properties.
|
||||
*
|
||||
* @param mmc The controller with which to query and to update.
|
||||
* @returns 0 on success, or an errno on failure
|
||||
*/
|
||||
static int sdmmc_read_and_parse_scr(struct mmc *mmc)
|
||||
{
|
||||
int rc;
|
||||
struct sdmmc_scr scr;
|
||||
|
||||
// Read the current block order, so we can restore it.
|
||||
int original_block_order = sdmmc_get_block_order(mmc, false);
|
||||
|
||||
// Always request a single 8-byte block.
|
||||
const int block_order = 3;
|
||||
const int num_blocks = 1;
|
||||
|
||||
// Momentarily step down to a smaller block size, so we don't
|
||||
// have to allocate a huge buffer for this command.
|
||||
rc = sdmmc_use_block_size(mmc, block_order);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "could not step down to a smaller block size! (%d)", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Request the CSD from the device.
|
||||
rc = sdmmc_send_app_command(mmc, CMD_APP_SEND_SCR, MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, 0, NULL, num_blocks, false, &scr);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "could not get the card's SCR!");
|
||||
sdmmc_use_block_size(mmc, original_block_order);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Store the SCR data.
|
||||
mmc->spec_version = scr.spec_version;
|
||||
|
||||
// Restore the original block order.
|
||||
rc = sdmmc_use_block_size(mmc, original_block_order);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "could not restore the original block size! (%d)", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads the active MMC card's Card Specific Data, and updates the MMC object's properties.
|
||||
*
|
||||
@ -1558,35 +1832,12 @@ static int sdmmc_read_and_parse_ext_csd(struct mmc *mmc)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decides on a block transfer sized based on the information observed,
|
||||
* and applies it to the card.
|
||||
*/
|
||||
static int sdmmc_set_up_block_transfer_size(struct mmc *mmc)
|
||||
{
|
||||
int rc;
|
||||
|
||||
// For now, we'll only ever set up 512B blocks, because
|
||||
// 1) every card supports this, and 2) we use SDMA, which only supports up to 512B
|
||||
mmc->read_block_order = 9;
|
||||
|
||||
// Inform the card of the block size we'll want to use.
|
||||
rc = sdmmc_send_simple_command(mmc, CMD_SET_BLKLEN, MMC_RESPONSE_LEN48, 1 << mmc->read_block_order, NULL);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "could not fetch the CID");
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Switches the SDMMC card and controller to the fullest bus width possible.
|
||||
*
|
||||
* @param mmc The MMC controller to switch up to a full bus width.
|
||||
*/
|
||||
static int sdmmc_switch_bus_width(struct mmc *mmc, enum sdmmc_bus_width width)
|
||||
static int sdmmc_mmc_switch_bus_width(struct mmc *mmc, enum sdmmc_bus_width width)
|
||||
{
|
||||
// Ask the card to adjust to the wider bus width.
|
||||
int rc = mmc->switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL,
|
||||
@ -1596,7 +1847,7 @@ static int sdmmc_switch_bus_width(struct mmc *mmc, enum sdmmc_bus_width width)
|
||||
return rc;
|
||||
}
|
||||
|
||||
// And switch the bus width on our side.
|
||||
// Apply the same changes on the host side.
|
||||
mmc->regs->host_control &= ~MMC_HOST_BUS_WIDTH_MASK;
|
||||
switch (width) {
|
||||
case MMC_BUS_WIDTH_4BIT:
|
||||
@ -1608,6 +1859,40 @@ static int sdmmc_switch_bus_width(struct mmc *mmc, enum sdmmc_bus_width width)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Switches the SDMMC card and controller to the fullest bus width possible.
|
||||
*
|
||||
* @param mmc The MMC controller to switch up to a full bus width.
|
||||
*/
|
||||
static int sdmmc_sd_switch_bus_width(struct mmc *mmc, enum sdmmc_bus_width width)
|
||||
{
|
||||
// By default, SD DAT3 is used for card detect. We'll need to
|
||||
// release it from this function by dropping its pull-up resistor
|
||||
// before we can use the line for data. Do so.
|
||||
int rc = sdmmc_send_simple_app_command(mmc, CMD_APP_SET_CARD_DETECT,
|
||||
MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, 0, NULL);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "could not stop using DAT3 as a card detect!");
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Ask the card to adjust to the wider bus width.
|
||||
rc = sdmmc_send_simple_app_command(mmc, CMD_APP_SWITCH_WIDTH,
|
||||
MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, width, NULL);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "could not switch mode on the card side!");
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Apply the same changes on the host side.
|
||||
mmc->regs->host_control &= ~MMC_HOST_BUS_WIDTH_MASK;
|
||||
if (mmc->max_bus_width == SD_BUS_WIDTH_4BIT) {
|
||||
mmc->regs->host_control |= MMC_HOST_BUS_WIDTH_4BIT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1616,12 +1901,12 @@ static int sdmmc_switch_bus_width(struct mmc *mmc, enum sdmmc_bus_width width)
|
||||
/**
|
||||
* Optimize our SDMMC transfer mode to fully utilize the bus.
|
||||
*/
|
||||
static int mmc_optimize_transfer_mode(struct mmc *mmc)
|
||||
static int sdmmc_optimize_transfer_mode(struct mmc *mmc)
|
||||
{
|
||||
int rc;
|
||||
|
||||
// Switch the device to its maximum bus width.
|
||||
rc = sdmmc_switch_bus_width(mmc, mmc->max_bus_width);
|
||||
rc = mmc->switch_bus_width(mmc, mmc->max_bus_width);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "could not switch the controller's bus width!");
|
||||
return rc;
|
||||
@ -1633,31 +1918,6 @@ static int mmc_optimize_transfer_mode(struct mmc *mmc)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves information about the card, and populates the MMC structure accordingly.
|
||||
* Used as part of the SDMMC initialization process.
|
||||
*/
|
||||
static int sdmmc_set_up_partitions(struct mmc *mmc)
|
||||
{
|
||||
// If the card doesn't support partitions, fail out.
|
||||
if (!(mmc->partition_support & MMC_SUPPORTS_HARDWARE_PARTS))
|
||||
return ENOTTY;
|
||||
|
||||
// If the card hasn't been partitioned, fail out.
|
||||
// We don't support setting up hardware partitioning.
|
||||
if (!mmc->partitioned) {
|
||||
mmc_print(mmc, "NOTE: card supports partitions but is not partitioned");
|
||||
return ENOTDIR;
|
||||
}
|
||||
|
||||
mmc_print(mmc, "detected a card with hardware partitions.");
|
||||
|
||||
// Use partitioning.
|
||||
return mmc->switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL,
|
||||
MMC_GROUP_ERASE_DEF, MMC_EXT_CSD_ERASE_GROUP_DEF_BIT, mmc->timeout);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Requests that an MMC target use the card's current relative address.
|
||||
*
|
||||
@ -1708,8 +1968,10 @@ static int sdmmc_get_relative_address(struct mmc *mmc)
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves information about the card, and populates the MMC structure accordingly.
|
||||
* Used as part of the SDMMC initialization process.
|
||||
* Shared card initialization for SD and MMC cards.
|
||||
* Used to bring the card fully online and gather information about the card.
|
||||
*
|
||||
* @param mmc The MMC controller that will perform the initilaization.
|
||||
*/
|
||||
static int sdmmc_card_init(struct mmc *mmc)
|
||||
{
|
||||
@ -1747,8 +2009,9 @@ static int sdmmc_card_init(struct mmc *mmc)
|
||||
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);
|
||||
// Set up a block transfer size of 512B blocks.
|
||||
// 1) every card supports this, and 2) we use SDMA, which only supports up to 512B
|
||||
rc = sdmmc_use_block_size(mmc, MMC_DEFAULT_BLOCK_ORDER);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "could not set up block transfer sizes! (%d)", rc);
|
||||
return rc;
|
||||
@ -1809,13 +2072,16 @@ 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;
|
||||
|
||||
// If this is a SDv2 or higher card, check for an SDHC card.
|
||||
if (mmc->spec_version >= SD_VERSION_2) {
|
||||
// If this is a SDv2 or higher card, check for an SDHC card,
|
||||
// and for low-voltage support.
|
||||
if (mmc->spec_version >= SD_VERSION_2_0) {
|
||||
argument |= MMC_SD_OPERATING_COND_HIGH_CAPACITY;
|
||||
argument |= MMC_SD_OPERATING_COND_ACCEPTS_1V8;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
// Ask the SD card to identify its state. It will respond with readiness and a capacity magic.
|
||||
|
||||
// Ask the SD card to identify its state.
|
||||
rc = sdmmc_send_simple_app_command(mmc, CMD_APP_SEND_OP_COND,
|
||||
MMC_RESPONSE_LEN48, MMC_CHECKS_NONE, argument, response);
|
||||
if (rc) {
|
||||
@ -1863,15 +2129,6 @@ static int sdmmc_mmc_card_init(struct mmc *mmc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Switch to a transfer mode that can more efficiently utilize the bus.
|
||||
/*
|
||||
rc = mmc_optimize_transfer_mode(mmc);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "could not optimize bus utlization! (%d)", rc);
|
||||
}
|
||||
*/
|
||||
(void)mmc_optimize_transfer_mode;
|
||||
|
||||
// Read and handle card's Extended Card Specific Data (ext-CSD).
|
||||
rc = sdmmc_read_and_parse_ext_csd(mmc);
|
||||
if (rc) {
|
||||
@ -1879,12 +2136,6 @@ static int sdmmc_mmc_card_init(struct mmc *mmc)
|
||||
return EPIPE;
|
||||
}
|
||||
|
||||
// Set up MMC card partitioning, if supported.
|
||||
rc = sdmmc_set_up_partitions(mmc);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "NOTE: card cannot be used with hardware partitions", rc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1903,7 +2154,6 @@ static bool sdmmc_check_pattern_present(uint32_t response)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Handles SD-specific card initialization.
|
||||
*/
|
||||
@ -1921,8 +2171,6 @@ 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);
|
||||
if (rc || !sdmmc_check_pattern_present(response)) {
|
||||
@ -1937,7 +2185,7 @@ static int sdmmc_sd_card_init(struct mmc *mmc)
|
||||
// If this responded, indicate that this is a v2 card.
|
||||
else {
|
||||
// store that this is a v2 card
|
||||
mmc->spec_version = SD_VERSION_2;
|
||||
mmc->spec_version = SD_VERSION_2_0;
|
||||
}
|
||||
|
||||
// Wait for the card to finish being busy.
|
||||
@ -1951,6 +2199,15 @@ static int sdmmc_sd_card_init(struct mmc *mmc)
|
||||
// always use block addressing.
|
||||
mmc->uses_block_addressing = !!(ocr & MMC_SD_OPERATING_COND_HIGH_CAPACITY);
|
||||
|
||||
// If the card supports using 1V8, drop down using lower voltages.
|
||||
if (ocr & MMC_SD_OPERATING_COND_ACCEPTS_1V8) {
|
||||
if (mmc->operating_voltage != MMC_VOLTAGE_1V8) {
|
||||
rc = mmc->switch_to_low_voltage(mmc);
|
||||
if (rc)
|
||||
mmc_print(mmc, "WARNING: could not switch to low-voltage mode! (%d)", rc);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the common core card initialization.
|
||||
rc = sdmmc_card_init(mmc);
|
||||
if (rc) {
|
||||
@ -1958,8 +2215,13 @@ static int sdmmc_sd_card_init(struct mmc *mmc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
// FIXME: optimize bus utilization here?
|
||||
// is this just a call to the same routine as for eMMC?
|
||||
// Read the card's SCR.
|
||||
rc = sdmmc_read_and_parse_scr(mmc);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "failed to read SCR! (%d)!", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2115,6 +2377,7 @@ static void sdmmc_apply_card_type(struct mmc *mmc, enum sdmmc_card_type type)
|
||||
mmc->card_init = sdmmc_mmc_card_init;
|
||||
mmc->establish_relative_address = sdmmc_set_relative_address;
|
||||
mmc->switch_mode = sdmmc_mmc_switch_mode;
|
||||
mmc->switch_bus_width = sdmmc_mmc_switch_bus_width;
|
||||
break;
|
||||
|
||||
// SD-protocol cards
|
||||
@ -2122,6 +2385,7 @@ static void sdmmc_apply_card_type(struct mmc *mmc, enum sdmmc_card_type type)
|
||||
mmc->card_init = sdmmc_sd_card_init;
|
||||
mmc->establish_relative_address = sdmmc_get_relative_address;
|
||||
mmc->switch_mode = sdmmc_sd_switch_mode;
|
||||
mmc->switch_bus_width = sdmmc_sd_switch_bus_width;
|
||||
break;
|
||||
|
||||
// Switch-cart protocol cards
|
||||
@ -2149,6 +2413,7 @@ static int sdmmc_initialize_defaults(struct mmc *mmc)
|
||||
// Set up function pointers for each of our per-instance functions.
|
||||
mmc->set_up_clock_and_io = sdmmc4_set_up_clock_and_io;
|
||||
mmc->enable_supplies = sdmmc4_enable_supplies;
|
||||
mmc->switch_to_low_voltage = sdmmc_always_fail;
|
||||
mmc->card_present = sdmmc_builtin_card_present;
|
||||
|
||||
// The EMMC controller always uses an EMMC card.
|
||||
@ -2161,7 +2426,7 @@ static int sdmmc_initialize_defaults(struct mmc *mmc)
|
||||
case SWITCH_MICROSD:
|
||||
mmc->name = "uSD";
|
||||
mmc->card_type = MMC_CARD_SD;
|
||||
mmc->max_bus_width = MMC_BUS_WIDTH_4BIT;
|
||||
mmc->max_bus_width = SD_BUS_WIDTH_4BIT;
|
||||
mmc->operating_voltage = MMC_VOLTAGE_3V3;
|
||||
mmc->card_detect_gpio = GPIO_MICROSD_CARD_DETECT;
|
||||
|
||||
@ -2169,6 +2434,7 @@ static int sdmmc_initialize_defaults(struct mmc *mmc)
|
||||
// Negotiation has a chance to change this, later.
|
||||
mmc->set_up_clock_and_io = sdmmc1_set_up_clock_and_io;
|
||||
mmc->enable_supplies = sdmmc1_enable_supplies;
|
||||
mmc->switch_to_low_voltage = sdmmc1_switch_to_low_voltage;
|
||||
mmc->card_present = sdmmc_external_card_present;
|
||||
sdmmc_apply_card_type(mmc, MMC_CARD_SD);
|
||||
|
||||
@ -2188,7 +2454,6 @@ static int sdmmc_initialize_defaults(struct mmc *mmc)
|
||||
|
||||
/**
|
||||
* Set up a new SDMMC driver.
|
||||
* FIXME: clean up!
|
||||
*
|
||||
* @param mmc The SDMMC structure to be initiailized with the device state.
|
||||
* @param controler The controller description to be used; usually SWITCH_EMMC
|
||||
@ -2243,6 +2508,12 @@ int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller)
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Switch to a transfer mode that can more efficiently utilize the bus.
|
||||
rc = sdmmc_optimize_transfer_mode(mmc);
|
||||
if (rc) {
|
||||
mmc_print(mmc, "WARNING: could not optimize bus utlization! (%d)", rc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2356,7 +2627,7 @@ int sdmmc_write(struct mmc *mmc, const void *buffer, uint32_t block, unsigned in
|
||||
// If this card uses byte addressing rather than sector addressing,
|
||||
// multiply by the block size.
|
||||
if (!mmc->uses_block_addressing) {
|
||||
extent *= sdmmc_get_block_size(mmc, false);
|
||||
extent *= sdmmc_get_block_size(mmc, true);
|
||||
}
|
||||
|
||||
// Execute the relevant read.
|
||||
|
@ -22,6 +22,9 @@ enum sdmmc_bus_width {
|
||||
MMC_BUS_WIDTH_1BIT = 0,
|
||||
MMC_BUS_WIDTH_4BIT = 1,
|
||||
MMC_BUS_WIDTH_8BIT = 2,
|
||||
|
||||
SD_BUS_WIDTH_1BIT = 0,
|
||||
SD_BUS_WIDTH_4BIT = 2
|
||||
};
|
||||
|
||||
|
||||
@ -58,8 +61,9 @@ enum sdmmc_spec_version {
|
||||
MMC_VERSION_4 = 0,
|
||||
|
||||
/* SD card versions */
|
||||
SD_VERSION_1 = 1,
|
||||
SD_VERSION_2 = 2,
|
||||
SD_VERSION_1_0 = 0,
|
||||
SD_VERSION_1_1 = 1,
|
||||
SD_VERSION_2_0 = 2,
|
||||
|
||||
};
|
||||
|
||||
@ -140,6 +144,7 @@ struct mmc {
|
||||
/* Per-controller operations. */
|
||||
int (*set_up_clock_and_io)(struct mmc *mmc);
|
||||
int (*enable_supplies)(struct mmc *mmc);
|
||||
int (*switch_to_low_voltage)(struct mmc *mmc);
|
||||
bool (*card_present)(struct mmc *mmc);
|
||||
|
||||
/* Per-card-type operations */
|
||||
@ -147,6 +152,7 @@ struct mmc {
|
||||
int (*establish_relative_address)(struct mmc *mmc);
|
||||
int (*switch_mode)(struct mmc *mmc, enum sdmmc_switch_access_mode mode,
|
||||
enum sdmmc_switch_field field, uint16_t value, uint32_t timeout);
|
||||
int (*switch_bus_width)(struct mmc *mmc, enum sdmmc_bus_width width);
|
||||
|
||||
/* Card properties */
|
||||
uint8_t cid[15];
|
||||
@ -162,6 +168,7 @@ struct mmc {
|
||||
uint32_t partition_switch_time;
|
||||
|
||||
uint8_t read_block_order;
|
||||
uint8_t write_block_order;
|
||||
bool uses_block_addressing;
|
||||
|
||||
/* Pointers to hardware structures */
|
||||
|
@ -13,12 +13,19 @@
|
||||
* Enables a given power supply.
|
||||
*
|
||||
* @param supply The power domain on the Switch that is to be enabled.
|
||||
* @param use_low_voltage If the supply supports multiple voltages, use the lower one.
|
||||
* Some devices start in a high power mode, but an can be switched to a lower one.
|
||||
* Set this to false unless you know what you're doing.
|
||||
*/
|
||||
void supply_enable(enum switch_power_supply supply)
|
||||
void supply_enable(enum switch_power_supply supply, bool use_low_voltage)
|
||||
{
|
||||
uint32_t voltage = 0;
|
||||
|
||||
switch(supply) {
|
||||
case SUPPLY_MICROSD:
|
||||
max77620_regulator_set_voltage(SUPPLY_MICROSD_REGULATOR, SUPPLY_MICROSD_VOLTAGE);
|
||||
voltage = use_low_voltage ? SUPPLY_MICROSD_LOW_VOLTAGE : SUPPLY_MICROSD_VOLTAGE;
|
||||
|
||||
max77620_regulator_set_voltage(SUPPLY_MICROSD_REGULATOR, voltage);
|
||||
max77620_regulator_enable(SUPPLY_MICROSD_REGULATOR, true);
|
||||
return;
|
||||
|
||||
|
@ -18,14 +18,19 @@ enum switch_power_constants {
|
||||
/* MicroSD card */
|
||||
SUPPLY_MICROSD_REGULATOR = 6,
|
||||
SUPPLY_MICROSD_VOLTAGE = 3300000,
|
||||
SUPPLY_MICROSD_LOW_VOLTAGE = 1800000,
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Enables a given power supply.
|
||||
*
|
||||
* @param supply The power domain on the Switch that is to be enabled.
|
||||
* @param use_low_voltage If the supply supports multiple voltages, use the lower one.
|
||||
* Some devices start in a high power mode, but an can be switched to a lower one.
|
||||
* Set this to false unless you know what you're doing.
|
||||
*/
|
||||
void supply_enable(enum switch_power_supply supply);
|
||||
void supply_enable(enum switch_power_supply supply, bool use_low_voltage);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user