aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-07-31 21:36:58 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-07-31 21:36:58 -0400
commit07f00f06ba9a5533d6650d46d3e938f6cbeee97e (patch)
tree854142fbef263efe8c5b3e9d7450cb44b7fb84c6 /drivers
parentMerge git://www.linux-watchdog.org/linux-watchdog (diff)
parentmmc: rtsx_pci: Remove deprecated create_singlethread_workqueue (diff)
downloadlinux-dev-07f00f06ba9a5533d6650d46d3e938f6cbeee97e.tar.xz
linux-dev-07f00f06ba9a5533d6650d46d3e938f6cbeee97e.zip
Merge tag 'mmc-v4.8' of git://git.linaro.org/people/ulf.hansson/mmc
Pull MMC updates from Ulf Hansson: "MMC core: - A couple of changes to improve the support for erase/discard/trim cmds - Add eMMC HS400 enhanced strobe support - Show OCR and DSR registers in SYSFS for MMC/SD cards - Correct and improve busy detection logic for MMC switch (CMD6) cmds - Disable HPI cmds for certain broken Hynix eMMC cards - Allow MMC hosts to specify non-support for SD and MMC cmds - Some minor additional fixes MMC host: - sdhci: Re-works, fixes and clean-ups - sdhci: Add HW auto re-tuning support - sdhci: Re-factor code to prepare for adding support for eMMC CMDQ - sdhci-esdhc-imx: Fixes and clean-ups - sdhci-esdhc-imx: Update system PM support - sdhci-esdhc-imx: Enable HW auto re-tuning - sdhci-bcm2835: Remove driver as sdhci-iproc is used instead - sdhci-brcmstb: Add new driver for Broadcom BRCMSTB SoCs - sdhci-msm: Add support for UHS cards - sdhci-tegra: Improve support for UHS cards - sdhci-of-arasan: Update phy support for Rockchip SoCs - sdhci-of-arasan: Deploy enhanced strobe support - dw_mmc: Some fixes and clean-ups - dw_mmc: Enable support for erase/discard/trim cmds - dw_mmc: Enable CMD23 support - mediatek: Some fixes related to the eMMC HS400 support - sh_mmcif: Improve support for HW busy detection - rtsx_pci: Enable support for erase/discard/trim cmds" * tag 'mmc-v4.8' of git://git.linaro.org/people/ulf.hansson/mmc: (135 commits) mmc: rtsx_pci: Remove deprecated create_singlethread_workqueue mmc: rtsx_pci: Enable MMC_CAP_ERASE to allow erase/discard/trim requests mmc: rtsx_pci: Use the provided busy timeout from the mmc core mmc: sdhci-pltfm: Drop define for SDHCI_PLTFM_PMOPS mmc: sdhci-pltfm: Convert to use the SET_SYSTEM_SLEEP_PM_OPS mmc: sdhci-pltfm: Make sdhci_pltfm_suspend|resume() static mmc: sdhci-esdhc-imx: Use common sdhci_suspend|resume_host() mmc: sdhci-esdhc-imx: Assign system PM ops within #ifdef CONFIG_PM_SLEEP mmc: sdhci-sirf: Remove non needed #ifdef CONFIG_PM* for dev_pm_ops mmc: sdhci-s3c: Remove non needed #ifdef CONFIG_PM for dev_pm_ops mmc: sdhci-pxav3: Remove non needed #ifdef CONFIG_PM for dev_pm_ops mmc: sdhci-of-esdhc: Simplify code by using SIMPLE_DEV_PM_OPS mmc: sdhci-acpi: Simplify code by using SET_SYSTEM_SLEEP_PM_OPS mmc: sdhci-pci-core: Simplify code by using SET_SYSTEM_SLEEP_PM_OPS mmc: Change the max discard sectors and erase response when HW busy detect phy: rockchip-emmc: Wait even longer for the DLL to lock phy: rockchip-emmc: Be tolerant to card clock of 0 in power on mmc: sdhci-of-arasan: Revert: Always power the PHY off/on when clock changes mmc: sdhci-msm: Add support for UHS cards mmc: sdhci-msm: Add set_uhs_signaling() implementation ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mmc/card/block.c13
-rw-r--r--drivers/mmc/core/bus.c3
-rw-r--r--drivers/mmc/core/core.c95
-rw-r--r--drivers/mmc/core/debugfs.c3
-rw-r--r--drivers/mmc/core/host.c8
-rw-r--r--drivers/mmc/core/mmc.c253
-rw-r--r--drivers/mmc/core/mmc_ops.c27
-rw-r--r--drivers/mmc/core/quirks.c2
-rw-r--r--drivers/mmc/core/sd.c19
-rw-r--r--drivers/mmc/host/Kconfig22
-rw-r--r--drivers/mmc/host/Makefile2
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c2
-rw-r--r--drivers/mmc/host/dw_mmc-k3.c7
-rw-r--r--drivers/mmc/host/dw_mmc-rockchip.c11
-rw-r--r--drivers/mmc/host/dw_mmc.c126
-rw-r--r--drivers/mmc/host/dw_mmc.h10
-rw-r--r--drivers/mmc/host/mtk-sd.c70
-rw-r--r--drivers/mmc/host/mxcmmc.c2
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c16
-rw-r--r--drivers/mmc/host/s3cmci.c2
-rw-r--r--drivers/mmc/host/s3cmci.h2
-rw-r--r--drivers/mmc/host/sdhci-acpi.c8
-rw-r--r--drivers/mmc/host/sdhci-bcm-kona.c8
-rw-r--r--drivers/mmc/host/sdhci-bcm2835.c204
-rw-r--r--drivers/mmc/host/sdhci-brcmstb.c143
-rw-r--r--drivers/mmc/host/sdhci-cns3xxx.c2
-rw-r--r--drivers/mmc/host/sdhci-dove.c2
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c153
-rw-r--r--drivers/mmc/host/sdhci-iproc.c15
-rw-r--r--drivers/mmc/host/sdhci-msm.c117
-rw-r--r--drivers/mmc/host/sdhci-of-arasan.c332
-rw-r--r--drivers/mmc/host/sdhci-of-at91.c2
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c16
-rw-r--r--drivers/mmc/host/sdhci-of-hlwd.c2
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c83
-rw-r--r--drivers/mmc/host/sdhci-pci.h2
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c13
-rw-r--r--drivers/mmc/host/sdhci-pltfm.h7
-rw-r--r--drivers/mmc/host/sdhci-pxav2.c2
-rw-r--r--drivers/mmc/host/sdhci-pxav3.c9
-rw-r--r--drivers/mmc/host/sdhci-s3c.c9
-rw-r--r--drivers/mmc/host/sdhci-sirf.c4
-rw-r--r--drivers/mmc/host/sdhci-st.c2
-rw-r--r--drivers/mmc/host/sdhci-tegra.c51
-rw-r--r--drivers/mmc/host/sdhci.c745
-rw-r--r--drivers/mmc/host/sdhci.h30
-rw-r--r--drivers/mmc/host/sdhci_f_sdh30.c2
-rw-r--r--drivers/mmc/host/sh_mmcif.c53
-rw-r--r--drivers/mmc/host/sh_mobile_sdhi.c14
-rw-r--r--drivers/mmc/host/tmio_mmc.h2
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c2
-rw-r--r--drivers/phy/phy-rockchip-emmc.c241
52 files changed, 1984 insertions, 986 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 10b553765ee7..48a5dd740f3b 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -1801,8 +1801,7 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
do_data_tag = (card->ext_csd.data_tag_unit_size) &&
(prq->cmd_flags & REQ_META) &&
(rq_data_dir(prq) == WRITE) &&
- ((brq->data.blocks * brq->data.blksz) >=
- card->ext_csd.data_tag_unit_size);
+ blk_rq_bytes(prq) >= card->ext_csd.data_tag_unit_size;
/* Argument of CMD23 */
packed_cmd_hdr[(i * 2)] = cpu_to_le32(
(do_rel_wr ? MMC_CMD23_ARG_REL_WR : 0) |
@@ -1977,8 +1976,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
* When 4KB native sector is enabled, only 8 blocks
* multiple read or write is allowed
*/
- if ((brq->data.blocks & 0x07) &&
- (card->ext_csd.data_sector_size == 4096)) {
+ if (mmc_large_sector(card) &&
+ !IS_ALIGNED(blk_rq_sectors(rqc), 8)) {
pr_err("%s: Transfer size is not 4KB sector size aligned\n",
req->rq_disk->disk_name);
mq_rq = mq->mqrq_cur;
@@ -2501,12 +2500,6 @@ force_ro_fail:
return ret;
}
-#define CID_MANFID_SANDISK 0x2
-#define CID_MANFID_TOSHIBA 0x11
-#define CID_MANFID_MICRON 0x13
-#define CID_MANFID_SAMSUNG 0x15
-#define CID_MANFID_KINGSTON 0x70
-
static const struct mmc_fixup blk_fixups[] =
{
MMC_FIXUP("SEM02G", CID_MANFID_SANDISK, 0x100, add_quirk,
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 4bc48f10452f..c64266f5a399 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -332,12 +332,13 @@ int mmc_add_card(struct mmc_card *card)
mmc_card_ddr52(card) ? "DDR " : "",
type);
} else {
- pr_info("%s: new %s%s%s%s%s card at address %04x\n",
+ pr_info("%s: new %s%s%s%s%s%s card at address %04x\n",
mmc_hostname(card->host),
mmc_card_uhs(card) ? "ultra high speed " :
(mmc_card_hs(card) ? "high speed " : ""),
mmc_card_hs400(card) ? "HS400 " :
(mmc_card_hs200(card) ? "HS200 " : ""),
+ mmc_card_hs400es(card) ? "Enhanced strobe " : "",
mmc_card_ddr52(card) ? "DDR " : "",
uhs_bus_speed_mode, type, card->rca);
}
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 8b4dfd45433b..e55cde6d436d 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1127,6 +1127,15 @@ void mmc_set_initial_state(struct mmc_host *host)
host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY;
host->ios.drv_type = 0;
+ host->ios.enhanced_strobe = false;
+
+ /*
+ * Make sure we are in non-enhanced strobe mode before we
+ * actually enable it in ext_csd.
+ */
+ if ((host->caps2 & MMC_CAP2_HS400_ES) &&
+ host->ops->hs400_enhanced_strobe)
+ host->ops->hs400_enhanced_strobe(host, &host->ios);
mmc_set_ios(host);
}
@@ -1925,17 +1934,15 @@ void mmc_init_erase(struct mmc_card *card)
* to that size and alignment.
*
* For SD cards that define Allocation Unit size, limit erases to one
- * Allocation Unit at a time. For MMC cards that define High Capacity
- * Erase Size, whether it is switched on or not, limit to that size.
- * Otherwise just have a stab at a good value. For modern cards it
- * will end up being 4MiB. Note that if the value is too small, it
- * can end up taking longer to erase.
+ * Allocation Unit at a time.
+ * For MMC, have a stab at ai good value and for modern cards it will
+ * end up being 4MiB. Note that if the value is too small, it can end
+ * up taking longer to erase. Also note, erase_size is already set to
+ * High Capacity Erase Size if available when this function is called.
*/
if (mmc_card_sd(card) && card->ssr.au) {
card->pref_erase = card->ssr.au;
card->erase_shift = ffs(card->ssr.au) - 1;
- } else if (card->ext_csd.hc_erase_size) {
- card->pref_erase = card->ext_csd.hc_erase_size;
} else if (card->erase_size) {
sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11;
if (sz < 128)
@@ -2060,7 +2067,8 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
unsigned int to, unsigned int arg)
{
struct mmc_command cmd = {0};
- unsigned int qty = 0;
+ unsigned int qty = 0, busy_timeout = 0;
+ bool use_r1b_resp = false;
unsigned long timeout;
int err;
@@ -2128,8 +2136,22 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_ERASE;
cmd.arg = arg;
- cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
- cmd.busy_timeout = mmc_erase_timeout(card, arg, qty);
+ busy_timeout = mmc_erase_timeout(card, arg, qty);
+ /*
+ * If the host controller supports busy signalling and the timeout for
+ * the erase operation does not exceed the max_busy_timeout, we should
+ * use R1B response. Or we need to prevent the host from doing hw busy
+ * detection, which is done by converting to a R1 response instead.
+ */
+ if (card->host->max_busy_timeout &&
+ busy_timeout > card->host->max_busy_timeout) {
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+ } else {
+ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ cmd.busy_timeout = busy_timeout;
+ use_r1b_resp = true;
+ }
+
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) {
pr_err("mmc_erase: erase error %d, status %#x\n",
@@ -2141,7 +2163,14 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
if (mmc_host_is_spi(card->host))
goto out;
- timeout = jiffies + msecs_to_jiffies(MMC_CORE_TIMEOUT_MS);
+ /*
+ * In case of when R1B + MMC_CAP_WAIT_WHILE_BUSY is used, the polling
+ * shall be avoided.
+ */
+ if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
+ goto out;
+
+ timeout = jiffies + msecs_to_jiffies(busy_timeout);
do {
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_STATUS;
@@ -2321,23 +2350,41 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,
unsigned int arg)
{
struct mmc_host *host = card->host;
- unsigned int max_discard, x, y, qty = 0, max_qty, timeout;
+ unsigned int max_discard, x, y, qty = 0, max_qty, min_qty, timeout;
unsigned int last_timeout = 0;
- if (card->erase_shift)
+ if (card->erase_shift) {
max_qty = UINT_MAX >> card->erase_shift;
- else if (mmc_card_sd(card))
+ min_qty = card->pref_erase >> card->erase_shift;
+ } else if (mmc_card_sd(card)) {
max_qty = UINT_MAX;
- else
+ min_qty = card->pref_erase;
+ } else {
max_qty = UINT_MAX / card->erase_size;
+ min_qty = card->pref_erase / card->erase_size;
+ }
- /* Find the largest qty with an OK timeout */
+ /*
+ * We should not only use 'host->max_busy_timeout' as the limitation
+ * when deciding the max discard sectors. We should set a balance value
+ * to improve the erase speed, and it can not get too long timeout at
+ * the same time.
+ *
+ * Here we set 'card->pref_erase' as the minimal discard sectors no
+ * matter what size of 'host->max_busy_timeout', but if the
+ * 'host->max_busy_timeout' is large enough for more discard sectors,
+ * then we can continue to increase the max discard sectors until we
+ * get a balance value.
+ */
do {
y = 0;
for (x = 1; x && x <= max_qty && max_qty - x >= qty; x <<= 1) {
timeout = mmc_erase_timeout(card, arg, qty + x);
- if (timeout > host->max_busy_timeout)
+
+ if (qty + x > min_qty &&
+ timeout > host->max_busy_timeout)
break;
+
if (timeout < last_timeout)
break;
last_timeout = timeout;
@@ -2491,17 +2538,21 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
mmc_go_idle(host);
- mmc_send_if_cond(host, host->ocr_avail);
+ if (!(host->caps2 & MMC_CAP2_NO_SD))
+ mmc_send_if_cond(host, host->ocr_avail);
/* Order's important: probe SDIO, then SD, then MMC */
if (!(host->caps2 & MMC_CAP2_NO_SDIO))
if (!mmc_attach_sdio(host))
return 0;
- if (!mmc_attach_sd(host))
- return 0;
- if (!mmc_attach_mmc(host))
- return 0;
+ if (!(host->caps2 & MMC_CAP2_NO_SD))
+ if (!mmc_attach_sd(host))
+ return 0;
+
+ if (!(host->caps2 & MMC_CAP2_NO_MMC))
+ if (!mmc_attach_mmc(host))
+ return 0;
mmc_power_off(host);
return -EIO;
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 9382a57a5aa4..c8451ce557ae 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -148,7 +148,8 @@ static int mmc_ios_show(struct seq_file *s, void *data)
str = "mmc HS200";
break;
case MMC_TIMING_MMC_HS400:
- str = "mmc HS400";
+ str = mmc_card_hs400es(host->card) ?
+ "mmc HS400 enhanced strobe" : "mmc HS400";
break;
default:
str = "invalid";
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 1be42fab1a30..98f25ffb4258 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -313,6 +313,14 @@ int mmc_of_parse(struct mmc_host *host)
host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR;
if (of_property_read_bool(np, "mmc-hs400-1_2v"))
host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
+ if (of_property_read_bool(np, "mmc-hs400-enhanced-strobe"))
+ host->caps2 |= MMC_CAP2_HS400_ES;
+ if (of_property_read_bool(np, "no-sdio"))
+ host->caps2 |= MMC_CAP2_NO_SDIO;
+ if (of_property_read_bool(np, "no-sd"))
+ host->caps2 |= MMC_CAP2_NO_SD;
+ if (of_property_read_bool(np, "no-mmc"))
+ host->caps2 |= MMC_CAP2_NO_MMC;
host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr);
if (host->dsr_req && (host->dsr & ~0xffff)) {
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 5d438ad3ee32..f2d185cf8a8b 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -45,6 +45,17 @@ static const unsigned int tacc_mant[] = {
35, 40, 45, 50, 55, 60, 70, 80,
};
+static const struct mmc_fixup mmc_ext_csd_fixups[] = {
+ /*
+ * Certain Hynix eMMC 4.41 cards might get broken when HPI feature
+ * is used so disable the HPI feature for such buggy cards.
+ */
+ MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_HYNIX,
+ 0x014a, add_quirk, MMC_QUIRK_BROKEN_HPI, 5),
+
+ END_FIXUP
+};
+
#define UNSTUFF_BITS(resp,start,size) \
({ \
const int __size = size; \
@@ -235,6 +246,11 @@ static void mmc_select_card_type(struct mmc_card *card)
avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V;
}
+ if ((caps2 & MMC_CAP2_HS400_ES) &&
+ card->ext_csd.strobe_support &&
+ (avail_type & EXT_CSD_CARD_TYPE_HS400))
+ avail_type |= EXT_CSD_CARD_TYPE_HS400ES;
+
card->ext_csd.hs_max_dtr = hs_max_dtr;
card->ext_csd.hs200_max_dtr = hs200_max_dtr;
card->mmc_avail_type = avail_type;
@@ -370,6 +386,9 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
*/
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
+ /* fixup device after ext_csd revision field is updated */
+ mmc_fixup_device(card, mmc_ext_csd_fixups);
+
card->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0];
card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1];
card->ext_csd.raw_sectors[2] = ext_csd[EXT_CSD_SEC_CNT + 2];
@@ -386,6 +405,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
mmc_card_set_blockaddr(card);
}
+ card->ext_csd.strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT];
card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
mmc_select_card_type(card);
@@ -500,7 +520,8 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
card->cid.year += 16;
/* check whether the eMMC card supports BKOPS */
- if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
+ if (!mmc_card_broken_hpi(card) &&
+ ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
card->ext_csd.bkops = 1;
card->ext_csd.man_bkops_en =
(ext_csd[EXT_CSD_BKOPS_EN] &
@@ -513,7 +534,8 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
}
/* check whether the eMMC card supports HPI */
- if (!broken_hpi && (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1)) {
+ if (!mmc_card_broken_hpi(card) &&
+ !broken_hpi && (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1)) {
card->ext_csd.hpi = 1;
if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2)
card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION;
@@ -727,6 +749,7 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
+MMC_DEV_ATTR(ocr, "%08x\n", card->ocr);
static ssize_t mmc_fwrev_show(struct device *dev,
struct device_attribute *attr,
@@ -744,6 +767,22 @@ static ssize_t mmc_fwrev_show(struct device *dev,
static DEVICE_ATTR(fwrev, S_IRUGO, mmc_fwrev_show, NULL);
+static ssize_t mmc_dsr_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mmc_card *card = mmc_dev_to_card(dev);
+ struct mmc_host *host = card->host;
+
+ if (card->csd.dsr_imp && host->dsr_req)
+ return sprintf(buf, "0x%x\n", host->dsr);
+ else
+ /* return default DSR value */
+ return sprintf(buf, "0x%x\n", 0x404);
+}
+
+static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL);
+
static struct attribute *mmc_std_attrs[] = {
&dev_attr_cid.attr,
&dev_attr_csd.attr,
@@ -762,6 +801,8 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_enhanced_area_size.attr,
&dev_attr_raw_rpmb_size_mult.attr,
&dev_attr_rel_sectors.attr,
+ &dev_attr_ocr.attr,
+ &dev_attr_dsr.attr,
NULL,
};
ATTRIBUTE_GROUPS(mmc_std);
@@ -959,6 +1000,19 @@ static int mmc_select_bus_width(struct mmc_card *card)
return err;
}
+/* Caller must hold re-tuning */
+static int mmc_switch_status(struct mmc_card *card)
+{
+ u32 status;
+ int err;
+
+ err = mmc_send_status(card, &status);
+ if (err)
+ return err;
+
+ return mmc_switch_status_error(card->host, status);
+}
+
/*
* Switch to the high-speed mode
*/
@@ -969,9 +1023,11 @@ static int mmc_select_hs(struct mmc_card *card)
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
card->ext_csd.generic_cmd6_time,
- true, true, true);
- if (!err)
+ true, false, true);
+ if (!err) {
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
+ err = mmc_switch_status(card);
+ }
return err;
}
@@ -1047,23 +1103,9 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
return err;
}
-/* Caller must hold re-tuning */
-static int mmc_switch_status(struct mmc_card *card)
-{
- u32 status;
- int err;
-
- err = mmc_send_status(card, &status);
- if (err)
- return err;
-
- return mmc_switch_status_error(card->host, status);
-}
-
static int mmc_select_hs400(struct mmc_card *card)
{
struct mmc_host *host = card->host;
- bool send_status = true;
unsigned int max_dtr;
int err = 0;
u8 val;
@@ -1075,19 +1117,12 @@ static int mmc_select_hs400(struct mmc_card *card)
host->ios.bus_width == MMC_BUS_WIDTH_8))
return 0;
- if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
- send_status = false;
-
- /* Reduce frequency to HS frequency */
- max_dtr = card->ext_csd.hs_max_dtr;
- mmc_set_clock(host, max_dtr);
-
/* Switch card to HS mode */
val = EXT_CSD_TIMING_HS;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time,
- true, send_status, true);
+ true, false, true);
if (err) {
pr_err("%s: switch to high-speed from hs200 failed, err:%d\n",
mmc_hostname(host), err);
@@ -1097,11 +1132,13 @@ static int mmc_select_hs400(struct mmc_card *card)
/* Set host controller to HS timing */
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
- if (!send_status) {
- err = mmc_switch_status(card);
- if (err)
- goto out_err;
- }
+ /* Reduce frequency to HS frequency */
+ max_dtr = card->ext_csd.hs_max_dtr;
+ mmc_set_clock(host, max_dtr);
+
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
/* Switch card to DDR */
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
@@ -1120,7 +1157,7 @@ static int mmc_select_hs400(struct mmc_card *card)
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time,
- true, send_status, true);
+ true, false, true);
if (err) {
pr_err("%s: switch to hs400 failed, err:%d\n",
mmc_hostname(host), err);
@@ -1131,11 +1168,9 @@ static int mmc_select_hs400(struct mmc_card *card)
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
mmc_set_bus_speed(card);
- if (!send_status) {
- err = mmc_switch_status(card);
- if (err)
- goto out_err;
- }
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
return 0;
@@ -1153,14 +1188,10 @@ int mmc_hs200_to_hs400(struct mmc_card *card)
int mmc_hs400_to_hs200(struct mmc_card *card)
{
struct mmc_host *host = card->host;
- bool send_status = true;
unsigned int max_dtr;
int err;
u8 val;
- if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
- send_status = false;
-
/* Reduce frequency to HS */
max_dtr = card->ext_csd.hs_max_dtr;
mmc_set_clock(host, max_dtr);
@@ -1169,49 +1200,43 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
val = EXT_CSD_TIMING_HS;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
val, card->ext_csd.generic_cmd6_time,
- true, send_status, true);
+ true, false, true);
if (err)
goto out_err;
mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
- if (!send_status) {
- err = mmc_switch_status(card);
- if (err)
- goto out_err;
- }
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
/* Switch HS DDR to HS */
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time,
- true, send_status, true);
+ true, false, true);
if (err)
goto out_err;
mmc_set_timing(host, MMC_TIMING_MMC_HS);
- if (!send_status) {
- err = mmc_switch_status(card);
- if (err)
- goto out_err;
- }
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
/* Switch HS to HS200 */
val = EXT_CSD_TIMING_HS200 |
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
- val, card->ext_csd.generic_cmd6_time, true,
- send_status, true);
+ val, card->ext_csd.generic_cmd6_time,
+ true, false, true);
if (err)
goto out_err;
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
- if (!send_status) {
- err = mmc_switch_status(card);
- if (err)
- goto out_err;
- }
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
mmc_set_bus_speed(card);
@@ -1223,6 +1248,78 @@ out_err:
return err;
}
+static int mmc_select_hs400es(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ int err = 0;
+ u8 val;
+
+ if (!(host->caps & MMC_CAP_8_BIT_DATA)) {
+ err = -ENOTSUPP;
+ goto out_err;
+ }
+
+ err = mmc_select_bus_width(card);
+ if (err < 0)
+ goto out_err;
+
+ /* Switch card to HS mode */
+ err = mmc_select_hs(card);
+ if (err) {
+ pr_err("%s: switch to high-speed failed, err:%d\n",
+ mmc_hostname(host), err);
+ goto out_err;
+ }
+
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
+
+ /* Switch card to DDR with strobe bit */
+ val = EXT_CSD_DDR_BUS_WIDTH_8 | EXT_CSD_BUS_WIDTH_STROBE;
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH,
+ val,
+ card->ext_csd.generic_cmd6_time);
+ if (err) {
+ pr_err("%s: switch to bus width for hs400es failed, err:%d\n",
+ mmc_hostname(host), err);
+ goto out_err;
+ }
+
+ /* Switch card to HS400 */
+ val = EXT_CSD_TIMING_HS400 |
+ card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, val,
+ card->ext_csd.generic_cmd6_time,
+ true, false, true);
+ if (err) {
+ pr_err("%s: switch to hs400es failed, err:%d\n",
+ mmc_hostname(host), err);
+ goto out_err;
+ }
+
+ /* Set host controller to HS400 timing and frequency */
+ mmc_set_timing(host, MMC_TIMING_MMC_HS400);
+
+ /* Controller enable enhanced strobe function */
+ host->ios.enhanced_strobe = true;
+ if (host->ops->hs400_enhanced_strobe)
+ host->ops->hs400_enhanced_strobe(host, &host->ios);
+
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
+ __func__, err);
+ return err;
+}
+
static void mmc_select_driver_type(struct mmc_card *card)
{
int card_drv_type, drive_strength, drv_type;
@@ -1250,7 +1347,6 @@ static void mmc_select_driver_type(struct mmc_card *card)
static int mmc_select_hs200(struct mmc_card *card)
{
struct mmc_host *host = card->host;
- bool send_status = true;
unsigned int old_timing, old_signal_voltage;
int err = -EINVAL;
u8 val;
@@ -1268,34 +1364,30 @@ static int mmc_select_hs200(struct mmc_card *card)
mmc_select_driver_type(card);
- if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
- send_status = false;
-
/*
* Set the bus width(4 or 8) with host's support and
* switch to HS200 mode if bus width is set successfully.
*/
err = mmc_select_bus_width(card);
- if (err >= 0) {
+ if (err > 0) {
val = EXT_CSD_TIMING_HS200 |
card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time,
- true, send_status, true);
+ true, false, true);
if (err)
goto err;
old_timing = host->ios.timing;
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
- if (!send_status) {
- err = mmc_switch_status(card);
- /*
- * mmc_select_timing() assumes timing has not changed if
- * it is a switch error.
- */
- if (err == -EBADMSG)
- mmc_set_timing(host, old_timing);
- }
+
+ err = mmc_switch_status(card);
+ /*
+ * mmc_select_timing() assumes timing has not changed if
+ * it is a switch error.
+ */
+ if (err == -EBADMSG)
+ mmc_set_timing(host, old_timing);
}
err:
if (err) {
@@ -1310,7 +1402,7 @@ err:
}
/*
- * Activate High Speed or HS200 mode if supported.
+ * Activate High Speed, HS200 or HS400ES mode if supported.
*/
static int mmc_select_timing(struct mmc_card *card)
{
@@ -1319,7 +1411,9 @@ static int mmc_select_timing(struct mmc_card *card)
if (!mmc_can_ext_csd(card))
goto bus_speed;
- if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
+ if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES)
+ err = mmc_select_hs400es(card);
+ else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
err = mmc_select_hs200(card);
else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS)
err = mmc_select_hs(card);
@@ -1583,7 +1677,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
} else if (mmc_card_hs(card)) {
/* Select the desired bus width optionally */
err = mmc_select_bus_width(card);
- if (err >= 0) {
+ if (err > 0) {
err = mmc_select_hs_ddr(card);
if (err)
goto free_card;
@@ -1616,7 +1710,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* If cache size is higher than 0, this indicates
* the existence of cache and it can be turned on.
*/
- if (card->ext_csd.cache_size > 0) {
+ if (!mmc_card_broken_hpi(card) &&
+ card->ext_csd.cache_size > 0) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_CACHE_CTRL, 1,
card->ext_csd.generic_cmd6_time);
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 62355bda608f..ad6e9798e949 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -480,6 +480,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
u32 status = 0;
bool use_r1b_resp = use_busy_signal;
bool expired = false;
+ bool busy = false;
mmc_retune_hold(host);
@@ -533,21 +534,26 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
timeout_ms = MMC_OPS_TIMEOUT_MS;
/* Must check status to be sure of no errors. */
- timeout = jiffies + msecs_to_jiffies(timeout_ms);
+ timeout = jiffies + msecs_to_jiffies(timeout_ms) + 1;
do {
+ /*
+ * Due to the possibility of being preempted after
+ * sending the status command, check the expiration
+ * time first.
+ */
+ expired = time_after(jiffies, timeout);
if (send_status) {
- /*
- * Due to the possibility of being preempted after
- * sending the status command, check the expiration
- * time first.
- */
- expired = time_after(jiffies, timeout);
err = __mmc_send_status(card, &status, ignore_crc);
if (err)
goto out;
}
if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
break;
+ if (host->ops->card_busy) {
+ if (!host->ops->card_busy(host))
+ break;
+ busy = true;
+ }
if (mmc_host_is_spi(host))
break;
@@ -556,19 +562,20 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
* does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only
* rely on waiting for the stated timeout to be sufficient.
*/
- if (!send_status) {
+ if (!send_status && !host->ops->card_busy) {
mmc_delay(timeout_ms);
goto out;
}
/* Timeout if the device never leaves the program state. */
- if (expired && R1_CURRENT_STATE(status) == R1_STATE_PRG) {
+ if (expired &&
+ (R1_CURRENT_STATE(status) == R1_STATE_PRG || busy)) {
pr_err("%s: Card stuck in programming state! %s\n",
mmc_hostname(host), __func__);
err = -ETIMEDOUT;
goto out;
}
- } while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
+ } while (R1_CURRENT_STATE(status) == R1_STATE_PRG || busy);
err = mmc_switch_status_error(host, status);
out:
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c
index fad660b95809..ca9cade317c7 100644
--- a/drivers/mmc/core/quirks.c
+++ b/drivers/mmc/core/quirks.c
@@ -72,6 +72,8 @@ void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table)
f->cis_vendor == (u16) SDIO_ANY_ID) &&
(f->cis_device == card->cis.device ||
f->cis_device == (u16) SDIO_ANY_ID) &&
+ (f->ext_csd_rev == EXT_CSD_REV_ANY ||
+ f->ext_csd_rev == card->ext_csd.rev) &&
rev >= f->rev_start && rev <= f->rev_end) {
dev_dbg(&card->dev, "calling %pf\n", f->vendor_fixup);
f->vendor_fixup(card, f->data);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index b95bd24d92f4..0123936241b0 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -675,8 +675,25 @@ MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
+MMC_DEV_ATTR(ocr, "%08x\n", card->ocr);
+static ssize_t mmc_dsr_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mmc_card *card = mmc_dev_to_card(dev);
+ struct mmc_host *host = card->host;
+
+ if (card->csd.dsr_imp && host->dsr_req)
+ return sprintf(buf, "0x%x\n", host->dsr);
+ else
+ /* return default DSR value */
+ return sprintf(buf, "0x%x\n", 0x404);
+}
+
+static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL);
+
static struct attribute *sd_std_attrs[] = {
&dev_attr_cid.attr,
&dev_attr_csd.attr,
@@ -690,6 +707,8 @@ static struct attribute *sd_std_attrs[] = {
&dev_attr_name.attr,
&dev_attr_oemid.attr,
&dev_attr_serial.attr,
+ &dev_attr_ocr.attr,
+ &dev_attr_dsr.attr,
NULL,
};
ATTRIBUTE_GROUPS(sd_std);
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 0aa484c10c0a..5274f503a39a 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -122,6 +122,7 @@ config MMC_SDHCI_OF_ARASAN
tristate "SDHCI OF support for the Arasan SDHCI controllers"
depends on MMC_SDHCI_PLTFM
depends on OF
+ depends on COMMON_CLK
help
This selects the Arasan Secure Digital Host Controller Interface
(SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC.
@@ -296,17 +297,6 @@ config MMC_SDHCI_BCM_KONA
If you have a controller with this interface, say Y or M here.
-config MMC_SDHCI_BCM2835
- tristate "SDHCI platform support for the BCM2835 SD/MMC Controller"
- depends on ARCH_BCM2835
- depends on MMC_SDHCI_PLTFM
- select MMC_SDHCI_IO_ACCESSORS
- help
- This selects the BCM2835 SD/MMC controller. If you have a BCM2835
- platform with SD or MMC devices, say Y or M here.
-
- If unsure, say N.
-
config MMC_SDHCI_F_SDH30
tristate "SDHCI support for Fujitsu Semiconductor F_SDH30"
depends on MMC_SDHCI_PLTFM
@@ -798,3 +788,13 @@ config MMC_SDHCI_MICROCHIP_PIC32
If you have a controller with this interface, say Y or M here.
If unsure, say N.
+config MMC_SDHCI_BRCMSTB
+ tristate "Broadcom SDIO/SD/MMC support"
+ depends on ARCH_BRCMSTB || BMIPS_GENERIC
+ depends on MMC_SDHCI_PLTFM
+ default y
+ help
+ This selects support for the SDIO/SD/MMC Host Controller on
+ Broadcom STB SoCs.
+
+ If unsure, say Y.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index af918d261ff9..e2bdaaf43184 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -71,11 +71,11 @@ obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
-obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o
+obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o
ifeq ($(CONFIG_CB710_DEBUG),y)
CFLAGS-cb710-mmc += -DDEBUG
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index 7e3a3247b852..da0ef1765735 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -157,7 +157,7 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
* HOLD register should be bypassed in case there is no phase shift
* applied on CMD/DATA that is sent to the card.
*/
- if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel))
+ if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel) && host->cur_slot)
set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->cur_slot->flags);
}
diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c
index 63c2e2ed1288..8e9d886bfcda 100644
--- a/drivers/mmc/host/dw_mmc-k3.c
+++ b/drivers/mmc/host/dw_mmc-k3.c
@@ -32,6 +32,12 @@ struct k3_priv {
struct regmap *reg;
};
+static unsigned long dw_mci_hi6220_caps[] = {
+ MMC_CAP_CMD23,
+ MMC_CAP_CMD23,
+ 0
+};
+
static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{
int ret;
@@ -126,6 +132,7 @@ static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios)
}
static const struct dw_mci_drv_data hi6220_data = {
+ .caps = dw_mci_hi6220_caps,
.switch_voltage = dw_mci_hi6220_switch_voltage,
.set_ios = dw_mci_hi6220_set_ios,
.parse_dt = dw_mci_hi6220_parse_dt,
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index 358b0dc853b0..25eae359a5ea 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -285,9 +285,6 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
/* It is slot 8 on Rockchip SoCs */
host->sdio_id0 = 8;
- /* It needs this quirk on all Rockchip SoCs */
- host->pdata->quirks |= DW_MCI_QUIRK_BROKEN_DTO;
-
if (of_device_is_compatible(host->dev->of_node,
"rockchip,rk3288-dw-mshc"))
host->bus_hz /= RK3288_CLKGEN_DIV;
@@ -297,10 +294,10 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
/* Common capabilities of RK3288 SoC */
static unsigned long dw_mci_rk3288_dwmmc_caps[4] = {
- MMC_CAP_ERASE | MMC_CAP_CMD23,
- MMC_CAP_ERASE | MMC_CAP_CMD23,
- MMC_CAP_ERASE | MMC_CAP_CMD23,
- MMC_CAP_ERASE | MMC_CAP_CMD23,
+ MMC_CAP_CMD23,
+ MMC_CAP_CMD23,
+ MMC_CAP_CMD23,
+ MMC_CAP_CMD23,
};
static const struct dw_mci_drv_data rk2928_drv_data = {
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 2cc6123b1df9..32380d5d4f6b 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -44,11 +44,11 @@
/* Common flag combinations */
#define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \
SDMMC_INT_HTO | SDMMC_INT_SBE | \
- SDMMC_INT_EBE)
+ SDMMC_INT_EBE | SDMMC_INT_HLE)
#define DW_MCI_CMD_ERROR_FLAGS (SDMMC_INT_RTO | SDMMC_INT_RCRC | \
- SDMMC_INT_RESP_ERR)
+ SDMMC_INT_RESP_ERR | SDMMC_INT_HLE)
#define DW_MCI_ERROR_FLAGS (DW_MCI_DATA_ERROR_FLAGS | \
- DW_MCI_CMD_ERROR_FLAGS | SDMMC_INT_HLE)
+ DW_MCI_CMD_ERROR_FLAGS)
#define DW_MCI_SEND_STATUS 1
#define DW_MCI_RECV_STATUS 2
#define DW_MCI_DMA_THRESHOLD 16
@@ -92,7 +92,7 @@ struct idmac_desc {
__le32 des1; /* Buffer sizes */
#define IDMAC_SET_BUFFER1_SIZE(d, s) \
- ((d)->des1 = ((d)->des1 & 0x03ffe000) | ((s) & 0x1fff))
+ ((d)->des1 = ((d)->des1 & cpu_to_le32(0x03ffe000)) | (cpu_to_le32((s) & 0x1fff)))
__le32 des2; /* buffer 1 physical address */
@@ -105,6 +105,7 @@ struct idmac_desc {
static bool dw_mci_reset(struct dw_mci *host);
static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset);
static int dw_mci_card_busy(struct mmc_host *mmc);
+static int dw_mci_get_cd(struct mmc_host *mmc);
#if defined(CONFIG_DEBUG_FS)
static int dw_mci_req_show(struct seq_file *s, void *v)
@@ -898,23 +899,35 @@ done:
mci_writel(host, FIFOTH, fifoth_val);
}
-static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data)
+static void dw_mci_ctrl_thld(struct dw_mci *host, struct mmc_data *data)
{
unsigned int blksz = data->blksz;
u32 blksz_depth, fifo_depth;
u16 thld_size;
-
- WARN_ON(!(data->flags & MMC_DATA_READ));
+ u8 enable;
/*
* CDTHRCTL doesn't exist prior to 240A (in fact that register offset is
* in the FIFO region, so we really shouldn't access it).
*/
- if (host->verid < DW_MMC_240A)
+ if (host->verid < DW_MMC_240A ||
+ (host->verid < DW_MMC_280A && data->flags & MMC_DATA_WRITE))
+ return;
+
+ /*
+ * Card write Threshold is introduced since 2.80a
+ * It's used when HS400 mode is enabled.
+ */
+ if (data->flags & MMC_DATA_WRITE &&
+ !(host->timing != MMC_TIMING_MMC_HS400))
return;
+ if (data->flags & MMC_DATA_WRITE)
+ enable = SDMMC_CARD_WR_THR_EN;
+ else
+ enable = SDMMC_CARD_RD_THR_EN;
+
if (host->timing != MMC_TIMING_MMC_HS200 &&
- host->timing != MMC_TIMING_MMC_HS400 &&
host->timing != MMC_TIMING_UHS_SDR104)
goto disable;
@@ -930,11 +943,11 @@ static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data)
* Currently just choose blksz.
*/
thld_size = blksz;
- mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(thld_size, 1));
+ mci_writel(host, CDTHRCTL, SDMMC_SET_THLD(thld_size, enable));
return;
disable:
- mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(0, 0));
+ mci_writel(host, CDTHRCTL, 0);
}
static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
@@ -1005,12 +1018,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
host->sg = NULL;
host->data = data;
- if (data->flags & MMC_DATA_READ) {
+ if (data->flags & MMC_DATA_READ)
host->dir_status = DW_MCI_RECV_STATUS;
- dw_mci_ctrl_rd_thld(host, data);
- } else {
+ else
host->dir_status = DW_MCI_SEND_STATUS;
- }
+
+ dw_mci_ctrl_thld(host, data);
if (dw_mci_submit_data_dma(host, data)) {
if (host->data->flags & MMC_DATA_READ)
@@ -1099,12 +1112,11 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0;
- if ((clock << div) != slot->__clk_old || 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);
+ 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);
/* disable clock */
mci_writel(host, CLKENA, 0);
@@ -1127,9 +1139,6 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
/* inform CIU */
mci_send_cmd(slot, sdmmc_cmd_bits, 0);
-
- /* keep the clock with reflecting clock dividor */
- slot->__clk_old = clock << div;
}
host->current_speed = clock;
@@ -1253,15 +1262,15 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
* atomic, otherwise the card could be removed in between and the
* request wouldn't fail until another card was inserted.
*/
- spin_lock_bh(&host->lock);
- if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) {
- spin_unlock_bh(&host->lock);
+ if (!dw_mci_get_cd(mmc)) {
mrq->cmd->error = -ENOMEDIUM;
mmc_request_done(mmc, mrq);
return;
}
+ spin_lock_bh(&host->lock);
+
dw_mci_queue_request(host, slot, mrq);
spin_unlock_bh(&host->lock);
@@ -1451,8 +1460,7 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
int gpio_cd = mmc_gpio_get_cd(mmc);
/* Use platform get_cd function, else try onboard card detect */
- if ((mmc->caps & MMC_CAP_NEEDS_POLL) ||
- (mmc->caps & MMC_CAP_NONREMOVABLE))
+ if ((mmc->caps & MMC_CAP_NEEDS_POLL) || !mmc_card_is_removable(mmc))
present = 1;
else if (gpio_cd >= 0)
present = gpio_cd;
@@ -1761,6 +1769,33 @@ static void dw_mci_tasklet_func(unsigned long priv)
}
if (cmd->data && err) {
+ /*
+ * During UHS tuning sequence, sending the stop
+ * command after the response CRC error would
+ * throw the system into a confused state
+ * causing all future tuning phases to report
+ * failure.
+ *
+ * In such case controller will move into a data
+ * transfer state after a response error or
+ * response CRC error. Let's let that finish
+ * before trying to send a stop, so we'll go to
+ * STATE_SENDING_DATA.
+ *
+ * Although letting the data transfer take place
+ * will waste a bit of time (we already know
+ * the command was bad), it can't cause any
+ * errors since it's possible it would have
+ * taken place anyway if this tasklet got
+ * delayed. Allowing the transfer to take place
+ * avoids races and keeps things simple.
+ */
+ if ((err != -ETIMEDOUT) &&
+ (cmd->opcode == MMC_SEND_TUNING_BLOCK)) {
+ state = STATE_SENDING_DATA;
+ continue;
+ }
+
dw_mci_stop_dma(host);
send_stop_abort(host, data);
state = STATE_SENDING_STOP;
@@ -1801,8 +1836,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
* If all data-related interrupts don't come
* within the given time in reading data state.
*/
- if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) &&
- (host->dir_status == DW_MCI_RECV_STATUS))
+ if (host->dir_status == DW_MCI_RECV_STATUS)
dw_mci_set_drto(host);
break;
}
@@ -1844,8 +1878,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
* interrupt doesn't come within the given time.
* in reading data state.
*/
- if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) &&
- (host->dir_status == DW_MCI_RECV_STATUS))
+ if (host->dir_status == DW_MCI_RECV_STATUS)
dw_mci_set_drto(host);
break;
}
@@ -2411,8 +2444,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
}
if (pending & SDMMC_INT_DATA_OVER) {
- if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO)
- del_timer(&host->dto_timer);
+ del_timer(&host->dto_timer);
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
if (!host->data_status)
@@ -2474,7 +2506,8 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_TI |
SDMMC_IDMAC_INT_RI);
mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI);
- host->dma_ops->complete((void *)host);
+ if (!test_bit(EVENT_DATA_ERROR, &host->pending_events))
+ host->dma_ops->complete((void *)host);
}
} else {
pending = mci_readl(host, IDSTS);
@@ -2482,7 +2515,8 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
SDMMC_IDMAC_INT_RI);
mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
- host->dma_ops->complete((void *)host);
+ if (!test_bit(EVENT_DATA_ERROR, &host->pending_events))
+ host->dma_ops->complete((void *)host);
}
}
@@ -2570,6 +2604,12 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
if (host->pdata->caps)
mmc->caps = host->pdata->caps;
+ /*
+ * Support MMC_CAP_ERASE by default.
+ * It needs to use trim/discard/erase commands.
+ */
+ mmc->caps |= MMC_CAP_ERASE;
+
if (host->pdata->pm_caps)
mmc->pm_caps = host->pdata->pm_caps;
@@ -2616,10 +2656,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
mmc->max_seg_size = mmc->max_req_size;
}
- if (dw_mci_get_cd(mmc))
- set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
- else
- clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
+ dw_mci_get_cd(mmc);
ret = mmc_add_host(mmc);
if (ret)
@@ -3006,11 +3043,8 @@ int dw_mci_probe(struct dw_mci *host)
setup_timer(&host->cmd11_timer,
dw_mci_cmd11_timer, (unsigned long)host);
- host->quirks = host->pdata->quirks;
-
- if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO)
- setup_timer(&host->dto_timer,
- dw_mci_dto_timer, (unsigned long)host);
+ setup_timer(&host->dto_timer,
+ dw_mci_dto_timer, (unsigned long)host);
spin_lock_init(&host->lock);
spin_lock_init(&host->irq_lock);
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 1e8d8380f9cf..9e740bc232a8 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -15,6 +15,7 @@
#define _DW_MMC_H_
#define DW_MMC_240A 0x240a
+#define DW_MMC_280A 0x280a
#define SDMMC_CTRL 0x000
#define SDMMC_PWREN 0x004
@@ -175,7 +176,10 @@
/* Version ID register define */
#define SDMMC_GET_VERID(x) ((x) & 0xFFFF)
/* Card read threshold */
-#define SDMMC_SET_RD_THLD(v, x) (((v) & 0xFFF) << 16 | (x))
+#define SDMMC_SET_THLD(v, x) (((v) & 0xFFF) << 16 | (x))
+#define SDMMC_CARD_WR_THR_EN BIT(2)
+#define SDMMC_CARD_RD_THR_EN BIT(0)
+/* UHS-1 register defines */
#define SDMMC_UHS_18V BIT(0)
/* All ctrl reset bits */
#define SDMMC_CTRL_ALL_RESET_FLAGS \
@@ -245,9 +249,6 @@ extern int dw_mci_resume(struct dw_mci *host);
* @queue_node: List node for placing this node in the @queue list of
* &struct dw_mci.
* @clock: Clock rate configured by set_ios(). Protected by host->lock.
- * @__clk_old: The last updated clock with reflecting clock divider.
- * Keeping track of this helps us to avoid spamming the console
- * with CONFIG_MMC_CLKGATE.
* @flags: Random state bits associated with the slot.
* @id: Number of this slot.
* @sdio_id: Number of this slot in the SDIO interrupt registers.
@@ -262,7 +263,6 @@ struct dw_mci_slot {
struct list_head queue_node;
unsigned int clock;
- unsigned int __clk_old;
unsigned long flags;
#define DW_MMC_CARD_PRESENT 0
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 5642f71f8bf0..84e9afcb5c09 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -287,6 +287,11 @@ struct msdc_save_para {
u32 emmc50_cfg0;
};
+struct msdc_tune_para {
+ u32 iocon;
+ u32 pad_tune;
+};
+
struct msdc_delay_phase {
u8 maxlen;
u8 start;
@@ -326,7 +331,10 @@ struct msdc_host {
unsigned char timing;
bool vqmmc_enabled;
u32 hs400_ds_delay;
+ bool hs400_mode; /* current eMMC will run at hs400 mode */
struct msdc_save_para save_para; /* used when gate HCLK */
+ struct msdc_tune_para def_tune_para; /* default tune setting */
+ struct msdc_tune_para saved_tune_para; /* tune result of CMD21/CMD19 */
};
static void sdr_set_bits(void __iomem *reg, u32 bs)
@@ -582,6 +590,18 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
msdc_set_timeout(host, host->timeout_ns, host->timeout_clks);
sdr_set_bits(host->base + MSDC_INTEN, flags);
+ /*
+ * mmc_select_hs400() will drop to 50Mhz and High speed mode,
+ * tune result of hs200/200Mhz is not suitable for 50Mhz
+ */
+ if (host->sclk <= 52000000) {
+ writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
+ writel(host->def_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
+ } else {
+ writel(host->saved_tune_para.iocon, host->base + MSDC_IOCON);
+ writel(host->saved_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
+ }
+
dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing);
}
@@ -781,7 +801,13 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
}
if (!sbc_error && !(events & MSDC_INT_CMDRDY)) {
- msdc_reset_hw(host);
+ if (cmd->opcode != MMC_SEND_TUNING_BLOCK &&
+ cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200)
+ /*
+ * should not clear fifo/interrupt as the tune data
+ * may have alreay come.
+ */
+ msdc_reset_hw(host);
if (events & MSDC_INT_RSPCRCERR) {
cmd->error = -EILSEQ;
host->error |= REQ_CMD_EIO;
@@ -865,7 +891,11 @@ static void msdc_start_command(struct msdc_host *host,
static void msdc_cmd_next(struct msdc_host *host,
struct mmc_request *mrq, struct mmc_command *cmd)
{
- if (cmd->error || (mrq->sbc && mrq->sbc->error))
+ if ((cmd->error &&
+ !(cmd->error == -EILSEQ &&
+ (cmd->opcode == MMC_SEND_TUNING_BLOCK ||
+ cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))) ||
+ (mrq->sbc && mrq->sbc->error))
msdc_request_done(host, mrq);
else if (cmd == mrq->sbc)
msdc_start_command(host, mrq, mrq->cmd);
@@ -1158,6 +1188,8 @@ static void msdc_init_hw(struct msdc_host *host)
/* Configure to default data timeout */
sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, 3);
+ host->def_tune_para.iocon = readl(host->base + MSDC_IOCON);
+ host->def_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
dev_dbg(host->dev, "init hardware done!");
}
@@ -1296,7 +1328,7 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
{
struct msdc_host *host = mmc_priv(mmc);
u32 rise_delay = 0, fall_delay = 0;
- struct msdc_delay_phase final_rise_delay, final_fall_delay;
+ struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
u8 final_delay, final_maxlen;
int cmd_err;
int i;
@@ -1309,6 +1341,11 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
if (!cmd_err)
rise_delay |= (1 << i);
}
+ final_rise_delay = get_best_delay(host, rise_delay);
+ /* if rising edge has enough margin, then do not scan falling edge */
+ if (final_rise_delay.maxlen >= 10 ||
+ (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
+ goto skip_fall;
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
for (i = 0; i < PAD_DELAY_MAX; i++) {
@@ -1318,10 +1355,9 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
if (!cmd_err)
fall_delay |= (1 << i);
}
-
- final_rise_delay = get_best_delay(host, rise_delay);
final_fall_delay = get_best_delay(host, fall_delay);
+skip_fall:
final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
if (final_maxlen == final_rise_delay.maxlen) {
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
@@ -1342,7 +1378,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
{
struct msdc_host *host = mmc_priv(mmc);
u32 rise_delay = 0, fall_delay = 0;
- struct msdc_delay_phase final_rise_delay, final_fall_delay;
+ struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
u8 final_delay, final_maxlen;
int i, ret;
@@ -1355,6 +1391,11 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
if (!ret)
rise_delay |= (1 << i);
}
+ final_rise_delay = get_best_delay(host, rise_delay);
+ /* if rising edge has enough margin, then do not scan falling edge */
+ if (final_rise_delay.maxlen >= 10 ||
+ (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
+ goto skip_fall;
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
@@ -1365,14 +1406,10 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
if (!ret)
fall_delay |= (1 << i);
}
-
- final_rise_delay = get_best_delay(host, rise_delay);
final_fall_delay = get_best_delay(host, fall_delay);
+skip_fall:
final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
- /* Rising edge is more stable, prefer to use it */
- if (final_rise_delay.maxlen >= 10)
- final_maxlen = final_rise_delay.maxlen;
if (final_maxlen == final_rise_delay.maxlen) {
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
@@ -1402,16 +1439,21 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
dev_err(host->dev, "Tune response fail!\n");
return ret;
}
- ret = msdc_tune_data(mmc, opcode);
- if (ret == -EIO)
- dev_err(host->dev, "Tune data fail!\n");
+ if (host->hs400_mode == false) {
+ ret = msdc_tune_data(mmc, opcode);
+ if (ret == -EIO)
+ dev_err(host->dev, "Tune data fail!\n");
+ }
+ host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON);
+ host->saved_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
return ret;
}
static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct msdc_host *host = mmc_priv(mmc);
+ host->hs400_mode = true;
writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE);
return 0;
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index 3d1ea5e0e549..fb3ca8296273 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -1065,7 +1065,7 @@ static int mxcmci_probe(struct platform_device *pdev)
if (pdata)
dat3_card_detect = pdata->dat3_card_detect;
- else if (!(mmc->caps & MMC_CAP_NONREMOVABLE)
+ else if (mmc_card_is_removable(mmc)
&& !of_property_read_bool(pdev->dev.of_node, "cd-gpios"))
dat3_card_detect = true;
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index 93137483ecde..396c9b7e4121 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -38,7 +38,6 @@ struct realtek_pci_sdmmc {
struct rtsx_pcr *pcr;
struct mmc_host *mmc;
struct mmc_request *mrq;
- struct workqueue_struct *workq;
#define SDMMC_WORKQ_NAME "rtsx_pci_sdmmc_workq"
struct work_struct work;
@@ -244,7 +243,7 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
stat_idx = sd_status_index(rsp_type);
if (rsp_type == SD_RSP_TYPE_R1b)
- timeout = 3000;
+ timeout = cmd->busy_timeout ? cmd->busy_timeout : 3000;
if (cmd->opcode == SD_SWITCH_VOLTAGE) {
err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
@@ -885,7 +884,7 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
if (sd_rw_cmd(mrq->cmd) || sdio_extblock_cmd(mrq->cmd, data))
host->using_cookie = sd_pre_dma_transfer(host, data, false);
- queue_work(host->workq, &host->work);
+ schedule_work(&host->work);
}
static int sd_set_bus_width(struct realtek_pci_sdmmc *host,
@@ -1360,7 +1359,7 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host)
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
- MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
+ MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_ERASE;
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
mmc->max_current_330 = 400;
mmc->max_current_180 = 800;
@@ -1404,11 +1403,6 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
return -ENOMEM;
host = mmc_priv(mmc);
- host->workq = create_singlethread_workqueue(SDMMC_WORKQ_NAME);
- if (!host->workq) {
- mmc_free_host(mmc);
- return -ENOMEM;
- }
host->pcr = pcr;
host->mmc = mmc;
host->pdev = pdev;
@@ -1462,9 +1456,7 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
mmc_remove_host(mmc);
host->eject = true;
- flush_workqueue(host->workq);
- destroy_workqueue(host->workq);
- host->workq = NULL;
+ flush_work(&host->work);
mmc_free_host(mmc);
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 39814f3dc96f..c531deef3258 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -1365,7 +1365,7 @@ static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
.no_detect = 1,
};
-#ifdef CONFIG_CPU_FREQ
+#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
static int s3cmci_cpufreq_transition(struct notifier_block *nb,
unsigned long val, void *data)
diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h
index cc2e46cb5c64..30c2c0dd1bc8 100644
--- a/drivers/mmc/host/s3cmci.h
+++ b/drivers/mmc/host/s3cmci.h
@@ -74,7 +74,7 @@ struct s3cmci_host {
struct dentry *debug_regs;
#endif
-#ifdef CONFIG_CPU_FREQ
+#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
struct notifier_block freq_transition;
#endif
};
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
index 008709c5cb09..8fe0756c8e1e 100644
--- a/drivers/mmc/host/sdhci-acpi.c
+++ b/drivers/mmc/host/sdhci-acpi.c
@@ -532,11 +532,6 @@ static int sdhci_acpi_resume(struct device *dev)
return sdhci_resume_host(c->host);
}
-#else
-
-#define sdhci_acpi_suspend NULL
-#define sdhci_acpi_resume NULL
-
#endif
#ifdef CONFIG_PM
@@ -560,8 +555,7 @@ static int sdhci_acpi_runtime_resume(struct device *dev)
#endif
static const struct dev_pm_ops sdhci_acpi_pm_ops = {
- .suspend = sdhci_acpi_suspend,
- .resume = sdhci_acpi_resume,
+ SET_SYSTEM_SLEEP_PM_OPS(sdhci_acpi_suspend, sdhci_acpi_resume)
SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend,
sdhci_acpi_runtime_resume, NULL)
};
diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c
index 00a8a40a3729..e5c634bdfdd9 100644
--- a/drivers/mmc/host/sdhci-bcm-kona.c
+++ b/drivers/mmc/host/sdhci-bcm-kona.c
@@ -264,12 +264,12 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
}
dev_dbg(dev, "non-removable=%c\n",
- (host->mmc->caps & MMC_CAP_NONREMOVABLE) ? 'Y' : 'N');
+ mmc_card_is_removable(host->mmc) ? 'N' : 'Y');
dev_dbg(dev, "cd_gpio %c, wp_gpio %c\n",
(mmc_gpio_get_cd(host->mmc) != -ENOSYS) ? 'Y' : 'N',
(mmc_gpio_get_ro(host->mmc) != -ENOSYS) ? 'Y' : 'N');
- if (host->mmc->caps & MMC_CAP_NONREMOVABLE)
+ if (!mmc_card_is_removable(host->mmc))
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
dev_dbg(dev, "is_8bit=%c\n",
@@ -288,7 +288,7 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
}
/* if device is eMMC, emulate card insert right here */
- if (host->mmc->caps & MMC_CAP_NONREMOVABLE) {
+ if (!mmc_card_is_removable(host->mmc)) {
ret = sdhci_bcm_kona_sd_card_emulate(host, 1);
if (ret) {
dev_err(dev,
@@ -326,7 +326,7 @@ err_pltfm_free:
static struct platform_driver sdhci_bcm_kona_driver = {
.driver = {
.name = "sdhci-kona",
- .pm = SDHCI_PLTFM_PMOPS,
+ .pm = &sdhci_pltfm_pmops,
.of_match_table = sdhci_bcm_kona_of_match,
},
.probe = sdhci_bcm_kona_probe,
diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c
deleted file mode 100644
index 4a6a1d1386cb..000000000000
--- a/drivers/mmc/host/sdhci-bcm2835.c
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * BCM2835 SDHCI
- * Copyright (C) 2012 Stephen Warren
- * Based on U-Boot's MMC driver for the BCM2835 by Oleksandr Tymoshenko & me
- * Portions of the code there were obviously based on the Linux kernel at:
- * git://github.com/raspberrypi/linux.git rpi-3.6.y
- * commit f5b930b "Main bcm2708 linux port" signed-off-by Dom Cobley.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/mmc/host.h>
-#include "sdhci-pltfm.h"
-
-/*
- * 400KHz is max freq for card ID etc. Use that as min card clock. We need to
- * know the min to enable static calculation of max BCM2835_SDHCI_WRITE_DELAY.
- */
-#define MIN_FREQ 400000
-
-/*
- * The Arasan has a bugette whereby it may lose the content of successive
- * writes to registers that are within two SD-card clock cycles of each other
- * (a clock domain crossing problem). It seems, however, that the data
- * register does not have this problem, which is just as well - otherwise we'd
- * have to nobble the DMA engine too.
- *
- * This should probably be dynamically calculated based on the actual card
- * frequency. However, this is the longest we'll have to wait, and doesn't
- * seem to slow access down too much, so the added complexity doesn't seem
- * worth it for now.
- *
- * 1/MIN_FREQ is (max) time per tick of eMMC clock.
- * 2/MIN_FREQ is time for two ticks.
- * Multiply by 1000000 to get uS per two ticks.
- * *1000000 for uSecs.
- * +1 for hack rounding.
- */
-#define BCM2835_SDHCI_WRITE_DELAY (((2 * 1000000) / MIN_FREQ) + 1)
-
-struct bcm2835_sdhci {
- u32 shadow;
-};
-
-static void bcm2835_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
-{
- writel(val, host->ioaddr + reg);
-
- udelay(BCM2835_SDHCI_WRITE_DELAY);
-}
-
-static inline u32 bcm2835_sdhci_readl(struct sdhci_host *host, int reg)
-{
- u32 val = readl(host->ioaddr + reg);
-
- if (reg == SDHCI_CAPABILITIES)
- val |= SDHCI_CAN_VDD_330;
-
- return val;
-}
-
-static void bcm2835_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
-{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct bcm2835_sdhci *bcm2835_host = sdhci_pltfm_priv(pltfm_host);
- u32 oldval = (reg == SDHCI_COMMAND) ? bcm2835_host->shadow :
- bcm2835_sdhci_readl(host, reg & ~3);
- u32 word_num = (reg >> 1) & 1;
- u32 word_shift = word_num * 16;
- u32 mask = 0xffff << word_shift;
- u32 newval = (oldval & ~mask) | (val << word_shift);
-
- if (reg == SDHCI_TRANSFER_MODE)
- bcm2835_host->shadow = newval;
- else
- bcm2835_sdhci_writel(host, newval, reg & ~3);
-}
-
-static u16 bcm2835_sdhci_readw(struct sdhci_host *host, int reg)
-{
- u32 val = bcm2835_sdhci_readl(host, (reg & ~3));
- u32 word_num = (reg >> 1) & 1;
- u32 word_shift = word_num * 16;
- u32 word = (val >> word_shift) & 0xffff;
-
- return word;
-}
-
-static void bcm2835_sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
-{
- u32 oldval = bcm2835_sdhci_readl(host, reg & ~3);
- u32 byte_num = reg & 3;
- u32 byte_shift = byte_num * 8;
- u32 mask = 0xff << byte_shift;
- u32 newval = (oldval & ~mask) | (val << byte_shift);
-
- bcm2835_sdhci_writel(host, newval, reg & ~3);
-}
-
-static u8 bcm2835_sdhci_readb(struct sdhci_host *host, int reg)
-{
- u32 val = bcm2835_sdhci_readl(host, (reg & ~3));
- u32 byte_num = reg & 3;
- u32 byte_shift = byte_num * 8;
- u32 byte = (val >> byte_shift) & 0xff;
-
- return byte;
-}
-
-static unsigned int bcm2835_sdhci_get_min_clock(struct sdhci_host *host)
-{
- return MIN_FREQ;
-}
-
-static const struct sdhci_ops bcm2835_sdhci_ops = {
- .write_l = bcm2835_sdhci_writel,
- .write_w = bcm2835_sdhci_writew,
- .write_b = bcm2835_sdhci_writeb,
- .read_l = bcm2835_sdhci_readl,
- .read_w = bcm2835_sdhci_readw,
- .read_b = bcm2835_sdhci_readb,
- .set_clock = sdhci_set_clock,
- .get_max_clock = sdhci_pltfm_clk_get_max_clock,
- .get_min_clock = bcm2835_sdhci_get_min_clock,
- .set_bus_width = sdhci_set_bus_width,
- .reset = sdhci_reset,
- .set_uhs_signaling = sdhci_set_uhs_signaling,
-};
-
-static const struct sdhci_pltfm_data bcm2835_sdhci_pdata = {
- .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
- SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
- .ops = &bcm2835_sdhci_ops,
-};
-
-static int bcm2835_sdhci_probe(struct platform_device *pdev)
-{
- struct sdhci_host *host;
- struct bcm2835_sdhci *bcm2835_host;
- struct sdhci_pltfm_host *pltfm_host;
- int ret;
-
- host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata,
- sizeof(*bcm2835_host));
- if (IS_ERR(host))
- return PTR_ERR(host);
-
- pltfm_host = sdhci_priv(host);
-
- pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(pltfm_host->clk)) {
- ret = PTR_ERR(pltfm_host->clk);
- goto err;
- }
- ret = clk_prepare_enable(pltfm_host->clk);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable host clk\n");
- goto err;
- }
-
- ret = sdhci_add_host(host);
- if (ret)
- goto err_clk;
-
- return 0;
-err_clk:
- clk_disable_unprepare(pltfm_host->clk);
-err:
- sdhci_pltfm_free(pdev);
- return ret;
-}
-
-static const struct of_device_id bcm2835_sdhci_of_match[] = {
- { .compatible = "brcm,bcm2835-sdhci" },
- { }
-};
-MODULE_DEVICE_TABLE(of, bcm2835_sdhci_of_match);
-
-static struct platform_driver bcm2835_sdhci_driver = {
- .driver = {
- .name = "sdhci-bcm2835",
- .of_match_table = bcm2835_sdhci_of_match,
- .pm = SDHCI_PLTFM_PMOPS,
- },
- .probe = bcm2835_sdhci_probe,
- .remove = sdhci_pltfm_unregister,
-};
-module_platform_driver(bcm2835_sdhci_driver);
-
-MODULE_DESCRIPTION("BCM2835 SDHCI driver");
-MODULE_AUTHOR("Stephen Warren");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c
new file mode 100644
index 000000000000..cce10fe3e19e
--- /dev/null
+++ b/drivers/mmc/host/sdhci-brcmstb.c
@@ -0,0 +1,143 @@
+/*
+ * sdhci-brcmstb.c Support for SDHCI on Broadcom BRCMSTB SoC's
+ *
+ * Copyright (C) 2015 Broadcom Corporation
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/mmc/host.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include "sdhci-pltfm.h"
+
+#ifdef CONFIG_PM_SLEEP
+
+static int sdhci_brcmstb_suspend(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ int res;
+
+ res = sdhci_suspend_host(host);
+ if (res)
+ return res;
+ clk_disable_unprepare(pltfm_host->clk);
+ return res;
+}
+
+static int sdhci_brcmstb_resume(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ int err;
+
+ err = clk_prepare_enable(pltfm_host->clk);
+ if (err)
+ return err;
+ return sdhci_resume_host(host);
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(sdhci_brcmstb_pmops, sdhci_brcmstb_suspend,
+ sdhci_brcmstb_resume);
+
+static const struct sdhci_ops sdhci_brcmstb_ops = {
+ .set_clock = sdhci_set_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_reset,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+};
+
+static struct sdhci_pltfm_data sdhci_brcmstb_pdata = {
+ .ops = &sdhci_brcmstb_ops,
+};
+
+static int sdhci_brcmstb_probe(struct platform_device *pdev)
+{
+ struct sdhci_host *host;
+ struct sdhci_pltfm_host *pltfm_host;
+ struct clk *clk;
+ int res;
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Clock not found in Device Tree\n");
+ clk = NULL;
+ }
+ res = clk_prepare_enable(clk);
+ if (res)
+ return res;
+
+ host = sdhci_pltfm_init(pdev, &sdhci_brcmstb_pdata, 0);
+ if (IS_ERR(host)) {
+ res = PTR_ERR(host);
+ goto err_clk;
+ }
+
+ /* Enable MMC_CAP2_HC_ERASE_SZ for better max discard calculations */
+ host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
+
+ sdhci_get_of_property(pdev);
+ mmc_of_parse(host->mmc);
+
+ /*
+ * Supply the existing CAPS, but clear the UHS modes. This
+ * will allow these modes to be specified by device tree
+ * properties through mmc_of_parse().
+ */
+ host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
+ host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
+ host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
+ SDHCI_SUPPORT_DDR50);
+ host->quirks |= SDHCI_QUIRK_MISSING_CAPS |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
+
+ res = sdhci_add_host(host);
+ if (res)
+ goto err;
+
+ pltfm_host = sdhci_priv(host);
+ pltfm_host->clk = clk;
+ return res;
+
+err:
+ sdhci_pltfm_free(pdev);
+err_clk:
+ clk_disable_unprepare(clk);
+ return res;
+}
+
+static const struct of_device_id sdhci_brcm_of_match[] = {
+ { .compatible = "brcm,bcm7425-sdhci" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match);
+
+static struct platform_driver sdhci_brcmstb_driver = {
+ .driver = {
+ .name = "sdhci-brcmstb",
+ .owner = THIS_MODULE,
+ .pm = &sdhci_brcmstb_pmops,
+ .of_match_table = of_match_ptr(sdhci_brcm_of_match),
+ },
+ .probe = sdhci_brcmstb_probe,
+ .remove = sdhci_pltfm_unregister,
+};
+
+module_platform_driver(sdhci_brcmstb_driver);
+
+MODULE_DESCRIPTION("SDHCI driver for Broadcom BRCMSTB SoCs");
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c
index 59f2923f8054..bd286db7f9af 100644
--- a/drivers/mmc/host/sdhci-cns3xxx.c
+++ b/drivers/mmc/host/sdhci-cns3xxx.c
@@ -101,7 +101,7 @@ static int sdhci_cns3xxx_probe(struct platform_device *pdev)
static struct platform_driver sdhci_cns3xxx_driver = {
.driver = {
.name = "sdhci-cns3xxx",
- .pm = SDHCI_PLTFM_PMOPS,
+ .pm = &sdhci_pltfm_pmops,
},
.probe = sdhci_cns3xxx_probe,
.remove = sdhci_pltfm_unregister,
diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c
index 407c21f152b2..de9f9603dbdc 100644
--- a/drivers/mmc/host/sdhci-dove.c
+++ b/drivers/mmc/host/sdhci-dove.c
@@ -117,7 +117,7 @@ MODULE_DEVICE_TABLE(of, sdhci_dove_of_match_table);
static struct platform_driver sdhci_dove_driver = {
.driver = {
.name = "sdhci-dove",
- .pm = SDHCI_PLTFM_PMOPS,
+ .pm = &sdhci_pltfm_pmops,
.of_match_table = sdhci_dove_of_match_table,
},
.probe = sdhci_dove_probe,
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 9d3ae1f4bd3c..99e0b334f9df 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -39,11 +39,13 @@
#define ESDHC_VENDOR_SPEC_VSELECT (1 << 1)
#define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8)
#define ESDHC_WTMK_LVL 0x44
+#define ESDHC_WTMK_DEFAULT_VAL 0x10401040
#define ESDHC_MIX_CTRL 0x48
#define ESDHC_MIX_CTRL_DDREN (1 << 3)
#define ESDHC_MIX_CTRL_AC23EN (1 << 7)
#define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22)
#define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23)
+#define ESDHC_MIX_CTRL_AUTO_TUNE_EN (1 << 24)
#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
#define ESDHC_MIX_CTRL_HS400_EN (1 << 26)
/* Bits 3 and 6 are not SDHCI standard definitions */
@@ -75,7 +77,8 @@
#define ESDHC_TUNING_CTRL 0xcc
#define ESDHC_STD_TUNING_EN (1 << 24)
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
-#define ESDHC_TUNING_START_TAP 0x1
+#define ESDHC_TUNING_START_TAP_DEFAULT 0x1
+#define ESDHC_TUNING_START_TAP_MASK 0xff
#define ESDHC_TUNING_STEP_MASK 0x00070000
#define ESDHC_TUNING_STEP_SHIFT 16
@@ -299,7 +302,8 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
/* imx6q/dl does not have cap_1 register, fake one */
val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
| SDHCI_SUPPORT_SDR50
- | SDHCI_USE_SDR50_TUNING;
+ | SDHCI_USE_SDR50_TUNING
+ | (SDHCI_TUNING_MODE_3 << SDHCI_RETUNING_MODE_SHIFT);
if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
val |= SDHCI_SUPPORT_HS400;
@@ -469,32 +473,29 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
- if (val & SDHCI_CTRL_TUNED_CLK)
+ if (val & SDHCI_CTRL_TUNED_CLK) {
new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL;
- else
+ new_val |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
+ } else {
new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
+ new_val &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
+ }
writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
- u32 tuning_ctrl;
if (val & SDHCI_CTRL_TUNED_CLK) {
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
} else {
v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
+ m &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
}
if (val & SDHCI_CTRL_EXEC_TUNING) {
v |= ESDHC_MIX_CTRL_EXE_TUNE;
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
- tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL);
- tuning_ctrl |= ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP;
- if (imx_data->boarddata.tuning_step) {
- tuning_ctrl &= ~ESDHC_TUNING_STEP_MASK;
- tuning_ctrl |= imx_data->boarddata.tuning_step << ESDHC_TUNING_STEP_SHIFT;
- }
- writel(tuning_ctrl, host->ioaddr + ESDHC_TUNING_CTRL);
+ m |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
} else {
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
}
@@ -751,6 +752,7 @@ static void esdhc_post_tuning(struct sdhci_host *host)
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
+ reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
}
@@ -838,6 +840,11 @@ static void esdhc_set_strobe_dll(struct sdhci_host *host)
u32 v;
if (host->mmc->actual_clock > ESDHC_STROBE_DLL_CLK_FREQ) {
+ /* disable clock before enabling strobe dll */
+ writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) &
+ ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
+ host->ioaddr + ESDHC_VENDOR_SPEC);
+
/* force a reset on strobe dll */
writel(ESDHC_STROBE_DLL_CTRL_RESET,
host->ioaddr + ESDHC_STROBE_DLL_CTRL);
@@ -899,6 +906,8 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN;
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
imx_data->is_ddr = 1;
+ /* update clock after enable DDR for strobe DLL lock */
+ host->ops->set_clock(host, host->clock);
esdhc_set_strobe_dll(host);
break;
}
@@ -957,6 +966,62 @@ static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
.ops = &sdhci_esdhc_ops,
};
+static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
+ int tmp;
+
+ if (esdhc_is_usdhc(imx_data)) {
+ /*
+ * The imx6q ROM code will change the default watermark
+ * level setting to something insane. Change it back here.
+ */
+ writel(ESDHC_WTMK_DEFAULT_VAL, host->ioaddr + ESDHC_WTMK_LVL);
+
+ /*
+ * ROM code will change the bit burst_length_enable setting
+ * to zero if this usdhc is choosed to boot system. Change
+ * it back here, otherwise it will impact the performance a
+ * lot. This bit is used to enable/disable the burst length
+ * for the external AHB2AXI bridge, it's usefully especially
+ * for INCR transfer because without burst length indicator,
+ * the AHB2AXI bridge does not know the burst length in
+ * advance. And without burst length indicator, AHB INCR
+ * transfer can only be converted to singles on the AXI side.
+ */
+ writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
+ | ESDHC_BURST_LEN_EN_INCR,
+ host->ioaddr + SDHCI_HOST_CONTROL);
+ /*
+ * errata ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
+ * TO1.1, it's harmless for MX6SL
+ */
+ writel(readl(host->ioaddr + 0x6c) | BIT(7),
+ host->ioaddr + 0x6c);
+
+ /* disable DLL_CTRL delay line settings */
+ writel(0x0, host->ioaddr + ESDHC_DLL_CTRL);
+
+ if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
+ tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
+ tmp |= ESDHC_STD_TUNING_EN |
+ ESDHC_TUNING_START_TAP_DEFAULT;
+ if (imx_data->boarddata.tuning_start_tap) {
+ tmp &= ~ESDHC_TUNING_START_TAP_MASK;
+ tmp |= imx_data->boarddata.tuning_start_tap;
+ }
+
+ if (imx_data->boarddata.tuning_step) {
+ tmp &= ~ESDHC_TUNING_STEP_MASK;
+ tmp |= imx_data->boarddata.tuning_step
+ << ESDHC_TUNING_STEP_SHIFT;
+ }
+ writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
+ }
+ }
+}
+
#ifdef CONFIG_OF
static int
sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
@@ -975,6 +1040,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
boarddata->wp_type = ESDHC_WP_GPIO;
of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
+ of_property_read_u32(np, "fsl,tuning-start-tap",
+ &boarddata->tuning_start_tap);
if (of_find_property(np, "no-1-8-v", NULL))
boarddata->support_vsel = false;
@@ -1147,58 +1214,27 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
if (IS_ERR(imx_data->pins_default))
dev_warn(mmc_dev(host->mmc), "could not get default state\n");
- host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
-
if (imx_data->socdata->flags & ESDHC_FLAG_ENGCM07207)
/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK
| SDHCI_QUIRK_BROKEN_ADMA;
- /*
- * The imx6q ROM code will change the default watermark level setting
- * to something insane. Change it back here.
- */
if (esdhc_is_usdhc(imx_data)) {
- writel(0x10401040, host->ioaddr + ESDHC_WTMK_LVL);
-
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
host->mmc->caps |= MMC_CAP_1_8V_DDR;
-
- /*
- * ROM code will change the bit burst_length_enable setting
- * to zero if this usdhc is choosed to boot system. Change
- * it back here, otherwise it will impact the performance a
- * lot. This bit is used to enable/disable the burst length
- * for the external AHB2AXI bridge, it's usefully especially
- * for INCR transfer because without burst length indicator,
- * the AHB2AXI bridge does not know the burst length in
- * advance. And without burst length indicator, AHB INCR
- * transfer can only be converted to singles on the AXI side.
- */
- writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
- | ESDHC_BURST_LEN_EN_INCR,
- host->ioaddr + SDHCI_HOST_CONTROL);
-
if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
- /*
- * errata ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
- * TO1.1, it's harmless for MX6SL
- */
- writel(readl(host->ioaddr + 0x6c) | BIT(7),
- host->ioaddr + 0x6c);
+ /* clear tuning bits in case ROM has set it already */
+ writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
+ writel(0x0, host->ioaddr + SDHCI_ACMD12_ERR);
+ writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
}
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
sdhci_esdhc_ops.platform_execute_tuning =
esdhc_executing_tuning;
- if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
- writel(readl(host->ioaddr + ESDHC_TUNING_CTRL) |
- ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP,
- host->ioaddr + ESDHC_TUNING_CTRL);
-
if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
@@ -1212,6 +1248,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
if (err)
goto disable_clk;
+ sdhci_esdhc_imx_hwinit(host);
+
err = sdhci_add_host(host);
if (err)
goto disable_clk;
@@ -1255,6 +1293,25 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int sdhci_esdhc_suspend(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+
+ return sdhci_suspend_host(host);
+}
+
+static int sdhci_esdhc_resume(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+
+ /* re-initialize hw state in case it's lost in low power mode */
+ sdhci_esdhc_imx_hwinit(host);
+
+ return sdhci_resume_host(host);
+}
+#endif
+
#ifdef CONFIG_PM
static int sdhci_esdhc_runtime_suspend(struct device *dev)
{
@@ -1291,7 +1348,7 @@ static int sdhci_esdhc_runtime_resume(struct device *dev)
#endif
static const struct dev_pm_ops sdhci_esdhc_pmops = {
- SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume)
+ SET_SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume)
SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
sdhci_esdhc_runtime_resume, NULL)
};
diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index 1110f73b08aa..726246665850 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -164,8 +164,17 @@ static const struct sdhci_pltfm_data sdhci_iproc_pltfm_data = {
static const struct sdhci_iproc_data iproc_data = {
.pdata = &sdhci_iproc_pltfm_data,
- .caps = 0x05E90000,
- .caps1 = 0x00000064,
+ .caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT)
+ & SDHCI_MAX_BLOCK_MASK) |
+ SDHCI_CAN_VDD_330 |
+ SDHCI_CAN_VDD_180 |
+ SDHCI_CAN_DO_SUSPEND |
+ SDHCI_CAN_DO_HISPD |
+ SDHCI_CAN_DO_ADMA2 |
+ SDHCI_CAN_DO_SDMA,
+ .caps1 = SDHCI_DRIVER_TYPE_C |
+ SDHCI_DRIVER_TYPE_D |
+ SDHCI_SUPPORT_DDR50,
.mmc_caps = MMC_CAP_1_8V_DDR,
};
@@ -251,7 +260,7 @@ static struct platform_driver sdhci_iproc_driver = {
.driver = {
.name = "sdhci-iproc",
.of_match_table = sdhci_iproc_of_match,
- .pm = SDHCI_PLTFM_PMOPS,
+ .pm = &sdhci_pltfm_pmops,
},
.probe = sdhci_iproc_probe,
.remove = sdhci_pltfm_unregister,
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 0653fe730150..8ef44a2a2fd9 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -32,6 +32,21 @@
#define CORE_POWER 0x0
#define CORE_SW_RST BIT(7)
+#define CORE_PWRCTL_STATUS 0xdc
+#define CORE_PWRCTL_MASK 0xe0
+#define CORE_PWRCTL_CLEAR 0xe4
+#define CORE_PWRCTL_CTL 0xe8
+#define CORE_PWRCTL_BUS_OFF BIT(0)
+#define CORE_PWRCTL_BUS_ON BIT(1)
+#define CORE_PWRCTL_IO_LOW BIT(2)
+#define CORE_PWRCTL_IO_HIGH BIT(3)
+#define CORE_PWRCTL_BUS_SUCCESS BIT(0)
+#define CORE_PWRCTL_IO_SUCCESS BIT(2)
+#define REQ_BUS_OFF BIT(0)
+#define REQ_BUS_ON BIT(1)
+#define REQ_IO_LOW BIT(2)
+#define REQ_IO_HIGH BIT(3)
+#define INT_MASK 0xf
#define MAX_PHASES 16
#define CORE_DLL_LOCK BIT(7)
#define CORE_DLL_EN BIT(16)
@@ -56,6 +71,7 @@
struct sdhci_msm_host {
struct platform_device *pdev;
void __iomem *core_mem; /* MSM SDCC mapped address */
+ int pwr_irq; /* power irq */
struct clk *clk; /* main SD/MMC bus clock */
struct clk *pclk; /* SDHC peripheral bus clock */
struct clk *bus_clk; /* SDHC bus voter clock */
@@ -410,6 +426,85 @@ retry:
return rc;
}
+static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
+ unsigned int uhs)
+{
+ struct mmc_host *mmc = host->mmc;
+ u16 ctrl_2;
+
+ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ /* Select Bus Speed Mode for host */
+ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+ switch (uhs) {
+ case MMC_TIMING_UHS_SDR12:
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
+ break;
+ case MMC_TIMING_UHS_SDR25:
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+ break;
+ case MMC_TIMING_MMC_HS200:
+ case MMC_TIMING_UHS_SDR104:
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+ break;
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+ break;
+ }
+
+ /*
+ * When clock frequency is less than 100MHz, the feedback clock must be
+ * provided and DLL must not be used so that tuning can be skipped. To
+ * provide feedback clock, the mode selection can be any value less
+ * than 3'b011 in bits [2:0] of HOST CONTROL2 register.
+ */
+ if (host->clock <= 100000000 &&
+ (uhs == MMC_TIMING_MMC_HS400 ||
+ uhs == MMC_TIMING_MMC_HS200 ||
+ uhs == MMC_TIMING_UHS_SDR104))
+ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+
+ dev_dbg(mmc_dev(mmc), "%s: clock=%u uhs=%u ctrl_2=0x%x\n",
+ mmc_hostname(host->mmc), host->clock, uhs, ctrl_2);
+ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+}
+
+static void sdhci_msm_voltage_switch(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 irq_status, irq_ack = 0;
+
+ irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
+ irq_status &= INT_MASK;
+
+ writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR);
+
+ if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF))
+ irq_ack |= CORE_PWRCTL_BUS_SUCCESS;
+ if (irq_status & (CORE_PWRCTL_IO_LOW | CORE_PWRCTL_IO_HIGH))
+ irq_ack |= CORE_PWRCTL_IO_SUCCESS;
+
+ /*
+ * The driver has to acknowledge the interrupt, switch voltages and
+ * report back if it succeded or not to this register. The voltage
+ * switches are handled by the sdhci core, so just report success.
+ */
+ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL);
+}
+
+static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
+{
+ struct sdhci_host *host = (struct sdhci_host *)data;
+
+ sdhci_msm_voltage_switch(host);
+
+ return IRQ_HANDLED;
+}
+
static const struct of_device_id sdhci_msm_dt_match[] = {
{ .compatible = "qcom,sdhci-msm-v4" },
{},
@@ -422,11 +517,13 @@ static const struct sdhci_ops sdhci_msm_ops = {
.reset = sdhci_reset,
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
- .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .set_uhs_signaling = sdhci_msm_set_uhs_signaling,
+ .voltage_switch = sdhci_msm_voltage_switch,
};
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+ SDHCI_QUIRK_NO_CARD_NO_RESET |
SDHCI_QUIRK_SINGLE_POWER_WRITE,
.ops = &sdhci_msm_ops,
};
@@ -473,7 +570,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
msm_host->pclk = devm_clk_get(&pdev->dev, "iface");
if (IS_ERR(msm_host->pclk)) {
ret = PTR_ERR(msm_host->pclk);
- dev_err(&pdev->dev, "Perpheral clk setup failed (%d)\n", ret);
+ dev_err(&pdev->dev, "Peripheral clk setup failed (%d)\n", ret);
goto bus_clk_disable;
}
@@ -545,6 +642,22 @@ static int sdhci_msm_probe(struct platform_device *pdev)
CORE_VENDOR_SPEC_CAPABILITIES0);
}
+ /* Setup IRQ for handling power/voltage tasks with PMIC */
+ msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
+ if (msm_host->pwr_irq < 0) {
+ dev_err(&pdev->dev, "Get pwr_irq failed (%d)\n",
+ msm_host->pwr_irq);
+ goto clk_disable;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL,
+ sdhci_msm_pwr_irq, IRQF_ONESHOT,
+ dev_name(&pdev->dev), host);
+ if (ret) {
+ dev_err(&pdev->dev, "Request IRQ failed (%d)\n", ret);
+ goto clk_disable;
+ }
+
ret = sdhci_add_host(host);
if (ret)
goto clk_disable;
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index b6f4c1d41636..e0f193f7e3e5 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -19,27 +19,136 @@
* your option) any later version.
*/
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
+#include <linux/regmap.h>
#include "sdhci-pltfm.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
+/*
+ * On some SoCs the syscon area has a feature where the upper 16-bits of
+ * each 32-bit register act as a write mask for the lower 16-bits. This allows
+ * atomic updates of the register without locking. This macro is used on SoCs
+ * that have that feature.
+ */
+#define HIWORD_UPDATE(val, mask, shift) \
+ ((val) << (shift) | (mask) << ((shift) + 16))
+
+/**
+ * struct sdhci_arasan_soc_ctl_field - Field used in sdhci_arasan_soc_ctl_map
+ *
+ * @reg: Offset within the syscon of the register containing this field
+ * @width: Number of bits for this field
+ * @shift: Bit offset within @reg of this field (or -1 if not avail)
+ */
+struct sdhci_arasan_soc_ctl_field {
+ u32 reg;
+ u16 width;
+ s16 shift;
+};
+
+/**
+ * struct sdhci_arasan_soc_ctl_map - Map in syscon to corecfg registers
+ *
+ * It's up to the licensee of the Arsan IP block to make these available
+ * somewhere if needed. Presumably these will be scattered somewhere that's
+ * accessible via the syscon API.
+ *
+ * @baseclkfreq: Where to find corecfg_baseclkfreq
+ * @hiword_update: If true, use HIWORD_UPDATE to access the syscon
+ */
+struct sdhci_arasan_soc_ctl_map {
+ struct sdhci_arasan_soc_ctl_field baseclkfreq;
+ bool hiword_update;
+};
+
/**
* struct sdhci_arasan_data
- * @clk_ahb: Pointer to the AHB clock
- * @phy: Pointer to the generic phy
+ * @host: Pointer to the main SDHCI host structure.
+ * @clk_ahb: Pointer to the AHB clock
+ * @phy: Pointer to the generic phy
+ * @sdcardclk_hw: Struct for the clock we might provide to a PHY.
+ * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw.
+ * @soc_ctl_base: Pointer to regmap for syscon for soc_ctl registers.
+ * @soc_ctl_map: Map to get offsets into soc_ctl registers.
*/
struct sdhci_arasan_data {
+ struct sdhci_host *host;
struct clk *clk_ahb;
struct phy *phy;
+
+ struct clk_hw sdcardclk_hw;
+ struct clk *sdcardclk;
+
+ struct regmap *soc_ctl_base;
+ const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
+};
+
+static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = {
+ .baseclkfreq = { .reg = 0xf000, .width = 8, .shift = 8 },
+ .hiword_update = true,
};
+/**
+ * sdhci_arasan_syscon_write - Write to a field in soc_ctl registers
+ *
+ * This function allows writing to fields in sdhci_arasan_soc_ctl_map.
+ * Note that if a field is specified as not available (shift < 0) then
+ * this function will silently return an error code. It will be noisy
+ * and print errors for any other (unexpected) errors.
+ *
+ * @host: The sdhci_host
+ * @fld: The field to write to
+ * @val: The value to write
+ */
+static int sdhci_arasan_syscon_write(struct sdhci_host *host,
+ const struct sdhci_arasan_soc_ctl_field *fld,
+ u32 val)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ struct regmap *soc_ctl_base = sdhci_arasan->soc_ctl_base;
+ u32 reg = fld->reg;
+ u16 width = fld->width;
+ s16 shift = fld->shift;
+ int ret;
+
+ /*
+ * Silently return errors for shift < 0 so caller doesn't have
+ * to check for fields which are optional. For fields that
+ * are required then caller needs to do something special
+ * anyway.
+ */
+ if (shift < 0)
+ return -EINVAL;
+
+ if (sdhci_arasan->soc_ctl_map->hiword_update)
+ ret = regmap_write(soc_ctl_base, reg,
+ HIWORD_UPDATE(val, GENMASK(width, 0),
+ shift));
+ else
+ ret = regmap_update_bits(soc_ctl_base, reg,
+ GENMASK(shift + width, shift),
+ val << shift);
+
+ /* Yell about (unexpected) regmap errors */
+ if (ret)
+ pr_warn("%s: Regmap write fail: %d\n",
+ mmc_hostname(host->mmc), ret);
+
+ return ret;
+}
+
static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host)
{
u32 div;
@@ -79,6 +188,21 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
}
}
+static void sdhci_arasan_hs400_enhanced_strobe(struct mmc_host *mmc,
+ struct mmc_ios *ios)
+{
+ u32 vendor;
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ vendor = readl(host->ioaddr + SDHCI_ARASAN_VENDOR_REGISTER);
+ if (ios->enhanced_strobe)
+ vendor |= VENDOR_ENHANCED_STROBE;
+ else
+ vendor &= ~VENDOR_ENHANCED_STROBE;
+
+ writel(vendor, host->ioaddr + SDHCI_ARASAN_VENDOR_REGISTER);
+}
+
static struct sdhci_ops sdhci_arasan_ops = {
.set_clock = sdhci_arasan_set_clock,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
@@ -172,9 +296,168 @@ static int sdhci_arasan_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
sdhci_arasan_resume);
+static const struct of_device_id sdhci_arasan_of_match[] = {
+ /* SoC-specific compatible strings w/ soc_ctl_map */
+ {
+ .compatible = "rockchip,rk3399-sdhci-5.1",
+ .data = &rk3399_soc_ctl_map,
+ },
+
+ /* Generic compatible below here */
+ { .compatible = "arasan,sdhci-8.9a" },
+ { .compatible = "arasan,sdhci-5.1" },
+ { .compatible = "arasan,sdhci-4.9a" },
+
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
+
+/**
+ * sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate
+ *
+ * Return the current actual rate of the SD card clock. This can be used
+ * to communicate with out PHY.
+ *
+ * @hw: Pointer to the hardware clock structure.
+ * @parent_rate The parent rate (should be rate of clk_xin).
+ * Returns the card clock rate.
+ */
+static unsigned long sdhci_arasan_sdcardclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+
+{
+ struct sdhci_arasan_data *sdhci_arasan =
+ container_of(hw, struct sdhci_arasan_data, sdcardclk_hw);
+ struct sdhci_host *host = sdhci_arasan->host;
+
+ return host->mmc->actual_clock;
+}
+
+static const struct clk_ops arasan_sdcardclk_ops = {
+ .recalc_rate = sdhci_arasan_sdcardclk_recalc_rate,
+};
+
+/**
+ * sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq
+ *
+ * The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This
+ * function can be used to make that happen.
+ *
+ * NOTES:
+ * - Many existing devices don't seem to do this and work fine. To keep
+ * compatibility for old hardware where the device tree doesn't provide a
+ * register map, this function is a noop if a soc_ctl_map hasn't been provided
+ * for this platform.
+ * - It's assumed that clk_xin is not dynamic and that we use the SDHCI divider
+ * to achieve lower clock rates. That means that this function is called once
+ * at probe time and never called again.
+ *
+ * @host: The sdhci_host
+ */
+static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ const struct sdhci_arasan_soc_ctl_map *soc_ctl_map =
+ sdhci_arasan->soc_ctl_map;
+ u32 mhz = DIV_ROUND_CLOSEST(clk_get_rate(pltfm_host->clk), 1000000);
+
+ /* Having a map is optional */
+ if (!soc_ctl_map)
+ return;
+
+ /* If we have a map, we expect to have a syscon */
+ if (!sdhci_arasan->soc_ctl_base) {
+ pr_warn("%s: Have regmap, but no soc-ctl-syscon\n",
+ mmc_hostname(host->mmc));
+ return;
+ }
+
+ sdhci_arasan_syscon_write(host, &soc_ctl_map->baseclkfreq, mhz);
+}
+
+/**
+ * sdhci_arasan_register_sdclk - Register the sdclk for a PHY to use
+ *
+ * Some PHY devices need to know what the actual card clock is. In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Note: without seriously re-architecting SDHCI's clock code and testing on
+ * all platforms, there's no way to create a totally beautiful clock here
+ * with all clock ops implemented. Instead, we'll just create a clock that can
+ * be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common clock
+ * framework that we're doing things behind its back. This should be sufficient
+ * to create nice clean device tree bindings and later (if needed) we can try
+ * re-architecting SDHCI if we see some benefit to it.
+ *
+ * @sdhci_arasan: Our private data structure.
+ * @clk_xin: Pointer to the functional clock
+ * @dev: Pointer to our struct device.
+ * Returns 0 on success and error value on error
+ */
+static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan,
+ struct clk *clk_xin,
+ struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct clk_init_data sdcardclk_init;
+ const char *parent_clk_name;
+ int ret;
+
+ /* Providing a clock to the PHY is optional; no error if missing */
+ if (!of_find_property(np, "#clock-cells", NULL))
+ return 0;
+
+ ret = of_property_read_string_index(np, "clock-output-names", 0,
+ &sdcardclk_init.name);
+ if (ret) {
+ dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
+ return ret;
+ }
+
+ parent_clk_name = __clk_get_name(clk_xin);
+ sdcardclk_init.parent_names = &parent_clk_name;
+ sdcardclk_init.num_parents = 1;
+ sdcardclk_init.flags = CLK_GET_RATE_NOCACHE;
+ sdcardclk_init.ops = &arasan_sdcardclk_ops;
+
+ sdhci_arasan->sdcardclk_hw.init = &sdcardclk_init;
+ sdhci_arasan->sdcardclk =
+ devm_clk_register(dev, &sdhci_arasan->sdcardclk_hw);
+ sdhci_arasan->sdcardclk_hw.init = NULL;
+
+ ret = of_clk_add_provider(np, of_clk_src_simple_get,
+ sdhci_arasan->sdcardclk);
+ if (ret)
+ dev_err(dev, "Failed to add clock provider\n");
+
+ return ret;
+}
+
+/**
+ * sdhci_arasan_unregister_sdclk - Undoes sdhci_arasan_register_sdclk()
+ *
+ * Should be called any time we're exiting and sdhci_arasan_register_sdclk()
+ * returned success.
+ *
+ * @dev: Pointer to our struct device.
+ */
+static void sdhci_arasan_unregister_sdclk(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+
+ if (!of_find_property(np, "#clock-cells", NULL))
+ return;
+
+ of_clk_del_provider(dev->of_node);
+}
+
static int sdhci_arasan_probe(struct platform_device *pdev)
{
int ret;
+ const struct of_device_id *match;
+ struct device_node *node;
struct clk *clk_xin;
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
@@ -187,6 +470,24 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
pltfm_host = sdhci_priv(host);
sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
+ sdhci_arasan->host = host;
+
+ match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node);
+ sdhci_arasan->soc_ctl_map = match->data;
+
+ node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
+ if (node) {
+ sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node);
+ of_node_put(node);
+
+ if (IS_ERR(sdhci_arasan->soc_ctl_base)) {
+ ret = PTR_ERR(sdhci_arasan->soc_ctl_base);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Can't get syscon: %d\n",
+ ret);
+ goto err_pltfm_free;
+ }
+ }
sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
if (IS_ERR(sdhci_arasan->clk_ahb)) {
@@ -217,10 +518,16 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
sdhci_get_of_property(pdev);
pltfm_host->clk = clk_xin;
+ sdhci_arasan_update_baseclkfreq(host);
+
+ ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev);
+ if (ret)
+ goto clk_disable_all;
+
ret = mmc_of_parse(host->mmc);
if (ret) {
dev_err(&pdev->dev, "parsing dt failed (%u)\n", ret);
- goto clk_disable_all;
+ goto unreg_clk;
}
sdhci_arasan->phy = ERR_PTR(-ENODEV);
@@ -231,13 +538,13 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
if (IS_ERR(sdhci_arasan->phy)) {
ret = PTR_ERR(sdhci_arasan->phy);
dev_err(&pdev->dev, "No phy for arasan,sdhci-5.1.\n");
- goto clk_disable_all;
+ goto unreg_clk;
}
ret = phy_init(sdhci_arasan->phy);
if (ret < 0) {
dev_err(&pdev->dev, "phy_init err.\n");
- goto clk_disable_all;
+ goto unreg_clk;
}
ret = phy_power_on(sdhci_arasan->phy);
@@ -245,6 +552,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "phy_power_on err.\n");
goto err_phy_power;
}
+
+ host->mmc_host_ops.hs400_enhanced_strobe =
+ sdhci_arasan_hs400_enhanced_strobe;
}
ret = sdhci_add_host(host);
@@ -259,6 +569,8 @@ err_add_host:
err_phy_power:
if (!IS_ERR(sdhci_arasan->phy))
phy_exit(sdhci_arasan->phy);
+unreg_clk:
+ sdhci_arasan_unregister_sdclk(&pdev->dev);
clk_disable_all:
clk_disable_unprepare(clk_xin);
clk_dis_ahb:
@@ -281,6 +593,8 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
phy_exit(sdhci_arasan->phy);
}
+ sdhci_arasan_unregister_sdclk(&pdev->dev);
+
ret = sdhci_pltfm_unregister(pdev);
clk_disable_unprepare(clk_ahb);
@@ -288,14 +602,6 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
return ret;
}
-static const struct of_device_id sdhci_arasan_of_match[] = {
- { .compatible = "arasan,sdhci-8.9a" },
- { .compatible = "arasan,sdhci-5.1" },
- { .compatible = "arasan,sdhci-4.9a" },
- { }
-};
-MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
-
static struct platform_driver sdhci_arasan_driver = {
.driver = {
.name = "sdhci-arasan",
diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c
index d4cef713d246..a9b7fc06c434 100644
--- a/drivers/mmc/host/sdhci-of-at91.c
+++ b/drivers/mmc/host/sdhci-of-at91.c
@@ -288,7 +288,7 @@ static int sdhci_at91_probe(struct platform_device *pdev)
* Disable SDHCI_QUIRK_BROKEN_CARD_DETECTION to be sure nobody tries
* to enable polling via device tree with broken-cd property.
*/
- if (!(host->mmc->caps & MMC_CAP_NONREMOVABLE) &&
+ if (mmc_card_is_removable(host->mmc) &&
mmc_gpio_get_cd(host->mmc) < 0) {
host->mmc->caps |= MMC_CAP_NEEDS_POLL;
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 3f34d354f1fc..239be2fde242 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -481,7 +481,7 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask)
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static u32 esdhc_proctl;
static int esdhc_of_suspend(struct device *dev)
{
@@ -504,16 +504,12 @@ static int esdhc_of_resume(struct device *dev)
}
return ret;
}
-
-static const struct dev_pm_ops esdhc_pmops = {
- .suspend = esdhc_of_suspend,
- .resume = esdhc_of_resume,
-};
-#define ESDHC_PMOPS (&esdhc_pmops)
-#else
-#define ESDHC_PMOPS NULL
#endif
+static SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_ops,
+ esdhc_of_suspend,
+ esdhc_of_resume);
+
static const struct sdhci_ops sdhci_esdhc_be_ops = {
.read_l = esdhc_be_readl,
.read_w = esdhc_be_readw,
@@ -657,7 +653,7 @@ static struct platform_driver sdhci_esdhc_driver = {
.driver = {
.name = "sdhci-esdhc",
.of_match_table = sdhci_esdhc_of_match,
- .pm = ESDHC_PMOPS,
+ .pm = &esdhc_of_dev_pm_ops,
},
.probe = sdhci_esdhc_probe,
.remove = sdhci_pltfm_unregister,
diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c
index 4079a96ad37e..ac00c5efb2a3 100644
--- a/drivers/mmc/host/sdhci-of-hlwd.c
+++ b/drivers/mmc/host/sdhci-of-hlwd.c
@@ -85,7 +85,7 @@ static struct platform_driver sdhci_hlwd_driver = {
.driver = {
.name = "sdhci-hlwd",
.of_match_table = sdhci_hlwd_of_match,
- .pm = SDHCI_PLTFM_PMOPS,
+ .pm = &sdhci_pltfm_pmops,
},
.probe = sdhci_hlwd_probe,
.remove = sdhci_pltfm_unregister,
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index a4dbf7421edc..897cfd24ca2e 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -419,13 +419,13 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
};
/* Define Host controllers for Intel Merrifield platform */
-#define INTEL_MRFL_EMMC_0 0
-#define INTEL_MRFL_EMMC_1 1
+#define INTEL_MRFLD_EMMC_0 0
+#define INTEL_MRFLD_EMMC_1 1
-static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot)
+static int intel_mrfld_mmc_probe_slot(struct sdhci_pci_slot *slot)
{
- if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_0) &&
- (PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_1))
+ if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFLD_EMMC_0) &&
+ (PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFLD_EMMC_1))
/* SD support is not ready yet */
return -ENODEV;
@@ -435,12 +435,12 @@ static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot)
return 0;
}
-static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = {
+static const struct sdhci_pci_fixes sdhci_intel_mrfld_mmc = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_BROKEN_HS200 |
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
.allow_runtime_pm = true,
- .probe_slot = intel_mrfl_mmc_probe_slot,
+ .probe_slot = intel_mrfld_mmc_probe_slot,
};
/* O2Micro extra registers */
@@ -1104,10 +1104,10 @@ static const struct pci_device_id pci_ids[] = {
{
.vendor = PCI_VENDOR_ID_INTEL,
- .device = PCI_DEVICE_ID_INTEL_MRFL_MMC,
+ .device = PCI_DEVICE_ID_INTEL_MRFLD_MMC,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
- .driver_data = (kernel_ulong_t)&sdhci_intel_mrfl_mmc,
+ .driver_data = (kernel_ulong_t)&sdhci_intel_mrfld_mmc,
},
{
@@ -1413,8 +1413,7 @@ static const struct sdhci_ops sdhci_pci_ops = {
* *
\*****************************************************************************/
-#ifdef CONFIG_PM
-
+#ifdef CONFIG_PM_SLEEP
static int sdhci_pci_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -1496,7 +1495,9 @@ static int sdhci_pci_resume(struct device *dev)
return 0;
}
+#endif
+#ifdef CONFIG_PM
static int sdhci_pci_runtime_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -1562,17 +1563,10 @@ static int sdhci_pci_runtime_resume(struct device *dev)
return 0;
}
-
-#else /* CONFIG_PM */
-
-#define sdhci_pci_suspend NULL
-#define sdhci_pci_resume NULL
-
-#endif /* CONFIG_PM */
+#endif
static const struct dev_pm_ops sdhci_pci_pm_ops = {
- .suspend = sdhci_pci_suspend,
- .resume = sdhci_pci_resume,
+ SET_SYSTEM_SLEEP_PM_OPS(sdhci_pci_suspend, sdhci_pci_resume)
SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend,
sdhci_pci_runtime_resume, NULL)
};
@@ -1760,11 +1754,12 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
static void sdhci_pci_runtime_pm_allow(struct device *dev)
{
- pm_runtime_put_noidle(dev);
- pm_runtime_allow(dev);
+ pm_suspend_ignore_children(dev, 1);
pm_runtime_set_autosuspend_delay(dev, 50);
pm_runtime_use_autosuspend(dev);
- pm_suspend_ignore_children(dev, 1);
+ pm_runtime_allow(dev);
+ /* Stay active until mmc core scans for a card */
+ pm_runtime_put_noidle(dev);
}
static void sdhci_pci_runtime_pm_forbid(struct device *dev)
@@ -1810,15 +1805,13 @@ static int sdhci_pci_probe(struct pci_dev *pdev,
return -ENODEV;
}
- ret = pci_enable_device(pdev);
+ ret = pcim_enable_device(pdev);
if (ret)
return ret;
- chip = kzalloc(sizeof(struct sdhci_pci_chip), GFP_KERNEL);
- if (!chip) {
- ret = -ENOMEM;
- goto err;
- }
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
chip->pdev = pdev;
chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data;
@@ -1834,7 +1827,7 @@ static int sdhci_pci_probe(struct pci_dev *pdev,
if (chip->fixes && chip->fixes->probe) {
ret = chip->fixes->probe(chip);
if (ret)
- goto free;
+ return ret;
}
slots = chip->num_slots; /* Quirk may have changed this */
@@ -1844,8 +1837,7 @@ static int sdhci_pci_probe(struct pci_dev *pdev,
if (IS_ERR(slot)) {
for (i--; i >= 0; i--)
sdhci_pci_remove_slot(chip->slots[i]);
- ret = PTR_ERR(slot);
- goto free;
+ return PTR_ERR(slot);
}
chip->slots[i] = slot;
@@ -1855,35 +1847,18 @@ static int sdhci_pci_probe(struct pci_dev *pdev,
sdhci_pci_runtime_pm_allow(&pdev->dev);
return 0;
-
-free:
- pci_set_drvdata(pdev, NULL);
- kfree(chip);
-
-err:
- pci_disable_device(pdev);
- return ret;
}
static void sdhci_pci_remove(struct pci_dev *pdev)
{
int i;
- struct sdhci_pci_chip *chip;
+ struct sdhci_pci_chip *chip = pci_get_drvdata(pdev);
- chip = pci_get_drvdata(pdev);
-
- if (chip) {
- if (chip->allow_runtime_pm)
- sdhci_pci_runtime_pm_forbid(&pdev->dev);
-
- for (i = 0; i < chip->num_slots; i++)
- sdhci_pci_remove_slot(chip->slots[i]);
-
- pci_set_drvdata(pdev, NULL);
- kfree(chip);
- }
+ if (chip->allow_runtime_pm)
+ sdhci_pci_runtime_pm_forbid(&pdev->dev);
- pci_disable_device(pdev);
+ for (i = 0; i < chip->num_slots; i++)
+ sdhci_pci_remove_slot(chip->slots[i]);
}
static struct pci_driver sdhci_driver = {
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index 89e7151684a1..7e0788712e1a 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -14,7 +14,7 @@
#define PCI_DEVICE_ID_INTEL_BSW_EMMC 0x2294
#define PCI_DEVICE_ID_INTEL_BSW_SDIO 0x2295
#define PCI_DEVICE_ID_INTEL_BSW_SD 0x2296
-#define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190
+#define PCI_DEVICE_ID_INTEL_MRFLD_MMC 0x1190
#define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9
#define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa
#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index 64f287a03cd3..1d17dcfc3ffb 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -215,29 +215,26 @@ int sdhci_pltfm_unregister(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_unregister);
-#ifdef CONFIG_PM
-int sdhci_pltfm_suspend(struct device *dev)
+#ifdef CONFIG_PM_SLEEP
+static int sdhci_pltfm_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
return sdhci_suspend_host(host);
}
-EXPORT_SYMBOL_GPL(sdhci_pltfm_suspend);
-int sdhci_pltfm_resume(struct device *dev)
+static int sdhci_pltfm_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
return sdhci_resume_host(host);
}
-EXPORT_SYMBOL_GPL(sdhci_pltfm_resume);
+#endif
const struct dev_pm_ops sdhci_pltfm_pmops = {
- .suspend = sdhci_pltfm_suspend,
- .resume = sdhci_pltfm_resume,
+ SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume)
};
EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops);
-#endif /* CONFIG_PM */
static int __init sdhci_pltfm_drv_init(void)
{
diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h
index d38053bf9e4d..3280f2077959 100644
--- a/drivers/mmc/host/sdhci-pltfm.h
+++ b/drivers/mmc/host/sdhci-pltfm.h
@@ -109,13 +109,6 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
return (void *)host->private;
}
-#ifdef CONFIG_PM
-extern int sdhci_pltfm_suspend(struct device *dev);
-extern int sdhci_pltfm_resume(struct device *dev);
extern const struct dev_pm_ops sdhci_pltfm_pmops;
-#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
-#else
-#define SDHCI_PLTFM_PMOPS NULL
-#endif
#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */
diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c
index 1d8dd3540636..347eae2d7b6a 100644
--- a/drivers/mmc/host/sdhci-pxav2.c
+++ b/drivers/mmc/host/sdhci-pxav2.c
@@ -252,7 +252,7 @@ static struct platform_driver sdhci_pxav2_driver = {
.driver = {
.name = "sdhci-pxav2",
.of_match_table = of_match_ptr(sdhci_pxav2_of_match),
- .pm = SDHCI_PLTFM_PMOPS,
+ .pm = &sdhci_pltfm_pmops,
},
.probe = sdhci_pxav2_probe,
.remove = sdhci_pxav2_remove,
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c
index 30132500aa1c..dd1938d341f7 100644
--- a/drivers/mmc/host/sdhci-pxav3.c
+++ b/drivers/mmc/host/sdhci-pxav3.c
@@ -583,24 +583,17 @@ static int sdhci_pxav3_runtime_resume(struct device *dev)
}
#endif
-#ifdef CONFIG_PM
static const struct dev_pm_ops sdhci_pxav3_pmops = {
SET_SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume)
SET_RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend,
sdhci_pxav3_runtime_resume, NULL)
};
-#define SDHCI_PXAV3_PMOPS (&sdhci_pxav3_pmops)
-
-#else
-#define SDHCI_PXAV3_PMOPS NULL
-#endif
-
static struct platform_driver sdhci_pxav3_driver = {
.driver = {
.name = "sdhci-pxav3",
.of_match_table = of_match_ptr(sdhci_pxav3_of_match),
- .pm = SDHCI_PXAV3_PMOPS,
+ .pm = &sdhci_pxav3_pmops,
},
.probe = sdhci_pxav3_probe,
.remove = sdhci_pxav3_remove,
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index 70c724bc6fc7..784c5a848fb4 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -714,19 +714,12 @@ static int sdhci_s3c_runtime_resume(struct device *dev)
}
#endif
-#ifdef CONFIG_PM
static const struct dev_pm_ops sdhci_s3c_pmops = {
SET_SYSTEM_SLEEP_PM_OPS(sdhci_s3c_suspend, sdhci_s3c_resume)
SET_RUNTIME_PM_OPS(sdhci_s3c_runtime_suspend, sdhci_s3c_runtime_resume,
NULL)
};
-#define SDHCI_S3C_PMOPS (&sdhci_s3c_pmops)
-
-#else
-#define SDHCI_S3C_PMOPS NULL
-#endif
-
#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212)
static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
.no_divider = true,
@@ -765,7 +758,7 @@ static struct platform_driver sdhci_s3c_driver = {
.driver = {
.name = "s3c-sdhci",
.of_match_table = of_match_ptr(sdhci_s3c_dt_match),
- .pm = SDHCI_S3C_PMOPS,
+ .pm = &sdhci_s3c_pmops,
},
};
diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c
index 34866f668dd7..5d068639dd3f 100644
--- a/drivers/mmc/host/sdhci-sirf.c
+++ b/drivers/mmc/host/sdhci-sirf.c
@@ -260,9 +260,9 @@ static int sdhci_sirf_resume(struct device *dev)
return sdhci_resume_host(host);
}
+#endif
static SIMPLE_DEV_PM_OPS(sdhci_sirf_pm_ops, sdhci_sirf_suspend, sdhci_sirf_resume);
-#endif
static const struct of_device_id sdhci_sirf_of_match[] = {
{ .compatible = "sirf,prima2-sdhc" },
@@ -274,9 +274,7 @@ static struct platform_driver sdhci_sirf_driver = {
.driver = {
.name = "sdhci-sirf",
.of_match_table = sdhci_sirf_of_match,
-#ifdef CONFIG_PM_SLEEP
.pm = &sdhci_sirf_pm_ops,
-#endif
},
.probe = sdhci_sirf_probe,
.remove = sdhci_pltfm_unregister,
diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c
index 320e1c2f8853..c95ba83366a0 100644
--- a/drivers/mmc/host/sdhci-st.c
+++ b/drivers/mmc/host/sdhci-st.c
@@ -183,7 +183,7 @@ static void st_mmcss_cconfig(struct device_node *np, struct sdhci_host *host)
writel_relaxed(cconf2, host->ioaddr + ST_MMC_CCONFIG_REG_2);
- if (mhost->caps & MMC_CAP_NONREMOVABLE)
+ if (!mmc_card_is_removable(mhost))
cconf3 |= ST_MMC_CCONFIG_EMMC_SLOT_TYPE;
else
/* CARD _D ET_CTRL */
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index bcc0de47fe7e..1e93dc4e303e 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -148,28 +148,37 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
return;
misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
- /* Erratum: Enable SDHCI spec v3.00 support */
- if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
- misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
- /* Advertise UHS modes as supported by host */
- if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
- misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
- else
- misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50;
- if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
- misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
- else
- misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50;
- if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
- misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
- else
- misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104;
- sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
-
clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+
+ misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 |
+ SDHCI_MISC_CTRL_ENABLE_SDR50 |
+ SDHCI_MISC_CTRL_ENABLE_DDR50 |
+ SDHCI_MISC_CTRL_ENABLE_SDR104);
+
clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE;
- if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50)
- clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
+
+ /*
+ * If the board does not define a regulator for the SDHCI
+ * IO voltage, then don't advertise support for UHS modes
+ * even if the device supports it because the IO voltage
+ * cannot be configured.
+ */
+ if (!IS_ERR(host->mmc->supply.vqmmc)) {
+ /* Erratum: Enable SDHCI spec v3.00 support */
+ if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
+ misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
+ /* Advertise UHS modes as supported by host */
+ if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
+ misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
+ if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
+ misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
+ if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
+ misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
+ if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50)
+ clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
+ }
+
+ sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB)
@@ -474,7 +483,7 @@ static struct platform_driver sdhci_tegra_driver = {
.driver = {
.name = "sdhci-tegra",
.of_match_table = sdhci_tegra_dt_match,
- .pm = SDHCI_PLTFM_PMOPS,
+ .pm = &sdhci_pltfm_pmops,
},
.probe = sdhci_tegra_probe,
.remove = sdhci_pltfm_unregister,
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 0e3d7c056cb1..cd65d474afa2 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -45,65 +45,62 @@ static unsigned int debug_quirks2;
static void sdhci_finish_data(struct sdhci_host *);
-static void sdhci_finish_command(struct sdhci_host *);
-static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
-static int sdhci_get_cd(struct mmc_host *mmc);
static void sdhci_dumpregs(struct sdhci_host *host)
{
- pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
- mmc_hostname(host->mmc));
-
- pr_debug(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
- sdhci_readl(host, SDHCI_DMA_ADDRESS),
- sdhci_readw(host, SDHCI_HOST_VERSION));
- pr_debug(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n",
- sdhci_readw(host, SDHCI_BLOCK_SIZE),
- sdhci_readw(host, SDHCI_BLOCK_COUNT));
- pr_debug(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
- sdhci_readl(host, SDHCI_ARGUMENT),
- sdhci_readw(host, SDHCI_TRANSFER_MODE));
- pr_debug(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
- sdhci_readl(host, SDHCI_PRESENT_STATE),
- sdhci_readb(host, SDHCI_HOST_CONTROL));
- pr_debug(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n",
- sdhci_readb(host, SDHCI_POWER_CONTROL),
- sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
- pr_debug(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n",
- sdhci_readb(host, SDHCI_WAKE_UP_CONTROL),
- sdhci_readw(host, SDHCI_CLOCK_CONTROL));
- pr_debug(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n",
- sdhci_readb(host, SDHCI_TIMEOUT_CONTROL),
- sdhci_readl(host, SDHCI_INT_STATUS));
- pr_debug(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
- sdhci_readl(host, SDHCI_INT_ENABLE),
- sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
- pr_debug(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
- sdhci_readw(host, SDHCI_ACMD12_ERR),
- sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
- pr_debug(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n",
- sdhci_readl(host, SDHCI_CAPABILITIES),
- sdhci_readl(host, SDHCI_CAPABILITIES_1));
- pr_debug(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n",
- sdhci_readw(host, SDHCI_COMMAND),
- sdhci_readl(host, SDHCI_MAX_CURRENT));
- pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n",
- sdhci_readw(host, SDHCI_HOST_CONTROL2));
+ pr_err(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n",
+ mmc_hostname(host->mmc));
+
+ pr_err(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
+ sdhci_readl(host, SDHCI_DMA_ADDRESS),
+ sdhci_readw(host, SDHCI_HOST_VERSION));
+ pr_err(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n",
+ sdhci_readw(host, SDHCI_BLOCK_SIZE),
+ sdhci_readw(host, SDHCI_BLOCK_COUNT));
+ pr_err(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
+ sdhci_readl(host, SDHCI_ARGUMENT),
+ sdhci_readw(host, SDHCI_TRANSFER_MODE));
+ pr_err(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
+ sdhci_readl(host, SDHCI_PRESENT_STATE),
+ sdhci_readb(host, SDHCI_HOST_CONTROL));
+ pr_err(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n",
+ sdhci_readb(host, SDHCI_POWER_CONTROL),
+ sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
+ pr_err(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n",
+ sdhci_readb(host, SDHCI_WAKE_UP_CONTROL),
+ sdhci_readw(host, SDHCI_CLOCK_CONTROL));
+ pr_err(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n",
+ sdhci_readb(host, SDHCI_TIMEOUT_CONTROL),
+ sdhci_readl(host, SDHCI_INT_STATUS));
+ pr_err(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
+ sdhci_readl(host, SDHCI_INT_ENABLE),
+ sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
+ pr_err(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
+ sdhci_readw(host, SDHCI_ACMD12_ERR),
+ sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
+ pr_err(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n",
+ sdhci_readl(host, SDHCI_CAPABILITIES),
+ sdhci_readl(host, SDHCI_CAPABILITIES_1));
+ pr_err(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n",
+ sdhci_readw(host, SDHCI_COMMAND),
+ sdhci_readl(host, SDHCI_MAX_CURRENT));
+ pr_err(DRIVER_NAME ": Host ctl2: 0x%08x\n",
+ sdhci_readw(host, SDHCI_HOST_CONTROL2));
if (host->flags & SDHCI_USE_ADMA) {
if (host->flags & SDHCI_USE_64_BIT_DMA)
- pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n",
- readl(host->ioaddr + SDHCI_ADMA_ERROR),
- readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI),
- readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
+ pr_err(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n",
+ readl(host->ioaddr + SDHCI_ADMA_ERROR),
+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI),
+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
else
- pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
- readl(host->ioaddr + SDHCI_ADMA_ERROR),
- readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
+ pr_err(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
+ readl(host->ioaddr + SDHCI_ADMA_ERROR),
+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
}
- pr_debug(DRIVER_NAME ": ===========================================\n");
+ pr_err(DRIVER_NAME ": ===========================================\n");
}
/*****************************************************************************\
@@ -112,12 +109,17 @@ static void sdhci_dumpregs(struct sdhci_host *host)
* *
\*****************************************************************************/
+static inline bool sdhci_data_line_cmd(struct mmc_command *cmd)
+{
+ return cmd->data || cmd->flags & MMC_RSP_BUSY;
+}
+
static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
{
u32 present;
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||
- (host->mmc->caps & MMC_CAP_NONREMOVABLE))
+ !mmc_card_is_removable(host->mmc))
return;
if (enable) {
@@ -193,7 +195,9 @@ EXPORT_SYMBOL_GPL(sdhci_reset);
static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
{
if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
- if (!sdhci_get_cd(host->mmc))
+ struct mmc_host *mmc = host->mmc;
+
+ if (!mmc->ops->get_cd(mmc))
return;
}
@@ -210,10 +214,10 @@ static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
}
}
-static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
-
static void sdhci_init(struct sdhci_host *host, int soft)
{
+ struct mmc_host *mmc = host->mmc;
+
if (soft)
sdhci_do_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
else
@@ -225,13 +229,17 @@ static void sdhci_init(struct sdhci_host *host, int soft)
SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END |
SDHCI_INT_RESPONSE;
+ if (host->tuning_mode == SDHCI_TUNING_MODE_2 ||
+ host->tuning_mode == SDHCI_TUNING_MODE_3)
+ host->ier |= SDHCI_INT_RETUNE;
+
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
if (soft) {
/* force clock reconfiguration */
host->clock = 0;
- sdhci_set_ios(host->mmc, &host->mmc->ios);
+ mmc->ops->set_ios(mmc, &mmc->ios);
}
}
@@ -429,8 +437,6 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
{
u32 mask;
- BUG_ON(!host->data);
-
if (host->blocks == 0)
return;
@@ -747,14 +753,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
u8 ctrl;
struct mmc_data *data = cmd->data;
- WARN_ON(host->data);
-
- if (data || (cmd->flags & MMC_RSP_BUSY))
+ if (sdhci_data_line_cmd(cmd))
sdhci_set_timeout(host, cmd);
if (!data)
return;
+ WARN_ON(host->data);
+
/* Sanity checks */
BUG_ON(data->blksz * data->blocks > 524288);
BUG_ON(data->blksz > host->mmc->max_blk_size);
@@ -879,6 +885,12 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
}
+static inline bool sdhci_auto_cmd12(struct sdhci_host *host,
+ struct mmc_request *mrq)
+{
+ return !mrq->sbc && (host->flags & SDHCI_AUTO_CMD12);
+}
+
static void sdhci_set_transfer_mode(struct sdhci_host *host,
struct mmc_command *cmd)
{
@@ -909,12 +921,12 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
* If we are sending CMD23, CMD12 never gets sent
* on successful completion (so no Auto-CMD12).
*/
- if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12) &&
+ if (sdhci_auto_cmd12(host, cmd->mrq) &&
(cmd->opcode != SD_IO_RW_EXTENDED))
mode |= SDHCI_TRNS_AUTO_CMD12;
- else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
+ else if (cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) {
mode |= SDHCI_TRNS_AUTO_CMD23;
- sdhci_writel(host, host->mrq->sbc->arg, SDHCI_ARGUMENT2);
+ sdhci_writel(host, cmd->mrq->sbc->arg, SDHCI_ARGUMENT2);
}
}
@@ -926,14 +938,63 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
}
-static void sdhci_finish_data(struct sdhci_host *host)
+static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
+{
+ return (!(host->flags & SDHCI_DEVICE_DEAD) &&
+ ((mrq->cmd && mrq->cmd->error) ||
+ (mrq->sbc && mrq->sbc->error) ||
+ (mrq->data && ((mrq->data->error && !mrq->data->stop) ||
+ (mrq->data->stop && mrq->data->stop->error))) ||
+ (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)));
+}
+
+static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
+{
+ int i;
+
+ for (i = 0; i < SDHCI_MAX_MRQS; i++) {
+ if (host->mrqs_done[i] == mrq) {
+ WARN_ON(1);
+ return;
+ }
+ }
+
+ for (i = 0; i < SDHCI_MAX_MRQS; i++) {
+ if (!host->mrqs_done[i]) {
+ host->mrqs_done[i] = mrq;
+ break;
+ }
+ }
+
+ WARN_ON(i >= SDHCI_MAX_MRQS);
+
+ tasklet_schedule(&host->finish_tasklet);
+}
+
+static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
{
- struct mmc_data *data;
+ if (host->cmd && host->cmd->mrq == mrq)
+ host->cmd = NULL;
+
+ if (host->data_cmd && host->data_cmd->mrq == mrq)
+ host->data_cmd = NULL;
+
+ if (host->data && host->data->mrq == mrq)
+ host->data = NULL;
- BUG_ON(!host->data);
+ if (sdhci_needs_reset(host, mrq))
+ host->pending_reset = true;
+
+ __sdhci_finish_mrq(host, mrq);
+}
+
+static void sdhci_finish_data(struct sdhci_host *host)
+{
+ struct mmc_command *data_cmd = host->data_cmd;
+ struct mmc_data *data = host->data;
- data = host->data;
host->data = NULL;
+ host->data_cmd = NULL;
if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA)) ==
(SDHCI_REQ_USE_DMA | SDHCI_USE_ADMA))
@@ -958,20 +1019,41 @@ static void sdhci_finish_data(struct sdhci_host *host)
*/
if (data->stop &&
(data->error ||
- !host->mrq->sbc)) {
+ !data->mrq->sbc)) {
/*
* The controller needs a reset of internal state machines
* upon error conditions.
*/
if (data->error) {
- sdhci_do_reset(host, SDHCI_RESET_CMD);
+ if (!host->cmd || host->cmd == data_cmd)
+ sdhci_do_reset(host, SDHCI_RESET_CMD);
sdhci_do_reset(host, SDHCI_RESET_DATA);
}
+ /* Avoid triggering warning in sdhci_send_command() */
+ host->cmd = NULL;
sdhci_send_command(host, data->stop);
- } else
- tasklet_schedule(&host->finish_tasklet);
+ } else {
+ sdhci_finish_mrq(host, data->mrq);
+ }
+}
+
+static void sdhci_mod_timer(struct sdhci_host *host, struct mmc_request *mrq,
+ unsigned long timeout)
+{
+ if (sdhci_data_line_cmd(mrq->cmd))
+ mod_timer(&host->data_timer, timeout);
+ else
+ mod_timer(&host->timer, timeout);
+}
+
+static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq)
+{
+ if (sdhci_data_line_cmd(mrq->cmd))
+ del_timer(&host->data_timer);
+ else
+ del_timer(&host->timer);
}
void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
@@ -989,12 +1071,12 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
timeout = 10;
mask = SDHCI_CMD_INHIBIT;
- if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))
+ if (sdhci_data_line_cmd(cmd))
mask |= SDHCI_DATA_INHIBIT;
/* We shouldn't wait for data inihibit for stop commands, even
though they might use busy signaling */
- if (host->mrq->data && (cmd == host->mrq->data->stop))
+ if (cmd->mrq->data && (cmd == cmd->mrq->data->stop))
mask &= ~SDHCI_DATA_INHIBIT;
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
@@ -1003,7 +1085,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
mmc_hostname(host->mmc));
sdhci_dumpregs(host);
cmd->error = -EIO;
- tasklet_schedule(&host->finish_tasklet);
+ sdhci_finish_mrq(host, cmd->mrq);
return;
}
timeout--;
@@ -1015,10 +1097,13 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
else
timeout += 10 * HZ;
- mod_timer(&host->timer, timeout);
+ sdhci_mod_timer(host, cmd->mrq, timeout);
host->cmd = cmd;
- host->busy_handle = 0;
+ if (sdhci_data_line_cmd(cmd)) {
+ WARN_ON(host->data_cmd);
+ host->data_cmd = cmd;
+ }
sdhci_prepare_data(host, cmd);
@@ -1030,7 +1115,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
pr_err("%s: Unsupported response type!\n",
mmc_hostname(host->mmc));
cmd->error = -EINVAL;
- tasklet_schedule(&host->finish_tasklet);
+ sdhci_finish_mrq(host, cmd->mrq);
return;
}
@@ -1059,40 +1144,58 @@ EXPORT_SYMBOL_GPL(sdhci_send_command);
static void sdhci_finish_command(struct sdhci_host *host)
{
+ struct mmc_command *cmd = host->cmd;
int i;
- BUG_ON(host->cmd == NULL);
+ host->cmd = NULL;
- if (host->cmd->flags & MMC_RSP_PRESENT) {
- if (host->cmd->flags & MMC_RSP_136) {
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136) {
/* CRC is stripped so we need to do some shifting. */
for (i = 0;i < 4;i++) {
- host->cmd->resp[i] = sdhci_readl(host,
+ cmd->resp[i] = sdhci_readl(host,
SDHCI_RESPONSE + (3-i)*4) << 8;
if (i != 3)
- host->cmd->resp[i] |=
+ cmd->resp[i] |=
sdhci_readb(host,
SDHCI_RESPONSE + (3-i)*4-1);
}
} else {
- host->cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE);
+ cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE);
+ }
+ }
+
+ /*
+ * The host can send and interrupt when the busy state has
+ * ended, allowing us to wait without wasting CPU cycles.
+ * The busy signal uses DAT0 so this is similar to waiting
+ * for data to complete.
+ *
+ * Note: The 1.0 specification is a bit ambiguous about this
+ * feature so there might be some problems with older
+ * controllers.
+ */
+ if (cmd->flags & MMC_RSP_BUSY) {
+ if (cmd->data) {
+ DBG("Cannot wait for busy signal when also doing a data transfer");
+ } else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ) &&
+ cmd == host->data_cmd) {
+ /* Command complete before busy is ended */
+ return;
}
}
/* Finished CMD23, now send actual command. */
- if (host->cmd == host->mrq->sbc) {
- host->cmd = NULL;
- sdhci_send_command(host, host->mrq->cmd);
+ if (cmd == cmd->mrq->sbc) {
+ sdhci_send_command(host, cmd->mrq->cmd);
} else {
/* Processed actual command. */
if (host->data && host->data_early)
sdhci_finish_data(host);
- if (!host->cmd->data)
- tasklet_schedule(&host->finish_tasklet);
-
- host->cmd = NULL;
+ if (!cmd->data)
+ sdhci_finish_mrq(host, cmd->mrq);
}
}
@@ -1373,26 +1476,22 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
spin_lock_irqsave(&host->lock, flags);
- WARN_ON(host->mrq != NULL);
-
sdhci_led_activate(host);
/*
* Ensure we don't send the STOP for non-SET_BLOCK_COUNTED
* requests if Auto-CMD12 is enabled.
*/
- if (!mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) {
+ if (sdhci_auto_cmd12(host, mrq)) {
if (mrq->stop) {
mrq->data->stop = NULL;
mrq->stop = NULL;
}
}
- host->mrq = mrq;
-
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
- host->mrq->cmd->error = -ENOMEDIUM;
- tasklet_schedule(&host->finish_tasklet);
+ mrq->cmd->error = -ENOMEDIUM;
+ sdhci_finish_mrq(host, mrq);
} else {
if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
sdhci_send_command(host, mrq->sbc);
@@ -1617,7 +1716,7 @@ static int sdhci_get_cd(struct mmc_host *mmc)
return 0;
/* If nonremovable, assume that the card is always present. */
- if (host->mmc->caps & MMC_CAP_NONREMOVABLE)
+ if (!mmc_card_is_removable(host->mmc))
return 1;
/*
@@ -1733,13 +1832,14 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
switch (ios->signal_voltage) {
case MMC_SIGNAL_VOLTAGE_330:
+ if (!(host->flags & SDHCI_SIGNALING_330))
+ return -EINVAL;
/* Set 1.8V Signal Enable in the Host Control2 register to 0 */
ctrl &= ~SDHCI_CTRL_VDD_180;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
if (!IS_ERR(mmc->supply.vqmmc)) {
- ret = regulator_set_voltage(mmc->supply.vqmmc, 2700000,
- 3600000);
+ ret = mmc_regulator_set_vqmmc(mmc, ios);
if (ret) {
pr_warn("%s: Switching to 3.3V signalling voltage failed\n",
mmc_hostname(mmc));
@@ -1759,9 +1859,10 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
return -EAGAIN;
case MMC_SIGNAL_VOLTAGE_180:
+ if (!(host->flags & SDHCI_SIGNALING_180))
+ return -EINVAL;
if (!IS_ERR(mmc->supply.vqmmc)) {
- ret = regulator_set_voltage(mmc->supply.vqmmc,
- 1700000, 1950000);
+ ret = mmc_regulator_set_vqmmc(mmc, ios);
if (ret) {
pr_warn("%s: Switching to 1.8V signalling voltage failed\n",
mmc_hostname(mmc));
@@ -1790,9 +1891,10 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
return -EAGAIN;
case MMC_SIGNAL_VOLTAGE_120:
+ if (!(host->flags & SDHCI_SIGNALING_120))
+ return -EINVAL;
if (!IS_ERR(mmc->supply.vqmmc)) {
- ret = regulator_set_voltage(mmc->supply.vqmmc, 1100000,
- 1300000);
+ ret = mmc_regulator_set_vqmmc(mmc, ios);
if (ret) {
pr_warn("%s: Switching to 1.2V signalling voltage failed\n",
mmc_hostname(mmc));
@@ -1811,10 +1913,10 @@ static int sdhci_card_busy(struct mmc_host *mmc)
struct sdhci_host *host = mmc_priv(mmc);
u32 present_state;
- /* Check whether DAT[3:0] is 0000 */
+ /* Check whether DAT[0] is 0 */
present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
- return !(present_state & SDHCI_DATA_LVL_MASK);
+ return !(present_state & SDHCI_DATA_0_LVL_MASK);
}
static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -1909,7 +2011,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
/*
* Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
- * of loops reaches 40 times or a timeout of 150ms occurs.
+ * of loops reaches 40 times.
*/
do {
struct mmc_command cmd = {0};
@@ -1920,13 +2022,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
cmd.retries = 0;
cmd.data = NULL;
+ cmd.mrq = &mrq;
cmd.error = 0;
if (tuning_loop_counter-- == 0)
break;
mrq.cmd = &cmd;
- host->mrq = &mrq;
/*
* In response to CMD19, the card sends 64 bytes of tuning
@@ -1956,7 +2058,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
sdhci_send_command(host, &cmd);
host->cmd = NULL;
- host->mrq = NULL;
+ sdhci_del_timer(host, &mrq);
spin_unlock_irqrestore(&host->lock, flags);
/* Wait for Buffer Read Ready interrupt */
@@ -2086,6 +2188,24 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
sdhci_pre_dma_transfer(host, mrq->data, COOKIE_PRE_MAPPED);
}
+static inline bool sdhci_has_requests(struct sdhci_host *host)
+{
+ return host->cmd || host->data_cmd;
+}
+
+static void sdhci_error_out_mrqs(struct sdhci_host *host, int err)
+{
+ if (host->data_cmd) {
+ host->data_cmd->error = err;
+ sdhci_finish_mrq(host, host->data_cmd->mrq);
+ }
+
+ if (host->cmd) {
+ host->cmd->error = err;
+ sdhci_finish_mrq(host, host->cmd->mrq);
+ }
+}
+
static void sdhci_card_event(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
@@ -2096,12 +2216,12 @@ static void sdhci_card_event(struct mmc_host *mmc)
if (host->ops->card_event)
host->ops->card_event(host);
- present = sdhci_get_cd(host->mmc);
+ present = mmc->ops->get_cd(mmc);
spin_lock_irqsave(&host->lock, flags);
- /* Check host->mrq first in case we are runtime suspended */
- if (host->mrq && !present) {
+ /* Check sdhci_has_requests() first in case we are runtime suspended */
+ if (sdhci_has_requests(host) && !present) {
pr_err("%s: Card removed during transfer!\n",
mmc_hostname(host->mmc));
pr_err("%s: Resetting controller.\n",
@@ -2110,8 +2230,7 @@ static void sdhci_card_event(struct mmc_host *mmc)
sdhci_do_reset(host, SDHCI_RESET_CMD);
sdhci_do_reset(host, SDHCI_RESET_DATA);
- host->mrq->cmd->error = -ENOMEDIUM;
- tasklet_schedule(&host->finish_tasklet);
+ sdhci_error_out_mrqs(host, -ENOMEDIUM);
}
spin_unlock_irqrestore(&host->lock, flags);
@@ -2140,28 +2259,28 @@ static const struct mmc_host_ops sdhci_ops = {
* *
\*****************************************************************************/
-static void sdhci_tasklet_finish(unsigned long param)
+static bool sdhci_request_done(struct sdhci_host *host)
{
- struct sdhci_host *host;
unsigned long flags;
struct mmc_request *mrq;
-
- host = (struct sdhci_host*)param;
+ int i;
spin_lock_irqsave(&host->lock, flags);
- /*
- * If this tasklet gets rescheduled while running, it will
- * be run again afterwards but without any active request.
- */
- if (!host->mrq) {
- spin_unlock_irqrestore(&host->lock, flags);
- return;
+ for (i = 0; i < SDHCI_MAX_MRQS; i++) {
+ mrq = host->mrqs_done[i];
+ if (mrq) {
+ host->mrqs_done[i] = NULL;
+ break;
+ }
}
- del_timer(&host->timer);
+ if (!mrq) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return true;
+ }
- mrq = host->mrq;
+ sdhci_del_timer(host, mrq);
/*
* Always unmap the data buffers if they were mapped by
@@ -2183,13 +2302,7 @@ static void sdhci_tasklet_finish(unsigned long param)
* The controller needs a reset of internal state machines
* upon error conditions.
*/
- if (!(host->flags & SDHCI_DEVICE_DEAD) &&
- ((mrq->cmd && mrq->cmd->error) ||
- (mrq->sbc && mrq->sbc->error) ||
- (mrq->data && ((mrq->data->error && !mrq->data->stop) ||
- (mrq->data->stop && mrq->data->stop->error))) ||
- (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) {
-
+ if (sdhci_needs_reset(host, mrq)) {
/* Some controllers need this kick or reset won't work here */
if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)
/* This is to force an update */
@@ -2197,20 +2310,31 @@ static void sdhci_tasklet_finish(unsigned long param)
/* Spec says we should do both at the same time, but Ricoh
controllers do not like that. */
- sdhci_do_reset(host, SDHCI_RESET_CMD);
- sdhci_do_reset(host, SDHCI_RESET_DATA);
- }
+ if (!host->cmd)
+ sdhci_do_reset(host, SDHCI_RESET_CMD);
+ if (!host->data_cmd)
+ sdhci_do_reset(host, SDHCI_RESET_DATA);
- host->mrq = NULL;
- host->cmd = NULL;
- host->data = NULL;
+ host->pending_reset = false;
+ }
- sdhci_led_deactivate(host);
+ if (!sdhci_has_requests(host))
+ sdhci_led_deactivate(host);
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
mmc_request_done(host->mmc, mrq);
+
+ return false;
+}
+
+static void sdhci_tasklet_finish(unsigned long param)
+{
+ struct sdhci_host *host = (struct sdhci_host *)param;
+
+ while (!sdhci_request_done(host))
+ ;
}
static void sdhci_timeout_timer(unsigned long data)
@@ -2222,7 +2346,30 @@ static void sdhci_timeout_timer(unsigned long data)
spin_lock_irqsave(&host->lock, flags);
- if (host->mrq) {
+ if (host->cmd && !sdhci_data_line_cmd(host->cmd)) {
+ pr_err("%s: Timeout waiting for hardware cmd interrupt.\n",
+ mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+
+ host->cmd->error = -ETIMEDOUT;
+ sdhci_finish_mrq(host, host->cmd->mrq);
+ }
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void sdhci_timeout_data_timer(unsigned long data)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+
+ host = (struct sdhci_host *)data;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->data || host->data_cmd ||
+ (host->cmd && sdhci_data_line_cmd(host->cmd))) {
pr_err("%s: Timeout waiting for hardware interrupt.\n",
mmc_hostname(host->mmc));
sdhci_dumpregs(host);
@@ -2230,13 +2377,12 @@ static void sdhci_timeout_timer(unsigned long data)
if (host->data) {
host->data->error = -ETIMEDOUT;
sdhci_finish_data(host);
+ } else if (host->data_cmd) {
+ host->data_cmd->error = -ETIMEDOUT;
+ sdhci_finish_mrq(host, host->data_cmd->mrq);
} else {
- if (host->cmd)
- host->cmd->error = -ETIMEDOUT;
- else
- host->mrq->cmd->error = -ETIMEDOUT;
-
- tasklet_schedule(&host->finish_tasklet);
+ host->cmd->error = -ETIMEDOUT;
+ sdhci_finish_mrq(host, host->cmd->mrq);
}
}
@@ -2252,9 +2398,14 @@ static void sdhci_timeout_timer(unsigned long data)
static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
{
- BUG_ON(intmask == 0);
-
if (!host->cmd) {
+ /*
+ * SDHCI recovers from errors by resetting the cmd and data
+ * circuits. Until that is done, there very well might be more
+ * interrupts, so ignore them in that case.
+ */
+ if (host->pending_reset)
+ return;
pr_err("%s: Got command interrupt 0x%08x even though no command operation was in progress.\n",
mmc_hostname(host->mmc), (unsigned)intmask);
sdhci_dumpregs(host);
@@ -2285,37 +2436,14 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
return;
}
- tasklet_schedule(&host->finish_tasklet);
+ sdhci_finish_mrq(host, host->cmd->mrq);
return;
}
- /*
- * The host can send and interrupt when the busy state has
- * ended, allowing us to wait without wasting CPU cycles.
- * Unfortunately this is overloaded on the "data complete"
- * interrupt, so we need to take some care when handling
- * it.
- *
- * Note: The 1.0 specification is a bit ambiguous about this
- * feature so there might be some problems with older
- * controllers.
- */
- if (host->cmd->flags & MMC_RSP_BUSY) {
- if (host->cmd->data)
- DBG("Cannot wait for busy signal when also doing a data transfer");
- else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ)
- && !host->busy_handle) {
- /* Mark that command complete before busy is ended */
- host->busy_handle = 1;
- return;
- }
-
- /* The controller does not support the end-of-busy IRQ,
- * fall through and take the SDHCI_INT_RESPONSE */
- } else if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) &&
- host->cmd->opcode == MMC_STOP_TRANSMISSION && !host->data) {
+ if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) &&
+ !(host->cmd->flags & MMC_RSP_BUSY) && !host->data &&
+ host->cmd->opcode == MMC_STOP_TRANSMISSION)
*mask &= ~SDHCI_INT_DATA_END;
- }
if (intmask & SDHCI_INT_RESPONSE)
sdhci_finish_command(host);
@@ -2357,7 +2485,6 @@ static void sdhci_adma_show_error(struct sdhci_host *host) { }
static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
{
u32 command;
- BUG_ON(intmask == 0);
/* CMD19 generates _only_ Buffer Read Ready interrupt */
if (intmask & SDHCI_INT_DATA_AVAIL) {
@@ -2371,15 +2498,20 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
}
if (!host->data) {
+ struct mmc_command *data_cmd = host->data_cmd;
+
+ if (data_cmd)
+ host->data_cmd = NULL;
+
/*
* The "data complete" interrupt is also used to
* indicate that a busy state has ended. See comment
* above in sdhci_cmd_irq().
*/
- if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) {
+ if (data_cmd && (data_cmd->flags & MMC_RSP_BUSY)) {
if (intmask & SDHCI_INT_DATA_TIMEOUT) {
- host->cmd->error = -ETIMEDOUT;
- tasklet_schedule(&host->finish_tasklet);
+ data_cmd->error = -ETIMEDOUT;
+ sdhci_finish_mrq(host, data_cmd->mrq);
return;
}
if (intmask & SDHCI_INT_DATA_END) {
@@ -2388,14 +2520,22 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
* before the command completed, so make
* sure we do things in the proper order.
*/
- if (host->busy_handle)
- sdhci_finish_command(host);
- else
- host->busy_handle = 1;
+ if (host->cmd == data_cmd)
+ return;
+
+ sdhci_finish_mrq(host, data_cmd->mrq);
return;
}
}
+ /*
+ * SDHCI recovers from errors by resetting the cmd and data
+ * circuits. Until that is done, there very well might be more
+ * interrupts, so ignore them in that case.
+ */
+ if (host->pending_reset)
+ return;
+
pr_err("%s: Got data interrupt 0x%08x even though no data operation was in progress.\n",
mmc_hostname(host->mmc), (unsigned)intmask);
sdhci_dumpregs(host);
@@ -2453,7 +2593,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
}
if (intmask & SDHCI_INT_DATA_END) {
- if (host->cmd) {
+ if (host->cmd == host->data_cmd) {
/*
* Data managed to finish before the
* command completed. Make sure we do
@@ -2537,6 +2677,9 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
pr_err("%s: Card is consuming too much power!\n",
mmc_hostname(host->mmc));
+ if (intmask & SDHCI_INT_RETUNE)
+ mmc_retune_needed(host->mmc);
+
if (intmask & SDHCI_INT_CARD_INT) {
sdhci_enable_sdio_irq_nolock(host, false);
host->thread_isr |= SDHCI_INT_CARD_INT;
@@ -2546,7 +2689,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE |
SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK |
SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER |
- SDHCI_INT_CARD_INT);
+ SDHCI_INT_RETUNE | SDHCI_INT_CARD_INT);
if (intmask) {
unexpected |= intmask;
@@ -2582,8 +2725,10 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
spin_unlock_irqrestore(&host->lock, flags);
if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
- sdhci_card_event(host->mmc);
- mmc_detect_change(host->mmc, msecs_to_jiffies(200));
+ struct mmc_host *mmc = host->mmc;
+
+ mmc->ops->card_event(mmc);
+ mmc_detect_change(mmc, msecs_to_jiffies(200));
}
if (isr & SDHCI_INT_CARD_INT) {
@@ -2605,18 +2750,31 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
\*****************************************************************************/
#ifdef CONFIG_PM
+/*
+ * To enable wakeup events, the corresponding events have to be enabled in
+ * the Interrupt Status Enable register too. See 'Table 1-6: Wakeup Signal
+ * Table' in the SD Host Controller Standard Specification.
+ * It is useless to restore SDHCI_INT_ENABLE state in
+ * sdhci_disable_irq_wakeups() since it will be set by
+ * sdhci_enable_card_detection() or sdhci_init().
+ */
void sdhci_enable_irq_wakeups(struct sdhci_host *host)
{
u8 val;
u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE
| SDHCI_WAKE_ON_INT;
+ u32 irq_val = SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE |
+ SDHCI_INT_CARD_INT;
val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL);
val |= mask ;
/* Avoid fake wake up */
- if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+ if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) {
val &= ~(SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE);
+ irq_val &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
+ }
sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL);
+ sdhci_writel(host, irq_val, SDHCI_INT_ENABLE);
}
EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);
@@ -2636,7 +2794,8 @@ int sdhci_suspend_host(struct sdhci_host *host)
sdhci_disable_card_detection(host);
mmc_retune_timer_stop(host->mmc);
- mmc_retune_needed(host->mmc);
+ if (host->tuning_mode != SDHCI_TUNING_MODE_3)
+ mmc_retune_needed(host->mmc);
if (!device_may_wakeup(mmc_dev(host->mmc))) {
host->ier = 0;
@@ -2654,6 +2813,7 @@ EXPORT_SYMBOL_GPL(sdhci_suspend_host);
int sdhci_resume_host(struct sdhci_host *host)
{
+ struct mmc_host *mmc = host->mmc;
int ret = 0;
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
@@ -2667,7 +2827,7 @@ int sdhci_resume_host(struct sdhci_host *host)
sdhci_init(host, 0);
host->pwr = 0;
host->clock = 0;
- sdhci_set_ios(host->mmc, &host->mmc->ios);
+ mmc->ops->set_ios(mmc, &mmc->ios);
} else {
sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER));
mmiowb();
@@ -2696,7 +2856,8 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host)
unsigned long flags;
mmc_retune_timer_stop(host->mmc);
- mmc_retune_needed(host->mmc);
+ if (host->tuning_mode != SDHCI_TUNING_MODE_3)
+ mmc_retune_needed(host->mmc);
spin_lock_irqsave(&host->lock, flags);
host->ier &= SDHCI_INT_CARD_INT;
@@ -2716,6 +2877,7 @@ EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host);
int sdhci_runtime_resume_host(struct sdhci_host *host)
{
+ struct mmc_host *mmc = host->mmc;
unsigned long flags;
int host_flags = host->flags;
@@ -2729,8 +2891,8 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
/* Force clock and power re-program */
host->pwr = 0;
host->clock = 0;
- sdhci_start_signal_voltage_switch(host->mmc, &host->mmc->ios);
- sdhci_set_ios(host->mmc, &host->mmc->ios);
+ mmc->ops->start_signal_voltage_switch(mmc, &mmc->ios);
+ mmc->ops->set_ios(mmc, &mmc->ios);
if ((host_flags & SDHCI_PV_ENABLED) &&
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) {
@@ -2781,6 +2943,8 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
host->mmc_host_ops = sdhci_ops;
mmc->ops = &host->mmc_host_ops;
+ host->flags = SDHCI_SIGNALING_330;
+
return host;
}
@@ -2816,10 +2980,41 @@ static int sdhci_set_dma_mask(struct sdhci_host *host)
return ret;
}
-int sdhci_add_host(struct sdhci_host *host)
+void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
+{
+ u16 v;
+
+ if (host->read_caps)
+ return;
+
+ host->read_caps = true;
+
+ if (debug_quirks)
+ host->quirks = debug_quirks;
+
+ if (debug_quirks2)
+ host->quirks2 = debug_quirks2;
+
+ sdhci_do_reset(host, SDHCI_RESET_ALL);
+
+ v = ver ? *ver : sdhci_readw(host, SDHCI_HOST_VERSION);
+ host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
+
+ if (host->quirks & SDHCI_QUIRK_MISSING_CAPS)
+ return;
+
+ host->caps = caps ? *caps : sdhci_readl(host, SDHCI_CAPABILITIES);
+
+ if (host->version < SDHCI_SPEC_300)
+ return;
+
+ host->caps1 = caps1 ? *caps1 : sdhci_readl(host, SDHCI_CAPABILITIES_1);
+}
+EXPORT_SYMBOL_GPL(__sdhci_read_caps);
+
+int sdhci_setup_host(struct sdhci_host *host)
{
struct mmc_host *mmc;
- u32 caps[2] = {0, 0};
u32 max_current_caps;
unsigned int ocr_avail;
unsigned int override_timeout_clk;
@@ -2832,34 +3027,28 @@ int sdhci_add_host(struct sdhci_host *host)
mmc = host->mmc;
- if (debug_quirks)
- host->quirks = debug_quirks;
- if (debug_quirks2)
- host->quirks2 = debug_quirks2;
+ /*
+ * If there are external regulators, get them. Note this must be done
+ * early before resetting the host and reading the capabilities so that
+ * the host can take the appropriate action if regulators are not
+ * available.
+ */
+ ret = mmc_regulator_get_supply(mmc);
+ if (ret == -EPROBE_DEFER)
+ return ret;
- override_timeout_clk = host->timeout_clk;
+ sdhci_read_caps(host);
- sdhci_do_reset(host, SDHCI_RESET_ALL);
+ override_timeout_clk = host->timeout_clk;
- host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
- host->version = (host->version & SDHCI_SPEC_VER_MASK)
- >> SDHCI_SPEC_VER_SHIFT;
if (host->version > SDHCI_SPEC_300) {
pr_err("%s: Unknown controller version (%d). You may experience problems.\n",
mmc_hostname(mmc), host->version);
}
- caps[0] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
- sdhci_readl(host, SDHCI_CAPABILITIES);
-
- if (host->version >= SDHCI_SPEC_300)
- caps[1] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ?
- host->caps1 :
- sdhci_readl(host, SDHCI_CAPABILITIES_1);
-
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
host->flags |= SDHCI_USE_SDMA;
- else if (!(caps[0] & SDHCI_CAN_DO_SDMA))
+ else if (!(host->caps & SDHCI_CAN_DO_SDMA))
DBG("Controller doesn't have SDMA capability\n");
else
host->flags |= SDHCI_USE_SDMA;
@@ -2871,7 +3060,7 @@ int sdhci_add_host(struct sdhci_host *host)
}
if ((host->version >= SDHCI_SPEC_200) &&
- (caps[0] & SDHCI_CAN_DO_ADMA2))
+ (host->caps & SDHCI_CAN_DO_ADMA2))
host->flags |= SDHCI_USE_ADMA;
if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) &&
@@ -2887,7 +3076,7 @@ int sdhci_add_host(struct sdhci_host *host)
* SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
* implement.
*/
- if (caps[0] & SDHCI_CAN_64BIT)
+ if (host->caps & SDHCI_CAN_64BIT)
host->flags |= SDHCI_USE_64_BIT_DMA;
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
@@ -2963,10 +3152,10 @@ int sdhci_add_host(struct sdhci_host *host)
}
if (host->version >= SDHCI_SPEC_300)
- host->max_clk = (caps[0] & SDHCI_CLOCK_V3_BASE_MASK)
+ host->max_clk = (host->caps & SDHCI_CLOCK_V3_BASE_MASK)
>> SDHCI_CLOCK_BASE_SHIFT;
else
- host->max_clk = (caps[0] & SDHCI_CLOCK_BASE_MASK)
+ host->max_clk = (host->caps & SDHCI_CLOCK_BASE_MASK)
>> SDHCI_CLOCK_BASE_SHIFT;
host->max_clk *= 1000000;
@@ -2985,7 +3174,7 @@ int sdhci_add_host(struct sdhci_host *host)
* In case of Host Controller v3.00, find out whether clock
* multiplier is supported.
*/
- host->clk_mul = (caps[1] & SDHCI_CLOCK_MUL_MASK) >>
+ host->clk_mul = (host->caps1 & SDHCI_CLOCK_MUL_MASK) >>
SDHCI_CLOCK_MUL_SHIFT;
/*
@@ -3017,7 +3206,7 @@ int sdhci_add_host(struct sdhci_host *host)
mmc->f_max = max_clk;
if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
- host->timeout_clk = (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >>
+ host->timeout_clk = (host->caps & SDHCI_TIMEOUT_CLK_MASK) >>
SDHCI_TIMEOUT_CLK_SHIFT;
if (host->timeout_clk == 0) {
if (host->ops->get_timeout_clock) {
@@ -3031,7 +3220,7 @@ int sdhci_add_host(struct sdhci_host *host)
}
}
- if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
+ if (host->caps & SDHCI_TIMEOUT_CLK_UNIT)
host->timeout_clk *= 1000;
if (override_timeout_clk)
@@ -3072,27 +3261,22 @@ int sdhci_add_host(struct sdhci_host *host)
if (host->quirks2 & SDHCI_QUIRK2_HOST_NO_CMD23)
mmc->caps &= ~MMC_CAP_CMD23;
- if (caps[0] & SDHCI_CAN_DO_HISPD)
+ if (host->caps & SDHCI_CAN_DO_HISPD)
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
- !(mmc->caps & MMC_CAP_NONREMOVABLE) &&
+ mmc_card_is_removable(mmc) &&
mmc_gpio_get_cd(host->mmc) < 0)
mmc->caps |= MMC_CAP_NEEDS_POLL;
- /* If there are external regulators, get them */
- ret = mmc_regulator_get_supply(mmc);
- if (ret == -EPROBE_DEFER)
- goto undma;
-
/* If vqmmc regulator and no 1.8V signalling, then there's no UHS */
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = regulator_enable(mmc->supply.vqmmc);
if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000,
1950000))
- caps[1] &= ~(SDHCI_SUPPORT_SDR104 |
- SDHCI_SUPPORT_SDR50 |
- SDHCI_SUPPORT_DDR50);
+ host->caps1 &= ~(SDHCI_SUPPORT_SDR104 |
+ SDHCI_SUPPORT_SDR50 |
+ SDHCI_SUPPORT_DDR50);
if (ret) {
pr_warn("%s: Failed to enable vqmmc regulator: %d\n",
mmc_hostname(mmc), ret);
@@ -3100,28 +3284,30 @@ int sdhci_add_host(struct sdhci_host *host)
}
}
- if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V)
- caps[1] &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
- SDHCI_SUPPORT_DDR50);
+ if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V) {
+ host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
+ SDHCI_SUPPORT_DDR50);
+ }
/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
- if (caps[1] & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
- SDHCI_SUPPORT_DDR50))
+ if (host->caps1 & (SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
+ SDHCI_SUPPORT_DDR50))
mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
/* SDR104 supports also implies SDR50 support */
- if (caps[1] & SDHCI_SUPPORT_SDR104) {
+ if (host->caps1 & SDHCI_SUPPORT_SDR104) {
mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
/* SD3.0: SDR104 is supported so (for eMMC) the caps2
* field can be promoted to support HS200.
*/
if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200))
mmc->caps2 |= MMC_CAP2_HS200;
- } else if (caps[1] & SDHCI_SUPPORT_SDR50)
+ } else if (host->caps1 & SDHCI_SUPPORT_SDR50) {
mmc->caps |= MMC_CAP_UHS_SDR50;
+ }
if (host->quirks2 & SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 &&
- (caps[1] & SDHCI_SUPPORT_HS400))
+ (host->caps1 & SDHCI_SUPPORT_HS400))
mmc->caps2 |= MMC_CAP2_HS400;
if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) &&
@@ -3130,25 +3316,25 @@ int sdhci_add_host(struct sdhci_host *host)
1300000)))
mmc->caps2 &= ~MMC_CAP2_HSX00_1_2V;
- if ((caps[1] & SDHCI_SUPPORT_DDR50) &&
- !(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50))
+ if ((host->caps1 & SDHCI_SUPPORT_DDR50) &&
+ !(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50))
mmc->caps |= MMC_CAP_UHS_DDR50;
/* Does the host need tuning for SDR50? */
- if (caps[1] & SDHCI_USE_SDR50_TUNING)
+ if (host->caps1 & SDHCI_USE_SDR50_TUNING)
host->flags |= SDHCI_SDR50_NEEDS_TUNING;
/* Driver Type(s) (A, C, D) supported by the host */
- if (caps[1] & SDHCI_DRIVER_TYPE_A)
+ if (host->caps1 & SDHCI_DRIVER_TYPE_A)
mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
- if (caps[1] & SDHCI_DRIVER_TYPE_C)
+ if (host->caps1 & SDHCI_DRIVER_TYPE_C)
mmc->caps |= MMC_CAP_DRIVER_TYPE_C;
- if (caps[1] & SDHCI_DRIVER_TYPE_D)
+ if (host->caps1 & SDHCI_DRIVER_TYPE_D)
mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
/* Initial value for re-tuning timer count */
- host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
- SDHCI_RETUNING_TIMER_COUNT_SHIFT;
+ host->tuning_count = (host->caps1 & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
+ SDHCI_RETUNING_TIMER_COUNT_SHIFT;
/*
* In case Re-tuning Timer is not disabled, the actual value of
@@ -3158,7 +3344,7 @@ int sdhci_add_host(struct sdhci_host *host)
host->tuning_count = 1 << (host->tuning_count - 1);
/* Re-tuning mode supported by the Host Controller */
- host->tuning_mode = (caps[1] & SDHCI_RETUNING_MODE_MASK) >>
+ host->tuning_mode = (host->caps1 & SDHCI_RETUNING_MODE_MASK) >>
SDHCI_RETUNING_MODE_SHIFT;
ocr_avail = 0;
@@ -3187,7 +3373,7 @@ int sdhci_add_host(struct sdhci_host *host)
}
}
- if (caps[0] & SDHCI_CAN_VDD_330) {
+ if (host->caps & SDHCI_CAN_VDD_330) {
ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->max_current_330 = ((max_current_caps &
@@ -3195,7 +3381,7 @@ int sdhci_add_host(struct sdhci_host *host)
SDHCI_MAX_CURRENT_330_SHIFT) *
SDHCI_MAX_CURRENT_MULTIPLIER;
}
- if (caps[0] & SDHCI_CAN_VDD_300) {
+ if (host->caps & SDHCI_CAN_VDD_300) {
ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
mmc->max_current_300 = ((max_current_caps &
@@ -3203,7 +3389,7 @@ int sdhci_add_host(struct sdhci_host *host)
SDHCI_MAX_CURRENT_300_SHIFT) *
SDHCI_MAX_CURRENT_MULTIPLIER;
}
- if (caps[0] & SDHCI_CAN_VDD_180) {
+ if (host->caps & SDHCI_CAN_VDD_180) {
ocr_avail |= MMC_VDD_165_195;
mmc->max_current_180 = ((max_current_caps &
@@ -3240,6 +3426,15 @@ int sdhci_add_host(struct sdhci_host *host)
goto unreg;
}
+ if ((mmc->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
+ MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR)) ||
+ (mmc->caps2 & (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS400_1_8V)))
+ host->flags |= SDHCI_SIGNALING_180;
+
+ if (mmc->caps2 & MMC_CAP2_HSX00_1_2V)
+ host->flags |= SDHCI_SIGNALING_120;
+
spin_lock_init(&host->lock);
/*
@@ -3281,7 +3476,7 @@ int sdhci_add_host(struct sdhci_host *host)
if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) {
mmc->max_blk_size = 2;
} else {
- mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
+ mmc->max_blk_size = (host->caps & SDHCI_MAX_BLOCK_MASK) >>
SDHCI_MAX_BLOCK_SHIFT;
if (mmc->max_blk_size >= 3) {
pr_warn("%s: Invalid maximum block size, assuming 512 bytes\n",
@@ -3297,6 +3492,28 @@ int sdhci_add_host(struct sdhci_host *host)
*/
mmc->max_blk_count = (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535;
+ return 0;
+
+unreg:
+ if (!IS_ERR(mmc->supply.vqmmc))
+ regulator_disable(mmc->supply.vqmmc);
+undma:
+ if (host->align_buffer)
+ dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz +
+ host->adma_table_sz, host->align_buffer,
+ host->align_addr);
+ host->adma_table = NULL;
+ host->align_buffer = NULL;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sdhci_setup_host);
+
+int __sdhci_add_host(struct sdhci_host *host)
+{
+ struct mmc_host *mmc = host->mmc;
+ int ret;
+
/*
* Init tasklets.
*/
@@ -3304,6 +3521,8 @@ int sdhci_add_host(struct sdhci_host *host)
sdhci_tasklet_finish, (unsigned long)host);
setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
+ setup_timer(&host->data_timer, sdhci_timeout_data_timer,
+ (unsigned long)host);
init_waitqueue_head(&host->buf_ready_int);
@@ -3353,10 +3572,10 @@ unirq:
free_irq(host->irq, host);
untasklet:
tasklet_kill(&host->finish_tasklet);
-unreg:
+
if (!IS_ERR(mmc->supply.vqmmc))
regulator_disable(mmc->supply.vqmmc);
-undma:
+
if (host->align_buffer)
dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz +
host->adma_table_sz, host->align_buffer,
@@ -3366,7 +3585,18 @@ undma:
return ret;
}
+EXPORT_SYMBOL_GPL(__sdhci_add_host);
+
+int sdhci_add_host(struct sdhci_host *host)
+{
+ int ret;
+ ret = sdhci_setup_host(host);
+ if (ret)
+ return ret;
+
+ return __sdhci_add_host(host);
+}
EXPORT_SYMBOL_GPL(sdhci_add_host);
void sdhci_remove_host(struct sdhci_host *host, int dead)
@@ -3379,12 +3609,10 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
host->flags |= SDHCI_DEVICE_DEAD;
- if (host->mrq) {
+ if (sdhci_has_requests(host)) {
pr_err("%s: Controller removed during "
" transfer!\n", mmc_hostname(mmc));
-
- host->mrq->cmd->error = -ENOMEDIUM;
- tasklet_schedule(&host->finish_tasklet);
+ sdhci_error_out_mrqs(host, -ENOMEDIUM);
}
spin_unlock_irqrestore(&host->lock, flags);
@@ -3404,6 +3632,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
free_irq(host->irq, host);
del_timer_sync(&host->timer);
+ del_timer_sync(&host->data_timer);
tasklet_kill(&host->finish_tasklet);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 609f87ca536b..0411c9f36461 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -128,6 +128,7 @@
#define SDHCI_INT_CARD_INSERT 0x00000040
#define SDHCI_INT_CARD_REMOVE 0x00000080
#define SDHCI_INT_CARD_INT 0x00000100
+#define SDHCI_INT_RETUNE 0x00001000
#define SDHCI_INT_ERROR 0x00008000
#define SDHCI_INT_TIMEOUT 0x00010000
#define SDHCI_INT_CRC 0x00020000
@@ -186,6 +187,7 @@
#define SDHCI_CAN_DO_ADMA1 0x00100000
#define SDHCI_CAN_DO_HISPD 0x00200000
#define SDHCI_CAN_DO_SDMA 0x00400000
+#define SDHCI_CAN_DO_SUSPEND 0x00800000
#define SDHCI_CAN_VDD_330 0x01000000
#define SDHCI_CAN_VDD_300 0x02000000
#define SDHCI_CAN_VDD_180 0x04000000
@@ -314,6 +316,9 @@ struct sdhci_adma2_64_desc {
*/
#define SDHCI_MAX_SEGS 128
+/* Allow for a a command request and a data request at the same time */
+#define SDHCI_MAX_MRQS 2
+
enum sdhci_cookie {
COOKIE_UNMAPPED,
COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */
@@ -447,6 +452,9 @@ struct sdhci_host {
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
+#define SDHCI_SIGNALING_330 (1<<14) /* Host is capable of 3.3V signaling */
+#define SDHCI_SIGNALING_180 (1<<15) /* Host is capable of 1.8V signaling */
+#define SDHCI_SIGNALING_120 (1<<16) /* Host is capable of 1.2V signaling */
unsigned int version; /* SDHCI spec. version */
@@ -460,12 +468,13 @@ struct sdhci_host {
bool runtime_suspended; /* Host is runtime suspended */
bool bus_on; /* Bus power prevents runtime suspend */
bool preset_enabled; /* Preset is enabled */
+ bool pending_reset; /* Cmd/data reset is pending */
- struct mmc_request *mrq; /* Current request */
+ struct mmc_request *mrqs_done[SDHCI_MAX_MRQS]; /* Requests done */
struct mmc_command *cmd; /* Current command */
+ struct mmc_command *data_cmd; /* Current data command */
struct mmc_data *data; /* Current data request */
unsigned int data_early:1; /* Data finished before cmd */
- unsigned int busy_handle:1; /* Handling the order of Busy-end */
struct sg_mapping_iter sg_miter; /* SG state for PIO */
unsigned int blocks; /* remaining PIO blocks */
@@ -486,9 +495,11 @@ struct sdhci_host {
struct tasklet_struct finish_tasklet; /* Tasklet structures */
struct timer_list timer; /* Timer for timeouts */
+ struct timer_list data_timer; /* Timer for data timeouts */
- u32 caps; /* Alternative CAPABILITY_0 */
- u32 caps1; /* Alternative CAPABILITY_1 */
+ u32 caps; /* CAPABILITY_0 */
+ u32 caps1; /* CAPABILITY_1 */
+ bool read_caps; /* Capability flags have been read */
unsigned int ocr_avail_sdio; /* OCR bit masks */
unsigned int ocr_avail_sd;
@@ -508,6 +519,8 @@ struct sdhci_host {
unsigned int tuning_count; /* Timer count for re-tuning */
unsigned int tuning_mode; /* Re-tuning mode supported by host */
#define SDHCI_TUNING_MODE_1 0
+#define SDHCI_TUNING_MODE_2 1
+#define SDHCI_TUNING_MODE_3 2
unsigned long private[0] ____cacheline_aligned;
};
@@ -645,11 +658,20 @@ static inline void *sdhci_priv(struct sdhci_host *host)
}
extern void sdhci_card_detect(struct sdhci_host *host);
+extern void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps,
+ u32 *caps1);
+extern int sdhci_setup_host(struct sdhci_host *host);
+extern int __sdhci_add_host(struct sdhci_host *host);
extern int sdhci_add_host(struct sdhci_host *host);
extern void sdhci_remove_host(struct sdhci_host *host, int dead);
extern void sdhci_send_command(struct sdhci_host *host,
struct mmc_command *cmd);
+static inline void sdhci_read_caps(struct sdhci_host *host)
+{
+ __sdhci_read_caps(host, NULL, NULL, NULL);
+}
+
static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host)
{
return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED);
diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c
index 983b8b32ef96..111b66f5439b 100644
--- a/drivers/mmc/host/sdhci_f_sdh30.c
+++ b/drivers/mmc/host/sdhci_f_sdh30.c
@@ -222,7 +222,7 @@ static struct platform_driver sdhci_f_sdh30_driver = {
.driver = {
.name = "f_sdh30",
.of_match_table = f_sdh30_dt_ids,
- .pm = SDHCI_PLTFM_PMOPS,
+ .pm = &sdhci_pltfm_pmops,
},
.probe = sdhci_f_sdh30_probe,
.remove = sdhci_f_sdh30_remove,
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index dd64b8663984..900778421be6 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -574,7 +574,7 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
if (state1 & STS1_CMDSEQ) {
sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK);
sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, ~CMD_CTRL_BREAK);
- for (timeout = 10000000; timeout; timeout--) {
+ for (timeout = 10000; timeout; timeout--) {
if (!(sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1)
& STS1_CMDSEQ))
break;
@@ -819,10 +819,12 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
tmp |= CMD_SET_RTYP_NO;
break;
case MMC_RSP_R1:
- case MMC_RSP_R1B:
case MMC_RSP_R3:
tmp |= CMD_SET_RTYP_6B;
break;
+ case MMC_RSP_R1B:
+ tmp |= CMD_SET_RBSY | CMD_SET_RTYP_6B;
+ break;
case MMC_RSP_R2:
tmp |= CMD_SET_RTYP_17B;
break;
@@ -830,17 +832,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
dev_err(dev, "Unsupported response type.\n");
break;
}
- switch (opc) {
- /* RBSY */
- case MMC_SLEEP_AWAKE:
- case MMC_SWITCH:
- case MMC_STOP_TRANSMISSION:
- case MMC_SET_WRITE_PROT:
- case MMC_CLR_WRITE_PROT:
- case MMC_ERASE:
- tmp |= CMD_SET_RBSY;
- break;
- }
+
/* WDAT / DATW */
if (data) {
tmp |= CMD_SET_WDAT;
@@ -925,23 +917,13 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
{
struct mmc_command *cmd = mrq->cmd;
u32 opc = cmd->opcode;
- u32 mask;
+ u32 mask = 0;
unsigned long flags;
- switch (opc) {
- /* response busy check */
- case MMC_SLEEP_AWAKE:
- case MMC_SWITCH:
- case MMC_STOP_TRANSMISSION:
- case MMC_SET_WRITE_PROT:
- case MMC_CLR_WRITE_PROT:
- case MMC_ERASE:
+ if (cmd->flags & MMC_RSP_BUSY)
mask = MASK_START_CMD | MASK_MRBSYE;
- break;
- default:
+ else
mask = MASK_START_CMD | MASK_MCRSPE;
- break;
- }
if (host->ccs_enable)
mask |= MASK_MCCSTO;
@@ -1009,22 +991,6 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->state = STATE_REQUEST;
spin_unlock_irqrestore(&host->lock, flags);
- switch (mrq->cmd->opcode) {
- /* MMCIF does not support SD/SDIO command */
- case MMC_SLEEP_AWAKE: /* = SD_IO_SEND_OP_COND (5) */
- case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */
- if ((mrq->cmd->flags & MMC_CMD_MASK) != MMC_CMD_BCR)
- break;
- case MMC_APP_CMD:
- case SD_IO_RW_DIRECT:
- host->state = STATE_IDLE;
- mrq->cmd->error = -ETIMEDOUT;
- mmc_request_done(mmc, mrq);
- return;
- default:
- break;
- }
-
host->mrq = mrq;
sh_mmcif_start_cmd(host, mrq);
@@ -1488,6 +1454,9 @@ static int sh_mmcif_probe(struct platform_device *pdev)
sh_mmcif_init_ocr(host);
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_WAIT_WHILE_BUSY;
+ mmc->caps2 |= MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
+ mmc->max_busy_timeout = 10000;
+
if (pd && pd->caps)
mmc->caps |= pd->caps;
mmc->max_segs = 32;
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index f750f9494410..c3b651bf89cb 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -39,6 +39,12 @@
#define EXT_ACC 0xe4
+#define SDHI_VER_GEN2_SDR50 0x490c
+/* very old datasheets said 0x490c for SDR104, too. They are wrong! */
+#define SDHI_VER_GEN2_SDR104 0xcb0d
+#define SDHI_VER_GEN3_SD 0xcc10
+#define SDHI_VER_GEN3_SDMMC 0xcd10
+
#define host_to_priv(host) container_of((host)->pdata, struct sh_mobile_sdhi, mmc_data)
struct sh_mobile_sdhi_of_data {
@@ -109,14 +115,14 @@ static void sh_mobile_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
* sh_mobile_sdhi_of_data :: dma_buswidth
*/
switch (sd_ctrl_read16(host, CTL_VERSION)) {
- case 0x490C:
+ case SDHI_VER_GEN2_SDR50:
val = (width == 32) ? 0x0001 : 0x0000;
break;
- case 0xCB0D:
+ case SDHI_VER_GEN2_SDR104:
val = (width == 32) ? 0x0000 : 0x0001;
break;
- case 0xCC10: /* Gen3, SD only */
- case 0xCD10: /* Gen3, SD + MMC */
+ case SDHI_VER_GEN3_SD:
+ case SDHI_VER_GEN3_SDMMC:
if (width == 64)
val = 0x0000;
else if (width == 32)
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 1aac2ad8edf2..7f63ec05bdf4 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -259,7 +259,7 @@ static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,
static inline void sd_ctrl_write32_as_16_and_16(struct tmio_mmc_host *host, int addr, u32 val)
{
- writew(val, host->ctl + (addr << host->bus_shift));
+ writew(val & 0xffff, host->ctl + (addr << host->bus_shift));
writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
}
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index f44e2ab7aea2..92467efc4e2c 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -1086,7 +1086,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->caps & MMC_CAP_NONREMOVABLE ||
+ !mmc_card_is_removable(mmc) ||
mmc->slot.cd_irq >= 0);
if (tmio_mmc_clk_enable(_host) < 0) {
diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c
index 6ebcf3e41c46..fd57345ffed2 100644
--- a/drivers/phy/phy-rockchip-emmc.c
+++ b/drivers/phy/phy-rockchip-emmc.c
@@ -14,6 +14,7 @@
* GNU General Public License for more details.
*/
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
@@ -31,42 +32,64 @@
((val) << (shift) | (mask) << ((shift) + 16))
/* Register definition */
-#define GRF_EMMCPHY_CON0 0x0
-#define GRF_EMMCPHY_CON1 0x4
-#define GRF_EMMCPHY_CON2 0x8
-#define GRF_EMMCPHY_CON3 0xc
-#define GRF_EMMCPHY_CON4 0x10
-#define GRF_EMMCPHY_CON5 0x14
-#define GRF_EMMCPHY_CON6 0x18
-#define GRF_EMMCPHY_STATUS 0x20
-
-#define PHYCTRL_PDB_MASK 0x1
-#define PHYCTRL_PDB_SHIFT 0x0
-#define PHYCTRL_PDB_PWR_ON 0x1
-#define PHYCTRL_PDB_PWR_OFF 0x0
-#define PHYCTRL_ENDLL_MASK 0x1
-#define PHYCTRL_ENDLL_SHIFT 0x1
-#define PHYCTRL_ENDLL_ENABLE 0x1
-#define PHYCTRL_ENDLL_DISABLE 0x0
-#define PHYCTRL_CALDONE_MASK 0x1
-#define PHYCTRL_CALDONE_SHIFT 0x6
-#define PHYCTRL_CALDONE_DONE 0x1
-#define PHYCTRL_CALDONE_GOING 0x0
-#define PHYCTRL_DLLRDY_MASK 0x1
-#define PHYCTRL_DLLRDY_SHIFT 0x5
-#define PHYCTRL_DLLRDY_DONE 0x1
-#define PHYCTRL_DLLRDY_GOING 0x0
+#define GRF_EMMCPHY_CON0 0x0
+#define GRF_EMMCPHY_CON1 0x4
+#define GRF_EMMCPHY_CON2 0x8
+#define GRF_EMMCPHY_CON3 0xc
+#define GRF_EMMCPHY_CON4 0x10
+#define GRF_EMMCPHY_CON5 0x14
+#define GRF_EMMCPHY_CON6 0x18
+#define GRF_EMMCPHY_STATUS 0x20
+
+#define PHYCTRL_PDB_MASK 0x1
+#define PHYCTRL_PDB_SHIFT 0x0
+#define PHYCTRL_PDB_PWR_ON 0x1
+#define PHYCTRL_PDB_PWR_OFF 0x0
+#define PHYCTRL_ENDLL_MASK 0x1
+#define PHYCTRL_ENDLL_SHIFT 0x1
+#define PHYCTRL_ENDLL_ENABLE 0x1
+#define PHYCTRL_ENDLL_DISABLE 0x0
+#define PHYCTRL_CALDONE_MASK 0x1
+#define PHYCTRL_CALDONE_SHIFT 0x6
+#define PHYCTRL_CALDONE_DONE 0x1
+#define PHYCTRL_CALDONE_GOING 0x0
+#define PHYCTRL_DLLRDY_MASK 0x1
+#define PHYCTRL_DLLRDY_SHIFT 0x5
+#define PHYCTRL_DLLRDY_DONE 0x1
+#define PHYCTRL_DLLRDY_GOING 0x0
+#define PHYCTRL_FREQSEL_200M 0x0
+#define PHYCTRL_FREQSEL_50M 0x1
+#define PHYCTRL_FREQSEL_100M 0x2
+#define PHYCTRL_FREQSEL_150M 0x3
+#define PHYCTRL_FREQSEL_MASK 0x3
+#define PHYCTRL_FREQSEL_SHIFT 0xc
+#define PHYCTRL_DR_MASK 0x7
+#define PHYCTRL_DR_SHIFT 0x4
+#define PHYCTRL_DR_50OHM 0x0
+#define PHYCTRL_DR_33OHM 0x1
+#define PHYCTRL_DR_66OHM 0x2
+#define PHYCTRL_DR_100OHM 0x3
+#define PHYCTRL_DR_40OHM 0x4
+#define PHYCTRL_OTAPDLYENA 0x1
+#define PHYCTRL_OTAPDLYENA_MASK 0x1
+#define PHYCTRL_OTAPDLYENA_SHIFT 0xb
+#define PHYCTRL_OTAPDLYSEL_MASK 0xf
+#define PHYCTRL_OTAPDLYSEL_SHIFT 0x7
struct rockchip_emmc_phy {
unsigned int reg_offset;
struct regmap *reg_base;
+ struct clk *emmcclk;
};
-static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
- bool on_off)
+static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
{
+ struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
unsigned int caldone;
unsigned int dllrdy;
+ unsigned int freqsel = PHYCTRL_FREQSEL_200M;
+ unsigned long rate;
+ unsigned long timeout;
/*
* Keep phyctrl_pdb and phyctrl_endll low to allow
@@ -87,6 +110,43 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
if (on_off == PHYCTRL_PDB_PWR_OFF)
return 0;
+ rate = clk_get_rate(rk_phy->emmcclk);
+
+ if (rate != 0) {
+ unsigned long ideal_rate;
+ unsigned long diff;
+
+ switch (rate) {
+ case 1 ... 74999999:
+ ideal_rate = 50000000;
+ freqsel = PHYCTRL_FREQSEL_50M;
+ break;
+ case 75000000 ... 124999999:
+ ideal_rate = 100000000;
+ freqsel = PHYCTRL_FREQSEL_100M;
+ break;
+ case 125000000 ... 174999999:
+ ideal_rate = 150000000;
+ freqsel = PHYCTRL_FREQSEL_150M;
+ break;
+ default:
+ ideal_rate = 200000000;
+ break;
+ };
+
+ diff = (rate > ideal_rate) ?
+ rate - ideal_rate : ideal_rate - rate;
+
+ /*
+ * In order for tuning delays to be accurate we need to be
+ * pretty spot on for the DLL range, so warn if we're too
+ * far off. Also warn if we're above the 200 MHz max. Don't
+ * warn for really slow rates since we won't be tuning then.
+ */
+ if ((rate > 50000000 && diff > 15000000) || (rate > 200000000))
+ dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate);
+ }
+
/*
* According to the user manual, calpad calibration
* cycle takes more than 2us without the minimal recommended
@@ -113,20 +173,62 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
return -ETIMEDOUT;
}
+ /* Set the frequency of the DLL operation */
+ regmap_write(rk_phy->reg_base,
+ rk_phy->reg_offset + GRF_EMMCPHY_CON0,
+ HIWORD_UPDATE(freqsel, PHYCTRL_FREQSEL_MASK,
+ PHYCTRL_FREQSEL_SHIFT));
+
+ /* Turn on the DLL */
regmap_write(rk_phy->reg_base,
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
PHYCTRL_ENDLL_MASK,
PHYCTRL_ENDLL_SHIFT));
+
/*
- * After enable analog DLL circuits, we need extra 10.2us
- * for dll to be ready for work.
+ * We turned on the DLL even though the rate was 0 because we the
+ * clock might be turned on later. ...but we can't wait for the DLL
+ * to lock when the rate is 0 because it will never lock with no
+ * input clock.
+ *
+ * Technically we should be checking the lock later when the clock
+ * is turned on, but for now we won't.
*/
- udelay(11);
- regmap_read(rk_phy->reg_base,
- rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
- &dllrdy);
- dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
+ if (rate == 0)
+ return 0;
+
+ /*
+ * After enabling analog DLL circuits docs say that we need 10.2 us if
+ * our source clock is at 50 MHz and that lock time scales linearly
+ * with clock speed. If we are powering on the PHY and the card clock
+ * is super slow (like 100 kHZ) this could take as long as 5.1 ms as
+ * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms
+ * Hopefully we won't be running at 100 kHz, but we should still make
+ * sure we wait long enough.
+ *
+ * NOTE: There appear to be corner cases where the DLL seems to take
+ * extra long to lock for reasons that aren't understood. In some
+ * extreme cases we've seen it take up to over 10ms (!). We'll be
+ * generous and give it 50ms. We still busy wait here because:
+ * - In most cases it should be super fast.
+ * - This is not called lots during normal operation so it shouldn't
+ * be a power or performance problem to busy wait. We expect it
+ * only at boot / resume. In both cases, eMMC is probably on the
+ * critical path so busy waiting a little extra time should be OK.
+ */
+ timeout = jiffies + msecs_to_jiffies(50);
+ do {
+ udelay(1);
+
+ regmap_read(rk_phy->reg_base,
+ rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
+ &dllrdy);
+ dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
+ if (dllrdy == PHYCTRL_DLLRDY_DONE)
+ break;
+ } while (!time_after(jiffies, timeout));
+
if (dllrdy != PHYCTRL_DLLRDY_DONE) {
pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
return -ETIMEDOUT;
@@ -135,33 +237,82 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
return 0;
}
-static int rockchip_emmc_phy_power_off(struct phy *phy)
+static int rockchip_emmc_phy_init(struct phy *phy)
{
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
int ret = 0;
- /* Power down emmc phy analog blocks */
- ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF);
- if (ret)
- return ret;
+ /*
+ * We purposely get the clock here and not in probe to avoid the
+ * circular dependency problem. We expect:
+ * - PHY driver to probe
+ * - SDHCI driver to start probe
+ * - SDHCI driver to register it's clock
+ * - SDHCI driver to get the PHY
+ * - SDHCI driver to init the PHY
+ *
+ * The clock is optional, so upon any error we just set to NULL.
+ *
+ * NOTE: we don't do anything special for EPROBE_DEFER here. Given the
+ * above expected use case, EPROBE_DEFER isn't sensible to expect, so
+ * it's just like any other error.
+ */
+ rk_phy->emmcclk = clk_get(&phy->dev, "emmcclk");
+ if (IS_ERR(rk_phy->emmcclk)) {
+ dev_dbg(&phy->dev, "Error getting emmcclk: %d\n", ret);
+ rk_phy->emmcclk = NULL;
+ }
+
+ return ret;
+}
+
+static int rockchip_emmc_phy_exit(struct phy *phy)
+{
+ struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
+
+ clk_put(rk_phy->emmcclk);
return 0;
}
+static int rockchip_emmc_phy_power_off(struct phy *phy)
+{
+ /* Power down emmc phy analog blocks */
+ return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_OFF);
+}
+
static int rockchip_emmc_phy_power_on(struct phy *phy)
{
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
- int ret = 0;
- /* Power up emmc phy analog blocks */
- ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON);
- if (ret)
- return ret;
+ /* Drive impedance: 50 Ohm */
+ regmap_write(rk_phy->reg_base,
+ rk_phy->reg_offset + GRF_EMMCPHY_CON6,
+ HIWORD_UPDATE(PHYCTRL_DR_50OHM,
+ PHYCTRL_DR_MASK,
+ PHYCTRL_DR_SHIFT));
- return 0;
+ /* Output tap delay: enable */
+ regmap_write(rk_phy->reg_base,
+ rk_phy->reg_offset + GRF_EMMCPHY_CON0,
+ HIWORD_UPDATE(PHYCTRL_OTAPDLYENA,
+ PHYCTRL_OTAPDLYENA_MASK,
+ PHYCTRL_OTAPDLYENA_SHIFT));
+
+ /* Output tap delay */
+ regmap_write(rk_phy->reg_base,
+ rk_phy->reg_offset + GRF_EMMCPHY_CON0,
+ HIWORD_UPDATE(4,
+ PHYCTRL_OTAPDLYSEL_MASK,
+ PHYCTRL_OTAPDLYSEL_SHIFT));
+
+ /* Power up emmc phy analog blocks */
+ return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON);
}
static const struct phy_ops ops = {
+ .init = rockchip_emmc_phy_init,
+ .exit = rockchip_emmc_phy_exit,
.power_on = rockchip_emmc_phy_power_on,
.power_off = rockchip_emmc_phy_power_off,
.owner = THIS_MODULE,