From bceb00ad8de9df3594587b9b11c9dde137cca07c Mon Sep 17 00:00:00 2001 From: "Kate J. Temkin" Date: Fri, 4 May 2018 18:47:39 -0600 Subject: [PATCH] fusee: sdmmc: clean up and prepare for better SD/MMC/version separations --- fusee/fusee-primary/src/sdmmc.c | 309 +++++++++++++------------------- fusee/fusee-primary/src/sdmmc.h | 62 ++++++- 2 files changed, 182 insertions(+), 189 deletions(-) diff --git a/fusee/fusee-primary/src/sdmmc.c b/fusee/fusee-primary/src/sdmmc.c index 1224fb18e..7734c58f1 100644 --- a/fusee/fusee-primary/src/sdmmc.c +++ b/fusee/fusee-primary/src/sdmmc.c @@ -248,6 +248,7 @@ enum sdmmc_command { CMD_APP_COMMAND = 55, }; + /** * String descriptions of each command. */ @@ -274,44 +275,6 @@ static const char *sdmmc_command_string[] = { }; -/** - * Methods by which we can touch registers accessed via. - * CMD_SWITCH_MODE. - */ -enum sdmmc_switch_access_mode { - - /* Normal commands */ - MMC_SWITCH_MODE_CMD_SET = 0, - MMC_SWITCH_MODE_SET_BITS = 1, - MMC_SWITCH_MODE_CLEAR_BITS = 2, - MMC_SWITCH_MODE_WRITE_BYTE = 3, - - /* EXTCSD access */ - MMC_SWITCH_EXTCSD_NORMAL = 1, -}; - - -/** - * Offsets into the SWITCH_MODE argument. - */ -enum sdmmc_switch_argument_offsets { - MMC_SWITCH_VALUE_SHIFT = 0, - MMC_SWITCH_FIELD_SHIFT = 16, - MMC_SWITCH_ACCESS_MODE_SHIFT = 24, -}; - - -/** - * Fields that can be modified by CMD_SWITCH_MODE. - */ -enum sdmmc_switch_field { - /* Fields */ - MMC_GROUP_ERASE_DEF = 175, - MMC_PARTITION_CONFIG = 179, - MMC_BUS_WIDTH = 183, -}; - - /** * SDMMC command argument numbers */ @@ -397,9 +360,6 @@ enum sdmmc_ext_csd_extents { }; -/* Forward declarations. */ -static int sdmmc_switch_mode(struct mmc *mmc, enum sdmmc_switch_access_mode mode, enum sdmmc_switch_field field, uint16_t value, uint32_t timeout); - /* SDMMC debug enable */ static int sdmmc_loglevel = 0; @@ -486,6 +446,7 @@ static const char *sdmmc_get_command_string(enum sdmmc_command command) } } + /** * Debug: print out any errors that occurred during a command timeout */ @@ -548,7 +509,7 @@ static int sdmmc_hardware_reset(struct mmc *mmc) /** * Performs low-level initialization for SDMMC4, used for the eMMC. */ -static int sdmmc4_hardware_init(struct mmc *mmc) +static int sdmmc4_set_up_clock_and_io(struct mmc *mmc) { volatile struct tegra_car *car = car_get_regs(); volatile struct tegra_padctl *padctl = padctl_get_regs(); @@ -582,6 +543,16 @@ static int sdmmc4_hardware_init(struct mmc *mmc) } +/** + * Enables power supplies for SDMMC4, used for eMMC. + */ +static int sdmmc4_enable_supplies(struct mmc *mmc) +{ + // As a booot device, SDMMC4's power supply is always on. + return 0; +} + + /** * Enables power supplies for SDMMC1, used for the SD card slot. */ @@ -611,7 +582,7 @@ static int sdmmc1_enable_supplies(struct mmc *mmc) /** * Performs low-level initialization for SDMMC1, used for the SD card slot. */ -static int sdmmc1_hardware_init(struct mmc *mmc) +static int sdmmc1_set_up_clock_and_io(struct mmc *mmc) { volatile struct tegra_car *car = car_get_regs(); volatile struct tegra_pinmux *pinmux = pinmux_get_regs(); @@ -668,49 +639,6 @@ static int sdmmc1_hardware_init(struct mmc *mmc) } -/** - * Sets up the I/O and clocking resources necessary to use the given controller. - */ -static int sdmmc_setup_controller_clock_and_io(struct mmc *mmc) -{ - // Always use the per-controller initialization functions. - switch (mmc->controller) { - case SWITCH_MICROSD: - return sdmmc1_hardware_init(mmc); - case SWITCH_EMMC: - return sdmmc4_hardware_init(mmc); - default: - mmc_print(mmc, "initializing an unsupport SDMMC controller!"); - return ENODEV; - } - - return 0; -} - - - -/** - * Sets up the I/O and clocking resources necessary to use the given controller. - */ -static int sdmmc_enable_supplies(struct mmc *mmc) -{ - // Always use the per-controller initialization functions. - switch (mmc->controller) { - case SWITCH_MICROSD: - return sdmmc1_enable_supplies(mmc); - - // As a boot device, the eMMC is always on. - case SWITCH_EMMC: - return 0; - default: - mmc_print(mmc, "trying to power on an unsupported controller!"); - return ENODEV; - } - - return 0; -} - - /** * Configures clocking parameters for a given controller. * @@ -718,6 +646,8 @@ static int sdmmc_enable_supplies(struct mmc *mmc) */ 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) @@ -748,8 +678,6 @@ static int sdmmc_set_up_clocking_parameters(struct mmc *mmc) } - - /** * Initialize the low-level SDMMC hardware. * Thanks to hexkyz for this init code. @@ -766,7 +694,7 @@ static int sdmmc_hardware_init(struct mmc *mmc) int rc; // Initialize the Tegra resources necessary to use the given piece of hardware. - rc = sdmmc_setup_controller_clock_and_io(mmc); + rc = mmc->set_up_clock_and_io(mmc); if (rc) { mmc_print(mmc, "ERROR: could not set up controller for use!"); return rc; @@ -780,7 +708,7 @@ static int sdmmc_hardware_init(struct mmc *mmc) } // Turn on the card's power supplies... - rc = sdmmc_enable_supplies(mmc); + rc = mmc->enable_supplies(mmc); if (rc) { mmc_print(mmc, "ERROR: could power on the card!"); return rc; @@ -864,11 +792,11 @@ static int sdmmc_hardware_init(struct mmc *mmc) // Clear SDHCI_CTRL_SDMA and SDHCI_CTRL_ADMA2 regs->host_control &= 0xE7; - + // Set the timeout to be the maximum value regs->timeout_control &= ~(0x0F); regs->timeout_control |= 0x0E; - + // Clear SDHCI_CTRL_4BITBUS and SDHCI_CTRL_8BITBUS regs->host_control &= 0xFD; regs->host_control &= 0xDF; @@ -1585,7 +1513,6 @@ static int sdmmc_read_and_parse_csd(struct mmc *mmc) } - /** * Reads the active MMC card's Card Specific Data, and updates the MMC object's properties. * @@ -1642,6 +1569,7 @@ static int sdmmc_set_up_block_transfer_size(struct mmc *mmc) return 0; } + /** * Switches the SDMMC card and controller to the fullest bus width possible. * @@ -1650,7 +1578,7 @@ static int sdmmc_set_up_block_transfer_size(struct mmc *mmc) static int sdmmc_switch_bus_width(struct mmc *mmc, enum sdmmc_bus_width width) { // Ask the card to adjust to the wider bus width. - int rc = sdmmc_switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL, + int rc = mmc->switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL, MMC_BUS_WIDTH, width, mmc->timeout); if (rc) { mmc_print(mmc, "could not switch mode on the card side!"); @@ -1714,7 +1642,7 @@ static int sdmmc_set_up_partitions(struct mmc *mmc) mmc_print(mmc, "detected a card with hardware partitions."); // Use partitioning. - return sdmmc_switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL, + return mmc->switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL, MMC_GROUP_ERASE_DEF, MMC_EXT_CSD_ERASE_GROUP_DEF_BIT, mmc->timeout); } @@ -1768,30 +1696,6 @@ static int sdmmc_get_relative_address(struct mmc *mmc) } -/** - * 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; - } -} - - /** * Retrieves information about the card, and populates the MMC structure accordingly. * Used as part of the SDMMC initialization process. @@ -1812,7 +1716,7 @@ static int sdmmc_card_init(struct mmc *mmc) memcpy(mmc->cid, response, sizeof(mmc->cid)); // Establish a relative address to communicate with - rc = sdmmc_get_or_set_relative_address(mmc); + rc = mmc->establish_relative_address(mmc); if (rc) { mmc_print(mmc, "could not establish a relative address! (%d)", rc); return rc; @@ -1918,7 +1822,6 @@ static int sdmmc_sd_wait_for_card_readiness(struct mmc *mmc, uint32_t *response) } - /** * Handles MMC-specific card initialization. */ @@ -2050,44 +1953,6 @@ static int sdmmc_sd_card_init(struct mmc *mmc) } - - -/** - * Handle any speciailized initialization required by the given device type. - * - * @param mmc The device to initialize. - */ -static int sdmmc_handle_card_type_init(struct mmc *mmc) -{ - int rc; - - switch (mmc->card_type) { - - // Handle initialization of eMMC cards. - case MMC_CARD_EMMC: - // FIXME: also handle MMC and SD cards that aren't eMMC - rc = sdmmc_mmc_card_init(mmc); - break; - - // Handle initialization of SD. - case MMC_CARD_SD: - rc = sdmmc_sd_card_init(mmc); - break; - - default: - mmc_print(mmc,"initialization of this device not yet supported!"); - rc = ENOTTY; - break; - } - - return rc; -} - - - - - - /** * @returns true iff the given READ_STATUS response indicates readiness */ @@ -2150,7 +2015,7 @@ static int sdmmc_wait_for_card_ready(struct mmc *mmc, uint32_t timeout) * * @return 0 on success, or an error code on failure */ -static int sdmmc_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, enum sdmmc_switch_access_mode mode, enum sdmmc_switch_field field, uint16_t value, uint32_t timeout) { // Collapse our various parameters into a single argument. uint32_t argument = @@ -2178,6 +2043,25 @@ static int sdmmc_switch_mode(struct mmc *mmc, enum sdmmc_switch_access_mode 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. */ @@ -2187,22 +2071,80 @@ static bool sdmmc_supports_hardware_partitions(struct mmc *mmc) } +/** + * card detect method for built-in cards. + */ +bool sdmmc_builtin_card_present(struct mmc *mmc) +{ + return true; +} + +/** + * card detect method for GPIO-based card detects + */ +bool sdmmc_external_card_present(struct mmc *mmc) +{ + return !gpio_read(mmc->card_detect_gpio); +} + +/** + * Switches a given SDMMC Controller where + */ +static void sdmmc_apply_card_type(struct mmc *mmc, enum sdmmc_card_type type) +{ + // Store the card type for our own reference... + mmc->card_type = type; + + // Set up our per-protocol function pointers. + switch (type) { + + // MMC-protoco cards + case MMC_CARD_EMMC: + case MMC_CARD_MMC: + mmc->card_init = sdmmc_mmc_card_init; + mmc->establish_relative_address = sdmmc_set_relative_address; + mmc->switch_mode = sdmmc_mmc_switch_mode; + break; + + // SD-protocol cards + case MMC_CARD_SD: + mmc->card_init = sdmmc_sd_card_init; + mmc->establish_relative_address = sdmmc_get_relative_address; + mmc->switch_mode = sdmmc_sd_switch_mode; + break; + + // Switch-cart protocol cards + case MMC_CARD_CART: + printk("BUG: trying to use an impossible code path!\n"); + panic(0); + } +} + + /** * Populates the given MMC object with defaults for its controller. * * @param mmc The mmc object to populate. */ -static void sdmmc_initialize_defaults(struct mmc *mmc) +static int sdmmc_initialize_defaults(struct mmc *mmc) { // Set up based on the controller switch (mmc->controller) { case SWITCH_EMMC: mmc->name = "eMMC"; - mmc->card_type = MMC_CARD_EMMC; mmc->max_bus_width = MMC_BUS_WIDTH_8BIT; mmc->operating_voltage = MMC_VOLTAGE_1V8; + mmc->card_detect_gpio = GPIO_MICROSD_CARD_DETECT; - // The Switch's eMMC always uses block addressin.g + // 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->card_present = sdmmc_external_card_present; + + // The EMMC controller always uses an EMMC card. + sdmmc_apply_card_type(mmc, MMC_CARD_EMMC); + + // The Switch's eMMC always uses block addressing. mmc->uses_block_addressing = true; break; @@ -2212,15 +2154,23 @@ static void sdmmc_initialize_defaults(struct mmc *mmc) mmc->max_bus_width = MMC_BUS_WIDTH_4BIT; mmc->operating_voltage = MMC_VOLTAGE_3V3; + // For the microSD card slot, assume we have an SD-type card. + // 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; + sdmmc_apply_card_type(mmc, MMC_CARD_SD); + // Start off assuming byte addressing; we'll detect and correct this // later, if necessary. mmc->uses_block_addressing = false; break; default: - printk("ERROR: initialization not yet writen for SDMMC%d", mmc->controller); - break; + printk("ERROR: initialization not yet writen for SDMMC%d", mmc->controller + 1); + return ENOSYS; } + + return 0; } @@ -2239,7 +2189,14 @@ int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller) // Get a reference to the registers for the relevant SDMMC controller. mmc->controller = controller; mmc->regs = sdmmc_get_regs(controller); - sdmmc_initialize_defaults(mmc); + + // Set the defaults for the card, including the default function pointers + // for the assumed card type, and the per-controller options. + rc = sdmmc_initialize_defaults(mmc); + if (rc) { + printk("ERROR: controller SDMMC%d not currently supported!\n", controller + 1); + return rc; + } // Default to a timeout of 1S. mmc->timeout = 1000000; @@ -2262,7 +2219,7 @@ int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller) } // Handle the initialization that's specific to the card type. - rc = sdmmc_handle_card_type_init(mmc); + rc = mmc->card_init(mmc); if (rc) { mmc_print(mmc, "failed to set run card-specific initialization (%d)!", rc); return rc; @@ -2293,7 +2250,7 @@ int sdmmc_select_partition(struct mmc *mmc, enum sdmmc_partition partition) // Set the PARTITION_CONFIG register to select the active partition. mmc_print(mmc, "switching to partition %d", partition); - rc = sdmmc_switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL, MMC_PARTITION_CONFIG, argument, 0); + rc = mmc->switch_mode(mmc, MMC_SWITCH_EXTCSD_NORMAL, MMC_PARTITION_CONFIG, argument, 0); if (rc) { mmc_print(mmc, "failed to select partition %d (%02x, rc=%d)", partition, argument, rc); } @@ -2388,7 +2345,6 @@ int sdmmc_write(struct mmc *mmc, void *buffer, uint32_t block, unsigned int coun return sdmmc_send_command(mmc, command, MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, extent, NULL, count, true, count > 1, buffer); } - /** * Checks to see whether an SD card is present. * @@ -2397,18 +2353,5 @@ int sdmmc_write(struct mmc *mmc, void *buffer, uint32_t block, unsigned int coun */ bool sdmmc_card_present(struct mmc *mmc) { - switch (mmc->controller) { - - // The eMMC is always present. - case SWITCH_EMMC: - return true; - - // The Switch's microSD card has a GPIO card detect pin. - case SWITCH_MICROSD: - return !gpio_read(GPIO_MICROSD_CARD_DETECT); - - default: - mmc_print(mmc, "cannot figure out how to determine card presence!"); - return false; - } + return mmc->card_present(mmc); } diff --git a/fusee/fusee-primary/src/sdmmc.h b/fusee/fusee-primary/src/sdmmc.h index 15ed540b4..6757190ba 100644 --- a/fusee/fusee-primary/src/sdmmc.h +++ b/fusee/fusee-primary/src/sdmmc.h @@ -9,6 +9,7 @@ #include #include #include "utils.h" +#include "gpio.h" /* Opaque pointer to the Tegra SDMMC registers */ struct tegra_sdmmc; @@ -83,6 +84,44 @@ enum sdmmc_write_permission { }; +/** + * Methods by which we can touch registers accessed via. + * CMD_SWITCH_MODE. + */ +enum sdmmc_switch_access_mode { + + /* Normal commands */ + MMC_SWITCH_MODE_CMD_SET = 0, + MMC_SWITCH_MODE_SET_BITS = 1, + MMC_SWITCH_MODE_CLEAR_BITS = 2, + MMC_SWITCH_MODE_WRITE_BYTE = 3, + + /* EXTCSD access */ + MMC_SWITCH_EXTCSD_NORMAL = 1, +}; + + +/** + * Offsets into the SWITCH_MODE argument. + */ +enum sdmmc_switch_argument_offsets { + MMC_SWITCH_VALUE_SHIFT = 0, + MMC_SWITCH_FIELD_SHIFT = 16, + MMC_SWITCH_ACCESS_MODE_SHIFT = 24, +}; + + +/** + * Fields that can be modified by CMD_SWITCH_MODE. + */ +enum sdmmc_switch_field { + /* Fields */ + MMC_GROUP_ERASE_DEF = 175, + MMC_PARTITION_CONFIG = 179, + MMC_BUS_WIDTH = 183, +}; + + /** * Primary data structure describing a Fusée MMC driver. @@ -92,11 +131,23 @@ struct mmc { /* Controller properties */ char *name; - unsigned int timeout; - enum sdmmc_card_type card_type; bool use_dma; + unsigned int timeout; + enum tegra_named_gpio card_detect_gpio; + enum sdmmc_card_type card_type; enum sdmmc_write_permission write_enable; + /* Per-controller operations. */ + int (*set_up_clock_and_io)(struct mmc *mmc); + int (*enable_supplies)(struct mmc *mmc); + bool (*card_present)(struct mmc *mmc); + + /* Per-card-type operations */ + int (*card_init)(struct mmc *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); + /* Card properties */ uint8_t cid[15]; uint32_t relative_address; @@ -130,13 +181,12 @@ struct mmc; * Parititions supported by the Switch eMMC. */ enum sdmmc_partition { - MMC_PARTITION_USER = 0, - MMC_PARTITION_BOOT1 = 1, - MMC_PARITTION_BOOT2 = 2, + SDMMC_PARTITION_USER = 0, + SDMMC_PARTITION_BOOT0 = 1, + SDMMC_PARTITION_BOOT1 = 2, }; - /** * Sets the current SDMMC debugging loglevel. *