Linux MultiMedia Card development
 help / color / mirror / Atom feed
* [PATCH v4 2/5] mmc: zx: Initial support for ZX mmc controller
From: Jun Nie @ 2016-11-07 13:04 UTC (permalink / raw)
  To: shawn.guo, xie.baoyou
  Cc: ulf.hansson, jh80.chung, jason.liu, linux-mmc, Jun Nie
In-Reply-To: <1478523888-4524-1-git-send-email-jun.nie@linaro.org>

This platform driver adds initial support for the DW host controller
found on ZTE SoCs.

It has been tested on ZX296718 EVB board currently. More support on
timing tuning will be added when hardware is available.

Signed-off-by: Jun Nie <jun.nie@linaro.org>
---
 drivers/mmc/host/Kconfig     |   9 ++
 drivers/mmc/host/Makefile    |   1 +
 drivers/mmc/host/dw_mmc-zx.c | 242 +++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/dw_mmc-zx.h |  31 ++++++
 4 files changed, 283 insertions(+)
 create mode 100644 drivers/mmc/host/dw_mmc-zx.c
 create mode 100644 drivers/mmc/host/dw_mmc-zx.h

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 5274f50..4dafbc2 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -662,6 +662,15 @@ config MMC_DW_ROCKCHIP
 	  Synopsys DesignWare Memory Card Interface driver. Select this option
 	  for platforms based on RK3066, RK3188 and RK3288 SoC's.
 
+config MMC_DW_ZX
+	tristate "ZTE specific extensions for Synopsys DW Memory Card Interface"
+	depends on MMC_DW && ARCH_ZX
+	select MMC_DW_PLTFM
+	help
+	  This selects support for ZTE SoC specific extensions to the
+	  Synopsys DesignWare Memory Card Interface driver. Select this option
+	  for platforms based on ZX296718 SoC's.
+
 config MMC_SH_MMCIF
 	tristate "SuperH Internal MMCIF support"
 	depends on HAS_DMA
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e2bdaaf..9766143 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_MMC_DW_EXYNOS)	+= dw_mmc-exynos.o
 obj-$(CONFIG_MMC_DW_K3)		+= dw_mmc-k3.o
 obj-$(CONFIG_MMC_DW_PCI)	+= dw_mmc-pci.o
 obj-$(CONFIG_MMC_DW_ROCKCHIP)	+= dw_mmc-rockchip.o
+obj-$(CONFIG_MMC_DW_ZX)		+= dw_mmc-zx.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_VUB300)	+= vub300.o
diff --git a/drivers/mmc/host/dw_mmc-zx.c b/drivers/mmc/host/dw_mmc-zx.c
new file mode 100644
index 0000000..de77408
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-zx.c
@@ -0,0 +1,242 @@
+/*
+ * ZX Specific Extensions for Synopsys DW Multimedia Card Interface driver
+ *
+ * Copyright (C) 2016, Linaro Ltd.
+ * Copyright (C) 2016, ZTE Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+#include "dw_mmc-zx.h"
+
+struct dw_mci_zx_priv_data {
+	struct regmap	*sysc_base;
+};
+
+enum delay_type {
+	DELAY_TYPE_READ,	/* read dqs delay */
+	DELAY_TYPE_CLK,		/* clk sample delay */
+};
+
+static int dw_mci_zx_emmc_set_delay(struct dw_mci *host, unsigned int delay,
+				    enum delay_type dflag)
+{
+	struct dw_mci_zx_priv_data *priv = host->priv;
+	struct regmap *sysc_base = priv->sysc_base;
+	unsigned int clksel;
+	unsigned int loop = 1000;
+	int ret;
+
+	if (!sysc_base)
+		return -EINVAL;
+
+	ret = regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0,
+				 PARA_HALF_CLK_MODE | PARA_DLL_BYPASS_MODE |
+				 PARA_PHASE_DET_SEL_MASK |
+				 PARA_DLL_LOCK_NUM_MASK |
+				 DLL_REG_SET | PARA_DLL_START_MASK,
+				 PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4));
+	if (ret)
+		return ret;
+
+	ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG1, &clksel);
+	if (ret)
+		return ret;
+
+	if (dflag == DELAY_TYPE_CLK) {
+		clksel &= ~CLK_SAMP_DELAY_MASK;
+		clksel |= CLK_SAMP_DELAY(delay);
+	} else {
+		clksel &= ~READ_DQS_DELAY_MASK;
+		clksel |= READ_DQS_DELAY(delay);
+	}
+
+	regmap_write(sysc_base, LB_AON_EMMC_CFG_REG1, clksel);
+	regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0,
+			   PARA_DLL_START_MASK | PARA_DLL_LOCK_NUM_MASK |
+			   DLL_REG_SET,
+			   PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4) |
+			   DLL_REG_SET);
+
+	do {
+		ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG2, &clksel);
+		if (ret)
+			return ret;
+
+	} while (--loop && !(clksel & ZX_DLL_LOCKED));
+
+	if (!loop) {
+		dev_err(host->dev, "Error: %s dll lock fail\n", __func__);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int dw_mci_zx_emmc_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
+{
+	struct dw_mci *host = slot->host;
+	struct mmc_host *mmc = slot->mmc;
+	int len, start = 0, end = 0, delay, best = 0;
+
+	for (delay = 1 ; delay < 128; delay++) {
+		dw_mci_zx_emmc_set_delay(host, delay, DELAY_TYPE_CLK);
+		if (mmc_send_tuning(mmc, opcode, NULL)) {
+			if (start >= 0) {
+				end = delay - 1;
+				/* check and update longest good range */
+				if ((end - start) > len) {
+					best = (start + end) >> 1;
+					len = end - start;
+				}
+			}
+			start = -1;
+			end = 0;
+			continue;
+		}
+		if (start < 0)
+			start = delay;
+	}
+
+	if (start >= 0) {
+		end = delay - 1;
+		if ((end - start) > len) {
+			best = (start + end) >> 1;
+			len = end - start;
+		}
+	}
+	if (best < 0)
+		return -EIO;
+
+	dev_info(host->dev, "%s best range: start %d end %d\n", __func__,
+		 start, end);
+	dw_mci_zx_emmc_set_delay(host, best, DELAY_TYPE_CLK);
+	return 0;
+}
+
+static int dw_mci_zx_prepare_hs400_tuning(struct dw_mci *host,
+					  struct mmc_ios *ios)
+{
+	int ret;
+
+	/* config phase shift as 90 degree */
+	ret = dw_mci_zx_emmc_set_delay(host, 32, DELAY_TYPE_READ);
+	if (ret < 0)
+		return -EIO;
+
+	return 0;
+}
+
+static int dw_mci_zx_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
+{
+	struct dw_mci *host = slot->host;
+
+	if (host->verid == 0x290a) /* only for emmc */
+		return dw_mci_zx_emmc_execute_tuning(slot, opcode);
+	/* TODO: Add 0x210a dedicated tuning for sd/sdio */
+
+	return 0;
+}
+
+static int dw_mci_zx_parse_dt(struct dw_mci *host)
+{
+	struct device_node *np = host->dev->of_node;
+	struct device_node *node;
+	struct dw_mci_zx_priv_data *priv;
+	struct regmap *sysc_base;
+	int ret;
+
+	/* syscon is needed only by emmc */
+	node = of_parse_phandle(np, "zte,aon-syscon", 0);
+	if (node) {
+		sysc_base = syscon_node_to_regmap(node);
+		of_node_put(node);
+
+		if (IS_ERR(sysc_base)) {
+			ret = PTR_ERR(sysc_base);
+			if (ret != -EPROBE_DEFER)
+				dev_err(host->dev, "Can't get syscon: %d\n",
+					ret);
+			return ret;
+		}
+	} else {
+		return 0;
+	}
+
+	priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	priv->sysc_base = sysc_base;
+	host->priv = priv;
+
+	return 0;
+}
+
+static unsigned long zx_dwmmc_caps[3] = {
+	MMC_CAP_CMD23,
+	MMC_CAP_CMD23,
+	MMC_CAP_CMD23,
+};
+
+static const struct dw_mci_drv_data zx_drv_data = {
+	.caps			= zx_dwmmc_caps,
+	.execute_tuning		= dw_mci_zx_execute_tuning,
+	.prepare_hs400_tuning	= dw_mci_zx_prepare_hs400_tuning,
+	.parse_dt               = dw_mci_zx_parse_dt,
+};
+
+static const struct of_device_id dw_mci_zx_match[] = {
+	{ .compatible = "zte,zx296718-dw-mshc", .data = &zx_drv_data},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_zx_match);
+
+static int dw_mci_zx_probe(struct platform_device *pdev)
+{
+	const struct dw_mci_drv_data *drv_data;
+	const struct of_device_id *match;
+
+	match = of_match_node(dw_mci_zx_match, pdev->dev.of_node);
+	drv_data = match->data;
+
+	return dw_mci_pltfm_register(pdev, drv_data);
+}
+
+static const struct dev_pm_ops dw_mci_zx_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				pm_runtime_force_resume)
+	SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
+			   dw_mci_runtime_resume,
+			   NULL)
+};
+
+static struct platform_driver dw_mci_zx_pltfm_driver = {
+	.probe		= dw_mci_zx_probe,
+	.remove		= dw_mci_pltfm_remove,
+	.driver		= {
+		.name		= "dwmmc_zx",
+		.of_match_table	= dw_mci_zx_match,
+		.pm		= &dw_mci_zx_dev_pm_ops,
+	},
+};
+
+module_platform_driver(dw_mci_zx_pltfm_driver);
+
+MODULE_DESCRIPTION("ZTE emmc/sd driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/dw_mmc-zx.h b/drivers/mmc/host/dw_mmc-zx.h
new file mode 100644
index 0000000..f369997
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-zx.h
@@ -0,0 +1,31 @@
+#ifndef _DW_MMC_ZX_H_
+#define _DW_MMC_ZX_H_
+
+/* ZX296718 SoC specific DLL register offset. */
+#define LB_AON_EMMC_CFG_REG0  0x1B0
+#define LB_AON_EMMC_CFG_REG1  0x1B4
+#define LB_AON_EMMC_CFG_REG2  0x1B8
+
+/* LB_AON_EMMC_CFG_REG0 register defines */
+#define PARA_DLL_START(x)	((x) & 0xFF)
+#define PARA_DLL_START_MASK	0xFF
+#define DLL_REG_SET		BIT(8)
+#define PARA_DLL_LOCK_NUM(x)	(((x) & 7) << 16)
+#define PARA_DLL_LOCK_NUM_MASK  (7 << 16)
+#define PARA_PHASE_DET_SEL(x)	(((x) & 7) << 20)
+#define PARA_PHASE_DET_SEL_MASK	(7 << 20)
+#define PARA_DLL_BYPASS_MODE	BIT(23)
+#define PARA_HALF_CLK_MODE	BIT(24)
+
+/* LB_AON_EMMC_CFG_REG1 register defines */
+#define READ_DQS_DELAY(x)	((x) & 0x7F)
+#define READ_DQS_DELAY_MASK	(0x7F)
+#define READ_DQS_BYPASS_MODE	BIT(7)
+#define CLK_SAMP_DELAY(x)	(((x) & 0x7F) << 8)
+#define CLK_SAMP_DELAY_MASK	(0x7F << 8)
+#define CLK_SAMP_BYPASS_MODE	BIT(15)
+
+/* LB_AON_EMMC_CFG_REG2 register defines */
+#define ZX_DLL_LOCKED		BIT(2)
+
+#endif /* _DW_MMC_ZX_H_ */
-- 
1.9.1


^ permalink raw reply related

* Re: [PATCH v3] mmc: mxs: Initialize the spinlock prior to using it
From: Ulf Hansson @ 2016-11-07 12:43 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: Sascha Hauer, Marek Vašut, Stefan Wahren, linux-mmc,
	Fabio Estevam
In-Reply-To: <1478375107-32513-1-git-send-email-festevam@gmail.com>

On 5 November 2016 at 20:45, Fabio Estevam <festevam@gmail.com> wrote:
> From: Fabio Estevam <fabio.estevam@nxp.com>
>
> An interrupt may occur right after devm_request_irq() is called and
> prior to the spinlock initialization, leading to a kernel oops,
> as the interrupt handler uses the spinlock.
>
> In order to prevent this problem, move the spinlock initialization
> prior to requesting the interrupts.
>
> Fixes: e4243f13d10e (mmc: mxs-mmc: add mmc host driver for i.MX23/28)
> Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
> Reviewed-by: Marek Vasut <marex@denx.de>

Thanks, applied for fixes!

Kind regards
Uffe

> ---
> Changes since v2:
> - Add a Fixes tag as suggested by Stefan
> Changes since v1:
> - Make it a standalone patch instead of patch series
> - Cc stable as suggested by Stefan
>
>  drivers/mmc/host/mxs-mmc.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
> index d839147..44ecebd 100644
> --- a/drivers/mmc/host/mxs-mmc.c
> +++ b/drivers/mmc/host/mxs-mmc.c
> @@ -661,13 +661,13 @@ static int mxs_mmc_probe(struct platform_device *pdev)
>
>         platform_set_drvdata(pdev, mmc);
>
> +       spin_lock_init(&host->lock);
> +
>         ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0,
>                                dev_name(&pdev->dev), host);
>         if (ret)
>                 goto out_free_dma;
>
> -       spin_lock_init(&host->lock);
> -
>         ret = mmc_add_host(mmc);
>         if (ret)
>                 goto out_free_dma;
> --
> 2.7.4
>

^ permalink raw reply

* Re: [PATCH 0/4] mmc: sdhci: Fixes
From: Ulf Hansson @ 2016-11-07 12:43 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: linux-mmc, Harjani Ritesh
In-Reply-To: <1478094551-24819-1-git-send-email-adrian.hunter@intel.com>

On 2 November 2016 at 14:49, Adrian Hunter <adrian.hunter@intel.com> wrote:
> Hi
>
> Here are 4 small sdhci fixes.  Some of the fixes improve sdhci ability to
> deal with errors (e.g. CRC errors ) when software command queuing is active.
>
>
> Adrian Hunter (4):
>       mmc: sdhci: Fix CMD line reset interfering with ongoing data transfer
>       mmc: sdhci: Fix unexpected data interrupt handling
>       mmc: sdhci: Reset cmd and data circuits after tuning failure
>       mmc: sdhci: Fix missing enhanced strobe setting during runtime resume
>
>  drivers/mmc/host/sdhci.c | 36 ++++++++++++++++++++++++++----------
>  1 file changed, 26 insertions(+), 10 deletions(-)
>
>
> Regards
> Adrian

Thanks, applied for fixes!

Kind regards
Uffe

^ permalink raw reply

* Re: [PATCH 0/3] MMC cleanups
From: Ulf Hansson @ 2016-11-07 12:43 UTC (permalink / raw)
  To: Linus Walleij; +Cc: linux-mmc, Chunyan Zhang, Baolin Wang
In-Reply-To: <1478253919-2469-1-git-send-email-linus.walleij@linaro.org>

On 4 November 2016 at 11:05, Linus Walleij <linus.walleij@linaro.org> wrote:
> This is a set of three cleanup patches for the MMC core/card/block
> code. I had to make them to start to understand what the code was
> doing.
>
> It would be great if we could apply these as a base for further
> refactoring of the core code, e.g. for multiqueue efforts.
>
> Linus Walleij (3):
>   mmc: block: make gen_err a bool variable
>   mmc: block: convert ecc_err to a bool
>   mmc: core: use enum mmc_blk_status properly
>
>  drivers/mmc/card/block.c    | 34 ++++++++++++++++++----------------
>  drivers/mmc/card/mmc_test.c | 44 ++++++++++++++++++++++++++++++++++----------
>  drivers/mmc/core/core.c     | 35 ++++++++++++++++++-----------------
>  include/linux/mmc/card.h    | 12 ------------
>  include/linux/mmc/core.h    | 15 ++++++++++++++-
>  include/linux/mmc/host.h    |  2 +-
>  6 files changed, 85 insertions(+), 57 deletions(-)
>
> --
> 2.7.4
>

Thanks, applied for next!

Kind regards
Uffe

