All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v10 0/3] mmc: sdhci-msm: Add support for Qualcomm chipsets
@ 2014-03-04 19:27 Georgi Djakov
  2014-03-04 19:27 ` [PATCH v10 1/3] mmc: sdhci-msm: Qualcomm SDHCI binding documentation Georgi Djakov
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Georgi Djakov @ 2014-03-04 19:27 UTC (permalink / raw)
  To: linux-mmc, cjb, ulf.hansson, devicetree, grant.likely, robh+dt,
	pawel.moll, mark.rutland, swarren, ijc+devicetree, galak, rob
  Cc: linux-doc, linux-kernel, linux-arm-msm, Georgi Djakov

This patchset adds basic support of the Secure Digital Host Controller
Interface compliant controller found in Qualcomm SoCs.

Tested with eMMC and various micro SD cards on APQ8074 Dragonboard.
Applies to linux-next.

Changes from v9:
- Make platform_execute_tuning() static (suggested by Josh Cartwright) 
- Fix buffer size in platform_execute_tuning() (suggested by Josh Cartwright)
- Fix typo in patch subject (suggested by Kumar Gala)
- Move voltage ranges from DT to the driver (suggested by Bjorn Andersson)
- Simplify sdhci_msm_setup_vreg and irq handler (suggested by Bjorn Andersson)
- Disable prw irq in sdhci_msm_remove (suggested by Bjorn Andersson)
- Fail on peripheral clock errors (suggested by Bjorn Andersson)
- Remove unnecessary core_memres assignment (suggested by Bjorn Andersson)
- Fixes on freeing resources in probe() (suggested by Bjorn Andersson)

Changes from v8:
- Added controller version suffix to the DT compatible string.
- Switched Kconfig dependency from ARCH_MSM to the new ARCH_QCOM multiplatform.
- Addressed comments from Stephen Boyd on the 3rd patch (execute tunning).
- Added signed-off-by lines of the initial driver authors.
- Picked up tested-by. https://lkml.org/lkml/2013/11/14/85
- Minor changes on comments, prints and formatting.

Changes from v7:
- Added call to sdhci_get_of_property().
- Refactored sdhci_msm_dt_parse_vreg_info().
- Fixed possible ERR_PTR() dereferencing.
- Updated DT binding documentation.
- Removed lpm and currents from DT.
- Removed bus-speed-mode from DT.
- Updated and moved the sanity checks.
- Various typo and coding style fixes.
- Added platform_execute_tunning implementation.

Changes from v6:
- Fixed wrong pointer in sdhci_msm_pwr_irq().
- Added platform_execute_tuning() callback as the MSM SDHC does not
  support tuning as in SDHC 3.0 spec and will need custom implementation
  in order to support SDR104, HS200 and HS400.
- Removed the always-on devicetree property - if the regulator is
  configured as always-on, it will not be disabled anyway.
- Removed devm_pinctrl_get_select_default() - the default pins are 
  already set from the device core.
- Removed wrapper function sdhci_msm_set_vdd_io_vol() and enum
  vdd_io_level and now calling regulator_set_voltage() directly.
- Converted #defines to use BIT() macro.
- Added IS_ERR(vreg->reg) check at the beginning of sdhci_msm_vreg
  functions.
- Do not print errors when regulators init return -EPROBE_DEFER as the
  deffered init is not an actual error.
- Handle each power irq status bit separately in sdhci_msm_pwr_irq().
- Ensure that any pending power irq is acknowledged before enabling it,
  otherwise the irq handler will be fired prematurely.
- Minor changes.

Changes from v5:
- Driver is split into multiple patches
- Do not initialize variables that are assigned later in code
- Remove some useless comments
- Use shorter variable names
- Change pr_err() to dev_err()
- Optimized sdhci_msm_setup_vreg()
- Some code alignment fixes
- Improved DT values sanity check
- Added dev_dbg print for sdhci controller version in probe()
- Added usleep_range() after SW reset - it can take some time
- Added SDHCI_QUIRK_SINGLE_POWER_WRITE - power handled by PMIC
- Renamed DT property vdd-io to vddio

Changes from v4:
- Simplified sdhci_msm_vreg_disable() and sdhci_msm_set_vdd_io_vol()
- Use devm_ioremap_resource() instead of devm_ioremap()
- Converted IS_ERR_OR_NULL to IS_ERR
- Disable regulators in sdhci_msm_remove()
- Check for DT node at the beginning in sdhci_msm_probe()
- Removed more redundant code
- Changes in some error messages
- Minor fixes

Changes from v3:
- Allocate memory for all required structs at once
- Added termination entry in sdhci_msm_dt_match[]
- Fixed a missing sdhci_pltfm_free() in probe()
- Removed redundant of_match_ptr
- Removed the unneeded function sdhci_msm_vreg_reset()

Changes from v2:
- Added DT bindings for clocks
- Moved voltage regulators data to platform data
- Removed unneeded includes
- Removed obsolete and wrapper functions
- Removed error checking where unnecessary
- Removed redundant _clk suffix from clock names
- Just return instead of goto where possible
- Minor fixes

Georgi Djakov (3):
  mmc: sdhci-msm: Qualcomm SDHCI binding documentation
  mmc: sdhci-msm: Initial support for Qualcomm chipsets
  mmc: sdhci-msm: Add platform_execute_tuning implementation

 .../devicetree/bindings/mmc/sdhci-msm.txt          |   63 ++
 drivers/mmc/host/Kconfig                           |   13 +
 drivers/mmc/host/Makefile                          |    1 +
 drivers/mmc/host/sdhci-msm.c                       |  838 ++++++++++++++++++++
 4 files changed, 915 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-msm.txt
 create mode 100644 drivers/mmc/host/sdhci-msm.c

-- 
1.7.9.5

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v10 1/3] mmc: sdhci-msm: Qualcomm SDHCI binding documentation
  2014-03-04 19:27 [PATCH v10 0/3] mmc: sdhci-msm: Add support for Qualcomm chipsets Georgi Djakov
@ 2014-03-04 19:27 ` Georgi Djakov
  2014-03-05  4:25   ` Ulf Hansson
  2014-03-05  6:56   ` Rob Herring
  2014-03-04 19:27 ` [PATCH v10 2/3] mmc: sdhci-msm: Initial support for Qualcomm chipsets Georgi Djakov
  2014-03-04 19:27 ` [PATCH v10 3/3] mmc: sdhci-msm: Add platform_execute_tuning implementation Georgi Djakov
  2 siblings, 2 replies; 11+ messages in thread
From: Georgi Djakov @ 2014-03-04 19:27 UTC (permalink / raw)
  To: linux-mmc, cjb, ulf.hansson, devicetree, grant.likely, robh+dt,
	pawel.moll, mark.rutland, swarren, ijc+devicetree, galak, rob
  Cc: linux-doc, linux-kernel, linux-arm-msm, Georgi Djakov

This patch adds the device-tree binding documentation for
Qualcomm SDHCI driver. It contains the differences between
the core properties in mmc.txt and the properties used by
the sdhci-msm driver.

Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com>
---
 .../devicetree/bindings/mmc/sdhci-msm.txt          |   63 ++++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-msm.txt

diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
new file mode 100644
index 0000000..c635c53
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -0,0 +1,63 @@
+* Qualcomm SDHCI controller (sdhci-msm)
+
+This file documents differences between the core properties in mmc.txt
+and the properties used by the sdhci-msm driver.
+
+Required properties:
+- compatible: Should contain "qcom,sdhci-msm-v4".
+- reg: Base address and length of the register set listed in reg-names.
+- reg-names: Should contain the following:
+	"hc_mem"   - Host controller register map
+	"core_mem" - SD Core register map
+- interrupts: Should contain an interrupt-specifiers for the interrupts listed in interrupt-names.
+- interrupt-names: Should contain the following:
+	"hc_irq"     - Host controller interrupt
+	"pwr_irq"    - PMIC interrupt
+- vdd-supply: Phandle to the regulator for the vdd (core voltage) supply.
+- vdd-io-supply: Phandle to the regulator for the vdd-io (i/o voltage) supply.
+- pinctrl-names: Should contain only one value - "default".
+- pinctrl-0: Should specify pin control groups used for this controller.
+- clocks: A list of phandle + clock-specifier pairs for the clocks listed in clock-names.
+- clock-names: Should contain the following:
+	"iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required)
+	"core"	- SDC MMC clock (MCLK) (required)
+	"bus"	- SDCC bus voter clock (optional)
+
+Example:
+
+	sdhc_1: sdhci@f9824900 {
+		compatible = "qcom,sdhci-msm-v4";
+		reg = <0xf9824900 0x11c>, <0xf9824000 0x800>;
+		reg-names = "hc_mem", "core_mem";
+		interrupts = <0 123 0>, <0 138 0>;
+		interrupt-names = "hc_irq", "pwr_irq";
+		bus-width = <8>;
+		non-removable;
+
+		vdd-supply = <&pm8941_l20>;
+		vdd-io-supply = <&pm8941_s3>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&sdc1_clk &sdc1_cmd &sdc1_data>;
+
+		clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>;
+		clock-names = "core", "iface";
+	};
+
+	sdhc_2: sdhci@f98a4900 {
+		compatible = "qcom,sdhci-msm-v4";
+		reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>;
+		reg-names = "hc_mem", "core_mem";
+		interrupts = <0 125 0>, <0 221 0>;
+		interrupt-names = "hc_irq", "pwr_irq";
+		bus-width = <4>;
+
+		vdd-supply = <&pm8941_l21>;
+		vdd-io-supply = <&pm8941_l13>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&sdc2_clk &sdc2_cmd &sdc2_data>;
+
+		clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>;
+		clock-names = "core", "iface";
+	};
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v10 2/3] mmc: sdhci-msm: Initial support for Qualcomm chipsets
  2014-03-04 19:27 [PATCH v10 0/3] mmc: sdhci-msm: Add support for Qualcomm chipsets Georgi Djakov
  2014-03-04 19:27 ` [PATCH v10 1/3] mmc: sdhci-msm: Qualcomm SDHCI binding documentation Georgi Djakov
@ 2014-03-04 19:27 ` Georgi Djakov
  2014-03-05  4:41   ` Ulf Hansson
  2014-03-04 19:27 ` [PATCH v10 3/3] mmc: sdhci-msm: Add platform_execute_tuning implementation Georgi Djakov
  2 siblings, 1 reply; 11+ messages in thread
