From 5e13c86df896b657e453626305cd04a8a6e69610 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 7acb3a6f3496c5f12197ea3272bdeedfa9133246 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 | 32 +++-- arch/arm/mach-ux500/board-u5500-sdi.c | 244 +++++++++++++++++++++++++++++---- 2 files changed, 240 insertions(+), 36 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 920251cf834..14a7b6e69d0 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 @@ -50,6 +51,7 @@ static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios) */ 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); @@ -64,19 +66,23 @@ static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios) 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 @@ -121,14 +127,6 @@ static void sdi0_configure(struct device *parent) db8500_add_sdi0(parent, &mop500_sdi0_data, U8500_SDI_V2_PERIPHID); } -void mop500_sdi_tc35892_init(struct device *parent) -{ - mop500_sdi0_data.gpio_cd = GPIO_SDMMC_CD; - sdi0_en = GPIO_SDMMC_EN; - sdi0_vsel = GPIO_SDMMC_1V8_3V_SEL; - sdi0_configure(parent); -} - /* * SDI1 (SDIO WLAN) */ @@ -241,6 +239,16 @@ static struct mmci_platform_data mop500_sdi4_data = { #endif }; +void mop500_sdi_tc35892_init(struct device *parent) +{ + mop500_sdi0_data.gpio_cd = GPIO_SDMMC_CD; + sdi0_en = GPIO_SDMMC_EN; + sdi0_vsel = GPIO_SDMMC_1V8_3V_SEL; + sdi0_configure(parent); + /* WLAN SDIO channel */ + db8500_add_sdi1(parent, &mop500_sdi1_data, U8500_SDI_V2_PERIPHID); +} + void __init mop500_sdi_init(struct device *parent) { /* PoP:ed eMMC */ @@ -267,6 +275,8 @@ void __init snowball_sdi_init(struct device *parent) sdi0_en = SNOWBALL_SDMMC_EN_GPIO; sdi0_vsel = SNOWBALL_SDMMC_1V8_3V_GPIO; sdi0_configure(parent); + /* WLAN SDIO channel */ + db8500_add_sdi1(parent, &mop500_sdi1_data, U8500_SDI_V2_PERIPHID); } void __init hrefv60_sdi_init(struct device *parent) diff --git a/arch/arm/mach-ux500/board-u5500-sdi.c b/arch/arm/mach-ux500/board-u5500-sdi.c index 836112eedde..de6e0e5192b 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(struct device *parent) { - 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(parent, &u5500_sdi2_data, periphid); + else + db5500_add_sdi0(parent, &u5500_sdi0_data, periphid); - db5500_add_sdi0(parent, &u5500_sdi0_data); + sdi1_configure(); + db5500_add_sdi1(parent, &u5500_sdi1_data, periphid); + db5500_add_sdi3(parent, &u5500_sdi3_data, periphid); } -- cgit v1.2.3 From 7cb48060b2f5907d7fc67ade5eeb29f81afff480 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 032b84791a1..2ad77daff3c 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -56,6 +56,7 @@ static unsigned int fmax = 515633; * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register * @pwrreg_powerup: power up value for MMCIPOWER register * @signal_direction: input/out direction of bus signals can be indicated + * @non_power_of_2_blksize: true if block sizes can be other than power of two */ struct variant_data { unsigned int clkreg; @@ -68,6 +69,7 @@ struct variant_data { bool blksz_datactrl16; u32 pwrreg_powerup; bool signal_direction; + bool non_power_of_2_blksize; }; static struct variant_data variant_arm = { @@ -117,6 +119,7 @@ static struct variant_data variant_ux500v2 = { .blksz_datactrl16 = true, .pwrreg_powerup = MCI_PWR_ON, .signal_direction = true, + .non_power_of_2_blksize = true, }; /* @@ -629,7 +632,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); @@ -1031,11 +1033,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 79ba4708658f7503c5b9ff3a771a3c37c5d3f7ef 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 | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 2ad77daff3c..beb2d963c4d 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -643,9 +643,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. + */ + u32 clk; + 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)) + clk = host->clk_reg & ~variant->clkreg_enable; + else + clk = host->clk_reg | variant->clkreg_enable; + + mmci_write_clkreg(host, clk); + } + /* * Attempt to use DMA operation mode, if this * should fail, fall back to PIO mode @@ -865,22 +888,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)) { - u32 clk; - if (count < 8) - clk = host->clk_reg & ~variant->clkreg_enable; - else - clk = host->clk_reg | variant->clkreg_enable; - - mmci_write_clkreg(host, clk); - } - /* * SDIO especially may want to send something that is * not divisible by 4 (as opposed to card sectors -- cgit v1.2.3 From ae377cfb84e6e6a7663792fc5bcb271d2459677d 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 beb2d963c4d..f5d50361759 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -46,6 +46,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) @@ -61,6 +62,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; @@ -101,6 +103,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, @@ -113,6 +116,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, @@ -122,6 +126,31 @@ static struct variant_data variant_ux500v2 = { .non_power_of_2_blksize = 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 */ @@ -436,8 +465,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; @@ -472,6 +505,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) @@ -486,6 +520,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); @@ -530,6 +569,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; @@ -1040,17 +1082,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 d437ccf62d6..c2b33326547 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 2ba5452332b14b39eb20d54c5add45f81ed341fd 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-u5500-sdi.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-ux500/board-u5500-sdi.c b/arch/arm/mach-ux500/board-u5500-sdi.c index de6e0e5192b..74be6fad286 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 | -- cgit v1.2.3 From 8824cdd4e414fbd49edb83a9805784e994645b0c 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 82baa9eb77339c5d0c6864a637abc903ea56f9ab 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 f5d50361759..6e03104b7a2 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1370,6 +1371,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 04c12decba2b243cb7910547d0d894ab87da96bd 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 14a7b6e69d0..d6a42ddd68a 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 74be6fad286..c7b828942e7 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 5d92f256f768c38ea345bcbd8434944d8d03a734 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 d6a42ddd68a..334a41a50ce 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 c7b828942e7..31a8c8ecdc9 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 2ca835ac89ca2ceb0ac1d37b0d38b7025b905fa4 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 31a8c8ecdc9..0600c95278d 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 fa6bcad633752fc296e85f7eb46132626b91b857 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 0600c95278d..76d3cbe10d1 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(struct device *parent) { 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(parent, &u5500_sdi2_data, periphid); else db5500_add_sdi0(parent, &u5500_sdi0_data, periphid); -- cgit v1.2.3 From 7831c0b624cf6dcb4d860dd2452faed6c32e42d2 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 | 35 ++++++++++++++++++++++++++++++++++- drivers/mmc/core/core.h | 1 + drivers/mmc/core/host.c | 1 + include/linux/mmc/host.h | 17 +++++++++++++++++ 5 files changed, 61 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index dabec556ebb..a73621abc85 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1411,6 +1411,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 ba821fe70bc..9cce415f1ff 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2010,7 +2010,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); @@ -2273,8 +2273,13 @@ int mmc_suspend_host(struct mmc_host *host) int err = 0; 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; + err = mmc_cache_ctrl(host, 0); if (err) goto out; @@ -2300,6 +2305,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; } } mmc_bus_put(host); @@ -2321,6 +2330,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)) { @@ -2355,6 +2370,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 91c84c7a182..d87d1152ea2 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -330,6 +330,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); + 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 cbde4b7e675..015eb334ce5 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -292,6 +292,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 */ @@ -340,6 +345,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); @@ -429,4 +435,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 339efff8f0dd0bb236b26c80963de7a1f64c8a1e 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 6e03104b7a2..eaeae3232db 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -696,14 +696,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)) clk = host->clk_reg & ~variant->clkreg_enable; else -- cgit v1.2.3 From 322d9ea24f9aa36a2afda47c75dce32510128519 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 76d3cbe10d1..b21db6cb149 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 8aedfd5e16fab7dd358cc621cdfc74920ee6d308 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 334a41a50ce..902f0d4667a 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -199,6 +199,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 @@ -237,6 +238,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 b21db6cb149..8954e8a3ffd 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 e3847f822f6ca232b86e7e0784adcc56e87d9362 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 8954e8a3ffd..25b519b5476 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(struct device *parent) { - 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 458f4d55240c3584d4f9ef7f18f3f65e6ad87a3b 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 a73621abc85..c2f8d781ce1 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -107,6 +107,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; }; @@ -167,6 +168,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) { @@ -1662,9 +1744,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); @@ -1724,9 +1809,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 3d08684763fe0d1b9ca53c14ca8e558e53c60e9c 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 eaeae3232db..5370858044b 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1554,6 +1554,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); @@ -1561,12 +1613,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); } } @@ -1580,9 +1631,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 0c13ac50ca4263239e4937ae560ffe79f1c9e7c9 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 5370858044b..70ae28efb3c 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -59,6 +59,7 @@ static unsigned int fmax = 515633; * @pwrreg_powerup: power up value for MMCIPOWER register * @signal_direction: input/out direction of bus signals can be indicated * @non_power_of_2_blksize: true if block sizes can be other than power of two + * @pwrreg_ctrl_power: bits in MMCIPOWER register controls ext. power supply */ struct variant_data { unsigned int clkreg; @@ -73,6 +74,7 @@ struct variant_data { u32 pwrreg_powerup; bool signal_direction; bool non_power_of_2_blksize; + bool pwrreg_ctrl_power; }; static struct variant_data variant_arm = { @@ -80,6 +82,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 = { @@ -87,6 +90,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 = { @@ -1553,7 +1557,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); @@ -1605,7 +1609,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); @@ -1642,8 +1648,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 1822dc467592a9bc31f92dec9a9fb7b8b91e31bb 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 70ae28efb3c..6c810d4f36a 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1562,10 +1562,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); /* @@ -1583,7 +1595,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) @@ -1605,6 +1617,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 30f4d595f710bdf53400a343c49678e41ab59ecc 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 c2f8d781ce1..4c1b59aff9d 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1815,6 +1815,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 748c13b21b4177de79677f7b37c15680c607d09b 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 6c810d4f36a..b36a68dfd72 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -375,10 +375,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; @@ -397,19 +418,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. @@ -421,14 +437,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 = { @@ -446,16 +458,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; @@ -489,30 +491,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; @@ -548,18 +562,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; } @@ -574,22 +584,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, @@ -597,29 +598,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); } } @@ -640,6 +631,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) { } @@ -824,7 +819,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) @@ -861,8 +856,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 dfecd98a94bcdc5a21bc1c8f794483b9dfaca979 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 b36a68dfd72..501227da03e 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -379,6 +379,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; } @@ -418,10 +420,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) @@ -435,6 +435,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 */ @@ -779,8 +782,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 004eb1db89e61c558d0d8f50e46e21d1aaaea36e 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 902f0d4667a..9fb8cae0dbc 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 | @@ -234,7 +233,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 c3921020050e5a2d4c09cc3709ac1e192e399b69 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 501227da03e..1dc8d379e4d 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -608,12 +608,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 be2ec17ae364a4e1e6595ad5d620062df149a9ca 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 1dc8d379e4d..c7a645930ce 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1235,6 +1235,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; @@ -1251,6 +1266,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 d7d57e67456f336f10f7e9fcb4b39ac34d3e7f2a 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 9fb8cae0dbc..19dbf5ca68a 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 86d5a71911a2a4f83f713c9dcfe2a830b875b094 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 19dbf5ca68a..1b0a5bf6cd7 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 ca65739c9a13fab438ef901a286b1858cbd3bbc5 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 1b0a5bf6cd7..988d5f58ee1 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -220,7 +220,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 cae518fd57679b100e53728681a2e8932b23e7ab 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 c7a645930ce..2d3d16183c1 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -265,6 +265,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) @@ -375,13 +376,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) @@ -421,7 +422,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) @@ -523,7 +524,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; @@ -540,15 +541,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 @@ -642,11 +643,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; } @@ -656,10 +662,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; @@ -714,12 +720,21 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) mmci_write_clkreg(host, clk); } + 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 */ @@ -743,7 +758,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); } @@ -787,7 +801,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); } @@ -863,16 +877,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); } } @@ -1091,6 +1105,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); @@ -1106,14 +1121,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 0ffd7158af8a87c04eb65c32b4b7008318e8fbea 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 988d5f58ee1..1e3d8b25b2d 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -260,7 +260,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 4920bb837ad14634b8d8b257452442ac5d97a82f 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 2d3d16183c1..e3d67089265 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -593,7 +593,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 5137c4ea06a48c3a9e47c8fbbb81701db9e816f9 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 e3d67089265..368f855ceac 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -789,6 +789,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); @@ -881,6 +889,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 54899fce7321b4548fdc97acd430547e675107b6 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 368f855ceac..e477a687cbf 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1515,7 +1515,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 4d982e5cc14492aaccfd977ebf72820c1d042394 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 | 15 +++++++++++++++ arch/arm/mach-ux500/board-mop500.c | 5 ++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c index 1e3d8b25b2d..828b230dd27 100644 --- a/arch/arm/mach-ux500/board-mop500-sdi.c +++ b/arch/arm/mach-ux500/board-mop500-sdi.c @@ -323,3 +323,18 @@ void __init hrefv60_sdi_init(struct device *parent) /* WLAN SDIO channel */ db8500_add_sdi1(parent, &mop500_sdi1_data, U8500_SDI_V2_PERIPHID); } + +void __init mach_u8520_sdi_init(struct device *parent) +{ + /* PoP:ed eMMC */ + db8500_add_sdi2(parent, &mop500_sdi2_data, U8500_SDI_V2_PERIPHID); + /* On-board eMMC */ + db8500_add_sdi4(parent, &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_configure(parent); + /* WLAN SDIO channel */ + db8500_add_sdi1(parent, &mop500_sdi1_data, U8500_SDI_V2_PERIPHID); +} diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 77d03c1fbd0..3b8bd438b7c 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -697,7 +697,10 @@ static void __init hrefv60_init_machine(void) ARRAY_SIZE(mop500_platform_devs)); mop500_i2c_init(parent); - hrefv60_sdi_init(parent); + if (machine_is_u8520()) + mach_u8520_sdi_init(parent); + else + hrefv60_sdi_init(parent); mop500_spi_init(parent); mop500_uart_init(parent); -- cgit v1.2.3 From 2a8fe9f1219bffa4c076d33a19a982908480024f Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 18 Apr 2012 15:23:06 +0200 Subject: mmci: the AMBA level regulator has been removed in 3.4 See 1e45860f541497d73162305d48b638d9b87e1ae3 "ARM: 7366/3: amba: Remove AMBA level regulator support" for more precision. Signed-off-by: Philippe Langlais --- drivers/mmc/host/mmci.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index e477a687cbf..7df4119aaa2 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1664,7 +1664,6 @@ static int mmci_save(struct amba_device *dev) spin_unlock_irqrestore(&host->lock, flags); clk_disable(host->clk); - amba_vcore_disable(dev); } return ret; @@ -1678,7 +1677,6 @@ static int mmci_restore(struct amba_device *dev) if (mmc) { struct mmci_host *host = mmc_priv(mmc); - amba_vcore_enable(dev); clk_enable(host->clk); spin_lock_irqsave(&host->lock, flags); -- cgit v1.2.3