^ permalink raw reply

* Re: [PATCH 2/3 v2] mmc: mmci: refactor ST Micro busy detection
From: Ulf Hansson @ 2016-11-07 12:43 UTC (permalink / raw)
  To: Linus Walleij
  Cc: linux-mmc, linux-arm-kernel@lists.infradead.org,
	Srinivas Kandagatla, Russell King
In-Reply-To: <1477386367-18514-2-git-send-email-linus.walleij@linaro.org>

On 25 October 2016 at 11:06, Linus Walleij <linus.walleij@linaro.org> wrote:
> The ST Micro-specific busy detection was made after the assumption
> that only this variant supports busy detection. So when doing busy
> detection, the host immediately tries to use some ST-specific
> register bits.
>
> Since the qualcomm variant also supports some busy detection
> schemes, encapsulate the variant flags better in the variant struct
> and prepare to add more variants by just providing some bitmasks
> to the logic.
>
> Put the entire busy detection logic within an if()-clause in the
> mmci_cmd_irq() function so the code is only executed when busy
> detection is enabled, and so that it is kept in (almost) one
> place, and add comments describing what is going on so the
> code can be understood.
>
> Tested on the Ux500 by introducing some prints in the busy
> detection path and noticing how the IRQ is enabled, used and
> disabled successfully.
>
> Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

Thanks, applied for next!

Kind regards
Uffe

