E: balasub@cis.ohio-state.edu
D: Wrote SYS V IPC (part of standard kernel since 0.99.10)
+N: Chris Ball
+E: chris@printf.net
+D: Former maintainer of the MMC/SD/SDIO subsystem.
+
N: Dario Ballabio
E: ballabio_dario@emc.com
E: dario.ballabio@tiscalinet.it
--- /dev/null
+Broadcom IPROC SDHCI controller
+
+This file documents differences between the core properties described
+by mmc.txt and the properties that represent the IPROC SDHCI controller.
+
+Required properties:
+- compatible : Should be "brcm,sdhci-iproc-cygnus".
+- clocks : The clock feeding the SDHCI controller.
+
+Optional properties:
+ - sdhci,auto-cmd12: specifies that controller should use auto CMD12.
+
+Example:
+
+sdhci0: sdhci@0x18041000 {
+ compatible = "brcm,sdhci-iproc-cygnus";
+ reg = <0x18041000 0x100>;
+ interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&lcpll0_clks BCM_CYGNUS_LCPLL0_SDIO_CLK>;
+ bus-width = <4>;
+ sdhci,auto-cmd12;
+ no-1-8-v;
+};
in transmit mode and CIU clock phase shift value in receive mode for double
data rate mode operation. Refer notes below for the order of the cells and the
valid values.
+* samsung,dw-mshc-hs400-timing: Specifies the value of CIU TX and RX clock phase
+ shift value for hs400 mode operation.
Notes for the sdr-timing and ddr-timing values:
- if CIU clock divider value is 0 (that is divide by 1), both tx and rx
phase shift clocks should be 0.
+* samsung,read-strobe-delay: RCLK (Data strobe) delay to control HS400 mode
+ (Latency value for delay line in Read path)
+
Required properties for a slot (Deprecated - Recommend to use one slot per host):
* gpios: specifies a list of gpios used for command, clock and data bus. The
samsung,dw-mshc-ciu-div = <3>;
samsung,dw-mshc-sdr-timing = <2 3>;
samsung,dw-mshc-ddr-timing = <1 2>;
+ samsung,dw-mshc-hs400-timing = <0 2>;
+ samsung,read-strobe-delay = <90>;
bus-width = <8>;
};
to select a proper data sampling window in case the clock quality is not good
due to signal path is too long on the board. Please refer to eSDHC/uSDHC
chapter, DLL (Delay Line) section in RM for details.
+- voltage-ranges : Specify the voltage range in case there are software
+ transparent level shifters on the outputs of the controller. Two cells are
+ required, first cell specifies minimum slot voltage (mV), second cell
+ specifies maximum slot voltage (mV). Several ranges could be specified.
Examples:
--- /dev/null
+mmc-card / eMMC bindings
+------------------------
+
+This documents describes the devicetree bindings for a mmc-host controller
+child node describing a mmc-card / an eMMC, see "Use of Function subnodes"
+in mmc.txt
+
+Required properties:
+-compatible : Must be "mmc-card"
+-reg : Must be <0>
+
+Optional properties:
+-broken-hpi : Use this to indicate that the mmc-card has a broken hpi
+ implementation, and that hpi should not be used
+
+Example:
+
+&mmc2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc2_pins_a>;
+ vmmc-supply = <®_vcc3v3>;
+ bus-width = <8>;
+ non-removable;
+ status = "okay";
+
+ mmccard: mmccard@0 {
+ reg = <0>;
+ compatible = "mmc-card";
+ broken-hpi;
+ };
+};
used by the sdhci-st driver.
Required properties:
-- compatible : Must be "st,sdhci"
-- clock-names : Should be "mmc"
- See: Documentation/devicetree/bindings/resource-names.txt
-- clocks : Phandle of the clock used by the sdhci controler
- See: Documentation/devicetree/bindings/clock/clock-bindings.txt
+- compatible: Must be "st,sdhci" and it can be compatible to "st,sdhci-stih407"
+ to set the internal glue logic used for configuring the MMC
+ subsystem (mmcss) inside the FlashSS (available in STiH407 SoC
+ family).
+
+- clock-names: Should be "mmc".
+ See: Documentation/devicetree/bindings/resource-names.txt
+- clocks: Phandle to the clock.
+ See: Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+- interrupts: One mmc interrupt should be described here.
+- interrupt-names: Should be "mmcirq".
+
+- pinctrl-names: A pinctrl state names "default" must be defined.
+- pinctrl-0: Phandle referencing pin configuration of the sd/emmc controller.
+ See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
+
+- reg: This must provide the host controller base address and it can also
+ contain the FlashSS Top register for TX/RX delay used by the driver
+ to configure DLL inside the flashSS, if so reg-names must also be
+ specified.
Optional properties:
-- non-removable: non-removable slot
- See: Documentation/devicetree/bindings/mmc/mmc.txt
-- bus-width: Number of data lines
- See: Documentation/devicetree/bindings/mmc/mmc.txt
+- reg-names: Should be "mmc" and "top-mmc-delay". "top-mmc-delay" is optional
+ for eMMC on stih407 family silicon to configure DLL inside FlashSS.
+
+- non-removable: Non-removable slot. Also used for configuring mmcss in STiH407 SoC
+ family.
+ See: Documentation/devicetree/bindings/mmc/mmc.txt.
+
+- bus-width: Number of data lines.
+ See: Documentation/devicetree/bindings/mmc/mmc.txt.
+
+- max-frequency: Can be 200MHz, 100Mz or 50MHz (default) and used for
+ configuring the CCONFIG3 in the mmcss.
+ See: Documentation/devicetree/bindings/mmc/mmc.txt.
+
+- resets: Phandle and reset specifier pair to softreset line of HC IP.
+ See: Documentation/devicetree/bindings/reset/reset.txt
+
+- vqmmc-supply: Phandle to the regulator dt node, mentioned as the vcc/vdd
+ supply in eMMC/SD specs.
+
+- sd-uhs--sdr50: To enable the SDR50 in the mmcss.
+ See: Documentation/devicetree/bindings/mmc/mmc.txt.
+
+- sd-uhs-sdr104: To enable the SDR104 in the mmcss.
+ See: Documentation/devicetree/bindings/mmc/mmc.txt.
+
+- sd-uhs-ddr50: To enable the DDR50 in the mmcss.
+ See: Documentation/devicetree/bindings/mmc/mmc.txt.
Example:
+/* Example stih416e eMMC configuration */
+
mmc0: sdhci@fe81e000 {
compatible = "st,sdhci";
status = "disabled";
pinctrl-0 = <&pinctrl_mmc0>;
clock-names = "mmc";
clocks = <&clk_s_a1_ls 1>;
- bus-width = <8>
+ bus-width = <8>
+
+/* Example SD stih407 family configuration */
+
+mmc1: sdhci@09080000 {
+ compatible = "st,sdhci-stih407", "st,sdhci";
+ status = "disabled";
+ reg = <0x09080000 0x7ff>;
+ reg-names = "mmc";
+ interrupts = <GIC_SPI 90 IRQ_TYPE_NONE>;
+ interrupt-names = "mmcirq";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sd1>;
+ clock-names = "mmc";
+ clocks = <&clk_s_c0_flexgen CLK_MMC_1>;
+ resets = <&softreset STIH407_MMC1_SOFTRESET>;
+ bus-width = <4>;
+};
+
+/* Example eMMC stih407 family configuration */
+
+mmc0: sdhci@09060000 {
+ compatible = "st,sdhci-stih407", "st,sdhci";
+ status = "disabled";
+ reg = <0x09060000 0x7ff>, <0x9061008 0x20>;
+ reg-names = "mmc", "top-mmc-delay";
+ interrupts = <GIC_SPI 92 IRQ_TYPE_NONE>;
+ interrupt-names = "mmcirq";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_mmc0>;
+ clock-names = "mmc";
+ clocks = <&clk_s_c0_flexgen CLK_MMC_0>;
+ vqmmc-supply = <&vmmc_reg>;
+ max-frequency = <200000000>;
+ bus-width = <8>;
+ non-removable;
+ sd-uhs-sdr50;
+ sd-uhs-sdr104;
+ sd-uhs-ddr50;
};
F: include/linux/mfd/
MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
-M: Chris Ball <chris@printf.net>
M: Ulf Hansson <ulf.hansson@linaro.org>
L: linux-mmc@vger.kernel.org
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
T: git git://git.linaro.org/people/ulf.hansson/mmc.git
S: Maintained
F: drivers/mmc/
F: drivers/mmc/host/sdricoh_cs.c
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
-M: Chris Ball <chris@printf.net>
L: linux-mmc@vger.kernel.org
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
-S: Maintained
+S: Orphan
F: drivers/mmc/host/sdhci.*
F: drivers/mmc/host/sdhci-pltfm.[ch]
K: \bsecure_computing
K: \bTIF_SECCOMP\b
-SECURE DIGITAL HOST CONTROLLER INTERFACE, OPEN FIRMWARE BINDINGS (SDHCI-OF)
-M: Anton Vorontsov <anton@enomsg.org>
-L: linuxppc-dev@lists.ozlabs.org
-L: linux-mmc@vger.kernel.org
-S: Maintained
-F: drivers/mmc/host/sdhci-pltfm.[ch]
-
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) SAMSUNG DRIVER
M: Ben Dooks <ben-linux@fluff.org>
+M: Jaehoon Chung <jh80.chung@samsung.com>
L: linux-mmc@vger.kernel.org
S: Maintained
-F: drivers/mmc/host/sdhci-s3c.c
+F: drivers/mmc/host/sdhci-s3c*
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) ST SPEAR DRIVER
M: Viresh Kumar <viresh.linux@gmail.com>
static inline void omap_hsmmc_mux(struct omap_hsmmc_platform_data
*mmc_controller, int controller_nr)
{
- if (gpio_is_valid(mmc_controller->switch_pin) &&
- (mmc_controller->switch_pin < OMAP_MAX_GPIO_LINES))
- omap_mux_init_gpio(mmc_controller->switch_pin,
+ if (gpio_is_valid(mmc_controller->gpio_cd) &&
+ (mmc_controller->gpio_cd < OMAP_MAX_GPIO_LINES))
+ omap_mux_init_gpio(mmc_controller->gpio_cd,
+ OMAP_PIN_INPUT_PULLUP);
+ if (gpio_is_valid(mmc_controller->gpio_cod) &&
+ (mmc_controller->gpio_cod < OMAP_MAX_GPIO_LINES))
+ omap_mux_init_gpio(mmc_controller->gpio_cod,
OMAP_PIN_INPUT_PULLUP);
if (gpio_is_valid(mmc_controller->gpio_wp) &&
(mmc_controller->gpio_wp < OMAP_MAX_GPIO_LINES))
mmc->internal_clock = !c->ext_clock;
mmc->reg_offset = 0;
- mmc->switch_pin = c->gpio_cd;
+ if (c->cover_only) {
+ /* detect if mobile phone cover removed */
+ mmc->gpio_cd = -EINVAL;
+ mmc->gpio_cod = c->gpio_cd;
+ } else {
+ /* card detect pin on the mmc socket itself */
+ mmc->gpio_cd = c->gpio_cd;
+ mmc->gpio_cod = -EINVAL;
+ }
mmc->gpio_wp = c->gpio_wp;
mmc->remux = c->remux;
mmc->init_card = c->init_card;
- if (c->cover_only)
- mmc->cover = 1;
-
if (c->nonremovable)
mmc->nonremovable = 1;
if (!mmc_pdata)
continue;
- mmc_pdata->switch_pin = c->gpio_cd;
+ if (c->cover_only) {
+ /* detect if mobile phone cover removed */
+ mmc_pdata->gpio_cd = -EINVAL;
+ mmc_pdata->gpio_cod = c->gpio_cd;
+ } else {
+ /* card detect pin on the mmc socket itself */
+ mmc_pdata->gpio_cd = c->gpio_cd;
+ mmc_pdata->gpio_cod = -EINVAL;
+ }
mmc_pdata->gpio_wp = c->gpio_wp;
res = omap_device_register(pdev);
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
int stop;
+ bool pm = false;
might_sleep();
host->claimed = 1;
host->claimer = current;
host->claim_cnt += 1;
+ if (host->claim_cnt == 1)
+ pm = true;
} else
wake_up(&host->wq);
spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait);
- if (host->ops->enable && !stop && host->claim_cnt == 1)
- host->ops->enable(host);
+
+ if (pm)
+ pm_runtime_get_sync(mmc_dev(host));
+
return stop;
}
-
EXPORT_SYMBOL(__mmc_claim_host);
/**
WARN_ON(!host->claimed);
- if (host->ops->disable && host->claim_cnt == 1)
- host->ops->disable(host);
-
spin_lock_irqsave(&host->lock, flags);
if (--host->claim_cnt) {
/* Release for nested claim */
host->claimer = NULL;
spin_unlock_irqrestore(&host->lock, flags);
wake_up(&host->wq);
+ pm_runtime_mark_last_busy(mmc_dev(host));
+ pm_runtime_put_autosuspend(mmc_dev(host));
}
}
EXPORT_SYMBOL(mmc_release_host);
*/
#include <linux/err.h>
+#include <linux/of.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/pm_runtime.h>
{
int err = 0, idx;
unsigned int part_size;
+ struct device_node *np;
+ bool broken_hpi = false;
/* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
card->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE];
}
}
+ np = mmc_of_find_child_device(card->host, 0);
+ if (np && of_device_is_compatible(np, "mmc-card"))
+ broken_hpi = of_property_read_bool(np, "broken-hpi");
+ of_node_put(np);
+
/*
* The EXT_CSD format is meant to be forward compatible. As long
* as CSD_STRUCTURE does not change, all values for EXT_CSD_REV
}
/* check whether the eMMC card supports HPI */
- if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1) {
+ if (!broken_hpi && (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1)) {
card->ext_csd.hpi = 1;
if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2)
card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION;
struct mmc_pwrseq_match {
const char *compatible;
- int (*alloc)(struct mmc_host *host, struct device *dev);
+ struct mmc_pwrseq *(*alloc)(struct mmc_host *host, struct device *dev);
};
static struct mmc_pwrseq_match pwrseq_match[] = {
struct platform_device *pdev;
struct device_node *np;
struct mmc_pwrseq_match *match;
+ struct mmc_pwrseq *pwrseq;
int ret = 0;
np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0);
goto err;
}
- ret = match->alloc(host, &pdev->dev);
- if (!ret)
- dev_info(host->parent, "allocated mmc-pwrseq\n");
+ pwrseq = match->alloc(host, &pdev->dev);
+ if (IS_ERR(pwrseq)) {
+ ret = PTR_ERR(host->pwrseq);
+ goto err;
+ }
+
+ host->pwrseq = pwrseq;
+ dev_info(host->parent, "allocated mmc-pwrseq\n");
err:
of_node_put(np);
if (pwrseq && pwrseq->ops && pwrseq->ops->free)
pwrseq->ops->free(host);
+
+ host->pwrseq = NULL;
}
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);
+struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host,
+ struct device *dev);
+struct mmc_pwrseq *mmc_pwrseq_emmc_alloc(struct mmc_host *host,
+ struct device *dev);
#else
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 = {
return NOTIFY_DONE;
}
-int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev)
+struct mmc_pwrseq *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;
+ return ERR_PTR(-ENOMEM);
pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW);
if (IS_ERR(pwrseq->reset_gpio)) {
register_restart_handler(&pwrseq->reset_nb);
pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops;
- host->pwrseq = &pwrseq->pwrseq;
- return 0;
+ return &pwrseq->pwrseq;
free:
kfree(pwrseq);
- return ret;
+ return ERR_PTR(ret);
}
clk_put(pwrseq->ext_clk);
kfree(pwrseq);
- host->pwrseq = NULL;
}
static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
.free = mmc_pwrseq_simple_free,
};
-int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev)
+struct mmc_pwrseq *mmc_pwrseq_simple_alloc(struct mmc_host *host,
+ struct device *dev)
{
struct mmc_pwrseq_simple *pwrseq;
int i, nr_gpios, ret = 0;
pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple) + nr_gpios *
sizeof(struct gpio_desc *), GFP_KERNEL);
if (!pwrseq)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
pwrseq->ext_clk = clk_get(dev, "ext_clock");
if (IS_ERR(pwrseq->ext_clk) &&
pwrseq->nr_gpios = nr_gpios;
pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops;
- host->pwrseq = &pwrseq->pwrseq;
- return 0;
+ return &pwrseq->pwrseq;
clk_put:
if (!IS_ERR(pwrseq->ext_clk))
clk_put(pwrseq->ext_clk);
free:
kfree(pwrseq);
- return ret;
+ return ERR_PTR(ret);
}
int err;
if (card->type == MMC_TYPE_SDIO)
- return sdio_enable_wide(card);
-
- if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
- (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+ err = sdio_enable_wide(card);
+ else if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
+ (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
if (err)
return err;
+ err = sdio_enable_wide(card);
+ if (err <= 0)
+ mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1);
} else
return 0;
- err = sdio_enable_wide(card);
- if (err <= 0)
- mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1);
+ if (err > 0) {
+ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+ err = 0;
+ }
return err;
}
/*
* Switch to wider bus (if supported).
*/
- if (card->host->caps & MMC_CAP_4_BIT_DATA) {
+ if (card->host->caps & MMC_CAP_4_BIT_DATA)
err = sdio_enable_4bit_bus(card);
- if (err > 0) {
- mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
- err = 0;
- }
- }
/* Set the driver strength for the card */
sdio_select_driver_type(card);
* Switch to wider bus (if supported).
*/
err = sdio_enable_4bit_bus(card);
- if (err > 0)
- mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
- else if (err)
+ if (err)
goto remove;
}
finish:
} else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
/* We may have switched to 1-bit mode during suspend */
err = sdio_enable_4bit_bus(host->card);
- if (err > 0) {
- mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
- err = 0;
- }
}
if (!err && host->sdio_irqs) {
config MMC_SDHCI_OF_ESDHC
tristate "SDHCI OF support for the Freescale eSDHC controller"
depends on MMC_SDHCI_PLTFM
- depends on PPC_OF
+ depends on PPC
select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
help
This selects the Freescale eSDHC controller support.
config MMC_SDHCI_OF_HLWD
tristate "SDHCI OF support for the Nintendo Wii SDHCI controllers"
depends on MMC_SDHCI_PLTFM
- depends on PPC_OF
+ depends on PPC
select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
help
This selects the Secure Digital Host Controller Interface (SDHCI)
tristate "Marvell MMP2 SD Host Controller support (PXAV3)"
depends on CLKDEV_LOOKUP
depends on MMC_SDHCI_PLTFM
- depends on ARCH_MMP || COMPILE_TEST
+ depends on ARCH_BERLIN || ARCH_MMP || ARCH_MVEBU || COMPILE_TEST
default CPU_MMP2
help
This selects the Marvell(R) PXAV3 SD Host Controller.
config MMC_SDHCI_SPEAR
tristate "SDHCI support on ST SPEAr platform"
depends on MMC_SDHCI && PLAT_SPEAR
+ depends on OF
help
This selects the Secure Digital Host Controller Interface (SDHCI)
often referrered to as the HSMMC block in some of the ST SPEAR range
If unsure, say N.
+config MMC_SDHCI_IPROC
+ tristate "SDHCI platform support for the iProc SD/MMC Controller"
+ depends on ARCH_BCM_IPROC || COMPILE_TEST
+ depends on MMC_SDHCI_PLTFM
+ default ARCH_BCM_IPROC
+ select MMC_SDHCI_IO_ACCESSORS
+ help
+ This selects the iProc SD/MMC controller.
+
+ If you have an IPROC platform with SD or MMC devices,
+ say Y or M here.
+
+ If unsure, say N.
+
config MMC_MOXART
tristate "MOXART SD/MMC Host Controller support"
depends on ARCH_MOXART && MMC
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
+obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
#define ATMCI_REGS_SIZE 0x100
/* Register access macros */
-#define atmci_readl(port,reg) \
+#ifdef CONFIG_AVR32
+#define atmci_readl(port, reg) \
__raw_readl((port)->regs + reg)
-#define atmci_writel(port,reg,value) \
+#define atmci_writel(port, reg, value) \
__raw_writel((value), (port)->regs + reg)
+#else
+#define atmci_readl(port, reg) \
+ readl_relaxed((port)->regs + reg)
+#define atmci_writel(port, reg, value) \
+ writel_relaxed((value), (port)->regs + reg)
+#endif
/* On AVR chips the Peripheral DMA Controller is not connected to MCI. */
#ifdef CONFIG_AVR32
u8 ciu_div;
u32 sdr_timing;
u32 ddr_timing;
+ u32 hs400_timing;
+ u32 tuned_sample;
u32 cur_speed;
+ u32 dqs_delay;
+ u32 saved_dqs_en;
+ u32 saved_strobe_ctrl;
};
static struct dw_mci_exynos_compatible {
},
};
+static inline u8 dw_mci_exynos_get_ciu_div(struct dw_mci *host)
+{
+ struct dw_mci_exynos_priv_data *priv = host->priv;
+
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
+ return EXYNOS4412_FIXED_CIU_CLK_DIV;
+ else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
+ return EXYNOS4210_FIXED_CIU_CLK_DIV;
+ else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
+ return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL64)) + 1;
+ else
+ return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL)) + 1;
+}
+
static int dw_mci_exynos_priv_init(struct dw_mci *host)
{
struct dw_mci_exynos_priv_data *priv = host->priv;
SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT);
}
+ if (priv->ctrl_type >= DW_MCI_TYPE_EXYNOS5420) {
+ priv->saved_strobe_ctrl = mci_readl(host, HS400_DLINE_CTRL);
+ priv->saved_dqs_en = mci_readl(host, HS400_DQS_EN);
+ priv->saved_dqs_en |= AXI_NON_BLOCKING_WR;
+ mci_writel(host, HS400_DQS_EN, priv->saved_dqs_en);
+ if (!priv->dqs_delay)
+ priv->dqs_delay =
+ DQS_CTRL_GET_RD_DELAY(priv->saved_strobe_ctrl);
+ }
+
return 0;
}
return 0;
}
+static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
+{
+ struct dw_mci_exynos_priv_data *priv = host->priv;
+ u32 clksel;
+
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
+ clksel = mci_readl(host, CLKSEL64);
+ else
+ clksel = mci_readl(host, CLKSEL);
+
+ clksel = (clksel & ~SDMMC_CLKSEL_TIMING_MASK) | timing;
+
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
+ mci_writel(host, CLKSEL64, clksel);
+ else
+ mci_writel(host, CLKSEL, clksel);
+}
+
#ifdef CONFIG_PM_SLEEP
static int dw_mci_exynos_suspend(struct device *dev)
{
}
}
-static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing)
{
struct dw_mci_exynos_priv_data *priv = host->priv;
- unsigned int wanted = ios->clock;
- unsigned long actual;
- u8 div = priv->ciu_div + 1;
+ u32 dqs, strobe;
- if (ios->timing == MMC_TIMING_MMC_DDR52) {
- if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
- priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
- mci_writel(host, CLKSEL64, priv->ddr_timing);
- else
- mci_writel(host, CLKSEL, priv->ddr_timing);
- /* Should be double rate for DDR mode */
- if (ios->bus_width == MMC_BUS_WIDTH_8)
- wanted <<= 1;
+ /*
+ * Not supported to configure register
+ * related to HS400
+ */
+ if (priv->ctrl_type < DW_MCI_TYPE_EXYNOS5420)
+ return;
+
+ dqs = priv->saved_dqs_en;
+ strobe = priv->saved_strobe_ctrl;
+
+ if (timing == MMC_TIMING_MMC_HS400) {
+ dqs |= DATA_STROBE_EN;
+ strobe = DQS_CTRL_RD_DELAY(strobe, priv->dqs_delay);
} else {
- if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
- priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
- mci_writel(host, CLKSEL64, priv->sdr_timing);
- else
- mci_writel(host, CLKSEL, priv->sdr_timing);
+ dqs &= ~DATA_STROBE_EN;
}
+ mci_writel(host, HS400_DQS_EN, dqs);
+ mci_writel(host, HS400_DLINE_CTRL, strobe);
+}
+
+static void dw_mci_exynos_adjust_clock(struct dw_mci *host, unsigned int wanted)
+{
+ struct dw_mci_exynos_priv_data *priv = host->priv;
+ unsigned long actual;
+ u8 div;
+ int ret;
/*
* Don't care if wanted clock is zero or
* ciu clock is unavailable
if (wanted < EXYNOS_CCLKIN_MIN)
wanted = EXYNOS_CCLKIN_MIN;
- if (wanted != priv->cur_speed) {
- int ret = clk_set_rate(host->ciu_clk, wanted * div);
- if (ret)
- dev_warn(host->dev,
- "failed to set clk-rate %u error: %d\n",
- wanted * div, ret);
- actual = clk_get_rate(host->ciu_clk);
- host->bus_hz = actual / div;
- priv->cur_speed = wanted;
- host->current_speed = 0;
+ if (wanted == priv->cur_speed)
+ return;
+
+ div = dw_mci_exynos_get_ciu_div(host);
+ ret = clk_set_rate(host->ciu_clk, wanted * div);
+ if (ret)
+ dev_warn(host->dev,
+ "failed to set clk-rate %u error: %d\n",
+ wanted * div, ret);
+ actual = clk_get_rate(host->ciu_clk);
+ host->bus_hz = actual / div;
+ priv->cur_speed = wanted;
+ host->current_speed = 0;
+}
+
+static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+{
+ struct dw_mci_exynos_priv_data *priv = host->priv;
+ unsigned int wanted = ios->clock;
+ u32 timing = ios->timing, clksel;
+
+ switch (timing) {
+ case MMC_TIMING_MMC_HS400:
+ /* Update tuned sample timing */
+ clksel = SDMMC_CLKSEL_UP_SAMPLE(
+ priv->hs400_timing, priv->tuned_sample);
+ wanted <<= 1;
+ break;
+ case MMC_TIMING_MMC_DDR52:
+ clksel = priv->ddr_timing;
+ /* Should be double rate for DDR mode */
+ if (ios->bus_width == MMC_BUS_WIDTH_8)
+ wanted <<= 1;
+ break;
+ default:
+ clksel = priv->sdr_timing;
}
+
+ /* Set clock timing for the requested speed mode*/
+ dw_mci_exynos_set_clksel_timing(host, clksel);
+
+ /* Configure setting for HS400 */
+ dw_mci_exynos_config_hs400(host, timing);
+
+ /* Configure clock rate */
+ dw_mci_exynos_adjust_clock(host, wanted);
}
static int dw_mci_exynos_parse_dt(struct dw_mci *host)
return ret;
priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
+
+ ret = of_property_read_u32_array(np,
+ "samsung,dw-mshc-hs400-timing", timing, 2);
+ if (!ret && of_property_read_u32(np,
+ "samsung,read-strobe-delay", &priv->dqs_delay))
+ dev_dbg(host->dev,
+ "read-strobe-delay is not found, assuming usage of default value\n");
+
+ priv->hs400_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1],
+ HS400_FIXED_CIU_CLK_DIV);
host->priv = priv;
return 0;
}
clksel = mci_readl(host, CLKSEL64);
else
clksel = mci_readl(host, CLKSEL);
- clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample);
+ clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
mci_writel(host, CLKSEL64, clksel);
clksel = mci_readl(host, CLKSEL64);
else
clksel = mci_readl(host, CLKSEL);
+
sample = (clksel + 1) & 0x7;
- clksel = (clksel & ~0x7) | sample;
+ clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
+
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
mci_writel(host, CLKSEL64, clksel);
else
mci_writel(host, CLKSEL, clksel);
+
return sample;
}
static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot)
{
struct dw_mci *host = slot->host;
+ struct dw_mci_exynos_priv_data *priv = host->priv;
struct mmc_host *mmc = slot->mmc;
u8 start_smpl, smpl, candiates = 0;
s8 found = -1;
} while (start_smpl != smpl);
found = dw_mci_exynos_get_best_clksmpl(candiates);
- if (found >= 0)
+ if (found >= 0) {
dw_mci_exynos_set_clksmpl(host, found);
- else
+ priv->tuned_sample = found;
+ } else {
ret = -EIO;
+ }
return ret;
}
+static int dw_mci_exynos_prepare_hs400_tuning(struct dw_mci *host,
+ struct mmc_ios *ios)
+{
+ struct dw_mci_exynos_priv_data *priv = host->priv;
+
+ dw_mci_exynos_set_clksel_timing(host, priv->hs400_timing);
+ dw_mci_exynos_adjust_clock(host, (ios->clock) << 1);
+
+ return 0;
+}
+
/* Common capabilities of Exynos4/Exynos5 SoC */
static unsigned long exynos_dwmmc_caps[4] = {
MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
.set_ios = dw_mci_exynos_set_ios,
.parse_dt = dw_mci_exynos_parse_dt,
.execute_tuning = dw_mci_exynos_execute_tuning,
+ .prepare_hs400_tuning = dw_mci_exynos_prepare_hs400_tuning,
};
static const struct of_device_id dw_mci_exynos_match[] = {
#ifndef _DW_MMC_EXYNOS_H_
#define _DW_MMC_EXYNOS_H_
-/* Extended Register's Offset */
#define SDMMC_CLKSEL 0x09C
#define SDMMC_CLKSEL64 0x0A8
+/* Extended Register's Offset */
+#define SDMMC_HS400_DQS_EN 0x180
+#define SDMMC_HS400_ASYNC_FIFO_CTRL 0x184
+#define SDMMC_HS400_DLINE_CTRL 0x188
+
/* 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_GET_DIV(x) (((x) >> 24) & 0x7)
+#define SDMMC_CLKSEL_UP_SAMPLE(x, y) (((x) & ~SDMMC_CLKSEL_CCLK_SAMPLE(7)) |\
+ SDMMC_CLKSEL_CCLK_SAMPLE(y))
#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_TIMING_MASK SDMMC_CLKSEL_TIMING(0x7, 0x7, 0x7)
#define SDMMC_CLKSEL_WAKEUP_INT BIT(11)
+/* RCLK_EN register defines */
+#define DATA_STROBE_EN BIT(0)
+#define AXI_NON_BLOCKING_WR BIT(7)
+
+/* DLINE_CTRL register defines */
+#define DQS_CTRL_RD_DELAY(x, y) (((x) & ~0x3FF) | ((y) & 0x3FF))
+#define DQS_CTRL_GET_RD_DELAY(x) ((x) & 0x3FF)
+
/* Protector Register */
#define SDMMC_EMMCP_BASE 0x1000
#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010)
/* Fixed clock divider */
#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
+#define HS400_FIXED_CIU_CLK_DIV 1
/* Minimal required clock frequency for cclkin, unit: HZ */
#define EXYNOS_CCLKIN_MIN 50000000
return 0;
}
+/* Common capabilities of RK3288 SoC */
+static unsigned long dw_mci_rk3288_dwmmc_caps[4] = {
+ MMC_CAP_RUNTIME_RESUME, /* emmc */
+ MMC_CAP_RUNTIME_RESUME, /* sdmmc */
+ MMC_CAP_RUNTIME_RESUME, /* sdio0 */
+ MMC_CAP_RUNTIME_RESUME, /* sdio1 */
+};
static const struct dw_mci_drv_data rk2928_drv_data = {
.prepare_command = dw_mci_rockchip_prepare_command,
.init = dw_mci_rockchip_init,
};
static const struct dw_mci_drv_data rk3288_drv_data = {
+ .caps = dw_mci_rk3288_dwmmc_caps,
.prepare_command = dw_mci_rockchip_prepare_command,
.set_ios = dw_mci_rk3288_set_ios,
.setup_clock = dw_mci_rk3288_setup_clock,
u32 des2; /*Buffer sizes */
#define IDMAC_64ADDR_SET_BUFFER1_SIZE(d, s) \
- ((d)->des2 = ((d)->des2 & 0x03ffe000) | ((s) & 0x1fff))
+ ((d)->des2 = ((d)->des2 & cpu_to_le32(0x03ffe000)) | \
+ ((cpu_to_le32(s)) & cpu_to_le32(0x1fff)))
u32 des3; /* Reserved */
};
struct idmac_desc {
- u32 des0; /* Control Descriptor */
+ __le32 des0; /* Control Descriptor */
#define IDMAC_DES0_DIC BIT(1)
#define IDMAC_DES0_LD BIT(2)
#define IDMAC_DES0_FD BIT(3)
#define IDMAC_DES0_CES BIT(30)
#define IDMAC_DES0_OWN BIT(31)
- u32 des1; /* Buffer sizes */
+ __le32 des1; /* Buffer sizes */
#define IDMAC_SET_BUFFER1_SIZE(d, s) \
((d)->des1 = ((d)->des1 & 0x03ffe000) | ((s) & 0x1fff))
- u32 des2; /* buffer 1 physical address */
+ __le32 des2; /* buffer 1 physical address */
- u32 des3; /* buffer 2 physical address */
+ __le32 des3; /* buffer 2 physical address */
};
#endif /* CONFIG_MMC_DW_IDMAC */
static bool dw_mci_reset(struct dw_mci *host);
static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset);
+static int dw_mci_card_busy(struct mmc_host *mmc);
#if defined(CONFIG_DEBUG_FS)
static int dw_mci_req_show(struct seq_file *s, void *v)
return cmdr;
}
+static void dw_mci_wait_while_busy(struct dw_mci *host, u32 cmd_flags)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(500);
+
+ /*
+ * Databook says that before issuing a new data transfer command
+ * we need to check to see if the card is busy. Data transfer commands
+ * all have SDMMC_CMD_PRV_DAT_WAIT set, so we'll key off that.
+ *
+ * ...also allow sending for SDMMC_CMD_VOLT_SWITCH where busy is
+ * expected.
+ */
+ if ((cmd_flags & SDMMC_CMD_PRV_DAT_WAIT) &&
+ !(cmd_flags & SDMMC_CMD_VOLT_SWITCH)) {
+ while (mci_readl(host, STATUS) & SDMMC_STATUS_BUSY) {
+ if (time_after(jiffies, timeout)) {
+ /* Command will fail; we'll pass error then */
+ dev_err(host->dev, "Busy; trying anyway\n");
+ break;
+ }
+ udelay(10);
+ }
+ }
+}
+
static void dw_mci_start_command(struct dw_mci *host,
struct mmc_command *cmd, u32 cmd_flags)
{
mci_writel(host, CMDARG, cmd->arg);
wmb();
+ dw_mci_wait_while_busy(host, cmd_flags);
mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
}
* Set the OWN bit and disable interrupts for this
* descriptor
*/
- desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC |
- IDMAC_DES0_CH;
+ desc->des0 = cpu_to_le32(IDMAC_DES0_OWN |
+ IDMAC_DES0_DIC | IDMAC_DES0_CH);
/* Buffer length */
IDMAC_SET_BUFFER1_SIZE(desc, length);
/* Physical address to DMA to/from */
- desc->des2 = mem_addr;
+ desc->des2 = cpu_to_le32(mem_addr);
}
/* Set first descriptor */
desc = host->sg_cpu;
- desc->des0 |= IDMAC_DES0_FD;
+ desc->des0 |= cpu_to_le32(IDMAC_DES0_FD);
/* Set last descriptor */
desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
- desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
- desc->des0 |= IDMAC_DES0_LD;
+ desc->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | IDMAC_DES0_DIC));
+ desc->des0 |= cpu_to_le32(IDMAC_DES0_LD);
}
wmb();
/* Forward link the descriptor list */
for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
- p->des3 = host->sg_dma + (sizeof(struct idmac_desc) *
- (i + 1));
+ p->des3 = cpu_to_le32(host->sg_dma +
+ (sizeof(struct idmac_desc) * (i + 1)));
/* Set the last descriptor as the end-of-ring descriptor */
- p->des3 = host->sg_dma;
- p->des0 = IDMAC_DES0_ER;
+ p->des3 = cpu_to_le32(host->sg_dma);
+ p->des0 = cpu_to_le32(IDMAC_DES0_ER);
}
dw_mci_idmac_reset(host);
return;
if (host->timing != MMC_TIMING_MMC_HS200 &&
+ host->timing != MMC_TIMING_MMC_HS400 &&
host->timing != MMC_TIMING_UHS_SDR104)
goto disable;
mci_writel(host, CMDARG, arg);
wmb();
+ dw_mci_wait_while_busy(host, cmd);
mci_writel(host, CMD, SDMMC_CMD_START | cmd);
while (time_before(jiffies, timeout)) {
dw_mci_start_command(host, cmd, cmdflags);
+ if (cmd->opcode == SD_SWITCH_VOLTAGE) {
+ unsigned long irqflags;
+
+ /*
+ * Databook says to fail after 2ms w/ no response, but evidence
+ * shows that sometimes the cmd11 interrupt takes over 130ms.
+ * We'll set to 500ms, plus an extra jiffy just in case jiffies
+ * is just about to roll over.
+ *
+ * We do this whole thing under spinlock and only if the
+ * command hasn't already completed (indicating the the irq
+ * already ran so we don't want the timeout).
+ */
+ spin_lock_irqsave(&host->irq_lock, irqflags);
+ if (!test_bit(EVENT_CMD_COMPLETE, &host->pending_events))
+ mod_timer(&host->cmd11_timer,
+ jiffies + msecs_to_jiffies(500) + 1);
+ spin_unlock_irqrestore(&host->irq_lock, irqflags);
+ }
+
if (mrq->stop)
host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
else
regs = mci_readl(slot->host, UHS_REG);
/* DDR mode set */
- if (ios->timing == MMC_TIMING_MMC_DDR52)
+ if (ios->timing == MMC_TIMING_MMC_DDR52 ||
+ ios->timing == MMC_TIMING_MMC_HS400)
regs |= ((0x1 << slot->id) << 16);
else
regs &= ~((0x1 << slot->id) << 16);
if (drv_data && drv_data->set_ios)
drv_data->set_ios(slot->host, ios);
- /* Slot specific timing and width adjustment */
- dw_mci_setup_bus(slot, false);
-
- if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0)
- slot->host->state = STATE_IDLE;
-
switch (ios->power_mode) {
case MMC_POWER_UP:
if (!IS_ERR(mmc->supply.vmmc)) {
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)
- dev_err(slot->host->dev,
- "failed to enable vqmmc regulator\n");
- else
+ if (!slot->host->vqmmc_enabled) {
+ if (!IS_ERR(mmc->supply.vqmmc)) {
+ ret = regulator_enable(mmc->supply.vqmmc);
+ if (ret < 0)
+ dev_err(slot->host->dev,
+ "failed to enable vqmmc\n");
+ else
+ slot->host->vqmmc_enabled = true;
+
+ } else {
+ /* Keep track so we don't reset again */
slot->host->vqmmc_enabled = true;
+ }
+
+ /* Reset our state machine after powering on */
+ dw_mci_ctrl_reset(slot->host,
+ SDMMC_CTRL_ALL_RESET_FLAGS);
}
+
+ /* Adjust clock / bus width after power is up */
+ dw_mci_setup_bus(slot, false);
+
break;
case MMC_POWER_OFF:
+ /* Turn clock off before power goes down */
+ dw_mci_setup_bus(slot, false);
+
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
- if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled) {
+ if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled)
regulator_disable(mmc->supply.vqmmc);
- slot->host->vqmmc_enabled = false;
- }
+ slot->host->vqmmc_enabled = false;
regs = mci_readl(slot->host, PWREN);
regs &= ~(1 << slot->id);
default:
break;
}
+
+ if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0)
+ slot->host->state = STATE_IDLE;
}
static int dw_mci_card_busy(struct mmc_host *mmc)
return err;
}
+static int dw_mci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ 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;
+
+ if (drv_data && drv_data->prepare_hs400_tuning)
+ return drv_data->prepare_hs400_tuning(host, ios);
+
+ return 0;
+}
+
static const struct mmc_host_ops dw_mci_ops = {
.request = dw_mci_request,
.pre_req = dw_mci_pre_req,
.card_busy = dw_mci_card_busy,
.start_signal_voltage_switch = dw_mci_switch_voltage,
.init_card = dw_mci_init_card,
+ .prepare_hs400_tuning = dw_mci_prepare_hs400_tuning,
};
static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
if (test_and_clear_bit(EVENT_DATA_ERROR,
&host->pending_events)) {
dw_mci_stop_dma(host);
- send_stop_abort(host, data);
+ if (data->stop ||
+ !(host->data_status & (SDMMC_INT_DRTO |
+ SDMMC_INT_EBE)))
+ send_stop_abort(host, data);
state = STATE_DATA_ERROR;
break;
}
if (test_and_clear_bit(EVENT_DATA_ERROR,
&host->pending_events)) {
dw_mci_stop_dma(host);
- send_stop_abort(host, data);
+ if (data->stop ||
+ !(host->data_status & (SDMMC_INT_DRTO |
+ SDMMC_INT_EBE)))
+ send_stop_abort(host, data);
state = STATE_DATA_ERROR;
break;
}
buf += len;
cnt -= len;
if (host->part_buf_count == 2) {
- mci_writew(host, DATA(host->data_offset),
- host->part_buf16);
+ mci_fifo_writew(host->fifo_reg, host->part_buf16);
host->part_buf_count = 0;
}
}
cnt -= len;
/* push data from aligned buffer into fifo */
for (i = 0; i < items; ++i)
- mci_writew(host, DATA(host->data_offset),
- aligned_buf[i]);
+ mci_fifo_writew(host->fifo_reg, aligned_buf[i]);
}
} else
#endif
{
u16 *pdata = buf;
for (; cnt >= 2; cnt -= 2)
- mci_writew(host, DATA(host->data_offset), *pdata++);
+ mci_fifo_writew(host->fifo_reg, *pdata++);
buf = pdata;
}
/* put anything remaining in the part_buf */
/* Push data if we have reached the expected data length */
if ((data->bytes_xfered + init_cnt) ==
(data->blksz * data->blocks))
- mci_writew(host, DATA(host->data_offset),
- host->part_buf16);
+ mci_fifo_writew(host->fifo_reg, host->part_buf16);
}
}
int items = len >> 1;
int i;
for (i = 0; i < items; ++i)
- aligned_buf[i] = mci_readw(host,
- DATA(host->data_offset));
+ aligned_buf[i] = mci_fifo_readw(host->fifo_reg);
/* memcpy from aligned buffer into output buffer */
memcpy(buf, aligned_buf, len);
buf += len;
{
u16 *pdata = buf;
for (; cnt >= 2; cnt -= 2)
- *pdata++ = mci_readw(host, DATA(host->data_offset));
+ *pdata++ = mci_fifo_readw(host->fifo_reg);
buf = pdata;
}
if (cnt) {
- host->part_buf16 = mci_readw(host, DATA(host->data_offset));
+ host->part_buf16 = mci_fifo_readw(host->fifo_reg);
dw_mci_pull_final_bytes(host, buf, cnt);
}
}
buf += len;
cnt -= len;
if (host->part_buf_count == 4) {
- mci_writel(host, DATA(host->data_offset),
- host->part_buf32);
+ mci_fifo_writel(host->fifo_reg, host->part_buf32);
host->part_buf_count = 0;
}
}
cnt -= len;
/* push data from aligned buffer into fifo */
for (i = 0; i < items; ++i)
- mci_writel(host, DATA(host->data_offset),
- aligned_buf[i]);
+ mci_fifo_writel(host->fifo_reg, aligned_buf[i]);
}
} else
#endif
{
u32 *pdata = buf;
for (; cnt >= 4; cnt -= 4)
- mci_writel(host, DATA(host->data_offset), *pdata++);
+ mci_fifo_writel(host->fifo_reg, *pdata++);
buf = pdata;
}
/* put anything remaining in the part_buf */
/* Push data if we have reached the expected data length */
if ((data->bytes_xfered + init_cnt) ==
(data->blksz * data->blocks))
- mci_writel(host, DATA(host->data_offset),
- host->part_buf32);
+ mci_fifo_writel(host->fifo_reg, host->part_buf32);
}
}
int items = len >> 2;
int i;
for (i = 0; i < items; ++i)
- aligned_buf[i] = mci_readl(host,
- DATA(host->data_offset));
+ aligned_buf[i] = mci_fifo_readl(host->fifo_reg);
/* memcpy from aligned buffer into output buffer */
memcpy(buf, aligned_buf, len);
buf += len;
{
u32 *pdata = buf;
for (; cnt >= 4; cnt -= 4)
- *pdata++ = mci_readl(host, DATA(host->data_offset));
+ *pdata++ = mci_fifo_readl(host->fifo_reg);
buf = pdata;
}
if (cnt) {
- host->part_buf32 = mci_readl(host, DATA(host->data_offset));
+ host->part_buf32 = mci_fifo_readl(host->fifo_reg);
dw_mci_pull_final_bytes(host, buf, cnt);
}
}
cnt -= len;
if (host->part_buf_count == 8) {
- mci_writeq(host, DATA(host->data_offset),
- host->part_buf);
+ mci_fifo_writeq(host->fifo_reg, host->part_buf);
host->part_buf_count = 0;
}
}
cnt -= len;
/* push data from aligned buffer into fifo */
for (i = 0; i < items; ++i)
- mci_writeq(host, DATA(host->data_offset),
- aligned_buf[i]);
+ mci_fifo_writeq(host->fifo_reg, aligned_buf[i]);
}
} else
#endif
{
u64 *pdata = buf;
for (; cnt >= 8; cnt -= 8)
- mci_writeq(host, DATA(host->data_offset), *pdata++);
+ mci_fifo_writeq(host->fifo_reg, *pdata++);
buf = pdata;
}
/* put anything remaining in the part_buf */
/* Push data if we have reached the expected data length */
if ((data->bytes_xfered + init_cnt) ==
(data->blksz * data->blocks))
- mci_writeq(host, DATA(host->data_offset),
- host->part_buf);
+ mci_fifo_writeq(host->fifo_reg, host->part_buf);
}
}
int items = len >> 3;
int i;
for (i = 0; i < items; ++i)
- aligned_buf[i] = mci_readq(host,
- DATA(host->data_offset));
+ aligned_buf[i] = mci_fifo_readq(host->fifo_reg);
+
/* memcpy from aligned buffer into output buffer */
memcpy(buf, aligned_buf, len);
buf += len;
{
u64 *pdata = buf;
for (; cnt >= 8; cnt -= 8)
- *pdata++ = mci_readq(host, DATA(host->data_offset));
+ *pdata++ = mci_fifo_readq(host->fifo_reg);
buf = pdata;
}
if (cnt) {
- host->part_buf = mci_readq(host, DATA(host->data_offset));
+ host->part_buf = mci_fifo_readq(host->fifo_reg);
dw_mci_pull_final_bytes(host, buf, cnt);
}
}
/* Check volt switch first, since it can look like an error */
if ((host->state == STATE_SENDING_CMD11) &&
(pending & SDMMC_INT_VOLT_SWITCH)) {
+ unsigned long irqflags;
+
mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH);
pending &= ~SDMMC_INT_VOLT_SWITCH;
+
+ /*
+ * Hold the lock; we know cmd11_timer can't be kicked
+ * off after the lock is released, so safe to delete.
+ */
+ spin_lock_irqsave(&host->irq_lock, irqflags);
dw_mci_cmd_interrupt(host, pending);
+ spin_unlock_irqrestore(&host->irq_lock, irqflags);
+
+ del_timer(&host->cmd11_timer);
}
if (pending & DW_MCI_CMD_ERROR_FLAGS) {
/* Handle SDIO Interrupts */
for (i = 0; i < host->num_slots; i++) {
struct dw_mci_slot *slot = host->slot[i];
+
+ if (!slot)
+ continue;
+
if (pending & SDMMC_INT_SDIO(slot->sdio_id)) {
mci_writel(host, RINTSTS,
SDMMC_INT_SDIO(slot->sdio_id));
return ret;
}
+static void dw_mci_cmd11_timer(unsigned long arg)
+{
+ struct dw_mci *host = (struct dw_mci *)arg;
+
+ if (host->state != STATE_SENDING_CMD11) {
+ dev_warn(host->dev, "Unexpected CMD11 timeout\n");
+ return;
+ }
+
+ host->cmd_status = SDMMC_INT_RTO;
+ set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
+ tasklet_schedule(&host->tasklet);
+}
+
#ifdef CONFIG_OF
static struct dw_mci_of_quirks {
char *quirk;
}
#endif /* CONFIG_OF */
+static void dw_mci_enable_cd(struct dw_mci *host)
+{
+ struct dw_mci_board *brd = host->pdata;
+ unsigned long irqflags;
+ u32 temp;
+ int i;
+
+ /* No need for CD if broken card detection */
+ if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
+ return;
+
+ /* No need for CD if all slots have a non-error GPIO */
+ for (i = 0; i < host->num_slots; i++) {
+ struct dw_mci_slot *slot = host->slot[i];
+
+ if (IS_ERR_VALUE(mmc_gpio_get_cd(slot->mmc)))
+ break;
+ }
+ if (i == host->num_slots)
+ return;
+
+ spin_lock_irqsave(&host->irq_lock, irqflags);
+ temp = mci_readl(host, INTMASK);
+ temp |= SDMMC_INT_CD;
+ mci_writel(host, INTMASK, temp);
+ spin_unlock_irqrestore(&host->irq_lock, irqflags);
+}
+
int dw_mci_probe(struct dw_mci *host)
{
const struct dw_mci_drv_data *drv_data = host->drv_data;
}
}
+ setup_timer(&host->cmd11_timer,
+ dw_mci_cmd11_timer, (unsigned long)host);
+
host->quirks = host->pdata->quirks;
spin_lock_init(&host->lock);
dev_info(host->dev, "Version ID is %04x\n", host->verid);
if (host->verid < DW_MMC_240A)
- host->data_offset = DATA_OFFSET;
+ host->fifo_reg = host->regs + DATA_OFFSET;
else
- host->data_offset = DATA_240A_OFFSET;
+ host->fifo_reg = host->regs + DATA_240A_OFFSET;
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1;
/*
- * Enable interrupts for command done, data over, data empty, card det,
+ * Enable interrupts for command done, data over, data empty,
* receive ready and error such as transmit, receive timeout, crc error
*/
mci_writel(host, RINTSTS, 0xFFFFFFFF);
mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
SDMMC_INT_TXDR | SDMMC_INT_RXDR |
- DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
+ DW_MCI_ERROR_FLAGS);
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
dev_info(host->dev, "DW MMC controller at irq %d, "
goto err_dmaunmap;
}
+ /* Now that slots are all setup, we can enable card detect */
+ dw_mci_enable_cd(host);
+
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
dev_info(host->dev, "Internal DMAC interrupt fix enabled.\n");
mci_writel(host, RINTSTS, 0xFFFFFFFF);
mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
SDMMC_INT_TXDR | SDMMC_INT_RXDR |
- DW_MCI_ERROR_FLAGS | SDMMC_INT_CD);
+ DW_MCI_ERROR_FLAGS);
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE);
for (i = 0; i < host->num_slots; i++) {
dw_mci_setup_bus(slot, true);
}
}
+
+ /* Now that slots are all setup, we can enable card detect */
+ dw_mci_enable_cd(host);
+
return 0;
}
EXPORT_SYMBOL(dw_mci_resume);
#define SDMMC_CTRL_ALL_RESET_FLAGS \
(SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)
+/* FIFO register access macros. These should not change the data endian-ness
+ * as they are written to memory to be dealt with by the upper layers */
+#define mci_fifo_readw(__reg) __raw_readw(__reg)
+#define mci_fifo_readl(__reg) __raw_readl(__reg)
+#define mci_fifo_readq(__reg) __raw_readq(__reg)
+
+#define mci_fifo_writew(__value, __reg) __raw_writew(__reg, __value)
+#define mci_fifo_writel(__value, __reg) __raw_writel(__reg, __value)
+#define mci_fifo_writeq(__value, __reg) __raw_writeq(__reg, __value)
+
/* Register access macros */
#define mci_readl(dev, reg) \
- __raw_readl((dev)->regs + SDMMC_##reg)
+ readl_relaxed((dev)->regs + SDMMC_##reg)
#define mci_writel(dev, reg, value) \
- __raw_writel((value), (dev)->regs + SDMMC_##reg)
+ writel_relaxed((value), (dev)->regs + SDMMC_##reg)
/* 16-bit FIFO access macros */
#define mci_readw(dev, reg) \
- __raw_readw((dev)->regs + SDMMC_##reg)
+ readw_relaxed((dev)->regs + SDMMC_##reg)
#define mci_writew(dev, reg, value) \
- __raw_writew((value), (dev)->regs + SDMMC_##reg)
+ writew_relaxed((value), (dev)->regs + SDMMC_##reg)
/* 64-bit FIFO access macros */
#ifdef readq
#define mci_readq(dev, reg) \
- __raw_readq((dev)->regs + SDMMC_##reg)
+ readq_relaxed((dev)->regs + SDMMC_##reg)
#define mci_writeq(dev, reg, value) \
- __raw_writeq((value), (dev)->regs + SDMMC_##reg)
+ writeq_relaxed((value), (dev)->regs + SDMMC_##reg)
#else
/*
* Dummy readq implementation for architectures that don't define it.
(*(volatile u64 __force *)((dev)->regs + SDMMC_##reg))
#define mci_writeq(dev, reg, value) \
(*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value))
+
+#define __raw_writeq(__value, __reg) \
+ (*(volatile u64 __force *)(__reg) = (__value))
+#define __raw_readq(__reg) (*(volatile u64 __force *)(__reg))
#endif
extern int dw_mci_probe(struct dw_mci *host);
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);
+ int (*prepare_hs400_tuning)(struct dw_mci *host,
+ struct mmc_ios *ios);
};
#endif /* _DW_MMC_H_ */
return 0;
}
-static struct of_device_id mmc_spi_of_match_table[] = {
+static const struct of_device_id mmc_spi_of_match_table[] = {
{ .compatible = "mmc-spi-slot", },
{},
};
dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max);
/* Get regulators and the supported OCR mask */
- mmc_regulator_get_supply(mmc);
+ ret = mmc_regulator_get_supply(mmc);
+ if (ret == -EPROBE_DEFER)
+ goto clk_disable;
+
if (!mmc->ocr_avail)
mmc->ocr_avail = plat->ocr_mask;
else if (plat->ocr_mask)
struct omap_hsmmc_next next_data;
struct omap_hsmmc_platform_data *pdata;
- /* To handle board related suspend/resume functionality for MMC */
- int (*suspend)(struct device *dev);
- int (*resume)(struct device *dev);
-
/* return MMC cover switch state, can be NULL if not supported.
*
* possible return values:
*/
int (*get_cover_state)(struct device *dev);
- /* Card detection IRQs */
- int card_detect_irq;
-
int (*card_detect)(struct device *dev);
- int (*get_ro)(struct device *dev);
-
};
struct omap_mmc_of_data {
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);
-
- 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);
#endif
-static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id);
+static irqreturn_t omap_hsmmc_cover_irq(int irq, void *dev_id);
static int omap_hsmmc_gpio_init(struct mmc_host *mmc,
struct omap_hsmmc_host *host,
{
int ret;
- if (gpio_is_valid(pdata->switch_pin)) {
- if (pdata->cover)
- host->get_cover_state =
- omap_hsmmc_get_cover_state;
- else
- host->card_detect = omap_hsmmc_card_detect;
- host->card_detect_irq =
- gpio_to_irq(pdata->switch_pin);
- mmc_gpio_set_cd_isr(mmc, omap_hsmmc_detect);
- ret = mmc_gpio_request_cd(mmc, pdata->switch_pin, 0);
+ if (gpio_is_valid(pdata->gpio_cod)) {
+ ret = mmc_gpio_request_cd(mmc, pdata->gpio_cod, 0);
if (ret)
return ret;
- } else {
- pdata->switch_pin = -EINVAL;
+
+ host->get_cover_state = omap_hsmmc_get_cover_state;
+ mmc_gpio_set_cd_isr(mmc, omap_hsmmc_cover_irq);
+ } else if (gpio_is_valid(pdata->gpio_cd)) {
+ ret = mmc_gpio_request_cd(mmc, pdata->gpio_cd, 0);
+ if (ret)
+ return ret;
+
+ host->card_detect = omap_hsmmc_card_detect;
}
if (gpio_is_valid(pdata->gpio_wp)) {
- host->get_ro = omap_hsmmc_get_wp;
ret = mmc_gpio_request_ro(mmc, pdata->gpio_wp);
if (ret)
return ret;
- } else {
- pdata->gpio_wp = -EINVAL;
}
return 0;
return;
host->mrq = NULL;
mmc_request_done(host->mmc, mrq);
+ pm_runtime_mark_last_busy(host->dev);
+ pm_runtime_put_autosuspend(host->dev);
}
/*
}
/*
- * irq handler to notify the core about card insertion/removal
+ * irq handler when (cell-phone) cover is mounted/removed
*/
-static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
+static irqreturn_t omap_hsmmc_cover_irq(int irq, void *dev_id)
{
struct omap_hsmmc_host *host = dev_id;
- int carddetect;
sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
- if (host->card_detect)
- carddetect = host->card_detect(host->dev);
- else {
- omap_hsmmc_protect_card(host);
- carddetect = -ENOSYS;
- }
-
- if (carddetect)
- mmc_detect_change(host->mmc, (HZ * 200) / 1000);
- else
- mmc_detect_change(host->mmc, (HZ * 50) / 1000);
+ omap_hsmmc_protect_card(host);
+ mmc_detect_change(host->mmc, (HZ * 200) / 1000);
return IRQ_HANDLED;
}
host->mrq = NULL;
mmc_request_done(host->mmc, mrq);
+ pm_runtime_mark_last_busy(host->dev);
+ pm_runtime_put_autosuspend(host->dev);
}
}
BUG_ON(host->req_in_progress);
BUG_ON(host->dma_ch != -1);
+ pm_runtime_get_sync(host->dev);
if (host->protect_card) {
if (host->reqs_blocked < 3) {
/*
req->data->error = -EBADF;
req->cmd->retries = 0;
mmc_request_done(mmc, req);
+ pm_runtime_mark_last_busy(host->dev);
+ pm_runtime_put_autosuspend(host->dev);
return;
} else if (host->reqs_blocked)
host->reqs_blocked = 0;
req->data->error = err;
host->mrq = NULL;
mmc_request_done(mmc, req);
+ pm_runtime_mark_last_busy(host->dev);
+ pm_runtime_put_autosuspend(host->dev);
return;
}
if (req->sbc && !(host->flags & AUTO_CMD23)) {
return host->card_detect(host->dev);
}
-static int omap_hsmmc_get_ro(struct mmc_host *mmc)
-{
- struct omap_hsmmc_host *host = mmc_priv(mmc);
-
- if (!host->get_ro)
- return -ENOSYS;
- return host->get_ro(host->dev);
-}
-
static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
{
struct omap_hsmmc_host *host = mmc_priv(mmc);
set_sd_bus_power(host);
}
-static int omap_hsmmc_enable_fclk(struct mmc_host *mmc)
-{
- struct omap_hsmmc_host *host = mmc_priv(mmc);
-
- pm_runtime_get_sync(host->dev);
-
- return 0;
-}
-
-static int omap_hsmmc_disable_fclk(struct mmc_host *mmc)
-{
- struct omap_hsmmc_host *host = mmc_priv(mmc);
-
- pm_runtime_mark_last_busy(host->dev);
- pm_runtime_put_autosuspend(host->dev);
-
- return 0;
-}
-
static int omap_hsmmc_multi_io_quirk(struct mmc_card *card,
unsigned int direction, int blk_size)
{
}
static struct mmc_host_ops omap_hsmmc_ops = {
- .enable = omap_hsmmc_enable_fclk,
- .disable = omap_hsmmc_disable_fclk,
.post_req = omap_hsmmc_post_req,
.pre_req = omap_hsmmc_pre_req,
.request = omap_hsmmc_request,
.set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
- .get_ro = omap_hsmmc_get_ro,
+ .get_ro = mmc_gpio_get_ro,
.init_card = omap_hsmmc_init_card,
.enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
};
if (of_find_property(np, "ti,dual-volt", NULL))
pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
- pdata->switch_pin = -EINVAL;
+ pdata->gpio_cd = -EINVAL;
+ pdata->gpio_cod = -EINVAL;
pdata->gpio_wp = -EINVAL;
if (of_find_property(np, "ti,non-removable", NULL)) {
if (ret < 0)
goto err_slot_name;
}
- if (host->card_detect_irq && host->get_cover_state) {
+ if (host->get_cover_state) {
ret = device_create_file(&mmc->class_dev,
- &dev_attr_cover_switch);
+ &dev_attr_cover_switch);
if (ret < 0)
goto err_slot_name;
}
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int omap_hsmmc_suspend(struct device *dev)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
pm_runtime_put_autosuspend(host->dev);
return 0;
}
-
-#else
-#define omap_hsmmc_suspend NULL
-#define omap_hsmmc_resume NULL
#endif
static int omap_hsmmc_runtime_suspend(struct device *dev)
}
static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
- .suspend = omap_hsmmc_suspend,
- .resume = omap_hsmmc_resume,
+ SET_SYSTEM_SLEEP_PM_OPS(omap_hsmmc_suspend, omap_hsmmc_resume)
.runtime_suspend = omap_hsmmc_runtime_suspend,
.runtime_resume = omap_hsmmc_runtime_resume,
};
#include <linux/mmc/host.h>
#include <linux/mmc/pm.h>
#include <linux/mmc/slot-gpio.h>
-#include <linux/mmc/sdhci.h>
#include "sdhci.h"
struct sdhci_bcm_kona_dev {
struct mutex write_lock; /* protect back to back writes */
- struct clk *external_clk;
};
}
}
-/*
- * Get the base clock. Use central clock source for now. Not sure if different
- * clock speed to each dev is allowed
- */
-static unsigned int sdhci_bcm_kona_get_max_clk(struct sdhci_host *host)
-{
- struct sdhci_bcm_kona_dev *kona_dev;
- struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
- kona_dev = sdhci_pltfm_priv(pltfm_priv);
-
- return host->mmc->f_max;
-}
-
-static unsigned int sdhci_bcm_kona_get_timeout_clock(struct sdhci_host *host)
-{
- return sdhci_bcm_kona_get_max_clk(host);
-}
-
static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host,
u8 power_mode)
{
static struct sdhci_ops sdhci_bcm_kona_ops = {
.set_clock = sdhci_set_clock,
- .get_max_clock = sdhci_bcm_kona_get_max_clk,
- .get_timeout_clock = sdhci_bcm_kona_get_timeout_clock,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
.platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks,
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_reset,
goto err_pltfm_free;
}
- /* Get and enable the external clock */
- kona_dev->external_clk = devm_clk_get(dev, NULL);
- if (IS_ERR(kona_dev->external_clk)) {
- dev_err(dev, "Failed to get external clock\n");
- ret = PTR_ERR(kona_dev->external_clk);
+ /* Get and enable the core clock */
+ pltfm_priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(pltfm_priv->clk)) {
+ dev_err(dev, "Failed to get core clock\n");
+ ret = PTR_ERR(pltfm_priv->clk);
goto err_pltfm_free;
}
- if (clk_set_rate(kona_dev->external_clk, host->mmc->f_max) != 0) {
- dev_err(dev, "Failed to set rate external clock\n");
+ if (clk_set_rate(pltfm_priv->clk, host->mmc->f_max) != 0) {
+ dev_err(dev, "Failed to set rate core clock\n");
goto err_pltfm_free;
}
- if (clk_prepare_enable(kona_dev->external_clk) != 0) {
- dev_err(dev, "Failed to enable external clock\n");
+ if (clk_prepare_enable(pltfm_priv->clk) != 0) {
+ dev_err(dev, "Failed to enable core clock\n");
goto err_pltfm_free;
}
sdhci_bcm_kona_sd_reset(host);
err_clk_disable:
- clk_disable_unprepare(kona_dev->external_clk);
+ clk_disable_unprepare(pltfm_priv->clk);
err_pltfm_free:
sdhci_pltfm_free(pdev);
return ret;
}
-static int sdhci_bcm_kona_remove(struct platform_device *pdev)
-{
- struct sdhci_host *host = platform_get_drvdata(pdev);
- struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
- struct sdhci_bcm_kona_dev *kona_dev = sdhci_pltfm_priv(pltfm_priv);
- int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
-
- sdhci_remove_host(host, dead);
-
- clk_disable_unprepare(kona_dev->external_clk);
-
- sdhci_pltfm_free(pdev);
-
- return 0;
-}
-
static struct platform_driver sdhci_bcm_kona_driver = {
.driver = {
.name = "sdhci-kona",
.of_match_table = sdhci_bcm_kona_of_match,
},
.probe = sdhci_bcm_kona_probe,
- .remove = sdhci_bcm_kona_remove,
+ .remove = sdhci_pltfm_unregister,
};
module_platform_driver(sdhci_bcm_kona_driver);
return ret;
}
-static int bcm2835_sdhci_remove(struct platform_device *pdev)
-{
- return sdhci_pltfm_unregister(pdev);
-}
-
static const struct of_device_id bcm2835_sdhci_of_match[] = {
{ .compatible = "brcm,bcm2835-sdhci" },
{ }
.pm = SDHCI_PLTFM_PMOPS,
},
.probe = bcm2835_sdhci_probe,
- .remove = bcm2835_sdhci_remove,
+ .remove = sdhci_pltfm_unregister,
};
module_platform_driver(bcm2835_sdhci_driver);
return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata, 0);
}
-static int sdhci_cns3xxx_remove(struct platform_device *pdev)
-{
- return sdhci_pltfm_unregister(pdev);
-}
-
static struct platform_driver sdhci_cns3xxx_driver = {
.driver = {
.name = "sdhci-cns3xxx",
.pm = SDHCI_PLTFM_PMOPS,
},
.probe = sdhci_cns3xxx_probe,
- .remove = sdhci_cns3xxx_remove,
+ .remove = sdhci_pltfm_unregister,
};
module_platform_driver(sdhci_cns3xxx_driver);
#include "sdhci-pltfm.h"
-struct sdhci_dove_priv {
- struct clk *clk;
-};
-
static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)
{
u16 ret;
{
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
- struct sdhci_dove_priv *priv;
int ret;
- priv = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_dove_priv),
- GFP_KERNEL);
- if (!priv) {
- dev_err(&pdev->dev, "unable to allocate private data");
- return -ENOMEM;
- }
-
- priv->clk = devm_clk_get(&pdev->dev, NULL);
-
host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata, 0);
if (IS_ERR(host))
return PTR_ERR(host);
pltfm_host = sdhci_priv(host);
- pltfm_host->priv = priv;
+ pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
- if (!IS_ERR(priv->clk))
- clk_prepare_enable(priv->clk);
+ if (!IS_ERR(pltfm_host->clk))
+ clk_prepare_enable(pltfm_host->clk);
ret = mmc_of_parse(host->mmc);
if (ret)
return 0;
err_sdhci_add:
- if (!IS_ERR(priv->clk))
- clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(pltfm_host->clk);
sdhci_pltfm_free(pdev);
return ret;
}
-static int sdhci_dove_remove(struct platform_device *pdev)
-{
- struct sdhci_host *host = platform_get_drvdata(pdev);
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_dove_priv *priv = pltfm_host->priv;
-
- sdhci_pltfm_unregister(pdev);
-
- if (!IS_ERR(priv->clk))
- clk_disable_unprepare(priv->clk);
-
- return 0;
-}
-
static const struct of_device_id sdhci_dove_of_match_table[] = {
{ .compatible = "marvell,dove-sdhci", },
{}
.of_match_table = sdhci_dove_of_match_table,
},
.probe = sdhci_dove_probe,
- .remove = sdhci_dove_remove,
+ .remove = sdhci_pltfm_unregister,
};
module_platform_driver(sdhci_dove_driver);
new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
else
new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
- writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
+ writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
return;
case SDHCI_HOST_CONTROL2:
new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
#ifdef CONFIG_OF
static int
sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
+ struct sdhci_host *host,
struct esdhc_platform_data *boarddata)
{
struct device_node *np = pdev->dev.of_node;
if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
boarddata->delay_line = 0;
+ mmc_of_parse_voltage(np, &host->ocr_mask);
+
return 0;
}
#else
static inline int
sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
+ struct sdhci_host *host,
struct esdhc_platform_data *boarddata)
{
return -ENODEV;
host->ioaddr + ESDHC_TUNING_CTRL);
boarddata = &imx_data->boarddata;
- if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
+ if (sdhci_esdhc_imx_probe_dt(pdev, host, boarddata) < 0) {
if (!host->mmc->parent->platform_data) {
dev_err(mmc_dev(host->mmc), "no board data!\n");
err = -EINVAL;
host->mmc->parent->platform_data);
}
- /* write_protect */
- if (boarddata->wp_type == ESDHC_WP_GPIO) {
- err = mmc_gpio_request_ro(host->mmc, boarddata->wp_gpio);
- if (err) {
- dev_err(mmc_dev(host->mmc),
- "failed to request write-protect gpio!\n");
- goto disable_clk;
- }
- host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
- }
-
/* card_detect */
- switch (boarddata->cd_type) {
- case ESDHC_CD_GPIO:
- err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio, 0);
- if (err) {
- dev_err(mmc_dev(host->mmc),
- "failed to request card-detect gpio!\n");
- goto disable_clk;
- }
- /* fall through */
-
- case ESDHC_CD_CONTROLLER:
- /* we have a working card_detect back */
+ if (boarddata->cd_type == ESDHC_CD_CONTROLLER)
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
- break;
-
- case ESDHC_CD_PERMANENT:
- host->mmc->caps |= MMC_CAP_NONREMOVABLE;
- break;
-
- case ESDHC_CD_NONE:
- break;
- }
switch (boarddata->max_bus_width) {
case 8:
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
}
+ /* call to generic mmc_of_parse to support additional capabilities */
+ err = mmc_of_parse(host->mmc);
+ if (err)
+ goto disable_clk;
+
err = sdhci_add_host(host);
if (err)
goto disable_clk;
--- /dev/null
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * iProc SDHCI platform driver
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mmc/host.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include "sdhci-pltfm.h"
+
+struct sdhci_iproc_data {
+ const struct sdhci_pltfm_data *pdata;
+ u32 caps;
+ u32 caps1;
+};
+
+struct sdhci_iproc_host {
+ const struct sdhci_iproc_data *data;
+ u32 shadow_cmd;
+ u32 shadow_blk;
+};
+
+#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
+
+static inline u32 sdhci_iproc_readl(struct sdhci_host *host, int reg)
+{
+ u32 val = readl(host->ioaddr + reg);
+
+ pr_debug("%s: readl [0x%02x] 0x%08x\n",
+ mmc_hostname(host->mmc), reg, val);
+ return val;
+}
+
+static u16 sdhci_iproc_readw(struct sdhci_host *host, int reg)
+{
+ u32 val = sdhci_iproc_readl(host, (reg & ~3));
+ u16 word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
+ return word;
+}
+
+static u8 sdhci_iproc_readb(struct sdhci_host *host, int reg)
+{
+ u32 val = sdhci_iproc_readl(host, (reg & ~3));
+ u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff;
+ return byte;
+}
+
+static inline void sdhci_iproc_writel(struct sdhci_host *host, u32 val, int reg)
+{
+ pr_debug("%s: writel [0x%02x] 0x%08x\n",
+ mmc_hostname(host->mmc), reg, val);
+
+ writel(val, host->ioaddr + reg);
+
+ if (host->clock <= 400000) {
+ /* Round up to micro-second four SD clock delay */
+ if (host->clock)
+ udelay((4 * 1000000 + host->clock - 1) / host->clock);
+ else
+ udelay(10);
+ }
+}
+
+/*
+ * The Arasan has a bugette whereby it may lose the content of successive
+ * writes to the same register that are within two SD-card clock cycles of
+ * each other (a clock domain crossing problem). The data
+ * register does not have this problem, which is just as well - otherwise we'd
+ * have to nobble the DMA engine too.
+ *
+ * This wouldn't be a problem with the code except that we can only write the
+ * controller with 32-bit writes. So two different 16-bit registers are
+ * written back to back creates the problem.
+ *
+ * In reality, this only happens when SDHCI_BLOCK_SIZE and SDHCI_BLOCK_COUNT
+ * are written followed by SDHCI_TRANSFER_MODE and SDHCI_COMMAND.
+ * The BLOCK_SIZE and BLOCK_COUNT are meaningless until a command issued so
+ * the work around can be further optimized. We can keep shadow values of
+ * BLOCK_SIZE, BLOCK_COUNT, and TRANSFER_MODE until a COMMAND is issued.
+ * Then, write the BLOCK_SIZE+BLOCK_COUNT in a single 32-bit write followed
+ * by the TRANSFER+COMMAND in another 32-bit write.
+ */
+static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_iproc_host *iproc_host = sdhci_pltfm_priv(pltfm_host);
+ u32 word_shift = REG_OFFSET_IN_BITS(reg);
+ u32 mask = 0xffff << word_shift;
+ u32 oldval, newval;
+
+ if (reg == SDHCI_COMMAND) {
+ /* Write the block now as we are issuing a command */
+ if (iproc_host->shadow_blk != 0) {
+ sdhci_iproc_writel(host, iproc_host->shadow_blk,
+ SDHCI_BLOCK_SIZE);
+ iproc_host->shadow_blk = 0;
+ }
+ oldval = iproc_host->shadow_cmd;
+ } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
+ /* Block size and count are stored in shadow reg */
+ oldval = iproc_host->shadow_blk;
+ } else {
+ /* Read reg, all other registers are not shadowed */
+ oldval = sdhci_iproc_readl(host, (reg & ~3));
+ }
+ newval = (oldval & ~mask) | (val << word_shift);
+
+ if (reg == SDHCI_TRANSFER_MODE) {
+ /* Save the transfer mode until the command is issued */
+ iproc_host->shadow_cmd = newval;
+ } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
+ /* Save the block info until the command is issued */
+ iproc_host->shadow_blk = newval;
+ } else {
+ /* Command or other regular 32-bit write */
+ sdhci_iproc_writel(host, newval, reg & ~3);
+ }
+}
+
+static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+ u32 oldval = sdhci_iproc_readl(host, (reg & ~3));
+ u32 byte_shift = REG_OFFSET_IN_BITS(reg);
+ u32 mask = 0xff << byte_shift;
+ u32 newval = (oldval & ~mask) | (val << byte_shift);
+
+ sdhci_iproc_writel(host, newval, reg & ~3);
+}
+
+static const struct sdhci_ops sdhci_iproc_ops = {
+ .read_l = sdhci_iproc_readl,
+ .read_w = sdhci_iproc_readw,
+ .read_b = sdhci_iproc_readb,
+ .write_l = sdhci_iproc_writel,
+ .write_w = sdhci_iproc_writew,
+ .write_b = sdhci_iproc_writeb,
+ .set_clock = sdhci_set_clock,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_reset,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+};
+
+static const struct sdhci_pltfm_data sdhci_iproc_pltfm_data = {
+ .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
+ .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN,
+ .ops = &sdhci_iproc_ops,
+};
+
+static const struct sdhci_iproc_data iproc_data = {
+ .pdata = &sdhci_iproc_pltfm_data,
+ .caps = 0x05E90000,
+ .caps1 = 0x00000064,
+};
+
+static const struct of_device_id sdhci_iproc_of_match[] = {
+ { .compatible = "brcm,sdhci-iproc-cygnus", .data = &iproc_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sdhci_iproc_of_match);
+
+static int sdhci_iproc_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ const struct sdhci_iproc_data *iproc_data;
+ struct sdhci_host *host;
+ struct sdhci_iproc_host *iproc_host;
+ struct sdhci_pltfm_host *pltfm_host;
+ int ret;
+
+ match = of_match_device(sdhci_iproc_of_match, &pdev->dev);
+ if (!match)
+ return -EINVAL;
+ iproc_data = match->data;
+
+ host = sdhci_pltfm_init(pdev, iproc_data->pdata, sizeof(*iproc_host));
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ pltfm_host = sdhci_priv(host);
+ iproc_host = sdhci_pltfm_priv(pltfm_host);
+
+ iproc_host->data = iproc_data;
+
+ mmc_of_parse(host->mmc);
+ sdhci_get_of_property(pdev);
+
+ /* Enable EMMC 1/8V DDR capable */
+ host->mmc->caps |= MMC_CAP_1_8V_DDR;
+
+ pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pltfm_host->clk)) {
+ ret = PTR_ERR(pltfm_host->clk);
+ goto err;
+ }
+
+ if (iproc_host->data->pdata->quirks & SDHCI_QUIRK_MISSING_CAPS) {
+ host->caps = iproc_host->data->caps;
+ host->caps1 = iproc_host->data->caps1;
+ }
+
+ return sdhci_add_host(host);
+
+err:
+ sdhci_pltfm_free(pdev);
+ return ret;
+}
+
+static int sdhci_iproc_remove(struct platform_device *pdev)
+{
+ return sdhci_pltfm_unregister(pdev);
+}
+
+static struct platform_driver sdhci_iproc_driver = {
+ .driver = {
+ .name = "sdhci-iproc",
+ .of_match_table = sdhci_iproc_of_match,
+ .pm = SDHCI_PLTFM_PMOPS,
+ },
+ .probe = sdhci_iproc_probe,
+ .remove = sdhci_iproc_remove,
+};
+module_platform_driver(sdhci_iproc_driver);
+
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("IPROC SDHCI driver");
+MODULE_LICENSE("GPL v2");
#include "sdhci-pltfm.h"
+#define CORE_MCI_VERSION 0x50
+#define CORE_VERSION_MAJOR_SHIFT 28
+#define CORE_VERSION_MAJOR_MASK (0xf << CORE_VERSION_MAJOR_SHIFT)
+#define CORE_VERSION_MINOR_MASK 0xff
+
#define CORE_HC_MODE 0x78
#define HC_MODE_EN 0x1
#define CORE_POWER 0x0
#define CORE_VENDOR_SPEC 0x10c
#define CORE_CLK_PWRSAVE BIT(1)
+#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c
+
#define CDR_SELEXT_SHIFT 20
#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)
#define CMUX_SHIFT_PHASE_SHIFT 24
struct sdhci_msm_host *msm_host;
struct resource *core_memres;
int ret;
- u16 host_version;
+ u16 host_version, core_minor;
+ u32 core_version, caps;
+ u8 core_major;
msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL);
if (!msm_host)
host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
SDHCI_VENDOR_VER_SHIFT));
+ core_version = readl_relaxed(msm_host->core_mem + CORE_MCI_VERSION);
+ core_major = (core_version & CORE_VERSION_MAJOR_MASK) >>
+ CORE_VERSION_MAJOR_SHIFT;
+ core_minor = core_version & CORE_VERSION_MINOR_MASK;
+ dev_dbg(&pdev->dev, "MCI Version: 0x%08x, major: 0x%04x, minor: 0x%02x\n",
+ core_version, core_major, core_minor);
+
+ /*
+ * Support for some capabilities is not advertised by newer
+ * controller versions and must be explicitly enabled.
+ */
+ if (core_major >= 1 && core_minor != 0x11 && core_minor != 0x12) {
+ caps = readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES);
+ caps |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT;
+ writel_relaxed(caps, host->ioaddr +
+ CORE_VENDOR_SPEC_CAPABILITIES0);
+ }
+
ret = sdhci_add_host(host);
if (ret)
goto clk_disable;
pltfm_host->priv = sdhci_arasan;
pltfm_host->clk = clk_xin;
+ ret = mmc_of_parse(host->mmc);
+ if (ret) {
+ dev_err(&pdev->dev, "parsing dt failed (%u)\n", ret);
+ goto clk_disable_all;
+ }
+
ret = sdhci_add_host(host);
if (ret)
goto err_pltfm_free;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
- clk_disable_unprepare(pltfm_host->clk);
clk_disable_unprepare(sdhci_arasan->clk_ahb);
return sdhci_pltfm_unregister(pdev);
return ret;
}
-static int sdhci_esdhc_remove(struct platform_device *pdev)
-{
- return sdhci_pltfm_unregister(pdev);
-}
-
static const struct of_device_id sdhci_esdhc_of_match[] = {
{ .compatible = "fsl,mpc8379-esdhc" },
{ .compatible = "fsl,mpc8536-esdhc" },
.pm = ESDHC_PMOPS,
},
.probe = sdhci_esdhc_probe,
- .remove = sdhci_esdhc_remove,
+ .remove = sdhci_pltfm_unregister,
};
module_platform_driver(sdhci_esdhc_driver);
return sdhci_pltfm_register(pdev, &sdhci_hlwd_pdata, 0);
}
-static int sdhci_hlwd_remove(struct platform_device *pdev)
-{
- return sdhci_pltfm_unregister(pdev);
-}
-
static const struct of_device_id sdhci_hlwd_of_match[] = {
{ .compatible = "nintendo,hollywood-sdhci" },
{ }
.pm = SDHCI_PLTFM_PMOPS,
},
.probe = sdhci_hlwd_probe,
- .remove = sdhci_hlwd_remove,
+ .remove = sdhci_pltfm_unregister,
};
module_platform_driver(sdhci_hlwd_driver);
static const struct sdhci_pci_fixes sdhci_rtsx = {
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_BROKEN_64_BIT_DMA |
SDHCI_QUIRK2_BROKEN_DDR50,
.probe_slot = rtsx_probe_slot,
};
u32 bus_width;
int size;
- if (of_device_is_available(np)) {
- if (of_get_property(np, "sdhci,auto-cmd12", NULL))
- host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+ if (of_get_property(np, "sdhci,auto-cmd12", NULL))
+ host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
- if (of_get_property(np, "sdhci,1-bit-only", NULL) ||
- (of_property_read_u32(np, "bus-width", &bus_width) == 0 &&
- bus_width == 1))
- host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
+ if (of_get_property(np, "sdhci,1-bit-only", NULL) ||
+ (of_property_read_u32(np, "bus-width", &bus_width) == 0 &&
+ bus_width == 1))
+ host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
- if (sdhci_of_wp_inverted(np))
- host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
+ if (sdhci_of_wp_inverted(np))
+ host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
- if (of_get_property(np, "broken-cd", NULL))
- host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ if (of_get_property(np, "broken-cd", NULL))
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
- if (of_get_property(np, "no-1-8-v", NULL))
- host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
+ if (of_get_property(np, "no-1-8-v", NULL))
+ host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
- if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc"))
- host->quirks |= SDHCI_QUIRK_BROKEN_DMA;
+ if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc"))
+ host->quirks |= SDHCI_QUIRK_BROKEN_DMA;
- if (of_device_is_compatible(np, "fsl,p2020-esdhc") ||
- of_device_is_compatible(np, "fsl,p1010-esdhc") ||
- of_device_is_compatible(np, "fsl,t4240-esdhc") ||
- of_device_is_compatible(np, "fsl,mpc8536-esdhc"))
- host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
+ if (of_device_is_compatible(np, "fsl,p2020-esdhc") ||
+ of_device_is_compatible(np, "fsl,p1010-esdhc") ||
+ of_device_is_compatible(np, "fsl,t4240-esdhc") ||
+ of_device_is_compatible(np, "fsl,mpc8536-esdhc"))
+ host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
- clk = of_get_property(np, "clock-frequency", &size);
- if (clk && size == sizeof(*clk) && *clk)
- pltfm_host->clock = be32_to_cpup(clk);
+ clk = of_get_property(np, "clock-frequency", &size);
+ if (clk && size == sizeof(*clk) && *clk)
+ pltfm_host->clock = be32_to_cpup(clk);
- if (of_find_property(np, "keep-power-in-suspend", NULL))
- host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
+ if (of_find_property(np, "keep-power-in-suspend", NULL))
+ host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
- if (of_find_property(np, "enable-sdio-wakeup", NULL))
- host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
- }
+ if (of_find_property(np, "enable-sdio-wakeup", NULL))
+ host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
}
#else
void sdhci_get_of_property(struct platform_device *pdev) {}
int sdhci_pltfm_unregister(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
sdhci_remove_host(host, dead);
+ clk_disable_unprepare(pltfm_host->clk);
sdhci_pltfm_free(pdev);
return 0;
#define SIRF_TUNING_COUNT 128
struct sdhci_sirf_priv {
- struct clk *clk;
int gpio_cd;
};
-static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host)
-{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
- return clk_get_rate(priv->clk);
-}
-
static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width)
{
u8 ctrl;
int tuning_seq_cnt = 3;
u8 phase, tuned_phases[SIRF_TUNING_COUNT];
u8 tuned_phase_cnt = 0;
- int rc, longest_range = 0;
+ int rc = 0, longest_range = 0;
int start = -1, end = 0, tuning_value = -1, range = 0;
u16 clock_setting;
struct mmc_host *mmc = host->mmc;
phase = 0;
do {
sdhci_writel(host,
- clock_setting | phase | (phase << 7) | (phase << 16),
+ clock_setting | phase,
SDHCI_CLK_DELAY_SETTING);
if (!mmc_send_tuning(mmc)) {
*/
phase = tuning_value;
sdhci_writel(host,
- clock_setting | phase | (phase << 7) | (phase << 16),
+ clock_setting | phase,
SDHCI_CLK_DELAY_SETTING);
dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
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,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
.set_bus_width = sdhci_sirf_set_bus_width,
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
return PTR_ERR(host);
pltfm_host = sdhci_priv(host);
+ pltfm_host->clk = clk;
priv = sdhci_pltfm_priv(pltfm_host);
- priv->clk = clk;
priv->gpio_cd = gpio_cd;
sdhci_get_of_property(pdev);
- ret = clk_prepare_enable(priv->clk);
+ ret = clk_prepare_enable(pltfm_host->clk);
if (ret)
goto err_clk_prepare;
err_request_cd:
sdhci_remove_host(host, 0);
err_sdhci_add:
- clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(pltfm_host->clk);
err_clk_prepare:
sdhci_pltfm_free(pdev);
return ret;
}
-static int sdhci_sirf_remove(struct platform_device *pdev)
-{
- struct sdhci_host *host = platform_get_drvdata(pdev);
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
-
- sdhci_pltfm_unregister(pdev);
-
- clk_disable_unprepare(priv->clk);
- return 0;
-}
-
#ifdef CONFIG_PM_SLEEP
static int sdhci_sirf_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
int ret;
ret = sdhci_suspend_host(host);
if (ret)
return ret;
- clk_disable(priv->clk);
+ clk_disable(pltfm_host->clk);
return 0;
}
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
int ret;
- ret = clk_enable(priv->clk);
+ ret = clk_enable(pltfm_host->clk);
if (ret) {
dev_dbg(dev, "Resume: Error enabling clock\n");
return ret;
#endif
},
.probe = sdhci_sirf_probe,
- .remove = sdhci_sirf_remove,
+ .remove = sdhci_pltfm_unregister,
};
module_platform_driver(sdhci_sirf_driver);
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/mmc/host.h>
-#include <linux/mmc/sdhci-spear.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/io.h>
#include "sdhci.h"
struct spear_sdhci {
struct clk *clk;
- struct sdhci_plat_data *data;
+ int card_int_gpio;
};
/* sdhci ops */
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
-#ifdef CONFIG_OF
-static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev)
+static void sdhci_probe_config_dt(struct device_node *np,
+ struct spear_sdhci *host)
{
- struct device_node *np = pdev->dev.of_node;
- struct sdhci_plat_data *pdata = NULL;
int cd_gpio;
cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
if (!gpio_is_valid(cd_gpio))
cd_gpio = -1;
- /* If pdata is required */
- if (cd_gpio != -1) {
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- dev_err(&pdev->dev, "DT: kzalloc failed\n");
- else
- pdata->card_int_gpio = cd_gpio;
- }
-
- return pdata;
-}
-#else
-static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev)
-{
- return ERR_PTR(-ENOSYS);
+ host->card_int_gpio = cd_gpio;
}
-#endif
static int sdhci_probe(struct platform_device *pdev)
{
- struct device_node *np = pdev->dev.of_node;
struct sdhci_host *host;
struct resource *iomem;
struct spear_sdhci *sdhci;
dev_dbg(&pdev->dev, "Error setting desired clk, clk=%lu\n",
clk_get_rate(sdhci->clk));
- if (np) {
- sdhci->data = sdhci_probe_config_dt(pdev);
- if (IS_ERR(sdhci->data)) {
- dev_err(&pdev->dev, "DT: Failed to get pdata\n");
- goto disable_clk;
- }
- } else {
- sdhci->data = dev_get_platdata(&pdev->dev);
- }
-
+ sdhci_probe_config_dt(pdev->dev.of_node, sdhci);
/*
* It is optional to use GPIOs for sdhci card detection. If
- * sdhci->data is NULL, then use original sdhci lines otherwise
+ * sdhci->card_int_gpio < 0, then use original sdhci lines otherwise
* GPIO lines. We use the built-in GPIO support for this.
*/
- if (sdhci->data && sdhci->data->card_int_gpio >= 0) {
- ret = mmc_gpio_request_cd(host->mmc,
- sdhci->data->card_int_gpio, 0);
+ if (sdhci->card_int_gpio >= 0) {
+ ret = mmc_gpio_request_cd(host->mmc, sdhci->card_int_gpio, 0);
if (ret < 0) {
dev_dbg(&pdev->dev,
"failed to request card-detect gpio%d\n",
- sdhci->data->card_int_gpio);
+ sdhci->card_int_gpio);
goto disable_clk;
}
}
#include <linux/module.h>
#include <linux/err.h>
#include <linux/mmc/host.h>
-
+#include <linux/reset.h>
#include "sdhci-pltfm.h"
+struct st_mmc_platform_data {
+ struct reset_control *rstc;
+ void __iomem *top_ioaddr;
+};
+
+/* MMCSS glue logic to setup the HC on some ST SoCs (e.g. STiH407 family) */
+
+#define ST_MMC_CCONFIG_REG_1 0x400
+#define ST_MMC_CCONFIG_TIMEOUT_CLK_UNIT BIT(24)
+#define ST_MMC_CCONFIG_TIMEOUT_CLK_FREQ BIT(12)
+#define ST_MMC_CCONFIG_TUNING_COUNT_DEFAULT BIT(8)
+#define ST_MMC_CCONFIG_ASYNC_WAKEUP BIT(0)
+#define ST_MMC_CCONFIG_1_DEFAULT \
+ ((ST_MMC_CCONFIG_TIMEOUT_CLK_UNIT) | \
+ (ST_MMC_CCONFIG_TIMEOUT_CLK_FREQ) | \
+ (ST_MMC_CCONFIG_TUNING_COUNT_DEFAULT))
+
+#define ST_MMC_CCONFIG_REG_2 0x404
+#define ST_MMC_CCONFIG_HIGH_SPEED BIT(28)
+#define ST_MMC_CCONFIG_ADMA2 BIT(24)
+#define ST_MMC_CCONFIG_8BIT BIT(20)
+#define ST_MMC_CCONFIG_MAX_BLK_LEN 16
+#define MAX_BLK_LEN_1024 1
+#define MAX_BLK_LEN_2048 2
+#define BASE_CLK_FREQ_200 0xc8
+#define BASE_CLK_FREQ_100 0x64
+#define BASE_CLK_FREQ_50 0x32
+#define ST_MMC_CCONFIG_2_DEFAULT \
+ (ST_MMC_CCONFIG_HIGH_SPEED | ST_MMC_CCONFIG_ADMA2 | \
+ ST_MMC_CCONFIG_8BIT | \
+ (MAX_BLK_LEN_1024 << ST_MMC_CCONFIG_MAX_BLK_LEN))
+
+#define ST_MMC_CCONFIG_REG_3 0x408
+#define ST_MMC_CCONFIG_EMMC_SLOT_TYPE BIT(28)
+#define ST_MMC_CCONFIG_64BIT BIT(24)
+#define ST_MMC_CCONFIG_ASYNCH_INTR_SUPPORT BIT(20)
+#define ST_MMC_CCONFIG_1P8_VOLT BIT(16)
+#define ST_MMC_CCONFIG_3P0_VOLT BIT(12)
+#define ST_MMC_CCONFIG_3P3_VOLT BIT(8)
+#define ST_MMC_CCONFIG_SUSP_RES_SUPPORT BIT(4)
+#define ST_MMC_CCONFIG_SDMA BIT(0)
+#define ST_MMC_CCONFIG_3_DEFAULT \
+ (ST_MMC_CCONFIG_ASYNCH_INTR_SUPPORT | \
+ ST_MMC_CCONFIG_3P3_VOLT | \
+ ST_MMC_CCONFIG_SUSP_RES_SUPPORT | \
+ ST_MMC_CCONFIG_SDMA)
+
+#define ST_MMC_CCONFIG_REG_4 0x40c
+#define ST_MMC_CCONFIG_D_DRIVER BIT(20)
+#define ST_MMC_CCONFIG_C_DRIVER BIT(16)
+#define ST_MMC_CCONFIG_A_DRIVER BIT(12)
+#define ST_MMC_CCONFIG_DDR50 BIT(8)
+#define ST_MMC_CCONFIG_SDR104 BIT(4)
+#define ST_MMC_CCONFIG_SDR50 BIT(0)
+#define ST_MMC_CCONFIG_4_DEFAULT 0
+
+#define ST_MMC_CCONFIG_REG_5 0x410
+#define ST_MMC_CCONFIG_TUNING_FOR_SDR50 BIT(8)
+#define RETUNING_TIMER_CNT_MAX 0xf
+#define ST_MMC_CCONFIG_5_DEFAULT 0
+
+/* I/O configuration for Arasan IP */
+#define ST_MMC_GP_OUTPUT 0x450
+#define ST_MMC_GP_OUTPUT_CD BIT(12)
+
+#define ST_MMC_STATUS_R 0x460
+
+#define ST_TOP_MMC_DLY_FIX_OFF(x) (x - 0x8)
+
+/* TOP config registers to manage static and dynamic delay */
+#define ST_TOP_MMC_TX_CLK_DLY ST_TOP_MMC_DLY_FIX_OFF(0x8)
+#define ST_TOP_MMC_RX_CLK_DLY ST_TOP_MMC_DLY_FIX_OFF(0xc)
+/* MMC delay control register */
+#define ST_TOP_MMC_DLY_CTRL ST_TOP_MMC_DLY_FIX_OFF(0x18)
+#define ST_TOP_MMC_DLY_CTRL_DLL_BYPASS_CMD BIT(0)
+#define ST_TOP_MMC_DLY_CTRL_DLL_BYPASS_PH_SEL BIT(1)
+#define ST_TOP_MMC_DLY_CTRL_TX_DLL_ENABLE BIT(8)
+#define ST_TOP_MMC_DLY_CTRL_RX_DLL_ENABLE BIT(9)
+#define ST_TOP_MMC_DLY_CTRL_ATUNE_NOT_CFG_DLY BIT(10)
+#define ST_TOP_MMC_START_DLL_LOCK BIT(11)
+
+/* register to provide the phase-shift value for DLL */
+#define ST_TOP_MMC_TX_DLL_STEP_DLY ST_TOP_MMC_DLY_FIX_OFF(0x1c)
+#define ST_TOP_MMC_RX_DLL_STEP_DLY ST_TOP_MMC_DLY_FIX_OFF(0x20)
+#define ST_TOP_MMC_RX_CMD_STEP_DLY ST_TOP_MMC_DLY_FIX_OFF(0x24)
+
+/* phase shift delay on the tx clk 2.188ns */
+#define ST_TOP_MMC_TX_DLL_STEP_DLY_VALID 0x6
+
+#define ST_TOP_MMC_DLY_MAX 0xf
+
+#define ST_TOP_MMC_DYN_DLY_CONF \
+ (ST_TOP_MMC_DLY_CTRL_TX_DLL_ENABLE | \
+ ST_TOP_MMC_DLY_CTRL_ATUNE_NOT_CFG_DLY | \
+ ST_TOP_MMC_START_DLL_LOCK)
+
+/*
+ * For clock speeds greater than 90MHz, we need to check that the
+ * DLL procedure has finished before switching to ultra-speed modes.
+ */
+#define CLK_TO_CHECK_DLL_LOCK 90000000
+
+static inline void st_mmcss_set_static_delay(void __iomem *ioaddr)
+{
+ if (!ioaddr)
+ return;
+
+ writel_relaxed(0x0, ioaddr + ST_TOP_MMC_DLY_CTRL);
+ writel_relaxed(ST_TOP_MMC_DLY_MAX,
+ ioaddr + ST_TOP_MMC_TX_CLK_DLY);
+}
+
+/**
+ * st_mmcss_cconfig: configure the Arasan HC inside the flashSS.
+ * @np: dt device node.
+ * @host: sdhci host
+ * Description: this function is to configure the Arasan host controller.
+ * On some ST SoCs, i.e. STiH407 family, the MMC devices inside a dedicated
+ * flashSS sub-system which needs to be configured to be compliant to eMMC 4.5
+ * or eMMC4.3. This has to be done before registering the sdhci host.
+ */
+static void st_mmcss_cconfig(struct device_node *np, struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct mmc_host *mhost = host->mmc;
+ u32 cconf2, cconf3, cconf4, cconf5;
+
+ if (!of_device_is_compatible(np, "st,sdhci-stih407"))
+ return;
+
+ cconf2 = ST_MMC_CCONFIG_2_DEFAULT;
+ cconf3 = ST_MMC_CCONFIG_3_DEFAULT;
+ cconf4 = ST_MMC_CCONFIG_4_DEFAULT;
+ cconf5 = ST_MMC_CCONFIG_5_DEFAULT;
+
+ writel_relaxed(ST_MMC_CCONFIG_1_DEFAULT,
+ host->ioaddr + ST_MMC_CCONFIG_REG_1);
+
+ /* Set clock frequency, default to 50MHz if max-frequency is not
+ * provided */
+
+ switch (mhost->f_max) {
+ case 200000000:
+ clk_set_rate(pltfm_host->clk, mhost->f_max);
+ cconf2 |= BASE_CLK_FREQ_200;
+ break;
+ case 100000000:
+ clk_set_rate(pltfm_host->clk, mhost->f_max);
+ cconf2 |= BASE_CLK_FREQ_100;
+ break;
+ default:
+ clk_set_rate(pltfm_host->clk, 50000000);
+ cconf2 |= BASE_CLK_FREQ_50;
+ }
+
+ writel_relaxed(cconf2, host->ioaddr + ST_MMC_CCONFIG_REG_2);
+
+ if (mhost->caps & MMC_CAP_NONREMOVABLE)
+ cconf3 |= ST_MMC_CCONFIG_EMMC_SLOT_TYPE;
+ else
+ /* CARD _D ET_CTRL */
+ writel_relaxed(ST_MMC_GP_OUTPUT_CD,
+ host->ioaddr + ST_MMC_GP_OUTPUT);
+
+ if (mhost->caps & MMC_CAP_UHS_SDR50) {
+ /* use 1.8V */
+ cconf3 |= ST_MMC_CCONFIG_1P8_VOLT;
+ cconf4 |= ST_MMC_CCONFIG_SDR50;
+ /* Use tuning */
+ cconf5 |= ST_MMC_CCONFIG_TUNING_FOR_SDR50;
+ /* Max timeout for retuning */
+ cconf5 |= RETUNING_TIMER_CNT_MAX;
+ }
+
+ if (mhost->caps & MMC_CAP_UHS_SDR104) {
+ /*
+ * SDR104 implies the HC can support HS200 mode, so
+ * it's mandatory to use 1.8V
+ */
+ cconf3 |= ST_MMC_CCONFIG_1P8_VOLT;
+ cconf4 |= ST_MMC_CCONFIG_SDR104;
+ /* Max timeout for retuning */
+ cconf5 |= RETUNING_TIMER_CNT_MAX;
+ }
+
+ if (mhost->caps & MMC_CAP_UHS_DDR50)
+ cconf4 |= ST_MMC_CCONFIG_DDR50;
+
+ writel_relaxed(cconf3, host->ioaddr + ST_MMC_CCONFIG_REG_3);
+ writel_relaxed(cconf4, host->ioaddr + ST_MMC_CCONFIG_REG_4);
+ writel_relaxed(cconf5, host->ioaddr + ST_MMC_CCONFIG_REG_5);
+}
+
+static inline void st_mmcss_set_dll(void __iomem *ioaddr)
+{
+ if (!ioaddr)
+ return;
+
+ writel_relaxed(ST_TOP_MMC_DYN_DLY_CONF, ioaddr + ST_TOP_MMC_DLY_CTRL);
+ writel_relaxed(ST_TOP_MMC_TX_DLL_STEP_DLY_VALID,
+ ioaddr + ST_TOP_MMC_TX_DLL_STEP_DLY);
+}
+
+static int st_mmcss_lock_dll(void __iomem *ioaddr)
+{
+ unsigned long curr, value;
+ unsigned long finish = jiffies + HZ;
+
+ /* Checks if the DLL procedure is finished */
+ do {
+ curr = jiffies;
+ value = readl(ioaddr + ST_MMC_STATUS_R);
+ if (value & 0x1)
+ return 0;
+
+ cpu_relax();
+ } while (!time_after_eq(curr, finish));
+
+ return -EBUSY;
+}
+
+static int sdhci_st_set_dll_for_clock(struct sdhci_host *host)
+{
+ int ret = 0;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct st_mmc_platform_data *pdata = pltfm_host->priv;
+
+ if (host->clock > CLK_TO_CHECK_DLL_LOCK) {
+ st_mmcss_set_dll(pdata->top_ioaddr);
+ ret = st_mmcss_lock_dll(host->ioaddr);
+ }
+
+ return ret;
+}
+
+static void sdhci_st_set_uhs_signaling(struct sdhci_host *host,
+ unsigned int uhs)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct st_mmc_platform_data *pdata = pltfm_host->priv;
+ u16 ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ int ret = 0;
+
+ /* Select Bus Speed Mode for host */
+ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+ switch (uhs) {
+ /*
+ * Set V18_EN -- UHS modes do not work without this.
+ * does not change signaling voltage
+ */
+
+ case MMC_TIMING_UHS_SDR12:
+ st_mmcss_set_static_delay(pdata->top_ioaddr);
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR12 | SDHCI_CTRL_VDD_180;
+ break;
+ case MMC_TIMING_UHS_SDR25:
+ st_mmcss_set_static_delay(pdata->top_ioaddr);
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR25 | SDHCI_CTRL_VDD_180;
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ st_mmcss_set_static_delay(pdata->top_ioaddr);
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR50 | SDHCI_CTRL_VDD_180;
+ ret = sdhci_st_set_dll_for_clock(host);
+ break;
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+ st_mmcss_set_static_delay(pdata->top_ioaddr);
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180;
+ ret = sdhci_st_set_dll_for_clock(host);
+ break;
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ st_mmcss_set_static_delay(pdata->top_ioaddr);
+ ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180;
+ break;
+ }
+
+ if (ret)
+ dev_warn(mmc_dev(host->mmc), "Error setting dll for clock "
+ "(uhs %d)\n", uhs);
+
+ dev_dbg(mmc_dev(host->mmc), "uhs %d, ctrl_2 %04X\n", uhs, ctrl_2);
+
+ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+}
+
static u32 sdhci_st_readl(struct sdhci_host *host, int reg)
{
u32 ret;
.set_bus_width = sdhci_set_bus_width,
.read_l = sdhci_st_readl,
.reset = sdhci_reset,
+ .set_uhs_signaling = sdhci_st_set_uhs_signaling,
};
static const struct sdhci_pltfm_data sdhci_st_pdata = {
.ops = &sdhci_st_ops,
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
- SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_NO_HISPD_BIT,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_STOP_WITH_TC,
};
static int sdhci_st_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
struct sdhci_host *host;
+ struct st_mmc_platform_data *pdata;
struct sdhci_pltfm_host *pltfm_host;
struct clk *clk;
int ret = 0;
u16 host_version;
+ struct resource *res;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
clk = devm_clk_get(&pdev->dev, "mmc");
if (IS_ERR(clk)) {
return PTR_ERR(clk);
}
+ pdata->rstc = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(pdata->rstc))
+ pdata->rstc = NULL;
+ else
+ reset_control_deassert(pdata->rstc);
+
host = sdhci_pltfm_init(pdev, &sdhci_st_pdata, 0);
if (IS_ERR(host)) {
dev_err(&pdev->dev, "Failed sdhci_pltfm_init\n");
- return PTR_ERR(host);
+ ret = PTR_ERR(host);
+ goto err_pltfm_init;
}
ret = mmc_of_parse(host->mmc);
clk_prepare_enable(clk);
+ /* Configure the FlashSS Top registers for setting eMMC TX/RX delay */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "top-mmc-delay");
+ pdata->top_ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pdata->top_ioaddr)) {
+ dev_warn(&pdev->dev, "FlashSS Top Dly registers not available");
+ pdata->top_ioaddr = NULL;
+ }
+
pltfm_host = sdhci_priv(host);
+ pltfm_host->priv = pdata;
pltfm_host->clk = clk;
+ /* Configure the Arasan HC inside the flashSS */
+ st_mmcss_cconfig(np, host);
+
ret = sdhci_add_host(host);
if (ret) {
dev_err(&pdev->dev, "Failed sdhci_add_host\n");
clk_disable_unprepare(clk);
err_of:
sdhci_pltfm_free(pdev);
+err_pltfm_init:
+ if (pdata->rstc)
+ reset_control_assert(pdata->rstc);
return ret;
}
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct st_mmc_platform_data *pdata = pltfm_host->priv;
+ int ret;
- clk_disable_unprepare(pltfm_host->clk);
+ ret = sdhci_pltfm_unregister(pdev);
+
+ if (pdata->rstc)
+ reset_control_assert(pdata->rstc);
- return sdhci_pltfm_unregister(pdev);
+ return ret;
}
#ifdef CONFIG_PM_SLEEP
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct st_mmc_platform_data *pdata = pltfm_host->priv;
int ret = sdhci_suspend_host(host);
if (ret)
goto out;
+ if (pdata->rstc)
+ reset_control_assert(pdata->rstc);
+
clk_disable_unprepare(pltfm_host->clk);
out:
return ret;
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct st_mmc_platform_data *pdata = pltfm_host->priv;
+ struct device_node *np = dev->of_node;
clk_prepare_enable(pltfm_host->clk);
+ if (pdata->rstc)
+ reset_control_deassert(pdata->rstc);
+
+ st_mmcss_cconfig(np, host);
+
return sdhci_resume_host(host);
}
#endif
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
-#include <linux/gpio.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/slot-gpio.h>
+#include <linux/gpio/consumer.h>
#include "sdhci-pltfm.h"
#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;
struct sdhci_tegra {
const struct sdhci_tegra_soc_data *soc_data;
- int power_gpio;
+ struct gpio_desc *power_gpio;
};
static u16 tegra_sdhci_readw(struct sdhci_host *host, int 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;
- }
+ 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 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,
NVQUIRK_DISABLE_SDR104,
};
+static const struct sdhci_ops tegra114_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,
+ .reset = tegra_sdhci_reset,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+};
+
static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
- .ops = &tegra_sdhci_ops,
+ .ops = &tegra114_sdhci_ops,
};
static struct sdhci_tegra_soc_data soc_data_tegra114 = {
.pdata = &sdhci_tegra114_pdata,
.nvquirks = NVQUIRK_DISABLE_SDR50 |
NVQUIRK_DISABLE_DDR50 |
- NVQUIRK_DISABLE_SDR104 |
- NVQUIRK_SHADOW_XFER_MODE_REG,
+ NVQUIRK_DISABLE_SDR104,
};
static const struct of_device_id sdhci_tegra_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
-static int sdhci_tegra_parse_dt(struct device *dev)
-{
- struct device_node *np = dev->of_node;
- struct sdhci_host *host = dev_get_drvdata(dev);
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_tegra *tegra_host = pltfm_host->priv;
-
- tegra_host->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
- return mmc_of_parse(host->mmc);
-}
-
static int sdhci_tegra_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
tegra_host->soc_data = soc_data;
pltfm_host->priv = tegra_host;
- rc = sdhci_tegra_parse_dt(&pdev->dev);
+ rc = mmc_of_parse(host->mmc);
if (rc)
goto err_parse_dt;
- if (gpio_is_valid(tegra_host->power_gpio)) {
- rc = gpio_request(tegra_host->power_gpio, "sdhci_power");
- if (rc) {
- dev_err(mmc_dev(host->mmc),
- "failed to allocate power gpio\n");
- goto err_power_req;
- }
- gpio_direction_output(tegra_host->power_gpio, 1);
+ tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(tegra_host->power_gpio)) {
+ rc = PTR_ERR(tegra_host->power_gpio);
+ goto err_power_req;
}
- clk = clk_get(mmc_dev(host->mmc), NULL);
+ clk = devm_clk_get(mmc_dev(host->mmc), NULL);
if (IS_ERR(clk)) {
dev_err(mmc_dev(host->mmc), "clk err\n");
rc = PTR_ERR(clk);
err_add_host:
clk_disable_unprepare(pltfm_host->clk);
- clk_put(pltfm_host->clk);
err_clk_get:
- if (gpio_is_valid(tegra_host->power_gpio))
- gpio_free(tegra_host->power_gpio);
err_power_req:
err_parse_dt:
err_alloc_tegra_host:
return rc;
}
-static int sdhci_tegra_remove(struct platform_device *pdev)
-{
- struct sdhci_host *host = platform_get_drvdata(pdev);
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_tegra *tegra_host = pltfm_host->priv;
- int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
-
- sdhci_remove_host(host, dead);
-
- if (gpio_is_valid(tegra_host->power_gpio))
- gpio_free(tegra_host->power_gpio);
-
- clk_disable_unprepare(pltfm_host->clk);
- clk_put(pltfm_host->clk);
-
- sdhci_pltfm_free(pdev);
-
- return 0;
-}
-
static struct platform_driver sdhci_tegra_driver = {
.driver = {
.name = "sdhci-tegra",
.pm = SDHCI_PLTFM_PMOPS,
},
.probe = sdhci_tegra_probe,
- .remove = sdhci_tegra_remove,
+ .remove = sdhci_pltfm_unregister,
};
module_platform_driver(sdhci_tegra_driver);
#include <linux/mmc/mmc.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
#include <linux/mmc/slot-gpio.h>
#include "sdhci.h"
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
struct mmc_data *data,
struct sdhci_host_next *next);
+static int sdhci_do_get_cd(struct sdhci_host *host);
#ifdef CONFIG_PM
static int sdhci_runtime_pm_get(struct sdhci_host *host);
* If we are sending CMD23, CMD12 never gets sent
* on successful completion (so no Auto-CMD12).
*/
- if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12))
+ if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12) &&
+ (cmd->opcode != SD_IO_RW_EXTENDED))
mode |= SDHCI_TRNS_AUTO_CMD12;
else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
mode |= SDHCI_TRNS_AUTO_CMD23;
sdhci_runtime_pm_get(host);
- present = mmc_gpio_get_cd(host->mmc);
+ /* Firstly check card presence */
+ present = sdhci_do_get_cd(host);
spin_lock_irqsave(&host->lock, flags);
host->mrq = mrq;
- /*
- * Firstly check card presence from cd-gpio. The return could
- * be one of the following possibilities:
- * negative: cd-gpio is not available
- * zero: cd-gpio is used, and card is removed
- * one: cd-gpio is used, and card is present
- */
- if (present < 0) {
- /* If polling, assume that the card is always present. */
- if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
- present = 1;
- else
- present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
- SDHCI_CARD_PRESENT;
- }
-
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
/* Auto-CMD23 stuff only works in ADMA or PIO. */
if ((host->version >= SDHCI_SPEC_300) &&
((host->flags & SDHCI_USE_ADMA) ||
- !(host->flags & SDHCI_USE_SDMA))) {
+ !(host->flags & SDHCI_USE_SDMA)) &&
+ !(host->quirks2 & SDHCI_QUIRK2_ACMD23_BROKEN)) {
host->flags |= SDHCI_AUTO_CMD23;
DBG("%s: Auto-CMD23 available\n", mmc_hostname(mmc));
} else {
#include <linux/types.h>
#include <linux/io.h>
-#include <linux/mmc/sdhci.h>
+#include <linux/mmc/host.h>
/*
* Controller registers
*/
#define SDHCI_MAX_SEGS 128
+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 */
+
+ unsigned int quirks; /* Deviations from spec. */
+
+/* Controller doesn't honor resets unless we touch the clock register */
+#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
+/* Controller has bad caps bits, but really supports DMA */
+#define SDHCI_QUIRK_FORCE_DMA (1<<1)
+/* Controller doesn't like to be reset when there is no card inserted. */
+#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2)
+/* Controller doesn't like clearing the power reg before a change */
+#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3)
+/* Controller has flaky internal state so reset it on each ios change */
+#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4)
+/* Controller has an unusable DMA engine */
+#define SDHCI_QUIRK_BROKEN_DMA (1<<5)
+/* Controller has an unusable ADMA engine */
+#define SDHCI_QUIRK_BROKEN_ADMA (1<<6)
+/* Controller can only DMA from 32-bit aligned addresses */
+#define SDHCI_QUIRK_32BIT_DMA_ADDR (1<<7)
+/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
+#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<8)
+/* Controller can only ADMA chunks that are a multiple of 32 bits */
+#define SDHCI_QUIRK_32BIT_ADMA_SIZE (1<<9)
+/* Controller needs to be reset after each request to stay stable */
+#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<10)
+/* Controller needs voltage and power writes to happen separately */
+#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<11)
+/* Controller provides an incorrect timeout value for transfers */
+#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12)
+/* Controller has an issue with buffer bits for small transfers */
+#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13)
+/* Controller does not provide transfer-complete interrupt when not busy */
+#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14)
+/* Controller has unreliable card detection */
+#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
+/* Controller reports inverted write-protect state */
+#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
+/* Controller does not like fast PIO transfers */
+#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
+/* Controller has to be forced to use block size of 2048 bytes */
+#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20)
+/* Controller cannot do multi-block transfers */
+#define SDHCI_QUIRK_NO_MULTIBLOCK (1<<21)
+/* Controller can only handle 1-bit data transfers */
+#define SDHCI_QUIRK_FORCE_1_BIT_DATA (1<<22)
+/* Controller needs 10ms delay between applying power and clock */
+#define SDHCI_QUIRK_DELAY_AFTER_POWER (1<<23)
+/* Controller uses SDCLK instead of TMCLK for data timeouts */
+#define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24)
+/* Controller reports wrong base clock capability */
+#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25)
+/* Controller cannot support End Attribute in NOP ADMA descriptor */
+#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26)
+/* Controller is missing device caps. Use caps provided by host */
+#define SDHCI_QUIRK_MISSING_CAPS (1<<27)
+/* Controller uses Auto CMD12 command to stop the transfer */
+#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28)
+/* Controller doesn't have HISPD bit field in HI-SPEED SD card */
+#define SDHCI_QUIRK_NO_HISPD_BIT (1<<29)
+/* Controller treats ADMA descriptors with length 0000h incorrectly */
+#define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC (1<<30)
+/* The read-only detection via SDHCI_PRESENT_STATE register is unstable */
+#define SDHCI_QUIRK_UNSTABLE_RO_DETECT (1<<31)
+
+ unsigned int quirks2; /* More deviations from spec. */
+
+#define SDHCI_QUIRK2_HOST_OFF_CARD_ON (1<<0)
+#define SDHCI_QUIRK2_HOST_NO_CMD23 (1<<1)
+/* The system physically doesn't support 1.8v, even if the host does */
+#define SDHCI_QUIRK2_NO_1_8_V (1<<2)
+#define SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1<<3)
+#define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1<<4)
+/* Controller has a non-standard host control register */
+#define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5)
+/* Controller does not support HS200 */
+#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6)
+/* Controller does not support DDR50 */
+#define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7)
+/* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */
+#define SDHCI_QUIRK2_STOP_WITH_TC (1<<8)
+/* Controller does not support 64-bit DMA */
+#define SDHCI_QUIRK2_BROKEN_64_BIT_DMA (1<<9)
+/* need clear transfer mode register before send cmd */
+#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)
+/* Controller broken with using ACMD23 */
+#define SDHCI_QUIRK2_ACMD23_BROKEN (1<<14)
+
+ int irq; /* Device IRQ */
+ void __iomem *ioaddr; /* Mapped address */
+
+ const struct sdhci_ops *ops; /* Low level hw interface */
+
+ /* Internal data */
+ struct mmc_host *mmc; /* MMC structure */
+ u64 dma_mask; /* custom DMA mask */
+
+#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
+ struct led_classdev led; /* LED control */
+ char led_name[32];
+#endif
+
+ spinlock_t lock; /* Mutex */
+
+ int flags; /* Host attributes */
+#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */
+#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
+#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
+#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
+#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
+#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */
+#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
+#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
+#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
+#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
+#define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */
+#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
+#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
+#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
+
+ unsigned int version; /* SDHCI spec. version */
+
+ unsigned int max_clk; /* Max possible freq (MHz) */
+ unsigned int timeout_clk; /* Timeout freq (KHz) */
+ unsigned int clk_mul; /* Clock Muliplier value */
+
+ unsigned int clock; /* Current clock (MHz) */
+ u8 pwr; /* Current voltage */
+
+ bool runtime_suspended; /* Host is runtime suspended */
+ bool bus_on; /* Bus power prevents runtime suspend */
+ bool preset_enabled; /* Preset is enabled */
+
+ struct mmc_request *mrq; /* Current request */
+ struct mmc_command *cmd; /* Current command */
+ struct mmc_data *data; /* Current data request */
+ unsigned int data_early:1; /* Data finished before cmd */
+ unsigned int busy_handle:1; /* Handling the order of Busy-end */
+
+ struct sg_mapping_iter sg_miter; /* SG state for PIO */
+ unsigned int blocks; /* remaining PIO blocks */
+
+ int sg_count; /* Mapped sg entries */
+
+ void *adma_table; /* ADMA descriptor table */
+ void *align_buffer; /* Bounce buffer */
+
+ size_t adma_table_sz; /* ADMA descriptor table size */
+ size_t align_buffer_sz; /* Bounce buffer size */
+
+ dma_addr_t adma_addr; /* Mapped ADMA descr. table */
+ dma_addr_t align_addr; /* Mapped bounce buffer */
+
+ unsigned int desc_sz; /* ADMA descriptor size */
+ unsigned int align_sz; /* ADMA alignment */
+ unsigned int align_mask; /* ADMA alignment mask */
+
+ struct tasklet_struct finish_tasklet; /* Tasklet structures */
+
+ struct timer_list timer; /* Timer for timeouts */
+
+ u32 caps; /* Alternative CAPABILITY_0 */
+ u32 caps1; /* Alternative CAPABILITY_1 */
+
+ unsigned int ocr_avail_sdio; /* OCR bit masks */
+ unsigned int ocr_avail_sd;
+ unsigned int ocr_avail_mmc;
+ u32 ocr_mask; /* available voltages */
+
+ unsigned timing; /* Current timing */
+
+ u32 thread_isr;
+
+ /* cached registers */
+ u32 ier;
+
+ wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
+ unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
+
+ unsigned int tuning_count; /* Timer count for re-tuning */
+ unsigned int tuning_mode; /* Re-tuning mode supported by 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;
+};
+
struct sdhci_ops {
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
u32 (*read_l)(struct sdhci_host *host, int reg);
struct mmc_command *cmd = mrq->cmd;
u32 opc = cmd->opcode;
u32 mask;
+ unsigned long flags;
switch (opc) {
/* response busy check */
/* set arg */
sh_mmcif_writel(host->addr, MMCIF_CE_ARG, cmd->arg);
/* set cmd */
+ spin_lock_irqsave(&host->lock, flags);
sh_mmcif_writel(host->addr, MMCIF_CE_CMD_SET, opc);
host->wait_for = MMCIF_WAIT_FOR_CMD;
schedule_delayed_work(&host->timeout_work, host->timeout);
+ spin_unlock_irqrestore(&host->lock, flags);
}
static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
struct sh_mmcif_host *host = dev_id;
struct mmc_request *mrq;
bool wait = false;
+ unsigned long flags;
+ int wait_work;
+
+ spin_lock_irqsave(&host->lock, flags);
+ wait_work = host->wait_for;
+ spin_unlock_irqrestore(&host->lock, flags);
cancel_delayed_work_sync(&host->timeout_work);
* All handlers return true, if processing continues, and false, if the
* request has to be completed - successfully or not
*/
- switch (host->wait_for) {
+ switch (wait_work) {
case MMCIF_WAIT_FOR_REQUEST:
/* We're too late, the timeout has already kicked in */
mutex_unlock(&host->thread_lock);
/* Don't run after mmc_remove_host() */
return;
- dev_err(&host->pd->dev, "Timeout waiting for %u on CMD%u\n",
- host->wait_for, mrq->cmd->opcode);
-
spin_lock_irqsave(&host->lock, flags);
if (host->state == STATE_IDLE) {
spin_unlock_irqrestore(&host->lock, flags);
return;
}
+ dev_err(&host->pd->dev, "Timeout waiting for %u on CMD%u\n",
+ host->wait_for, mrq->cmd->opcode);
+
host->state = STATE_TIMEOUT;
spin_unlock_irqrestore(&host->lock, flags);
struct mmc_data *data)
{
struct sunxi_idma_des *pdes = (struct sunxi_idma_des *)host->sg_cpu;
- struct sunxi_idma_des *pdes_pa = (struct sunxi_idma_des *)host->sg_dma;
+ dma_addr_t next_desc = host->sg_dma;
int i, max_len = (1 << host->idma_des_size_bits);
for (i = 0; i < data->sg_len; i++) {
else
pdes[i].buf_size = data->sg[i].length;
+ next_desc += sizeof(struct sunxi_idma_des);
pdes[i].buf_addr_ptr1 = sg_dma_address(&data->sg[i]);
- pdes[i].buf_addr_ptr2 = (u32)&pdes_pa[i + 1];
+ pdes[i].buf_addr_ptr2 = (u32)next_desc;
}
pdes[0].config |= SDXC_IDMAC_DES0_FD;
return PTR_ERR(host->clk_sample);
}
- host->reset = devm_reset_control_get(&pdev->dev, "ahb");
+ host->reset = devm_reset_control_get_optional(&pdev->dev, "ahb");
+ if (PTR_ERR(host->reset) == -EPROBE_DEFER)
+ return PTR_ERR(host->reset);
ret = clk_prepare_enable(host->clk_ahb);
if (ret) {
mmc->f_min = 400000;
mmc->f_max = 50000000;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
- MMC_CAP_ERASE;
+ MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ;
ret = mmc_of_parse(mmc);
if (ret)
void tmio_mmc_host_free(struct tmio_mmc_host *host)
{
mmc_free_host(host->mmc);
-
- host->mmc = NULL;
}
EXPORT_SYMBOL(tmio_mmc_host_free);
.max_blk_size = 2048,
};
-static struct of_device_id wmt_mci_dt_ids[] = {
+static const struct of_device_id wmt_mci_dt_ids[] = {
{ .compatible = "wm,wm8505-sdhc", .data = &wm8505_caps },
{ /* Sentinel */ },
};
#define mmc_cmd_type(cmd) ((cmd)->flags & MMC_CMD_MASK)
unsigned int retries; /* max number of retries */
- unsigned int error; /* command error */
+ int error; /* command error */
/*
* Standard errno values are used for errors, but some have specific
unsigned int timeout_clks; /* data timeout (in clocks) */
unsigned int blksz; /* data block size */
unsigned int blocks; /* number of blocks */
- unsigned int error; /* data error */
+ int error; /* data error */
unsigned int flags;
#define MMC_DATA_WRITE (1 << 8)
* struct dw_mci - MMC controller state shared between all slots
* @lock: Spinlock protecting the queue and associated data.
* @regs: Pointer to MMIO registers.
+ * @fifo_reg: Pointer to MMIO registers for data FIFO
* @sg: Scatterlist entry currently being processed by PIO code, if any.
* @sg_miter: PIO mapping scatterlist iterator.
* @cur_slot: The slot which is currently using the controller.
* @current_speed: Configured rate of the controller.
* @num_slots: Number of slots available.
* @verid: Denote Version ID.
- * @data_offset: Set the offset of DATA register according to VERID.
* @dev: Device associated with the MMC controller.
* @pdata: Platform data associated with the MMC controller.
* @drv_data: Driver specific data for identified variant of the controller
spinlock_t lock;
spinlock_t irq_lock;
void __iomem *regs;
+ void __iomem *fifo_reg;
struct scatterlist *sg;
struct sg_mapping_iter sg_miter;
u32 num_slots;
u32 fifoth_val;
u16 verid;
- u16 data_offset;
struct device *dev;
struct dw_mci_board *pdata;
const struct dw_mci_drv_data *drv_data;
int irq;
int sdio_id0;
+
+ struct timer_list cmd11_timer;
};
/* DMA ops for Internal/External DMAC interface */
};
struct mmc_host_ops {
- /*
- * 'enable' is called when the host is claimed and 'disable' is called
- * when the host is released. 'enable' and 'disable' are deprecated.
- */
- int (*enable)(struct mmc_host *host);
- int (*disable)(struct mmc_host *host);
/*
* It is optional for the host to implement pre_req and post_req in
* order to support double buffering of requests (prepare one
+++ /dev/null
-/*
- * include/linux/mmc/sdhci-spear.h
- *
- * SDHCI declarations specific to ST SPEAr platform
- *
- * Copyright (C) 2010 ST Microelectronics
- * Viresh Kumar <viresh.linux@gmail.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#ifndef LINUX_MMC_SDHCI_SPEAR_H
-#define LINUX_MMC_SDHCI_SPEAR_H
-
-#include <linux/platform_device.h>
-/*
- * struct sdhci_plat_data: spear sdhci platform data structure
- *
- * card_int_gpio: gpio pin used for card detection
- */
-struct sdhci_plat_data {
- int card_int_gpio;
-};
-
-/* This function is used to set platform_data field of pdev->dev */
-static inline void
-sdhci_set_plat_data(struct platform_device *pdev, struct sdhci_plat_data *data)
-{
- pdev->dev.platform_data = data;
-}
-
-#endif /* LINUX_MMC_SDHCI_SPEAR_H */
+++ /dev/null
-/*
- * linux/include/linux/mmc/sdhci.h - Secure Digital Host Controller Interface
- *
- * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- */
-#ifndef LINUX_MMC_SDHCI_H
-#define LINUX_MMC_SDHCI_H
-
-#include <linux/scatterlist.h>
-#include <linux/compiler.h>
-#include <linux/types.h>
-#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 */
-
- unsigned int quirks; /* Deviations from spec. */
-
-/* Controller doesn't honor resets unless we touch the clock register */
-#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
-/* Controller has bad caps bits, but really supports DMA */
-#define SDHCI_QUIRK_FORCE_DMA (1<<1)
-/* Controller doesn't like to be reset when there is no card inserted. */
-#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2)
-/* Controller doesn't like clearing the power reg before a change */
-#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3)
-/* Controller has flaky internal state so reset it on each ios change */
-#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4)
-/* Controller has an unusable DMA engine */
-#define SDHCI_QUIRK_BROKEN_DMA (1<<5)
-/* Controller has an unusable ADMA engine */
-#define SDHCI_QUIRK_BROKEN_ADMA (1<<6)
-/* Controller can only DMA from 32-bit aligned addresses */
-#define SDHCI_QUIRK_32BIT_DMA_ADDR (1<<7)
-/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
-#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<8)
-/* Controller can only ADMA chunks that are a multiple of 32 bits */
-#define SDHCI_QUIRK_32BIT_ADMA_SIZE (1<<9)
-/* Controller needs to be reset after each request to stay stable */
-#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<10)
-/* Controller needs voltage and power writes to happen separately */
-#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<11)
-/* Controller provides an incorrect timeout value for transfers */
-#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12)
-/* Controller has an issue with buffer bits for small transfers */
-#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13)
-/* Controller does not provide transfer-complete interrupt when not busy */
-#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14)
-/* Controller has unreliable card detection */
-#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
-/* Controller reports inverted write-protect state */
-#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
-/* Controller does not like fast PIO transfers */
-#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
-/* Controller has to be forced to use block size of 2048 bytes */
-#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20)
-/* Controller cannot do multi-block transfers */
-#define SDHCI_QUIRK_NO_MULTIBLOCK (1<<21)
-/* Controller can only handle 1-bit data transfers */
-#define SDHCI_QUIRK_FORCE_1_BIT_DATA (1<<22)
-/* Controller needs 10ms delay between applying power and clock */
-#define SDHCI_QUIRK_DELAY_AFTER_POWER (1<<23)
-/* Controller uses SDCLK instead of TMCLK for data timeouts */
-#define SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK (1<<24)
-/* Controller reports wrong base clock capability */
-#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25)
-/* Controller cannot support End Attribute in NOP ADMA descriptor */
-#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26)
-/* Controller is missing device caps. Use caps provided by host */
-#define SDHCI_QUIRK_MISSING_CAPS (1<<27)
-/* Controller uses Auto CMD12 command to stop the transfer */
-#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28)
-/* Controller doesn't have HISPD bit field in HI-SPEED SD card */
-#define SDHCI_QUIRK_NO_HISPD_BIT (1<<29)
-/* Controller treats ADMA descriptors with length 0000h incorrectly */
-#define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC (1<<30)
-/* The read-only detection via SDHCI_PRESENT_STATE register is unstable */
-#define SDHCI_QUIRK_UNSTABLE_RO_DETECT (1<<31)
-
- unsigned int quirks2; /* More deviations from spec. */
-
-#define SDHCI_QUIRK2_HOST_OFF_CARD_ON (1<<0)
-#define SDHCI_QUIRK2_HOST_NO_CMD23 (1<<1)
-/* The system physically doesn't support 1.8v, even if the host does */
-#define SDHCI_QUIRK2_NO_1_8_V (1<<2)
-#define SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1<<3)
-#define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1<<4)
-/* Controller has a non-standard host control register */
-#define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5)
-/* Controller does not support HS200 */
-#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6)
-/* Controller does not support DDR50 */
-#define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7)
-/* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */
-#define SDHCI_QUIRK2_STOP_WITH_TC (1<<8)
-/* Controller does not support 64-bit DMA */
-#define SDHCI_QUIRK2_BROKEN_64_BIT_DMA (1<<9)
-/* need clear transfer mode register before send cmd */
-#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 */
-
- const struct sdhci_ops *ops; /* Low level hw interface */
-
- /* Internal data */
- struct mmc_host *mmc; /* MMC structure */
- u64 dma_mask; /* custom DMA mask */
-
-#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
- struct led_classdev led; /* LED control */
- char led_name[32];
-#endif
-
- spinlock_t lock; /* Mutex */
-
- int flags; /* Host attributes */
-#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */
-#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
-#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
-#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
-#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
-#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */
-#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
-#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
-#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
-#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
-#define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */
-#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
-#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
-#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
-
- unsigned int version; /* SDHCI spec. version */
-
- unsigned int max_clk; /* Max possible freq (MHz) */
- unsigned int timeout_clk; /* Timeout freq (KHz) */
- unsigned int clk_mul; /* Clock Muliplier value */
-
- unsigned int clock; /* Current clock (MHz) */
- u8 pwr; /* Current voltage */
-
- bool runtime_suspended; /* Host is runtime suspended */
- bool bus_on; /* Bus power prevents runtime suspend */
- bool preset_enabled; /* Preset is enabled */
-
- struct mmc_request *mrq; /* Current request */
- struct mmc_command *cmd; /* Current command */
- struct mmc_data *data; /* Current data request */
- unsigned int data_early:1; /* Data finished before cmd */
- unsigned int busy_handle:1; /* Handling the order of Busy-end */
-
- struct sg_mapping_iter sg_miter; /* SG state for PIO */
- unsigned int blocks; /* remaining PIO blocks */
-
- int sg_count; /* Mapped sg entries */
-
- void *adma_table; /* ADMA descriptor table */
- void *align_buffer; /* Bounce buffer */
-
- size_t adma_table_sz; /* ADMA descriptor table size */
- size_t align_buffer_sz; /* Bounce buffer size */
-
- dma_addr_t adma_addr; /* Mapped ADMA descr. table */
- dma_addr_t align_addr; /* Mapped bounce buffer */
-
- unsigned int desc_sz; /* ADMA descriptor size */
- unsigned int align_sz; /* ADMA alignment */
- unsigned int align_mask; /* ADMA alignment mask */
-
- struct tasklet_struct finish_tasklet; /* Tasklet structures */
-
- struct timer_list timer; /* Timer for timeouts */
-
- u32 caps; /* Alternative CAPABILITY_0 */
- u32 caps1; /* Alternative CAPABILITY_1 */
-
- unsigned int ocr_avail_sdio; /* OCR bit masks */
- unsigned int ocr_avail_sd;
- unsigned int ocr_avail_mmc;
- u32 ocr_mask; /* available voltages */
-
- unsigned timing; /* Current timing */
-
- u32 thread_isr;
-
- /* cached registers */
- u32 ier;
-
- wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
- unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
-
- unsigned int tuning_count; /* Timer count for re-tuning */
- unsigned int tuning_mode; /* Re-tuning mode supported by 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 */
u32 caps; /* Used for the MMC driver on 2430 and later */
u32 pm_caps; /* PM capabilities of the mmc */
- /* switch pin can be for card detect (default) or card cover */
- unsigned cover:1;
-
/* use the internal clock */
unsigned internal_clock:1;
#define HSMMC_HAS_HSPE_SUPPORT (1 << 2)
unsigned features;
- int switch_pin; /* gpio (card detect) */
+ int gpio_cd; /* gpio (card detect) */
+ int gpio_cod; /* gpio (cover detect) */
int gpio_wp; /* gpio (write protect) */
int (*set_power)(struct device *dev, int power_on, int vdd);