From: Georgi Djakov @ 2014-03-04 19:27 UTC (permalink / raw)
  To: linux-mmc, cjb, ulf.hansson, devicetree, grant.likely, robh+dt,
	pawel.moll, mark.rutland, swarren, ijc+devicetree, galak, rob
  Cc: linux-doc, linux-kernel, linux-arm-msm, Georgi Djakov

This platform driver adds the initial support of Secure
Digital Host Controller Interface compliant controller
found in Qualcomm chipsets.

Signed-off-by: Asutosh Das <asutoshd@codeaurora.org>
Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
Tested-by: Ivan T. Ivanov <iivanov@mm-sol.com>
Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com>
---
 drivers/mmc/host/Kconfig     |   13 ++
 drivers/mmc/host/Makefile    |    1 +
 drivers/mmc/host/sdhci-msm.c |  427 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 441 insertions(+)
 create mode 100644 drivers/mmc/host/sdhci-msm.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 82cc34d..66ef8b9 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -334,6 +334,19 @@ config MMC_ATMELMCI
 
 	  If unsure, say N.
 
+config MMC_SDHCI_MSM
+	tristate "Qualcomm SDHCI Controller Support"
+	depends on ARCH_QCOM
+	depends on MMC_SDHCI_PLTFM
+	help
+	  This selects the Secure Digital Host Controller Interface (SDHCI)
+	  support present in Qualcomm SOCs. The controller supports
+	  SD/MMC/SDIO devices.
+
+	  If you have a controller with this interface, say Y or M here.
+
+	  If unsure, say N.
+
 config MMC_MSM
 	tristate "Qualcomm SDCC Controller Support"
 	depends on MMC && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50)
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index f162f87a0..0c8aa5e 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ESDHC)	+= sdhci-of-esdhc.o
 obj-$(CONFIG_MMC_SDHCI_OF_HLWD)		+= sdhci-of-hlwd.o
 obj-$(CONFIG_MMC_SDHCI_BCM_KONA)	+= sdhci-bcm-kona.o
 obj-$(CONFIG_MMC_SDHCI_BCM2835)		+= sdhci-bcm2835.o