> ---
> ChangeLog v1->v2:
> - Rebase on new register naming.
> ---
>  drivers/mmc/host/mmci.c | 113 +++++++++++++++++++++++++++++++++++-------------
>  drivers/mmc/host/mmci.h |   2 +-
>  2 files changed, 85 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index 6a8ea9c633d4..7f68fa7a961e 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -71,7 +71,12 @@ static unsigned int fmax = 515633;
>   * @f_max: maximum clk frequency supported by the controller.
>   * @signal_direction: input/out direction of bus signals can be indicated
>   * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
> - * @busy_detect: true if busy detection on dat0 is supported
> + * @busy_detect: true if the variant supports busy detection on DAT0.
> + * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM
> + * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register
> + *                   indicating that the card is busy
> + * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for
> + *                   getting busy end detection interrupts
>   * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
>   * @explicit_mclk_control: enable explicit mclk control in driver.
>   * @qcom_fifo: enables qcom specific fifo pio read logic.
> @@ -98,6 +103,9 @@ struct variant_data {
>         bool                    signal_direction;
>         bool                    pwrreg_clkgate;
>         bool                    busy_detect;
> +       u32                     busy_dpsm_flag;
> +       u32                     busy_detect_flag;
> +       u32                     busy_detect_mask;
>         bool                    pwrreg_nopower;
>         bool                    explicit_mclk_control;
>         bool                    qcom_fifo;
> @@ -178,6 +186,9 @@ static struct variant_data variant_ux500 = {
>         .signal_direction       = true,
>         .pwrreg_clkgate         = true,
>         .busy_detect            = true,
> +       .busy_dpsm_flag         = MCI_DPSM_ST_BUSYMODE,
> +       .busy_detect_flag       = MCI_ST_CARDBUSY,
> +       .busy_detect_mask       = MCI_ST_BUSYENDMASK,
>         .pwrreg_nopower         = true,
>  };
>
> @@ -199,6 +210,9 @@ static struct variant_data variant_ux500v2 = {
>         .signal_direction       = true,
>         .pwrreg_clkgate         = true,
>         .busy_detect            = true,
> +       .busy_dpsm_flag         = MCI_DPSM_ST_BUSYMODE,
> +       .busy_detect_flag       = MCI_ST_CARDBUSY,
> +       .busy_detect_mask       = MCI_ST_BUSYENDMASK,
>         .pwrreg_nopower         = true,
>  };
>
> @@ -220,6 +234,7 @@ static struct variant_data variant_qcom = {
>         .qcom_dml               = true,
>  };
>
> +/* Busy detection for the ST Micro variant */
>  static int mmci_card_busy(struct mmc_host *mmc)
>  {
>         struct mmci_host *host = mmc_priv(mmc);
> @@ -227,7 +242,7 @@ static int mmci_card_busy(struct mmc_host *mmc)
>         int busy = 0;
>
>         spin_lock_irqsave(&host->lock, flags);
> -       if (readl(host->base + MMCISTATUS) & MCI_ST_CARDBUSY)
> +       if (readl(host->base + MMCISTATUS) & host->variant->busy_detect_flag)
>                 busy = 1;
>         spin_unlock_irqrestore(&host->lock, flags);
>
> @@ -294,8 +309,8 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
>   */
>  static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)
>  {
> -       /* Keep ST Micro busy mode if enabled */
> -       datactrl |= host->datactrl_reg & MCI_DPSM_ST_BUSYMODE;
> +       /* Keep busy mode in DPSM if enabled */
> +       datactrl |= host->datactrl_reg & host->variant->busy_dpsm_flag;
>
>         if (host->datactrl_reg != datactrl) {
>                 host->datactrl_reg = datactrl;
> @@ -973,37 +988,66 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
>              unsigned int status)
>  {
>         void __iomem *base = host->base;
> -       bool sbc, busy_resp;
> +       bool sbc;
>
>         if (!cmd)
>                 return;
>
>         sbc = (cmd == host->mrq->sbc);
> -       busy_resp = host->variant->busy_detect && (cmd->flags & MMC_RSP_BUSY);
>
> -       if (!((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|
> -               MCI_CMDSENT|MCI_CMDRESPEND)))
> +       /*
> +        * We need to be one of these interrupts to be considered worth
> +        * handling. Note that we tag on any latent IRQs postponed
> +        * due to waiting for busy status.
> +        */
> +       if (!((status|host->busy_status) &
> +             (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND)))
>                 return;
>
> -       /* Check if we need to wait for busy completion. */
> -       if (host->busy_status && (status & MCI_ST_CARDBUSY))
> -               return;
> +       /*
> +        * ST Micro variant: handle busy detection.
> +        */
> +       if (host->variant->busy_detect) {
> +               bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY);
>
> -       /* Enable busy completion if needed and supported. */
> -       if (!host->busy_status && busy_resp &&
> -               !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
> -               (readl(base + MMCISTATUS) & MCI_ST_CARDBUSY)) {
> -               writel(readl(base + MMCIMASK0) | MCI_ST_BUSYEND,
> -                       base + MMCIMASK0);
> -               host->busy_status = status & (MCI_CMDSENT|MCI_CMDRESPEND);
> -               return;
> -       }
> +               /* We are busy with a command, return */
> +               if (host->busy_status &&
> +                   (status & host->variant->busy_detect_flag))
> +                       return;
> +
> +               /*
> +                * We were not busy, but we now got a busy response on
> +                * something that was not an error, and we double-check
> +                * that the special busy status bit is still set before
> +                * proceeding.
> +                */
> +               if (!host->busy_status && busy_resp &&
> +                   !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
> +                   (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) {
> +                       /* Unmask the busy IRQ */
> +                       writel(readl(base + MMCIMASK0) |
> +                              host->variant->busy_detect_mask,
> +                              base + MMCIMASK0);
> +                       /*
> +                        * Now cache the last response status code (until
> +                        * the busy bit goes low), and return.
> +                        */
> +                       host->busy_status =
> +                               status & (MCI_CMDSENT|MCI_CMDRESPEND);
> +                       return;
> +               }
>
> -       /* At busy completion, mask the IRQ and complete the request. */
> -       if (host->busy_status) {
> -               writel(readl(base + MMCIMASK0) & ~MCI_ST_BUSYEND,
> -                       base + MMCIMASK0);
> -               host->busy_status = 0;
> +               /*
> +                * At this point we are not busy with a command, we have
> +                * not recieved a new busy request, mask the busy IRQ and
> +                * fall through to process the IRQ.
> +                */
> +               if (host->busy_status) {
> +                       writel(readl(base + MMCIMASK0) &
> +                              ~host->variant->busy_detect_mask,
> +                              base + MMCIMASK0);
> +                       host->busy_status = 0;
> +               }
>         }
>
>         host->cmd = NULL;
> @@ -1257,9 +1301,11 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
>                         mmci_data_irq(host, host->data, status);
>                 }
>
> -               /* Don't poll for busy completion in irq context. */
> -               if (host->busy_status)
> -                       status &= ~MCI_ST_CARDBUSY;
> +               /*
> +                * Don't poll for busy completion in irq context.
> +                */
> +               if (host->variant->busy_detect && host->busy_status)
> +                       status &= ~host->variant->busy_detect_flag;
>
>                 ret = 1;
>         } while (status);
> @@ -1612,9 +1658,18 @@ static int mmci_probe(struct amba_device *dev,
>         /* We support these capabilities. */
>         mmc->caps |= MMC_CAP_CMD23;
>
> +       /*
> +        * Enable busy detection.
> +        */
>         if (variant->busy_detect) {
>                 mmci_ops.card_busy = mmci_card_busy;
> -               mmci_write_datactrlreg(host, MCI_DPSM_ST_BUSYMODE);
> +               /*
> +                * Not all variants have a flag to enable busy detection
> +                * in the DPSM, but if they do, set it here.
> +                */
> +               if (variant->busy_dpsm_flag)
> +                       mmci_write_datactrlreg(host,
> +                                              host->variant->busy_dpsm_flag);
>                 mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
>                 mmc->max_busy_timeout = 0;
>         }
> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
> index 7cabf270050b..56322c6afba4 100644
> --- a/drivers/mmc/host/mmci.h
> +++ b/drivers/mmc/host/mmci.h
> @@ -174,7 +174,7 @@
>  /* Extended status bits for the ST Micro variants */
>  #define MCI_ST_SDIOITMASK      (1 << 22)
>  #define MCI_ST_CEATAENDMASK    (1 << 23)
> -#define MCI_ST_BUSYEND         (1 << 24)
> +#define MCI_ST_BUSYENDMASK     (1 << 24)
>
>  #define MMCIMASK1              0x040
>  #define MMCIFIFOCNT            0x048
> --
> 2.7.4
>

^ permalink raw reply

* Re: [PATCH 1/3 v2] mmc: mmci: clean up header defines
From: Ulf Hansson @ 2016-11-07 12:43 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Srinivas Kandagatla, linux-mmc,
	linux-arm-kernel@lists.infradead.org, Russell King
In-Reply-To: <1477386367-18514-1-git-send-email-linus.walleij@linaro.org>

On 25 October 2016 at 11:06, Linus Walleij <linus.walleij@linaro.org> wrote:
> There was some confusion in the CPSM (Command Path State Machine)
> and DPSM (Data Path State Machine) regarding the naming of the
> registers, clarify the meaning of this acronym so the naming is
> understandable, and consistently use BIT() to define these fields.
>
> Consequently name the register bit defines MCI_[C|D]PSM_* and
> adjust the driver as well.
>
> Include new definitions for a few bits found in a patch from
> Srinivas Kandagatla.
>
> Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

Thanks, applied for next!

Kind regards
Uffe

> ---
> ChangeLog v1->v2:
> - Use more consequent register naming.
> ---
>  drivers/mmc/host/mmci.c | 16 ++++++------
>  drivers/mmc/host/mmci.h | 69 +++++++++++++++++++++++++++----------------------
>  2 files changed, 46 insertions(+), 39 deletions(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index df990bb8c873..6a8ea9c633d4 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -137,7 +137,7 @@ static struct variant_data variant_u300 = {
>         .clkreg_enable          = MCI_ST_U300_HWFCEN,
>         .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
>         .datalength_bits        = 16,
> -       .datactrl_mask_sdio     = MCI_ST_DPSM_SDIOEN,
> +       .datactrl_mask_sdio     = MCI_DPSM_ST_SDIOEN,
>         .st_sdio                        = true,
>         .pwrreg_powerup         = MCI_PWR_ON,
>         .f_max                  = 100000000,
> @@ -152,7 +152,7 @@ static struct variant_data variant_nomadik = {
>         .clkreg                 = MCI_CLK_ENABLE,
>         .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
>         .datalength_bits        = 24,
> -       .datactrl_mask_sdio     = MCI_ST_DPSM_SDIOEN,
> +       .datactrl_mask_sdio     = MCI_DPSM_ST_SDIOEN,
>         .st_sdio                = true,
>         .st_clkdiv              = true,
>         .pwrreg_powerup         = MCI_PWR_ON,
> @@ -170,7 +170,7 @@ static struct variant_data variant_ux500 = {
>         .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
>         .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE,
>         .datalength_bits        = 24,
> -       .datactrl_mask_sdio     = MCI_ST_DPSM_SDIOEN,
> +       .datactrl_mask_sdio     = MCI_DPSM_ST_SDIOEN,
>         .st_sdio                = true,
>         .st_clkdiv              = true,
>         .pwrreg_powerup         = MCI_PWR_ON,
> @@ -188,9 +188,9 @@ static struct variant_data variant_ux500v2 = {
>         .clkreg_enable          = MCI_ST_UX500_HWFCEN,
>         .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
>         .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE,
> -       .datactrl_mask_ddrmode  = MCI_ST_DPSM_DDRMODE,
> +       .datactrl_mask_ddrmode  = MCI_DPSM_ST_DDRMODE,
>         .datalength_bits        = 24,
> -       .datactrl_mask_sdio     = MCI_ST_DPSM_SDIOEN,
> +       .datactrl_mask_sdio     = MCI_DPSM_ST_SDIOEN,
>         .st_sdio                = true,
>         .st_clkdiv              = true,
>         .blksz_datactrl16       = true,
> @@ -210,7 +210,7 @@ static struct variant_data variant_qcom = {
>                                   MCI_QCOM_CLK_SELECT_IN_FBCLK,
>         .clkreg_8bit_bus_enable = MCI_QCOM_CLK_WIDEBUS_8,
>         .datactrl_mask_ddrmode  = MCI_QCOM_CLK_SELECT_IN_DDR_MODE,
> -       .data_cmd_enable        = MCI_QCOM_CSPM_DATCMD,
> +       .data_cmd_enable        = MCI_CPSM_QCOM_DATCMD,
>         .blksz_datactrl4        = true,
>         .datalength_bits        = 24,
>         .pwrreg_powerup         = MCI_PWR_UP,
> @@ -295,7 +295,7 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
>  static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)
>  {
>         /* Keep ST Micro busy mode if enabled */
> -       datactrl |= host->datactrl_reg & MCI_ST_DPSM_BUSYMODE;
> +       datactrl |= host->datactrl_reg & MCI_DPSM_ST_BUSYMODE;
>
>         if (host->datactrl_reg != datactrl) {
>                 host->datactrl_reg = datactrl;
> @@ -1614,7 +1614,7 @@ static int mmci_probe(struct amba_device *dev,
>
>         if (variant->busy_detect) {
>                 mmci_ops.card_busy = mmci_card_busy;
> -               mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
> +               mmci_write_datactrlreg(host, MCI_DPSM_ST_BUSYMODE);
>                 mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
>                 mmc->max_busy_timeout = 0;
>         }
> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
> index a1f5e4f49e2a..7cabf270050b 100644
> --- a/drivers/mmc/host/mmci.h
> +++ b/drivers/mmc/host/mmci.h
> @@ -51,25 +51,27 @@
>  #define MCI_QCOM_CLK_SELECT_IN_DDR_MODE        (BIT(14) | BIT(15))
>
>  #define MMCIARGUMENT           0x008
> -#define MMCICOMMAND            0x00c
> -#define MCI_CPSM_RESPONSE      (1 << 6)
> -#define MCI_CPSM_LONGRSP       (1 << 7)
> -#define MCI_CPSM_INTERRUPT     (1 << 8)
> -#define MCI_CPSM_PENDING       (1 << 9)
> -#define MCI_CPSM_ENABLE                (1 << 10)
> -/* Argument flag extenstions in the ST Micro versions */
> -#define MCI_ST_SDIO_SUSP       (1 << 11)
> -#define MCI_ST_ENCMD_COMPL     (1 << 12)
> -#define MCI_ST_NIEN            (1 << 13)
> -#define MCI_ST_CE_ATACMD       (1 << 14)
>
> -/* Modified on Qualcomm Integrations */
> -#define MCI_QCOM_CSPM_DATCMD           BIT(12)
> -#define MCI_QCOM_CSPM_MCIABORT         BIT(13)
> -#define MCI_QCOM_CSPM_CCSENABLE                BIT(14)
> -#define MCI_QCOM_CSPM_CCSDISABLE       BIT(15)
> -#define MCI_QCOM_CSPM_AUTO_CMD19       BIT(16)
> -#define MCI_QCOM_CSPM_AUTO_CMD21       BIT(21)
> +/* The command register controls the Command Path State Machine (CPSM) */
> +#define MMCICOMMAND            0x00c
> +#define MCI_CPSM_RESPONSE      BIT(6)
> +#define MCI_CPSM_LONGRSP       BIT(7)
> +#define MCI_CPSM_INTERRUPT     BIT(8)
> +#define MCI_CPSM_PENDING       BIT(9)
> +#define MCI_CPSM_ENABLE                BIT(10)
> +/* Command register flag extenstions in the ST Micro versions */
> +#define MCI_CPSM_ST_SDIO_SUSP          BIT(11)
> +#define MCI_CPSM_ST_ENCMD_COMPL                BIT(12)
> +#define MCI_CPSM_ST_NIEN               BIT(13)
> +#define MCI_CPSM_ST_CE_ATACMD          BIT(14)
> +/* Command register flag extensions in the Qualcomm versions */
> +#define MCI_CPSM_QCOM_PROGENA          BIT(11)
> +#define MCI_CPSM_QCOM_DATCMD           BIT(12)
> +#define MCI_CPSM_QCOM_MCIABORT         BIT(13)
> +#define MCI_CPSM_QCOM_CCSENABLE                BIT(14)
> +#define MCI_CPSM_QCOM_CCSDISABLE       BIT(15)
> +#define MCI_CPSM_QCOM_AUTO_CMD19       BIT(16)
> +#define MCI_CPSM_QCOM_AUTO_CMD21       BIT(21)
>
>  #define MMCIRESPCMD            0x010
>  #define MMCIRESPONSE0          0x014
> @@ -78,22 +80,27 @@
>  #define MMCIRESPONSE3          0x020
>  #define MMCIDATATIMER          0x024
>  #define MMCIDATALENGTH         0x028
> +
> +/* The data control register controls the Data Path State Machine (DPSM) */
>  #define MMCIDATACTRL           0x02c
> -#define MCI_DPSM_ENABLE                (1 << 0)
> -#define MCI_DPSM_DIRECTION     (1 << 1)
> -#define MCI_DPSM_MODE          (1 << 2)
> -#define MCI_DPSM_DMAENABLE     (1 << 3)
> -#define MCI_DPSM_BLOCKSIZE     (1 << 4)
> +#define MCI_DPSM_ENABLE                BIT(0)
> +#define MCI_DPSM_DIRECTION     BIT(1)
> +#define MCI_DPSM_MODE          BIT(2)
> +#define MCI_DPSM_DMAENABLE     BIT(3)
> +#define MCI_DPSM_BLOCKSIZE     BIT(4)
>  /* Control register extensions in the ST Micro U300 and Ux500 versions */
> -#define MCI_ST_DPSM_RWSTART    (1 << 8)
> -#define MCI_ST_DPSM_RWSTOP     (1 << 9)
> -#define MCI_ST_DPSM_RWMOD      (1 << 10)
> -#define MCI_ST_DPSM_SDIOEN     (1 << 11)
> +#define MCI_DPSM_ST_RWSTART    BIT(8)
> +#define MCI_DPSM_ST_RWSTOP     BIT(9)
> +#define MCI_DPSM_ST_RWMOD      BIT(10)
> +#define MCI_DPSM_ST_SDIOEN     BIT(11)
>  /* Control register extensions in the ST Micro Ux500 versions */
> -#define MCI_ST_DPSM_DMAREQCTL  (1 << 12)
> -#define MCI_ST_DPSM_DBOOTMODEEN        (1 << 13)
> -#define MCI_ST_DPSM_BUSYMODE   (1 << 14)
> -#define MCI_ST_DPSM_DDRMODE    (1 << 15)
> +#define MCI_DPSM_ST_DMAREQCTL  BIT(12)
> +#define MCI_DPSM_ST_DBOOTMODEEN        BIT(13)
> +#define MCI_DPSM_ST_BUSYMODE   BIT(14)
> +#define MCI_DPSM_ST_DDRMODE    BIT(15)
> +/* Control register extensions in the Qualcomm versions */
> +#define MCI_DPSM_QCOM_DATA_PEND        BIT(17)
> +#define MCI_DPSM_QCOM_RX_DATA_PEND BIT(20)
>
>  #define MMCIDATACNT            0x030
>  #define MMCISTATUS             0x034
> --
> 2.7.4
>

^ permalink raw reply

* Re: [PATCH v2 0/2] mmc: sdhci: Fix sdhci caps register bits with corrections provided by dt
From: Ulf Hansson @ 2016-11-07 12:42 UTC (permalink / raw)
  To: Zach Brown
  Cc: Adrian Hunter, Rob Herring, Mark Rutland, linux-mmc,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <1478100376-602-1-git-send-email-zach.brown@ni.com>

On 2 November 2016 at 16:26, Zach Brown <zach.brown@ni.com> wrote:
> For various reasons the sdhci caps register can be incorrect. This patch set
> introduces a general way to correct the bits when they are read to accurately
> reflect the capabilties of the controller/board combo.
>
> The first patch creates sdhci-caps and sdhci-caps-mask dt properties that
> combined represent the correction to the sdhci caps register.
>
> The second patch uses the new dt properties to correct the caps from the
> register as they read during __sdhci_read_caps.
>
> Changes from RFC:
>  * /s/registers/register
>  * Moved sdhci dt properties into new documentation file sdhci.txt
> v2:
>  * Fixed style issue, all branches of 'if' should have {}
>
>
> Zach Brown (2):
>   mmc: sdhci: dt: Add device tree properties sdhci-caps and
>     sdhci-caps-mask
>   mmc: sdhci: Use sdhci-caps-mask and sdhci-caps to change the caps read
>         during __sdhci_read_caps
>
>  Documentation/devicetree/bindings/mmc/sdhci.txt | 14 ++++++++++++++
>  drivers/mmc/host/sdhci.c                        | 24 ++++++++++++++++++++++--
>  2 files changed, 36 insertions(+), 2 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/mmc/sdhci.txt
>
> --
> 2.7.4
>

Thanks, applied for next!

Kind regards
Uffe

^ permalink raw reply

* Re: [PATCH v7 11/11] arm64: dts: r8a7796: salvator-x: Enable UHS-I SDR-104
From: Simon Horman @ 2016-11-07 12:32 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Wolfram Sang, Ulf Hansson, Magnus Damm, Linux MMC List,
	Linux-Renesas, linux-arm-kernel@lists.infradead.org
In-Reply-To: <CAMuHMdVLAK0TKLoB07_U1XNW9kCOyen80gPrkaXOLqAq0=BA+Q@mail.gmail.com>

On Tue, Oct 25, 2016 at 12:49:42PM +0200, Geert Uytterhoeven wrote:
> On Tue, Sep 13, 2016 at 12:57 PM, Simon Horman
> <horms+renesas@verge.net.au> wrote:
> > Add the sd-uhs-sdr104 property to SDHI0 and SDHI1.
> 
> SDHI0 and SDHI3.

Thanks, I will fix that.

^ permalink raw reply

* Re: [PATCH v7 03/11] arm64: dts: r8a7795: salvator-x: Enable UHS-I SDR-104
From: Simon Horman @ 2016-11-07 12:32 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Wolfram Sang, Ulf Hansson, Magnus Damm, Linux MMC List,
	Linux-Renesas, linux-arm-kernel@lists.infradead.org
In-Reply-To: <CAMuHMdW8WMFMWzjr+KnrifbOgvJV3dZWxC3zVYMfSvd=L6KAsw@mail.gmail.com>

On Tue, Oct 25, 2016 at 12:48:40PM +0200, Geert Uytterhoeven wrote:
> On Tue, Sep 13, 2016 at 12:57 PM, Simon Horman
> <horms+renesas@verge.net.au> wrote:
> > Add the sd-uhs-sdr104 property to SDHI0 and SDHI1.
> 
> SDHI0 and SDHI3?

Thanks, I will fix that.

^ permalink raw reply

* Re: [PATCH v2 0/2] mmc: sdhci-iproc: Add byte register access support
From: Ulf Hansson @ 2016-11-07 12:14 UTC (permalink / raw)
  To: Scott Branden
  Cc: Rob Herring, Mark Rutland, Ray Jui, Scott Branden, Adrian Hunter,
	BCM Kernel Feedback, linux-mmc, devicetree@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <1478018277-10097-1-git-send-email-scott.branden@broadcom.com>

On 1 November 2016 at 17:37, Scott Branden <scott.branden@broadcom.com> wrote:
> Add brcm,sdhci-iproc compat string and code for support of newer versions of
> sdhci-iproc controller that allow byte-wise register accesses.
>
> Changes from v1:
> - added details to bindings documentation to clarify usage of brcm,sdhci-iproc
> compatibility string
>
> Scott Branden (2):
>   mmc: sdhci-iproc: Add brcm,sdhci-iproc compat string in bindings
>     document
>   mmc: sdhci-iproc: support standard byte register accesses
>
>  .../devicetree/bindings/mmc/brcm,sdhci-iproc.txt   |  9 ++++++
>  drivers/mmc/host/sdhci-iproc.c                     | 35 ++++++++++++++++++++--
>  2 files changed, 42 insertions(+), 2 deletions(-)
>
> --
> 2.5.0
>

It seems I had already applied v1 of this series. I have dropped those
now, waiting to receive an ack from Rob first.

Kind regards
Uffe

^ permalink raw reply

* Re: [PATCH] mmc: sdhci-pci-core: Tuning mode support for HS200 on AMD Platforms
From: Ulf Hansson @ 2016-11-07 12:00 UTC (permalink / raw)
  To: Shyam Sundar S K
  Cc: Adrian Hunter, linux-mmc, Sen, Pankaj, Shah, Nehal-bakulchandra,
	Agrawal, Nitesh-kumar
In-Reply-To: <8510d441-230a-9d93-222b-ba870869f094@amd.com>

On 27 October 2016 at 11:52, Shyam Sundar S K <ssundark@amd.com> wrote:
> Made changes to the earlier submission based on the comments
> received from Adrian.
>
> Reviewed-by: Sen, Pankaj <Pankaj.Sen@amd.com>
> Reviewed-by: Shah, Nehal-bakulchandra <Nehal-bakulchandra.Shah@amd.com>
> Signed-off-by: S-k, Shyam-sundar <Shyam-sundar.S-k@amd.com>
>
> Also, adding patch from Adrian for handling the device specific
> private data.
>
> From: Adrian Hunter <adrian.hunter@intel.com>
> Date: Mon, 10 Oct 2016 10:04:45 +0300
> Subject: [PATCH] mmc: sdhci-pci: Let devices define their own private data

Shyam,

Seems like this should be two separate changes, one made by Adrian and
one by you. Can you please re-spin.

Kind regards
Uffe

>
> Let devices define their own private data to facilitate device-specific
> operations.  The size of the private structure is specified in the
> sdhci_pci_fixes structure, then sdhci_pci_probe_slot() will allocate extra
> space for it, and sdhci_pci_priv() can be used to get a reference to it.
>
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>  drivers/mmc/host/sdhci-pci-core.c | 181 +++++++++++++++++++++++++++++++++++++-
>  drivers/mmc/host/sdhci-pci.h      |   7 ++
>  2 files changed, 186 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
> index 1d9e00a..9576f82 100644
> --- a/drivers/mmc/host/sdhci-pci-core.c
> +++ b/drivers/mmc/host/sdhci-pci-core.c
> @@ -817,6 +817,171 @@ enum amd_chipset_gen {
>         AMD_CHIPSET_UNKNOWN,
>  };
>
> +static const struct sdhci_ops amd_sdhci_pci_ops;
> +
> +struct amd_tuning_descriptor {
> +       u8 tune_around;
> +       bool this_tune_ok;
> +       bool last_tune_ok;
> +       u8 valid_front;
> +       u8 valid_window_max;
> +       u8 tune_low_max;
> +       u8 tune_low;
> +       u8 valid_window;
> +       u8 tune_result;
> +};
> +
> +static int amd_tuning_reset(struct sdhci_host *host)
> +{
> +       unsigned int val;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&host->lock, flags);
> +
> +       val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +       val |= SDHCI_CTRL_PRESET_VAL_ENABLE | SDHCI_CTRL_EXEC_TUNING;
> +       sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
> +
> +       val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
> +       val &= ~SDHCI_CTRL_EXEC_TUNING;
> +       sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
> +
> +       spin_unlock_irqrestore(&host->lock, flags);
> +
> +       return 0;
> +}
> +
> +static int amd_config_tuning_phase(struct sdhci_host *host, u8 phase)
> +{
> +       struct sdhci_pci_slot *slot = sdhci_priv(host);
> +       struct pci_dev *pdev = slot->chip->pdev;
> +       unsigned int val;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&host->lock, flags);
> +
> +       pci_read_config_dword(pdev, 0xb8, &val);
> +       val &= ~0x1f;
> +       val |= (0x10800 | (phase << 1));
> +       pci_write_config_dword(pdev, 0xb8, val);
> +
> +       spin_unlock_irqrestore(&host->lock, flags);
> +
> +       return 0;
> +}
> +
> +static int amd_find_good_phase(struct sdhci_host *host)
> +{
> +       struct sdhci_pci_slot *slot = sdhci_priv(host);
> +       struct pci_dev *pdev = slot->chip->pdev;
> +       struct amd_tuning_descriptor *td = sdhci_pci_priv(slot);
> +
> +       unsigned int val;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&host->lock, flags);
> +
> +       if (td->this_tune_ok)
> +               td->valid_front += 1;
> +       if ((!td->this_tune_ok && td->last_tune_ok) ||
> +           (td->tune_around == 11)) {
> +               if (td->valid_window > td->valid_window_max) {
> +                       td->valid_window_max = td->valid_window;
> +                       td->tune_low_max = td->tune_low;
> +               }
> +       }
> +       if (td->this_tune_ok && (!td->last_tune_ok))
> +               td->tune_low = td->tune_around;
> +       if (!td->this_tune_ok && td->last_tune_ok)
> +               td->valid_window = 0;
> +       else if (td->this_tune_ok)
> +               td->valid_window += 1;
> +
> +       td->last_tune_ok = td->this_tune_ok;
> +
> +       if (td->tune_around == 11) {
> +               if ((td->valid_front + td->valid_window) >
> +                                               td->valid_window_max) {
> +                       if (td->valid_front > td->valid_window)
> +                               td->tune_result = ((td->valid_front -
> +                                               td->valid_window) >> 1);
> +                       else
> +                               td->tune_result = td->tune_low +
> +                               ((td->valid_window + td->valid_front) >> 1);
> +               } else {
> +                       td->tune_result = td->tune_low_max +
> +                                       (td->valid_window_max >> 1);
> +               }
> +
> +               if (td->tune_result > 0x0b)
> +                       td->tune_result = 0x0b;
> +
> +               pci_read_config_dword(pdev, 0xb8, &val);
> +               val &= ~0x1f;
> +               val |= (0x10800 | (td->tune_result << 1));
> +               pci_write_config_dword(pdev, 0xb8, val);
> +       }
> +
> +       spin_unlock_irqrestore(&host->lock, flags);
> +
> +       return 0;
> +}
> +
> +static int amd_enable_manual_tuning(struct sdhci_pci_slot *slot)
> +{
> +       struct pci_dev *pdev = slot->chip->pdev;
> +       unsigned int val;
> +
> +       pci_read_config_dword(pdev, 0xd0, &val);
> +       val &= 0xffffffcf;
> +       val |= 0x30;
> +       pci_write_config_dword(pdev, 0xd0, val);
> +
> +       return 0;
> +}
> +
> +static int amd_execute_tuning(struct sdhci_host *host, u32 opcode)
> +{
> +       struct sdhci_pci_slot *slot = sdhci_priv(host);
> +       struct amd_tuning_descriptor *td = sdhci_pci_priv(slot);
> +       u8 ctrl;
> +
> +       amd_tuning_reset(host);
> +       memset(td, 0, sizeof(struct amd_tuning_descriptor));
> +
> +       /*********************************************************************/
> +       /* Enabling Software Tuning */
> +       /********************************************************************/
> +       /* 1. First switch the eMMC to HS200 Mode
> +        * 2. Prepare the registers by using the sampling clock select
> +        * 3. Send the CMD21 12 times with block length of 64 bytes
> +        * 4. Everytime change the clk phase and check for CRC error
> +        *      (CMD and DATA),if error, soft reset the CMD and DATA line
> +        * 5. Calculate the window and set the clock phase.
> +        */
> +
> +       for (td->tune_around = 0; td->tune_around < 12; td->tune_around++) {
> +               amd_config_tuning_phase(host, td->tune_around);
> +
> +               if (mmc_send_tuning(host->mmc, opcode, NULL)) {
> +                       td->this_tune_ok = false;
> +                       host->mmc->need_retune = 0;
> +                       mdelay(4);
> +                       ctrl = SDHCI_RESET_CMD | SDHCI_RESET_DATA;
> +                       sdhci_writeb(host, ctrl, SDHCI_SOFTWARE_RESET);
> +               } else {
> +                       td->this_tune_ok = true;
> +               }
> +
> +               amd_find_good_phase(host);
> +       }
> +
> +       host->mmc->retune_period = 0;
> +
> +       amd_enable_manual_tuning(slot);
> +       return 0;
> +}
> +
>  static int amd_probe(struct sdhci_pci_chip *chip)
>  {
>         struct pci_dev  *smbus_dev;
> @@ -841,7 +1006,6 @@ static int amd_probe(struct sdhci_pci_chip *chip)
>
>         if ((gen == AMD_CHIPSET_BEFORE_ML) || (gen == AMD_CHIPSET_CZ)) {
>                 chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD;
> -               chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
>         }
>
>         return 0;
> @@ -849,6 +1013,7 @@ static int amd_probe(struct sdhci_pci_chip *chip)
>
>  static const struct sdhci_pci_fixes sdhci_amd = {
>         .probe          = amd_probe,
> +       .ops            = &amd_sdhci_pci_ops,
>  };
>
>  static const struct pci_device_id pci_ids[] = {
> @@ -1469,6 +1634,17 @@ static const struct sdhci_ops sdhci_pci_ops = {
>         .select_drive_strength  = sdhci_pci_select_drive_strength,
>  };
>
> +static const struct sdhci_ops amd_sdhci_pci_ops = {
> +       .set_clock      = sdhci_set_clock,
> +       .enable_dma     = sdhci_pci_enable_dma,
> +       .set_bus_width  = sdhci_pci_set_bus_width,
> +       .reset          = sdhci_reset,
> +       .set_uhs_signaling = sdhci_set_uhs_signaling,
> +       .hw_reset               = sdhci_pci_hw_reset,
> +       .select_drive_strength  = sdhci_pci_select_drive_strength,
> +       .platform_execute_tuning = amd_execute_tuning,
> +};
> +
>  /*****************************************************************************\
>   *                                                                           *
>   * Suspend/resume                                                            *
> @@ -1646,6 +1822,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
>         struct sdhci_pci_slot *slot;
>         struct sdhci_host *host;
>         int ret, bar = first_bar + slotno;
> +       size_t priv_size = chip->fixes ? chip->fixes->priv_size : 0;
>
>         if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
>                 dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar);
> @@ -1667,7 +1844,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
>                 return ERR_PTR(-ENODEV);
>         }
>
> -       host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot));
> +       host = sdhci_alloc_host(&pdev->dev, sizeof(*slot) + priv_size);
>         if (IS_ERR(host)) {
>                 dev_err(&pdev->dev, "cannot allocate host\n");
>                 return ERR_CAST(host);
> diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
> index 6bccf56..0bfd568 100644
> --- a/drivers/mmc/host/sdhci-pci.h
> +++ b/drivers/mmc/host/sdhci-pci.h
> @@ -67,6 +67,7 @@ struct sdhci_pci_fixes {
>         int                     (*resume) (struct sdhci_pci_chip *);
>
>         const struct sdhci_ops  *ops;
> +       size_t                  priv_size;
>  };
>
>  struct sdhci_pci_slot {
> @@ -87,6 +88,7 @@ struct sdhci_pci_slot {
>                                      struct mmc_card *card,
>                                      unsigned int max_dtr, int host_drv,
>                                      int card_drv, int *drv_type);
> +       unsigned long           private[0] ____cacheline_aligned;
>  };
>
>  struct sdhci_pci_chip {
> @@ -101,4 +103,9 @@ struct sdhci_pci_chip {
>         struct sdhci_pci_slot   *slots[MAX_SLOTS]; /* Pointers to host slots */
>  };
>
> +static inline void *sdhci_pci_priv(struct sdhci_pci_slot *slot)
> +{
> +       return (void *)slot->private;
> +}
> +
>  #endif /* __SDHCI_PCI_H */
> --
> 2.7.4
>
> On 10/10/2016 1:25 PM, Adrian Hunter wrote:
>> On 04/10/16 11:42, Shyam Sundar S K wrote:
>>> This patch adds support for HS200 tuning mode on AMD eMMC-4.5.1
>>>
>>> Reviewed-by: Sen, Pankaj <Pankaj.Sen@amd.com>
>>> Reviewed-by: Shah, Nehal-bakulchandra <Nehal-bakulchandra.Shah@amd.com>
>>> Signed-off-by: S-k, Shyam-sundar <Shyam-sundar.S-k@amd.com>
>>> ---
>>>  drivers/mmc/host/sdhci-pci-core.c | 182 +++++++++++++++++++++++++++++++++++++-
>>>  1 file changed, 180 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
>>> index 897cfd2..5893ec4 100644
>>> --- a/drivers/mmc/host/sdhci-pci-core.c
>>> +++ b/drivers/mmc/host/sdhci-pci-core.c
>>> @@ -734,6 +734,7 @@ static const struct sdhci_pci_fixes sdhci_via = {
>>>      .probe          = via_probe,
>>>  };
>>>
>>> +
>>
>> Unnecessary blank line
>>
>>>  static int rtsx_probe_slot(struct sdhci_pci_slot *slot)
>>>  {
>>>      slot->host->mmc->caps2 |= MMC_CAP2_HS200;
>>> @@ -755,6 +756,172 @@ enum amd_chipset_gen {
>>>      AMD_CHIPSET_UNKNOWN,
>>>  };
>>>
>>> +struct tuning_descriptor {
>>
>> It would be nicer to prefix all structure and function names by something
>> specific to the device e.g. amd_...
>>
>>> +    unsigned char tune_around;
>>> +    bool this_tune_ok;
>>> +    bool last_tune_ok;
>>> +    bool valid_front_end;
>>> +    unsigned char valid_front;
>>> +    unsigned char valid_window_max;
>>> +    unsigned char tune_low_max;
>>> +    unsigned char tune_low;
>>> +    unsigned char valid_window;
>>> +    unsigned char tune_result;
>>
>> 'unsigned char' -> 'u8'
>>
>>> +};
>>> +
>>> +static struct sdhci_ops sdhci_pci_ops;
>>> +static struct tuning_descriptor tdescriptor;
>>
>> tdescriptor should not be global.  You need somewhere to put private
>> data.  How about this:
>>
>>
>> From: Adrian Hunter <adrian.hunter@intel.com>
>> Date: Mon, 10 Oct 2016 10:04:45 +0300
>> Subject: [PATCH] mmc: sdhci-pci: Let devices define their own private data
>>
>> Let devices define their own private data to facilitate device-specific
>> operations.  The size of the private structure is specified in the
>> sdhci_pci_fixes structure, then sdhci_pci_probe_slot() will allocate extra
>> space for it, and sdhci_pci_priv() can be used to get a reference to it.
>>
>> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
>> ---
>>  drivers/mmc/host/sdhci-pci-core.c | 3 ++-
>>  drivers/mmc/host/sdhci-pci.h      | 8 ++++++++
>>  2 files changed, 10 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
>> index 1d9e00a00e9f..782c8d25c0c8 100644
>> --- a/drivers/mmc/host/sdhci-pci-core.c
>> +++ b/drivers/mmc/host/sdhci-pci-core.c
>> @@ -1646,6 +1646,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
>>       struct sdhci_pci_slot *slot;
>>       struct sdhci_host *host;
>>       int ret, bar = first_bar + slotno;
>> +     size_t priv_size = chip->fixes ? chip->fixes->priv_size : 0;
>>
>>       if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
>>               dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar);
>> @@ -1667,7 +1668,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
>>               return ERR_PTR(-ENODEV);
>>       }
>>
>> -     host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot));
>> +     host = sdhci_alloc_host(&pdev->dev, sizeof(*slot) + priv_size);
>>       if (IS_ERR(host)) {
>>               dev_err(&pdev->dev, "cannot allocate host\n");
>>               return ERR_CAST(host);
>> diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
>> index 6bccf56bc5ff..6a1be6afe089 100644
>> --- a/drivers/mmc/host/sdhci-pci.h
>> +++ b/drivers/mmc/host/sdhci-pci.h
>> @@ -67,6 +67,7 @@ struct sdhci_pci_fixes {
>>       int                     (*resume) (struct sdhci_pci_chip *);
>>
>>       const struct sdhci_ops  *ops;
>> +     size_t                  priv_size;
>>  };
>>
>>  struct sdhci_pci_slot {
>> @@ -87,6 +88,8 @@ struct sdhci_pci_slot {
>>                                    struct mmc_card *card,
>>                                    unsigned int max_dtr, int host_drv,
>>                                    int card_drv, int *drv_type);
>> +
>> +     unsigned long           private[0] ____cacheline_aligned;
>>  };
>>
>>  struct sdhci_pci_chip {
>> @@ -101,4 +104,9 @@ struct sdhci_pci_chip {
>>       struct sdhci_pci_slot   *slots[MAX_SLOTS]; /* Pointers to host slots */
>>  };
>>
>> +static inline void *sdhci_pci_priv(struct sdhci_pci_slot *slot)
>> +{
>> +     return (void *)slot->private;
>> +}
>> +
>>  #endif /* __SDHCI_PCI_H */
>>

^ permalink raw reply

* Re: [PATCH] mmc: mmc: Use 500ms as the default generic CMD6 timeout
From: Ulf Hansson @ 2016-11-07 10:50 UTC (permalink / raw)
  To: Linus Walleij
  Cc: linux-mmc@vger.kernel.org, Jaehoon Chung, Adrian Hunter,
	Chaotian Jing, Stephen Boyd, stable
In-Reply-To: <CACRpkdZQCo9=TbwjJNppdQGArWLNxHSLuZVYnNOadnOt3773CQ@mail.gmail.com>

On 7 November 2016 at 11:03, Linus Walleij <linus.walleij@linaro.org> wrote:
> On Fri, Nov 4, 2016 at 6:32 PM, Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
>> In the eMMC 4.51 version of the spec, an EXT_CSD field called
>> GENERIC_CMD6_TIME[248] was added. This allows cards to specify the maximum
>> time it may need to move out from its busy state, when a CMD6 command has
>> been sent.
>>
>> In cases when the card is compliant to versions < 4.51 of the eMMC spec,
>> obviously the core needs to use a fall-back value for this timeout, which
>> currently is set to 10 minutes. This value is completely in the wrong range
>> and importantly in some cases it causes a card initialization to take more
>> than 10 minute to complete.
>>
>> Earlier this scenario was avoided as the mmc core used CMD13 to poll the
>> card, to find out when it stopped signaling busy. Commit 08573eaf1a70
>> ("mmc: mmc: do not use CMD13 to get status after speed mode switch")
>> changed this behavior.
>>
>> Instead of reverting that commit, which would cause other issues, let's
>> instead start by picking a simple solution for the problem, by using a
>> 500ms default generic CMD6 timeout.
>>
>> The reason for using exactly 500ms, comes from observations that shows it's
>> quite common for cards to specify 250ms. 500ms is two times that value so
>> likely it should be enough for most cards.
>>
>> Cc: <stable@vger.kernel.org> # v4.8+
>> Fixes: 08573eaf1a70 ("mmc: mmc: do not use CMD13 to get status after speed
>> mode switch")
>> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
>
> This fixes my rootfs issue. It still doesn't appear immediately as it did
> before, but maybe before was wrong then. (Or we should hammer
> a bit with  CMD13 and wait for a correct CRC?)

Yes, I see this as a quick fix for stable/rc.

So, I intend to submit a new series which builds on top of this and
which hopefully will improves the behaviour.

>
> Anyways: things are a lot better now, so:
> Tested-by: Linus Walleij <linus.walleij@linaro.org>

Thanks for testing!

Kind regards
Uffe

^ permalink raw reply

* [PATCH v6 11/14] mmc: sdhci-msm: Add HS400 platform support
From: Ritesh Harjani @ 2016-11-07 11:24 UTC (permalink / raw)
  To: ulf.hansson, linux-mmc, adrian.hunter, shawn.lin, sboyd
  Cc: devicetree, linux-clk, david.brown, andy.gross, linux-arm-msm,
	georgi.djakov, alex.lemberg, mateusz.nowak, Yuliy.Izrailov,
	asutoshd, kdorfman, david.griego, stummala, venkatg, rnayak,
	pramod.gurav, Ritesh Harjani
In-Reply-To: <1478517877-23733-1-git-send-email-riteshh@codeaurora.org>

From: Venkat Gopalakrishnan <venkatg@codeaurora.org>

The following msm platform specific changes are added to support HS400.
- Allow tuning for HS400 mode.
- Configure HS400 timing mode using the VENDOR_SPECIFIC_FUNC register.

Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
Signed-off-by: Ritesh Harjani <riteshh@codeaurora.org>
---
 drivers/mmc/host/sdhci-msm.c | 127 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 117 insertions(+), 10 deletions(-)

diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 220567c..2561c41 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -31,6 +31,7 @@
 #define HC_MODE_EN		0x1
 #define CORE_POWER		0x0
 #define CORE_SW_RST		BIT(7)
+#define FF_CLK_SW_RST_DIS	BIT(13)
 
 #define CORE_PWRCTL_STATUS	0xdc
 #define CORE_PWRCTL_MASK	0xe0
@@ -64,11 +65,18 @@
 
 #define CORE_VENDOR_SPEC	0x10c
 #define CORE_CLK_PWRSAVE	BIT(1)
+#define CORE_HC_MCLK_SEL_DFLT	(2 << 8)
+#define CORE_HC_MCLK_SEL_HS400	(3 << 8)
+#define CORE_HC_MCLK_SEL_MASK	(3 << 8)
+#define CORE_HC_SELECT_IN_EN	BIT(18)
+#define CORE_HC_SELECT_IN_HS400	(6 << 19)
+#define CORE_HC_SELECT_IN_MASK	(7 << 19)
 
 #define CORE_VENDOR_SPEC_CAPABILITIES0	0x11c
 
 #define TCXO_FREQ		19200000
 #define SDHCI_MSM_MIN_CLOCK	400000
+#define CORE_FREQ_100MHZ	(100 * 1000 * 1000)
 
 #define CDR_SELEXT_SHIFT	20
 #define CDR_SELEXT_MASK		(0xf << CDR_SELEXT_SHIFT)
@@ -85,6 +93,8 @@ struct sdhci_msm_host {
 	unsigned long clk_rate;
 	struct mmc_host *mmc;
 	bool use_14lpp_dll_reset;
+	bool tuning_done;
+	bool calibration_done;
 };
 
 /* Platform specific tuning */
@@ -173,8 +183,8 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
  * Find out the greatest range of consecuitive selected
  * DLL clock output phases that can be used as sampling
  * setting for SD3.0 UHS-I card read operation (in SDR104
- * timing mode) or for eMMC4.5 card read operation (in HS200
- * timing mode).
+ * timing mode) or for eMMC4.5 card read operation (in
+ * HS400/HS200 timing mode).
  * Select the 3/4 of the range and configure the DLL with the
  * selected DLL clock output phase.
  */
@@ -426,9 +436,10 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
 	 * Tuning is required for SDR104, HS200 and HS400 cards and
 	 * if clock frequency is greater than 100MHz in these modes.
 	 */
-	if (host->clock <= 100 * 1000 * 1000 ||
-	    !((ios.timing == MMC_TIMING_MMC_HS200) ||
-	      (ios.timing == MMC_TIMING_UHS_SDR104)))
+	if (host->clock <= CORE_FREQ_100MHZ ||
+	    !(ios.timing == MMC_TIMING_MMC_HS400 ||
+	    ios.timing == MMC_TIMING_MMC_HS200 ||
+	    ios.timing == MMC_TIMING_UHS_SDR104))
 		return 0;
 
 retry:
@@ -479,6 +490,8 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
 		rc = -EIO;
 	}
 
+	if (!rc)
+		msm_host->tuning_done = true;
 	return rc;
 }
 
@@ -486,7 +499,10 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
 					unsigned int uhs)
 {
 	struct mmc_host *mmc = host->mmc;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
 	u16 ctrl_2;
+	u32 config;
 
 	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 	/* Select Bus Speed Mode for host */
@@ -501,6 +517,7 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
 	case MMC_TIMING_UHS_SDR50:
 		ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
 		break;
+	case MMC_TIMING_MMC_HS400:
 	case MMC_TIMING_MMC_HS200:
 	case MMC_TIMING_UHS_SDR104:
 		ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
@@ -517,11 +534,31 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
 	 * 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;
+	if (host->clock <= CORE_FREQ_100MHZ) {
+		if ((uhs == MMC_TIMING_MMC_HS400) ||
+		    (uhs == MMC_TIMING_MMC_HS200) ||
+		    (uhs == MMC_TIMING_UHS_SDR104))
+			ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+		/*
+		 * Make sure DLL is disabled when not required
+		 *
+		 * Write 1 to DLL_RST bit of DLL_CONFIG register
+		 */
+		config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+		config |= CORE_DLL_RST;
+		writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+
+		/* Write 1 to DLL_PDN bit of DLL_CONFIG register */
+		config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+		config |= CORE_DLL_PDN;
+		writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+
+		/*
+		 * The DLL needs to be restored and CDCLP533 recalibrated
+		 * when the clock frequency is set back to 400MHz.
+		 */
+		msm_host->calibration_done = false;
+	}
 
 	dev_dbg(mmc_dev(mmc), "%s: clock=%u uhs=%u ctrl_2=0x%x\n",
 		mmc_hostname(host->mmc), host->clock, uhs, ctrl_2);
@@ -634,6 +671,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
 	struct mmc_ios curr_ios = host->mmc->ios;
+	u32 config;
 	int rc;
 
 	if (!clock) {
@@ -652,6 +690,70 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
 	    (curr_ios.timing == MMC_TIMING_MMC_DDR52) ||
 	    (curr_ios.timing == MMC_TIMING_MMC_HS400))
 		clock *= 2;
+	/*
+	 * In general all timing modes are controlled via UHS mode select in
+	 * Host Control2 register. eMMC specific HS200/HS400 doesn't have
+	 * their respective modes defined here, hence we use these values.
+	 *
+	 * HS200 - SDR104 (Since they both are equivalent in functionality)
+	 * HS400 - This involves multiple configurations
+	 *		Initially SDR104 - when tuning is required as HS200
+	 *		Then when switching to DDR @ 400MHz (HS400) we use
+	 *		the vendor specific HC_SELECT_IN to control the mode.
+	 *
+	 * In addition to controlling the modes we also need to select the
+	 * correct input clock for DLL depending on the mode.
+	 *
+	 * HS400 - divided clock (free running MCLK/2)
+	 * All other modes - default (free running MCLK)
+	 */
+	if (curr_ios.timing == MMC_TIMING_MMC_HS400) {
+		/* Select the divided clock (free running MCLK/2) */
+		config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
+		config &= ~CORE_HC_MCLK_SEL_MASK;
+		config |= CORE_HC_MCLK_SEL_HS400;
+
+		writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
+		/*
+		 * Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC
+		 * register
+		 */
+		if (msm_host->tuning_done && !msm_host->calibration_done) {
+			/*
+			 * Write 0x6 to HC_SELECT_IN and 1 to HC_SELECT_IN_EN
+			 * field in VENDOR_SPEC_FUNC
+			 */
+			config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
+			config |= CORE_HC_SELECT_IN_HS400;
+			config |= CORE_HC_SELECT_IN_EN;
+			writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
+		}
+	} else {
+		/* Select the default clock (free running MCLK) */
+		config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
+		config &= ~CORE_HC_MCLK_SEL_MASK;
+		config |= CORE_HC_MCLK_SEL_DFLT;
+		writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
+
+		/*
+		 * Disable HC_SELECT_IN to be able to use the UHS mode select
+		 * configuration from Host Control2 register for all other
+		 * modes.
+		 *
+		 * Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field
+		 * in VENDOR_SPEC_FUNC
+		 */
+		config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
+		config &= ~CORE_HC_SELECT_IN_EN;
+		config &= ~CORE_HC_SELECT_IN_MASK;
+		writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
+	}
+
+	/*
+	 * Make sure above writes impacting free running MCLK are completed
+	 * before changing the clk_rate at GCC.
+	 */
+	wmb();
 
 	if (clock != msm_host->clk_rate) {
 		rc = clk_set_rate(msm_host->clk, clock);
@@ -790,6 +892,11 @@ static int sdhci_msm_probe(struct platform_device *pdev)
 	/* Set HC_MODE_EN bit in HC_MODE register */
 	writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
 
+	/* Set FF_CLK_SW_RST_DIS bit in HC_MODE register */
+	config = readl_relaxed(msm_host->core_mem + CORE_HC_MODE);
+	config |= FF_CLK_SW_RST_DIS;
+	writel_relaxed(config, msm_host->core_mem + CORE_HC_MODE);
+
 	host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
 	dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n",
 		host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, 
a Linux Foundation Collaborative Project.


^ permalink raw reply related

* [PATCH v6 14/14] sdhci: sdhci-msm: update dll configuration
From: Ritesh Harjani @ 2016-11-07 11:24 UTC (permalink / raw)
  To: ulf.hansson, linux-mmc, adrian.hunter, shawn.lin, sboyd
  Cc: devicetree, linux-clk, david.brown, andy.gross, linux-arm-msm,
	georgi.djakov, alex.lemberg, mateusz.nowak, Yuliy.Izrailov,
	asutoshd, kdorfman, david.griego, stummala, venkatg, rnayak,
	pramod.gurav, Ritesh Harjani, Krishna Konda
In-Reply-To: <1478517877-23733-1-git-send-email-riteshh@codeaurora.org>

The newer msm sdhci's cores use a different DLL hardware for HS400.
Update the configuration and calibration of the newer DLL block.

The HS400 DLL block used previously is CDC LP 533 and requires
programming multiple registers and waiting for configuration to
complete and then enable it. It has about 18 register writes and
two register reads.

The newer HS400 DLL block is SDC4 DLL and requires two register
writes for configuration and one register read to confirm that it
is initialized. There is an additional register write to enable
the power save mode for SDC4 DLL block.

Signed-off-by: Ritesh Harjani <riteshh@codeaurora.org>
Signed-off-by: Krishna Konda <kkonda@codeaurora.org>
---
 drivers/mmc/host/sdhci-msm.c | 132 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 130 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index ae19d1a..84812e6 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -51,6 +51,7 @@
 #define INT_MASK		0xf
 #define MAX_PHASES		16
 #define CORE_DLL_LOCK		BIT(7)
+#define CORE_DDR_DLL_LOCK	BIT(11)
 #define CORE_DLL_EN		BIT(16)
 #define CORE_CDR_EN		BIT(17)
 #define CORE_CK_OUT_EN		BIT(18)
@@ -62,6 +63,7 @@
 #define CORE_DLL_STATUS		0x108
 
 #define CORE_DLL_CONFIG_2	0x1b4
+#define CORE_DDR_CAL_EN		BIT(0)
 #define CORE_FLL_CYCLE_CNT	BIT(18)
 #define CORE_DLL_CLOCK_DISABLE	BIT(21)
 
@@ -100,6 +102,11 @@
 #define CORE_DDR_200_CFG		0x184
 #define CORE_CDC_T4_DLY_SEL		BIT(0)
 #define CORE_START_CDC_TRAFFIC		BIT(6)
+#define CORE_VENDOR_SPEC3	0x1b0
+#define CORE_PWRSAVE_DLL	BIT(3)
+
+#define CORE_DDR_CONFIG		0x1b8
+#define DDR_CONFIG_POR_VAL	0x80040853
 
 #define CORE_VENDOR_SPEC_CAPABILITIES0	0x11c
 
@@ -126,6 +133,7 @@ struct sdhci_msm_host {
 	bool tuning_done;
 	bool calibration_done;
 	u8 saved_tuning_phase;
+	bool use_cdclp533;
 };
 
 /* Platform specific tuning */
@@ -583,6 +591,93 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
 	return ret;
 }
 
+static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host)
+{
+	u32 dll_status, config;
+	int ret;
+
+	pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__);
+
+	/*
+	 * Currently the CORE_DDR_CONFIG register defaults to desired
+	 * configuration on reset. Currently reprogramming the power on
+	 * reset (POR) value in case it might have been modified by
+	 * bootloaders. In the future, if this changes, then the desired
+	 * values will need to be programmed appropriately.
+	 */
+	writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + CORE_DDR_CONFIG);
+
+	/* Write 1 to DDR_CAL_EN field in CORE_DLL_CONFIG_2 */
+	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2);
+	config |= CORE_DDR_CAL_EN;
+	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2);
+
+	/* Poll on DDR_DLL_LOCK bit in CORE_DLL_STATUS to be set */
+	ret = readl_relaxed_poll_timeout(host->ioaddr + CORE_DLL_STATUS,
+					 dll_status,
+					 (dll_status & CORE_DDR_DLL_LOCK),
+					 10, 1000);
+
+	if (ret == -ETIMEDOUT) {
+		pr_err("%s: %s: CM_DLL_SDC4 calibration was not completed\n",
+		       mmc_hostname(host->mmc), __func__);
+		goto out;
+	}
+
+	/* set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3 */
+	config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC3);
+	config |= CORE_PWRSAVE_DLL;
+	writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC3);
+
+	/*
+	 * Drain writebuffer to ensure above DLL calibration
+	 * and PWRSAVE DLL is enabled.
+	 */
+	wmb();
+out:
+	pr_debug("%s: %s: Exit, ret %d\n", mmc_hostname(host->mmc),
+		 __func__, ret);
+	return ret;
+}
+
+static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+	int ret;
+	u32 config;
+
+	pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__);
+
+	/*
+	 * Retuning in HS400 (DDR mode) will fail, just reset the
+	 * tuning block and restore the saved tuning phase.
+	 */
+	ret = msm_init_cm_dll(host);
+	if (ret)
+		goto out;
+
+	/* Set the selected phase in delay line hw block */
+	ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase);
+	if (ret)
+		goto out;
+
+	/* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */
+	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+	config |= CORE_CMD_DAT_TRACK_SEL;
+	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+	if (msm_host->use_cdclp533)
+		/* Calibrate CDCLP533 DLL HW */
+		ret = sdhci_msm_cdclp533_calibration(host);
+	else
+		/* Calibrate CM_DLL_SDC4 HW */
+		ret = sdhci_msm_cm_dll_sdc4_calibration(host);
+out:
+	pr_debug("%s: %s: Exit, ret %d\n", mmc_hostname(host->mmc),
+		 __func__, ret);
+	return ret;
+}
+
 static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
 {
 	int tuning_seq_cnt = 3;
@@ -731,7 +826,7 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
 	if (host->clock > CORE_FREQ_100MHZ &&
 	    msm_host->tuning_done && !msm_host->calibration_done &&
 	    (mmc->ios.timing == MMC_TIMING_MMC_HS400))
-		if (!sdhci_msm_cdclp533_calibration(host))
+		if (!sdhci_msm_hs400_dll_calibration(host))
 			msm_host->calibration_done = true;
 	spin_lock_irq(&host->lock);
 }
