mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
fusee: use full bus width and faster initial clock speed
This commit is contained in:
parent
20c225c829
commit
b20a04ede5
2 changed files with 66 additions and 52 deletions
|
@ -228,6 +228,7 @@ enum sdmmc_command {
|
||||||
CMD_SET_DSR = 4,
|
CMD_SET_DSR = 4,
|
||||||
CMD_TOGGLE_SLEEP_AWAKE = 5,
|
CMD_TOGGLE_SLEEP_AWAKE = 5,
|
||||||
CMD_SWITCH_MODE = 6,
|
CMD_SWITCH_MODE = 6,
|
||||||
|
CMD_APP_SWITCH_WIDTH = 6,
|
||||||
CMD_TOGGLE_CARD_SELECT = 7,
|
CMD_TOGGLE_CARD_SELECT = 7,
|
||||||
CMD_SEND_EXT_CSD = 8,
|
CMD_SEND_EXT_CSD = 8,
|
||||||
CMD_SEND_IF_COND = 8,
|
CMD_SEND_IF_COND = 8,
|
||||||
|
@ -244,6 +245,7 @@ enum sdmmc_command {
|
||||||
CMD_WRITE_MULTIPLE_BLOCK = 25,
|
CMD_WRITE_MULTIPLE_BLOCK = 25,
|
||||||
|
|
||||||
CMD_APP_SEND_OP_COND = 41,
|
CMD_APP_SEND_OP_COND = 41,
|
||||||
|
CMD_APP_SET_CARD_DETECT = 42,
|
||||||
CMD_APP_COMMAND = 55,
|
CMD_APP_COMMAND = 55,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -434,6 +436,8 @@ static const char *sdmmc_get_command_string(enum sdmmc_command command)
|
||||||
return "CMD_APP_COMMAND";
|
return "CMD_APP_COMMAND";
|
||||||
case CMD_APP_SEND_OP_COND:
|
case CMD_APP_SEND_OP_COND:
|
||||||
return "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_WRITE_SINGLE_BLOCK:
|
case CMD_WRITE_SINGLE_BLOCK:
|
||||||
return "CMD_WRITE_SINGLE_BLOCK";
|
return "CMD_WRITE_SINGLE_BLOCK";
|
||||||
case CMD_WRITE_MULTIPLE_BLOCK:
|
case CMD_WRITE_MULTIPLE_BLOCK:
|
||||||
|
@ -819,7 +823,7 @@ static int sdmmc_hardware_init(struct mmc *mmc)
|
||||||
// Set SDHCI_DIVIDER and SDHCI_DIVIDER_HI
|
// Set SDHCI_DIVIDER and SDHCI_DIVIDER_HI
|
||||||
// FIXME: divider SD if necessary
|
// FIXME: divider SD if necessary
|
||||||
regs->clock_control &= ~(0xFFC0);
|
regs->clock_control &= ~(0xFFC0);
|
||||||
regs->clock_control |= (0x30 << 8); // 200kHz, initially
|
regs->clock_control |= (0x18 << 8); // 400kHz, initially
|
||||||
|
|
||||||
// Set SDHCI_CLOCK_CARD_EN
|
// Set SDHCI_CLOCK_CARD_EN
|
||||||
regs->clock_control |= 0x04;
|
regs->clock_control |= 0x04;
|
||||||
|
@ -1402,9 +1406,9 @@ static int sdmmc_send_simple_app_command(struct mmc *mmc, enum sdmmc_command com
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
// First, send the application command.
|
// 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) {
|
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;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1581,12 +1585,13 @@ static int sdmmc_set_up_block_transfer_size(struct mmc *mmc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switches the SDMMC card and controller to the fullest bus width possible.
|
* 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.
|
* @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.
|
// 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,
|
||||||
|
@ -1596,7 +1601,7 @@ static int sdmmc_switch_bus_width(struct mmc *mmc, enum sdmmc_bus_width width)
|
||||||
return rc;
|
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;
|
mmc->regs->host_control &= ~MMC_HOST_BUS_WIDTH_MASK;
|
||||||
switch (width) {
|
switch (width) {
|
||||||
case MMC_BUS_WIDTH_4BIT:
|
case MMC_BUS_WIDTH_4BIT:
|
||||||
|
@ -1608,6 +1613,40 @@ static int sdmmc_switch_bus_width(struct mmc *mmc, enum sdmmc_bus_width width)
|
||||||
default:
|
default:
|
||||||
break;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1616,12 +1655,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.
|
* 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;
|
int rc;
|
||||||
|
|
||||||
// Switch the device to its maximum bus width.
|
// 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) {
|
if (rc) {
|
||||||
mmc_print(mmc, "could not switch the controller's bus width!");
|
mmc_print(mmc, "could not switch the controller's bus width!");
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -1633,31 +1672,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.
|
* Requests that an MMC target use the card's current relative address.
|
||||||
*
|
*
|
||||||
|
@ -1708,8 +1722,10 @@ static int sdmmc_get_relative_address(struct mmc *mmc)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves information about the card, and populates the MMC structure accordingly.
|
* Shared card initialization for SD and MMC cards.
|
||||||
* Used as part of the SDMMC initialization process.
|
* 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)
|
static int sdmmc_card_init(struct mmc *mmc)
|
||||||
{
|
{
|
||||||
|
@ -1754,6 +1770,15 @@ static int sdmmc_card_init(struct mmc *mmc)
|
||||||
return rc;
|
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, "could not optimize bus utlization! (%d)", rc);
|
||||||
|
|
||||||
|
// TODO: possibly skip this?
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1863,15 +1888,6 @@ static int sdmmc_mmc_card_init(struct mmc *mmc)
|
||||||
return rc;
|
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).
|
// Read and handle card's Extended Card Specific Data (ext-CSD).
|
||||||
rc = sdmmc_read_and_parse_ext_csd(mmc);
|
rc = sdmmc_read_and_parse_ext_csd(mmc);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
|
@ -1879,12 +1895,6 @@ static int sdmmc_mmc_card_init(struct mmc *mmc)
|
||||||
return EPIPE;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1958,8 +1968,6 @@ static int sdmmc_sd_card_init(struct mmc *mmc)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: optimize bus utilization here?
|
|
||||||
// is this just a call to the same routine as for eMMC?
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2115,6 +2123,7 @@ static void sdmmc_apply_card_type(struct mmc *mmc, enum sdmmc_card_type type)
|
||||||
mmc->card_init = sdmmc_mmc_card_init;
|
mmc->card_init = sdmmc_mmc_card_init;
|
||||||
mmc->establish_relative_address = sdmmc_set_relative_address;
|
mmc->establish_relative_address = sdmmc_set_relative_address;
|
||||||
mmc->switch_mode = sdmmc_mmc_switch_mode;
|
mmc->switch_mode = sdmmc_mmc_switch_mode;
|
||||||
|
mmc->switch_bus_width = sdmmc_mmc_switch_bus_width;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// SD-protocol cards
|
// SD-protocol cards
|
||||||
|
@ -2122,6 +2131,7 @@ static void sdmmc_apply_card_type(struct mmc *mmc, enum sdmmc_card_type type)
|
||||||
mmc->card_init = sdmmc_sd_card_init;
|
mmc->card_init = sdmmc_sd_card_init;
|
||||||
mmc->establish_relative_address = sdmmc_get_relative_address;
|
mmc->establish_relative_address = sdmmc_get_relative_address;
|
||||||
mmc->switch_mode = sdmmc_sd_switch_mode;
|
mmc->switch_mode = sdmmc_sd_switch_mode;
|
||||||
|
mmc->switch_bus_width = sdmmc_sd_switch_bus_width;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Switch-cart protocol cards
|
// Switch-cart protocol cards
|
||||||
|
@ -2161,7 +2171,7 @@ static int sdmmc_initialize_defaults(struct mmc *mmc)
|
||||||
case SWITCH_MICROSD:
|
case SWITCH_MICROSD:
|
||||||
mmc->name = "uSD";
|
mmc->name = "uSD";
|
||||||
mmc->card_type = MMC_CARD_SD;
|
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->operating_voltage = MMC_VOLTAGE_3V3;
|
||||||
mmc->card_detect_gpio = GPIO_MICROSD_CARD_DETECT;
|
mmc->card_detect_gpio = GPIO_MICROSD_CARD_DETECT;
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,9 @@ enum sdmmc_bus_width {
|
||||||
MMC_BUS_WIDTH_1BIT = 0,
|
MMC_BUS_WIDTH_1BIT = 0,
|
||||||
MMC_BUS_WIDTH_4BIT = 1,
|
MMC_BUS_WIDTH_4BIT = 1,
|
||||||
MMC_BUS_WIDTH_8BIT = 2,
|
MMC_BUS_WIDTH_8BIT = 2,
|
||||||
|
|
||||||
|
SD_BUS_WIDTH_1BIT = 0,
|
||||||
|
SD_BUS_WIDTH_4BIT = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -147,6 +150,7 @@ struct 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, enum sdmmc_switch_access_mode mode,
|
||||||
enum sdmmc_switch_field field, uint16_t value, uint32_t timeout);
|
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 */
|
/* Card properties */
|
||||||
uint8_t cid[15];
|
uint8_t cid[15];
|
||||||
|
|
Loading…
Reference in a new issue