From 79ad305cc7c918c1480b187f5a5992c108a0c547 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 Signed-off-by: Mian Yousaf Kaukab fs/partitions: Adapt blkdevparts to new interface of kernel 2.6.35 moved into block/partitions for 3.3 Signed-off-by: Philippe Langlais --- block/partitions/Kconfig | 19 ++++++ block/partitions/Makefile | 1 + block/partitions/blkdev_parts.c | 127 ++++++++++++++++++++++++++++++++++++++++ block/partitions/blkdev_parts.h | 14 +++++ block/partitions/check.c | 4 ++ 5 files changed, 165 insertions(+) create mode 100755 block/partitions/blkdev_parts.c create mode 100755 block/partitions/blkdev_parts.h diff --git a/block/partitions/Kconfig b/block/partitions/Kconfig index cb5f0a3f1b0..097be1934ee 100644 --- a/block/partitions/Kconfig +++ b/block/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/block/partitions/Makefile b/block/partitions/Makefile index 03af8eac51d..48b216c53db 100644 --- a/block/partitions/Makefile +++ b/block/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/block/partitions/blkdev_parts.c b/block/partitions/blkdev_parts.c new file mode 100755 index 00000000000..030565b7ce7 --- /dev/null +++ b/block/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) +{ + char blkdev_name[BDEVNAME_SIZE]; + + /* Check if there are any partitions to handle */ + if (cmdline == NULL) + return 0; + + /* Get the name of the blockdevice we are operating upon */ + if (bdevname(state->bdev, blkdev_name) == NULL) { + printk(KERN_WARNING "Could not get a blkdev name\n"); + return 0; + } + + /* Parse for partitions and add them to the state */ + return parse_blkdev_parts(blkdev_name, state); +} + diff --git a/block/partitions/blkdev_parts.h b/block/partitions/blkdev_parts.h new file mode 100755 index 00000000000..16d2b571625 --- /dev/null +++ b/block/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); + diff --git a/block/partitions/check.c b/block/partitions/check.c index bc908672c97..3020c577c3b 100644 --- a/block/partitions/check.c +++ b/block/partitions/check.c @@ -22,6 +22,7 @@ #include "acorn.h" #include "amiga.h" #include "atari.h" +#include "blkdev_parts.h" #include "ldm.h" #include "mac.h" #include "msdos.h" @@ -41,6 +42,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 00227856051dff57a011b29c6bb656c57e89590c Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 14 Dec 2011 11:16:02 +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 fix board-mop500-sdi --- arch/arm/mach-ux500/board-mop500-sdi.c | 88 ++++++------ arch/arm/mach-ux500/board-u5500-sdi.c | 244 +++++++++++++++++++++++++++++---- 2 files changed, 267 insertions(+), 65 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 5dde4d4ebe8..a1cb5e7339b 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" /* * v2 has a new version of this block that need to be forced, the number found @@ -58,6 +59,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); @@ -66,26 +68,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 @@ -104,39 +110,10 @@ static struct mmci_platform_data mop500_sdi0_data = { #endif }; -static void sdi0_configure(void) -{ - int ret; - - 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); - - /* Add the device, force v2 to subrevision 1 */ - db8500_add_sdi0(&mop500_sdi0_data, U8500_SDI_V2_PERIPHID); -} - -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(); -} - /* * SDI1 (SDIO WLAN) */ +#ifdef SDIO_DMA_ON #ifdef CONFIG_STE_DMA40 static struct stedma40_chan_cfg sdi1_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, @@ -156,6 +133,7 @@ static struct stedma40_chan_cfg sdi1_dma_cfg_tx = { .dst_info.data_width = STEDMA40_WORD_WIDTH, }; #endif +#endif static struct mmci_platform_data mop500_sdi1_data = { .ocr_mask = MMC_VDD_29_30, @@ -163,17 +141,49 @@ static struct mmci_platform_data mop500_sdi1_data = { .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; + + 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); + + /* Add the device, force v2 to subrevision 1 */ + db8500_add_sdi0(&mop500_sdi0_data, U8500_SDI_V2_PERIPHID); + db8500_add_sdi1(&mop500_sdi1_data, U8500_SDI_V2_PERIPHID); +} + +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_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, @@ -253,7 +263,7 @@ void __init mop500_sdi_init(void) /* On-board eMMC */ db8500_add_sdi4(&mop500_sdi4_data, U8500_SDI_V2_PERIPHID); /* - * 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. */ @@ -270,7 +280,7 @@ void __init snowball_sdi_init(void) mop500_sdi0_data.cd_invert = true; sdi0_en = SNOWBALL_SDMMC_EN_GPIO; sdi0_vsel = SNOWBALL_SDMMC_1V8_3V_GPIO; - sdi0_configure(); + sdi0_sdi1_configure(); } void __init hrefv60_sdi_init(void) @@ -283,7 +293,5 @@ void __init hrefv60_sdi_init(void) mop500_sdi0_data.gpio_cd = HREFV60_SDMMC_CD_GPIO; sdi0_en = HREFV60_SDMMC_EN_GPIO; sdi0_vsel = HREFV60_SDMMC_1V8_3V_GPIO; - sdi0_configure(); - /* WLAN SDIO channel */ - db8500_add_sdi1(&mop500_sdi1_data, U8500_SDI_V2_PERIPHID); + sdi0_sdi1_configure(); } diff --git a/arch/arm/mach-ux500/board-u5500-sdi.c b/arch/arm/mach-ux500/board-u5500-sdi.c index 63c3f8058ff..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 53d8c65480e14d93863b1f24cb4ea36fa1e0c911 Mon Sep 17 00:00:00 2001 From: Ulf Hansson <(address hidden)> Date: Tue, 8 Nov 2011 10:52:49 +0100 Subject: mmci: Support non-power-of-two block sizes for ux500v2 variant For the ux500v2 variant of the PL18x block, non power of two block sizes are supported. This will make it possible to decrease data overhead for SDIO transfers. Signed-off-by: Stefan Nilsson XK <(address hidden)> Signed-off-by: Ulf Hansson <(address hidden)> --- 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 11e589cd823..a9f249b7940 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -53,6 +53,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; @@ -63,6 +64,7 @@ struct variant_data { bool sdio; bool st_clkdiv; bool blksz_datactrl16; + bool non_power_of_2_blksize; }; static struct variant_data variant_arm = { @@ -104,6 +106,7 @@ static struct variant_data variant_ux500v2 = { .sdio = true, .st_clkdiv = true, .blksz_datactrl16 = true, + .non_power_of_2_blksize = true, }; /* @@ -597,7 +600,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); @@ -983,11 +985,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 97845db7f5cc05ee76de7ac44295732a577f81f7 Mon Sep 17 00:00:00 2001 From: Ulf Hansson <(address hidden)> Date: Tue, 8 Nov 2011 10:53:06 +0100 Subject: mmci: Prepare for SDIO before setting up DMA job Move the SDIO preparation to be done before the DMA job is setup. This makes it possible to do DMA for SDIO transfers as well as the earlier supported pio mode. Signed-off-by: Ulf Hansson <(address hidden)> Signed-off-by: Stefan Nilsson XK <(address hidden)> --- 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 a9f249b7940..4bc4a115a52 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -609,6 +609,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 @@ -637,11 +642,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 ba4596790b03097bfbd2d74bdca1b62173e504ca Mon Sep 17 00:00:00 2001 From: Ulf Hansson <(address hidden)> Date: Tue, 8 Nov 2011 10:53:22 +0100 Subject: mmci: Fix incorrect handling of HW flow control for SDIO For data writes smaller <= 8 bytes (only SDIO case), HW flow control was disabled but never re-enabled again. This meant that a following large read request would randomly give buffer overrun errors. Moreover HW flow control is not needed for transfers that fits in the FIFO of PL18x. Thus it is disabled for write operations <= the FIFO size. Signed-off-by: Ulf Hansson <(address hidden)> Signed-off-by: Stefan Nilsson XK <(address hidden)> --- 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 4bc4a115a52..ffc32b8b45d 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -611,9 +611,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 @@ -816,23 +839,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 2ddaaa83fe38b274ee41c3dfff5afedaf48f4bdc Mon Sep 17 00:00:00 2001 From: Ulf Hansson <(address hidden)> Date: Tue, 8 Nov 2011 10:57:32 +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. Signed-off-by: Sebastian Rasmussen <(address hidden)> Signed-off-by: Ulf Hansson <(address hidden)> Reviewed-by: Linus Walleij <(address hidden)> --- 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 ffc32b8b45d..fd6fde49280 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -54,6 +54,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; @@ -65,18 +66,21 @@ struct variant_data { bool st_clkdiv; bool blksz_datactrl16; bool non_power_of_2_blksize; + u32 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 = { @@ -85,6 +89,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 = { @@ -95,6 +100,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 = { @@ -107,6 +113,7 @@ static struct variant_data variant_ux500v2 = { .st_clkdiv = true, .blksz_datactrl16 = true, .non_power_of_2_blksize = true, + .pwrreg_powerup = MCI_PWR_ON, }; /* @@ -1026,6 +1033,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; @@ -1052,11 +1060,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 31b9cb87603216abb11b85b27460aeb508d2a038 Mon Sep 17 00:00:00 2001 From: Ulf Hansson <(address hidden)> Date: Tue, 8 Nov 2011 10:58:26 +0100 Subject: 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. Signed-off-by: Ulf Hansson <(address hidden)> Signed-off-by: Sebastian Rasmussen <(address hidden)> Acked-by: Linus Walleij <(address hidden)> --- drivers/mmc/host/mmci.c | 21 +++++++++++++++++++++ drivers/mmc/host/mmci.h | 10 ---------- include/linux/amba/mmci.h | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index fd6fde49280..483a143db63 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -55,6 +55,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; @@ -67,6 +68,7 @@ struct variant_data { bool blksz_datactrl16; bool non_power_of_2_blksize; u32 pwrreg_powerup; + bool signal_direction; }; static struct variant_data variant_arm = { @@ -90,6 +92,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 = { @@ -101,6 +104,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 = { @@ -114,6 +118,7 @@ static struct variant_data variant_ux500v2 = { .blksz_datactrl16 = true, .non_power_of_2_blksize = true, .pwrreg_powerup = MCI_PWR_ON, + .signal_direction = true, }; /* @@ -1074,6 +1079,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 0101e9c17fa..b51bf5fa85f 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -6,6 +6,19 @@ #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; @@ -31,6 +44,8 @@ struct dma_chan; * @capabilities: the capabilities of the block as implemented in * this platform, signify anything MMC_CAP_* from mmc/host.h * @capabilities2: more capabilities, MMC_CAP2_* 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 @@ -54,6 +69,7 @@ struct mmci_platform_data { bool cd_invert; unsigned long capabilities; unsigned long capabilities2; + u32 sigdir; bool (*dma_filter)(struct dma_chan *chan, void *filter_param); void *dma_rx_param; void *dma_tx_param; -- cgit v1.2.3 From c9932c2383e818d846650a813d81d14cae9f405a Mon Sep 17 00:00:00 2001 From: Ulf Hansson <(address hidden)> Date: Tue, 8 Nov 2011 10:59:07 +0100 Subject: mmci: add constraints on alignment for SDIO Buffers must be 4 bytes aligned due to restrictions that the PL18x FIFO accesses must be done in a 4-byte aligned manner. Enable DMA_REQCTL for SDIO to support write of not 32 bytes aligned sg element lengths. In PIO mode any buffer length can be handled as long as the buffer address is 4 byte aligned. Signed-off-by: Ulf Hansson <(address hidden)> Signed-off-by: Per Forlin <(address hidden)> Signed-off-by: Stefan Nilsson XK <(address hidden)> --- drivers/mmc/host/mmci.c | 55 +++++++++++++++++++++++++++++++++++++++++-------- drivers/mmc/host/mmci.h | 7 +++++++ 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 483a143db63..0ad9c4aa45e 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -45,6 +45,7 @@ static unsigned int fmax = 515633; * struct variant_data - MMCI variant-specific quirks * @clkreg: default value for MCICLOCK register * @clkreg_enable: enable value for MMCICLOCK register + * @dma_sdio_req_ctrl: enable value for DMAREQCTL register for SDIO write * @datalength_bits: number of bits in the MMCIDATALENGTH register * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY * is asserted (likewise for RX) @@ -60,6 +61,7 @@ static unsigned int fmax = 515633; struct variant_data { unsigned int clkreg; unsigned int clkreg_enable; + unsigned int dma_sdio_req_ctrl; unsigned int datalength_bits; unsigned int fifosize; unsigned int fifohalfsize; @@ -100,6 +102,7 @@ static struct variant_data variant_ux500 = { .fifohalfsize = 8 * 4, .clkreg = MCI_CLK_ENABLE, .clkreg_enable = MCI_ST_UX500_HWFCEN, + .dma_sdio_req_ctrl = MCI_ST_DPSM_DMAREQCTL, .datalength_bits = 24, .sdio = true, .st_clkdiv = true, @@ -112,6 +115,7 @@ static struct variant_data variant_ux500v2 = { .fifohalfsize = 8 * 4, .clkreg = MCI_CLK_ENABLE, .clkreg_enable = MCI_ST_UX500_HWFCEN, + .dma_sdio_req_ctrl = MCI_ST_DPSM_DMAREQCTL, .datalength_bits = 24, .sdio = true, .st_clkdiv = true, @@ -121,6 +125,31 @@ static struct variant_data variant_ux500v2 = { .signal_direction = true, }; +/* + * Validate mmc prerequisites + */ +static int mmci_validate_data(struct mmci_host *host, + struct mmc_data *data) +{ + if (!data) + return 0; + + if (!host->variant->non_power_of_2_blksize && + !is_power_of_2(data->blksz)) { + dev_err(mmc_dev(host->mmc), + "unsupported block size (%d bytes)\n", data->blksz); + return -EINVAL; + } + + if (data->sg->offset & 3) { + dev_err(mmc_dev(host->mmc), + "unsupported alginment (0x%x)\n", data->sg->offset); + return -EINVAL; + } + + return 0; +} + /* * This must be called with host->lock held */ @@ -416,8 +445,12 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, if (!chan) return -EINVAL; - /* If less than or equal to the fifo size, don't bother with DMA */ - if (data->blksz * data->blocks <= variant->fifosize) + /* + * If less than or equal to the fifo size, don't bother with DMA + * SDIO transfers may not be 4 bytes aligned, fall back to PIO + */ + if (data->blksz * data->blocks <= variant->fifosize || + (data->blksz * data->blocks) & 3) return -EINVAL; device = chan->device; @@ -452,6 +485,7 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) { int ret; struct mmc_data *data = host->data; + struct variant_data *variant = host->variant; ret = mmci_dma_prep_data(host, host->data, NULL); if (ret) @@ -466,6 +500,11 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) datactrl |= MCI_DPSM_DMAENABLE; + /* Some hardware versions need special flags for SDIO DMA write */ + if (variant->sdio && host->mmc->card && mmc_card_sdio(host->mmc->card) + && (data->flags & MMC_DATA_WRITE)) + datactrl |= variant->dma_sdio_req_ctrl; + /* Trigger the DMA transfer */ writel(datactrl, host->base + MMCIDATACTRL); @@ -510,6 +549,9 @@ static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq, if (!data) return; + if (mmci_validate_data(host, mrq->data)) + return; + if (data->host_cookie) { data->host_cookie = 0; return; @@ -1003,17 +1045,12 @@ 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 && - !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; + mrq->cmd->error = mmci_validate_data(host, mrq->data); + if (mrq->cmd->error) { mmc_request_done(mmc, mrq); return; } diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 5600755edfa..f7baed12810 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -60,6 +60,13 @@ #define MCI_ST_DPSM_RWMOD (1 << 10) #define MCI_ST_DPSM_SDIOEN (1 << 11) /* Control register extensions in the ST Micro Ux500 versions */ +/* + * DMA request control is required for write + * if transfer size is not 32 byte aligned. + * DMA request control is also needed if the total + * transfer size is 32 byte aligned but any of the + * sg element lengths are not aligned with 32 byte. + */ #define MCI_ST_DPSM_DMAREQCTL (1 << 12) #define MCI_ST_DPSM_DBOOTMODEEN (1 << 13) #define MCI_ST_DPSM_BUSYMODE (1 << 14) -- cgit v1.2.3 From 48f10ebb054e53868189053f86d09a4eb6277f12 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 a1cb5e7339b..1ed9669b1a1 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -32,21 +32,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: /* @@ -67,8 +59,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 @@ -96,13 +87,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 0ad9c4aa45e..e3b478afd90 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1080,6 +1080,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) @@ -1099,10 +1103,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 b51bf5fa85f..32a89cf5ec4 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -31,7 +31,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 0e5a4cfa5ae74f0f2dbd96244bd1ffe79f234936 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 f7baed12810..1c05b191fa9 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -157,7 +157,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 6cddb09dc4c8b53828eb49fc3dabf8282dab01c4 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Fri, 14 Oct 2011 13:54:36 +0200 Subject: mmc: mmc_test: add 4 byte alignment tests Add test cases to verify alignment with 4 bytes and 32 bytes for sg element lengths and memory addresses. These tests were added to investigate DMA constraints when transferring buffers that are not aligned with 32 bytes. ST-Ericsson ID: 363565 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: NA Change-Id: I6363a018647f22e5d69920af2cb30dfe2b2a699e Signed-off-by: Per Forlin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34362 Reviewed-by: QATOOLS Reviewed-by: Ulf HANSSON --- drivers/mmc/card/mmc_test.c | 180 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 759714ed6be..6622f2e6e05 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -1253,6 +1253,130 @@ static int mmc_test_align_multi_read(struct mmc_test_card *test) return 0; } + +/* helper function for various address alignment and sg length alignment */ +static int mmc_test_align_multi(struct mmc_test_card *test, bool do_write, + struct scatterlist *sg, + u32 *sizes, int sg_len, int offset) +{ + int ret, i; + unsigned int size; + u32 buf_off; + u32 sg_size; + + if (test->card->host->max_blk_count == 1) + return RESULT_UNSUP_HOST; + + size = PAGE_SIZE * 2; + size = min(size, test->card->host->max_req_size); + size = min(size, test->card->host->max_seg_size); + size = min(size, test->card->host->max_blk_count * 512); + size -= offset; + size -= size % 512; + + if (size < 1024) + return RESULT_UNSUP_HOST; + + for (i = 0, sg_size = 0; + i < sg_len && sg_size + sizes[i] < size; i++) + sg_size += sizes[i]; + + if (sg_size < size) + sizes[i-1] += size - sg_size; + sg_len = i; + + sg_init_table(sg, sg_len); + for (i = 0, buf_off = offset; i < sg_len; i++) { + sg_set_buf(&sg[i], test->buffer + buf_off, sizes[i]); + buf_off += sizes[i]; + } + + ret = mmc_test_transfer(test, sg, sg_len, 0, size/512, 512, do_write); + if (ret) + return ret; + + return 0; +} + +static int mmc_test_align_length_32(struct mmc_test_card *test, bool do_write) +{ + u32 sizes[] = {512, 32*1, 32*2, 32*3, 32*4, 32*5, 32*6, 32*7, + 32*8, 32*9, 32*10, 32*11, 32*12, 32*13, 2048}; + struct scatterlist sg[ARRAY_SIZE(sizes)]; + + return mmc_test_align_multi(test, do_write, sg, sizes, + ARRAY_SIZE(sg), 0); +} + +static int mmc_test_align_length_4(struct mmc_test_card *test, bool do_write) +{ + u32 sizes[] = {512, 4*1, 4*2, 4*3, 4*4, 4*5, 4*6, 4*7, + 4*8, 4*9, 520, 1040, 2080}; + struct scatterlist sg[ARRAY_SIZE(sizes)]; + + return mmc_test_align_multi(test, do_write, sg, sizes, + ARRAY_SIZE(sg), 0); +} + +static int mmc_test_align_length_4_write(struct mmc_test_card *test) +{ + bool do_write = true; + return mmc_test_align_length_4(test, do_write); +} + +static int mmc_test_align_length_4_read(struct mmc_test_card *test) +{ + bool do_write = false; + return mmc_test_align_length_4(test, do_write); +} + +static int mmc_test_align_length_32_write(struct mmc_test_card *test) +{ + bool do_write = true; + return mmc_test_align_length_32(test, do_write); +} + +static int mmc_test_align_length_32_read(struct mmc_test_card *test) +{ + bool do_write = false; + return mmc_test_align_length_32(test, do_write); +} + +/* helper function for testing address alignment */ +static int mmc_test_align_address(struct mmc_test_card *test, bool do_write, + u32 offset) +{ + u32 sizes[] = {512, 512, 1024, 1024, 2048}; + struct scatterlist sg[ARRAY_SIZE(sizes)]; + + return mmc_test_align_multi(test, do_write, sg, + sizes, ARRAY_SIZE(sg), offset); +} + +static int mmc_test_align_address_4_write(struct mmc_test_card *test) +{ + bool do_write = true; + return mmc_test_align_address(test, do_write, 4); +} + +static int mmc_test_align_address_4_read(struct mmc_test_card *test) +{ + bool do_write = false; + return mmc_test_align_address(test, do_write, 4); +} + +static int mmc_test_align_address_32_write(struct mmc_test_card *test) +{ + bool do_write = true; + return mmc_test_align_address(test, do_write, 32); +} + +static int mmc_test_align_address_32_read(struct mmc_test_card *test) +{ + bool do_write = false; + return mmc_test_align_address(test, do_write, 32); +} + static int mmc_test_xfersize_write(struct mmc_test_card *test) { int ret; @@ -2450,6 +2574,62 @@ static const struct mmc_test_case mmc_test_cases[] = { .cleanup = mmc_test_cleanup, }, + { + .name = "4 bytes aligned sg-element length write", + .prepare = mmc_test_prepare_write, + .run = mmc_test_align_length_4_write, + .cleanup = mmc_test_cleanup, + }, + + { + .name = "4 bytes aligned sg-element length read", + .prepare = mmc_test_prepare_read, + .run = mmc_test_align_length_4_read, + .cleanup = mmc_test_cleanup, + }, + + { + .name = "32 bytes aligned sg-element length write", + .prepare = mmc_test_prepare_write, + .run = mmc_test_align_length_32_write, + .cleanup = mmc_test_cleanup, + }, + + { + .name = "32 bytes aligned sg-element length read", + .prepare = mmc_test_prepare_read, + .run = mmc_test_align_length_32_read, + .cleanup = mmc_test_cleanup, + }, + + { + .name = "4 bytes aligned sg-element address write", + .prepare = mmc_test_prepare_write, + .run = mmc_test_align_address_4_write, + .cleanup = mmc_test_cleanup, + }, + + { + .name = "4 bytes aligned sg-element address read", + .prepare = mmc_test_prepare_read, + .run = mmc_test_align_address_4_read, + .cleanup = mmc_test_cleanup, + }, + + { + .name = "32 bytes aligned sg-element address write", + .prepare = mmc_test_prepare_write, + .run = mmc_test_align_address_32_write, + .cleanup = mmc_test_cleanup, + }, + + { + .name = "32 bytes aligned sg-element address read", + .prepare = mmc_test_prepare_read, + .run = mmc_test_align_address_32_read, + .cleanup = mmc_test_cleanup, + }, + { .name = "Correct xfer_size at write (start failure)", .run = mmc_test_xfersize_write, -- cgit v1.2.3 From 8aaa470f29165d4032674a37740c32f1ee6b2ecd Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 21 Oct 2011 14:01:12 +0200 Subject: mmc: mmci: Support MMC_PM_KEEP_POWER Add MMC_PM_KEEP_POWER to pm_caps so SDIO clients are able to use this option to prevent power off in suspend. Change-Id: I36df77e66ca8845ceeffb9d5163d4c1e77821b7d Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34918 --- drivers/mmc/host/mmci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index e3b478afd90..441a66b97ba 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1331,6 +1332,9 @@ static int __devinit mmci_probe(struct amba_device *dev, mmc->caps = plat->capabilities; mmc->caps2 = plat->capabilities2; + /* We support these PM capabilities. */ + mmc->pm_caps = MMC_PM_KEEP_POWER; + /* * We can do SGIO */ -- cgit v1.2.3 From 1a587901e4f71fa546dc2758447df68b44c83c86 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Wed, 26 Oct 2011 11:33:30 +0200 Subject: ARM: ux500: SDI: enable DMA on all SDI devices Enable DMA again now when power save for MMC and DMA is in place. Patch is tested: * suspend and resume successfully in Android. * suspend video playback from sd-card and resume it successfully in Android. ST-Ericsson ID: 370129 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I4eb9367c3feeb8661e33035d4d3022205b9e924b Signed-off-by: Per Forlin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/35325 Reviewed-by: Stefan NILSSON9 Tested-by: Stefan NILSSON9 Reviewed-by: Ulf HANSSON --- arch/arm/mach-ux500/board-mop500-sdi.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 1ed9669b1a1..59ac76f980b 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -108,7 +108,6 @@ static struct mmci_platform_data mop500_sdi0_data = { /* * SDI1 (SDIO WLAN) */ -#ifdef SDIO_DMA_ON #ifdef CONFIG_STE_DMA40 static struct stedma40_chan_cfg sdi1_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, @@ -128,7 +127,6 @@ static struct stedma40_chan_cfg sdi1_dma_cfg_tx = { .dst_info.data_width = STEDMA40_WORD_WIDTH, }; #endif -#endif static struct mmci_platform_data mop500_sdi1_data = { .ocr_mask = MMC_VDD_29_30, @@ -136,13 +134,11 @@ static struct mmci_platform_data mop500_sdi1_data = { .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) -- cgit v1.2.3 From 4fbdb04bc9449e941576f7a36d592ded16684b4a Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 3 Nov 2011 13:11:53 +0100 Subject: ARM: ux500: Optimize ios_handler for SD-card The ios_handler may be called several times without a changed power_mode, thus make sure the power_mode has changed before we act on it. Change-Id: I72933da44b6930a0ed4b0f0f1e294443dba2b30d Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36215 --- arch/arm/mach-ux500/board-mop500-sdi.c | 6 ++++++ arch/arm/mach-ux500/board-u5500-sdi.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 59ac76f980b..814c4e5c3b6 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -38,6 +38,11 @@ static int sdi0_vsel = -1; static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios) { + static int power_mode = -1; + + if (power_mode == ios->power_mode) + return 0; + switch (ios->power_mode) { case MMC_POWER_UP: case MMC_POWER_ON: @@ -59,6 +64,7 @@ static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios) break; } + power_mode = ios->power_mode; return 0; } diff --git a/arch/arm/mach-ux500/board-u5500-sdi.c b/arch/arm/mach-ux500/board-u5500-sdi.c index b2801f76fa1..881035c4f4d 100644 --- a/arch/arm/mach-ux500/board-u5500-sdi.c +++ b/arch/arm/mach-ux500/board-u5500-sdi.c @@ -66,6 +66,11 @@ static struct mmci_platform_data u5500_sdi0_data = { static int u5500_sdi1_ios_handler(struct device *dev, struct mmc_ios *ios) { + static int power_mode = -1; + + if (power_mode == ios->power_mode) + return 0; + switch (ios->power_mode) { case MMC_POWER_UP: case MMC_POWER_ON: @@ -85,6 +90,7 @@ static int u5500_sdi1_ios_handler(struct device *dev, struct mmc_ios *ios) break; } + power_mode = ios->power_mode; return 0; } -- cgit v1.2.3 From d94e2157bf0c467b9f3ca9fddcdeb712e52a3148 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 4 Nov 2011 15:13:19 +0100 Subject: ARM: ux500: Enable levelshifter at MMC_POWER_ON only There is no need to enable the levelshifter in the MMC_POWER_UP state. Moreover we are not in a stable state for starting to communicate with the SD-card. Thus we shall not enable the levelshifter in this state. Change-Id: I6fbcb5643513ebf658a2eb198d4a6ac24fbac872 Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36502 Reviewed-by: P.Hanumath PRASAD Tested-by: P.Hanumath PRASAD Reviewed-by: Stefan NILSSON9 Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/board-mop500-sdi.c | 1 + arch/arm/mach-ux500/board-u5500-sdi.c | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 814c4e5c3b6..9a9f3b8117a 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -45,6 +45,7 @@ static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_UP: + break; case MMC_POWER_ON: /* * Level shifter voltage should depend on vdd to when deciding diff --git a/arch/arm/mach-ux500/board-u5500-sdi.c b/arch/arm/mach-ux500/board-u5500-sdi.c index 881035c4f4d..c3c21adeb1c 100644 --- a/arch/arm/mach-ux500/board-u5500-sdi.c +++ b/arch/arm/mach-ux500/board-u5500-sdi.c @@ -73,6 +73,7 @@ static int u5500_sdi1_ios_handler(struct device *dev, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_UP: + break; case MMC_POWER_ON: /* * Level shifter voltage should depend on vdd to when deciding -- cgit v1.2.3 From 7dafde60177b2f5941aa3f7750881122c9e77c2c Mon Sep 17 00:00:00 2001 From: Hanumath Prasad Date: Mon, 31 Oct 2011 14:51:19 +0530 Subject: mach-ux500:u5500: Add missing CONFIG_STE_DMA40 into board-u5500-sdi.c Signed-off-by: Hanumath Prasad Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/board-u5500-sdi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-ux500/board-u5500-sdi.c b/arch/arm/mach-ux500/board-u5500-sdi.c index c3c21adeb1c..7a82397e72f 100644 --- a/arch/arm/mach-ux500/board-u5500-sdi.c +++ b/arch/arm/mach-ux500/board-u5500-sdi.c @@ -95,6 +95,7 @@ static int u5500_sdi1_ios_handler(struct device *dev, struct mmc_ios *ios) return 0; } +#ifdef CONFIG_STE_DMA40 static struct stedma40_chan_cfg sdi1_dma_cfg_rx = { .mode = STEDMA40_MODE_LOGICAL, .dir = STEDMA40_PERIPH_TO_MEM, @@ -112,6 +113,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, -- cgit v1.2.3 From 7f2d6216391c5f528d282d883776059d2b2a074a Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 12 Jan 2012 11:46:46 +0100 Subject: sdi: ux500: Coding style fixes Fix the most obvious violations of the kernel coding style Signed-off-by: Jonas Aaberg --- arch/arm/mach-ux500/board-mop500-sdi.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 9a9f3b8117a..4ac7a9ad5fd 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -150,21 +150,21 @@ static struct mmci_platform_data mop500_sdi1_data = { static void sdi0_sdi1_configure(void) { - int ret; + int ret; - ret = gpio_request(sdi0_en, "level shifter enable"); - if (!ret) - ret = gpio_request(sdi0_vsel, - "level shifter 1v8-3v select"); + 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; - } + 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 */ db8500_add_sdi0(&mop500_sdi0_data, U8500_SDI_V2_PERIPHID); -- cgit v1.2.3 From 86beb5ffbddf9bd2913ae4a5adb5105d8aaecb18 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 12 Jan 2012 14:58:51 +0100 Subject: sdi: u5500: detect pre-R3A boards Signed-off-by: Rabin Vincent --- arch/arm/mach-ux500/board-u5500-sdi.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-ux500/board-u5500-sdi.c b/arch/arm/mach-ux500/board-u5500-sdi.c index 7a82397e72f..a33bc0cd28a 100644 --- a/arch/arm/mach-ux500/board-u5500-sdi.c +++ b/arch/arm/mach-ux500/board-u5500-sdi.c @@ -233,31 +233,24 @@ static void sdi1_configure(void) #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) { 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) + /* + * 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. + */ + if (u5500_get_boot_mmc() == 2) db5500_add_sdi2(&u5500_sdi2_data, periphid); else db5500_add_sdi0(&u5500_sdi0_data, periphid); -- cgit v1.2.3 From 67a9b70e631e62508c53e1ddd8e1206a7ee6e2b2 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 24 Nov 2011 15:00:26 +0100 Subject: mmc: block: Remove use of mmc_blk_set_blksize According to the specifications for SD and (e)MMC default blocksize (named BLOCKLEN in Spec.) must always be 512 bytes. Since we hardcoded to always use 512 bytes, we do not explicitly have to set it. Future improvements should potentially make it possible to use a greater blocksize than 512 bytes, but until then let's skip this. Change-Id: Id8b844858caef3a28df838d85086593b8b16277d Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39740 Reviewed-by: QABUILD Reviewed-by: Stefan NILSSON9 --- drivers/mmc/card/block.c | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index c6a383d0244..536ec500ed9 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1613,24 +1613,6 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md) return ret; } -static int -mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) -{ - int err; - - mmc_claim_host(card->host); - err = mmc_set_blocklen(card, 512); - mmc_release_host(card->host); - - if (err) { - pr_err("%s: unable to set block size to 512: %d\n", - md->disk->disk_name, err); - return -EINVAL; - } - - return 0; -} - static void mmc_blk_remove_req(struct mmc_blk_data *md) { struct mmc_card *card; @@ -1758,7 +1740,6 @@ static const struct mmc_fixup blk_fixups[] = static int mmc_blk_probe(struct mmc_card *card) { struct mmc_blk_data *md, *part_md; - int err; char cap_str[10]; /* @@ -1771,10 +1752,6 @@ static int mmc_blk_probe(struct mmc_card *card) if (IS_ERR(md)) return PTR_ERR(md); - err = mmc_blk_set_blksize(md, card); - if (err) - goto out; - string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2, cap_str, sizeof(cap_str)); pr_info("%s: %s %s %s %s\n", @@ -1799,7 +1776,7 @@ static int mmc_blk_probe(struct mmc_card *card) out: mmc_blk_remove_parts(card, md); mmc_blk_remove_req(md); - return err; + return 0; } static void mmc_blk_remove(struct mmc_card *card) @@ -1835,8 +1812,6 @@ static int mmc_blk_resume(struct mmc_card *card) struct mmc_blk_data *md = mmc_get_drvdata(card); if (md) { - mmc_blk_set_blksize(md, card); - /* * Resume involves the card going into idle state, * so current partition is always the main one. -- cgit v1.2.3 From 8acefc274af7ab8fd123c8f5199198509c29b80a Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 15 Nov 2011 16:39:54 +0100 Subject: mmc: Minimize resume-time by deferring resume Typically an sd/mmc card takes around 200 - 1100 ms to initialize when the power to the card has been cut, which is what happens during a suspend/resume sequence. All device's resume time adds up to the total kernel resume time. Some use cases requires the kernel to be resumed fast, to be able to meet deadlines. One use case example is WLAN SOFT_AP, but there are certainly more. This patch schedules a delayed work to do a deferred resume of the mmc host, if the bus holds a card of SD or MMC type. The reason for not supporting SDIO and SDcombo cards at this stage, is because the SDIO API is synchronus, which complicates request locking mechanism when waiting for a deferred resume to be completed. While waiting for a deferred resume to be completed, detect works are prevented from doing a new rescan. If a mmcblk request arrives, the deferred resume will be synced immediately. The deferred resume is scheduled 3000 ms after the resume request arrived. The idea behind this timer value is to let the mmc host being able to accept a new suspend request before it has been deferred resumed and thus not increase the resume to suspend time if not really needed. Change-Id: I7e97e59c8709cf5d8e3c76478771ddf6062a54ec Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39692 --- drivers/mmc/card/block.c | 8 ++++++++ drivers/mmc/core/core.c | 36 +++++++++++++++++++++++++++++++++++- drivers/mmc/core/core.h | 1 + drivers/mmc/core/host.c | 1 + include/linux/mmc/host.h | 17 +++++++++++++++++ 5 files changed, 62 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 536ec500ed9..1a0e399629e 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1379,6 +1379,14 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; + /* + * We must make sure we have not claimed the host before + * doing a flush to prevent deadlock, thus we check if + * the host needs a resume first. + */ + if (mmc_host_needs_resume(card->host)) + mmc_resume_host_sync(card->host); + if (req && !mq->mqrq_prev->req) /* claim host only for the first request */ mmc_claim_host(card->host); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 132378b89d7..273de4191d4 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2137,7 +2137,7 @@ void mmc_rescan(struct work_struct *work) container_of(work, struct mmc_host, detect.work); int i; - if (host->rescan_disable) + if (host->rescan_disable || mmc_host_needs_resume(host)) return; mmc_bus_get(host); @@ -2402,7 +2402,13 @@ int mmc_suspend_host(struct mmc_host *host) if (host->caps & MMC_CAP_DISABLE) cancel_delayed_work(&host->disable); cancel_delayed_work(&host->detect); + cancel_delayed_work_sync(&host->resume); mmc_flush_scheduled_work(); + + /* Skip suspend, if deferred resume were scheduled but not completed. */ + if (mmc_host_needs_resume(host)) + return 0; + if (mmc_try_claim_host(host)) { err = mmc_cache_ctrl(host, 0); mmc_do_release_host(host); @@ -2443,6 +2449,10 @@ int mmc_suspend_host(struct mmc_host *host) mmc_release_host(host); host->pm_flags = 0; err = 0; + } else if (mmc_card_mmc(host->card) || + mmc_card_sd(host->card)) { + host->pm_state |= MMC_HOST_DEFERRED_RESUME | + MMC_HOST_NEEDS_RESUME; } } else { err = -EBUSY; @@ -2467,6 +2477,12 @@ int mmc_resume_host(struct mmc_host *host) { int err = 0; + if (mmc_host_deferred_resume(host)) { + mmc_schedule_delayed_work(&host->resume, + msecs_to_jiffies(3000)); + return 0; + } + mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { if (!mmc_card_keep_power(host)) { @@ -2501,6 +2517,24 @@ int mmc_resume_host(struct mmc_host *host) } EXPORT_SYMBOL(mmc_resume_host); +void mmc_resume_work(struct work_struct *work) +{ + struct mmc_host *host = + container_of(work, struct mmc_host, resume.work); + + host->pm_state &= ~MMC_HOST_DEFERRED_RESUME; + mmc_resume_host(host); + host->pm_state &= ~MMC_HOST_NEEDS_RESUME; + + mmc_detect_change(host, 0); +} + +void mmc_resume_host_sync(struct mmc_host *host) +{ + flush_delayed_work_sync(&host->resume); +} +EXPORT_SYMBOL(mmc_resume_host_sync); + /* Do the card removal on suspend if card is assumed removeable * Do that in pm notifier while userspace isn't yet frozen, so we will be able to sync the card. diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 3bdafbca354..5796d2d85f4 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -59,6 +59,7 @@ static inline void mmc_delay(unsigned int ms) void mmc_rescan(struct work_struct *work); void mmc_start_host(struct mmc_host *host); void mmc_stop_host(struct mmc_host *host); +void mmc_resume_work(struct work_struct *work); int _mmc_detect_card_removed(struct mmc_host *host); diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index c3704e293a7..fe6e81529cb 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -331,6 +331,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable); + INIT_DELAYED_WORK(&host->resume, mmc_resume_work); #ifdef CONFIG_PM host->pm_notify.notifier_call = mmc_pm_notify; #endif diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index ee2b0363c04..37f995176bb 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -318,6 +318,11 @@ struct mmc_host { int detect_change; /* card detect flag */ struct mmc_hotplug hotplug; + struct delayed_work resume; /* deferred resume work */ + unsigned int pm_state; /* used for deferred resume */ +#define MMC_HOST_DEFERRED_RESUME (1 << 0) +#define MMC_HOST_NEEDS_RESUME (1 << 1) + const struct mmc_bus_ops *bus_ops; /* current bus driver */ unsigned int bus_refs; /* reference counter */ @@ -366,6 +371,7 @@ static inline void *mmc_priv(struct mmc_host *host) extern int mmc_suspend_host(struct mmc_host *); extern int mmc_resume_host(struct mmc_host *); +extern void mmc_resume_host_sync(struct mmc_host *); extern int mmc_power_save_host(struct mmc_host *host); extern int mmc_power_restore_host(struct mmc_host *host); @@ -464,4 +470,15 @@ static inline unsigned int mmc_host_clk_rate(struct mmc_host *host) return host->ios.clock; } #endif + +static inline int mmc_host_deferred_resume(struct mmc_host *host) +{ + return host->pm_state & MMC_HOST_DEFERRED_RESUME; +} + +static inline int mmc_host_needs_resume(struct mmc_host *host) +{ + return host->pm_state & MMC_HOST_NEEDS_RESUME; +} + #endif /* LINUX_MMC_HOST_H */ -- cgit v1.2.3 From fa83cc414fa17cb567f1a8448d18ce0b2da5cc48 Mon Sep 17 00:00:00 2001 From: Stefan Nilsson XK Date: Mon, 21 Nov 2011 16:42:20 +0100 Subject: mmc: mmci: Use HWFC when possible HW flow control should not be used for write transfers less then 8 bytes. All other transfers should use HW flow control. ST-Ericsson Linux next: NA ST-Ericsson ID: 398871 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I51044d26cfc7df033852becbbc6e796396dfb45f Signed-off-by: Stefan Nilsson XK Signed-off-by: Fredrik Soderstedt Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/40191 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Ulf HANSSON --- drivers/mmc/host/mmci.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 441a66b97ba..94eeeca0e79 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -674,14 +674,11 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) 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. + * The ST Micro variant for SDIO write transfer sizes + * less then 8 bytes needs to have clock H/W flow + * control disabled. */ - if ((host->size <= variant->fifosize) && + if ((host->size < 8) && (data->flags & MMC_DATA_WRITE)) writel(readl(host->base + MMCICLOCK) & ~variant->clkreg_enable, -- cgit v1.2.3 From f4eada709e173ef485b5d590a52c3197bea6b68a Mon Sep 17 00:00:00 2001 From: Stefan Nilsson XK Date: Sat, 20 Nov 2010 11:07:28 +0100 Subject: mmc: mmci: Fix 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. ST-Ericsson Linux next: - ST-Ericsson ID: 401293 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ieb14fc792c43429bf396532ed403b1ecc0efcd74 Signed-off-by: Stefan Nilsson XK Signed-off-by: Ulf Hansson Signed-off-by: Fredrik Soderstedt Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/40802 Reviewed-by: QABUILD --- 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 94eeeca0e79..c92c4d37660 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -863,7 +863,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 send something that is + * not divisible by 4 (as opposed to card sectors + * etc). Therefore make sure to always read the last bytes + * while only doing full 32-bit reads towards the FIFO. + */ + if (unlikely(count & 0x3)) { + if (count < 4) { + unsigned char buf[4]; + readsl(base + MMCIFIFO, buf, 1); + memcpy(ptr, buf, count); + } else { + readsl(base + MMCIFIFO, ptr, count >> 2); + count &= ~0x3; + } + } else { + readsl(base + MMCIFIFO, ptr, count >> 2); + } ptr += count; remain -= count; -- cgit v1.2.3 From 24dcd7248c2cec2bf756c1534b292ef02d86aff4 Mon Sep 17 00:00:00 2001 From: Narayanan G Date: Mon, 5 Dec 2011 10:46:50 +0530 Subject: u5500: Move the SD/MMC card to different phy chan Currently, the eMMC and external SD/MMC card use same physical channels in the same event group. This patch moves the SD/MMC card to use a different eventgroup. There are several cross cases in which both these are used simultaneously. There seems to be an improvement in performace of SD card cross use-cases if these are in different channels. ST-Ericsson ID: 401481 ST-Ericsson FOSS-OUT ID: NA ST-Ericsson Linux next: NA Change-Id: I2661ce31177f0e637200ce7d65f7430b821e3292 Signed-off-by: Narayanan G Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/40898 Reviewed-by: QATOOLS Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/board-u5500-sdi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-ux500/board-u5500-sdi.c b/arch/arm/mach-ux500/board-u5500-sdi.c index a33bc0cd28a..5692c942512 100644 --- a/arch/arm/mach-ux500/board-u5500-sdi.c +++ b/arch/arm/mach-ux500/board-u5500-sdi.c @@ -99,7 +99,7 @@ static int u5500_sdi1_ios_handler(struct device *dev, struct mmc_ios *ios) 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, + .src_dev_type = DB5500_DMA_DEV34_SDMMC1_RX, .dst_dev_type = STEDMA40_DEV_DST_MEMORY, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, @@ -109,7 +109,7 @@ 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, + .dst_dev_type = DB5500_DMA_DEV34_SDMMC1_TX, .src_info.data_width = STEDMA40_WORD_WIDTH, .dst_info.data_width = STEDMA40_WORD_WIDTH, }; -- cgit v1.2.3 From 602713d6ce3726a832d4b00bb27bafde860d7885 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 28 Nov 2011 16:00:22 +0100 Subject: ARM: ux500: Use MMC_CAP2_NO_SLEEP_CMD for eMMC devices Prevent mmc core from sending sleep commands during suspend. ST-Ericsson ID: 399692 ST-Ericsson FOSS-OUT ID: NA Change-Id: I7969818579646eea6fc97055ab0b0cf4c324c05d Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/40056 Reviewed-by: Naveen Kumar GADDIPATI Reviewed-by: P.Hanumath PRASAD Tested-by: Naveen Kumar GADDIPATI Reviewed-by: Srinidhi KASAGAR Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44237 --- arch/arm/mach-ux500/board-mop500-sdi.c | 2 ++ arch/arm/mach-ux500/board-u5500-sdi.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 4ac7a9ad5fd..238f830d25a 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -207,6 +207,7 @@ static struct mmci_platform_data mop500_sdi2_data = { .f_max = 50000000, .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA | MMC_CAP_MMC_HIGHSPEED, + .capabilities2 = MMC_CAP2_NO_SLEEP_CMD, .gpio_cd = -1, .gpio_wp = -1, #ifdef CONFIG_STE_DMA40 @@ -245,6 +246,7 @@ static struct mmci_platform_data mop500_sdi4_data = { .f_max = 50000000, .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA | MMC_CAP_MMC_HIGHSPEED, + .capabilities2 = MMC_CAP2_NO_SLEEP_CMD, .gpio_cd = -1, .gpio_wp = -1, #ifdef CONFIG_STE_DMA40 diff --git a/arch/arm/mach-ux500/board-u5500-sdi.c b/arch/arm/mach-ux500/board-u5500-sdi.c index 5692c942512..d7fd6811ac4 100644 --- a/arch/arm/mach-ux500/board-u5500-sdi.c +++ b/arch/arm/mach-ux500/board-u5500-sdi.c @@ -51,6 +51,7 @@ static struct mmci_platform_data u5500_sdi0_data = { .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA | MMC_CAP_MMC_HIGHSPEED, + .capabilities2 = MMC_CAP2_NO_SLEEP_CMD, .gpio_cd = -1, .gpio_wp = -1, #ifdef CONFIG_STE_DMA40 @@ -160,6 +161,7 @@ static struct mmci_platform_data u5500_sdi2_data = { .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA | MMC_CAP_MMC_HIGHSPEED, + .capabilities2 = MMC_CAP2_NO_SLEEP_CMD, .gpio_cd = -1, .gpio_wp = -1, #ifdef CONFIG_STE_DMA40 -- cgit v1.2.3 From 12da152a7e084a43f0bda9b0c0d8ec011408b5ab Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 16 Jan 2012 11:11:37 +0100 Subject: u5500: sdi: deprecate v1 hardware DB5500 v1 is no longer supported and now the code makes the assumption that it runs on hardware revision >= v2. So, lets deprecate all the v1 codes. Signed-off-by: srinidhi kasagar --- arch/arm/mach-ux500/board-u5500-sdi.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/arch/arm/mach-ux500/board-u5500-sdi.c b/arch/arm/mach-ux500/board-u5500-sdi.c index d7fd6811ac4..101a69f2fbc 100644 --- a/arch/arm/mach-ux500/board-u5500-sdi.c +++ b/arch/arm/mach-ux500/board-u5500-sdi.c @@ -233,16 +233,9 @@ static void sdi1_configure(void) gpio_direction_output(pin[1], 0); } -#define SDI_PID_V1 0x00480180 -#define SDI_PID_V2 0x10480180 void __init u5500_sdi_init(void) { - u32 periphid = 0; - - if (cpu_is_u5500v1()) - periphid = SDI_PID_V1; - else - periphid = SDI_PID_V2; + u32 periphid = 0x10480180; /* * Fix me in 5500 v2.1 -- cgit v1.2.3 From 89495799643be634261e8b156bbece26027f3b5c Mon Sep 17 00:00:00 2001 From: Johan Rudholm Date: Mon, 9 Jan 2012 14:32:37 +0100 Subject: mmc: Add old-style pwr/perm boot part locking Add backwards-compatible sysfs nodes for locking the boot partitions power and permanently read-only. This is done by writing "pwr_ro" or "perm_ro" to /sys/block/mmcblkXbootY/ro_lock ST-Ericsson ID: 344197 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: NA Change-Id: Ibe1874bbb3b1b6ce9f7955537b5e083e97f69d11 Signed-off-by: Johan Rudholm Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44842 Reviewed-by: Ulf HANSSON --- drivers/mmc/card/block.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 1a0e399629e..3bf0d2c841b 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -108,6 +108,7 @@ struct mmc_blk_data { unsigned int part_curr; struct device_attribute force_ro; struct device_attribute power_ro_lock; + struct device_attribute power_ro_lock_legacy; int area_type; }; @@ -168,6 +169,87 @@ 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_ro_lock + & EXT_CSD_BOOT_WP_B_PERM_WP_EN) + out_text = EXT_CSD_BOOT_WP_PERM_WP_TEXT; + else if (card->ext_csd.boot_ro_lock + & 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, *part_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", ret); + else + card->ext_csd.boot_ro_lock = set; + + mmc_release_host(card->host); + + if (!ret) { + pr_info("%s: Locking boot partition " + "%s", + md->disk->disk_name, + buf); + set_disk_ro(md->disk, 1); + + list_for_each_entry(part_md, &md->part, part) + if (part_md->area_type == + MMC_BLK_DATA_AREA_BOOT) { + pr_info("%s: Locking boot partition " + "%s", + part_md->disk->disk_name, + buf); + set_disk_ro(part_md->disk, 1); + } + } + } + ret = count; + + mmc_blk_put(md); + return ret; +} + static ssize_t power_ro_lock_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1630,9 +1712,12 @@ static void mmc_blk_remove_req(struct mmc_blk_data *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) && - card->ext_csd.boot_ro_lockable) + card->ext_csd.boot_ro_lockable) { device_remove_file(disk_to_dev(md->disk), &md->power_ro_lock); + device_remove_file(disk_to_dev(md->disk), + &md->power_ro_lock_legacy); + } /* Stop new requests from getting into the queue */ del_gendisk(md->disk); @@ -1692,9 +1777,23 @@ static int mmc_add_disk(struct mmc_blk_data *md) &md->power_ro_lock); if (ret) goto power_ro_lock_fail; + + /* Legacy mode */ + mode = S_IRUGO | S_IWUSR; + + md->power_ro_lock_legacy.show = boot_partition_ro_lock_show; + md->power_ro_lock_legacy.store = boot_partition_ro_lock_store; + md->power_ro_lock_legacy.attr.mode = mode; + md->power_ro_lock_legacy.attr.name = "ro_lock"; + ret = device_create_file(disk_to_dev(md->disk), + &md->power_ro_lock_legacy); + if (ret) + goto power_ro_lock_fail_legacy; } return ret; +power_ro_lock_fail_legacy: + device_remove_file(disk_to_dev(md->disk), &md->power_ro_lock); power_ro_lock_fail: device_remove_file(disk_to_dev(md->disk), &md->force_ro); force_ro_fail: -- cgit v1.2.3 From 827e2c0b1e57631bbea02d25e997a5d826c23e42 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 5 Dec 2011 18:35:50 +0100 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. Tested-by: Linus Walleij Signed-off-by: Ulf Hansson Signed-off-by: Linus Walleij Change-Id: Ibdad8f4d9295b539928556e94c7652d3bdb66ddc Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44393 Reviewed-by: QABUILD --- 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 c92c4d37660..ed1c3d57312 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -211,14 +211,8 @@ 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); pm_runtime_put(mmc_dev(host->mmc)); 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 40ff8a03d4f933eb83a6cfcceae71a74f5a6c12b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 5 Dec 2011 18:35:55 +0100 Subject: mmc: mmci: Change from using legacy suspend This patch switch from using the legacy suspend/resume to the new way of registering PM callbacks. No functional change is done. Tested-by: Linus Walleij Signed-off-by: Ulf Hansson Signed-off-by: Linus Walleij Change-Id: I13a620c58d1c89f9d012503c40a51e7a79b1d663 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44394 Reviewed-by: QABUILD --- drivers/mmc/host/mmci.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index ed1c3d57312..b0b96d45ec1 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1523,10 +1523,11 @@ static int __devexit mmci_remove(struct amba_device *dev) return 0; } -#ifdef CONFIG_PM -static int mmci_suspend(struct amba_device *dev, pm_message_t state) +#ifdef CONFIG_SUSPEND +static int mmci_suspend(struct device *dev) { - struct mmc_host *mmc = amba_get_drvdata(dev); + struct amba_device *adev = to_amba_device(dev); + struct mmc_host *mmc = amba_get_drvdata(adev); int ret = 0; if (mmc) { @@ -1540,9 +1541,10 @@ static int mmci_suspend(struct amba_device *dev, pm_message_t state) return ret; } -static int mmci_resume(struct amba_device *dev) +static int mmci_resume(struct device *dev) { - struct mmc_host *mmc = amba_get_drvdata(dev); + struct amba_device *adev = to_amba_device(dev); + struct mmc_host *mmc = amba_get_drvdata(adev); int ret = 0; if (mmc) { @@ -1555,11 +1557,12 @@ static int mmci_resume(struct amba_device *dev) return ret; } -#else -#define mmci_suspend NULL -#define mmci_resume NULL #endif +static const struct dev_pm_ops mmci_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mmci_suspend, mmci_resume) +}; + static struct amba_id mmci_ids[] = { { .id = 0x00041180, @@ -1605,11 +1608,10 @@ MODULE_DEVICE_TABLE(amba, mmci_ids); static struct amba_driver mmci_driver = { .drv = { .name = DRIVER_NAME, + .pm = &mmci_dev_pm_ops, }, .probe = mmci_probe, .remove = __devexit_p(mmci_remove), - .suspend = mmci_suspend, - .resume = mmci_resume, .id_table = mmci_ids, }; -- cgit v1.2.3 From 80b82d2f7d457fa5fec7794dee47ecef073ddd3e Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 5 Dec 2011 18:35:56 +0100 Subject: mmc: mmci: Cache MMCICLOCK and MMCIPOWER register Instead of reading a register value everytime we need to apply a new value for it, maintain a cached copy for it. This also means we are able to skip writes that are not needed. Tested-by: Linus Walleij Signed-off-by: Ulf Hansson Signed-off-by: Linus Walleij Change-Id: I57fdb1aff8906334ff42d6d808c526e69a0203a4 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44395 Reviewed-by: Fredrik SODERSTEDT Reviewed-by: QABUILD Reviewed-by: Johan RUDHOLM --- drivers/mmc/host/mmci.c | 55 ++++++++++++++++++++++++++++++++----------------- drivers/mmc/host/mmci.h | 3 ++- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index b0b96d45ec1..7aaff1b5757 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -151,6 +151,28 @@ static int mmci_validate_data(struct mmci_host *host, return 0; } +/* + * This must be called with host->lock held + */ +static void mmci_write_clkreg(struct mmci_host *host, u32 clk) +{ + if (host->clk_reg != clk) { + host->clk_reg = clk; + writel(clk, host->base + MMCICLOCK); + } +} + +/* + * This must be called with host->lock held + */ +static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr) +{ + if (host->pwr_reg != pwr) { + host->pwr_reg = pwr; + writel(pwr, host->base + MMCIPOWER); + } +} + /* * This must be called with host->lock held */ @@ -198,7 +220,7 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) clk |= MCI_ST_8BIT_BUS; - writel(clk, host->base + MMCICLOCK); + mmci_write_clkreg(host, clk); } static void @@ -661,26 +683,25 @@ 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)) { - /* - * The ST Micro variants has a special bit - * to enable SDIO. - */ - datactrl |= MCI_ST_DPSM_SDIOEN; /* * The ST Micro variant for SDIO write transfer sizes * less then 8 bytes needs to have clock H/W flow * control disabled. */ - if ((host->size < 8) && - (data->flags & MMC_DATA_WRITE)) - writel(readl(host->base + MMCICLOCK) & - ~variant->clkreg_enable, - host->base + MMCICLOCK); + u32 clk; + if ((host->size < 8) && (data->flags & MMC_DATA_WRITE)) + clk = host->clk_reg & ~variant->clkreg_enable; else - writel(readl(host->base + MMCICLOCK) | - variant->clkreg_enable, - host->base + MMCICLOCK); + clk = host->clk_reg | variant->clkreg_enable; + + mmci_write_clkreg(host, clk); + + /* + * The ST Micro variants has a special bit + * to enable SDIO. + */ + datactrl |= MCI_ST_DPSM_SDIOEN; } /* @@ -1156,11 +1177,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) spin_lock_irqsave(&host->lock, flags); mmci_set_clkreg(host, ios->clock); - - if (host->pwr != pwr) { - host->pwr = pwr; - writel(pwr, host->base + MMCIPOWER); - } + mmci_write_pwrreg(host, pwr); spin_unlock_irqrestore(&host->lock, flags); } diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 1c05b191fa9..c2b33326547 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -186,7 +186,8 @@ struct mmci_host { unsigned int mclk; unsigned int cclk; - u32 pwr; + u32 pwr_reg; + u32 clk_reg; struct mmci_platform_data *plat; struct variant_data *variant; -- cgit v1.2.3 From ef128fade3498bcb48b7ab1dbb0238bcbe25ac2d Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 5 Dec 2011 18:35:57 +0100 Subject: mmc: mmci: Fixup use of runtime PM and use autosuspend Added use of runtime PM autosuspend feature, with a fixed timeout of 50 ms. This will prevent adding a latency, although very minor, for _every_ request. Moreover the runtime_get_sync is now also used in set_ios and suspend since the runtime resourses are needed here as well. Tested-by: Linus Walleij Signed-off-by: Ulf Hansson Signed-off-by: Linus Walleij Change-Id: Iabf2e109abb8a5a0459d77d813e04d8a2e9289c5 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44396 Reviewed-by: QABUILD --- drivers/mmc/host/mmci.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 7aaff1b5757..a7123b44762 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -233,8 +233,10 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq) host->mrq = NULL; host->cmd = NULL; - pm_runtime_put(mmc_dev(host->mmc)); mmc_request_done(host->mmc, mrq); + + pm_runtime_mark_last_busy(mmc_dev(host->mmc)); + pm_runtime_put_autosuspend(mmc_dev(host->mmc)); } static void mmci_set_mask1(struct mmci_host *host, unsigned int mask) @@ -1110,6 +1112,8 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) unsigned long flags; int ret; + pm_runtime_get_sync(mmc_dev(mmc)); + if (host->plat->ios_handler && host->plat->ios_handler(mmc_dev(mmc), ios)) dev_err(mmc_dev(mmc), "platform ios_handler failed\n"); @@ -1130,7 +1134,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * power should be rare so we print an error * and return here. */ - return; + goto out; } } /* @@ -1180,6 +1184,10 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) mmci_write_pwrreg(host, pwr); spin_unlock_irqrestore(&host->lock, flags); + + out: + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); } static int mmci_get_ro(struct mmc_host *mmc) @@ -1456,6 +1464,8 @@ static int __devinit mmci_probe(struct amba_device *dev, mmci_dma_setup(host); + pm_runtime_set_autosuspend_delay(&dev->dev, 50); + pm_runtime_use_autosuspend(&dev->dev); pm_runtime_put(&dev->dev); mmc_add_host(mmc); @@ -1551,8 +1561,10 @@ static int mmci_suspend(struct device *dev) struct mmci_host *host = mmc_priv(mmc); ret = mmc_suspend_host(mmc); - if (ret == 0) + if (ret == 0) { + pm_runtime_get_sync(dev); writel(0, host->base + MMCIMASK0); + } } return ret; @@ -1568,6 +1580,7 @@ static int mmci_resume(struct device *dev) struct mmci_host *host = mmc_priv(mmc); writel(MCI_IRQENABLE, host->base + MMCIMASK0); + pm_runtime_put(dev); ret = mmc_resume_host(mmc); } -- cgit v1.2.3 From 37d2d013111bd3045fdedade2284277ca924ed43 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 5 Dec 2011 18:35:58 +0100 Subject: mmc: mmci: Decrease current consumption in suspend To decrease current consumption in suspend state the VCORE regulator, the MCLK and PCLK for the ARM PL18x are now disabled. When resuming the resourses are re-enabled and register values for MMCICLOCK, MMCIPOWER and MMCIMASK0 are restored. Change-Id: I400237bf67fa37861d6fb47c4a2fd493b58c3a60 Tested-by: Linus Walleij Signed-off-by: Ulf Hansson Signed-off-by: Linus Walleij Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44397 Reviewed-by: QABUILD --- drivers/mmc/host/mmci.c | 62 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index a7123b44762..1af3785e669 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1551,6 +1551,58 @@ static int __devexit mmci_remove(struct amba_device *dev) } #ifdef CONFIG_SUSPEND +static int mmci_save(struct amba_device *dev) +{ + struct mmc_host *mmc = amba_get_drvdata(dev); + unsigned long flags; + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + + spin_lock_irqsave(&host->lock, flags); + + /* + * Make sure we do not get any interrupts when we disabled the + * clock and the regulator and as well make sure to clear the + * registers for clock and power. + */ + writel(0, host->base + MMCIMASK0); + writel(0, host->base + MMCIPOWER); + writel(0, host->base + MMCICLOCK); + + spin_unlock_irqrestore(&host->lock, flags); + + clk_disable(host->clk); + amba_vcore_disable(dev); + } + + return 0; +} + +static int mmci_restore(struct amba_device *dev) +{ + struct mmc_host *mmc = amba_get_drvdata(dev); + unsigned long flags; + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + + amba_vcore_enable(dev); + clk_enable(host->clk); + + spin_lock_irqsave(&host->lock, flags); + + /* Restore registers and re-enable interrupts. */ + writel(host->clk_reg, host->base + MMCICLOCK); + writel(host->pwr_reg, host->base + MMCIPOWER); + writel(MCI_IRQENABLE, host->base + MMCIMASK0); + + spin_unlock_irqrestore(&host->lock, flags); + } + + return 0; +} + static int mmci_suspend(struct device *dev) { struct amba_device *adev = to_amba_device(dev); @@ -1558,12 +1610,11 @@ static int mmci_suspend(struct device *dev) int ret = 0; if (mmc) { - struct mmci_host *host = mmc_priv(mmc); - ret = mmc_suspend_host(mmc); if (ret == 0) { pm_runtime_get_sync(dev); - writel(0, host->base + MMCIMASK0); + mmci_save(adev); + amba_pclk_disable(adev); } } @@ -1577,9 +1628,8 @@ static int mmci_resume(struct device *dev) int ret = 0; if (mmc) { - struct mmci_host *host = mmc_priv(mmc); - - writel(MCI_IRQENABLE, host->base + MMCIMASK0); + amba_pclk_enable(adev); + mmci_restore(adev); pm_runtime_put(dev); ret = mmc_resume_host(mmc); -- cgit v1.2.3 From 2b7bb72b6d3d827e2b2dbd67bb015dad54272fcf Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 5 Dec 2011 18:35:59 +0100 Subject: mmc: mmci: Implement PM runtime callbacks to save power In the runtime_suspend callback we make use of mmci_save to disable the VCORE regulator and the MCLK to decrease current consumption. At runtime resume, we use mmci_restore to re-enable the resourses again. From now on this will mean that especially the mmci_restore function must be fast to execute since otherwise request latency will be introduced. For the ARM Primcell PL180, the MMCIPOWER register is used to control an external power supply to the card. Thus we need to prevent the runtime callbacks from doing save and restore, otherwise the power to card will be cut. This is done by adding a new flag to the variant data. Tested-by: Linus Walleij Signed-off-by: Ulf Hansson Signed-off-by: Linus Walleij Change-Id: Ic6af2f59e57df82acb4be730bb855e1d39c9439b Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44398 Reviewed-by: QABUILD --- drivers/mmc/host/mmci.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 1af3785e669..a39ab5045ae 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -58,6 +58,7 @@ static unsigned int fmax = 515633; * @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 + * @pwrreg_ctrl_power: bits in MMCIPOWER register controls ext. power supply */ struct variant_data { unsigned int clkreg; @@ -72,6 +73,7 @@ struct variant_data { bool non_power_of_2_blksize; u32 pwrreg_powerup; bool signal_direction; + bool pwrreg_ctrl_power; }; static struct variant_data variant_arm = { @@ -79,6 +81,7 @@ static struct variant_data variant_arm = { .fifohalfsize = 8 * 4, .datalength_bits = 16, .pwrreg_powerup = MCI_PWR_UP, + .pwrreg_ctrl_power = true, }; static struct variant_data variant_arm_extended_fifo = { @@ -86,6 +89,7 @@ static struct variant_data variant_arm_extended_fifo = { .fifohalfsize = 64 * 4, .datalength_bits = 16, .pwrreg_powerup = MCI_PWR_UP, + .pwrreg_ctrl_power = true, }; static struct variant_data variant_u300 = { @@ -1550,7 +1554,7 @@ static int __devexit mmci_remove(struct amba_device *dev) return 0; } -#ifdef CONFIG_SUSPEND +#if defined(CONFIG_SUSPEND) || defined(CONFIG_PM_RUNTIME) static int mmci_save(struct amba_device *dev) { struct mmc_host *mmc = amba_get_drvdata(dev); @@ -1602,7 +1606,9 @@ static int mmci_restore(struct amba_device *dev) return 0; } +#endif +#ifdef CONFIG_SUSPEND static int mmci_suspend(struct device *dev) { struct amba_device *adev = to_amba_device(dev); @@ -1639,8 +1645,43 @@ static int mmci_resume(struct device *dev) } #endif +#ifdef CONFIG_PM_RUNTIME +static int mmci_runtime_suspend(struct device *dev) +{ + struct amba_device *adev = to_amba_device(dev); + struct mmc_host *mmc = amba_get_drvdata(adev); + int ret = 0; + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + struct variant_data *variant = host->variant; + if (!variant->pwrreg_ctrl_power) + ret = mmci_save(adev); + } + + return ret; +} + +static int mmci_runtime_resume(struct device *dev) +{ + struct amba_device *adev = to_amba_device(dev); + struct mmc_host *mmc = amba_get_drvdata(adev); + int ret = 0; + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + struct variant_data *variant = host->variant; + if (!variant->pwrreg_ctrl_power) + ret = mmci_restore(adev); + } + + return ret; +} +#endif + static const struct dev_pm_ops mmci_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(mmci_suspend, mmci_resume) + SET_RUNTIME_PM_OPS(mmci_runtime_suspend, mmci_runtime_resume, NULL) }; static struct amba_id mmci_ids[] = { -- cgit v1.2.3 From bea57566463fda0f60145e37113c0d8552aae791 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 5 Dec 2011 18:36:00 +0100 Subject: mmc: mmci: Use ios_handler to save power To disable a levelshifter when we are in an idle state will decrease current consumption. We make use of the ios_handler at runtime suspend and at regular suspend to accomplish this. Of course depending on the used levelshifter the decrease of current differs. For ST6G3244ME the value is up to ~200 uA. Tested-by: Linus Walleij Signed-off-by: Ulf Hansson Signed-off-by: Linus Walleij Change-Id: Ie782d3af7932730c442dcb6c7f12b2f69dcac75e Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44399 Reviewed-by: QABUILD Reviewed-by: QATEST --- 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 a39ab5045ae..973f8600223 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1559,10 +1559,22 @@ static int mmci_save(struct amba_device *dev) { struct mmc_host *mmc = amba_get_drvdata(dev); unsigned long flags; + struct mmc_ios ios; + int ret = 0; if (mmc) { struct mmci_host *host = mmc_priv(mmc); + /* Let the ios_handler act on a POWER_OFF to save power. */ + if (host->plat->ios_handler) { + memcpy(&ios, &mmc->ios, sizeof(struct mmc_ios)); + ios.power_mode = MMC_POWER_OFF; + ret = host->plat->ios_handler(mmc_dev(mmc), + &ios); + if (ret) + return ret; + } + spin_lock_irqsave(&host->lock, flags); /* @@ -1580,7 +1592,7 @@ static int mmci_save(struct amba_device *dev) amba_vcore_disable(dev); } - return 0; + return ret; } static int mmci_restore(struct amba_device *dev) @@ -1602,6 +1614,11 @@ static int mmci_restore(struct amba_device *dev) writel(MCI_IRQENABLE, host->base + MMCIMASK0); spin_unlock_irqrestore(&host->lock, flags); + + /* Restore settings done by the ios_handler. */ + if (host->plat->ios_handler) + host->plat->ios_handler(mmc_dev(mmc), + &mmc->ios); } return 0; -- cgit v1.2.3 From 83630c6ad5fcb259bdabad0ac4cfec592c70b8cc Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Tue, 31 Jan 2012 14:36:51 +0530 Subject: mmc: init sysfs attr to fix lockdep warnings BUG: key df230ba4 not in .data! ------------[ cut here ]------------ WARNING: at kernel/lockdep.c:2885 sysfs_add_file_mode+0x4c/0xb0() Modules linked in: [] (unwind_backtrace+0x0/0xec) from [] (warn_slowpath_common+0x4c/0x64) [] (warn_slowpath_common+0x4c/0x64) from [] (warn_slowpath_null+0x18/0x1c) [] (warn_slowpath_null+0x18/0x1c) from [] (sysfs_add_file_mode+0x4c/0xb0) [] (sysfs_add_file_mode+0x4c/0xb0) from [] (mmc_add_disk+0xb8/0x154) [] (mmc_add_disk+0xb8/0x154) from [] (mmc_blk_probe+0xe8/0x138) [] (mmc_blk_probe+0xe8/0x138) from [] (mmc_bus_probe+0x18/0x1c) [] (mmc_bus_probe+0x18/0x1c) from [] (really_probe+0x98/0x148) [] (really_probe+0x98/0x148) from [] (driver_probe_device+0x48/0x60) [] (driver_probe_device+0x48/0x60) from [] (bus_for_each_drv+0x44/0x74) [] (bus_for_each_drv+0x44/0x74) from [] (device_attach+0x78/0xa4) [] (device_attach+0x78/0xa4) from [] (bus_probe_device+0x24/0x40) [] (bus_probe_device+0x24/0x40) from [] (device_add+0x174/0x2b4) [] (device_add+0x174/0x2b4) from [] (mmc_add_card+0x138/0x188) [] (mmc_add_card+0x138/0x188) from [] (mmc_attach_mmc+0x108/0x178) [] (mmc_attach_mmc+0x108/0x178) from [] (mmc_rescan_try_freq+0x5c/0x7c) [] (mmc_rescan_try_freq+0x5c/0x7c) from [] (mmc_rescan+0x1c8/0x20c) [] (mmc_rescan+0x1c8/0x20c) from [] (process_one_work+0x2ac/0x4c8) [] (process_one_work+0x2ac/0x4c8) from [] (worker_thread+0x144/0x234) [] (worker_thread+0x144/0x234) from [] (kthread+0x80/0x88) [] (kthread+0x80/0x88) from [] (kernel_thread_exit+0x0/0x8) ---[ end trace 90d62d7618c6b90e ]--- Change-Id: I123e84b08e085d1b562674533417cb43f295095d Signed-off-by: Rabin Vincent ST-Ericsson ID: 413918 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I216974bfa1323d3a390ee8e8acc029c13a9525f5 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/47172 Tested-by: Rabin VINCENT Reviewed-by: QABUILD Reviewed-by: Johan RUDHOLM Reviewed-by: Rabin VINCENT --- drivers/mmc/card/block.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 3bf0d2c841b..6b7ab103da2 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1783,6 +1783,7 @@ static int mmc_add_disk(struct mmc_blk_data *md) md->power_ro_lock_legacy.show = boot_partition_ro_lock_show; md->power_ro_lock_legacy.store = boot_partition_ro_lock_store; + sysfs_attr_init(&md->power_ro_lock_legacy.attr); md->power_ro_lock_legacy.attr.mode = mode; md->power_ro_lock_legacy.attr.name = "ro_lock"; ret = device_create_file(disk_to_dev(md->disk), -- cgit v1.2.3 From b3d6f6eb73d909ab00d7613043d557ba6f95dafd Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 2 Feb 2012 15:10:29 +0100 Subject: mmc: mmci: Cleanup code for DMA handling The cookie is now used to indicate if dma_unmap_sg shall be done in post_request. At DMA errors, the DMA job is immediately not only terminated but also unmapped. To indicate that this has been done the cookie is reset to zero. post_request will thus only do dma_umap_sg for requests which has a cookie not set to zero. Some corresponding duplicated code could then be removed and moreover some minor corrections at DMA errors for terminating the same DMA job twice has also been fixed. ST-Ericsson Linux next: N/A ST-Ericsson ID: 371812 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I2fedfc906ec861ab431f43c333237adb85f2fa99 Signed-off-by: Per Forlin Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/47628 --- drivers/mmc/host/mmci.c | 153 ++++++++++++++++++++++++------------------------ 1 file changed, 75 insertions(+), 78 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 973f8600223..031300ee743 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -374,10 +374,31 @@ static inline void mmci_dma_release(struct mmci_host *host) host->dma_rx_channel = host->dma_tx_channel = NULL; } +static void mmci_dma_data_error(struct mmci_host *host) +{ + dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n"); + dmaengine_terminate_all(host->dma_current); + host->data->host_cookie = 0; +} + static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) { - struct dma_chan *chan = host->dma_current; + struct dma_chan *chan; enum dma_data_direction dir; + + if (data->flags & MMC_DATA_READ) { + dir = DMA_FROM_DEVICE; + chan = host->dma_rx_channel; + } else { + dir = DMA_TO_DEVICE; + chan = host->dma_tx_channel; + } + + dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); +} + +static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) +{ u32 status; int i; @@ -396,19 +417,14 @@ static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) * contiguous buffers. On TX, we'll get a FIFO underrun error. */ if (status & MCI_RXDATAAVLBLMASK) { - dmaengine_terminate_all(chan); - if (!data->error) + if (!data->error) { data->error = -EIO; - } - - if (data->flags & MMC_DATA_WRITE) { - dir = DMA_TO_DEVICE; - } else { - dir = DMA_FROM_DEVICE; + mmci_dma_data_error(host); + } } if (!data->host_cookie) - dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); + mmci_dma_unmap(host, data); /* * Use of DMA with scatter-gather is impossible. @@ -420,14 +436,10 @@ static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) } } -static void mmci_dma_data_error(struct mmci_host *host) -{ - dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n"); - dmaengine_terminate_all(host->dma_current); -} - -static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, - struct mmci_host_next *next) +/* prepares DMA channel and DMA descriptor, returns non-zero on failure */ +static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, + struct dma_chan **dma_chan, + struct dma_async_tx_descriptor **dma_desc) { struct variant_data *variant = host->variant; struct dma_slave_config conf = { @@ -444,16 +456,6 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, enum dma_data_direction buffer_dirn; int nr_sg; - /* Check if next job is already prepared */ - if (data->host_cookie && !next && - host->dma_current && host->dma_desc_current) - return 0; - - if (!next) { - host->dma_current = NULL; - host->dma_desc_current = NULL; - } - if (data->flags & MMC_DATA_READ) { conf.direction = DMA_DEV_TO_MEM; buffer_dirn = DMA_FROM_DEVICE; @@ -487,30 +489,42 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, if (!desc) goto unmap_exit; - if (next) { - next->dma_chan = chan; - next->dma_desc = desc; - } else { - host->dma_current = chan; - host->dma_desc_current = desc; - } + *dma_chan = chan; + *dma_desc = desc; return 0; unmap_exit: - if (!next) - dmaengine_terminate_all(chan); dma_unmap_sg(device->dev, data->sg, data->sg_len, buffer_dirn); return -ENOMEM; } +static int inline mmci_dma_prep_data(struct mmci_host *host, + struct mmc_data *data) +{ + /* Check if next job is already prepared. */ + if (host->dma_current && host->dma_desc_current) + return 0; + + /* No job were prepared thus do it now. */ + return __mmci_dma_prep_data(host, data, &host->dma_current, + &host->dma_desc_current); +} + +static inline int mmci_dma_prep_next(struct mmci_host *host, + struct mmc_data *data) +{ + struct mmci_host_next *nd = &host->next_data; + return __mmci_dma_prep_data(host, data, &nd->dma_chan, &nd->dma_desc); +} + static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) { int ret; struct mmc_data *data = host->data; struct variant_data *variant = host->variant; - ret = mmci_dma_prep_data(host, host->data, NULL); + ret = mmci_dma_prep_data(host, host->data); if (ret) return ret; @@ -546,18 +560,14 @@ static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data) struct mmci_host_next *next = &host->next_data; if (data->host_cookie && data->host_cookie != next->cookie) { - pr_warning("[%s] invalid cookie: data->host_cookie %d" + pr_err("[%s] invalid cookie: data->host_cookie %d" " host->next_data.cookie %d\n", __func__, data->host_cookie, host->next_data.cookie); - data->host_cookie = 0; + BUG(); } - if (!data->host_cookie) - return; - host->dma_desc_current = next->dma_desc; host->dma_current = next->dma_chan; - next->dma_desc = NULL; next->dma_chan = NULL; } @@ -572,22 +582,13 @@ static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq, if (!data) return; - if (mmci_validate_data(host, mrq->data)) - return; + BUG_ON(data->host_cookie); - if (data->host_cookie) { - data->host_cookie = 0; + if (mmci_validate_data(host, data)) return; - } - /* if config for dma */ - if (((data->flags & MMC_DATA_WRITE) && host->dma_tx_channel) || - ((data->flags & MMC_DATA_READ) && host->dma_rx_channel)) { - if (mmci_dma_prep_data(host, data, nd)) - data->host_cookie = 0; - else - data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie; - } + if (!mmci_dma_prep_next(host, data)) + data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie; } static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq, @@ -595,29 +596,19 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq, { struct mmci_host *host = mmc_priv(mmc); struct mmc_data *data = mrq->data; - struct dma_chan *chan; - enum dma_data_direction dir; - if (!data) + if (!data || !data->host_cookie) return; - if (data->flags & MMC_DATA_READ) { - dir = DMA_FROM_DEVICE; - chan = host->dma_rx_channel; - } else { - dir = DMA_TO_DEVICE; - chan = host->dma_tx_channel; - } - + mmci_dma_unmap(host, data); - /* if config for dma */ - if (chan) { - if (err) - dmaengine_terminate_all(chan); - if (data->host_cookie) - dma_unmap_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, dir); - mrq->data->host_cookie = 0; + if (err) { + struct dma_chan *chan; + if (data->flags & MMC_DATA_READ) + chan = host->dma_rx_channel; + else + chan = host->dma_tx_channel; + dmaengine_terminate_all(chan); } } @@ -638,6 +629,10 @@ static inline void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) { } +static inline void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) +{ +} + static inline void mmci_dma_data_error(struct mmci_host *host) { } @@ -821,7 +816,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, if (status & MCI_DATAEND || data->error) { if (dma_inprogress(host)) - mmci_dma_unmap(host, data); + mmci_dma_finalize(host, data); mmci_stop_data(host); if (!data->error) @@ -858,8 +853,10 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, if (!cmd->data || cmd->error) { if (host->data) { /* Terminate the DMA transfer */ - if (dma_inprogress(host)) + if (dma_inprogress(host)) { mmci_dma_data_error(host); + mmci_dma_unmap(host, host->data); + } mmci_stop_data(host); } mmci_request_end(host, cmd->mrq); -- cgit v1.2.3 From b9120af71490ca2e626f3a39a2147067b47116ff Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 22 Feb 2012 10:23:55 +0100 Subject: mmc: mmci: Do not finalize dma job at data error irq mmci_dma_finalize must not be executed when a data error irq has been received. We do mmci_dma_unmap directly instead and moreover make sure the dma handles are reset as soon as the dma job is done/terminated. Change-Id: Ib4ed3f380f4383cb2a28613552569b950c5204a5 Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/49925 Reviewed-by: Per FORLIN --- drivers/mmc/host/mmci.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 031300ee743..fc536304e14 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -378,6 +378,8 @@ static void mmci_dma_data_error(struct mmci_host *host) { dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n"); dmaengine_terminate_all(host->dma_current); + host->dma_current = NULL; + host->dma_desc_current = NULL; host->data->host_cookie = 0; } @@ -417,10 +419,8 @@ static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) * contiguous buffers. On TX, we'll get a FIFO underrun error. */ if (status & MCI_RXDATAAVLBLMASK) { - if (!data->error) { - data->error = -EIO; - mmci_dma_data_error(host); - } + data->error = -EIO; + mmci_dma_data_error(host); } if (!data->host_cookie) @@ -434,6 +434,9 @@ static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) dev_err(mmc_dev(host->mmc), "buggy DMA detected. Taking evasive action.\n"); mmci_dma_release(host); } + + host->dma_current = NULL; + host->dma_desc_current = NULL; } /* prepares DMA channel and DMA descriptor, returns non-zero on failure */ @@ -776,8 +779,10 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, u32 remain, success; /* Terminate the DMA transfer */ - if (dma_inprogress(host)) + if (dma_inprogress(host)) { mmci_dma_data_error(host); + mmci_dma_unmap(host, data); + } /* * Calculate how far we are into the transfer. Note that -- cgit v1.2.3 From f9a6c36974e60dca6598f27f045448fcf5cbaa87 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 22 Feb 2012 10:57:24 +0100 Subject: ARM: u8500: board-sdi: Remove not needed ocr_mask For sdi0 and sdi4 there are external regulators connected to the cards ("vmmc"). These will be used to find out the supported ocr_masks. Change-Id: I67b80bc14c78a45866aa4b2d5f07903d2ee414d3 Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/49937 Reviewed-by: Jonas ABERG Reviewed-by: QABUILD --- arch/arm/mach-ux500/board-mop500-sdi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 238f830d25a..94c6f6ad311 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -95,7 +95,6 @@ static struct stedma40_chan_cfg mop500_sdi0_dma_cfg_tx = { static struct mmci_platform_data mop500_sdi0_data = { .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 | @@ -242,7 +241,6 @@ static struct stedma40_chan_cfg mop500_sdi4_dma_cfg_tx = { #endif static struct mmci_platform_data mop500_sdi4_data = { - .ocr_mask = MMC_VDD_29_30, .f_max = 50000000, .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA | MMC_CAP_MMC_HIGHSPEED, -- cgit v1.2.3 From 7ad16a2df65271381f0d66d248ae5587e5fce63c Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 24 Feb 2012 15:37:27 +0100 Subject: mmc: mmci: Fixup post error handling for dma The next pointers for the next dma job needs to be reset when an error occurs. Change-Id: Ieae63ccb97b11c0f6edb7b383c84259082b83760 Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/50454 Reviewed-by: Per FORLIN Reviewed-by: QABUILD --- drivers/mmc/host/mmci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index fc536304e14..964301c7db0 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -606,12 +606,16 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq, mmci_dma_unmap(host, data); if (err) { + struct mmci_host_next *next = &host->next_data; struct dma_chan *chan; if (data->flags & MMC_DATA_READ) chan = host->dma_rx_channel; else chan = host->dma_tx_channel; dmaengine_terminate_all(chan); + + next->dma_desc = NULL; + next->dma_chan = NULL; } } -- cgit v1.2.3 From fad33da84fa8ba04c8114443076bcf6191df44d0 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 24 Feb 2012 11:22:42 +0100 Subject: mmc: mmci: Add support for start_signal_voltage_switch This function switches I/O voltage, which is needed to support UHS-I cards with 1.8V I/O. Change-Id: Id33a9c6e15bd4ba598e533bf9115cb7583369706 Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/50383 Reviewed-by: Per FORLIN --- drivers/mmc/host/mmci.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 964301c7db0..5b1a083e20a 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1232,6 +1232,21 @@ static int mmci_get_cd(struct mmc_host *mmc) return status; } +static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mmci_host *host = mmc_priv(mmc); + int ret = 0; + + if (host->plat->ios_handler) { + pm_runtime_get_sync(mmc_dev(mmc)); + ret = host->plat->ios_handler(mmc_dev(mmc), ios); + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + } + + return ret; +} + static irqreturn_t mmci_cd_irq(int irq, void *dev_id) { struct mmci_host *host = dev_id; @@ -1248,6 +1263,7 @@ static const struct mmc_host_ops mmci_ops = { .set_ios = mmci_set_ios, .get_ro = mmci_get_ro, .get_cd = mmci_get_cd, + .start_signal_voltage_switch = mmci_sig_volt_switch, }; static int __devinit mmci_probe(struct amba_device *dev, -- cgit v1.2.3 From eb3dbd9420af66022c3f8cd1aed5fdc684490421 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 24 Feb 2012 11:29:27 +0100 Subject: ARM: u8500: board-sdi: ios_handler handles voltage switch The ios_handler, which is used for SD-cards, is now able to switch voltage level between 3V and 1.8V. This is needed to support UHS-I card which supports 1.8V I/O. Change-Id: I3f2d467a384c6b672064ec1d993c6d8b1acba7e5 Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/50398 Reviewed-by: QABUILD Reviewed-by: Per FORLIN --- arch/arm/mach-ux500/board-mop500-sdi.c | 43 +++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 94c6f6ad311..754ef06a8a7 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -38,8 +38,37 @@ static int sdi0_vsel = -1; static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios) { - static int power_mode = -1; + static unsigned char power_mode = MMC_POWER_ON; + static unsigned char signal_voltage = MMC_SIGNAL_VOLTAGE_330; + if (signal_voltage == ios->signal_voltage) + goto do_power; + + /* + * We need to re-init the levelshifter when switching I/O voltage level. + * Max discharge time according to ST6G3244ME spec is 1 ms. + */ + if (power_mode == MMC_POWER_ON) { + power_mode = MMC_POWER_OFF; + gpio_direction_output(sdi0_en, 0); + msleep(1); + } + + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + gpio_direction_output(sdi0_vsel, 0); + break; + case MMC_SIGNAL_VOLTAGE_180: + gpio_direction_output(sdi0_vsel, 1); + break; + default: + pr_warning("Non supported signal voltage for levelshifter.\n"); + break; + } + + signal_voltage = ios->signal_voltage; + +do_power: if (power_mode == ios->power_mode) return 0; @@ -47,25 +76,17 @@ static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios) case MMC_POWER_UP: break; 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_direction_output(sdi0_vsel, 0); gpio_direction_output(sdi0_en, 1); + /* Max settling time according to ST6G3244ME spec is 100 us. */ udelay(100); break; case MMC_POWER_OFF: - gpio_direction_output(sdi0_vsel, 0); gpio_direction_output(sdi0_en, 0); break; } power_mode = ios->power_mode; + return 0; } -- cgit v1.2.3 From ec46eab73045afc4d2a26a21a36b09dad4ae6707 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 1 Mar 2012 12:48:48 +0100 Subject: ARM: u8500: board-sdi: Enable SDR12 and SDR25 for SD-card Enable support for SDR12 and SDR25 for UHS-I cards. This means 1.8V I/O voltage will be used instead of the default 3V. Change-Id: If154c3797549d3cbedccafcc4b3de75dfd035879 Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/51101 Reviewed-by: QABUILD Reviewed-by: Per FORLIN --- arch/arm/mach-ux500/board-mop500-sdi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 754ef06a8a7..ec88d0ff0fd 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -119,7 +119,9 @@ static struct mmci_platform_data mop500_sdi0_data = { .f_max = 50000000, .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_MMC_HIGHSPEED, + MMC_CAP_MMC_HIGHSPEED | + MMC_CAP_UHS_SDR12 | + MMC_CAP_UHS_SDR25, .gpio_wp = -1, .sigdir = MCI_ST_FBCLKEN | MCI_ST_CMDDIREN | -- cgit v1.2.3 From caec39e2259b403bc17173f5e3e6921e2b120cd5 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Tue, 28 Feb 2012 11:24:11 +0100 Subject: ARM: u8500: SDI: enable ERASE_CAP on sdi2 Tested with iozone and should give around 5-20% increased write performance for continuous writes. Random writes a bit less improvements. Change-Id: Ia48f8084fabeb6b927f4bb0bdf88c39da05c8f58 Signed-off-by: Per Forlin Signed-off-by: Johan RUDHOLM Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/50692 --- arch/arm/mach-ux500/board-mop500-sdi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index ec88d0ff0fd..13a55eb039b 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -228,7 +228,8 @@ 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 | - MMC_CAP_MMC_HIGHSPEED, + MMC_CAP_MMC_HIGHSPEED | + MMC_CAP_ERASE, .capabilities2 = MMC_CAP2_NO_SLEEP_CMD, .gpio_cd = -1, .gpio_wp = -1, -- cgit v1.2.3 From 4aa4b727ba8e1ff06c6d5f6559c91a68d0199b4a Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 29 Feb 2012 16:03:21 +0100 Subject: mmc: core: Clean up after mmc_pre_req if card was removed Make sure mmc_start_req cancel the prepared job, if the request was prevented to be started due to the card has been removed. This bug was introduced in commit: mmc: allow upper layers to know immediately if card has been removed Change-Id: I5e35e668bd3715c62081e725eca8eb073d19e9a6 Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/51442 Reviewed-by: Per FORLIN --- drivers/mmc/core/core.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 273de4191d4..976856af759 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -243,16 +243,17 @@ static void mmc_wait_done(struct mmc_request *mrq) complete(&mrq->completion); } -static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) +static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) { init_completion(&mrq->completion); mrq->done = mmc_wait_done; if (mmc_card_removed(host->card)) { mrq->cmd->error = -ENOMEDIUM; complete(&mrq->completion); - return; + return -ENOMEDIUM; } mmc_start_request(host, mrq); + return 0; } static void mmc_wait_for_req_done(struct mmc_host *host, @@ -336,6 +337,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, struct mmc_async_req *areq, int *error) { int err = 0; + int start_err = 0; struct mmc_async_req *data = host->areq; /* Prepare a new request */ @@ -345,30 +347,23 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, if (host->areq) { 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; - goto out; - } } - if (areq) - __mmc_start_req(host, areq->mrq); + if (!err && areq) + start_err = __mmc_start_req(host, areq->mrq); if (host->areq) mmc_post_req(host, host->areq->mrq, 0); - host->areq = areq; - out: + /* Cancel a prepared request if it was not started. */ + if ((err || start_err) && areq) + mmc_post_req(host, areq->mrq, -EINVAL); + + if (err) + host->areq = NULL; + else + host->areq = areq; + if (error) *error = err; return data; -- cgit v1.2.3 From fc2c81f74e3f55a5550c9360a937735a3988bf3e Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Sat, 24 Sep 2011 12:14:56 +0200 Subject: ARM: mmci: DMA preparations in parellel with cmd Do DMA preparations after start_command() instead of before. DMA is being prepared while the controller process the read/write cmd. This flow will decrease the start up latency which improves the throughput, especially for small transfers. ST-Ericsson Linux next: N/A ST-Ericsson ID: 371812 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I98db024658cac5fe6ee09e4250c31e53a643bead Signed-off-by: Per Forlin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36180 Reviewed-by: QABUILD Reviewed-by: Ulf HANSSON --- drivers/mmc/host/mmci.c | 79 +++++++++++++++++++++++++++++++++---------------- drivers/mmc/host/mmci.h | 1 + 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 5b1a083e20a..6e501f4b281 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -264,6 +264,7 @@ static void mmci_stop_data(struct mmci_host *host) writel(0, host->base + MMCIDATACTRL); mmci_set_mask1(host, 0); host->data = NULL; + host->datactrl_reg = 0; } static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) @@ -374,13 +375,13 @@ static inline void mmci_dma_release(struct mmci_host *host) host->dma_rx_channel = host->dma_tx_channel = NULL; } -static void mmci_dma_data_error(struct mmci_host *host) +static void mmci_dma_data_error(struct mmci_host *host, struct mmc_data *data) { dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n"); dmaengine_terminate_all(host->dma_current); host->dma_current = NULL; host->dma_desc_current = NULL; - host->data->host_cookie = 0; + data->host_cookie = 0; } static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) @@ -420,7 +421,7 @@ static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) */ if (status & MCI_RXDATAAVLBLMASK) { data->error = -EIO; - mmci_dma_data_error(host); + mmci_dma_data_error(host, data); } if (!data->host_cookie) @@ -521,7 +522,7 @@ static inline int mmci_dma_prep_next(struct mmci_host *host, return __mmci_dma_prep_data(host, data, &nd->dma_chan, &nd->dma_desc); } -static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) +static int mmci_dma_start_data(struct mmci_host *host) { int ret; struct mmc_data *data = host->data; @@ -538,15 +539,15 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) dmaengine_submit(host->dma_desc_current); dma_async_issue_pending(host->dma_current); - datactrl |= MCI_DPSM_DMAENABLE; + host->datactrl_reg |= MCI_DPSM_DMAENABLE; /* Some hardware versions need special flags for SDIO DMA write */ if (variant->sdio && host->mmc->card && mmc_card_sdio(host->mmc->card) && (data->flags & MMC_DATA_WRITE)) - datactrl |= variant->dma_sdio_req_ctrl; + host->datactrl_reg |= variant->dma_sdio_req_ctrl; /* Trigger the DMA transfer */ - writel(datactrl, host->base + MMCIDATACTRL); + writel(host->datactrl_reg, host->base + MMCIDATACTRL); /* * Let the MMCI say when the data is ended and it's time @@ -640,11 +641,16 @@ static inline void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *da { } -static inline void mmci_dma_data_error(struct mmci_host *host) +static inline void mmci_dma_data_error(struct mmci_host *host, struct mmc_data *data) { } -static inline int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) +static inline int mmci_dma_start_data(struct mmci_host *host) +{ + return -ENOSYS; +} + +static inline int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data) { return -ENOSYS; } @@ -654,10 +660,10 @@ static inline int mmci_dma_start_data(struct mmci_host *host, unsigned int datac #endif -static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) +static void mmci_setup_datactrl(struct mmci_host *host, struct mmc_data *data) { struct variant_data *variant = host->variant; - unsigned int datactrl, timeout, irqmask; + unsigned int datactrl, timeout; unsigned long long clks; void __iomem *base; int blksz_bits; @@ -711,12 +717,21 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) */ datactrl |= MCI_ST_DPSM_SDIOEN; } + host->datactrl_reg = datactrl; + writel(datactrl, base + MMCIDATACTRL); +} + +static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) +{ + unsigned int irqmask; + struct variant_data *variant = host->variant; + void __iomem *base = host->base; /* * Attempt to use DMA operation mode, if this * should fail, fall back to PIO mode */ - if (!mmci_dma_start_data(host, datactrl)) + if (!mmci_dma_start_data(host)) return; /* IRQ mode, map the SG list for CPU reading/writing */ @@ -740,7 +755,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) irqmask = MCI_TXFIFOHALFEMPTYMASK; } - writel(datactrl, base + MMCIDATACTRL); writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0); mmci_set_mask1(host, irqmask); } @@ -784,7 +798,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, /* Terminate the DMA transfer */ if (dma_inprogress(host)) { - mmci_dma_data_error(host); + mmci_dma_data_error(host, data); mmci_dma_unmap(host, data); } @@ -860,16 +874,16 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, } if (!cmd->data || cmd->error) { - if (host->data) { - /* Terminate the DMA transfer */ - if (dma_inprogress(host)) { - mmci_dma_data_error(host); - mmci_dma_unmap(host, host->data); - } - mmci_stop_data(host); + /* Terminate the DMA transfer */ + if (dma_inprogress(host)) { + mmci_dma_data_error(host, host->mrq->data); + mmci_dma_unmap(host, host->mrq->data); } + if (host->data) + mmci_stop_data(host); mmci_request_end(host, cmd->mrq); } else if (!(cmd->data->flags & MMC_DATA_READ)) { + mmci_setup_datactrl(host, cmd->data); mmci_start_data(host, cmd->data); } } @@ -1088,6 +1102,7 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct mmci_host *host = mmc_priv(mmc); unsigned long flags; + bool dmaprep_after_cmd = false; WARN_ON(host->mrq != NULL); @@ -1103,14 +1118,28 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) host->mrq = mrq; - if (mrq->data) + if (mrq->data) { + dmaprep_after_cmd = + (host->variant->clkreg_enable && + (mrq->data->flags & MMC_DATA_READ)) || + !(mrq->data->flags & MMC_DATA_READ); mmci_get_next_data(host, mrq->data); - - if (mrq->data && mrq->data->flags & MMC_DATA_READ) - mmci_start_data(host, mrq->data); + if (mrq->data->flags & MMC_DATA_READ) { + mmci_setup_datactrl(host, mrq->data); + if (!dmaprep_after_cmd) + mmci_start_data(host, mrq->data); + } + } mmci_start_command(host, mrq->cmd, 0); + if (mrq->data && dmaprep_after_cmd) { + mmci_dma_prep_data(host, mrq->data); + + if (mrq->data->flags & MMC_DATA_READ) + mmci_start_data(host, mrq->data); + } + spin_unlock_irqrestore(&host->lock, flags); } diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index c2b33326547..ba09a603787 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -188,6 +188,7 @@ struct mmci_host { unsigned int cclk; u32 pwr_reg; u32 clk_reg; + u32 datactrl_reg; struct mmci_platform_data *plat; struct variant_data *variant; -- cgit v1.2.3 From 5a0a045d11b9e80160bc04736f323efe4cc7cb1e Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 2 Mar 2012 10:49:05 +0100 Subject: ARM: u8500: board-sdi: Use SLEEP cmd for sdi4 (eMMC) According to the eMMC spec the card needs to be put in sleep state before the VCC power is cut to the card. Sandisk iNAND will not be able to re-initialized in a resume unless this sequence is followed. ST-Ericsson Linux next: NA ST-Ericsson ID: 417815 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Id41cc0d2d66d87621889bc5b6b4e98fcaaba2286 Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/51463 --- arch/arm/mach-ux500/board-mop500-sdi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 13a55eb039b..118d29b863f 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -268,7 +268,6 @@ static struct mmci_platform_data mop500_sdi4_data = { .f_max = 50000000, .capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA | MMC_CAP_MMC_HIGHSPEED, - .capabilities2 = MMC_CAP2_NO_SLEEP_CMD, .gpio_cd = -1, .gpio_wp = -1, #ifdef CONFIG_STE_DMA40 -- cgit v1.2.3 From 739efa9f234d2d29667da8422f669e3712796e52 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Wed, 9 Nov 2011 10:55:24 +0100 Subject: mmc: mmci: optimize pre_req if no previous request Don't prepare DMA in the pre_req hook if there is no previous request. Instead, prepare DMA while start command is being issued. ST-Ericsson Linux next: N/A ST-Ericsson ID: 371812 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I0008b79cbe0e87f830365e689795f344aac3014b Signed-off-by: Per Forlin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36181 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Ulf HANSSON --- drivers/mmc/host/mmci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 6e501f4b281..11f6f7ba415 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -591,7 +591,12 @@ static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq, if (mmci_validate_data(host, data)) return; - if (!mmci_dma_prep_next(host, data)) + /* + * Don't prepare DMA if there is no previous request, + * is_first_req is set. Instead, prepare DMA while + * start command is being issued. + */ + if (!is_first_req && !mmci_dma_prep_next(host, data)) data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie; } -- cgit v1.2.3 From e9b894b8d741ac0c2a724d39ab1fa2073fb3dafc Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 1 Nov 2011 12:03:59 +0100 Subject: mmc: mmci: Limit command frequency when using a levelshifter Due to timing issues for command direction signaling when using a levelshifter, we limit the frequency to 25 MHz while sending commands. The frequency is just limited during the command send/response, then it is restored to it's original value. ST-Ericsson ID: 355531 ST-Ericsson Linux next: - ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I15d06e3c331a4db717a904937ce6ce60972d185e Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/51785 --- drivers/mmc/host/mmci.c | 15 +++++++++++++++ drivers/mmc/host/mmci.h | 1 + 2 files changed, 16 insertions(+) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 11f6f7ba415..543159430bb 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -786,6 +786,14 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) if (/*interrupt*/0) c |= MCI_CPSM_INTERRUPT; + /* + * For levelshifters we must not use more than 25MHz when + * sending commands. + */ + host->cclk_desired = host->cclk; + if (host->plat->ios_handler && (host->cclk_desired > 25000000)) + mmci_set_clkreg(host, 25000000); + host->cmd = cmd; writel(cmd->arg, base + MMCIARGUMENT); @@ -878,6 +886,13 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, cmd->resp[3] = readl(base + MMCIRESPONSE3); } + /* + * For levelshifters we might have decreased cclk to 25MHz when + * sending commands, then we restore the frequency here. + */ + if (host->plat->ios_handler && (host->cclk_desired > host->cclk)) + mmci_set_clkreg(host, host->cclk_desired); + if (!cmd->data || cmd->error) { /* Terminate the DMA transfer */ if (dma_inprogress(host)) { diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index ba09a603787..5a17beafd05 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -186,6 +186,7 @@ struct mmci_host { unsigned int mclk; unsigned int cclk; + unsigned int cclk_desired; u32 pwr_reg; u32 clk_reg; u32 datactrl_reg; -- cgit v1.2.3 From dc84273d06bd9a78f951ac56509df64f3581996b Mon Sep 17 00:00:00 2001 From: Fredrik Soderstedt Date: Mon, 19 Dec 2011 20:14:31 +0100 Subject: mmc: mmci : Do not use polling for non removable cards Do not use polling to detect non removable cards. ST-Ericsson ID: 372618 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I18797e5f85d83213bbf520277576425bed4c68f2 Signed-off-by: Fredrik Soderstedt Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/51859 --- drivers/mmc/host/mmci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 543159430bb..18a091cf39d 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1512,7 +1512,8 @@ static int __devinit mmci_probe(struct amba_device *dev, } if ((host->plat->status || host->gpio_cd != -ENOSYS) - && host->gpio_cd_irq < 0) + && host->gpio_cd_irq < 0 + && !(mmc->caps & MMC_CAP_NONREMOVABLE)) mmc->caps |= MMC_CAP_NEEDS_POLL; ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host); -- cgit v1.2.3 From 285f0b3fe3a59c56db9ae68e6a1ae0e2a67b72f9 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 9 Mar 2012 08:22:48 +0100 Subject: mmc: mach-ux500: Update U8520 SD-card GPIO pins ST-Ericsson ID: 371953 Signed-off-by: Bengt Jonsson --- arch/arm/mach-ux500/board-mop500-sdi.c | 13 +++++++++++++ arch/arm/mach-ux500/board-mop500.c | 5 ++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 118d29b863f..e043a31ce25 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -316,3 +316,16 @@ void __init hrefv60_sdi_init(void) sdi0_vsel = HREFV60_SDMMC_1V8_3V_GPIO; sdi0_sdi1_configure(); } + +void __init mach_u8520_sdi_init(void) +{ + /* PoP:ed eMMC */ + db8500_add_sdi2(&mop500_sdi2_data, U8500_SDI_V2_PERIPHID); + /* On-board eMMC */ + db8500_add_sdi4(&mop500_sdi4_data, U8500_SDI_V2_PERIPHID); + /* External Micro SD slot */ + mop500_sdi0_data.gpio_cd = U8520_SDMMC_CD_GPIO; + sdi0_en = U8520_SDMMC_EN_GPIO; + sdi0_vsel = U8520_SDMMC_1V8_3V_GPIO; + sdi0_sdi1_configure(); +} diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 5c00712907d..09a3729995b 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -673,7 +673,10 @@ static void __init hrefv60_init_machine(void) ARRAY_SIZE(mop500_platform_devs)); mop500_i2c_init(); - hrefv60_sdi_init(); + if (machine_is_u8520()) + mach_u8520_sdi_init(); + else + hrefv60_sdi_init(); mop500_spi_init(); mop500_uart_init(); -- cgit v1.2.3