@@ -842,7 +937,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
 	struct mmc_ios curr_ios = host->mmc->ios;
-	u32 config;
+	u32 config, dll_lock;
 	int rc;
 
 	if (!clock) {
@@ -899,7 +994,33 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
 			config |= CORE_HC_SELECT_IN_EN;
 			writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
 		}
+		if (!msm_host->clk_rate && !msm_host->use_cdclp533) {
+			/*
+			 * Poll on DLL_LOCK or DDR_DLL_LOCK bits in
+			 * CORE_DLL_STATUS to be set.  This should get set
+			 * within 15 us at 200 MHz.
+			 */
+			rc = readl_relaxed_poll_timeout(host->ioaddr +
+							CORE_DLL_STATUS,
+							dll_lock,
+							(dll_lock &
+							(CORE_DLL_LOCK |
+							CORE_DDR_DLL_LOCK)), 10,
+							1000);
+			if (rc == -ETIMEDOUT)
+				pr_err("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n",
+				       mmc_hostname(host->mmc), dll_lock);
+		}
 	} else {
+		if (!msm_host->use_cdclp533) {
+			/* set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3 */
+			config = readl_relaxed(host->ioaddr +
+					CORE_VENDOR_SPEC3);
+			config &= ~CORE_PWRSAVE_DLL;
+			writel_relaxed(config, host->ioaddr +
+					CORE_VENDOR_SPEC3);
+		}
+
 		/* Select the default clock (free running MCLK) */
 		config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
 		config &= ~CORE_HC_MCLK_SEL_MASK;
@@ -1086,6 +1207,13 @@ static int sdhci_msm_probe(struct platform_device *pdev)
 		msm_host->use_14lpp_dll_reset = true;
 
 	/*
+	 * SDCC 5 controller with major version 1, minor version 0x34 and later
+	 * with HS 400 mode support will use CM DLL instead of CDC LP 533 DLL.
+	 */
+	if ((core_major == 1) && (core_minor < 0x34))
+		msm_host->use_cdclp533 = true;
+
+	/*
 	 * Support for some capabilities is not advertised by newer
 	 * controller versions and must be explicitly enabled.
 	 */
-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, 
a Linux Foundation Collaborative Project.


^ permalink raw reply related

* [PATCH v6 02/14] clk: qcom: Add rcg ops to return floor value closest to the requested rate
From: Ritesh Harjani @ 2016-11-07 11:24 UTC (permalink / raw)
  To: ulf.hansson, linux-mmc, adrian.hunter, shawn.lin, sboyd
  Cc: devicetree, linux-clk, david.brown, andy.gross, linux-arm-msm,
	georgi.djakov, alex.lemberg, mateusz.nowak, Yuliy.Izrailov,
	asutoshd, kdorfman, david.griego, stummala, venkatg, rnayak,
	pramod.gurav
In-Reply-To: <1478517877-23733-1-git-send-email-riteshh@codeaurora.org>

From: Rajendra Nayak <rnayak@codeaurora.org>

The default behaviour with clk_rcg2_ops is for the
clk_round_rate()/clk_set_rate() to return/set a ceil clock
rate closest to the requested rate by looking up the corresponding
frequency table.
However, we do have some instances (mainly sdcc on various platforms)
of clients expecting a clk_set_rate() to set a floor value instead.
Add a new clk_rcg2_floor_ops to handle this for such specific
rcg instances

Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
---
 drivers/clk/qcom/clk-rcg.h  |  1 +
 drivers/clk/qcom/clk-rcg2.c | 75 +++++++++++++++++++++++++++++++++++++++------
 drivers/clk/qcom/common.c   | 26 ++++++++++++++++
 drivers/clk/qcom/common.h   |  2 ++
 4 files changed, 95 insertions(+), 9 deletions(-)

diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index b904c33..1b3e8d2 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -173,6 +173,7 @@ struct clk_rcg2 {
 #define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr)
 
 extern const struct clk_ops clk_rcg2_ops;
+extern const struct clk_ops clk_rcg2_floor_ops;
 extern const struct clk_ops clk_rcg2_shared_ops;
 extern const struct clk_ops clk_edp_pixel_ops;
 extern const struct clk_ops clk_byte_ops;
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index a071bba..04433a6 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -47,6 +47,11 @@
 #define N_REG			0xc
 #define D_REG			0x10
 
+enum {
+	FLOOR,
+	CEIL,
+};
+
 static int clk_rcg2_is_enabled(struct clk_hw *hw)
 {
 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
@@ -176,15 +181,25 @@ static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index)
 	return calc_rate(parent_rate, m, n, mode, hid_div);
 }
 
-static int _freq_tbl_determine_rate(struct clk_hw *hw,
-		const struct freq_tbl *f, struct clk_rate_request *req)
+static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
+				    struct clk_rate_request *req, bool match)
 {
 	unsigned long clk_flags, rate = req->rate;
 	struct clk_hw *p;
 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
 	int index;
 
-	f = qcom_find_freq(f, rate);
+	switch (match) {
+	case FLOOR:
+		f = qcom_find_freq_floor(f, rate);
+		break;
+	case CEIL:
+		f = qcom_find_freq(f, rate);
+		break;
+	default:
+		return -EINVAL;
+	};
+
 	if (!f)
 		return -EINVAL;
 
@@ -221,7 +236,15 @@ static int clk_rcg2_determine_rate(struct clk_hw *hw,
 {
 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
 
-	return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req);
+	return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, CEIL);
+}
+
+static int clk_rcg2_determine_floor_rate(struct clk_hw *hw,
+					 struct clk_rate_request *req)
+{
+	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+
+	return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, FLOOR);
 }
 
 static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