+obj-$(CONFIG_MMC_SDHCI_MSM)		+= sdhci-msm.o
 
 ifeq ($(CONFIG_CB710_DEBUG),y)
 	CFLAGS-cb710-mmc	+= -DDEBUG
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
new file mode 100644
index 0000000..46e4e0b
--- /dev/null
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -0,0 +1,427 @@
+/*
+ * drivers/mmc/host/sdhci-msm.c - Qualcomm SDHCI Platform driver
+ *
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+
+#include "sdhci-pltfm.h"
+
+#define CORE_HC_MODE		0x78
+#define HC_MODE_EN		0x1
+
+#define CORE_POWER		0x0
+#define CORE_SW_RST		BIT(7)
+
+#define CORE_PWRCTL_STATUS	0xdc
+#define CORE_PWRCTL_MASK	0xe0
+#define CORE_PWRCTL_CLEAR	0xe4
+#define CORE_PWRCTL_CTL		0xe8
+
+#define CORE_PWRCTL_BUS_OFF	BIT(0)
+#define CORE_PWRCTL_BUS_ON	BIT(1)
+#define CORE_PWRCTL_IO_LOW	BIT(2)
+#define CORE_PWRCTL_IO_HIGH	BIT(3)
+
+#define CORE_PWRCTL_BUS_SUCCESS	BIT(0)
+#define CORE_PWRCTL_BUS_FAIL	BIT(1)
+#define CORE_PWRCTL_IO_SUCCESS	BIT(2)
+#define CORE_PWRCTL_IO_FAIL	BIT(3)
+
+#define INT_MASK		0xf
+
+#define QCOM_SDHCI_VOLTAGE_LOW	1800000
+#define QCOM_SDHCI_VOLTAGE_HIGH	2950000
+
+
+struct sdhci_msm_pltfm_data {
+	u32 caps;				/* Supported UHS-I Modes */
+	u32 caps2;				/* More capabilities */
+	struct regulator *vdd;			/* VDD/VCC regulator */
+	struct regulator *vdd_io;		/* VDD IO regulator */
+};
+
+struct sdhci_msm_host {
+	struct platform_device *pdev;
+	void __iomem *core_mem;	/* MSM SDCC mapped address */
+	int pwr_irq;		/* power irq */
+	struct clk *clk;	/* main SD/MMC bus clock */
+	struct clk *pclk;	/* SDHC peripheral bus clock */
+	struct clk *bus_clk;	/* SDHC bus voter clock */
+	struct sdhci_msm_pltfm_data pdata;
+	struct mmc_host *mmc;
+	struct sdhci_pltfm_data sdhci_msm_pdata;
+};
+
+/* MSM platform specific tuning */
+int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+	/*
+	 * Tuning is required for SDR104, HS200 and HS400 cards and if the clock
+	 * frequency greater than 100MHz in those modes. The standard tuning
+	 * procedure should not be executed, but a custom implementation will be
+	 * added here instead.
+	 */
+	return 0;
+}
+
+static int sdhci_msm_vreg_enable(struct device *dev, struct regulator *vreg)
+{
+	int ret = 0;
+
+	if (!regulator_is_enabled(vreg)) {
+		/* Set voltage level */
+		ret = regulator_set_voltage(vreg, QCOM_SDHCI_VOLTAGE_HIGH,
+					    QCOM_SDHCI_VOLTAGE_HIGH);
+		if (ret)
+			return ret;
+	}
+
+	ret = regulator_enable(vreg);
+	if (ret)
+		dev_err(dev, "Failed to enable regulator (%d)\n", ret);
+
+	return ret;
+}
+
+static int sdhci_msm_vreg_disable(struct device *dev, struct regulator *vreg)
+{
+	int ret = 0;
+
+	if (!regulator_is_enabled(vreg))
+		return ret;
+
+	/* Set min. voltage to 0 */
+	ret = regulator_set_voltage(vreg, 0, QCOM_SDHCI_VOLTAGE_HIGH);
+	if (ret)
+		return ret;
+
+	ret = regulator_disable(vreg);
+	if (ret)
+		dev_err(dev, "Failed to disable regulator (%d)\n", ret);
+
+	return ret;
+}
+
+static int sdhci_msm_setup_vreg(struct sdhci_msm_host *msm_host, bool enable)
+{
+	int ret;
+
+	if (enable) {
+		ret = sdhci_msm_vreg_enable(&msm_host->pdev->dev,
+				msm_host->pdata.vdd);
+		ret |= sdhci_msm_vreg_enable(&msm_host->pdev->dev,
+				msm_host->pdata.vdd_io);
+	} else {
+		ret = sdhci_msm_vreg_disable(&msm_host->pdev->dev,
+				msm_host->pdata.vdd);
+		ret |= sdhci_msm_vreg_disable(&msm_host->pdev->dev,
+				msm_host->pdata.vdd_io);
+	}
+
+	return ret;
+}
+
+static int sdhci_msm_vreg_init(struct device *dev,
+			       struct sdhci_msm_pltfm_data *pdata)
+{
+	pdata->vdd = devm_regulator_get(dev, "vdd-supply");
+	if (IS_ERR(pdata->vdd))
+		return PTR_ERR(pdata->vdd);
+
+	pdata->vdd_io = devm_regulator_get(dev, "vdd-io-supply");
+	if (IS_ERR(pdata->vdd_io))
+		return PTR_ERR(pdata->vdd_io);
+
+	return 0;
+}
+
+static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
+{
+	struct sdhci_host *host = (struct sdhci_host *)data;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = pltfm_host->priv;
+	u8 irq_status;
+	u8 irq_ack = 0;
+	int ret = 0;
+
+	irq_status = readb_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
+	dev_dbg(mmc_dev(msm_host->mmc), "%s: Received IRQ(%d), status=0x%x\n",
+		mmc_hostname(msm_host->mmc), irq, irq_status);
+
+	/* Clear the interrupt */
+	writeb_relaxed(irq_status, (msm_host->core_mem + CORE_PWRCTL_CLEAR));
+
+	/* Ensure that the write has reached the device */
+	mb();
+
+	if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF)) {
+		/* Handle BUS ON/OFF */
+		bool bus_on = (irq_status & CORE_PWRCTL_BUS_ON) ? 1 : 0;
+
+		ret = sdhci_msm_setup_vreg(msm_host, bus_on);
+	} else if (irq_status & (CORE_PWRCTL_IO_LOW | CORE_PWRCTL_IO_HIGH)) {
+		/* Handle IO LOW/HIGH */
+		int io_high = (irq_status & CORE_PWRCTL_IO_HIGH) ? 1 : 0;
+
+		if (io_high) {
+			ret = regulator_set_voltage(msm_host->pdata.vdd_io,
+					QCOM_SDHCI_VOLTAGE_HIGH,
+					QCOM_SDHCI_VOLTAGE_HIGH);
+		} else {
+			ret = regulator_set_voltage(msm_host->pdata.vdd_io,
+					QCOM_SDHCI_VOLTAGE_LOW,
+					QCOM_SDHCI_VOLTAGE_LOW);
+		}
+	}
+
+	if (ret)
+		irq_ack = CORE_PWRCTL_BUS_FAIL;
+	else
+		irq_ack = CORE_PWRCTL_BUS_SUCCESS;
+
+	/* ACK status to the core */
+	writeb_relaxed(irq_ack, (msm_host->core_mem + CORE_PWRCTL_CTL));
+
+	/* Commit the write to the device */
+	mb();
+
+	dev_dbg(mmc_dev(msm_host->mmc), "%s: Handled IRQ(%d), ret=%d, ack=0x%x\n",
+		 mmc_hostname(msm_host->mmc), irq, ret, irq_ack);
+	return IRQ_HANDLED;
+}
+
+static const struct of_device_id sdhci_msm_dt_match[] = {
+	{ .compatible = "qcom,sdhci-msm-v4" },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
+
+static struct sdhci_ops sdhci_msm_ops = {
+	.platform_execute_tuning = sdhci_msm_execute_tuning,
+};
+
+static int sdhci_msm_probe(struct platform_device *pdev)
+{
+	struct sdhci_host *host;
+	struct sdhci_pltfm_host *pltfm_host;
+	struct sdhci_msm_host *msm_host;
+	struct resource *core_memres;
+	int ret, dead;
+	u16 host_version;
+
+	if (!pdev->dev.of_node) {
+		dev_err(&pdev->dev, "No device tree data\n");
+		return -ENOENT;
+	}
+
+	msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL);
+	if (!msm_host)
+		return -ENOMEM;
+
+	msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops;
+	host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0);
+	if (IS_ERR(host))
+		return PTR_ERR(host);
+
+	pltfm_host = sdhci_priv(host);
+	pltfm_host->priv = msm_host;
+	msm_host->mmc = host->mmc;
+	msm_host->pdev = pdev;
+
+	ret = mmc_of_parse(host->mmc);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed parsing mmc device tree\n");
+		goto pltfm_free;
+	}
+
+	sdhci_get_of_property(pdev);
+
+	/* Setup SDCC bus voter clock. */
+	msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus");
+	if (!IS_ERR(msm_host->bus_clk)) {
+		/* Vote for max. clk rate for max. performance */
+		ret = clk_set_rate(msm_host->bus_clk, INT_MAX);
+		if (ret)
+			goto pltfm_free;
+		ret = clk_prepare_enable(msm_host->bus_clk);
+		if (ret)
+			goto pltfm_free;
+	}
+
+	/* Setup main peripheral bus clock */
+	msm_host->pclk = devm_clk_get(&pdev->dev, "iface");
+	if (IS_ERR(msm_host->pclk)) {
+		ret = PTR_ERR(msm_host->pclk);
+		dev_err(&pdev->dev, "Perpheral clk setup failed (%d)\n", ret);
+		goto bus_clk_disable;
+	}
+
+	ret = clk_prepare_enable(msm_host->pclk);
+	if (ret)
+		goto bus_clk_disable;
+
+	/* Setup SDC MMC clock */
+	msm_host->clk = devm_clk_get(&pdev->dev, "core");
+	if (IS_ERR(msm_host->clk)) {
+		ret = PTR_ERR(msm_host->clk);
+		dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret);
+		goto pclk_disable;
+	}
+
+	ret = clk_prepare_enable(msm_host->clk);
+	if (ret)
+		goto pclk_disable;
+
+	/* Setup regulators */
+	ret = sdhci_msm_vreg_init(&pdev->dev, &msm_host->pdata);
+	if (ret) {
+		if (ret != -EPROBE_DEFER)
+			dev_err(&pdev->dev,
+				"Regulator setup failed (%d)\n", ret);
+		goto clk_disable;
+	}
+
+	core_memres = platform_get_resource_byname(pdev,
+						   IORESOURCE_MEM, "core_mem");
+	msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres);
+
+	if (IS_ERR(msm_host->core_mem)) {
+		dev_err(&pdev->dev, "Failed to remap registers\n");
+		ret = PTR_ERR(msm_host->core_mem);
+		goto clk_disable;
+	}
+
+	/* 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);
+
+	/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
+	usleep_range(1000, 5000);
+	if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) {
+		dev_err(&pdev->dev, "Stuck in reset\n");
+		ret = -ETIMEDOUT;
+		goto clk_disable;
+	}
+
+	/* Set HC_MODE_EN bit in HC_MODE register */
+	writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
+
+	/*
+	 * Following are the deviations from SDHC spec v3.0 -
+	 * 1. Card detection is handled using separate GPIO.
+	 * 2. Bus power control is handled by interacting with PMIC.
+	 */
+	host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+	host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE;
+
+	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) >>
+			       SDHCI_VENDOR_VER_SHIFT));
+
+	/* Setup PWRCTL irq */
+	msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
+	if (msm_host->pwr_irq < 0) {
+		dev_err(&pdev->dev, "Failed to get pwr_irq by name (%d)\n",
+			msm_host->pwr_irq);
+		goto clk_disable;
+	}
+	ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL,
+					sdhci_msm_pwr_irq, IRQF_ONESHOT,
+					dev_name(&pdev->dev), host);
+	if (ret) {
+		dev_err(&pdev->dev, "Request threaded irq(%d) failed (%d)\n",
+			msm_host->pwr_irq, ret);
+		goto clk_disable;
+	}
+
+	/* Enable power irq */
+	writel_relaxed(INT_MASK, (msm_host->core_mem + CORE_PWRCTL_MASK));
+
+	msm_host->mmc->caps |= msm_host->pdata.caps;
+	msm_host->mmc->caps2 |= msm_host->pdata.caps2;
+
+	ret = sdhci_add_host(host);
+	if (ret) {
+		dev_err(&pdev->dev, "Add host failed (%d)\n", ret);
+		goto vreg_disable;
+	}
+
+	ret = clk_set_rate(msm_host->clk, host->max_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "MClk rate set failed (%d)\n", ret);
+		goto remove_host;
+	}
+
+	return 0;
+
+remove_host:
+	dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
+	sdhci_remove_host(host, dead);
+vreg_disable:
+	sdhci_msm_vreg_disable(&pdev->dev, msm_host->pdata.vdd);
+	sdhci_msm_vreg_disable(&pdev->dev, msm_host->pdata.vdd_io);
+clk_disable:
+	clk_disable_unprepare(msm_host->clk);
+pclk_disable:
+	clk_disable_unprepare(msm_host->pclk);
+bus_clk_disable:
+	if (!IS_ERR(msm_host->bus_clk))
+		clk_disable_unprepare(msm_host->bus_clk);
+pltfm_free:
+	sdhci_pltfm_free(pdev);
+	return ret;
+}
+
+static int sdhci_msm_remove(struct platform_device *pdev)
+{
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = pltfm_host->priv;
+	int dead;
+
+	/* Disable power irq */
+	writel_relaxed(~INT_MASK, (msm_host->core_mem + CORE_PWRCTL_MASK));
+
+	dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
+	sdhci_remove_host(host, dead);
+	sdhci_pltfm_free(pdev);
+	sdhci_msm_vreg_disable(&pdev->dev, msm_host->pdata.vdd);
+	sdhci_msm_vreg_disable(&pdev->dev, msm_host->pdata.vdd_io);
+	clk_disable_unprepare(msm_host->clk);
+	clk_disable_unprepare(msm_host->pclk);
+	if (!IS_ERR(msm_host->bus_clk))
+		clk_disable_unprepare(msm_host->bus_clk);
+	return 0;
+}
+
+static struct platform_driver sdhci_msm_driver = {
+	.probe = sdhci_msm_probe,
+	.remove = sdhci_msm_remove,
+	.driver = {
+		   .name = "sdhci_msm",
+		   .owner = THIS_MODULE,
+		   .of_match_table = sdhci_msm_dt_match,
+	},
+};
+
+module_platform_driver(sdhci_msm_driver);
+
+MODULE_DESCRIPTION("Qualcomm Secure Digital Host Controller Interface driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v10 3/3] mmc: sdhci-msm: Add platform_execute_tuning implementation
  2014-03-04 19:27 [PATCH v10 0/3] mmc: sdhci-msm: Add support for Qualcomm chipsets Georgi Djakov
  2014-03-04 19:27 ` [PATCH v10 1/3] mmc: sdhci-msm: Qualcomm SDHCI binding documentation Georgi Djakov
  2014-03-04 19:27 ` [PATCH v10 2/3] mmc: sdhci-msm: Initial support for Qualcomm chipsets Georgi Djakov
@ 2014-03-04 19:27 ` Georgi Djakov
  2 siblings, 0 replies; 11+ messages in thread
From: Georgi Djakov @ 2014-03-04 19:27 UTC (permalink / raw)
  To: linux-mmc, cjb, ulf.hansson, devicetree, grant.likely, robh+dt,
	pawel.moll, mark.rutland, swarren, ijc+devicetree, galak, rob
  Cc: linux-doc, linux-kernel, linux-arm-msm, Georgi Djakov

This patch adds implementation for platform specific tuning in order to support
HS200 bus speed mode on Qualcomm SDHCI controller.

Signed-off-by: Asutosh Das <asutoshd@codeaurora.org>
Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com>
---
 drivers/mmc/host/sdhci-msm.c |  425 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 418 insertions(+), 7 deletions(-)

diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 46e4e0b..10a9f65 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -18,6 +18,8 @@
 #include <linux/of_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/delay.h>
+#include <linux/mmc/mmc.h>
+#include <linux/slab.h>
 
 #include "sdhci-pltfm.h"
 
@@ -43,10 +45,47 @@
 #define CORE_PWRCTL_IO_FAIL	BIT(3)
 
 #define INT_MASK		0xf
-
 #define QCOM_SDHCI_VOLTAGE_LOW	1800000
 #define QCOM_SDHCI_VOLTAGE_HIGH	2950000
+#define MAX_PHASES		16
+
+#define CORE_DLL_LOCK		BIT(7)
+#define CORE_DLL_EN		BIT(16)
+#define CORE_CDR_EN		BIT(17)
+#define CORE_CK_OUT_EN		BIT(18)
+#define CORE_CDR_EXT_EN		BIT(19)
+#define CORE_DLL_PDN		BIT(29)
+#define CORE_DLL_RST		BIT(30)
+#define CORE_DLL_CONFIG		0x100
+#define CORE_DLL_TEST_CTL	0x104
+#define CORE_DLL_STATUS		0x108
+
+#define CORE_VENDOR_SPEC	0x10c
+#define CORE_CLK_PWRSAVE	BIT(1)
+#define CORE_IO_PAD_PWR_SWITCH	BIT(16)
+
+#define CDR_SELEXT_SHIFT	20
+#define CDR_SELEXT_MASK		(0xf << CDR_SELEXT_SHIFT)
+#define CMUX_SHIFT_PHASE_SHIFT	24
+#define CMUX_SHIFT_PHASE_MASK	(7 << CMUX_SHIFT_PHASE_SHIFT)
+
+static const u32 tuning_block_64[] = {
+	0x00ff0fff, 0xccc3ccff, 0xffcc3cc3, 0xeffefffe,
+	0xddffdfff, 0xfbfffbff, 0xff7fffbf, 0xefbdf777,
+	0xf0fff0ff, 0x3cccfc0f, 0xcfcc33cc, 0xeeffefff,
+	0xfdfffdff, 0xffbfffdf, 0xfff7ffbb, 0xde7b7ff7
+};
 
+static const u32 tuning_block_128[] = {
+	0xff00ffff, 0x0000ffff, 0xccccffff, 0xcccc33cc,
+	0xcc3333cc, 0xffffcccc, 0xffffeeff, 0xffeeeeff,
+	0xffddffff, 0xddddffff, 0xbbffffff, 0xbbffffff,
+	0xffffffbb, 0xffffff77, 0x77ff7777, 0xffeeddbb,
+	0x00ffffff, 0x00ffffff, 0xccffff00, 0xcc33cccc,
+	0x3333cccc, 0xffcccccc, 0xffeeffff, 0xeeeeffff,
+	0xddffffff, 0xddffffff, 0xffffffdd, 0xffffffbb,
+	0xffffbbbb, 0xffff77ff, 0xff7777ff, 0xeeddbb77
+};
 
 struct sdhci_msm_pltfm_data {
 	u32 caps;				/* Supported UHS-I Modes */
@@ -67,18 +106,390 @@ struct sdhci_msm_host {
 	struct sdhci_pltfm_data sdhci_msm_pdata;
 };
 
-/* MSM platform specific tuning */
-int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
+/* Platform specific tuning */
+static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll)
+{
+	u32 wait_cnt = 50;
+	u8 ck_out_en;
+	struct mmc_host *mmc = host->mmc;
+
+	/* Poll for CK_OUT_EN bit.  max. poll time = 50us */
+	ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
+			CORE_CK_OUT_EN);
+
+	while (ck_out_en != poll) {
+		if (--wait_cnt == 0) {
+			dev_err(mmc_dev(mmc), "%s: CK_OUT_EN bit is not %d\n",
+			       mmc_hostname(mmc), poll);
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+
+		ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
+				CORE_CK_OUT_EN);
+	}
+
+	return 0;
+}
+
+static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
+{
+	int rc;
+	static const u8 grey_coded_phase_table[] = {
+		0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4,
+		0xc, 0xd, 0xf, 0xe, 0xa, 0xb, 0x9, 0x8
+	};
+	unsigned long flags;
+	u32 config;
+	struct mmc_host *mmc = host->mmc;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+	config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN);
+	config |= (CORE_CDR_EXT_EN | CORE_DLL_EN);
+	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+
+	/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */
+	rc = msm_dll_poll_ck_out_en(host, 0);
+	if (rc)
+		goto err_out;
+
+	/*
+	 * Write the selected DLL clock output phase (0 ... 15)
+	 * to CDR_SELEXT bit field of DLL_CONFIG register.
+	 */
+	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+	config &= ~CDR_SELEXT_MASK;
+	config |= grey_coded_phase_table[phase] << CDR_SELEXT_SHIFT;
+	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);
+
+	/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */
+	rc = msm_dll_poll_ck_out_en(host, 1);
+	if (rc)
+		goto err_out;
+
+	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+	config |= CORE_CDR_EN;
+	config &= ~CORE_CDR_EXT_EN;
+	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+	goto out;
+
+err_out:
+	dev_err(mmc_dev(mmc), "%s: Failed to set DLL phase: %d\n",
+	       mmc_hostname(mmc), phase);
+out:
+	spin_unlock_irqrestore(&host->lock, flags);
+	return rc;
+}
+
+/*
+ * 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).
+ * Select the 3/4 of the range and configure the DLL with the
+ * selected DLL clock output phase.
+ */
+
+static int msm_find_most_appropriate_phase(struct sdhci_host *host,
+					   u8 *phase_table, u8 total_phases)
+{
+	int ret;
+	u8 ranges[MAX_PHASES][MAX_PHASES] = { {0}, {0} };
+	u8 phases_per_row[MAX_PHASES] = { 0 };
+	int row_index = 0, col_index = 0, selected_row_index = 0, curr_max = 0;
+	int i, cnt, phase_0_raw_index = 0, phase_15_raw_index = 0;
+	bool phase_0_found = false, phase_15_found = false;
+	struct mmc_host *mmc = host->mmc;
+
+	if (!total_phases || (total_phases > MAX_PHASES)) {
+		dev_err(mmc_dev(mmc), "%s: Invalid argument: total_phases=%d\n",
+		       mmc_hostname(mmc), total_phases);
+		return -EINVAL;
+	}
+
+	for (cnt = 0; cnt < total_phases; cnt++) {
+		ranges[row_index][col_index] = phase_table[cnt];
+		phases_per_row[row_index] += 1;
+		col_index++;
+
+		if ((cnt + 1) == total_phases) {
+			continue;
+		/* check if next phase in phase_table is consecutive or not */
+		} else if ((phase_table[cnt] + 1) != phase_table[cnt + 1]) {
+			row_index++;
+			col_index = 0;
+		}
+	}
+
+	if (row_index >= MAX_PHASES)
+		return -EINVAL;
+
+	/* Check if phase-0 is present in first valid window? */
+	if (!ranges[0][0]) {
+		phase_0_found = true;
+		phase_0_raw_index = 0;
+		/* Check if cycle exist between 2 valid windows */
+		for (cnt = 1; cnt <= row_index; cnt++) {
+			if (phases_per_row[cnt]) {
+				for (i = 0; i < phases_per_row[cnt]; i++) {
+					if (ranges[cnt][i] == 15) {
+						phase_15_found = true;
+						phase_15_raw_index = cnt;
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	/* If 2 valid windows form cycle then merge them as single window */
+	if (phase_0_found && phase_15_found) {
+		/* number of phases in raw where phase 0 is present */
+		u8 phases_0 = phases_per_row[phase_0_raw_index];
+		/* number of phases in raw where phase 15 is present */
+		u8 phases_15 = phases_per_row[phase_15_raw_index];
+
+		if (phases_0 + phases_15 >= MAX_PHASES)
+			/*
+			 * If there are more than 1 phase windows then total
+			 * number of phases in both the windows should not be
+			 * more than or equal to MAX_PHASES.
+			 */
+			return -EINVAL;
+
+		/* Merge 2 cyclic windows */
+		i = phases_15;
+		for (cnt = 0; cnt < phases_0; cnt++) {
+			ranges[phase_15_raw_index][i] =
+			    ranges[phase_0_raw_index][cnt];
+			if (++i >= MAX_PHASES)
+				break;
+		}
+
+		phases_per_row[phase_0_raw_index] = 0;
+		phases_per_row[phase_15_raw_index] = phases_15 + phases_0;
+	}
+
+	for (cnt = 0; cnt <= row_index; cnt++) {
+		if (phases_per_row[cnt] > curr_max) {
+			curr_max = phases_per_row[cnt];
+			selected_row_index = cnt;
+		}
+	}
+
+	i = (curr_max * 3) / 4;
+	if (i)
+		i--;
+
+	ret = ranges[selected_row_index][i];
+
+	if (ret >= MAX_PHASES) {
+		ret = -EINVAL;
+		dev_err(mmc_dev(mmc), "%s: Invalid phase selected=%d\n",
+		       mmc_hostname(mmc), ret);
+	}
+
+	return ret;
+}
+
+static inline void msm_cm_dll_set_freq(struct sdhci_host *host)
+{
+	u32 mclk_freq = 0, config;
+
+	/* Program the MCLK value to MCLK_FREQ bit field */
+	if (host->clock <= 112000000)
+		mclk_freq = 0;
+	else if (host->clock <= 125000000)
+		mclk_freq = 1;
+	else if (host->clock <= 137000000)
+		mclk_freq = 2;
+	else if (host->clock <= 150000000)
+		mclk_freq = 3;
+	else if (host->clock <= 162000000)
+		mclk_freq = 4;
+	else if (host->clock <= 175000000)
+		mclk_freq = 5;
+	else if (host->clock <= 187000000)
+		mclk_freq = 6;
+	else if (host->clock <= 200000000)
+		mclk_freq = 7;
+
+	config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+	config &= ~CMUX_SHIFT_PHASE_MASK;
+	config |= mclk_freq << CMUX_SHIFT_PHASE_SHIFT;
+	writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+}
+
+/* Initialize the DLL (Programmable Delay Line) */
+static int msm_init_cm_dll(struct sdhci_host *host)
 {
+	struct mmc_host *mmc = host->mmc;
+	int wait_cnt = 50;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+
 	/*
-	 * Tuning is required for SDR104, HS200 and HS400 cards and if the clock
-	 * frequency greater than 100MHz in those modes. The standard tuning
-	 * procedure should not be executed, but a custom implementation will be
-	 * added here instead.
+	 * Make sure that clock is always enabled when DLL
+	 * 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);
+
+	/* 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);
+
+	/* 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);
+	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);
+
+	/* 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);
+
+	/* Set DLL_EN bit to 1. */
+	writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+			| CORE_DLL_EN), 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);
+
+	/* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
+	while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) &
+		 CORE_DLL_LOCK)) {
+		/* max. wait for 50us sec for LOCK bit to be set */
+		if (--wait_cnt == 0) {
+			dev_err(mmc_dev(mmc), "%s: DLL failed to LOCK\n",
+			       mmc_hostname(mmc));
+			spin_unlock_irqrestore(&host->lock, flags);
+			return -ETIMEDOUT;
+		}
+		udelay(1);
+	}
+
+	spin_unlock_irqrestore(&host->lock, flags);
 	return 0;
 }
 
+static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+	int tuning_seq_cnt = 3;
+	u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
+	const u32 *tuning_block_pattern = tuning_block_64;
+	int size = sizeof(tuning_block_64);	/* Pattern size in bytes */
+	int rc;
+	struct mmc_host *mmc = host->mmc;
+	struct mmc_ios ios = host->mmc->ios;
+
+	/*
+	 * 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)))
+		return 0;
+
+	if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
+	    (mmc->ios.bus_width == MMC_BUS_WIDTH_8)) {
+		tuning_block_pattern = tuning_block_128;
+		size = sizeof(tuning_block_128);
+	}
+
+	data_buf = kmalloc(size, GFP_KERNEL);
+	if (!data_buf)
+		return -ENOMEM;
+
+retry:
+	/* First of all reset the tuning block */
+	rc = msm_init_cm_dll(host);
+	if (rc)
+		goto out;
+
+	phase = 0;
+	do {
+		struct mmc_command cmd = { 0 };
+		struct mmc_data data = { 0 };
+		struct mmc_request mrq = {
+			.cmd = &cmd,
+			.data = &data
+		};
+		struct scatterlist sg;
+
+		/* Set the phase in delay line hw block */
+		rc = msm_config_cm_dll_phase(host, phase);
+		if (rc)
+			goto out;
+
+		cmd.opcode = opcode;
+		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+		data.blksz = size;
+		data.blocks = 1;
+		data.flags = MMC_DATA_READ;
+		data.timeout_ns = NSEC_PER_SEC;	/* 1 second */
+
+		data.sg = &sg;
+		data.sg_len = 1;
+		sg_init_one(&sg, data_buf, size);
+		memset(data_buf, 0, size);
+		mmc_wait_for_req(mmc, &mrq);
+
+		if (!cmd.error && !data.error &&
+		    !memcmp(data_buf, tuning_block_pattern, size)) {
+			/* Tuning is successful at this tuning point */
+			tuned_phases[tuned_phase_cnt++] = phase;
+			dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
+				 mmc_hostname(mmc), phase);
+		}
+	} while (++phase < ARRAY_SIZE(tuned_phases));
+
+	if (tuned_phase_cnt) {
+		rc = msm_find_most_appropriate_phase(host, tuned_phases,
+						     tuned_phase_cnt);
+		if (rc < 0)
+			goto out;
+		else
+			phase = rc;
+
+		/*
+		 * Finally set the selected phase in delay
+		 * line hw block.
+		 */
+		rc = msm_config_cm_dll_phase(host, phase);
+		if (rc)
+			goto out;
+		dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
+			 mmc_hostname(mmc), phase);
+	} else {
+		if (--tuning_seq_cnt)
+			goto retry;
+		/* Tuning failed */
+		dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n",
+		       mmc_hostname(mmc));
+		rc = -EIO;
+	}
+
+out:
+	kfree(data_buf);
+	return rc;
+}
+
 static int sdhci_msm_vreg_enable(struct device *dev, struct regulator *vreg)
 {
 	int ret = 0;
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v10 1/3] mmc: sdhci-msm: Qualcomm SDHCI binding documentation
  2014-03-04 19:27 ` [PATCH v10 1/3] mmc: sdhci-msm: Qualcomm SDHCI binding documentation Georgi Djakov
@ 2014-03-05  4:25   ` Ulf Hansson
  2014-03-05 20:21     ` Georgi Djakov
  2014-03-05  6:56   ` Rob Herring
  1 sibling, 1 reply; 11+ messages in thread
From: Ulf Hansson @ 2014-03-05  4:25 UTC (permalink / raw)
  To: Georgi Djakov
  Cc: linux-mmc, Chris Ball, devicetree, grant.likely, Rob Herring,
	Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell,
	Kumar Gala, Rob Landley, linux-doc, linux-kernel, linux-arm-msm

On 4 March 2014 20:27, Georgi Djakov <gdjakov@mm-sol.com> wrote:
> This patch adds the device-tree binding documentation for
> Qualcomm SDHCI driver. It contains the differences between
> the core properties in mmc.txt and the properties used by
> the sdhci-msm driver.
>
> Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com>
> ---
>  .../devicetree/bindings/mmc/sdhci-msm.txt          |   63 ++++++++++++++++++++
>  1 file changed, 63 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-msm.txt
>
> diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
> new file mode 100644
> index 0000000..c635c53
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
> @@ -0,0 +1,63 @@
> +* Qualcomm SDHCI controller (sdhci-msm)
> +
> +This file documents differences between the core properties in mmc.txt
> +and the properties used by the sdhci-msm driver.
> +
> +Required properties:
> +- compatible: Should contain "qcom,sdhci-msm-v4".
> +- reg: Base address and length of the register set listed in reg-names.
> +- reg-names: Should contain the following:
> +       "hc_mem"   - Host controller register map
> +       "core_mem" - SD Core register map
> +- interrupts: Should contain an interrupt-specifiers for the interrupts listed in interrupt-names.
> +- interrupt-names: Should contain the following:
> +       "hc_irq"     - Host controller interrupt
> +       "pwr_irq"    - PMIC interrupt
> +- vdd-supply: Phandle to the regulator for the vdd (core voltage) supply.
> +- vdd-io-supply: Phandle to the regulator for the vdd-io (i/o voltage) supply.

The common naming of the above regulators are "vmmc" and "vqmmc". Is
there any specific reason to why you can't use these names?

Kind regards
Ulf Hansson

> +- pinctrl-names: Should contain only one value - "default".
> +- pinctrl-0: Should specify pin control groups used for this controller.
> +- clocks: A list of phandle + clock-specifier pairs for the clocks listed in clock-names.
> +- clock-names: Should contain the following:
> +       "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required)
> +       "core"  - SDC MMC clock (MCLK) (required)
> +       "bus"   - SDCC bus voter clock (optional)
> +
> +Example:
> +
> +       sdhc_1: sdhci@f9824900 {
> +               compatible = "qcom,sdhci-msm-v4";
> +               reg = <0xf9824900 0x11c>, <0xf9824000 0x800>;
> +               reg-names = "hc_mem", "core_mem";
> +               interrupts = <0 123 0>, <0 138 0>;
> +               interrupt-names = "hc_irq", "pwr_irq";
> +               bus-width = <8>;
> +               non-removable;
> +
> +               vdd-supply = <&pm8941_l20>;
> +               vdd-io-supply = <&pm8941_s3>;
> +
> +               pinctrl-names = "default";
> +               pinctrl-0 = <&sdc1_clk &sdc1_cmd &sdc1_data>;
> +
> +               clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>;
> +               clock-names = "core", "iface";
> +       };
> +
> +       sdhc_2: sdhci@f98a4900 {
> +               compatible = "qcom,sdhci-msm-v4";
> +               reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>;
> +               reg-names = "hc_mem", "core_mem";
> +               interrupts = <0 125 0>, <0 221 0>;
> +               interrupt-names = "hc_irq", "pwr_irq";
> +               bus-width = <4>;
> +
> +               vdd-supply = <&pm8941_l21>;
> +               vdd-io-supply = <&pm8941_l13>;
> +
> +               pinctrl-names = "default";
> +               pinctrl-0 = <&sdc2_clk &sdc2_cmd &sdc2_data>;
> +
> +               clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>;
> +               clock-names = "core", "iface";
> +       };
> --
> 1.7.9.5
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v10 2/3] mmc: sdhci-msm: Initial support for Qualcomm chipsets
  2014-03-04 19:27 ` [PATCH v10 2/3] mmc: sdhci-msm: Initial support for Qualcomm chipsets Georgi Djakov
@ 2014-03-05  4:41   ` Ulf Hansson
  2014-03-05 20:22     ` Georgi Djakov
  0 siblings, 1 reply; 11+ messages in thread
From: Ulf Hansson @ 2014-03-05  4:41 UTC (permalink / raw)
  To: Georgi Djakov
  Cc: linux-mmc, Chris Ball, devicetree, grant.likely, Rob Herring,
	Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell,
	Kumar Gala, Rob Landley, linux-doc, linux-kernel, linux-arm-msm

On 4 March 2014 20:27, Georgi Djakov <gdjakov@mm-sol.com> wrote:
> This platform driver adds the initial support of Secure
> Digital Host Controller Interface compliant controller
> found in Qualcomm chipsets.
>
> Signed-off-by: Asutosh Das <asutoshd@codeaurora.org>
> Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
> Tested-by: Ivan T. Ivanov <iivanov@mm-sol.com>
> Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com>
> ---
>  drivers/mmc/host/Kconfig     |   13 ++
>  drivers/mmc/host/Makefile    |    1 +
>  drivers/mmc/host/sdhci-msm.c |  427 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 441 insertions(+)
>  create mode 100644 drivers/mmc/host/sdhci-msm.c
>
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 82cc34d..66ef8b9 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -334,6 +334,19 @@ config MMC_ATMELMCI
>
>           If unsure, say N.
>
> +config MMC_SDHCI_MSM
> +       tristate "Qualcomm SDHCI Controller Support"
> +       depends on ARCH_QCOM
> +       depends on MMC_SDHCI_PLTFM
> +       help
> +         This selects the Secure Digital Host Controller Interface (SDHCI)
> +         support present in Qualcomm SOCs. The controller supports
> +         SD/MMC/SDIO devices.
> +
> +         If you have a controller with this interface, say Y or M here.
> +
> +         If unsure, say N.
> +
>  config MMC_MSM
>         tristate "Qualcomm SDCC Controller Support"
>         depends on MMC && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50)
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index f162f87a0..0c8aa5e 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -63,6 +63,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ESDHC)      += sdhci-of-esdhc.o
>  obj-$(CONFIG_MMC_SDHCI_OF_HLWD)                += sdhci-of-hlwd.o
>  obj-$(CONFIG_MMC_SDHCI_BCM_KONA)       += sdhci-bcm-kona.o
>  obj-$(CONFIG_MMC_SDHCI_BCM2835)                += sdhci-bcm2835.o
> +obj-$(CONFIG_MMC_SDHCI_MSM)            += sdhci-msm.o
>
>  ifeq ($(CONFIG_CB710_DEBUG),y)
>         CFLAGS-cb710-mmc        += -DDEBUG
> diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
> new file mode 100644
> index 0000000..46e4e0b
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-msm.c
> @@ -0,0 +1,427 @@
> +/*
> + * drivers/mmc/host/sdhci-msm.c - Qualcomm SDHCI Platform driver
> + *
> + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/delay.h>
> +
> +#include "sdhci-pltfm.h"
> +
> +#define CORE_HC_MODE           0x78
> +#define HC_MODE_EN             0x1
> +
> +#define CORE_POWER             0x0
> +#define CORE_SW_RST            BIT(7)
> +
> +#define CORE_PWRCTL_STATUS     0xdc
> +#define CORE_PWRCTL_MASK       0xe0
> +#define CORE_PWRCTL_CLEAR      0xe4
> +#define CORE_PWRCTL_CTL                0xe8
> +
> +#define CORE_PWRCTL_BUS_OFF    BIT(0)
> +#define CORE_PWRCTL_BUS_ON     BIT(1)
> +#define CORE_PWRCTL_IO_LOW     BIT(2)
> +#define CORE_PWRCTL_IO_HIGH    BIT(3)
> +
> +#define CORE_PWRCTL_BUS_SUCCESS        BIT(0)
> +#define CORE_PWRCTL_BUS_FAIL   BIT(1)
> +#define CORE_PWRCTL_IO_SUCCESS BIT(2)
> +#define CORE_PWRCTL_IO_FAIL    BIT(3)
> +
> +#define INT_MASK               0xf
> +
> +#define QCOM_SDHCI_VOLTAGE_LOW 1800000
> +#define QCOM_SDHCI_VOLTAGE_HIGH        2950000
> +
> +
> +struct sdhci_msm_pltfm_data {
> +       u32 caps;                               /* Supported UHS-I Modes */
> +       u32 caps2;                              /* More capabilities */

