diff options
author | Philippe Langlais <philippe.langlais@stericsson.com> | 2011-12-06 11:58:13 +0100 |
---|---|---|
committer | Philippe Langlais <philippe.langlais@stericsson.com> | 2011-12-06 11:58:13 +0100 |
commit | a119957feee74d71818ee8caa593596fa55fbed9 (patch) | |
tree | 78d688c5213044cd2cef22a1de7e31ade85d215d | |
parent | 3240c33248a9dbc8d890f4e95471f809f47494f1 (diff) | |
parent | 179594de57bacdfcde54bfb6e1151e3eefd390ba (diff) |
Merge branch 'storage-mmc' into linux-stable-ux500-3.1
30 files changed, 1457 insertions, 251 deletions
diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 4657c0073ab..ee49e5f7f33 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -11,6 +11,7 @@ #include <linux/amba/mmci.h> #include <linux/mmc/host.h> #include <linux/platform_device.h> +#include <linux/delay.h> #include <asm/mach-types.h> #include <plat/ste_dma40.h> @@ -25,21 +26,13 @@ * SDI 0 (MicroSD slot) */ -/* MMCIPOWER bits */ -#define MCI_DATA2DIREN (1 << 2) -#define MCI_CMDDIREN (1 << 3) -#define MCI_DATA0DIREN (1 << 4) -#define MCI_DATA31DIREN (1 << 5) -#define MCI_FBCLKEN (1 << 7) - /* GPIO pins used by the sdi0 level shifter */ static int sdi0_en = -1; static int sdi0_vsel = -1; -static u32 mop500_sdi0_vdd_handler(struct device *dev, unsigned int vdd, - unsigned char power_mode) +static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios) { - switch (power_mode) { + switch (ios->power_mode) { case MMC_POWER_UP: case MMC_POWER_ON: /* @@ -52,6 +45,7 @@ static u32 mop500_sdi0_vdd_handler(struct device *dev, unsigned int vdd, */ gpio_direction_output(sdi0_vsel, 0); gpio_direction_output(sdi0_en, 1); + udelay(100); break; case MMC_POWER_OFF: gpio_direction_output(sdi0_vsel, 0); @@ -59,68 +53,122 @@ static u32 mop500_sdi0_vdd_handler(struct device *dev, unsigned int vdd, break; } - return MCI_FBCLKEN | MCI_CMDDIREN | MCI_DATA0DIREN | - MCI_DATA2DIREN | MCI_DATA31DIREN; + return 0; } +#ifdef MMC_SUSPEND_WORKAROUND #ifdef CONFIG_STE_DMA40 struct stedma40_chan_cfg mop500_sdi0_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_PERIPH_TO_MEM, - .src_dev_type = DB8500_DMA_DEV29_SD_MM0_RX, + .src_dev_type = DB8500_DMA_DEV1_SD_MMC0_RX, .dst_dev_type = STEDMA40_DEV_DST_MEMORY, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, + .use_fixed_channel = true, + .phy_channel = 0, }; static struct stedma40_chan_cfg mop500_sdi0_dma_cfg_tx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_MEM_TO_PERIPH, .src_dev_type = STEDMA40_DEV_SRC_MEMORY, - .dst_dev_type = DB8500_DMA_DEV29_SD_MM0_TX, + .dst_dev_type = DB8500_DMA_DEV1_SD_MMC0_TX, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, + .use_fixed_channel = true, + .phy_channel = 0, }; #endif +#endif static struct mmci_platform_data mop500_sdi0_data = { - .vdd_handler = mop500_sdi0_vdd_handler, + .ios_handler = mop500_sdi0_ios_handler, .ocr_mask = MMC_VDD_29_30, .f_max = 50000000, .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED, .gpio_wp = -1, + .sigdir = MCI_ST_FBCLKEN | + MCI_ST_CMDDIREN | + MCI_ST_DATA0DIREN | + MCI_ST_DATA2DIREN, +#ifdef MMC_SUSPEND_WORKAROUND #ifdef CONFIG_STE_DMA40 .dma_filter = stedma40_filter, .dma_rx_param = &mop500_sdi0_dma_cfg_rx, .dma_tx_param = &mop500_sdi0_dma_cfg_tx, #endif +#endif +}; + +/* + * SDI1 (SDIO WLAN) + */ +#ifdef SDIO_DMA_ON +#ifdef CONFIG_STE_DMA40 +static struct stedma40_chan_cfg sdi1_dma_cfg_rx = { + .mode = STEDMA40_MODE_LOGICAL, + .dir = STEDMA40_PERIPH_TO_MEM, + .src_dev_type = DB8500_DMA_DEV32_SD_MM1_RX, + .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .src_info.data_width = STEDMA40_WORD_WIDTH, + .dst_info.data_width = STEDMA40_WORD_WIDTH, +}; + +static struct stedma40_chan_cfg sdi1_dma_cfg_tx = { + .mode = STEDMA40_MODE_LOGICAL, + .dir = STEDMA40_MEM_TO_PERIPH, + .src_dev_type = STEDMA40_DEV_SRC_MEMORY, + .dst_dev_type = DB8500_DMA_DEV32_SD_MM1_TX, + .src_info.data_width = STEDMA40_WORD_WIDTH, + .dst_info.data_width = STEDMA40_WORD_WIDTH, +}; +#endif +#endif + +static struct mmci_platform_data mop500_sdi1_data = { + .ocr_mask = MMC_VDD_29_30, + .f_max = 50000000, + .capabilities = MMC_CAP_4_BIT_DATA, + .gpio_cd = -1, + .gpio_wp = -1, +#ifdef SDIO_DMA_ON +#ifdef CONFIG_STE_DMA40 + .dma_filter = stedma40_filter, + .dma_rx_param = &sdi1_dma_cfg_rx, + .dma_tx_param = &sdi1_dma_cfg_tx, +#endif +#endif }; -static void sdi0_configure(void) +static void sdi0_sdi1_configure(void) { - int ret; + int ret; + u32 periphid = 0; - ret = gpio_request(sdi0_en, "level shifter enable"); - if (!ret) - ret = gpio_request(sdi0_vsel, - "level shifter 1v8-3v select"); - if (ret) { - pr_warning("unable to config sdi0 gpios for level shifter.\n"); - return; - } + ret = gpio_request(sdi0_en, "level shifter enable"); + if (!ret) + ret = gpio_request(sdi0_vsel, + "level shifter 1v8-3v select"); + + if (ret) { + pr_warning("unable to config sdi0 gpios for level shifter.\n"); + return; + } - /* Select the default 2.9V and enable level shifter */ - gpio_direction_output(sdi0_vsel, 0); - gpio_direction_output(sdi0_en, 1); + /* Select the default 2.9V and enable level shifter */ + gpio_direction_output(sdi0_vsel, 0); + gpio_direction_output(sdi0_en, 1); - /* Add the device, force v2 to subrevision 1 */ - if (cpu_is_u8500v2()) - db8500_add_sdi0(&mop500_sdi0_data, 0x10480180); - else - db8500_add_sdi0(&mop500_sdi0_data, 0); + /* v2 has a new version of this block that need to be forced */ + if (cpu_is_u8500v20_or_later()) + periphid = 0x10480180; + + db8500_add_sdi0(&mop500_sdi0_data, periphid); + db8500_add_sdi1(&mop500_sdi1_data, periphid); } void mop500_sdi_tc35892_init(void) @@ -128,13 +176,13 @@ void mop500_sdi_tc35892_init(void) mop500_sdi0_data.gpio_cd = GPIO_SDMMC_CD; sdi0_en = GPIO_SDMMC_EN; sdi0_vsel = GPIO_SDMMC_1V8_3V_SEL; - sdi0_configure(); + sdi0_sdi1_configure(); } /* * SDI 2 (POP eMMC, not on DB8500ed) */ - +#ifdef MMC_SUSPEND_WORKAROUND #ifdef CONFIG_STE_DMA40 struct stedma40_chan_cfg mop500_sdi2_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, @@ -154,24 +202,29 @@ static struct stedma40_chan_cfg mop500_sdi2_dma_cfg_tx = { .dst_info.data_width = STEDMA40_WORD_WIDTH, }; #endif +#endif static struct mmci_platform_data mop500_sdi2_data = { .ocr_mask = MMC_VDD_165_195, .f_max = 50000000, - .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA, + .capabilities = MMC_CAP_4_BIT_DATA | + MMC_CAP_8_BIT_DATA, .gpio_cd = -1, .gpio_wp = -1, +#ifdef MMC_SUSPEND_WORKAROUND #ifdef CONFIG_STE_DMA40 .dma_filter = stedma40_filter, .dma_rx_param = &mop500_sdi2_dma_cfg_rx, .dma_tx_param = &mop500_sdi2_dma_cfg_tx, #endif +#endif }; /* * SDI 4 (on-board eMMC) */ +#ifdef MMC_SUSPEND_WORKAROUND #ifdef CONFIG_STE_DMA40 struct stedma40_chan_cfg mop500_sdi4_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, @@ -191,19 +244,23 @@ static struct stedma40_chan_cfg mop500_sdi4_dma_cfg_tx = { .dst_info.data_width = STEDMA40_WORD_WIDTH, }; #endif +#endif static struct mmci_platform_data mop500_sdi4_data = { .ocr_mask = MMC_VDD_29_30, .f_max = 50000000, - .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA | - MMC_CAP_MMC_HIGHSPEED, + .capabilities = MMC_CAP_4_BIT_DATA | + MMC_CAP_8_BIT_DATA | + MMC_CAP_MMC_HIGHSPEED, .gpio_cd = -1, .gpio_wp = -1, +#ifdef MMC_SUSPEND_WORKAROUND #ifdef CONFIG_STE_DMA40 .dma_filter = stedma40_filter, .dma_rx_param = &mop500_sdi4_dma_cfg_rx, .dma_tx_param = &mop500_sdi4_dma_cfg_tx, #endif +#endif }; void __init mop500_sdi_init(void) @@ -236,11 +293,11 @@ void __init mop500_sdi_init(void) sdi0_en = SNOWBALL_SDMMC_EN_GPIO; sdi0_vsel = SNOWBALL_SDMMC_1V8_3V_GPIO; } - sdi0_configure(); + sdi0_sdi1_configure(); } /* - * On boards with the TC35892 GPIO expander, sdi0 will finally + * On boards with the TC35892 GPIO expander, sdi0 and sdi1 will finally * be added when the TC35892 initializes and calls * mop500_sdi_tc35892_init() above. */ diff --git a/arch/arm/mach-ux500/board-u5500-sdi.c b/arch/arm/mach-ux500/board-u5500-sdi.c index 739fb4c5b16..475e41e0628 100644 --- a/arch/arm/mach-ux500/board-u5500-sdi.c +++ b/arch/arm/mach-ux500/board-u5500-sdi.c @@ -5,34 +5,28 @@ * License terms: GNU General Public License (GPL) version 2 */ +#include <linux/kernel.h> +#include <linux/gpio.h> +#include <linux/amba/bus.h> #include <linux/amba/mmci.h> #include <linux/mmc/host.h> -#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/delay.h> -#include <plat/pincfg.h> -#include <mach/db5500-regs.h> +#include <asm/mach-types.h> #include <plat/ste_dma40.h> +#include <mach/devices.h> +#include <mach/hardware.h> +#include <mach/ste-dma40-db5500.h> -#include "pins-db5500.h" #include "devices-db5500.h" -#include "ste-dma40-db5500.h" - -static pin_cfg_t u5500_sdi_pins[] = { - /* SDI0 (POP eMMC) */ - GPIO5_MC0_DAT0 | PIN_DIR_INPUT | PIN_PULL_UP, - GPIO6_MC0_DAT1 | PIN_DIR_INPUT | PIN_PULL_UP, - GPIO7_MC0_DAT2 | PIN_DIR_INPUT | PIN_PULL_UP, - GPIO8_MC0_DAT3 | PIN_DIR_INPUT | PIN_PULL_UP, - GPIO9_MC0_DAT4 | PIN_DIR_INPUT | PIN_PULL_UP, - GPIO10_MC0_DAT5 | PIN_DIR_INPUT | PIN_PULL_UP, - GPIO11_MC0_DAT6 | PIN_DIR_INPUT | PIN_PULL_UP, - GPIO12_MC0_DAT7 | PIN_DIR_INPUT | PIN_PULL_UP, - GPIO13_MC0_CMD | PIN_DIR_INPUT | PIN_PULL_UP, - GPIO14_MC0_CLK | PIN_DIR_OUTPUT | PIN_VAL_LOW, -}; +#include "board-u5500.h" +/* + * SDI 0 (eMMC) + */ #ifdef CONFIG_STE_DMA40 -struct stedma40_chan_cfg u5500_sdi0_dma_cfg_rx = { +static struct stedma40_chan_cfg sdi0_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_PERIPH_TO_MEM, .src_dev_type = DB5500_DMA_DEV24_SDMMC0_RX, @@ -41,7 +35,7 @@ struct stedma40_chan_cfg u5500_sdi0_dma_cfg_rx = { .dst_info.data_width = STEDMA40_WORD_WIDTH, }; -static struct stedma40_chan_cfg u5500_sdi0_dma_cfg_tx = { +static struct stedma40_chan_cfg sdi0_dma_cfg_tx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_MEM_TO_PERIPH, .src_dev_type = STEDMA40_DEV_SRC_MEMORY, @@ -61,14 +55,208 @@ static struct mmci_platform_data u5500_sdi0_data = { .gpio_wp = -1, #ifdef CONFIG_STE_DMA40 .dma_filter = stedma40_filter, - .dma_rx_param = &u5500_sdi0_dma_cfg_rx, - .dma_tx_param = &u5500_sdi0_dma_cfg_tx, + .dma_rx_param = &sdi0_dma_cfg_rx, + .dma_tx_param = &sdi0_dma_cfg_tx, +#endif +}; + +/* + * SDI 1 (MicroSD slot) + */ + +static int u5500_sdi1_ios_handler(struct device *dev, struct mmc_ios *ios) +{ + switch (ios->power_mode) { + case MMC_POWER_UP: + case MMC_POWER_ON: + /* + * Level shifter voltage should depend on vdd to when deciding + * on either 1.8V or 2.9V. Once the decision has been made the + * level shifter must be disabled and re-enabled with a changed + * select signal in order to switch the voltage. Since there is + * no framework support yet for indicating 1.8V in vdd, use the + * default 2.9V. + */ + gpio_set_value_cansleep(GPIO_MMC_CARD_CTRL, 1); + udelay(100); + break; + case MMC_POWER_OFF: + gpio_set_value_cansleep(GPIO_MMC_CARD_CTRL, 0); + break; + } + + return 0; +} + +#ifdef SD_WORKAROUND +static struct stedma40_chan_cfg sdi1_dma_cfg_rx = { + .mode = STEDMA40_MODE_LOGICAL, + .dir = STEDMA40_PERIPH_TO_MEM, + .src_dev_type = DB5500_DMA_DEV25_SDMMC1_RX, + .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .src_info.data_width = STEDMA40_WORD_WIDTH, + .dst_info.data_width = STEDMA40_WORD_WIDTH, +}; + +static struct stedma40_chan_cfg sdi1_dma_cfg_tx = { + .mode = STEDMA40_MODE_LOGICAL, + .dir = STEDMA40_MEM_TO_PERIPH, + .src_dev_type = STEDMA40_DEV_SRC_MEMORY, + .dst_dev_type = DB5500_DMA_DEV25_SDMMC1_TX, + .src_info.data_width = STEDMA40_WORD_WIDTH, + .dst_info.data_width = STEDMA40_WORD_WIDTH, +}; +#endif +static struct mmci_platform_data u5500_sdi1_data = { + .ios_handler = u5500_sdi1_ios_handler, + .ocr_mask = MMC_VDD_29_30, + .f_max = 50000000, + .capabilities = MMC_CAP_4_BIT_DATA | + MMC_CAP_SD_HIGHSPEED | + MMC_CAP_MMC_HIGHSPEED, + .gpio_cd = GPIO_SDMMC_CD, + .gpio_wp = -1, + .cd_invert = true, +#ifdef SD_WORKAROUND +#ifdef CONFIG_STE_DMA40 + .dma_filter = stedma40_filter, + .dma_rx_param = &sdi1_dma_cfg_rx, + .dma_tx_param = &sdi1_dma_cfg_tx, +#endif +#endif +}; + +/* + * SDI2 (EMMC2) + */ + +static struct stedma40_chan_cfg sdi2_dma_cfg_rx = { + .mode = STEDMA40_MODE_LOGICAL, + .dir = STEDMA40_PERIPH_TO_MEM, + .src_dev_type = DB5500_DMA_DEV26_SDMMC2_RX, + .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .src_info.data_width = STEDMA40_WORD_WIDTH, + .dst_info.data_width = STEDMA40_WORD_WIDTH, +}; + +static struct stedma40_chan_cfg sdi2_dma_cfg_tx = { + .mode = STEDMA40_MODE_LOGICAL, + .dir = STEDMA40_MEM_TO_PERIPH, + .src_dev_type = STEDMA40_DEV_SRC_MEMORY, + .dst_dev_type = DB5500_DMA_DEV26_SDMMC2_TX, + .src_info.data_width = STEDMA40_WORD_WIDTH, + .dst_info.data_width = STEDMA40_WORD_WIDTH, +}; + +static struct mmci_platform_data u5500_sdi2_data = { + .ocr_mask = MMC_VDD_165_195, + .f_max = 50000000, + .capabilities = MMC_CAP_4_BIT_DATA | + MMC_CAP_8_BIT_DATA | + MMC_CAP_MMC_HIGHSPEED, + .gpio_cd = -1, + .gpio_wp = -1, +#ifdef CONFIG_STE_DMA40 + .dma_filter = stedma40_filter, + .dma_rx_param = &sdi2_dma_cfg_rx, + .dma_tx_param = &sdi2_dma_cfg_tx, +#endif +}; + +/* + * SDI 3 (SDIO WLAN) + */ +#ifdef SDIO_DMA_ON +#ifdef CONFIG_STE_DMA40 +static struct stedma40_chan_cfg sdi3_dma_cfg_rx = { + .mode = STEDMA40_MODE_LOGICAL, + .dir = STEDMA40_PERIPH_TO_MEM, + .src_dev_type = DB5500_DMA_DEV27_SDMMC3_RX, + .dst_dev_type = STEDMA40_DEV_DST_MEMORY, + .src_info.data_width = STEDMA40_WORD_WIDTH, + .dst_info.data_width = STEDMA40_WORD_WIDTH, +}; + +static struct stedma40_chan_cfg sdi3_dma_cfg_tx = { + .mode = STEDMA40_MODE_LOGICAL, + .dir = STEDMA40_MEM_TO_PERIPH, + .src_dev_type = STEDMA40_DEV_SRC_MEMORY, + .dst_dev_type = DB5500_DMA_DEV27_SDMMC3_TX, + .src_info.data_width = STEDMA40_WORD_WIDTH, + .dst_info.data_width = STEDMA40_WORD_WIDTH, +}; +#endif +#endif + +static struct mmci_platform_data u5500_sdi3_data = { + .ocr_mask = MMC_VDD_29_30, + .f_max = 50000000, + .capabilities = MMC_CAP_4_BIT_DATA, + .gpio_cd = -1, + .gpio_wp = -1, +#ifdef SDIO_DMA_ON +#ifdef CONFIG_STE_DMA40 + .dma_filter = stedma40_filter, + .dma_rx_param = &sdi3_dma_cfg_rx, + .dma_tx_param = &sdi3_dma_cfg_tx, +#endif #endif }; +static void sdi1_configure(void) +{ + int pin[2]; + int ret; + + /* Level-shifter GPIOs */ + pin[0] = GPIO_MMC_CARD_CTRL; + pin[1] = GPIO_MMC_CARD_VSEL; + + ret = gpio_request(pin[0], "MMC_CARD_CTRL"); + if (!ret) + ret = gpio_request(pin[1], "MMC_CARD_VSEL"); + + if (ret) { + pr_warning("unable to config sdi0 gpios for level shifter.\n"); + return; + } + /* Select the default 2.9V and eanble level shifter */ + gpio_direction_output(pin[0], 1); + gpio_direction_output(pin[1], 0); +} + +#define SDI_PID_V1 0x00480180 +#define SDI_PID_V2 0x10480180 +#define BACKUPRAM_ROM_DEBUG_ADDR 0xFFC +#define MMC_BLOCK_ID 0x20 void __init u5500_sdi_init(void) { - nmk_config_pins(u5500_sdi_pins, ARRAY_SIZE(u5500_sdi_pins)); + u32 periphid = 0; + int mmc_blk = 0; + + if (cpu_is_u5500v1()) + periphid = SDI_PID_V1; + else + periphid = SDI_PID_V2; + + if (cpu_is_u5500v2()) + /* + * Fix me in 5500 v2.1 + * Dynamic detection of booting device by reading + * ROM debug register from BACKUP RAM and register the + * corresponding EMMC. + * This is done due to wrong configuration of MMC0 clock + * in ROM code for u5500 v2. + */ + mmc_blk = readl(__io_address(U5500_BACKUPRAM1_BASE) + + BACKUPRAM_ROM_DEBUG_ADDR); + + if (mmc_blk & MMC_BLOCK_ID) + db5500_add_sdi2(&u5500_sdi2_data, periphid); + else + db5500_add_sdi0(&u5500_sdi0_data, periphid); - db5500_add_sdi0(&u5500_sdi0_data); + sdi1_configure(); + db5500_add_sdi1(&u5500_sdi1_data, periphid); + db5500_add_sdi3(&u5500_sdi3_data, periphid); } diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 4c1a648d00f..6be812ca3fb 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -94,6 +94,11 @@ struct mmc_blk_data { unsigned int read_only; unsigned int part_type; unsigned int name_idx; + unsigned int reset_done; +#define MMC_BLK_READ BIT(0) +#define MMC_BLK_WRITE BIT(1) +#define MMC_BLK_DISCARD BIT(2) +#define MMC_BLK_SECDISCARD BIT(3) /* * Only set in main mmc_blk_data associated @@ -102,6 +107,8 @@ struct mmc_blk_data { */ unsigned int part_curr; struct device_attribute force_ro; + struct device_attribute boot_partition_ro_lock; + int area_type; }; static DEFINE_MUTEX(open_lock); @@ -109,11 +116,11 @@ static DEFINE_MUTEX(open_lock); enum mmc_blk_status { MMC_BLK_SUCCESS = 0, MMC_BLK_PARTIAL, - MMC_BLK_RETRY, - MMC_BLK_RETRY_SINGLE, - MMC_BLK_DATA_ERR, MMC_BLK_CMD_ERR, + MMC_BLK_RETRY, MMC_BLK_ABORT, + MMC_BLK_DATA_ERR, + MMC_BLK_ECC_ERR, }; module_param(perdev_minors, int, 0444); @@ -160,6 +167,72 @@ static void mmc_blk_put(struct mmc_blk_data *md) mutex_unlock(&open_lock); } +#define EXT_CSD_BOOT_WP_PWR_WP_TEXT "pwr_ro" +#define EXT_CSD_BOOT_WP_PERM_WP_TEXT "perm_ro" +#define EXT_CSD_BOOT_WP_WP_DISABLED_TEXT "rw" +static ssize_t boot_partition_ro_lock_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev)); + struct mmc_card *card = md->queue.card; + const char *out_text; + + if (card->ext_csd.boot_locked + & EXT_CSD_BOOT_WP_B_PERM_WP_EN) + out_text = EXT_CSD_BOOT_WP_PERM_WP_TEXT; + else if (card->ext_csd.boot_locked + & EXT_CSD_BOOT_WP_B_PWR_WP_EN) + out_text = EXT_CSD_BOOT_WP_PWR_WP_TEXT; + else + out_text = EXT_CSD_BOOT_WP_WP_DISABLED_TEXT; + + ret = snprintf(buf, PAGE_SIZE, "%s\n", out_text); + + return ret; +} + +static ssize_t boot_partition_ro_lock_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + struct mmc_blk_data *md; + struct mmc_card *card; + u8 set = 0; + + md = mmc_blk_get(dev_to_disk(dev)); + card = md->queue.card; + + if (!strncmp(buf, EXT_CSD_BOOT_WP_PWR_WP_TEXT, + strlen(EXT_CSD_BOOT_WP_PWR_WP_TEXT))) + set = EXT_CSD_BOOT_WP_B_PWR_WP_EN; + else if (!strncmp(buf, EXT_CSD_BOOT_WP_PERM_WP_TEXT, + strlen(EXT_CSD_BOOT_WP_PERM_WP_TEXT))) + set = EXT_CSD_BOOT_WP_B_PERM_WP_EN; + + if (set) { + mmc_claim_host(card->host); + + ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BOOT_WP, + set, + card->ext_csd.part_time); + if (ret) + pr_err("Boot Partition Lock failed: %d\n", ret); + else + card->ext_csd.boot_locked = set; + + mmc_release_host(card->host); + + if (!ret) + set_disk_ro(md->disk, 1); + } + ret = count; + + mmc_blk_put(md); + return ret; +} + static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -291,7 +364,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, struct mmc_card *card; struct mmc_command cmd = {0}; struct mmc_data data = {0}; - struct mmc_request mrq = {0}; + struct mmc_request mrq = {NULL}; struct scatterlist sg; int err; @@ -442,19 +515,24 @@ static inline int mmc_blk_part_switch(struct mmc_card *card, { int ret; struct mmc_blk_data *main_md = mmc_get_drvdata(card); + if (main_md->part_curr == md->part_type) return 0; if (mmc_card_mmc(card)) { - card->ext_csd.part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK; - card->ext_csd.part_config |= md->part_type; + u8 part_config = card->ext_csd.part_config; + + part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK; + part_config |= md->part_type; ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_PART_CONFIG, card->ext_csd.part_config, + EXT_CSD_PART_CONFIG, part_config, card->ext_csd.part_time); if (ret) return ret; -} + + card->ext_csd.part_config = part_config; + } main_md->part_curr = md->part_type; return 0; @@ -466,7 +544,7 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) u32 result; __be32 *blocks; - struct mmc_request mrq = {0}; + struct mmc_request mrq = {NULL}; struct mmc_command cmd = {0}; struct mmc_data data = {0}; unsigned int timeout_us; @@ -616,7 +694,7 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error, * Otherwise we don't understand what happened, so abort. */ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req, - struct mmc_blk_request *brq) + struct mmc_blk_request *brq, int *ecc_err) { bool prev_cmd_status_valid = true; u32 status, stop_status = 0; @@ -641,6 +719,12 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req, if (err) return ERR_ABORT; + /* Flag ECC errors */ + if ((status & R1_CARD_ECC_FAILED) || + (brq->stop.resp[0] & R1_CARD_ECC_FAILED) || + (brq->cmd.resp[0] & R1_CARD_ECC_FAILED)) + *ecc_err = 1; + /* * Check the current card state. If it is in some data transfer * mode, tell it to stop (and hopefully transition back to TRAN.) @@ -658,6 +742,8 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req, */ if (err) return ERR_ABORT; + if (stop_status & R1_CARD_ECC_FAILED) + *ecc_err = 1; } /* Check for set block count errors */ @@ -670,6 +756,10 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req, return mmc_blk_cmd_error(req, "r/w cmd", brq->cmd.error, prev_cmd_status_valid, status); + /* Data errors */ + if (!brq->stop.error) + return ERR_CONTINUE; + /* Now for stop errors. These aren't fatal to the transfer. */ pr_err("%s: error %d sending stop command, original cmd response %#x, card status %#x\n", req->rq_disk->disk_name, brq->stop.error, @@ -686,12 +776,45 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req, return ERR_CONTINUE; } +static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host, + int type) +{ + int err; + + if (md->reset_done & type) + return -EEXIST; + + md->reset_done |= type; + err = mmc_hw_reset(host); + /* Ensure we switch back to the correct partition */ + if (err != -EOPNOTSUPP) { + struct mmc_blk_data *main_md = mmc_get_drvdata(host->card); + int part_err; + + main_md->part_curr = main_md->part_type; + part_err = mmc_blk_part_switch(host->card, md); + if (part_err) { + /* + * We have failed to get back into the correct + * partition, so we need to abort the whole request. + */ + return -ENODEV; + } + } + return err; +} + +static inline void mmc_blk_reset_success(struct mmc_blk_data *md, int type) +{ + md->reset_done &= ~type; +} + static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; unsigned int from, nr, arg; - int err = 0; + int err = 0, type = MMC_BLK_DISCARD; if (!mmc_can_erase(card)) { err = -EOPNOTSUPP; @@ -705,7 +828,7 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) arg = MMC_TRIM_ARG; else arg = MMC_ERASE_ARG; - +retry: if (card->quirks & MMC_QUIRK_INAND_CMD38) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, INAND_CMD38_ARG_EXT_CSD, @@ -718,6 +841,10 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) } err = mmc_erase(card, from, nr, arg); out: + if (err == -EIO && !mmc_blk_reset(md, card->host, type)) + goto retry; + if (!err) + mmc_blk_reset_success(md, type); spin_lock_irq(&md->lock); __blk_end_request(req, err, blk_rq_bytes(req)); spin_unlock_irq(&md->lock); @@ -731,7 +858,7 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq, struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; unsigned int from, nr, arg; - int err = 0; + int err = 0, type = MMC_BLK_SECDISCARD; if (!mmc_can_secure_erase_trim(card)) { err = -EOPNOTSUPP; @@ -745,7 +872,7 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq, arg = MMC_SECURE_TRIM1_ARG; else arg = MMC_SECURE_ERASE_ARG; - +retry: if (card->quirks & MMC_QUIRK_INAND_CMD38) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, INAND_CMD38_ARG_EXT_CSD, @@ -769,6 +896,10 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq, err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG); } out: + if (err == -EIO && !mmc_blk_reset(md, card->host, type)) + goto retry; + if (!err) + mmc_blk_reset_success(md, type); spin_lock_irq(&md->lock); __blk_end_request(req, err, blk_rq_bytes(req)); spin_unlock_irq(&md->lock); @@ -825,11 +956,11 @@ static inline void mmc_apply_rel_rw(struct mmc_blk_request *brq, static int mmc_blk_err_check(struct mmc_card *card, struct mmc_async_req *areq) { - enum mmc_blk_status ret = MMC_BLK_SUCCESS; struct mmc_queue_req *mq_mrq = container_of(areq, struct mmc_queue_req, mmc_active); struct mmc_blk_request *brq = &mq_mrq->brq; struct request *req = mq_mrq->req; + int ecc_err = 0; /* * sbc.error indicates a problem with the set block count @@ -841,8 +972,9 @@ static int mmc_blk_err_check(struct mmc_card *card, * stop.error indicates a problem with the stop command. Data * may have been transferred, or may still be transferring. */ - if (brq->sbc.error || brq->cmd.error || brq->stop.error) { - switch (mmc_blk_cmd_recovery(card, req, brq)) { + if (brq->sbc.error || brq->cmd.error || brq->stop.error || + brq->data.error) { + switch (mmc_blk_cmd_recovery(card, req, brq, &ecc_err)) { case ERR_RETRY: return MMC_BLK_RETRY; case ERR_ABORT: @@ -894,23 +1026,21 @@ static int mmc_blk_err_check(struct mmc_card *card, brq->cmd.resp[0], brq->stop.resp[0]); if (rq_data_dir(req) == READ) { - if (brq->data.blocks > 1) { - /* Redo read one sector at a time */ - pr_warning("%s: retrying using single block read\n", - req->rq_disk->disk_name); - return MMC_BLK_RETRY_SINGLE; - } + if (ecc_err) + return MMC_BLK_ECC_ERR; return MMC_BLK_DATA_ERR; } else { return MMC_BLK_CMD_ERR; } } - if (ret == MMC_BLK_SUCCESS && - blk_rq_bytes(req) != brq->data.bytes_xfered) - ret = MMC_BLK_PARTIAL; + if (!brq->data.bytes_xfered) + return MMC_BLK_RETRY; - return ret; + if (blk_rq_bytes(req) != brq->data.bytes_xfered) + return MMC_BLK_PARTIAL; + + return MMC_BLK_SUCCESS; } static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, @@ -1049,12 +1179,41 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, mmc_queue_bounce_pre(mqrq); } +static int mmc_blk_cmd_err(struct mmc_blk_data *md, struct mmc_card *card, + struct mmc_blk_request *brq, struct request *req, + int ret) +{ + /* + * If this is an SD card and we're writing, we can first + * mark the known good sectors as ok. + * + * If the card is not SD, we can still ok written sectors + * as reported by the controller (which might be less than + * the real number of written sectors, but never more). + */ + if (mmc_card_sd(card)) { + u32 blocks; + + blocks = mmc_sd_num_wr_blocks(card); + if (blocks != (u32)-1) { + spin_lock_irq(&md->lock); + ret = __blk_end_request(req, 0, blocks << 9); + spin_unlock_irq(&md->lock); + } + } else { + spin_lock_irq(&md->lock); + ret = __blk_end_request(req, 0, brq->data.bytes_xfered); + spin_unlock_irq(&md->lock); + } + return ret; +} + static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) { struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; struct mmc_blk_request *brq = &mq->mqrq_cur->brq; - int ret = 1, disable_multi = 0, retry = 0; + int ret = 1, disable_multi = 0, retry = 0, type; enum mmc_blk_status status; struct mmc_queue_req *mq_rq; struct request *req; @@ -1076,6 +1235,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) mq_rq = container_of(areq, struct mmc_queue_req, mmc_active); brq = &mq_rq->brq; req = mq_rq->req; + type = rq_data_dir(req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE; mmc_queue_bounce_post(mq_rq); switch (status) { @@ -1084,17 +1244,17 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) /* * A block was successfully transferred. */ + mmc_blk_reset_success(md, type); spin_lock_irq(&md->lock); ret = __blk_end_request(req, 0, brq->data.bytes_xfered); spin_unlock_irq(&md->lock); + /* + * If the blk_end_request function returns non-zero even + * though all data has been transferred and no errors + * were returned by the host controller, it's a bug. + */ if (status == MMC_BLK_SUCCESS && ret) { - /* - * The blk_end_request has returned non zero - * even though all data is transfered and no - * erros returned by host. - * If this happen it's a bug. - */ printk(KERN_ERR "%s BUG rq_tot %d d_xfer %d\n", __func__, blk_rq_bytes(req), brq->data.bytes_xfered); @@ -1103,16 +1263,36 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) } break; case MMC_BLK_CMD_ERR: - goto cmd_err; - case MMC_BLK_RETRY_SINGLE: - disable_multi = 1; - break; + ret = mmc_blk_cmd_err(md, card, brq, req, ret); + if (!mmc_blk_reset(md, card->host, type)) + break; + goto cmd_abort; case MMC_BLK_RETRY: if (retry++ < 5) break; + /* Fall through */ case MMC_BLK_ABORT: + if (!mmc_blk_reset(md, card->host, type)) + break; goto cmd_abort; - case MMC_BLK_DATA_ERR: + case MMC_BLK_DATA_ERR: { + int err; + + err = mmc_blk_reset(md, card->host, type); + if (!err) + break; + if (err == -ENODEV) + goto cmd_abort; + /* Fall through */ + } + case MMC_BLK_ECC_ERR: + if (brq->data.blocks > 1) { + /* Redo read one sector at a time */ + pr_warning("%s: retrying using single block read\n", + req->rq_disk->disk_name); + disable_multi = 1; + break; + } /* * After an error, we redo I/O one sector at a * time, so we only reach here after trying to @@ -1129,7 +1309,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) if (ret) { /* - * In case of a none complete request + * In case of a incomplete request * prepare it again and resend. */ mmc_blk_rw_rq_prep(mq_rq, card, disable_multi, mq); @@ -1139,30 +1319,6 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) return 1; - cmd_err: - /* - * If this is an SD card and we're writing, we can first - * mark the known good sectors as ok. - * - * If the card is not SD, we can still ok written sectors - * as reported by the controller (which might be less than - * the real number of written sectors, but never more). - */ - if (mmc_card_sd(card)) { - u32 blocks; - - blocks = mmc_sd_num_wr_blocks(card); - if (blocks != (u32)-1) { - spin_lock_irq(&md->lock); - ret = __blk_end_request(req, 0, blocks << 9); - spin_unlock_irq(&md->lock); - } - } else { - spin_lock_irq(&md->lock); - ret = __blk_end_request(req, 0, brq->data.bytes_xfered); - spin_unlock_irq(&md->lock); - } - cmd_abort: spin_lock_irq(&md->lock); while (ret) @@ -1190,6 +1346,11 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ret = mmc_blk_part_switch(card, md); if (ret) { + if (req) { + spin_lock_irq(&md->lock); + __blk_end_request_all(req, -EIO); + spin_unlock_irq(&md->lock); + } ret = 0; goto out; } @@ -1228,7 +1389,8 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, struct device *parent, sector_t size, bool default_ro, - const char *subname) + const char *subname, + int area_type) { struct mmc_blk_data *md; int devidx, ret; @@ -1253,10 +1415,11 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, if (!subname) { md->name_idx = find_first_zero_bit(name_use, max_devices); __set_bit(md->name_idx, name_use); - } - else + } else { md->name_idx = ((struct mmc_blk_data *) dev_to_disk(parent)->private_data)->name_idx; + md->area_type = area_type; + } /* * Set the read-only status based on the supported commands @@ -1351,7 +1514,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) size = card->csd.capacity << (card->csd.read_blkbits - 9); } - md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL); + md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL, false); return md; } @@ -1360,13 +1523,14 @@ static int mmc_blk_alloc_part(struct mmc_card *card, unsigned int part_type, sector_t size, bool default_ro, - const char *subname) + const char *subname, + int area_type) { char cap_str[10]; struct mmc_blk_data *part_md; part_md = mmc_blk_alloc_req(card, disk_to_dev(md->disk), size, default_ro, - subname); + subname, area_type); if (IS_ERR(part_md)) return PTR_ERR(part_md); part_md->part_type = part_type; @@ -1380,26 +1544,30 @@ static int mmc_blk_alloc_part(struct mmc_card *card, return 0; } +/* MMC Physical partitions consist of two boot partitions and + * up to four general purpose partitions. + * For each partition enabled in EXT_CSD a block device will be allocatedi + * to provide access to the partition. + */ + static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md) { - int ret = 0; + int idx, ret = 0; if (!mmc_card_mmc(card)) return 0; - if (card->ext_csd.boot_size) { - ret = mmc_blk_alloc_part(card, md, EXT_CSD_PART_CONFIG_ACC_BOOT0, - card->ext_csd.boot_size >> 9, - true, - "boot0"); - if (ret) - return ret; - ret = mmc_blk_alloc_part(card, md, EXT_CSD_PART_CONFIG_ACC_BOOT1, - card->ext_csd.boot_size >> 9, - true, - "boot1"); - if (ret) - return ret; + for (idx = 0; idx < card->nr_parts; idx++) { + if (card->part[idx].size) { + ret = mmc_blk_alloc_part(card, md, + card->part[idx].part_cfg, + card->part[idx].size >> 9, + card->part[idx].force_ro, + card->part[idx].name, + card->part[idx].area_type); + if (ret) + return ret; + } } return ret; @@ -1428,6 +1596,9 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md) if (md) { if (md->disk->flags & GENHD_FL_UP) { device_remove_file(disk_to_dev(md->disk), &md->force_ro); + if (md->area_type == MMC_BLK_DATA_AREA_BOOT) + device_remove_file(disk_to_dev(md->disk), + &md->boot_partition_ro_lock); /* Stop new requests from getting into the queue */ del_gendisk(md->disk); @@ -1465,7 +1636,24 @@ static int mmc_add_disk(struct mmc_blk_data *md) md->force_ro.attr.mode = S_IRUGO | S_IWUSR; ret = device_create_file(disk_to_dev(md->disk), &md->force_ro); if (ret) - del_gendisk(md->disk); + goto force_ro_fail; + + if (md->area_type == MMC_BLK_DATA_AREA_BOOT) { + md->boot_partition_ro_lock.show = boot_partition_ro_lock_show; + md->boot_partition_ro_lock.store = boot_partition_ro_lock_store; + md->boot_partition_ro_lock.attr.name = "boot_partition_ro_lock"; + md->boot_partition_ro_lock.attr.mode = S_IRUGO | S_IWUSR; + ret = device_create_file(disk_to_dev(md->disk), + &md->boot_partition_ro_lock); + if (ret) + goto boot_partition_ro_lock_fail; + } + return ret; + +boot_partition_ro_lock_fail: + device_remove_file(disk_to_dev(md->disk), &md->force_ro); +force_ro_fail: + del_gendisk(md->disk); return ret; } diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 2bf229acd3b..9cdce636713 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -2328,6 +2328,31 @@ static int mmc_test_profile_sglen_r_nonblock_perf(struct mmc_test_card *test) return mmc_test_rw_multiple_sg_len(test, &test_data); } +/* + * eMMC hardware reset. + */ +static int mmc_test_hw_reset(struct mmc_test_card *test) +{ + struct mmc_card *card = test->card; + struct mmc_host *host = card->host; + int err; + + err = mmc_hw_reset_check(host); + if (!err) + return RESULT_OK; + + if (err == -ENOSYS) + return RESULT_FAIL; + + if (err != -EOPNOTSUPP) + return err; + + if (!mmc_can_reset(card)) + return RESULT_UNSUP_CARD; + + return RESULT_UNSUP_HOST; +} + static const struct mmc_test_case mmc_test_cases[] = { { .name = "Basic write (no data verification)", @@ -2650,6 +2675,11 @@ static const struct mmc_test_case mmc_test_cases[] = { .run = mmc_test_profile_sglen_r_nonblock_perf, .cleanup = mmc_test_area_cleanup, }, + + { + .name = "eMMC hardware reset", + .run = mmc_test_hw_reset, + }, }; static DEFINE_MUTEX(mmc_test_lock); diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 45fb362e3f0..5196312bb55 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -108,7 +108,7 @@ static void mmc_request(struct request_queue *q) wake_up_process(mq->thread); } -struct scatterlist *mmc_alloc_sg(int sg_len, int *err) +static struct scatterlist *mmc_alloc_sg(int sg_len, int *err) { struct scatterlist *sg; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index b27b94078c2..ec769490300 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -24,6 +24,8 @@ #include <linux/regulator/consumer.h> #include <linux/pm_runtime.h> #include <linux/suspend.h> +#include <linux/fault-inject.h> +#include <linux/random.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> @@ -83,6 +85,43 @@ static void mmc_flush_scheduled_work(void) flush_workqueue(workqueue); } +#ifdef CONFIG_FAIL_MMC_REQUEST + +/* + * Internal function. Inject random data errors. + * If mmc_data is NULL no errors are injected. + */ +static void mmc_should_fail_request(struct mmc_host *host, + struct mmc_request *mrq) +{ + struct mmc_command *cmd = mrq->cmd; + struct mmc_data *data = mrq->data; + static const int data_errors[] = { + -ETIMEDOUT, + -EILSEQ, + -EIO, + }; + + if (!data) + return; + + if (cmd->error || data->error || + !should_fail(&host->fail_mmc_request, data->blksz * data->blocks)) + return; + + data->error = data_errors[random32() % ARRAY_SIZE(data_errors)]; + data->bytes_xfered = (random32() % (data->bytes_xfered >> 9)) << 9; +} + +#else /* CONFIG_FAIL_MMC_REQUEST */ + +static inline void mmc_should_fail_request(struct mmc_host *host, + struct mmc_request *mrq) +{ +} + +#endif /* CONFIG_FAIL_MMC_REQUEST */ + /** * mmc_request_done - finish processing an MMC request * @host: MMC host which completed request @@ -102,13 +141,15 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) } if (err && cmd->retries) { - pr_debug("%s: req failed (CMD%u): %d, retrying...\n", - mmc_hostname(host), cmd->opcode, err); - - cmd->retries--; - cmd->error = 0; - host->ops->request(host, mrq); + /* + * Request starter must handle retries - see + * mmc_wait_for_req_done(). + */ + if (mrq->done) + mrq->done(mrq); } else { + mmc_should_fail_request(host, mrq); + led_trigger_event(host->led, LED_OFF); pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n", @@ -212,7 +253,21 @@ static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) static void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq) { - wait_for_completion(&mrq->completion); + struct mmc_command *cmd; + + while (1) { + wait_for_completion(&mrq->completion); + + cmd = mrq->cmd; + if (!cmd->error || !cmd->retries) + break; + + pr_debug("%s: req failed (CMD%u): %d, retrying...\n", + mmc_hostname(host), cmd->opcode, cmd->error); + cmd->retries--; + cmd->error = 0; + host->ops->request(host, mrq); + } } /** @@ -279,8 +334,14 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, mmc_wait_for_req_done(host, host->areq->mrq); err = host->areq->err_check(host->card, host->areq); if (err) { + /* post process the completed failed request */ mmc_post_req(host, host->areq->mrq, 0); if (areq) + /* + * Cancel the new prepared request, because + * it can't run until the failed + * request has been properly handled. + */ mmc_post_req(host, areq->mrq, -EINVAL); host->areq = NULL; @@ -330,7 +391,7 @@ EXPORT_SYMBOL(mmc_wait_for_req); */ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries) { - struct mmc_request mrq = {0}; + struct mmc_request mrq = {NULL}; WARN_ON(!host->claimed); @@ -1119,13 +1180,11 @@ static void mmc_power_up(struct mmc_host *host) bit = fls(host->ocr_avail) - 1; host->ios.vdd = bit; - if (mmc_host_is_spi(host)) { + if (mmc_host_is_spi(host)) host->ios.chip_select = MMC_CS_HIGH; - host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; - } else { + else host->ios.chip_select = MMC_CS_DONTCARE; - host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; - } + host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; host->ios.power_mode = MMC_POWER_UP; host->ios.bus_width = MMC_BUS_WIDTH_1; host->ios.timing = MMC_TIMING_LEGACY; @@ -1151,7 +1210,7 @@ static void mmc_power_up(struct mmc_host *host) mmc_host_clk_release(host); } -static void mmc_power_off(struct mmc_host *host) +void mmc_power_off(struct mmc_host *host) { mmc_host_clk_hold(host); @@ -1173,6 +1232,13 @@ static void mmc_power_off(struct mmc_host *host) host->ios.timing = MMC_TIMING_LEGACY; mmc_set_ios(host); + /* + * Some configurations, such as the 802.11 SDIO card in the OLPC + * XO-1.5, require a short delay after poweroff before the card + * can be successfully turned on again. + */ + mmc_delay(1); + mmc_host_clk_release(host); } @@ -1241,8 +1307,7 @@ void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops) } /* - * Remove the current bus handler from a host. Assumes that there are - * no interesting cards left, so the bus is powered down. + * Remove the current bus handler from a host. */ void mmc_detach_bus(struct mmc_host *host) { @@ -1259,8 +1324,6 @@ void mmc_detach_bus(struct mmc_host *host) spin_unlock_irqrestore(&host->lock, flags); - mmc_power_off(host); - mmc_bus_put(host); } @@ -1480,7 +1543,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, if (err) { printk(KERN_ERR "mmc_erase: group start error %d, " "status %#x\n", err, cmd.resp[0]); - err = -EINVAL; + err = -EIO; goto out; } @@ -1495,7 +1558,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, if (err) { printk(KERN_ERR "mmc_erase: group end error %d, status %#x\n", err, cmd.resp[0]); - err = -EINVAL; + err = -EIO; goto out; } @@ -1727,6 +1790,94 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen) } EXPORT_SYMBOL(mmc_set_blocklen); +static void mmc_hw_reset_for_init(struct mmc_host *host) +{ + if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) + return; + mmc_host_clk_hold(host); + host->ops->hw_reset(host); + mmc_host_clk_release(host); +} + +int mmc_can_reset(struct mmc_card *card) +{ + u8 rst_n_function; + + if (!mmc_card_mmc(card)) + return 0; + rst_n_function = card->ext_csd.rst_n_function; + if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED) + return 0; + return 1; +} +EXPORT_SYMBOL(mmc_can_reset); + +static int mmc_do_hw_reset(struct mmc_host *host, int check) +{ + struct mmc_card *card = host->card; + + if (!host->bus_ops->power_restore) + return -EOPNOTSUPP; + + if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) + return -EOPNOTSUPP; + + if (!card) + return -EINVAL; + + if (!mmc_can_reset(card)) + return -EOPNOTSUPP; + + mmc_host_clk_hold(host); + mmc_set_clock(host, host->f_init); + + host->ops->hw_reset(host); + + /* If the reset has happened, then a status command will fail */ + if (check) { + struct mmc_command cmd = {0}; + int err; + + cmd.opcode = MMC_SEND_STATUS; + if (!mmc_host_is_spi(card->host)) + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if (!err) { + mmc_host_clk_release(host); + return -ENOSYS; + } + } + + host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_DDR); + if (mmc_host_is_spi(host)) { + host->ios.chip_select = MMC_CS_HIGH; + host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; + } else { + host->ios.chip_select = MMC_CS_DONTCARE; + host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; + } + host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ios.timing = MMC_TIMING_LEGACY; + mmc_set_ios(host); + + mmc_host_clk_release(host); + + return host->bus_ops->power_restore(host); +} + +int mmc_hw_reset(struct mmc_host *host) +{ + return mmc_do_hw_reset(host, 0); +} +EXPORT_SYMBOL(mmc_hw_reset); + +int mmc_hw_reset_check(struct mmc_host *host) +{ + return mmc_do_hw_reset(host, 1); +} +EXPORT_SYMBOL(mmc_hw_reset_check); + static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) { host->f_init = freq; @@ -1738,6 +1889,12 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) mmc_power_up(host); /* + * Some eMMCs (with VCCQ always on) may not be reset after power up, so + * do a hardware reset if possible. + */ + mmc_hw_reset_for_init(host); + + /* * sdio_reset sends CMD52 to reset card. Since we do not know * if the card is being re-initialized, just send it. CMD52 * should be ignored by SD/eMMC cards. @@ -1845,6 +2002,7 @@ void mmc_stop_host(struct mmc_host *host) mmc_claim_host(host); mmc_detach_bus(host); + mmc_power_off(host); mmc_release_host(host); mmc_bus_put(host); return; @@ -1974,6 +2132,7 @@ int mmc_suspend_host(struct mmc_host *host) host->bus_ops->remove(host); mmc_claim_host(host); mmc_detach_bus(host); + mmc_power_off(host); mmc_release_host(host); host->pm_flags = 0; err = 0; @@ -2061,6 +2220,7 @@ int mmc_pm_notify(struct notifier_block *notify_block, host->bus_ops->remove(host); mmc_detach_bus(host); + mmc_power_off(host); mmc_release_host(host); host->pm_flags = 0; break; diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index d9411ed2a39..14664f1fb16 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -43,6 +43,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11); void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); +void mmc_power_off(struct mmc_host *host); static inline void mmc_delay(unsigned int ms) { diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 998797ed67a..6045ea46936 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -7,11 +7,13 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include <linux/moduleparam.h> #include <linux/debugfs.h> #include <linux/fs.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/stat.h> +#include <linux/fault-inject.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> @@ -19,6 +21,14 @@ #include "core.h" #include "mmc_ops.h" +#ifdef CONFIG_FAIL_MMC_REQUEST + +static DECLARE_FAULT_ATTR(fail_default_attr); +static char *fail_request; +module_param(fail_request, charp, 0); + +#endif /* CONFIG_FAIL_MMC_REQUEST */ + /* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */ static int mmc_ios_show(struct seq_file *s, void *data) { @@ -113,6 +123,15 @@ static int mmc_ios_show(struct seq_file *s, void *data) case MMC_TIMING_SD_HS: str = "sd high-speed"; break; + case MMC_TIMING_UHS_SDR50: + str = "sd uhs SDR50"; + break; + case MMC_TIMING_UHS_SDR104: + str = "sd uhs SDR104"; + break; + case MMC_TIMING_UHS_DDR50: + str = "sd uhs DDR50"; + break; default: str = "invalid"; break; @@ -188,6 +207,15 @@ void mmc_add_host_debugfs(struct mmc_host *host) root, &host->clk_delay)) goto err_node; #endif +#ifdef CONFIG_FAIL_MMC_REQUEST + if (fail_request) + setup_fault_attr(&fail_default_attr, fail_request); + host->fail_mmc_request = fail_default_attr; + if (IS_ERR(fault_create_debugfs_attr("fail_mmc_request", + root, + &host->fail_mmc_request))) + goto err_node; +#endif return; err_node: diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 5700b1cbdfe..903c70b8ac4 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -239,7 +239,9 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd) */ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) { - int err = 0; + int err = 0, idx; + unsigned int part_size; + u8 hc_erase_grp_sz = 0, hc_wp_grp_sz = 0; BUG_ON(!card); @@ -337,10 +339,26 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.rel_sectors = ext_csd[EXT_CSD_REL_WR_SEC_C]; /* + * Note that the call to mmc_part_add defaults to read + * only. If this default assumption is changed, the call must + * take into account the value of boot_locked below. + */ + card->ext_csd.boot_locked = ext_csd[EXT_CSD_BOOT_WP] & + (EXT_CSD_BOOT_WP_B_PERM_WP_EN | + EXT_CSD_BOOT_WP_B_PWR_WP_EN); + + /* * There are two boot regions of equal size, defined in * multiples of 128K. */ - card->ext_csd.boot_size = ext_csd[EXT_CSD_BOOT_MULT] << 17; + if (ext_csd[EXT_CSD_BOOT_MULT] && mmc_boot_partition_access(card->host)) { + for (idx = 0; idx < MMC_NUM_BOOT_PARTITION; idx++) { + part_size = ext_csd[EXT_CSD_BOOT_MULT] << 17; + mmc_part_add(card, part_size, + EXT_CSD_PART_CONFIG_ACC_BOOT0 + idx, + "boot%d", idx, true, MMC_BLK_DATA_AREA_BOOT); + } + } } card->ext_csd.raw_hc_erase_gap_size = @@ -359,11 +377,12 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) * card has the Enhanced area enabled. If so, export enhanced * area offset and size to user by adding sysfs interface. */ + card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT]; if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) && (ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) { - u8 hc_erase_grp_sz = + hc_erase_grp_sz = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; - u8 hc_wp_grp_sz = + hc_wp_grp_sz = ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; card->ext_csd.enhanced_area_en = 1; @@ -392,6 +411,42 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.enhanced_area_offset = -EINVAL; card->ext_csd.enhanced_area_size = -EINVAL; } + + /* + * General purpose partition feature support -- + * If ext_csd has the size of general purpose partitions, + * set size, part_cfg, partition name in mmc_part. + */ + if (ext_csd[EXT_CSD_PARTITION_SUPPORT] & + EXT_CSD_PART_SUPPORT_PART_EN) { + if (card->ext_csd.enhanced_area_en != 1) { + hc_erase_grp_sz = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + hc_wp_grp_sz = + ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + + card->ext_csd.enhanced_area_en = 1; + } + + for (idx = 0; idx < MMC_NUM_GP_PARTITION; idx++) { + if (!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3] && + !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] && + !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2]) + continue; + part_size = + (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2] + << 16) + + (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] + << 8) + + ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3]; + part_size *= (size_t)(hc_erase_grp_sz * + hc_wp_grp_sz); + mmc_part_add(card, part_size << 19, + EXT_CSD_PART_CONFIG_ACC_GP0 + idx, + "gp%d", idx, false, + MMC_BLK_DATA_AREA_GP); + } + } card->ext_csd.sec_trim_mult = ext_csd[EXT_CSD_SEC_TRIM_MULT]; card->ext_csd.sec_erase_mult = @@ -402,14 +457,23 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) ext_csd[EXT_CSD_TRIM_MULT]; } - if (card->ext_csd.rev >= 5) + if (card->ext_csd.rev >= 5) { card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM]; + card->ext_csd.rst_n_function = ext_csd[EXT_CSD_RST_N_FUNCTION]; + } + card->ext_csd.raw_erased_mem_count = ext_csd[EXT_CSD_ERASED_MEM_CONT]; if (ext_csd[EXT_CSD_ERASED_MEM_CONT]) card->erased_byte = 0xFF; else card->erased_byte = 0x0; + if (card->ext_csd.rev >= 6) + card->ext_csd.generic_cmd6_time = 10 * + ext_csd[EXT_CSD_GENERIC_CMD6_TIME]; + else + card->ext_csd.generic_cmd6_time = 0; + out: return err; } @@ -530,6 +594,86 @@ static struct device_type mmc_type = { }; /* + * Select the PowerClass for the current bus width + * If power class is defined for 4/8 bit bus in the + * extended CSD register, select it by executing the + * mmc_switch command. + */ +static int mmc_select_powerclass(struct mmc_card *card, + unsigned int bus_width, u8 *ext_csd) +{ + int err = 0; + unsigned int pwrclass_val; + unsigned int index = 0; + struct mmc_host *host; + + BUG_ON(!card); + + host = card->host; + BUG_ON(!host); + + if (ext_csd == NULL) + return 0; + + /* Power class selection is supported for versions >= 4.0 */ + if (card->csd.mmca_vsn < CSD_SPEC_VER_4) + return 0; + + /* Power class values are defined only for 4/8 bit bus */ + if (bus_width == EXT_CSD_BUS_WIDTH_1) + return 0; + + switch (1 << host->ios.vdd) { + case MMC_VDD_165_195: + if (host->ios.clock <= 26000000) + index = EXT_CSD_PWR_CL_26_195; + else if (host->ios.clock <= 52000000) + index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? + EXT_CSD_PWR_CL_52_195 : + EXT_CSD_PWR_CL_DDR_52_195; + else if (host->ios.clock <= 200000000) + index = EXT_CSD_PWR_CL_200_195; + break; + case MMC_VDD_32_33: + case MMC_VDD_33_34: + case MMC_VDD_34_35: + case MMC_VDD_35_36: + if (host->ios.clock <= 26000000) + index = EXT_CSD_PWR_CL_26_360; + else if (host->ios.clock <= 52000000) + index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? + EXT_CSD_PWR_CL_52_360 : + EXT_CSD_PWR_CL_DDR_52_360; + else if (host->ios.clock <= 200000000) + index = EXT_CSD_PWR_CL_200_360; + break; + default: + pr_warning("%s: Voltage range not supported " + "for power class.\n", mmc_hostname(host)); + return -EINVAL; + } + + pwrclass_val = ext_csd[index]; + + if (bus_width & (EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_BUS_WIDTH_8)) + pwrclass_val = (pwrclass_val & EXT_CSD_PWR_CL_8BIT_MASK) >> + EXT_CSD_PWR_CL_8BIT_SHIFT; + else + pwrclass_val = (pwrclass_val & EXT_CSD_PWR_CL_4BIT_MASK) >> + EXT_CSD_PWR_CL_4BIT_SHIFT; + + /* If the power class is different from the default value */ + if (pwrclass_val > 0) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_POWER_CLASS, + pwrclass_val, + 0); + } + + return err; +} + +/* * Handle the detection and initialisation of a card. * * In the case of a resume, "oldcard" will contain the card @@ -548,11 +692,16 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, BUG_ON(!host); WARN_ON(!host->claimed); + /* Set correct bus mode for MMC before attempting init */ + if (!mmc_host_is_spi(host)) + mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN); + /* * Since we're changing the OCR value, we seem to * need to tell some cards to go back to the idle * state. We wait 1ms to give cards time to * respond. + * mmc_go_idle is needed for eMMC that are asleep */ mmc_go_idle(host); @@ -668,7 +817,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, */ if (card->ext_csd.enhanced_area_en) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_ERASE_GROUP_DEF, 1, 0); + EXT_CSD_ERASE_GROUP_DEF, 1, + card->ext_csd.generic_cmd6_time); if (err && err != -EBADMSG) goto free_card; @@ -711,7 +861,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if ((card->ext_csd.hs_max_dtr != 0) && (host->caps & MMC_CAP_MMC_HIGHSPEED)) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, 1, 0); + EXT_CSD_HS_TIMING, 1, + card->ext_csd.generic_cmd6_time); if (err && err != -EBADMSG) goto free_card; @@ -780,10 +931,18 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, bus_width = bus_widths[idx]; if (bus_width == MMC_BUS_WIDTH_1) ddr = 0; /* no DDR for 1-bit width */ + err = mmc_select_powerclass(card, ext_csd_bits[idx][0], + ext_csd); + if (err) + pr_err("%s: power class selection to " + "bus width %d failed\n", + mmc_hostname(card->host), + 1 << bus_width); + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, ext_csd_bits[idx][0], - 0); + card->ext_csd.generic_cmd6_time); if (!err) { mmc_set_bus_width(card->host, bus_width); @@ -803,10 +962,18 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } if (!err && ddr) { + err = mmc_select_powerclass(card, ext_csd_bits[idx][1], + ext_csd); + if (err) + pr_err("%s: power class selection to " + "bus width %d ddr %d failed\n", + mmc_hostname(card->host), + 1 << bus_width, ddr); + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, ext_csd_bits[idx][1], - 0); + card->ext_csd.generic_cmd6_time); } if (err) { printk(KERN_WARNING "%s: switch to bus width %d ddr %d " @@ -891,6 +1058,7 @@ static void mmc_detect(struct mmc_host *host) mmc_claim_host(host); mmc_detach_bus(host); + mmc_power_off(host); mmc_release_host(host); } } @@ -900,16 +1068,20 @@ static void mmc_detect(struct mmc_host *host) */ static int mmc_suspend(struct mmc_host *host) { + int err = 0; + BUG_ON(!host); BUG_ON(!host->card); mmc_claim_host(host); - if (!mmc_host_is_spi(host)) + if (mmc_card_can_sleep(host)) + err = mmc_card_sleep(host); + else if (!mmc_host_is_spi(host)) mmc_deselect_cards(host); host->card->state &= ~MMC_STATE_HIGHSPEED; mmc_release_host(host); - return 0; + return err; } /* @@ -1016,6 +1188,10 @@ int mmc_attach_mmc(struct mmc_host *host) BUG_ON(!host); WARN_ON(!host->claimed); + /* Set correct bus mode for MMC before attempting attach */ + if (!mmc_host_is_spi(host)) + mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN); + err = mmc_send_op_cond(host, 0, &ocr); if (err) return err; diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 770c3d06f5d..7aa13d01a83 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -233,7 +233,7 @@ static int mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, u32 opcode, void *buf, unsigned len) { - struct mmc_request mrq = {0}; + struct mmc_request mrq = {NULL}; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; @@ -454,7 +454,7 @@ static int mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, u8 len) { - struct mmc_request mrq = {0}; + struct mmc_request mrq = {NULL}; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c index 3a596217029..6c3cf98a62e 100644 --- a/drivers/mmc/core/quirks.c +++ b/drivers/mmc/core/quirks.c @@ -21,6 +21,14 @@ #define SDIO_DEVICE_ID_TI_WL1271 0x4076 #endif +#ifndef SDIO_VENDOR_ID_STE +#define SDIO_VENDOR_ID_STE 0x0020 +#endif + +#ifndef SDIO_DEVICE_ID_STE_CW1200 +#define SDIO_DEVICE_ID_STE_CW1200 0x2280 +#endif + /* * This hook just adds a quirk for all sdio devices */ @@ -46,6 +54,9 @@ static const struct mmc_fixup mmc_fixup_methods[] = { SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, add_quirk, MMC_QUIRK_DISABLE_CD), + SDIO_FIXUP(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200, + add_quirk, MMC_QUIRK_BROKEN_BYTE_MODE_512), + END_FIXUP }; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 0370e03e314..342b18c4afc 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -929,8 +929,6 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, err = mmc_send_relative_addr(host, &card->rca); if (err) return err; - - mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); } if (!oldcard) { @@ -1043,6 +1041,7 @@ static void mmc_sd_detect(struct mmc_host *host) mmc_claim_host(host); mmc_detach_bus(host); + mmc_power_off(host); mmc_release_host(host); } } diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index 021fed15380..46a785419fa 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -67,7 +67,7 @@ EXPORT_SYMBOL_GPL(mmc_app_cmd); int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, struct mmc_command *cmd, int retries) { - struct mmc_request mrq = {0}; + struct mmc_request mrq = {NULL}; int i, err; @@ -244,7 +244,7 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca) int mmc_app_send_scr(struct mmc_card *card, u32 *scr) { int err; - struct mmc_request mrq = {0}; + struct mmc_request mrq = {NULL}; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; @@ -303,7 +303,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) int mmc_sd_switch(struct mmc_card *card, int mode, int group, u8 value, u8 *resp) { - struct mmc_request mrq = {0}; + struct mmc_request mrq = {NULL}; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; @@ -348,7 +348,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, int mmc_app_sd_status(struct mmc_card *card, void *ssr) { int err; - struct mmc_request mrq = {0}; + struct mmc_request mrq = {NULL}; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 262fff01917..698d813cff3 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -408,8 +408,6 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, */ if (oldcard) oldcard->rca = card->rca; - - mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); } /* @@ -597,6 +595,7 @@ out: mmc_claim_host(host); mmc_detach_bus(host); + mmc_power_off(host); mmc_release_host(host); } } diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c index f087d876c57..b0517cc0620 100644 --- a/drivers/mmc/core/sdio_ops.c +++ b/drivers/mmc/core/sdio_ops.c @@ -121,7 +121,7 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz) { - struct mmc_request mrq = {0}; + struct mmc_request mrq = {NULL}; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg; @@ -144,8 +144,11 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, cmd.arg |= fn << 28; cmd.arg |= incr_addr ? 0x04000000 : 0x00000000; cmd.arg |= addr << 9; - if (blocks == 1 && blksz <= 512) - cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */ + if (blocks == 1 && blksz < 512) + cmd.arg |= blksz; /* byte mode */ + else if (blocks == 1 && blksz == 512 && + !(mmc_card_broken_byte_mode_512(card))) + cmd.arg |= 0; /* byte mode, 0==512 */ else cmd.arg |= 0x08000000 | blocks; /* block mode */ cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 56e9a416826..8d901ba1e54 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -19,6 +19,7 @@ #include <linux/err.h> #include <linux/highmem.h> #include <linux/log2.h> +#include <linux/pm_runtime.h> #include <linux/mmc/host.h> #include <linux/mmc/card.h> #include <linux/amba/bus.h> @@ -52,6 +53,9 @@ static unsigned int fmax = 515633; * @sdio: variant supports SDIO * @st_clkdiv: true if using a ST-specific clock divider algorithm * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register + * @non_power_of_2_blksize: true if block sizes can be other than power of two + * @pwrreg_powerup: power up value for MMCIPOWER register + * @signal_direction: input/out direction of bus signals can be indicated */ struct variant_data { unsigned int clkreg; @@ -62,18 +66,23 @@ struct variant_data { bool sdio; bool st_clkdiv; bool blksz_datactrl16; + bool non_power_of_2_blksize; + unsigned int pwrreg_powerup; + bool signal_direction; }; static struct variant_data variant_arm = { .fifosize = 16 * 4, .fifohalfsize = 8 * 4, .datalength_bits = 16, + .pwrreg_powerup = MCI_PWR_UP, }; static struct variant_data variant_arm_extended_fifo = { .fifosize = 128 * 4, .fifohalfsize = 64 * 4, .datalength_bits = 16, + .pwrreg_powerup = MCI_PWR_UP, }; static struct variant_data variant_u300 = { @@ -82,6 +91,8 @@ static struct variant_data variant_u300 = { .clkreg_enable = MCI_ST_U300_HWFCEN, .datalength_bits = 16, .sdio = true, + .pwrreg_powerup = MCI_PWR_ON, + .signal_direction = true, }; static struct variant_data variant_ux500 = { @@ -92,6 +103,8 @@ static struct variant_data variant_ux500 = { .datalength_bits = 24, .sdio = true, .st_clkdiv = true, + .pwrreg_powerup = MCI_PWR_ON, + .signal_direction = true, }; static struct variant_data variant_ux500v2 = { @@ -103,6 +116,9 @@ static struct variant_data variant_ux500v2 = { .sdio = true, .st_clkdiv = true, .blksz_datactrl16 = true, + .non_power_of_2_blksize = true, + .pwrreg_powerup = MCI_PWR_ON, + .signal_direction = true, }; /* @@ -165,13 +181,7 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq) host->mrq = NULL; host->cmd = NULL; - /* - * Need to drop the host lock here; mmc_request_done may call - * back into the driver... - */ - spin_unlock(&host->lock); mmc_request_done(host->mmc, mrq); - spin_lock(&host->lock); } static void mmci_set_mask1(struct mmci_host *host, unsigned int mask) @@ -529,7 +539,7 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq, if (chan) { if (err) dmaengine_terminate_all(chan); - if (err || data->host_cookie) + if (data->host_cookie) dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, dir); mrq->data->host_cookie = 0; @@ -592,7 +602,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) writel(host->size, base + MMCIDATALENGTH); blksz_bits = ffs(data->blksz) - 1; - BUG_ON(1 << blksz_bits != data->blksz); if (variant->blksz_datactrl16) datactrl = MCI_DPSM_ENABLE | (data->blksz << 16); @@ -602,6 +611,34 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) if (data->flags & MMC_DATA_READ) datactrl |= MCI_DPSM_DIRECTION; + /* The ST Micro variants has a special bit to enable SDIO */ + if (variant->sdio && host->mmc->card) + if (mmc_card_sdio(host->mmc->card)) { + /* + * The ST Micro variants has a special bit + * to enable SDIO. + */ + datactrl |= MCI_ST_DPSM_SDIOEN; + + /* + * The ST Micro variant for SDIO transfer sizes + * less then or equal to 8 bytes needs to have clock + * H/W flow control disabled. Since flow control is + * not really needed for anything that fits in the + * FIFO, we can disable it for any write smaller + * than the FIFO size. + */ + if ((host->size <= variant->fifosize) && + (data->flags & MMC_DATA_WRITE)) + writel(readl(host->base + MMCICLOCK) & + ~variant->clkreg_enable, + host->base + MMCICLOCK); + else + writel(readl(host->base + MMCICLOCK) | + variant->clkreg_enable, + host->base + MMCICLOCK); + } + /* * Attempt to use DMA operation mode, if this * should fail, fall back to PIO mode @@ -630,11 +667,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) irqmask = MCI_TXFIFOHALFEMPTYMASK; } - /* The ST Micro variants has a special bit to enable SDIO */ - if (variant->sdio && host->mmc->card) - if (mmc_card_sdio(host->mmc->card)) - datactrl |= MCI_ST_DPSM_SDIOEN; - writel(datactrl, base + MMCIDATACTRL); writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0); mmci_set_mask1(host, irqmask); @@ -776,7 +808,24 @@ static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int rema if (count <= 0) break; - readsl(base + MMCIFIFO, ptr, count >> 2); + /* + * SDIO especially may want to receive something that is + * not divisible by 4 (as opposed to card sectors + * etc). Therefore make sure we always read the last bytes + * out of the FIFO. + */ + switch (count) { + case 1: + case 3: + readsb(base + MMCIFIFO, ptr, count); + break; + case 2: + readsw(base + MMCIFIFO, ptr, 1); + break; + default: + readsl(base + MMCIFIFO, ptr, count >> 2); + break; + } ptr += count; remain -= count; @@ -805,23 +854,6 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem count = min(remain, maxcnt); /* - * The ST Micro variant for SDIO transfer sizes - * less then 8 bytes should have clock H/W flow - * control disabled. - */ - if (variant->sdio && - mmc_card_sdio(host->mmc->card)) { - if (count < 8) - writel(readl(host->base + MMCICLOCK) & - ~variant->clkreg_enable, - host->base + MMCICLOCK); - else - writel(readl(host->base + MMCICLOCK) | - variant->clkreg_enable, - host->base + MMCICLOCK); - } - - /* * SDIO especially may want to send something that is * not divisible by 4 (as opposed to card sectors * etc), and the FIFO only accept full 32-bit writes. @@ -972,11 +1004,14 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct mmci_host *host = mmc_priv(mmc); + struct variant_data *variant = host->variant; unsigned long flags; WARN_ON(host->mrq != NULL); - if (mrq->data && !is_power_of_2(mrq->data->blksz)) { + if (mrq->data && + !variant->non_power_of_2_blksize && + !is_power_of_2(mrq->data->blksz)) { dev_err(mmc_dev(mmc), "unsupported block size (%d bytes)\n", mrq->data->blksz); mrq->cmd->error = -EINVAL; @@ -1002,10 +1037,15 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct mmci_host *host = mmc_priv(mmc); + struct variant_data *variant = host->variant; u32 pwr = 0; unsigned long flags; int ret; + if (host->plat->ios_handler && + host->plat->ios_handler(mmc_dev(mmc), ios)) + dev_err(mmc_dev(mmc), "platform ios_handler failed\n"); + switch (ios->power_mode) { case MMC_POWER_OFF: if (host->vcc) @@ -1025,19 +1065,35 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) return; } } - if (host->plat->vdd_handler) - pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd, - ios->power_mode); - /* The ST version does not have this, fall through to POWER_ON */ - if (host->hw_designer != AMBA_VENDOR_ST) { - pwr |= MCI_PWR_UP; - break; - } + /* + * The ST Micro variant doesn't have the PL180s MCI_PWR_UP + * and instead uses MCI_PWR_ON so apply whatever value is + * configured in the variant data. + */ + pwr |= variant->pwrreg_powerup; + + break; case MMC_POWER_ON: pwr |= MCI_PWR_ON; break; } + if (variant->signal_direction && ios->power_mode != MMC_POWER_OFF) { + /* + * The ST Micro variant has some additional bits + * indicating signal direction for the signals in + * the SD/MMC bus and feedback-clock usage. + */ + pwr |= host->plat->sigdir; + + if (ios->bus_width == MMC_BUS_WIDTH_4) + pwr &= ~MCI_ST_DATA74DIREN; + else if (ios->bus_width == MMC_BUS_WIDTH_1) + pwr &= (~MCI_ST_DATA74DIREN & + ~MCI_ST_DATA31DIREN & + ~MCI_ST_DATA2DIREN); + } + if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) { if (host->hw_designer != AMBA_VENDOR_ST) pwr |= MCI_ROD; @@ -1320,6 +1376,10 @@ static int __devinit mmci_probe(struct amba_device *dev, amba_set_drvdata(dev, mmc); + pm_runtime_enable(mmc->parent); + if (pm_runtime_get_sync(mmc->parent) < 0) + dev_err(mmc_dev(mmc), "failed pm_runtime_get_sync\n"); + dev_info(&dev->dev, "%s: PL%03x manf %x rev%u at 0x%08llx irq %d,%d (pio)\n", mmc_hostname(mmc), amba_part(dev), amba_manf(dev), amba_rev(dev), (unsigned long long)dev->res.start, @@ -1412,6 +1472,9 @@ static int mmci_suspend(struct amba_device *dev, pm_message_t state) ret = mmc_suspend_host(mmc); if (ret == 0) writel(0, host->base + MMCIMASK0); + + if (pm_runtime_put_sync(mmc->parent) < 0) + dev_err(mmc_dev(mmc), "failed pm_runtime_put_sync\n"); } return ret; @@ -1425,6 +1488,9 @@ static int mmci_resume(struct amba_device *dev) if (mmc) { struct mmci_host *host = mmc_priv(mmc); + if (pm_runtime_get_sync(mmc->parent) < 0) + dev_err(mmc_dev(mmc), "failed pm_runtime_get_sync\n"); + writel(MCI_IRQENABLE, host->base + MMCIMASK0); ret = mmc_resume_host(mmc); diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 79e4143ab9d..89eb2e3556d 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -13,16 +13,6 @@ #define MCI_PWR_ON 0x03 #define MCI_OD (1 << 6) #define MCI_ROD (1 << 7) -/* - * The ST Micro version does not have ROD and reuse the voltage registers - * for direction settings - */ -#define MCI_ST_DATA2DIREN (1 << 2) -#define MCI_ST_CMDDIREN (1 << 3) -#define MCI_ST_DATA0DIREN (1 << 4) -#define MCI_ST_DATA31DIREN (1 << 5) -#define MCI_ST_FBCLKEN (1 << 7) -#define MCI_ST_DATA74DIREN (1 << 8) #define MMCICLOCK 0x004 #define MCI_CLK_ENABLE (1 << 8) @@ -160,7 +150,7 @@ (MCI_RXFIFOHALFFULLMASK | MCI_RXDATAAVLBLMASK | \ MCI_TXFIFOHALFEMPTYMASK) -#define NR_SG 16 +#define NR_SG 128 struct clk; struct variant_data; diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 26c528648f3..d7cebb30b6d 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -98,6 +98,9 @@ static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot) SDHCI_TIMEOUT_CLK_UNIT | SDHCI_CAN_VDD_330 | SDHCI_CAN_DO_SDMA; + + slot->host->mmc->caps2 = MMC_CAP2_BOOTPART_NOACC; + return 0; } diff --git a/fs/mpage.c b/fs/mpage.c index fdfae9fa98c..cc782f9e68d 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -53,6 +53,8 @@ static void mpage_end_io(struct bio *bio, int err) prefetchw(&bvec->bv_page->flags); if (bio_data_dir(bio) == READ) { if (uptodate) { + /* FIXME: fix to solve cache coherence issues. */ + flush_dcache_page(page); SetPageUptodate(page); } else { ClearPageUptodate(page); diff --git a/fs/partitions/Kconfig b/fs/partitions/Kconfig index cb5f0a3f1b0..097be1934ee 100644 --- a/fs/partitions/Kconfig +++ b/fs/partitions/Kconfig @@ -68,6 +68,25 @@ config ACORN_PARTITION_RISCIX of machines called RISCiX. If you say 'Y' here, Linux will be able to read disks partitioned under RISCiX. +config BLKDEV_PARTITION + bool "Blockdev commandline partition support" if PARTITION_ADVANCED + default n + help + Say Y if you like to setup partitions for block devices by reading + from the kernel command line (kernel boot arguments). + + The format of the partitions on the command line: + blkdevparts=<blkdev-def>[;<blkdev-def>] + <blkdev-def> := <blkdev-id>:<partdef>[,<partdef>] + <partdef> := <size>[@<offset>] + + <blkdev-id> := unique id used to map driver to blockdev name + <size> := size in numbers of sectors + <offset> := offset in sectors for partition to start at + + Example: + blkdevparts=mmc0:1024@0,524288@1024;mmc1:8192@0,8192@8192 + config OSF_PARTITION bool "Alpha OSF partition support" if PARTITION_ADVANCED default y if ALPHA diff --git a/fs/partitions/Makefile b/fs/partitions/Makefile index 03af8eac51d..48b216c53db 100644 --- a/fs/partitions/Makefile +++ b/fs/partitions/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_BLOCK) := check.o obj-$(CONFIG_ACORN_PARTITION) += acorn.o obj-$(CONFIG_AMIGA_PARTITION) += amiga.o obj-$(CONFIG_ATARI_PARTITION) += atari.o +obj-$(CONFIG_BLKDEV_PARTITION) += blkdev_parts.o obj-$(CONFIG_MAC_PARTITION) += mac.o obj-$(CONFIG_LDM_PARTITION) += ldm.o obj-$(CONFIG_MSDOS_PARTITION) += msdos.o diff --git a/fs/partitions/blkdev_parts.c b/fs/partitions/blkdev_parts.c new file mode 100755 index 00000000000..030565b7ce7 --- /dev/null +++ b/fs/partitions/blkdev_parts.c @@ -0,0 +1,127 @@ +/* + * + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Ulf Hansson <ulf.hansson@stericsson.com> for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * + * Create partitions for block devices by reading from the kernel + * command line (kernel boot arguments). + * + */ + +#include "check.h" +#include "blkdev_parts.h" + +static char *cmdline; + +/* + * This is the handler for our kernel commandline parameter, + * called from main.c::checksetup(). + * Note that we can not yet kmalloc() anything, so we only save + * the commandline for later processing. + */ +static int cmdline_setup(char *s) +{ + cmdline = s; + return 1; +} +__setup("blkdevparts=", cmdline_setup); + +/* Parse for a matching blkdev-id and return pointer to partdef */ +static char *parse_blkdev_id(char *blkdev_name) +{ + int blkdev_id_len; + char *p, *blkdev_id; + + /* Start parsing for a matching blkdev-id */ + p = blkdev_id = cmdline; + while (blkdev_id != NULL) { + + /* Find the end of the blkdev-id string */ + p = strchr(blkdev_id, ':'); + if (p == NULL) + return NULL; + + /* Check if we found a matching blkdev-id */ + blkdev_id_len = p - blkdev_id; + if (strlen(blkdev_name) == blkdev_id_len) { + if (strncmp(blkdev_name, blkdev_id, blkdev_id_len) == 0) + return p; + } + + /* Move to next blkdev-id string if there is one */ + blkdev_id = strchr(p, ';'); + if (blkdev_id != NULL) + blkdev_id++; + } + return NULL; +} + +static int parse_partdef(char **part, struct parsed_partitions *state, int part_nbr) +{ + sector_t size, offset; + char *p = *part; + + /* Skip the beginning "," or ":" */ + p++; + + /* Fetch and verify size from partdef */ + size = simple_strtoull(p, &p, 10); + if ((size == 0) || (*p != '@')) + return 0; + + /* Skip the "@" */ + p++; + + /* Fetch offset from partdef and check if there are more parts */ + offset = simple_strtoull(p, &p, 10); + if (*p == ',') + *part = p; + else + *part = NULL; + + /* Add partition to state */ + put_partition(state, part_nbr, offset, size); + printk(KERN_INFO "\nPartition: size=%llu, offset=%llu\n", + (unsigned long long) size, + (unsigned long long) offset); + return 1; +} + +static int parse_blkdev_parts(char *blkdev_name, struct parsed_partitions *state) +{ + char *partdef; + int part_nbr = 0; + + /* Find partdef */ + partdef = parse_blkdev_id(blkdev_name); + + /* Add parts */ + while (partdef != NULL) { + /* Find next part and add it to state */ + part_nbr++; + if (!parse_partdef(&partdef, state, part_nbr)) + return 0; + } + return part_nbr; +} + +int blkdev_partition(struct parsed_partitions *state) +{ + char blkdev_name[BDEVNAME_SIZE]; + + /* Check if there are any partitions to handle */ + if (cmdline == NULL) + return 0; + + /* Get the name of the blockdevice we are operating upon */ + if (bdevname(state->bdev, blkdev_name) == NULL) { + printk(KERN_WARNING "Could not get a blkdev name\n"); + return 0; + } + + /* Parse for partitions and add them to the state */ + return parse_blkdev_parts(blkdev_name, state); +} + diff --git a/fs/partitions/blkdev_parts.h b/fs/partitions/blkdev_parts.h new file mode 100755 index 00000000000..16d2b571625 --- /dev/null +++ b/fs/partitions/blkdev_parts.h @@ -0,0 +1,14 @@ +/* + * + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Ulf Hansson <ulf.hansson@stericsson.com> for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * + * Create partitions for block devices by reading from the kernel + * command line (kernel boot arguments). + * + */ + +int blkdev_partition(struct parsed_partitions *state); + diff --git a/fs/partitions/check.c b/fs/partitions/check.c index e3c63d1c5e1..c8dc879e26b 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -27,6 +27,7 @@ #include "acorn.h" #include "amiga.h" #include "atari.h" +#include "blkdev_parts.h" #include "ldm.h" #include "mac.h" #include "msdos.h" @@ -50,6 +51,9 @@ static int (*check_part[])(struct parsed_partitions *) = { * Probe partition formats with tables at disk address 0 * that also have an ADFS boot block at 0xdc0. */ +#ifdef CONFIG_BLKDEV_PARTITION + blkdev_partition, +#endif #ifdef CONFIG_ACORN_PARTITION_ICS adfspart_check_ICS, #endif diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index 21114810c7c..23536c95fb6 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -6,6 +6,20 @@ #include <linux/mmc/host.h> +/* + * These defines is places here due to access is needed from machine + * configuration files. + * + * The ST Micro version does not have ROD and reuse the voltage registers + * for direction settings. + */ +#define MCI_ST_DATA2DIREN (1 << 2) +#define MCI_ST_CMDDIREN (1 << 3) +#define MCI_ST_DATA0DIREN (1 << 4) +#define MCI_ST_DATA31DIREN (1 << 5) +#define MCI_ST_FBCLKEN (1 << 7) +#define MCI_ST_DATA74DIREN (1 << 8) + /* Just some dummy forwarding */ struct dma_chan; @@ -18,7 +32,8 @@ struct dma_chan; * @ocr_mask: available voltages on the 4 pins from the block, this * is ignored if a regulator is used, see the MMC_VDD_* masks in * mmc/host.h - * @vdd_handler: a callback function to translate a MMC_VDD_* + * @ios_handler: a callback function to act on specfic ios changes, + * used for example to control a levelshifter * mask into a value to be binary (or set some other custom bits * in MMCIPWR) or:ed and written into the MMCIPWR register of the * block. May also control external power based on the power_mode. @@ -30,6 +45,8 @@ struct dma_chan; * @cd_invert: true if the gpio_cd pin value is active low * @capabilities: the capabilities of the block as implemented in * this platform, signify anything MMC_CAP_* from mmc/host.h + * @sigdir: a bit field indicating for what bits in the MMC bus the host + * should enable signal direction indication. * @dma_filter: function used to select an appropriate RX and TX * DMA channel to be used for DMA, if and only if you're deploying the * generic DMA engine @@ -45,13 +62,13 @@ struct dma_chan; struct mmci_platform_data { unsigned int f_max; unsigned int ocr_mask; - u32 (*vdd_handler)(struct device *, unsigned int vdd, - unsigned char power_mode); + int (*ios_handler)(struct device *, struct mmc_ios *); unsigned int (*status)(struct device *); int gpio_wp; int gpio_cd; bool cd_invert; unsigned long capabilities; + unsigned int sigdir; bool (*dma_filter)(struct dma_chan *chan, void *filter_param); void *dma_rx_param; void *dma_tx_param; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index b460fc2af8a..dc20c5b6e5f 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -12,6 +12,7 @@ #include <linux/mmc/core.h> #include <linux/mod_devicetable.h> +#include <linux/genhd.h> struct mmc_cid { unsigned int manfid; @@ -50,8 +51,10 @@ struct mmc_ext_csd { u8 rel_sectors; u8 rel_param; u8 part_config; + u8 rst_n_function; unsigned int part_time; /* Units: ms */ unsigned int sa_timeout; /* Units: 100ns */ + unsigned int generic_cmd6_time; /* Units: 10ms */ unsigned int hs_max_dtr; unsigned int sectors; unsigned int card_type; @@ -63,7 +66,7 @@ struct mmc_ext_csd { bool enhanced_area_en; /* enable bit */ unsigned long long enhanced_area_offset; /* Units: Byte */ unsigned int enhanced_area_size; /* Units: KB */ - unsigned int boot_size; /* in bytes */ + unsigned int boot_locked; u8 raw_partition_support; /* 160 */ u8 raw_erased_mem_count; /* 181 */ u8 raw_ext_csd_structure; /* 194 */ @@ -157,6 +160,32 @@ struct sdio_func_tuple; #define SDIO_MAX_FUNCS 7 +/* The number of MMC physical partitions. These consist of: + * boot partitions (2), general purpose partitions (4) in MMC v4.4. + */ +#define MMC_NUM_BOOT_PARTITION 2 +#define MMC_NUM_GP_PARTITION 4 +#define MMC_NUM_PHY_PARTITION 6 + +/* + * Partition area type, boot or gp + */ +enum mmc_part_area_type { + MMC_BLK_DATA_AREA_BOOT, + MMC_BLK_DATA_AREA_GP, +}; + +/* + * MMC Physical partitions + */ +struct mmc_part { + unsigned int size; /* partition size (in bytes) */ + unsigned int part_cfg; /* partition type */ + char name[DISK_NAME_LEN]; + bool force_ro; /* to make boot parts RO by default */ + int area_type; +}; + /* * MMC device */ @@ -188,6 +217,7 @@ struct mmc_card { #define MMC_QUIRK_DISABLE_CD (1<<5) /* disconnect CD/DAT[3] resistor */ #define MMC_QUIRK_INAND_CMD38 (1<<6) /* iNAND devices have broken CMD38 */ #define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */ +#define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8) /* Avoid sending 512 bytes in byte mode */ unsigned int erase_size; /* erase size in sectors */ unsigned int erase_shift; /* if erase unit is power 2 */ @@ -216,9 +246,26 @@ struct mmc_card { unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */ struct dentry *debugfs_root; + struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ + unsigned int nr_parts; }; /* + * This function fill contents in mmc_part. + */ +static inline void mmc_part_add(struct mmc_card *card, unsigned int size, + unsigned int part_cfg, char *name, int idx, bool ro, + int area_type) +{ + card->part[card->nr_parts].size = size; + card->part[card->nr_parts].part_cfg = part_cfg; + sprintf(card->part[card->nr_parts].name, name, idx); + card->part[card->nr_parts].force_ro = ro; + card->part[card->nr_parts].area_type = area_type; + card->nr_parts++; +} + +/* * The world is not perfect and supplies us with broken mmc/sdio devices. * For at least some of these bugs we need a work-around. */ @@ -377,6 +424,11 @@ static inline int mmc_card_nonstd_func_interface(const struct mmc_card *c) return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF; } +static inline int mmc_card_broken_byte_mode_512(const struct mmc_card *c) +{ + return c->quirks & MMC_QUIRK_BROKEN_BYTE_MODE_512; +} + #define mmc_card_name(c) ((c)->cid.prod_name) #define mmc_card_id(c) (dev_name(&(c)->dev)) diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index b8b1b7a311f..56e5625b6f4 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -162,6 +162,9 @@ extern int mmc_erase_group_aligned(struct mmc_card *card, unsigned int from, extern unsigned int mmc_calc_max_discard(struct mmc_card *card); extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen); +extern int mmc_hw_reset(struct mmc_host *host); +extern int mmc_hw_reset_check(struct mmc_host *host); +extern int mmc_can_reset(struct mmc_card *card); extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *); extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 1d09562ccf7..79117a252f0 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -12,6 +12,7 @@ #include <linux/leds.h> #include <linux/sched.h> +#include <linux/fault-inject.h> #include <linux/mmc/core.h> #include <linux/mmc/pm.h> @@ -108,6 +109,9 @@ struct mmc_host_ops { * It is optional for the host to implement pre_req and post_req in * order to support double buffering of requests (prepare one * request while another request is active). + * pre_req() must always be followed by a post_req(). + * To undo a call made to pre_req(), call post_req() with + * a nonzero err condition. */ void (*post_req)(struct mmc_host *host, struct mmc_request *req, int err); @@ -147,6 +151,7 @@ struct mmc_host_ops { int (*execute_tuning)(struct mmc_host *host); void (*enable_preset_value)(struct mmc_host *host, bool enable); int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv); + void (*hw_reset)(struct mmc_host *host); }; struct mmc_card; @@ -229,6 +234,11 @@ struct mmc_host { #define MMC_CAP_MAX_CURRENT_600 (1 << 28) /* Host max current limit is 600mA */ #define MMC_CAP_MAX_CURRENT_800 (1 << 29) /* Host max current limit is 800mA */ #define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */ +#define MMC_CAP_HW_RESET (1 << 31) /* Hardware reset */ + + unsigned int caps2; /* More host capabilities */ + +#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */ mmc_pm_flag_t pm_caps; /* supported pm features */ @@ -302,6 +312,19 @@ struct mmc_host { struct mmc_async_req *areq; /* active async req */ +#ifdef CONFIG_FAIL_MMC_REQUEST + struct fault_attr fail_mmc_request; +#endif + +#ifdef CONFIG_MMC_EMBEDDED_SDIO + struct { + struct sdio_cis *cis; + struct sdio_cccr *cccr; + struct sdio_embedded_func *funcs; + int num_funcs; + } embedded_sdio_data; +#endif + unsigned long private[0] ____cacheline_aligned; }; @@ -394,4 +417,10 @@ static inline int mmc_host_cmd23(struct mmc_host *host) { return host->caps & MMC_CAP_CMD23; } + +static inline int mmc_boot_partition_access(struct mmc_host *host) +{ + return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC); +} + #endif /* LINUX_MMC_HOST_H */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 5a794cb503e..dc884f60798 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -270,18 +270,26 @@ struct _mmc_csd { * EXT_CSD fields */ +#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */ #define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ #define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ +#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ #define EXT_CSD_WR_REL_PARAM 166 /* RO */ +#define EXT_CSD_BOOT_WP 173 /* R/W */ #define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ #define EXT_CSD_PART_CONFIG 179 /* R/W */ #define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ #define EXT_CSD_BUS_WIDTH 183 /* R/W */ #define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_POWER_CLASS 187 /* R/W */ #define EXT_CSD_REV 192 /* RO */ #define EXT_CSD_STRUCTURE 194 /* RO */ #define EXT_CSD_CARD_TYPE 196 /* RO */ #define EXT_CSD_PART_SWITCH_TIME 199 /* RO */ +#define EXT_CSD_PWR_CL_52_195 200 /* RO */ +#define EXT_CSD_PWR_CL_26_195 201 /* RO */ +#define EXT_CSD_PWR_CL_52_360 202 /* RO */ +#define EXT_CSD_PWR_CL_26_360 203 /* RO */ #define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ #define EXT_CSD_S_A_TIMEOUT 217 /* RO */ #define EXT_CSD_REL_WR_SEC_C 222 /* RO */ @@ -293,6 +301,12 @@ struct _mmc_csd { #define EXT_CSD_SEC_ERASE_MULT 230 /* RO */ #define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */ #define EXT_CSD_TRIM_MULT 232 /* RO */ +#define EXT_CSD_PWR_CL_200_195 236 /* RO */ +#define EXT_CSD_PWR_CL_200_360 237 /* RO */ +#define EXT_CSD_PWR_CL_DDR_52_195 238 /* RO */ +#define EXT_CSD_PWR_CL_DDR_52_360 239 /* RO */ +#define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */ +#define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */ /* * EXT_CSD field definitions @@ -300,9 +314,16 @@ struct _mmc_csd { #define EXT_CSD_WR_REL_PARAM_EN (1<<2) +#define EXT_CSD_BOOT_WP_B_PWR_WP_DIS (0x40) +#define EXT_CSD_BOOT_WP_B_PERM_WP_DIS (0x10) +#define EXT_CSD_BOOT_WP_B_PERM_WP_EN (0x04) +#define EXT_CSD_BOOT_WP_B_PWR_WP_EN (0x01) + #define EXT_CSD_PART_CONFIG_ACC_MASK (0x7) #define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1) -#define EXT_CSD_PART_CONFIG_ACC_BOOT1 (0x2) +#define EXT_CSD_PART_CONFIG_ACC_GP0 (0x4) + +#define EXT_CSD_PART_SUPPORT_PART_EN (0x1) #define EXT_CSD_CMD_SET_NORMAL (1<<0) #define EXT_CSD_CMD_SET_SECURE (1<<1) @@ -328,6 +349,13 @@ struct _mmc_csd { #define EXT_CSD_SEC_BD_BLK_EN BIT(2) #define EXT_CSD_SEC_GB_CL_EN BIT(4) +#define EXT_CSD_RST_N_EN_MASK 0x3 +#define EXT_CSD_RST_N_ENABLED 1 /* RST_n is enabled on card */ + +#define EXT_CSD_PWR_CL_8BIT_MASK 0xF0 /* 8 bit PWR CLS */ +#define EXT_CSD_PWR_CL_4BIT_MASK 0x0F /* 8 bit PWR CLS */ +#define EXT_CSD_PWR_CL_8BIT_SHIFT 4 +#define EXT_CSD_PWR_CL_4BIT_SHIFT 0 /* * MMC_SWITCH access modes */ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index c0cb9c4bc46..1c7dbbf9e44 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1070,6 +1070,17 @@ config FAIL_IO_TIMEOUT Only works with drivers that use the generic timeout handling, for others it wont do anything. +config FAIL_MMC_REQUEST + bool "Fault-injection capability for MMC IO" + select DEBUG_FS + depends on FAULT_INJECTION && MMC + help + Provide fault-injection capability for MMC IO. + This will make the mmc core return data errors. This is + useful to test the error handling in the mmc block device + and to test how the mmc host driver handles retries from + the block device. + config FAULT_INJECTION_DEBUG_FS bool "Debugfs entries for fault-injection capabilities" depends on FAULT_INJECTION && SYSFS && DEBUG_FS |