@@ -265,12 +288,23 @@ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
 	return update_config(rcg);
 }
 
-static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
+static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
+			       bool match)
 {
 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
 	const struct freq_tbl *f;
 
-	f = qcom_find_freq(rcg->freq_tbl, rate);
+	switch (match) {
+	case FLOOR:
+		f = qcom_find_freq_floor(rcg->freq_tbl, rate);
+		break;
+	case CEIL:
+		f = qcom_find_freq(rcg->freq_tbl, rate);
+		break;
+	default:
+		return -EINVAL;
+	};
+
 	if (!f)
 		return -EINVAL;
 
@@ -280,13 +314,25 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
 static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
 			    unsigned long parent_rate)
 {
-	return __clk_rcg2_set_rate(hw, rate);
+	return __clk_rcg2_set_rate(hw, rate, CEIL);
+}
+
+static int clk_rcg2_set_floor_rate(struct clk_hw *hw, unsigned long rate,
+				   unsigned long parent_rate)
+{
+	return __clk_rcg2_set_rate(hw, rate, FLOOR);
 }
 
 static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw,
 		unsigned long rate, unsigned long parent_rate, u8 index)
 {
-	return __clk_rcg2_set_rate(hw, rate);
+	return __clk_rcg2_set_rate(hw, rate, CEIL);
+}
+
+static int clk_rcg2_set_floor_rate_and_parent(struct clk_hw *hw,
+		unsigned long rate, unsigned long parent_rate, u8 index)
+{
+	return __clk_rcg2_set_rate(hw, rate, FLOOR);
 }
 
 const struct clk_ops clk_rcg2_ops = {
@@ -300,6 +346,17 @@ static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw,
 };
 EXPORT_SYMBOL_GPL(clk_rcg2_ops);
 