Why do you need these caps, there are already a part of the mmc host.

> +       struct regulator *vdd;                  /* VDD/VCC regulator */
> +       struct regulator *vdd_io;               /* VDD IO regulator */

Why do you need to duplicate the regulators for sdhci_msm? sdhci core
is already taking care of regulators, I suppose you should adopt to
that!?

> +};
> +
> +struct sdhci_msm_host {
> +       struct platform_device *pdev;
> +       void __iomem *core_mem; /* MSM SDCC mapped address */
> +       int pwr_irq;            /* power irq */
> +       struct clk *clk;        /* main SD/MMC bus clock */
> +       struct clk *pclk;       /* SDHC peripheral bus clock */
> +       struct clk *bus_clk;    /* SDHC bus voter clock */
> +       struct sdhci_msm_pltfm_data pdata;
> +       struct mmc_host *mmc;
> +       struct sdhci_pltfm_data sdhci_msm_pdata;
> +};
> +
> +/* MSM platform specific tuning */
> +int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
> +{
> +       /*
> +        * Tuning is required for SDR104, HS200 and HS400 cards and if the clock
> +        * frequency greater than 100MHz in those modes. The standard tuning
> +        * procedure should not be executed, but a custom implementation will be
> +        * added here instead.
> +        */
> +       return 0;
> +}
> +
> +static int sdhci_msm_vreg_enable(struct device *dev, struct regulator *vreg)
> +{
> +       int ret = 0;
> +
> +       if (!regulator_is_enabled(vreg)) {
> +               /* Set voltage level */
> +               ret = regulator_set_voltage(vreg, QCOM_SDHCI_VOLTAGE_HIGH,
> +                                           QCOM_SDHCI_VOLTAGE_HIGH);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       ret = regulator_enable(vreg);
> +       if (ret)
> +               dev_err(dev, "Failed to enable regulator (%d)\n", ret);
> +
> +       return ret;
> +}
> +
> +static int sdhci_msm_vreg_disable(struct device *dev, struct regulator *vreg)
> +{
> +       int ret = 0;
> +
> +       if (!regulator_is_enabled(vreg))
> +               return ret;
> +
> +       /* Set min. voltage to 0 */
> +       ret = regulator_set_voltage(vreg, 0, QCOM_SDHCI_VOLTAGE_HIGH);
> +       if (ret)
> +               return ret;
> +
> +       ret = regulator_disable(vreg);
> +       if (ret)
> +               dev_err(dev, "Failed to disable regulator (%d)\n", ret);
> +
> +       return ret;
> +}
> +
> +static int sdhci_msm_setup_vreg(struct sdhci_msm_host *msm_host, bool enable)
> +{
> +       int ret;
> +
> +       if (enable) {
> +               ret = sdhci_msm_vreg_enable(&msm_host->pdev->dev,
> +                               msm_host->pdata.vdd);
> +               ret |= sdhci_msm_vreg_enable(&msm_host->pdev->dev,
> +                               msm_host->pdata.vdd_io);
> +       } else {
> +               ret = sdhci_msm_vreg_disable(&msm_host->pdev->dev,
> +                               msm_host->pdata.vdd);
> +               ret |= sdhci_msm_vreg_disable(&msm_host->pdev->dev,
> +                               msm_host->pdata.vdd_io);
> +       }
> +
> +       return ret;
> +}
> +
> +static int sdhci_msm_vreg_init(struct device *dev,
> +                              struct sdhci_msm_pltfm_data *pdata)
> +{
> +       pdata->vdd = devm_regulator_get(dev, "vdd-supply");
> +       if (IS_ERR(pdata->vdd))
> +               return PTR_ERR(pdata->vdd);
> +
> +       pdata->vdd_io = devm_regulator_get(dev, "vdd-io-supply");
> +       if (IS_ERR(pdata->vdd_io))
> +               return PTR_ERR(pdata->vdd_io);
> +
> +       return 0;
> +}
> +

The hole regulator handling seems like it's being duplicated from the
sdhci core. If the regulator handling from the sdhci core don't suite
your need I guess you should extend that instead - to prevent the
duplication of code and structs.

Moreover I think it's time for sdhci core to move on to use the
mmc_regulator_get_supply() API. Additionally it should be able to use
mmc_regulator_set_ocr() API to change voltage. I guess that's not
related to this patch though, but just wanted to provide you my view
on it.


> +static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
> +{
> +       struct sdhci_host *host = (struct sdhci_host *)data;
> +       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +       struct sdhci_msm_host *msm_host = pltfm_host->priv;
> +       u8 irq_status;
> +       u8 irq_ack = 0;
> +       int ret = 0;
> +
> +       irq_status = readb_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
> +       dev_dbg(mmc_dev(msm_host->mmc), "%s: Received IRQ(%d), status=0x%x\n",
> +               mmc_hostname(msm_host->mmc), irq, irq_status);
> +
> +       /* Clear the interrupt */
> +       writeb_relaxed(irq_status, (msm_host->core_mem + CORE_PWRCTL_CLEAR));
> +
> +       /* Ensure that the write has reached the device */
> +       mb();
> +
> +       if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF)) {
> +               /* Handle BUS ON/OFF */
> +               bool bus_on = (irq_status & CORE_PWRCTL_BUS_ON) ? 1 : 0;
> +
> +               ret = sdhci_msm_setup_vreg(msm_host, bus_on);
> +       } else if (irq_status & (CORE_PWRCTL_IO_LOW | CORE_PWRCTL_IO_HIGH)) {
> +               /* Handle IO LOW/HIGH */
> +               int io_high = (irq_status & CORE_PWRCTL_IO_HIGH) ? 1 : 0;
> +
> +               if (io_high) {
> +                       ret = regulator_set_voltage(msm_host->pdata.vdd_io,
> +                                       QCOM_SDHCI_VOLTAGE_HIGH,
> +                                       QCOM_SDHCI_VOLTAGE_HIGH);
> +               } else {
> +                       ret = regulator_set_voltage(msm_host->pdata.vdd_io,
> +                                       QCOM_SDHCI_VOLTAGE_LOW,
> +                                       QCOM_SDHCI_VOLTAGE_LOW);
> +               }
> +       }
> +
> +       if (ret)
> +               irq_ack = CORE_PWRCTL_BUS_FAIL;
> +       else
> +               irq_ack = CORE_PWRCTL_BUS_SUCCESS;
> +
> +       /* ACK status to the core */
> +       writeb_relaxed(irq_ack, (msm_host->core_mem + CORE_PWRCTL_CTL));
> +
> +       /* Commit the write to the device */
> +       mb();
> +
> +       dev_dbg(mmc_dev(msm_host->mmc), "%s: Handled IRQ(%d), ret=%d, ack=0x%x\n",
> +                mmc_hostname(msm_host->mmc), irq, ret, irq_ack);
> +       return IRQ_HANDLED;
> +}
> +
> +static const struct of_device_id sdhci_msm_dt_match[] = {
> +       { .compatible = "qcom,sdhci-msm-v4" },
> +       {},
> +};
> +
> +MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
> +
> +static struct sdhci_ops sdhci_msm_ops = {
> +       .platform_execute_tuning = sdhci_msm_execute_tuning,
> +};
> +
> +static int sdhci_msm_probe(struct platform_device *pdev)
> +{
> +       struct sdhci_host *host;
> +       struct sdhci_pltfm_host *pltfm_host;
> +       struct sdhci_msm_host *msm_host;
> +       struct resource *core_memres;
> +       int ret, dead;
> +       u16 host_version;
> +
> +       if (!pdev->dev.of_node) {
> +               dev_err(&pdev->dev, "No device tree data\n");
> +               return -ENOENT;
> +       }
> +
> +       msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL);
> +       if (!msm_host)
> +               return -ENOMEM;
> +
> +       msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops;
> +       host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0);
> +       if (IS_ERR(host))
> +               return PTR_ERR(host);
> +
> +       pltfm_host = sdhci_priv(host);
> +       pltfm_host->priv = msm_host;
> +       msm_host->mmc = host->mmc;
> +       msm_host->pdev = pdev;
> +
> +       ret = mmc_of_parse(host->mmc);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Failed parsing mmc device tree\n");
> +               goto pltfm_free;
> +       }
> +
> +       sdhci_get_of_property(pdev);
> +
> +       /* Setup SDCC bus voter clock. */
> +       msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus");
> +       if (!IS_ERR(msm_host->bus_clk)) {
> +               /* Vote for max. clk rate for max. performance */
> +               ret = clk_set_rate(msm_host->bus_clk, INT_MAX);
> +               if (ret)
> +                       goto pltfm_free;
> +               ret = clk_prepare_enable(msm_host->bus_clk);
> +               if (ret)
> +                       goto pltfm_free;
> +       }
> +
> +       /* Setup main peripheral bus clock */
> +       msm_host->pclk = devm_clk_get(&pdev->dev, "iface");
> +       if (IS_ERR(msm_host->pclk)) {
> +               ret = PTR_ERR(msm_host->pclk);
> +               dev_err(&pdev->dev, "Perpheral clk setup failed (%d)\n", ret);
> +               goto bus_clk_disable;
> +       }
> +
> +       ret = clk_prepare_enable(msm_host->pclk);
> +       if (ret)
> +               goto bus_clk_disable;
> +
> +       /* Setup SDC MMC clock */
> +       msm_host->clk = devm_clk_get(&pdev->dev, "core");
> +       if (IS_ERR(msm_host->clk)) {
> +               ret = PTR_ERR(msm_host->clk);
> +               dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret);
> +               goto pclk_disable;
> +       }
> +
> +       ret = clk_prepare_enable(msm_host->clk);
> +       if (ret)
> +               goto pclk_disable;
> +
> +       /* Setup regulators */
> +       ret = sdhci_msm_vreg_init(&pdev->dev, &msm_host->pdata);
> +       if (ret) {
> +               if (ret != -EPROBE_DEFER)
> +                       dev_err(&pdev->dev,
> +                               "Regulator setup failed (%d)\n", ret);
> +               goto clk_disable;
> +       }
> +
> +       core_memres = platform_get_resource_byname(pdev,
> +                                                  IORESOURCE_MEM, "core_mem");
> +       msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres);
> +
> +       if (IS_ERR(msm_host->core_mem)) {
> +               dev_err(&pdev->dev, "Failed to remap registers\n");
> +               ret = PTR_ERR(msm_host->core_mem);
> +               goto clk_disable;
> +       }
> +
> +       /* 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);
> +
> +       /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
> +       usleep_range(1000, 5000);
> +       if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) {
> +               dev_err(&pdev->dev, "Stuck in reset\n");
> +               ret = -ETIMEDOUT;
> +               goto clk_disable;
> +       }
> +
> +       /* Set HC_MODE_EN bit in HC_MODE register */
> +       writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
> +
> +       /*
> +        * Following are the deviations from SDHC spec v3.0 -
> +        * 1. Card detection is handled using separate GPIO.
> +        * 2. Bus power control is handled by interacting with PMIC.
> +        */
> +       host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
> +       host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE;
> +
> +       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) >>
> +                              SDHCI_VENDOR_VER_SHIFT));
> +
> +       /* Setup PWRCTL irq */
> +       msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
> +       if (msm_host->pwr_irq < 0) {
> +               dev_err(&pdev->dev, "Failed to get pwr_irq by name (%d)\n",
> +                       msm_host->pwr_irq);
> +               goto clk_disable;
> +       }
> +       ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL,
> +                                       sdhci_msm_pwr_irq, IRQF_ONESHOT,
> +                                       dev_name(&pdev->dev), host);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Request threaded irq(%d) failed (%d)\n",
> +                       msm_host->pwr_irq, ret);
> +               goto clk_disable;
> +       }
> +
> +       /* Enable power irq */
> +       writel_relaxed(INT_MASK, (msm_host->core_mem + CORE_PWRCTL_MASK));
> +
> +       msm_host->mmc->caps |= msm_host->pdata.caps;
> +       msm_host->mmc->caps2 |= msm_host->pdata.caps2;
> +
> +       ret = sdhci_add_host(host);
> +       if (ret) {
> +               dev_err(&pdev->dev, "Add host failed (%d)\n", ret);
> +               goto vreg_disable;
> +       }
> +
> +       ret = clk_set_rate(msm_host->clk, host->max_clk);

