diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2017-04-03 22:01:20 +0300 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2017-04-03 22:01:20 +0300 |
commit | 03b22057e8ed2d1df416c9ae8e6e247e0b87ecc8 (patch) | |
tree | cd89bdd4053e46ff3cbd67adeffa24310c04e725 /drivers/mmc/host | |
parent | 96083b2e90cddfb688e70630a1dbfdfe5fb0262d (diff) | |
parent | a71c9a1c779f2499fb2afc0553e543f18aff6edf (diff) | |
download | linux-03b22057e8ed2d1df416c9ae8e6e247e0b87ecc8.tar.xz |
Merge tag 'v4.11-rc5' into next
Sync up with mainline to bring in changes to input subsystem merged
through other trees.
Diffstat (limited to 'drivers/mmc/host')
47 files changed, 1359 insertions, 542 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 2eb97014dc3f..f08691a58d7e 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -683,6 +683,15 @@ config MMC_DW_ROCKCHIP Synopsys DesignWare Memory Card Interface driver. Select this option for platforms based on RK3066, RK3188 and RK3288 SoC's. +config MMC_DW_ZX + tristate "ZTE specific extensions for Synopsys DW Memory Card Interface" + depends on MMC_DW && ARCH_ZX + select MMC_DW_PLTFM + help + This selects support for ZTE SoC specific extensions to the + Synopsys DesignWare Memory Card Interface driver. Select this option + for platforms based on ZX296718 SoC's. + config MMC_SH_MMCIF tristate "SuperH Internal MMCIF support" depends on HAS_DMA diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index ccc9c4cba154..6d548c4ee2fa 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o +obj-$(CONFIG_MMC_DW_ZX) += dw_mmc-zx.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_VUB300) += vub300.o diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 36b5af8eadb8..1e2600da105f 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -36,6 +36,7 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/mmc/slot-gpio.h> +#include <linux/interrupt.h> #include <linux/platform_data/mmc-davinci.h> diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index e1335289316c..25691cca1881 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -13,7 +13,6 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/mmc/host.h> -#include <linux/mmc/dw_mmc.h> #include <linux/mmc/mmc.h> #include <linux/of.h> #include <linux/of_gpio.h> diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 9821e6bd5d5e..e38fb0020bb1 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -11,7 +11,6 @@ #include <linux/clk.h> #include <linux/mfd/syscon.h> #include <linux/mmc/host.h> -#include <linux/mmc/dw_mmc.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/platform_device.h> diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index ab82796b01e2..ab8713297edb 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -18,7 +18,6 @@ #include <linux/slab.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> -#include <linux/mmc/dw_mmc.h> #include "dw_mmc.h" #define PCI_BAR_NO 2 diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 1236d49ba36e..58c13e21bd5a 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -20,7 +20,6 @@ #include <linux/slab.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> -#include <linux/mmc/dw_mmc.h> #include <linux/of.h> #include <linux/clk.h> diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 9a46e4694227..372fb6e948c1 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -11,7 +11,6 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/mmc/host.h> -#include <linux/mmc/dw_mmc.h> #include <linux/of_address.h> #include <linux/mmc/slot-gpio.h> #include <linux/pm_runtime.h> diff --git a/drivers/mmc/host/dw_mmc-zx.c b/drivers/mmc/host/dw_mmc-zx.c new file mode 100644 index 000000000000..d38e94ae2b85 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-zx.c @@ -0,0 +1,241 @@ +/* + * ZX Specific Extensions for Synopsys DW Multimedia Card Interface driver + * + * Copyright (C) 2016, Linaro Ltd. + * Copyright (C) 2016, ZTE Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/clk.h> +#include <linux/mfd/syscon.h> +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include "dw_mmc.h" +#include "dw_mmc-pltfm.h" +#include "dw_mmc-zx.h" + +struct dw_mci_zx_priv_data { + struct regmap *sysc_base; +}; + +enum delay_type { + DELAY_TYPE_READ, /* read dqs delay */ + DELAY_TYPE_CLK, /* clk sample delay */ +}; + +static int dw_mci_zx_emmc_set_delay(struct dw_mci *host, unsigned int delay, + enum delay_type dflag) +{ + struct dw_mci_zx_priv_data *priv = host->priv; + struct regmap *sysc_base = priv->sysc_base; + unsigned int clksel; + unsigned int loop = 1000; + int ret; + + if (!sysc_base) + return -EINVAL; + + ret = regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0, + PARA_HALF_CLK_MODE | PARA_DLL_BYPASS_MODE | + PARA_PHASE_DET_SEL_MASK | + PARA_DLL_LOCK_NUM_MASK | + DLL_REG_SET | PARA_DLL_START_MASK, + PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4)); + if (ret) + return ret; + + ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG1, &clksel); + if (ret) + return ret; + + if (dflag == DELAY_TYPE_CLK) { + clksel &= ~CLK_SAMP_DELAY_MASK; + clksel |= CLK_SAMP_DELAY(delay); + } else { + clksel &= ~READ_DQS_DELAY_MASK; + clksel |= READ_DQS_DELAY(delay); + } + + regmap_write(sysc_base, LB_AON_EMMC_CFG_REG1, clksel); + regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0, + PARA_DLL_START_MASK | PARA_DLL_LOCK_NUM_MASK | + DLL_REG_SET, + PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4) | + DLL_REG_SET); + + do { + ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG2, &clksel); + if (ret) + return ret; + + } while (--loop && !(clksel & ZX_DLL_LOCKED)); + + if (!loop) { + dev_err(host->dev, "Error: %s dll lock fail\n", __func__); + return -EIO; + } + + return 0; +} + +static int dw_mci_zx_emmc_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +{ + struct dw_mci *host = slot->host; + struct mmc_host *mmc = slot->mmc; + int ret, len = 0, start = 0, end = 0, delay, best = 0; + + for (delay = 1; delay < 128; delay++) { + ret = dw_mci_zx_emmc_set_delay(host, delay, DELAY_TYPE_CLK); + if (!ret && mmc_send_tuning(mmc, opcode, NULL)) { + if (start >= 0) { + end = delay - 1; + /* check and update longest good range */ + if ((end - start) > len) { + best = (start + end) >> 1; + len = end - start; + } + } + start = -1; + end = 0; + continue; + } + if (start < 0) + start = delay; + } + + if (start >= 0) { + end = delay - 1; + if ((end - start) > len) { + best = (start + end) >> 1; + len = end - start; + } + } + if (best < 0) + return -EIO; + + dev_info(host->dev, "%s best range: start %d end %d\n", __func__, + start, end); + return dw_mci_zx_emmc_set_delay(host, best, DELAY_TYPE_CLK); +} + +static int dw_mci_zx_prepare_hs400_tuning(struct dw_mci *host, + struct mmc_ios *ios) +{ + int ret; + + /* config phase shift as 90 degree */ + ret = dw_mci_zx_emmc_set_delay(host, 32, DELAY_TYPE_READ); + if (ret < 0) + return -EIO; + + return 0; +} + +static int dw_mci_zx_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +{ + struct dw_mci *host = slot->host; + + if (host->verid == 0x290a) /* only for emmc */ + return dw_mci_zx_emmc_execute_tuning(slot, opcode); + /* TODO: Add 0x210a dedicated tuning for sd/sdio */ + + return 0; +} + +static int dw_mci_zx_parse_dt(struct dw_mci *host) +{ + struct device_node *np = host->dev->of_node; + struct device_node *node; + struct dw_mci_zx_priv_data *priv; + struct regmap *sysc_base; + int ret; + + /* syscon is needed only by emmc */ + node = of_parse_phandle(np, "zte,aon-syscon", 0); + if (node) { + sysc_base = syscon_node_to_regmap(node); + of_node_put(node); + + if (IS_ERR(sysc_base)) { + ret = PTR_ERR(sysc_base); + if (ret != -EPROBE_DEFER) + dev_err(host->dev, "Can't get syscon: %d\n", + ret); + return ret; + } + } else { + return 0; + } + + priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->sysc_base = sysc_base; + host->priv = priv; + + return 0; +} + +static unsigned long zx_dwmmc_caps[3] = { + MMC_CAP_CMD23, + MMC_CAP_CMD23, + MMC_CAP_CMD23, +}; + +static const struct dw_mci_drv_data zx_drv_data = { + .caps = zx_dwmmc_caps, + .execute_tuning = dw_mci_zx_execute_tuning, + .prepare_hs400_tuning = dw_mci_zx_prepare_hs400_tuning, + .parse_dt = dw_mci_zx_parse_dt, +}; + +static const struct of_device_id dw_mci_zx_match[] = { + { .compatible = "zte,zx296718-dw-mshc", .data = &zx_drv_data}, + {}, +}; +MODULE_DEVICE_TABLE(of, dw_mci_zx_match); + +static int dw_mci_zx_probe(struct platform_device *pdev) +{ + const struct dw_mci_drv_data *drv_data; + const struct of_device_id *match; + + match = of_match_node(dw_mci_zx_match, pdev->dev.of_node); + drv_data = match->data; + + return dw_mci_pltfm_register(pdev, drv_data); +} + +static const struct dev_pm_ops dw_mci_zx_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, + dw_mci_runtime_resume, + NULL) +}; + +static struct platform_driver dw_mci_zx_pltfm_driver = { + .probe = dw_mci_zx_probe, + .remove = dw_mci_pltfm_remove, + .driver = { + .name = "dwmmc_zx", + .of_match_table = dw_mci_zx_match, + .pm = &dw_mci_zx_dev_pm_ops, + }, +}; + +module_platform_driver(dw_mci_zx_pltfm_driver); + +MODULE_DESCRIPTION("ZTE emmc/sd driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/dw_mmc-zx.h b/drivers/mmc/host/dw_mmc-zx.h new file mode 100644 index 000000000000..f369997a39ec --- /dev/null +++ b/drivers/mmc/host/dw_mmc-zx.h @@ -0,0 +1,31 @@ +#ifndef _DW_MMC_ZX_H_ +#define _DW_MMC_ZX_H_ + +/* ZX296718 SoC specific DLL register offset. */ +#define LB_AON_EMMC_CFG_REG0 0x1B0 +#define LB_AON_EMMC_CFG_REG1 0x1B4 +#define LB_AON_EMMC_CFG_REG2 0x1B8 + +/* LB_AON_EMMC_CFG_REG0 register defines */ +#define PARA_DLL_START(x) ((x) & 0xFF) +#define PARA_DLL_START_MASK 0xFF +#define DLL_REG_SET BIT(8) +#define PARA_DLL_LOCK_NUM(x) (((x) & 7) << 16) +#define PARA_DLL_LOCK_NUM_MASK (7 << 16) +#define PARA_PHASE_DET_SEL(x) (((x) & 7) << 20) +#define PARA_PHASE_DET_SEL_MASK (7 << 20) +#define PARA_DLL_BYPASS_MODE BIT(23) +#define PARA_HALF_CLK_MODE BIT(24) + +/* LB_AON_EMMC_CFG_REG1 register defines */ +#define READ_DQS_DELAY(x) ((x) & 0x7F) +#define READ_DQS_DELAY_MASK (0x7F) +#define READ_DQS_BYPASS_MODE BIT(7) +#define CLK_SAMP_DELAY(x) (((x) & 0x7F) << 8) +#define CLK_SAMP_DELAY_MASK (0x7F << 8) +#define CLK_SAMP_BYPASS_MODE BIT(15) + +/* LB_AON_EMMC_CFG_REG2 register defines */ +#define ZX_DLL_LOCKED BIT(2) + +#endif /* _DW_MMC_ZX_H_ */ diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 73db08558e4d..a9ac0b457313 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -32,7 +32,6 @@ #include <linux/mmc/mmc.h> #include <linux/mmc/sd.h> #include <linux/mmc/sdio.h> -#include <linux/mmc/dw_mmc.h> #include <linux/bitops.h> #include <linux/regulator/consumer.h> #include <linux/of.h> @@ -1113,11 +1112,15 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) mci_writel(host, CTRL, temp); /* - * Use the initial fifoth_val for PIO mode. + * Use the initial fifoth_val for PIO mode. If wm_algined + * is set, we set watermark same as data size. * If next issued data may be transfered by DMA mode, * prev_blksz should be invalidated. */ - mci_writel(host, FIFOTH, host->fifoth_val); + if (host->wm_aligned) + dw_mci_adjust_fifoth(host, data); + else + mci_writel(host, FIFOTH, host->fifoth_val); host->prev_blksz = 0; } else { /* @@ -1179,11 +1182,13 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) if ((clock != slot->__clk_old && !test_bit(DW_MMC_CARD_NEEDS_POLL, &slot->flags)) || force_clkinit) { - dev_info(&slot->mmc->class_dev, - "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n", - slot->id, host->bus_hz, clock, - div ? ((host->bus_hz / div) >> 1) : - host->bus_hz, div); + /* Silent the verbose log if calling from PM context */ + if (!force_clkinit) + dev_info(&slot->mmc->class_dev, + "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n", + slot->id, host->bus_hz, clock, + div ? ((host->bus_hz / div) >> 1) : + host->bus_hz, div); /* * If card is polling, display the message only @@ -2977,6 +2982,11 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms); + of_property_read_u32(np, "data-addr", &host->data_addr_override); + + if (of_get_property(np, "fifo-watermark-aligned", NULL)) + host->wm_aligned = true; + if (!of_property_read_u32(np, "clock-frequency", &clock_frequency)) pdata->bus_hz = clock_frequency; @@ -3180,7 +3190,9 @@ int dw_mci_probe(struct dw_mci *host) host->verid = SDMMC_GET_VERID(mci_readl(host, VERID)); dev_info(host->dev, "Version ID is %04x\n", host->verid); - if (host->verid < DW_MMC_240A) + if (host->data_addr_override) + host->fifo_reg = host->regs + host->data_addr_override; + else if (host->verid < DW_MMC_240A) host->fifo_reg = host->regs + DATA_OFFSET; else host->fifo_reg = host->regs + DATA_240A_OFFSET; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index c59465829387..ce347361f3dc 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -14,6 +14,269 @@ #ifndef _DW_MMC_H_ #define _DW_MMC_H_ +#include <linux/scatterlist.h> +#include <linux/mmc/core.h> +#include <linux/dmaengine.h> +#include <linux/reset.h> +#include <linux/interrupt.h> + +#define MAX_MCI_SLOTS 2 + +enum dw_mci_state { + STATE_IDLE = 0, + STATE_SENDING_CMD, + STATE_SENDING_DATA, + STATE_DATA_BUSY, + STATE_SENDING_STOP, + STATE_DATA_ERROR, + STATE_SENDING_CMD11, + STATE_WAITING_CMD11_DONE, +}; + +enum { + EVENT_CMD_COMPLETE = 0, + EVENT_XFER_COMPLETE, + EVENT_DATA_COMPLETE, + EVENT_DATA_ERROR, +}; + +enum dw_mci_cookie { + COOKIE_UNMAPPED, + COOKIE_PRE_MAPPED, /* mapped by pre_req() of dwmmc */ + COOKIE_MAPPED, /* mapped by prepare_data() of dwmmc */ +}; + +struct mmc_data; + +enum { + TRANS_MODE_PIO = 0, + TRANS_MODE_IDMAC, + TRANS_MODE_EDMAC +}; + +struct dw_mci_dma_slave { + struct dma_chan *ch; + enum dma_transfer_direction direction; +}; + +/** + * struct dw_mci - MMC controller state shared between all slots + * @lock: Spinlock protecting the queue and associated data. + * @irq_lock: Spinlock protecting the INTMASK setting. + * @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. + * @mrq: The request currently being processed on @cur_slot, + * or NULL if the controller is idle. + * @cmd: The command currently being sent to the card, or NULL. + * @data: The data currently being transferred, or NULL if no data + * transfer is in progress. + * @stop_abort: The command currently prepared for stoping transfer. + * @prev_blksz: The former transfer blksz record. + * @timing: Record of current ios timing. + * @use_dma: Whether DMA channel is initialized or not. + * @using_dma: Whether DMA is in use for the current transfer. + * @dma_64bit_address: Whether DMA supports 64-bit address mode or not. + * @sg_dma: Bus address of DMA buffer. + * @sg_cpu: Virtual address of DMA buffer. + * @dma_ops: Pointer to platform-specific DMA callbacks. + * @cmd_status: Snapshot of SR taken upon completion of the current + * @ring_size: Buffer size for idma descriptors. + * command. Only valid when EVENT_CMD_COMPLETE is pending. + * @dms: structure of slave-dma private data. + * @phy_regs: physical address of controller's register map + * @data_status: Snapshot of SR taken upon completion of the current + * data transfer. Only valid when EVENT_DATA_COMPLETE or + * EVENT_DATA_ERROR is pending. + * @stop_cmdr: Value to be loaded into CMDR when the stop command is + * to be sent. + * @dir_status: Direction of current transfer. + * @tasklet: Tasklet running the request state machine. + * @pending_events: Bitmask of events flagged by the interrupt handler + * to be processed by the tasklet. + * @completed_events: Bitmask of events which the state machine has + * processed. + * @state: Tasklet state. + * @queue: List of slots waiting for access to the controller. + * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus + * rate and timeout calculations. + * @current_speed: Configured rate of the controller. + * @num_slots: Number of slots available. + * @fifoth_val: The value of FIFOTH register. + * @verid: Denote Version ID. + * @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 + * @priv: Implementation defined private data. + * @biu_clk: Pointer to bus interface unit clock instance. + * @ciu_clk: Pointer to card interface unit clock instance. + * @slot: Slots sharing this MMC controller. + * @fifo_depth: depth of FIFO. + * @data_addr_override: override fifo reg offset with this value. + * @wm_aligned: force fifo watermark equal with data length in PIO mode. + * Set as true if alignment is needed. + * @data_shift: log2 of FIFO item size. + * @part_buf_start: Start index in part_buf. + * @part_buf_count: Bytes of partial data in part_buf. + * @part_buf: Simple buffer for partial fifo reads/writes. + * @push_data: Pointer to FIFO push function. + * @pull_data: Pointer to FIFO pull function. + * @vqmmc_enabled: Status of vqmmc, should be true or false. + * @irq_flags: The flags to be passed to request_irq. + * @irq: The irq value to be passed to request_irq. + * @sdio_id0: Number of slot0 in the SDIO interrupt registers. + * @cmd11_timer: Timer for SD3.0 voltage switch over scheme. + * @dto_timer: Timer for broken data transfer over scheme. + * + * Locking + * ======= + * + * @lock is a softirq-safe spinlock protecting @queue as well as + * @cur_slot, @mrq and @state. These must always be updated + * at the same time while holding @lock. + * + * @irq_lock is an irq-safe spinlock protecting the INTMASK register + * to allow the interrupt handler to modify it directly. Held for only long + * enough to read-modify-write INTMASK and no other locks are grabbed when + * holding this one. + * + * The @mrq field of struct dw_mci_slot is also protected by @lock, + * and must always be written at the same time as the slot is added to + * @queue. + * + * @pending_events and @completed_events are accessed using atomic bit + * operations, so they don't need any locking. + * + * None of the fields touched by the interrupt handler need any + * locking. However, ordering is important: Before EVENT_DATA_ERROR or + * EVENT_DATA_COMPLETE is set in @pending_events, all data-related + * interrupts must be disabled and @data_status updated with a + * snapshot of SR. Similarly, before EVENT_CMD_COMPLETE is set, the + * CMDRDY interrupt must be disabled and @cmd_status updated with a + * snapshot of SR, and before EVENT_XFER_COMPLETE can be set, the + * bytes_xfered field of @data must be written. This is ensured by + * using barriers. + */ +struct dw_mci { + spinlock_t lock; + spinlock_t irq_lock; + void __iomem *regs; + void __iomem *fifo_reg; + u32 data_addr_override; + bool wm_aligned; + + struct scatterlist *sg; + struct sg_mapping_iter sg_miter; + + struct dw_mci_slot *cur_slot; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + struct mmc_command stop_abort; + unsigned int prev_blksz; + unsigned char timing; + + /* DMA interface members*/ + int use_dma; + int using_dma; + int dma_64bit_address; + + dma_addr_t sg_dma; + void *sg_cpu; + const struct dw_mci_dma_ops *dma_ops; + /* For idmac */ + unsigned int ring_size; + + /* For edmac */ + struct dw_mci_dma_slave *dms; + /* Registers's physical base address */ + resource_size_t phy_regs; + + u32 cmd_status; + u32 data_status; + u32 stop_cmdr; + u32 dir_status; + struct tasklet_struct tasklet; + unsigned long pending_events; + unsigned long completed_events; + enum dw_mci_state state; + struct list_head queue; + + u32 bus_hz; + u32 current_speed; + u32 num_slots; + u32 fifoth_val; + u16 verid; + struct device *dev; + struct dw_mci_board *pdata; + const struct dw_mci_drv_data *drv_data; + void *priv; + struct clk *biu_clk; + struct clk *ciu_clk; + struct dw_mci_slot *slot[MAX_MCI_SLOTS]; + + /* FIFO push and pull */ + int fifo_depth; + int data_shift; + u8 part_buf_start; + u8 part_buf_count; + union { + u16 part_buf16; + u32 part_buf32; + u64 part_buf; + }; + void (*push_data)(struct dw_mci *host, void *buf, int cnt); + void (*pull_data)(struct dw_mci *host, void *buf, int cnt); + + bool vqmmc_enabled; + unsigned long irq_flags; /* IRQ flags */ + int irq; + + int sdio_id0; + + struct timer_list cmd11_timer; + struct timer_list dto_timer; +}; + +/* DMA ops for Internal/External DMAC interface */ +struct dw_mci_dma_ops { + /* DMA Ops */ + int (*init)(struct dw_mci *host); + int (*start)(struct dw_mci *host, unsigned int sg_len); + void (*complete)(void *host); + void (*stop)(struct dw_mci *host); + void (*cleanup)(struct dw_mci *host); + void (*exit)(struct dw_mci *host); +}; + +struct dma_pdata; + +/* Board platform data */ +struct dw_mci_board { + u32 num_slots; + + unsigned int bus_hz; /* Clock speed at the cclk_in pad */ + + u32 caps; /* Capabilities */ + u32 caps2; /* More capabilities */ + u32 pm_caps; /* PM capabilities */ + /* + * Override fifo depth. If 0, autodetect it from the FIFOTH register, + * but note that this may not be reliable after a bootloader has used + * it. + */ + unsigned int fifo_depth; + + /* delay in mS before detecting cards after interrupt */ + u32 detect_delay_ms; + + struct reset_control *rstc; + struct dw_mci_dma_ops *dma_ops; + struct dma_pdata *data; +}; + #define DW_MMC_240A 0x240a #define DW_MMC_280A 0x280a diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 09739352834c..5a959783304b 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -35,6 +35,7 @@ #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/regulator/consumer.h> +#include <linux/interrupt.h> #define DRIVER_NAME "meson-gx-mmc" @@ -82,6 +83,7 @@ #define CFG_RC_CC_MASK 0xf #define CFG_STOP_CLOCK BIT(22) #define CFG_CLK_ALWAYS_ON BIT(18) +#define CFG_CHK_DS BIT(20) #define CFG_AUTO_CLK BIT(23) #define SD_EMMC_STATUS 0x48 @@ -131,7 +133,7 @@ struct meson_host { struct clk_mux mux; struct clk *mux_clk; struct clk *mux_parent[MUX_CLK_NUM_PARENTS]; - unsigned long mux_parent_rate[MUX_CLK_NUM_PARENTS]; + unsigned long current_clock; struct clk_divider cfg_div; struct clk *cfg_div_clk; @@ -178,7 +180,7 @@ struct sd_emmc_desc { static int meson_mmc_clk_set(struct meson_host *host, unsigned long clk_rate) { struct mmc_host *mmc = host->mmc; - int ret = 0; + int ret; u32 cfg; if (clk_rate) { @@ -188,7 +190,7 @@ static int meson_mmc_clk_set(struct meson_host *host, unsigned long clk_rate) clk_rate = mmc->f_min; } - if (clk_rate == mmc->actual_clock) + if (clk_rate == host->current_clock) return 0; /* stop clock */ @@ -201,29 +203,34 @@ static int meson_mmc_clk_set(struct meson_host *host, unsigned long clk_rate) dev_dbg(host->dev, "change clock rate %u -> %lu\n", mmc->actual_clock, clk_rate); - if (clk_rate == 0) { + if (!clk_rate) { mmc->actual_clock = 0; + host->current_clock = 0; + /* return with clock being stopped */ return 0; } ret = clk_set_rate(host->cfg_div_clk, clk_rate); - if (ret) - dev_warn(host->dev, "Unable to set cfg_div_clk to %lu. ret=%d\n", - clk_rate, ret); - else if (clk_rate && clk_rate != clk_get_rate(host->cfg_div_clk)) - dev_warn(host->dev, "divider requested rate %lu != actual rate %lu: ret=%d\n", - clk_rate, clk_get_rate(host->cfg_div_clk), ret); - else - mmc->actual_clock = clk_rate; - - /* (re)start clock, if non-zero */ - if (!ret && clk_rate) { - cfg = readl(host->regs + SD_EMMC_CFG); - cfg &= ~CFG_STOP_CLOCK; - writel(cfg, host->regs + SD_EMMC_CFG); + if (ret) { + dev_err(host->dev, "Unable to set cfg_div_clk to %lu. ret=%d\n", + clk_rate, ret); + return ret; } - return ret; + mmc->actual_clock = clk_get_rate(host->cfg_div_clk); + host->current_clock = clk_rate; + + if (clk_rate != mmc->actual_clock) + dev_dbg(host->dev, + "divider requested rate %lu != actual rate %u\n", + clk_rate, mmc->actual_clock); + + /* (re)start clock */ + cfg = readl(host->regs + SD_EMMC_CFG); + cfg &= ~CFG_STOP_CLOCK; + writel(cfg, host->regs + SD_EMMC_CFG); + + return 0; } /* @@ -239,7 +246,6 @@ static int meson_mmc_clk_init(struct meson_host *host) const char *mux_parent_names[MUX_CLK_NUM_PARENTS]; unsigned int mux_parent_count = 0; const char *clk_div_parents[1]; - unsigned int f_min = UINT_MAX; u32 clk_reg, cfg; /* get the mux parents */ @@ -256,20 +262,10 @@ static int meson_mmc_clk_init(struct meson_host *host) return ret; } - host->mux_parent_rate[i] = clk_get_rate(host->mux_parent[i]); mux_parent_names[i] = __clk_get_name(host->mux_parent[i]); mux_parent_count++; - if (host->mux_parent_rate[i] < f_min) - f_min = host->mux_parent_rate[i]; } - /* cacluate f_min based on input clocks, and max divider value */ - if (f_min != UINT_MAX) - f_min = DIV_ROUND_UP(CLK_SRC_XTAL_RATE, CLK_DIV_MAX); - else - f_min = 4000000; /* default min: 400 MHz */ - host->mmc->f_min = f_min; - /* create the mux */ snprintf(clk_name, sizeof(clk_name), "%s#mux", dev_name(host->dev)); init.name = clk_name; @@ -324,9 +320,13 @@ static int meson_mmc_clk_init(struct meson_host *host) writel(cfg, host->regs + SD_EMMC_CFG); ret = clk_prepare_enable(host->cfg_div_clk); - if (!ret) - ret = meson_mmc_clk_set(host, f_min); + if (ret) + return ret; + /* Get the nearest minimum clock to 400KHz */ + host->mmc->f_min = clk_round_rate(host->cfg_div_clk, 400000); + + ret = meson_mmc_clk_set(host, host->mmc->f_min); if (!ret) clk_disable_unprepare(host->cfg_div_clk); @@ -378,7 +378,6 @@ static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) meson_mmc_clk_set(host, ios->clock); /* Bus width */ - val = readl(host->regs + SD_EMMC_CFG); switch (ios->bus_width) { case MMC_BUS_WIDTH_1: bus_width = CFG_BUS_WIDTH_1; @@ -393,7 +392,6 @@ static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) dev_err(host->dev, "Invalid ios->bus_width: %u. Setting to 4.\n", ios->bus_width); bus_width = CFG_BUS_WIDTH_4; - return; } val = readl(host->regs + SD_EMMC_CFG); @@ -411,6 +409,16 @@ static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) val &= ~(CFG_RC_CC_MASK << CFG_RC_CC_SHIFT); val |= ilog2(SD_EMMC_CFG_CMD_GAP) << CFG_RC_CC_SHIFT; + val &= ~CFG_DDR; + if (ios->timing == MMC_TIMING_UHS_DDR50 || + ios->timing == MMC_TIMING_MMC_DDR52 || + ios->timing == MMC_TIMING_MMC_HS400) + val |= CFG_DDR; + + val &= ~CFG_CHK_DS; + if (ios->timing == MMC_TIMING_MMC_HS400) + val |= CFG_CHK_DS; + writel(val, host->regs + SD_EMMC_CFG); if (val != orig) @@ -480,9 +488,9 @@ static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd) blk_len = cfg & (CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT); blk_len >>= CFG_BLK_LEN_SHIFT; if (blk_len != ilog2(cmd->data->blksz)) { - dev_warn(host->dev, "%s: update blk_len %d -> %d\n", + dev_dbg(host->dev, "%s: update blk_len %d -> %d\n", __func__, blk_len, - ilog2(cmd->data->blksz)); + ilog2(cmd->data->blksz)); blk_len = ilog2(cmd->data->blksz); cfg &= ~(CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT); cfg |= blk_len << CFG_BLK_LEN_SHIFT; @@ -545,11 +553,6 @@ static void meson_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) /* Stop execution */ writel(0, host->regs + SD_EMMC_START); - /* clear, ack, enable all interrupts */ - writel(0, host->regs + SD_EMMC_IRQ_EN); - writel(IRQ_EN_MASK, host->regs + SD_EMMC_STATUS); - writel(IRQ_EN_MASK, host->regs + SD_EMMC_IRQ_EN); - host->mrq = mrq; if (mrq->sbc) @@ -669,7 +672,6 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id) struct mmc_command *cmd = host->cmd; struct mmc_data *data; unsigned int xfer_bytes; - int ret = IRQ_HANDLED; if (WARN_ON(!mrq)) return IRQ_NONE; @@ -678,14 +680,12 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id) return IRQ_NONE; data = cmd->data; - if (data) { + if (data && data->flags & MMC_DATA_READ) { xfer_bytes = data->blksz * data->blocks; - if (data->flags & MMC_DATA_READ) { - WARN_ON(xfer_bytes > host->bounce_buf_size); - sg_copy_from_buffer(data->sg, data->sg_len, - host->bounce_buf, xfer_bytes); - data->bytes_xfered = xfer_bytes; - } + WARN_ON(xfer_bytes > host->bounce_buf_size); + sg_copy_from_buffer(data->sg, data->sg_len, + host->bounce_buf, xfer_bytes); + data->bytes_xfered = xfer_bytes; } meson_mmc_read_resp(host->mmc, cmd); @@ -694,7 +694,7 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id) else meson_mmc_start_cmd(host->mmc, data->stop); - return ret; + return IRQ_HANDLED; } /* @@ -742,7 +742,8 @@ static int meson_mmc_probe(struct platform_device *pdev) ret = mmc_of_parse(mmc); if (ret) { - dev_warn(&pdev->dev, "error parsing DT: %d\n", ret); + if (ret != -EPROBE_DEFER) + dev_warn(&pdev->dev, "error parsing DT: %d\n", ret); goto free_host; } @@ -780,6 +781,7 @@ static int meson_mmc_probe(struct platform_device *pdev) /* clear, ack, enable all interrupts */ writel(0, host->regs + SD_EMMC_IRQ_EN); writel(IRQ_EN_MASK, host->regs + SD_EMMC_STATUS); + writel(IRQ_EN_MASK, host->regs + SD_EMMC_IRQ_EN); ret = devm_request_threaded_irq(&pdev->dev, host->irq, meson_mmc_irq, meson_mmc_irq_thread, @@ -787,8 +789,11 @@ static int meson_mmc_probe(struct platform_device *pdev) if (ret) goto free_host; + mmc->max_blk_count = CMD_CFG_LENGTH_MASK; + mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size; + /* data bounce buffer */ - host->bounce_buf_size = SZ_512K; + host->bounce_buf_size = mmc->max_req_size; host->bounce_buf = dma_alloc_coherent(host->dev, host->bounce_buf_size, &host->bounce_dma_addr, GFP_KERNEL); @@ -814,12 +819,11 @@ static int meson_mmc_remove(struct platform_device *pdev) { struct meson_host *host = dev_get_drvdata(&pdev->dev); - if (WARN_ON(!host)) - return 0; + /* disable interrupts */ + writel(0, host->regs + SD_EMMC_IRQ_EN); - if (host->bounce_buf) - dma_free_coherent(host->dev, host->bounce_buf_size, - host->bounce_buf, host->bounce_dma_addr); + dma_free_coherent(host->dev, host->bounce_buf_size, + host->bounce_buf, host->bounce_dma_addr); clk_disable_unprepare(host->cfg_div_clk); clk_disable_unprepare(host->core_clk); diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index b5972440c1bf..0c6420bb2f00 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -507,6 +507,7 @@ static void mmci_dma_data_error(struct mmci_host *host) { dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n"); dmaengine_terminate_all(host->dma_current); + host->dma_in_progress = false; host->dma_current = NULL; host->dma_desc_current = NULL; host->data->host_cookie = 0; @@ -565,6 +566,7 @@ static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) mmci_dma_release(host); } + host->dma_in_progress = false; host->dma_current = NULL; host->dma_desc_current = NULL; } @@ -665,6 +667,7 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) dev_vdbg(mmc_dev(host->mmc), "Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n", data->sg_len, data->blksz, data->blocks, data->flags); + host->dma_in_progress = true; dmaengine_submit(host->dma_desc_current); dma_async_issue_pending(host->dma_current); @@ -740,8 +743,10 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq, if (host->dma_desc_current == next->dma_desc) host->dma_desc_current = NULL; - if (host->dma_current == next->dma_chan) + if (host->dma_current == next->dma_chan) { + host->dma_in_progress = false; host->dma_current = NULL; + } next->dma_desc = NULL; next->dma_chan = NULL; diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 56322c6afba4..4a8bef1aac8f 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -245,8 +245,9 @@ struct mmci_host { struct dma_chan *dma_tx_channel; struct dma_async_tx_descriptor *dma_desc_current; struct mmci_host_next next_data; + bool dma_in_progress; -#define dma_inprogress(host) ((host)->dma_current) +#define dma_inprogress(host) ((host)->dma_in_progress) #else #define dma_inprogress(host) (0) #endif diff --git a/drivers/mmc/host/mmci_qcom_dml.c b/drivers/mmc/host/mmci_qcom_dml.c index 2b7fc3764803..00750c9d3514 100644 --- a/drivers/mmc/host/mmci_qcom_dml.c +++ b/drivers/mmc/host/mmci_qcom_dml.c @@ -170,7 +170,7 @@ int dml_hw_init(struct mmci_host *host, struct device_node *np) writel_relaxed(producer_id | (consumer_id << CONSUMER_PIPE_ID_SHFT), base + DML_PIPE_ID); - /* Make sure dml intialization is finished */ + /* Make sure dml initialization is finished */ mb(); return 0; diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 10ef2ae1d2f6..b235d8da0602 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -28,6 +28,7 @@ #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/interrupt.h> #include <linux/mmc/card.h> #include <linux/mmc/core.h> @@ -579,7 +580,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) } } sdr_set_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD | MSDC_CFG_CKDIV, - (mode << 8) | (div % 0xff)); + (mode << 8) | div); sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN); while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB)) cpu_relax(); @@ -1074,11 +1075,8 @@ static int msdc_card_busy(struct mmc_host *mmc) struct msdc_host *host = mmc_priv(mmc); u32 status = readl(host->base + MSDC_PS); - /* check if any pin between dat[0:3] is low */ - if (((status >> 16) & 0xf) != 0xf) - return 1; - - return 0; + /* only check if data0 is low */ + return !(status & BIT(16)); } static void msdc_request_timeout(struct work_struct *work) @@ -1561,7 +1559,7 @@ static int msdc_drv_probe(struct platform_device *pdev) host->src_clk_freq = clk_get_rate(host->src_clk); /* Set host parameters to mmc */ mmc->ops = &mt_msdc_ops; - mmc->f_min = host->src_clk_freq / (4 * 255); + mmc->f_min = DIV_ROUND_UP(host->src_clk_freq, 4 * 255); mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23; /* MMC core transfer sizes tunable parameters */ diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index c8b8ac66ff7e..add1e70195ea 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -153,7 +153,11 @@ static void mxs_mmc_request_done(struct mxs_mmc_host *host) } } - if (data) { + if (cmd == mrq->sbc) { + /* Finished CMD23, now send actual command. */ + mxs_mmc_start_cmd(host, mrq->cmd); + return; + } else if (data) { dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, ssp->dma_dir); /* @@ -166,7 +170,7 @@ static void mxs_mmc_request_done(struct mxs_mmc_host *host) data->bytes_xfered = 0; host->data = NULL; - if (mrq->stop) { + if (data->stop && (data->error || !mrq->sbc)) { mxs_mmc_start_cmd(host, mrq->stop); return; } @@ -495,7 +499,11 @@ static void mxs_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) WARN_ON(host->mrq != NULL); host->mrq = mrq; - mxs_mmc_start_cmd(host, mrq->cmd); + + if (mrq->sbc) + mxs_mmc_start_cmd(host, mrq->sbc); + else + mxs_mmc_start_cmd(host, mrq->cmd); } static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) @@ -642,7 +650,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) /* set mmc core parameters */ mmc->ops = &mxs_mmc_ops; mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | - MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL; + MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23; host->broken_cd = of_property_read_bool(np, "broken-cd"); diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index be3c49fa7382..bd49f34d7654 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -893,7 +893,7 @@ static void mmc_omap_cover_handler(unsigned long param) * If no card is inserted, we postpone polling until * the cover has been closed. */ - if (slot->mmc->card == NULL || !mmc_card_present(slot->mmc->card)) + if (slot->mmc->card == NULL) return; mod_timer(&slot->cover_timer, diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index ad11c4cc12ed..a58bd653ed8b 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1162,7 +1162,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status) if (status & ERR_EN) { omap_hsmmc_dbg_report_irq(host, status); - if (status & (CTO_EN | CCRC_EN)) + if (status & (CTO_EN | CCRC_EN | CEB_EN)) end_cmd = 1; if (host->data || host->response_busy) { end_trans = !end_cmd; @@ -1469,10 +1469,11 @@ static int omap_hsmmc_setup_dma_transfer(struct omap_hsmmc_host *host, } static void set_data_timeout(struct omap_hsmmc_host *host, - unsigned int timeout_ns, + unsigned long long timeout_ns, unsigned int timeout_clks) { - unsigned int timeout, cycle_ns; + unsigned long long timeout = timeout_ns; + unsigned int cycle_ns; uint32_t reg, clkd, dto = 0; reg = OMAP_HSMMC_READ(host->base, SYSCTL); @@ -1481,7 +1482,7 @@ static void set_data_timeout(struct omap_hsmmc_host *host, clkd = 1; cycle_ns = 1000000000 / (host->clk_rate / clkd); - timeout = timeout_ns / cycle_ns; + do_div(timeout, cycle_ns); timeout += timeout_clks; if (timeout) { while ((timeout & 0x80000000) == 0) { @@ -1527,16 +1528,24 @@ static int omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req) { int ret; + unsigned long long timeout; + host->data = req->data; if (req->data == NULL) { OMAP_HSMMC_WRITE(host->base, BLK, 0); - /* - * Set an arbitrary 100ms data timeout for commands with - * busy signal. - */ - if (req->cmd->flags & MMC_RSP_BUSY) - set_data_timeout(host, 100000000U, 0); + if (req->cmd->flags & MMC_RSP_BUSY) { + timeout = req->cmd->busy_timeout * NSEC_PER_MSEC; + + /* + * Set an arbitrary 100ms data timeout for commands with + * busy signal and no indication of busy_timeout. + */ + if (!timeout) + timeout = 100000000U; + + set_data_timeout(host, timeout, 0); + } return 0; } diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index ecb99a8d2fa2..41b57713b620 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -707,7 +707,7 @@ static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host, u8 opcode, u8 sample_point) { int err; - struct mmc_command cmd = {0}; + struct mmc_command cmd = {}; err = sd_change_phase(host, sample_point, true); if (err < 0) diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index dc1abd14acbc..12d2fbe9c520 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -682,7 +682,7 @@ static int sd_tuning_rx_cmd(struct rtsx_usb_sdmmc *host, u8 opcode, u8 sample_point) { int err; - struct mmc_command cmd = {0}; + struct mmc_command cmd = {}; err = sd_change_phase(host, sample_point, 0); if (err) diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 932a4b1fed33..7a173f8c455b 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -21,6 +21,7 @@ #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/gpio.h> +#include <linux/interrupt.h> #include <linux/irq.h> #include <linux/io.h> diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 278a5a435ab7..9dcb7048e3b1 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -467,7 +467,10 @@ static int sdhci_acpi_probe(struct platform_device *pdev) if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) { bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL); - if (mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0, NULL)) { + err = mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0, NULL); + if (err) { + if (err == -EPROBE_DEFER) + goto err_free; dev_warn(dev, "failed to setup card detect gpio\n"); c->use_runtime_pm = false; } diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c index 4b0ecb981842..316cfec3f005 100644 --- a/drivers/mmc/host/sdhci-cadence.c +++ b/drivers/mmc/host/sdhci-cadence.c @@ -17,6 +17,7 @@ #include <linux/iopoll.h> #include <linux/module.h> #include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> #include "sdhci-pltfm.h" @@ -25,7 +26,7 @@ #define SDHCI_CDNS_HRS04_ACK BIT(26) #define SDHCI_CDNS_HRS04_RD BIT(25) #define SDHCI_CDNS_HRS04_WR BIT(24) -#define SDHCI_CDNS_HRS04_RDATA_SHIFT 12 +#define SDHCI_CDNS_HRS04_RDATA_SHIFT 16 #define SDHCI_CDNS_HRS04_WDATA_SHIFT 8 #define SDHCI_CDNS_HRS04_ADDR_SHIFT 0 diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index de132e281753..ece8b37e51dd 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -24,30 +24,36 @@ SDHCI_QUIRK_PIO_NEEDS_DELAY | \ SDHCI_QUIRK_NO_HISPD_BIT) -#define ESDHC_PROCTL 0x28 - -#define ESDHC_SYSTEM_CONTROL 0x2c -#define ESDHC_CLOCK_MASK 0x0000fff0 -#define ESDHC_PREDIV_SHIFT 8 -#define ESDHC_DIVIDER_SHIFT 4 -#define ESDHC_CLOCK_PEREN 0x00000004 -#define ESDHC_CLOCK_HCKEN 0x00000002 -#define ESDHC_CLOCK_IPGEN 0x00000001 - /* pltfm-specific */ #define ESDHC_HOST_CONTROL_LE 0x20 /* - * P2020 interpretation of the SDHCI_HOST_CONTROL register + * eSDHC register definition */ -#define ESDHC_CTRL_4BITBUS (0x1 << 1) -#define ESDHC_CTRL_8BITBUS (0x2 << 1) -#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1) - -/* OF-specific */ -#define ESDHC_DMA_SYSCTL 0x40c -#define ESDHC_DMA_SNOOP 0x00000040 -#define ESDHC_HOST_CONTROL_RES 0x01 +/* Present State Register */ +#define ESDHC_PRSSTAT 0x24 +#define ESDHC_CLOCK_STABLE 0x00000008 + +/* Protocol Control Register */ +#define ESDHC_PROCTL 0x28 +#define ESDHC_CTRL_4BITBUS (0x1 << 1) +#define ESDHC_CTRL_8BITBUS (0x2 << 1) +#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1) +#define ESDHC_HOST_CONTROL_RES 0x01 + +/* System Control Register */ +#define ESDHC_SYSTEM_CONTROL 0x2c +#define ESDHC_CLOCK_MASK 0x0000fff0 +#define ESDHC_PREDIV_SHIFT 8 +#define ESDHC_DIVIDER_SHIFT 4 +#define ESDHC_CLOCK_SDCLKEN 0x00000008 +#define ESDHC_CLOCK_PEREN 0x00000004 +#define ESDHC_CLOCK_HCKEN 0x00000002 +#define ESDHC_CLOCK_IPGEN 0x00000001 + +/* Control Register for DMA transfer */ +#define ESDHC_DMA_SYSCTL 0x40c +#define ESDHC_DMA_SNOOP 0x00000040 #endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */ diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c index d7046d67415a..3275d4995812 100644 --- a/drivers/mmc/host/sdhci-iproc.c +++ b/drivers/mmc/host/sdhci-iproc.c @@ -211,14 +211,19 @@ static const struct sdhci_iproc_data iproc_data = { static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = { .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | - SDHCI_QUIRK_MISSING_CAPS, + SDHCI_QUIRK_MISSING_CAPS | + SDHCI_QUIRK_NO_HISPD_BIT, .ops = &sdhci_iproc_32only_ops, }; static const struct sdhci_iproc_data bcm2835_data = { .pdata = &sdhci_bcm2835_pltfm_data, - .caps = SDHCI_CAN_VDD_330, - .caps1 = 0x00000000, + .caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT) + & SDHCI_MAX_BLOCK_MASK) | + SDHCI_CAN_VDD_330 | + SDHCI_CAN_DO_HISPD, + .caps1 = SDHCI_DRIVER_TYPE_A | + SDHCI_DRIVER_TYPE_C, .mmc_caps = 0x00000000, }; diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 32879b845b75..10cdc84d5113 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -69,6 +69,7 @@ #define CORE_DLL_CLOCK_DISABLE BIT(21) #define CORE_VENDOR_SPEC 0x10c +#define CORE_VENDOR_SPEC_POR_VAL 0xa1c #define CORE_CLK_PWRSAVE BIT(1) #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) @@ -102,6 +103,7 @@ #define CORE_DDR_200_CFG 0x184 #define CORE_CDC_T4_DLY_SEL BIT(0) +#define CORE_CMDIN_RCLK_EN BIT(1) #define CORE_START_CDC_TRAFFIC BIT(6) #define CORE_VENDOR_SPEC3 0x1b0 #define CORE_PWRSAVE_DLL BIT(3) @@ -138,6 +140,46 @@ struct sdhci_msm_host { bool use_cdclp533; }; +static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, + unsigned int clock) +{ + struct mmc_ios ios = host->mmc->ios; + /* + * The SDHC requires internal clock frequency to be double the + * actual clock that will be set for DDR mode. The controller + * uses the faster clock(100/400MHz) for some of its parts and + * send the actual required clock (50/200MHz) to the card. + */ + if (ios.timing == MMC_TIMING_UHS_DDR50 || + ios.timing == MMC_TIMING_MMC_DDR52 || + ios.timing == MMC_TIMING_MMC_HS400 || + host->flags & SDHCI_HS400_TUNING) + clock *= 2; + return clock; +} + +static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host, + unsigned int clock) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_ios curr_ios = host->mmc->ios; + int rc; + + clock = msm_get_clock_rate_for_bus_mode(host, clock); + rc = clk_set_rate(msm_host->clk, clock); + if (rc) { + pr_err("%s: Failed to set clock at rate %u at timing %d\n", + mmc_hostname(host->mmc), clock, + curr_ios.timing); + return; + } + msm_host->clk_rate = clock; + pr_debug("%s: Setting clock at rate %lu at timing %d\n", + mmc_hostname(host->mmc), clk_get_rate(msm_host->clk), + curr_ios.timing); +} + /* Platform specific tuning */ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) { @@ -464,6 +506,122 @@ static int msm_init_cm_dll(struct sdhci_host *host) return 0; } +static void msm_hc_select_default(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + u32 config; + + if (!msm_host->use_cdclp533) { + config = readl_relaxed(host->ioaddr + + CORE_VENDOR_SPEC3); + config &= ~CORE_PWRSAVE_DLL; + writel_relaxed(config, host->ioaddr + + CORE_VENDOR_SPEC3); + } + + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config &= ~CORE_HC_MCLK_SEL_MASK; + config |= CORE_HC_MCLK_SEL_DFLT; + writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + + /* + * Disable HC_SELECT_IN to be able to use the UHS mode select + * configuration from Host Control2 register for all other + * modes. + * Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field + * in VENDOR_SPEC_FUNC + */ + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config &= ~CORE_HC_SELECT_IN_EN; + config &= ~CORE_HC_SELECT_IN_MASK; + writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + + /* + * Make sure above writes impacting free running MCLK are completed + * before changing the clk_rate at GCC. + */ + wmb(); +} + +static void msm_hc_select_hs400(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_ios ios = host->mmc->ios; + u32 config, dll_lock; + int rc; + + /* Select the divided clock (free running MCLK/2) */ + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config &= ~CORE_HC_MCLK_SEL_MASK; + config |= CORE_HC_MCLK_SEL_HS400; + + writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + /* + * Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC + * register + */ + if ((msm_host->tuning_done || ios.enhanced_strobe) && + !msm_host->calibration_done) { + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config |= CORE_HC_SELECT_IN_HS400; + config |= CORE_HC_SELECT_IN_EN; + writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + } + if (!msm_host->clk_rate && !msm_host->use_cdclp533) { + /* + * Poll on DLL_LOCK or DDR_DLL_LOCK bits in + * CORE_DLL_STATUS to be set. This should get set + * within 15 us at 200 MHz. + */ + rc = readl_relaxed_poll_timeout(host->ioaddr + + CORE_DLL_STATUS, + dll_lock, + (dll_lock & + (CORE_DLL_LOCK | + CORE_DDR_DLL_LOCK)), 10, + 1000); + if (rc == -ETIMEDOUT) + pr_err("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n", + mmc_hostname(host->mmc), dll_lock); + } + /* + * Make sure above writes impacting free running MCLK are completed + * before changing the clk_rate at GCC. + */ + wmb(); +} + +/* + * sdhci_msm_hc_select_mode :- In general all timing modes are + * controlled via UHS mode select in Host Control2 register. + * eMMC specific HS200/HS400 doesn't have their respective modes + * defined here, hence we use these values. + * + * HS200 - SDR104 (Since they both are equivalent in functionality) + * HS400 - This involves multiple configurations + * Initially SDR104 - when tuning is required as HS200 + * Then when switching to DDR @ 400MHz (HS400) we use + * the vendor specific HC_SELECT_IN to control the mode. + * + * In addition to controlling the modes we also need to select the + * correct input clock for DLL depending on the mode. + * + * HS400 - divided clock (free running MCLK/2) + * All other modes - default (free running MCLK) + */ +void sdhci_msm_hc_select_mode(struct sdhci_host *host) +{ + struct mmc_ios ios = host->mmc->ios; + + if (ios.timing == MMC_TIMING_MMC_HS400 || + host->flags & SDHCI_HS400_TUNING) + msm_hc_select_hs400(host); + else + msm_hc_select_default(host); +} + static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -506,19 +664,7 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) config &= ~CORE_START_CDC_TRAFFIC; writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); - /* - * Perform CDC Register Initialization Sequence - * - * CORE_CSR_CDC_CTLR_CFG0 0x11800EC - * CORE_CSR_CDC_CTLR_CFG1 0x3011111 - * CORE_CSR_CDC_CAL_TIMER_CFG0 0x1201000 - * CORE_CSR_CDC_CAL_TIMER_CFG1 0x4 - * CORE_CSR_CDC_REFCOUNT_CFG 0xCB732020 - * CORE_CSR_CDC_COARSE_CAL_CFG 0xB19 - * CORE_CSR_CDC_DELAY_CFG 0x3AC - * CORE_CDC_OFFSET_CFG 0x0 - * CORE_CDC_SLAVE_DDA_CFG 0x16334 - */ + /* Perform CDC Register Initialization Sequence */ writel_relaxed(0x11800EC, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0); writel_relaxed(0x3011111, host->ioaddr + CORE_CSR_CDC_CTLR_CFG1); @@ -526,7 +672,7 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) writel_relaxed(0x4, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG1); writel_relaxed(0xCB732020, host->ioaddr + CORE_CSR_CDC_REFCOUNT_CFG); writel_relaxed(0xB19, host->ioaddr + CORE_CSR_CDC_COARSE_CAL_CFG); - writel_relaxed(0x3AC, host->ioaddr + CORE_CSR_CDC_DELAY_CFG); + writel_relaxed(0x4E2, host->ioaddr + CORE_CSR_CDC_DELAY_CFG); writel_relaxed(0x0, host->ioaddr + CORE_CDC_OFFSET_CFG); writel_relaxed(0x16334, host->ioaddr + CORE_CDC_SLAVE_DDA_CFG); @@ -579,6 +725,7 @@ out: static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) { + struct mmc_host *mmc = host->mmc; u32 dll_status, config; int ret; @@ -593,6 +740,12 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) */ writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + CORE_DDR_CONFIG); + if (mmc->ios.enhanced_strobe) { + config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG); + config |= CORE_CMDIN_RCLK_EN; + writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); + } + config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); config |= CORE_DDR_CAL_EN; writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2); @@ -627,6 +780,7 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_host *mmc = host->mmc; int ret; u32 config; @@ -640,14 +794,17 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) if (ret) goto out; - /* Set the selected phase in delay line hw block */ - ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase); - if (ret) - goto out; + if (!mmc->ios.enhanced_strobe) { + /* Set the selected phase in delay line hw block */ + ret = msm_config_cm_dll_phase(host, + msm_host->saved_tuning_phase); + if (ret) + goto out; + config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config |= CORE_CMD_DAT_TRACK_SEL; + writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + } - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); - config |= CORE_CMD_DAT_TRACK_SEL; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); if (msm_host->use_cdclp533) ret = sdhci_msm_cdclp533_calibration(host); else @@ -658,12 +815,12 @@ out: return ret; } -static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) +static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode) { + struct sdhci_host *host = mmc_priv(mmc); int tuning_seq_cnt = 3; u8 phase, tuned_phases[16], tuned_phase_cnt = 0; int rc; - struct mmc_host *mmc = host->mmc; struct mmc_ios ios = host->mmc->ios; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); @@ -678,6 +835,17 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) ios.timing == MMC_TIMING_UHS_SDR104)) return 0; + /* + * For HS400 tuning in HS200 timing requires: + * - select MCLK/2 in VENDOR_SPEC + * - program MCLK to 400MHz (or nearest supported) in GCC + */ + if (host->flags & SDHCI_HS400_TUNING) { + sdhci_msm_hc_select_mode(host); + msm_set_clock_rate_for_bus_mode(host, ios.clock); + host->flags &= ~SDHCI_HS400_TUNING; + } + retry: /* First of all reset the tuning block */ rc = msm_init_cm_dll(host); @@ -732,6 +900,30 @@ retry: return rc; } +/* + * sdhci_msm_hs400 - Calibrate the DLL for HS400 bus speed mode operation. + * This needs to be done for both tuning and enhanced_strobe mode. + * DLL operation is only needed for clock > 100MHz. For clock <= 100MHz + * fixed feedback clock is used. + */ +static void sdhci_msm_hs400(struct sdhci_host *host, struct mmc_ios *ios) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + int ret; + + if (host->clock > CORE_FREQ_100MHZ && + (msm_host->tuning_done || ios->enhanced_strobe) && + !msm_host->calibration_done) { + ret = sdhci_msm_hs400_dll_calibration(host); + if (!ret) + msm_host->calibration_done = true; + else + pr_err("%s: Failed to calibrate DLL for hs400 mode (%d)\n", + mmc_hostname(host->mmc), ret); + } +} + static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) { @@ -800,12 +992,10 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); spin_unlock_irq(&host->lock); - /* CDCLP533 HW calibration is only required for HS400 mode*/ - if (host->clock > CORE_FREQ_100MHZ && - msm_host->tuning_done && !msm_host->calibration_done && - mmc->ios.timing == MMC_TIMING_MMC_HS400) - if (!sdhci_msm_hs400_dll_calibration(host)) - msm_host->calibration_done = true; + + if (mmc->ios.timing == MMC_TIMING_MMC_HS400) + sdhci_msm_hs400(host, &mmc->ios); + spin_lock_irq(&host->lock); } @@ -893,9 +1083,6 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); - struct mmc_ios curr_ios = host->mmc->ios; - u32 config, dll_lock; - int rc; if (!clock) { msm_host->clk_rate = clock; @@ -903,117 +1090,11 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) } spin_unlock_irq(&host->lock); - /* - * The SDHC requires internal clock frequency to be double the - * actual clock that will be set for DDR mode. The controller - * uses the faster clock(100/400MHz) for some of its parts and - * send the actual required clock (50/200MHz) to the card. - */ - if (curr_ios.timing == MMC_TIMING_UHS_DDR50 || - curr_ios.timing == MMC_TIMING_MMC_DDR52 || - curr_ios.timing == MMC_TIMING_MMC_HS400) - clock *= 2; - /* - * In general all timing modes are controlled via UHS mode select in - * Host Control2 register. eMMC specific HS200/HS400 doesn't have - * their respective modes defined here, hence we use these values. - * - * HS200 - SDR104 (Since they both are equivalent in functionality) - * HS400 - This involves multiple configurations - * Initially SDR104 - when tuning is required as HS200 - * Then when switching to DDR @ 400MHz (HS400) we use - * the vendor specific HC_SELECT_IN to control the mode. - * - * In addition to controlling the modes we also need to select the - * correct input clock for DLL depending on the mode. - * - * HS400 - divided clock (free running MCLK/2) - * All other modes - default (free running MCLK) - */ - if (curr_ios.timing == MMC_TIMING_MMC_HS400) { - /* Select the divided clock (free running MCLK/2) */ - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); - config &= ~CORE_HC_MCLK_SEL_MASK; - config |= CORE_HC_MCLK_SEL_HS400; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); - /* - * Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC - * register - */ - if (msm_host->tuning_done && !msm_host->calibration_done) { - /* - * Write 0x6 to HC_SELECT_IN and 1 to HC_SELECT_IN_EN - * field in VENDOR_SPEC_FUNC - */ - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); - config |= CORE_HC_SELECT_IN_HS400; - config |= CORE_HC_SELECT_IN_EN; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); - } - if (!msm_host->clk_rate && !msm_host->use_cdclp533) { - /* - * Poll on DLL_LOCK or DDR_DLL_LOCK bits in - * CORE_DLL_STATUS to be set. This should get set - * within 15 us at 200 MHz. - */ - rc = readl_relaxed_poll_timeout(host->ioaddr + - CORE_DLL_STATUS, - dll_lock, - (dll_lock & - (CORE_DLL_LOCK | - CORE_DDR_DLL_LOCK)), 10, - 1000); - if (rc == -ETIMEDOUT) - pr_err("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n", - mmc_hostname(host->mmc), dll_lock); - } - } else { - if (!msm_host->use_cdclp533) { - config = readl_relaxed(host->ioaddr + - CORE_VENDOR_SPEC3); - config &= ~CORE_PWRSAVE_DLL; - writel_relaxed(config, host->ioaddr + - CORE_VENDOR_SPEC3); - } + sdhci_msm_hc_select_mode(host); - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); - config &= ~CORE_HC_MCLK_SEL_MASK; - config |= CORE_HC_MCLK_SEL_DFLT; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + msm_set_clock_rate_for_bus_mode(host, clock); - /* - * Disable HC_SELECT_IN to be able to use the UHS mode select - * configuration from Host Control2 register for all other - * modes. - * Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field - * in VENDOR_SPEC_FUNC - */ - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); - config &= ~CORE_HC_SELECT_IN_EN; - config &= ~CORE_HC_SELECT_IN_MASK; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); - } - - /* - * Make sure above writes impacting free running MCLK are completed - * before changing the clk_rate at GCC. - */ - wmb(); - - rc = clk_set_rate(msm_host->clk, clock); - if (rc) { - pr_err("%s: Failed to set clock at rate %u at timing %d\n", - mmc_hostname(host->mmc), clock, - curr_ios.timing); - goto out_lock; - } - msm_host->clk_rate = clock; - pr_debug("%s: Setting clock at rate %lu at timing %d\n", - mmc_hostname(host->mmc), clk_get_rate(msm_host->clk), - curr_ios.timing); - -out_lock: spin_lock_irq(&host->lock); out: __sdhci_msm_set_clock(host, clock); @@ -1027,7 +1108,6 @@ static const struct of_device_id sdhci_msm_dt_match[] = { MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match); static const struct sdhci_ops sdhci_msm_ops = { - .platform_execute_tuning = sdhci_msm_execute_tuning, .reset = sdhci_reset, .set_clock = sdhci_msm_set_clock, .get_min_clock = sdhci_msm_get_min_clock, @@ -1134,17 +1214,9 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto clk_disable; } - config = readl_relaxed(msm_host->core_mem + CORE_POWER); - config |= CORE_SW_RST; - writel_relaxed(config, msm_host->core_mem + CORE_POWER); - - /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */ - usleep_range(1000, 5000); - if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) { - dev_err(&pdev->dev, "Stuck in reset\n"); - ret = -ETIMEDOUT; - goto clk_disable; - } + /* Reset the vendor spec register to power on reset state */ + writel_relaxed(CORE_VENDOR_SPEC_POR_VAL, + host->ioaddr + CORE_VENDOR_SPEC); /* Set HC_MODE_EN bit in HC_MODE register */ writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE)); @@ -1210,6 +1282,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) MSM_MMC_AUTOSUSPEND_DELAY_MS); pm_runtime_use_autosuspend(&pdev->dev); + host->mmc_host_ops.execute_tuning = sdhci_msm_execute_tuning; ret = sdhci_add_host(host); if (ret) goto pm_runtime_disable; diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 410a55b1c25f..1cfd7f900339 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -28,13 +28,9 @@ #include "sdhci-pltfm.h" #include <linux/of.h> -#define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c #define SDHCI_ARASAN_VENDOR_REGISTER 0x78 #define VENDOR_ENHANCED_STROBE BIT(0) -#define CLK_CTRL_TIMEOUT_SHIFT 16 -#define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT) -#define CLK_CTRL_TIMEOUT_MIN_EXP 13 #define PHY_CLK_TOO_SLOW_HZ 400000 @@ -163,15 +159,15 @@ static int sdhci_arasan_syscon_write(struct sdhci_host *host, static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) { - u32 div; unsigned long freq; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET); - div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT; + /* SDHCI timeout clock is in kHz */ + freq = DIV_ROUND_UP(clk_get_rate(pltfm_host->clk), 1000); - freq = clk_get_rate(pltfm_host->clk); - freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div); + /* or in MHz */ + if (host->caps & SDHCI_TIMEOUT_CLK_UNIT) + freq = DIV_ROUND_UP(freq, 1000); return freq; } diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index 2f9ad213377a..d5430ed02a67 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -29,6 +29,8 @@ #include "sdhci-pltfm.h" +#define SDMMC_MC1R 0x204 +#define SDMMC_MC1R_DDR BIT(3) #define SDMMC_CACR 0x230 #define SDMMC_CACR_CAPWREN BIT(0) #define SDMMC_CACR_KEY (0x46 << 8) @@ -85,11 +87,37 @@ static void sdhci_at91_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); } +/* + * In this specific implementation of the SDHCI controller, the power register + * needs to have a valid voltage set even when the power supply is managed by + * an external regulator. + */ +static void sdhci_at91_set_power(struct sdhci_host *host, unsigned char mode, + unsigned short vdd) +{ + if (!IS_ERR(host->mmc->supply.vmmc)) { + struct mmc_host *mmc = host->mmc; + + spin_unlock_irq(&host->lock); + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); + spin_lock_irq(&host->lock); + } + sdhci_set_power_noreg(host, mode, vdd); +} + +void sdhci_at91_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) +{ + if (timing == MMC_TIMING_MMC_DDR52) + sdhci_writeb(host, SDMMC_MC1R_DDR, SDMMC_MC1R); + sdhci_set_uhs_signaling(host, timing); +} + static const struct sdhci_ops sdhci_at91_sama5d2_ops = { .set_clock = sdhci_at91_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, - .set_uhs_signaling = sdhci_set_uhs_signaling, + .set_uhs_signaling = sdhci_at91_set_uhs_signaling, + .set_power = sdhci_at91_set_power, }; static const struct sdhci_pltfm_data soc_data_sama5d2 = { diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 9a6eb4492172..d3aa67142839 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -431,6 +431,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host); int pre_div = 1; int div = 1; + u32 timeout; u32 temp; host->mmc->actual_clock = 0; @@ -451,8 +452,8 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) } temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); - temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN - | ESDHC_CLOCK_MASK); + temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | + ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK); sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); while (host->max_clk / pre_div / 16 > clock && pre_div < 256) @@ -472,7 +473,21 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) | (div << ESDHC_DIVIDER_SHIFT) | (pre_div << ESDHC_PREDIV_SHIFT)); sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); - mdelay(1); + + /* Wait max 20 ms */ + timeout = 20; + while (!(sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)) { + if (timeout == 0) { + pr_err("%s: Internal clock never stabilised.\n", + mmc_hostname(host->mmc)); + return; + } + timeout--; + mdelay(1); + } + + temp |= ESDHC_CLOCK_SDCLKEN; + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); } static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) @@ -569,16 +584,19 @@ static const struct sdhci_ops sdhci_esdhc_le_ops = { }; static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = { - .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION - | SDHCI_QUIRK_NO_CARD_NO_RESET - | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks = ESDHC_DEFAULT_QUIRKS | +#ifdef CONFIG_PPC + SDHCI_QUIRK_BROKEN_CARD_DETECTION | +#endif + SDHCI_QUIRK_NO_CARD_NO_RESET | + SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .ops = &sdhci_esdhc_be_ops, }; static const struct sdhci_pltfm_data sdhci_esdhc_le_pdata = { - .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION - | SDHCI_QUIRK_NO_CARD_NO_RESET - | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks = ESDHC_DEFAULT_QUIRKS | + SDHCI_QUIRK_NO_CARD_NO_RESET | + SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .ops = &sdhci_esdhc_le_ops, }; @@ -643,8 +661,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) of_device_is_compatible(np, "fsl,p5020-esdhc") || of_device_is_compatible(np, "fsl,p4080-esdhc") || of_device_is_compatible(np, "fsl,p1020-esdhc") || - of_device_is_compatible(np, "fsl,t1040-esdhc") || - of_device_is_compatible(np, "fsl,ls1021a-esdhc")) + of_device_is_compatible(np, "fsl,t1040-esdhc")) host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; if (of_device_is_compatible(np, "fsl,ls1021a-esdhc")) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 1a72d32af07f..86560d590786 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -424,7 +424,6 @@ static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot) static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) { slot->host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; - slot->cd_con_id = NULL; slot->cd_idx = 0; slot->cd_override_level = true; if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BXT_SD || @@ -452,6 +451,8 @@ static void sdhci_intel_set_power(struct sdhci_host *host, unsigned char mode, if (mode == MMC_POWER_OFF) return; + spin_unlock_irq(&host->lock); + /* * Bus power might not enable after D3 -> D0 transition due to the * present state not yet having propagated. Retry for up to 2ms. @@ -464,6 +465,8 @@ static void sdhci_intel_set_power(struct sdhci_host *host, unsigned char mode, reg |= SDHCI_POWER_ON; sdhci_writeb(host, reg, SDHCI_POWER_CONTROL); } + + spin_lock_irq(&host->lock); } static const struct sdhci_ops sdhci_intel_byt_ops = { @@ -866,6 +869,86 @@ enum amd_chipset_gen { AMD_CHIPSET_UNKNOWN, }; +/* AMD registers */ +#define AMD_SD_AUTO_PATTERN 0xB8 +#define AMD_MSLEEP_DURATION 4 +#define AMD_SD_MISC_CONTROL 0xD0 +#define AMD_MAX_TUNE_VALUE 0x0B +#define AMD_AUTO_TUNE_SEL 0x10800 +#define AMD_FIFO_PTR 0x30 +#define AMD_BIT_MASK 0x1F + +static void amd_tuning_reset(struct sdhci_host *host) +{ + unsigned int val; + + val = sdhci_readw(host, SDHCI_HOST_CONTROL2); + val |= SDHCI_CTRL_PRESET_VAL_ENABLE | SDHCI_CTRL_EXEC_TUNING; + sdhci_writew(host, val, SDHCI_HOST_CONTROL2); + + val = sdhci_readw(host, SDHCI_HOST_CONTROL2); + val &= ~SDHCI_CTRL_EXEC_TUNING; + sdhci_writew(host, val, SDHCI_HOST_CONTROL2); +} + +static void amd_config_tuning_phase(struct pci_dev *pdev, u8 phase) +{ + unsigned int val; + + pci_read_config_dword(pdev, AMD_SD_AUTO_PATTERN, &val); + val &= ~AMD_BIT_MASK; + val |= (AMD_AUTO_TUNE_SEL | (phase << 1)); + pci_write_config_dword(pdev, AMD_SD_AUTO_PATTERN, val); +} + +static void amd_enable_manual_tuning(struct pci_dev *pdev) +{ + unsigned int val; + + pci_read_config_dword(pdev, AMD_SD_MISC_CONTROL, &val); + val |= AMD_FIFO_PTR; + pci_write_config_dword(pdev, AMD_SD_MISC_CONTROL, val); +} + +static int amd_execute_tuning(struct sdhci_host *host, u32 opcode) +{ + struct sdhci_pci_slot *slot = sdhci_priv(host); + struct pci_dev *pdev = slot->chip->pdev; + u8 valid_win = 0; + u8 valid_win_max = 0; + u8 valid_win_end = 0; + u8 ctrl, tune_around; + + amd_tuning_reset(host); + + for (tune_around = 0; tune_around < 12; tune_around++) { + amd_config_tuning_phase(pdev, tune_around); + + if (mmc_send_tuning(host->mmc, opcode, NULL)) { + valid_win = 0; + msleep(AMD_MSLEEP_DURATION); + ctrl = SDHCI_RESET_CMD | SDHCI_RESET_DATA; + sdhci_writeb(host, ctrl, SDHCI_SOFTWARE_RESET); + } else if (++valid_win > valid_win_max) { + valid_win_max = valid_win; + valid_win_end = tune_around; + } + } + + if (!valid_win_max) { + dev_err(&pdev->dev, "no tuning point found\n"); + return -EIO; + } + + amd_config_tuning_phase(pdev, valid_win_end - valid_win_max / 2); + + amd_enable_manual_tuning(pdev); + + host->mmc->retune_period = 0; + + return 0; +} + static int amd_probe(struct sdhci_pci_chip *chip) { struct pci_dev *smbus_dev; @@ -888,16 +971,24 @@ static int amd_probe(struct sdhci_pci_chip *chip) } } - if ((gen == AMD_CHIPSET_BEFORE_ML) || (gen == AMD_CHIPSET_CZ)) { + if (gen == AMD_CHIPSET_BEFORE_ML || gen == AMD_CHIPSET_CZ) chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD; - chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; - } return 0; } +static const struct sdhci_ops amd_sdhci_pci_ops = { + .set_clock = sdhci_set_clock, + .enable_dma = sdhci_pci_enable_dma, + .set_bus_width = sdhci_pci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .platform_execute_tuning = amd_execute_tuning, +}; + static const struct sdhci_pci_fixes sdhci_amd = { .probe = amd_probe, + .ops = &amd_sdhci_pci_ops, }; static const struct pci_device_id pci_ids[] = { @@ -1817,7 +1908,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP; if (slot->cd_idx >= 0) { - ret = mmc_gpiod_request_cd(host->mmc, slot->cd_con_id, slot->cd_idx, + ret = mmc_gpiod_request_cd(host->mmc, NULL, slot->cd_idx, slot->cd_override_level, 0, NULL); if (ret == -EPROBE_DEFER) goto remove; diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 4abdaed72bd4..36f743464fcc 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -81,7 +81,6 @@ struct sdhci_pci_slot { int cd_gpio; int cd_irq; - char *cd_con_id; int cd_idx; bool cd_override_level; diff --git a/drivers/mmc/host/sdhci-s3c-regs.h b/drivers/mmc/host/sdhci-s3c-regs.h deleted file mode 100644 index e34049ad44cc..000000000000 --- a/drivers/mmc/host/sdhci-s3c-regs.h +++ /dev/null @@ -1,87 +0,0 @@ -/* linux/arch/arm/plat-s3c/include/plat/regs-sdhci.h - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks <ben@simtec.co.uk> - * - * S3C Platform - SDHCI (HSMMC) register definitions - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef __PLAT_S3C_SDHCI_REGS_H -#define __PLAT_S3C_SDHCI_REGS_H __FILE__ - -#define S3C_SDHCI_CONTROL2 (0x80) -#define S3C_SDHCI_CONTROL3 (0x84) -#define S3C64XX_SDHCI_CONTROL4 (0x8C) - -#define S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR (1 << 31) -#define S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK (1 << 30) -#define S3C_SDHCI_CTRL2_CDINVRXD3 (1 << 29) -#define S3C_SDHCI_CTRL2_SLCARDOUT (1 << 28) - -#define S3C_SDHCI_CTRL2_FLTCLKSEL_MASK (0xf << 24) -#define S3C_SDHCI_CTRL2_FLTCLKSEL_SHIFT (24) -#define S3C_SDHCI_CTRL2_FLTCLKSEL(_x) ((_x) << 24) - -#define S3C_SDHCI_CTRL2_LVLDAT_MASK (0xff << 16) -#define S3C_SDHCI_CTRL2_LVLDAT_SHIFT (16) -#define S3C_SDHCI_CTRL2_LVLDAT(_x) ((_x) << 16) - -#define S3C_SDHCI_CTRL2_ENFBCLKTX (1 << 15) -#define S3C_SDHCI_CTRL2_ENFBCLKRX (1 << 14) -#define S3C_SDHCI_CTRL2_SDCDSEL (1 << 13) -#define S3C_SDHCI_CTRL2_SDSIGPC (1 << 12) -#define S3C_SDHCI_CTRL2_ENBUSYCHKTXSTART (1 << 11) - -#define S3C_SDHCI_CTRL2_DFCNT_MASK (0x3 << 9) -#define S3C_SDHCI_CTRL2_DFCNT_SHIFT (9) -#define S3C_SDHCI_CTRL2_DFCNT_NONE (0x0 << 9) -#define S3C_SDHCI_CTRL2_DFCNT_4SDCLK (0x1 << 9) -#define S3C_SDHCI_CTRL2_DFCNT_16SDCLK (0x2 << 9) -#define S3C_SDHCI_CTRL2_DFCNT_64SDCLK (0x3 << 9) - -#define S3C_SDHCI_CTRL2_ENCLKOUTHOLD (1 << 8) -#define S3C_SDHCI_CTRL2_RWAITMODE (1 << 7) -#define S3C_SDHCI_CTRL2_DISBUFRD (1 << 6) -#define S3C_SDHCI_CTRL2_SELBASECLK_MASK (0x3 << 4) -#define S3C_SDHCI_CTRL2_SELBASECLK_SHIFT (4) -#define S3C_SDHCI_CTRL2_PWRSYNC (1 << 3) -#define S3C_SDHCI_CTRL2_ENCLKOUTMSKCON (1 << 1) -#define S3C_SDHCI_CTRL2_HWINITFIN (1 << 0) - -#define S3C_SDHCI_CTRL3_FCSEL3 (1 << 31) -#define S3C_SDHCI_CTRL3_FCSEL2 (1 << 23) -#define S3C_SDHCI_CTRL3_FCSEL1 (1 << 15) -#define S3C_SDHCI_CTRL3_FCSEL0 (1 << 7) - -#define S3C_SDHCI_CTRL3_FIA3_MASK (0x7f << 24) -#define S3C_SDHCI_CTRL3_FIA3_SHIFT (24) -#define S3C_SDHCI_CTRL3_FIA3(_x) ((_x) << 24) - -#define S3C_SDHCI_CTRL3_FIA2_MASK (0x7f << 16) -#define S3C_SDHCI_CTRL3_FIA2_SHIFT (16) -#define S3C_SDHCI_CTRL3_FIA2(_x) ((_x) << 16) - -#define S3C_SDHCI_CTRL3_FIA1_MASK (0x7f << 8) -#define S3C_SDHCI_CTRL3_FIA1_SHIFT (8) -#define S3C_SDHCI_CTRL3_FIA1(_x) ((_x) << 8) - -#define S3C_SDHCI_CTRL3_FIA0_MASK (0x7f << 0) -#define S3C_SDHCI_CTRL3_FIA0_SHIFT (0) -#define S3C_SDHCI_CTRL3_FIA0(_x) ((_x) << 0) - -#define S3C64XX_SDHCI_CONTROL4_DRIVE_MASK (0x3 << 16) -#define S3C64XX_SDHCI_CONTROL4_DRIVE_SHIFT (16) -#define S3C64XX_SDHCI_CONTROL4_DRIVE_2mA (0x0 << 16) -#define S3C64XX_SDHCI_CONTROL4_DRIVE_4mA (0x1 << 16) -#define S3C64XX_SDHCI_CONTROL4_DRIVE_7mA (0x2 << 16) -#define S3C64XX_SDHCI_CONTROL4_DRIVE_9mA (0x3 << 16) - -#define S3C64XX_SDHCI_CONTROL4_BUSY (1) - -#endif /* __PLAT_S3C_SDHCI_REGS_H */ diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index de219ca7ea7c..3e5c83d435ae 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -29,11 +29,80 @@ #include <linux/mmc/host.h> -#include "sdhci-s3c-regs.h" #include "sdhci.h" #define MAX_BUS_CLK (4) +#define S3C_SDHCI_CONTROL2 (0x80) +#define S3C_SDHCI_CONTROL3 (0x84) +#define S3C64XX_SDHCI_CONTROL4 (0x8C) + +#define S3C64XX_SDHCI_CTRL2_ENSTAASYNCCLR BIT(31) +#define S3C64XX_SDHCI_CTRL2_ENCMDCNFMSK BIT(30) +#define S3C_SDHCI_CTRL2_CDINVRXD3 BIT(29) +#define S3C_SDHCI_CTRL2_SLCARDOUT BIT(28) + +#define S3C_SDHCI_CTRL2_FLTCLKSEL_MASK (0xf << 24) +#define S3C_SDHCI_CTRL2_FLTCLKSEL_SHIFT (24) +#define S3C_SDHCI_CTRL2_FLTCLKSEL(_x) ((_x) << 24) + +#define S3C_SDHCI_CTRL2_LVLDAT_MASK (0xff << 16) +#define S3C_SDHCI_CTRL2_LVLDAT_SHIFT (16) +#define S3C_SDHCI_CTRL2_LVLDAT(_x) ((_x) << 16) + +#define S3C_SDHCI_CTRL2_ENFBCLKTX BIT(15) +#define S3C_SDHCI_CTRL2_ENFBCLKRX BIT(14) +#define S3C_SDHCI_CTRL2_SDCDSEL BIT(13) +#define S3C_SDHCI_CTRL2_SDSIGPC BIT(12) +#define S3C_SDHCI_CTRL2_ENBUSYCHKTXSTART BIT(11) + +#define S3C_SDHCI_CTRL2_DFCNT_MASK (0x3 << 9) +#define S3C_SDHCI_CTRL2_DFCNT_SHIFT (9) +#define S3C_SDHCI_CTRL2_DFCNT_NONE (0x0 << 9) +#define S3C_SDHCI_CTRL2_DFCNT_4SDCLK (0x1 << 9) +#define S3C_SDHCI_CTRL2_DFCNT_16SDCLK (0x2 << 9) +#define S3C_SDHCI_CTRL2_DFCNT_64SDCLK (0x3 << 9) + +#define S3C_SDHCI_CTRL2_ENCLKOUTHOLD BIT(8) +#define S3C_SDHCI_CTRL2_RWAITMODE BIT(7) +#define S3C_SDHCI_CTRL2_DISBUFRD BIT(6) + +#define S3C_SDHCI_CTRL2_SELBASECLK_MASK (0x3 << 4) +#define S3C_SDHCI_CTRL2_SELBASECLK_SHIFT (4) +#define S3C_SDHCI_CTRL2_PWRSYNC BIT(3) +#define S3C_SDHCI_CTRL2_ENCLKOUTMSKCON BIT(1) +#define S3C_SDHCI_CTRL2_HWINITFIN BIT(0) + +#define S3C_SDHCI_CTRL3_FCSEL3 BIT(31) +#define S3C_SDHCI_CTRL3_FCSEL2 BIT(23) +#define S3C_SDHCI_CTRL3_FCSEL1 BIT(15) +#define S3C_SDHCI_CTRL3_FCSEL0 BIT(7) + +#define S3C_SDHCI_CTRL3_FIA3_MASK (0x7f << 24) +#define S3C_SDHCI_CTRL3_FIA3_SHIFT (24) +#define S3C_SDHCI_CTRL3_FIA3(_x) ((_x) << 24) + +#define S3C_SDHCI_CTRL3_FIA2_MASK (0x7f << 16) +#define S3C_SDHCI_CTRL3_FIA2_SHIFT (16) +#define S3C_SDHCI_CTRL3_FIA2(_x) ((_x) << 16) + +#define S3C_SDHCI_CTRL3_FIA1_MASK (0x7f << 8) +#define S3C_SDHCI_CTRL3_FIA1_SHIFT (8) +#define S3C_SDHCI_CTRL3_FIA1(_x) ((_x) << 8) + +#define S3C_SDHCI_CTRL3_FIA0_MASK (0x7f << 0) +#define S3C_SDHCI_CTRL3_FIA0_SHIFT (0) +#define S3C_SDHCI_CTRL3_FIA0(_x) ((_x) << 0) + +#define S3C64XX_SDHCI_CONTROL4_DRIVE_MASK (0x3 << 16) +#define S3C64XX_SDHCI_CONTROL4_DRIVE_SHIFT (16) +#define S3C64XX_SDHCI_CONTROL4_DRIVE_2mA (0x0 << 16) +#define S3C64XX_SDHCI_CONTROL4_DRIVE_4mA (0x1 << 16) +#define S3C64XX_SDHCI_CONTROL4_DRIVE_7mA (0x2 << 16) +#define S3C64XX_SDHCI_CONTROL4_DRIVE_9mA (0x3 << 16) + +#define S3C64XX_SDHCI_CONTROL4_BUSY (1) + /** * struct sdhci_s3c - S3C SDHCI instance * @host: The SDHCI host created diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0def99590d16..63bc33a54d0d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1362,7 +1362,9 @@ void sdhci_enable_clk(struct sdhci_host *host, u16 clk) return; } timeout--; - mdelay(1); + spin_unlock_irq(&host->lock); + usleep_range(900, 1100); + spin_lock_irq(&host->lock); } clk |= SDHCI_CLOCK_CARD_EN; @@ -1828,6 +1830,9 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) struct sdhci_host *host = mmc_priv(mmc); unsigned long flags; + if (enable) + pm_runtime_get_noresume(host->mmc->parent); + spin_lock_irqsave(&host->lock, flags); if (enable) host->flags |= SDHCI_SDIO_IRQ_ENABLED; @@ -1836,6 +1841,9 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) sdhci_enable_sdio_irq_nolock(host, enable); spin_unlock_irqrestore(&host->lock, flags); + + if (!enable) + pm_runtime_put_noidle(host->mmc->parent); } static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, @@ -2021,8 +2029,8 @@ static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode, unsigned long flags) { struct mmc_host *mmc = host->mmc; - struct mmc_command cmd = {0}; - struct mmc_request mrq = {NULL}; + struct mmc_command cmd = {}; + struct mmc_request mrq = {}; cmd.opcode = opcode; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; @@ -2114,7 +2122,6 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) spin_lock_irqsave(&host->lock, flags); hs400_tuning = host->flags & SDHCI_HS400_TUNING; - host->flags &= ~SDHCI_HS400_TUNING; if (host->tuning_mode == SDHCI_TUNING_MODE_1) tuning_count = host->tuning_count; @@ -2156,7 +2163,9 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) if (host->ops->platform_execute_tuning) { spin_unlock_irqrestore(&host->lock, flags); - return host->ops->platform_execute_tuning(host, opcode); + err = host->ops->platform_execute_tuning(host, opcode); + spin_lock_irqsave(&host->lock, flags); + goto out_unlock; } host->mmc->retune_period = tuning_count; @@ -2167,6 +2176,7 @@ int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) sdhci_end_tuning(host); out_unlock: + host->flags &= ~SDHCI_HS400_TUNING; spin_unlock_irqrestore(&host->lock, flags); return err; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 0b66f210ae82..edf3adfbc213 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -17,6 +17,8 @@ #include <linux/compiler.h> #include <linux/types.h> #include <linux/io.h> +#include <linux/leds.h> +#include <linux/interrupt.h> #include <linux/mmc/host.h> diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 900778421be6..4062d6bef3c8 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1079,26 +1079,10 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->state = STATE_IDLE; } -static int sh_mmcif_get_cd(struct mmc_host *mmc) -{ - struct sh_mmcif_host *host = mmc_priv(mmc); - struct device *dev = sh_mmcif_host_to_dev(host); - struct sh_mmcif_plat_data *p = dev->platform_data; - int ret = mmc_gpio_get_cd(mmc); - - if (ret >= 0) - return ret; - - if (!p || !p->get_cd) - return -ENOSYS; - else - return p->get_cd(host->pd); -} - static struct mmc_host_ops sh_mmcif_ops = { .request = sh_mmcif_request, .set_ios = sh_mmcif_set_ios, - .get_cd = sh_mmcif_get_cd, + .get_cd = mmc_gpio_get_cd, }; static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) @@ -1443,8 +1427,8 @@ static int sh_mmcif_probe(struct platform_device *pdev) host->mmc = mmc; host->addr = reg; host->timeout = msecs_to_jiffies(10000); - host->ccs_enable = !pd || !pd->ccs_unsupported; - host->clk_ctrl2_enable = pd && pd->clk_ctrl2_present; + host->ccs_enable = true; + host->clk_ctrl2_enable = false; host->pd = pdev; @@ -1509,12 +1493,6 @@ static int sh_mmcif_probe(struct platform_device *pdev) } } - if (pd && pd->use_cd_gpio) { - ret = mmc_gpio_request_cd(mmc, pd->cd_gpio, 0); - if (ret < 0) - goto err_clk; - } - mutex_init(&host->thread_lock); ret = mmc_add_host(mmc); diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index d46c2d00c182..bc6be0dbea39 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -143,6 +143,7 @@ MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match); struct sh_mobile_sdhi { struct clk *clk; + struct clk *clk_cd; struct tmio_mmc_data mmc_data; struct tmio_mmc_dma dma_priv; struct pinctrl *pinctrl; @@ -190,6 +191,12 @@ static int sh_mobile_sdhi_clk_enable(struct tmio_mmc_host *host) if (ret < 0) return ret; + ret = clk_prepare_enable(priv->clk_cd); + if (ret < 0) { + clk_disable_unprepare(priv->clk); + return ret; + } + /* * The clock driver may not know what maximum frequency * actually works, so it should be set with the max-frequency @@ -255,6 +262,7 @@ static void sh_mobile_sdhi_clk_disable(struct tmio_mmc_host *host) struct sh_mobile_sdhi *priv = host_to_priv(host); clk_disable_unprepare(priv->clk); + clk_disable_unprepare(priv->clk_cd); } static int sh_mobile_sdhi_card_busy(struct mmc_host *mmc) @@ -335,9 +343,6 @@ static unsigned int sh_mobile_sdhi_init_tuning(struct tmio_mmc_host *host) { struct sh_mobile_sdhi *priv; - if (!(host->mmc->caps & MMC_CAP_UHS_SDR104)) - return 0; - priv = host_to_priv(host); /* set sampling clock selection range */ @@ -444,12 +449,7 @@ static int sh_mobile_sdhi_select_tuning(struct tmio_mmc_host *host) static bool sh_mobile_sdhi_check_scc_error(struct tmio_mmc_host *host) { - struct sh_mobile_sdhi *priv; - - if (!(host->mmc->caps & MMC_CAP_UHS_SDR104)) - return 0; - - priv = host_to_priv(host); + struct sh_mobile_sdhi *priv = host_to_priv(host); /* Check SCC error */ if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) & @@ -468,9 +468,6 @@ static void sh_mobile_sdhi_hw_reset(struct tmio_mmc_host *host) { struct sh_mobile_sdhi *priv; - if (!(host->mmc->caps & MMC_CAP_UHS_SDR104)) - return; - priv = host_to_priv(host); /* Reset SCC */ @@ -556,8 +553,7 @@ static void sh_mobile_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable) static int sh_mobile_sdhi_probe(struct platform_device *pdev) { - const struct of_device_id *of_id = - of_match_device(sh_mobile_sdhi_of_match, &pdev->dev); + const struct sh_mobile_sdhi_of_data *of_data = of_device_get_match_data(&pdev->dev); struct sh_mobile_sdhi *priv; struct tmio_mmc_data *mmc_data; struct tmio_mmc_data *mmd = pdev->dev.platform_data; @@ -584,6 +580,21 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) goto eprobe; } + /* + * Some controllers provide a 2nd clock just to run the internal card + * detection logic. Unfortunately, the existing driver architecture does + * not support a separation of clocks for runtime PM usage. When + * native hotplug is used, the tmio driver assumes that the core + * must continue to run for card detect to stay active, so we cannot + * disable it. + * Additionally, it is prohibited to supply a clock to the core but not + * to the card detect circuit. That leaves us with if separate clocks + * are presented, we must treat them both as virtually 1 clock. + */ + priv->clk_cd = devm_clk_get(&pdev->dev, "cd"); + if (IS_ERR(priv->clk_cd)) + priv->clk_cd = NULL; + priv->pinctrl = devm_pinctrl_get(&pdev->dev); if (!IS_ERR(priv->pinctrl)) { priv->pins_default = pinctrl_lookup_state(priv->pinctrl, @@ -598,9 +609,8 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) goto eprobe; } - if (of_id && of_id->data) { - const struct sh_mobile_sdhi_of_data *of_data = of_id->data; + if (of_data) { mmc_data->flags |= of_data->tmio_flags; mmc_data->ocr_mask = of_data->tmio_ocr_mask; mmc_data->capabilities |= of_data->capabilities; @@ -623,11 +633,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) host->card_busy = sh_mobile_sdhi_card_busy; host->start_signal_voltage_switch = sh_mobile_sdhi_start_signal_voltage_switch; - host->init_tuning = sh_mobile_sdhi_init_tuning; - host->prepare_tuning = sh_mobile_sdhi_prepare_tuning; - host->select_tuning = sh_mobile_sdhi_select_tuning; - host->check_scc_error = sh_mobile_sdhi_check_scc_error; - host->hw_reset = sh_mobile_sdhi_hw_reset; } /* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */ @@ -659,40 +664,40 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) */ mmc_data->flags |= TMIO_MMC_HAVE_CMD12_CTRL; - /* - * All SDHI need SDIO_INFO1 reserved bit - */ - mmc_data->flags |= TMIO_MMC_SDIO_STATUS_QUIRK; + /* All SDHI have SDIO status bits which must be 1 */ + mmc_data->flags |= TMIO_MMC_SDIO_STATUS_SETBITS; ret = tmio_mmc_host_probe(host, mmc_data); if (ret < 0) goto efree; - if (host->mmc->caps & MMC_CAP_UHS_SDR104) { + /* Enable tuning iff we have an SCC and a supported mode */ + if (of_data && of_data->scc_offset && + (host->mmc->caps & MMC_CAP_UHS_SDR104 || + host->mmc->caps2 & MMC_CAP2_HS200_1_8V_SDR)) { + const struct sh_mobile_sdhi_scc *taps = of_data->taps; + bool hit = false; + host->mmc->caps |= MMC_CAP_HW_RESET; - if (of_id && of_id->data) { - const struct sh_mobile_sdhi_of_data *of_data; - const struct sh_mobile_sdhi_scc *taps; - bool hit = false; - - of_data = of_id->data; - taps = of_data->taps; - - for (i = 0; i < of_data->taps_num; i++) { - if (taps[i].clk_rate == 0 || - taps[i].clk_rate == host->mmc->f_max) { - host->scc_tappos = taps->tap; - hit = true; - break; - } + for (i = 0; i < of_data->taps_num; i++) { + if (taps[i].clk_rate == 0 || + taps[i].clk_rate == host->mmc->f_max) { + host->scc_tappos = taps->tap; + hit = true; + break; } + } - if (!hit) - dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104\n"); + if (!hit) + dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104\n"); - priv->scc_ctl = host->ctl + of_data->scc_offset; - } + priv->scc_ctl = host->ctl + of_data->scc_offset; + host->init_tuning = sh_mobile_sdhi_init_tuning; + host->prepare_tuning = sh_mobile_sdhi_prepare_tuning; + host->select_tuning = sh_mobile_sdhi_select_tuning; + host->check_scc_error = sh_mobile_sdhi_check_scc_error; + host->hw_reset = sh_mobile_sdhi_hw_reset; } i = 0; diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index b1d1303389a7..6ffcd2838272 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -5,6 +5,7 @@ * (C) Copyright 2013-2014 O2S GmbH <www.o2s.ch> * (C) Copyright 2013-2014 David Lanzend�rfer <david.lanzendoerfer@o2s.ch> * (C) Copyright 2013-2014 Hans de Goede <hdegoede@redhat.com> + * (C) Copyright 2017 Sootech SA * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -101,6 +102,7 @@ (SDXC_SOFT_RESET | SDXC_FIFO_RESET | SDXC_DMA_RESET) /* clock control bits */ +#define SDXC_MASK_DATA0 BIT(31) #define SDXC_CARD_CLOCK_ON BIT(16) #define SDXC_LOW_POWER_ON BIT(17) @@ -253,6 +255,11 @@ struct sunxi_mmc_cfg { /* does the IP block support autocalibration? */ bool can_calibrate; + + /* Does DATA0 needs to be masked while the clock is updated */ + bool mask_data0; + + bool needs_new_timings; }; struct sunxi_mmc_host { @@ -654,11 +661,16 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en) unsigned long expire = jiffies + msecs_to_jiffies(750); u32 rval; + dev_dbg(mmc_dev(host->mmc), "%sabling the clock\n", + oclk_en ? "en" : "dis"); + rval = mmc_readl(host, REG_CLKCR); - rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON); + rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON | SDXC_MASK_DATA0); if (oclk_en) rval |= SDXC_CARD_CLOCK_ON; + if (host->cfg->mask_data0) + rval |= SDXC_MASK_DATA0; mmc_writel(host, REG_CLKCR, rval); @@ -678,46 +690,29 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en) return -EIO; } + if (host->cfg->mask_data0) { + rval = mmc_readl(host, REG_CLKCR); + mmc_writel(host, REG_CLKCR, rval & ~SDXC_MASK_DATA0); + } + return 0; } static int sunxi_mmc_calibrate(struct sunxi_mmc_host *host, int reg_off) { - u32 reg = readl(host->reg_base + reg_off); - u32 delay; - unsigned long timeout; - if (!host->cfg->can_calibrate) return 0; - reg &= ~(SDXC_CAL_DL_MASK << SDXC_CAL_DL_SW_SHIFT); - reg &= ~SDXC_CAL_DL_SW_EN; - - writel(reg | SDXC_CAL_START, host->reg_base + reg_off); - - dev_dbg(mmc_dev(host->mmc), "calibration started\n"); - - timeout = jiffies + HZ * SDXC_CAL_TIMEOUT; - - while (!((reg = readl(host->reg_base + reg_off)) & SDXC_CAL_DONE)) { - if (time_before(jiffies, timeout)) - cpu_relax(); - else { - reg &= ~SDXC_CAL_START; - writel(reg, host->reg_base + reg_off); - - return -ETIMEDOUT; - } - } - - delay = (reg >> SDXC_CAL_DL_SHIFT) & SDXC_CAL_DL_MASK; - - reg &= ~SDXC_CAL_START; - reg |= (delay << SDXC_CAL_DL_SW_SHIFT) | SDXC_CAL_DL_SW_EN; - - writel(reg, host->reg_base + reg_off); - - dev_dbg(mmc_dev(host->mmc), "calibration ended, reg is 0x%x\n", reg); + /* + * FIXME: + * This is not clear how the calibration is supposed to work + * yet. The best rate have been obtained by simply setting the + * delay to 0, as Allwinner does in its BSP. + * + * The only mode that doesn't have such a delay is HS400, that + * is in itself a TODO. + */ + writel(SDXC_CAL_DL_SW_EN, host->reg_base + reg_off); return 0; } @@ -745,6 +740,7 @@ static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host, index = SDXC_CLK_50M_DDR; } } else { + dev_dbg(mmc_dev(host->mmc), "Invalid clock... returning\n"); return -EINVAL; } @@ -757,10 +753,21 @@ static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host, static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, struct mmc_ios *ios) { + struct mmc_host *mmc = host->mmc; long rate; u32 rval, clock = ios->clock; int ret; + ret = sunxi_mmc_oclk_onoff(host, 0); + if (ret) + return ret; + + /* Our clock is gated now */ + mmc->actual_clock = 0; + + if (!ios->clock) + return 0; + /* 8 bit DDR requires a higher module clock */ if (ios->timing == MMC_TIMING_MMC_DDR52 && ios->bus_width == MMC_BUS_WIDTH_8) @@ -768,25 +775,21 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, rate = clk_round_rate(host->clk_mmc, clock); if (rate < 0) { - dev_err(mmc_dev(host->mmc), "error rounding clk to %d: %ld\n", + dev_err(mmc_dev(mmc), "error rounding clk to %d: %ld\n", clock, rate); return rate; } - dev_dbg(mmc_dev(host->mmc), "setting clk to %d, rounded %ld\n", + dev_dbg(mmc_dev(mmc), "setting clk to %d, rounded %ld\n", clock, rate); /* setting clock rate */ ret = clk_set_rate(host->clk_mmc, rate); if (ret) { - dev_err(mmc_dev(host->mmc), "error setting clk to %ld: %d\n", + dev_err(mmc_dev(mmc), "error setting clk to %ld: %d\n", rate, ret); return ret; } - ret = sunxi_mmc_oclk_onoff(host, 0); - if (ret) - return ret; - /* clear internal divider */ rval = mmc_readl(host, REG_CLKCR); rval &= ~0xff; @@ -798,6 +801,9 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, } mmc_writel(host, REG_CLKCR, rval); + if (host->cfg->needs_new_timings) + mmc_writel(host, REG_SD_NTSR, SDXC_2X_TIMING_MODE); + ret = sunxi_mmc_clk_set_phase(host, ios, rate); if (ret) return ret; @@ -806,9 +812,22 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, if (ret) return ret; - /* TODO: enable calibrate on sdc2 SDXC_REG_DS_DL_REG of A64 */ + /* + * FIXME: + * + * In HS400 we'll also need to calibrate the data strobe + * signal. This should only happen on the MMC2 controller (at + * least on the A64). + */ + + ret = sunxi_mmc_oclk_onoff(host, 1); + if (ret) + return ret; + + /* And we just enabled our clock back */ + mmc->actual_clock = rate; - return sunxi_mmc_oclk_onoff(host, 1); + return 0; } static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) @@ -882,7 +901,7 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) mmc_writel(host, REG_GCTRL, rval); /* set up clock */ - if (ios->clock && ios->power_mode) { + if (ios->power_mode) { host->ferror = sunxi_mmc_clk_set_rate(host, ios); /* Android code had a usleep_range(50000, 55000); here */ } @@ -1089,6 +1108,14 @@ static const struct sunxi_mmc_cfg sun50i_a64_cfg = { .idma_des_size_bits = 16, .clk_delays = NULL, .can_calibrate = true, + .mask_data0 = true, + .needs_new_timings = true, +}; + +static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = { + .idma_des_size_bits = 13, + .clk_delays = NULL, + .can_calibrate = true, }; static const struct of_device_id sunxi_mmc_of_match[] = { @@ -1097,6 +1124,7 @@ static const struct of_device_id sunxi_mmc_of_match[] = { { .compatible = "allwinner,sun7i-a20-mmc", .data = &sun7i_a20_cfg }, { .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg }, { .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg }, + { .compatible = "allwinner,sun50i-a64-emmc", .data = &sun50i_a64_emmc_cfg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match); diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 9e20bcf3aa8d..2b349d48fb9a 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -24,6 +24,7 @@ #include <linux/pagemap.h> #include <linux/scatterlist.h> #include <linux/spinlock.h> +#include <linux/interrupt.h> #define CTL_SD_CMD 0x00 #define CTL_ARG_REG 0x04 @@ -90,6 +91,8 @@ #define TMIO_SDIO_STAT_EXWT 0x8000 #define TMIO_SDIO_MASK_ALL 0xc007 +#define TMIO_SDIO_SETBITS_MASK 0x0006 + /* Define some IRQ masks */ /* This is the mask used at reset by the chip */ #define TMIO_MASK_ALL 0x837f031d diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 2064fa1a5bf1..6b789a739d4d 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -134,18 +134,25 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) struct tmio_mmc_host *host = mmc_priv(mmc); if (enable && !host->sdio_irq_enabled) { + u16 sdio_status; + /* Keep device active while SDIO irq is enabled */ pm_runtime_get_sync(mmc_dev(mmc)); - host->sdio_irq_enabled = true; + host->sdio_irq_enabled = true; host->sdio_irq_mask = TMIO_SDIO_MASK_ALL & ~TMIO_SDIO_STAT_IOIRQ; - sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001); + + /* Clear obsolete interrupts before enabling */ + sdio_status = sd_ctrl_read16(host, CTL_SDIO_STATUS) & ~TMIO_SDIO_MASK_ALL; + if (host->pdata->flags & TMIO_MMC_SDIO_STATUS_SETBITS) + sdio_status |= TMIO_SDIO_SETBITS_MASK; + sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status); + sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); } else if (!enable && host->sdio_irq_enabled) { host->sdio_irq_mask = TMIO_SDIO_MASK_ALL; sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); - sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000); host->sdio_irq_enabled = false; pm_runtime_mark_last_busy(mmc_dev(mmc)); @@ -711,9 +718,8 @@ static bool __tmio_mmc_sdcard_irq(struct tmio_mmc_host *host, return false; } -static void tmio_mmc_sdio_irq(int irq, void *devid) +static void __tmio_mmc_sdio_irq(struct tmio_mmc_host *host) { - struct tmio_mmc_host *host = devid; struct mmc_host *mmc = host->mmc; struct tmio_mmc_data *pdata = host->pdata; unsigned int ireg, status; @@ -726,8 +732,8 @@ static void tmio_mmc_sdio_irq(int irq, void *devid) ireg = status & TMIO_SDIO_MASK_ALL & ~host->sdio_irq_mask; sdio_status = status & ~TMIO_SDIO_MASK_ALL; - if (pdata->flags & TMIO_MMC_SDIO_STATUS_QUIRK) - sdio_status |= 6; + if (pdata->flags & TMIO_MMC_SDIO_STATUS_SETBITS) + sdio_status |= TMIO_SDIO_SETBITS_MASK; sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status); @@ -754,7 +760,7 @@ irqreturn_t tmio_mmc_irq(int irq, void *devid) if (__tmio_mmc_sdcard_irq(host, ireg, status)) return IRQ_HANDLED; - tmio_mmc_sdio_irq(irq, devid); + __tmio_mmc_sdio_irq(host); return IRQ_HANDLED; } @@ -902,6 +908,12 @@ static int tmio_mmc_clk_enable(struct tmio_mmc_host *host) return host->clk_enable(host); } +static void tmio_mmc_clk_disable(struct tmio_mmc_host *host) +{ + if (host->clk_disable) + host->clk_disable(host); +} + static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd) { struct mmc_host *mmc = host->mmc; @@ -1145,7 +1157,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host, ret = mmc_of_parse(mmc); if (ret < 0) - goto host_free; + return ret; _host->pdata = pdata; platform_set_drvdata(pdev, mmc); @@ -1155,14 +1167,12 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host, ret = tmio_mmc_init_ocr(_host); if (ret < 0) - goto host_free; + return ret; _host->ctl = devm_ioremap(&pdev->dev, res_ctl->start, resource_size(res_ctl)); - if (!_host->ctl) { - ret = -ENOMEM; - goto host_free; - } + if (!_host->ctl) + return -ENOMEM; tmio_mmc_ops.card_busy = _host->card_busy; tmio_mmc_ops.start_signal_voltage_switch = _host->start_signal_voltage_switch; @@ -1179,8 +1189,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host, _host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD || mmc->caps & MMC_CAP_NEEDS_POLL || - !mmc_card_is_removable(mmc) || - mmc->slot.cd_irq >= 0); + !mmc_card_is_removable(mmc)); /* * On Gen2+, eMMC with NONREMOVABLE currently fails because native @@ -1200,10 +1209,8 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host, * Check the sanity of mmc->f_min to prevent tmio_mmc_set_clock() from * looping forever... */ - if (mmc->f_min == 0) { - ret = -EINVAL; - goto host_free; - } + if (mmc->f_min == 0) + return -EINVAL; /* * While using internal tmio hardware logic for card detection, we need @@ -1232,7 +1239,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host, if (pdata->flags & TMIO_MMC_SDIO_IRQ) { _host->sdio_irq_mask = TMIO_SDIO_MASK_ALL; sd_ctrl_write16(_host, CTL_SDIO_IRQ_MASK, _host->sdio_irq_mask); - sd_ctrl_write16(_host, CTL_TRANSACTION_CTL, 0x0000); + sd_ctrl_write16(_host, CTL_TRANSACTION_CTL, 0x0001); } spin_lock_init(&_host->lock); @@ -1268,10 +1275,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host, } return 0; - -host_free: - - return ret; } EXPORT_SYMBOL(tmio_mmc_host_probe); @@ -1280,6 +1283,9 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) struct platform_device *pdev = host->pdev; struct mmc_host *mmc = host->mmc; + if (host->pdata->flags & TMIO_MMC_SDIO_IRQ) + sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000); + if (!host->native_hotplug) pm_runtime_get_sync(&pdev->dev); @@ -1292,6 +1298,8 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + + tmio_mmc_clk_disable(host); } EXPORT_SYMBOL(tmio_mmc_host_remove); @@ -1306,8 +1314,7 @@ int tmio_mmc_host_runtime_suspend(struct device *dev) if (host->clk_cache) tmio_mmc_clk_stop(host); - if (host->clk_disable) - host->clk_disable(host); + tmio_mmc_clk_disable(host); return 0; } diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c index d2c386f09d69..1d843357422e 100644 --- a/drivers/mmc/host/ushc.c +++ b/drivers/mmc/host/ushc.c @@ -426,6 +426,9 @@ static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id struct ushc_data *ushc; int ret; + if (intf->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + mmc = mmc_alloc_host(sizeof(struct ushc_data), &intf->dev); if (mmc == NULL) return -ENOMEM; diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index 63fac78b3d46..6380044c0628 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -13,6 +13,7 @@ #include <linux/dma-mapping.h> #include <linux/highmem.h> #include <linux/delay.h> +#include <linux/interrupt.h> #include <linux/mmc/host.h> diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index bb3e0d1dd355..c061e7c704be 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -640,8 +640,6 @@ static void __vub300_irqpoll_response(struct vub300_mmc_host *vub300) mutex_lock(&vub300->irq_mutex); if (vub300->irq_enabled) mmc_signal_sdio_irq(vub300->mmc); - else if (vub300->irqs_queued) - vub300->irqs_queued += 1; else vub300->irqs_queued += 1; vub300->irq_disabled = 0; @@ -728,8 +726,7 @@ static void vub300_deadwork_thread(struct work_struct *work) */ } else if (vub300->card_present) { check_vub300_port_status(vub300); - } else if (vub300->mmc && vub300->mmc->card && - mmc_card_present(vub300->mmc->card)) { + } else if (vub300->mmc && vub300->mmc->card) { /* * the MMC core must not have responded * to the previous indication - lets @@ -1756,8 +1753,7 @@ static void vub300_cmndwork_thread(struct work_struct *work) int data_length; mutex_lock(&vub300->cmd_mutex); init_completion(&vub300->command_complete); - if (likely(vub300->vub_name[0]) || !vub300->mmc->card || - !mmc_card_present(vub300->mmc->card)) { + if (likely(vub300->vub_name[0]) || !vub300->mmc->card) { /* * the name of the EMPTY Pseudo firmware file * is used as a flag to indicate that the file diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index 80a3b11f3217..bd04e8bae010 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -1437,11 +1437,14 @@ err: static void wbsd_release_dma(struct wbsd_host *host) { - if (!dma_mapping_error(mmc_dev(host->mmc), host->dma_addr)) { + /* + * host->dma_addr is valid here iff host->dma_buffer is not NULL. + */ + if (host->dma_buffer) { dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); + kfree(host->dma_buffer); } - kfree(host->dma_buffer); if (host->dma >= 0) free_dma(host->dma); diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index 5af00559e9d6..21ebba88679c 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -20,6 +20,7 @@ #include <linux/irq.h> #include <linux/clk.h> #include <linux/gpio.h> +#include <linux/interrupt.h> #include <linux/of.h> #include <linux/of_address.h> |