+const struct clk_ops clk_rcg2_floor_ops = {
+	.is_enabled = clk_rcg2_is_enabled,
+	.get_parent = clk_rcg2_get_parent,
+	.set_parent = clk_rcg2_set_parent,
+	.recalc_rate = clk_rcg2_recalc_rate,
+	.determine_rate = clk_rcg2_determine_floor_rate,
+	.set_rate = clk_rcg2_set_floor_rate,
+	.set_rate_and_parent = clk_rcg2_set_floor_rate_and_parent,
+};
+EXPORT_SYMBOL_GPL(clk_rcg2_floor_ops);
+
 static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate)
 {
 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
@@ -323,7 +380,7 @@ static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate)
 		pr_err("%s: RCG did not turn on\n", name);
 
 	/* set clock rate */
-	ret = __clk_rcg2_set_rate(hw, rate);
+	ret = __clk_rcg2_set_rate(hw, rate, CEIL);
 	if (ret)
 		return ret;
 
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index fffcbaf..cf6b87f 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -46,6 +46,32 @@ struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
 }
 EXPORT_SYMBOL_GPL(qcom_find_freq);
 
+const
+struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
+				      unsigned long rate)
+{
+	int size = 0;
+
+	if (!f)
+		return NULL;
+
+	/*
+	 * The freq table has entries in the ascending order of frequencies
+	 * To find the floor for a given frequency, we need to do a reverse
+	 * lookup of the table
+	 */
+	for (; f->freq; f++, size++)
+		;
+
+	for (f--; size; f--, size--)
+		if (rate >= f->freq)
+			return f;
+
+	/* could not find any rates lower than *rate* */
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(qcom_find_freq_floor);
+
 int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src)
 {
 	int i, num_parents = clk_hw_get_num_parents(hw);
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index ae9bdeb..76886a1 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -34,6 +34,8 @@ struct qcom_cc_desc {
 
 extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f,
 					     unsigned long rate);
+extern const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
+						   unsigned long rate);
 extern int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map,
 			       u8 src);
 
-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, 
a Linux Foundation Collaborative Project.


^ permalink raw reply related

* [PATCH v6 00/14] mmc: sdhci-msm: Add clk-rates, DDR, HS400 support
From: Ritesh Harjani @ 2016-11-07 11:24 UTC (permalink / raw)
  To: ulf.hansson, linux-mmc, adrian.hunter, shawn.lin, sboyd
  Cc: devicetree, linux-clk, david.brown, andy.gross, linux-arm-msm,
	georgi.djakov, alex.lemberg, mateusz.nowak, Yuliy.Izrailov,
	asutoshd, kdorfman, david.griego, stummala, venkatg, rnayak,
	pramod.gurav, Ritesh Harjani

Hi, 

This is v6 version of the patch series which adds support for MSM8996.
Adds HS400 driver support as well.
These are tested on internal msm8996 & db410c HW.

Changes from v5 -> v6 :- 
1. Earlier in v5 series DT node was added to get the clk-rates table
needed for sdhci-msm driver. But this is removed in this(v6) patch series
and instead the clk changes are done in the clk driver as per Rob H comment.

2. Added clk driver changes(patch 1-3) to provide floor rate values of requested
clock for sdhc client.
For following boards- apq8084, msm8996, msm8916, msm8974.

3. Other minor patch comments were addressed.

Changes from v4 -> v5 :-
1. Added HS400 sdhci-msm controller specific changes:- (Patch 10, 11, 12)
2. Addressed comment from Adrian on Patch 07 @[3].
3. Addressed comment from Arnd on Patch 03, to directly add
   clk_table into sdhci_msm_host. [4]
4. Addressed comment from Bjorn to not enforce having clk-rates property
   in DT for older targets based on discussion at [5]
5. Retained Acks from Adrian on patches (01 & 02 & 06) where there were no
   changes made while addressing above comments.

Older history:- 
This is v4 version of the patch series.
Patches 01, 02, 05 & 06 were Acked-by Adrian.

Changes from v3 -> v4 :-
1. Addressed comments from Adrian on Patch 03, 07, 08.
2. Addressed comments from Bjorn on Patch 03.
3. Added clk-rate support for sdhc DT nodes to all MSM platforms.
   in Pacth 04.
4. Rebased on next branch of Ulf.

Changes from v2 -> v3 :-
1. Addded Patch 01 based on Bjorn comment[2] - 
   This fixes/unrolls the poor coding style of read/writes of
   registers from base sdhci-msm driver.

2. Fixed/unrolled poor style of reads/writes of registers in Patch 02,
   based on Bjorn comment[2]. Also changed name of flag from
   use_updated_dll_reset -> use_14lpp_dll_reset.

Changes from v1->v2 :-
1. Removed patch 06 & 08 from v1 patch series[1]
(which were introducing unnecessary quirks).
   Instead have implemented __sdhci_msm_set_clock version of
   sdhci_set_clock in sdhci_msm driver itself in patch 07 of
   this patch series.
2. Enabled extra quirk (SDHCI_QUIRK2_PRESET_VALUE_BROKEN) in
   patch 05 of this patch series. 


Description of patches :-
This patchset adds clk-rates & other required changes to
upstream sdhci-msm driver from codeaurora tree.
It has been tested on a db410c Dragonboard and msm8996 based
platform.

Patch 0001-0003- Adds support in qcom clk driver to return
floor value of requested clock rate instead of ceil rate
for sdhc clients.

Patch 0004- Adds updated dll sequence for newer controllers
which has minor_version >= 0x42. This is required for msm8996.

MSM controller HW recommendation is to use the base MCI clock
and directly control this MCI clock at GCC in order to
change the clk-rate. 
Patches 06-08 bring in required change for this to
sdhci-msm.

MSM controller would require 2x clock rate from source
for DDR bus speed modes. Patch 09 adds this support.

Patch 0010- adds DDR support in DT for sdhc1 of msm8916.

Patches 0011-0014- Adds HS400 support to sdhci-msm.


[1]:- http://www.spinics.net/lists/linux-mmc/msg38467.html
[2]:- http://www.spinics.net/lists/linux-mmc/msg38578.html 
[3]:- https://patchwork.kernel.org/patch/9289345/
[4]:- https://www.spinics.net/lists/linux-mmc/msg39107.html
[5]:- http://www.spinics.net/lists/linux-mmc/msg38749.html
[6]:- https://patchwork.kernel.org/patch/9297381/


Rajendra Nayak (3):
  clk: Add clk_hw_get_clk() helper API to be used by clk providers
  clk: qcom: Add rcg ops to return floor value closest to the requested
    rate
  clk: qcom: Move all sdcc rcgs to use clk_rcg2_floor_ops

Ritesh Harjani (9):
  mmc: sdhci-msm: Change poor style writel/readl of registers
  mmc: sdhci-msm: Add get_min_clock() and get_max_clock() callback
  mmc: sdhci-msm: Enable few quirks
  mmc: sdhci-msm: Implement set_clock callback for sdhci-msm
  mmc: sdhci-msm: Add clock changes for DDR mode.
  arm64: dts: qcom: msm8916: Add ddr support to sdhc1
  mmc: sdhci-msm: Save the calculated tuning phase
  mmc: sdhci-msm: Add calibration tuning for CDCLP533 circuit
  sdhci: sdhci-msm: update dll configuration

Venkat Gopalakrishnan (2):
  mmc: sdhci-msm: Update DLL reset sequence
  mmc: sdhci-msm: Add HS400 platform support

 arch/arm64/boot/dts/qcom/msm8916.dtsi |   1 +
 drivers/clk/clk.c                     |   6 +
 drivers/clk/qcom/clk-rcg.h            |   1 +
 drivers/clk/qcom/clk-rcg2.c           |  75 +++-
 drivers/clk/qcom/common.c             |  26 ++
 drivers/clk/qcom/common.h             |   2 +
 drivers/clk/qcom/gcc-apq8084.c        |   8 +-
 drivers/clk/qcom/gcc-msm8916.c        |   4 +-
 drivers/clk/qcom/gcc-msm8974.c        |   8 +-
 drivers/clk/qcom/gcc-msm8996.c        |   8 +-
 drivers/mmc/host/sdhci-msm.c          | 650 ++++++++++++++++++++++++++++++++--
 include/linux/clk-provider.h          |   1 +
 12 files changed, 733 insertions(+), 57 deletions(-)

-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, 
a Linux Foundation Collaborative Project.


^ permalink raw reply

* [PATCH v6 13/14] mmc: sdhci-msm: Add calibration tuning for CDCLP533 circuit
From: Ritesh Harjani @ 2016-11-07 11:24 UTC (permalink / raw)
  To: ulf.hansson, linux-mmc, adrian.hunter, shawn.lin, sboyd
  Cc: devicetree, linux-clk, david.brown, andy.gross, linux-arm-msm,
	georgi.djakov, alex.lemberg, mateusz.nowak, Yuliy.Izrailov,
	asutoshd, kdorfman, david.griego, stummala, venkatg, rnayak,
	pramod.gurav, Ritesh Harjani
In-Reply-To: <1478517877-23733-1-git-send-email-riteshh@codeaurora.org>

In HS400 mode a new RCLK is introduced on the interface for read data
transfers. The eMMC5.0 device transmits the read data to the host with
respect to rising and falling edges of RCLK. In order to ensure correct
operation of read data transfers in HS400 mode, the incoming RX data
needs to be sampled by delayed version of RCLK.

The CDCLP533 delay circuit shifts the RCLK by T/4. It needs to be
initialized, configured and enabled once during HS400 mode switch and
when operational voltage/clock is changed.

Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
Signed-off-by: Ritesh Harjani <riteshh@codeaurora.org>
---
 drivers/mmc/host/sdhci-msm.c | 163 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 163 insertions(+)

diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 6431bb8..ae19d1a 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -19,6 +19,7 @@
 #include <linux/delay.h>
 #include <linux/mmc/mmc.h>
 #include <linux/slab.h>
+#include <linux/iopoll.h>
 
 #include "sdhci-pltfm.h"
 
@@ -57,6 +58,7 @@
 #define CORE_DLL_PDN		BIT(29)
 #define CORE_DLL_RST		BIT(30)
 #define CORE_DLL_CONFIG		0x100
+#define CORE_CMD_DAT_TRACK_SEL	BIT(0)
 #define CORE_DLL_STATUS		0x108
 
 #define CORE_DLL_CONFIG_2	0x1b4
@@ -72,6 +74,33 @@
 #define CORE_HC_SELECT_IN_HS400	(6 << 19)
 #define CORE_HC_SELECT_IN_MASK	(7 << 19)
 
+#define CORE_CSR_CDC_CTLR_CFG0		0x130
+#define CORE_SW_TRIG_FULL_CALIB		BIT(16)
+#define CORE_HW_AUTOCAL_ENA		BIT(17)
+
+#define CORE_CSR_CDC_CTLR_CFG1		0x134
+#define CORE_CSR_CDC_CAL_TIMER_CFG0	0x138
+#define CORE_TIMER_ENA			BIT(16)
+
+#define CORE_CSR_CDC_CAL_TIMER_CFG1	0x13C
+#define CORE_CSR_CDC_REFCOUNT_CFG	0x140
+#define CORE_CSR_CDC_COARSE_CAL_CFG	0x144
+#define CORE_CDC_OFFSET_CFG		0x14C
+#define CORE_CSR_CDC_DELAY_CFG		0x150
+#define CORE_CDC_SLAVE_DDA_CFG		0x160
+#define CORE_CSR_CDC_STATUS0		0x164
+#define CORE_CALIBRATION_DONE		BIT(0)
+
+#define CORE_CDC_ERROR_CODE_MASK	0x7000000
+
+#define CORE_CSR_CDC_GEN_CFG		0x178
+#define CORE_CDC_SWITCH_BYPASS_OFF	BIT(0)
+#define CORE_CDC_SWITCH_RC_EN		BIT(1)
+
+#define CORE_DDR_200_CFG		0x184
+#define CORE_CDC_T4_DLY_SEL		BIT(0)
+#define CORE_START_CDC_TRAFFIC		BIT(6)
+
 #define CORE_VENDOR_SPEC_CAPABILITIES0	0x11c
 
 #define INVALID_TUNING_PHASE	-1
@@ -429,6 +458,131 @@ static int msm_init_cm_dll(struct sdhci_host *host)
 	return 0;
 }
 
