1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-11-22 20:06:40 +00:00

fusee: handle SDMMC speeds sanely

This commit is contained in:
Kate J. Temkin 2018-05-18 13:17:04 -06:00
parent ec7e0d923d
commit 817c42efc2
3 changed files with 208 additions and 64 deletions

View file

@ -51,8 +51,6 @@ enum {
CLK_SOURCE_MASK = (0b111 << 29), CLK_SOURCE_MASK = (0b111 << 29),
CLK_SOURCE_FIRST = 0, CLK_SOURCE_FIRST = 0,
CLK_DIVIDER_UNITY = 0, CLK_DIVIDER_UNITY = 0,
CLK_DIVIDER_32 = 32,
}; };

View file

@ -126,11 +126,21 @@ enum sdmmc_response_type {
/** /**
* Lengths of SD command responses * Lengths of SD command responses
*/ */
enum sdmmc_response_sizes { enum sdmmc_constants {
/* Bytes in a LEN136 response */ /* Bytes in a LEN136 response */
MMC_RESPONSE_SIZE_LEN136 = 15, MMC_RESPONSE_SIZE_LEN136 = 15,
}; };
/**
* SDMMC clock divider constants
*/
enum sdmmc_clock_dividers {
MMC_CLOCK_DIVIDER_SDR12 = 32, // 16.5, from the TRM table
MMC_CLOCK_DIVIDER_SDR25 = 16, // 8.5, from the table
MMC_CLOCK_DIVIDER_SDR50 = 8, // 4.5, from the table
MMC_CLOCK_DIVIDER_SDR104 = 3, // 2, from the datasheet
};
/** /**
* SDMMC response sanity checks * SDMMC response sanity checks
* see the standard for when these should be used * see the standard for when these should be used
@ -153,6 +163,8 @@ enum sdmmc_register_bits {
MMC_BUFFER_WRITE_ENABLE = (1 << 10), MMC_BUFFER_WRITE_ENABLE = (1 << 10),
MMC_BUFFER_READ_ENABLE = (1 << 11), MMC_BUFFER_READ_ENABLE = (1 << 11),
MMC_DAT0_LINE_STATE = (1 << 20), MMC_DAT0_LINE_STATE = (1 << 20),
MMC_READ_ACTIVE = (1 << 9),
MMC_WRITE_ACTIVE = (1 << 8),
/* Block size register */ /* Block size register */
MMC_DMA_BOUNDARY_MAXIMUM = (0x7 << 12), MMC_DMA_BOUNDARY_MAXIMUM = (0x7 << 12),
@ -193,6 +205,8 @@ enum sdmmc_register_bits {
/* Clock control */ /* Clock control */
MMC_CLOCK_CONTROL_CARD_CLOCK_ENABLE = (1 << 2), MMC_CLOCK_CONTROL_CARD_CLOCK_ENABLE = (1 << 2),
MMC_CLOCK_CONTROL_FREQUENCY_MASK = (0x3FF << 6),
MMC_CLOCK_CONTROL_FREQUENCY_INIT = (0x18 << 8),
/* Host control */ /* Host control */
MMC_DMA_SELECT_MASK = (0x3 << 3), MMC_DMA_SELECT_MASK = (0x3 << 3),
@ -200,6 +214,15 @@ enum sdmmc_register_bits {
MMC_HOST_BUS_WIDTH_MASK = (1 << 1) | (1 << 5), MMC_HOST_BUS_WIDTH_MASK = (1 << 1) | (1 << 5),
MMC_HOST_BUS_WIDTH_4BIT = (1 << 1), MMC_HOST_BUS_WIDTH_4BIT = (1 << 1),
MMC_HOST_BUS_WIDTH_8BIT = (1 << 5), MMC_HOST_BUS_WIDTH_8BIT = (1 << 5),
MMC_HOST_ENABLE_HIGH_SPEED = (1 << 2),
/* Host control 2 */
MMC_HOST2_DRIVE_STRENGTH_MASK = (0x3 << 4),
MMC_HOST2_DRIVE_STRENGTH_B = (0x0 << 4),
MMC_HOST2_DRIVE_STRENGTH_A = (0x1 << 4),
MMC_HOST2_DRIVE_STRENGTH_C = (0x2 << 4),
MMC_HOST2_DRIVE_STRENGTH_D = (0x3 << 4),
MMC_HOST2_USE_1V8_SIGNALING = (1 << 3),
/* Software reset */ /* Software reset */
MMC_SOFT_RESET_FULL = (1 << 0), MMC_SOFT_RESET_FULL = (1 << 0),
@ -327,6 +350,12 @@ enum sdmmc_command_magic {
MMC_DEFAULT_BLOCK_ORDER = 9, MMC_DEFAULT_BLOCK_ORDER = 9,
MMC_VOLTAGE_SWITCH_TIME = 5000, // 5mS MMC_VOLTAGE_SWITCH_TIME = 5000, // 5mS
MMC_POST_CLOCK_DELAY = 1000, // 1mS MMC_POST_CLOCK_DELAY = 1000, // 1mS
/* Switch mode constants */
SDMMC_SWITCH_MODE_MODE_SHIFT = 31,
SDMMC_SWITCH_MODE_ALL_FUNCTIONS_UNUSED = 0xFFFFFF,
SDMMC_SWITCH_MODE_FUNCTION_MASK = 0xF,
SDMMC_SWITCH_MODE_GROUP_SIZE_BITS = 4,
}; };
@ -384,6 +413,20 @@ enum sdmmc_ext_csd_extents {
}; };
/**
* Bus speeds possible for an SDMMC controller.
*/
enum sdmmc_bus_speed {
SDMMC_SPEED_SDR12 = 0,
SDMMC_SPEED_SDR25 = 1,
SDMMC_SPEED_SDR50 = 2,
SDMMC_SPEED_SDR104 = 3,
SDMMC_SPEED_DDR50 = 4,
SDMMC_SPEED_INIT = -1,
};
/** /**
* Bitfield struct representing an SD SCR. * Bitfield struct representing an SD SCR.
*/ */
@ -569,12 +612,13 @@ static int sdmmc4_set_up_clock_and_io(struct mmc *mmc)
// Put SDMMC4 in reset // Put SDMMC4 in reset
car->rst_dev_l_set |= 0x8000; car->rst_dev_l_set |= 0x8000;
// Set SDMMC4 clock source (PLLP_OUT0) and divisor (32). // Set SDMMC4 clock source (PLLP_OUT0) and divisor (17).
// We use 32 beacuse Nintendo does, and they probably know what they're doing? // Divider value is the suggested base from the dataseet.
car->clk_src[CLK_SOURCE_SDMMC4] = CLK_SOURCE_FIRST | CLK_DIVIDER_32; // TODO: abstract me!
car->clk_src[CLK_SOURCE_SDMMC4] = CLK_SOURCE_FIRST | MMC_CLOCK_DIVIDER_SDR12;
// Set the legacy divier used for detecting timeouts. // Set the legacy divier used for detecting timeouts.
car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = CLK_SOURCE_FIRST | CLK_DIVIDER_32; car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = CLK_SOURCE_FIRST | MMC_CLOCK_DIVIDER_SDR12;
// Set SDMMC4 clock enable // Set SDMMC4 clock enable
car->clk_enb_l_set |= 0x8000; car->clk_enb_l_set |= 0x8000;
@ -608,6 +652,9 @@ static void sdmmc_set_working_voltage(struct mmc *mmc, enum sdmmc_bus_voltage vo
mmc->regs->power_control &= ~MMC_POWER_CONTROL_VOLTAGE_MASK; mmc->regs->power_control &= ~MMC_POWER_CONTROL_VOLTAGE_MASK;
mmc->regs->power_control |= voltage << MMC_POWER_CONTROL_VOLTAGE_SHIFT; mmc->regs->power_control |= voltage << MMC_POWER_CONTROL_VOLTAGE_SHIFT;
// Switch to 1V8 signaling.
mmc->regs->host_control2 |= MMC_HOST2_USE_1V8_SIGNALING;
// Mark the power as on. // Mark the power as on.
mmc->regs->power_control |= MMC_POWER_CONTROL_POWER_ENABLE; mmc->regs->power_control |= MMC_POWER_CONTROL_POWER_ENABLE;
} }
@ -860,12 +907,13 @@ static int sdmmc1_set_up_clock_and_io(struct mmc *mmc)
// Put SDMMC1 in reset // Put SDMMC1 in reset
car->rst_dev_l_set |= CAR_CONTROL_SDMMC1; car->rst_dev_l_set |= CAR_CONTROL_SDMMC1;
// Set SDMMC1 clock source (PLLP_OUT0) and divisor (32). // Set SDMMC1 clock source (PLLP_OUT0) and divisor (div by 17).
// We use 32 beacuse Nintendo does, and they probably know what they're doing? // This is the datasheet recommended value for SDR12.
car->clk_src[CLK_SOURCE_SDMMC1] = CLK_SOURCE_FIRST | CLK_DIVIDER_32; // TODO: abstract this into a clock source specification function!
car->clk_src[CLK_SOURCE_SDMMC1] = CLK_SOURCE_FIRST | MMC_CLOCK_DIVIDER_SDR12;
// Set the legacy divier used for detecting timeouts. // Set the legacy divier used for detecting timeouts.
car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = CLK_SOURCE_FIRST | CLK_DIVIDER_32; car->clk_src_y[CLK_SOURCE_SDMMC_LEGACY] = CLK_SOURCE_FIRST | MMC_CLOCK_DIVIDER_SDR12;
// Set SDMMC1 clock enable // Set SDMMC1 clock enable
car->clk_enb_l_set |= CAR_CONTROL_SDMMC1; car->clk_enb_l_set |= CAR_CONTROL_SDMMC1;
@ -884,6 +932,53 @@ static int sdmmc1_set_up_clock_and_io(struct mmc *mmc)
} }
/**
* Applies the appropriate clock dividers to the CAR and SD controller to enable use of the
* provided speed. Does not handle any requisite communications with the card.
*
* @param mmc The controller to affect.
* @param speed The speed to apply.
* @param enable_after If set, the SDMMC clock will be enabled after the change. If not, it will be left disabled.
*/
static int sdmmc_apply_clock_speed(struct mmc *mmc, enum sdmmc_bus_speed speed, bool enable_after)
{
// TODO: also set the clock for the main divider
// Ensure the clocks are not currently running to avoid glitches.
sdmmc_clock_enable(mmc, false);
// Clear the registers of any existing values, so we can apply new ones.
mmc->regs->clock_control &= ~MMC_CLOCK_CONTROL_FREQUENCY_MASK;
mmc->regs->host_control &= ~MMC_HOST_ENABLE_HIGH_SPEED;
mmc->regs->host_control2 &= ~MMC_HOST2_DRIVE_STRENGTH_MASK;
// Apply the dividers according to the speed provided.
switch (speed) {
// 400kHz initialization mode.
case SDMMC_SPEED_INIT:
mmc->regs->clock_control |= MMC_CLOCK_CONTROL_FREQUENCY_INIT;
break;
// 25MHz default speed
case SDMMC_SPEED_SDR12:
break;
default:
mmc_print(mmc, "ERROR: switching to unsupported speed!\n");
return ENOSYS;
}
// Re-enable the clock, if necessary.
if (enable_after) {
sdmmc_clock_enable(mmc, true);
udelay(MMC_POST_CLOCK_DELAY);
}
return 0;
}
/** /**
* Initialize the low-level SDMMC hardware. * Initialize the low-level SDMMC hardware.
* Thanks to hexkyz for this init code. * Thanks to hexkyz for this init code.
@ -978,38 +1073,30 @@ static int sdmmc_hardware_init(struct mmc *mmc)
// Clear SDHCI_PROG_CLOCK_MODE // Clear SDHCI_PROG_CLOCK_MODE
regs->clock_control &= ~(0x20); regs->clock_control &= ~(0x20);
// Clear SDHCI_CTRL_SDMA and SDHCI_CTRL_ADMA2 // Ensure we're using Single-operation DMA (SDMA) mode for DMA.
regs->host_control &= 0xE7; regs->host_control &= ~MMC_DMA_SELECT_MASK;
// Set the timeout to be the maximum value // Set the timeout to be the maximum value
regs->timeout_control &= ~(0x0F); regs->timeout_control &= ~(0x0F);
regs->timeout_control |= 0x0E; regs->timeout_control |= 0x0E;
// Clear SDHCI_CTRL_4BITBUS and SDHCI_CTRL_8BITBUS // Ensure we start off using a single-bit bus.
regs->host_control &= 0xFD; mmc->regs->host_control &= ~MMC_HOST_BUS_WIDTH_MASK;
regs->host_control &= 0xDF;
// TODO: move me into enable voltages, if applicable? // TODO: move me into enable voltages, if applicable?
// Clear TAP_VAL_UPDATED_BY_HW // Clear TAP_VAL_UPDATED_BY_HW
regs->vendor_tuning_cntrl0 &= ~(0x20000); regs->vendor_tuning_cntrl0 &= ~(0x20000);
// Clear SDHCI_CTRL_HISPD // Start off in non-high-speed mode.
regs->host_control &= 0xFB; regs->host_control &= ~MMC_HOST_ENABLE_HIGH_SPEED;
// Clear SDHCI_CTRL_VDD_180 // Clear SDHCI_CTRL_VDD_180
regs->host_control2 &= ~(0x08); regs->host_control2 &= ~(0x08);
// Set SDHCI_DIVIDER and SDHCI_DIVIDER_HI // Set up the card's initialization.
// FIXME: divider SD if necessary sdmmc_apply_clock_speed(mmc, SDMMC_SPEED_INIT, true);
regs->clock_control &= ~(0xFFC0);
regs->clock_control |= (0x18 << 8); // 200kHz, initially
// 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;
return 0; return 0;
} }
@ -1115,11 +1202,14 @@ static int sdmmc_flush_bounce_buffer(struct mmc *mmc)
} }
/** /**
* Blocks until the SD driver has completed issuing a command. * Generic SDMMC waiting function.
* *
* @param mmc The MMC controller on which to wait. * @param mmc The MMC controller on which to wait.
* @param target_irq A bitmask that specifies the bits that * @param target_irq A bitmask that specifies the interrupt bits that
* will make this function return success * will make this function return success.
* @param state_condition A bitmask that specifies a collection of bits
* that indicate business in present_state. If zero, all of the relevant
* conditions becoming false will cause a sucessful return.
* @param fault_conditions A bitmask that specifies the bits that * @param fault_conditions A bitmask that specifies the bits that
* will make this function trigger its fault handler. * will make this function trigger its fault handler.
* @param fault_handler A function that's called to handle DMA faults. * @param fault_handler A function that's called to handle DMA faults.
@ -1129,8 +1219,9 @@ static int sdmmc_flush_bounce_buffer(struct mmc *mmc)
* @return 0 on sucess, EFAULT if a fault condition occurs, * @return 0 on sucess, EFAULT if a fault condition occurs,
* or an error code if a transfer failure occurs * or an error code if a transfer failure occurs
*/ */
static int sdmmc_wait_for_interrupt(struct mmc *mmc, static int sdmmc_wait_for_event(struct mmc *mmc,
uint32_t target_irq, uint32_t fault_conditions, fault_handler_t fault_handler) uint32_t target_irq, uint32_t state_conditions,
uint32_t fault_conditions, fault_handler_t fault_handler)
{ {
uint32_t timebase = get_time(); uint32_t timebase = get_time();
int rc; int rc;
@ -1144,14 +1235,14 @@ static int sdmmc_wait_for_interrupt(struct mmc *mmc,
// If we don't have a handler, fault. // If we don't have a handler, fault.
if (!fault_handler) { if (!fault_handler) {
mmc_print(mmc, "ERROR: unhandled DMA fault!\n"); mmc_print(mmc, "ERROR: unhandled DMA fault!");
return EFAULT; return EFAULT;
} }
// Call the DMA fault handler. // Call the DMA fault handler.
rc = fault_handler(mmc); rc = fault_handler(mmc);
if (rc) { if (rc) {
mmc_print(mmc, "ERROR: unhandled DMA fault!\n (%d)", rc); mmc_print(mmc, "ERROR: unhandled DMA fault! (%d)", rc);
return rc; return rc;
} }
@ -1166,6 +1257,9 @@ static int sdmmc_wait_for_interrupt(struct mmc *mmc,
if (mmc->regs->int_status & target_irq) if (mmc->regs->int_status & target_irq)
return 0; return 0;
if (state_conditions && !(mmc->regs->present_state & state_conditions))
return 0;
// If an error occurs, return it. // If an error occurs, return it.
if (mmc->regs->int_status & MMC_STATUS_ERROR_MASK) if (mmc->regs->int_status & MMC_STATUS_ERROR_MASK)
return (mmc->regs->int_status & MMC_STATUS_ERROR_MASK); return (mmc->regs->int_status & MMC_STATUS_ERROR_MASK);
@ -1179,7 +1273,7 @@ static int sdmmc_wait_for_interrupt(struct mmc *mmc,
*/ */
static int sdmmc_wait_for_command_completion(struct mmc *mmc) static int sdmmc_wait_for_command_completion(struct mmc *mmc)
{ {
return sdmmc_wait_for_interrupt(mmc, MMC_STATUS_COMMAND_COMPLETE, 0, NULL); return sdmmc_wait_for_event(mmc, MMC_STATUS_COMMAND_COMPLETE, 0, 0, NULL);
} }
@ -1191,8 +1285,11 @@ static int sdmmc_wait_for_command_completion(struct mmc *mmc)
*/ */
static int sdmmc_wait_for_transfer_completion(struct mmc *mmc) static int sdmmc_wait_for_transfer_completion(struct mmc *mmc)
{ {
return sdmmc_wait_for_interrupt(mmc, MMC_STATUS_TRANSFER_COMPLETE, int rc = sdmmc_wait_for_event(mmc, MMC_STATUS_TRANSFER_COMPLETE,
MMC_STATUS_DMA_INTERRUPT, sdmmc_flush_bounce_buffer); MMC_WRITE_ACTIVE | MMC_READ_ACTIVE, MMC_STATUS_DMA_INTERRUPT, sdmmc_flush_bounce_buffer);
return rc;
} }
@ -1894,7 +1991,7 @@ static int sdmmc_mmc_switch_bus_width(struct mmc *mmc, enum sdmmc_bus_width widt
{ {
// Ask the card to adjust to the wider bus width. // Ask the card to adjust to the wider bus width.
int rc = mmc->switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL, int rc = mmc->switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL,
MMC_BUS_WIDTH, width, mmc->timeout); MMC_BUS_WIDTH, width, mmc->timeout, NULL);
if (rc) { if (rc) {
mmc_print(mmc, "could not switch mode on the card side!"); mmc_print(mmc, "could not switch mode on the card side!");
return rc; return rc;
@ -1965,8 +2062,14 @@ static int sdmmc_optimize_transfer_mode(struct mmc *mmc)
return rc; return rc;
} }
// We started off with the controller opearting with a divided clock,
// which is meant to keep us within the pre-init operating frequency of 400kHz.
// We're now set up, so we can drop the divider. From this point forward, the
// clock is now driven by the CAR frequency.
sdmmc_apply_clock_speed(mmc, SDMMC_SPEED_SDR12, true);
// TODO: step up into high speed modes // TODO: step up into high speed modes
return 0; return 0;
} }
@ -2338,7 +2441,7 @@ static int sdmmc_wait_for_card_ready(struct mmc *mmc, uint32_t timeout)
* *
* @return 0 on success, or an error code on failure * @return 0 on success, or an error code on failure
*/ */
static int sdmmc_mmc_switch_mode(struct mmc *mmc, enum sdmmc_switch_access_mode mode, enum sdmmc_switch_field field, uint16_t value, uint32_t timeout) static int sdmmc_mmc_switch_mode(struct mmc *mmc, int mode, int field, int value, uint32_t timeout, void *unused)
{ {
// Collapse our various parameters into a single argument. // Collapse our various parameters into a single argument.
uint32_t argument = uint32_t argument =
@ -2366,25 +2469,6 @@ static int sdmmc_mmc_switch_mode(struct mmc *mmc, enum sdmmc_switch_access_mode
} }
/**
* Issues a SWITCH_MODE command, which can be used to write registers on the SD card's controller,
* and thus to e.g. switch partitions.
*
* @param mmc The MMC device to use for comms.
* @param mode The access mode with which to access the controller.
* @param field The field to access.
* @param value The argument to the access mode.
* @param timeout The timeout, which is often longer than the normal MMC timeout.
*
* @return 0 on success, or an error code on failure
*/
static int sdmmc_sd_switch_mode(struct mmc *mmc, enum sdmmc_switch_access_mode mode, enum sdmmc_switch_field field, uint16_t value, uint32_t timeout)
{
mmc_print(mmc, "ERROR: SD card mode switching not yet implemented");
return ENOSYS;
}
/** /**
* @return True iff the given MMC card supports hardare partitions. * @return True iff the given MMC card supports hardare partitions.
*/ */
@ -2410,6 +2494,69 @@ bool sdmmc_external_card_present(struct mmc *mmc)
return !gpio_read(mmc->card_detect_gpio); return !gpio_read(mmc->card_detect_gpio);
} }
/**
* Issues an SD card mode-switch command.
*
* @param mmc The controller to use.
* @param mode The mode flag -- one to set function data, zero to query.
* @param group The SD card function group-- see the SD card Physical Layer spec.
*/
static int sdmmc_sd_switch_mode(struct mmc *mmc, int mode, int group, int value, uint32_t timeout, void *response)
{
int rc;
// Read the current block order, so we can restore it.
int original_block_order = sdmmc_get_block_order(mmc, false);
// Always request a single 64-byte block.
const int block_order = 6;
const int num_blocks = 1;
// Build the argument we're going to issue.
const int group_shift = group * SDMMC_SWITCH_MODE_GROUP_SIZE_BITS;
uint32_t argument = (mode << SDMMC_SWITCH_MODE_MODE_SHIFT) | SDMMC_SWITCH_MODE_ALL_FUNCTIONS_UNUSED;
argument &= ~(SDMMC_SWITCH_MODE_FUNCTION_MASK << group_shift);
argument |= value << group_shift;
// 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;
}
// Issue the command itself.
rc = sdmmc_send_command(mmc, CMD_SWITCH_MODE, MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, argument, NULL, num_blocks, false, false, response);
if (rc) {
mmc_print(mmc, "could not issue switch command!");
sdmmc_use_block_size(mmc, original_block_order);
return rc;
}
// 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;
}
/**
* Switches the host controller and SD card to high speed mode.
*/
/*
static int sdmmc_sd_switch_to_high_speed(struct mmc *mmc)
{
return ENOSYS;
}
*/
/** /**
* Switches a given SDMMC Controller where * Switches a given SDMMC Controller where
*/ */
@ -2507,7 +2654,7 @@ static int sdmmc_initialize_defaults(struct mmc *mmc)
* *
* @param mmc The SDMMC structure to be initiailized with the device state. * @param mmc The SDMMC structure to be initiailized with the device state.
* @param controler The controller description to be used; usually SWITCH_EMMC * @param controler The controller description to be used; usually SWITCH_EMMC
* or SWTICH_MICROSD. * or SWITCH_MICROSD.
*/ */
int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller) int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller)
{ {
@ -2589,7 +2736,7 @@ int sdmmc_select_partition(struct mmc *mmc, enum sdmmc_partition partition)
// Set the PARTITION_CONFIG register to select the active partition. // Set the PARTITION_CONFIG register to select the active partition.
mmc_print(mmc, "switching to partition %d", partition); mmc_print(mmc, "switching to partition %d", partition);
rc = mmc->switch_mode(mmc, MMC_SWITCH_MODE_WRITE_BYTE, MMC_PARTITION_CONFIG, argument, 0); rc = mmc->switch_mode(mmc, MMC_SWITCH_MODE_WRITE_BYTE, MMC_PARTITION_CONFIG, argument, 0, NULL);
if (rc) { if (rc) {
mmc_print(mmc, "failed to select partition %d (%02x, rc=%d)", partition, argument, rc); mmc_print(mmc, "failed to select partition %d (%02x, rc=%d)", partition, argument, rc);
} }

View file

@ -126,7 +126,6 @@ enum sdmmc_switch_field {
}; };
/** /**
* Primary data structure describing a Fusée MMC driver. * Primary data structure describing a Fusée MMC driver.
*/ */
@ -150,9 +149,9 @@ struct mmc {
/* Per-card-type operations */ /* Per-card-type operations */
int (*card_init)(struct mmc *mmc); int (*card_init)(struct mmc *mmc);
int (*establish_relative_address)(struct mmc *mmc); int (*establish_relative_address)(struct mmc *mmc);
int (*switch_mode)(struct mmc *mmc, enum sdmmc_switch_access_mode mode, int (*switch_mode)(struct mmc *mmc, int a, int b, int c, uint32_t timeout, void *response);
enum sdmmc_switch_field field, uint16_t value, uint32_t timeout);
int (*switch_bus_width)(struct mmc *mmc, enum sdmmc_bus_width width); int (*switch_bus_width)(struct mmc *mmc, enum sdmmc_bus_width width);
int (*switch_to_high_speed)(struct mmc *mmc);
/* Card properties */ /* Card properties */
uint8_t cid[15]; uint8_t cid[15];