summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt25
-rw-r--r--Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt25
-rw-r--r--Documentation/devicetree/bindings/mmc/mmc.txt62
-rw-r--r--Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt30
-rw-r--r--Documentation/devicetree/bindings/mmc/sdhci-pxa.txt15
-rw-r--r--arch/arm/mach-omap2/board-omap3pandora.c14
-rw-r--r--drivers/mmc/card/block.c6
-rw-r--r--drivers/mmc/card/mmc_test.c18
-rw-r--r--drivers/mmc/core/Makefile2
-rw-r--r--drivers/mmc/core/bus.c4
-rw-r--r--drivers/mmc/core/core.c152
-rw-r--r--drivers/mmc/core/core.h7
-rw-r--r--drivers/mmc/core/host.c65
-rw-r--r--drivers/mmc/core/mmc.c101
-rw-r--r--drivers/mmc/core/mmc_ops.c30
-rw-r--r--drivers/mmc/core/pwrseq.c112
-rw-r--r--drivers/mmc/core/pwrseq.h43
-rw-r--r--drivers/mmc/core/pwrseq_emmc.c101
-rw-r--r--drivers/mmc/core/pwrseq_simple.c145
-rw-r--r--drivers/mmc/core/sd.c27
-rw-r--r--drivers/mmc/core/sdio.c14
-rw-r--r--drivers/mmc/core/sdio_bus.c11
-rw-r--r--drivers/mmc/core/slot-gpio.c182
-rw-r--r--drivers/mmc/core/slot-gpio.h13
-rw-r--r--drivers/mmc/host/Kconfig15
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c113
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.h56
-rw-r--r--drivers/mmc/host/dw_mmc-rockchip.c2
-rw-r--r--drivers/mmc/host/dw_mmc.c124
-rw-r--r--drivers/mmc/host/dw_mmc.h9
-rw-r--r--drivers/mmc/host/mmci.c7
-rw-r--r--drivers/mmc/host/moxart-mmc.c24
-rw-r--r--drivers/mmc/host/mvsdio.c10
-rw-r--r--drivers/mmc/host/mxs-mmc.c3
-rw-r--r--drivers/mmc/host/omap_hsmmc.c150
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c542
-rw-r--r--drivers/mmc/host/sdhci-acpi.c13
-rw-r--r--drivers/mmc/host/sdhci-bcm-kona.c4
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c17
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c20
-rw-r--r--drivers/mmc/host/sdhci-pci.c7
-rw-r--r--drivers/mmc/host/sdhci-pxav3.c119
-rw-r--r--drivers/mmc/host/sdhci-s3c.c12
-rw-r--r--drivers/mmc/host/sdhci-sirf.c74
-rw-r--r--drivers/mmc/host/sdhci-st.c4
-rw-r--r--drivers/mmc/host/sdhci-tegra.c30
-rw-r--r--drivers/mmc/host/sdhci.c125
-rw-r--r--drivers/mmc/host/sdhci.h1
-rw-r--r--drivers/mmc/host/sdhci_f_sdh30.c237
-rw-r--r--drivers/mmc/host/sh_mobile_sdhi.c117
-rw-r--r--drivers/mmc/host/sunxi-mmc.c29
-rw-r--r--drivers/mmc/host/tmio_mmc.c15
-rw-r--r--drivers/mmc/host/tmio_mmc.h43
-rw-r--r--drivers/mmc/host/tmio_mmc_dma.c38
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c61
-rw-r--r--drivers/mmc/host/toshsd.c17
-rw-r--r--drivers/mmc/host/vub300.c4
-rw-r--r--include/linux/mfd/tmio.h28
-rw-r--r--include/linux/mmc/card.h2
-rw-r--r--include/linux/mmc/core.h1
-rw-r--r--include/linux/mmc/dw_mmc.h6
-rw-r--r--include/linux/mmc/host.h4
-rw-r--r--include/linux/mmc/mmc.h10
-rw-r--r--include/linux/mmc/sdhci.h10
-rw-r--r--include/linux/mmc/sh_mobile_sdhi.h15
-rw-r--r--include/linux/mmc/slot-gpio.h5
-rw-r--r--include/linux/platform_data/mmc-omap.h4
68 files changed, 2152 insertions, 1180 deletions
diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt
new file mode 100644
index 000000000000..0cb827bf9435
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt
@@ -0,0 +1,25 @@
+* The simple eMMC hardware reset provider
+
+The purpose of this driver is to perform standard eMMC hw reset
+procedure, as descibed by Jedec 4.4 specification. This procedure is
+performed just after MMC core enabled power to the given mmc host (to
+fix possible issues if bootloader has left eMMC card in initialized or
+unknown state), and before performing complete system reboot (also in
+case of emergency reboot call). The latter is needed on boards, which
+doesn't have hardware reset logic connected to emmc card and (limited or
+broken) ROM bootloaders are unable to read second stage from the emmc
+card if the card is left in unknown or already initialized state.
+
+Required properties:
+- compatible : contains "mmc-pwrseq-emmc".
+- reset-gpios : contains a GPIO specifier. The reset GPIO is asserted
+ and then deasserted to perform eMMC card reset. To perform
+ reset procedure as described in Jedec 4.4 specification, the
+ gpio line should be defined as GPIO_ACTIVE_LOW.
+
+Example:
+
+ sdhci0_pwrseq {
+ compatible = "mmc-pwrseq-emmc";
+ reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
+ }
diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
new file mode 100644
index 000000000000..a462c50f19a8
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
@@ -0,0 +1,25 @@
+* The simple MMC power sequence provider
+
+The purpose of the simple MMC power sequence provider is to supports a set of
+common properties between various SOC designs. It thus enables us to use the
+same provider for several SOC designs.
+
+Required properties:
+- compatible : contains "mmc-pwrseq-simple".
+
+Optional properties:
+- reset-gpios : contains a list of GPIO specifiers. The reset GPIOs are asserted
+ at initialization and prior we start the power up procedure of the card.
+ They will be de-asserted right after the power has been provided to the
+ card.
+- clocks : Must contain an entry for the entry in clock-names.
+ See ../clocks/clock-bindings.txt for details.
+- clock-names : Must include the following entry:
+ "ext_clock" (External clock provided to the card).
+
+Example:
+
+ sdhci0_pwrseq {
+ compatible = "mmc-pwrseq-simple";
+ reset-gpios = <&gpio1 12 0>;
+ }
diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt
index b52628b18a53..438899e8829b 100644
--- a/Documentation/devicetree/bindings/mmc/mmc.txt
+++ b/Documentation/devicetree/bindings/mmc/mmc.txt
@@ -64,7 +64,43 @@ Optional SDIO properties:
- keep-power-in-suspend: Preserves card power during a suspend/resume cycle
- enable-sdio-wakeup: Enables wake up of host system on SDIO IRQ assertion
-Example:
+
+MMC power sequences:
+--------------------
+
+System on chip designs may specify a specific MMC power sequence. To
+successfully detect an (e)MMC/SD/SDIO card, that power sequence must be
+maintained while initializing the card.
+
+Optional property:
+- mmc-pwrseq: phandle to the MMC power sequence node. See "mmc-pwrseq-*"
+ for documentation of MMC power sequence bindings.
+
+
+Use of Function subnodes
+------------------------
+
+On embedded systems the cards connected to a host may need additional
+properties. These can be specified in subnodes to the host controller node.
+The subnodes are identified by the standard 'reg' property.
+Which information exactly can be specified depends on the bindings for the
+SDIO function driver for the subnode, as specified by the compatible string.
+
+Required host node properties when using function subnodes:
+- #address-cells: should be one. The cell is the slot id.
+- #size-cells: should be zero.
+
+Required function subnode properties:
+- compatible: name of SDIO function following generic names recommended practice
+- reg: Must contain the SDIO function number of the function this subnode
+ describes. A value of 0 denotes the memory SD function, values from
+ 1 to 7 denote the SDIO functions.
+
+
+Examples
+--------
+
+Basic example:
sdhci@ab000000 {
compatible = "sdhci";
@@ -77,4 +113,28 @@ sdhci@ab000000 {
max-frequency = <50000000>;
keep-power-in-suspend;
enable-sdio-wakeup;
+ mmc-pwrseq = <&sdhci0_pwrseq>
}
+
+Example with sdio function subnode:
+
+mmc3: mmc@01c12000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc3_pins_a>;
+ vmmc-supply = <&reg_vmmc3>;
+ bus-width = <4>;
+ non-removable;
+ mmc-pwrseq = <&sdhci0_pwrseq>
+ status = "okay";
+
+ brcmf: bcrmf@1 {
+ reg = <1>;
+ compatible = "brcm,bcm43xx-fmac";
+ interrupt-parent = <&pio>;
+ interrupts = <10 8>; /* PH10 / EINT10 */
+ interrupt-names = "host-wake";
+ };
+};
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt b/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt
new file mode 100644
index 000000000000..de2c53cff4f1
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt
@@ -0,0 +1,30 @@
+* Fujitsu SDHCI controller
+
+This file documents differences between the core properties in mmc.txt
+and the properties used by the sdhci_f_sdh30 driver.
+
+Required properties:
+- compatible: "fujitsu,mb86s70-sdhci-3.0"
+- clocks: Must contain an entry for each entry in clock-names. It is a
+ list of phandles and clock-specifier pairs.
+ See ../clocks/clock-bindings.txt for details.
+- clock-names: Should contain the following two entries:
+ "iface" - clock used for sdhci interface
+ "core" - core clock for sdhci controller
+
+Optional properties:
+- vqmmc-supply: phandle to the regulator device tree node, mentioned
+ as the VCCQ/VDD_IO supply in the eMMC/SD specs.
+
+Example:
+
+ sdhci1: mmc@36600000 {
+ compatible = "fujitsu,mb86s70-sdhci-3.0";
+ reg = <0 0x36600000 0x1000>;
+ interrupts = <0 172 0x4>,
+ <0 173 0x4>;
+ bus-width = <4>;
+ vqmmc-supply = <&vccq_sdhci1>;
+ clocks = <&clock 2 2 0>, <&clock 2 3 0>;
+ clock-names = "iface", "core";
+ };
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt b/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt
index 4dd6deb90719..3d1b449d6097 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt
@@ -9,9 +9,13 @@ Required properties:
- reg:
* for "mrvl,pxav2-mmc" and "mrvl,pxav3-mmc", one register area for
the SDHCI registers.
- * for "marvell,armada-380-sdhci", two register areas. The first one
- for the SDHCI registers themselves, and the second one for the
- AXI/Mbus bridge registers of the SDHCI unit.
+
+ * for "marvell,armada-380-sdhci", three register areas. The first
+ one for the SDHCI registers themselves, the second one for the
+ AXI/Mbus bridge registers of the SDHCI unit, the third one for the
+ SDIO3 Configuration register
+- reg names: should be "sdhci", "mbus", "conf-sdio3". only mandatory
+ for "marvell,armada-380-sdhci"
- clocks: Array of clocks required for SDHCI; requires at least one for
I/O clock.
- clock-names: Array of names corresponding to clocks property; shall be
@@ -35,7 +39,10 @@ sdhci@d4280800 {
sdhci@d8000 {
compatible = "marvell,armada-380-sdhci";
- reg = <0xd8000 0x1000>, <0xdc000 0x100>;
+ reg-names = "sdhci", "mbus", "conf-sdio3";
+ reg = <0xd8000 0x1000>,
+ <0xdc000 0x100>;
+ <0x18454 0x4>;
interrupts = <0 25 0x4>;
clocks = <&gateclk 17>;
clock-names = "io";
diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c
index 7f1708738c30..969e1003dd92 100644
--- a/arch/arm/mach-omap2/board-omap3pandora.c
+++ b/arch/arm/mach-omap2/board-omap3pandora.c
@@ -254,12 +254,14 @@ static void pandora_wl1251_init_card(struct mmc_card *card)
* We have TI wl1251 attached to MMC3. Pass this information to
* SDIO core because it can't be probed by normal methods.
*/
- card->quirks |= MMC_QUIRK_NONSTD_SDIO;
- card->cccr.wide_bus = 1;
- card->cis.vendor = 0x104c;
- card->cis.device = 0x9066;
- card->cis.blksize = 512;
- card->cis.max_dtr = 20000000;
+ if (card->type == MMC_TYPE_SDIO || card->type == MMC_TYPE_SD_COMBO) {
+ card->quirks |= MMC_QUIRK_NONSTD_SDIO;
+ card->cccr.wide_bus = 1;
+ card->cis.vendor = 0x104c;
+ card->cis.device = 0x9066;
+ card->cis.blksize = 512;
+ card->cis.max_dtr = 20000000;
+ }
}
static struct omap2_hsmmc_info omap3pandora_mmc[] = {
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 4409d79ed650..c69afb5e264e 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -2147,7 +2147,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
*/
snprintf(md->disk->disk_name, sizeof(md->disk->disk_name),
- "mmcblk%d%s", md->name_idx, subname ? subname : "");
+ "mmcblk%u%s", md->name_idx, subname ? subname : "");
if (mmc_card_mmc(card))
blk_queue_logical_block_size(md->queue.queue,
@@ -2193,7 +2193,6 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
{
sector_t size;
- struct mmc_blk_data *md;
if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
/*
@@ -2209,9 +2208,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
size = card->csd.capacity << (card->csd.read_blkbits - 9);
}
- md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
+ return mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
MMC_BLK_DATA_AREA_MAIN);
- return md;
}
static int mmc_blk_alloc_part(struct mmc_card *card,
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 0a7430f94d29..7dac4695163b 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -2342,20 +2342,16 @@ static int mmc_test_hw_reset(struct mmc_test_card *test)
struct mmc_host *host = card->host;
int err;
- err = mmc_hw_reset_check(host);
+ if (!mmc_card_mmc(card) || !mmc_can_reset(card))
+ return RESULT_UNSUP_CARD;
+
+ err = mmc_hw_reset(host);
if (!err)
return RESULT_OK;
+ else if (err == -EOPNOTSUPP)
+ return RESULT_UNSUP_HOST;
- if (err == -ENOSYS)
- return RESULT_FAIL;
-
- if (err != -EOPNOTSUPP)
- return err;
-
- if (!mmc_can_reset(card))
- return RESULT_UNSUP_CARD;
-
- return RESULT_UNSUP_HOST;
+ return RESULT_FAIL;
}
static const struct mmc_test_case mmc_test_cases[] = {
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index 38ed210ce2f3..2c25138f28b7 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -8,5 +8,5 @@ mmc_core-y := core.o bus.o host.o \
sdio.o sdio_ops.o sdio_bus.o \
sdio_cis.o sdio_io.o sdio_irq.o \
quirks.o slot-gpio.o
-
+mmc_core-$(CONFIG_OF) += pwrseq.o pwrseq_simple.o pwrseq_emmc.o
mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 86d271148528..c5ef10065a4a 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -16,6 +16,7 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/stat.h>
+#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/mmc/card.h>
@@ -321,6 +322,8 @@ int mmc_add_card(struct mmc_card *card)
#endif
mmc_init_context_info(card->host);
+ card->dev.of_node = mmc_of_find_child_device(card->host, 0);
+
ret = device_add(&card->dev);
if (ret)
return ret;
@@ -349,6 +352,7 @@ void mmc_remove_card(struct mmc_card *card)
mmc_hostname(card->host), card->rca);
}
device_del(&card->dev);
+ of_node_put(card->dev.of_node);
}
put_device(&card->dev);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 9584bffa8b22..23f10f72e5f3 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -40,6 +40,7 @@
#include "bus.h"
#include "host.h"
#include "sdio_bus.h"
+#include "pwrseq.h"
#include "mmc_ops.h"
#include "sd_ops.h"
@@ -185,13 +186,14 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
EXPORT_SYMBOL(mmc_request_done);
-static void
-mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
+static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
#ifdef CONFIG_MMC_DEBUG
unsigned int i, sz;
struct scatterlist *sg;
#endif
+ if (mmc_card_removed(host->card))
+ return -ENOMEDIUM;
if (mrq->sbc) {
pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n",
@@ -251,6 +253,8 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
mmc_host_clk_hold(host);
led_trigger_event(host->led, LED_FULL);
host->ops->request(host, mrq);
+
+ return 0;
}
/**
@@ -271,7 +275,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
BUG_ON(!card);
- if (!card->ext_csd.bkops_en || mmc_card_doing_bkops(card))
+ if (!card->ext_csd.man_bkops_en || mmc_card_doing_bkops(card))
return;
err = mmc_read_bkops_status(card);
@@ -345,29 +349,34 @@ static void mmc_wait_done(struct mmc_request *mrq)
*/
static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
{
+ int err;
+
mrq->done = mmc_wait_data_done;
mrq->host = host;
- if (mmc_card_removed(host->card)) {
- mrq->cmd->error = -ENOMEDIUM;
+
+ err = mmc_start_request(host, mrq);
+ if (err) {
+ mrq->cmd->error = err;
mmc_wait_data_done(mrq);
- return -ENOMEDIUM;
}
- mmc_start_request(host, mrq);
- return 0;
+ return err;
}
static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{
+ int err;
+
init_completion(&mrq->completion);
mrq->done = mmc_wait_done;
- if (mmc_card_removed(host->card)) {
- mrq->cmd->error = -ENOMEDIUM;
+
+ err = mmc_start_request(host, mrq);
+ if (err) {
+ mrq->cmd->error = err;
complete(&mrq->completion);
- return -ENOMEDIUM;
}
- mmc_start_request(host, mrq);
- return 0;
+
+ return err;
}
/*
@@ -1077,6 +1086,30 @@ void mmc_set_ungated(struct mmc_host *host)
}
#endif
+int mmc_execute_tuning(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ u32 opcode;
+ int err;
+
+ if (!host->ops->execute_tuning)
+ return 0;
+
+ if (mmc_card_mmc(card))
+ opcode = MMC_SEND_TUNING_BLOCK_HS200;
+ else
+ opcode = MMC_SEND_TUNING_BLOCK;
+
+ mmc_host_clk_hold(host);
+ err = host->ops->execute_tuning(host, opcode);
+ mmc_host_clk_release(host);
+
+ if (err)
+ pr_err("%s: tuning execution failed\n", mmc_hostname(host));
+
+ return err;
+}
+
/*
* Change the bus mode (open drain/push-pull) of a host.
*/
@@ -1232,6 +1265,34 @@ EXPORT_SYMBOL(mmc_of_parse_voltage);
#endif /* CONFIG_OF */
+static int mmc_of_get_func_num(struct device_node *node)
+{
+ u32 reg;
+ int ret;
+
+ ret = of_property_read_u32(node, "reg", &reg);
+ if (ret < 0)
+ return ret;
+
+ return reg;
+}
+
+struct device_node *mmc_of_find_child_device(struct mmc_host *host,
+ unsigned func_num)
+{
+ struct device_node *node;
+
+ if (!host->parent || !host->parent->of_node)
+ return NULL;
+
+ for_each_child_of_node(host->parent->of_node, node) {
+ if (mmc_of_get_func_num(node) == func_num)
+ return node;
+ }
+
+ return NULL;
+}
+
#ifdef CONFIG_REGULATOR
/**
@@ -1555,6 +1616,8 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
mmc_host_clk_hold(host);
+ mmc_pwrseq_pre_power_on(host);
+
host->ios.vdd = fls(ocr) - 1;
host->ios.power_mode = MMC_POWER_UP;
/* Set initial state and call mmc_set_ios */
@@ -1574,6 +1637,8 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
*/
mmc_delay(10);
+ mmc_pwrseq_post_power_on(host);
+
host->ios.clock = host->f_init;
host->ios.power_mode = MMC_POWER_ON;
@@ -1595,6 +1660,8 @@ void mmc_power_off(struct mmc_host *host)
mmc_host_clk_hold(host);
+ mmc_pwrseq_power_off(host);
+
host->ios.clock = 0;
host->ios.vdd = 0;
@@ -2245,67 +2312,28 @@ static void mmc_hw_reset_for_init(struct mmc_host *host)
mmc_host_clk_release(host);
}
-int mmc_can_reset(struct mmc_card *card)
-{
- u8 rst_n_function;
-
- if (!mmc_card_mmc(card))
- return 0;
- rst_n_function = card->ext_csd.rst_n_function;
- if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
- return 0;
- return 1;
-}
-EXPORT_SYMBOL(mmc_can_reset);
-
-static int mmc_do_hw_reset(struct mmc_host *host, int check)
+int mmc_hw_reset(struct mmc_host *host)
{
- struct mmc_card *card = host->card;
-
- if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
- return -EOPNOTSUPP;
+ int ret;
- if (!card)
+ if (!host->card)
return -EINVAL;
- if (!mmc_can_reset(card))
+ mmc_bus_get(host);
+ if (!host->bus_ops || host->bus_dead || !host->bus_ops->reset) {
+ mmc_bus_put(host);
return -EOPNOTSUPP;
-
- mmc_host_clk_hold(host);
- mmc_set_clock(host, host->f_init);
-
- host->ops->hw_reset(host);
-
- /* If the reset has happened, then a status command will fail */
- if (check) {
- u32 status;
-
- if (!mmc_send_status(card, &status)) {
- mmc_host_clk_release(host);
- return -ENOSYS;
- }
}
- /* Set initial state and call mmc_set_ios */
- mmc_set_initial_state(host);
+ ret = host->bus_ops->reset(host);
+ mmc_bus_put(host);
- mmc_host_clk_release(host);
+ pr_warn("%s: tried to reset card\n", mmc_hostname(host));
- return host->bus_ops->power_restore(host);
-}
-
-int mmc_hw_reset(struct mmc_host *host)
-{
- return mmc_do_hw_reset(host, 0);
+ return ret;
}
EXPORT_SYMBOL(mmc_hw_reset);
-int mmc_hw_reset_check(struct mmc_host *host)
-{
- return mmc_do_hw_reset(host, 1);
-}
-EXPORT_SYMBOL(mmc_hw_reset_check);
-
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq;
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index d76597c65e3a..cfba3c05aab1 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -27,11 +27,15 @@ struct mmc_bus_ops {
int (*power_restore)(struct mmc_host *);
int (*alive)(struct mmc_host *);
int (*shutdown)(struct mmc_host *);
+ int (*reset)(struct mmc_host *);
};
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
void mmc_detach_bus(struct mmc_host *host);
+struct device_node *mmc_of_find_child_device(struct mmc_host *host,
+ unsigned func_num);
+
void mmc_init_erase(struct mmc_card *card);
void mmc_set_chip_select(struct mmc_host *host, int mode);
@@ -82,5 +86,8 @@ void mmc_add_card_debugfs(struct mmc_card *card);
void mmc_remove_card_debugfs(struct mmc_card *card);
void mmc_init_context_info(struct mmc_host *host);
+
+int mmc_execute_tuning(struct mmc_card *card);
+
#endif
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 270d58a4c43d..8be0df758e68 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -29,13 +29,20 @@
#include "core.h"
#include "host.h"
+#include "slot-gpio.h"
+#include "pwrseq.h"
#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
+static DEFINE_IDR(mmc_host_idr);
+static DEFINE_SPINLOCK(mmc_host_lock);
+
static void mmc_host_classdev_release(struct device *dev)
{
struct mmc_host *host = cls_dev_to_mmc_host(dev);
- mutex_destroy(&host->slot.lock);
+ spin_lock(&mmc_host_lock);
+ idr_remove(&mmc_host_idr, host->index);
+ spin_unlock(&mmc_host_lock);
kfree(host);
}
@@ -54,9 +61,6 @@ void mmc_unregister_host_class(void)
class_unregister(&mmc_host_class);
}
-static DEFINE_IDR(mmc_host_idr);
-static DEFINE_SPINLOCK(mmc_host_lock);
-
#ifdef CONFIG_MMC_CLKGATE
static ssize_t clkgate_delay_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -367,16 +371,10 @@ int mmc_of_parse(struct mmc_host *host)
ret = mmc_gpiod_request_cd(host, "cd", 0, true,
0, &cd_gpio_invert);
- if (ret) {
- if (ret == -EPROBE_DEFER)
- return ret;
- if (ret != -ENOENT) {
- dev_err(host->parent,
- "Failed to request CD GPIO: %d\n",
- ret);
- }
- } else
+ if (!ret)
dev_info(host->parent, "Got CD GPIO\n");
+ else if (ret != -ENOENT)
+ return ret;
/*
* There are two ways to flag that the CD line is inverted:
@@ -397,16 +395,10 @@ int mmc_of_parse(struct mmc_host *host)
ro_cap_invert = of_property_read_bool(np, "wp-inverted");
ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert);
- if (ret) {
- if (ret == -EPROBE_DEFER)
- goto out;
- if (ret != -ENOENT) {
- dev_err(host->parent,
- "Failed to request WP GPIO: %d\n",
- ret);
- }
- } else
+ if (!ret)
dev_info(host->parent, "Got WP GPIO\n");
+ else if (ret != -ENOENT)
+ return ret;
/* See the comment on CD inversion above */
if (ro_cap_invert ^ ro_gpio_invert)
@@ -457,11 +449,7 @@ int mmc_of_parse(struct mmc_host *host)
host->dsr_req = 0;
}
- return 0;
-
-out:
- mmc_gpio_free_cd(host);
- return ret;
+ return mmc_pwrseq_alloc(host);
}
EXPORT_SYMBOL(mmc_of_parse);
@@ -491,8 +479,10 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host->index = err;
spin_unlock(&mmc_host_lock);
idr_preload_end();
- if (err < 0)
- goto free;
+ if (err < 0) {
+ kfree(host);
+ return NULL;
+ }
dev_set_name(&host->class_dev, "mmc%d", host->index);
@@ -501,10 +491,12 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host->class_dev.class = &mmc_host_class;
device_initialize(&host->class_dev);
- mmc_host_clk_init(host);
+ if (mmc_gpio_alloc(host)) {
+ put_device(&host->class_dev);
+ return NULL;
+ }
- mutex_init(&host->slot.lock);
- host->slot.cd_irq = -EINVAL;
+ mmc_host_clk_init(host);
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
@@ -525,10 +517,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host->max_blk_count = PAGE_CACHE_SIZE / 512;
return host;
-
-free:
- kfree(host);
- return NULL;
}
EXPORT_SYMBOL(mmc_alloc_host);
@@ -601,10 +589,7 @@ EXPORT_SYMBOL(mmc_remove_host);
*/
void mmc_free_host(struct mmc_host *host)
{
- spin_lock(&mmc_host_lock);
- idr_remove(&mmc_host_idr, host->index);
- spin_unlock(&mmc_host_lock);
-
+ mmc_pwrseq_free(host);
put_device(&host->class_dev);
}
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 7466ce098e60..1d41e8541f38 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -483,11 +483,13 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
/* check whether the eMMC card supports BKOPS */
if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
card->ext_csd.bkops = 1;
- card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
+ card->ext_csd.man_bkops_en =
+ (ext_csd[EXT_CSD_BKOPS_EN] &
+ EXT_CSD_MANUAL_BKOPS_MASK);
card->ext_csd.raw_bkops_status =
ext_csd[EXT_CSD_BKOPS_STATUS];
- if (!card->ext_csd.bkops_en)
- pr_info("%s: BKOPS_EN bit is not set\n",
+ if (!card->ext_csd.man_bkops_en)
+ pr_info("%s: MAN_BKOPS_EN bit is not set\n",
mmc_hostname(card->host));
}
@@ -1155,38 +1157,6 @@ 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.
@@ -1194,7 +1164,6 @@ EXPORT_SYMBOL(tuning_blk_pattern_8bit);
static int mmc_hs200_tuning(struct mmc_card *card)
{
struct mmc_host *host = card->host;
- int err = 0;
/*
* Timing should be adjusted to the HS400 target
@@ -1205,18 +1174,7 @@ static int mmc_hs200_tuning(struct mmc_card *card)
if (host->ops->prepare_hs400_tuning)
host->ops->prepare_hs400_tuning(host, &host->ios);
- if (host->ops->execute_tuning) {
- mmc_host_clk_hold(host);
- err = host->ops->execute_tuning(host,
- MMC_SEND_TUNING_BLOCK_HS200);
- mmc_host_clk_release(host);
-
- if (err)
- pr_err("%s: tuning execution failed\n",
- mmc_hostname(host));
- }
-
- return err;
+ return mmc_execute_tuning(card);
}
/*
@@ -1297,6 +1255,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
/*
+ * Call the optional HC's init_card function to handle quirks.
+ */
+ if (host->ops->init_card)
+ host->ops->init_card(host, card);
+
+ /*
* For native busses: set card RCA and quit open drain mode.
*/
if (!mmc_host_is_spi(host)) {
@@ -1821,6 +1785,46 @@ static int mmc_power_restore(struct mmc_host *host)
return ret;
}
+int mmc_can_reset(struct mmc_card *card)
+{
+ u8 rst_n_function;
+
+ rst_n_function = card->ext_csd.rst_n_function;
+ if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED)
+ return 0;
+ return 1;
+}
+EXPORT_SYMBOL(mmc_can_reset);
+
+static int mmc_reset(struct mmc_host *host)
+{
+ struct mmc_card *card = host->card;
+ u32 status;
+
+ if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
+ return -EOPNOTSUPP;
+
+ if (!mmc_can_reset(card))
+ return -EOPNOTSUPP;
+
+ mmc_host_clk_hold(host);
+ mmc_set_clock(host, host->f_init);
+
+ host->ops->hw_reset(host);
+
+ /* If the reset has happened, then a status command will fail */
+ if (!mmc_send_status(card, &status)) {
+ mmc_host_clk_release(host);
+ return -ENOSYS;
+ }
+
+ /* Set initial state and call mmc_set_ios */
+ mmc_set_initial_state(host);
+ mmc_host_clk_release(host);
+
+ return mmc_power_restore(host);
+}
+
static const struct mmc_bus_ops mmc_ops = {
.remove = mmc_remove,
.detect = mmc_detect,
@@ -1831,6 +1835,7 @@ static const struct mmc_bus_ops mmc_ops = {
.power_restore = mmc_power_restore,
.alive = mmc_alive,
.shutdown = mmc_shutdown,
+ .reset = mmc_reset,
};
/*
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 3b044c5b029c..0ea042dc7443 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -23,6 +23,36 @@
#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
+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 inline int __mmc_send_status(struct mmc_card *card, u32 *status,
bool ignore_crc)
{
diff --git a/drivers/mmc/core/pwrseq.c b/drivers/mmc/core/pwrseq.c
new file mode 100644
index 000000000000..862356123d78
--- /dev/null
+++ b/drivers/mmc/core/pwrseq.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014 Linaro Ltd
+ *
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * MMC power sequence management
+ */
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include <linux/mmc/host.h>
+
+#include "pwrseq.h"
+
+struct mmc_pwrseq_match {
+ const char *compatible;
+ int (*alloc)(struct mmc_host *host, struct device *dev);
+};
+
+static struct mmc_pwrseq_match pwrseq_match[] = {
+ {
+ .compatible = "mmc-pwrseq-simple",
+ .alloc = mmc_pwrseq_simple_alloc,
+ }, {
+ .compatible = "mmc-pwrseq-emmc",
+ .alloc = mmc_pwrseq_emmc_alloc,
+ },
+};
+
+static struct mmc_pwrseq_match *mmc_pwrseq_find(struct device_node *np)
+{
+ struct mmc_pwrseq_match *match = ERR_PTR(-ENODEV);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pwrseq_match); i++) {
+ if (of_device_is_compatible(np, pwrseq_match[i].compatible)) {
+ match = &pwrseq_match[i];
+ break;
+ }
+ }
+
+ return match;
+}
+
+int mmc_pwrseq_alloc(struct mmc_host *host)
+{
+ struct platform_device *pdev;
+ struct device_node *np;
+ struct mmc_pwrseq_match *match;
+ int ret = 0;
+
+ np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0);
+ if (!np)
+ return 0;
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ match = mmc_pwrseq_find(np);
+ if (IS_ERR(match)) {
+ ret = PTR_ERR(match);
+ goto err;
+ }
+
+ ret = match->alloc(host, &pdev->dev);
+ if (!ret)
+ dev_info(host->parent, "allocated mmc-pwrseq\n");
+
+err:
+ of_node_put(np);
+ return ret;
+}
+
+void mmc_pwrseq_pre_power_on(struct mmc_host *host)
+{
+ struct mmc_pwrseq *pwrseq = host->pwrseq;
+
+ if (pwrseq && pwrseq->ops && pwrseq->ops->pre_power_on)
+ pwrseq->ops->pre_power_on(host);
+}
+
+void mmc_pwrseq_post_power_on(struct mmc_host *host)
+{
+ struct mmc_pwrseq *pwrseq = host->pwrseq;
+
+ if (pwrseq && pwrseq->ops && pwrseq->ops->post_power_on)
+ pwrseq->ops->post_power_on(host);
+}
+
+void mmc_pwrseq_power_off(struct mmc_host *host)
+{
+ struct mmc_pwrseq *pwrseq = host->pwrseq;
+
+ if (pwrseq && pwrseq->ops && pwrseq->ops->power_off)
+ pwrseq->ops->power_off(host);
+}
+
+void mmc_pwrseq_free(struct mmc_host *host)
+{
+ struct mmc_pwrseq *pwrseq = host->pwrseq;
+
+ if (pwrseq && pwrseq->ops && pwrseq->ops->free)
+ pwrseq->ops->free(host);
+}
diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h
new file mode 100644
index 000000000000..aba3409e8d6e
--- /dev/null
+++ b/drivers/mmc/core/pwrseq.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 Linaro Ltd
+ *
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#ifndef _MMC_CORE_PWRSEQ_H
+#define _MMC_CORE_PWRSEQ_H
+
+struct mmc_pwrseq_ops {
+ void (*pre_power_on)(struct mmc_host *host);
+ void (*post_power_on)(struct mmc_host *host);
+ void (*power_off)(struct mmc_host *host);
+ void (*free)(struct mmc_host *host);
+};
+
+struct mmc_pwrseq {
+ struct mmc_pwrseq_ops *ops;
+};
+
+#ifdef CONFIG_OF
+
+int mmc_pwrseq_alloc(struct mmc_host *host);
+void mmc_pwrseq_pre_power_on(struct mmc_host *host);
+void mmc_pwrseq_post_power_on(struct mmc_host *host);
+void mmc_pwrseq_power_off(struct mmc_host *host);
+void mmc_pwrseq_free(struct mmc_host *host);
+
+int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev);
+int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev);
+
+#else
+
+static inline int mmc_pwrseq_alloc(struct mmc_host *host) { return 0; }
+static inline void mmc_pwrseq_pre_power_on(struct mmc_host *host) {}
+static inline void mmc_pwrseq_post_power_on(struct mmc_host *host) {}
+static inline void mmc_pwrseq_power_off(struct mmc_host *host) {}
+static inline void mmc_pwrseq_free(struct mmc_host *host) {}
+
+#endif
+
+#endif
diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c
new file mode 100644
index 000000000000..a2d545904fbf
--- /dev/null
+++ b/drivers/mmc/core/pwrseq_emmc.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015, Samsung Electronics Co., Ltd.
+ *
+ * Author: Marek Szyprowski <m.szyprowski@samsung.com>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Simple eMMC hardware reset provider
+ */
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/reboot.h>
+
+#include <linux/mmc/host.h>
+
+#include "pwrseq.h"
+
+struct mmc_pwrseq_emmc {
+ struct mmc_pwrseq pwrseq;
+ struct notifier_block reset_nb;
+ struct gpio_desc *reset_gpio;
+};
+
+static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq)
+{
+ gpiod_set_value(pwrseq->reset_gpio, 1);
+ udelay(1);
+ gpiod_set_value(pwrseq->reset_gpio, 0);
+ udelay(200);
+}
+
+static void mmc_pwrseq_emmc_reset(struct mmc_host *host)
+{
+ struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq,
+ struct mmc_pwrseq_emmc, pwrseq);
+
+ __mmc_pwrseq_emmc_reset(pwrseq);
+}
+
+static void mmc_pwrseq_emmc_free(struct mmc_host *host)
+{
+ struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq,
+ struct mmc_pwrseq_emmc, pwrseq);
+
+ unregister_restart_handler(&pwrseq->reset_nb);
+ gpiod_put(pwrseq->reset_gpio);
+ kfree(pwrseq);
+ host->pwrseq = NULL;
+}
+
+static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
+ .post_power_on = mmc_pwrseq_emmc_reset,
+ .free = mmc_pwrseq_emmc_free,
+};
+
+static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ struct mmc_pwrseq_emmc *pwrseq = container_of(this,
+ struct mmc_pwrseq_emmc, reset_nb);
+
+ __mmc_pwrseq_emmc_reset(pwrseq);
+ return NOTIFY_DONE;
+}
+
+int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev)
+{
+ struct mmc_pwrseq_emmc *pwrseq;
+ int ret = 0;
+
+ pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL);
+ if (!pwrseq)
+ return -ENOMEM;
+
+ pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW);
+ if (IS_ERR(pwrseq->reset_gpio)) {
+ ret = PTR_ERR(pwrseq->reset_gpio);
+ goto free;
+ }
+
+ /*
+ * register reset handler to ensure emmc reset also from
+ * emergency_reboot(), priority 129 schedules it just before
+ * system reboot
+ */
+ pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb;
+ pwrseq->reset_nb.priority = 129;
+ register_restart_handler(&pwrseq->reset_nb);
+
+ pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops;
+ host->pwrseq = &pwrseq->pwrseq;
+
+ return 0;
+free:
+ kfree(pwrseq);
+ return ret;
+}
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
new file mode 100644
index 000000000000..e9f1d8d84613
--- /dev/null
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2014 Linaro Ltd
+ *
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Simple MMC power sequence management
+ */
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
+
+#include <linux/mmc/host.h>
+
+#include "pwrseq.h"
+
+struct mmc_pwrseq_simple {
+ struct mmc_pwrseq pwrseq;
+ bool clk_enabled;
+ struct clk *ext_clk;
+ int nr_gpios;
+ struct gpio_desc *reset_gpios[0];
+};
+
+static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
+ int value)
+{
+ int i;
+
+ for (i = 0; i < pwrseq->nr_gpios; i++)
+ if (!IS_ERR(pwrseq->reset_gpios[i]))
+ gpiod_set_value_cansleep(pwrseq->reset_gpios[i], value);
+}
+
+static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)
+{
+ struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
+ struct mmc_pwrseq_simple, pwrseq);
+
+ if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) {
+ clk_prepare_enable(pwrseq->ext_clk);
+ pwrseq->clk_enabled = true;
+ }
+
+ mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
+}
+
+static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host)
+{
+ struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
+ struct mmc_pwrseq_simple, pwrseq);
+
+ mmc_pwrseq_simple_set_gpios_value(pwrseq, 0);
+}
+
+static void mmc_pwrseq_simple_power_off(struct mmc_host *host)
+{
+ struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
+ struct mmc_pwrseq_simple, pwrseq);
+
+ mmc_pwrseq_simple_set_gpios_value(pwrseq, 1);
+
+ if (!IS_ERR(pwrseq->ext_clk) && pwrseq->clk_enabled) {
+ clk_disable_unprepare(pwrseq->ext_clk);
+ pwrseq->clk_enabled = false;
+ }
+}
+
+static void mmc_pwrseq_simple_free(struct mmc_host *host)
+{
+ struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq,
+ struct mmc_pwrseq_simple, pwrseq);
+ int i;
+
+ for (i = 0; i < pwrseq->nr_gpios; i++)
+ if (!IS_ERR(pwrseq->reset_gpios[i]))
+ gpiod_put(pwrseq->reset_gpios[i]);
+
+ if (!IS_ERR(pwrseq->ext_clk))
+ clk_put(pwrseq->ext_clk);
+
+ kfree(pwrseq);
+ host->pwrseq = NULL;
+}
+
+static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
+ .pre_power_on = mmc_pwrseq_simple_pre_power_on,
+ .post_power_on = mmc_pwrseq_simple_post_power_on,
+ .power_off = mmc_pwrseq_simple_power_off,
+ .free = mmc_pwrseq_simple_free,
+};
+
+int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev)
+{
+ struct mmc_pwrseq_simple *pwrseq;
+ int i, nr_gpios, ret = 0;
+
+ nr_gpios = of_gpio_named_count(dev->of_node, "reset-gpios");
+ if (nr_gpios < 0)
+ nr_gpios = 0;
+
+ pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple) + nr_gpios *
+ sizeof(struct gpio_desc *), GFP_KERNEL);
+ if (!pwrseq)
+ return -ENOMEM;
+
+ pwrseq->ext_clk = clk_get(dev, "ext_clock");
+ if (IS_ERR(pwrseq->ext_clk) &&
+ PTR_ERR(pwrseq->ext_clk) != -ENOENT) {
+ ret = PTR_ERR(pwrseq->ext_clk);
+ goto free;
+ }
+
+ for (i = 0; i < nr_gpios; i++) {
+ pwrseq->reset_gpios[i] = gpiod_get_index(dev, "reset", i,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(pwrseq->reset_gpios[i]) &&
+ PTR_ERR(pwrseq->reset_gpios[i]) != -ENOENT &&
+ PTR_ERR(pwrseq->reset_gpios[i]) != -ENOSYS) {
+ ret = PTR_ERR(pwrseq->reset_gpios[i]);
+
+ while (--i)
+ gpiod_put(pwrseq->reset_gpios[i]);
+
+ goto clk_put;
+ }
+ }
+
+ pwrseq->nr_gpios = nr_gpios;
+ pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops;
+ host->pwrseq = &pwrseq->pwrseq;
+
+ return 0;
+clk_put:
+ if (!IS_ERR(pwrseq->ext_clk))
+ clk_put(pwrseq->ext_clk);
+free:
+ kfree(pwrseq);
+ return ret;
+}
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index d90a6de7901d..ad4d43eae99d 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -660,15 +660,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
* SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
* SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
*/
- if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning &&
- (card->sd_bus_speed == UHS_SDR50_BUS_SPEED ||
- card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) {
- mmc_host_clk_hold(card->host);
- err = card->host->ops->execute_tuning(card->host,
- MMC_SEND_TUNING_BLOCK);
- mmc_host_clk_release(card->host);
- }
-
+ if (!mmc_host_is_spi(card->host) &&
+ (card->sd_bus_speed == UHS_SDR50_BUS_SPEED ||
+ card->sd_bus_speed == UHS_SDR104_BUS_SPEED))
+ err = mmc_execute_tuning(card);
out:
kfree(status);
@@ -933,6 +928,12 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
}
/*
+ * Call the optional HC's init_card function to handle quirks.
+ */
+ if (host->ops->init_card)
+ host->ops->init_card(host, card);
+
+ /*
* For native busses: get card RCA and quit open drain mode.
*/
if (!mmc_host_is_spi(host)) {
@@ -1191,6 +1192,12 @@ static int mmc_sd_power_restore(struct mmc_host *host)
return ret;
}
+static int mmc_sd_reset(struct mmc_host *host)
+{
+ mmc_power_cycle(host, host->card->ocr);
+ return mmc_sd_power_restore(host);
+}
+
static const struct mmc_bus_ops mmc_sd_ops = {
.remove = mmc_sd_remove,
.detect = mmc_sd_detect,
@@ -1201,6 +1208,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
.power_restore = mmc_sd_power_restore,
.alive = mmc_sd_alive,
.shutdown = mmc_sd_suspend,
+ .reset = mmc_sd_reset,
};
/*
@@ -1271,4 +1279,3 @@ err:
return err;
}
-
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index fd0750b5a634..ce6cc47206b0 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -567,17 +567,11 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card)
* SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
* SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
*/
- if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning &&
- ((card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR50) ||
- (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104))) {
- mmc_host_clk_hold(card->host);
- err = card->host->ops->execute_tuning(card->host,
- MMC_SEND_TUNING_BLOCK);
- mmc_host_clk_release(card->host);
- }
-
+ if (!mmc_host_is_spi(card->host) &&
+ ((card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR50) ||
+ (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)))
+ err = mmc_execute_tuning(card);
out:
-
return err;
}
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index 60885316afba..bee02e644d62 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -22,7 +22,9 @@
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdio_func.h>
+#include <linux/of.h>
+#include "core.h"
#include "sdio_cis.h"
#include "sdio_bus.h"
@@ -295,6 +297,13 @@ static void sdio_acpi_set_handle(struct sdio_func *func)
static inline void sdio_acpi_set_handle(struct sdio_func *func) {}
#endif
+static void sdio_set_of_node(struct sdio_func *func)
+{
+ struct mmc_host *host = func->card->host;
+
+ func->dev.of_node = mmc_of_find_child_device(host, func->num);
+}
+
/*
* Register a new SDIO function with the driver model.
*/
@@ -304,6 +313,7 @@ int sdio_add_func(struct sdio_func *func)
dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num);
+ sdio_set_of_node(func);
sdio_acpi_set_handle(func);
ret = device_add(&func->dev);
if (ret == 0) {
@@ -327,6 +337,7 @@ void sdio_remove_func(struct sdio_func *func)
dev_pm_domain_detach(&func->dev, false);
device_del(&func->dev);
+ of_node_put(func->dev.of_node);
put_device(&func->dev);
}
diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c
index 69bbf2adb329..27117ba47073 100644
--- a/drivers/mmc/core/slot-gpio.c
+++ b/drivers/mmc/core/slot-gpio.c
@@ -18,11 +18,14 @@
#include <linux/module.h>
#include <linux/slab.h>
+#include "slot-gpio.h"
+
struct mmc_gpio {
struct gpio_desc *ro_gpio;
struct gpio_desc *cd_gpio;
bool override_ro_active_level;
bool override_cd_active_level;
+ irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id);
char *ro_label;
char cd_label[0];
};
@@ -38,32 +41,20 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int mmc_gpio_alloc(struct mmc_host *host)
+int mmc_gpio_alloc(struct mmc_host *host)
{
size_t len = strlen(dev_name(host->parent)) + 4;
- struct mmc_gpio *ctx;
-
- mutex_lock(&host->slot.lock);
-
- ctx = host->slot.handler_priv;
- if (!ctx) {
- /*
- * devm_kzalloc() can be called after device_initialize(), even
- * before device_add(), i.e., between mmc_alloc_host() and
- * mmc_add_host()
- */
- ctx = devm_kzalloc(&host->class_dev, sizeof(*ctx) + 2 * len,
- GFP_KERNEL);
- if (ctx) {
- ctx->ro_label = ctx->cd_label + len;
- snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));
- snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent));
- host->slot.handler_priv = ctx;
- }
+ struct mmc_gpio *ctx = devm_kzalloc(host->parent,
+ sizeof(*ctx) + 2 * len, GFP_KERNEL);
+
+ if (ctx) {
+ ctx->ro_label = ctx->cd_label + len;
+ snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));
+ snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent));
+ host->slot.handler_priv = ctx;
+ host->slot.cd_irq = -EINVAL;
}
- mutex_unlock(&host->slot.lock);
-
return ctx ? 0 : -ENOMEM;
}
@@ -103,29 +94,19 @@ EXPORT_SYMBOL(mmc_gpio_get_cd);
* @gpio: gpio number requested
*
* As devm_* managed functions are used in mmc_gpio_request_ro(), client
- * drivers do not need to explicitly call mmc_gpio_free_ro() for freeing up,
- * if the requesting and freeing are only needed at probing and unbinding time
- * for once. However, if client drivers do something special like runtime
- * switching for write-protection, they are responsible for calling
- * mmc_gpio_request_ro() and mmc_gpio_free_ro() as a pair on their own.
+ * drivers do not need to worry about freeing up memory.
*
* Returns zero on success, else an error.
*/
int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
{
- struct mmc_gpio *ctx;
+ struct mmc_gpio *ctx = host->slot.handler_priv;
int ret;
if (!gpio_is_valid(gpio))
return -EINVAL;
- ret = mmc_gpio_alloc(host);
- if (ret < 0)
- return ret;
-
- ctx = host->slot.handler_priv;
-
- ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN,
+ ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN,
ctx->ro_label);
if (ret < 0)
return ret;
@@ -156,8 +137,10 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
irq = -EINVAL;
if (irq >= 0) {
- ret = devm_request_threaded_irq(&host->class_dev, irq,
- NULL, mmc_gpio_cd_irqt,
+ if (!ctx->cd_gpio_isr)
+ ctx->cd_gpio_isr = mmc_gpio_cd_irqt;
+ ret = devm_request_threaded_irq(host->parent, irq,
+ NULL, ctx->cd_gpio_isr,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
ctx->cd_label, host);
if (ret < 0)
@@ -171,6 +154,19 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
}
EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
+/* Register an alternate interrupt service routine for
+ * the card-detect GPIO.
+ */
+void mmc_gpio_set_cd_isr(struct mmc_host *host,
+ irqreturn_t (*isr)(int irq, void *dev_id))
+{
+ struct mmc_gpio *ctx = host->slot.handler_priv;
+
+ WARN_ON(ctx->cd_gpio_isr);
+ ctx->cd_gpio_isr = isr;
+}
+EXPORT_SYMBOL(mmc_gpio_set_cd_isr);
+
/**
* mmc_gpio_request_cd - request a gpio for card-detection
* @host: mmc host
@@ -178,11 +174,7 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
* @debounce: debounce time in microseconds
*
* As devm_* managed functions are used in mmc_gpio_request_cd(), client
- * drivers do not need to explicitly call mmc_gpio_free_cd() for freeing up,
- * if the requesting and freeing are only needed at probing and unbinding time
- * for once. However, if client drivers do something special like runtime
- * switching for card-detection, they are responsible for calling
- * mmc_gpio_request_cd() and mmc_gpio_free_cd() as a pair on their own.
+ * drivers do not need to worry about freeing up memory.
*
* If GPIO debouncing is desired, set the debounce parameter to a non-zero
* value. The caller is responsible for ensuring that the GPIO driver associated
@@ -193,16 +185,10 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
unsigned int debounce)
{
- struct mmc_gpio *ctx;
+ struct mmc_gpio *ctx = host->slot.handler_priv;
int ret;
- ret = mmc_gpio_alloc(host);
- if (ret < 0)
- return ret;
-
- ctx = host->slot.handler_priv;
-
- ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN,
+ ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN,
ctx->cd_label);
if (ret < 0)
/*
@@ -226,55 +212,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
EXPORT_SYMBOL(mmc_gpio_request_cd);
/**
- * mmc_gpio_free_ro - free the write-protection gpio
- * @host: mmc host
- *
- * It's provided only for cases that client drivers need to manually free
- * up the write-protection gpio requested by mmc_gpio_request_ro().
- */
-void mmc_gpio_free_ro(struct mmc_host *host)
-{
- struct mmc_gpio *ctx = host->slot.handler_priv;
- int gpio;
-
- if (!ctx || !ctx->ro_gpio)
- return;
-
- gpio = desc_to_gpio(ctx->ro_gpio);
- ctx->ro_gpio = NULL;
-
- devm_gpio_free(&host->class_dev, gpio);
-}
-EXPORT_SYMBOL(mmc_gpio_free_ro);
-
-/**
- * mmc_gpio_free_cd - free the card-detection gpio
- * @host: mmc host
- *
- * It's provided only for cases that client drivers need to manually free
- * up the card-detection gpio requested by mmc_gpio_request_cd().
- */
-void mmc_gpio_free_cd(struct mmc_host *host)
-{
- struct mmc_gpio *ctx = host->slot.handler_priv;
- int gpio;
-
- if (!ctx || !ctx->cd_gpio)
- return;
-
- if (host->slot.cd_irq >= 0) {
- devm_free_irq(&host->class_dev, host->slot.cd_irq, host);
- host->slot.cd_irq = -EINVAL;
- }
-
- gpio = desc_to_gpio(ctx->cd_gpio);
- ctx->cd_gpio = NULL;
-
- devm_gpio_free(&host->class_dev, gpio);
-}
-EXPORT_SYMBOL(mmc_gpio_free_cd);
-
-/**
* mmc_gpiod_request_cd - request a gpio descriptor for card-detection
* @host: mmc host
* @con_id: function within the GPIO consumer
@@ -285,8 +222,7 @@ EXPORT_SYMBOL(mmc_gpio_free_cd);
* 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
- * mmc_gpio_free_cd(). Note also that it must be called prior to mmc_add_host()
+ * descriptor API. Note that it must be called prior to mmc_add_host()
* otherwise the caller must also call mmc_gpiod_request_cd_irq().
*
* Returns zero on success, else an error.
@@ -295,16 +231,10 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
unsigned int idx, bool override_active_level,
unsigned int debounce, bool *gpio_invert)
{
- struct mmc_gpio *ctx;
+ struct mmc_gpio *ctx = host->slot.handler_priv;
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->cd_label;
@@ -339,8 +269,7 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd);
* 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
- * mmc_gpio_free_ro().
+ * descriptor API.
*
* Returns zero on success, else an error.
*/
@@ -348,16 +277,10 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
unsigned int idx, bool override_active_level,
unsigned int debounce, bool *gpio_invert)
{
- struct mmc_gpio *ctx;
+ struct mmc_gpio *ctx = host->slot.handler_priv;
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;
@@ -380,28 +303,3 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
return 0;
}
EXPORT_SYMBOL(mmc_gpiod_request_ro);
-
-/**
- * mmc_gpiod_free_cd - free the card-detection gpio descriptor
- * @host: mmc host
- *
- * It's provided only for cases that client drivers need to manually free
- * up the card-detection gpio requested by mmc_gpiod_request_cd().
- */
-void mmc_gpiod_free_cd(struct mmc_host *host)
-{
- struct mmc_gpio *ctx = host->slot.handler_priv;
-
- if (!ctx || !ctx->cd_gpio)
- return;
-
- if (host->slot.cd_irq >= 0) {
- devm_free_irq(&host->class_dev, host->slot.cd_irq, host);
- host->slot.cd_irq = -EINVAL;
- }
-
- devm_gpiod_put(host->parent, ctx->cd_gpio);
-
- ctx->cd_gpio = NULL;
-}
-EXPORT_SYMBOL(mmc_gpiod_free_cd);
diff --git a/drivers/mmc/core/slot-gpio.h b/drivers/mmc/core/slot-gpio.h
new file mode 100644
index 000000000000..8c1854dc5d58
--- /dev/null
+++ b/drivers/mmc/core/slot-gpio.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (C) 2014 Linaro Ltd
+ *
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#ifndef _MMC_CORE_SLOTGPIO_H
+#define _MMC_CORE_SLOTGPIO_H
+
+int mmc_gpio_alloc(struct mmc_host *host);
+
+#endif
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 2d6fbdd11803..61ac63a3776a 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -57,6 +57,7 @@ config MMC_SDHCI_IO_ACCESSORS
config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
bool
+ depends on MMC_SDHCI
select MMC_SDHCI_IO_ACCESSORS
help
This option is selected by drivers running on big endian hosts
@@ -82,6 +83,7 @@ config MMC_SDHCI_PCI
config MMC_RICOH_MMC
bool "Ricoh MMC Controller Disabler"
depends on MMC_SDHCI_PCI
+ default y
help
This adds a pci quirk to disable Ricoh MMC Controller. This
proprietary controller is unnecessary because the SDHCI driver
@@ -228,6 +230,7 @@ config MMC_SDHCI_PXAV3
tristate "Marvell MMP2 SD Host Controller support (PXAV3)"
depends on CLKDEV_LOOKUP
depends on MMC_SDHCI_PLTFM
+ depends on ARCH_MMP || COMPILE_TEST
default CPU_MMP2
help
This selects the Marvell(R) PXAV3 SD Host Controller.
@@ -240,6 +243,7 @@ config MMC_SDHCI_PXAV2
tristate "Marvell PXA9XX SD Host Controller support (PXAV2)"
depends on CLKDEV_LOOKUP
depends on MMC_SDHCI_PLTFM
+ depends on ARCH_MMP || COMPILE_TEST
default CPU_PXA910
help
This selects the Marvell(R) PXAV2 SD Host Controller.
@@ -292,6 +296,17 @@ config MMC_SDHCI_BCM2835
If unsure, say N.
+config MMC_SDHCI_F_SDH30
+ tristate "SDHCI support for Fujitsu Semiconductor F_SDH30"
+ depends on MMC_SDHCI_PLTFM
+ depends on OF
+ help
+ This selects the Secure Digital Host Controller Interface (SDHCI)
+ Needed by some Fujitsu SoC for MMC / SD / SDIO support.
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
config MMC_MOXART
tristate "MOXART SD/MMC Host Controller support"
depends on ARCH_MOXART && MMC
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index f7b0a77cf419..6a7cfe0de332 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o
+obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index 509365cb22c6..fe32948c6114 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -21,43 +21,7 @@
#include "dw_mmc.h"
#include "dw_mmc-pltfm.h"
-
-#define NUM_PINS(x) (x + 2)
-
-#define SDMMC_CLKSEL 0x09C
-#define SDMMC_CLKSEL64 0x0A8
-#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0)
-#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16)
-#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
-#define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7)
-#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \
- SDMMC_CLKSEL_CCLK_DRIVE(y) | \
- SDMMC_CLKSEL_CCLK_DIVIDER(z))
-#define SDMMC_CLKSEL_WAKEUP_INT BIT(11)
-
-#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
-#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
-
-/* Block number in eMMC */
-#define DWMCI_BLOCK_NUM 0xFFFFFFFF
-
-#define SDMMC_EMMCP_BASE 0x1000
-#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010)
-#define SDMMC_MPSBEGIN0 (SDMMC_EMMCP_BASE + 0x0200)
-#define SDMMC_MPSEND0 (SDMMC_EMMCP_BASE + 0x0204)
-#define SDMMC_MPSCTRL0 (SDMMC_EMMCP_BASE + 0x020C)
-
-/* SMU control bits */
-#define DWMCI_MPSCTRL_SECURE_READ_BIT BIT(7)
-#define DWMCI_MPSCTRL_SECURE_WRITE_BIT BIT(6)
-#define DWMCI_MPSCTRL_NON_SECURE_READ_BIT BIT(5)
-#define DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4)
-#define DWMCI_MPSCTRL_USE_FUSE_KEY BIT(3)
-#define DWMCI_MPSCTRL_ECB_MODE BIT(2)
-#define DWMCI_MPSCTRL_ENCRYPTION BIT(1)
-#define DWMCI_MPSCTRL_VALID BIT(0)
-
-#define EXYNOS_CCLKIN_MIN 50000000 /* unit: HZ */
+#include "dw_mmc-exynos.h"
/* Variations in Exynos specific dw-mshc controller */
enum dw_mci_exynos_type {
@@ -114,11 +78,11 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
mci_writel(host, MPSBEGIN0, 0);
- mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM);
- mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT |
- DWMCI_MPSCTRL_NON_SECURE_READ_BIT |
- DWMCI_MPSCTRL_VALID |
- DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT);
+ mci_writel(host, MPSEND0, SDMMC_ENDING_SEC_NR_MAX);
+ mci_writel(host, MPSCTRL0, SDMMC_MPSCTRL_SECURE_WRITE_BIT |
+ SDMMC_MPSCTRL_NON_SECURE_READ_BIT |
+ SDMMC_MPSCTRL_VALID |
+ SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT);
}
return 0;
@@ -127,9 +91,9 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host)
static int dw_mci_exynos_setup_clock(struct dw_mci *host)
{
struct dw_mci_exynos_priv_data *priv = host->priv;
- unsigned long rate = clk_get_rate(host->ciu_clk);
- host->bus_hz = rate / (priv->ciu_div + 1);
+ host->bus_hz /= (priv->ciu_div + 1);
+
return 0;
}
@@ -232,8 +196,11 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
mci_writel(host, CLKSEL, priv->sdr_timing);
}
- /* Don't care if wanted clock is zero */
- if (!wanted)
+ /*
+ * Don't care if wanted clock is zero or
+ * ciu clock is unavailable
+ */
+ if (!wanted || IS_ERR(host->ciu_clk))
return;
/* Guaranteed minimum frequency for cclkin */
@@ -263,10 +230,8 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
int ret;
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- dev_err(host->dev, "mem alloc failed for private data\n");
+ if (!priv)
return -ENOMEM;
- }
for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
if (of_device_is_compatible(np, exynos_compat[idx].compatible))
@@ -375,64 +340,23 @@ out:
return loc;
}
-static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
- struct dw_mci_tuning_data *tuning_data)
+static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot)
{
struct dw_mci *host = slot->host;
struct mmc_host *mmc = slot->mmc;
- const u8 *blk_pattern = tuning_data->blk_pattern;
- u8 *blk_test;
- unsigned int blksz = tuning_data->blksz;
u8 start_smpl, smpl, candiates = 0;
s8 found = -1;
int ret = 0;
- blk_test = kmalloc(blksz, GFP_KERNEL);
- if (!blk_test)
- return -ENOMEM;
-
start_smpl = dw_mci_exynos_get_clksmpl(host);
do {
- struct mmc_request mrq = {NULL};
- struct mmc_command cmd = {0};
- struct mmc_command stop = {0};
- struct mmc_data data = {0};
- struct scatterlist sg;
-
- cmd.opcode = opcode;
- cmd.arg = 0;
- cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
-
- stop.opcode = MMC_STOP_TRANSMISSION;
- stop.arg = 0;
- stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
-
- data.blksz = blksz;
- data.blocks = 1;
- data.flags = MMC_DATA_READ;
- data.sg = &sg;
- data.sg_len = 1;
-
- sg_init_one(&sg, blk_test, blksz);
- mrq.cmd = &cmd;
- mrq.stop = &stop;
- mrq.data = &data;
- host->mrq = &mrq;
-
mci_writel(host, TMOUT, ~0);
smpl = dw_mci_exynos_move_next_clksmpl(host);
- mmc_wait_for_req(mmc, &mrq);
+ if (!mmc_send_tuning(mmc))
+ candiates |= (1 << smpl);
- if (!cmd.error && !data.error) {
- if (!memcmp(blk_pattern, blk_test, blksz))
- candiates |= (1 << smpl);
- } else {
- dev_dbg(host->dev,
- "Tuning error: cmd.error:%d, data.error:%d\n",
- cmd.error, data.error);
- }
} while (start_smpl != smpl);
found = dw_mci_exynos_get_best_clksmpl(candiates);
@@ -441,7 +365,6 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
else
ret = -EIO;
- kfree(blk_test);
return ret;
}
@@ -499,7 +422,7 @@ static const struct dev_pm_ops dw_mci_exynos_pmops = {
static struct platform_driver dw_mci_exynos_pltfm_driver = {
.probe = dw_mci_exynos_probe,
- .remove = __exit_p(dw_mci_pltfm_remove),
+ .remove = dw_mci_pltfm_remove,
.driver = {
.name = "dwmmc_exynos",
.of_match_table = dw_mci_exynos_match,
diff --git a/drivers/mmc/host/dw_mmc-exynos.h b/drivers/mmc/host/dw_mmc-exynos.h
new file mode 100644
index 000000000000..7872ce586b55
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-exynos.h
@@ -0,0 +1,56 @@
+/*
+ * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver
+ *
+ * Copyright (C) 2012-2014 Samsung 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.
+ */
+
+#ifndef _DW_MMC_EXYNOS_H_
+#define _DW_MMC_EXYNOS_H_
+
+/* Extended Register's Offset */
+#define SDMMC_CLKSEL 0x09C
+#define SDMMC_CLKSEL64 0x0A8
+
+/* CLKSEL register defines */
+#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0)
+#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16)
+#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
+#define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7)
+#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \
+ SDMMC_CLKSEL_CCLK_DRIVE(y) | \
+ SDMMC_CLKSEL_CCLK_DIVIDER(z))
+#define SDMMC_CLKSEL_WAKEUP_INT BIT(11)
+
+/* Protector Register */
+#define SDMMC_EMMCP_BASE 0x1000
+#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010)
+#define SDMMC_MPSBEGIN0 (SDMMC_EMMCP_BASE + 0x0200)
+#define SDMMC_MPSEND0 (SDMMC_EMMCP_BASE + 0x0204)
+#define SDMMC_MPSCTRL0 (SDMMC_EMMCP_BASE + 0x020C)
+
+/* SMU control defines */
+#define SDMMC_MPSCTRL_SECURE_READ_BIT BIT(7)
+#define SDMMC_MPSCTRL_SECURE_WRITE_BIT BIT(6)
+#define SDMMC_MPSCTRL_NON_SECURE_READ_BIT BIT(5)
+#define SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4)
+#define SDMMC_MPSCTRL_USE_FUSE_KEY BIT(3)
+#define SDMMC_MPSCTRL_ECB_MODE BIT(2)
+#define SDMMC_MPSCTRL_ENCRYPTION BIT(1)
+#define SDMMC_MPSCTRL_VALID BIT(0)
+
+/* Maximum number of Ending sector */
+#define SDMMC_ENDING_SEC_NR_MAX 0xFFFFFFFF
+
+/* Fixed clock divider */
+#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
+#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
+
+/* Minimal required clock frequency for cclkin, unit: HZ */
+#define EXYNOS_CCLKIN_MIN 50000000
+
+#endif /* _DW_MMC_EXYNOS_H_ */
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index 5650ac488cf3..e2a726a503ee 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -133,7 +133,7 @@ static SIMPLE_DEV_PM_OPS(dw_mci_rockchip_pmops,
static struct platform_driver dw_mci_rockchip_pltfm_driver = {
.probe = dw_mci_rockchip_probe,
- .remove = __exit_p(dw_mci_pltfm_remove),
+ .remove = dw_mci_pltfm_remove,
.driver = {
.name = "dwmmc_rockchip",
.of_match_table = dw_mci_rockchip_match,
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 67c04518ec4c..4d2e3c2e1830 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -27,6 +27,7 @@
#include <linux/stat.h>
#include <linux/delay.h>
#include <linux/irq.h>
+#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
@@ -313,7 +314,9 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
if (cmdr == MMC_READ_SINGLE_BLOCK ||
cmdr == MMC_READ_MULTIPLE_BLOCK ||
cmdr == MMC_WRITE_BLOCK ||
- cmdr == MMC_WRITE_MULTIPLE_BLOCK) {
+ cmdr == MMC_WRITE_MULTIPLE_BLOCK ||
+ cmdr == MMC_SEND_TUNING_BLOCK ||
+ cmdr == MMC_SEND_TUNING_BLOCK_HS200) {
stop->opcode = MMC_STOP_TRANSMISSION;
stop->arg = 0;
stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
@@ -758,6 +761,7 @@ disable:
static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
{
+ unsigned long irqflags;
int sg_len;
u32 temp;
@@ -794,9 +798,11 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
mci_writel(host, CTRL, temp);
/* Disable RX/TX IRQs, let DMA handle it */
+ spin_lock_irqsave(&host->irq_lock, irqflags);
temp = mci_readl(host, INTMASK);
temp &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR);
mci_writel(host, INTMASK, temp);
+ spin_unlock_irqrestore(&host->irq_lock, irqflags);
host->dma_ops->start(host, sg_len);
@@ -805,6 +811,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
{
+ unsigned long irqflags;
u32 temp;
data->error = -EINPROGRESS;
@@ -833,9 +840,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
host->part_buf_count = 0;
mci_writel(host, RINTSTS, SDMMC_INT_TXDR | SDMMC_INT_RXDR);
+
+ spin_lock_irqsave(&host->irq_lock, irqflags);
temp = mci_readl(host, INTMASK);
temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR;
mci_writel(host, INTMASK, temp);
+ spin_unlock_irqrestore(&host->irq_lock, irqflags);
temp = mci_readl(host, CTRL);
temp &= ~SDMMC_CTRL_DMA_ENABLE;
@@ -926,7 +936,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
/* enable clock; only low power if no SDIO */
clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
- if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->sdio_id)))
+ if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags))
clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id;
mci_writel(host, CLKENA, clk_en_a);
@@ -1109,6 +1119,12 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
return;
}
}
+ 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_ON:
if (!IS_ERR(mmc->supply.vqmmc) && !slot->host->vqmmc_enabled) {
ret = regulator_enable(mmc->supply.vqmmc);
if (ret < 0)
@@ -1117,10 +1133,6 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
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))
@@ -1245,27 +1257,37 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
return present;
}
-/*
- * Disable lower power mode.
- *
- * Low power mode will stop the card clock when idle. According to the
- * description of the CLKENA register we should disable low power mode
- * for SDIO cards if we need SDIO interrupts to work.
- *
- * This function is fast if low power mode is already disabled.
- */
-static void dw_mci_disable_low_power(struct dw_mci_slot *slot)
+static void dw_mci_init_card(struct mmc_host *mmc, struct mmc_card *card)
{
+ struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
- u32 clk_en_a;
- const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
- clk_en_a = mci_readl(host, CLKENA);
+ /*
+ * Low power mode will stop the card clock when idle. According to the
+ * description of the CLKENA register we should disable low power mode
+ * for SDIO cards if we need SDIO interrupts to work.
+ */
+ if (mmc->caps & MMC_CAP_SDIO_IRQ) {
+ const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
+ u32 clk_en_a_old;
+ u32 clk_en_a;
- if (clk_en_a & clken_low_pwr) {
- mci_writel(host, CLKENA, clk_en_a & ~clken_low_pwr);
- mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
- SDMMC_CMD_PRV_DAT_WAIT, 0);
+ clk_en_a_old = mci_readl(host, CLKENA);
+
+ if (card->type == MMC_TYPE_SDIO ||
+ card->type == MMC_TYPE_SD_COMBO) {
+ set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
+ clk_en_a = clk_en_a_old & ~clken_low_pwr;
+ } else {
+ clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags);
+ clk_en_a = clk_en_a_old | clken_low_pwr;
+ }
+
+ if (clk_en_a != clk_en_a_old) {
+ mci_writel(host, CLKENA, clk_en_a);
+ mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
+ SDMMC_CMD_PRV_DAT_WAIT, 0);
+ }
}
}
@@ -1273,25 +1295,20 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
+ unsigned long irqflags;
u32 int_mask;
+ spin_lock_irqsave(&host->irq_lock, irqflags);
+
/* Enable/disable Slot Specific SDIO interrupt */
int_mask = mci_readl(host, INTMASK);
- if (enb) {
- /*
- * Turn off low power mode if it was enabled. This is a bit of
- * a heavy operation and we disable / enable IRQs a lot, so
- * we'll leave low power mode disabled and it will get
- * re-enabled again in dw_mci_setup_bus().
- */
- dw_mci_disable_low_power(slot);
+ if (enb)
+ int_mask |= SDMMC_INT_SDIO(slot->sdio_id);
+ else
+ int_mask &= ~SDMMC_INT_SDIO(slot->sdio_id);
+ mci_writel(host, INTMASK, int_mask);
- mci_writel(host, INTMASK,
- (int_mask | SDMMC_INT_SDIO(slot->sdio_id)));
- } else {
- mci_writel(host, INTMASK,
- (int_mask & ~SDMMC_INT_SDIO(slot->sdio_id)));
- }
+ spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
@@ -1299,30 +1316,10 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
const struct dw_mci_drv_data *drv_data = host->drv_data;
- struct dw_mci_tuning_data tuning_data;
int err = -ENOSYS;
- if (opcode == MMC_SEND_TUNING_BLOCK_HS200) {
- if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) {
- tuning_data.blk_pattern = tuning_blk_pattern_8bit;
- tuning_data.blksz = sizeof(tuning_blk_pattern_8bit);
- } else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
- tuning_data.blk_pattern = tuning_blk_pattern_4bit;
- tuning_data.blksz = sizeof(tuning_blk_pattern_4bit);
- } else {
- return -EINVAL;
- }
- } else if (opcode == MMC_SEND_TUNING_BLOCK) {
- tuning_data.blk_pattern = tuning_blk_pattern_4bit;
- tuning_data.blksz = sizeof(tuning_blk_pattern_4bit);
- } else {
- dev_err(host->dev,
- "Undefined command(%d) for tuning\n", opcode);
- return -EINVAL;
- }
-
if (drv_data && drv_data->execute_tuning)
- err = drv_data->execute_tuning(slot, opcode, &tuning_data);
+ err = drv_data->execute_tuning(slot);
return err;
}
@@ -1337,7 +1334,7 @@ static const struct mmc_host_ops dw_mci_ops = {
.execute_tuning = dw_mci_execute_tuning,
.card_busy = dw_mci_card_busy,
.start_signal_voltage_switch = dw_mci_switch_voltage,
-
+ .init_card = dw_mci_init_card,
};
static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
@@ -2319,9 +2316,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
#ifdef CONFIG_MMC_DW_IDMAC
mmc->max_segs = host->ring_size;
mmc->max_blk_size = 65536;
- mmc->max_blk_count = host->ring_size;
mmc->max_seg_size = 0x1000;
- mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count;
+ mmc->max_req_size = mmc->max_seg_size * host->ring_size;
+ mmc->max_blk_count = mmc->max_req_size / 512;
#else
mmc->max_segs = 64;
mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
@@ -2533,10 +2530,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
u32 clock_frequency;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- dev_err(dev, "could not allocate memory for pdata\n");
+ if (!pdata)
return ERR_PTR(-ENOMEM);
- }
/* find out number of slots supported */
if (of_property_read_u32(dev->of_node, "num-slots",
@@ -2660,6 +2655,7 @@ int dw_mci_probe(struct dw_mci *host)
host->quirks = host->pdata->quirks;
spin_lock_init(&host->lock);
+ spin_lock_init(&host->irq_lock);
INIT_LIST_HEAD(&host->queue);
/*
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 0d0f7a271d63..18c4afe683b8 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -244,15 +244,11 @@ struct dw_mci_slot {
unsigned long flags;
#define DW_MMC_CARD_PRESENT 0
#define DW_MMC_CARD_NEED_INIT 1
+#define DW_MMC_CARD_NO_LOW_PWR 2
int id;
int sdio_id;
};
-struct dw_mci_tuning_data {
- const u8 *blk_pattern;
- unsigned int blksz;
-};
-
/**
* dw_mci driver data - dw-mshc implementation specific driver data.
* @caps: mmc subsystem specified capabilities of the controller(s).
@@ -274,7 +270,6 @@ struct dw_mci_drv_data {
void (*prepare_command)(struct dw_mci *host, u32 *cmdr);
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
int (*parse_dt)(struct dw_mci *host);
- int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode,
- struct dw_mci_tuning_data *tuning_data);
+ int (*execute_tuning)(struct dw_mci_slot *slot);
};
#endif /* _DW_MMC_H_ */
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 8232e9a02d40..7fe16194ebc8 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -430,7 +430,6 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
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");
@@ -439,10 +438,6 @@ static void mmci_dma_setup(struct mmci_host *host)
/* initialize pre request cookie */
host->next_data.cookie = 1;
- /* Try to acquire a generic DMA engine slave channel */
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
/*
* If only an RX channel is specified, the driver will
* attempt to use it bidirectionally, however if it is
@@ -1739,10 +1734,10 @@ static int mmci_probe(struct amba_device *dev,
pm_runtime_set_autosuspend_delay(&dev->dev, 50);
pm_runtime_use_autosuspend(&dev->dev);
- pm_runtime_put(&dev->dev);
mmc_add_host(mmc);
+ pm_runtime_put(&dev->dev);
return 0;
clk_disable:
diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c
index f3e18d08e852..006f1862444b 100644
--- a/drivers/mmc/host/moxart-mmc.c
+++ b/drivers/mmc/host/moxart-mmc.c
@@ -17,6 +17,7 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
+#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#include <linux/dma-mapping.h>
@@ -562,7 +563,6 @@ static int moxart_probe(struct platform_device *pdev)
struct dma_slave_config cfg;
struct clk *clk;
void __iomem *reg_mmc;
- dma_cap_mask_t mask;
int irq, ret;
u32 i;
@@ -586,9 +586,8 @@ static int moxart_probe(struct platform_device *pdev)
goto out;
}
- clk = of_clk_get(node, 0);
+ clk = devm_clk_get(dev, NULL);
if (IS_ERR(clk)) {
- dev_err(dev, "of_clk_get failed\n");
ret = PTR_ERR(clk);
goto out;
}
@@ -599,10 +598,9 @@ static int moxart_probe(struct platform_device *pdev)
goto out;
}
- mmc_of_parse(mmc);
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
+ ret = mmc_of_parse(mmc);
+ if (ret)
+ goto out;
host = mmc_priv(mmc);
host->mmc = mmc;
@@ -611,8 +609,8 @@ static int moxart_probe(struct platform_device *pdev)
host->timeout = msecs_to_jiffies(1000);
host->sysclk = clk_get_rate(clk);
host->fifo_width = readl(host->base + REG_FEATURE) << 2;
- host->dma_chan_tx = of_dma_request_slave_channel(node, "tx");
- host->dma_chan_rx = of_dma_request_slave_channel(node, "rx");
+ host->dma_chan_tx = dma_request_slave_channel_reason(dev, "tx");
+ host->dma_chan_rx = dma_request_slave_channel_reason(dev, "rx");
spin_lock_init(&host->lock);
@@ -622,6 +620,11 @@ static int moxart_probe(struct platform_device *pdev)
mmc->ocr_avail = 0xffff00; /* Support 2.0v - 3.6v power. */
if (IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) {
+ if (PTR_ERR(host->dma_chan_tx) == -EPROBE_DEFER ||
+ PTR_ERR(host->dma_chan_rx) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto out;
+ }
dev_dbg(dev, "PIO mode transfer enabled\n");
host->have_dma = false;
} else {
@@ -700,9 +703,6 @@ static int moxart_remove(struct platform_device *pdev)
writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF,
host->base + REG_CLOCK_CONTROL);
}
-
- kfree(host);
-
return 0;
}
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index 4f8618f4522d..a448498e3af2 100644
--- a/drivers/mmc/host/mvsdio.c
+++ b/drivers/mmc/host/mvsdio.c
@@ -25,7 +25,6 @@
#include <linux/of_irq.h>
#include <linux/mmc/host.h>
#include <linux/mmc/slot-gpio.h>
-#include <linux/pinctrl/consumer.h>
#include <asm/sizes.h>
#include <asm/unaligned.h>
@@ -704,7 +703,6 @@ static int mvsd_probe(struct platform_device *pdev)
const struct mbus_dram_target_info *dram;
struct resource *r;
int ret, irq;
- struct pinctrl *pinctrl;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
@@ -721,10 +719,6 @@ static int mvsd_probe(struct platform_device *pdev)
host->mmc = mmc;
host->dev = &pdev->dev;
- pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
- if (IS_ERR(pinctrl))
- dev_warn(&pdev->dev, "no pins associated\n");
-
/*
* Some non-DT platforms do not pass a clock, and the clock
* frequency is passed through platform_data. On DT platforms,
@@ -828,8 +822,6 @@ static int mvsd_probe(struct platform_device *pdev)
out:
if (mmc) {
- mmc_gpio_free_cd(mmc);
- mmc_gpio_free_ro(mmc);
if (!IS_ERR(host->clk))
clk_disable_unprepare(host->clk);
mmc_free_host(mmc);
@@ -844,8 +836,6 @@ static int mvsd_remove(struct platform_device *pdev)
struct mvsd_host *host = mmc_priv(mmc);
- mmc_gpio_free_cd(mmc);
- mmc_gpio_free_ro(mmc);
mmc_remove_host(mmc);
del_timer_sync(&host->timer);
mvsd_power_down(host);
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index 60c4ca97a727..a82411a2c024 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -677,8 +677,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
return 0;
out_free_dma:
- if (ssp->dmach)
- dma_release_channel(ssp->dmach);
+ dma_release_channel(ssp->dmach);
out_clk_disable:
clk_disable_unprepare(ssp->clk);
out_mmc_free:
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 7c71dcdcba8b..f84cfb01716d 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -36,6 +36,7 @@
#include <linux/mmc/host.h>
#include <linux/mmc/core.h>
#include <linux/mmc/mmc.h>
+#include <linux/mmc/slot-gpio.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/gpio.h>
@@ -251,55 +252,24 @@ static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host);
static int omap_hsmmc_card_detect(struct device *dev)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
- struct omap_hsmmc_platform_data *mmc = host->pdata;
- /* NOTE: assumes card detect signal is active-low */
- return !gpio_get_value_cansleep(mmc->switch_pin);
+ return mmc_gpio_get_cd(host->mmc);
}
static int omap_hsmmc_get_wp(struct device *dev)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
- struct omap_hsmmc_platform_data *mmc = host->pdata;
- /* NOTE: assumes write protect signal is active-high */
- return gpio_get_value_cansleep(mmc->gpio_wp);
+ return mmc_gpio_get_ro(host->mmc);
}
static int omap_hsmmc_get_cover_state(struct device *dev)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
- struct omap_hsmmc_platform_data *mmc = host->pdata;
- /* NOTE: assumes card detect signal is active-low */
- return !gpio_get_value_cansleep(mmc->switch_pin);
+ return mmc_gpio_get_cd(host->mmc);
}
-#ifdef CONFIG_PM
-
-static int omap_hsmmc_suspend_cdirq(struct device *dev)
-{
- struct omap_hsmmc_host *host = dev_get_drvdata(dev);
-
- disable_irq(host->card_detect_irq);
- return 0;
-}
-
-static int omap_hsmmc_resume_cdirq(struct device *dev)
-{
- struct omap_hsmmc_host *host = dev_get_drvdata(dev);
-
- enable_irq(host->card_detect_irq);
- return 0;
-}
-
-#else
-
-#define omap_hsmmc_suspend_cdirq NULL
-#define omap_hsmmc_resume_cdirq NULL
-
-#endif
-
#ifdef CONFIG_REGULATOR
static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd)
@@ -464,7 +434,10 @@ static inline int omap_hsmmc_have_reg(void)
#endif
-static int omap_hsmmc_gpio_init(struct omap_hsmmc_host *host,
+static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id);
+
+static int omap_hsmmc_gpio_init(struct mmc_host *mmc,
+ struct omap_hsmmc_host *host,
struct omap_hsmmc_platform_data *pdata)
{
int ret;
@@ -477,46 +450,24 @@ static int omap_hsmmc_gpio_init(struct omap_hsmmc_host *host,
host->card_detect = omap_hsmmc_card_detect;
host->card_detect_irq =
gpio_to_irq(pdata->switch_pin);
- ret = gpio_request(pdata->switch_pin, "mmc_cd");
+ mmc_gpio_set_cd_isr(mmc, omap_hsmmc_detect);
+ ret = mmc_gpio_request_cd(mmc, pdata->switch_pin, 0);
if (ret)
return ret;
- ret = gpio_direction_input(pdata->switch_pin);
- if (ret)
- goto err_free_sp;
} else {
pdata->switch_pin = -EINVAL;
}
if (gpio_is_valid(pdata->gpio_wp)) {
host->get_ro = omap_hsmmc_get_wp;
- ret = gpio_request(pdata->gpio_wp, "mmc_wp");
- if (ret)
- goto err_free_cd;
- ret = gpio_direction_input(pdata->gpio_wp);
+ ret = mmc_gpio_request_ro(mmc, pdata->gpio_wp);
if (ret)
- goto err_free_wp;
+ return ret;
} else {
pdata->gpio_wp = -EINVAL;
}
return 0;
-
-err_free_wp:
- gpio_free(pdata->gpio_wp);
-err_free_cd:
- if (gpio_is_valid(pdata->switch_pin))
-err_free_sp:
- gpio_free(pdata->switch_pin);
- return ret;
-}
-
-static void omap_hsmmc_gpio_free(struct omap_hsmmc_host *host,
- struct omap_hsmmc_platform_data *pdata)
-{
- if (gpio_is_valid(pdata->gpio_wp))
- gpio_free(pdata->gpio_wp);
- if (gpio_is_valid(pdata->switch_pin))
- gpio_free(pdata->switch_pin);
}
/*
@@ -1978,13 +1929,6 @@ static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
{
struct omap_hsmmc_platform_data *pdata;
struct device_node *np = dev->of_node;
- u32 bus_width, max_freq;
- int cd_gpio, wp_gpio;
-
- cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
- wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
- if (cd_gpio == -EPROBE_DEFER || wp_gpio == -EPROBE_DEFER)
- return ERR_PTR(-EPROBE_DEFER);
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@@ -1993,34 +1937,20 @@ static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
if (of_find_property(np, "ti,dual-volt", NULL))
pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
- pdata->switch_pin = cd_gpio;
- pdata->gpio_wp = wp_gpio;
+ pdata->switch_pin = -EINVAL;
+ pdata->gpio_wp = -EINVAL;
if (of_find_property(np, "ti,non-removable", NULL)) {
pdata->nonremovable = true;
pdata->no_regulator_off_init = true;
}
- of_property_read_u32(np, "bus-width", &bus_width);
- if (bus_width == 4)
- pdata->caps |= MMC_CAP_4_BIT_DATA;
- else if (bus_width == 8)
- pdata->caps |= MMC_CAP_8_BIT_DATA;
if (of_find_property(np, "ti,needs-special-reset", NULL))
pdata->features |= HSMMC_HAS_UPDATED_RESET;
- if (!of_property_read_u32(np, "max-frequency", &max_freq))
- pdata->max_freq = max_freq;
-
if (of_find_property(np, "ti,needs-special-hs-handling", NULL))
pdata->features |= HSMMC_HAS_HSPE_SUPPORT;
- if (of_find_property(np, "keep-power-in-suspend", NULL))
- pdata->pm_caps |= MMC_PM_KEEP_POWER;
-
- if (of_find_property(np, "enable-sdio-wakeup", NULL))
- pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
-
return pdata;
}
#else
@@ -2078,6 +2008,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
goto err;
}
+ ret = mmc_of_parse(mmc);
+ if (ret)
+ goto err1;
+
host = mmc_priv(mmc);
host->mmc = mmc;
host->pdata = pdata;
@@ -2091,7 +2025,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
host->next_data.cookie = 1;
host->pbias_enabled = 0;
- ret = omap_hsmmc_gpio_init(host, pdata);
+ ret = omap_hsmmc_gpio_init(mmc, host, pdata);
if (ret)
goto err_gpio;
@@ -2106,7 +2040,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
if (pdata->max_freq > 0)
mmc->f_max = pdata->max_freq;
- else
+ else if (mmc->f_max == 0)
mmc->f_max = OMAP_MMC_MAX_CLOCK;
spin_lock_init(&host->irq_lock);
@@ -2160,7 +2094,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
if (mmc_pdata(host)->nonremovable)
mmc->caps |= MMC_CAP_NONREMOVABLE;
- mmc->pm_caps = mmc_pdata(host)->pm_caps;
+ mmc->pm_caps |= mmc_pdata(host)->pm_caps;
omap_hsmmc_conf_bus_power(host);
@@ -2222,22 +2156,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
mmc->ocr_avail = mmc_pdata(host)->ocr_mask;
- /* Request IRQ for card detect */
- if (host->card_detect_irq) {
- ret = devm_request_threaded_irq(&pdev->dev,
- host->card_detect_irq,
- NULL, omap_hsmmc_detect,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- mmc_hostname(mmc), host);
- if (ret) {
- dev_err(mmc_dev(host->mmc),
- "Unable to grab MMC CD IRQ\n");
- goto err_irq_cd;
- }
- host->suspend = omap_hsmmc_suspend_cdirq;
- host->resume = omap_hsmmc_resume_cdirq;
- }
-
omap_hsmmc_disable_irq(host);
/*
@@ -2276,7 +2194,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
err_slot_name:
mmc_remove_host(mmc);
-err_irq_cd:
if (host->use_reg)
omap_hsmmc_reg_put(host);
err_irq:
@@ -2289,7 +2206,6 @@ err_irq:
if (host->dbclk)
clk_disable_unprepare(host->dbclk);
err1:
- omap_hsmmc_gpio_free(host, pdata);
err_gpio:
mmc_free_host(mmc);
err:
@@ -2315,32 +2231,12 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
if (host->dbclk)
clk_disable_unprepare(host->dbclk);
- omap_hsmmc_gpio_free(host, host->pdata);
mmc_free_host(host->mmc);
return 0;
}
#ifdef CONFIG_PM
-static int omap_hsmmc_prepare(struct device *dev)
-{
- struct omap_hsmmc_host *host = dev_get_drvdata(dev);
-
- if (host->suspend)
- return host->suspend(dev);
-
- return 0;
-}
-
-static void omap_hsmmc_complete(struct device *dev)
-{
- struct omap_hsmmc_host *host = dev_get_drvdata(dev);
-
- if (host->resume)
- host->resume(dev);
-
-}
-
static int omap_hsmmc_suspend(struct device *dev)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
@@ -2398,8 +2294,6 @@ static int omap_hsmmc_resume(struct device *dev)
}
#else
-#define omap_hsmmc_prepare NULL
-#define omap_hsmmc_complete NULL
#define omap_hsmmc_suspend NULL
#define omap_hsmmc_resume NULL
#endif
@@ -2484,8 +2378,6 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
.suspend = omap_hsmmc_suspend,
.resume = omap_hsmmc_resume,
- .prepare = omap_hsmmc_prepare,
- .complete = omap_hsmmc_complete,
.runtime_suspend = omap_hsmmc_runtime_suspend,
.runtime_resume = omap_hsmmc_runtime_resume,
};
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index c70b602f8f1e..1d3d6c4bfdc6 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -28,6 +28,7 @@
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
#include <linux/mmc/card.h>
#include <linux/mfd/rtsx_pci.h>
#include <asm/unaligned.h>
@@ -53,9 +54,9 @@ struct realtek_pci_sdmmc {
#define SDMMC_POWER_ON 1
#define SDMMC_POWER_OFF 0
- unsigned int sg_count;
+ int sg_count;
s32 cookie;
- unsigned int cookie_sg_count;
+ int cookie_sg_count;
bool using_cookie;
};
@@ -71,30 +72,83 @@ static inline void sd_clear_error(struct realtek_pci_sdmmc *host)
}
#ifdef DEBUG
-static void sd_print_debug_regs(struct realtek_pci_sdmmc *host)
+static void dump_reg_range(struct realtek_pci_sdmmc *host, u16 start, u16 end)
{
- struct rtsx_pcr *pcr = host->pcr;
- u16 i;
- u8 *ptr;
+ u16 len = end - start + 1;
+ int i;
+ u8 data[8];
+
+ for (i = 0; i < len; i += 8) {
+ int j;
+ int n = min(8, len - i);
+
+ memset(&data, 0, sizeof(data));
+ for (j = 0; j < n; j++)
+ rtsx_pci_read_register(host->pcr, start + i + j,
+ data + j);
+ dev_dbg(sdmmc_dev(host), "0x%04X(%d): %8ph\n",
+ start + i, n, data);
+ }
+}
- /* Print SD host internal registers */
- rtsx_pci_init_cmd(pcr);
- for (i = 0xFDA0; i <= 0xFDAE; i++)
- rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0);
- for (i = 0xFD52; i <= 0xFD69; i++)
- rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0);
- rtsx_pci_send_cmd(pcr, 100);
-
- ptr = rtsx_pci_get_cmd_data(pcr);
- for (i = 0xFDA0; i <= 0xFDAE; i++)
- dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
- for (i = 0xFD52; i <= 0xFD69; i++)
- dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
+static void sd_print_debug_regs(struct realtek_pci_sdmmc *host)
+{
+ dump_reg_range(host, 0xFDA0, 0xFDB3);
+ dump_reg_range(host, 0xFD52, 0xFD69);
}
#else
#define sd_print_debug_regs(host)
#endif /* DEBUG */
+static inline int sd_get_cd_int(struct realtek_pci_sdmmc *host)
+{
+ return rtsx_pci_readl(host->pcr, RTSX_BIPR) & SD_EXIST;
+}
+
+static void sd_cmd_set_sd_cmd(struct rtsx_pcr *pcr, struct mmc_command *cmd)
+{
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0, 0xFF,
+ SD_CMD_START | cmd->opcode);
+ rtsx_pci_write_be32(pcr, SD_CMD1, cmd->arg);
+}
+
+static void sd_cmd_set_data_len(struct rtsx_pcr *pcr, u16 blocks, u16 blksz)
+{
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, blocks);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, blocks >> 8);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, blksz);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, blksz >> 8);
+}
+
+static int sd_response_type(struct mmc_command *cmd)
+{
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_NONE:
+ return SD_RSP_TYPE_R0;
+ case MMC_RSP_R1:
+ return SD_RSP_TYPE_R1;
+ case MMC_RSP_R1 & ~MMC_RSP_CRC:
+ return SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
+ case MMC_RSP_R1B:
+ return SD_RSP_TYPE_R1b;
+ case MMC_RSP_R2:
+ return SD_RSP_TYPE_R2;
+ case MMC_RSP_R3:
+ return SD_RSP_TYPE_R3;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sd_status_index(int resp_type)
+{
+ if (resp_type == SD_RSP_TYPE_R0)
+ return 0;
+ else if (resp_type == SD_RSP_TYPE_R2)
+ return 16;
+
+ return 5;
+}
/*
* sd_pre_dma_transfer - do dma_map_sg() or using cookie
*
@@ -166,123 +220,6 @@ static void sdmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
data->host_cookie = 0;
}
-static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,
- u8 *buf, int buf_len, int timeout)
-{
- struct rtsx_pcr *pcr = host->pcr;
- int err, i;
- u8 trans_mode;
-
- dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD%d\n", __func__, cmd[0] - 0x40);
-
- if (!buf)
- buf_len = 0;
-
- if ((cmd[0] & 0x3F) == MMC_SEND_TUNING_BLOCK)
- trans_mode = SD_TM_AUTO_TUNING;
- else
- trans_mode = SD_TM_NORMAL_READ;
-
- rtsx_pci_init_cmd(pcr);
-
- for (i = 0; i < 5; i++)
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0 + i, 0xFF, cmd[i]);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H,
- 0xFF, (u8)(byte_cnt >> 8));
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF,
- SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
- SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6);
- if (trans_mode != SD_TM_AUTO_TUNING)
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
- CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER,
- 0xFF, trans_mode | SD_TRANSFER_START);
- rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
- SD_TRANSFER_END, SD_TRANSFER_END);
-
- err = rtsx_pci_send_cmd(pcr, timeout);
- if (err < 0) {
- sd_print_debug_regs(host);
- dev_dbg(sdmmc_dev(host),
- "rtsx_pci_send_cmd fail (err = %d)\n", err);
- return err;
- }
-
- if (buf && buf_len) {
- err = rtsx_pci_read_ppbuf(pcr, buf, buf_len);
- if (err < 0) {
- dev_dbg(sdmmc_dev(host),
- "rtsx_pci_read_ppbuf fail (err = %d)\n", err);
- return err;
- }
- }
-
- return 0;
-}
-
-static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,
- u8 *buf, int buf_len, int timeout)
-{
- struct rtsx_pcr *pcr = host->pcr;
- int err, i;
- u8 trans_mode;
-
- if (!buf)
- buf_len = 0;
-
- if (buf && buf_len) {
- err = rtsx_pci_write_ppbuf(pcr, buf, buf_len);
- if (err < 0) {
- dev_dbg(sdmmc_dev(host),
- "rtsx_pci_write_ppbuf fail (err = %d)\n", err);
- return err;
- }
- }
-
- trans_mode = cmd ? SD_TM_AUTO_WRITE_2 : SD_TM_AUTO_WRITE_3;
- rtsx_pci_init_cmd(pcr);
-
- if (cmd) {
- dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d\n", __func__,
- cmd[0] - 0x40);
-
- for (i = 0; i < 5; i++)
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
- SD_CMD0 + i, 0xFF, cmd[i]);
- }
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H,
- 0xFF, (u8)(byte_cnt >> 8));
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF,
- SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
- SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
- trans_mode | SD_TRANSFER_START);
- rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
- SD_TRANSFER_END, SD_TRANSFER_END);
-
- err = rtsx_pci_send_cmd(pcr, timeout);
- if (err < 0) {
- sd_print_debug_regs(host);
- dev_dbg(sdmmc_dev(host),
- "rtsx_pci_send_cmd fail (err = %d)\n", err);
- return err;
- }
-
- return 0;
-}
-
static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
struct mmc_command *cmd)
{
@@ -293,47 +230,18 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
int timeout = 100;
int i;
u8 *ptr;
- int stat_idx = 0;
- u8 rsp_type;
- int rsp_len = 5;
+ int rsp_type;
+ int stat_idx;
bool clock_toggled = false;
dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
__func__, cmd_idx, arg);
- /* Response type:
- * R0
- * R1, R5, R6, R7
- * R1b
- * R2
- * R3, R4
- */
- switch (mmc_resp_type(cmd)) {
- case MMC_RSP_NONE:
- rsp_type = SD_RSP_TYPE_R0;
- rsp_len = 0;
- break;
- case MMC_RSP_R1:
- rsp_type = SD_RSP_TYPE_R1;
- break;
- case MMC_RSP_R1 & ~MMC_RSP_CRC:
- rsp_type = SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
- break;
- case MMC_RSP_R1B:
- rsp_type = SD_RSP_TYPE_R1b;
- break;
- case MMC_RSP_R2:
- rsp_type = SD_RSP_TYPE_R2;
- rsp_len = 16;
- break;
- case MMC_RSP_R3:
- rsp_type = SD_RSP_TYPE_R3;
- break;
- default:
- dev_dbg(sdmmc_dev(host), "cmd->flag is not valid\n");
- err = -EINVAL;
+ rsp_type = sd_response_type(cmd);
+ if (rsp_type < 0)
goto out;
- }
+
+ stat_idx = sd_status_index(rsp_type);
if (rsp_type == SD_RSP_TYPE_R1b)
timeout = 3000;
@@ -348,13 +256,7 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
}
rtsx_pci_init_cmd(pcr);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0, 0xFF, 0x40 | cmd_idx);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD1, 0xFF, (u8)(arg >> 24));
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD2, 0xFF, (u8)(arg >> 16));
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD3, 0xFF, (u8)(arg >> 8));
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD4, 0xFF, (u8)arg);
-
+ sd_cmd_set_sd_cmd(pcr, cmd);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, rsp_type);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
0x01, PINGPONG_BUFFER);
@@ -368,12 +270,10 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
/* Read data from ping-pong buffer */
for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++)
rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
- stat_idx = 16;
} else if (rsp_type != SD_RSP_TYPE_R0) {
/* Read data from SD_CMDx registers */
for (i = SD_CMD0; i <= SD_CMD4; i++)
rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
- stat_idx = 5;
}
rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0);
@@ -438,71 +338,213 @@ out:
SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
}
-static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
+static int sd_read_data(struct realtek_pci_sdmmc *host, struct mmc_command *cmd,
+ u16 byte_cnt, u8 *buf, int buf_len, int timeout)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ int err;
+ u8 trans_mode;
+
+ dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
+ __func__, cmd->opcode, cmd->arg);
+
+ if (!buf)
+ buf_len = 0;
+
+ if (cmd->opcode == MMC_SEND_TUNING_BLOCK)
+ trans_mode = SD_TM_AUTO_TUNING;
+ else
+ trans_mode = SD_TM_NORMAL_READ;
+
+ rtsx_pci_init_cmd(pcr);
+ sd_cmd_set_sd_cmd(pcr, cmd);
+ sd_cmd_set_data_len(pcr, 1, byte_cnt);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF,
+ SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+ SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6);
+ if (trans_mode != SD_TM_AUTO_TUNING)
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+ CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER);
+
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER,
+ 0xFF, trans_mode | SD_TRANSFER_START);
+ rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
+ SD_TRANSFER_END, SD_TRANSFER_END);
+
+ err = rtsx_pci_send_cmd(pcr, timeout);
+ if (err < 0) {
+ sd_print_debug_regs(host);
+ dev_dbg(sdmmc_dev(host),
+ "rtsx_pci_send_cmd fail (err = %d)\n", err);
+ return err;
+ }
+
+ if (buf && buf_len) {
+ err = rtsx_pci_read_ppbuf(pcr, buf, buf_len);
+ if (err < 0) {
+ dev_dbg(sdmmc_dev(host),
+ "rtsx_pci_read_ppbuf fail (err = %d)\n", err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int sd_write_data(struct realtek_pci_sdmmc *host,
+ struct mmc_command *cmd, u16 byte_cnt, u8 *buf, int buf_len,
+ int timeout)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ int err;
+
+ dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
+ __func__, cmd->opcode, cmd->arg);
+
+ if (!buf)
+ buf_len = 0;
+
+ sd_send_cmd_get_rsp(host, cmd);
+ if (cmd->error)
+ return cmd->error;
+
+ if (buf && buf_len) {
+ err = rtsx_pci_write_ppbuf(pcr, buf, buf_len);
+ if (err < 0) {
+ dev_dbg(sdmmc_dev(host),
+ "rtsx_pci_write_ppbuf fail (err = %d)\n", err);
+ return err;
+ }
+ }
+
+ rtsx_pci_init_cmd(pcr);
+ sd_cmd_set_data_len(pcr, 1, byte_cnt);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF,
+ SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+ SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
+ SD_TRANSFER_START | SD_TM_AUTO_WRITE_3);
+ rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
+ SD_TRANSFER_END, SD_TRANSFER_END);
+
+ err = rtsx_pci_send_cmd(pcr, timeout);
+ if (err < 0) {
+ sd_print_debug_regs(host);
+ dev_dbg(sdmmc_dev(host),
+ "rtsx_pci_send_cmd fail (err = %d)\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int sd_read_long_data(struct realtek_pci_sdmmc *host,
+ struct mmc_request *mrq)
{
struct rtsx_pcr *pcr = host->pcr;
struct mmc_host *mmc = host->mmc;
struct mmc_card *card = mmc->card;
+ struct mmc_command *cmd = mrq->cmd;
struct mmc_data *data = mrq->data;
int uhs = mmc_card_uhs(card);
- int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
- u8 cfg2, trans_mode;
+ u8 cfg2 = 0;
int err;
+ int resp_type;
size_t data_len = data->blksz * data->blocks;
- if (read) {
- cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
- SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0;
- trans_mode = SD_TM_AUTO_READ_3;
- } else {
- cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 |
- SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0;
- trans_mode = SD_TM_AUTO_WRITE_3;
- }
+ dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
+ __func__, cmd->opcode, cmd->arg);
+
+ resp_type = sd_response_type(cmd);
+ if (resp_type < 0)
+ return resp_type;
if (!uhs)
cfg2 |= SD_NO_CHECK_WAIT_CRC_TO;
rtsx_pci_init_cmd(pcr);
-
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, 0x00);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, 0x02);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L,
- 0xFF, (u8)data->blocks);
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H,
- 0xFF, (u8)(data->blocks >> 8));
-
+ sd_cmd_set_sd_cmd(pcr, cmd);
+ sd_cmd_set_data_len(pcr, data->blocks, data->blksz);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0,
DMA_DONE_INT, DMA_DONE_INT);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3,
- 0xFF, (u8)(data_len >> 24));
+ 0xFF, (u8)(data_len >> 24));
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2,
- 0xFF, (u8)(data_len >> 16));
+ 0xFF, (u8)(data_len >> 16));
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1,
- 0xFF, (u8)(data_len >> 8));
+ 0xFF, (u8)(data_len >> 8));
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)data_len);
- if (read) {
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
- 0x03 | DMA_PACK_SIZE_MASK,
- DMA_DIR_FROM_CARD | DMA_EN | DMA_512);
- } else {
- rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
- 0x03 | DMA_PACK_SIZE_MASK,
- DMA_DIR_TO_CARD | DMA_EN | DMA_512);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
+ 0x03 | DMA_PACK_SIZE_MASK,
+ DMA_DIR_FROM_CARD | DMA_EN | DMA_512);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
+ 0x01, RING_BUFFER);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, cfg2 | resp_type);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
+ SD_TRANSFER_START | SD_TM_AUTO_READ_2);
+ rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
+ SD_TRANSFER_END, SD_TRANSFER_END);
+ rtsx_pci_send_cmd_no_wait(pcr);
+
+ err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, 1, 10000);
+ if (err < 0) {
+ sd_print_debug_regs(host);
+ sd_clear_error(host);
+ return err;
}
+ return 0;
+}
+
+static int sd_write_long_data(struct realtek_pci_sdmmc *host,
+ struct mmc_request *mrq)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ struct mmc_host *mmc = host->mmc;
+ struct mmc_card *card = mmc->card;
+ struct mmc_command *cmd = mrq->cmd;
+ struct mmc_data *data = mrq->data;
+ int uhs = mmc_card_uhs(card);
+ u8 cfg2;
+ int err;
+ size_t data_len = data->blksz * data->blocks;
+
+ sd_send_cmd_get_rsp(host, cmd);
+ if (cmd->error)
+ return cmd->error;
+
+ dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
+ __func__, cmd->opcode, cmd->arg);
+
+ cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+ SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0;
+
+ if (!uhs)
+ cfg2 |= SD_NO_CHECK_WAIT_CRC_TO;
+
+ rtsx_pci_init_cmd(pcr);
+ sd_cmd_set_data_len(pcr, data->blocks, data->blksz);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0,
+ DMA_DONE_INT, DMA_DONE_INT);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3,
+ 0xFF, (u8)(data_len >> 24));
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2,
+ 0xFF, (u8)(data_len >> 16));
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1,
+ 0xFF, (u8)(data_len >> 8));
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)data_len);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
+ 0x03 | DMA_PACK_SIZE_MASK,
+ DMA_DIR_TO_CARD | DMA_EN | DMA_512);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
0x01, RING_BUFFER);
-
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, cfg2);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
- trans_mode | SD_TRANSFER_START);
+ SD_TRANSFER_START | SD_TM_AUTO_WRITE_3);
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
SD_TRANSFER_END, SD_TRANSFER_END);
-
rtsx_pci_send_cmd_no_wait(pcr);
-
- err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, read, 10000);
+ err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, 0, 10000);
if (err < 0) {
sd_clear_error(host);
return err;
@@ -511,6 +553,23 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
return 0;
}
+static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
+{
+ struct mmc_data *data = mrq->data;
+
+ if (host->sg_count < 0) {
+ data->error = host->sg_count;
+ dev_dbg(sdmmc_dev(host), "%s: sg_count = %d is invalid\n",
+ __func__, host->sg_count);
+ return data->error;
+ }
+
+ if (data->flags & MMC_DATA_READ)
+ return sd_read_long_data(host, mrq);
+
+ return sd_write_long_data(host, mrq);
+}
+
static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host)
{
rtsx_pci_write_register(host->pcr, SD_CFG1,
@@ -528,10 +587,7 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host,
{
struct mmc_command *cmd = mrq->cmd;
struct mmc_data *data = mrq->data;
- u8 _cmd[5], *buf;
-
- _cmd[0] = 0x40 | (u8)cmd->opcode;
- put_unaligned_be32(cmd->arg, (u32 *)(&_cmd[1]));
+ u8 *buf;
buf = kzalloc(data->blksz, GFP_NOIO);
if (!buf) {
@@ -543,7 +599,7 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host,
if (host->initial_mode)
sd_disable_initial_mode(host);
- cmd->error = sd_read_data(host, _cmd, (u16)data->blksz, buf,
+ cmd->error = sd_read_data(host, cmd, (u16)data->blksz, buf,
data->blksz, 200);
if (host->initial_mode)
@@ -553,7 +609,7 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host,
} else {
sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz);
- cmd->error = sd_write_data(host, _cmd, (u16)data->blksz, buf,
+ cmd->error = sd_write_data(host, cmd, (u16)data->blksz, buf,
data->blksz, 200);
}
@@ -653,14 +709,14 @@ static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host,
u8 opcode, u8 sample_point)
{
int err;
- u8 cmd[5] = {0};
+ struct mmc_command cmd = {0};
err = sd_change_phase(host, sample_point, true);
if (err < 0)
return err;
- cmd[0] = 0x40 | opcode;
- err = sd_read_data(host, cmd, 0x40, NULL, 0, 100);
+ cmd.opcode = opcode;
+ err = sd_read_data(host, &cmd, 0x40, NULL, 0, 100);
if (err < 0) {
/* Wait till SD DATA IDLE */
sd_wait_data_idle(host);
@@ -727,6 +783,12 @@ static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode)
return 0;
}
+static inline int sdio_extblock_cmd(struct mmc_command *cmd,
+ struct mmc_data *data)
+{
+ return (cmd->opcode == SD_IO_RW_EXTENDED) && (data->blksz == 512);
+}
+
static inline int sd_rw_cmd(struct mmc_command *cmd)
{
return mmc_op_multi(cmd->opcode) ||
@@ -748,7 +810,7 @@ static void sd_request(struct work_struct *work)
unsigned int data_size = 0;
int err;
- if (host->eject) {
+ if (host->eject || !sd_get_cd_int(host)) {
cmd->error = -ENOMEDIUM;
goto finish;
}
@@ -776,17 +838,15 @@ static void sd_request(struct work_struct *work)
if (mrq->data)
data_size = data->blocks * data->blksz;
- if (!data_size || sd_rw_cmd(cmd)) {
+ if (!data_size) {
sd_send_cmd_get_rsp(host, cmd);
+ } else if (sd_rw_cmd(cmd) || sdio_extblock_cmd(cmd, data)) {
+ cmd->error = sd_rw_multi(host, mrq);
+ if (!host->using_cookie)
+ sdmmc_post_req(host->mmc, host->mrq, 0);
- if (!cmd->error && data_size) {
- sd_rw_multi(host, mrq);
- if (!host->using_cookie)
- sdmmc_post_req(host->mmc, host->mrq, 0);
-
- if (mmc_op_multi(cmd->opcode) && mrq->stop)
- sd_send_cmd_get_rsp(host, mrq->stop);
- }
+ if (mmc_op_multi(cmd->opcode) && mrq->stop)
+ sd_send_cmd_get_rsp(host, mrq->stop);
} else {
sd_normal_rw(host, mrq);
}
@@ -801,8 +861,10 @@ static void sd_request(struct work_struct *work)
mutex_unlock(&pcr->pcr_mutex);
finish:
- if (cmd->error)
- dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error);
+ if (cmd->error) {
+ dev_dbg(sdmmc_dev(host), "CMD %d 0x%08x error(%d)\n",
+ cmd->opcode, cmd->arg, cmd->error);
+ }
mutex_lock(&host->host_mutex);
host->mrq = NULL;
@@ -820,7 +882,7 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq = mrq;
mutex_unlock(&host->host_mutex);
- if (sd_rw_cmd(mrq->cmd))
+ if (sd_rw_cmd(mrq->cmd) || sdio_extblock_cmd(mrq->cmd, data))
host->using_cookie = sd_pre_dma_transfer(host, data, false);
queue_work(host->workq, &host->work);
@@ -1066,7 +1128,7 @@ static int sdmmc_get_cd(struct mmc_host *mmc)
u32 val;
if (host->eject)
- return -ENOMEDIUM;
+ return cd;
mutex_lock(&pcr->pcr_mutex);
@@ -1317,6 +1379,7 @@ static void rtsx_pci_sdmmc_card_event(struct platform_device *pdev)
{
struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
+ host->cookie = -1;
mmc_detect_change(host->mmc, 0);
}
@@ -1349,6 +1412,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
host->pcr = pcr;
host->mmc = mmc;
host->pdev = pdev;
+ host->cookie = -1;
host->power_state = SDMMC_POWER_OFF;
INIT_WORK(&host->work, sd_request);
platform_set_drvdata(pdev, host);
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
index 970314e0aac8..a45ed39d062c 100644
--- a/drivers/mmc/host/sdhci-acpi.c
+++ b/drivers/mmc/host/sdhci-acpi.c
@@ -158,7 +158,7 @@ static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev,
host = c->host;
- /* Platform specific code during emmc proble slot goes here */
+ /* Platform specific code during emmc probe slot goes here */
if (hid && uid && !strcmp(hid, "80860F14") && !strcmp(uid, "1") &&
sdhci_readl(host, SDHCI_CAPABILITIES) == 0x446cc8b2 &&
@@ -179,7 +179,7 @@ static int sdhci_acpi_sdio_probe_slot(struct platform_device *pdev,
host = c->host;
- /* Platform specific code during emmc proble slot goes here */
+ /* Platform specific code during sdio probe slot goes here */
return 0;
}
@@ -195,7 +195,7 @@ static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev,
host = c->host;
- /* Platform specific code during emmc proble slot goes here */
+ /* Platform specific code during sd probe slot goes here */
return 0;
}
@@ -448,18 +448,13 @@ static int sdhci_acpi_runtime_resume(struct device *dev)
return sdhci_runtime_resume_host(c->host);
}
-static int sdhci_acpi_runtime_idle(struct device *dev)
-{
- return 0;
-}
-
#endif
static const struct dev_pm_ops sdhci_acpi_pm_ops = {
.suspend = sdhci_acpi_suspend,
.resume = sdhci_acpi_resume,
SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend,
- sdhci_acpi_runtime_resume, sdhci_acpi_runtime_idle)
+ sdhci_acpi_runtime_resume, NULL)
};
static struct platform_driver sdhci_acpi_driver = {
diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c
index e7e4fbdcbfe0..34bb8f92586e 100644
--- a/drivers/mmc/host/sdhci-bcm-kona.c
+++ b/drivers/mmc/host/sdhci-bcm-kona.c
@@ -254,7 +254,9 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
kona_dev = sdhci_pltfm_priv(pltfm_priv);
mutex_init(&kona_dev->write_lock);
- mmc_of_parse(host->mmc);
+ ret = mmc_of_parse(host->mmc);
+ if (ret)
+ goto err_pltfm_free;
if (!host->mmc->f_max) {
dev_err(&pdev->dev, "Missing max-freq for SDHCI cfg\n");
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index af1f7c0f9545..10ef8244a239 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -1080,10 +1080,10 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
goto disable_clk;
pm_runtime_set_active(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
pm_runtime_use_autosuspend(&pdev->dev);
pm_suspend_ignore_children(&pdev->dev, 1);
+ pm_runtime_enable(&pdev->dev);
return 0;
@@ -1103,16 +1103,15 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
struct pltfm_imx_data *imx_data = pltfm_host->priv;
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
- sdhci_remove_host(host, dead);
-
- pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
- if (!IS_ENABLED(CONFIG_PM)) {
- clk_disable_unprepare(imx_data->clk_per);
- clk_disable_unprepare(imx_data->clk_ipg);
- clk_disable_unprepare(imx_data->clk_ahb);
- }
+ sdhci_remove_host(host, dead);
+
+ clk_disable_unprepare(imx_data->clk_per);
+ clk_disable_unprepare(imx_data->clk_ipg);
+ clk_disable_unprepare(imx_data->clk_ahb);
sdhci_pltfm_free(pdev);
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 8872c85c63d4..17fe02ed6672 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -276,6 +276,14 @@ static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
ESDHC_CTRL_BUSWIDTH_MASK, ctrl);
}
+static void esdhc_reset(struct sdhci_host *host, u8 mask)
+{
+ sdhci_reset(host, mask);
+
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+}
+
static const struct sdhci_ops sdhci_esdhc_ops = {
.read_l = esdhc_readl,
.read_w = esdhc_readw,
@@ -290,7 +298,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
.platform_init = esdhc_of_platform_init,
.adma_workaround = esdhci_of_adma_workaround,
.set_bus_width = esdhc_pltfm_set_bus_width,
- .reset = sdhci_reset,
+ .reset = esdhc_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
@@ -362,13 +370,19 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
}
/* call to generic mmc_of_parse to support additional capabilities */
- mmc_of_parse(host->mmc);
+ ret = mmc_of_parse(host->mmc);
+ if (ret)
+ goto err;
+
mmc_of_parse_voltage(np, &host->ocr_mask);
ret = sdhci_add_host(host);
if (ret)
- sdhci_pltfm_free(pdev);
+ goto err;
+ return 0;
+ err:
+ sdhci_pltfm_free(pdev);
return ret;
}
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 4f38554ce679..29eaff78238e 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -1367,11 +1367,6 @@ static int sdhci_pci_runtime_resume(struct device *dev)
return 0;
}
-static int sdhci_pci_runtime_idle(struct device *dev)
-{
- return 0;
-}
-
#else /* CONFIG_PM */
#define sdhci_pci_suspend NULL
@@ -1383,7 +1378,7 @@ static const struct dev_pm_ops sdhci_pci_pm_ops = {
.suspend = sdhci_pci_suspend,
.resume = sdhci_pci_resume,
SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend,
- sdhci_pci_runtime_resume, sdhci_pci_runtime_idle)
+ sdhci_pci_runtime_resume, NULL)
};
/*****************************************************************************\
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c
index ca3424e7ef71..b5103a247bc1 100644
--- a/drivers/mmc/host/sdhci-pxav3.c
+++ b/drivers/mmc/host/sdhci-pxav3.c
@@ -62,6 +62,7 @@ struct sdhci_pxa {
struct clk *clk_core;
struct clk *clk_io;
u8 power_mode;
+ void __iomem *sdio3_conf_reg;
};
/*
@@ -72,6 +73,14 @@ struct sdhci_pxa {
#define SDHCI_WINDOW_BASE(i) (0x84 + ((i) << 3))
#define SDHCI_MAX_WIN_NUM 8
+/*
+ * Fields below belong to SDIO3 Configuration Register (third register
+ * region for the Armada 38x flavor)
+ */
+
+#define SDIO3_CONF_CLK_INV BIT(0)
+#define SDIO3_CONF_SD_FB_CLK BIT(2)
+
static int mv_conf_mbus_windows(struct platform_device *pdev,
const struct mbus_dram_target_info *dram)
{
@@ -118,6 +127,51 @@ static int mv_conf_mbus_windows(struct platform_device *pdev,
return 0;
}
+static int armada_38x_quirks(struct platform_device *pdev,
+ struct sdhci_host *host)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_pxa *pxa = pltfm_host->priv;
+ struct resource *res;
+
+ host->quirks |= SDHCI_QUIRK_MISSING_CAPS;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "conf-sdio3");
+ if (res) {
+ pxa->sdio3_conf_reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pxa->sdio3_conf_reg))
+ return PTR_ERR(pxa->sdio3_conf_reg);
+ } else {
+ /*
+ * According to erratum 'FE-2946959' both SDR50 and DDR50
+ * modes require specific clock adjustments in SDIO3
+ * Configuration register, if the adjustment is not done,
+ * remove them from the capabilities.
+ */
+ host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
+ host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50);
+
+ dev_warn(&pdev->dev, "conf-sdio3 register not found: disabling SDR50 and DDR50 modes.\nConsider updating your dtb\n");
+ }
+
+ /*
+ * According to erratum 'ERR-7878951' Armada 38x SDHCI
+ * controller has different capabilities than the ones shown
+ * in its registers
+ */
+ host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
+ if (of_property_read_bool(np, "no-1-8-v")) {
+ host->caps &= ~SDHCI_CAN_VDD_180;
+ host->mmc->caps &= ~MMC_CAP_1_8V_DDR;
+ } else {
+ host->caps &= ~SDHCI_CAN_VDD_330;
+ }
+ host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_USE_SDR50_TUNING);
+
+ return 0;
+}
+
static void pxav3_reset(struct sdhci_host *host, u8 mask)
{
struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
@@ -194,6 +248,8 @@ static void pxav3_gen_init_74_clocks(struct sdhci_host *host, u8 power_mode)
static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_pxa *pxa = pltfm_host->priv;
u16 ctrl_2;
/*
@@ -223,6 +279,24 @@ static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
break;
}
+ /*
+ * Update SDIO3 Configuration register according to erratum
+ * FE-2946959
+ */
+ if (pxa->sdio3_conf_reg) {
+ u8 reg_val = readb(pxa->sdio3_conf_reg);
+
+ if (uhs == MMC_TIMING_UHS_SDR50 ||
+ uhs == MMC_TIMING_UHS_DDR50) {
+ reg_val &= ~SDIO3_CONF_CLK_INV;
+ reg_val |= SDIO3_CONF_SD_FB_CLK;
+ } else {
+ reg_val |= SDIO3_CONF_CLK_INV;
+ reg_val &= ~SDIO3_CONF_SD_FB_CLK;
+ }
+ writeb(reg_val, pxa->sdio3_conf_reg);
+ }
+
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
dev_dbg(mmc_dev(host->mmc),
"%s uhs = %d, ctrl_2 = %04X\n",
@@ -268,8 +342,8 @@ static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev)
if (!pdata)
return NULL;
- of_property_read_u32(np, "mrvl,clk-delay-cycles", &clk_delay_cycles);
- if (clk_delay_cycles > 0)
+ if (!of_property_read_u32(np, "mrvl,clk-delay-cycles",
+ &clk_delay_cycles))
pdata->clk_delay_cycles = clk_delay_cycles;
return pdata;
@@ -318,15 +392,18 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
if (!IS_ERR(pxa->clk_core))
clk_prepare_enable(pxa->clk_core);
+ /* enable 1/8V DDR capable */
+ host->mmc->caps |= MMC_CAP_1_8V_DDR;
+
if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) {
+ ret = armada_38x_quirks(pdev, host);
+ if (ret < 0)
+ goto err_clk_get;
ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info());
if (ret < 0)
goto err_mbus_win;
}
- /* enable 1/8V DDR capable */
- host->mmc->caps |= MMC_CAP_1_8V_DDR;
-
match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev);
if (match) {
ret = mmc_of_parse(host->mmc);
@@ -365,10 +442,11 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
}
}
- pm_runtime_enable(&pdev->dev);
- pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS);
pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
pm_suspend_ignore_children(&pdev->dev, 1);
ret = sdhci_add_host(host);
@@ -391,14 +469,13 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
return 0;
err_add_host:
- pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
err_of_parse:
err_cd_req:
err_mbus_win:
clk_disable_unprepare(pxa->clk_io);
- if (!IS_ERR(pxa->clk_core))
- clk_disable_unprepare(pxa->clk_core);
+ clk_disable_unprepare(pxa->clk_core);
err_clk_get:
sdhci_pltfm_free(pdev);
return ret;
@@ -411,12 +488,13 @@ static int sdhci_pxav3_remove(struct platform_device *pdev)
struct sdhci_pxa *pxa = pltfm_host->priv;
pm_runtime_get_sync(&pdev->dev);
- sdhci_remove_host(host, 1);
pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
+ sdhci_remove_host(host, 1);
clk_disable_unprepare(pxa->clk_io);
- if (!IS_ERR(pxa->clk_core))
- clk_disable_unprepare(pxa->clk_core);
+ clk_disable_unprepare(pxa->clk_core);
sdhci_pltfm_free(pdev);
@@ -457,11 +535,11 @@ static int sdhci_pxav3_runtime_suspend(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_pxa *pxa = pltfm_host->priv;
- unsigned long flags;
+ int ret;
- spin_lock_irqsave(&host->lock, flags);
- host->runtime_suspended = true;
- spin_unlock_irqrestore(&host->lock, flags);
+ ret = sdhci_runtime_suspend_host(host);
+ if (ret)
+ return ret;
clk_disable_unprepare(pxa->clk_io);
if (!IS_ERR(pxa->clk_core))
@@ -475,17 +553,12 @@ static int sdhci_pxav3_runtime_resume(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_pxa *pxa = pltfm_host->priv;
- unsigned long flags;
clk_prepare_enable(pxa->clk_io);
if (!IS_ERR(pxa->clk_core))
clk_prepare_enable(pxa->clk_core);
- spin_lock_irqsave(&host->lock, flags);
- host->runtime_suspended = false;
- spin_unlock_irqrestore(&host->lock, flags);
-
- return 0;
+ return sdhci_runtime_resume_host(host);
}
#endif
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index c45b8932d843..c6d2dd7317c1 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -12,6 +12,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
@@ -312,7 +313,14 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
sdhci_s3c_set_clock(host, clock);
+ /* Reset SD Clock Enable */
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ spin_unlock_irq(&host->lock);
ret = clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock);
+ spin_lock_irq(&host->lock);
if (ret != 0) {
dev_err(dev, "%s: failed to set clock rate %uHz\n",
mmc_hostname(host->mmc), clock);
@@ -607,7 +615,9 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
pm_runtime_use_autosuspend(&pdev->dev);
pm_suspend_ignore_children(&pdev->dev, 1);
- mmc_of_parse(host->mmc);
+ ret = mmc_of_parse(host->mmc);
+ if (ret)
+ goto err_req_regs;
ret = sdhci_add_host(host);
if (ret) {
diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c
index dd29d47c07aa..f6f82ec3618d 100644
--- a/drivers/mmc/host/sdhci-sirf.c
+++ b/drivers/mmc/host/sdhci-sirf.c
@@ -15,7 +15,9 @@
#include <linux/mmc/slot-gpio.h>
#include "sdhci-pltfm.h"
+#define SDHCI_CLK_DELAY_SETTING 0x4C
#define SDHCI_SIRF_8BITBUS BIT(3)
+#define SIRF_TUNING_COUNT 128
struct sdhci_sirf_priv {
struct clk *clk;
@@ -49,7 +51,76 @@ static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width)
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
+static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+ int tuning_seq_cnt = 3;
+ u8 phase, tuned_phases[SIRF_TUNING_COUNT];
+ u8 tuned_phase_cnt = 0;
+ int rc, longest_range = 0;
+ int start = -1, end = 0, tuning_value = -1, range = 0;
+ u16 clock_setting;
+ struct mmc_host *mmc = host->mmc;
+
+ clock_setting = sdhci_readw(host, SDHCI_CLK_DELAY_SETTING);
+ clock_setting &= ~0x3fff;
+
+retry:
+ phase = 0;
+ do {
+ sdhci_writel(host,
+ clock_setting | phase | (phase << 7) | (phase << 16),
+ SDHCI_CLK_DELAY_SETTING);
+
+ if (!mmc_send_tuning(mmc)) {
+ /* Tuning is successful at this tuning point */
+ tuned_phases[tuned_phase_cnt++] = phase;
+ dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
+ mmc_hostname(mmc), phase);
+ if (start == -1)
+ start = phase;
+ end = phase;
+ range++;
+ if (phase == (SIRF_TUNING_COUNT - 1)
+ && range > longest_range)
+ tuning_value = (start + end) / 2;
+ } else {
+ dev_dbg(mmc_dev(mmc), "%s: Found bad phase = %d\n",
+ mmc_hostname(mmc), phase);
+ if (range > longest_range) {
+ tuning_value = (start + end) / 2;
+ longest_range = range;
+ }
+ start = -1;
+ end = range = 0;
+ }
+ } while (++phase < ARRAY_SIZE(tuned_phases));
+
+ if (tuned_phase_cnt && tuning_value > 0) {
+ /*
+ * Finally set the selected phase in delay
+ * line hw block.
+ */
+ phase = tuning_value;
+ sdhci_writel(host,
+ clock_setting | phase | (phase << 7) | (phase << 16),
+ SDHCI_CLK_DELAY_SETTING);
+
+ dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
+ mmc_hostname(mmc), phase);
+ } else {
+ if (--tuning_seq_cnt)
+ goto retry;
+ /* Tuning failed */
+ dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n",
+ mmc_hostname(mmc));
+ rc = -EIO;
+ }
+
+ return rc;
+}
+
static struct sdhci_ops sdhci_sirf_ops = {
+ .platform_execute_tuning = sdhci_sirf_execute_tuning,
.set_clock = sdhci_set_clock,
.get_max_clock = sdhci_sirf_get_max_clk,
.set_bus_width = sdhci_sirf_set_bus_width,
@@ -138,9 +209,6 @@ static int sdhci_sirf_remove(struct platform_device *pdev)
sdhci_pltfm_unregister(pdev);
- if (gpio_is_valid(priv->gpio_cd))
- mmc_gpio_free_cd(host->mmc);
-
clk_disable_unprepare(priv->clk);
return 0;
}
diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c
index 328f348c7243..882b07e9667e 100644
--- a/drivers/mmc/host/sdhci-st.c
+++ b/drivers/mmc/host/sdhci-st.c
@@ -78,10 +78,9 @@ static int sdhci_st_probe(struct platform_device *pdev)
}
ret = mmc_of_parse(host->mmc);
-
if (ret) {
dev_err(&pdev->dev, "Failed mmc_of_parse\n");
- return ret;
+ goto err_of;
}
clk_prepare_enable(clk);
@@ -108,6 +107,7 @@ static int sdhci_st_probe(struct platform_device *pdev)
err_out:
clk_disable_unprepare(clk);
+err_of:
sdhci_pltfm_free(pdev);
return ret;
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 59797106af93..f3778d58d1cd 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -41,6 +41,7 @@
#define NVQUIRK_DISABLE_SDR50 BIT(3)
#define NVQUIRK_DISABLE_SDR104 BIT(4)
#define NVQUIRK_DISABLE_DDR50 BIT(5)
+#define NVQUIRK_SHADOW_XFER_MODE_REG BIT(6)
struct sdhci_tegra_soc_data {
const struct sdhci_pltfm_data *pdata;
@@ -67,6 +68,31 @@ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
return readw(host->ioaddr + reg);
}
+static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
+
+ if (soc_data->nvquirks & NVQUIRK_SHADOW_XFER_MODE_REG) {
+ switch (reg) {
+ case SDHCI_TRANSFER_MODE:
+ /*
+ * Postpone this write, we must do it together with a
+ * command write that is down below.
+ */
+ pltfm_host->xfer_mode_shadow = val;
+ return;
+ case SDHCI_COMMAND:
+ writel((val << 16) | pltfm_host->xfer_mode_shadow,
+ host->ioaddr + SDHCI_TRANSFER_MODE);
+ return;
+ }
+ }
+
+ writew(val, host->ioaddr + reg);
+}
+
static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -147,6 +173,7 @@ static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
static const struct sdhci_ops tegra_sdhci_ops = {
.get_ro = tegra_sdhci_get_ro,
.read_w = tegra_sdhci_readw,
+ .write_w = tegra_sdhci_writew,
.write_l = tegra_sdhci_writel,
.set_clock = sdhci_set_clock,
.set_bus_width = tegra_sdhci_set_bus_width,
@@ -201,7 +228,8 @@ static struct sdhci_tegra_soc_data soc_data_tegra114 = {
.pdata = &sdhci_tegra114_pdata,
.nvquirks = NVQUIRK_DISABLE_SDR50 |
NVQUIRK_DISABLE_DDR50 |
- NVQUIRK_DISABLE_SDR104,
+ NVQUIRK_DISABLE_SDR104 |
+ NVQUIRK_SHADOW_XFER_MODE_REG,
};
static const struct of_device_id sdhci_tegra_dt_match[] = {
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index f1a488ee432f..0ad412a4876f 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -53,6 +53,9 @@ static void sdhci_finish_command(struct sdhci_host *);
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
static void sdhci_tuning_timer(unsigned long data);
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
+static int sdhci_pre_dma_transfer(struct sdhci_host *host,
+ struct mmc_data *data,
+ struct sdhci_host_next *next);
#ifdef CONFIG_PM
static int sdhci_runtime_pm_get(struct sdhci_host *host);
@@ -505,9 +508,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
goto fail;
BUG_ON(host->align_addr & host->align_mask);
- host->sg_count = dma_map_sg(mmc_dev(host->mmc),
- data->sg, data->sg_len, direction);
- if (host->sg_count == 0)
+ host->sg_count = sdhci_pre_dma_transfer(host, data, NULL);
+ if (host->sg_count < 0)
goto unmap_align;
desc = host->adma_table;
@@ -531,8 +533,6 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
if (offset) {
if (data->flags & MMC_DATA_WRITE) {
buffer = sdhci_kmap_atomic(sg, &flags);
- WARN_ON(((long)buffer & (PAGE_SIZE - 1)) >
- (PAGE_SIZE - offset));
memcpy(align, buffer, offset);
sdhci_kunmap_atomic(buffer, &flags);
}
@@ -639,8 +639,6 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
(sg_dma_address(sg) & host->align_mask);
buffer = sdhci_kmap_atomic(sg, &flags);
- WARN_ON(((long)buffer & (PAGE_SIZE - 1)) >
- (PAGE_SIZE - size));
memcpy(buffer, align, size);
sdhci_kunmap_atomic(buffer, &flags);
@@ -649,8 +647,9 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
}
}
- dma_unmap_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len, direction);
+ if (!data->host_cookie)
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, direction);
}
static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
@@ -846,11 +845,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
} else {
int sg_cnt;
- sg_cnt = dma_map_sg(mmc_dev(host->mmc),
- data->sg, data->sg_len,
- (data->flags & MMC_DATA_READ) ?
- DMA_FROM_DEVICE :
- DMA_TO_DEVICE);
+ sg_cnt = sdhci_pre_dma_transfer(host, data, NULL);
if (sg_cnt == 0) {
/*
* This only happens when someone fed
@@ -909,7 +904,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
static void sdhci_set_transfer_mode(struct sdhci_host *host,
struct mmc_command *cmd)
{
- u16 mode;
+ u16 mode = 0;
struct mmc_data *data = cmd->data;
if (data == NULL) {
@@ -927,9 +922,11 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
WARN_ON(!host->data);
- mode = SDHCI_TRNS_BLK_CNT_EN;
+ if (!(host->quirks2 & SDHCI_QUIRK2_SUPPORT_SINGLE))
+ mode = SDHCI_TRNS_BLK_CNT_EN;
+
if (mmc_op_multi(cmd->opcode) || data->blocks > 1) {
- mode |= SDHCI_TRNS_MULTI;
+ mode = SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_MULTI;
/*
* If we are sending CMD23, CMD12 never gets sent
* on successful completion (so no Auto-CMD12).
@@ -963,8 +960,10 @@ static void sdhci_finish_data(struct sdhci_host *host)
if (host->flags & SDHCI_USE_ADMA)
sdhci_adma_table_post(host, data);
else {
- dma_unmap_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len, (data->flags & MMC_DATA_READ) ?
+ if (!data->host_cookie)
+ dma_unmap_sg(mmc_dev(host->mmc),
+ data->sg, data->sg_len,
+ (data->flags & MMC_DATA_READ) ?
DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
}
@@ -1630,7 +1629,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
* signalling timeout and CRC errors even on CMD0. Resetting
* it on each ios seems to solve the problem.
*/
- if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
+ if (host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
mmiowb();
@@ -1832,6 +1831,10 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
ctrl |= SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ /* Some controller need to do more when switching */
+ if (host->ops->voltage_switch)
+ host->ops->voltage_switch(host);
+
/* 1.8V regulator output should be stable within 5 ms */
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
if (ctrl & SDHCI_CTRL_VDD_180)
@@ -1960,6 +1963,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
ctrl |= SDHCI_CTRL_EXEC_TUNING;
+ if (host->quirks2 & SDHCI_QUIRK2_TUNING_WORK_AROUND)
+ ctrl |= SDHCI_CTRL_TUNED_CLK;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
/*
@@ -2129,6 +2134,77 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
}
}
+static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
+ int err)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct mmc_data *data = mrq->data;
+
+ if (host->flags & SDHCI_REQ_USE_DMA) {
+ if (data->host_cookie)
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ data->flags & MMC_DATA_WRITE ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ mrq->data->host_cookie = 0;
+ }
+}
+
+static int sdhci_pre_dma_transfer(struct sdhci_host *host,
+ struct mmc_data *data,
+ struct sdhci_host_next *next)
+{
+ int sg_count;
+
+ if (!next && data->host_cookie &&
+ data->host_cookie != host->next_data.cookie) {
+ pr_debug(DRIVER_NAME "[%s] invalid cookie: %d, next-cookie %d\n",
+ __func__, data->host_cookie, host->next_data.cookie);
+ data->host_cookie = 0;
+ }
+
+ /* Check if next job is already prepared */
+ if (next ||
+ (!next && data->host_cookie != host->next_data.cookie)) {
+ sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len,
+ data->flags & MMC_DATA_WRITE ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ } else {
+ sg_count = host->next_data.sg_count;
+ host->next_data.sg_count = 0;
+ }
+
+
+ if (sg_count == 0)
+ return -EINVAL;
+
+ if (next) {
+ next->sg_count = sg_count;
+ data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
+ } else
+ host->sg_count = sg_count;
+
+ return sg_count;
+}
+
+static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
+ bool is_first_req)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (mrq->data->host_cookie) {
+ mrq->data->host_cookie = 0;
+ return;
+ }
+
+ if (host->flags & SDHCI_REQ_USE_DMA)
+ if (sdhci_pre_dma_transfer(host,
+ mrq->data,
+ &host->next_data) < 0)
+ mrq->data->host_cookie = 0;
+}
+
static void sdhci_card_event(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
@@ -2162,6 +2238,8 @@ static void sdhci_card_event(struct mmc_host *mmc)
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
+ .post_req = sdhci_post_req,
+ .pre_req = sdhci_pre_req,
.set_ios = sdhci_set_ios,
.get_cd = sdhci_get_cd,
.get_ro = sdhci_get_ro,
@@ -2793,9 +2871,9 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
/* Force clock and power re-program */
host->pwr = 0;
host->clock = 0;
+ sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios);
sdhci_do_set_ios(host, &host->mmc->ios);
- sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios);
if ((host_flags & SDHCI_PV_ENABLED) &&
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) {
spin_lock_irqsave(&host->lock, flags);
@@ -3019,6 +3097,7 @@ int sdhci_add_host(struct sdhci_host *host)
host->max_clk = host->ops->get_max_clock(host);
}
+ host->next_data.cookie = 1;
/*
* In case of Host Controller v3.00, find out whether clock
* multiplier is supported.
@@ -3338,9 +3417,9 @@ int sdhci_add_host(struct sdhci_host *host)
setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
- if (host->version >= SDHCI_SPEC_300) {
- init_waitqueue_head(&host->buf_ready_int);
+ init_waitqueue_head(&host->buf_ready_int);
+ if (host->version >= SDHCI_SPEC_300) {
/* Initialize re-tuning timer */
init_timer(&host->tuning_timer);
host->tuning_timer.data = (unsigned long)host;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 41a2c34299ed..0315e1844330 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -339,6 +339,7 @@ struct sdhci_ops {
void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
void (*platform_init)(struct sdhci_host *host);
void (*card_event)(struct sdhci_host *host);
+ void (*voltage_switch)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c
new file mode 100644
index 000000000000..2fe8b91481b3
--- /dev/null
+++ b/drivers/mmc/host/sdhci_f_sdh30.c
@@ -0,0 +1,237 @@
+/*
+ * linux/drivers/mmc/host/sdhci_f_sdh30.c
+ *
+ * Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd
+ * Vincent Yang <vincent.yang@tw.fujitsu.com>
+ * Copyright (C) 2015 Linaro Ltd Andy Green <andy.green@linaro.org>
+ *
+ * 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, version 2 of the License.
+ */
+
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+
+#include "sdhci-pltfm.h"
+
+/* F_SDH30 extended Controller registers */
+#define F_SDH30_AHB_CONFIG 0x100
+#define F_SDH30_AHB_BIGED 0x00000040
+#define F_SDH30_BUSLOCK_DMA 0x00000020
+#define F_SDH30_BUSLOCK_EN 0x00000010
+#define F_SDH30_SIN 0x00000008
+#define F_SDH30_AHB_INCR_16 0x00000004
+#define F_SDH30_AHB_INCR_8 0x00000002
+#define F_SDH30_AHB_INCR_4 0x00000001
+
+#define F_SDH30_TUNING_SETTING 0x108
+#define F_SDH30_CMD_CHK_DIS 0x00010000
+
+#define F_SDH30_IO_CONTROL2 0x114
+#define F_SDH30_CRES_O_DN 0x00080000
+#define F_SDH30_MSEL_O_1_8 0x00040000
+
+#define F_SDH30_ESD_CONTROL 0x124
+#define F_SDH30_EMMC_RST 0x00000002
+#define F_SDH30_EMMC_HS200 0x01000000
+
+#define F_SDH30_CMD_DAT_DELAY 0x200
+
+#define F_SDH30_MIN_CLOCK 400000
+
+struct f_sdhost_priv {
+ struct clk *clk_iface;
+ struct clk *clk;
+ u32 vendor_hs200;
+ struct device *dev;
+};
+
+void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
+{
+ struct f_sdhost_priv *priv = sdhci_priv(host);
+ u32 ctrl = 0;
+
+ usleep_range(2500, 3000);
+ ctrl = sdhci_readl(host, F_SDH30_IO_CONTROL2);
+ ctrl |= F_SDH30_CRES_O_DN;
+ sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
+ ctrl |= F_SDH30_MSEL_O_1_8;
+ sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
+
+ ctrl &= ~F_SDH30_CRES_O_DN;
+ sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
+ usleep_range(2500, 3000);
+
+ if (priv->vendor_hs200) {
+ dev_info(priv->dev, "%s: setting hs200\n", __func__);
+ ctrl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
+ ctrl |= priv->vendor_hs200;
+ sdhci_writel(host, ctrl, F_SDH30_ESD_CONTROL);
+ }
+
+ ctrl = sdhci_readl(host, F_SDH30_TUNING_SETTING);
+ ctrl |= F_SDH30_CMD_CHK_DIS;
+ sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
+}
+
+unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
+{
+ return F_SDH30_MIN_CLOCK;
+}
+
+void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
+{
+ if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0)
+ sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
+
+ sdhci_reset(host, mask);
+}
+
+static const struct sdhci_ops sdhci_f_sdh30_ops = {
+ .voltage_switch = sdhci_f_sdh30_soft_voltage_switch,
+ .get_min_clock = sdhci_f_sdh30_get_min_clock,
+ .reset = sdhci_f_sdh30_reset,
+ .set_clock = sdhci_set_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+};
+
+static int sdhci_f_sdh30_probe(struct platform_device *pdev)
+{
+ struct sdhci_host *host;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int irq, ctrl = 0, ret = 0;
+ struct f_sdhost_priv *priv;
+ u32 reg = 0;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "%s: no irq specified\n", __func__);
+ return irq;
+ }
+
+ host = sdhci_alloc_host(dev, sizeof(struct sdhci_host) +
+ sizeof(struct f_sdhost_priv));
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ priv = sdhci_priv(host);
+ priv->dev = dev;
+
+ host->quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
+ SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
+ host->quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE |
+ SDHCI_QUIRK2_TUNING_WORK_AROUND;
+
+ ret = mmc_of_parse(host->mmc);
+ if (ret)
+ goto err;
+
+ platform_set_drvdata(pdev, host);
+
+ sdhci_get_of_property(pdev);
+ host->hw_name = "f_sdh30";
+ host->ops = &sdhci_f_sdh30_ops;
+ host->irq = irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->ioaddr)) {
+ ret = PTR_ERR(host->ioaddr);
+ goto err;
+ }
+
+ priv->clk_iface = devm_clk_get(&pdev->dev, "iface");
+ if (IS_ERR(priv->clk_iface)) {
+ ret = PTR_ERR(priv->clk_iface);
+ goto err;
+ }
+
+ ret = clk_prepare_enable(priv->clk_iface);
+ if (ret)
+ goto err;
+
+ priv->clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ goto err_clk;
+ }
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ goto err_clk;
+
+ /* init vendor specific regs */
+ ctrl = sdhci_readw(host, F_SDH30_AHB_CONFIG);
+ ctrl |= F_SDH30_SIN | F_SDH30_AHB_INCR_16 | F_SDH30_AHB_INCR_8 |
+ F_SDH30_AHB_INCR_4;
+ ctrl &= ~(F_SDH30_AHB_BIGED | F_SDH30_BUSLOCK_EN);
+ sdhci_writew(host, ctrl, F_SDH30_AHB_CONFIG);
+
+ reg = sdhci_readl(host, F_SDH30_ESD_CONTROL);
+ sdhci_writel(host, reg & ~F_SDH30_EMMC_RST, F_SDH30_ESD_CONTROL);
+ msleep(20);
+ sdhci_writel(host, reg | F_SDH30_EMMC_RST, F_SDH30_ESD_CONTROL);
+
+ reg = sdhci_readl(host, SDHCI_CAPABILITIES);
+ if (reg & SDHCI_CAN_DO_8BIT)
+ priv->vendor_hs200 = F_SDH30_EMMC_HS200;
+
+ ret = sdhci_add_host(host);
+ if (ret)
+ goto err_add_host;
+
+ return 0;
+
+err_add_host:
+ clk_disable_unprepare(priv->clk);
+err_clk:
+ clk_disable_unprepare(priv->clk_iface);
+err:
+ sdhci_free_host(host);
+ return ret;
+}
+
+static int sdhci_f_sdh30_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct f_sdhost_priv *priv = sdhci_priv(host);
+
+ sdhci_remove_host(host, readl(host->ioaddr + SDHCI_INT_STATUS) ==
+ 0xffffffff);
+
+ clk_disable_unprepare(priv->clk_iface);
+ clk_disable_unprepare(priv->clk);
+
+ sdhci_free_host(host);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id f_sdh30_dt_ids[] = {
+ { .compatible = "fujitsu,mb86s70-sdhci-3.0" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, f_sdh30_dt_ids);
+
+static struct platform_driver sdhci_f_sdh30_driver = {
+ .driver = {
+ .name = "f_sdh30",
+ .of_match_table = f_sdh30_dt_ids,
+ .pm = SDHCI_PLTFM_PMOPS,
+ },
+ .probe = sdhci_f_sdh30_probe,
+ .remove = sdhci_f_sdh30_remove,
+};
+
+module_platform_driver(sdhci_f_sdh30_driver);
+
+MODULE_DESCRIPTION("F_SDH30 SD Card Controller driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("FUJITSU SEMICONDUCTOR LTD.");
+MODULE_ALIAS("platform:f_sdh30");
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index 00c8ebdf8ec7..6906a905cd54 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -35,10 +35,13 @@
#define EXT_ACC 0xe4
+#define host_to_priv(host) container_of((host)->pdata, struct sh_mobile_sdhi, mmc_data)
+
struct sh_mobile_sdhi_of_data {
unsigned long tmio_flags;
unsigned long capabilities;
unsigned long capabilities2;
+ enum dma_slave_buswidth dma_buswidth;
dma_addr_t dma_rx_offset;
};
@@ -58,6 +61,7 @@ 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,
+ .dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES,
.dma_rx_offset = 0x2000,
};
@@ -84,16 +88,43 @@ struct sh_mobile_sdhi {
struct tmio_mmc_dma dma_priv;
};
+static void sh_mobile_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
+{
+ u32 val;
+
+ /*
+ * see also
+ * sh_mobile_sdhi_of_data :: dma_buswidth
+ */
+ switch (sd_ctrl_read16(host, CTL_VERSION)) {
+ case 0x490C:
+ val = (width == 32) ? 0x0001 : 0x0000;
+ break;
+ case 0xCB0D:
+ val = (width == 32) ? 0x0000 : 0x0001;
+ break;
+ default:
+ /* nothing to do */
+ return;
+ }
+
+ sd_ctrl_write16(host, EXT_ACC, val);
+}
+
static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct tmio_mmc_host *host = mmc_priv(mmc);
- struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
+ struct sh_mobile_sdhi *priv = host_to_priv(host);
int ret = clk_prepare_enable(priv->clk);
if (ret < 0)
return ret;
*f = clk_get_rate(priv->clk);
+
+ /* enable 16bit data access on SDBUF as default */
+ sh_mobile_sdhi_sdbuf_width(host, 16);
+
return 0;
}
@@ -101,7 +132,7 @@ static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct tmio_mmc_host *host = mmc_priv(mmc);
- struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
+ struct sh_mobile_sdhi *priv = host_to_priv(host);
clk_disable_unprepare(priv->clk);
}
@@ -113,7 +144,7 @@ static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
udelay(1);
if (!timeout) {
- dev_warn(host->pdata->dev, "timeout waiting for SD bus idle\n");
+ dev_warn(&host->pdev->dev, "timeout waiting for SD bus idle\n");
return -EBUSY;
}
@@ -156,14 +187,13 @@ static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card,
return blk_size;
}
-static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev)
+static void sh_mobile_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable)
{
- mmc_detect_change(platform_get_drvdata(pdev), msecs_to_jiffies(100));
-}
+ sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0);
-static const struct sh_mobile_sdhi_ops sdhi_ops = {
- .cd_wakeup = sh_mobile_sdhi_cd_wakeup,
-};
+ /* enable 32bit access if DMA mode if possibile */
+ sh_mobile_sdhi_sdbuf_width(host, enable ? 32 : 16);
+}
static int sh_mobile_sdhi_probe(struct platform_device *pdev)
{
@@ -177,7 +207,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
int irq, ret, i = 0;
bool multiplexed_isr = true;
struct tmio_mmc_dma *dma_priv;
- u16 ver;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
@@ -192,26 +221,31 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data = &priv->mmc_data;
dma_priv = &priv->dma_priv;
- if (p) {
- if (p->init) {
- ret = p->init(pdev, &sdhi_ops);
- if (ret)
- return ret;
- }
- }
-
priv->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(priv->clk)) {
ret = PTR_ERR(priv->clk);
dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
- goto eclkget;
+ goto eprobe;
}
- mmc_data->clk_enable = sh_mobile_sdhi_clk_enable;
- mmc_data->clk_disable = sh_mobile_sdhi_clk_disable;
+ host = tmio_mmc_host_alloc(pdev);
+ if (!host) {
+ ret = -ENOMEM;
+ goto eprobe;
+ }
+
+ host->dma = dma_priv;
+ host->write16_hook = sh_mobile_sdhi_write16_hook;
+ host->clk_enable = sh_mobile_sdhi_clk_enable;
+ host->clk_disable = sh_mobile_sdhi_clk_disable;
+ host->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk;
+ /* SD control register space size is 0x100, 0x200 for bus_shift=1 */
+ if (resource_size(res) > 0x100)
+ host->bus_shift = 1;
+ else
+ host->bus_shift = 0;
+
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;
@@ -231,11 +265,10 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
dma_priv->slave_id_rx = p->dma_slave_rx;
}
}
-
- dma_priv->alignment_shift = 1; /* 2-byte alignment */
dma_priv->filter = shdma_chan_filter;
+ dma_priv->enable = sh_mobile_sdhi_enable_dma;
- mmc_data->dma = dma_priv;
+ mmc_data->alignment_shift = 1; /* 2-byte alignment */
/*
* All SDHI blocks support 2-byte and larger block sizes in 4-bit
@@ -258,33 +291,18 @@ 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;
mmc_data->capabilities |= of_data->capabilities;
mmc_data->capabilities2 |= of_data->capabilities2;
- dma_priv->dma_rx_offset = of_data->dma_rx_offset;
+ mmc_data->dma_rx_offset = of_data->dma_rx_offset;
+ dma_priv->dma_buswidth = of_data->dma_buswidth;
}
- /* SD control register space size is 0x100, 0x200 for bus_shift=1 */
- mmc_data->bus_shift = resource_size(res) >> 9;
-
- ret = tmio_mmc_host_probe(&host, pdev, mmc_data);
+ ret = tmio_mmc_host_probe(host, mmc_data);
if (ret < 0)
- goto eprobe;
-
- /*
- * FIXME:
- * this Workaround can be more clever method
- */
- ver = sd_ctrl_read16(host, CTL_VERSION);
- if (ver == 0xCB0D)
- sd_ctrl_write16(host, EXT_ACC, 1);
+ goto efree;
/*
* Allow one or more specific (named) ISRs or
@@ -351,10 +369,9 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
eirq:
tmio_mmc_host_remove(host);
+efree:
+ tmio_mmc_host_free(host);
eprobe:
-eclkget:
- if (p && p->cleanup)
- p->cleanup(pdev);
return ret;
}
@@ -362,13 +379,9 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct tmio_mmc_host *host = mmc_priv(mmc);
- struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
tmio_mmc_host_remove(host);
- if (p && p->cleanup)
- p->cleanup(pdev);
-
return 0;
}
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index 15cb8b7ffc34..6af0a28ba37d 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -252,7 +252,7 @@ static int sunxi_mmc_reset_host(struct sunxi_mmc_host *host)
unsigned long expire = jiffies + msecs_to_jiffies(250);
u32 rval;
- mmc_writel(host, REG_CMDR, SDXC_HARDWARE_RESET);
+ mmc_writel(host, REG_GCTRL, SDXC_HARDWARE_RESET);
do {
rval = mmc_readl(host, REG_GCTRL);
} while (time_before(jiffies, expire) && (rval & SDXC_HARDWARE_RESET));
@@ -310,7 +310,9 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host,
}
pdes[0].config |= SDXC_IDMAC_DES0_FD;
- pdes[i - 1].config = SDXC_IDMAC_DES0_OWN | SDXC_IDMAC_DES0_LD;
+ pdes[i - 1].config |= SDXC_IDMAC_DES0_LD | SDXC_IDMAC_DES0_ER;
+ pdes[i - 1].config &= ~SDXC_IDMAC_DES0_DIC;
+ pdes[i - 1].buf_addr_ptr2 = 0;
/*
* Avoid the io-store starting the idmac hitting io-mem before the
@@ -570,6 +572,15 @@ static irqreturn_t sunxi_mmc_handle_manual_stop(int irq, void *dev_id)
}
dev_err(mmc_dev(host->mmc), "data error, sending stop command\n");
+
+ /*
+ * We will never have more than one outstanding request,
+ * and we do not complete the request until after
+ * we've cleared host->manual_stop_mrq so we do not need to
+ * spin lock this function.
+ * Additionally we have wait states within this function
+ * so having it in a lock is a very bad idea.
+ */
sunxi_mmc_send_manual_stop(host, mrq);
spin_lock_irqsave(&host->lock, iflags);
@@ -616,7 +627,7 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
struct mmc_ios *ios)
{
- u32 rate, oclk_dly, rval, sclk_dly, src_clk;
+ u32 rate, oclk_dly, rval, sclk_dly;
int ret;
rate = clk_round_rate(host->clk_mmc, ios->clock);
@@ -661,14 +672,6 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
sclk_dly = 4;
}
- src_clk = clk_get_rate(clk_get_parent(host->clk_mmc));
- if (src_clk >= 300000000 && src_clk <= 400000000) {
- if (oclk_dly)
- oclk_dly--;
- if (sclk_dly)
- sclk_dly--;
- }
-
clk_sunxi_mmc_phase_control(host->clk_mmc, sclk_dly, oclk_dly);
return sunxi_mmc_oclk_onoff(host, 1);
@@ -766,6 +769,7 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
unsigned long iflags;
u32 imask = SDXC_INTERRUPT_ERROR_BIT;
u32 cmd_val = SDXC_START | (cmd->opcode & 0x3f);
+ bool wait_dma = host->wait_dma;
int ret;
/* Check for set_ios errors (should never happen) */
@@ -816,7 +820,7 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
if (cmd->data->flags & MMC_DATA_WRITE)
cmd_val |= SDXC_WRITE;
else
- host->wait_dma = true;
+ wait_dma = true;
} else {
imask |= SDXC_COMMAND_DONE;
}
@@ -850,6 +854,7 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
}
host->mrq = mrq;
+ host->wait_dma = wait_dma;
mmc_writel(host, REG_IMASK, host->sdio_imask | imask);
mmc_writel(host, REG_CARG, cmd->arg);
mmc_writel(host, REG_CMDR, cmd_val);
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index 2ca0afaab792..f746df493892 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -88,14 +88,19 @@ static int tmio_mmc_probe(struct platform_device *pdev)
if (!res)
return -EINVAL;
- /* SD control register space size is 0x200, 0x400 for bus_shift=1 */
- pdata->bus_shift = resource_size(res) >> 10;
pdata->flags |= TMIO_MMC_HAVE_HIGH_REG;
- ret = tmio_mmc_host_probe(&host, pdev, pdata);
- if (ret)
+ host = tmio_mmc_host_alloc(pdev);
+ if (!host)
goto cell_disable;
+ /* SD control register space size is 0x200, 0x400 for bus_shift=1 */
+ host->bus_shift = resource_size(res) >> 10;
+
+ ret = tmio_mmc_host_probe(host, pdata);
+ if (ret)
+ goto host_free;
+
ret = request_irq(irq, tmio_mmc_irq, IRQF_TRIGGER_FALLING,
dev_name(&pdev->dev), host);
if (ret)
@@ -108,6 +113,8 @@ static int tmio_mmc_probe(struct platform_device *pdev)
host_remove:
tmio_mmc_host_remove(host);
+host_free:
+ tmio_mmc_host_free(host);
cell_disable:
if (cell->disable)
cell->disable(pdev);
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index a34ecbe1c1ad..fc3805ed69d1 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -16,6 +16,7 @@
#ifndef TMIO_MMC_H
#define TMIO_MMC_H
+#include <linux/dmaengine.h>
#include <linux/highmem.h>
#include <linux/mmc/tmio.h>
#include <linux/mutex.h>
@@ -39,6 +40,17 @@
#define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD)
struct tmio_mmc_data;
+struct tmio_mmc_host;
+
+struct tmio_mmc_dma {
+ void *chan_priv_tx;
+ void *chan_priv_rx;
+ int slave_id_tx;
+ int slave_id_rx;
+ enum dma_slave_buswidth dma_buswidth;
+ bool (*filter)(struct dma_chan *chan, void *arg);
+ void (*enable)(struct tmio_mmc_host *host, bool enable);
+};
struct tmio_mmc_host {
void __iomem *ctl;
@@ -56,9 +68,11 @@ struct tmio_mmc_host {
struct scatterlist *sg_orig;
unsigned int sg_len;
unsigned int sg_off;
+ unsigned long bus_shift;
struct platform_device *pdev;
struct tmio_mmc_data *pdata;
+ struct tmio_mmc_dma *dma;
/* DMA support */
bool force_pio;
@@ -83,10 +97,17 @@ struct tmio_mmc_host {
struct mutex ios_lock; /* protect set_ios() context */
bool native_hotplug;
bool sdio_irq_enabled;
+
+ int (*write16_hook)(struct tmio_mmc_host *host, int addr);
+ int (*clk_enable)(struct platform_device *pdev, unsigned int *f);
+ void (*clk_disable)(struct platform_device *pdev);
+ int (*multi_io_quirk)(struct mmc_card *card,
+ unsigned int direction, int blk_size);
};
-int tmio_mmc_host_probe(struct tmio_mmc_host **host,
- struct platform_device *pdev,
+struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev);
+void tmio_mmc_host_free(struct tmio_mmc_host *host);
+int tmio_mmc_host_probe(struct tmio_mmc_host *host,
struct tmio_mmc_data *pdata);
void tmio_mmc_host_remove(struct tmio_mmc_host *host);
void tmio_mmc_do_data_irq(struct tmio_mmc_host *host);
@@ -151,19 +172,19 @@ int tmio_mmc_host_runtime_resume(struct device *dev);
static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
{
- return readw(host->ctl + (addr << host->pdata->bus_shift));
+ return readw(host->ctl + (addr << host->bus_shift));
}
static inline void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr,
u16 *buf, int count)
{
- readsw(host->ctl + (addr << host->pdata->bus_shift), buf, count);
+ readsw(host->ctl + (addr << host->bus_shift), buf, count);
}
static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
{
- return readw(host->ctl + (addr << host->pdata->bus_shift)) |
- readw(host->ctl + ((addr + 2) << host->pdata->bus_shift)) << 16;
+ return readw(host->ctl + (addr << host->bus_shift)) |
+ readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
}
static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val)
@@ -171,21 +192,21 @@ static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val
/* If there is a hook and it returns non-zero then there
* is an error and the write should be skipped
*/
- if (host->pdata->write16_hook && host->pdata->write16_hook(host, addr))
+ if (host->write16_hook && host->write16_hook(host, addr))
return;
- writew(val, host->ctl + (addr << host->pdata->bus_shift));
+ writew(val, host->ctl + (addr << host->bus_shift));
}
static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,
u16 *buf, int count)
{
- writesw(host->ctl + (addr << host->pdata->bus_shift), buf, count);
+ writesw(host->ctl + (addr << host->bus_shift), buf, count);
}
static inline void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, u32 val)
{
- writew(val, host->ctl + (addr << host->pdata->bus_shift));
- writew(val >> 16, host->ctl + ((addr + 2) << host->pdata->bus_shift));
+ writew(val, host->ctl + (addr << host->bus_shift));
+ writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
}
diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c
index 7d077388b9eb..331bb618e398 100644
--- a/drivers/mmc/host/tmio_mmc_dma.c
+++ b/drivers/mmc/host/tmio_mmc_dma.c
@@ -28,8 +28,8 @@ void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable)
if (!host->chan_tx || !host->chan_rx)
return;
- if (host->pdata->flags & TMIO_MMC_HAVE_CTL_DMA_REG)
- sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0);
+ if (host->dma->enable)
+ host->dma->enable(host, enable);
}
void tmio_mmc_abort_dma(struct tmio_mmc_host *host)
@@ -49,11 +49,10 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host)
struct scatterlist *sg = host->sg_ptr, *sg_tmp;
struct dma_async_tx_descriptor *desc = NULL;
struct dma_chan *chan = host->chan_rx;
- struct tmio_mmc_data *pdata = host->pdata;
dma_cookie_t cookie;
int ret, i;
bool aligned = true, multiple = true;
- unsigned int align = (1 << pdata->dma->alignment_shift) - 1;
+ unsigned int align = (1 << host->pdata->alignment_shift) - 1;
for_each_sg(sg, sg_tmp, host->sg_len, i) {
if (sg_tmp->offset & align)
@@ -126,11 +125,10 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host)
struct scatterlist *sg = host->sg_ptr, *sg_tmp;
struct dma_async_tx_descriptor *desc = NULL;
struct dma_chan *chan = host->chan_tx;
- struct tmio_mmc_data *pdata = host->pdata;
dma_cookie_t cookie;
int ret, i;
bool aligned = true, multiple = true;
- unsigned int align = (1 << pdata->dma->alignment_shift) - 1;
+ unsigned int align = (1 << host->pdata->alignment_shift) - 1;
for_each_sg(sg, sg_tmp, host->sg_len, i) {
if (sg_tmp->offset & align)
@@ -262,8 +260,8 @@ out:
void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata)
{
/* We can only either use DMA for both Tx and Rx or not use it at all */
- if (!pdata->dma || (!host->pdev->dev.of_node &&
- (!pdata->dma->chan_priv_tx || !pdata->dma->chan_priv_rx)))
+ if (!host->dma || (!host->pdev->dev.of_node &&
+ (!host->dma->chan_priv_tx || !host->dma->chan_priv_rx)))
return;
if (!host->chan_tx && !host->chan_rx) {
@@ -280,7 +278,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
dma_cap_set(DMA_SLAVE, mask);
host->chan_tx = dma_request_slave_channel_compat(mask,
- pdata->dma->filter, pdata->dma->chan_priv_tx,
+ host->dma->filter, host->dma->chan_priv_tx,
&host->pdev->dev, "tx");
dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__,
host->chan_tx);
@@ -288,18 +286,20 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
if (!host->chan_tx)
return;
- if (pdata->dma->chan_priv_tx)
- cfg.slave_id = pdata->dma->slave_id_tx;
+ if (host->dma->chan_priv_tx)
+ cfg.slave_id = host->dma->slave_id_tx;
cfg.direction = DMA_MEM_TO_DEV;
- cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->pdata->bus_shift);
- cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift);
+ cfg.dst_addr_width = host->dma->dma_buswidth;
+ if (!cfg.dst_addr_width)
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
cfg.src_addr = 0;
ret = dmaengine_slave_config(host->chan_tx, &cfg);
if (ret < 0)
goto ecfgtx;
host->chan_rx = dma_request_slave_channel_compat(mask,
- pdata->dma->filter, pdata->dma->chan_priv_rx,
+ host->dma->filter, host->dma->chan_priv_rx,
&host->pdev->dev, "rx");
dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__,
host->chan_rx);
@@ -307,11 +307,13 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
if (!host->chan_rx)
goto ereqrx;
- if (pdata->dma->chan_priv_rx)
- cfg.slave_id = pdata->dma->slave_id_rx;
+ if (host->dma->chan_priv_rx)
+ cfg.slave_id = host->dma->slave_id_rx;
cfg.direction = DMA_DEV_TO_MEM;
- cfg.src_addr = cfg.dst_addr + pdata->dma->dma_rx_offset;
- cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ cfg.src_addr = cfg.dst_addr + host->pdata->dma_rx_offset;
+ cfg.src_addr_width = host->dma->dma_buswidth;
+ if (!cfg.src_addr_width)
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
cfg.dst_addr = 0;
ret = dmaengine_slave_config(host->chan_rx, &cfg);
if (ret < 0)
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index 250bf8c9f998..a31c3573d386 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -835,13 +835,12 @@ fail:
static int tmio_mmc_clk_update(struct tmio_mmc_host *host)
{
struct mmc_host *mmc = host->mmc;
- struct tmio_mmc_data *pdata = host->pdata;
int ret;
- if (!pdata->clk_enable)
+ if (!host->clk_enable)
return -ENOTSUPP;
- ret = pdata->clk_enable(host->pdev, &mmc->f_max);
+ ret = host->clk_enable(host->pdev, &mmc->f_max);
if (!ret)
mmc->f_min = mmc->f_max / 512;
@@ -1005,10 +1004,9 @@ 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);
+ if (host->multi_io_quirk)
+ return host->multi_io_quirk(card, direction, blk_size);
return blk_size;
}
@@ -1054,12 +1052,37 @@ static void tmio_mmc_of_parse(struct platform_device *pdev,
pdata->flags |= TMIO_MMC_WRPROTECT_DISABLE;
}
-int tmio_mmc_host_probe(struct tmio_mmc_host **host,
- struct platform_device *pdev,
- struct tmio_mmc_data *pdata)
+struct tmio_mmc_host*
+tmio_mmc_host_alloc(struct platform_device *pdev)
{
- struct tmio_mmc_host *_host;
+ struct tmio_mmc_host *host;
struct mmc_host *mmc;
+
+ mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &pdev->dev);
+ if (!mmc)
+ return NULL;
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->pdev = pdev;
+
+ return host;
+}
+EXPORT_SYMBOL(tmio_mmc_host_alloc);
+
+void tmio_mmc_host_free(struct tmio_mmc_host *host)
+{
+ mmc_free_host(host->mmc);
+
+ host->mmc = NULL;
+}
+EXPORT_SYMBOL(tmio_mmc_host_free);
+
+int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
+ struct tmio_mmc_data *pdata)
+{
+ struct platform_device *pdev = _host->pdev;
+ struct mmc_host *mmc = _host->mmc;
struct resource *res_ctl;
int ret;
u32 irq_mask = TMIO_MASK_CMD;
@@ -1067,25 +1090,17 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
tmio_mmc_of_parse(pdev, pdata);
if (!(pdata->flags & TMIO_MMC_HAS_IDLE_WAIT))
- pdata->write16_hook = NULL;
+ _host->write16_hook = NULL;
res_ctl = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res_ctl)
return -EINVAL;
- mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &pdev->dev);
- if (!mmc)
- return -ENOMEM;
-
ret = mmc_of_parse(mmc);
if (ret < 0)
goto host_free;
- pdata->dev = &pdev->dev;
- _host = mmc_priv(mmc);
_host->pdata = pdata;
- _host->mmc = mmc;
- _host->pdev = pdev;
platform_set_drvdata(pdev, mmc);
_host->set_pwr = pdata->set_pwr;
@@ -1192,12 +1207,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
mmc_gpiod_request_cd_irq(mmc);
}
- *host = _host;
-
return 0;
host_free:
- mmc_free_host(mmc);
return ret;
}
@@ -1222,7 +1234,6 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
pm_runtime_disable(&pdev->dev);
iounmap(host->ctl);
- mmc_free_host(mmc);
}
EXPORT_SYMBOL(tmio_mmc_host_remove);
@@ -1237,8 +1248,8 @@ int tmio_mmc_host_runtime_suspend(struct device *dev)
if (host->clk_cache)
tmio_mmc_clk_stop(host);
- if (host->pdata->clk_disable)
- host->pdata->clk_disable(host->pdev);
+ if (host->clk_disable)
+ host->clk_disable(host->pdev);
return 0;
}
diff --git a/drivers/mmc/host/toshsd.c b/drivers/mmc/host/toshsd.c
index 4666262edaca..e2cdd5fb1423 100644
--- a/drivers/mmc/host/toshsd.c
+++ b/drivers/mmc/host/toshsd.c
@@ -176,7 +176,8 @@ static irqreturn_t toshsd_thread_irq(int irq, void *dev_id)
spin_lock_irqsave(&host->lock, flags);
if (!sg_miter_next(sg_miter))
- return IRQ_HANDLED;
+ goto done;
+
buf = sg_miter->addr;
/* Ensure we dont read more than one block. The chip will interrupt us
@@ -198,6 +199,7 @@ static irqreturn_t toshsd_thread_irq(int irq, void *dev_id)
sg_miter->consumed = count;
sg_miter_stop(sg_miter);
+done:
spin_unlock_irqrestore(&host->lock, flags);
return IRQ_HANDLED;
@@ -699,18 +701,7 @@ static struct pci_driver toshsd_driver = {
.driver.pm = &toshsd_pm_ops,
};
-static int __init toshsd_drv_init(void)
-{
- return pci_register_driver(&toshsd_driver);
-}
-
-static void __exit toshsd_drv_exit(void)
-{
- pci_unregister_driver(&toshsd_driver);
-}
-
-module_init(toshsd_drv_init);
-module_exit(toshsd_drv_exit);
+module_pci_driver(toshsd_driver);
MODULE_AUTHOR("Ondrej Zary, Richard Betts");
MODULE_DESCRIPTION("Toshiba PCI Secure Digital Host Controller Interface driver");
diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c
index 4262296c12fa..fbabbb82b354 100644
--- a/drivers/mmc/host/vub300.c
+++ b/drivers/mmc/host/vub300.c
@@ -659,7 +659,7 @@ static void __vub300_irqpoll_response(struct vub300_mmc_host *vub300)
static void __do_poll(struct vub300_mmc_host *vub300)
{
/* cmd_mutex is held by vub300_pollwork_thread */
- long commretval;
+ unsigned long commretval;
mod_timer(&vub300->inactivity_timer, jiffies + HZ);
init_completion(&vub300->irqpoll_complete);
send_irqpoll(vub300);
@@ -671,8 +671,6 @@ static void __do_poll(struct vub300_mmc_host *vub300)
vub300->usb_timed_out = 1;
usb_kill_urb(vub300->command_out_urb);
usb_kill_urb(vub300->command_res_urb);
- } else if (commretval < 0) {
- vub300_queue_poll_work(vub300, 1);
} else { /* commretval > 0 */
__vub300_irqpoll_response(vub300);
}
diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h
index 57388171610d..605812820e48 100644
--- a/include/linux/mfd/tmio.h
+++ b/include/linux/mfd/tmio.h
@@ -96,11 +96,6 @@
#define TMIO_MMC_SDIO_STATUS_QUIRK (1 << 8)
/*
- * Some controllers have DMA enable/disable register
- */
-#define TMIO_MMC_HAVE_CTL_DMA_REG (1 << 9)
-
-/*
* Some controllers allows to set SDx actual clock
*/
#define TMIO_MMC_CLK_ACTUAL (1 << 10)
@@ -112,18 +107,6 @@ void tmio_core_mmc_clk_div(void __iomem *cnf, int shift, int state);
struct dma_chan;
-struct tmio_mmc_dma {
- void *chan_priv_tx;
- void *chan_priv_rx;
- int slave_id_tx;
- int slave_id_rx;
- int alignment_shift;
- dma_addr_t dma_rx_offset;
- bool (*filter)(struct dma_chan *chan, void *arg);
-};
-
-struct tmio_mmc_host;
-
/*
* data for the MMC controller
*/
@@ -132,19 +115,12 @@ struct tmio_mmc_data {
unsigned long capabilities;
unsigned long capabilities2;
unsigned long flags;
- unsigned long bus_shift;
u32 ocr_mask; /* available voltages */
- struct tmio_mmc_dma *dma;
- struct device *dev;
unsigned int cd_gpio;
+ int alignment_shift;
+ dma_addr_t dma_rx_offset;
void (*set_pwr)(struct platform_device *host, int state);
void (*set_clk_div)(struct platform_device *host, int state);
- int (*write16_hook)(struct tmio_mmc_host *host, int addr);
- /* clock management callbacks */
- int (*clk_enable)(struct platform_device *pdev, unsigned int *f);
- void (*clk_disable)(struct platform_device *pdev);
- int (*multi_io_quirk)(struct mmc_card *card,
- unsigned int direction, int blk_size);
};
/*
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 4d69c00497bd..a6cf4c063e4e 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -83,7 +83,7 @@ struct mmc_ext_csd {
bool hpi; /* HPI support bit */
unsigned int hpi_cmd; /* cmd used as HPI */
bool bkops; /* background support bit */
- bool bkops_en; /* background enable bit */
+ bool man_bkops_en; /* manual bkops enable bit */
unsigned int data_sector_size; /* 512 bytes or 4KB */
unsigned int data_tag_unit_size; /* DATA TAG UNIT size */
unsigned int boot_ro_lock; /* ro lock support */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index cb2b0400d284..160448f920ac 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -182,7 +182,6 @@ extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);
extern int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount,
bool is_rel_write);
extern int mmc_hw_reset(struct mmc_host *host);
-extern int mmc_hw_reset_check(struct mmc_host *host);
extern int mmc_can_reset(struct mmc_card *card);
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *);
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index 42b724e8d503..471fb3116dbe 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -106,6 +106,11 @@ struct mmc_data;
* @cur_slot, @mrq and @state. These must always be updated
* at the same time while holding @lock.
*
+ * @irq_lock is an irq-safe spinlock protecting the INTMASK register
+ * to allow the interrupt handler to modify it directly. Held for only long
+ * enough to read-modify-write INTMASK and no other locks are grabbed when
+ * holding this one.
+ *
* The @mrq field of struct dw_mci_slot is also protected by @lock,
* and must always be written at the same time as the slot is added to
* @queue.
@@ -125,6 +130,7 @@ struct mmc_data;
*/
struct dw_mci {
spinlock_t lock;
+ spinlock_t irq_lock;
void __iomem *regs;
struct scatterlist *sg;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 9f322706f7cb..0c8cbe5d1550 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -166,7 +166,6 @@ struct mmc_async_req {
* struct mmc_slot - MMC slot functions
*
* @cd_irq: MMC/SD-card slot hotplug detection IRQ or -EINVAL
- * @lock: protect the @handler_priv pointer
* @handler_priv: MMC/SD-card slot context
*
* Some MMC/SD host controllers implement slot-functions like card and
@@ -176,7 +175,6 @@ struct mmc_async_req {
*/
struct mmc_slot {
int cd_irq;
- struct mutex lock;
void *handler_priv;
};
@@ -197,6 +195,7 @@ struct mmc_context_info {
};
struct regulator;
+struct mmc_pwrseq;
struct mmc_supply {
struct regulator *vmmc; /* Card power supply */
@@ -208,6 +207,7 @@ struct mmc_host {
struct device class_dev;
int index;
const struct mmc_host_ops *ops;
+ struct mmc_pwrseq *pwrseq;
unsigned int f_min;
unsigned int f_max;
unsigned int f_init;
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 49ad7a943638..124f562118b8 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -53,11 +53,6 @@
#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */
#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */
-#define MMC_TUNING_BLK_PATTERN_4BIT_SIZE 64
-#define MMC_TUNING_BLK_PATTERN_8BIT_SIZE 128
-extern const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE];
-extern const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE];
-
/* class 3 */
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
@@ -433,6 +428,11 @@ struct _mmc_csd {
#define EXT_CSD_BKOPS_LEVEL_2 0x2
/*
+ * BKOPS modes
+ */
+#define EXT_CSD_MANUAL_BKOPS_MASK 0x01
+
+/*
* MMC_SWITCH access modes
*/
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index f767a0de611f..c3e3db196738 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -17,6 +17,11 @@
#include <linux/io.h>
#include <linux/mmc/host.h>
+struct sdhci_host_next {
+ unsigned int sg_count;
+ s32 cookie;
+};
+
struct sdhci_host {
/* Data set by hardware interface driver */
const char *hw_name; /* Hardware bus name */
@@ -106,6 +111,10 @@ struct sdhci_host {
#define SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1<<10)
/* Capability register bit-63 indicates HS400 support */
#define SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1<<11)
+/* forced tuned clock */
+#define SDHCI_QUIRK2_TUNING_WORK_AROUND (1<<12)
+/* disable the block count for single block transactions */
+#define SDHCI_QUIRK2_SUPPORT_SINGLE (1<<13)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
@@ -203,6 +212,7 @@ struct sdhci_host {
#define SDHCI_TUNING_MODE_1 0
struct timer_list tuning_timer; /* Timer for tuning */
+ struct sdhci_host_next next_data;
unsigned long private[0] ____cacheline_aligned;
};
#endif /* LINUX_MMC_SDHCI_H */
diff --git a/include/linux/mmc/sh_mobile_sdhi.h b/include/linux/mmc/sh_mobile_sdhi.h
index 68927ae50845..da77e5e2041d 100644
--- a/include/linux/mmc/sh_mobile_sdhi.h
+++ b/include/linux/mmc/sh_mobile_sdhi.h
@@ -3,20 +3,10 @@
#include <linux/types.h>
-struct platform_device;
-
#define SH_MOBILE_SDHI_IRQ_CARD_DETECT "card_detect"
#define SH_MOBILE_SDHI_IRQ_SDCARD "sdcard"
#define SH_MOBILE_SDHI_IRQ_SDIO "sdio"
-/**
- * struct sh_mobile_sdhi_ops - SDHI driver callbacks
- * @cd_wakeup: trigger a card-detection run
- */
-struct sh_mobile_sdhi_ops {
- void (*cd_wakeup)(const struct platform_device *pdev);
-};
-
struct sh_mobile_sdhi_info {
int dma_slave_tx;
int dma_slave_rx;
@@ -25,11 +15,6 @@ struct sh_mobile_sdhi_info {
unsigned long tmio_caps2;
u32 tmio_ocr_mask; /* available MMC voltages */
unsigned int cd_gpio;
-
- /* callbacks for board specific setup code */
- int (*init)(struct platform_device *pdev,
- const struct sh_mobile_sdhi_ops *ops);
- void (*cleanup)(struct platform_device *pdev);
};
#endif /* LINUX_MMC_SH_MOBILE_SDHI_H */
diff --git a/include/linux/mmc/slot-gpio.h b/include/linux/mmc/slot-gpio.h
index e56fa24c9322..3945a8c9d3cb 100644
--- a/include/linux/mmc/slot-gpio.h
+++ b/include/linux/mmc/slot-gpio.h
@@ -15,12 +15,10 @@ struct mmc_host;
int mmc_gpio_get_ro(struct mmc_host *host);
int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio);
-void mmc_gpio_free_ro(struct mmc_host *host);
int mmc_gpio_get_cd(struct mmc_host *host);
int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
unsigned int debounce);
-void mmc_gpio_free_cd(struct mmc_host *host);
int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
unsigned int idx, bool override_active_level,
@@ -28,7 +26,8 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
unsigned int idx, bool override_active_level,
unsigned int debounce, bool *gpio_invert);
-void mmc_gpiod_free_cd(struct mmc_host *host);
+void mmc_gpio_set_cd_isr(struct mmc_host *host,
+ irqreturn_t (*isr)(int irq, void *dev_id));
void mmc_gpiod_request_cd_irq(struct mmc_host *host);
#endif
diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h
index 5c188f4e9bec..929469291406 100644
--- a/include/linux/platform_data/mmc-omap.h
+++ b/include/linux/platform_data/mmc-omap.h
@@ -31,10 +31,6 @@ struct omap_mmc_platform_data {
void (*cleanup)(struct device *dev);
void (*shutdown)(struct device *dev);
- /* To handle board related suspend/resume functionality for MMC */
- int (*suspend)(struct device *dev, int slot);
- int (*resume)(struct device *dev, int slot);
-
/* Return context loss count due to PM states changing */
int (*get_context_loss_count)(struct device *dev);