Don't you need to set the rate before adding the host?

> +       if (ret) {
> +               dev_err(&pdev->dev, "MClk rate set failed (%d)\n", ret);
> +               goto remove_host;
> +       }
> +
> +       return 0;
> +
> +remove_host:
> +       dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
> +       sdhci_remove_host(host, dead);
> +vreg_disable:
> +       sdhci_msm_vreg_disable(&pdev->dev, msm_host->pdata.vdd);
> +       sdhci_msm_vreg_disable(&pdev->dev, msm_host->pdata.vdd_io);
> +clk_disable:
> +       clk_disable_unprepare(msm_host->clk);
> +pclk_disable:
> +       clk_disable_unprepare(msm_host->pclk);
> +bus_clk_disable:
> +       if (!IS_ERR(msm_host->bus_clk))
> +               clk_disable_unprepare(msm_host->bus_clk);
> +pltfm_free:
> +       sdhci_pltfm_free(pdev);
> +       return ret;
> +}
> +
> +static int sdhci_msm_remove(struct platform_device *pdev)
> +{
> +       struct sdhci_host *host = platform_get_drvdata(pdev);
> +       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +       struct sdhci_msm_host *msm_host = pltfm_host->priv;
> +       int dead;
> +
> +       /* Disable power irq */
> +       writel_relaxed(~INT_MASK, (msm_host->core_mem + CORE_PWRCTL_MASK));
> +
> +       dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
> +       sdhci_remove_host(host, dead);
> +       sdhci_pltfm_free(pdev);
> +       sdhci_msm_vreg_disable(&pdev->dev, msm_host->pdata.vdd);
> +       sdhci_msm_vreg_disable(&pdev->dev, msm_host->pdata.vdd_io);
> +       clk_disable_unprepare(msm_host->clk);
> +       clk_disable_unprepare(msm_host->pclk);
> +       if (!IS_ERR(msm_host->bus_clk))
> +               clk_disable_unprepare(msm_host->bus_clk);
> +       return 0;
> +}
> +
> +static struct platform_driver sdhci_msm_driver = {
> +       .probe = sdhci_msm_probe,
> +       .remove = sdhci_msm_remove,
> +       .driver = {
> +                  .name = "sdhci_msm",
> +                  .owner = THIS_MODULE,
> +                  .of_match_table = sdhci_msm_dt_match,
> +       },
> +};
> +
> +module_platform_driver(sdhci_msm_driver);
> +
> +MODULE_DESCRIPTION("Qualcomm Secure Digital Host Controller Interface driver");
> +MODULE_LICENSE("GPL v2");
> --
> 1.7.9.5
>