+static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+	u32 config, calib_done;
+	int ret;
+
+	pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__);
+
+	/*
+	 * Retuning in HS400 (DDR mode) will fail, just reset the
+	 * tuning block and restore the saved tuning phase.
+	 */
+	ret = msm_init_cm_dll(host);
+	if (ret)
+		goto out;
+
+	/* Set the selected phase in delay line hw block */
+	ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase);
+	if (ret)
+		goto out;
+
+	/* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */
+	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+	config |= CORE_CMD_DAT_TRACK_SEL;
+	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+
+	/* Write 0 to CDC_T4_DLY_SEL field in VENDOR_SPEC_DDR200_CFG */
+	config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG);
+	config &= ~CORE_CDC_T4_DLY_SEL;
+	writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG);
+
+	/* Write 0 to CDC_SWITCH_BYPASS_OFF field in CORE_CSR_CDC_GEN_CFG */
+	config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_GEN_CFG);
+	config &= ~CORE_CDC_SWITCH_BYPASS_OFF;
+	writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_GEN_CFG);
+
+	/* Write 1 to CDC_SWITCH_RC_EN field in CORE_CSR_CDC_GEN_CFG */
+	config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_GEN_CFG);
+	config |= CORE_CDC_SWITCH_RC_EN;
+	writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_GEN_CFG);
+
+	/* Write 0 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */
+	config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG);
+	config &= ~CORE_START_CDC_TRAFFIC;
+	writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG);
+
+	/*
+	 * Perform CDC Register Initialization Sequence
+	 *
+	 * CORE_CSR_CDC_CTLR_CFG0	0x11800EC
+	 * CORE_CSR_CDC_CTLR_CFG1	0x3011111
+	 * CORE_CSR_CDC_CAL_TIMER_CFG0	0x1201000
+	 * CORE_CSR_CDC_CAL_TIMER_CFG1	0x4
+	 * CORE_CSR_CDC_REFCOUNT_CFG	0xCB732020
+	 * CORE_CSR_CDC_COARSE_CAL_CFG	0xB19
+	 * CORE_CSR_CDC_DELAY_CFG	0x3AC
+	 * CORE_CDC_OFFSET_CFG		0x0
+	 * CORE_CDC_SLAVE_DDA_CFG	0x16334
+	 */
+
+	writel_relaxed(0x11800EC, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+	writel_relaxed(0x3011111, host->ioaddr + CORE_CSR_CDC_CTLR_CFG1);
+	writel_relaxed(0x1201000, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0);
+	writel_relaxed(0x4, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG1);
+	writel_relaxed(0xCB732020, host->ioaddr + CORE_CSR_CDC_REFCOUNT_CFG);
+	writel_relaxed(0xB19, host->ioaddr + CORE_CSR_CDC_COARSE_CAL_CFG);
+	writel_relaxed(0x3AC, host->ioaddr + CORE_CSR_CDC_DELAY_CFG);
+	writel_relaxed(0x0, host->ioaddr + CORE_CDC_OFFSET_CFG);
+	writel_relaxed(0x16334, host->ioaddr + CORE_CDC_SLAVE_DDA_CFG);
+
+	/* CDC HW Calibration */
+
+	/* Write 1 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */
+	config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+	config |= CORE_SW_TRIG_FULL_CALIB;
+	writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+
+	/* Write 0 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */
+	config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+	config &= ~CORE_SW_TRIG_FULL_CALIB;
+	writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+
+	/* Write 1 to HW_AUTOCAL_ENA field in CORE_CSR_CDC_CTLR_CFG0 */
+	config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+	config |= CORE_HW_AUTOCAL_ENA;
+	writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_CTLR_CFG0);
+
+	/* Write 1 to TIMER_ENA field in CORE_CSR_CDC_CAL_TIMER_CFG0 */
+	config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0);
+	config |= CORE_TIMER_ENA;
+	writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_CAL_TIMER_CFG0);
+
+	/* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */
+	ret = readl_relaxed_poll_timeout(host->ioaddr + CORE_CSR_CDC_STATUS0,
+					 calib_done,
+					 (calib_done & CORE_CALIBRATION_DONE),
+					 1, 50);
+
+	if (ret == -ETIMEDOUT) {
+		pr_err("%s: %s: CDC calibration was not completed\n",
+		       mmc_hostname(host->mmc), __func__);
+		goto out;
+	}
+
+	/* Verify CDC_ERROR_CODE field in CORE_CSR_CDC_STATUS0 is 0 */
+	ret = readl_relaxed(host->ioaddr + CORE_CSR_CDC_STATUS0)
+			& CORE_CDC_ERROR_CODE_MASK;
+	if (ret) {
+		pr_err("%s: %s: CDC error code %d\n",
+		       mmc_hostname(host->mmc), __func__, ret);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Write 1 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */
+	config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG);
+	config |= CORE_START_CDC_TRAFFIC;
+	writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG);
+out:
+	pr_debug("%s: %s: Exit, ret %d\n", mmc_hostname(host->mmc),
+		 __func__, ret);
+	return ret;
+}
+
 static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
 {
 	int tuning_seq_cnt = 3;
@@ -571,6 +725,15 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
 	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);
+
+	spin_unlock_irq(&host->lock);
+	/* CDCLP533 HW calibration is only required for HS400 mode*/
+	if (host->clock > CORE_FREQ_100MHZ &&
+	    msm_host->tuning_done && !msm_host->calibration_done &&
+	    (mmc->ios.timing == MMC_TIMING_MMC_HS400))
+		if (!sdhci_msm_cdclp533_calibration(host))
+			msm_host->calibration_done = true;
+	spin_lock_irq(&host->lock);
 }
 
 static void sdhci_msm_voltage_switch(struct sdhci_host *host)
-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, 
a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH v6 12/14] mmc: sdhci-msm: Save the calculated tuning phase
From: Ritesh Harjani @ 2016-11-07 11:24 UTC (permalink / raw)
  To: ulf.hansson-QSEj5FYQhm4dnm+yROfE0A,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA,
	adrian.hunter-ral2JQCrhuEAvxtiuMwx3w,
	shawn.lin-TNX95d0MmH7DzftRWevZcw, sboyd-sgV2jX0FEOL9JmXXK+q4OQ
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	david.brown-QSEj5FYQhm4dnm+yROfE0A,
	andy.gross-QSEj5FYQhm4dnm+yROfE0A,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	georgi.djakov-QSEj5FYQhm4dnm+yROfE0A,
	alex.lemberg-XdAiOPVOjttBDgjK7y7TUQ,
	mateusz.nowak-ral2JQCrhuEAvxtiuMwx3w,
	Yuliy.Izrailov-XdAiOPVOjttBDgjK7y7TUQ,
	asutoshd-sgV2jX0FEOL9JmXXK+q4OQ, kdorfman-sgV2jX0FEOL9JmXXK+q4OQ,
	david.griego-QSEj5FYQhm4dnm+yROfE0A,
	stummala-sgV2jX0FEOL9JmXXK+q4OQ, venkatg-sgV2jX0FEOL9JmXXK+q4OQ,
	rnayak-sgV2jX0FEOL9JmXXK+q4OQ,
	pramod.gurav-QSEj5FYQhm4dnm+yROfE0A, Ritesh Harjani
In-Reply-To: <1478517877-23733-1-git-send-email-riteshh-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>

Save the tuning phase once the tuning is performed.
This phase value will be used while calibrating DLL
for HS400 mode.

Signed-off-by: Ritesh Harjani <riteshh-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
 drivers/mmc/host/sdhci-msm.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 2561c41..6431bb8 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -74,6 +74,7 @@
 
 #define CORE_VENDOR_SPEC_CAPABILITIES0	0x11c
 
+#define INVALID_TUNING_PHASE	-1
 #define TCXO_FREQ		19200000
 #define SDHCI_MSM_MIN_CLOCK	400000
 #define CORE_FREQ_100MHZ	(100 * 1000 * 1000)
@@ -95,6 +96,7 @@ struct sdhci_msm_host {
 	bool use_14lpp_dll_reset;
 	bool tuning_done;
 	bool calibration_done;
+	u8 saved_tuning_phase;
 };
 
 /* Platform specific tuning */
@@ -134,6 +136,9 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
 	u32 config;
 	struct mmc_host *mmc = host->mmc;
 
+	if (phase > 0xf)
+		return -EINVAL;
+
 	spin_lock_irqsave(&host->lock, flags);
 
 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
@@ -431,6 +436,8 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
 	int rc;
 	struct mmc_host *mmc = host->mmc;
 	struct mmc_ios ios = host->mmc->ios;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
 
 	/*
 	 * Tuning is required for SDR104, HS200 and HS400 cards and
@@ -455,6 +462,7 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
 		if (rc)
 			return rc;
 
+		msm_host->saved_tuning_phase = phase;
 		rc = mmc_send_tuning(mmc, opcode, NULL);
 		if (!rc) {
 			/* Tuning is successful at this tuning point */
@@ -826,6 +834,8 @@ static int sdhci_msm_probe(struct platform_device *pdev)
 
 	sdhci_get_of_property(pdev);
 
+	msm_host->saved_tuning_phase = INVALID_TUNING_PHASE;
+
 	/* Setup SDCC bus voter clock. */
 	msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus");
 	if (!IS_ERR(msm_host->bus_clk)) {
-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, 
a Linux Foundation Collaborative Project.

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH v6 10/14] arm64: dts: qcom: msm8916: Add ddr support to sdhc1
From: Ritesh Harjani @ 2016-11-07 11:24 UTC (permalink / raw)
  To: ulf.hansson-QSEj5FYQhm4dnm+yROfE0A,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA,
	adrian.hunter-ral2JQCrhuEAvxtiuMwx3w,
	shawn.lin-TNX95d0MmH7DzftRWevZcw, sboyd-sgV2jX0FEOL9JmXXK+q4OQ
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	david.brown-QSEj5FYQhm4dnm+yROfE0A,
	andy.gross-QSEj5FYQhm4dnm+yROfE0A,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	georgi.djakov-QSEj5FYQhm4dnm+yROfE0A,
	alex.lemberg-XdAiOPVOjttBDgjK7y7TUQ,
	mateusz.nowak-ral2JQCrhuEAvxtiuMwx3w,
	Yuliy.Izrailov-XdAiOPVOjttBDgjK7y7TUQ,
	asutoshd-sgV2jX0FEOL9JmXXK+q4OQ, kdorfman-sgV2jX0FEOL9JmXXK+q4OQ,
	david.griego-QSEj5FYQhm4dnm+yROfE0A,
	stummala-sgV2jX0FEOL9JmXXK+q4OQ, venkatg-sgV2jX0FEOL9JmXXK+q4OQ,
	rnayak-sgV2jX0FEOL9JmXXK+q4OQ,
	pramod.gurav-QSEj5FYQhm4dnm+yROfE0A, Ritesh Harjani
In-Reply-To: <1478517877-23733-1-git-send-email-riteshh-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>

This adds mmc-ddr-1_8v support to DT for sdhc1 of msm8916.

Signed-off-by: Ritesh Harjani <riteshh-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
 arch/arm64/boot/dts/qcom/msm8916.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi
index 466ca57..ed15e87 100644
--- a/arch/arm64/boot/dts/qcom/msm8916.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi
@@ -514,6 +514,7 @@
 			clocks = <&gcc GCC_SDCC1_APPS_CLK>,
 				 <&gcc GCC_SDCC1_AHB_CLK>;
 			clock-names = "core", "iface";
+			mmc-ddr-1_8v;
 			bus-width = <8>;
 			non-removable;
 			status = "disabled";
-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, 
a Linux Foundation Collaborative Project.

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH v6 09/14] mmc: sdhci-msm: Add clock changes for DDR mode.
From: Ritesh Harjani @ 2016-11-07 11:24 UTC (permalink / raw)
  To: ulf.hansson, linux-mmc, adrian.hunter, shawn.lin, sboyd
  Cc: devicetree, linux-clk, david.brown, andy.gross, linux-arm-msm,
	georgi.djakov, alex.lemberg, mateusz.nowak, Yuliy.Izrailov,
	asutoshd, kdorfman, david.griego, stummala, venkatg, rnayak,
	pramod.gurav, Ritesh Harjani
In-Reply-To: <1478517877-23733-1-git-send-email-riteshh@codeaurora.org>

SDHC MSM controller need 2x clock for MCLK at GCC.
Hence make required changes to have 2x clock for
DDR timing modes.

Signed-off-by: Ritesh Harjani <riteshh@codeaurora.org>
---
 drivers/mmc/host/sdhci-msm.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index ff0915b..220567c 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -633,6 +633,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
 {
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+	struct mmc_ios curr_ios = host->mmc->ios;
 	int rc;
 
 	if (!clock) {
@@ -641,11 +642,23 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
 	}
 
 	spin_unlock_irq(&host->lock);
+	/*
+	 * The SDHC requires internal clock frequency to be double the
+	 * actual clock that will be set for DDR mode. The controller
+	 * uses the faster clock(100/400MHz) for some of its parts and
+	 * send the actual required clock (50/200MHz) to the card.
+	 */
+	if ((curr_ios.timing == MMC_TIMING_UHS_DDR50) ||
+	    (curr_ios.timing == MMC_TIMING_MMC_DDR52) ||
+	    (curr_ios.timing == MMC_TIMING_MMC_HS400))
+		clock *= 2;
+
 	if (clock != msm_host->clk_rate) {
 		rc = clk_set_rate(msm_host->clk, clock);
 		if (rc) {
-			pr_err("%s: Failed to set clock at rate %u\n",
-			       mmc_hostname(host->mmc), clock);
+			pr_err("%s: Failed to set clock at rate %u at timing %d\n",
+			       mmc_hostname(host->mmc), clock,
+			       curr_ios.timing);
 			spin_lock_irq(&host->lock);
 			goto out;
 		}
-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, 
a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH v6 08/14] mmc: sdhci-msm: Implement set_clock callback for sdhci-msm
From: Ritesh Harjani @ 2016-11-07 11:24 UTC (permalink / raw)
  To: ulf.hansson, linux-mmc, adrian.hunter, shawn.lin, sboyd
  Cc: devicetree, linux-clk, david.brown, andy.gross, linux-arm-msm,
	georgi.djakov, alex.lemberg, mateusz.nowak, Yuliy.Izrailov,
	asutoshd, kdorfman, david.griego, stummala, venkatg, rnayak,
	pramod.gurav, Ritesh Harjani
In-Reply-To: <1478517877-23733-1-git-send-email-riteshh@codeaurora.org>

sdhci-msm controller may have different clk-rates for each
bus speed mode. Thus implement set_clock callback for
sdhci-msm driver.

Signed-off-by: Sahitya Tummala <stummala@codeaurora.org>
Signed-off-by: Ritesh Harjani <riteshh@codeaurora.org>
---
 drivers/mmc/host/sdhci-msm.c | 87 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 86 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index fde2777..ff0915b 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -82,6 +82,7 @@ struct sdhci_msm_host {
 	struct clk *clk;	/* main SD/MMC bus clock */
 	struct clk *pclk;	/* SDHC peripheral bus clock */
 	struct clk *bus_clk;	/* SDHC bus voter clock */
+	unsigned long clk_rate;
 	struct mmc_host *mmc;
 	bool use_14lpp_dll_reset;
 };
@@ -573,6 +574,90 @@ static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host)
 	return SDHCI_MSM_MIN_CLOCK;
 }
 
