From 221414db1934c1c883501998f510bb75acfbaa51 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 7 Aug 2014 18:07:07 +0200 Subject: mmc: sdhci-s3c: fix runtime PM handling on sdhci_add_host() failure Runtime Power Management handling for the sdhci_add_host() failure case in sdhci_s3c_probe() should match the code in sdhci_s3c_remove() (which uses pm_runtime_disable() call which matches the earlier pm_runtime_enable() one). Fix it. This patch fixes "BUG: spinlock bad magic on CPU#0, swapper/0/1" and "Unbalanced pm_runtime_enable!" warnings. >From the kernel log: ... [ 1.659631] s3c-sdhci 12530000.sdhci: sdhci_add_host() failed [ 1.665096] BUG: spinlock bad magic on CPU#0, swapper/0/1 [ 1.670433] lock: 0xea01e484, .magic: 00000000, .owner: /-1, .owner_cpu: 0 [ 1.677895] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.16.0-next-20140804-00008-ga59480f-dirty #707 [ 1.687037] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [ 1.694740] [] (show_stack) from [] (dump_stack+0x68/0xb8) [ 1.701948] [] (dump_stack) from [] (do_raw_spin_lock+0x15c/0x1a4) [ 1.709848] [] (do_raw_spin_lock) from [] (_raw_spin_lock_irqsave+0x20/0x28) [ 1.718619] [] (_raw_spin_lock_irqsave) from [] (sdhci_do_set_ios+0x1c/0x5cc) [ 1.727464] [] (sdhci_do_set_ios) from [] (sdhci_runtime_resume_host+0x50/0x104) [ 1.736574] [] (sdhci_runtime_resume_host) from [] (pm_generic_runtime_resume+0x2c/0x40) [ 1.746383] [] (pm_generic_runtime_resume) from [] (__rpm_callback+0x34/0x70) [ 1.755233] [] (__rpm_callback) from [] (rpm_callback+0x28/0x88) [ 1.762958] [] (rpm_callback) from [] (rpm_resume+0x384/0x4ec) [ 1.770511] [] (rpm_resume) from [] (pm_runtime_forbid+0x58/0x64) [ 1.778325] [] (pm_runtime_forbid) from [] (sdhci_s3c_probe+0x4a4/0x540) [ 1.786749] [] (sdhci_s3c_probe) from [] (platform_drv_probe+0x2c/0x5c) [ 1.795076] [] (platform_drv_probe) from [] (driver_probe_device+0x114/0x234) [ 1.803929] [] (driver_probe_device) from [] (__driver_attach+0x8c/0x90) [ 1.812347] [] (__driver_attach) from [] (bus_for_each_dev+0x54/0x88) [ 1.820506] [] (bus_for_each_dev) from [] (bus_add_driver+0xd8/0x1cc) [ 1.828665] [] (bus_add_driver) from [] (driver_register+0x78/0xf4) [ 1.836652] [] (driver_register) from [] (do_one_initcall+0x80/0x1d0) [ 1.844816] [] (do_one_initcall) from [] (kernel_init_freeable+0x108/0x1d4) [ 1.853503] [] (kernel_init_freeable) from [] (kernel_init+0x8/0xe4) [ 1.861568] [] (kernel_init) from [] (ret_from_fork+0x14/0x3c) [ 1.869582] platform 12530000.sdhci: Driver s3c-sdhci requests probe deferral ... [ 1.997047] s3c-sdhci 12530000.sdhci: Unbalanced pm_runtime_enable! ... [ 2.027235] s3c-sdhci 12530000.sdhci: sdhci_add_host() failed [ 2.032884] platform 12530000.sdhci: Driver s3c-sdhci requests probe deferral ... Tested on Hardkernel's Exynos4412 based ODROID-U3 board. Fixes: 9f4e8151dbbc ("mmc: sdhci-s3c: Enable runtime power management") Cc: Mark Brown Cc: Jaehoon Chung Cc: Ben Dooks Signed-off-by: Bartlomiej Zolnierkiewicz Acked-by: Kyungmin Park Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-s3c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index fa5954a05449..1e47903fa184 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -606,8 +606,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev) ret = sdhci_add_host(host); if (ret) { dev_err(dev, "sdhci_add_host() failed\n"); - pm_runtime_forbid(&pdev->dev); - pm_runtime_get_noresume(&pdev->dev); goto err_req_regs; } @@ -618,6 +616,8 @@ static int sdhci_s3c_probe(struct platform_device *pdev) return 0; err_req_regs: + pm_runtime_disable(&pdev->dev); + err_no_busclks: clk_disable_unprepare(sc->clk_io); -- cgit v1.2.3 From 9cb15142d0e336874ecc753e9a4e5696c46a67d9 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 29 Jul 2014 03:50:30 +0100 Subject: mmc: mmci: Add qcom dml support to the driver. On Qualcomm APQ8064 SOCs, SD card controller has an additional glue called DML (Data Mover Local/Lite) to assist dma transfers. This hardware needs to be setup before any dma transfer is requested. DML itself is not a DMA engine, its just a gule between the SD card controller and dma controller. Most of this code has been ported from qualcomm's 3.4 kernel. This patch adds the code necessary to intialize the hardware and setup before doing any dma transfers. Reviewed-by: Linus Walleij Signed-off-by: Srinivas Kandagatla Reviewed-by: Stephen Boyd Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 11 +++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/mmci.c | 18 +++- drivers/mmc/host/mmci_qcom_dml.c | 177 +++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/mmci_qcom_dml.h | 31 +++++++ 5 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 drivers/mmc/host/mmci_qcom_dml.c create mode 100644 drivers/mmc/host/mmci_qcom_dml.h (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 451135822464..58aedcc900d1 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -14,6 +14,17 @@ config MMC_ARMMMCI If unsure, say N. +config MMC_QCOM_DML + tristate "Qualcomm Data Mover for SD Card Controller" + depends on MMC_ARMMMCI && QCOM_BAM_DMA + default y + help + This selects the Qualcomm Data Mover lite/local on SD Card controller. + This option will enable the dma to work correctly, if you are using + Qcom SOCs and MMC, you would probably need this option to get DMA working. + + if unsure, say N. + config MMC_PXA tristate "Intel PXA25x/26x/27x Multimedia Card Interface support" depends on ARCH_PXA diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index f211eede8db5..2d223463e6c5 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_MMC_ARMMMCI) += mmci.o +obj-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o obj-$(CONFIG_MMC_PXA) += pxamci.o obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_MXS) += mxs-mmc.o diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index e4d470704150..a7b3af9e9a2a 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -43,6 +43,7 @@ #include #include "mmci.h" +#include "mmci_qcom_dml.h" #define DRIVER_NAME "mmci-pl18x" @@ -74,6 +75,7 @@ static unsigned int fmax = 515633; * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply * @explicit_mclk_control: enable explicit mclk control in driver. * @qcom_fifo: enables qcom specific fifo pio read logic. + * @qcom_dml: enables qcom specific dma glue for dma transfers. * @reversed_irq_handling: handle data irq before cmd irq. */ struct variant_data { @@ -98,6 +100,7 @@ struct variant_data { bool pwrreg_nopower; bool explicit_mclk_control; bool qcom_fifo; + bool qcom_dml; bool reversed_irq_handling; }; @@ -208,6 +211,7 @@ static struct variant_data variant_qcom = { .f_max = 208000000, .explicit_mclk_control = true, .qcom_fifo = true, + .qcom_dml = true, }; static int mmci_card_busy(struct mmc_host *mmc) @@ -421,6 +425,7 @@ static void mmci_dma_setup(struct mmci_host *host) { const char *rxname, *txname; dma_cap_mask_t mask; + struct variant_data *variant = host->variant; host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx"); host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx"); @@ -471,6 +476,10 @@ static void mmci_dma_setup(struct mmci_host *host) if (max_seg_size < host->mmc->max_seg_size) host->mmc->max_seg_size = max_seg_size; } + + if (variant->qcom_dml && host->dma_rx_channel && host->dma_tx_channel) + if (dml_hw_init(host, host->mmc->parent->of_node)) + variant->qcom_dml = false; } /* @@ -572,6 +581,7 @@ static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, struct dma_async_tx_descriptor *desc; enum dma_data_direction buffer_dirn; int nr_sg; + unsigned long flags = DMA_CTRL_ACK; if (data->flags & MMC_DATA_READ) { conf.direction = DMA_DEV_TO_MEM; @@ -596,9 +606,12 @@ static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, if (nr_sg == 0) return -EINVAL; + if (host->variant->qcom_dml) + flags |= DMA_PREP_INTERRUPT; + dmaengine_slave_config(chan, &conf); desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg, - conf.direction, DMA_CTRL_ACK); + conf.direction, flags); if (!desc) goto unmap_exit; @@ -647,6 +660,9 @@ 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); + if (host->variant->qcom_dml) + dml_start_xfer(host, data); + datactrl |= MCI_DPSM_DMAENABLE; /* Trigger the DMA transfer */ diff --git a/drivers/mmc/host/mmci_qcom_dml.c b/drivers/mmc/host/mmci_qcom_dml.c new file mode 100644 index 000000000000..2b7fc3764803 --- /dev/null +++ b/drivers/mmc/host/mmci_qcom_dml.c @@ -0,0 +1,177 @@ +/* + * + * Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include "mmci.h" + +/* Registers */ +#define DML_CONFIG 0x00 +#define PRODUCER_CRCI_MSK GENMASK(1, 0) +#define PRODUCER_CRCI_DISABLE 0 +#define PRODUCER_CRCI_X_SEL BIT(0) +#define PRODUCER_CRCI_Y_SEL BIT(1) +#define CONSUMER_CRCI_MSK GENMASK(3, 2) +#define CONSUMER_CRCI_DISABLE 0 +#define CONSUMER_CRCI_X_SEL BIT(2) +#define CONSUMER_CRCI_Y_SEL BIT(3) +#define PRODUCER_TRANS_END_EN BIT(4) +#define BYPASS BIT(16) +#define DIRECT_MODE BIT(17) +#define INFINITE_CONS_TRANS BIT(18) + +#define DML_SW_RESET 0x08 +#define DML_PRODUCER_START 0x0c +#define DML_CONSUMER_START 0x10 +#define DML_PRODUCER_PIPE_LOGICAL_SIZE 0x14 +#define DML_CONSUMER_PIPE_LOGICAL_SIZE 0x18 +#define DML_PIPE_ID 0x1c +#define PRODUCER_PIPE_ID_SHFT 0 +#define PRODUCER_PIPE_ID_MSK GENMASK(4, 0) +#define CONSUMER_PIPE_ID_SHFT 16 +#define CONSUMER_PIPE_ID_MSK GENMASK(20, 16) + +#define DML_PRODUCER_BAM_BLOCK_SIZE 0x24 +#define DML_PRODUCER_BAM_TRANS_SIZE 0x28 + +/* other definitions */ +#define PRODUCER_PIPE_LOGICAL_SIZE 4096 +#define CONSUMER_PIPE_LOGICAL_SIZE 4096 + +#define DML_OFFSET 0x800 + +void dml_start_xfer(struct mmci_host *host, struct mmc_data *data) +{ + u32 config; + void __iomem *base = host->base + DML_OFFSET; + + if (data->flags & MMC_DATA_READ) { + /* Read operation: configure DML for producer operation */ + /* Set producer CRCI-x and disable consumer CRCI */ + config = readl_relaxed(base + DML_CONFIG); + config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_X_SEL; + config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_DISABLE; + writel_relaxed(config, base + DML_CONFIG); + + /* Set the Producer BAM block size */ + writel_relaxed(data->blksz, base + DML_PRODUCER_BAM_BLOCK_SIZE); + + /* Set Producer BAM Transaction size */ + writel_relaxed(data->blocks * data->blksz, + base + DML_PRODUCER_BAM_TRANS_SIZE); + /* Set Producer Transaction End bit */ + config = readl_relaxed(base + DML_CONFIG); + config |= PRODUCER_TRANS_END_EN; + writel_relaxed(config, base + DML_CONFIG); + /* Trigger producer */ + writel_relaxed(1, base + DML_PRODUCER_START); + } else { + /* Write operation: configure DML for consumer operation */ + /* Set consumer CRCI-x and disable producer CRCI*/ + config = readl_relaxed(base + DML_CONFIG); + config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_X_SEL; + config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_DISABLE; + writel_relaxed(config, base + DML_CONFIG); + /* Clear Producer Transaction End bit */ + config = readl_relaxed(base + DML_CONFIG); + config &= ~PRODUCER_TRANS_END_EN; + writel_relaxed(config, base + DML_CONFIG); + /* Trigger consumer */ + writel_relaxed(1, base + DML_CONSUMER_START); + } + + /* make sure the dml is configured before dma is triggered */ + wmb(); +} + +static int of_get_dml_pipe_index(struct device_node *np, const char *name) +{ + int index; + struct of_phandle_args dma_spec; + + index = of_property_match_string(np, "dma-names", name); + + if (index < 0) + return -ENODEV; + + if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index, + &dma_spec)) + return -ENODEV; + + if (dma_spec.args_count) + return dma_spec.args[0]; + + return -ENODEV; +} + +/* Initialize the dml hardware connected to SD Card controller */ +int dml_hw_init(struct mmci_host *host, struct device_node *np) +{ + u32 config; + void __iomem *base; + int consumer_id, producer_id; + + consumer_id = of_get_dml_pipe_index(np, "tx"); + producer_id = of_get_dml_pipe_index(np, "rx"); + + if (producer_id < 0 || consumer_id < 0) + return -ENODEV; + + base = host->base + DML_OFFSET; + + /* Reset the DML block */ + writel_relaxed(1, base + DML_SW_RESET); + + /* Disable the producer and consumer CRCI */ + config = (PRODUCER_CRCI_DISABLE | CONSUMER_CRCI_DISABLE); + /* + * Disable the bypass mode. Bypass mode will only be used + * if data transfer is to happen in PIO mode and don't + * want the BAM interface to connect with SDCC-DML. + */ + config &= ~BYPASS; + /* + * Disable direct mode as we don't DML to MASTER the AHB bus. + * BAM connected with DML should MASTER the AHB bus. + */ + config &= ~DIRECT_MODE; + /* + * Disable infinite mode transfer as we won't be doing any + * infinite size data transfers. All data transfer will be + * of finite data size. + */ + config &= ~INFINITE_CONS_TRANS; + writel_relaxed(config, base + DML_CONFIG); + + /* + * Initialize the logical BAM pipe size for producer + * and consumer. + */ + writel_relaxed(PRODUCER_PIPE_LOGICAL_SIZE, + base + DML_PRODUCER_PIPE_LOGICAL_SIZE); + writel_relaxed(CONSUMER_PIPE_LOGICAL_SIZE, + base + DML_CONSUMER_PIPE_LOGICAL_SIZE); + + /* Initialize Producer/consumer pipe id */ + writel_relaxed(producer_id | (consumer_id << CONSUMER_PIPE_ID_SHFT), + base + DML_PIPE_ID); + + /* Make sure dml intialization is finished */ + mb(); + + return 0; +} diff --git a/drivers/mmc/host/mmci_qcom_dml.h b/drivers/mmc/host/mmci_qcom_dml.h new file mode 100644 index 000000000000..6e405d09d534 --- /dev/null +++ b/drivers/mmc/host/mmci_qcom_dml.h @@ -0,0 +1,31 @@ +/* + * + * Copyright (c) 2011, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MMC_QCOM_DML_H__ +#define __MMC_QCOM_DML_H__ + +#ifdef CONFIG_MMC_QCOM_DML +int dml_hw_init(struct mmci_host *host, struct device_node *np); +void dml_start_xfer(struct mmci_host *host, struct mmc_data *data); +#else +static inline int dml_hw_init(struct mmci_host *host, struct device_node *np) +{ + return -ENOSYS; +} +static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data) +{ +} +#endif /* CONFIG_MMC_QCOM_DML */ + +#endif /* __MMC_QCOM_DML_H__ */ -- cgit v1.2.3 From ca6429d4952398570a3963bd4b5842b519087df8 Mon Sep 17 00:00:00 2001 From: Tim Kryger Date: Mon, 11 Aug 2014 22:05:12 -0700 Subject: mmc: core: Remove fixed voltage regulator logic There is no need for regulator consumers to include special logic for fixed voltage regulators as they support regulator_set_voltage() just like their non-fixed regulator counterparts. Signed-off-by: Tim Kryger Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d03a080fb9cd..2eb7c82ba4a2 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1263,7 +1263,6 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, if (vdd_bit) { int tmp; - int voltage; /* * REVISIT mmc_vddrange_to_ocrmask() may have set some @@ -1280,22 +1279,7 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, max_uV = min_uV + 100 * 1000; } - /* - * If we're using a fixed/static regulator, don't call - * regulator_set_voltage; it would fail. - */ - voltage = regulator_get_voltage(supply); - - if (!regulator_can_change_voltage(supply)) - min_uV = max_uV = voltage; - - if (voltage < 0) - result = voltage; - else if (voltage < min_uV || voltage > max_uV) - result = regulator_set_voltage(supply, min_uV, max_uV); - else - result = 0; - + result = regulator_set_voltage(supply, min_uV, max_uV); if (result == 0 && !mmc->regulator_enabled) { result = regulator_enable(supply); if (!result) -- cgit v1.2.3 From 45c19e2a812f9f98c0ad65c8d4a5648b6edcd0f8 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Tue, 12 Aug 2014 11:37:52 +0100 Subject: mmc: sdhci-pltfm: Do not use parent as the host's device The code selecting a device for the sdhci host has been continuously tweaked (4b711cb13843f5082e82970dd1e8031383134a65 "mmc: sdhci-pltfm: Add structure for host-specific data" and a4d2177f00a5252d825236c5124bc1e9918bdb41 "mmc: sdhci-pltfm: dt device does not pass parent to sdhci_alloc_host" while there does not seem to be any reason to use platform device's parent in the first place. The comment saying "Some PCI-based MFD need the parent here" seem to refer to Timberdale FPGA driver (the only MFD driver registering SDHCI cell, drivers/mfd/timberdale.c) but again, the only situation when parent device matter is runtime PM, which is not implemented for Timberdale. Signed-off-by: Pawel Moll Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pltfm.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 7e834fb78f42..c5b01d6bb85d 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -123,7 +123,6 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, size_t priv_size) { struct sdhci_host *host; - struct device_node *np = pdev->dev.of_node; struct resource *iomem; int ret; @@ -136,13 +135,8 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, if (resource_size(iomem) < 0x100) dev_err(&pdev->dev, "Invalid iomem size!\n"); - /* Some PCI-based MFD need the parent here */ - if (pdev->dev.parent != &platform_bus && !np) - host = sdhci_alloc_host(pdev->dev.parent, - sizeof(struct sdhci_pltfm_host) + priv_size); - else - host = sdhci_alloc_host(&pdev->dev, - sizeof(struct sdhci_pltfm_host) + priv_size); + host = sdhci_alloc_host(&pdev->dev, + sizeof(struct sdhci_pltfm_host) + priv_size); if (IS_ERR(host)) { ret = PTR_ERR(host); -- cgit v1.2.3 From f629ba2c04c949aa62c85b48c0b73b915b98defc Mon Sep 17 00:00:00 2001 From: Addy Ke Date: Thu, 31 Jul 2014 14:01:38 +0800 Subject: mmc: dw_mmc: add support for RK3288 This patch focuses on clock setting for RK3288 mmc controller. In RK3288 mmc controller, CLKDIV register can only be set 0 or 1, and if DDR 8bit mode, CLKDIV register must be set 1. Signed-off-by: Addy Ke Signed-off-by: Doug Anderson Acked-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-pltfm.c | 56 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index d4a47a9f5584..b547f7ab18bd 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -21,17 +21,67 @@ #include #include #include +#include #include "dw_mmc.h" #include "dw_mmc-pltfm.h" +#define RK3288_CLKGEN_DIV 2 + static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr) { *cmdr |= SDMMC_CMD_USE_HOLD_REG; } -static const struct dw_mci_drv_data rockchip_drv_data = { +static int dw_mci_rk3288_setup_clock(struct dw_mci *host) +{ + host->bus_hz /= RK3288_CLKGEN_DIV; + + return 0; +} + +static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + int ret; + unsigned int cclkin; + u32 bus_hz; + + /* + * cclkin: source clock of mmc controller. + * bus_hz: card interface clock generated by CLKGEN. + * bus_hz = cclkin / RK3288_CLKGEN_DIV; + * ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div)) + * + * Note: div can only be 0 or 1 + * if DDR50 8bit mode(only emmc work in 8bit mode), + * div must be set 1 + */ + if ((ios->bus_width == MMC_BUS_WIDTH_8) && + (ios->timing == MMC_TIMING_MMC_DDR52)) + cclkin = 2 * ios->clock * RK3288_CLKGEN_DIV; + else + cclkin = ios->clock * RK3288_CLKGEN_DIV; + + ret = clk_set_rate(host->ciu_clk, cclkin); + if (ret) + dev_warn(host->dev, "failed to set rate %uHz\n", ios->clock); + + bus_hz = clk_get_rate(host->ciu_clk) / RK3288_CLKGEN_DIV; + if (bus_hz != host->bus_hz) { + host->bus_hz = bus_hz; + /* force dw_mci_setup_bus() */ + host->current_speed = 0; + } +} + +static const struct dw_mci_drv_data rk2928_drv_data = { + .prepare_command = dw_mci_pltfm_prepare_command, +}; + +static const struct dw_mci_drv_data rk3288_drv_data = { .prepare_command = dw_mci_pltfm_prepare_command, + .set_ios = dw_mci_rk3288_set_ios, + .setup_clock = dw_mci_rk3288_setup_clock, }; static const struct dw_mci_drv_data socfpga_drv_data = { @@ -95,7 +145,9 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); static const struct of_device_id dw_mci_pltfm_match[] = { { .compatible = "snps,dw-mshc", }, { .compatible = "rockchip,rk2928-dw-mshc", - .data = &rockchip_drv_data }, + .data = &rk2928_drv_data }, + { .compatible = "rockchip,rk3288-dw-mshc", + .data = &rk3288_drv_data }, { .compatible = "altr,socfpga-dw-mshc", .data = &socfpga_drv_data }, {}, -- cgit v1.2.3 From 7ca27a6f80a4042666a28977ff8ee3aa527c6cd4 Mon Sep 17 00:00:00 2001 From: Apelete Seketeli Date: Mon, 21 Jul 2014 06:37:44 +0200 Subject: mmc: jz4740: add dma infrastructure for data transfers Until now the MMC driver for JZ4740 SoC was relying on PIO mode only for data transfers. This patch allows the use of DMA for data trasnfers in addition to PIO mode by relying on DMA Engine. DMA tranfers performance might be further improved by taking advantage of the asynchronous request capability of the MMC framework. Signed-off-by: Apelete Seketeli Signed-off-by: Ulf Hansson --- drivers/mmc/host/jz4740_mmc.c | 174 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 166 insertions(+), 8 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 537d6c7a5ae4..049b13353917 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -30,7 +30,9 @@ #include #include #include +#include +#include #include #define JZ_REG_MMC_STRPCL 0x00 @@ -122,6 +124,7 @@ struct jz4740_mmc_host { int card_detect_irq; void __iomem *base; + struct resource *mem_res; struct mmc_request *req; struct mmc_command *cmd; @@ -136,8 +139,138 @@ struct jz4740_mmc_host { struct timer_list timeout_timer; struct sg_mapping_iter miter; enum jz4740_mmc_state state; + + /* DMA support */ + struct dma_chan *dma_rx; + struct dma_chan *dma_tx; + bool use_dma; + int sg_len; + +/* The DMA trigger level is 8 words, that is to say, the DMA read + * trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write + * trigger is when data words in MSC_TXFIFO is < 8. + */ +#define JZ4740_MMC_FIFO_HALF_SIZE 8 }; +/*----------------------------------------------------------------------------*/ +/* DMA infrastructure */ + +static void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host) +{ + if (!host->use_dma) + return; + + dma_release_channel(host->dma_tx); + dma_release_channel(host->dma_rx); +} + +static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host) +{ + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + host->dma_tx = dma_request_channel(mask, NULL, host); + if (!host->dma_tx) { + dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n"); + return -ENODEV; + } + + host->dma_rx = dma_request_channel(mask, NULL, host); + if (!host->dma_rx) { + dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n"); + goto free_master_write; + } + + return 0; + +free_master_write: + dma_release_channel(host->dma_tx); + return -ENODEV; +} + +static inline int jz4740_mmc_get_dma_dir(struct mmc_data *data) +{ + return (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; +} + +static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host, + struct mmc_data *data) +{ + struct dma_chan *chan; + enum dma_data_direction dir = jz4740_mmc_get_dma_dir(data); + + if (dir == DMA_TO_DEVICE) + chan = host->dma_tx; + else + chan = host->dma_rx; + + dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); +} + +static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host, + struct mmc_data *data) +{ + struct dma_chan *chan; + struct dma_async_tx_descriptor *desc; + struct dma_slave_config conf = { + .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, + .dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, + }; + enum dma_data_direction dir = jz4740_mmc_get_dma_dir(data); + + if (dir == DMA_TO_DEVICE) { + conf.direction = DMA_MEM_TO_DEV; + conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO; + conf.slave_id = JZ4740_DMA_TYPE_MMC_TRANSMIT; + chan = host->dma_tx; + } else { + conf.direction = DMA_DEV_TO_MEM; + conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO; + conf.slave_id = JZ4740_DMA_TYPE_MMC_RECEIVE; + chan = host->dma_rx; + } + + host->sg_len = dma_map_sg(chan->device->dev, + data->sg, + data->sg_len, + dir); + + if (host->sg_len == 0) { + dev_err(mmc_dev(host->mmc), + "Failed to map scatterlist for DMA operation\n"); + return -EINVAL; + } + + dmaengine_slave_config(chan, &conf); + desc = dmaengine_prep_slave_sg(chan, + data->sg, + host->sg_len, + conf.direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(mmc_dev(host->mmc), + "Failed to allocate DMA %s descriptor", + conf.direction == DMA_MEM_TO_DEV ? "TX" : "RX"); + goto dma_unmap; + } + + dmaengine_submit(desc); + dma_async_issue_pending(chan); + + return 0; + +dma_unmap: + jz4740_mmc_dma_unmap(host, data); + return -ENOMEM; +} + +/*----------------------------------------------------------------------------*/ + static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, unsigned int irq, bool enabled) { @@ -442,6 +575,8 @@ static void jz4740_mmc_send_command(struct jz4740_mmc_host *host, cmdat |= JZ_MMC_CMDAT_WRITE; if (cmd->data->flags & MMC_DATA_STREAM) cmdat |= JZ_MMC_CMDAT_STREAM; + if (host->use_dma) + cmdat |= JZ_MMC_CMDAT_DMA_EN; writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN); writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB); @@ -474,6 +609,7 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid) struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid; struct mmc_command *cmd = host->req->cmd; struct mmc_request *req = host->req; + struct mmc_data *data = cmd->data; bool timeout = false; if (cmd->error) @@ -484,23 +620,32 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid) if (cmd->flags & MMC_RSP_PRESENT) jz4740_mmc_read_response(host, cmd); - if (!cmd->data) + if (!data) break; jz_mmc_prepare_data_transfer(host); case JZ4740_MMC_STATE_TRANSFER_DATA: - if (cmd->data->flags & MMC_DATA_READ) - timeout = jz4740_mmc_read_data(host, cmd->data); + if (host->use_dma) { + /* Use DMA if enabled, data transfer direction was + * defined before in jz_mmc_prepare_data_transfer(). + */ + timeout = jz4740_mmc_start_dma_transfer(host, data); + data->bytes_xfered = data->blocks * data->blksz; + } else if (data->flags & MMC_DATA_READ) + /* If DMA is not enabled, rely on data flags + * to establish data transfer direction. + */ + timeout = jz4740_mmc_read_data(host, data); else - timeout = jz4740_mmc_write_data(host, cmd->data); + timeout = jz4740_mmc_write_data(host, data); if (unlikely(timeout)) { host->state = JZ4740_MMC_STATE_TRANSFER_DATA; break; } - jz4740_mmc_transfer_check_state(host, cmd->data); + jz4740_mmc_transfer_check_state(host, data); timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE); if (unlikely(timeout)) { @@ -757,7 +902,6 @@ static int jz4740_mmc_probe(struct platform_device* pdev) struct mmc_host *mmc; struct jz4740_mmc_host *host; struct jz4740_mmc_platform_data *pdata; - struct resource *res; pdata = pdev->dev.platform_data; @@ -784,10 +928,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev) goto err_free_host; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - host->base = devm_ioremap_resource(&pdev->dev, res); + host->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->base = devm_ioremap_resource(&pdev->dev, host->mem_res); if (IS_ERR(host->base)) { ret = PTR_ERR(host->base); + dev_err(&pdev->dev, "Failed to ioremap base memory\n"); goto err_free_host; } @@ -834,6 +979,10 @@ static int jz4740_mmc_probe(struct platform_device* pdev) /* It is not important when it times out, it just needs to timeout. */ set_timer_slack(&host->timeout_timer, HZ); + host->use_dma = true; + if (host->use_dma && jz4740_mmc_acquire_dma_channels(host) != 0) + host->use_dma = false; + platform_set_drvdata(pdev, host); ret = mmc_add_host(mmc); @@ -843,6 +992,10 @@ static int jz4740_mmc_probe(struct platform_device* pdev) } dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n"); + dev_info(&pdev->dev, "Using %s, %d-bit mode\n", + host->use_dma ? "DMA" : "PIO", + (mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1); + return 0; err_free_irq: @@ -850,6 +1003,8 @@ err_free_irq: err_free_gpios: jz4740_mmc_free_gpios(pdev); err_gpio_bulk_free: + if (host->use_dma) + jz4740_mmc_release_dma_channels(host); jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); err_free_host: mmc_free_host(mmc); @@ -872,6 +1027,9 @@ static int jz4740_mmc_remove(struct platform_device *pdev) jz4740_mmc_free_gpios(pdev); jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); + if (host->use_dma) + jz4740_mmc_release_dma_channels(host); + mmc_free_host(host->mmc); return 0; -- cgit v1.2.3 From bb2f45927f8e0d1fc0633f65cc1f17a40c80bf24 Mon Sep 17 00:00:00 2001 From: Apelete Seketeli Date: Mon, 21 Jul 2014 06:37:45 +0200 Subject: mmc: jz4740: prepare next dma transfer in parallel with current transfer Make use of the MMC asynchronous request capability to prepare the next DMA transfer request in parallel with the current transfer. This is done by adding pre-request and post-request callbacks that are used by the MMC framework during an active data transfer. It should help reduce the impact of DMA preparation overhead on the SD card performance. Signed-off-by: Apelete Seketeli Signed-off-by: Ulf Hansson --- drivers/mmc/host/jz4740_mmc.c | 138 +++++++++++++++++++++++++++++++++++------- 1 file changed, 116 insertions(+), 22 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 049b13353917..14738cddcadb 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -114,6 +114,11 @@ enum jz4740_mmc_state { JZ4740_MMC_STATE_DONE, }; +struct jz4740_mmc_host_next { + int sg_len; + s32 cookie; +}; + struct jz4740_mmc_host { struct mmc_host *mmc; struct platform_device *pdev; @@ -143,6 +148,7 @@ struct jz4740_mmc_host { /* DMA support */ struct dma_chan *dma_rx; struct dma_chan *dma_tx; + struct jz4740_mmc_host_next next_data; bool use_dma; int sg_len; @@ -184,6 +190,9 @@ static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host) goto free_master_write; } + /* Initialize DMA pre request cookie */ + host->next_data.cookie = 1; + return 0; free_master_write: @@ -196,23 +205,72 @@ static inline int jz4740_mmc_get_dma_dir(struct mmc_data *data) return (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; } +static inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host, + struct mmc_data *data) +{ + return (data->flags & MMC_DATA_READ) ? host->dma_rx : host->dma_tx; +} + static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host, struct mmc_data *data) { - struct dma_chan *chan; + struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); enum dma_data_direction dir = jz4740_mmc_get_dma_dir(data); - if (dir == DMA_TO_DEVICE) - chan = host->dma_tx; - else - chan = host->dma_rx; - dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); } +/* Prepares DMA data for current/next transfer, returns non-zero on failure */ +static int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host, + struct mmc_data *data, + struct jz4740_mmc_host_next *next, + struct dma_chan *chan) +{ + struct jz4740_mmc_host_next *next_data = &host->next_data; + enum dma_data_direction dir = jz4740_mmc_get_dma_dir(data); + int sg_len; + + if (!next && data->host_cookie && + data->host_cookie != host->next_data.cookie) { + dev_warn(mmc_dev(host->mmc), + "[%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; + } + + /* Check if next job is already prepared */ + if (next || data->host_cookie != host->next_data.cookie) { + sg_len = dma_map_sg(chan->device->dev, + data->sg, + data->sg_len, + dir); + + } else { + sg_len = next_data->sg_len; + next_data->sg_len = 0; + } + + if (sg_len <= 0) { + dev_err(mmc_dev(host->mmc), + "Failed to map scatterlist for DMA operation\n"); + return -EINVAL; + } + + if (next) { + next->sg_len = sg_len; + data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie; + } else + host->sg_len = sg_len; + + return 0; +} + static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host, struct mmc_data *data) { + int ret; struct dma_chan *chan; struct dma_async_tx_descriptor *desc; struct dma_slave_config conf = { @@ -221,9 +279,8 @@ static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host, .src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, .dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, }; - enum dma_data_direction dir = jz4740_mmc_get_dma_dir(data); - if (dir == DMA_TO_DEVICE) { + if (data->flags & MMC_DATA_WRITE) { conf.direction = DMA_MEM_TO_DEV; conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO; conf.slave_id = JZ4740_DMA_TYPE_MMC_TRANSMIT; @@ -235,16 +292,9 @@ static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host, chan = host->dma_rx; } - host->sg_len = dma_map_sg(chan->device->dev, - data->sg, - data->sg_len, - dir); - - if (host->sg_len == 0) { - dev_err(mmc_dev(host->mmc), - "Failed to map scatterlist for DMA operation\n"); - return -EINVAL; - } + ret = jz4740_mmc_prepare_dma_data(host, data, NULL, chan); + if (ret) + return ret; dmaengine_slave_config(chan, &conf); desc = dmaengine_prep_slave_sg(chan, @@ -269,6 +319,43 @@ dma_unmap: return -ENOMEM; } +static void jz4740_mmc_pre_request(struct mmc_host *mmc, + struct mmc_request *mrq, + bool is_first_req) +{ + struct jz4740_mmc_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + struct jz4740_mmc_host_next *next_data = &host->next_data; + + BUG_ON(data->host_cookie); + + if (host->use_dma) { + struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); + + if (jz4740_mmc_prepare_dma_data(host, data, next_data, chan)) + data->host_cookie = 0; + } +} + +static void jz4740_mmc_post_request(struct mmc_host *mmc, + struct mmc_request *mrq, + int err) +{ + struct jz4740_mmc_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (host->use_dma && data->host_cookie) { + jz4740_mmc_dma_unmap(host, data); + data->host_cookie = 0; + } + + if (err) { + struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); + + dmaengine_terminate_all(chan); + } +} + /*----------------------------------------------------------------------------*/ static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, @@ -627,14 +714,19 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid) case JZ4740_MMC_STATE_TRANSFER_DATA: if (host->use_dma) { - /* Use DMA if enabled, data transfer direction was - * defined before in jz_mmc_prepare_data_transfer(). + /* Use DMA if enabled. + * Data transfer direction is defined later by + * relying on data flags in + * jz4740_mmc_prepare_dma_data() and + * jz4740_mmc_start_dma_transfer(). */ timeout = jz4740_mmc_start_dma_transfer(host, data); data->bytes_xfered = data->blocks * data->blksz; } else if (data->flags & MMC_DATA_READ) - /* If DMA is not enabled, rely on data flags - * to establish data transfer direction. + /* Use PIO if DMA is not enabled. + * Data transfer direction was defined before + * by relying on data flags in + * jz_mmc_prepare_data_transfer(). */ timeout = jz4740_mmc_read_data(host, data); else @@ -809,6 +901,8 @@ static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) static const struct mmc_host_ops jz4740_mmc_ops = { .request = jz4740_mmc_request, + .pre_req = jz4740_mmc_pre_request, + .post_req = jz4740_mmc_post_request, .set_ios = jz4740_mmc_set_ios, .get_ro = mmc_gpio_get_ro, .get_cd = mmc_gpio_get_cd, -- cgit v1.2.3 From ae9060377a67a0840f980bc0e2cb32af07baf479 Mon Sep 17 00:00:00 2001 From: "Chuanxiao.Dong" Date: Fri, 1 Aug 2014 14:00:13 +0800 Subject: mmc: sdhci: fix the wrong type of curr curr should use signed type since it will contain the returned value which is possible to be a negative value. Using u32 will make the returned value to be true even there is a negative result. Change to use int instead of u32 Signed-off-by: Chuanxiao Dong Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 37b2a9ae52ef..d25deff305a5 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3049,7 +3049,7 @@ int sdhci_add_host(struct sdhci_host *host) */ max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT); if (!max_current_caps && !IS_ERR(mmc->supply.vmmc)) { - u32 curr = regulator_get_current_limit(mmc->supply.vmmc); + int curr = regulator_get_current_limit(mmc->supply.vmmc); if (curr > 0) { /* convert to SDHCI_MAX_CURRENT format */ -- cgit v1.2.3 From 2aa354650a8b024a507f5b0fd969b7cfa3848620 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Wed, 13 Aug 2014 08:13:43 -0700 Subject: mmc: dw_mmc: Make sure we don't get stuck when we get an error If we happened to get a data error at just the wrong time the dw_mmc driver could get into a state where it would never complete its request. That would leave the caller just hanging there. We fix this two ways and both of the two fixes on their own appear to fix the problems we've seen: 1. Fix a race in the tasklet where the interrupt setting the data error happens _just after_ we check for it, then we get a EVENT_XFER_COMPLETE. We fix this by repeating a bit of code. 2. Fix it so that if we detect that we've got an error in the "data busy" state and we're not going to do anything else we end the request and unblock anyone waiting. Signed-off-by: Doug Anderson Signed-off-by: Yuvaraj Kumar C D Acked-by: Seungwon Jeon Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 8f216edbdf08..7f227e964265 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1299,6 +1299,14 @@ static void dw_mci_tasklet_func(unsigned long priv) /* fall through */ case STATE_SENDING_DATA: + /* + * We could get a data error and never a transfer + * complete so we'd better check for it here. + * + * Note that we don't really care if we also got a + * transfer complete; stopping the DMA and sending an + * abort won't hurt. + */ if (test_and_clear_bit(EVENT_DATA_ERROR, &host->pending_events)) { dw_mci_stop_dma(host); @@ -1312,7 +1320,29 @@ static void dw_mci_tasklet_func(unsigned long priv) break; set_bit(EVENT_XFER_COMPLETE, &host->completed_events); + + /* + * Handle an EVENT_DATA_ERROR that might have shown up + * before the transfer completed. This might not have + * been caught by the check above because the interrupt + * could have gone off between the previous check and + * the check for transfer complete. + * + * Technically this ought not be needed assuming we + * get a DATA_COMPLETE eventually (we'll notice the + * error and end the request), but it shouldn't hurt. + * + * This has the advantage of sending the stop command. + */ + if (test_and_clear_bit(EVENT_DATA_ERROR, + &host->pending_events)) { + dw_mci_stop_dma(host); + send_stop_abort(host, data); + state = STATE_DATA_ERROR; + break; + } prev_state = state = STATE_DATA_BUSY; + /* fall through */ case STATE_DATA_BUSY: @@ -1335,6 +1365,22 @@ static void dw_mci_tasklet_func(unsigned long priv) /* stop command for open-ended transfer*/ if (data->stop) send_stop_abort(host, data); + } else { + /* + * If we don't have a command complete now we'll + * never get one since we just reset everything; + * better end the request. + * + * If we do have a command complete we'll fall + * through to the SENDING_STOP command and + * everything will be peachy keen. + */ + if (!test_bit(EVENT_CMD_COMPLETE, + &host->pending_events)) { + host->cmd = NULL; + dw_mci_request_end(host, mrq); + goto unlock; + } } /* -- cgit v1.2.3 From ee9d19d42c6b30e464a4c748654582fc6b3ff853 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 14 Aug 2014 09:00:19 +0800 Subject: mmc: au1xmmc: fix error return code in au1xmmc_probe() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: Ulf Hansson --- drivers/mmc/host/au1xmmc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index 9c9f6af29251..725f6a6fd89b 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -1028,9 +1028,12 @@ static int au1xmmc_probe(struct platform_device *pdev) host->clk = clk_get(&pdev->dev, ALCHEMY_PERIPH_CLK); if (IS_ERR(host->clk)) { dev_err(&pdev->dev, "cannot find clock\n"); + ret = PTR_ERR(host->clk); goto out_irq; } - if (clk_prepare_enable(host->clk)) { + + ret = clk_prepare_enable(host->clk); + if (ret) { dev_err(&pdev->dev, "cannot enable clock\n"); goto out_clk; } -- cgit v1.2.3 From d1419d50c1bf711e9fd27b516a739c86b23f7cf9 Mon Sep 17 00:00:00 2001 From: Roger Tseng Date: Fri, 15 Aug 2014 14:06:00 +0800 Subject: mmc: rtsx_pci_sdmmc: fix incorrect last byte in R2 response Current code erroneously fill the last byte of R2 response with an undefined value. In addition, the controller actually 'offloads' the last byte (CRC7, end bit) while receiving R2 response and thus it's impossible to get the actual value. This could cause mmc stack to obtain inconsistent CID from the same card after resume and misidentify it as a different card. Fix by assigning dummy CRC and end bit: {7'b0, 1} = 0x1 to the last byte of R2. Cc: # v3.8+ Fixes: ff984e57d36e ("mmc: Add realtek pcie sdmmc host driver") Signed-off-by: Roger Tseng Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index dfde4a210238..b2537e2f26b1 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -412,6 +412,13 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, } if (rsp_type == SD_RSP_TYPE_R2) { + /* + * The controller offloads the last byte {CRC-7, end bit 1'b1} + * of response type R2. Assign dummy CRC, 0, and end bit to the + * byte(ptr[16], goes into the LSB of resp[3] later). + */ + ptr[16] = 1; + for (i = 0; i < 4; i++) { cmd->resp[i] = get_unaligned_be32(ptr + 1 + i * 4); dev_dbg(sdmmc_dev(host), "cmd->resp[%d] = 0x%08x\n", -- cgit v1.2.3 From 6f67cc6fd1cf339a0f19b9d4a998ec3c0123b1b6 Mon Sep 17 00:00:00 2001 From: Roger Tseng Date: Fri, 15 Aug 2014 14:06:01 +0800 Subject: mmc: rtsx_usb_sdmmc: fix incorrect last byte in R2 response Current code erroneously fill the last byte of R2 response with an undefined value. In addition, the controller actually 'offloads' the last byte (CRC7, end bit) while receiving R2 response and thus it's impossible to get the actual value. This could cause mmc stack to obtain inconsistent CID from the same card after resume and misidentify it as a different card. Fix by assigning dummy CRC and end bit: {7'b0, 1} = 0x1 to the last byte of R2. Cc: # v3.16+ Fixes: c7f6558d84af ("mmc: Add realtek USB sdmmc host driver") Signed-off-by: Roger Tseng Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_usb_sdmmc.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index 5d3766e792f0..d9153a7d160d 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -435,6 +435,13 @@ static void sd_send_cmd_get_rsp(struct rtsx_usb_sdmmc *host, } if (rsp_type == SD_RSP_TYPE_R2) { + /* + * The controller offloads the last byte {CRC-7, end bit 1'b1} + * of response type R2. Assign dummy CRC, 0, and end bit to the + * byte(ptr[16], goes into the LSB of resp[3] later). + */ + ptr[16] = 1; + for (i = 0; i < 4; i++) { cmd->resp[i] = get_unaligned_be32(ptr + 1 + i * 4); dev_dbg(sdmmc_dev(host), "cmd->resp[%d] = 0x%08x\n", -- cgit v1.2.3 From c5abd5e8998e20c2e82999670a03f26f1437d744 Mon Sep 17 00:00:00 2001 From: Matthieu CASTET Date: Thu, 14 Aug 2014 16:03:17 +0200 Subject: sdhci : handle busy timeout irq When we wait for busy after sending a command, if there is a timeout, we got SDHCI_INT_DATA_TIMEOUT flags. Before this commit we got the message : "Got data interrupt 0x00100000 even though no data operation was in progress." and we need to wait 10s that sdhci_timeout_timer expires. Signed-off-by: Matthieu CASTET Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index d25deff305a5..bcad1084cc74 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2304,6 +2304,11 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) * above in sdhci_cmd_irq(). */ if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) { + if (intmask & SDHCI_INT_DATA_TIMEOUT) { + host->cmd->error = -ETIMEDOUT; + tasklet_schedule(&host->finish_tasklet); + return; + } if (intmask & SDHCI_INT_DATA_END) { sdhci_finish_command(host); return; -- cgit v1.2.3 From fac6a52fe9b14851d56b3c19c3d5a95790c46fac Mon Sep 17 00:00:00 2001 From: Matthieu CASTET Date: Thu, 14 Aug 2014 16:03:18 +0200 Subject: sdhci : recompute timeout_clk when needed when SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK is set, timeout_clk is sdclk. We need to update it when we change sdclk in sdhci_set_clock. This allow to have a more precisse timeout and max_busy_timeout. This can help for command that need a big busy wait (erase, ...). Signed-off-by: Matthieu CASTET Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index bcad1084cc74..4cc2ff90eeef 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1192,8 +1192,14 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) } clock_set: - if (real_div) + if (real_div) { host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div; + if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) { + host->timeout_clk = host->mmc->actual_clock / 1000; + host->mmc->max_busy_timeout = + (1 << 27) / host->timeout_clk; + } + } clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) -- cgit v1.2.3 From 0857c6912d5cd04d177473f2be38709f08982710 Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Thu, 14 Aug 2014 09:45:27 -0700 Subject: mmc: dw_mmc: Add dependency on DMA The dw_mmc drivers rely on the DMA API, so update the Kconfig entry to depend on HAS_DMA. Since the drivers should build on any platform with DMA, allow the driver to compile tested on non-ARC/ARM platforms. Signed-off-by: Andrew Bresticker Reviewed-by: Alim Akhtar Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 58aedcc900d1..63766f72330e 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -579,7 +579,8 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND config MMC_DW tristate "Synopsys DesignWare Memory Card Interface" - depends on ARC || ARM + depends on HAS_DMA + depends on ARC || ARM || COMPILE_TEST help This selects support for the Synopsys DesignWare Mobile Storage IP block, this provides host support for SD and MMC interfaces, in both -- cgit v1.2.3 From b1dec12032af3a5239171c76cd43a4d372d8829b Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Thu, 14 Aug 2014 09:45:28 -0700 Subject: mmc: dw_mmc: Add support for MIPS There are upcoming MIPS SoCs with dw_mmc hosts. Signed-off-by: Andrew Bresticker Reviewed-by: Alim Akhtar Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 63766f72330e..a43295c49a44 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -580,7 +580,7 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND config MMC_DW tristate "Synopsys DesignWare Memory Card Interface" depends on HAS_DMA - depends on ARC || ARM || COMPILE_TEST + depends on ARC || ARM || MIPS || COMPILE_TEST help This selects support for the Synopsys DesignWare Mobile Storage IP block, this provides host support for SD and MMC interfaces, in both -- cgit v1.2.3 From dea67c4ec8218b301d7cac7ee6e63dac0bc566cb Mon Sep 17 00:00:00 2001 From: Fu Zhonghui Date: Mon, 18 Aug 2014 10:48:14 +0800 Subject: mmc: core: sdio: Fix unconditional wake_up_process() on sdio thread 781e989cf59 ("mmc: sdhci: convert to new SDIO IRQ handling") and bf3b5ec66bd ("mmc: sdio_irq: rework sdio irq handling") disabled the use of our own custom threaded IRQ handler, but left in an unconditional wake_up_process() on that handler at resume-time. Link: https://bugzilla.kernel.org/show_bug.cgi?id=80151 In addition, the check for MMC_CAP_SDIO_IRQ capability is added before enable sdio IRQ. Signed-off-by: Jaehoon Chung Signed-off-by: Chris Ball Signed-off-by: Fu Zhonghui Cc: # v3.16+ Signed-off-by: Ulf Hansson --- drivers/mmc/core/sdio.c | 12 ++++++++++-- drivers/mmc/core/sdio_irq.c | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index e636d9e99e4a..3fc40a7140a8 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -992,8 +992,16 @@ static int mmc_sdio_resume(struct mmc_host *host) } } - if (!err && host->sdio_irqs) - wake_up_process(host->sdio_irq_thread); + if (!err && host->sdio_irqs) { + if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { + wake_up_process(host->sdio_irq_thread); + } else if (host->caps & MMC_CAP_SDIO_IRQ) { + mmc_host_clk_hold(host); + host->ops->enable_sdio_irq(host, 1); + mmc_host_clk_release(host); + } + } + mmc_release_host(host); host->pm_flags &= ~MMC_PM_KEEP_POWER; diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index 5cc13c8d35bb..696eca493844 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -208,7 +208,7 @@ static int sdio_card_irq_get(struct mmc_card *card) host->sdio_irqs--; return err; } - } else { + } else if (host->caps & MMC_CAP_SDIO_IRQ) { mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 1); mmc_host_clk_release(host); @@ -229,7 +229,7 @@ static int sdio_card_irq_put(struct mmc_card *card) if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { atomic_set(&host->sdio_irq_thread_abort, 1); kthread_stop(host->sdio_irq_thread); - } else { + } else if (host->caps & MMC_CAP_SDIO_IRQ) { mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 0); mmc_host_clk_release(host); -- cgit v1.2.3 From 2137f5d3b8e8e04cff06194cacd0f6357495ac94 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Tue, 12 Aug 2014 17:14:25 +0100 Subject: mmc: remove .owner field for drivers using module_platform_driver This patch removes the superflous .owner field for drivers which use the module_platform_driver API, as this is overriden in platform_driver_register anyway. Signed-off-by: Peter Griffin Signed-off-by: Ulf Hansson --- drivers/mmc/host/jz4740_mmc.c | 1 - drivers/mmc/host/moxart-mmc.c | 1 - drivers/mmc/host/mxcmmc.c | 1 - drivers/mmc/host/mxs-mmc.c | 1 - drivers/mmc/host/omap.c | 1 - drivers/mmc/host/omap_hsmmc.c | 1 - drivers/mmc/host/pxamci.c | 1 - drivers/mmc/host/rtsx_pci_sdmmc.c | 1 - drivers/mmc/host/rtsx_usb_sdmmc.c | 1 - drivers/mmc/host/s3cmci.c | 1 - drivers/mmc/host/sdhci-bcm-kona.c | 1 - drivers/mmc/host/sdhci-bcm2835.c | 1 - drivers/mmc/host/sdhci-cns3xxx.c | 1 - drivers/mmc/host/sdhci-dove.c | 1 - drivers/mmc/host/sdhci-esdhc-imx.c | 1 - drivers/mmc/host/sdhci-msm.c | 1 - drivers/mmc/host/sdhci-of-arasan.c | 1 - drivers/mmc/host/sdhci-of-esdhc.c | 1 - drivers/mmc/host/sdhci-of-hlwd.c | 1 - drivers/mmc/host/sdhci-pxav2.c | 1 - drivers/mmc/host/sdhci-pxav3.c | 1 - drivers/mmc/host/sdhci-s3c.c | 1 - drivers/mmc/host/sdhci-sirf.c | 1 - drivers/mmc/host/sdhci-spear.c | 1 - drivers/mmc/host/sdhci-tegra.c | 1 - drivers/mmc/host/sh_mmcif.c | 1 - drivers/mmc/host/sh_mobile_sdhi.c | 1 - drivers/mmc/host/sunxi-mmc.c | 1 - 28 files changed, 28 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 14738cddcadb..76e8bce6f46e 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -1161,7 +1161,6 @@ static struct platform_driver jz4740_mmc_driver = { .remove = jz4740_mmc_remove, .driver = { .name = "jz4740-mmc", - .owner = THIS_MODULE, .pm = JZ4740_MMC_PM_OPS, }, }; diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index b4b1efbf6c16..f3e18d08e852 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -717,7 +717,6 @@ static struct platform_driver moxart_mmc_driver = { .remove = moxart_remove, .driver = { .name = "mmc-moxart", - .owner = THIS_MODULE, .of_match_table = moxart_mmc_match, }, }; diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index ed1cb93c3784..ad111422ad55 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -1238,7 +1238,6 @@ static struct platform_driver mxcmci_driver = { .id_table = mxcmci_devtype, .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, .pm = &mxcmci_pm_ops, .of_match_table = mxcmci_of_match, } diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 140885a5a4e7..cd74e5143c36 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -735,7 +735,6 @@ static struct platform_driver mxs_mmc_driver = { .id_table = mxs_ssp_ids, .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &mxs_mmc_pm_ops, #endif diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 81974ecdfcbc..68dd6c79c378 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1494,7 +1494,6 @@ static struct platform_driver mmc_omap_driver = { .remove = mmc_omap_remove, .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, .of_match_table = of_match_ptr(mmc_omap_match), }, }; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 965672663ef0..ece163493a54 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -2489,7 +2489,6 @@ static struct platform_driver omap_hsmmc_driver = { .remove = omap_hsmmc_remove, .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, .pm = &omap_hsmmc_dev_pm_ops, .of_match_table = of_match_ptr(omap_mmc_of_match), }, diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 32fe11323f39..0971a234f449 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -885,7 +885,6 @@ static struct platform_driver pxamci_driver = { .remove = pxamci_remove, .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, .of_match_table = of_match_ptr(pxa_mmc_dt_ids), }, }; diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index b2537e2f26b1..d928216b6b5f 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1423,7 +1423,6 @@ static struct platform_driver rtsx_pci_sdmmc_driver = { .remove = rtsx_pci_sdmmc_drv_remove, .id_table = rtsx_pci_sdmmc_ids, .driver = { - .owner = THIS_MODULE, .name = DRV_NAME_RTSX_PCI_SDMMC, }, }; diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index d9153a7d160d..e46c574fa574 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -1452,7 +1452,6 @@ static struct platform_driver rtsx_usb_sdmmc_driver = { .remove = rtsx_usb_sdmmc_drv_remove, .id_table = rtsx_usb_sdmmc_ids, .driver = { - .owner = THIS_MODULE, .name = "rtsx_usb_sdmmc", }, }; diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index e5516a226362..e4e1a5388163 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1874,7 +1874,6 @@ MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids); static struct platform_driver s3cmci_driver = { .driver = { .name = "s3c-sdi", - .owner = THIS_MODULE, }, .id_table = s3cmci_driver_ids, .probe = s3cmci_probe, diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index dd780c315a63..2245988a01a9 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -359,7 +359,6 @@ static int sdhci_bcm_kona_remove(struct platform_device *pdev) static struct platform_driver sdhci_bcm_kona_driver = { .driver = { .name = "sdhci-kona", - .owner = THIS_MODULE, .pm = SDHCI_PLTFM_PMOPS, .of_match_table = sdhci_bcm_kona_of_match, }, diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c index 46af9a439d7b..439d259fdf1d 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -194,7 +194,6 @@ MODULE_DEVICE_TABLE(of, bcm2835_sdhci_of_match); static struct platform_driver bcm2835_sdhci_driver = { .driver = { .name = "sdhci-bcm2835", - .owner = THIS_MODULE, .of_match_table = bcm2835_sdhci_of_match, .pm = SDHCI_PLTFM_PMOPS, }, diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index 14b74075589a..a7935a8d0922 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -106,7 +106,6 @@ static int sdhci_cns3xxx_remove(struct platform_device *pdev) static struct platform_driver sdhci_cns3xxx_driver = { .driver = { .name = "sdhci-cns3xxx", - .owner = THIS_MODULE, .pm = SDHCI_PLTFM_PMOPS, }, .probe = sdhci_cns3xxx_probe, diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index e6278ec007d7..ca969d271a27 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -146,7 +146,6 @@ MODULE_DEVICE_TABLE(of, sdhci_dove_of_match_table); static struct platform_driver sdhci_dove_driver = { .driver = { .name = "sdhci-dove", - .owner = THIS_MODULE, .pm = SDHCI_PLTFM_PMOPS, .of_match_table = sdhci_dove_of_match_table, }, diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index ccec0e32590f..46677f09ad56 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1207,7 +1207,6 @@ static const struct dev_pm_ops sdhci_esdhc_pmops = { static struct platform_driver sdhci_esdhc_imx_driver = { .driver = { .name = "sdhci-esdhc-imx", - .owner = THIS_MODULE, .of_match_table = imx_esdhc_dt_ids, .pm = &sdhci_esdhc_pmops, }, diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 1a6661ed6205..2f19eabac3ea 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -610,7 +610,6 @@ static struct platform_driver sdhci_msm_driver = { .remove = sdhci_msm_remove, .driver = { .name = "sdhci_msm", - .owner = THIS_MODULE, .of_match_table = sdhci_msm_dt_match, }, }; diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 5bd1092310f2..981d66e5c023 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -213,7 +213,6 @@ MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); static struct platform_driver sdhci_arasan_driver = { .driver = { .name = "sdhci-arasan", - .owner = THIS_MODULE, .of_match_table = sdhci_arasan_of_match, .pm = &sdhci_arasan_dev_pm_ops, }, diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 8be4dcfb49a0..8872c85c63d4 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -388,7 +388,6 @@ MODULE_DEVICE_TABLE(of, sdhci_esdhc_of_match); static struct platform_driver sdhci_esdhc_driver = { .driver = { .name = "sdhci-esdhc", - .owner = THIS_MODULE, .of_match_table = sdhci_esdhc_of_match, .pm = ESDHC_PMOPS, }, diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index b341661369a2..be479279a1d5 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -89,7 +89,6 @@ MODULE_DEVICE_TABLE(of, sdhci_hlwd_of_match); static struct platform_driver sdhci_hlwd_driver = { .driver = { .name = "sdhci-hlwd", - .owner = THIS_MODULE, .of_match_table = sdhci_hlwd_of_match, .pm = SDHCI_PLTFM_PMOPS, }, diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 3c0f3c0a1cc8..b4c23e983baf 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -261,7 +261,6 @@ static int sdhci_pxav2_remove(struct platform_device *pdev) static struct platform_driver sdhci_pxav2_driver = { .driver = { .name = "sdhci-pxav2", - .owner = THIS_MODULE, #ifdef CONFIG_OF .of_match_table = sdhci_pxav2_of_match, #endif diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 6f842fb8e6b8..cca08b0c8c3e 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -492,7 +492,6 @@ static struct platform_driver sdhci_pxav3_driver = { #ifdef CONFIG_OF .of_match_table = sdhci_pxav3_of_match, #endif - .owner = THIS_MODULE, .pm = SDHCI_PXAV3_PMOPS, }, .probe = sdhci_pxav3_probe, diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 1e47903fa184..0ce6eb17deaf 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -747,7 +747,6 @@ static struct platform_driver sdhci_s3c_driver = { .remove = sdhci_s3c_remove, .id_table = sdhci_s3c_driver_ids, .driver = { - .owner = THIS_MODULE, .name = "s3c-sdhci", .of_match_table = of_match_ptr(sdhci_s3c_dt_match), .pm = SDHCI_S3C_PMOPS, diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 17004531d089..e15dc891ba01 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -167,7 +167,6 @@ MODULE_DEVICE_TABLE(of, sdhci_sirf_of_match); static struct platform_driver sdhci_sirf_driver = { .driver = { .name = "sdhci-sirf", - .owner = THIS_MODULE, .of_match_table = sdhci_sirf_of_match, #ifdef CONFIG_PM_SLEEP .pm = &sdhci_sirf_pm_ops, diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 9d535c7336ef..22e58268545f 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -230,7 +230,6 @@ MODULE_DEVICE_TABLE(of, sdhci_spear_id_table); static struct platform_driver sdhci_driver = { .driver = { .name = "sdhci", - .owner = THIS_MODULE, .pm = &sdhci_pm_ops, .of_match_table = of_match_ptr(sdhci_spear_id_table), }, diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 33100d10d176..59797106af93 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -318,7 +318,6 @@ static int sdhci_tegra_remove(struct platform_device *pdev) static struct platform_driver sdhci_tegra_driver = { .driver = { .name = "sdhci-tegra", - .owner = THIS_MODULE, .of_match_table = sdhci_tegra_dt_match, .pm = SDHCI_PLTFM_PMOPS, }, diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index d11708c815d7..7d9d6a321521 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1553,7 +1553,6 @@ static struct platform_driver sh_mmcif_driver = { .driver = { .name = DRIVER_NAME, .pm = &sh_mmcif_dev_pm_ops, - .owner = THIS_MODULE, .of_match_table = mmcif_of_match, }, }; diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 91058dabd11a..be5e25ddab53 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -341,7 +341,6 @@ static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { static struct platform_driver sh_mobile_sdhi_driver = { .driver = { .name = "sh_mobile_sdhi", - .owner = THIS_MODULE, .pm = &tmio_mmc_dev_pm_ops, .of_match_table = sh_mobile_sdhi_of_match, }, diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 024f67c98cdc..dafe5001a46e 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -1035,7 +1035,6 @@ static int sunxi_mmc_remove(struct platform_device *pdev) static struct platform_driver sunxi_mmc_driver = { .driver = { .name = "sunxi-mmc", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(sunxi_mmc_of_match), }, .probe = sunxi_mmc_probe, -- cgit v1.2.3 From eb4667426ba7b0676e888aa8ffc448fcf607d284 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Tue, 12 Aug 2014 17:14:26 +0100 Subject: mmc: dw_mmc-pci: Remove superflous #else condition on CONFIG_PM_SLEEP As the code is using SIMPLE_DEV_PM_OPS helper, this compiles away to nothing if CONFIG_PM_SLEEP is disabled. Thus we don't need to #define the suspend/resume callbacks to NULL. Signed-off-by: Peter Griffin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-pci.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index 6ada1b36685b..4c69fbd29811 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -95,9 +95,6 @@ static int dw_mci_pci_resume(struct device *dev) return dw_mci_resume(host); } -#else -#define dw_mci_pci_suspend NULL -#define dw_mci_pci_resume NULL #endif /* CONFIG_PM_SLEEP */ static SIMPLE_DEV_PM_OPS(dw_mci_pci_pmops, dw_mci_pci_suspend, dw_mci_pci_resume); -- cgit v1.2.3 From 0529b810944932038fae1691b635d585429e6030 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Tue, 12 Aug 2014 17:14:27 +0100 Subject: mmc: dw_mmc-pltfm: Remove superflous #else condition on CONFIG_PM_SLEEP As the code is using SIMPLE_DEV_PM_OPS helper, this compiles away to nothing if CONFIG_PM_SLEEP is disabled. Thus we don't need to #define the suspend/resume callbacks to NULL. Signed-off-by: Peter Griffin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-pltfm.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index b547f7ab18bd..1cd02828e5a3 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -134,9 +134,6 @@ static int dw_mci_pltfm_resume(struct device *dev) return dw_mci_resume(host); } -#else -#define dw_mci_pltfm_suspend NULL -#define dw_mci_pltfm_resume NULL #endif /* CONFIG_PM_SLEEP */ SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume); -- cgit v1.2.3 From f3a92b1a3258f8ef9a59bab24b9ea68b60908c44 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Tue, 12 Aug 2014 17:14:28 +0100 Subject: mmc: sdhci-pci: Use SET_RUNTIME_PM_OPS macro to set runtime pm callbacks This allows us to get rid of the #else condition, as the macro compiles away to nothing if not enabled. Signed-off-by: Peter Griffin Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index c3a1debc9289..310ecbcdfb7f 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -1269,20 +1269,13 @@ static int sdhci_pci_runtime_idle(struct device *dev) return 0; } -#else - -#define sdhci_pci_runtime_suspend NULL -#define sdhci_pci_runtime_resume NULL -#define sdhci_pci_runtime_idle NULL - #endif static const struct dev_pm_ops sdhci_pci_pm_ops = { .suspend = sdhci_pci_suspend, .resume = sdhci_pci_resume, - .runtime_suspend = sdhci_pci_runtime_suspend, - .runtime_resume = sdhci_pci_runtime_resume, - .runtime_idle = sdhci_pci_runtime_idle, + SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend, + sdhci_pci_runtime_resume, sdhci_pci_runtime_idle) }; /*****************************************************************************\ -- cgit v1.2.3 From 1d75f74b14dbeee1bd9f1e96efa1cbebeb2804cb Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Tue, 12 Aug 2014 17:14:29 +0100 Subject: mmc: sdhci-acpi.c: Use SET_RUNTIME_PM_OPS macro to set runtime pm callbacks This allows us to get rid of the #else condition, as the macro compiles away to nothing if not enabled. Signed-off-by: Peter Griffin Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-acpi.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 8c5337002c51..cc65b3fd0a60 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -385,20 +385,13 @@ static int sdhci_acpi_runtime_idle(struct device *dev) return 0; } -#else - -#define sdhci_acpi_runtime_suspend NULL -#define sdhci_acpi_runtime_resume NULL -#define sdhci_acpi_runtime_idle NULL - #endif static const struct dev_pm_ops sdhci_acpi_pm_ops = { .suspend = sdhci_acpi_suspend, .resume = sdhci_acpi_resume, - .runtime_suspend = sdhci_acpi_runtime_suspend, - .runtime_resume = sdhci_acpi_runtime_resume, - .runtime_idle = sdhci_acpi_runtime_idle, + SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend, + sdhci_acpi_runtime_resume, sdhci_acpi_runtime_idle) }; static struct platform_driver sdhci_acpi_driver = { -- cgit v1.2.3 From b315376573778b195e640a163675fb9f5937ddca Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Fri, 15 Aug 2014 14:02:15 +0100 Subject: mmc: sdhci-pxav3: set_uhs_signaling is initialized twice differently .set_uhs_signaling field is currently initialised twice once to the arch specific callback pxav3_set_uhs_signaling, and also to the generic sdhci_set_uhs_signaling callback. This means that uhs is currently broken for this platform currently, as pxav3 has some special constriants which means it can't use the generic callback. This happened in commit 96d7b78cfc2f ("mmc: sdhci: convert sdhci_set_uhs_signaling() into a library function") commit a702c8abb2a9 ("mmc: host: split up sdhci-pxa, create sdhci-pxav3.c")' Fix this and hopefully prevent it happening in the future by ensuring named initialisers always follow the declaration order in the structure definition. Signed-off-by: Peter Griffin Cc: # v3.16+ Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav3.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index cca08b0c8c3e..46c94d88fa93 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -224,12 +224,11 @@ static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) static const struct sdhci_ops pxav3_sdhci_ops = { .set_clock = sdhci_set_clock, - .set_uhs_signaling = pxav3_set_uhs_signaling, .platform_send_init_74_clocks = pxav3_gen_init_74_clocks, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .set_bus_width = sdhci_set_bus_width, .reset = pxav3_reset, - .set_uhs_signaling = sdhci_set_uhs_signaling, + .set_uhs_signaling = pxav3_set_uhs_signaling, }; static struct sdhci_pltfm_data sdhci_pxav3_pdata = { -- cgit v1.2.3 From 3d705d14fe4c72be83bae1610680e209ee226b9d Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 19 Aug 2014 10:45:51 +0200 Subject: mmc: implement Driver Stage Register handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some eMMC and SD cards implement a DSR register that allows to tune raise/fall times and drive strength of the CMD and DATA outputs. The values to use depend on the card in use and the host. It might be needed to reduce the drive strength to prevent voltage peaks above the host's specification. Implement a 'dsr' devicetree property that allows to specify the value to set the DSR to. For non-dt setups the new members of mmc_host can be set by board code. This patch was initially authored by Sascha Hauer. It contains improvements authored by Markus Niebel and Uwe Kleine-König. Signed-off-by: Sascha Hauer Signed-off-by: Markus Niebel Signed-off-by: Uwe Kleine-König Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 8 ++++++++ drivers/mmc/core/mmc.c | 8 ++++++++ drivers/mmc/core/mmc_ops.c | 20 ++++++++++++++++++++ drivers/mmc/core/mmc_ops.h | 1 + drivers/mmc/core/sd.c | 8 ++++++++ 5 files changed, 45 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 95cceae96944..d572b2beb65a 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -452,6 +452,14 @@ int mmc_of_parse(struct mmc_host *host) if (of_find_property(np, "mmc-hs400-1_2v", &len)) host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR; + host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr); + if (host->dsr_req && (host->dsr & ~0xffff)) { + dev_err(host->parent, + "device tree specified broken value for DSR: 0x%x, ignoring\n", + host->dsr); + host->dsr_req = 0; + } + return 0; out: diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 1eda8dd8c867..31c43165f8bc 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -162,6 +162,7 @@ static int mmc_decode_csd(struct mmc_card *card) csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1); csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); @@ -1271,6 +1272,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, goto free_card; } + /* + * handling only for cards supporting DSR and hosts requesting + * DSR configuration + */ + if (card->csd.dsr_imp && host->dsr_req) + mmc_set_dsr(host); + /* * Select card, as all following commands rely on that. */ diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index f51b5ba3bbea..ba0275e90617 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -93,6 +93,26 @@ int mmc_deselect_cards(struct mmc_host *host) return _mmc_select_card(host, NULL); } +/* + * Write the value specified in the device tree or board code into the optional + * 16 bit Driver Stage Register. This can be used to tune raise/fall times and + * drive strength of the DAT and CMD outputs. The actual meaning of a given + * value is hardware dependant. + * The presence of the DSR register can be determined from the CSD register, + * bit 76. + */ +int mmc_set_dsr(struct mmc_host *host) +{ + struct mmc_command cmd = {0}; + + cmd.opcode = MMC_SET_DSR; + + cmd.arg = (host->dsr << 16) | 0xffff; + cmd.flags = MMC_RSP_NONE | MMC_CMD_AC; + + return mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); +} + int mmc_go_idle(struct mmc_host *host) { int err; diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 80ae9f4e0293..390dac665b2a 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -14,6 +14,7 @@ int mmc_select_card(struct mmc_card *card); int mmc_deselect_cards(struct mmc_host *host); +int mmc_set_dsr(struct mmc_host *host); int mmc_go_idle(struct mmc_host *host); int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); int mmc_all_send_cid(struct mmc_host *host, u32 *cid); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 0c44510bf717..25913889cbaa 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -127,6 +127,7 @@ static int mmc_decode_csd(struct mmc_card *card) csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1); csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); @@ -953,6 +954,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, mmc_decode_cid(card); } + /* + * handling only for cards supporting DSR and hosts requesting + * DSR configuration + */ + if (card->csd.dsr_imp && host->dsr_req) + mmc_set_dsr(host); + /* * Select card, as all following commands rely on that. */ -- cgit v1.2.3 From 9ed7ca89e0d287e054d3816b8c0c57514eb23726 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 14 Aug 2014 14:39:00 +0200 Subject: mmc: core: Use regulator_get_voltage() if OCR mask is empty. The operation conditions register (OCR) stores the voltage profile of the card, however the list of possible voltages is restricted by the voltage range supported by the supply used as VCC/VDD. So in mmc_vddrange_to_ocrmask() a OCR mask is obtained to filter the not supported voltages, from the value read in the host controller OCR register. For fixed regulators, regulator_list_voltage() returns the fixed output for the first selector but this doesn't happen for switch (FET) regulators that obtain their voltage from their parent supply. A call to regulator_get_voltage() is needed in this case so the regulator core can return the FET's parent supply voltage output. This change is consistent with the fact that for other fixed regulators (that are not FETs) the OCR mask is returned even when mmc_regulator_set_ocr() checks if the regulator is fixed before calling regulator_set_voltage(). Without this patch, the following warning is reported when a FET is used as a vmmc-supply: dwmmc_exynos 12220000.mmc: Failed getting OCR mask: -22 Signed-off-by: Javier Martinez Canillas Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 2eb7c82ba4a2..68f5f4ba57e9 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1221,15 +1221,14 @@ int mmc_regulator_get_ocrmask(struct regulator *supply) int result = 0; int count; int i; + int vdd_uV; + int vdd_mV; count = regulator_count_voltages(supply); if (count < 0) return count; for (i = 0; i < count; i++) { - int vdd_uV; - int vdd_mV; - vdd_uV = regulator_list_voltage(supply, i); if (vdd_uV <= 0) continue; @@ -1238,6 +1237,15 @@ int mmc_regulator_get_ocrmask(struct regulator *supply) result |= mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV); } + if (!result) { + vdd_uV = regulator_get_voltage(supply); + if (vdd_uV <= 0) + return vdd_uV; + + vdd_mV = vdd_uV / 1000; + result = mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV); + } + return result; } EXPORT_SYMBOL_GPL(mmc_regulator_get_ocrmask); -- cgit v1.2.3 From 215ba3995ae89866a58b8df41136811c665a22b9 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 12 Jun 2014 09:47:45 +0200 Subject: mmc: atmel-mci: add 0x600 IP version Signed-off-by: Nicolas Ferre Acked-by: Ludovic Desroches Signed-off-by: Ulf Hansson --- drivers/mmc/host/atmel-mci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index bb585d940901..fde6a6fc732c 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -2344,6 +2344,7 @@ static void __init atmci_get_cap(struct atmel_mci *host) /* keep only major version number */ switch (version & 0xf00) { + case 0x600: case 0x500: host->caps.has_odd_clk_div = 1; case 0x400: -- cgit v1.2.3 From 7501c4315faf46b088ed4de4d8695a91d7d63869 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 24 Oct 2013 15:58:45 +0200 Subject: mmc: tmio: Keep host active while SDIO IRQ is enabled The host must be kept active to be able to serve SDIO IRQs, thus let's prevent it from going inactive while SDIO IRQ is enabled. Signed-off-by: Ulf Hansson Acked-by: Geert Uytterhoeven --- drivers/mmc/host/tmio_mmc.h | 1 + drivers/mmc/host/tmio_mmc_pio.c | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 100ffe0b2faf..d6ceb1a5e56f 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -101,6 +101,7 @@ struct tmio_mmc_host { struct mutex ios_lock; /* protect set_ios() context */ bool native_hotplug; bool resuming; + bool sdio_irq_enabled; }; int tmio_mmc_host_probe(struct tmio_mmc_host **host, diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index faf0924e71cb..5c2e5a36258a 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -129,15 +129,22 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) { struct tmio_mmc_host *host = mmc_priv(mmc); - if (enable) { + if (enable && !host->sdio_irq_enabled) { + /* Keep device active while SDIO irq is enabled */ + pm_runtime_get_sync(mmc_dev(mmc)); + host->sdio_irq_enabled = true; + host->sdio_irq_mask = TMIO_SDIO_MASK_ALL & ~TMIO_SDIO_STAT_IOIRQ; sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001); sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); - } else { + } else if (!enable && host->sdio_irq_enabled) { host->sdio_irq_mask = TMIO_SDIO_MASK_ALL; sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000); + + host->sdio_irq_enabled = false; + pm_runtime_put(mmc_dev(mmc)); } } @@ -1074,8 +1081,12 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, _host->sdcard_irq_mask &= ~irq_mask; - if (pdata->flags & TMIO_MMC_SDIO_IRQ) - tmio_mmc_enable_sdio_irq(mmc, 0); + _host->sdio_irq_enabled = false; + if (pdata->flags & TMIO_MMC_SDIO_IRQ) { + _host->sdio_irq_mask = TMIO_SDIO_MASK_ALL; + sd_ctrl_write16(_host, CTL_SDIO_IRQ_MASK, _host->sdio_irq_mask); + sd_ctrl_write16(_host, CTL_TRANSACTION_CTL, 0x0000); + } spin_lock_init(&_host->lock); mutex_init(&_host->ios_lock); -- cgit v1.2.3 From 0369483efc1719f47249fa94329aa94155edf69d Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 24 Oct 2013 16:42:33 +0200 Subject: mmc: tmio: Keep host active while serving requests Use runtime PM to keep the host active during I/O operations and other requests which requires the tmio hardware to be powered. Additionally make use of the runtime PM autosuspend feature with a default timeout of 50 ms. Signed-off-by: Ulf Hansson Acked-by: Geert Uytterhoeven --- drivers/mmc/host/tmio_mmc_pio.c | 55 ++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 23 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 5c2e5a36258a..d63d292933b4 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -144,7 +144,8 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000); host->sdio_irq_enabled = false; - pm_runtime_put(mmc_dev(mmc)); + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); } } @@ -252,6 +253,9 @@ static void tmio_mmc_reset_work(struct work_struct *work) tmio_mmc_abort_dma(host); mmc_request_done(host->mmc, mrq); + + pm_runtime_mark_last_busy(mmc_dev(host->mmc)); + pm_runtime_put_autosuspend(mmc_dev(host->mmc)); } /* called with host->lock held, interrupts disabled */ @@ -281,6 +285,9 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host) tmio_mmc_abort_dma(host); mmc_request_done(host->mmc, mrq); + + pm_runtime_mark_last_busy(mmc_dev(host->mmc)); + pm_runtime_put_autosuspend(mmc_dev(host->mmc)); } static void tmio_mmc_done_work(struct work_struct *work) @@ -735,6 +742,8 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) spin_unlock_irqrestore(&host->lock, flags); + pm_runtime_get_sync(mmc_dev(mmc)); + if (mrq->data) { ret = tmio_mmc_start_data(host, mrq->data); if (ret) @@ -753,6 +762,9 @@ fail: host->mrq = NULL; mrq->cmd->error = ret; mmc_request_done(mmc, mrq); + + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); } static int tmio_mmc_clk_update(struct mmc_host *mmc) @@ -831,6 +843,8 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct device *dev = &host->pdev->dev; unsigned long flags; + pm_runtime_get_sync(mmc_dev(mmc)); + mutex_lock(&host->ios_lock); spin_lock_irqsave(&host->lock, flags); @@ -866,7 +880,6 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (ios->power_mode == MMC_POWER_ON && ios->clock) { if (host->power != TMIO_MMC_ON_RUN) { tmio_mmc_clk_update(mmc); - pm_runtime_get_sync(dev); if (host->resuming) { tmio_mmc_reset(host); host->resuming = false; @@ -896,7 +909,6 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (old_power == TMIO_MMC_ON_RUN) { tmio_mmc_clk_stop(host); - pm_runtime_put(dev); if (pdata->clk_disable) pdata->clk_disable(host->pdev); } @@ -923,6 +935,9 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->mrq = NULL; mutex_unlock(&host->ios_lock); + + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); } static int tmio_mmc_get_ro(struct mmc_host *mmc) @@ -933,8 +948,13 @@ static int tmio_mmc_get_ro(struct mmc_host *mmc) if (ret >= 0) return ret; - return !((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) || - (sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT)); + pm_runtime_get_sync(mmc_dev(mmc)); + ret = !((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) || + (sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT)); + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + + return ret; } static const struct mmc_host_ops tmio_mmc_ops = { @@ -1040,27 +1060,14 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc->slot.cd_irq >= 0); _host->power = TMIO_MMC_OFF_STOP; - pm_runtime_enable(&pdev->dev); - ret = pm_runtime_resume(&pdev->dev); - if (ret < 0) - goto pm_disable; - if (tmio_mmc_clk_update(mmc) < 0) { mmc->f_max = pdata->hclk; mmc->f_min = mmc->f_max / 512; } /* - * There are 4 different scenarios for the card detection: - * 1) an external gpio irq handles the cd (best for power savings) - * 2) internal sdhi irq handles the cd - * 3) a worker thread polls the sdhi - indicated by MMC_CAP_NEEDS_POLL - * 4) the medium is non-removable - indicated by MMC_CAP_NONREMOVABLE - * - * While we increment the runtime PM counter for all scenarios when - * the mmc core activates us by calling an appropriate set_ios(), we - * must additionally ensure that in case 2) the tmio mmc hardware stays - * powered on during runtime for the card detection to work. + * While using internal tmio hardware logic for card detection, we need + * to ensure it stays powered for it to work. */ if (_host->native_hotplug) pm_runtime_get_noresume(&pdev->dev); @@ -1098,6 +1105,11 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, /* See if we also get DMA */ tmio_mmc_request_dma(_host, pdata); + pm_runtime_set_active(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 50); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + ret = mmc_add_host(mmc); if (pdata->clk_disable) pdata->clk_disable(pdev); @@ -1120,9 +1132,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, return 0; -pm_disable: - pm_runtime_disable(&pdev->dev); - iounmap(_host->ctl); host_free: mmc_free_host(mmc); -- cgit v1.2.3 From 9ae4ed7d30f433873351d5ba14ae000d10e10430 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 24 Oct 2013 17:42:53 +0200 Subject: mmc: tmio: Extract bus_width modifications to a separate function Move code for bus_width modification, out of the ->set_ios() callback and into a separate function, to simplify code. Signed-off-by: Ulf Hansson Acked-by: Geert Uytterhoeven --- drivers/mmc/host/tmio_mmc_pio.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index d63d292933b4..8f5b5ccc7a5f 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -831,6 +831,19 @@ static void tmio_mmc_power_off(struct tmio_mmc_host *host) host->set_pwr(host->pdev, 0); } +static void tmio_mmc_set_bus_width(struct tmio_mmc_host *host, + unsigned char bus_width) +{ + switch (bus_width) { + case MMC_BUS_WIDTH_1: + sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0); + break; + case MMC_BUS_WIDTH_4: + sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0); + break; + } +} + /* Set MMC clock / power. * Note: This controller uses a simple divider scheme therefore it cannot * run a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as @@ -914,16 +927,8 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } } - if (host->power != TMIO_MMC_OFF_STOP) { - switch (ios->bus_width) { - case MMC_BUS_WIDTH_1: - sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0); - break; - case MMC_BUS_WIDTH_4: - sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0); - break; - } - } + if (host->power != TMIO_MMC_OFF_STOP) + tmio_mmc_set_bus_width(host, ios->bus_width); /* Let things settle. delay taken from winCE driver */ udelay(140); -- cgit v1.2.3 From 3b292bb0e99018c448e0f5a26c29c68113a1b7bf Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 24 Oct 2013 17:53:15 +0200 Subject: mmc: tmio: Restructure ->set_ios() and adapt ->probe() to it An internal power state machine were beeing used to keep ->probe() and ->set_ios() in sync. Especially for handling specific scenarios while using CONFIG_MMC_CLKGATE. Moreover dependency to CONFIG_MMC_CLKGATE existed to handle runtime PM properly, which we moves away from here. By removing the state machine and instead make ->set_ios() rely on the information provided through the function's in-parameters, the code becomes significantly simplier. Additonally as a part of this rework we prepares for making the runtime PM callbacks responsible of clock gating. Signed-off-by: Ulf Hansson Acked-by: Geert Uytterhoeven --- drivers/mmc/host/tmio_mmc.h | 20 -------------- drivers/mmc/host/tmio_mmc_pio.c | 61 ++++++++++------------------------------- 2 files changed, 14 insertions(+), 67 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index d6ceb1a5e56f..f837723c30b9 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -40,22 +40,6 @@ struct tmio_mmc_data; -/* - * We differentiate between the following 3 power states: - * 1. card slot powered off, controller stopped. This is used, when either there - * is no card in the slot, or the card really has to be powered down. - * 2. card slot powered on, controller stopped. This is used, when a card is in - * the slot, but no activity is currently taking place. This is a power- - * saving mode with card-state preserved. This state can be entered, e.g. - * when MMC clock-gating is used. - * 3. card slot powered on, controller running. This is the actual active state. - */ -enum tmio_mmc_power { - TMIO_MMC_OFF_STOP, /* card power off, controller stopped */ - TMIO_MMC_ON_STOP, /* card power on, controller stopped */ - TMIO_MMC_ON_RUN, /* card power on, controller running */ -}; - struct tmio_mmc_host { void __iomem *ctl; struct mmc_command *cmd; @@ -63,9 +47,6 @@ struct tmio_mmc_host { struct mmc_data *data; struct mmc_host *mmc; - /* Controller and card power state */ - enum tmio_mmc_power power; - /* Callbacks for clock / power control */ void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); @@ -100,7 +81,6 @@ struct tmio_mmc_host { unsigned long last_req_ts; struct mutex ios_lock; /* protect set_ios() context */ bool native_hotplug; - bool resuming; bool sdio_irq_enabled; }; diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 8f5b5ccc7a5f..e573a15b7ee1 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -884,51 +884,23 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) spin_unlock_irqrestore(&host->lock, flags); - /* - * host->power toggles between false and true in both cases - either - * or not the controller can be runtime-suspended during inactivity. - * But if the controller has to be kept on, the runtime-pm usage_count - * is kept positive, so no suspending actually takes place. - */ - if (ios->power_mode == MMC_POWER_ON && ios->clock) { - if (host->power != TMIO_MMC_ON_RUN) { - tmio_mmc_clk_update(mmc); - if (host->resuming) { - tmio_mmc_reset(host); - host->resuming = false; - } - } - if (host->power == TMIO_MMC_OFF_STOP) - tmio_mmc_reset(host); + switch (ios->power_mode) { + case MMC_POWER_OFF: + tmio_mmc_power_off(host); + tmio_mmc_clk_stop(host); + break; + case MMC_POWER_UP: tmio_mmc_set_clock(host, ios->clock); - if (host->power == TMIO_MMC_OFF_STOP) - /* power up SD card and the bus */ - tmio_mmc_power_on(host, ios->vdd); - host->power = TMIO_MMC_ON_RUN; - /* start bus clock */ + tmio_mmc_power_on(host, ios->vdd); tmio_mmc_clk_start(host); - } else if (ios->power_mode != MMC_POWER_UP) { - struct tmio_mmc_data *pdata = host->pdata; - unsigned int old_power = host->power; - - if (old_power != TMIO_MMC_OFF_STOP) { - if (ios->power_mode == MMC_POWER_OFF) { - tmio_mmc_power_off(host); - host->power = TMIO_MMC_OFF_STOP; - } else { - host->power = TMIO_MMC_ON_STOP; - } - } - - if (old_power == TMIO_MMC_ON_RUN) { - tmio_mmc_clk_stop(host); - if (pdata->clk_disable) - pdata->clk_disable(host->pdev); - } - } - - if (host->power != TMIO_MMC_OFF_STOP) tmio_mmc_set_bus_width(host, ios->bus_width); + break; + case MMC_POWER_ON: + tmio_mmc_set_clock(host, ios->clock); + tmio_mmc_clk_start(host); + tmio_mmc_set_bus_width(host, ios->bus_width); + break; + } /* Let things settle. delay taken from winCE driver */ udelay(140); @@ -1064,7 +1036,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc->caps & MMC_CAP_NONREMOVABLE || mmc->slot.cd_irq >= 0); - _host->power = TMIO_MMC_OFF_STOP; if (tmio_mmc_clk_update(mmc) < 0) { mmc->f_max = pdata->hclk; mmc->f_min = mmc->f_max / 512; @@ -1116,8 +1087,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, pm_runtime_enable(&pdev->dev); ret = mmc_add_host(mmc); - if (pdata->clk_disable) - pdata->clk_disable(pdev); if (ret < 0) { tmio_mmc_host_remove(_host); return ret; @@ -1185,8 +1154,6 @@ int tmio_mmc_host_resume(struct device *dev) tmio_mmc_enable_dma(host, true); - /* The MMC core will perform the complete set up */ - host->resuming = true; return 0; } EXPORT_SYMBOL(tmio_mmc_host_resume); -- cgit v1.2.3 From ae12d25053fa20d67b16e568917b956006d5dc01 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 30 Oct 2013 00:16:17 +0100 Subject: mmc: tmio: Handle clock gating from runtime PM functions Add clock gating control as a part of the tmio library functions for runtime PM. Signed-off-by: Ulf Hansson Acked-by: Geert Uytterhoeven --- drivers/mmc/host/tmio_mmc.h | 3 ++- drivers/mmc/host/tmio_mmc_pio.c | 28 ++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index f837723c30b9..f1ee3e4355de 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -73,9 +73,10 @@ struct tmio_mmc_host { struct delayed_work delayed_reset_work; struct work_struct done; - /* Cache IRQ mask */ + /* Cache */ u32 sdcard_irq_mask; u32 sdio_irq_mask; + unsigned int clk_cache; spinlock_t lock; /* protect host private data */ unsigned long last_req_ts; diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index e573a15b7ee1..23451772c0fa 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -149,7 +149,8 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) } } -static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock) +static void tmio_mmc_set_clock(struct tmio_mmc_host *host, + unsigned int new_clock) { u32 clk = 0, clock; @@ -767,9 +768,9 @@ fail: pm_runtime_put_autosuspend(mmc_dev(mmc)); } -static int tmio_mmc_clk_update(struct mmc_host *mmc) +static int tmio_mmc_clk_update(struct tmio_mmc_host *host) { - struct tmio_mmc_host *host = mmc_priv(mmc); + struct mmc_host *mmc = host->mmc; struct tmio_mmc_data *pdata = host->pdata; int ret; @@ -911,6 +912,8 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ios->clock, ios->power_mode); host->mrq = NULL; + host->clk_cache = ios->clock; + mutex_unlock(&host->ios_lock); pm_runtime_mark_last_busy(mmc_dev(mmc)); @@ -1036,7 +1039,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc->caps & MMC_CAP_NONREMOVABLE || mmc->slot.cd_irq >= 0); - if (tmio_mmc_clk_update(mmc) < 0) { + if (tmio_mmc_clk_update(_host) < 0) { mmc->f_max = pdata->hclk; mmc->f_min = mmc->f_max / 512; } @@ -1162,6 +1165,15 @@ EXPORT_SYMBOL(tmio_mmc_host_resume); #ifdef CONFIG_PM_RUNTIME int tmio_mmc_host_runtime_suspend(struct device *dev) { + struct mmc_host *mmc = dev_get_drvdata(dev); + struct tmio_mmc_host *host = mmc_priv(mmc); + + if (host->clk_cache) + tmio_mmc_clk_stop(host); + + if (host->pdata->clk_disable) + host->pdata->clk_disable(host->pdev); + return 0; } EXPORT_SYMBOL(tmio_mmc_host_runtime_suspend); @@ -1171,6 +1183,14 @@ int tmio_mmc_host_runtime_resume(struct device *dev) struct mmc_host *mmc = dev_get_drvdata(dev); struct tmio_mmc_host *host = mmc_priv(mmc); + tmio_mmc_reset(host); + tmio_mmc_clk_update(host); + + if (host->clk_cache) { + tmio_mmc_set_clock(host, host->clk_cache); + tmio_mmc_clk_start(host); + } + tmio_mmc_enable_dma(host, true); return 0; -- cgit v1.2.3 From 20e955c3c746f434a2f97ab08ac6bd71f63c6afa Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 25 Aug 2014 11:55:57 +0200 Subject: mmc: tmio: Mask all IRQs when inactive To make sure we don't receive any spurious IRQs while we are inactive, mask the IRQs from within the ->runtime_suspend() callback. Signed-off-by: Ulf Hansson Acked-by: Geert Uytterhoeven --- drivers/mmc/host/tmio_mmc_pio.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 23451772c0fa..d52280d671f0 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1168,6 +1168,8 @@ int tmio_mmc_host_runtime_suspend(struct device *dev) struct mmc_host *mmc = dev_get_drvdata(dev); struct tmio_mmc_host *host = mmc_priv(mmc); + tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); + if (host->clk_cache) tmio_mmc_clk_stop(host); -- cgit v1.2.3 From 9ade7dbf20e8fc73d9784b772e320239de2acf08 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 25 Aug 2014 12:03:20 +0200 Subject: mmc: tmio: Make runtime PM callbacks available for CONFIG_PM To give the option for tmio hosts to use the runtime PM callbacks for CONFIG_PM_SLEEP as well as CONFIG_PM_RUNTIME, move them to CONFIG_PM. Signed-off-by: Ulf Hansson Acked-by: Geert Uytterhoeven --- drivers/mmc/host/tmio_mmc.h | 2 +- drivers/mmc/host/tmio_mmc_pio.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index f1ee3e4355de..2ad52d6b3975 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -149,7 +149,7 @@ int tmio_mmc_host_suspend(struct device *dev); int tmio_mmc_host_resume(struct device *dev); #endif -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM int tmio_mmc_host_runtime_suspend(struct device *dev); int tmio_mmc_host_runtime_resume(struct device *dev); #endif diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index d52280d671f0..494ecc5e73d0 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1162,7 +1162,7 @@ int tmio_mmc_host_resume(struct device *dev) EXPORT_SYMBOL(tmio_mmc_host_resume); #endif -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM int tmio_mmc_host_runtime_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); -- cgit v1.2.3 From f74f1e38206df93b9e11d5a8b84743e61c0768a6 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 25 Aug 2014 12:06:55 +0200 Subject: mmc: sdhi: Make runtime PM callbacks available for CONFIG_PM To be able to simplify system PM, let's re-use the runtime PM callbacks by converting to the SET_PM_RUNTIME_PM_OPS macro. Signed-off-by: Ulf Hansson Acked-by: Geert Uytterhoeven --- drivers/mmc/host/sh_mobile_sdhi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index be5e25ddab53..4cd0f09bc690 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -333,7 +333,7 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev) static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_host_suspend, tmio_mmc_host_resume) - SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, + SET_PM_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, tmio_mmc_host_runtime_resume, NULL) }; -- cgit v1.2.3 From 78f76df60926f7853df9ee2b707ea9254129775c Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 25 Aug 2014 12:12:02 +0200 Subject: mmc: tmio_mmc: Enable runtime PM support To take advantage of the clock gating support, use the runtime PM callbacks provided by the tmio core. Additionally, we make use of the SET_PM_RUNTIME_PM_OPS, which is a preparation needed to simplify system PM. Signed-off-by: Ulf Hansson Acked-by: Geert Uytterhoeven --- drivers/mmc/host/tmio_mmc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index cfad844730d8..58f50ef8f428 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -135,6 +135,9 @@ static int tmio_mmc_remove(struct platform_device *pdev) static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_suspend, tmio_mmc_resume) + SET_PM_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, + tmio_mmc_host_runtime_resume, + NULL) }; static struct platform_driver tmio_mmc_driver = { -- cgit v1.2.3 From 753a688c9f28ad3198611bbbd39f3b226c35ab98 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 25 Aug 2014 12:18:26 +0200 Subject: mmc: sdhi: Fixup system PM suspend lock-up At system PM suspend, the tmio core accessed the internal registers of the controller without first moving the device into active state. This caused a lock-up in system PM suspend phase. The reason for the register access were masking of IRQs. Since that is managed via the runtime PM suspend path, let's just re-use that path for system PM suspend. In other words force the device into runtime PM suspend state at system PM suspend and restore it to active state at system PM resume. Reported-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson Tested-by: Geert Uytterhoeven --- drivers/mmc/host/sh_mobile_sdhi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 4cd0f09bc690..2dafe28c8df9 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -332,7 +332,8 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev) } static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_host_suspend, tmio_mmc_host_resume) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) SET_PM_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, tmio_mmc_host_runtime_resume, NULL) -- cgit v1.2.3 From 70a15e1a3c58ee841415c84170b80a3859027686 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 25 Aug 2014 12:28:20 +0200 Subject: mmc: tmio_mmc: Fixup system PM suspend lock-up At system PM suspend, the tmio core accessed the internal registers of the controller without first moving the device into active state. This caused a lock-up in system PM suspend phase. The reason for the register access were masking of IRQs. Since that is managed via the runtime PM suspend path, let's just re-use that path for system PM suspend. In other words force the device into runtime PM suspend state at system PM suspend and restore it to active state at system PM resume. Reported-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson Acked-by: Geert Uytterhoeven --- drivers/mmc/host/tmio_mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 58f50ef8f428..659028ddb8b1 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -30,7 +30,7 @@ static int tmio_mmc_suspend(struct device *dev) const struct mfd_cell *cell = mfd_get_cell(pdev); int ret; - ret = tmio_mmc_host_suspend(dev); + ret = pm_runtime_force_suspend(dev); /* Tell MFD core it can disable us now.*/ if (!ret && cell->disable) @@ -50,7 +50,7 @@ static int tmio_mmc_resume(struct device *dev) ret = cell->resume(pdev); if (!ret) - ret = tmio_mmc_host_resume(dev); + ret = pm_runtime_force_resume(dev); return ret; } -- cgit v1.2.3 From 341eb6a4e7beea5730faf55610ef60584bcfffcf Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 25 Aug 2014 13:22:51 +0200 Subject: mmc: tmio: Remove library functions for system PM These library functions aren't used and nor needed, let's remove them. Signed-off-by: Ulf Hansson Acked-by: Geert Uytterhoeven --- drivers/mmc/host/tmio_mmc.h | 5 ----- drivers/mmc/host/tmio_mmc_pio.c | 23 ----------------------- 2 files changed, 28 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 2ad52d6b3975..a34ecbe1c1ad 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -144,11 +144,6 @@ static inline void tmio_mmc_abort_dma(struct tmio_mmc_host *host) } #endif -#ifdef CONFIG_PM_SLEEP -int tmio_mmc_host_suspend(struct device *dev); -int tmio_mmc_host_resume(struct device *dev); -#endif - #ifdef CONFIG_PM int tmio_mmc_host_runtime_suspend(struct device *dev); int tmio_mmc_host_runtime_resume(struct device *dev); diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 494ecc5e73d0..a99320818f19 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1139,29 +1139,6 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) } EXPORT_SYMBOL(tmio_mmc_host_remove); -#ifdef CONFIG_PM_SLEEP -int tmio_mmc_host_suspend(struct device *dev) -{ - struct mmc_host *mmc = dev_get_drvdata(dev); - struct tmio_mmc_host *host = mmc_priv(mmc); - - tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); - return 0; -} -EXPORT_SYMBOL(tmio_mmc_host_suspend); - -int tmio_mmc_host_resume(struct device *dev) -{ - struct mmc_host *mmc = dev_get_drvdata(dev); - struct tmio_mmc_host *host = mmc_priv(mmc); - - tmio_mmc_enable_dma(host, true); - - return 0; -} -EXPORT_SYMBOL(tmio_mmc_host_resume); -#endif - #ifdef CONFIG_PM int tmio_mmc_host_runtime_suspend(struct device *dev) { -- cgit v1.2.3 From 384b2cbd56a02efb16358ed7c0c039e4afca5ed0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 24 Aug 2014 19:58:48 -0700 Subject: mmc: tmio: care about DMA tx/rx addr offset Basically, SD_BUF0 Tx/Rx addresses are same in normal TMIO controller, but, it is different on Renesas R-Car SDHI controller if it uses DMAC (Rx address needs to add 0x2000 to Tx address) This patch adds new .dma_rx_offset and cares it Tested-by: Nguyen Xuan Nui Tested-by: Hiep Cao Minh Acked-by: Laurent Pinchart Acked-by: Ben Dooks Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 3 +++ drivers/mmc/host/tmio_mmc_dma.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 2dafe28c8df9..f0818cd0242c 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -39,6 +39,7 @@ struct sh_mobile_sdhi_of_data { unsigned long tmio_flags; unsigned long capabilities; unsigned long capabilities2; + dma_addr_t dma_rx_offset; }; static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = { @@ -56,6 +57,7 @@ static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, .capabilities2 = MMC_CAP2_NO_MULTI_READ, + .dma_rx_offset = 0x2000, }; static const struct of_device_id sh_mobile_sdhi_of_match[] = { @@ -228,6 +230,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->flags |= of_data->tmio_flags; mmc_data->capabilities |= of_data->capabilities; mmc_data->capabilities2 |= of_data->capabilities2; + dma_priv->dma_rx_offset = of_data->dma_rx_offset; } /* SD control register space size is 0x100, 0x200 for bus_shift=1 */ diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index eb8f1d5c34b1..bd646b041085 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -312,7 +312,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat if (pdata->dma->chan_priv_rx) cfg.slave_id = pdata->dma->slave_id_rx; cfg.direction = DMA_DEV_TO_MEM; - cfg.src_addr = cfg.dst_addr; + cfg.src_addr = cfg.dst_addr + pdata->dma->dma_rx_offset; cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; cfg.dst_addr = 0; ret = dmaengine_slave_config(host->chan_rx, &cfg); -- cgit v1.2.3 From f83bfa751babb521924f6d8c93055215d2eb33cc Mon Sep 17 00:00:00 2001 From: Shinobu Uehara Date: Sun, 24 Aug 2014 19:59:22 -0700 Subject: mmc: tmio: clear error IRQ status Next card access will be always error if it didn't clear error status Tested-by: Nguyen Xuan Nui Tested-by: Hiep Cao Minh Signed-off-by: Shinobu Uehara Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_pio.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index a99320818f19..3d0ad737abea 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -572,6 +572,9 @@ static void tmio_mmc_card_irq_status(struct tmio_mmc_host *host, pr_debug_status(*status); pr_debug_status(*ireg); + + /* Clear the status except the interrupt status */ + sd_ctrl_write32(host, CTL_STATUS, TMIO_MASK_IRQ); } static bool __tmio_mmc_card_detect_irq(struct tmio_mmc_host *host, -- cgit v1.2.3 From b8d11962c2d83c984d5afd091e5b725ad2fd5607 Mon Sep 17 00:00:00 2001 From: Shinobu Uehara Date: Sun, 24 Aug 2014 20:00:25 -0700 Subject: mmc: tmio: control multiple block transfer mode Renesas SDHI has "Multiple Block Transfer Mode" settings on SD_CMD register which controls CMD12 automatically. This patch cares it, because CMD12 is not needed when CMD53 (= SD_IO_RW_EXTENDED) [Kuninori Morimoto: tidyuped for upstreaming enabled this flags for all SH-Mobile/R-Car] Tested-by: Nguyen Xuan Nui Tested-by: Hiep Cao Minh Signed-off-by: Shinobu Uehara Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 5 +++++ drivers/mmc/host/tmio_mmc_pio.c | 10 ++++++++++ 2 files changed, 15 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index f0818cd0242c..4727586b063e 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -225,6 +225,11 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) */ mmc_data->flags |= TMIO_MMC_SDIO_IRQ; + /* + * All SDHI have CMD12 controll bit + */ + mmc_data->flags |= TMIO_MMC_HAVE_CMD12_CTRL; + if (of_id && of_id->data) { const struct sh_mobile_sdhi_of_data *of_data = of_id->data; mmc_data->flags |= of_data->tmio_flags; diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 3d0ad737abea..c2804827be9f 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -310,6 +311,7 @@ static void tmio_mmc_done_work(struct work_struct *work) #define TRANSFER_READ 0x1000 #define TRANSFER_MULTI 0x2000 #define SECURITY_CMD 0x4000 +#define NO_CMD12_ISSUE 0x4000 /* TMIO_MMC_HAVE_CMD12_CTRL */ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd) { @@ -346,6 +348,14 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command if (data->blocks > 1) { sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x100); c |= TRANSFER_MULTI; + + /* + * Disable auto CMD12 at IO_RW_EXTENDED when + * multiple block transfer + */ + if ((host->pdata->flags & TMIO_MMC_HAVE_CMD12_CTRL) && + (cmd->opcode == SD_IO_RW_EXTENDED)) + c |= NO_CMD12_ISSUE; } if (data->flags & MMC_DATA_READ) c |= TRANSFER_READ; -- cgit v1.2.3 From 6b98757e53cb0e93b02db4067c14afcb32c90615 Mon Sep 17 00:00:00 2001 From: Shinobu Uehara Date: Sun, 24 Aug 2014 20:00:52 -0700 Subject: mmc: tmio: add TMIO_MMC_SDIO_STATUS_QUIRK Renesas R-Car SDHI should set reserved bits on CTL_SDIO_STATUS register when writing. This patch adds new TMIO_MMC_SDIO_STATUS_QUIRK flags for this purpose [Kuninori Morimoto: tidyuped for upstreaming enabled this flags for all SH-Mobile/R-Car] Tested-by: Nguyen Xuan Nui Tested-by: Hiep Cao Minh Signed-off-by: Shinobu Uehara Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 5 +++++ drivers/mmc/host/tmio_mmc_pio.c | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 4727586b063e..ebcfc659b799 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -230,6 +230,11 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) */ mmc_data->flags |= TMIO_MMC_HAVE_CMD12_CTRL; + /* + * All SDHI need SDIO_INFO1 reserved bit + */ + mmc_data->flags |= TMIO_MMC_SDIO_STATUS_QUIRK; + if (of_id && of_id->data) { const struct sh_mobile_sdhi_of_data *of_data = of_id->data; mmc_data->flags |= of_data->tmio_flags; diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index c2804827be9f..c5ffa92ccb18 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -665,6 +665,7 @@ irqreturn_t tmio_mmc_sdio_irq(int irq, void *devid) struct mmc_host *mmc = host->mmc; struct tmio_mmc_data *pdata = host->pdata; unsigned int ireg, status; + unsigned int sdio_status; if (!(pdata->flags & TMIO_MMC_SDIO_IRQ)) return IRQ_HANDLED; @@ -672,7 +673,11 @@ irqreturn_t tmio_mmc_sdio_irq(int irq, void *devid) status = sd_ctrl_read16(host, CTL_SDIO_STATUS); ireg = status & TMIO_SDIO_MASK_ALL & ~host->sdcard_irq_mask; - sd_ctrl_write16(host, CTL_SDIO_STATUS, status & ~TMIO_SDIO_MASK_ALL); + sdio_status = status & ~TMIO_SDIO_MASK_ALL; + if (pdata->flags & TMIO_MMC_SDIO_STATUS_QUIRK) + sdio_status |= 6; + + sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status); if (mmc->caps & MMC_CAP_SDIO_IRQ && ireg & TMIO_SDIO_STAT_IOIRQ) mmc_signal_sdio_irq(mmc); -- cgit v1.2.3 From 81e888daab027e05130b0459e5d8e0b42dac4323 Mon Sep 17 00:00:00 2001 From: Shinobu Uehara Date: Sun, 24 Aug 2014 20:01:32 -0700 Subject: mmc: tmio: check ILL_FUNC instead of CBSY Some controllers need to check SD bus status when writing data. Then, it checks ILL_FUNC bit on SD_INFO2 register, and this method is controlled via TMIO_MMC_HAS_IDLE_WAIT flags. Same method is required on tmio_mmc_data_irq() which will be called after writing data. Current driver is checking CBSY bit for this purpose, but, some controllers doesn't have CBSY bit. This patch checks ILL_FUNC bit instead of CBSY bit if it has TMIO_MMC_HAS_IDLE_WAIT flags [Kuninori Morimoto: tidyuped for upstreaming] Tested-by: Nguyen Xuan Nui Tested-by: Hiep Cao Minh Signed-off-by: Shinobu Uehara Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_pio.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index c5ffa92ccb18..10b3144930e2 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -490,6 +490,9 @@ static void tmio_mmc_data_irq(struct tmio_mmc_host *host) goto out; if (host->chan_tx && (data->flags & MMC_DATA_WRITE) && !host->force_pio) { + u32 status = sd_ctrl_read32(host, CTL_STATUS); + bool done = false; + /* * Has all data been written out yet? Testing on SuperH showed, * that in most cases the first interrupt comes already with the @@ -498,7 +501,15 @@ static void tmio_mmc_data_irq(struct tmio_mmc_host *host) * DATAEND interrupt with the BUSY bit set, in this cases * waiting for one more interrupt fixes the problem. */ - if (!(sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_CMD_BUSY)) { + if (host->pdata->flags & TMIO_MMC_HAS_IDLE_WAIT) { + if (status & TMIO_STAT_ILL_FUNC) + done = true; + } else { + if (!(status & TMIO_STAT_CMD_BUSY)) + done = true; + } + + if (done) { tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_DATAEND); tasklet_schedule(&host->dma_complete); } -- cgit v1.2.3 From e85dd04ea8c8d32ba8eae278959d28df34338e9d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 24 Aug 2014 20:01:54 -0700 Subject: mmc: tmio: remove Renesas specific #ifdef This patch adds new TMIO_MMC_HAVE_CTL_DMA_REG flag, and remove Renesas specific #ifdef from tmio driver Tested-by: Nguyen Xuan Nui Tested-by: Hiep Cao Minh Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 5 +++++ drivers/mmc/host/tmio_mmc_dma.c | 6 ++---- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index ebcfc659b799..a077da02bb8e 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -235,6 +235,11 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) */ mmc_data->flags |= TMIO_MMC_SDIO_STATUS_QUIRK; + /* + * All SDHI have DMA control register + */ + mmc_data->flags |= TMIO_MMC_HAVE_CTL_DMA_REG; + if (of_id && of_id->data) { const struct sh_mobile_sdhi_of_data *of_data = of_id->data; mmc_data->flags |= of_data->tmio_flags; diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index bd646b041085..7d077388b9eb 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -28,10 +28,8 @@ void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable) if (!host->chan_tx || !host->chan_rx) return; -#if defined(CONFIG_SUPERH) || defined(CONFIG_ARCH_SHMOBILE) - /* Switch DMA mode on or off - SuperH specific? */ - sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0); -#endif + if (host->pdata->flags & TMIO_MMC_HAVE_CTL_DMA_REG) + sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0); } void tmio_mmc_abort_dma(struct tmio_mmc_host *host) -- cgit v1.2.3 From b30d6958a014812158d9729a3e0b6d448f6e8968 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 24 Aug 2014 20:02:16 -0700 Subject: mmc: tmio: remove SCLKEN bit setting from tmio_mmc_set_clock() TMIO clock is set via tmio_mmc_set_clock() -> tmio_mmc_clk_start(), and SCLKEN bit will be set on tmio_mmc_clk_start(). It is not needed on tmio_mmc_set_clock() function. The required clock setting will not be able to set in some clocks without this patch. Tested-by: Nguyen Xuan Nui Tested-by: Hiep Cao Minh Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_pio.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 10b3144930e2..7cfe939992de 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -159,7 +159,6 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, for (clock = host->mmc->f_min, clk = 0x80000080; new_clock >= (clock<<1); clk >>= 1) clock <<= 1; - clk |= 0x100; } if (host->set_clk_div) -- cgit v1.2.3 From da29fe2bf573f0ae56fdc2e790387cb73fc8c6f8 Mon Sep 17 00:00:00 2001 From: Shinobu Uehara Date: Sun, 24 Aug 2014 20:03:00 -0700 Subject: mmc: tmio: add actual clock support as option Some controller is supporting actual clock on SD_CLK_CTRL :: DIV[7:0]. Renesas SH-Mobile SDHI doesn't support, but, Renesas R-Car SDHI supports it. This patch adds new TMIO_MMC_CLK_ACTUAL flag for it. [Kuninori Morimoto: tidyuped for upstreaming] Tested-by: Nguyen Xuan Nui Tested-by: Hiep Cao Minh Signed-off-by: Shinobu Uehara Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 6 ++++-- drivers/mmc/host/tmio_mmc_pio.c | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index a077da02bb8e..d5d493719491 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -49,12 +49,14 @@ static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = { }; static const struct sh_mobile_sdhi_of_data of_rcar_gen1_compatible = { - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE, + .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE | + TMIO_MMC_CLK_ACTUAL, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, }; static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = { - .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE, + .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE | + TMIO_MMC_CLK_ACTUAL, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, .capabilities2 = MMC_CAP2_NO_MULTI_READ, .dma_rx_offset = 0x2000, diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 7cfe939992de..ba454131f9a8 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -159,6 +159,11 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, for (clock = host->mmc->f_min, clk = 0x80000080; new_clock >= (clock<<1); clk >>= 1) clock <<= 1; + + /* 1/1 clock is option */ + if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) && + ((clk >> 22) & 0x1)) + clk |= 0xff; } if (host->set_clk_div) -- cgit v1.2.3 From 4cdc2ec1da322776215c4d6bca0717a7a103a4dd Mon Sep 17 00:00:00 2001 From: addy ke Date: Tue, 19 Aug 2014 12:36:14 +0800 Subject: mmc: dw_mmc: move rockchip related code to a separate file To support HS200 and UHS-1, we need add a big hunk of code, as shown in the following patches. So a separate file for rockchip SOCs is suitable. Signed-off-by: Addy Ke Acked-by: Jaehoon Chung Tested-by: Doug Anderson Reviewed-by: Doug Anderson Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 9 +++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/dw_mmc-pltfm.c | 57 ---------------- drivers/mmc/host/dw_mmc-rockchip.c | 136 +++++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 57 deletions(-) create mode 100644 drivers/mmc/host/dw_mmc-rockchip.c (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index a43295c49a44..13860656104b 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -638,6 +638,15 @@ config MMC_DW_PCI If unsure, say N. +config MMC_DW_ROCKCHIP + tristate "Rockchip specific extensions for Synopsys DW Memory Card Interface" + depends on MMC_DW && ARCH_ROCKCHIP + select MMC_DW_PLTFM + help + This selects support for Rockchip SoC specific extensions to the + Synopsys DesignWare Memory Card Interface driver. Select this option + for platforms based on RK3066, RK3188 and RK3288 SoC's. + config MMC_SH_MMCIF tristate "SuperH Internal MMCIF support" depends on MMC_BLOCK && HAS_DMA diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 2d223463e6c5..b09ecfb88269 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o +obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_VUB300) += vub300.o diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 1cd02828e5a3..8b6572162ed9 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -26,64 +26,11 @@ #include "dw_mmc.h" #include "dw_mmc-pltfm.h" -#define RK3288_CLKGEN_DIV 2 - static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr) { *cmdr |= SDMMC_CMD_USE_HOLD_REG; } -static int dw_mci_rk3288_setup_clock(struct dw_mci *host) -{ - host->bus_hz /= RK3288_CLKGEN_DIV; - - return 0; -} - -static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) -{ - int ret; - unsigned int cclkin; - u32 bus_hz; - - /* - * cclkin: source clock of mmc controller. - * bus_hz: card interface clock generated by CLKGEN. - * bus_hz = cclkin / RK3288_CLKGEN_DIV; - * ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div)) - * - * Note: div can only be 0 or 1 - * if DDR50 8bit mode(only emmc work in 8bit mode), - * div must be set 1 - */ - if ((ios->bus_width == MMC_BUS_WIDTH_8) && - (ios->timing == MMC_TIMING_MMC_DDR52)) - cclkin = 2 * ios->clock * RK3288_CLKGEN_DIV; - else - cclkin = ios->clock * RK3288_CLKGEN_DIV; - - ret = clk_set_rate(host->ciu_clk, cclkin); - if (ret) - dev_warn(host->dev, "failed to set rate %uHz\n", ios->clock); - - bus_hz = clk_get_rate(host->ciu_clk) / RK3288_CLKGEN_DIV; - if (bus_hz != host->bus_hz) { - host->bus_hz = bus_hz; - /* force dw_mci_setup_bus() */ - host->current_speed = 0; - } -} - -static const struct dw_mci_drv_data rk2928_drv_data = { - .prepare_command = dw_mci_pltfm_prepare_command, -}; - -static const struct dw_mci_drv_data rk3288_drv_data = { - .prepare_command = dw_mci_pltfm_prepare_command, - .set_ios = dw_mci_rk3288_set_ios, - .setup_clock = dw_mci_rk3288_setup_clock, -}; - static const struct dw_mci_drv_data socfpga_drv_data = { .prepare_command = dw_mci_pltfm_prepare_command, }; @@ -141,10 +88,6 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); static const struct of_device_id dw_mci_pltfm_match[] = { { .compatible = "snps,dw-mshc", }, - { .compatible = "rockchip,rk2928-dw-mshc", - .data = &rk2928_drv_data }, - { .compatible = "rockchip,rk3288-dw-mshc", - .data = &rk3288_drv_data }, { .compatible = "altr,socfpga-dw-mshc", .data = &socfpga_drv_data }, {}, diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c new file mode 100644 index 000000000000..f0c2cb1a210d --- /dev/null +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include "dw_mmc.h" +#include "dw_mmc-pltfm.h" + +#define RK3288_CLKGEN_DIV 2 + +static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr) +{ + *cmdr |= SDMMC_CMD_USE_HOLD_REG; +} + +static int dw_mci_rk3288_setup_clock(struct dw_mci *host) +{ + host->bus_hz /= RK3288_CLKGEN_DIV; + + return 0; +} + +static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + int ret; + unsigned int cclkin; + u32 bus_hz; + + /* + * cclkin: source clock of mmc controller + * bus_hz: card interface clock generated by CLKGEN + * bus_hz = cclkin / RK3288_CLKGEN_DIV + * ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div)) + * + * Note: div can only be 0 or 1 + * if DDR50 8bit mode(only emmc work in 8bit mode), + * div must be set 1 + */ + if (ios->bus_width == MMC_BUS_WIDTH_8 && + ios->timing == MMC_TIMING_MMC_DDR52) + cclkin = 2 * ios->clock * RK3288_CLKGEN_DIV; + else + cclkin = ios->clock * RK3288_CLKGEN_DIV; + + ret = clk_set_rate(host->ciu_clk, cclkin); + if (ret) + dev_warn(host->dev, "failed to set rate %uHz\n", ios->clock); + + bus_hz = clk_get_rate(host->ciu_clk) / RK3288_CLKGEN_DIV; + if (bus_hz != host->bus_hz) { + host->bus_hz = bus_hz; + /* force dw_mci_setup_bus() */ + host->current_speed = 0; + } +} + +static const struct dw_mci_drv_data rk2928_drv_data = { + .prepare_command = dw_mci_rockchip_prepare_command, +}; + +static const struct dw_mci_drv_data rk3288_drv_data = { + .prepare_command = dw_mci_rockchip_prepare_command, + .set_ios = dw_mci_rk3288_set_ios, + .setup_clock = dw_mci_rk3288_setup_clock, +}; + +static const struct of_device_id dw_mci_rockchip_match[] = { + { .compatible = "rockchip,rk2928-dw-mshc", + .data = &rk2928_drv_data }, + { .compatible = "rockchip,rk3288-dw-mshc", + .data = &rk3288_drv_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, dw_mci_rockchip_match); + +static int dw_mci_rockchip_probe(struct platform_device *pdev) +{ + const struct dw_mci_drv_data *drv_data; + const struct of_device_id *match; + + if (!pdev->dev.of_node) + return -ENODEV; + + match = of_match_node(dw_mci_rockchip_match, pdev->dev.of_node); + drv_data = match->data; + + return dw_mci_pltfm_register(pdev, drv_data); +} + +#ifdef CONFIG_PM_SLEEP +static int dw_mci_rockchip_suspend(struct device *dev) +{ + struct dw_mci *host = dev_get_drvdata(dev); + + return dw_mci_suspend(host); +} + +static int dw_mci_rockchip_resume(struct device *dev) +{ + struct dw_mci *host = dev_get_drvdata(dev); + + return dw_mci_resume(host); +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(dw_mci_rockchip_pmops, + dw_mci_rockchip_suspend, + dw_mci_rockchip_resume); + +static struct platform_driver dw_mci_rockchip_pltfm_driver = { + .probe = dw_mci_rockchip_probe, + .remove = __exit_p(dw_mci_pltfm_remove), + .driver = { + .name = "dwmmc_rockchip", + .of_match_table = dw_mci_rockchip_match, + .pm = &dw_mci_rockchip_pmops, + }, +}; + +module_platform_driver(dw_mci_rockchip_pltfm_driver); + +MODULE_AUTHOR("Addy Ke "); +MODULE_DESCRIPTION("Rockchip Specific DW-MSHC Driver Extension"); +MODULE_ALIAS("platform:dwmmc-rockchip"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 066173b6436dfc57a96b2d940f4e727fe8131261 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 20 Aug 2014 13:27:44 +0300 Subject: mmc: sdhci: Add PCI IDs for Intel Braswell The hardware is the same as used in Baytrail. Add these new PCI IDs to the driver's list of supported IDs. Signed-off-by: Alan Cox Signed-off-by: Mika Westerberg Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci.c | 23 +++++++++++++++++++++++ drivers/mmc/host/sdhci-pci.h | 3 +++ 2 files changed, 26 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 310ecbcdfb7f..cfe8a2c85987 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -876,6 +876,29 @@ static const struct pci_device_id pci_ids[] = { .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BSW_EMMC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BSW_SDIO, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BSW_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd, + }, { .vendor = PCI_VENDOR_ID_INTEL, diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index c101477ef3be..9c1909b2a3ad 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -11,6 +11,9 @@ #define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15 #define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16 #define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50 +#define PCI_DEVICE_ID_INTEL_BSW_EMMC 0x2294 +#define PCI_DEVICE_ID_INTEL_BSW_SDIO 0x2295 +#define PCI_DEVICE_ID_INTEL_BSW_SD 0x2296 #define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190 #define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9 #define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa -- cgit v1.2.3 From 3df01a931348c4c295c0e8ace17d4d764d93ad2c Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Wed, 20 Aug 2014 21:39:20 +0800 Subject: mmc: sunxi: Declare ERASE capability Declare ERASE capability so we can use filesystems with the discard option and the fstrim tool. Signed-off-by: Chen-Yu Tsai Acked-by: Hans de Goede Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index dafe5001a46e..d1663b3c4143 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -990,7 +990,8 @@ static int sunxi_mmc_probe(struct platform_device *pdev) /* 400kHz ~ 50MHz */ mmc->f_min = 400000; mmc->f_max = 50000000; - mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; + mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | + MMC_CAP_ERASE; ret = mmc_of_parse(mmc); if (ret) -- cgit v1.2.3 From cc8aa7de48277f62fe3fced762d75f01ce57e909 Mon Sep 17 00:00:00 2001 From: Chuanxiao Dong Date: Thu, 14 Aug 2014 18:29:24 +0800 Subject: mmc: core: resolve divded by zero panic With one special SD card, below divide by zero error observed: ... [ 2.144300] divide error: 0000 [#1] PREEMPT SMP [ 2.148860] Modules linked in: [ 2.151898] [ 2.152685] Set up 4031 stolen pages starting at 0x0001f000, GTT offset 0K [ 2.157330] Set up 0 CI stolen pages starting at 0x00000000, GTT offset 131072K [ 2.167581] Pid: 5, comm: kworker/u:0 Not tainted 3.0.8-138216-g974a2ab #1 [ 2.169506] [drm] PSB GTT mem manager ready, tt_start 4031, tt_size 28737 pages [ 2.169906] [drm] SGX core id = 0x00000000 [ 2.169920] [drm] SGX core rev major = 0x00, minor = 0x00 [ 2.169934] [drm] SGX core rev maintenance = 0x00, designer = 0x00 [ 2.197370] Intel Corporation Medfield/iCDKB [ 2.201716] EIP: 0060:[] EFLAGS: 00010246 CPU: 1 [ 2.207198] EIP is at mmc_init_erase+0x76/0x150 [ 2.211704] EAX: 00002000 EBX: dcd1b400 ECX: 00002000 EDX: 00000000 [ 2.217957] ESI: 00000000 EDI: dcd5c800 EBP: dd867e84 ESP: dd867e7c [ 2.224214] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 [ 2.229605] Process kworker/u:0 (pid: 5, ti=dd866000 task=dd868000 task.ti=dd866000) [ 2.237325] Stack: [ 2.239322] dcd1b400 00000000 dd867eb0 c16a06da c1ab7c44 dd995aa8 00000003 00000000 [ 2.247054] 00000000 00000000 dcd5c800 00000000 dcd1b400 dd867ef8 c16a1012 c1698b00 [ 2.254785] 00000029 00000001 c194eb80 dcd5c9ec dd867e00 c1239b00 00000000 00000000 [ 2.262519] Call Trace: [ 2.264975] [] mmc_sd_setup_card+0x1da/0x4f0 [ 2.270183] [] mmc_sd_init_card+0x192/0xc40 [ 2.275304] [] ? __mmc_claim_host+0x160/0x160 [ 2.280610] [] ? __schedule_bug+0x50/0x80 [ 2.285556] [] mmc_attach_sd+0xc9/0x230 [ 2.290333] [] mmc_rescan+0x25f/0x2c0 [ 2.294943] [] process_one_work+0x103/0x400 [ 2.300065] [] ? mod_timer+0x1ad/0x3c0 [ 2.304756] [] ? mmc_suspend_host+0x1a0/0x1a0 [ 2.310056] [] worker_thread+0x12d/0x4a0 [ 2.314921] [] ? preempt_schedule+0x2d/0x50 [ 2.320047] [erase_size to 0. Then it triggered this divided by zero error when calculate card->pref_erase. Submit this patch to fix the issue. Signed-off-by: Yunpeng Gao Signed-off-by: Chuanxiao Dong Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 68f5f4ba57e9..e2e1dd40fb11 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1753,7 +1753,7 @@ void mmc_init_erase(struct mmc_card *card) card->erase_shift = ffs(card->ssr.au) - 1; } else if (card->ext_csd.hc_erase_size) { card->pref_erase = card->ext_csd.hc_erase_size; - } else { + } else if (card->erase_size) { sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11; if (sz < 128) card->pref_erase = 512 * 1024 / 512; @@ -1770,7 +1770,8 @@ void mmc_init_erase(struct mmc_card *card) if (sz) card->pref_erase += card->erase_size - sz; } - } + } else + card->pref_erase = 0; } static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card, -- cgit v1.2.3 From 51da2240906cb94e8f6ba55e403b6206df6fb2dd Mon Sep 17 00:00:00 2001 From: Yuvaraj CD Date: Fri, 22 Aug 2014 19:17:50 +0530 Subject: mmc: dw_mmc: use mmc_regulator_get_supply to handle regulators This patch makes use of mmc_regulator_get_supply() to handle the vmmc and vqmmc regulators.Also it moves the code handling the these regulators to dw_mci_set_ios().It turned on the vmmc and vqmmc during MMC_POWER_UP and MMC_POWER_ON,and turned off during MMC_POWER_OFF. Signed-off-by: Yuvaraj Kumar C D Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 72 +++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 37 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 7f227e964265..aadb0d6aa63f 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -936,6 +936,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct dw_mci_slot *slot = mmc_priv(mmc); const struct dw_mci_drv_data *drv_data = slot->host->drv_data; u32 regs; + int ret; switch (ios->bus_width) { case MMC_BUS_WIDTH_4: @@ -974,12 +975,38 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_UP: + if (!IS_ERR(mmc->supply.vmmc)) { + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, + ios->vdd); + if (ret) { + dev_err(slot->host->dev, + "failed to enable vmmc regulator\n"); + /*return, if failed turn on vmmc*/ + return; + } + } + if (!IS_ERR(mmc->supply.vqmmc) && !slot->host->vqmmc_enabled) { + ret = regulator_enable(mmc->supply.vqmmc); + if (ret < 0) + dev_err(slot->host->dev, + "failed to enable vqmmc regulator\n"); + else + slot->host->vqmmc_enabled = true; + } set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); regs = mci_readl(slot->host, PWREN); regs |= (1 << slot->id); mci_writel(slot->host, PWREN, regs); break; case MMC_POWER_OFF: + if (!IS_ERR(mmc->supply.vmmc)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + + if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled) { + regulator_disable(mmc->supply.vqmmc); + slot->host->vqmmc_enabled = false; + } + regs = mci_readl(slot->host, PWREN); regs &= ~(1 << slot->id); mci_writel(slot->host, PWREN, regs); @@ -2110,7 +2137,13 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) mmc->f_max = freq[1]; } - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + /*if there are external regulators, get them*/ + ret = mmc_regulator_get_supply(mmc); + if (ret == -EPROBE_DEFER) + goto err_setup_bus; + + if (!mmc->ocr_avail) + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; if (host->pdata->caps) mmc->caps = host->pdata->caps; @@ -2176,7 +2209,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) err_setup_bus: mmc_free_host(mmc); - return -EINVAL; + return ret; } static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) @@ -2469,24 +2502,6 @@ int dw_mci_probe(struct dw_mci *host) } } - host->vmmc = devm_regulator_get_optional(host->dev, "vmmc"); - if (IS_ERR(host->vmmc)) { - ret = PTR_ERR(host->vmmc); - if (ret == -EPROBE_DEFER) - goto err_clk_ciu; - - dev_info(host->dev, "no vmmc regulator found: %d\n", ret); - host->vmmc = NULL; - } else { - ret = regulator_enable(host->vmmc); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(host->dev, - "regulator_enable fail: %d\n", ret); - goto err_clk_ciu; - } - } - host->quirks = host->pdata->quirks; spin_lock_init(&host->lock); @@ -2630,8 +2645,6 @@ err_workqueue: err_dmaunmap: if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - if (host->vmmc) - regulator_disable(host->vmmc); err_clk_ciu: if (!IS_ERR(host->ciu_clk)) @@ -2667,9 +2680,6 @@ void dw_mci_remove(struct dw_mci *host) if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - if (host->vmmc) - regulator_disable(host->vmmc); - if (!IS_ERR(host->ciu_clk)) clk_disable_unprepare(host->ciu_clk); @@ -2686,9 +2696,6 @@ EXPORT_SYMBOL(dw_mci_remove); */ int dw_mci_suspend(struct dw_mci *host) { - if (host->vmmc) - regulator_disable(host->vmmc); - return 0; } EXPORT_SYMBOL(dw_mci_suspend); @@ -2697,15 +2704,6 @@ int dw_mci_resume(struct dw_mci *host) { int i, ret; - if (host->vmmc) { - ret = regulator_enable(host->vmmc); - if (ret) { - dev_err(host->dev, - "failed to enable regulator: %d\n", ret); - return ret; - } - } - if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS)) { ret = -ENODEV; return ret; -- cgit v1.2.3 From 0173055842cd1d9ed3984e70891c22dbf2f29372 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Fri, 22 Aug 2014 19:17:51 +0530 Subject: mmc: dw_mmc: Support voltage changes For UHS cards we need the ability to switch voltages from 3.3V to 1.8V. Add support to the dw_mmc driver to handle this. Note that dw_mmc needs a little bit of extra code since the interface needs a special bit programmed to the CMD register while CMD11 is progressing. This means adding a few extra states to the state machine to track. Signed-off-by: Doug Anderson Signed-off-by: Yuvaraj Kumar C D Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 137 +++++++++++++++++++++++++++++++++++++++++++--- drivers/mmc/host/dw_mmc.h | 5 +- 2 files changed, 132 insertions(+), 10 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index aadb0d6aa63f..23719249182b 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -234,10 +235,13 @@ err: } #endif /* defined(CONFIG_DEBUG_FS) */ +static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg); + static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) { struct mmc_data *data; struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot->host; const struct dw_mci_drv_data *drv_data = slot->host->drv_data; u32 cmdr; cmd->error = -EINPROGRESS; @@ -253,6 +257,34 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) else if (cmd->opcode != MMC_SEND_STATUS && cmd->data) cmdr |= SDMMC_CMD_PRV_DAT_WAIT; + if (cmd->opcode == SD_SWITCH_VOLTAGE) { + u32 clk_en_a; + + /* Special bit makes CMD11 not die */ + cmdr |= SDMMC_CMD_VOLT_SWITCH; + + /* Change state to continue to handle CMD11 weirdness */ + WARN_ON(slot->host->state != STATE_SENDING_CMD); + slot->host->state = STATE_SENDING_CMD11; + + /* + * We need to disable low power mode (automatic clock stop) + * while doing voltage switch so we don't confuse the card, + * since stopping the clock is a specific part of the UHS + * voltage change dance. + * + * Note that low power mode (SDMMC_CLKEN_LOW_PWR) will be + * unconditionally turned back on in dw_mci_setup_bus() if it's + * ever called with a non-zero clock. That shouldn't happen + * until the voltage change is all done. + */ + clk_en_a = mci_readl(host, CLKENA); + clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id); + mci_writel(host, CLKENA, clk_en_a); + mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | + SDMMC_CMD_PRV_DAT_WAIT, 0); + } + if (cmd->flags & MMC_RSP_PRESENT) { /* We expect a response, so set this bit */ cmdr |= SDMMC_CMD_RESP_EXP; @@ -775,11 +807,15 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) unsigned int clock = slot->clock; u32 div; u32 clk_en_a; + u32 sdmmc_cmd_bits = SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT; + + /* We must continue to set bit 28 in CMD until the change is complete */ + if (host->state == STATE_WAITING_CMD11_DONE) + sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; if (!clock) { mci_writel(host, CLKENA, 0); - mci_send_cmd(slot, - SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + mci_send_cmd(slot, sdmmc_cmd_bits, 0); } else if (clock != host->current_speed || force_clkinit) { div = host->bus_hz / clock; if (host->bus_hz % clock && host->bus_hz > clock) @@ -803,15 +839,13 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_writel(host, CLKSRC, 0); /* inform CIU */ - mci_send_cmd(slot, - SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + mci_send_cmd(slot, sdmmc_cmd_bits, 0); /* set clock to desired speed */ mci_writel(host, CLKDIV, div); /* inform CIU */ - mci_send_cmd(slot, - SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + mci_send_cmd(slot, sdmmc_cmd_bits, 0); /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; @@ -820,8 +854,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_writel(host, CLKENA, clk_en_a); /* inform CIU */ - mci_send_cmd(slot, - SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + mci_send_cmd(slot, sdmmc_cmd_bits, 0); /* keep the clock with reflecting clock dividor */ slot->__clk_old = clock << div; @@ -897,6 +930,17 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, slot->mrq = mrq; + if (host->state == STATE_WAITING_CMD11_DONE) { + dev_warn(&slot->mmc->class_dev, + "Voltage change didn't complete\n"); + /* + * this case isn't expected to happen, so we can + * either crash here or just try to continue on + * in the closest possible state + */ + host->state = STATE_IDLE; + } + if (host->state == STATE_IDLE) { host->state = STATE_SENDING_CMD; dw_mci_start_request(host, slot); @@ -973,6 +1017,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) /* Slot specific timing and width adjustment */ dw_mci_setup_bus(slot, false); + if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0) + slot->host->state = STATE_IDLE; + switch (ios->power_mode) { case MMC_POWER_UP: if (!IS_ERR(mmc->supply.vmmc)) { @@ -1016,6 +1063,59 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } } +static int dw_mci_card_busy(struct mmc_host *mmc) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + u32 status; + + /* + * Check the busy bit which is low when DAT[3:0] + * (the data lines) are 0000 + */ + status = mci_readl(slot->host, STATUS); + + return !!(status & SDMMC_STATUS_BUSY); +} + +static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot->host; + u32 uhs; + u32 v18 = SDMMC_UHS_18V << slot->id; + int min_uv, max_uv; + int ret; + + /* + * Program the voltage. Note that some instances of dw_mmc may use + * the UHS_REG for this. For other instances (like exynos) the UHS_REG + * does no harm but you need to set the regulator directly. Try both. + */ + uhs = mci_readl(host, UHS_REG); + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { + min_uv = 2700000; + max_uv = 3600000; + uhs &= ~v18; + } else { + min_uv = 1700000; + max_uv = 1950000; + uhs |= v18; + } + if (!IS_ERR(mmc->supply.vqmmc)) { + ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv); + + if (ret) { + dev_err(&mmc->class_dev, + "Regulator set error %d: %d - %d\n", + ret, min_uv, max_uv); + return ret; + } + } + mci_writel(host, UHS_REG, uhs); + + return 0; +} + static int dw_mci_get_ro(struct mmc_host *mmc) { int read_only; @@ -1158,6 +1258,9 @@ static const struct mmc_host_ops dw_mci_ops = { .get_cd = dw_mci_get_cd, .enable_sdio_irq = dw_mci_enable_sdio_irq, .execute_tuning = dw_mci_execute_tuning, + .card_busy = dw_mci_card_busy, + .start_signal_voltage_switch = dw_mci_switch_voltage, + }; static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) @@ -1181,7 +1284,11 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) dw_mci_start_request(host, slot); } else { dev_vdbg(host->dev, "list empty\n"); - host->state = STATE_IDLE; + + if (host->state == STATE_SENDING_CMD11) + host->state = STATE_WAITING_CMD11_DONE; + else + host->state = STATE_IDLE; } spin_unlock(&host->lock); @@ -1292,8 +1399,10 @@ static void dw_mci_tasklet_func(unsigned long priv) switch (state) { case STATE_IDLE: + case STATE_WAITING_CMD11_DONE: break; + case STATE_SENDING_CMD11: case STATE_SENDING_CMD: if (!test_and_clear_bit(EVENT_CMD_COMPLETE, &host->pending_events)) @@ -1894,6 +2003,14 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } if (pending) { + /* Check volt switch first, since it can look like an error */ + if ((host->state == STATE_SENDING_CMD11) && + (pending & SDMMC_INT_VOLT_SWITCH)) { + mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH); + pending &= ~SDMMC_INT_VOLT_SWITCH; + dw_mci_cmd_interrupt(host, pending); + } + if (pending & DW_MCI_CMD_ERROR_FLAGS) { mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); host->cmd_status = pending; @@ -1999,7 +2116,9 @@ static void dw_mci_work_routine_card(struct work_struct *work) switch (host->state) { case STATE_IDLE: + case STATE_WAITING_CMD11_DONE: break; + case STATE_SENDING_CMD11: case STATE_SENDING_CMD: mrq->cmd->error = -ENOMEDIUM; if (!mrq->data) diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 08fd956d81f3..01b99e8a9190 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -99,6 +99,7 @@ #define SDMMC_INT_HLE BIT(12) #define SDMMC_INT_FRUN BIT(11) #define SDMMC_INT_HTO BIT(10) +#define SDMMC_INT_VOLT_SWITCH BIT(10) /* overloads bit 10! */ #define SDMMC_INT_DRTO BIT(9) #define SDMMC_INT_RTO BIT(8) #define SDMMC_INT_DCRC BIT(7) @@ -113,6 +114,7 @@ /* Command register defines */ #define SDMMC_CMD_START BIT(31) #define SDMMC_CMD_USE_HOLD_REG BIT(29) +#define SDMMC_CMD_VOLT_SWITCH BIT(28) #define SDMMC_CMD_CCS_EXP BIT(23) #define SDMMC_CMD_CEATA_RD BIT(22) #define SDMMC_CMD_UPD_CLK BIT(21) @@ -130,6 +132,7 @@ /* Status register defines */ #define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF) #define SDMMC_STATUS_DMA_REQ BIT(31) +#define SDMMC_STATUS_BUSY BIT(9) /* FIFOTH register defines */ #define SDMMC_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \ ((r) & 0xFFF) << 16 | \ @@ -150,7 +153,7 @@ #define SDMMC_GET_VERID(x) ((x) & 0xFFFF) /* Card read threshold */ #define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x)) - +#define SDMMC_UHS_18V BIT(0) /* All ctrl reset bits */ #define SDMMC_CTRL_ALL_RESET_FLAGS \ (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET) -- cgit v1.2.3 From 1ba4c322caaecfb68c1e175599398caef946a525 Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Tue, 26 Aug 2014 10:50:42 +0800 Subject: mmc: sdhci-sirf: fix 8bit width enable by overwriting set_bus_width the implementation of CSR SDHCI controller is a modified version of the one described in the 1.0 specification, and not a normal 3.0 controller. and 8bit-width enable bit of CSR MMC hosts is 3, while stardard hosts use bit 5. this patch fixes the functionality of 8bit transfer in mmc controllers and improve performance for mmc0 a lot. Signed-off-by: Minda Chen Signed-off-by: Barry Song Reviewed-by: Romain Izard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-sirf.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index e15dc891ba01..68a39718a171 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -15,6 +15,8 @@ #include #include "sdhci-pltfm.h" +#define SDHCI_SIRF_8BITBUS BIT(3) + struct sdhci_sirf_priv { struct clk *clk; int gpio_cd; @@ -27,10 +29,30 @@ static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host) return clk_get_rate(priv->clk); } +static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width) +{ + u8 ctrl; + + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + ctrl &= ~(SDHCI_CTRL_4BITBUS | SDHCI_SIRF_8BITBUS); + + /* + * CSR atlas7 and prima2 SD host version is not 3.0 + * 8bit-width enable bit of CSR SD hosts is 3, + * while stardard hosts use bit 5 + */ + if (width == MMC_BUS_WIDTH_8) + ctrl |= SDHCI_SIRF_8BITBUS; + else if (width == MMC_BUS_WIDTH_4) + ctrl |= SDHCI_CTRL_4BITBUS; + + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); +} + static struct sdhci_ops sdhci_sirf_ops = { .set_clock = sdhci_set_clock, .get_max_clock = sdhci_sirf_get_max_clk, - .set_bus_width = sdhci_set_bus_width, + .set_bus_width = sdhci_sirf_set_bus_width, .reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, }; -- cgit v1.2.3 From 3cf890fc42b2d3bd5a3a7e4c1f61b0637efbe2ec Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Mon, 25 Aug 2014 11:19:04 -0700 Subject: mmc: dw_mmc: Pass back errors from mmc_of_parse() It's possible that mmc_of_parse() could return errors (possibly in some future version it might return -EPROBE_DEFER even). Let's pass those errors back. Signed-off-by: Doug Anderson Acked-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 23719249182b..835e913cafb7 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2259,7 +2259,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) /*if there are external regulators, get them*/ ret = mmc_regulator_get_supply(mmc); if (ret == -EPROBE_DEFER) - goto err_setup_bus; + goto err_host_allocated; if (!mmc->ocr_avail) mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; @@ -2283,7 +2283,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) if (host->pdata->caps2) mmc->caps2 = host->pdata->caps2; - mmc_of_parse(mmc); + ret = mmc_of_parse(mmc); + if (ret) + goto err_host_allocated; if (host->pdata->blk_settings) { mmc->max_segs = host->pdata->blk_settings->max_segs; @@ -2315,7 +2317,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) ret = mmc_add_host(mmc); if (ret) - goto err_setup_bus; + goto err_host_allocated; #if defined(CONFIG_DEBUG_FS) dw_mci_init_debugfs(slot); @@ -2326,7 +2328,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) return 0; -err_setup_bus: +err_host_allocated: mmc_free_host(mmc); return ret; } -- cgit v1.2.3 From a638640335d86549da2b7d38f82d584294333512 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 28 Aug 2014 10:07:19 +0200 Subject: mmc: tmio-mmc: Add support for SDHI on new R-Car Gen2 SoCs - r8a7792 (R-Car V2H) - r8a7793 (R-Car M2-N) - r8a7794 (R-Car E2) Signed-off-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index d5d493719491..f2585b6c1414 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -72,6 +72,9 @@ static const struct of_device_id sh_mobile_sdhi_of_match[] = { { .compatible = "renesas,sdhi-r8a7779", .data = &of_rcar_gen1_compatible, }, { .compatible = "renesas,sdhi-r8a7790", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,sdhi-r8a7791", .data = &of_rcar_gen2_compatible, }, + { .compatible = "renesas,sdhi-r8a7792", .data = &of_rcar_gen2_compatible, }, + { .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, }, + { .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, }, {}, }; MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match); -- cgit v1.2.3 From a6ff5aeb9b31f7b71a8566f8e130ad66bd103d20 Mon Sep 17 00:00:00 2001 From: Aisheng Dong Date: Wed, 27 Aug 2014 15:26:27 +0800 Subject: mmc: sdhci: add platform get_max_timeout_count hook Currently the max timeout count is hardcode to 1 << 27 for calcuate the max_busy_timeout, however, for some platforms the max timeout count may not be 1 << 27, e.g. i.MX uSDHC is 1 << 28. Thus 1 << 27 is not correct for such platform. It is also possible that other platforms may have different values. To be flexible, we add a get_max_timeout_count hook to get the correct maximum timeout value for these platforms. Reviewed-by: Shawn Guo Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 9 +++++++-- drivers/mmc/host/sdhci.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 4cc2ff90eeef..2ba9c4b8508d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1197,7 +1197,10 @@ clock_set: if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) { host->timeout_clk = host->mmc->actual_clock / 1000; host->mmc->max_busy_timeout = - (1 << 27) / host->timeout_clk; + host->ops->get_max_timeout_count ? + host->ops->get_max_timeout_count(host) : + 1 << 27; + host->mmc->max_busy_timeout /= host->timeout_clk; } } @@ -2937,7 +2940,9 @@ int sdhci_add_host(struct sdhci_host *host) if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) host->timeout_clk = mmc->f_max / 1000; - mmc->max_busy_timeout = (1 << 27) / host->timeout_clk; + mmc->max_busy_timeout = host->ops->get_max_timeout_count ? + host->ops->get_max_timeout_count(host) : 1 << 27; + mmc->max_busy_timeout /= host->timeout_clk; mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 4a5cd5e3fa3e..b13050968946 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -281,6 +281,7 @@ struct sdhci_ops { unsigned int (*get_max_clock)(struct sdhci_host *host); unsigned int (*get_min_clock)(struct sdhci_host *host); unsigned int (*get_timeout_clock)(struct sdhci_host *host); + unsigned int (*get_max_timeout_count)(struct sdhci_host *host); void (*set_bus_width)(struct sdhci_host *host, int width); void (*platform_send_init_74_clocks)(struct sdhci_host *host, u8 power_mode); -- cgit v1.2.3 From 10fd0ad967c05cda16b25f862e2a45eb63d83a21 Mon Sep 17 00:00:00 2001 From: Aisheng Dong Date: Wed, 27 Aug 2014 15:26:28 +0800 Subject: mmc: sdhci-esdhc-imx: fix incorrect max timeout cout for uSDHC The default sdhci code use the 1 << 27 as the max timeout counter to to calculate the max_busy_timeout, however it's not correct for uSDHC since its the max counter is 1 << 28. Implement esdhc_get_max_timeout_cout to handle it correctly. Reviewed-by: Shawn Guo Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 46677f09ad56..546ed02c4fcf 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -880,6 +880,14 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask) sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } +static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; + + return esdhc_is_usdhc(imx_data) ? 1 << 28 : 1 << 27; +} + static struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl_le, .read_w = esdhc_readw_le, @@ -889,6 +897,7 @@ static struct sdhci_ops sdhci_esdhc_ops = { .set_clock = esdhc_pltfm_set_clock, .get_max_clock = esdhc_pltfm_get_max_clock, .get_min_clock = esdhc_pltfm_get_min_clock, + .get_max_timeout_count = esdhc_get_max_timeout_count, .get_ro = esdhc_pltfm_get_ro, .set_bus_width = esdhc_pltfm_set_bus_width, .set_uhs_signaling = esdhc_set_uhs_signaling, -- cgit v1.2.3 From b45e668af4439bfc52ed92af44b6400661ba7ec8 Mon Sep 17 00:00:00 2001 From: Aisheng Dong Date: Wed, 27 Aug 2014 15:26:29 +0800 Subject: mmc: sdhci: add platform set_timeout hook Currently the common code assume 0xE is the maximum timeout counter value and use it to write into the timeout counter register. However, it's fairly possible that some other SoCs may have different max timeout register value. That means 0xE may be incorrect and becomes meaningless. It's also possible that other platforms has different timeout calculation algorithm. To be flexible, this patch provides a .set_timeout hook for those platforms to set the timeout on their way if they need. Reviewed-by: Shawn Guo Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 19 ++++++++++++++----- drivers/mmc/host/sdhci.h | 2 ++ 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2ba9c4b8508d..ff0e1c55d958 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -707,19 +707,28 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host) sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } -static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) +static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd) { u8 count; + + if (host->ops->set_timeout) { + host->ops->set_timeout(host, cmd); + } else { + count = sdhci_calc_timeout(host, cmd); + sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL); + } +} + +static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) +{ u8 ctrl; struct mmc_data *data = cmd->data; int ret; WARN_ON(host->data); - if (data || (cmd->flags & MMC_RSP_BUSY)) { - count = sdhci_calc_timeout(host, cmd); - sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL); - } + if (data || (cmd->flags & MMC_RSP_BUSY)) + sdhci_set_timeout(host, cmd); if (!data) return; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index b13050968946..9828ff83d84c 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -282,6 +282,8 @@ struct sdhci_ops { unsigned int (*get_min_clock)(struct sdhci_host *host); unsigned int (*get_timeout_clock)(struct sdhci_host *host); unsigned int (*get_max_timeout_count)(struct sdhci_host *host); + void (*set_timeout)(struct sdhci_host *host, + struct mmc_command *cmd); void (*set_bus_width)(struct sdhci_host *host, int width); void (*platform_send_init_74_clocks)(struct sdhci_host *host, u8 power_mode); -- cgit v1.2.3 From e33eb8e2818c9a7b41bac68a1c83ee4c136af9ba Mon Sep 17 00:00:00 2001 From: Aisheng Dong Date: Wed, 27 Aug 2014 15:26:30 +0800 Subject: mmc: sdhci-esdhc-imx: set the correct max timeout value for uSDHC The default sdhci driver write 0xE into timeout counter register to set the maximum timeout. The value is not correct for uSDHC since the max counter value for uSDHC is 0xF. Instead of using common timeout code in sdhci, we implement esdhc_set_timeout to handle the difference between eSDHC and uSDHC. Currently we simply set the max timeout value as before. But in the future, we probably may implement IMX specific timeout setting algorithm and use suitable timeout for different CMDs. Reviewed-by: Shawn Guo Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 546ed02c4fcf..587ee0edeb57 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -888,6 +888,16 @@ static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host) return esdhc_is_usdhc(imx_data) ? 1 << 28 : 1 << 27; } +static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; + + /* use maximum timeout counter */ + sdhci_writeb(host, esdhc_is_usdhc(imx_data) ? 0xF : 0xE, + SDHCI_TIMEOUT_CONTROL); +} + static struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl_le, .read_w = esdhc_readw_le, @@ -899,6 +909,7 @@ static struct sdhci_ops sdhci_esdhc_ops = { .get_min_clock = esdhc_pltfm_get_min_clock, .get_max_timeout_count = esdhc_get_max_timeout_count, .get_ro = esdhc_pltfm_get_ro, + .set_timeout = esdhc_set_timeout, .set_bus_width = esdhc_pltfm_set_bus_width, .set_uhs_signaling = esdhc_set_uhs_signaling, .reset = esdhc_reset, -- cgit v1.2.3 From 28aab053396125c8e191537ec2b9781ec0174b04 Mon Sep 17 00:00:00 2001 From: Aisheng Dong Date: Wed, 27 Aug 2014 15:26:31 +0800 Subject: mmc: sdhci: calculate timeout_clk conditionally in sdhci_add_host The timeout_clk calculation code in sdhci_add_host is meaningless for SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK. So only execute them with no SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK set. Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index ff0e1c55d958..1e50880d57de 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2931,27 +2931,27 @@ int sdhci_add_host(struct sdhci_host *host) } else mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200; - host->timeout_clk = - (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; - if (host->timeout_clk == 0) { - if (host->ops->get_timeout_clock) { - host->timeout_clk = host->ops->get_timeout_clock(host); - } else if (!(host->quirks & - SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) { - pr_err("%s: Hardware doesn't specify timeout clock " - "frequency.\n", mmc_hostname(mmc)); - return -ENODEV; + if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) { + host->timeout_clk = (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> + SDHCI_TIMEOUT_CLK_SHIFT; + if (host->timeout_clk == 0) { + if (host->ops->get_timeout_clock) { + host->timeout_clk = + host->ops->get_timeout_clock(host); + } else { + pr_err("%s: Hardware doesn't specify timeout clock frequency.\n", + mmc_hostname(mmc)); + return -ENODEV; + } } - } - if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT) - host->timeout_clk *= 1000; - if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) - host->timeout_clk = mmc->f_max / 1000; + if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT) + host->timeout_clk *= 1000; - mmc->max_busy_timeout = host->ops->get_max_timeout_count ? + mmc->max_busy_timeout = host->ops->get_max_timeout_count ? host->ops->get_max_timeout_count(host) : 1 << 27; - mmc->max_busy_timeout /= host->timeout_clk; + mmc->max_busy_timeout /= host->timeout_clk; + } mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; -- cgit v1.2.3 From 03d6f5ffc5c469e66bfe0a7d8120d29d4c3c07c9 Mon Sep 17 00:00:00 2001 From: Aisheng Dong Date: Wed, 27 Aug 2014 15:26:32 +0800 Subject: mmc: sdhci: move timeout_clk dynamically calculation code into common code The timeout_clk calculation code for SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK case is common and could be moved into common sdhci_do_set_ios, then platform code which is not using sdhci_set_clock does not need to write the same code again. Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1e50880d57de..f6a683b9ffc3 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1201,18 +1201,8 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) } clock_set: - if (real_div) { + if (real_div) host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div; - if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) { - host->timeout_clk = host->mmc->actual_clock / 1000; - host->mmc->max_busy_timeout = - host->ops->get_max_timeout_count ? - host->ops->get_max_timeout_count(host) : - 1 << 27; - host->mmc->max_busy_timeout /= host->timeout_clk; - } - } - clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) << SDHCI_DIVIDER_HI_SHIFT; @@ -1489,6 +1479,18 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) if (!ios->clock || ios->clock != host->clock) { host->ops->set_clock(host, ios->clock); host->clock = ios->clock; + + if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK && + host->clock) { + host->timeout_clk = host->mmc->actual_clock ? + host->mmc->actual_clock / 1000 : + host->clock / 1000; + host->mmc->max_busy_timeout = + host->ops->get_max_timeout_count ? + host->ops->get_max_timeout_count(host) : + 1 << 27; + host->mmc->max_busy_timeout /= host->timeout_clk; + } } sdhci_set_power(host, ios->power_mode, ios->vdd); -- cgit v1.2.3 From 0b10f478d2ad93b3808d018327465aaab77c8d2b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 30 Aug 2014 14:53:13 -0300 Subject: sdhci: Make sdhci_disable_irq_wakeups() static sdhci_disable_irq_wakeups() is exported, but it is not called outside sdhci.c. Make it static and do not export it, so that the following sparse warning is fixed: drivers/mmc/host/sdhci.c:2548:6: warning: symbol 'sdhci_disable_irq_wakeups' was not declared. Should it be static? Signed-off-by: Fabio Estevam Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f6a683b9ffc3..b5e22b8c4885 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2559,7 +2559,7 @@ void sdhci_enable_irq_wakeups(struct sdhci_host *host) } EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups); -void sdhci_disable_irq_wakeups(struct sdhci_host *host) +static void sdhci_disable_irq_wakeups(struct sdhci_host *host) { u8 val; u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE @@ -2569,7 +2569,6 @@ void sdhci_disable_irq_wakeups(struct sdhci_host *host) val &= ~mask; sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL); } -EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups); int sdhci_suspend_host(struct sdhci_host *host) { -- cgit v1.2.3 From e99783a45220a2c5f5a598e0e81213ecf2dbcf2f Mon Sep 17 00:00:00 2001 From: Chanho Min Date: Sat, 30 Aug 2014 12:40:40 +0900 Subject: mmc: sdhci: handle busy-end interrupt during command It is fully legal for a controller to start handling busy-end interrupt before it has signaled that the command has completed. So make sure we do things in the proper order, Or it results that command interrupt is ignored so it can cause unexpected operations. This is founded at some toshiba emmc with the bellow warning. "mmc0: Got command interrupt 0x00000001 even though no command operation was in progress." This issue has been also reported by Youssef TRIKI: It is not specific to Toshiba devices, and happens with eMMC devices as well as SD card which support Auto-CMD12 rather than CMD23. Also, similar patch is submitted by: Gwendal Grignou Changes since v1: Fixed conflict with the next of git.linaro.org/people/ulf.hansson/mmc.git and Tested if issue is fixed again. Signed-off-by: Hankyung Yu Signed-off-by: Chanho Min Tested-by: Youssef TRIKI Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b5e22b8c4885..335cdf3b5224 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1016,6 +1016,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) mod_timer(&host->timer, timeout); host->cmd = cmd; + host->busy_handle = 0; sdhci_prepare_data(host, cmd); @@ -2261,8 +2262,12 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) if (host->cmd->data) DBG("Cannot wait for busy signal when also " "doing a data transfer"); - else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ)) + else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) + && !host->busy_handle) { + /* Mark that command complete before busy is ended */ + host->busy_handle = 1; return; + } /* The controller does not support the end-of-busy IRQ, * fall through and take the SDHCI_INT_RESPONSE */ @@ -2330,7 +2335,15 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) return; } if (intmask & SDHCI_INT_DATA_END) { - sdhci_finish_command(host); + /* + * Some cards handle busy-end interrupt + * before the command completed, so make + * sure we do things in the proper order. + */ + if (host->busy_handle) + sdhci_finish_command(host); + else + host->busy_handle = 1; return; } } -- cgit v1.2.3 From f1b55a55e4ee6fe6d791cff994f6e4d990f69c1e Mon Sep 17 00:00:00 2001 From: "Gao, Yunpeng" Date: Mon, 18 Aug 2014 15:05:52 +0800 Subject: mmc: sdhci-pci: enable runtime pm for Intel Merrifield platform Enable runtime pm support on Intel Merrifield platform. Signed-off-by: Yunpeng Gao Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index cfe8a2c85987..ff9ab62e6eef 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -318,6 +318,7 @@ static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot) static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = { .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_BROKEN_HS200, + .allow_runtime_pm = true, .probe_slot = intel_mrfl_mmc_probe_slot, }; -- cgit v1.2.3 From 578b36b69c31c874f8303c62efb40b8bb9b46ae5 Mon Sep 17 00:00:00 2001 From: "Gao, Yunpeng" Date: Mon, 1 Sep 2014 11:35:40 +0800 Subject: mmc: sdhci-acpi: add probe_slot method for emmc/sd/sdio Similar to sdhci-pci controller, also add probe_slot and remove_slot method in the sdhci-acpi driver. Signed-off-by: Yunpeng Gao Signed-off-by: Chuanxiao Dong Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-acpi.c | 58 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index cc65b3fd0a60..3483c089baa7 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -67,6 +67,8 @@ struct sdhci_acpi_slot { unsigned int caps2; mmc_pm_flag_t pm_caps; unsigned int flags; + int (*probe_slot)(struct platform_device *); + int (*remove_slot)(struct platform_device *); }; struct sdhci_acpi_host { @@ -122,6 +124,51 @@ static const struct sdhci_acpi_chip sdhci_acpi_chip_int = { .ops = &sdhci_acpi_ops_int, }; +static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev) +{ + struct sdhci_acpi_host *c = platform_get_drvdata(pdev); + struct sdhci_host *host; + + if (!c || !c->host) + return 0; + + host = c->host; + + /* Platform specific code during emmc proble slot goes here */ + + return 0; +} + +static int sdhci_acpi_sdio_probe_slot(struct platform_device *pdev) +{ + struct sdhci_acpi_host *c = platform_get_drvdata(pdev); + struct sdhci_host *host; + + if (!c || !c->host) + return 0; + + host = c->host; + + /* Platform specific code during emmc proble slot goes here */ + + return 0; +} + +static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev) +{ + struct sdhci_acpi_host *c = platform_get_drvdata(pdev); + struct sdhci_host *host; + + if (!c || !c->host || !c->slot) + return 0; + + host = c->host; + + /* Platform specific code during emmc proble slot goes here */ + + return 0; +} + static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = { .chip = &sdhci_acpi_chip_int, .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | @@ -129,6 +176,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = { .caps2 = MMC_CAP2_HC_ERASE_SZ, .flags = SDHCI_ACPI_RUNTIME_PM, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + .probe_slot = sdhci_acpi_emmc_probe_slot, }; static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { @@ -137,12 +185,14 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD, .flags = SDHCI_ACPI_RUNTIME_PM, .pm_caps = MMC_PM_KEEP_POWER, + .probe_slot = sdhci_acpi_sdio_probe_slot, }; static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = { .flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL | SDHCI_ACPI_RUNTIME_PM, .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON, + .probe_slot = sdhci_acpi_sd_probe_slot, }; struct sdhci_acpi_uid_slot { @@ -277,6 +327,11 @@ static int sdhci_acpi_probe(struct platform_device *pdev) } if (c->slot) { + if (c->slot->probe_slot) { + err = c->slot->probe_slot(pdev); + if (err) + goto err_free; + } if (c->slot->chip) { host->ops = c->slot->chip->ops; host->quirks |= c->slot->chip->quirks; @@ -334,6 +389,9 @@ static int sdhci_acpi_remove(struct platform_device *pdev) pm_runtime_put_noidle(dev); } + if (c->slot && c->slot->remove_slot) + c->slot->remove_slot(pdev); + dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0); sdhci_remove_host(c->host, dead); sdhci_free_host(c->host); -- cgit v1.2.3 From e73708190557911893ce4fb7a551ff5285e62ca2 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Tue, 2 Sep 2014 11:23:55 +0200 Subject: mmc: pxamci: prepare and unprepare the clocks Add the clock prepare and unprepare call to the driver set_ios calls phase. This will remove a warning once the PXA architecture is migrated to the clock infrastructure. Signed-off-by: Robert Jarzmik Signed-off-by: Ulf Hansson --- drivers/mmc/host/pxamci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 0971a234f449..1b6d0bfe35f5 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -474,7 +474,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) unsigned int clk = rate / ios->clock; if (host->clkrt == CLKRT_OFF) - clk_enable(host->clk); + clk_prepare_enable(host->clk); if (ios->clock == 26000000) { /* to support 26MHz */ @@ -501,7 +501,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) pxamci_stop_clock(host); if (host->clkrt != CLKRT_OFF) { host->clkrt = CLKRT_OFF; - clk_disable(host->clk); + clk_disable_unprepare(host->clk); } } -- cgit v1.2.3 From 312449efd16bb06a1e4fda94793d3eb8b8bb16f6 Mon Sep 17 00:00:00 2001 From: "Chuanxiao.Dong" Date: Fri, 15 Aug 2014 11:28:07 +0800 Subject: mmc: core: Fix sequence for I/O voltage in DDR mode for eMMC Even (e)MMC card can support 3.3v to 1.2v vccq in DDR, but not all host controller can support this, like some of the SDHCI host which connect to an eMMC device. Some of these host controller still needs to use 1.8v vccq for supporting DDR mode. So the sequence will be: if (host and device can both support 1.2v IO) use 1.2v IO; else if (host and device can both support 1.8v IO) use 1.8v IO; so if host and device can only support 3.3v IO, this is the last choice. Signed-off-by: Chuanxiao Dong Signed-off-by: Yunpeng Gao Tested-by: Jean-Michel Hautbois Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 31c43165f8bc..69e6273ed228 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -988,19 +988,35 @@ static int mmc_select_hs_ddr(struct mmc_card *card) * 1.8V vccq at 3.3V core voltage (vcc) is not required * in the JEDEC spec for DDR. * - * Do not force change in vccq since we are obviously - * working and no change to vccq is needed. + * Even (e)MMC card can support 3.3v to 1.2v vccq, but not all + * host controller can support this, like some of the SDHCI + * controller which connect to an eMMC device. Some of these + * host controller still needs to use 1.8v vccq for supporting + * DDR mode. + * + * So the sequence will be: + * if (host and device can both support 1.2v IO) + * use 1.2v IO; + * else if (host and device can both support 1.8v IO) + * use 1.8v IO; + * so if host and device can only support 3.3v IO, this is the + * last choice. * * WARNING: eMMC rules are NOT the same as SD DDR */ - if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V) { - err = __mmc_set_signal_voltage(host, - MMC_SIGNAL_VOLTAGE_120); - if (err) - return err; - } + err = -EINVAL; + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V) + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); - mmc_set_timing(host, MMC_TIMING_MMC_DDR52); + if (err && (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_8V)) + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); + + /* make sure vccq is 3.3v after switching disaster */ + if (err) + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330); + + if (!err) + mmc_set_timing(host, MMC_TIMING_MMC_DDR52); return err; } -- cgit v1.2.3 From adc828556dfc3f87a5c1338fc5412cbec3c0b529 Mon Sep 17 00:00:00 2001 From: "Chuanxiao.Dong" Date: Tue, 19 Aug 2014 11:02:41 +0800 Subject: mmc: sdhci: check 1.2v IO capability for SDHC host Right now enable 1.2v IO voltage for SDHC is by using vqmmc. Thus for the host which doesn't have vqmmc, or its vqmmc does not support 1.2v, directly use MMC_CAP2_HS200 may cause HS200 failure. So needs to check if vqmmc is able to support 1.2v. If it does not support, disable 1.2v IO for HS200. Signed-off-by: Chuanxiao Dong Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 335cdf3b5224..7481bd863502 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3037,8 +3037,13 @@ int sdhci_add_host(struct sdhci_host *host) /* SD3.0: SDR104 is supported so (for eMMC) the caps2 * field can be promoted to support HS200. */ - if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200)) + if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200)) { mmc->caps2 |= MMC_CAP2_HS200; + if (IS_ERR(mmc->supply.vqmmc) || + !regulator_is_supported_voltage + (mmc->supply.vqmmc, 1100000, 1300000)) + mmc->caps2 &= ~MMC_CAP2_HS200_1_2V_SDR; + } } else if (caps[1] & SDHCI_SUPPORT_SDR50) mmc->caps |= MMC_CAP_UHS_SDR50; -- cgit v1.2.3 From 2e47e84245adcb1b3872210678b6146f674fb3ff Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 2 Sep 2014 19:08:53 -0700 Subject: mmc: Add .multi_io_quirk callback for multi I/O HW bug Historically, we have been using MMC_CAP* to handle host HW issues and currently the block layer uses MMC_CAP2_NO_MULTI_READ flag for a multi I/O HW bug workaround. There are a few tweaks needed to make MMC_CAP2_NO_MULTI_READ suite all situations. Therefore let's add an optional host ops callback to enable host drivers to return the number of blocks it allows per request. In a future patch and when host drivers have converted to the new callback, MMC_CAP2_NO_MULTI_READ shall be removed. Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index ede41f05c392..adab9038f6f7 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1402,6 +1402,16 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, if (card->host->caps2 & MMC_CAP2_NO_MULTI_READ && rq_data_dir(req) == READ) brq->data.blocks = 1; + + /* + * Some controllers have HW issues while operating + * in multiple I/O mode + */ + if (card->host->ops->multi_io_quirk) + brq->data.blocks = card->host->ops->multi_io_quirk(card, + (rq_data_dir(req) == READ) ? + MMC_DATA_READ : MMC_DATA_WRITE, + brq->data.blocks); } if (brq->data.blocks > 1 || do_rel_wr) { -- cgit v1.2.3 From b7574bad80f773dcc4c17950181cde9fb6701501 Mon Sep 17 00:00:00 2001 From: "Gao, Yunpeng" Date: Thu, 4 Sep 2014 15:18:05 +0800 Subject: mmc: sdhci-pci: disable preset register for Baytrail and Merrifield Due to HW issue, SDHCI host controller on Intel Baytrail/Merrifield platforms can not use preset register. So, disable preset registers for them by quirks. Signed-off-by: Yunpeng Gao Signed-off-by: Chuanxiao Dong Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index ff9ab62e6eef..580073bc164c 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -287,13 +287,15 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = { }; static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { - .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON, + .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON | + SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .allow_runtime_pm = true, .probe_slot = byt_sdio_probe_slot, }; static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { - .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON, + .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | + SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .allow_runtime_pm = true, .own_cd_for_runtime_pm = true, }; @@ -317,7 +319,8 @@ static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot) static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = { .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, - .quirks2 = SDHCI_QUIRK2_BROKEN_HS200, + .quirks2 = SDHCI_QUIRK2_BROKEN_HS200 | + SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .allow_runtime_pm = true, .probe_slot = intel_mrfl_mmc_probe_slot, }; -- cgit v1.2.3 From afd8c29d254d7fddbae454f217fa5facefe8f5b0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 8 Sep 2014 23:44:51 -0700 Subject: mmc: use .multi_io_quirk on omap_hsmmc Now, omap_hsmmc can use .multi_io_quirk callback instead of MMC_CAP2_NO_MULTI_READ flags. let's use it. Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index ece163493a54..df27bb4fc098 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1829,7 +1829,17 @@ static int omap_hsmmc_disable_fclk(struct mmc_host *mmc) return 0; } -static const struct mmc_host_ops omap_hsmmc_ops = { +static int omap_hsmmc_multi_io_quirk(struct mmc_card *card, + unsigned int direction, int blk_size) +{ + /* This controller can't do multiblock reads due to hw bugs */ + if (direction == MMC_DATA_READ) + return 1; + + return blk_size; +} + +static struct mmc_host_ops omap_hsmmc_ops = { .enable = omap_hsmmc_enable_fclk, .disable = omap_hsmmc_disable_fclk, .post_req = omap_hsmmc_post_req, @@ -2101,7 +2111,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) { dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n"); - mmc->caps2 |= MMC_CAP2_NO_MULTI_READ; + omap_hsmmc_ops.multi_io_quirk = omap_hsmmc_multi_io_quirk; } pm_runtime_enable(host->dev); -- cgit v1.2.3 From bbf0208d39121bd8873b032459cb2b5f35e14593 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 8 Sep 2014 23:45:25 -0700 Subject: mmc: use .multi_io_quirk on tmio_mmc Now, tmio_mmc can use .multi_io_quirk callback instead of MMC_CAP2_NO_MULTI_READ flags. let's use it. Signed-off-by: Kuninori Morimoto Acked-by: Lee Jones Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_pio.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index ba454131f9a8..ff5ff0f725c9 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -970,12 +970,25 @@ static int tmio_mmc_get_ro(struct mmc_host *mmc) return ret; } +static int tmio_multi_io_quirk(struct mmc_card *card, + unsigned int direction, int blk_size) +{ + struct tmio_mmc_host *host = mmc_priv(card->host); + struct tmio_mmc_data *pdata = host->pdata; + + if (pdata->multi_io_quirk) + return pdata->multi_io_quirk(card, direction, blk_size); + + return blk_size; +} + static const struct mmc_host_ops tmio_mmc_ops = { .request = tmio_mmc_request, .set_ios = tmio_mmc_set_ios, .get_ro = tmio_mmc_get_ro, .get_cd = mmc_gpio_get_cd, .enable_sdio_irq = tmio_mmc_enable_sdio_irq, + .multi_io_quirk = tmio_multi_io_quirk, }; static int tmio_mmc_init_ocr(struct tmio_mmc_host *host) -- cgit v1.2.3 From 8b4efe2f58b1699a57f6c30d17345b801ab4213b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 8 Sep 2014 23:45:46 -0700 Subject: mmc: use .multi_io_quirk on sh_mobile Now, sh_mobile_sdhi can use .multi_io_quirk callback instead of MMC_CAP2_NO_MULTI_READ flags. let's use it. Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index f2585b6c1414..a2e81a1ea6af 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -58,7 +58,6 @@ static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_CLK_ACTUAL, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, - .capabilities2 = MMC_CAP2_NO_MULTI_READ, .dma_rx_offset = 0x2000, }; @@ -139,6 +138,24 @@ static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr) return 0; } +static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card, + unsigned int direction, int blk_size) +{ + /* + * In Renesas controllers, when performing a + * multiple block read of one or two blocks, + * depending on the timing with which the + * response register is read, the response + * value may not be read properly. + * Use single block read for this HW bug + */ + if ((direction == MMC_DATA_READ) && + blk_size == 2) + return 1; + + return blk_size; +} + static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev) { mmc_detect_change(platform_get_drvdata(pdev), msecs_to_jiffies(100)); @@ -194,6 +211,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; mmc_data->write16_hook = sh_mobile_sdhi_write16_hook; + mmc_data->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk; if (p) { mmc_data->flags = p->tmio_flags; mmc_data->ocr_mask = p->tmio_ocr_mask; -- cgit v1.2.3 From 0abb71feb228ddbd17e0dfa13216541e036bb549 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 8 Sep 2014 23:46:49 -0700 Subject: mmc: remove MMC_CAP2_NO_MULTI_READ flags Now, mmc framework uses multi_io_quirk for I/O HW bug workaround. MMC_CAP2_NO_MULTI_READ flag is no longer needed Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index adab9038f6f7..413f984cb76d 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1398,11 +1398,6 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, if (disable_multi) brq->data.blocks = 1; - /* Some controllers can't do multiblock reads due to hw bugs */ - if (card->host->caps2 & MMC_CAP2_NO_MULTI_READ && - rq_data_dir(req) == READ) - brq->data.blocks = 1; - /* * Some controllers have HW issues while operating * in multiple I/O mode -- cgit v1.2.3 From f5b4d71f72c5f08c2e1d0af68ef881f85537e7a1 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 3 Sep 2014 11:02:23 +0200 Subject: mmc: card: Prevent partition scan for the eMMC boot areas It seems very unlikely that eMMC devices would hold a standard partitiontable in one of it's boot areas. Therefore, let's prevent them from being scanned. Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 413f984cb76d..e248b960e62c 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2136,7 +2136,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, md->disk->queue = md->queue.queue; md->disk->driverfs_dev = parent; set_disk_ro(md->disk, md->read_only || default_ro); - if (area_type & MMC_BLK_DATA_AREA_RPMB) + if (area_type & (MMC_BLK_DATA_AREA_RPMB | MMC_BLK_DATA_AREA_BOOT)) md->disk->flags |= GENHD_FL_NO_PART_SCAN; /* -- cgit v1.2.3 From 9fbc695075e905b9201100860eacac6349db6644 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 27 Aug 2014 13:00:50 +0200 Subject: mmc: slot-gpio: switch to use flags when getting GPIO When the slot GPIO driver gets the GPIO to be used for card detect, it is now possible to specify a flag to have the line set up as input. Get rid of the explicit setup call for input and use the flag. The extra argument works as there are transition varargs macros in place in the header, in the future we will make the flags argument compulsory. Reviewed-by: Alexandre Courbot Signed-off-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/core/slot-gpio.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 5f89cb83d5f0..908c2b29e79f 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -308,14 +308,10 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, if (!con_id) con_id = ctx->cd_label; - desc = devm_gpiod_get_index(host->parent, con_id, idx); + desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN); if (IS_ERR(desc)) return PTR_ERR(desc); - ret = gpiod_direction_input(desc); - if (ret < 0) - return ret; - if (debounce) { ret = gpiod_set_debounce(desc, debounce); if (ret < 0) -- cgit v1.2.3 From 9d2fa2428ae149ba3a5b7a4ceb0a9e11f1882b3b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 27 Aug 2014 13:00:51 +0200 Subject: mmc: slot-gpio: add gpiod variant to get wp GPIO This makes it possible to get the write protect (read only) GPIO line from a GPIO descriptor. Written to exactly mirror the card detect function. Acked-by: Alexandre Courbot Signed-off-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/core/slot-gpio.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 908c2b29e79f..e3fce4493fab 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -325,6 +325,54 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, } EXPORT_SYMBOL(mmc_gpiod_request_cd); +/** + * mmc_gpiod_request_ro - request a gpio descriptor for write protection + * @host: mmc host + * @con_id: function within the GPIO consumer + * @idx: index of the GPIO to obtain in the consumer + * @override_active_level: ignore %GPIO_ACTIVE_LOW flag + * @debounce: debounce time in microseconds + * + * Use this function in place of mmc_gpio_request_ro() to use the GPIO + * descriptor API. Note that it is paired with mmc_gpiod_free_ro() not + * mmc_gpio_free_ro(). + * + * Returns zero on success, else an error. + */ +int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, + unsigned int idx, bool override_active_level, + unsigned int debounce) +{ + struct mmc_gpio *ctx; + struct gpio_desc *desc; + int ret; + + ret = mmc_gpio_alloc(host); + if (ret < 0) + return ret; + + ctx = host->slot.handler_priv; + + if (!con_id) + con_id = ctx->ro_label; + + desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + if (debounce) { + ret = gpiod_set_debounce(desc, debounce); + if (ret < 0) + return ret; + } + + ctx->override_ro_active_level = override_active_level; + ctx->ro_gpio = desc; + + return 0; +} +EXPORT_SYMBOL(mmc_gpiod_request_ro); + /** * mmc_gpiod_free_cd - free the card-detection gpio descriptor * @host: mmc host -- cgit v1.2.3 From 98e90de99a0c43bd434da814c882c4332441871e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 27 Aug 2014 13:00:52 +0200 Subject: mmc: host: switch OF parser to use gpio descriptors This switches the central MMC OF parser to use gpio descriptors instead of grabbing GPIOs explicitly from the device tree. This strips out an unecessary use of the integer-based GPIO API that we want to get rid of, cuts down on code as the gpio descriptor code will handle active low flags. Acked-by: Alexandre Courbot Signed-off-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 68 +++++++++++++++++-------------------------------- 1 file changed, 23 insertions(+), 45 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index d572b2beb65a..31969436d77c 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -310,9 +310,7 @@ int mmc_of_parse(struct mmc_host *host) { struct device_node *np; u32 bus_width; - bool explicit_inv_wp, gpio_inv_wp = false; - enum of_gpio_flags flags; - int len, ret, gpio; + int len, ret; if (!host->parent || !host->parent->of_node) return 0; @@ -360,60 +358,40 @@ int mmc_of_parse(struct mmc_host *host) if (of_find_property(np, "non-removable", &len)) { host->caps |= MMC_CAP_NONREMOVABLE; } else { - bool explicit_inv_cd, gpio_inv_cd = false; - - explicit_inv_cd = of_property_read_bool(np, "cd-inverted"); + if (of_property_read_bool(np, "cd-inverted")) + host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; if (of_find_property(np, "broken-cd", &len)) host->caps |= MMC_CAP_NEEDS_POLL; - gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags); - if (gpio == -EPROBE_DEFER) - return gpio; - if (gpio_is_valid(gpio)) { - if (!(flags & OF_GPIO_ACTIVE_LOW)) - gpio_inv_cd = true; - - ret = mmc_gpio_request_cd(host, gpio, 0); - if (ret < 0) { - dev_err(host->parent, - "Failed to request CD GPIO #%d: %d!\n", - gpio, ret); + ret = mmc_gpiod_request_cd(host, "cd", 0, false, 0); + if (ret) { + if (ret == -EPROBE_DEFER) return ret; - } else { - dev_info(host->parent, "Got CD GPIO #%d.\n", - gpio); + if (ret != -ENOENT) { + dev_err(host->parent, + "Failed to request CD GPIO: %d\n", + ret); } - } - - if (explicit_inv_cd ^ gpio_inv_cd) - host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; + } else + dev_info(host->parent, "Got CD GPIO\n"); } /* Parse Write Protection */ - explicit_inv_wp = of_property_read_bool(np, "wp-inverted"); - - gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags); - if (gpio == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - goto out; - } - if (gpio_is_valid(gpio)) { - if (!(flags & OF_GPIO_ACTIVE_LOW)) - gpio_inv_wp = true; + if (of_property_read_bool(np, "wp-inverted")) + host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; - ret = mmc_gpio_request_ro(host, gpio); - if (ret < 0) { - dev_err(host->parent, - "Failed to request WP GPIO: %d!\n", ret); + ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0); + if (ret) { + if (ret == -EPROBE_DEFER) goto out; - } else { - dev_info(host->parent, "Got WP GPIO #%d.\n", - gpio); + if (ret != -ENOENT) { + dev_err(host->parent, + "Failed to request WP GPIO: %d\n", + ret); } - } - if (explicit_inv_wp ^ gpio_inv_wp) - host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; + } else + dev_info(host->parent, "Got WP GPIO\n"); if (of_find_property(np, "cap-sd-highspeed", &len)) host->caps |= MMC_CAP_SD_HIGHSPEED; -- cgit v1.2.3 From ce437aa4519c46a0f12455c320b6f65dde16c26e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 27 Aug 2014 15:13:54 +0200 Subject: mmc: mmci: augment driver to handle gpio descriptors Currently the MMCI driver will only handle GPIO descriptors implicitly through the device tree probe glue in mmc_of_init(), but devices instatiated other ways such as through board files and passing descriptors using the GPIO descriptor table will not be able to exploit descriptors. Augment the driver to look for a GPIO descriptor if device tree is not used for the device, and if that doesn't work, fall back to platform data GPIO assignment using the old API. The end goal is to get rid of the platform data integer GPIO assingments from the kernel. This enable the MMCI-embedding platforms to be converted to GPIO descritor tables. Cc: Alexandre Courbot Cc: Russell King Signed-off-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index a7b3af9e9a2a..c85b0d8ee727 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1674,16 +1674,35 @@ static int mmci_probe(struct amba_device *dev, writel(0, host->base + MMCIMASK1); writel(0xfff, host->base + MMCICLEAR); - /* If DT, cd/wp gpios must be supplied through it. */ - if (!np && gpio_is_valid(plat->gpio_cd)) { - ret = mmc_gpio_request_cd(mmc, plat->gpio_cd, 0); - if (ret) - goto clk_disable; - } - if (!np && gpio_is_valid(plat->gpio_wp)) { - ret = mmc_gpio_request_ro(mmc, plat->gpio_wp); - if (ret) - goto clk_disable; + /* + * If: + * - not using DT but using a descriptor table, or + * - using a table of descriptors ALONGSIDE DT, or + * look up these descriptors named "cd" and "wp" right here, fail + * silently of these do not exist and proceed to try platform data + */ + if (!np) { + ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0); + if (ret < 0) { + if (ret == -EPROBE_DEFER) + goto clk_disable; + else if (gpio_is_valid(plat->gpio_cd)) { + ret = mmc_gpio_request_cd(mmc, plat->gpio_cd, 0); + if (ret) + goto clk_disable; + } + } + + ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0); + if (ret < 0) { + if (ret == -EPROBE_DEFER) + goto clk_disable; + else if (gpio_is_valid(plat->gpio_wp)) { + ret = mmc_gpio_request_ro(mmc, plat->gpio_wp); + if (ret) + goto clk_disable; + } + } } ret = devm_request_irq(&dev->dev, dev->irq[0], mmci_irq, IRQF_SHARED, -- cgit v1.2.3 From 5df014df12b1178e0b76b7273913ce1531ecf20e Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 22 Aug 2014 05:54:55 +0100 Subject: mmc: mmci: Add sdio enable mask in variant data This patch adds sdio enable mask in variant data, SOCs like ST have special bits in datactrl register to enable sdio. Unconditionally setting this bit in this driver breaks other SOCs like Qualcomm which maps this bits to something else, so making this enable bit to come from variant data solves the issue. Originally the issue is detected while testing WLAN ath6kl on Qualcomm APQ8064. Reviewed-by: Linus Walleij Signed-off-by: Srinivas Kandagatla Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index c85b0d8ee727..370cd5ad111c 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -67,6 +67,7 @@ static unsigned int fmax = 515633; * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl * register + * @datactrl_mask_sdio: SDIO enable mask in datactrl register * @pwrreg_powerup: power up value for MMCIPOWER register * @f_max: maximum clk frequency supported by the controller. * @signal_direction: input/out direction of bus signals can be indicated @@ -88,6 +89,7 @@ struct variant_data { unsigned int fifohalfsize; unsigned int data_cmd_enable; unsigned int datactrl_mask_ddrmode; + unsigned int datactrl_mask_sdio; bool sdio; bool st_clkdiv; bool blksz_datactrl16; @@ -136,6 +138,7 @@ static struct variant_data variant_u300 = { .clkreg_enable = MCI_ST_U300_HWFCEN, .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, .datalength_bits = 16, + .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN, .sdio = true, .pwrreg_powerup = MCI_PWR_ON, .f_max = 100000000, @@ -149,6 +152,7 @@ static struct variant_data variant_nomadik = { .fifohalfsize = 8 * 4, .clkreg = MCI_CLK_ENABLE, .datalength_bits = 24, + .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN, .sdio = true, .st_clkdiv = true, .pwrreg_powerup = MCI_PWR_ON, @@ -166,6 +170,7 @@ static struct variant_data variant_ux500 = { .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE, .datalength_bits = 24, + .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN, .sdio = true, .st_clkdiv = true, .pwrreg_powerup = MCI_PWR_ON, @@ -185,6 +190,7 @@ static struct variant_data variant_ux500v2 = { .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE, .datactrl_mask_ddrmode = MCI_ST_DPSM_DDRMODE, .datalength_bits = 24, + .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN, .sdio = true, .st_clkdiv = true, .blksz_datactrl16 = true, @@ -808,16 +814,10 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) if (data->flags & MMC_DATA_READ) datactrl |= MCI_DPSM_DIRECTION; - /* The ST Micro variants has a special bit to enable SDIO */ if (variant->sdio && host->mmc->card) if (mmc_card_sdio(host->mmc->card)) { - /* - * The ST Micro variants has a special bit - * to enable SDIO. - */ u32 clk; - - datactrl |= MCI_ST_DPSM_SDIOEN; + datactrl |= variant->datactrl_mask_sdio; /* * The ST Micro variant for SDIO small write transfers -- cgit v1.2.3 From c73541336389f9fd72f592601c40a2e70320cf31 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 22 Aug 2014 05:55:16 +0100 Subject: mmc: mmci: rename sdio flag in vendor data to st_sdio This patch renames sdio flag in vendor data to st_sdio, as this flag is only used to enable ST specific sdio setup. This will also ensure that the ST specfic setup is not done on other vendor like Qualcomm. Originally the issue was detected while testing WLAN ath6kl on IFC6410 board with APQ8064 SOC. Signed-off-by: Srinivas Kandagatla Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 370cd5ad111c..c9dafed550f2 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -61,7 +61,7 @@ static unsigned int fmax = 515633; * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY * is asserted (likewise for RX) * @data_cmd_enable: enable value for data commands. - * @sdio: variant supports SDIO + * @st_sdio: enable ST specific SDIO logic * @st_clkdiv: true if using a ST-specific clock divider algorithm * @datactrl_mask_ddrmode: ddr mode mask in datactrl register. * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register @@ -90,7 +90,7 @@ struct variant_data { unsigned int data_cmd_enable; unsigned int datactrl_mask_ddrmode; unsigned int datactrl_mask_sdio; - bool sdio; + bool st_sdio; bool st_clkdiv; bool blksz_datactrl16; bool blksz_datactrl4; @@ -139,7 +139,7 @@ static struct variant_data variant_u300 = { .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, .datalength_bits = 16, .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN, - .sdio = true, + .st_sdio = true, .pwrreg_powerup = MCI_PWR_ON, .f_max = 100000000, .signal_direction = true, @@ -153,7 +153,7 @@ static struct variant_data variant_nomadik = { .clkreg = MCI_CLK_ENABLE, .datalength_bits = 24, .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN, - .sdio = true, + .st_sdio = true, .st_clkdiv = true, .pwrreg_powerup = MCI_PWR_ON, .f_max = 100000000, @@ -171,7 +171,7 @@ static struct variant_data variant_ux500 = { .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE, .datalength_bits = 24, .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN, - .sdio = true, + .st_sdio = true, .st_clkdiv = true, .pwrreg_powerup = MCI_PWR_ON, .f_max = 100000000, @@ -191,7 +191,7 @@ static struct variant_data variant_ux500v2 = { .datactrl_mask_ddrmode = MCI_ST_DPSM_DDRMODE, .datalength_bits = 24, .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN, - .sdio = true, + .st_sdio = true, .st_clkdiv = true, .blksz_datactrl16 = true, .pwrreg_powerup = MCI_PWR_ON, @@ -814,26 +814,26 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) if (data->flags & MMC_DATA_READ) datactrl |= MCI_DPSM_DIRECTION; - if (variant->sdio && host->mmc->card) - if (mmc_card_sdio(host->mmc->card)) { - u32 clk; - datactrl |= variant->datactrl_mask_sdio; + if (host->mmc->card && mmc_card_sdio(host->mmc->card)) { + u32 clk; - /* - * The ST Micro variant for SDIO small write transfers - * needs to have clock H/W flow control disabled, - * otherwise the transfer will not start. The threshold - * depends on the rate of MCLK. - */ - if (data->flags & MMC_DATA_WRITE && - (host->size < 8 || - (host->size <= 8 && host->mclk > 50000000))) - clk = host->clk_reg & ~variant->clkreg_enable; - else - clk = host->clk_reg | variant->clkreg_enable; + datactrl |= variant->datactrl_mask_sdio; - mmci_write_clkreg(host, clk); - } + /* + * The ST Micro variant for SDIO small write transfers + * needs to have clock H/W flow control disabled, + * otherwise the transfer will not start. The threshold + * depends on the rate of MCLK. + */ + if (variant->st_sdio && data->flags & MMC_DATA_WRITE && + (host->size < 8 || + (host->size <= 8 && host->mclk > 50000000))) + clk = host->clk_reg & ~variant->clkreg_enable; + else + clk = host->clk_reg | variant->clkreg_enable; + + mmci_write_clkreg(host, clk); + } if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 || host->mmc->ios.timing == MMC_TIMING_MMC_DDR52) -- cgit v1.2.3 From 87d2163dae1f2388c7ccda5269be8d58e24382dd Mon Sep 17 00:00:00 2001 From: Xiang Wang Date: Wed, 16 Jul 2014 15:50:09 +0800 Subject: mmc: sdhci-pxav3: fix error handling of sdhci_add_host Commit 0dcaa2499b7d111bd70da5b0976c34210c850fb3 improved error handling of sdhci_add_host. However, "err_of_parse" and "err_cd_req" should be placed after "pm_runtime_disable(&pdev->dev)". Signed-off-by: Xiang Wang Acked-by: Laurent Pinchart Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 46c94d88fa93..5036d7d39529 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -380,11 +380,11 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) return 0; -err_of_parse: -err_cd_req: err_add_host: pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); +err_of_parse: +err_cd_req: clk_disable_unprepare(clk); err_clk_get: err_mbus_win: -- cgit v1.2.3 From b4493eea4a6561bd9fa57aaa2f7d2b5ea31a1f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Mon, 15 Sep 2014 17:47:06 +0200 Subject: mmc: Move code that manages user area and gp partitions into functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move code that manages user area and general purpose partitions into functions. Signed-off-by: Grégory Soutadé Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 162 +++++++++++++++++++++++++++---------------------- 1 file changed, 89 insertions(+), 73 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 69e6273ed228..5b4b969c18bf 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -299,6 +299,93 @@ static void mmc_select_card_type(struct mmc_card *card) card->mmc_avail_type = avail_type; } +static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd) +{ + u8 hc_erase_grp_sz = 0, hc_wp_grp_sz = 0; + + /* + * Enhanced area feature support -- check whether the eMMC + * card has the Enhanced area enabled. If so, export enhanced + * area offset and size to user by adding sysfs interface. + */ + if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) && + (ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) { + hc_erase_grp_sz = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + hc_wp_grp_sz = + ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + + card->ext_csd.enhanced_area_en = 1; + /* + * calculate the enhanced data area offset, in bytes + */ + card->ext_csd.enhanced_area_offset = + (ext_csd[139] << 24) + (ext_csd[138] << 16) + + (ext_csd[137] << 8) + ext_csd[136]; + if (mmc_card_blockaddr(card)) + card->ext_csd.enhanced_area_offset <<= 9; + /* + * calculate the enhanced data area size, in kilobytes + */ + card->ext_csd.enhanced_area_size = + (ext_csd[142] << 16) + (ext_csd[141] << 8) + + ext_csd[140]; + card->ext_csd.enhanced_area_size *= + (size_t)(hc_erase_grp_sz * hc_wp_grp_sz); + card->ext_csd.enhanced_area_size <<= 9; + } else { + /* + * If the enhanced area is not enabled, disable these + * device attributes. + */ + card->ext_csd.enhanced_area_offset = -EINVAL; + card->ext_csd.enhanced_area_size = -EINVAL; + } +} + +static void mmc_manage_gp_partitions(struct mmc_card *card, u8 *ext_csd) +{ + unsigned int part_size; + u8 hc_erase_grp_sz = 0, hc_wp_grp_sz = 0; + int idx; + + /* + * General purpose partition feature support -- + * If ext_csd has the size of general purpose partitions, + * set size, part_cfg, partition name in mmc_part. + */ + if (ext_csd[EXT_CSD_PARTITION_SUPPORT] & + EXT_CSD_PART_SUPPORT_PART_EN) { + if (card->ext_csd.enhanced_area_en != 1) { + hc_erase_grp_sz = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + hc_wp_grp_sz = + ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + + card->ext_csd.enhanced_area_en = 1; + } + + for (idx = 0; idx < MMC_NUM_GP_PARTITION; idx++) { + if (!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3] && + !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] && + !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2]) + continue; + part_size = + (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2] + << 16) + + (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] + << 8) + + ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3]; + part_size *= (size_t)(hc_erase_grp_sz * + hc_wp_grp_sz); + mmc_part_add(card, part_size << 19, + EXT_CSD_PART_CONFIG_ACC_GP0 + idx, + "gp%d", idx, false, + MMC_BLK_DATA_AREA_GP); + } + } +} + /* * Decode extended CSD. */ @@ -306,7 +393,6 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) { int err = 0, idx; unsigned int part_size; - u8 hc_erase_grp_sz = 0, hc_wp_grp_sz = 0; BUG_ON(!card); @@ -403,80 +489,10 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) ext_csd[EXT_CSD_TRIM_MULT]; card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT]; if (card->ext_csd.rev >= 4) { - /* - * Enhanced area feature support -- check whether the eMMC - * card has the Enhanced area enabled. If so, export enhanced - * area offset and size to user by adding sysfs interface. - */ - if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) && - (ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) { - hc_erase_grp_sz = - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; - hc_wp_grp_sz = - ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + mmc_manage_enhanced_area(card, ext_csd); - card->ext_csd.enhanced_area_en = 1; - /* - * calculate the enhanced data area offset, in bytes - */ - card->ext_csd.enhanced_area_offset = - (ext_csd[139] << 24) + (ext_csd[138] << 16) + - (ext_csd[137] << 8) + ext_csd[136]; - if (mmc_card_blockaddr(card)) - card->ext_csd.enhanced_area_offset <<= 9; - /* - * calculate the enhanced data area size, in kilobytes - */ - card->ext_csd.enhanced_area_size = - (ext_csd[142] << 16) + (ext_csd[141] << 8) + - ext_csd[140]; - card->ext_csd.enhanced_area_size *= - (size_t)(hc_erase_grp_sz * hc_wp_grp_sz); - card->ext_csd.enhanced_area_size <<= 9; - } else { - /* - * If the enhanced area is not enabled, disable these - * device attributes. - */ - card->ext_csd.enhanced_area_offset = -EINVAL; - card->ext_csd.enhanced_area_size = -EINVAL; - } + mmc_manage_gp_partitions(card, ext_csd); - /* - * General purpose partition feature support -- - * If ext_csd has the size of general purpose partitions, - * set size, part_cfg, partition name in mmc_part. - */ - if (ext_csd[EXT_CSD_PARTITION_SUPPORT] & - EXT_CSD_PART_SUPPORT_PART_EN) { - if (card->ext_csd.enhanced_area_en != 1) { - hc_erase_grp_sz = - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; - hc_wp_grp_sz = - ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; - - card->ext_csd.enhanced_area_en = 1; - } - - for (idx = 0; idx < MMC_NUM_GP_PARTITION; idx++) { - if (!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3] && - !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] && - !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2]) - continue; - part_size = - (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2] - << 16) + - (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] - << 8) + - ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3]; - part_size *= (size_t)(hc_erase_grp_sz * - hc_wp_grp_sz); - mmc_part_add(card, part_size << 19, - EXT_CSD_PART_CONFIG_ACC_GP0 + idx, - "gp%d", idx, false, - MMC_BLK_DATA_AREA_GP); - } - } card->ext_csd.sec_trim_mult = ext_csd[EXT_CSD_SEC_TRIM_MULT]; card->ext_csd.sec_erase_mult = -- cgit v1.2.3 From 69803d4f487fc60ce740f1fe1f0d2092d97277b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Mon, 15 Sep 2014 17:47:09 +0200 Subject: mmc: Replace "enhanced_area_en" attribute by "partition_setting_completed" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace ext_csd "enhanced_area_en" attribute by "partition_setting_completed". It was used whether or not enhanced user area is defined and without checks of EXT_CSD_PARTITION_SETTING_COMPLETED bit. Signed-off-by: Grégory Soutadé Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 5b4b969c18bf..eb78fcd01e52 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -315,7 +315,6 @@ static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd) hc_wp_grp_sz = ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; - card->ext_csd.enhanced_area_en = 1; /* * calculate the enhanced data area offset, in bytes */ @@ -356,13 +355,11 @@ static void mmc_manage_gp_partitions(struct mmc_card *card, u8 *ext_csd) */ if (ext_csd[EXT_CSD_PARTITION_SUPPORT] & EXT_CSD_PART_SUPPORT_PART_EN) { - if (card->ext_csd.enhanced_area_en != 1) { + if (card->ext_csd.partition_setting_completed) { hc_erase_grp_sz = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; hc_wp_grp_sz = ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; - - card->ext_csd.enhanced_area_en = 1; } for (idx = 0; idx < MMC_NUM_GP_PARTITION; idx++) { @@ -489,6 +486,12 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) ext_csd[EXT_CSD_TRIM_MULT]; card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT]; if (card->ext_csd.rev >= 4) { + if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED] & + EXT_CSD_PART_SETTING_COMPLETED) + card->ext_csd.partition_setting_completed = 1; + else + card->ext_csd.partition_setting_completed = 0; + mmc_manage_enhanced_area(card, ext_csd); mmc_manage_gp_partitions(card, ext_csd); @@ -1348,7 +1351,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * If enhanced_area_en is TRUE, host needs to enable ERASE_GRP_DEF * bit. This bit will be lost every time after a reset or power off. */ - if (card->ext_csd.enhanced_area_en || + if (card->ext_csd.partition_setting_completed || (card->ext_csd.rev >= 3 && (host->caps2 & MMC_CAP2_HC_ERASE_SZ))) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_ERASE_GROUP_DEF, 1, -- cgit v1.2.3 From 994324bbabc7e9dce75322bbf839b846aca8e1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Mon, 15 Sep 2014 17:47:11 +0200 Subject: mmc: Checks EXT_CSD_PARTITION_SETTING_COMPLETED before partitions computation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checks EXT_CSD_PARTITION_SETTING_COMPLETED bit before computing enhanced user area offset and size, and adding mmc general purpose partitions. The two needs EXT_CSD_PARTITION_SETTING_COMPLETED bit be set to be valid (as described in JEDEC standard). Warn user in case of misconfiguration. Signed-off-by: Grégory Soutadé Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 81 +++++++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 37 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index eb78fcd01e52..ce11d89aa305 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -301,7 +301,13 @@ static void mmc_select_card_type(struct mmc_card *card) static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd) { - u8 hc_erase_grp_sz = 0, hc_wp_grp_sz = 0; + u8 hc_erase_grp_sz, hc_wp_grp_sz; + + /* + * Disable these attributes by default + */ + card->ext_csd.enhanced_area_offset = -EINVAL; + card->ext_csd.enhanced_area_size = -EINVAL; /* * Enhanced area feature support -- check whether the eMMC @@ -310,43 +316,41 @@ static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd) */ if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) && (ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) { - hc_erase_grp_sz = - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; - hc_wp_grp_sz = - ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + if (card->ext_csd.partition_setting_completed) { + hc_erase_grp_sz = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + hc_wp_grp_sz = + ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; - /* - * calculate the enhanced data area offset, in bytes - */ - card->ext_csd.enhanced_area_offset = - (ext_csd[139] << 24) + (ext_csd[138] << 16) + - (ext_csd[137] << 8) + ext_csd[136]; - if (mmc_card_blockaddr(card)) - card->ext_csd.enhanced_area_offset <<= 9; - /* - * calculate the enhanced data area size, in kilobytes - */ - card->ext_csd.enhanced_area_size = - (ext_csd[142] << 16) + (ext_csd[141] << 8) + - ext_csd[140]; - card->ext_csd.enhanced_area_size *= - (size_t)(hc_erase_grp_sz * hc_wp_grp_sz); - card->ext_csd.enhanced_area_size <<= 9; - } else { - /* - * If the enhanced area is not enabled, disable these - * device attributes. - */ - card->ext_csd.enhanced_area_offset = -EINVAL; - card->ext_csd.enhanced_area_size = -EINVAL; + /* + * calculate the enhanced data area offset, in bytes + */ + card->ext_csd.enhanced_area_offset = + (ext_csd[139] << 24) + (ext_csd[138] << 16) + + (ext_csd[137] << 8) + ext_csd[136]; + if (mmc_card_blockaddr(card)) + card->ext_csd.enhanced_area_offset <<= 9; + /* + * calculate the enhanced data area size, in kilobytes + */ + card->ext_csd.enhanced_area_size = + (ext_csd[142] << 16) + (ext_csd[141] << 8) + + ext_csd[140]; + card->ext_csd.enhanced_area_size *= + (size_t)(hc_erase_grp_sz * hc_wp_grp_sz); + card->ext_csd.enhanced_area_size <<= 9; + } else { + pr_warn("%s: defines enhanced area without partition setting complete\n", + mmc_hostname(card->host)); + } } } static void mmc_manage_gp_partitions(struct mmc_card *card, u8 *ext_csd) { - unsigned int part_size; - u8 hc_erase_grp_sz = 0, hc_wp_grp_sz = 0; int idx; + u8 hc_erase_grp_sz, hc_wp_grp_sz; + unsigned int part_size; /* * General purpose partition feature support -- @@ -355,18 +359,21 @@ static void mmc_manage_gp_partitions(struct mmc_card *card, u8 *ext_csd) */ if (ext_csd[EXT_CSD_PARTITION_SUPPORT] & EXT_CSD_PART_SUPPORT_PART_EN) { - if (card->ext_csd.partition_setting_completed) { - hc_erase_grp_sz = - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; - hc_wp_grp_sz = - ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; - } + hc_erase_grp_sz = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + hc_wp_grp_sz = + ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; for (idx = 0; idx < MMC_NUM_GP_PARTITION; idx++) { if (!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3] && !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] && !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2]) continue; + if (card->ext_csd.partition_setting_completed == 0) { + pr_warn("%s: has partition size defined without partition complete\n", + mmc_hostname(card->host)); + break; + } part_size = (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2] << 16) + -- cgit v1.2.3 From 7e1fc19b29e925d4e567a708625dad1f0fb8bf16 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 16 Sep 2014 09:54:55 +0200 Subject: mmc: dw_mmc: Remove unused function for !CONFIG_OF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dw_mci_of_find_slot_node() is only used in dw_mci_of_get_slot_quirks() if CONFIG_OF is defined, thus there is no need to have a !CONFIG_OF version of it. Fixes the following compile warning with !CONFIG_OF: CC [M] drivers/mmc/host/dw_mmc.o drivers/mmc/host/dw_mmc.c:2223:28: warning: ‘dw_mci_of_find_slot_node’ defined but not used [-Wunused-function] Signed-off-by: Tobias Klauser Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 835e913cafb7..109fefeb9597 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2220,10 +2220,6 @@ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot) { return 0; } -static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot) -{ - return NULL; -} #endif /* CONFIG_OF */ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) -- cgit v1.2.3 From 5e1344eb3f4804d2d50195e197fcbcdbd60ad049 Mon Sep 17 00:00:00 2001 From: Johan Rudholm Date: Wed, 17 Sep 2014 09:50:42 +0200 Subject: mmc: block: change stop errors to info Stop command errors are not fatal to the transfer since we make sure that the card returns to the transfer state and check the card status. Change an unnecessary error to an info. Signed-off-by: Johan Rudholm Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index e248b960e62c..5d2799730a32 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -977,7 +977,7 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req, return ERR_CONTINUE; /* Now for stop errors. These aren't fatal to the transfer. */ - pr_err("%s: error %d sending stop command, original cmd response %#x, card status %#x\n", + pr_info("%s: error %d sending stop command, original cmd response %#x, card status %#x\n", req->rq_disk->disk_name, brq->stop.error, brq->cmd.resp[0], status); -- cgit v1.2.3 From b9bd7ff8069a7125da897de657da421c497d4f15 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 10 Sep 2014 00:23:24 -0700 Subject: mmc: tmio: enable odd number size access Current tmio is using sd_ctrl_read16/write16_rep() for data transfer. It works if transfer size was even number, but, last 1 byte will be ignored if transfer size was odd number. This patch adds new tmio_mmc_transfer_data() and solve this issue. Tested-by: Shinobu Uehara Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_pio.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index ff5ff0f725c9..692e578cf4d7 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -376,6 +376,40 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command return 0; } +static void tmio_mmc_transfer_data(struct tmio_mmc_host *host, + unsigned short *buf, + unsigned int count) +{ + int is_read = host->data->flags & MMC_DATA_READ; + u8 *buf8; + + /* + * Transfer the data + */ + if (is_read) + sd_ctrl_read16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1); + else + sd_ctrl_write16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1); + + /* if count was even number */ + if (!(count & 0x1)) + return; + + /* if count was odd number */ + buf8 = (u8 *)(buf + (count >> 1)); + + /* + * FIXME + * + * driver and this function are assuming that + * it is used as little endian + */ + if (is_read) + *buf8 = sd_ctrl_read16(host, CTL_SD_DATA_PORT) & 0xff; + else + sd_ctrl_write16(host, CTL_SD_DATA_PORT, *buf8); +} + /* * This chip always returns (at least?) as much data as you ask for. * I'm unsure what happens if you ask for less than a block. This should be @@ -408,10 +442,7 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host) count, host->sg_off, data->flags); /* Transfer the data */ - if (data->flags & MMC_DATA_READ) - sd_ctrl_read16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1); - else - sd_ctrl_write16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1); + tmio_mmc_transfer_data(host, buf, count); host->sg_off += count; -- cgit v1.2.3 From bb98d9d1d2e791f368295c9c09c9a03fe3ac0e62 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Thu, 18 Sep 2014 23:33:49 +0400 Subject: mmc: tmio: prevent endless loop in tmio_mmc_set_clock() I spent a couple of days with the driver just hanging due to me forgetting to specify the external crystal frequency, so that clk_get_rate() returned 0 and thus the loop in tmio_mmc_set_clock() never ended. I don't think that's an acceptable behavior, so I suggest that the minimum frequency is checked for 0 in tmio_mmc_host_probe(). Signed-off-by: Sergei Shtylyov Acked-by: Ian Molton Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_pio.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 692e578cf4d7..e487ba4bb01a 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1121,6 +1121,15 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc->f_min = mmc->f_max / 512; } + /* + * Check the sanity of mmc->f_min to prevent tmio_mmc_set_clock() from + * looping forever... + */ + if (mmc->f_min == 0) { + ret = -EINVAL; + goto host_free; + } + /* * While using internal tmio hardware logic for card detection, we need * to ensure it stays powered for it to work. -- cgit v1.2.3 From d4d11449088ee9aca16fd1884b852b8b73a4bda1 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 22 Sep 2014 09:57:42 -0600 Subject: mmc: don't request CD IRQ until mmc_start_host() As soon as the CD IRQ is requested, it can trigger, since it's an externally controlled event. If it does, delayed_work host->detect will be scheduled. Many host controller probe()s are roughly structured as: *_probe() { host = sdhci_pltfm_init(); mmc_of_parse(host->mmc); rc = sdhci_add_host(host); if (rc) { sdhci_pltfm_free(); return rc; } In 3.17, CD IRQs can are enabled quite early via *_probe() -> mmc_of_parse() -> mmc_gpio_request_cd() -> mmc_gpiod_request_cd_irq(). Note that in linux-next, mmc_of_parse() calls mmc_gpio*d*_request_cd() rather than mmc_gpio_request_cd(), and mmc_gpio*d*_request_cd() doesn't call mmc_gpiod_request_cd_irq(). However, this issue still exists if mmc_gpio_request_cd() is called directly before mmc_start_host(). sdhci_add_host() may fail part way through (e.g. due to deferred probe for a vmmc regulator), and sdhci_pltfm_free() does nothing to unrequest the CD IRQ nor cancel the delayed_work. sdhci_pltfm_free() is coded to assume that if sdhci_add_host() failed, then the delayed_work cannot (or should not) have been triggered. This can lead to the following with CONFIG_DEBUG_OBJECTS_* enabled, when kfree(host) is eventually called inside sdhci_pltfm_free(): WARNING: CPU: 2 PID: 6 at lib/debugobjects.c:263 debug_print_object+0x8c/0xb4() ODEBUG: free active (active state 0) object type: timer_list hint: delayed_work_timer_fn+0x0/0x18 The object being complained about is host->detect. There's no need to request the CD IRQ so early; mmc_start_host() already requests it. For most SDHCI hosts at least, the typical call path that does this is: *_probe() -> sdhci_add_host() -> mmc_add_host() -> mmc_start_host(). Therefore, remove the call to mmc_gpiod_request_cd_irq() from mmc_gpio_request_cd(). This also matches mmc_gpio*d*_request_cd(), which already doesn't call mmc_gpiod_request_cd_irq(). However, some host controller drivers call mmc_gpio_request_cd() after mmc_start_host() has already been called, and assume that this will also call mmc_gpiod_request_cd_irq(). Update those drivers to explicitly call mmc_gpiod_request_cd_irq() themselves. Ideally, these drivers should be modified to move their call to mmc_gpio_request_cd() before their call to mmc_add_host(). However that's too large a change for stable. This solves the problem (eliminates the kernel error message above), since it guarantees that the IRQ can't trigger before mmc_start_host() is called. The critical point here is that once sdhci_add_host() calls mmc_add_host() -> mmc_start_host(), sdhci_add_host() is coded not to fail. In other words, if there's a chance that mmc_start_host() may have been called, and CD IRQs triggered, and the delayed_work scheduled, sdhci_add_host() won't fail, and so cleanup is no longer via sdhci_pltfm_free() (which doesn't free the IRQ or cancel the work queue) but instead must be via sdhci_remove_host(), which calls mmc_remove_host() -> mmc_stop_host(), which does free the IRQ and cancel the work queue. CC: Russell King Cc: Adrian Hunter Cc: Alexandre Courbot Cc: Linus Walleij Signed-off-by: Stephen Warren Acked-by: Adrian Hunter Cc: # v3.15+ Signed-off-by: Ulf Hansson --- drivers/mmc/core/slot-gpio.c | 2 -- drivers/mmc/host/mmc_spi.c | 1 + drivers/mmc/host/sdhci-sirf.c | 1 + drivers/mmc/host/tmio_mmc_pio.c | 1 + 4 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index e3fce4493fab..3af30e94fd0c 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -221,8 +221,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, ctx->override_cd_active_level = true; ctx->cd_gpio = gpio_to_desc(gpio); - mmc_gpiod_request_cd_irq(host); - return 0; } EXPORT_SYMBOL(mmc_gpio_request_cd); diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index cc8d4a6099cd..e4a07546f8b6 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1436,6 +1436,7 @@ static int mmc_spi_probe(struct spi_device *spi) host->pdata->cd_debounce); if (status != 0) goto fail_add_host; + mmc_gpiod_request_cd_irq(mmc); } if (host->pdata && host->pdata->flags & MMC_SPI_USE_RO_GPIO) { diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 68a39718a171..dd29d47c07aa 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -116,6 +116,7 @@ static int sdhci_sirf_probe(struct platform_device *pdev) ret); goto err_request_cd; } + mmc_gpiod_request_cd_irq(host->mmc); } return 0; diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index e487ba4bb01a..250bf8c9f998 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1189,6 +1189,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, tmio_mmc_host_remove(_host); return ret; } + mmc_gpiod_request_cd_irq(mmc); } *host = _host; -- cgit v1.2.3 From ffed1b94cbf869e81de0525608193f367db2ba36 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 22 Sep 2014 12:26:09 -0700 Subject: mmc: sdhci-msm: Make tuning block table endian agnostic If we're tuning on a big-endian CPU we'll never determine we properly tuned the device because we compare the data we received from the controller with a table that assumes the CPU is little-endian. Change the table to be an array of bytes instead of 32-bit words so we can use memcmp() without needing to byte-swap every word depending on the endianess of the CPU. Cc: Asutosh Das Cc: Venkat Gopalakrishnan Reviewed-by: Georgi Djakov Fixes: 415b5a75da43 "mmc: sdhci-msm: Add platform_execute_tuning implementation" Signed-off-by: Stephen Boyd Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 2f19eabac3ea..19539c61353c 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -46,22 +46,34 @@ #define CMUX_SHIFT_PHASE_SHIFT 24 #define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT) -static const u32 tuning_block_64[] = { - 0x00ff0fff, 0xccc3ccff, 0xffcc3cc3, 0xeffefffe, - 0xddffdfff, 0xfbfffbff, 0xff7fffbf, 0xefbdf777, - 0xf0fff0ff, 0x3cccfc0f, 0xcfcc33cc, 0xeeffefff, - 0xfdfffdff, 0xffbfffdf, 0xfff7ffbb, 0xde7b7ff7 +static const u8 tuning_block_64[] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, }; -static const u32 tuning_block_128[] = { - 0xff00ffff, 0x0000ffff, 0xccccffff, 0xcccc33cc, - 0xcc3333cc, 0xffffcccc, 0xffffeeff, 0xffeeeeff, - 0xffddffff, 0xddddffff, 0xbbffffff, 0xbbffffff, - 0xffffffbb, 0xffffff77, 0x77ff7777, 0xffeeddbb, - 0x00ffffff, 0x00ffffff, 0xccffff00, 0xcc33cccc, - 0x3333cccc, 0xffcccccc, 0xffeeffff, 0xeeeeffff, - 0xddffffff, 0xddffffff, 0xffffffdd, 0xffffffbb, - 0xffffbbbb, 0xffff77ff, 0xff7777ff, 0xeeddbb77 +static const u8 tuning_block_128[] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, }; struct sdhci_msm_host { @@ -358,7 +370,7 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) { int tuning_seq_cnt = 3; u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0; - const u32 *tuning_block_pattern = tuning_block_64; + const u8 *tuning_block_pattern = tuning_block_64; int size = sizeof(tuning_block_64); /* Pattern size in bytes */ int rc; struct mmc_host *mmc = host->mmc; -- cgit v1.2.3 From 48d11e067fc90ec9fc9c8f7ab982e5a83bf1887b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 22 Sep 2014 12:26:10 -0700 Subject: mmc: Consolidate emmc tuning blocks The same tuning block exists in the dw_mmc h.c and sdhci-msm.c files. Move these into mmc.c so that they can be shared across drivers. Reported-by: Jaehoon Chung Signed-off-by: Stephen Boyd Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 32 ++++++++++++++++++++++++++++++++ drivers/mmc/host/dw_mmc.c | 30 ------------------------------ drivers/mmc/host/sdhci-msm.c | 38 ++++---------------------------------- 3 files changed, 36 insertions(+), 64 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index ce11d89aa305..6390787fb32a 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1177,6 +1177,38 @@ bus_speed: return err; } +const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; +EXPORT_SYMBOL(tuning_blk_pattern_4bit); + +const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, +}; +EXPORT_SYMBOL(tuning_blk_pattern_8bit); + /* * Execute tuning sequence to seek the proper bus operating * conditions for HS200 and HS400, which sends CMD21 to the device. diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 109fefeb9597..69f0cc68d5b2 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -82,36 +82,6 @@ struct idmac_desc { }; #endif /* CONFIG_MMC_DW_IDMAC */ -static const u8 tuning_blk_pattern_4bit[] = { - 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, - 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, - 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, - 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, - 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, - 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, - 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, - 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, -}; - -static const u8 tuning_blk_pattern_8bit[] = { - 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, - 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, - 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, - 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, - 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, - 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, - 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, - 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, - 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, - 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, - 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, - 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, - 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, -}; - static bool dw_mci_reset(struct dw_mci *host); #if defined(CONFIG_DEBUG_FS) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 19539c61353c..30804385af6d 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -46,36 +46,6 @@ #define CMUX_SHIFT_PHASE_SHIFT 24 #define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT) -static const u8 tuning_block_64[] = { - 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, - 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, - 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, - 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, - 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, - 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, - 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, - 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, -}; - -static const u8 tuning_block_128[] = { - 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, - 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, - 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, - 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, - 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, - 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, - 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, - 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, - 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, - 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, - 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, - 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, - 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, -}; - struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -370,8 +340,8 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) { int tuning_seq_cnt = 3; u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0; - const u8 *tuning_block_pattern = tuning_block_64; - int size = sizeof(tuning_block_64); /* Pattern size in bytes */ + const u8 *tuning_block_pattern = tuning_blk_pattern_4bit; + int size = sizeof(tuning_blk_pattern_4bit); int rc; struct mmc_host *mmc = host->mmc; struct mmc_ios ios = host->mmc->ios; @@ -387,8 +357,8 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) && (mmc->ios.bus_width == MMC_BUS_WIDTH_8)) { - tuning_block_pattern = tuning_block_128; - size = sizeof(tuning_block_128); + tuning_block_pattern = tuning_blk_pattern_8bit; + size = sizeof(tuning_blk_pattern_8bit); } data_buf = kmalloc(size, GFP_KERNEL); -- cgit v1.2.3 From 6606110d89aefcb21b9e70adfe064987cbd8393a Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 12 Sep 2014 14:56:56 -0700 Subject: mmc: Convert pr_warning to pr_warn Use the much more common pr_warn instead of pr_warning. Other miscellanea: o Coalesce formats o Realign arguments o Remove extra spaces when coalescing formats Signed-off-by: Joe Perches Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 4 ++-- drivers/mmc/card/queue.c | 6 ++---- drivers/mmc/card/sdio_uart.c | 9 ++++----- drivers/mmc/core/core.c | 8 ++++---- drivers/mmc/core/mmc.c | 17 +++++++---------- drivers/mmc/core/mmc_ops.c | 4 ++-- drivers/mmc/core/sd.c | 33 ++++++++++++++------------------- drivers/mmc/core/sdio.c | 7 +++---- drivers/mmc/core/sdio_bus.c | 4 ++-- drivers/mmc/core/sdio_irq.c | 7 +++---- drivers/mmc/host/s3cmci.c | 3 ++- drivers/mmc/host/sdhci.c | 34 ++++++++++++++++------------------ drivers/mmc/host/tifm_sd.c | 4 ++-- drivers/mmc/host/wbsd.c | 21 ++++++--------------- 14 files changed, 69 insertions(+), 92 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 5d2799730a32..1fa4c80ff886 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1928,8 +1928,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) case MMC_BLK_ECC_ERR: if (brq->data.blocks > 1) { /* Redo read one sector at a time */ - pr_warning("%s: retrying using single block read\n", - req->rq_disk->disk_name); + pr_warn("%s: retrying using single block read\n", + req->rq_disk->disk_name); disable_multi = 1; break; } diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 3e049c13429c..feea926e32f8 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -229,14 +229,12 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, if (bouncesz > 512) { mqrq_cur->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); if (!mqrq_cur->bounce_buf) { - pr_warning("%s: unable to " - "allocate bounce cur buffer\n", + pr_warn("%s: unable to allocate bounce cur buffer\n", mmc_card_name(card)); } mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); if (!mqrq_prev->bounce_buf) { - pr_warning("%s: unable to " - "allocate bounce prev buffer\n", + pr_warn("%s: unable to allocate bounce prev buffer\n", mmc_card_name(card)); kfree(mqrq_cur->bounce_buf); mqrq_cur->bounce_buf = NULL; diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index f093cea0d060..d2de5925b73e 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -1063,8 +1063,8 @@ static int sdio_uart_probe(struct sdio_func *func, return -ENOMEM; if (func->class == SDIO_CLASS_UART) { - pr_warning("%s: need info on UART class basic setup\n", - sdio_func_id(func)); + pr_warn("%s: need info on UART class basic setup\n", + sdio_func_id(func)); kfree(port); return -ENOSYS; } else if (func->class == SDIO_CLASS_GPS) { @@ -1082,9 +1082,8 @@ static int sdio_uart_probe(struct sdio_func *func, break; } if (!tpl) { - pr_warning( - "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n", - sdio_func_id(func)); + pr_warn("%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n", + sdio_func_id(func)); kfree(port); return -EINVAL; } diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index e2e1dd40fb11..b1e209f479ad 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -433,8 +433,8 @@ static void mmc_wait_for_req_done(struct mmc_host *host, */ if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) { if (!mmc_interrupt_hpi(host->card)) { - pr_warning("%s: %s: Interrupted sanitize\n", - mmc_hostname(host), __func__); + pr_warn("%s: %s: Interrupted sanitize\n", + mmc_hostname(host), __func__); cmd->error = 0; break; } else { @@ -1417,8 +1417,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) if (!host->ops->start_signal_voltage_switch) return -EPERM; if (!host->ops->card_busy) - pr_warning("%s: cannot verify signal voltage switch\n", - mmc_hostname(host)); + pr_warn("%s: cannot verify signal voltage switch\n", + mmc_hostname(host)); cmd.opcode = SD_SWITCH_VOLTAGE; cmd.arg = 0; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 6390787fb32a..a301a78a2bd1 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -226,9 +226,7 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd) "Card will be ignored.\n", mmc_hostname(card->host)); } else { - pr_warning("%s: unable to read " - "EXT_CSD, performance might " - "suffer.\n", + pr_warn("%s: unable to read EXT_CSD, performance might suffer\n", mmc_hostname(card->host)); err = 0; } @@ -816,8 +814,8 @@ static int __mmc_select_powerclass(struct mmc_card *card, ext_csd->raw_pwr_cl_200_360; break; default: - pr_warning("%s: Voltage range not supported " - "for power class.\n", mmc_hostname(host)); + pr_warn("%s: Voltage range not supported for power class\n", + mmc_hostname(host)); return -EINVAL; } @@ -1490,8 +1488,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (err && err != -EBADMSG) goto free_card; if (err) { - pr_warning("%s: Enabling HPI failed\n", - mmc_hostname(card->host)); + pr_warn("%s: Enabling HPI failed\n", + mmc_hostname(card->host)); err = 0; } else card->ext_csd.hpi_en = 1; @@ -1512,9 +1510,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * Only if no error, cache is turned on successfully. */ if (err) { - pr_warning("%s: Cache is supported, " - "but failed to turn on (%d)\n", - mmc_hostname(card->host), err); + pr_warn("%s: Cache is supported, but failed to turn on (%d)\n", + mmc_hostname(card->host), err); card->ext_csd.cache_ctrl = 0; err = 0; } else { diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index ba0275e90617..7911e0510a1d 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -649,8 +649,8 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) int err; if (!card->ext_csd.hpi) { - pr_warning("%s: Card didn't support HPI command\n", - mmc_hostname(card->host)); + pr_warn("%s: Card didn't support HPI command\n", + mmc_hostname(card->host)); return -EINVAL; } diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 25913889cbaa..d90a6de7901d 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -229,8 +229,8 @@ static int mmc_read_ssr(struct mmc_card *card) u32 *ssr; if (!(card->csd.cmdclass & CCC_APP_SPEC)) { - pr_warning("%s: card lacks mandatory SD Status " - "function.\n", mmc_hostname(card->host)); + pr_warn("%s: card lacks mandatory SD Status function\n", + mmc_hostname(card->host)); return 0; } @@ -240,8 +240,8 @@ static int mmc_read_ssr(struct mmc_card *card) err = mmc_app_sd_status(card, ssr); if (err) { - pr_warning("%s: problem reading SD Status " - "register.\n", mmc_hostname(card->host)); + pr_warn("%s: problem reading SD Status register\n", + mmc_hostname(card->host)); err = 0; goto out; } @@ -265,8 +265,8 @@ static int mmc_read_ssr(struct mmc_card *card) card->ssr.erase_offset = eo * 1000; } } else { - pr_warning("%s: SD Status: Invalid Allocation Unit size.\n", - mmc_hostname(card->host)); + pr_warn("%s: SD Status: Invalid Allocation Unit size\n", + mmc_hostname(card->host)); } } out: @@ -286,8 +286,7 @@ static int mmc_read_switch(struct mmc_card *card) return 0; if (!(card->csd.cmdclass & CCC_SWITCH)) { - pr_warning("%s: card lacks mandatory switch " - "function, performance might suffer.\n", + pr_warn("%s: card lacks mandatory switch function, performance might suffer\n", mmc_hostname(card->host)); return 0; } @@ -316,7 +315,7 @@ static int mmc_read_switch(struct mmc_card *card) if (err != -EINVAL && err != -ENOSYS && err != -EFAULT) goto out; - pr_warning("%s: problem reading Bus Speed modes.\n", + pr_warn("%s: problem reading Bus Speed modes\n", mmc_hostname(card->host)); err = 0; @@ -372,8 +371,7 @@ int mmc_sd_switch_hs(struct mmc_card *card) goto out; if ((status[16] & 0xF) != 1) { - pr_warning("%s: Problem switching card " - "into high-speed mode!\n", + pr_warn("%s: Problem switching card into high-speed mode!\n", mmc_hostname(card->host)); err = 0; } else { @@ -440,7 +438,7 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status) return err; if ((status[15] & 0xF) != drive_strength) { - pr_warning("%s: Problem setting drive strength!\n", + pr_warn("%s: Problem setting drive strength!\n", mmc_hostname(card->host)); return 0; } @@ -518,7 +516,7 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status) return err; if ((status[16] & 0xF) != card->sd_bus_speed) - pr_warning("%s: Problem setting bus speed mode!\n", + pr_warn("%s: Problem setting bus speed mode!\n", mmc_hostname(card->host)); else { mmc_set_timing(card->host, timing); @@ -598,7 +596,7 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status) return err; if (((status[15] >> 4) & 0x0F) != current_limit) - pr_warning("%s: Problem setting current limit!\n", + pr_warn("%s: Problem setting current limit!\n", mmc_hostname(card->host)); } @@ -727,8 +725,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr) try_again: if (!retries) { ocr &= ~SD_OCR_S18R; - pr_warning("%s: Skipping voltage switch\n", - mmc_hostname(host)); + pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host)); } /* @@ -872,9 +869,7 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, } if (ro < 0) { - pr_warning("%s: host does not " - "support reading read-only " - "switch. assuming write-enable.\n", + pr_warn("%s: host does not support reading read-only switch, assuming write-enable\n", mmc_hostname(host)); } else if (ro > 0) { mmc_card_set_readonly(card); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 3fc40a7140a8..2439e717655b 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -216,8 +216,8 @@ static int sdio_enable_wide(struct mmc_card *card) return ret; if ((ctrl & SDIO_BUS_WIDTH_MASK) == SDIO_BUS_WIDTH_RESERVED) - pr_warning("%s: SDIO_CCCR_IF is invalid: 0x%02x\n", - mmc_hostname(card->host), ctrl); + pr_warn("%s: SDIO_CCCR_IF is invalid: 0x%02x\n", + mmc_hostname(card->host), ctrl); /* set as 4-bit bus width */ ctrl &= ~SDIO_BUS_WIDTH_MASK; @@ -605,8 +605,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, try_again: if (!retries) { - pr_warning("%s: Skipping voltage switch\n", - mmc_hostname(host)); + pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host)); ocr &= ~R4_18V_PRESENT; } diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 4fa8fef9147f..e4eb456df4ac 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -177,8 +177,8 @@ static int sdio_bus_remove(struct device *dev) drv->remove(func); if (func->irq_handler) { - pr_warning("WARNING: driver %s did not remove " - "its interrupt handler!\n", drv->name); + pr_warn("WARNING: driver %s did not remove its interrupt handler!\n", + drv->name); sdio_claim_host(func); sdio_release_irq(func); sdio_release_host(func); diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index 696eca493844..09cc67d028f0 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -69,16 +69,15 @@ static int process_sdio_pending_irqs(struct mmc_host *host) if (pending & (1 << i)) { func = card->sdio_func[i - 1]; if (!func) { - pr_warning("%s: pending IRQ for " - "non-existent function\n", + pr_warn("%s: pending IRQ for non-existent function\n", mmc_card_id(card)); ret = -EINVAL; } else if (func->irq_handler) { func->irq_handler(func); count++; } else { - pr_warning("%s: pending IRQ with no handler\n", - sdio_func_id(func)); + pr_warn("%s: pending IRQ with no handler\n", + sdio_func_id(func)); ret = -EINVAL; } } diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index e4e1a5388163..94cddf381ba3 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -985,7 +985,8 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data) * one block being transferred. */ if (data->blocks > 1) { - pr_warning("%s: can't do non-word sized block transfers (blksz %d)\n", __func__, data->blksz); + pr_warn("%s: can't do non-word sized block transfers (blksz %d)\n", + __func__, data->blksz); return -EINVAL; } } diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 7481bd863502..db113aba35d0 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1754,8 +1754,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, ret = regulator_set_voltage(mmc->supply.vqmmc, 2700000, 3600000); if (ret) { - pr_warning("%s: Switching to 3.3V signalling voltage " - " failed\n", mmc_hostname(mmc)); + pr_warn("%s: Switching to 3.3V signalling voltage failed\n", + mmc_hostname(mmc)); return -EIO; } } @@ -1767,8 +1767,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, if (!(ctrl & SDHCI_CTRL_VDD_180)) return 0; - pr_warning("%s: 3.3V regulator output did not became stable\n", - mmc_hostname(mmc)); + pr_warn("%s: 3.3V regulator output did not became stable\n", + mmc_hostname(mmc)); return -EAGAIN; case MMC_SIGNAL_VOLTAGE_180: @@ -1776,8 +1776,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, ret = regulator_set_voltage(mmc->supply.vqmmc, 1700000, 1950000); if (ret) { - pr_warning("%s: Switching to 1.8V signalling voltage " - " failed\n", mmc_hostname(mmc)); + pr_warn("%s: Switching to 1.8V signalling voltage failed\n", + mmc_hostname(mmc)); return -EIO; } } @@ -1794,8 +1794,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, if (ctrl & SDHCI_CTRL_VDD_180) return 0; - pr_warning("%s: 1.8V regulator output did not became stable\n", - mmc_hostname(mmc)); + pr_warn("%s: 1.8V regulator output did not became stable\n", + mmc_hostname(mmc)); return -EAGAIN; case MMC_SIGNAL_VOLTAGE_120: @@ -1803,8 +1803,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, ret = regulator_set_voltage(mmc->supply.vqmmc, 1100000, 1300000); if (ret) { - pr_warning("%s: Switching to 1.2V signalling voltage " - " failed\n", mmc_hostname(mmc)); + pr_warn("%s: Switching to 1.2V signalling voltage failed\n", + mmc_hostname(mmc)); return -EIO; } } @@ -2844,8 +2844,7 @@ int sdhci_add_host(struct sdhci_host *host) if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->ops->enable_dma) { if (host->ops->enable_dma(host)) { - pr_warning("%s: No suitable DMA " - "available. Falling back to PIO.\n", + pr_warn("%s: No suitable DMA available - falling back to PIO\n", mmc_hostname(mmc)); host->flags &= ~(SDHCI_USE_SDMA | SDHCI_USE_ADMA); @@ -2867,15 +2866,14 @@ int sdhci_add_host(struct sdhci_host *host) dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, host->adma_desc, host->adma_addr); kfree(host->align_buffer); - pr_warning("%s: Unable to allocate ADMA " - "buffers. Falling back to standard DMA.\n", + pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n", mmc_hostname(mmc)); host->flags &= ~SDHCI_USE_ADMA; host->adma_desc = NULL; host->align_buffer = NULL; } else if (host->adma_addr & 3) { - pr_warning("%s: unable to allocate aligned ADMA descriptor\n", - mmc_hostname(mmc)); + pr_warn("%s: unable to allocate aligned ADMA descriptor\n", + mmc_hostname(mmc)); host->flags &= ~SDHCI_USE_ADMA; dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, host->adma_desc, host->adma_addr); @@ -3202,8 +3200,8 @@ int sdhci_add_host(struct sdhci_host *host) mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT; if (mmc->max_blk_size >= 3) { - pr_warning("%s: Invalid maximum block size, " - "assuming 512 bytes\n", mmc_hostname(mmc)); + pr_warn("%s: Invalid maximum block size, assuming 512 bytes\n", + mmc_hostname(mmc)); mmc->max_blk_size = 0; } } diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index d1760ebcac03..93c4b40df90a 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -952,8 +952,8 @@ static int tifm_sd_probe(struct tifm_dev *sock) if (!(TIFM_SOCK_STATE_OCCUPIED & readl(sock->addr + SOCK_PRESENT_STATE))) { - pr_warning("%s : card gone, unexpectedly\n", - dev_name(&sock->dev)); + pr_warn("%s : card gone, unexpectedly\n", + dev_name(&sock->dev)); return rc; } diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index 1defd5ed3236..9a6dfb0c4ecc 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -803,8 +803,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) default: #ifdef CONFIG_MMC_DEBUG - pr_warning("%s: Data command %d is not " - "supported by this controller.\n", + pr_warn("%s: Data command %d is not supported by this controller\n", mmc_hostname(host->mmc), cmd->opcode); #endif cmd->error = -EINVAL; @@ -1429,8 +1428,8 @@ free: free_dma(dma); err: - pr_warning(DRIVER_NAME ": Unable to allocate DMA %d. " - "Falling back on FIFO.\n", dma); + pr_warn(DRIVER_NAME ": Unable to allocate DMA %d - falling back on FIFO\n", + dma); } static void wbsd_release_dma(struct wbsd_host *host) @@ -1664,9 +1663,7 @@ static int wbsd_init(struct device *dev, int base, int irq, int dma, ret = wbsd_scan(host); if (ret) { if (pnp && (ret == -ENODEV)) { - pr_warning(DRIVER_NAME - ": Unable to confirm device presence. You may " - "experience lock-ups.\n"); + pr_warn(DRIVER_NAME ": Unable to confirm device presence - you may experience lock-ups\n"); } else { wbsd_free_mmc(dev); return ret; @@ -1688,10 +1685,7 @@ static int wbsd_init(struct device *dev, int base, int irq, int dma, */ if (pnp) { if ((host->config != 0) && !wbsd_chip_validate(host)) { - pr_warning(DRIVER_NAME - ": PnP active but chip not configured! " - "You probably have a buggy BIOS. " - "Configuring chip manually.\n"); + pr_warn(DRIVER_NAME ": PnP active but chip not configured! You probably have a buggy BIOS. Configuring chip manually.\n"); wbsd_chip_config(host); } } else @@ -1884,10 +1878,7 @@ static int wbsd_pnp_resume(struct pnp_dev *pnp_dev) */ if (host->config != 0) { if (!wbsd_chip_validate(host)) { - pr_warning(DRIVER_NAME - ": PnP active but chip not configured! " - "You probably have a buggy BIOS. " - "Configuring chip manually.\n"); + pr_warn(DRIVER_NAME ": PnP active but chip not configured! You probably have a buggy BIOS. Configuring chip manually.\n"); wbsd_chip_config(host); } } -- cgit v1.2.3 From 7bca646e0fc34b19cd84e14ed474d1c95830cc80 Mon Sep 17 00:00:00 2001 From: Pramod Gurav Date: Tue, 23 Sep 2014 18:21:48 +0530 Subject: mmc: atmel-mci: Switch to using managed resource in probe This change uses managed resource APIs to allocate resources such as, clk, gpio, io in order to simplify the driver unload or failure cases. Hence does away with release statements of the same resources in error labels and remove function. Signed-off-by: Pramod Gurav Acked-by: Ludovic Desroches Signed-off-by: Ulf Hansson --- drivers/mmc/host/atmel-mci.c | 41 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index fde6a6fc732c..f32a51ee0fe3 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -2195,7 +2196,8 @@ static int __init atmci_init_slot(struct atmel_mci *host, /* Assume card is present initially */ set_bit(ATMCI_CARD_PRESENT, &slot->flags); if (gpio_is_valid(slot->detect_pin)) { - if (gpio_request(slot->detect_pin, "mmc_detect")) { + if (devm_gpio_request(&host->pdev->dev, slot->detect_pin, + "mmc_detect")) { dev_dbg(&mmc->class_dev, "no detect pin available\n"); slot->detect_pin = -EBUSY; } else if (gpio_get_value(slot->detect_pin) ^ @@ -2208,7 +2210,8 @@ static int __init atmci_init_slot(struct atmel_mci *host, mmc->caps |= MMC_CAP_NEEDS_POLL; if (gpio_is_valid(slot->wp_pin)) { - if (gpio_request(slot->wp_pin, "mmc_wp")) { + if (devm_gpio_request(&host->pdev->dev, slot->wp_pin, + "mmc_wp")) { dev_dbg(&mmc->class_dev, "no WP pin available\n"); slot->wp_pin = -EBUSY; } @@ -2232,7 +2235,6 @@ static int __init atmci_init_slot(struct atmel_mci *host, dev_dbg(&mmc->class_dev, "could not request IRQ %d for detect pin\n", gpio_to_irq(slot->detect_pin)); - gpio_free(slot->detect_pin); slot->detect_pin = -EBUSY; } } @@ -2257,10 +2259,7 @@ static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot, free_irq(gpio_to_irq(pin), slot); del_timer_sync(&slot->detect_timer); - gpio_free(pin); } - if (gpio_is_valid(slot->wp_pin)) - gpio_free(slot->wp_pin); slot->host->slot[id] = NULL; mmc_free_host(slot->mmc); @@ -2396,7 +2395,7 @@ static int __init atmci_probe(struct platform_device *pdev) if (irq < 0) return irq; - host = kzalloc(sizeof(struct atmel_mci), GFP_KERNEL); + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); if (!host) return -ENOMEM; @@ -2404,20 +2403,18 @@ static int __init atmci_probe(struct platform_device *pdev) spin_lock_init(&host->lock); INIT_LIST_HEAD(&host->queue); - host->mck = clk_get(&pdev->dev, "mci_clk"); - if (IS_ERR(host->mck)) { - ret = PTR_ERR(host->mck); - goto err_clk_get; - } + host->mck = devm_clk_get(&pdev->dev, "mci_clk"); + if (IS_ERR(host->mck)) + return PTR_ERR(host->mck); - ret = -ENOMEM; - host->regs = ioremap(regs->start, resource_size(regs)); + host->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); if (!host->regs) - goto err_ioremap; + return -ENOMEM; ret = clk_prepare_enable(host->mck); if (ret) - goto err_request_irq; + return ret; + atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST); host->bus_hz = clk_get_rate(host->mck); clk_disable_unprepare(host->mck); @@ -2428,7 +2425,7 @@ static int __init atmci_probe(struct platform_device *pdev) ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host); if (ret) - goto err_request_irq; + return ret; /* Get MCI capabilities and set operations according to it */ atmci_get_cap(host); @@ -2500,12 +2497,6 @@ err_init_slot: if (host->dma.chan) dma_release_channel(host->dma.chan); free_irq(irq, host); -err_request_irq: - iounmap(host->regs); -err_ioremap: - clk_put(host->mck); -err_clk_get: - kfree(host); return ret; } @@ -2533,10 +2524,6 @@ static int __exit atmci_remove(struct platform_device *pdev) dma_release_channel(host->dma.chan); free_irq(platform_get_irq(pdev, 0), host); - iounmap(host->regs); - - clk_put(host->mck); - kfree(host); return 0; } -- cgit v1.2.3 From 528bc7808f4e36a330ec17ef22803f1345091494 Mon Sep 17 00:00:00 2001 From: Pramod Gurav Date: Tue, 23 Sep 2014 15:50:06 +0530 Subject: mmc: atmel-mci: Release mmc resources on failure in probe This change takes care of releasing mmc resources on error cases in probe function which was missing. Also release timer in remove function. Signed-off-by: Pramod Gurav Acked-by: Ludovic Desroches Signed-off-by: Ulf Hansson --- drivers/mmc/host/atmel-mci.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index f32a51ee0fe3..772ef5b0e4d5 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -2377,7 +2377,7 @@ static int __init atmci_probe(struct platform_device *pdev) struct resource *regs; unsigned int nr_slots; int irq; - int ret; + int ret, i; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) @@ -2483,7 +2483,7 @@ static int __init atmci_probe(struct platform_device *pdev) if (!host->buffer) { ret = -ENOMEM; dev_err(&pdev->dev, "buffer allocation failed\n"); - goto err_init_slot; + goto err_dma_alloc; } } @@ -2493,7 +2493,13 @@ static int __init atmci_probe(struct platform_device *pdev) return 0; +err_dma_alloc: + for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { + if (host->slot[i]) + atmci_cleanup_slot(host->slot[i], i); + } err_init_slot: + del_timer_sync(&host->timer); if (host->dma.chan) dma_release_channel(host->dma.chan); free_irq(irq, host); @@ -2520,6 +2526,7 @@ static int __exit atmci_remove(struct platform_device *pdev) atmci_readl(host, ATMCI_SR); clk_disable_unprepare(host->mck); + del_timer_sync(&host->timer); if (host->dma.chan) dma_release_channel(host->dma.chan); -- cgit v1.2.3 From 7756a96d1617753d8387e59e0ba410e0dd512be4 Mon Sep 17 00:00:00 2001 From: Yi Sun Date: Tue, 9 Sep 2014 02:13:59 +0000 Subject: mmc: sdhci: execute tuning when device is not busy We find tuning timeout because of the secure erase operation lasts too long, so don't do tuning when device is busy. Signed-off-by: Yi Sun Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 7 ++++--- drivers/mmc/host/sdhci.h | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index db113aba35d0..cbd433712756 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1366,11 +1366,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) present_state = sdhci_readl(host, SDHCI_PRESENT_STATE); /* * Check if the re-tuning timer has already expired and there - * is no on-going data transfer. If so, we need to execute - * tuning procedure before sending command. + * is no on-going data transfer and DAT0 is not busy. If so, + * we need to execute tuning procedure before sending command. */ if ((host->flags & SDHCI_NEEDS_RETUNING) && - !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) { + !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ)) && + (present_state & SDHCI_DATA_0_LVL_MASK)) { if (mmc->card) { /* eMMC uses cmd21 but sd and sdio use cmd19 */ tuning_opcode = diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 9828ff83d84c..31896a779d4e 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -72,6 +72,7 @@ #define SDHCI_WRITE_PROTECT 0x00080000 #define SDHCI_DATA_LVL_MASK 0x00F00000 #define SDHCI_DATA_LVL_SHIFT 20 +#define SDHCI_DATA_0_LVL_MASK 0x00100000 #define SDHCI_HOST_CONTROL 0x28 #define SDHCI_CTRL_LED 0x01 -- cgit v1.2.3 From 8af465db967bf25a4617416c0cbaaaa506d444f5 Mon Sep 17 00:00:00 2001 From: Roger Tseng Date: Wed, 24 Sep 2014 17:07:13 +0800 Subject: mmc: core: Add new power_mode MMC_POWER_UNDEFINED Add MMC_POWER_UNDEFINED for power_mode in struct mmc_ios and use it as the initial value of host->ios.power_mode. For hosts with MMC_CAP2_NO_PRESCAN_POWERUP, this makes the later mmc_power_off() do real power-off things instead of NOP, and further prevents state messed up in cards that was already initialized (eg. by BIOS of UEFI driver). Signed-off-by: Roger Tseng Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index b1e209f479ad..cbb23215ad87 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2482,6 +2482,7 @@ void mmc_start_host(struct mmc_host *host) { host->f_init = max(freqs[0], host->f_min); host->rescan_disable = 0; + host->ios.power_mode = MMC_POWER_UNDEFINED; if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP) mmc_power_off(host); else -- cgit v1.2.3 From 517bf80fe74494e2d7f185c107085cde11f0e9a4 Mon Sep 17 00:00:00 2001 From: Roger Tseng Date: Wed, 24 Sep 2014 17:07:14 +0800 Subject: mmc: rtsx_pci: Set power related cap2 macros Set MMC_CAP2_NO_PRESCAN_POWERUP and MMC_CAP2_FULL_PWR_CYCLE for rtsx_pci_sdmmc and rtsx_usb_sdmmc to reflect properties of Realtek card reader hosts. Signed-off-by: Roger Tseng Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 1 + drivers/mmc/host/rtsx_usb_sdmmc.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index d928216b6b5f..c70b602f8f1e 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1299,6 +1299,7 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host) mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25; + mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE; mmc->max_current_330 = 400; mmc->max_current_180 = 800; mmc->ops = &realtek_pci_sdmmc_ops; diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index e46c574fa574..88af827e086b 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -1336,6 +1336,7 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host) MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | MMC_CAP_NEEDS_POLL; + mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE; mmc->max_current_330 = 400; mmc->max_current_180 = 800; -- cgit v1.2.3 From 5fef365b64b99d298d724b30b5c291b05d169f58 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 26 Sep 2014 21:34:58 +0200 Subject: mmc: atmel-mci: fix mismatched section on atmci_cleanup_slot As of 528bc7808f4e ("mmc: atmel-mci: Release mmc resources on failure in probe"), the atmci_probe() function calls atmci_cleanup_slot in the failure path. This causes a new warning whenever the driver is built: WARNING: drivers/mmc/host/built-in.o(.init.text+0xa04): Section mismatch in reference from the function atmci_probe() to the function .exit.text:atmci_cleanup_slot() The function __init atmci_probe() references a function __exit atmci_cleanup_slot(). Gcc correctly warns about this function getting dropped in the link stage for the built-in case, which would cause undefined behavior when this error path is hit. The solution is to simply drop the __exit annotation. Signed-off-by: Arnd Bergmann Fixes: 528bc7808f4e ("mmc: atmel-mci: Release mmc resources on failure in probe") Acked-by: Ludovic Desroches Signed-off-by: Ulf Hansson --- drivers/mmc/host/atmel-mci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 772ef5b0e4d5..77250d4b1979 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -2244,7 +2244,7 @@ static int __init atmci_init_slot(struct atmel_mci *host, return 0; } -static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot, +static void atmci_cleanup_slot(struct atmel_mci_slot *slot, unsigned int id) { /* Debugfs stuff is cleaned up by mmc core */ -- cgit v1.2.3 From 6800754c3674fb36350b2df9c3f84676e7e7a8f7 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 23 Sep 2014 23:00:25 +0300 Subject: mmc: Fix use of wrong device in mmc_gpiod_free_cd() mmc_gpiod_free_cd() is paired with mmc_gpiod_request_cd() and both must reference the same device which is the actual host controller device not the mmc_host class device. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/slot-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 3af30e94fd0c..38f76555d4bf 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -390,7 +390,7 @@ void mmc_gpiod_free_cd(struct mmc_host *host) host->slot.cd_irq = -EINVAL; } - devm_gpiod_put(&host->class_dev, ctx->cd_gpio); + devm_gpiod_put(host->parent, ctx->cd_gpio); ctx->cd_gpio = NULL; } -- cgit v1.2.3 From 6a98f1e83a2874a189754ded5254ae687828739e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 23 Sep 2014 23:00:26 +0300 Subject: mmc: Fix incorrect warning when setting 0 Hz via debugfs It is possible to turn off the card clock by setting the frequency to zero via debugfs e.g. echo 0 > /sys/kernel/debug/mmc0/clock However that produces an incorrect warning that is designed to warn if the frequency is below the minimum operating frequency. So correct the warning. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index cbb23215ad87..f26a5f1d926d 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -995,7 +995,7 @@ void mmc_set_chip_select(struct mmc_host *host, int mode) */ static void __mmc_set_clock(struct mmc_host *host, unsigned int hz) { - WARN_ON(hz < host->f_min); + WARN_ON(hz && hz < host->f_min); if (hz > host->f_max) hz = host->f_max; -- cgit v1.2.3 From 89168b48991537bec2573b3b6a8841df74465b12 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 2 Oct 2014 09:08:46 +0200 Subject: mmc: core: restore detect line inversion semantics commit 98e90de99a0c43bd434da814c882c4332441871e "mmc: host: switch OF parser to use gpio descriptors" switched the semantic behaviour of card detect and read only flags such that the inversion capability flag would only be set if inversion was explicitly specified in the device tree, in the hopes that no-one was using double inversion. It turns out that the XOR:ing between the explicit inversion was indeed in use, so we need to restore the old semantics where both ways of inversion are checked and the end result XOR:ed. Reported-by: Javier Martinez Canillas Tested-by: Javier Martinez Canillas Signed-off-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 32 ++++++++++++++++++++++++++++---- drivers/mmc/core/slot-gpio.c | 14 ++++++++++++-- drivers/mmc/host/mmci.c | 4 ++-- drivers/mmc/host/sdhci-acpi.c | 2 +- 4 files changed, 43 insertions(+), 9 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 31969436d77c..03c53b72a2d6 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -311,6 +311,7 @@ int mmc_of_parse(struct mmc_host *host) struct device_node *np; u32 bus_width; int len, ret; + bool cap_invert, gpio_invert; if (!host->parent || !host->parent->of_node) return 0; @@ -359,12 +360,15 @@ int mmc_of_parse(struct mmc_host *host) host->caps |= MMC_CAP_NONREMOVABLE; } else { if (of_property_read_bool(np, "cd-inverted")) - host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; + cap_invert = true; + else + cap_invert = false; if (of_find_property(np, "broken-cd", &len)) host->caps |= MMC_CAP_NEEDS_POLL; - ret = mmc_gpiod_request_cd(host, "cd", 0, false, 0); + ret = mmc_gpiod_request_cd(host, "cd", 0, true, + 0, &gpio_invert); if (ret) { if (ret == -EPROBE_DEFER) return ret; @@ -375,13 +379,29 @@ int mmc_of_parse(struct mmc_host *host) } } else dev_info(host->parent, "Got CD GPIO\n"); + + /* + * There are two ways to flag that the CD line is inverted: + * through the cd-inverted flag and by the GPIO line itself + * being inverted from the GPIO subsystem. This is a leftover + * from the times when the GPIO subsystem did not make it + * possible to flag a line as inverted. + * + * If the capability on the host AND the GPIO line are + * both inverted, the end result is that the CD line is + * not inverted. + */ + if (cap_invert ^ gpio_invert) + host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; } /* Parse Write Protection */ if (of_property_read_bool(np, "wp-inverted")) - host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; + cap_invert = true; + else + cap_invert = false; - ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0); + ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &gpio_invert); if (ret) { if (ret == -EPROBE_DEFER) goto out; @@ -393,6 +413,10 @@ int mmc_of_parse(struct mmc_host *host) } else dev_info(host->parent, "Got WP GPIO\n"); + /* See the comment on CD inversion above */ + if (cap_invert ^ gpio_invert) + host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; + if (of_find_property(np, "cap-sd-highspeed", &len)) host->caps |= MMC_CAP_SD_HIGHSPEED; if (of_find_property(np, "cap-mmc-highspeed", &len)) diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 38f76555d4bf..69bbf2adb329 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -281,6 +281,8 @@ EXPORT_SYMBOL(mmc_gpio_free_cd); * @idx: index of the GPIO to obtain in the consumer * @override_active_level: ignore %GPIO_ACTIVE_LOW flag * @debounce: debounce time in microseconds + * @gpio_invert: will return whether the GPIO line is inverted or not, set + * to NULL to ignore * * Use this function in place of mmc_gpio_request_cd() to use the GPIO * descriptor API. Note that it is paired with mmc_gpiod_free_cd() not @@ -291,7 +293,7 @@ EXPORT_SYMBOL(mmc_gpio_free_cd); */ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, unsigned int idx, bool override_active_level, - unsigned int debounce) + unsigned int debounce, bool *gpio_invert) { struct mmc_gpio *ctx; struct gpio_desc *desc; @@ -316,6 +318,9 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, return ret; } + if (gpio_invert) + *gpio_invert = !gpiod_is_active_low(desc); + ctx->override_cd_active_level = override_active_level; ctx->cd_gpio = desc; @@ -330,6 +335,8 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd); * @idx: index of the GPIO to obtain in the consumer * @override_active_level: ignore %GPIO_ACTIVE_LOW flag * @debounce: debounce time in microseconds + * @gpio_invert: will return whether the GPIO line is inverted or not, + * set to NULL to ignore * * Use this function in place of mmc_gpio_request_ro() to use the GPIO * descriptor API. Note that it is paired with mmc_gpiod_free_ro() not @@ -339,7 +346,7 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd); */ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, unsigned int idx, bool override_active_level, - unsigned int debounce) + unsigned int debounce, bool *gpio_invert) { struct mmc_gpio *ctx; struct gpio_desc *desc; @@ -364,6 +371,9 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, return ret; } + if (gpio_invert) + *gpio_invert = !gpiod_is_active_low(desc); + ctx->override_ro_active_level = override_active_level; ctx->ro_gpio = desc; diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index c9dafed550f2..43af791e2e45 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1682,7 +1682,7 @@ static int mmci_probe(struct amba_device *dev, * silently of these do not exist and proceed to try platform data */ if (!np) { - ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0); + ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL); if (ret < 0) { if (ret == -EPROBE_DEFER) goto clk_disable; @@ -1693,7 +1693,7 @@ static int mmci_probe(struct amba_device *dev, } } - ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0); + ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0, NULL); if (ret < 0) { if (ret == -EPROBE_DEFER) goto clk_disable; diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 3483c089baa7..327bc24ec8ce 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -352,7 +352,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev) if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) { bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL); - if (mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0)) { + if (mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0, NULL)) { dev_warn(dev, "failed to setup card detect gpio\n"); c->use_runtime_pm = false; } -- cgit v1.2.3 From 615413979487a1e25a3b76abbaa316280ca19d26 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 24 Sep 2014 10:27:27 +0300 Subject: mmc: sdhci: Add quirk for always getting TC with stop cmd Add a quirk for a host controller that always sets a Transfer Complete interrupt status for the stop command even when a busy response is not indicated. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index cbd433712756..a932f70bcc51 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2225,7 +2225,7 @@ static void sdhci_tuning_timer(unsigned long data) * * \*****************************************************************************/ -static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) +static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask) { BUG_ON(intmask == 0); @@ -2272,6 +2272,9 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) /* The controller does not support the end-of-busy IRQ, * fall through and take the SDHCI_INT_RESPONSE */ + } else if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) && + host->cmd->opcode == MMC_STOP_TRANSMISSION && !host->data) { + *mask &= ~SDHCI_INT_DATA_END; } if (intmask & SDHCI_INT_RESPONSE) @@ -2481,7 +2484,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) } if (intmask & SDHCI_INT_CMD_MASK) - sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); + sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK, + &intmask); if (intmask & SDHCI_INT_DATA_MASK) sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); -- cgit v1.2.3 From 934e31b9dc848df56a65768388609358a1836ba0 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 24 Sep 2014 10:27:28 +0300 Subject: mmc: sdhci-acpi: Set SDHCI_QUIRK2_STOP_WITH_TC for Intel host controllers Add quirk SDHCI_QUIRK2_STOP_WITH_TC for Intel host controllers. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-acpi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 327bc24ec8ce..6b2468b3ab07 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -175,7 +175,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = { MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR, .caps2 = MMC_CAP2_HC_ERASE_SZ, .flags = SDHCI_ACPI_RUNTIME_PM, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC, .probe_slot = sdhci_acpi_emmc_probe_slot, }; @@ -191,7 +191,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = { .flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL | SDHCI_ACPI_RUNTIME_PM, - .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON, + .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | + SDHCI_QUIRK2_STOP_WITH_TC, .probe_slot = sdhci_acpi_sd_probe_slot, }; -- cgit v1.2.3 From 7147eaf3a4fe7e7dbb6e1f89e328ea0507e0c32c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 24 Sep 2014 10:27:29 +0300 Subject: mmc: sdhci-acpi: Add a HID and UID for a SD Card host controller Add a HID (INT33BB) and UID (3) for a SD Card host controller. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-acpi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 6b2468b3ab07..e9a2c6f2a2e3 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -207,6 +207,7 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = { { "80860F14" , "3" , &sdhci_acpi_slot_int_sd }, { "80860F16" , NULL, &sdhci_acpi_slot_int_sd }, { "INT33BB" , "2" , &sdhci_acpi_slot_int_sdio }, + { "INT33BB" , "3" , &sdhci_acpi_slot_int_sd }, { "INT33C6" , NULL, &sdhci_acpi_slot_int_sdio }, { "INT3436" , NULL, &sdhci_acpi_slot_int_sdio }, { "PNP0D40" }, -- cgit v1.2.3 From e58e4a0d14a5b8b6ab2aa2942cb2440e45c1f8c9 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 24 Sep 2014 10:27:30 +0300 Subject: mmc: sdhci-pci: Set SDHCI_QUIRK2_STOP_WITH_TC for Intel BYT host controllers Add quirk SDHCI_QUIRK2_STOP_WITH_TC for Intel BYT host controllers. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 580073bc164c..31181d8dc561 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -283,7 +283,8 @@ static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot) static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = { .allow_runtime_pm = true, .probe_slot = byt_emmc_probe_slot, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_STOP_WITH_TC, }; static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { @@ -295,7 +296,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | - SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_STOP_WITH_TC, .allow_runtime_pm = true, .own_cd_for_runtime_pm = true, }; -- cgit v1.2.3 From ff59c520b5fec9733d5402fdfa76fa4435a40bba Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 24 Sep 2014 10:27:31 +0300 Subject: mmc: sdhci-pci: Add Bay Trail and Braswell SD card detect Add support for card detect for Bay Trail and Braswell SD Card host controllers in PCI mode. This uses the gpio descriptor API which can find gpio descriptors, for example, on an ACPI comapnion device. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci.c | 20 +++++++++++++++++++- drivers/mmc/host/sdhci-pci.h | 4 ++++ 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 31181d8dc561..4ca6ae6a175d 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "sdhci.h" @@ -280,6 +281,14 @@ static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot) return 0; } +static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->cd_con_id = NULL; + slot->cd_idx = 0; + slot->cd_override_level = true; + return 0; +} + static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = { .allow_runtime_pm = true, .probe_slot = byt_emmc_probe_slot, @@ -300,6 +309,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { SDHCI_QUIRK2_STOP_WITH_TC, .allow_runtime_pm = true, .own_cd_for_runtime_pm = true, + .probe_slot = byt_sd_probe_slot, }; /* Define Host controllers for Intel Merrifield platform */ @@ -1354,6 +1364,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( slot->pci_bar = bar; slot->rst_n_gpio = -EINVAL; slot->cd_gpio = -EINVAL; + slot->cd_idx = -1; /* Retrieve platform data if there is any */ if (*sdhci_pci_get_data) @@ -1412,6 +1423,13 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( host->mmc->slotno = slotno; host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP; + if (slot->cd_idx >= 0 && + mmc_gpiod_request_cd(host->mmc, slot->cd_con_id, slot->cd_idx, + slot->cd_override_level, 0, NULL)) { + dev_warn(&pdev->dev, "failed to setup card detect gpio\n"); + slot->cd_idx = -1; + } + ret = sdhci_add_host(host); if (ret) goto remove; @@ -1424,7 +1442,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( * Note sdhci_pci_add_own_cd() sets slot->cd_gpio to -EINVAL on failure. */ if (chip->fixes && chip->fixes->own_cd_for_runtime_pm && - !gpio_is_valid(slot->cd_gpio)) + !gpio_is_valid(slot->cd_gpio) && slot->cd_idx < 0) chip->allow_runtime_pm = false; return slot; diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 9c1909b2a3ad..d57c3d169914 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -64,6 +64,10 @@ struct sdhci_pci_slot { int cd_gpio; int cd_irq; + char *cd_con_id; + int cd_idx; + bool cd_override_level; + void (*hw_reset)(struct sdhci_host *host); }; -- cgit v1.2.3 From f5fa92e58bce2cbcce99c2c5260c0b338a1ef201 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 24 Sep 2014 10:27:32 +0300 Subject: mmc: sdhci: Let a driver override timeout clock frequency Let a driver override the timeout clock frequency by populating it before calling sdhci_add_host(). Note the value will otherwise be zero because sdhci_host is zeroed when allocated. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a932f70bcc51..ada1a3ea3a87 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2791,6 +2791,7 @@ int sdhci_add_host(struct sdhci_host *host) u32 caps[2] = {0, 0}; u32 max_current_caps; unsigned int ocr_avail; + unsigned int override_timeout_clk; int ret; WARN_ON(host == NULL); @@ -2804,6 +2805,8 @@ int sdhci_add_host(struct sdhci_host *host) if (debug_quirks2) host->quirks2 = debug_quirks2; + override_timeout_clk = host->timeout_clk; + sdhci_do_reset(host, SDHCI_RESET_ALL); host->version = sdhci_readw(host, SDHCI_HOST_VERSION); @@ -2970,6 +2973,9 @@ int sdhci_add_host(struct sdhci_host *host) mmc->max_busy_timeout /= host->timeout_clk; } + if (override_timeout_clk) + host->timeout_clk = override_timeout_clk; + mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; -- cgit v1.2.3 From a06586b62db5c63752e2e68daffec4baa275d594 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 24 Sep 2014 10:27:33 +0300 Subject: mmc: sdhci-pci: Fix Braswell eMMC timeout clock frequency Braswell eMMC host controller specifies an incorrect timeout clock frequncy in the capabilities registers. The correct value is 1 MHz. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 4ca6ae6a175d..61192973e7cb 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -272,6 +272,8 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR; slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ; slot->hw_reset = sdhci_pci_int_hw_reset; + if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC) + slot->host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */ return 0; } -- cgit v1.2.3 From b3f635ad6648d17178c3e2ab2f0a4823fcac1e83 Mon Sep 17 00:00:00 2001 From: Behan Webster Date: Thu, 25 Sep 2014 15:45:22 -0700 Subject: mmc, sdhci, bcm-kona, LLVMLinux: Remove use of __initconst The __initconst is in the wrong place, and when moved to the correct place it uncovers an error where the variable is used by non-init data structures. Instead merely make them const and put the const in the right spot. Signed-off-by: Behan Webster Reviewed-by: Mark Charlebois Acked-by: Arnd Bergmann Acked-by: Matt Porter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-bcm-kona.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 2245988a01a9..e7e4fbdcbfe0 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -225,7 +225,7 @@ static struct sdhci_pltfm_data sdhci_pltfm_data_kona = { SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, }; -static struct __initconst of_device_id sdhci_bcm_kona_of_match[] = { +static const struct of_device_id sdhci_bcm_kona_of_match[] = { { .compatible = "brcm,kona-sdhci"}, { .compatible = "bcm,kona-sdhci"}, /* deprecated name */ {} -- cgit v1.2.3 From 3db3525196a992da628fb210776d73ec4bb59460 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 6 Oct 2014 15:23:05 +0300 Subject: mmc: sdhci-acpi: Get UID directly from acpi_device UID was made available on acpi_device since commit ccf78040265b ("ACPI: Add _UID support for ACPI devices.") Use it from there instead of reprocessing the ACPI object info. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-acpi.c | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index e9a2c6f2a2e3..839f26cdc901 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -225,8 +225,8 @@ static const struct acpi_device_id sdhci_acpi_ids[] = { }; MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids); -static const struct sdhci_acpi_slot *sdhci_acpi_get_slot_by_ids(const char *hid, - const char *uid) +static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(const char *hid, + const char *uid) { const struct sdhci_acpi_uid_slot *u; @@ -241,24 +241,6 @@ static const struct sdhci_acpi_slot *sdhci_acpi_get_slot_by_ids(const char *hid, return NULL; } -static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(acpi_handle handle, - const char *hid) -{ - const struct sdhci_acpi_slot *slot; - struct acpi_device_info *info; - const char *uid = NULL; - acpi_status status; - - status = acpi_get_object_info(handle, &info); - if (!ACPI_FAILURE(status) && (info->valid & ACPI_VALID_UID)) - uid = info->unique_id.string; - - slot = sdhci_acpi_get_slot_by_ids(hid, uid); - - kfree(info); - return slot; -} - static int sdhci_acpi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -269,6 +251,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev) struct resource *iomem; resource_size_t len; const char *hid; + const char *uid; int err; if (acpi_bus_get_device(handle, &device)) @@ -278,6 +261,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev) return -ENODEV; hid = acpi_device_hid(device); + uid = device->pnp.unique_id; iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!iomem) @@ -296,7 +280,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev) c = sdhci_priv(host); c->host = host; - c->slot = sdhci_acpi_get_slot(handle, hid); + c->slot = sdhci_acpi_get_slot(hid, uid); c->pdev = pdev; c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM); -- cgit v1.2.3 From 7dafca835a459ab31792d9def69ed30cf6dc08ab Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 6 Oct 2014 15:23:06 +0300 Subject: mmc: sdhci-acpi: Pass HID and UID to probe_slot Pass HID and UID to probe_slot so extra setup can be done for specific ids. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-acpi.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 839f26cdc901..7548261221b5 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -67,7 +67,7 @@ struct sdhci_acpi_slot { unsigned int caps2; mmc_pm_flag_t pm_caps; unsigned int flags; - int (*probe_slot)(struct platform_device *); + int (*probe_slot)(struct platform_device *, const char *, const char *); int (*remove_slot)(struct platform_device *); }; @@ -124,7 +124,8 @@ static const struct sdhci_acpi_chip sdhci_acpi_chip_int = { .ops = &sdhci_acpi_ops_int, }; -static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev) +static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev, + const char *hid, const char *uid) { struct sdhci_acpi_host *c = platform_get_drvdata(pdev); struct sdhci_host *host; @@ -139,7 +140,8 @@ static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev) return 0; } -static int sdhci_acpi_sdio_probe_slot(struct platform_device *pdev) +static int sdhci_acpi_sdio_probe_slot(struct platform_device *pdev, + const char *hid, const char *uid) { struct sdhci_acpi_host *c = platform_get_drvdata(pdev); struct sdhci_host *host; @@ -154,7 +156,8 @@ static int sdhci_acpi_sdio_probe_slot(struct platform_device *pdev) return 0; } -static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev) +static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev, + const char *hid, const char *uid) { struct sdhci_acpi_host *c = platform_get_drvdata(pdev); struct sdhci_host *host; @@ -314,7 +317,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev) if (c->slot) { if (c->slot->probe_slot) { - err = c->slot->probe_slot(pdev); + err = c->slot->probe_slot(pdev, hid, uid); if (err) goto err_free; } -- cgit v1.2.3 From 8024379e0ae43b1154978c2291ce20f772cbb3bd Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 6 Oct 2014 15:23:07 +0300 Subject: mmc: sdhci-acpi: Fix Braswell eMMC timeout clock frequency Braswell eMMC host controller specifies an incorrect timeout clock frequncy in the capabilities registers. The correct value is 1 MHz. A similar fix was done for sdhci-pci, however in the sdhci-acpi case the HID/UID is not unique so the capabilities register values are matched also. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-acpi.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 7548261221b5..9cccc0e89b04 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -137,6 +137,11 @@ static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev, /* Platform specific code during emmc proble slot goes here */ + if (hid && uid && !strcmp(hid, "80860F14") && !strcmp(uid, "1") && + sdhci_readl(host, SDHCI_CAPABILITIES) == 0x446cc8b2 && + sdhci_readl(host, SDHCI_CAPABILITIES_1) == 0x00000807) + host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */ + return 0; } -- cgit v1.2.3