Kind regards
Ulf Hansson

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v10 1/3] mmc: sdhci-msm: Qualcomm SDHCI binding documentation
  2014-03-04 19:27 ` [PATCH v10 1/3] mmc: sdhci-msm: Qualcomm SDHCI binding documentation Georgi Djakov
  2014-03-05  4:25   ` Ulf Hansson
@ 2014-03-05  6:56   ` Rob Herring
  2014-03-05 22:10     ` Josh Cartwright
  2014-03-06 17:45     ` Georgi Djakov
  1 sibling, 2 replies; 11+ messages in thread
From: Rob Herring @ 2014-03-05  6:56 UTC (permalink / raw)
  To: Georgi Djakov
  Cc: linux-mmc, Chris Ball, Ulf Hansson, devicetree, Grant Likely,
	Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Kumar Gala, Rob Landley, linux-doc, linux-kernel,
	linux-arm-msm

On Tue, Mar 4, 2014 at 1:27 PM, Georgi Djakov <gdjakov@mm-sol.com> wrote:
> This patch adds the device-tree binding documentation for
> Qualcomm SDHCI driver. It contains the differences between
> the core properties in mmc.txt and the properties used by
> the sdhci-msm driver.
>
> Signed-off-by: Georgi Djakov <gdjakov@mm-sol.com>
> ---
>  .../devicetree/bindings/mmc/sdhci-msm.txt          |   63 ++++++++++++++++++++
>  1 file changed, 63 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-msm.txt
>
> diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
> new file mode 100644
> index 0000000..c635c53
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
> @@ -0,0 +1,63 @@
> +* Qualcomm SDHCI controller (sdhci-msm)
> +
> +This file documents differences between the core properties in mmc.txt
> +and the properties used by the sdhci-msm driver.
> +
> +Required properties:
> +- compatible: Should contain "qcom,sdhci-msm-v4".
> +- reg: Base address and length of the register set listed in reg-names.
> +- reg-names: Should contain the following:
> +       "hc_mem"   - Host controller register map
> +       "core_mem" - SD Core register map

reg-names should not be required and the order specified by the binding.

> +- interrupts: Should contain an interrupt-specifiers for the interrupts listed in interrupt-names.
> +- interrupt-names: Should contain the following:
> +       "hc_irq"     - Host controller interrupt
> +       "pwr_irq"    - PMIC interrupt

Same for interrupt-names.

> +- vdd-supply: Phandle to the regulator for the vdd (core voltage) supply.
> +- vdd-io-supply: Phandle to the regulator for the vdd-io (i/o voltage) supply.
> +- pinctrl-names: Should contain only one value - "default".
> +- pinctrl-0: Should specify pin control groups used for this controller.
> +- clocks: A list of phandle + clock-specifier pairs for the clocks listed in clock-names.
> +- clock-names: Should contain the following:
> +       "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required)
> +       "core"  - SDC MMC clock (MCLK) (required)
> +       "bus"   - SDCC bus voter clock (optional)
> +
> +Example:
> +
> +       sdhc_1: sdhci@f9824900 {
> +               compatible = "qcom,sdhci-msm-v4";
> +               reg = <0xf9824900 0x11c>, <0xf9824000 0x800>;

Are there cases where these are really in a different 4KB range? If
not, this should just be 1 range.

> +               reg-names = "hc_mem", "core_mem";
> +               interrupts = <0 123 0>, <0 138 0>;
> +               interrupt-names = "hc_irq", "pwr_irq";
> +               bus-width = <8>;
> +               non-removable;
> +
> +               vdd-supply = <&pm8941_l20>;
> +               vdd-io-supply = <&pm8941_s3>;
> +
> +               pinctrl-names = "default";
> +               pinctrl-0 = <&sdc1_clk &sdc1_cmd &sdc1_data>;
> +
> +               clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>;
> +               clock-names = "core", "iface";
> +       };
> +
> +       sdhc_2: sdhci@f98a4900 {
> +               compatible = "qcom,sdhci-msm-v4";
> +               reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>;
> +               reg-names = "hc_mem", "core_mem";
> +               interrupts = <0 125 0>, <0 221 0>;
> +               interrupt-names = "hc_irq", "pwr_irq";
> +               bus-width = <4>;
> +
> +               vdd-supply = <&pm8941_l21>;
> +               vdd-io-supply = <&pm8941_l13>;
> +
> +               pinctrl-names = "default";
> +               pinctrl-0 = <&sdc2_clk &sdc2_cmd &sdc2_data>;
> +
> +               clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>;
> +               clock-names = "core", "iface";
> +       };
> --
> 1.7.9.5
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v10 1/3] mmc: sdhci-msm: Qualcomm SDHCI binding documentation
  2014-03-05  4:25   ` Ulf Hansson
@ 2014-03-05 20:21     ` Georgi Djakov
  0 siblings, 0 replies; 11+ messages in thread
From: Georgi Djakov @ 2014-03-05 20:21 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: linux-mmc, Chris Ball, devicetree, grant.likely, Rob Herring,
	Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell,
	Kumar Gala, Rob Landley, linux-doc, linux-kernel, linux-arm-msm

On 03/05/2014 06:25 AM, Ulf Hansson wrote:
> On 4 March 2014 20:27, Georgi Djakov <gdjakov@mm-sol.com> wrote:
[..]
>> +Required properties:
>> +- compatible: Should contain "qcom,sdhci-msm-v4".
>> +- reg: Base address and length of the register set listed in reg-names.
>> +- reg-names: Should contain the following:
>> +       "hc_mem"   - Host controller register map
>> +       "core_mem" - SD Core register map
>> +- interrupts: Should contain an interrupt-specifiers for the interrupts listed in interrupt-names.
>> +- interrupt-names: Should contain the following:
>> +       "hc_irq"     - Host controller interrupt
>> +       "pwr_irq"    - PMIC interrupt
>> +- vdd-supply: Phandle to the regulator for the vdd (core voltage) supply.
>> +- vdd-io-supply: Phandle to the regulator for the vdd-io (i/o voltage) supply.
>
> The common naming of the above regulators are "vmmc" and "vqmmc". Is
> there any specific reason to why you can't use these names?

Hi Ulf,
They are named this way on the schematics, i will change them to the 
common names. Thanks!

BR,
Georgi

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v10 2/3] mmc: sdhci-msm: Initial support for Qualcomm chipsets
  2014-03-05  4:41   ` Ulf Hansson