+/**
+ * __sdhci_msm_set_clock - sdhci_msm clock control.
+ *
+ * Description:
+ * Implement MSM version of sdhci_set_clock.
+ * This is required since MSM controller does not
+ * use internal divider and instead directly control
+ * the GCC clock as per HW recommendation.
+ **/
+void __sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	u16 clk;
+	unsigned long timeout;
+
+	/*
+	 * Keep actual_clock as zero -
+	 * - since there is no divider used so no need of having actual_clock.
+	 * - MSM controller uses SDCLK for data timeout calculation. If
+	 *   actual_clock is zero, host->clock is taken for calculation.
+	 */
+	host->mmc->actual_clock = 0;
+
+	sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+	if (clock == 0)
+		return;
+
+	/*
+	 * MSM controller do not use clock divider.
+	 * Thus read SDHCI_CLOCK_CONTROL and only enable
+	 * clock with no divider value programmed.
+	 */
+	clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+
+	clk |= SDHCI_CLOCK_INT_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+	/* Wait max 20 ms */
+	timeout = 20;
+	while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+		& SDHCI_CLOCK_INT_STABLE)) {
+		if (timeout == 0) {
+			pr_err("%s: Internal clock never stabilised\n",
+			       mmc_hostname(host->mmc));
+			return;
+		}
+		timeout--;
+		mdelay(1);
+	}
+
+	clk |= SDHCI_CLOCK_CARD_EN;
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+}
+
+/* sdhci_msm_set_clock - Called with (host->lock) spinlock held. */
+static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+	int rc;
+
+	if (!clock) {
+		msm_host->clk_rate = clock;
+		goto out;
+	}
+
+	spin_unlock_irq(&host->lock);
+	if (clock != msm_host->clk_rate) {
+		rc = clk_set_rate(msm_host->clk, clock);
+		if (rc) {
+			pr_err("%s: Failed to set clock at rate %u\n",
+			       mmc_hostname(host->mmc), clock);
+			spin_lock_irq(&host->lock);
+			goto out;
+		}
+		msm_host->clk_rate = clock;
+		pr_debug("%s: Setting clock at rate %lu\n",
+			 mmc_hostname(host->mmc), clk_get_rate(msm_host->clk));
+	}
+	spin_lock_irq(&host->lock);
+out:
+	__sdhci_msm_set_clock(host, clock);
+}
+
 static const struct of_device_id sdhci_msm_dt_match[] = {
 	{ .compatible = "qcom,sdhci-msm-v4" },
 	{},
@@ -583,7 +668,7 @@ static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host)
 static const struct sdhci_ops sdhci_msm_ops = {
 	.platform_execute_tuning = sdhci_msm_execute_tuning,
 	.reset = sdhci_reset,
-	.set_clock = sdhci_set_clock,
+	.set_clock = sdhci_msm_set_clock,
 	.get_min_clock = sdhci_msm_get_min_clock,
 	.get_max_clock = sdhci_msm_get_max_clock,
 	.set_bus_width = sdhci_set_bus_width,
-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, 
a Linux Foundation Collaborative Project.


^ permalink raw reply related

* [PATCH v6 07/14] mmc: sdhci-msm: Enable few quirks
From: Ritesh Harjani @ 2016-11-07 11:24 UTC (permalink / raw)
  To: ulf.hansson, linux-mmc, adrian.hunter, shawn.lin, sboyd
  Cc: devicetree, linux-clk, david.brown, andy.gross, linux-arm-msm,
	georgi.djakov, alex.lemberg, mateusz.nowak, Yuliy.Izrailov,
	asutoshd, kdorfman, david.griego, stummala, venkatg, rnayak,
	pramod.gurav, Ritesh Harjani
In-Reply-To: <1478517877-23733-1-git-send-email-riteshh@codeaurora.org>

sdhc-msm controller needs this SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN
& SDHCI_QUIRK2_PRESET_VALUE_BROKEN to be set. Hence setting it.

Signed-off-by: Sahitya Tummala <stummala@codeaurora.org>
Signed-off-by: Ritesh Harjani <riteshh@codeaurora.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/mmc/host/sdhci-msm.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 66ca444..fde2777 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -594,7 +594,9 @@ static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host)
 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,
+		  SDHCI_QUIRK_SINGLE_POWER_WRITE |
+		  SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
 	.ops = &sdhci_msm_ops,
 };
 
-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, 
a Linux Foundation Collaborative Project.


^ permalink raw reply related

* [PATCH v6 06/14] mmc: sdhci-msm: Add get_min_clock() and get_max_clock() callback
From: Ritesh Harjani @ 2016-11-07 11:24 UTC (permalink / raw)
  To: ulf.hansson, linux-mmc, adrian.hunter, shawn.lin, sboyd
  Cc: devicetree, linux-clk, david.brown, andy.gross, linux-arm-msm,
	georgi.djakov, alex.lemberg, mateusz.nowak, Yuliy.Izrailov,
	asutoshd, kdorfman, david.griego, stummala, venkatg, rnayak,
	pramod.gurav, Ritesh Harjani
In-Reply-To: <1478517877-23733-1-git-send-email-riteshh@codeaurora.org>

This add get_min_clock() and get_max_clock() callback
for sdhci-msm. sdhci-msm min/max clocks may be different
hence implement these callbacks.

Signed-off-by: Sahitya Tummala <stummala@codeaurora.org>
Signed-off-by: Ritesh Harjani <riteshh@codeaurora.org>
---
 drivers/mmc/host/sdhci-msm.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 32b0b79..66ca444 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -68,6 +68,7 @@
 #define CORE_VENDOR_SPEC_CAPABILITIES0	0x11c
 
 #define TCXO_FREQ		19200000
+#define SDHCI_MSM_MIN_CLOCK	400000
 
 #define CDR_SELEXT_SHIFT	20
 #define CDR_SELEXT_MASK		(0xf << CDR_SELEXT_SHIFT)
@@ -559,6 +560,19 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+static unsigned int sdhci_msm_get_max_clock(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+
+	return clk_round_rate(msm_host->clk, ULONG_MAX);
+}
+
+static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host)
+{
+	return SDHCI_MSM_MIN_CLOCK;
+}
+
 static const struct of_device_id sdhci_msm_dt_match[] = {
 	{ .compatible = "qcom,sdhci-msm-v4" },
 	{},
@@ -570,6 +584,8 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
 	.platform_execute_tuning = sdhci_msm_execute_tuning,
 	.reset = sdhci_reset,
 	.set_clock = sdhci_set_clock,
+	.get_min_clock = sdhci_msm_get_min_clock,
+	.get_max_clock = sdhci_msm_get_max_clock,
 	.set_bus_width = sdhci_set_bus_width,
 	.set_uhs_signaling = sdhci_msm_set_uhs_signaling,
 	.voltage_switch = sdhci_msm_voltage_switch,
-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, 
a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH v6 05/14] mmc: sdhci-msm: Update DLL reset sequence
From: Ritesh Harjani @ 2016-11-07 11:24 UTC (permalink / raw)
  To: ulf.hansson, linux-mmc, adrian.hunter, shawn.lin, sboyd
  Cc: devicetree, linux-clk, david.brown, andy.gross, linux-arm-msm,
	georgi.djakov, alex.lemberg, mateusz.nowak, Yuliy.Izrailov,
	asutoshd, kdorfman, david.griego, stummala, venkatg, rnayak,
	pramod.gurav, Ritesh Harjani
In-Reply-To: <1478517877-23733-1-git-send-email-riteshh@codeaurora.org>

From: Venkat Gopalakrishnan <venkatg@codeaurora.org>

SDCC core with minor version >= 0x42 introduced new 14lpp
DLL. This has additional requirements in the reset sequence
for DLL tuning. Make necessary changes as needed.

Without this patch we see below errors on such SDHC controllers
	sdhci_msm 7464900.sdhci: mmc0: DLL failed to LOCK
	mmc0: tuning execution failed: -110

Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
Signed-off-by: Ritesh Harjani <riteshh@codeaurora.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/mmc/host/sdhci-msm.c | 48 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 42f42aa..32b0b79 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -58,11 +58,17 @@
 #define CORE_DLL_CONFIG		0x100
 #define CORE_DLL_STATUS		0x108
 
+#define CORE_DLL_CONFIG_2	0x1b4
+#define CORE_FLL_CYCLE_CNT	BIT(18)
+#define CORE_DLL_CLOCK_DISABLE	BIT(21)
+
 #define CORE_VENDOR_SPEC	0x10c
 #define CORE_CLK_PWRSAVE	BIT(1)
 
 #define CORE_VENDOR_SPEC_CAPABILITIES0	0x11c
 
+#define TCXO_FREQ		19200000
+
 #define CDR_SELEXT_SHIFT	20
 #define CDR_SELEXT_MASK		(0xf << CDR_SELEXT_SHIFT)
 #define CMUX_SHIFT_PHASE_SHIFT	24
@@ -76,6 +82,7 @@ struct sdhci_msm_host {
 	struct clk *pclk;	/* SDHC peripheral bus clock */
 	struct clk *bus_clk;	/* SDHC bus voter clock */
 	struct mmc_host *mmc;
+	bool use_14lpp_dll_reset;
 };
 
 /* Platform specific tuning */
@@ -304,6 +311,8 @@ static inline void msm_cm_dll_set_freq(struct sdhci_host *host)
 static int msm_init_cm_dll(struct sdhci_host *host)
 {
 	struct mmc_host *mmc = host->mmc;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
 	int wait_cnt = 50;
 	unsigned long flags;
 	u32 config = 0;
@@ -319,6 +328,16 @@ static int msm_init_cm_dll(struct sdhci_host *host)
 	config &= ~CORE_CLK_PWRSAVE;
 	writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
 
+	if (msm_host->use_14lpp_dll_reset) {
+		config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+		config &= ~CORE_CK_OUT_EN;
+		writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+
+		config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2);
+		config |= CORE_DLL_CLOCK_DISABLE;
+		writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2);
+	}
+
 	/* Write 1 to DLL_RST bit of DLL_CONFIG register */
 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
 	config |= CORE_DLL_RST;
@@ -330,6 +349,24 @@ static int msm_init_cm_dll(struct sdhci_host *host)
 	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
 	msm_cm_dll_set_freq(host);
 
+	if (msm_host->use_14lpp_dll_reset) {
+		u32 mclk_freq = 0;
+
+		if ((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2)
+					& CORE_FLL_CYCLE_CNT))
+			mclk_freq = (u32)((host->clock / TCXO_FREQ) * 8);
+		else
+			mclk_freq = (u32)((host->clock / TCXO_FREQ) * 4);
+
+		config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2);
+		config &= ~(0xFF << 10);
+		config |= mclk_freq << 10;
+
+		writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2);
+		/* wait for 5us before enabling DLL clock */
+		udelay(5);
+	}
+
 	/* Write 0 to DLL_RST bit of DLL_CONFIG register */
 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
 	config &= ~CORE_DLL_RST;
@@ -340,6 +377,14 @@ static int msm_init_cm_dll(struct sdhci_host *host)
 	config &= ~CORE_DLL_PDN;
 	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
 
+	if (msm_host->use_14lpp_dll_reset) {
+		msm_cm_dll_set_freq(host);
+		/* Enable the DLL clock */
+		config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2);
+		config &= ~CORE_DLL_CLOCK_DISABLE;
+		writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2);
+	}
+
 	/* Set DLL_EN bit to 1. */
 	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
 	config |= CORE_DLL_EN;
@@ -641,6 +686,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)
 	dev_dbg(&pdev->dev, "MCI Version: 0x%08x, major: 0x%04x, minor: 0x%02x\n",
 		core_version, core_major, core_minor);
 
+	if ((core_major == 1) && (core_minor >= 0x42))
+		msm_host->use_14lpp_dll_reset = true;
+
 	/*
 	 * Support for some capabilities is not advertised by newer
 	 * controller versions and must be explicitly enabled.
-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, 
a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH v6 04/14] mmc: sdhci-msm: Change poor style writel/readl of registers
From: Ritesh Harjani @ 2016-11-07 11:24 UTC (permalink / raw)
  To: ulf.hansson-QSEj5FYQhm4dnm+yROfE0A,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA,
	adrian.hunter-ral2JQCrhuEAvxtiuMwx3w,
	shawn.lin-TNX95d0MmH7DzftRWevZcw, sboyd-sgV2jX0FEOL9JmXXK+q4OQ
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	david.brown-QSEj5FYQhm4dnm+yROfE0A,
	andy.gross-QSEj5FYQhm4dnm+yROfE0A,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	georgi.djakov-QSEj5FYQhm4dnm+yROfE0A,
	alex.lemberg-XdAiOPVOjttBDgjK7y7TUQ,
	mateusz.nowak-ral2JQCrhuEAvxtiuMwx3w,
	Yuliy.Izrailov-XdAiOPVOjttBDgjK7y7TUQ,
	asutoshd-sgV2jX0FEOL9JmXXK+q4OQ, kdorfman-sgV2jX0FEOL9JmXXK+q4OQ,
	david.griego-QSEj5FYQhm4dnm+yROfE0A,
	stummala-sgV2jX0FEOL9JmXXK+q4OQ, venkatg-sgV2jX0FEOL9JmXXK+q4OQ,
	rnayak-sgV2jX0FEOL9JmXXK+q4OQ,
	pramod.gurav-QSEj5FYQhm4dnm+yROfE0A, Ritesh Harjani
In-Reply-To: <1478517877-23733-1-git-send-email-riteshh-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>

This patch changes the poor style of writel/readl registers
into more readable format. Also to avoid mixed style format
of readl/writel in sdhci-msm driver.

Signed-off-by: Ritesh Harjani <riteshh-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Acked-by: Adrian Hunter <adrian.hunter-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
 drivers/mmc/host/sdhci-msm.c | 54 ++++++++++++++++++++++++++------------------
 1 file changed, 32 insertions(+), 22 deletions(-)

diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 8ef44a2a..42f42aa 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -137,8 +137,9 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
 	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
 
 	/* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */
-	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
-			| CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
+	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+	config |= CORE_CK_OUT_EN;
+	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
 
 	/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */
 	rc = msm_dll_poll_ck_out_en(host, 1);
@@ -305,6 +306,7 @@ static int msm_init_cm_dll(struct sdhci_host *host)
 	struct mmc_host *mmc = host->mmc;
 	int wait_cnt = 50;
 	unsigned long flags;
+	u32 config = 0;
 
 	spin_lock_irqsave(&host->lock, flags);
 
@@ -313,33 +315,40 @@ static int msm_init_cm_dll(struct sdhci_host *host)
 	 * tuning is in progress. Keeping PWRSAVE ON may
 	 * turn off the clock.
 	 */
-	writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
-			& ~CORE_CLK_PWRSAVE), host->ioaddr + CORE_VENDOR_SPEC);
+	config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
+	config &= ~CORE_CLK_PWRSAVE;
+	writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
 
 	/* Write 1 to DLL_RST bit of DLL_CONFIG register */
-	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
-			| CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
+	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+	config |= CORE_DLL_RST;
+	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
 
 	/* Write 1 to DLL_PDN bit of DLL_CONFIG register */
-	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
-			| CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
+	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+	config |= CORE_DLL_PDN;
+	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
 	msm_cm_dll_set_freq(host);
 
 	/* Write 0 to DLL_RST bit of DLL_CONFIG register */
-	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
-			& ~CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
+	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+	config &= ~CORE_DLL_RST;
+	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
 
 	/* Write 0 to DLL_PDN bit of DLL_CONFIG register */
-	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
-			& ~CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
+	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+	config &= ~CORE_DLL_PDN;
+	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
 
 	/* Set DLL_EN bit to 1. */
-	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
-			| CORE_DLL_EN), host->ioaddr + CORE_DLL_CONFIG);
+	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+	config |= CORE_DLL_EN;
+	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
 
 	/* Set CK_OUT_EN bit to 1. */
-	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
-			| CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
+	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+	config |= CORE_CK_OUT_EN;
+	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
 
 	/* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
 	while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) &
@@ -536,7 +545,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
 	struct resource *core_memres;
 	int ret;
 	u16 host_version, core_minor;
-	u32 core_version, caps;
+	u32 core_version, config;
 	u8 core_major;
 
 	host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host));
@@ -605,8 +614,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)
 	}
 
 	/* Reset the core and Enable SDHC mode */
-	writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) |
-		       CORE_SW_RST, msm_host->core_mem + CORE_POWER);
+	config = readl_relaxed(msm_host->core_mem + CORE_POWER);
+	config |= CORE_SW_RST;
+	writel_relaxed(config, msm_host->core_mem + CORE_POWER);
 
 	/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
 	usleep_range(1000, 5000);
@@ -636,9 +646,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)
 	 * controller versions and must be explicitly enabled.
 	 */
 	if (core_major >= 1 && core_minor != 0x11 && core_minor != 0x12) {
-		caps = readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES);
-		caps |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT;
-		writel_relaxed(caps, host->ioaddr +
+		config = readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES);
+		config |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT;
+		writel_relaxed(config, host->ioaddr +
 			       CORE_VENDOR_SPEC_CAPABILITIES0);
 	}
 
-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, 
a Linux Foundation Collaborative Project.

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox