From c7659e48d8c4022c31090f567f5de35273d2d047 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 12 May 2010 11:55:51 +0200 Subject: Added commandline partitions for block devices Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/305 Reviewed-by: Jonas ABERG Reviewed-by: Linus WALLEIJ Tested-by: Linus WALLEIJ Signed-off-by: Mian Yousaf Kaukab Change-Id: I3725676a9177b0072a435865ae578a6e17bd7168 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/2206 --- fs/partitions/Kconfig | 19 +++++++ fs/partitions/Makefile | 1 + fs/partitions/blkdev_parts.c | 127 +++++++++++++++++++++++++++++++++++++++++++ fs/partitions/blkdev_parts.h | 14 +++++ fs/partitions/check.c | 4 ++ 5 files changed, 165 insertions(+) create mode 100755 fs/partitions/blkdev_parts.c create mode 100755 fs/partitions/blkdev_parts.h 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=[;] + := :[,] + := [@] + + := unique id used to map driver to blockdev name + := size in numbers of sectors + := 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..48f4136d720 --- /dev/null +++ b/fs/partitions/blkdev_parts.c @@ -0,0 +1,127 @@ +/* + * + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Ulf Hansson 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, struct block_device *bdev) +{ + 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(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..e6c5d40ed76 --- /dev/null +++ b/fs/partitions/blkdev_parts.h @@ -0,0 +1,14 @@ +/* + * + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Ulf Hansson 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, struct block_device *bdev); + 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 -- cgit v1.2.3 From 1c1500d9a5fbb815fe7bb2e7d4fecdb25b6c509c Mon Sep 17 00:00:00 2001 From: Robert Rosengren Date: Mon, 6 Dec 2010 13:58:42 +0100 Subject: fs/partitions: Adapt blkdevparts to new interface fs/partitions: Adapt blkdevparts to new interface of kernel 2.6.35 ST-Ericsson ID: ER282603 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I95fccf7f47fd5e4f716b6a0e6fec001d2c9428cf Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/10308 Tested-by: Robert ROSENGREN Reviewed-by: Ulf HANSSON Reviewed-by: Sebastian RASMUSSEN --- fs/partitions/blkdev_parts.c | 4 ++-- fs/partitions/blkdev_parts.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/partitions/blkdev_parts.c b/fs/partitions/blkdev_parts.c index 48f4136d720..030565b7ce7 100755 --- a/fs/partitions/blkdev_parts.c +++ b/fs/partitions/blkdev_parts.c @@ -107,7 +107,7 @@ static int parse_blkdev_parts(char *blkdev_name, struct parsed_partitions *state return part_nbr; } -int blkdev_partition(struct parsed_partitions *state, struct block_device *bdev) +int blkdev_partition(struct parsed_partitions *state) { char blkdev_name[BDEVNAME_SIZE]; @@ -116,7 +116,7 @@ int blkdev_partition(struct parsed_partitions *state, struct block_device *bdev) return 0; /* Get the name of the blockdevice we are operating upon */ - if (bdevname(bdev, blkdev_name) == NULL) { + if (bdevname(state->bdev, blkdev_name) == NULL) { printk(KERN_WARNING "Could not get a blkdev name\n"); return 0; } diff --git a/fs/partitions/blkdev_parts.h b/fs/partitions/blkdev_parts.h index e6c5d40ed76..16d2b571625 100755 --- a/fs/partitions/blkdev_parts.h +++ b/fs/partitions/blkdev_parts.h @@ -10,5 +10,5 @@ * */ -int blkdev_partition(struct parsed_partitions *state, struct block_device *bdev); +int blkdev_partition(struct parsed_partitions *state); -- cgit v1.2.3 From 629bb2de7dbb7bd1ef16771e330a1ed63408651c Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 22 Mar 2011 14:27:12 +0100 Subject: mach-ux500: Cumulative changes into board-*-sdi.c files Signed-off-by: Philippe Langlais mach-ux500: Add SDIO WLAN support on sdi1 Fixing sdi0 access on snowball MMC_CAP_SD_HIGHSPEED is not supported on snowball resulting on initialisation errors. Signed-off-by: Mathieu Poirier mach-ux500: sdio/wlan: Initialized SDIO after all others SDI, because it blocks SDI thread till CW1200/WLAN is up Temporary workaround, a better way in SDIO card detection must be found Signed-off-by: Philippe Langlais mach-ux500: Add SDIO VDD handler for GPIO config It seems as PL18X does not drive all pins in a correct way when the block is powered but not initialized. Since CW1200 is sensitive to this (it samples the DAT2 signal level on bootup to choose between SPI and SDIO mode), it must be assured that the pins are in a correct state at all times. This is done by adding a VDD handler for SDI1 which reconfigures all the pins to GPIO pins with pullups when PL18X is not in an initialized state. ST-Ericsson ID: 327586 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Stefan Nilsson XK mach-ux500: Enable DMA for sdi1 ST-Ericsson ID: 329007 Signed-off-by: Ulf Hansson mach-ux500: mmc: Fix for MMC working on u5500 and DMA support. Configuring DMA support on u5500 for MMC. ST Ericsson ID:WP257117 Signed-off-by: seshagh mach-ux500: sdi0: Enable 8 bit mode for MMC Enable 8-bit mode in platform data for EMMC driver in U4500. ST-Ericsson Id: ER 332947 Signed-off-by: Naveen Kumar Gaddipati mach-ux500: Use pm_runtime for sdi[x] for GPIOs + board-mop500-pins.c alignment Setup GPIO pins for sdi0, sdi1, sdi2 and sdi4 to be controlled by pm_runtime. GPIO pins for card detect and levelshifter is not included. ST-Ericsson ID: ER334765 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Ulf Hansson Signed-off-by: Philippe Langlais mach-ux500:sdi3: Added platform data for SDIO support Added platform data for SDIO support on U4500 board Needs rework for power saving part (MMC_CAP_DISABLE, wakeup_handler not present) ST-Ericsson Id: AP 337858 Signed-off-by: Naveen Kumar Gaddipati mach-ux500:u5500: Fixup vdd_handler for sdi1 Levelshifter was not enabled at POWER_ON, this is corrected. Signed-off-by: Ulf Hansson mach-ux500:u5500: set vsel of levelshifter to 2.9V configure the vsel to get 2.9V output for the levelshifter ST-Ericsson ID: ER 339616 Signed-off-by: Hanumath Prasad mach-ux500: Fix build problems temporary Very temporary fixes to make it build until we have proper fixes for this issues. Signed-off-by: Robert Marklund mach-ux500: mmc: Fix board merge & cleanup Signed-off-by: Philippe Langlais mach-ux500: Suppress ../../ for external include Signed-off-by: Philippe Langlais ARM: ux500: sdi: Remove u8500 v1 support ST-Ericsson Linux next: Not tested, ask SSM for ER ST-Ericsson ID: 342987 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Jonas Aaberg mach-ux500: Solved rebase conflict for sd/mmc U8500: From commit 69a86710 by Philippe Langlais, corrections done for MCI defines. From commit 2e7233f2 by Philippe Langlais, corrections done for SDIO_CMD53 workaround. U5500: From commit be4f9997 by Philippe Langlais, corrections done for SDIO_CMD53 workaround. u5500:SD/MMC migrate to kernel3.0 SD/MMC platform data migrate to kernel3.0 ST-Ericsson ID: 352334 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: NA Signed-off-by: Naveen Kumar Gaddipati mach-ux500:u8500: Clean up sdi[n] configurations This makes an inital clean up of the mmc/sd/sdio devices for kernel 3.0. Signed-off-by: Ulf Hansson Signed-off-by: Stefan Nilsson XK mach-ux500:u5500: Clean up sdi[n] configurations This makes an initial clean up of the mmc/sd/sdio devices for kernel 3.0. Signed-off-by: Stefan Nilsson XK ux500: u8500: Change DMA configuration for SD-card This patch is a mixture of two earlier patches: By Sebastian Rasmussen: ux500: Use other logical DMA channel for SD-card Previously PoP eMMC SDI has been in the same event group as SD-card SDI, which means that they have been sharing the same physical DMA channels. Moreover one of those channels was reserved for other purposes, causing transfers for each interface to compete for the same physical DMA channel. In addition there appears to be a HW issue that causes data to be lost in the middle of DMA transfers, which ended up with DMAC and MMCI not agreeing on how much data was still to be transferred, thereby resulting in a hang and eventually in a crash. This patch moves SD-card DMA transfers to another event group which means that it no longer competes for the same physical DMA channel. This results in that no data is lost in the transfers and no hang is observed. By Rabin Vincent: ux500: mop500: force SD/MMC and MSP2 TX onto different channels SD/MMC with event line 1 can be in channel 0 or 1. Force both Rx and Tx onto channel 0. Both will anyway not used at the same time, and with this change channel 1 will be available for MSP2 Tx without sharing. Signed-off-by: Ulf Hansson mach-ux500: Re-introduce specific mmc init for Snowball boards Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/board-mop500-sdi.c | 116 +++++++++++----- arch/arm/mach-ux500/board-u5500-sdi.c | 244 +++++++++++++++++++++++++++++---- 2 files changed, 303 insertions(+), 57 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index d0cb9e5eb87..8e78cca252a 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -11,15 +11,16 @@ #include #include #include +#include #include #include #include #include +#include #include "devices-db8500.h" #include "board-mop500.h" -#include "ste-dma40-db8500.h" /* * SDI 0 (MicroSD slot) @@ -52,6 +53,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); @@ -60,26 +62,30 @@ static u32 mop500_sdi0_vdd_handler(struct device *dev, unsigned int vdd, } return MCI_FBCLKEN | MCI_CMDDIREN | MCI_DATA0DIREN | - MCI_DATA2DIREN | MCI_DATA31DIREN; + MCI_DATA2DIREN; } #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 @@ -98,29 +104,72 @@ static struct mmci_platform_data mop500_sdi0_data = { #endif }; -static void sdi0_configure(void) +/* + * 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_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 +177,12 @@ 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 CONFIG_STE_DMA40 struct stedma40_chan_cfg mop500_sdi2_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, @@ -158,7 +206,8 @@ static struct stedma40_chan_cfg mop500_sdi2_dma_cfg_tx = { 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 CONFIG_STE_DMA40 @@ -195,8 +244,9 @@ static struct stedma40_chan_cfg mop500_sdi4_dma_cfg_tx = { 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 CONFIG_STE_DMA40 @@ -211,11 +261,13 @@ void __init mop500_sdi_init(void) u32 periphid = 0; /* v2 has a new version of this block that need to be forced */ - if (cpu_is_u8500v2()) + if (cpu_is_u8500v20_or_later()) { periphid = 0x10480180; - /* PoP:ed eMMC on top of DB8500 v1.0 has problems with high speed */ - if (!cpu_is_u8500v10()) + + /* PoP:ed eMMC on DB8500 v1.0 has problems with high speed */ mop500_sdi2_data.capabilities |= MMC_CAP_MMC_HIGHSPEED; + } + /* sdi2 on snowball is in ATL_B mode for FSMC (LAN) */ if (!machine_is_snowball()) db8500_add_sdi2(&mop500_sdi2_data, periphid); @@ -234,11 +286,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..dec306ea705 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 +#include +#include #include #include -#include +#include +#include -#include -#include +#include #include +#include +#include +#include -#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,214 @@ 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) + */ + +/* 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) + +static u32 u5500_sdi1_vdd_handler(struct device *dev, unsigned int vdd, + unsigned char power_mode) +{ + switch (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 MCI_FBCLKEN | MCI_CMDDIREN | MCI_DATA0DIREN | + MCI_DATA2DIREN; +} + +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, +}; + +static struct mmci_platform_data u5500_sdi1_data = { + .vdd_handler = u5500_sdi1_vdd_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 CONFIG_STE_DMA40 + .dma_filter = stedma40_filter, + .dma_rx_param = &sdi1_dma_cfg_rx, + .dma_tx_param = &sdi1_dma_cfg_tx, +#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); } -- cgit v1.2.3 From 0bda00d85ecceb2ad264fad81175e8bddce0c65d Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Thu, 6 May 2010 09:10:53 +0200 Subject: fix cache coherence issues with bounce buffers [Rabin VINCENT] This is for I$-D$ coherence issues when bounce buffers are used for the MMC driver and code is executed from a file system on eMMC. IMO a better fix is to either change flush_kernel_dcache_page() to flush the D$ even on VIPT non-aliasing caches or to replace the flush_kernel_dcache_page() in lib/scatterlist.c with flush_dcache_page(). Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Lee Jones --- fs/mpage.c | 2 ++ 1 file changed, 2 insertions(+) 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); -- cgit v1.2.3 From ac0c95c534db4e9c34323190f17f333f66e8d236 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Fri, 1 Jul 2011 18:55:22 +0200 Subject: mmc: core: add non-blocking mmc request function Previously there has only been one function mmc_wait_for_req() to start and wait for a request. This patch adds: * mmc_start_req() - starts a request wihtout waiting If there is on ongoing request wait for completion of that request and start the new one and return. Does not wait for the new command to complete. This patch also adds new function members in struct mmc_host_ops only called from core.c: * pre_req - asks the host driver to prepare for the next job * post_req - asks the host driver to clean up after a completed job The intention is to use pre_req() and post_req() to do cache maintenance while a request is active. pre_req() can be called while a request is active to minimize latency to start next job. post_req() can be used after the next job is started to clean up the request. This will minimize the host driver request end latency. post_req() is typically used before ending the block request and handing over the buffer to the block layer. Add a host-private member in mmc_data to be used by pre_req to mark the data. The host driver will then check this mark to see if the data is prepared or not. Signed-off-by: Per Forlin Acked-by: Kyungmin Park Acked-by: Arnd Bergmann Reviewed-by: Venkatraman S Tested-by: Sourav Poddar Tested-by: Linus Walleij Signed-off-by: Chris Ball Change-Id: I1cd15d44a1c401d4e461632ccd3185f444fc96bc Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30672 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- include/linux/mmc/host.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 1d09562ccf7..4f31b4fc8d4 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -302,6 +302,15 @@ struct mmc_host { struct mmc_async_req *areq; /* active async req */ +#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; }; -- cgit v1.2.3 From 7c51dca4f0b4494ebb983837bc4b9e825152e469 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 May 2011 11:42:18 +0200 Subject: mmc: Fix hangs related to insert/remove of cards During a rescan operation mmc_attach(sd|mmc|sdio) functions are called. The error handling in these function can trigger a detach of the bus, which also meant a power off. This is not notified by the rescan operation which then continues to the next attach function. If a power off has been done, the framework must never send any new commands to the host driver, without first doing a new power up. This will most likely trigger any host driver to hang. Moving power off out of detach and instead handle power off separately when it is actually needed, solves the issue. ST-Ericsson Linux next: - ST-Ericsson ID: 339637 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Id808dc84625a953f4469855ad56e8d3b46b5434d Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30814 --- drivers/mmc/core/core.c | 10 +++++----- drivers/mmc/core/core.h | 1 + drivers/mmc/core/mmc.c | 1 + drivers/mmc/core/sd.c | 1 + drivers/mmc/core/sdio.c | 1 + 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index b27b94078c2..d637982b035 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1151,7 +1151,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); @@ -1241,8 +1241,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 +1258,6 @@ void mmc_detach_bus(struct mmc_host *host) spin_unlock_irqrestore(&host->lock, flags); - mmc_power_off(host); - mmc_bus_put(host); } @@ -1845,6 +1842,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 +1972,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 +2060,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/mmc.c b/drivers/mmc/core/mmc.c index 5700b1cbdfe..59b5faa0072 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -891,6 +891,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); } } diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 0370e03e314..4c281a4bf05 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1043,6 +1043,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/sdio.c b/drivers/mmc/core/sdio.c index 262fff01917..ac492ac974e 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -597,6 +597,7 @@ out: mmc_claim_host(host); mmc_detach_bus(host); + mmc_power_off(host); mmc_release_host(host); } } -- cgit v1.2.3 From b0a922c26c20bda43d0953e1e07bc45ad3779a01 Mon Sep 17 00:00:00 2001 From: Stefan Nilsson XK Date: Sat, 20 Nov 2010 11:07:28 +0100 Subject: MMCI: Fix bug in PIO read for small SDIO packets Corrects a bug in MMCI host driver which silently causes small reads (< 4 bytes as only used in SDIO) from PL-18X to fail. Change-Id: I31428aa14cdd7f756f8ff776e87ef65c02de3bb3 Signed-off-by: Stefan Nilsson XK Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/9021 Reviewed-by: Jonas ABERG Reviewed-by: Henrik CARLING Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/9191 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30927 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/host/mmci.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 56e9a416826..baf93a82c56 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -776,7 +776,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; -- cgit v1.2.3 From 5a54316595a6b21b1478bdcac27ceecab05c2bc7 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 14 Sep 2011 17:13:20 +0200 Subject: mmci: Prepare for sdio before setting up dma job Makes it possible to use dma with sdio. Earlier pio mode was only supported. Change-Id: I174b0e2a8d12327db5bb086d11912c8247ce7a5e Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30992 --- drivers/mmc/host/mmci.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index baf93a82c56..cea09e265a2 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -602,6 +602,11 @@ 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)) + datactrl |= MCI_ST_DPSM_SDIOEN; + /* * Attempt to use DMA operation mode, if this * should fail, fall back to PIO mode @@ -630,11 +635,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); -- cgit v1.2.3 From e8003b7b5d6d180f3c00f57d836b8108db7d3047 Mon Sep 17 00:00:00 2001 From: Stefan Nilsson XK Date: Thu, 25 Nov 2010 15:38:02 +0100 Subject: MMCI: Fix incorrect handling of HW FC for SDIO For SDIO writes smaller than or equal to 8 bytes (only SDIO case), hardware flow control was turned off, but was never turned on again which meant that if a large read request followed, SDIO would randomly give buffer overrun errors since there was no flow control. Since HW flow control is not really needed for anything that fits in the HW FIFO of PL18X, HW flow control is now disabled for any write operation that is smaller than or equal to the FIFO size. ST-Ericsson ID: ER280840 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I9c47315dbc4b45011d89b1e58605056ba5441db1 Signed-off-by: Stefan Nilsson XK Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/9447 Reviewed-by: Ulf HANSSON Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/10931 Reviewed-by: QATOOLS Reviewed-by: Jonas ABERG Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30928 --- drivers/mmc/host/mmci.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index cea09e265a2..fe783a470e9 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -604,9 +604,32 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) /* The ST Micro variants has a special bit to enable SDIO */ if (variant->sdio && host->mmc->card) - if (mmc_card_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 @@ -821,23 +844,6 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem variant->fifosize : variant->fifohalfsize; 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 -- cgit v1.2.3 From 079937e0d1ef9371a7c090837ac19533c7b50bbb Mon Sep 17 00:00:00 2001 From: Stefan Nilsson XK Date: Wed, 9 Feb 2011 17:06:37 +0100 Subject: MMCI: Add support for non-power-of-two block sizes Adds support for any block size over SDIO. ST-Ericsson ID: AP272999 Change-Id: I5a2654360a2d38e6ab929b099c2bc04668ae9e85 Signed-off-by: Stefan Nilsson XK Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/15091 Reviewed-by: Jonas ABERG Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30929 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/host/mmci.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index fe783a470e9..eb655a151bd 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -52,6 +52,7 @@ 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 */ struct variant_data { unsigned int clkreg; @@ -62,6 +63,7 @@ struct variant_data { bool sdio; bool st_clkdiv; bool blksz_datactrl16; + bool non_power_of_2_blksize; }; static struct variant_data variant_arm = { @@ -103,6 +105,7 @@ static struct variant_data variant_ux500v2 = { .sdio = true, .st_clkdiv = true, .blksz_datactrl16 = true, + .non_power_of_2_blksize = true, }; /* @@ -592,7 +595,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); @@ -995,11 +997,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; -- cgit v1.2.3 From c4f5d0e11fb5e13d195f21f352769f5fd5d77488 Mon Sep 17 00:00:00 2001 From: Stefan Nilsson XK Date: Tue, 13 Sep 2011 09:18:35 +0200 Subject: mmc: core: Set correct bus mode before card init Earlier all cards where initiated with bus mode set as OPENDRAIN, and then later switched to PUSHPULL. According to the MMC/SD/SDIO specifications only MMC cards use OPENDRAIN during init. For both SD and SDIO the bus mode shall be PUSHPULL before attempting to init the card. The consequence of having incorrect bus mode can lead to not being able to detect the card. Therefore the default behavior have now been changed to PUSHPULL in mmc_power_up, and will only be temporarily switched when trying to attach or init a MMC card. Change-Id: Ied79604d7b3b9fcaf13fbc5566f9cdd109b3cc29 Signed-off-by: Stefan Nilsson XK Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30930 Reviewed-by: Ulf HANSSON --- drivers/mmc/core/core.c | 8 +++----- drivers/mmc/core/mmc.c | 8 ++++++++ drivers/mmc/core/sd.c | 2 -- drivers/mmc/core/sdio.c | 2 -- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d637982b035..61be204f55f 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1119,13 +1119,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; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 59b5faa0072..e746b672246 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -548,6 +548,10 @@ 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 @@ -1017,6 +1021,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/sd.c b/drivers/mmc/core/sd.c index 4c281a4bf05..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) { diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index ac492ac974e..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); } /* -- cgit v1.2.3 From 23fe82f93e88465e1ae721593f1d287db2e04dc5 Mon Sep 17 00:00:00 2001 From: Stefan Nilsson XK Date: Wed, 9 Mar 2011 10:58:13 +0100 Subject: mmc: sdio: Workaround for dev with broken CMD53 Adds a quirk which can be turned on for SDIO devices that do not support 512 byte requests in byte mode during CMD53. These requests will always be sent in block mode instead. This patch also enables this quirk for ST-Ericsson CW1200 WLAN device. Change-Id: Id31787fcbfeb0b97ba2e8c583e9363a7c08811d2 Signed-off-by: Stefan Nilsson XK Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30931 Reviewed-by: Ulf HANSSON --- drivers/mmc/core/quirks.c | 11 +++++++++++ drivers/mmc/core/sdio_ops.c | 7 +++++-- include/linux/mmc/card.h | 6 ++++++ 3 files changed, 22 insertions(+), 2 deletions(-) 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/sdio_ops.c b/drivers/mmc/core/sdio_ops.c index f087d876c57..bcef326639f 100644 --- a/drivers/mmc/core/sdio_ops.c +++ b/drivers/mmc/core/sdio_ops.c @@ -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/include/linux/mmc/card.h b/include/linux/mmc/card.h index b460fc2af8a..5e4ffc57784 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -188,6 +188,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 */ @@ -377,6 +378,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)) -- cgit v1.2.3 From 4442fc99572e1cb906ed8757e09d15cef82f5b09 Mon Sep 17 00:00:00 2001 From: Sebastian Rasmussen Date: Fri, 14 Jan 2011 19:08:24 +0100 Subject: MMCI: Put power register deviations in variant data. Use variant data to store hardware controller deviations concerning power registers to improve readability of the code. ST-Ericsson ID: ER282562 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I6285df49bc4dceb443df5af618ee980b0e62f59f Signed-off-by: Sebastian Rasmussen Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/12543 Reviewed-by: QATOOLS Reviewed-by: Linus WALLEIJ Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31644 --- drivers/mmc/host/mmci.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index eb655a151bd..3696a683669 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -53,6 +53,7 @@ static unsigned int fmax = 515633; * @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 */ struct variant_data { unsigned int clkreg; @@ -64,18 +65,21 @@ struct variant_data { bool st_clkdiv; bool blksz_datactrl16; bool non_power_of_2_blksize; + unsigned int pwrreg_powerup; }; 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 = { @@ -84,6 +88,7 @@ static struct variant_data variant_u300 = { .clkreg_enable = MCI_ST_U300_HWFCEN, .datalength_bits = 16, .sdio = true, + .pwrreg_powerup = MCI_PWR_ON, }; static struct variant_data variant_ux500 = { @@ -94,6 +99,7 @@ static struct variant_data variant_ux500 = { .datalength_bits = 24, .sdio = true, .st_clkdiv = true, + .pwrreg_powerup = MCI_PWR_ON, }; static struct variant_data variant_ux500v2 = { @@ -106,6 +112,7 @@ static struct variant_data variant_ux500v2 = { .st_clkdiv = true, .blksz_datactrl16 = true, .non_power_of_2_blksize = true, + .pwrreg_powerup = MCI_PWR_ON, }; /* @@ -1030,6 +1037,7 @@ 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; @@ -1056,11 +1064,15 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 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; -- cgit v1.2.3 From 38d2fe845632c6f836e1a539532bca575f3c8848 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 22 Sep 2011 09:32:31 +0200 Subject: mmc: mmci: Provide option to configure bus signal direction The ST Micro variant supports bus signal direction indication. A new member in the variant struct is added for this. Moreover the actual signal direction configuration is board specific, thus the amba mmci platform data is extended with a new member to be able provide mmci with these specific board configurations. This patch is based upon a patch from Sebastian Rasmussen. Change-Id: I430ff6469ec1546b0ee438c1629c6a07fdade5f7 Signed-off-by: Ulf Hansson Signed-off-by: Sebastian Rasmussen Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31882 --- drivers/mmc/host/mmci.c | 21 +++++++++++++++++++++ drivers/mmc/host/mmci.h | 10 ---------- include/linux/amba/mmci.h | 17 +++++++++++++++++ 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 3696a683669..470f035e087 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -54,6 +54,7 @@ static unsigned int fmax = 515633; * @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; @@ -66,6 +67,7 @@ struct variant_data { bool blksz_datactrl16; bool non_power_of_2_blksize; unsigned int pwrreg_powerup; + bool signal_direction; }; static struct variant_data variant_arm = { @@ -89,6 +91,7 @@ static struct variant_data variant_u300 = { .datalength_bits = 16, .sdio = true, .pwrreg_powerup = MCI_PWR_ON, + .signal_direction = true, }; static struct variant_data variant_ux500 = { @@ -100,6 +103,7 @@ static struct variant_data variant_ux500 = { .sdio = true, .st_clkdiv = true, .pwrreg_powerup = MCI_PWR_ON, + .signal_direction = true, }; static struct variant_data variant_ux500v2 = { @@ -113,6 +117,7 @@ static struct variant_data variant_ux500v2 = { .blksz_datactrl16 = true, .non_power_of_2_blksize = true, .pwrreg_powerup = MCI_PWR_ON, + .signal_direction = true, }; /* @@ -1078,6 +1083,22 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 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; diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 79e4143ab9d..5600755edfa 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) diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index 21114810c7c..3d127b50c67 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -6,6 +6,20 @@ #include +/* + * 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; @@ -30,6 +44,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 @@ -52,6 +68,7 @@ struct mmci_platform_data { 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; -- cgit v1.2.3 From e9e053fe49d95652259ca755c5106782330a37fa Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 22 Sep 2011 13:15:27 +0200 Subject: mmc: mmci: Change vdd_handler to a generic ios handler The purpose of the vdd_handler does not make sense. We remove it and use a more generic approach instead. A new "ios" handler is thus added, the purpose of which e.g. can be to control GPIO pins to a levelshifter. Previously the vdd_handler was also used for making additional changes to the MMCIPOWER register bits. This option is now removed. This patch is based upon a patch from Sebastian Rasmussen. Change-Id: I250c7d243946acd9cc9da2d4d18394db1256cd27 Signed-off-by: Ulf Hansson Signed-off-by: Sebastian Rasmussen Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31883 --- arch/arm/mach-ux500/board-mop500-sdi.c | 21 ++++++++------------- arch/arm/mach-ux500/board-u5500-sdi.c | 17 ++++------------- drivers/mmc/host/mmci.c | 8 ++++---- include/linux/amba/mmci.h | 6 +++--- 4 files changed, 19 insertions(+), 33 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 8e78cca252a..48c4ad7ca53 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -26,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: /* @@ -61,8 +53,7 @@ static u32 mop500_sdi0_vdd_handler(struct device *dev, unsigned int vdd, break; } - return MCI_FBCLKEN | MCI_CMDDIREN | MCI_DATA0DIREN | - MCI_DATA2DIREN; + return 0; } #ifdef CONFIG_STE_DMA40 @@ -90,13 +81,17 @@ static struct stedma40_chan_cfg mop500_sdi0_dma_cfg_tx = { #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 CONFIG_STE_DMA40 .dma_filter = stedma40_filter, .dma_rx_param = &mop500_sdi0_dma_cfg_rx, diff --git a/arch/arm/mach-ux500/board-u5500-sdi.c b/arch/arm/mach-ux500/board-u5500-sdi.c index dec306ea705..b2801f76fa1 100644 --- a/arch/arm/mach-ux500/board-u5500-sdi.c +++ b/arch/arm/mach-ux500/board-u5500-sdi.c @@ -64,17 +64,9 @@ static struct mmci_platform_data u5500_sdi0_data = { * SDI 1 (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) - -static u32 u5500_sdi1_vdd_handler(struct device *dev, unsigned int vdd, - unsigned char power_mode) +static int u5500_sdi1_ios_handler(struct device *dev, struct mmc_ios *ios) { - switch (power_mode) { + switch (ios->power_mode) { case MMC_POWER_UP: case MMC_POWER_ON: /* @@ -93,8 +85,7 @@ static u32 u5500_sdi1_vdd_handler(struct device *dev, unsigned int vdd, break; } - return MCI_FBCLKEN | MCI_CMDDIREN | MCI_DATA0DIREN | - MCI_DATA2DIREN; + return 0; } static struct stedma40_chan_cfg sdi1_dma_cfg_rx = { @@ -116,7 +107,7 @@ static struct stedma40_chan_cfg sdi1_dma_cfg_tx = { }; static struct mmci_platform_data u5500_sdi1_data = { - .vdd_handler = u5500_sdi1_vdd_handler, + .ios_handler = u5500_sdi1_ios_handler, .ocr_mask = MMC_VDD_29_30, .f_max = 50000000, .capabilities = MMC_CAP_4_BIT_DATA | diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 470f035e087..baa7883c49e 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1047,6 +1047,10 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 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) @@ -1066,10 +1070,6 @@ 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 Micro variant doesn't have the PL180s MCI_PWR_UP * and instead uses MCI_PWR_ON so apply whatever value is diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index 3d127b50c67..23536c95fb6 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -32,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. @@ -61,8 +62,7 @@ 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; -- cgit v1.2.3 From 94368056d4b34a0ff761ec4968864c670e6afb7c Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Mon, 29 Aug 2011 15:35:59 +0200 Subject: mmc: mmci: simplify err check in mmci_post_request The error condition indicates that mmci_post_request() should cleanup after the mmci_pre_request(). In this case the resources allocated by device_prep_slave_sg() are freed by calling dmaengine_terminate_all(). dma_unmap_sg() should always be performed if the host_cookie is set. Signed-off-by: Per Forlin Acked-by: Linus Walleij Signed-off-by: Chris Ball Change-Id: I04172ae0d6fc7089d126f7f2d7687c69fa5646f7 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31975 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/host/mmci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index baa7883c49e..13222bd32a1 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -544,7 +544,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; -- cgit v1.2.3 From b8f37075db9419b68f0d9e26b645f047d26f7dd6 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 15 Feb 2011 11:56:51 +0100 Subject: MMCI: Increase max_segs from 16 to 128 A significant increase (10-20%) in performance throughput for USB mass storage is the reason for incrementing the value. By some reason the USB driver allocates buffers which requires a scattergather list to contain a lot more than 16 elements to get optimal performance. This change sets the maximum elements to 128. Tests with large reads and large writes (100 MiB) show that the throughput increase is significant for write (10% for this test) but not for read. Tests are run on a Linux host with ext4 FS on the gadget mass storage device. The sg-len still exceeds 16 for the read tests but the performance gain is low or nothing. Change-Id: I5f23a470f6d979905f6952b947411cf848f667bb Signed-off-by: Ulf Hansson Signed-off-by: Per Forlin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/16071 Reviewed-by: Sebastian RASMUSSEN Reviewed-by: Stefan NILSSON9 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32160 --- drivers/mmc/host/mmci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 5600755edfa..89eb2e3556d 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -150,7 +150,7 @@ (MCI_RXFIFOHALFFULLMASK | MCI_RXDATAAVLBLMASK | \ MCI_TXFIFOHALFEMPTYMASK) -#define NR_SG 16 +#define NR_SG 128 struct clk; struct variant_data; -- cgit v1.2.3 From 4ed8a74f7343a90ddac4c905a4a964177713fca2 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 30 Sep 2011 16:10:36 +0200 Subject: mmc: mmci: Added initial support for pm_runtime Use pm_runtime to get and put our GPIO pins. Especially needed for making suspend/resume functionality working. Change-Id: Ie62c08f7e274d07d6be83075c1cbf6a58d4beab4 Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32762 Reviewed-by: Sebastian RASMUSSEN --- drivers/mmc/host/mmci.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 13222bd32a1..393ede681aa 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1381,6 +1382,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, @@ -1473,6 +1478,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; @@ -1486,6 +1494,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); -- cgit v1.2.3 From e8fbe80ff5c3175504bcb5f1658f0ee4dfe12772 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Fri, 19 Aug 2011 14:52:37 +0200 Subject: mmc: core: add random fault injection This adds support to inject data errors after a completed host transfer. The mmc core will return error even though the host transfer is successful. This simple fault injection proved to be very useful to test the non-blocking error handling in the mmc_blk_issue_rw_rq(). Random faults can also test how the host driver handles pre_req() and post_req() in case of errors. Signed-off-by: Per Forlin Acked-by: Akinobu Mita Reviewed-by: Linus Walleij Signed-off-by: Chris Ball Change-Id: Id26447a00f055c7cde48ff941188f18bf8eb9cad Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33379 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/core/core.c | 41 +++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/debugfs.c | 25 +++++++++++++++++++++++++ include/linux/mmc/host.h | 5 +++++ lib/Kconfig.debug | 11 +++++++++++ 4 files changed, 82 insertions(+) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 61be204f55f..284e48f09a1 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include @@ -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 @@ -109,6 +148,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) cmd->error = 0; host->ops->request(host, 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", diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 998797ed67a..5acd707699c 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -158,6 +159,23 @@ static int mmc_clock_opt_set(void *data, u64 val) return 0; } +#ifdef CONFIG_FAIL_MMC_REQUEST + +static DECLARE_FAULT_ATTR(fail_mmc_request); + +#ifdef KERNEL +/* + * Internal function. Pass the boot param fail_mmc_request to + * the setup fault injection attributes routine. + */ +static int __init setup_fail_mmc_request(char *str) +{ + return setup_fault_attr(&fail_mmc_request, str); +} +__setup("fail_mmc_request=", setup_fail_mmc_request); +#endif /* KERNEL */ +#endif /* CONFIG_FAIL_MMC_REQUEST */ + DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set, "%llu\n"); @@ -187,6 +205,13 @@ void mmc_add_host_debugfs(struct mmc_host *host) if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR), root, &host->clk_delay)) goto err_node; +#endif +#ifdef CONFIG_FAIL_MMC_REQUEST + host->fail_mmc_request = fail_mmc_request; + if (IS_ERR(fault_create_debugfs_attr("fail_mmc_request", + root, + &host->fail_mmc_request))) + goto err_node; #endif return; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 4f31b4fc8d4..f7bf464841d 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -302,6 +303,10 @@ 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; 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 -- cgit v1.2.3 From eb8dc1ecb0284c0139a22ace6eb750305b943c5c Mon Sep 17 00:00:00 2001 From: Venkatraman S Date: Tue, 23 Aug 2011 21:16:02 +0530 Subject: mmc: queue: declare mmc_alloc_sg as static Fix the sparse warning "drivers/mmc/card/queue.c:111:20: warning: symbol 'mmc_alloc_sg' was not declared. Should it be static?" Signed-off-by: Venkatraman S Signed-off-by: Chris Ball Change-Id: Ic349ddedf94f476c2766fff0901ba514d7f4aaca Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33406 Reviewed-by: QABUILD Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/card/queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; -- cgit v1.2.3 From a18dfe14c403bda90f4e652b3715342ee1c7c461 Mon Sep 17 00:00:00 2001 From: Venkatraman S Date: Thu, 25 Aug 2011 00:30:50 +0530 Subject: mmc: fix integer assignments to pointer Fix the sparse warning output "warning: Using plain integer as NULL pointer" Signed-off-by: Venkatraman S Signed-off-by: Chris Ball Change-Id: Ia4a80ea0d9be1d7d943049df09ed7cb368ef2ea2 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33407 Reviewed-by: QABUILD Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/card/block.c | 4 ++-- drivers/mmc/core/core.c | 2 +- drivers/mmc/core/mmc_ops.c | 4 ++-- drivers/mmc/core/sd_ops.c | 8 ++++---- drivers/mmc/core/sdio_ops.c | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 4c1a648d00f..9b907263668 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -291,7 +291,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; @@ -466,7 +466,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; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 284e48f09a1..bb99db843b0 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -371,7 +371,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); 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/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_ops.c b/drivers/mmc/core/sdio_ops.c index bcef326639f..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; -- cgit v1.2.3 From ddd462a68eb15fc50b4b29178a784849044ff6b2 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Wed, 7 Sep 2011 10:22:09 +0100 Subject: mmc: core: add a short delay in mmc_power_off Stress-testing the runtime power management of libertas_sdio through a rmmod/insmod loop revealed that it is quite easy to cause an ETIMEDOUT failure in mmc_sdio_power_restore() leading to: libertas_sdio: probe of mmc1:0001:1 failed with error -16 Experimentation shows that a very short delay (100us) is needed in the power down path before the card can be successfully booted again. We know that this setup is lacking poweroff clamps on the card's power lines, but as only a short delay is needed, apply this unconditionally. Also bump up to 1ms sleep for extra legroom. Signed-off-by: Daniel Drake Signed-off-by: Chris Ball Change-Id: Iaaffdfa9cad62abe41113960da265ed7b3a528b7 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33408 Reviewed-by: QABUILD Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/core/core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index bb99db843b0..45ea968e7dd 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1212,6 +1212,13 @@ 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); } -- cgit v1.2.3 From 7c635b872e0e7c517790842b0424be3c2e98ba88 Mon Sep 17 00:00:00 2001 From: Balaji T K Date: Thu, 8 Sep 2011 22:08:39 +0530 Subject: mmc: core: Put eMMC in Sleep mode before suspend Put MMC to sleep if it supports SLEEP/AWAKE (CMD5) in the mmc suspend so that Vcc (NAND core) can be cut to minimize power consumption. eMMC put into SLEEP can respond to CMD0 or H/W reset or CMD5. Current implemention on resume from suspend relies on CMD0 in mmc_init_card to get out of SLEEP mode. Signed-off-by: Balaji T K Acked-by: Venkatraman S Reviewed-by: Subhash Jadavani Acked-by: Adrian Hunter Signed-off-by: Chris Ball Change-Id: I910a07fce263626e37421920c08e454412d8fb67 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33432 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/core/mmc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index e746b672246..b74e6f14b3a 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -557,6 +557,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * 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); @@ -905,16 +906,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; } /* -- cgit v1.2.3 From cf81d83e1cee2cdcac1fa92915f0cde77613556c Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Mon, 29 Aug 2011 15:35:58 +0200 Subject: mmc: core: clarify how to use post_req in case of errors The err condition in post_req() is set to undo a call made to pre_req() that hasn't been started yet. The err condition is not set if an MMC request returns an error. Signed-off-by: Per Forlin Acked-by: Linus Walleij Signed-off-by: Chris Ball Change-Id: Ie91c9868301c654ae27937b666089d82017b75f2 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33433 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/core/core.c | 6 ++++++ include/linux/mmc/host.h | 3 +++ 2 files changed, 9 insertions(+) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 45ea968e7dd..0b4c2ed22bc 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -320,8 +320,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; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index f7bf464841d..8fadfa76cac 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -109,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); -- cgit v1.2.3 From efc9dcbcfd10b72bb9892ea8c36cbcb78738ba77 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 29 Aug 2011 16:42:11 +0300 Subject: mmc: core: add eMMC hardware reset support eMMC's may have a hardware reset line. This patch provides a host controller operation to implement hardware reset and a function to reset and reinitialize the card. Also, for MMC, the reset is always performed before initialization. The host must set the new host capability MMC_CAP_HW_RESET to enable hardware reset. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball Change-Id: I324ad5b70ce1093cef1bccead045a2539be4cbfc Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33452 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/core/core.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/mmc.c | 4 ++- include/linux/mmc/card.h | 1 + include/linux/mmc/core.h | 3 ++ include/linux/mmc/host.h | 2 ++ include/linux/mmc/mmc.h | 4 +++ 6 files changed, 107 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 0b4c2ed22bc..da6bd95fa4b 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1776,6 +1776,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; @@ -1786,6 +1874,12 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) #endif 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 diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index b74e6f14b3a..7adc30da836 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -402,8 +402,10 @@ 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]; + } if (ext_csd[EXT_CSD_ERASED_MEM_CONT]) card->erased_byte = 0xFF; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 5e4ffc57784..eb3c7a6b094 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -50,6 +50,7 @@ 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 hs_max_dtr; 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 8fadfa76cac..9091f585997 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -151,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; @@ -233,6 +234,7 @@ 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 */ mmc_pm_flag_t pm_caps; /* supported pm features */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 5a794cb503e..ed8fca890ee 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -272,6 +272,7 @@ struct _mmc_csd { #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_ERASE_GROUP_DEF 175 /* R/W */ #define EXT_CSD_PART_CONFIG 179 /* R/W */ @@ -328,6 +329,9 @@ 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 */ + /* * MMC_SWITCH access modes */ -- cgit v1.2.3 From 6bcde9af38253aa55b355dfff437ad93a31508b9 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 29 Aug 2011 16:42:14 +0300 Subject: mmc: mmc-test: add eMMC hardware reset test MMC core provides a checking function that checks if the reset has happended. Add a test to use that function. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball Change-Id: Ib78b84c6c74beb333d6f017062e8d009d39bc8da Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33453 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/card/mmc_test.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) 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); -- cgit v1.2.3 From f3b02ac0282a807d64eb350c40e40bada0b690a9 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 29 Aug 2011 16:42:15 +0300 Subject: mmc: block: add eMMC hardware reset support For cards that support hardware reset (just eMMC), try a reset and retry before returning an I/O error. However this is not done for ECC errors and is never done twice for the same operation type (READ, WRITE, DISCARD, SECURE DISCARD) until that type of operation again succeeds. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball Change-Id: I1de8391f1781501a3a526714135dd2632acb26c5 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33454 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/card/block.c | 201 +++++++++++++++++++++++++++++++++-------------- drivers/mmc/core/core.c | 4 +- 2 files changed, 144 insertions(+), 61 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 9b907263668..66c7596c554 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 @@ -109,11 +114,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); @@ -454,7 +459,7 @@ static inline int mmc_blk_part_switch(struct mmc_card *card, card->ext_csd.part_time); if (ret) return ret; -} + } main_md->part_curr = md->part_type; return 0; @@ -616,7 +621,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 +646,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 +669,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 +683,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 +703,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 +755,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 +768,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 +785,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 +799,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 +823,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 +883,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 +899,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 +953,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 +1106,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 +1162,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 +1171,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 +1190,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 +1236,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 +1246,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) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index da6bd95fa4b..9698d8a2e16 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1529,7 +1529,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; } @@ -1544,7 +1544,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; } -- cgit v1.2.3 From 29b02391be494df6fe294f6ffc004c90257c3269 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Fri, 2 Sep 2011 16:06:08 +0800 Subject: mmc: core: add sd uhs string for mmc_ios_show This is a minor fix. It makes mmc_ios_show print proper string when the host's timing is one of the newly added UHS-I modes. Signed-off-by: Aaron Lu Signed-off-by: Chris Ball Change-Id: I89afac7124075410bd4efd4d22194e96841a4a4f Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33457 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/core/debugfs.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 5acd707699c..96c603b0526 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -114,6 +114,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; -- cgit v1.2.3 From 534c994a189b05fd55a9518b12d5fe3029e6f4e8 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2011 12:48:20 +0300 Subject: mmc: block: fix boot partition switch error path In the case of a switch error, do not update partition config as though the switch succeeded, and ensure blk_end_request is called on the failed request. Signed-off-by: Adrian Hunter Acked-by: Andrei Warkentin Signed-off-by: Chris Ball Change-Id: I7dce34a7ff16adbc71d146a4dfa9f8c4bc484aab Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33462 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/card/block.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 66c7596c554..d7eb2c08233 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -447,18 +447,23 @@ 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; @@ -1273,6 +1278,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; } -- cgit v1.2.3 From c908e45af1f601da234815065f12b0b7c73cc6b6 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Sat, 24 Sep 2011 12:12:30 -0400 Subject: mmc: core: ext_csd.raw_* used in comparison but never set f39b2dd9d ("mmc: core: Bus width testing needs to handle suspend/resume") added code to only compare read-only ext_csd fields in bus width testing code, yet it's comparing some fields that are never set. The affected fields are ext_csd.raw_erased_mem_count and ext_csd.raw_partition_support. Signed-off-by: Andrei Warkentin Acked-by: Philip Rakity Cc: Signed-off-by: Chris Ball Change-Id: Id526cb4100a55654b485963129ca1fe1f2ebfe6f Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33463 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/core/mmc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 7adc30da836..58f6e3013c1 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -359,6 +359,7 @@ 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 = @@ -407,6 +408,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) 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 -- cgit v1.2.3 From 270fba6e001b5ab27f26295827f23cda879ed6d0 Mon Sep 17 00:00:00 2001 From: Girish K S Date: Fri, 23 Sep 2011 20:41:47 +0530 Subject: mmc: core: eMMC 4.5 Power Class Selection Feature This patch adds the power class selection feature available for mmc versions 4.0 and above. During the enumeration stage before switching to the lower data bus, check if the power class is supported for the current bus width. If the power class is available then switch to the power class and use the higher data bus. If power class is not supported then switch to the lower data bus in a worst case. Signed-off-by: Girish K S Signed-off-by: Chris Ball Change-Id: I2248d2e770fbd349e68e21b2cbde36ee16ad8a6f Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33628 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/core/mmc.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mmc/mmc.h | 14 ++++++++ 2 files changed, 110 insertions(+) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 58f6e3013c1..c632b1faf70 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -533,6 +533,86 @@ static struct device_type mmc_type = { .groups = mmc_attr_groups, }; +/* + * 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. * @@ -789,6 +869,14 @@ 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], @@ -812,6 +900,14 @@ 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], diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index ed8fca890ee..50af22730c7 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -279,10 +279,15 @@ struct _mmc_csd { #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 */ @@ -294,6 +299,11 @@ 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 */ /* * EXT_CSD field definitions @@ -332,6 +342,10 @@ struct _mmc_csd { #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 */ -- cgit v1.2.3 From 9997c3c17358458038c24a990941e9a40c167e45 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2011 12:48:21 +0300 Subject: mmc: block: support no access to boot partitions Intel Medfield platform blocks access to eMMC boot partitions which results in switch errors. Since there is no access, mmcboot0/1 devices should not be created. Add a host capability to reflect that. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball Change-Id: Idd40bbd8f8809afcabb385c2492680ff8182877e Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33630 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/card/block.c | 2 +- drivers/mmc/host/sdhci-pci.c | 3 +++ include/linux/mmc/host.h | 10 ++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index d7eb2c08233..aa66d3f3abb 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1480,7 +1480,7 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md) if (!mmc_card_mmc(card)) return 0; - if (card->ext_csd.boot_size) { + if (card->ext_csd.boot_size && mmc_boot_partition_access(card->host)) { ret = mmc_blk_alloc_part(card, md, EXT_CSD_PART_CONFIG_ACC_BOOT0, card->ext_csd.boot_size >> 9, true, 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/include/linux/mmc/host.h b/include/linux/mmc/host.h index 9091f585997..79117a252f0 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -236,6 +236,10 @@ struct mmc_host { #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 */ #ifdef CONFIG_MMC_CLKGATE @@ -413,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 */ -- cgit v1.2.3 From 5b24e94ea08fa649c21a3ebd8d203a2fc61c2936 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 6 Oct 2011 23:41:38 +0900 Subject: mmc: core: general purpose MMC partition support. It allows gerneral purpose partitions in MMC Device. And I try to simply make mmc_blk_alloc_parts using mmc_part structure suggested by Andrei Warkentin. After patching, we see general purpose partitions like this: > cat /proc/partitions 179 0 847872 mmcblk0 179 192 4096 mmcblk0gp3 179 160 4096 mmcblk0gp2 179 128 4096 mmcblk0gp1 179 96 1052672 mmcblk0gp0 179 64 1024 mmcblk0boot1 179 32 1024 mmcblk0boot0 Signed-off-by: Namjae Jeon Acked-by: Andrei Warkentin Signed-off-by: Chris Ball Change-Id: I245c7e970a99fb17d0d9aedccc52973d74f43f51 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33702 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/card/block.c | 31 ++++++++++++++++------------- drivers/mmc/core/mmc.c | 52 ++++++++++++++++++++++++++++++++++++++++++++---- include/linux/mmc/card.h | 34 ++++++++++++++++++++++++++++++- include/linux/mmc/mmc.h | 5 ++++- 4 files changed, 102 insertions(+), 20 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index aa66d3f3abb..2cf1ba6db91 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1473,26 +1473,29 @@ 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 && mmc_boot_partition_access(card->host)) { - 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); + if (ret) + return ret; + } } return ret; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index c632b1faf70..2a4c9a4d3c0 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); @@ -340,7 +342,14 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) * 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); + } + } } card->ext_csd.raw_hc_erase_gap_size = @@ -362,9 +371,9 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) 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; @@ -393,6 +402,41 @@ 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); + } + } card->ext_csd.sec_trim_mult = ext_csd[EXT_CSD_SEC_TRIM_MULT]; card->ext_csd.sec_erase_mult = diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index eb3c7a6b094..060b96e95c2 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -12,6 +12,7 @@ #include #include +#include struct mmc_cid { unsigned int manfid; @@ -64,7 +65,6 @@ 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 */ u8 raw_partition_support; /* 160 */ u8 raw_erased_mem_count; /* 181 */ u8 raw_ext_csd_structure; /* 194 */ @@ -158,6 +158,23 @@ 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 + +/* + * 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 */ +}; + /* * MMC device */ @@ -218,8 +235,23 @@ 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) +{ + 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->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. diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 50af22730c7..ea558eb44c7 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -270,6 +270,7 @@ 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 */ @@ -313,7 +314,9 @@ struct _mmc_csd { #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) -- cgit v1.2.3 From 0c353a46c56de9b9aa15dc7a6a328e74ae848a9b Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Tue, 13 Sep 2011 23:03:29 +0200 Subject: mmc: add module param to set fault injection attributes Replace setup("fail_mmc_request") and faulty "ifdef KERNEL" with a simple module_param(). The module param mmc_core.fail_request may be used to set the fault injection attributes during boot time or module load time. Signed-off-by: Per Forlin Reviewed-by: Akinobu Mita Signed-off-by: Chris Ball Change-Id: I64649cf0714d3bcebe114b922ce87076123dee99 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33704 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/core/debugfs.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 96c603b0526..da69aac4cc8 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -20,6 +20,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) { @@ -168,23 +176,6 @@ static int mmc_clock_opt_set(void *data, u64 val) return 0; } -#ifdef CONFIG_FAIL_MMC_REQUEST - -static DECLARE_FAULT_ATTR(fail_mmc_request); - -#ifdef KERNEL -/* - * Internal function. Pass the boot param fail_mmc_request to - * the setup fault injection attributes routine. - */ -static int __init setup_fail_mmc_request(char *str) -{ - return setup_fault_attr(&fail_mmc_request, str); -} -__setup("fail_mmc_request=", setup_fail_mmc_request); -#endif /* KERNEL */ -#endif /* CONFIG_FAIL_MMC_REQUEST */ - DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set, "%llu\n"); @@ -216,7 +207,9 @@ void mmc_add_host_debugfs(struct mmc_host *host) goto err_node; #endif #ifdef CONFIG_FAIL_MMC_REQUEST - host->fail_mmc_request = 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))) -- cgit v1.2.3 From fb463670d8a09094928197b50f9417368c193c58 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Sun, 9 Oct 2011 10:35:16 -0400 Subject: mmc: using module_param requires the inclusion of moduleparam.h Commit "mmc: add module param to set fault injection attributes" adds a module_param to this file. But it is relying on the old implicit "module.h is everywhere" behaviour, and without the explicit include of moduleparam.h, the pending module.h split up produces this error: core/debugfs.c:28:35: error: expected ')' before numeric constant Signed-off-by: Stephen Rothwell Signed-off-by: Paul Gortmaker Signed-off-by: Chris Ball Change-Id: I79d51d3856b2283d167c1071dd49e4fc5aa4edcf Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33706 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/core/debugfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index da69aac4cc8..6045ea46936 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -7,6 +7,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include #include #include #include -- cgit v1.2.3 From 33ff2dc228fa24871941582821e6f87046943006 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 3 Oct 2011 15:33:33 +0300 Subject: mmc: core: move ->request() call from atomic context mmc_request_done() is sometimes called from interrupt or other atomic context. Mostly all mmc_request_done() does is complete(), however it contains code to retry on error, which uses ->request(). As the error path is certainly not performance critical, this may be moved to the waiting function mmc_wait_for_req_done(). This allows ->request() to use runtime PM get_sync() and guarantee it is never in an atomic context. Signed-off-by: Adrian Hunter Acked-by: Ulf Hansson Signed-off-by: Chris Ball Change-Id: I3559026e3736c9c044d1ca2cbbfda0b2b12b4a07 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33707 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/core/core.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 9698d8a2e16..ec769490300 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -141,12 +141,12 @@ 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); @@ -253,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); + } } /** -- cgit v1.2.3 From 380fc06ce81c01a7b243875acbe430986bcb23e7 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 11 Oct 2011 15:31:17 +0200 Subject: mmc: mmci: Do not release spinlock in request_end The patch "mmc: core: move ->request() call from atomic context", is the reason to why this change is possible. This simplifies the error handling code execution path quite a lot and potentially also fixes some error handling hang problems. Signed-off-by: Ulf Hansson Change-Id: Icc2454053d692d8d19d81e2046ed5939d2aac915 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33713 Tested-by: Ulf HANSSON Reviewed-by: Ulf HANSSON --- drivers/mmc/host/mmci.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 393ede681aa..8d901ba1e54 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -181,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) -- cgit v1.2.3 From b721bcda1ae03601da0480397905f481d758ee63 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Fri, 23 Sep 2011 14:15:29 +0900 Subject: mmc: core: Add default timeout value for CMD6 EXT_CSD[248] includes the default maximum timeout for CMD6. This field is added at eMMC4.5 Spec. And it can be used for default timeout except for some operations which don't define the timeout (i.e. background operation, sanitize, flush cache) in eMMC4.5 Spec. Signed-off-by: Seungwon Jeon Signed-off-by: Jaehoon Chung Signed-off-by: Chris Ball Change-Id: I1761bbadb1d35d58c5de3f459808f4cd2559da99 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33717 Reviewed-by: Ulf HANSSON Tested-by: Ulf HANSSON --- drivers/mmc/core/mmc.c | 16 ++++++++++++---- include/linux/mmc/card.h | 1 + include/linux/mmc/mmc.h | 1 + 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 2a4c9a4d3c0..7dde373d143 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -458,6 +458,12 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) 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; } @@ -801,7 +807,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; @@ -844,7 +851,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; @@ -924,7 +932,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, 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); @@ -955,7 +963,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, 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 " diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 060b96e95c2..96b59ac339c 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -54,6 +54,7 @@ struct mmc_ext_csd { 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; diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index ea558eb44c7..cd63c2dc95c 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -305,6 +305,7 @@ struct _mmc_csd { #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 -- cgit v1.2.3 From 0fa3b2b9c77cbb1f8248f4b08200bdf4719daa4d Mon Sep 17 00:00:00 2001 From: Johan Rudholm Date: Fri, 7 Oct 2011 15:58:24 +0100 Subject: mmc: boot partition lock support Enable boot partitions to be power and permanently locked via a sysfs ro_lock node. ST-Ericsson ID: 344197 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: NA Change-Id: I5534c12b0c1867d562e27a2fb2012624ad3009b4 Signed-off-by: John Beckett Signed-off-by: Johan Rudholm Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33117 Reviewed-by: Ulf HANSSON --- drivers/mmc/card/block.c | 108 +++++++++++++++++++++++++++++++++++++++++++---- drivers/mmc/core/mmc.c | 14 +++++- include/linux/mmc/card.h | 14 +++++- include/linux/mmc/mmc.h | 6 +++ 4 files changed, 131 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 2cf1ba6db91..6be812ca3fb 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -107,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); @@ -165,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) { @@ -1321,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; @@ -1346,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 @@ -1444,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; } @@ -1453,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; @@ -1492,7 +1563,8 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md) card->part[idx].part_cfg, card->part[idx].size >> 9, card->part[idx].force_ro, - card->part[idx].name); + card->part[idx].name, + card->part[idx].area_type); if (ret) return ret; } @@ -1524,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); @@ -1561,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/core/mmc.c b/drivers/mmc/core/mmc.c index 7dde373d143..903c70b8ac4 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -338,6 +338,15 @@ 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. @@ -347,7 +356,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) 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); + "boot%d", idx, true, MMC_BLK_DATA_AREA_BOOT); } } } @@ -434,7 +443,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) hc_wp_grp_sz); mmc_part_add(card, part_size << 19, EXT_CSD_PART_CONFIG_ACC_GP0 + idx, - "gp%d", idx, false); + "gp%d", idx, false, + MMC_BLK_DATA_AREA_GP); } } card->ext_csd.sec_trim_mult = diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 96b59ac339c..dc20c5b6e5f 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -66,6 +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_locked; u8 raw_partition_support; /* 160 */ u8 raw_erased_mem_count; /* 181 */ u8 raw_ext_csd_structure; /* 194 */ @@ -166,6 +167,14 @@ struct sdio_func_tuple; #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 */ @@ -174,6 +183,7 @@ struct mmc_part { unsigned int part_cfg; /* partition type */ char name[DISK_NAME_LEN]; bool force_ro; /* to make boot parts RO by default */ + int area_type; }; /* @@ -244,12 +254,14 @@ struct mmc_card { * 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) + 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++; } diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index cd63c2dc95c..dc884f60798 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -275,6 +275,7 @@ struct _mmc_csd { #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 */ @@ -313,6 +314,11 @@ 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_GP0 (0x4) -- cgit v1.2.3 From 07aea3175602d19084858b780674c20b68fd9e39 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 30 Sep 2011 16:19:52 +0200 Subject: mach-ux500:u8500: Temporarily disable DMA for mmc and sd Due to problem with suspend and resume for mmc/sd, directly related to the DMA support in the MMCI driver, we temporarily disable DMA. Change-Id: I92d1090b7190503f4a136be2361495f11215a3ab Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32763 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33233 Tested-by: John FREDRIKSSON --- arch/arm/mach-ux500/board-mop500-sdi.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 48c4ad7ca53..ee49e5f7f33 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -56,6 +56,7 @@ static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios) return 0; } +#ifdef MMC_SUSPEND_WORKAROUND #ifdef CONFIG_STE_DMA40 struct stedma40_chan_cfg mop500_sdi0_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, @@ -79,6 +80,7 @@ static struct stedma40_chan_cfg mop500_sdi0_dma_cfg_tx = { .phy_channel = 0, }; #endif +#endif static struct mmci_platform_data mop500_sdi0_data = { .ios_handler = mop500_sdi0_ios_handler, @@ -92,11 +94,13 @@ static struct mmci_platform_data mop500_sdi0_data = { 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 }; /* @@ -178,6 +182,7 @@ void mop500_sdi_tc35892_init(void) /* * 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, @@ -197,6 +202,7 @@ 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, @@ -205,17 +211,20 @@ static struct mmci_platform_data mop500_sdi2_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, @@ -235,6 +244,7 @@ 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, @@ -244,11 +254,13 @@ static struct mmci_platform_data mop500_sdi4_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) -- cgit v1.2.3 From 179594de57bacdfcde54bfb6e1151e3eefd390ba Mon Sep 17 00:00:00 2001 From: Hanumath Prasad Date: Wed, 12 Oct 2011 17:04:55 +0530 Subject: mach-ux500:u5500: Temporarily disable DMA for sd Due to problem with multiple insertion of SD card in DMA mode the dma support for SD cad is disabled temporarily. ST-Ericsson Id: ER 365282 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ic4bf04f5b3a7d4fe8c2e1d18be11308f5e7eb108 Signed-off-by: Hanumath Prasad Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33785 Reviewed-by: QABUILD Reviewed-by: Preetham-rao K Reviewed-by: Ulf HANSSON Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/board-u5500-sdi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-u5500-sdi.c b/arch/arm/mach-ux500/board-u5500-sdi.c index b2801f76fa1..475e41e0628 100644 --- a/arch/arm/mach-ux500/board-u5500-sdi.c +++ b/arch/arm/mach-ux500/board-u5500-sdi.c @@ -88,6 +88,7 @@ static int u5500_sdi1_ios_handler(struct device *dev, struct mmc_ios *ios) return 0; } +#ifdef SD_WORKAROUND static struct stedma40_chan_cfg sdi1_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_PERIPH_TO_MEM, @@ -105,7 +106,7 @@ static struct stedma40_chan_cfg sdi1_dma_cfg_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, @@ -116,11 +117,13 @@ static struct mmci_platform_data u5500_sdi1_data = { .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 }; /* -- cgit v1.2.3