@ 2014-03-05 20:22     ` Georgi Djakov
  0 siblings, 0 replies; 11+ messages in thread
From: Georgi Djakov @ 2014-03-05 20:22 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: linux-mmc, Chris Ball, devicetree, grant.likely, Rob Herring,
	Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell,
	Kumar Gala, Rob Landley, linux-doc, linux-kernel, linux-arm-msm

On 03/05/2014 06:41 AM, Ulf Hansson wrote:
> On 4 March 2014 20:27, Georgi Djakov <gdjakov@mm-sol.com> wrote:
[..]
>> +
>> +struct sdhci_msm_pltfm_data {
>> +       u32 caps;                               /* Supported UHS-I Modes */
>> +       u32 caps2;                              /* More capabilities */
>
> Why do you need these caps, there are already a part of the mmc host.
>

Thanks! Will remove.

>> +       struct regulator *vdd;                  /* VDD/VCC regulator */
>> +       struct regulator *vdd_io;               /* VDD IO regulator */
>
> Why do you need to duplicate the regulators for sdhci_msm? sdhci core
> is already taking care of regulators, I suppose you should adopt to
> that!?
>

Ok, I'll try to adopt this.

>> +};
>> +
>> +struct sdhci_msm_host {
>> +       struct platform_device *pdev;
>> +       void __iomem *core_mem; /* MSM SDCC mapped address */
>> +       int pwr_irq;            /* power irq */
>> +       struct clk *clk;        /* main SD/MMC bus clock */
>> +       struct clk *pclk;       /* SDHC peripheral bus clock */
>> +       struct clk *bus_clk;    /* SDHC bus voter clock */
>> +       struct sdhci_msm_pltfm_data pdata;
>> +       struct mmc_host *mmc;
>> +       struct sdhci_pltfm_data sdhci_msm_pdata;
>> +};
>> +
[..]
>> +static int sdhci_msm_vreg_init(struct device *dev,
>> +                              struct sdhci_msm_pltfm_data *pdata)
>> +{
>> +       pdata->vdd = devm_regulator_get(dev, "vdd-supply");
>> +       if (IS_ERR(pdata->vdd))
>> +               return PTR_ERR(pdata->vdd);
>> +
>> +       pdata->vdd_io = devm_regulator_get(dev, "vdd-io-supply");
>> +       if (IS_ERR(pdata->vdd_io))
>> +               return PTR_ERR(pdata->vdd_io);
>> +
>> +       return 0;
>> +}
>> +
>
> The hole regulator handling seems like it's being duplicated from the
> sdhci core. If the regulator handling from the sdhci core don't suite
> your need I guess you should extend that instead - to prevent the
> duplication of code and structs.
>
> Moreover I think it's time for sdhci core to move on to use the
> mmc_regulator_get_supply() API. Additionally it should be able to use
> mmc_regulator_set_ocr() API to change voltage. I guess that's not
> related to this patch though, but just wanted to provide you my view
> on it.

Ok, I see. Thanks for the hints!

[..]
>> +       ret = sdhci_add_host(host);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "Add host failed (%d)\n", ret);
>> +               goto vreg_disable;
>> +       }
>> +
>> +       ret = clk_set_rate(msm_host->clk, host->max_clk);
>
> Don't you need to set the rate before adding the host?
>

I will just make use of the sdhci core for clock setup too. Thanks for 
reviewing!

BR,
Georgi

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v10 1/3] mmc: sdhci-msm: Qualcomm SDHCI binding documentation
  2014-03-05  6:56   ` Rob Herring
@ 2014-03-05 22:10     ` Josh Cartwright
  2014-03-06 17:45     ` Georgi Djakov
  1 sibling, 0 replies; 11+ messages in thread
From: Josh Cartwright @ 2014-03-05 22:10 UTC (permalink / raw)
  To: Rob Herring
  Cc: Georgi Djakov, linux-mmc, Chris Ball, Ulf Hansson, devicetree,
	Grant Likely, Rob Herring, Pawel Moll, Mark Rutland,
	Stephen Warren, Ian Campbell, Kumar Gala, Rob Landley, linux-doc,
	linux-kernel, linux-arm-msm

Hey Rob-

On Wed, Mar 05, 2014 at 12:56:22AM -0600, Rob Herring wrote:
> On Tue, Mar 4, 2014 at 1:27 PM, Georgi Djakov <gdjakov@mm-sol.com> wrote:
> > +- reg-names: Should contain the following:
> > +       "hc_mem"   - Host controller register map
> > +       "core_mem" - SD Core register map
>
> reg-names should not be required and the order specified by the binding.
>
> > +- interrupts: Should contain an interrupt-specifiers for the interrupts listed in interrupt-names.
> > +- interrupt-names: Should contain the following:
> > +       "hc_irq"     - Host controller interrupt
> > +       "pwr_irq"    - PMIC interrupt
>
> Same for interrupt-names.

Could you elaborate on this a bit?  I can understand not modifying an
existing binding to make {reg,interrupt}-names required, but for
completely new bindings, why is this a problem?

Not allowing a binding to require these properties renders
platform_get_{resource,irq}_byname() useless for drivers which
exclusively use DT for enumeration.  It also makes it impossible to make
a reg entry optional (although, there probably isn't a good usecase for
this).

Thanks,
  Josh

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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v10 1/3] mmc: sdhci-msm: Qualcomm SDHCI binding documentation
  2014-03-05  6:56   ` Rob Herring
  2014-03-05 22:10     ` Josh Cartwright
@ 2014-03-06 17:45     ` Georgi Djakov
  1 sibling, 0 replies; 11+ messages in thread
From: Georgi Djakov @ 2014-03-06 17:45 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-mmc, Chris Ball, Ulf Hansson, devicetree, Grant Likely,
	Rob Herring, Pawel Moll, Mark Rutland, Stephen Warren,
	Ian Campbell, Kumar Gala, Rob Landley, linux-doc, linux-kernel,
	linux-arm-msm

On 03/05/2014 08:56 AM, Rob Herring wrote:
> On Tue, Mar 4, 2014 at 1:27 PM, Georgi Djakov <gdjakov@mm-sol.com> wrote:
[..]
>> +Required properties:
>> +- compatible: Should contain "qcom,sdhci-msm-v4".
>> +- reg: Base address and length of the register set listed in reg-names.
>> +- reg-names: Should contain the following:
>> +       "hc_mem"   - Host controller register map
>> +       "core_mem" - SD Core register map
>
> reg-names should not be required and the order specified by the binding.
>

Ok, Rob! Thanks!

>> +- interrupts: Should contain an interrupt-specifiers for the interrupts listed in interrupt-names.
>> +- interrupt-names: Should contain the following:
>> +       "hc_irq"     - Host controller interrupt
>> +       "pwr_irq"    - PMIC interrupt
>
> Same for interrupt-names.
>

Ok.

>> +- vdd-supply: Phandle to the regulator for the vdd (core voltage) supply.
>> +- vdd-io-supply: Phandle to the regulator for the vdd-io (i/o voltage) supply.
>> +- pinctrl-names: Should contain only one value - "default".
>> +- pinctrl-0: Should specify pin control groups used for this controller.
>> +- clocks: A list of phandle + clock-specifier pairs for the clocks listed in clock-names.
>> +- clock-names: Should contain the following:
>> +       "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required)
>> +       "core"  - SDC MMC clock (MCLK) (required)
>> +       "bus"   - SDCC bus voter clock (optional)
>> +
>> +Example:
>> +
>> +       sdhc_1: sdhci@f9824900 {
>> +               compatible = "qcom,sdhci-msm-v4";
>> +               reg = <0xf9824900 0x11c>, <0xf9824000 0x800>;
>
> Are there cases where these are really in a different 4KB range? If
> not, this should just be 1 range.
>

Thanks for your suggestion. This sounds reasonable, but in this case,
I think that it might be better leaving the memory regions separated as 
it will help avoiding code duplication. I am using the generic 
sdhci_pltfm_init(), which assumes that the first resource is the HC 
iomem and configures the host. If I merge them, the HC address will be 
offset and this might require duplicating some of the init code.
Is it acceptable to leave it this way?

BR,
Georgi

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2014-03-06 17:45 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-03-04 19:27 [PATCH v10 0/3] mmc: sdhci-msm: Add support for Qualcomm chipsets Georgi Djakov
2014-03-04 19:27 ` [PATCH v10 1/3] mmc: sdhci-msm: Qualcomm SDHCI binding documentation Georgi Djakov
2014-03-05  4:25   ` Ulf Hansson
2014-03-05 20:21     ` Georgi Djakov
2014-03-05  6:56   ` Rob Herring
2014-03-05 22:10     ` Josh Cartwright
2014-03-06 17:45     ` Georgi Djakov
2014-03-04 19:27 ` [PATCH v10 2/3] mmc: sdhci-msm: Initial support for Qualcomm chipsets Georgi Djakov
2014-03-05  4:41   ` Ulf Hansson
2014-03-05 20:22     ` Georgi Djakov
2014-03-04 19:27 ` [PATCH v10 3/3] mmc: sdhci-msm: Add platform_execute_tuning implementation Georgi Djakov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.