1. mmc_ios说明
2.mmc_set_ios
3. mmc_set_bus_mode & mmc_set_bus_width
1. mmc_ios说明
struct mmc_ios 由mmc core定义的标准结构用于维护mmc一些与总线相关的io setting。如下:
struct mmc_ios { unsigned int clock; /* clock rate */ unsigned short vdd;/* vdd stores the bit number of the selected voltage range from below. */ unsigned char bus_mode; /* command output mode */#define MMC_BUSMODE_OPENDRAIN 1#define MMC_BUSMODE_PUSHPULL 2 unsigned char chip_select; /* SPI chip select */#define MMC_CS_DONTCARE 0#define MMC_CS_HIGH 1#define MMC_CS_LOW 2 unsigned char power_mode; /* power supply mode */#define MMC_POWER_OFF 0#define MMC_POWER_UP 1#define MMC_POWER_ON 2#define MMC_POWER_UNDEFINED 3 unsigned char bus_width; /* data bus width */#define MMC_BUS_WIDTH_1 0#define MMC_BUS_WIDTH_4 2#define MMC_BUS_WIDTH_8 3 unsigned char timing; /* timing specification used */#define MMC_TIMING_LEGACY 0#define MMC_TIMING_MMC_HS 1#define MMC_TIMING_SD_HS 2#define MMC_TIMING_UHS_SDR12 3#define MMC_TIMING_UHS_SDR25 4#define MMC_TIMING_UHS_SDR50 5#define MMC_TIMING_UHS_SDR104 6#define MMC_TIMING_UHS_DDR50 7#define MMC_TIMING_MMC_DDR52 8#define MMC_TIMING_MMC_HS200 9#define MMC_TIMING_MMC_HS400 10 unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */#define MMC_SIGNAL_VOLTAGE_330 0#define MMC_SIGNAL_VOLTAGE_180 1#define MMC_SIGNAL_VOLTAGE_120 2 unsigned char drv_type; /* driver type (A,B,C,D) */#define MMC_SET_DRIVER_TYPE_B 0#define MMC_SET_DRIVER_TYPE_A 1#define MMC_SET_DRIVER_TYPE_C 2#define MMC_SET_DRIVER_TYPE_D 3 bool enhanced_strobe; /* hs400es selection */}; 1)clock,时钟频率。
2)vdd,通过1 << vdd”可以得到MMC_VDD_x_x(详见参考include/linux/mmc/host.h中MMC_VDD_开头的定义),然后获取电压信息。
3)bus_mode,两种信号模式,open-drain(MMC_BUSMODE_OPENDRAIN)和push-pull(MMC_BUSMODE_PUSHPULL),对应不同的高低电平(可参考相应的高低电平)spec,例如[2]。
4)chip_select,只针对SPI指定片选信号的有效模式,包括无片选信号(MMC_CS_DONTCARE)、高电平有效(MMC_CS_HIGH)、低电平有效(MMC_CS_LOW)。
5)power_mode,当前的电源状态包括MMC_POWER_OFF、MMC_POWER_UP、MMC_POWER_ON和MMC_POWER_UNDEFINED。
6)bus_width,总线宽度,包括1-bit(MMC_BUS_WIDTH_1)、4-bit(MMC_BUS_WIDTH_4)和8-bit(MMC_BUS_WIDTH_8)。
7)timing,符合哪种总线时序(大多对应某一类)MMC包括:
MMC_TIMING_LEGACY,旧的、不再使用的规范; MMC_TIMING_MMC_HS,High speed MMC规范(详见相应规范)spec,这里不再详细介绍,下同); MMC_TIMING_SD_HS,High speed SD; MMC_TIMING_UHS_SDR12; MMC_TIMING_UHS_SDR25 MMC_TIMING_UHS_SDR50 MMC_TIMING_UHS_SDR104 MMC_TIMING_UHS_DDR50 MMC_TIMING_MMC_DDR52 MMC_TIMING_MMC_HS200 MMC_TIMING_MMC_HS400
8)signal_voltage,总线信号使用哪一种电压,3.3v(MMC_SIGNAL_VOLTAGE_330)、1.8v(MMC_SIGNAL_VOLTAGE_180)或者1.2v(MMC_SIGNAL_VOLTAGE_120)。
9)drv_type,驱动能力包括:
MMC_SET_DRIVER_TYPE_B MMC_SET_DRIVER_TYPE_A MMC_SET_DRIVER_TYPE_C MMC_SET_DRIVER_TYPE_D
2.mmc_set_ios
统一设置mmc总线的io设置(io setting)。
static inline void mmc_set_ios(struct mmc_host *host){ struct mmc_ios *ios = &host->ios; pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u " "width %u timing %u\n", mmc_hostname(host),ios->clock,ios->bus_mode, ios->power_mode,ios->chip_select,ios->vdd, 1 << ios->bus_width,ios->timing); host->ops->set_ios(host,ios);} 会调用host->ops->set_ios来对mmc总线的io setting设置,核心函数。sdhci类型的host,对应就是sdhci_set_ios(drivers\mmc\host\sdhci.c)。
void sdhci_set_ios(struct mmc_host *mmc,struct mmc_ios *ios){ struct sdhci_host *host = mmc_priv(mmc); u8 ctrl; if (ios->power_mode == MMC_POWER_UNDEFINED) return; if (host->flags & SDHCI_DEVICE_DEAD) { if (!IS_ERR(mmc->supply.vmmc) && ios->power_mode == MMC_POWER_OFF) mmc_regulator_set_ocr(mmc,mmc->supply.vmmc,0); return; } /* * Reset the chip on each power off. * Should clear out any weird states. */ if (ios->power_mode == MMC_POWER_OFF) { sdhci_writel(host,0,SDHCI_SIGNAL_ENABLE); sdhci_reinit(host); } if (host->version >= SDHCI_SPEC_300 && (ios->power_mode == MMC_POWER_UP) && !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) sdhci_enable_preset_value(host,false); if (!ios->clock || ios->clock != host->clock) { host->ops->set_clock(host, ios->clock); host->clock = ios->clock; if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK && host->clock) { host->timeout_clk = host->mmc->actual_clock ? host->mmc->actual_clock / 1000 : host->clock / 1000; host->mmc->max_busy_timeout = host->ops->get_max_timeout_count ? host->ops->get_max_timeout_count(host) : 1 << 27; host->mmc->max_busy_timeout /= host->timeout_clk; } } if (host->ops->set_power) host->ops->set_power(host, ios->power_mode, ios->vdd); else sdhci_set_power(host, ios->power_mode, ios->vdd); if (host->ops->platform_send_init_74_clocks) host->ops->platform_send_init_74_clocks(host, ios->power_mode); host->ops->set_bus_width(host, ios->bus_width); ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); if (!(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)) { if (ios->timing == MMC_TIMING_SD_HS || ios->timing == MMC_TIMING_MMC_HS || ios->timing == MMC_TIMING_MMC_HS400 || ios->timing == MMC_TIMING_MMC_HS200 || ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_SDR50 || ios->timing == MMC_TIMING_UHS_SDR104 || ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_UHS_SDR25) ctrl |= SDHCI_CTRL_HISPD; else ctrl &= ~SDHCI_CTRL_HISPD; } if (host->version >= SDHCI_SPEC_300) { u16 clk, ctrl_2; if (!host->preset_enabled) { sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* * We only need to set Driver Strength if the * preset value enable is not set. */ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK; if (ios->drv_type == MMC_SET_DRIVER_TYPE_A) ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A; else if (ios->drv_type == MMC_SET_DRIVER_TYPE_B) ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B; else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C) ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C; else if (ios->drv_type == MMC_SET_DRIVER_TYPE_D) ctrl_2 |= SDHCI_CTRL_DRV_TYPE_D; else { pr_warn("%s: invalid driver type, default to driver type B\n", mmc_hostname(mmc)); ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B; } sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); } else { /* * According to SDHC Spec v3.00, if the Preset Value * Enable in the Host Control 2 register is set, we * need to reset SD Clock Enable before changing High * Speed Enable to avoid generating clock gliches. */ /* Reset SD Clock Enable */ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); clk &= ~SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* Re-enable SD Clock */ host->ops->set_clock(host, host->clock); } /* Reset SD Clock Enable */ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); clk &= ~SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); host->ops->set_uhs_signaling(host, ios->timing); host->timing = ios->timing; if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN) && ((ios->timing == MMC_TIMING_UHS_SDR12) || (ios->timing == MMC_TIMING_UHS_SDR25) || (ios->timing == MMC_TIMING_UHS_SDR50) || (ios->timing == MMC_TIMING_UHS_SDR104) || (ios->timing == MMC_TIMING_UHS_DDR50) || (ios->timing == MMC_TIMING_MMC_DDR52))) { u16 preset; sdhci_enable_preset_value(host, true); preset = sdhci_get_preset_value(host); ios->drv_type = (preset & SDHCI_PRESET_DRV_MASK) >> SDHCI_PRESET_DRV_SHIFT; } /* Re-enable SD Clock */ host->ops->set_clock(host, host->clock); } else sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* * Some (ENE) controllers go apeshit on some ios operation, * signalling timeout and CRC errors even on CMD0. Resetting * it on each ios seems to solve the problem. */ if (host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); mmiowb();}
3. mmc_set_bus_mode & mmc_set_bus_width
mmc_set_bus_mode用于设置总线模式,有如下模式
MMC_BUSMODE_OPENDRAIN(开漏模式) MMC_BUSMODE_PUSHPULL(上拉模式)
mmc_set_bus_width用于设置总线宽度,有如下模式
MMC_BUS_WIDTH_1 MMC_BUS_WIDTH_4 MMC_BUS_WIDTH_8
/* * Change the bus mode (open drain/push-pull) of a host. */void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode){ host->ios.bus_mode = mode; mmc_set_ios(host);}/* * Change data bus width of a host. */void mmc_set_bus_width(struct mmc_host *host, unsigned int width){ host->ios.bus_width = width; mmc_set_ios(host);}