Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 3/10] mmc: sdhci: Export sdhci_execute_tuning() in sdhci.c
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement@free-electrons.com>

From: Ziji Hu <huziji@marvell.com>

Export sdhci_execute_tuning() from sdhci.c.
Thus vendor sdhci driver can execute its own tuning process.

Signed-off-by: Hu Ziji <huziji@marvell.com>
Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 drivers/mmc/host/sdhci.c | 3 ++-
 drivers/mmc/host/sdhci.h | 1 +
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2250ea22231f..330119cd5a93 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1946,7 +1946,7 @@ static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
 	return 0;
 }
 
-static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
+int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
 {
 	struct sdhci_host *host = mmc_priv(mmc);
 	u16 ctrl;
@@ -2135,6 +2135,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
 	spin_unlock_irqrestore(&host->lock, flags);
 	return err;
 }
+EXPORT_SYMBOL_GPL(sdhci_execute_tuning);
 
 static int sdhci_select_drive_strength(struct mmc_card *card,
 				       unsigned int max_dtr, int host_drv,
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index c38ab65b9a97..035274ddea54 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -689,6 +689,7 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
 void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
 int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
 				      struct mmc_ios *ios);
+int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
 
 #ifdef CONFIG_PM
 extern int sdhci_suspend_host(struct sdhci_host *host);
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH 4/10] MAINTAINERS: add entry for Marvell Xenon MMC Host Controller drivers
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement@free-electrons.com>

From: Ziji Hu <huziji@marvell.com>

Add maintainer entry for Marvell Xenon eMMC/SD/SDIO Host
Controller drivers.

Signed-off-by: Hu Ziji <huziji@marvell.com>
Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 MAINTAINERS | 5 +++++
 1 file changed, 5 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 207eaefd3b97..89adcd57aa25 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7578,6 +7578,11 @@ M:	Nicolas Pitre <nico@fluxnic.net>
 S:	Odd Fixes
 F:	drivers/mmc/host/mvsdio.*
 
+MARVELL XENON MMC/SD/SDIO HOST CONTROLLER DRIVER
+M:	Ziji Hu <huziji@marvell.com>
+L:	linux-mmc at vger.kernel.org
+S:	Supported
+
 MATROX FRAMEBUFFER DRIVER
 L:	linux-fbdev at vger.kernel.org
 S:	Orphan
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH 5/10] dt: bindings: Add bindings for Marvell Xenon SD Host Controller
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement@free-electrons.com>

From: Ziji Hu <huziji@marvell.com>

Marvell Xenon SDHC can support eMMC/SD/SDIO.
Add Xenon-specific properties.
Also add properties for Xenon PHY setting.

Signed-off-by: Hu Ziji <huziji@marvell.com>
Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt | 164 +++++++-
 MAINTAINERS                                                   |   1 +-
 2 files changed, 165 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt

diff --git a/Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt b/Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
new file mode 100644
index 000000000000..8b25ad28ebbd
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
@@ -0,0 +1,164 @@
+Marvell's Xenon SDHCI Controller device tree bindings
+This file documents differences between the core mmc properties
+described by mmc.txt and the properties used by the Xenon implementation.
+
+A single Xenon IP can support multiple slots.
+Each slot acts as an independent SDHC. It owns independent resources, such
+as register sets clock and PHY.
+Each slot should have an independent device tree node.
+
+Required Properties:
+- compatible: should be "marvell,sdhci-xenon" or "marvell,armada-3700-sdhci".
+
+- Input Clock Name
+  Some SOCs require additional clock for AXI bus.
+  The input clock for Xenon IP core should be named as "core".
+  The optional AXI clock should be named as "axi".
+  - clocks = <&core_clk>, <&axi_clock>;
+  - clock-names = "core", "axi";
+
+- Register Set Size
+  Different Xenon SDHC release has different register set size.
+  The specific size should also refer to the SOC implementation.
+
+Optional Properties:
+- Slot Index
+  A single Xenon IP can support multiple slots.
+  During initialization, each slot should set corresponding setting bit in
+  some Xenon-specific registers. The corresponding bit is determined by
+  this property.
+  - xenon,slotno = <slot_index>;
+  If this property is not provided, Xenon IP should contain only one slot
+  and the slot index will be 0x0 by default.
+
+- PHY Type
+  Xenon support mutilple types of PHYs.
+  To select eMMC 5.1 PHY, set:
+  - xenon,phy-type = "emmc 5.1 phy"
+  eMMC 5.1 PHY is the default choice if this property is not provided.
+  To select eMMC 5.0 PHY, set:
+  - xenon,phy-type = "emmc 5.0 phy"
+  To select SDH PHY, set:
+  - xenon,phy-type = "sdh phy"
+  Please note that eMMC PHY is a general PHY for eMMC/SD/SDIO, other than for
+  eMMC only.
+
+- Customized eMMC PHY Parameters
+  Some boards require different values of some specific eMMC PHY parameters.
+  Some SOCs also require specific workaround to set eMMC PHY.
+  These properties enable diverse boards to customize the eMMC PHY.
+  The supported eMMC PHY parameters are listed in below. All those properties
+  are only available for eMMC PHY 5.1 and eMMC PHY 5.0.
+  ZNR
+  valid range = [0:0x1F].
+  ZNR is set as 0xF by default if this property is not provided.
+  - xenon,phy-znr = <value>;
+
+  ZPR
+  valid range = [0:0x1F].
+  ZPR is set as 0xF by default if this property is not provided.
+  - xenon,phy-zpr = <value>;
+
+  Number of successful tuning times
+  Set the number of required consecutive successful sampling points used to
+  identify a valid sampling window, in tuning process.
+  Valid range = [1:7]. Set as 0x4 by default if this property is not provided.
+  - xenon,phy-nr-tun-times = <nr_times>;
+
+  Divider for TUN_STEP
+  Set the divider for calculating TUN_STEP.
+  Set as 64 by default if this property is not provided.
+  - xenon,phy-tun-step-divider = <divider>;
+
+  Force PHY into slow mode.
+  Only available when bus frequency lower than 50MHz in SDR mde.
+  Disabled by default. Please do not enable it unless it is necessary.
+  - xenon,phy-slow-mode;
+
+- Mask Conflict Error Report
+  Disable Conflict Error alert on some SOC. Disabled by default.
+  xenon,mask-conflict-err;
+
+- Re-tuning Counter
+  Xenon SDHC SOC usually doesn't provide re-tuning counter in
+  Capabilities Register 3 Bit[11:8].
+  This property provides the re-tuning counter.
+  xenon,tuning-count = <count>;
+  If this property is not set, default re-tuning counter will
+  be set as 0x9 in driver.
+
+- SOC PHY PAD Voltage Control register
+  Some SOCs have SOC PHY PAD Voltage Control register outside Xenon IP.
+  This register sets SOC PHY PAD Voltage to keep aligh with Vccq.
+  Two properties provide information of this control register.
+  These two properties are only valid when "marvell,armada-3700-sdhci"
+  is selected. Both of them must be provided when "marvell,armada-3700-sdhci"
+  is selected.
+  - xenon,pad-type
+    Two types: "sd" and "fixed-1-8v".
+    If "sd" is slected, SOC PHY PAD is set as 3.3V at the beginning and is
+    switched to 1.8V when SD in UHS-I.
+    If "fixed-1-8v" is slected, SOC PHY PAD is fixed 1.8V, such as for eMMC.
+  - reg
+    Physical address and size of SOC PHY PAD register.
+    Append after Xenon SDHC register space, as a second register field.
+
+  Please follow the examples with compatible "marvell,armada-3700-sdhci"
+  in below.
+
+Example:
+- For eMMC slot:
+
+	sdhci at aa0000 {
+		compatible = "marvell,sdhci-xenon";
+		reg = <0xaa0000 0x1000>;
+		interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>
+		clocks = <&emmcclk>;
+		clock-names = "core";
+		xenon,slotno = <0>;
+		xenon,phy-type = "emmc 5.1 phy";
+		bus-width = <8>;
+		tuning-count = <11>;
+	};
+
+- For SD/SDIO slot:
+
+	sdhci at ab0000 {
+		compatible = "marvell,sdhci-xenon";
+		reg = <0xab0000 0x1000>;
+		interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>
+		vqmmc-supply = <&sd_regulator>;
+		clocks = <&sdclk>;
+		clock-names = "core";
+		bus-width = <4>;
+		tuning-count = <9>;
+	};
+
+- For eMMC slot with compatible "marvell,armada-3700-sdhci":
+
+	sdhci at aa0000 {
+		compatible = "marvell,armada-3700-sdhci";
+		reg = <0xaa0000 0x1000>,
+		      <phy_addr 0x4>;
+		interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>
+		clocks = <&emmcclk>;
+		clock-names = "core";
+		bus-width = <8>;
+
+		xenon,pad-type = "fixed-1-8v";
+	};
+
+- For SD/SDIO slot with compatible "marvell,armada-3700-sdhci":
+
+	sdhci at ab0000 {
+		compatible = "marvell,armada-3700-sdhci";
+		reg = <0xab0000 0x1000>,
+		      <phy_addr 0x4>;
+		interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>
+		vqmmc-supply = <&sd_regulator>;
+		clocks = <&sdclk>;
+		clock-names = "core";
+		bus-width = <4>;
+
+		xenon,pad-type = "sd";
+	};
diff --git a/MAINTAINERS b/MAINTAINERS
index 89adcd57aa25..4aa0eac9bfc7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7582,6 +7582,7 @@ MARVELL XENON MMC/SD/SDIO HOST CONTROLLER DRIVER
 M:	Ziji Hu <huziji@marvell.com>
 L:	linux-mmc at vger.kernel.org
 S:	Supported
+F:	Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
 
 MATROX FRAMEBUFFER DRIVER
 L:	linux-fbdev at vger.kernel.org
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH 6/10] mmc: sdhci-xenon: Add Marvell Xenon SDHC core functionality
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement@free-electrons.com>

From: Ziji Hu <huziji@marvell.com>

Add Xenon eMMC/SD/SDIO host controller core functionality.
Add Xenon specific intialization process.
Add Xenon specific mmc_host_ops APIs.
Add Xenon specific register definitions.

Add CONFIG_MMC_SDHCI_XENON support in drivers/mmc/host/Kconfig.

Marvell Xenon SDHC conforms to SD Physical Layer Specification
Version 3.01 and is designed according to the guidelines provided
in the SD Host Controller Standard Specification Version 3.00.

Signed-off-by: Hu Ziji <huziji@marvell.com>
Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 MAINTAINERS                    |   1 +-
 drivers/mmc/host/Kconfig       |   9 +-
 drivers/mmc/host/Makefile      |   3 +-
 drivers/mmc/host/sdhci-xenon.c | 599 ++++++++++++++++++++++++++++++++++-
 drivers/mmc/host/sdhci-xenon.h | 134 ++++++++-
 5 files changed, 746 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mmc/host/sdhci-xenon.c
 create mode 100644 drivers/mmc/host/sdhci-xenon.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 4aa0eac9bfc7..859420e5dfd3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7582,6 +7582,7 @@ MARVELL XENON MMC/SD/SDIO HOST CONTROLLER DRIVER
 M:	Ziji Hu <huziji@marvell.com>
 L:	linux-mmc at vger.kernel.org
 S:	Supported
+F:	drivers/mmc/host/sdhci-xenon.*
 F:	Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
 
 MATROX FRAMEBUFFER DRIVER
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 5274f503a39a..85a53623526a 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -798,3 +798,12 @@ config MMC_SDHCI_BRCMSTB
 	  Broadcom STB SoCs.
 
 	  If unsure, say Y.
+
+config MMC_SDHCI_XENON
+	tristate "Marvell Xenon eMMC/SD/SDIO SDHCI driver"
+	depends on MMC_SDHCI && MMC_SDHCI_PLTFM
+	help
+	  This selects Marvell Xenon eMMC/SD/SDIO SDHCI.
+	  If you have a machine with integrated Marvell Xenon SDHC IP,
+	  say Y or M here.
+	  If unsure, say N.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e2bdaaf43184..75eaf743486c 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -80,3 +80,6 @@ obj-$(CONFIG_MMC_SDHCI_BRCMSTB)		+= sdhci-brcmstb.o
 ifeq ($(CONFIG_CB710_DEBUG),y)
 	CFLAGS-cb710-mmc	+= -DDEBUG
 endif
+
+obj-$(CONFIG_MMC_SDHCI_XENON)	+= sdhci-xenon-driver.o
+sdhci-xenon-driver-y		+= sdhci-xenon.o
diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c
new file mode 100644
index 000000000000..03ba183494d3
--- /dev/null
+++ b/drivers/mmc/host/sdhci-xenon.c
@@ -0,0 +1,599 @@
+/*
+ * Driver for Marvell SOC Platform Group Xenon SDHC as a platform device
+ *
+ * Copyright (C) 2016 Marvell, All Rights Reserved.
+ *
+ * Author:	Hu Ziji <huziji@marvell.com>
+ * Date:	2016-8-24
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * Inspired by Jisheng Zhang <jszhang@marvell.com>
+ * Special thanks to Video BG4 project team.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include "sdhci-pltfm.h"
+#include "sdhci.h"
+#include "sdhci-xenon.h"
+
+/* Set SDCLK-off-while-idle */
+static void xenon_set_sdclk_off_idle(struct sdhci_host *host,
+				     unsigned char slot_idx, bool enable)
+{
+	u32 reg;
+	u32 mask;
+
+	reg = sdhci_readl(host, SDHC_SYS_OP_CTRL);
+	/* Get the bit shift basing on the slot index */
+	mask = (0x1 << (SDCLK_IDLEOFF_ENABLE_SHIFT + slot_idx));
+	if (enable)
+		reg |= mask;
+	else
+		reg &= ~mask;
+
+	sdhci_writel(host, reg, SDHC_SYS_OP_CTRL);
+}
+
+/* Enable/Disable the Auto Clock Gating function */
+static void xenon_set_acg(struct sdhci_host *host, bool enable)
+{
+	u32 reg;
+
+	reg = sdhci_readl(host, SDHC_SYS_OP_CTRL);
+	if (enable)
+		reg &= ~AUTO_CLKGATE_DISABLE_MASK;
+	else
+		reg |= AUTO_CLKGATE_DISABLE_MASK;
+	sdhci_writel(host, reg, SDHC_SYS_OP_CTRL);
+}
+
+/* Enable this slot */
+static void xenon_enable_slot(struct sdhci_host *host,
+			      unsigned char slot_idx)
+{
+	u32 reg;
+
+	reg = sdhci_readl(host, SDHC_SYS_OP_CTRL);
+	reg |= (BIT(slot_idx) << SLOT_ENABLE_SHIFT);
+	sdhci_writel(host, reg, SDHC_SYS_OP_CTRL);
+
+	/*
+	 * Manually set the flag which all the slots require,
+	 * including SD, eMMC, SDIO
+	 */
+	host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
+}
+
+/* Disable this slot */
+static void xenon_disable_slot(struct sdhci_host *host,
+			       unsigned char slot_idx)
+{
+	u32 reg;
+
+	reg = sdhci_readl(host, SDHC_SYS_OP_CTRL);
+	reg &= ~(BIT(slot_idx) << SLOT_ENABLE_SHIFT);
+	sdhci_writel(host, reg, SDHC_SYS_OP_CTRL);
+}
+
+/* Enable Parallel Transfer Mode */
+static void xenon_enable_slot_parallel_tran(struct sdhci_host *host,
+					    unsigned char slot_idx)
+{
+	u32 reg;
+
+	reg = sdhci_readl(host, SDHC_SYS_EXT_OP_CTRL);
+	reg |= BIT(slot_idx);
+	sdhci_writel(host, reg, SDHC_SYS_EXT_OP_CTRL);
+}
+
+static void xenon_slot_tuning_setup(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	u32 reg;
+
+	/* Disable the Re-Tuning Request functionality */
+	reg = sdhci_readl(host, SDHC_SLOT_RETUNING_REQ_CTRL);
+	reg &= ~RETUNING_COMPATIBLE;
+	sdhci_writel(host, reg, SDHC_SLOT_RETUNING_REQ_CTRL);
+
+	/* Disbale the Re-tuning Event Signal Enable */
+	reg = sdhci_readl(host, SDHCI_SIGNAL_ENABLE);
+	reg &= ~SDHCI_INT_RETUNE;
+	sdhci_writel(host, reg, SDHCI_SIGNAL_ENABLE);
+
+	/* Force to use Tuning Mode 1 */
+	host->tuning_mode = SDHCI_TUNING_MODE_1;
+	/* Set re-tuning period */
+	host->tuning_count = 1 << (priv->tuning_count - 1);
+}
+
+/*
+ * Operations inside struct sdhci_ops
+ */
+/* Recover the Register Setting cleared during SOFTWARE_RESET_ALL */
+static void sdhci_xenon_reset_exit(struct sdhci_host *host,
+				   unsigned char slot_idx, u8 mask)
+{
+	/* Only SOFTWARE RESET ALL will clear the register setting */
+	if (!(mask & SDHCI_RESET_ALL))
+		return;
+
+	/* Disable tuning request and auto-retuing again */
+	xenon_slot_tuning_setup(host);
+
+	xenon_set_acg(host, true);
+
+	xenon_set_sdclk_off_idle(host, slot_idx, false);
+}
+
+static void sdhci_xenon_reset(struct sdhci_host *host, u8 mask)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+	sdhci_reset(host, mask);
+	sdhci_xenon_reset_exit(host, priv->slot_idx, mask);
+}
+
+/*
+ * Xenon defines different values for HS200 and SDR104
+ * in Host_Control_2
+ */
+static void xenon_set_uhs_signaling(struct sdhci_host *host,
+				    unsigned int timing)
+{
+	u16 ctrl_2;
+
+	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+	/* Select Bus Speed Mode for host */
+	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+	if (timing == MMC_TIMING_MMC_HS200)
+		ctrl_2 |= XENON_SDHCI_CTRL_HS200;
+	else if (timing == MMC_TIMING_UHS_SDR104)
+		ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+	else if (timing == MMC_TIMING_UHS_SDR12)
+		ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
+	else if (timing == MMC_TIMING_UHS_SDR25)
+		ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+	else if (timing == MMC_TIMING_UHS_SDR50)
+		ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+	else if ((timing == MMC_TIMING_UHS_DDR50) ||
+		 (timing == MMC_TIMING_MMC_DDR52))
+		ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+	else if (timing == MMC_TIMING_MMC_HS400)
+		ctrl_2 |= XENON_SDHCI_CTRL_HS400;
+	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+}
+
+static const struct sdhci_ops sdhci_xenon_ops = {
+	.set_clock		= sdhci_set_clock,
+	.set_bus_width		= sdhci_set_bus_width,
+	.reset			= sdhci_xenon_reset,
+	.set_uhs_signaling	= xenon_set_uhs_signaling,
+	.get_max_clock		= sdhci_pltfm_clk_get_max_clock,
+};
+
+static const struct sdhci_pltfm_data sdhci_xenon_pdata = {
+	.ops = &sdhci_xenon_ops,
+	.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
+			SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 |
+			SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
+			SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+};
+
+/*
+ * Xenon Specific Operations in mmc_host_ops
+ */
+static void xenon_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	unsigned long flags;
+
+	/*
+	 * HS400/HS200/eMMC HS doesn't have Preset Value register.
+	 * However, sdhci_set_ios will read HS400/HS200 Preset register.
+	 * Disable Preset Value register for HS400/HS200.
+	 * eMMC HS with preset_enabled set will trigger a bug in
+	 * get_preset_value().
+	 */
+	spin_lock_irqsave(&host->lock, flags);
+	if ((ios->timing == MMC_TIMING_MMC_HS400) ||
+	    (ios->timing == MMC_TIMING_MMC_HS200) ||
+	    (ios->timing == MMC_TIMING_MMC_HS)) {
+		host->preset_enabled = false;
+		host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
+	} else {
+		host->quirks2 &= ~SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
+	}
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	sdhci_set_ios(mmc, ios);
+
+	if (host->clock > DEFAULT_SDCLK_FREQ) {
+		spin_lock_irqsave(&host->lock, flags);
+		xenon_set_sdclk_off_idle(host, priv->slot_idx, true);
+		spin_unlock_irqrestore(&host->lock, flags);
+	}
+}
+
+static int __emmc_signal_voltage_switch(struct mmc_host *mmc,
+					const unsigned char signal_voltage)
+{
+	u32 ctrl;
+	unsigned char voltage_code;
+	struct sdhci_host *host = mmc_priv(mmc);
+
+	if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
+		voltage_code = EMMC_VCCQ_3_3V;
+	else if (signal_voltage == MMC_SIGNAL_VOLTAGE_180)
+		voltage_code = EMMC_VCCQ_1_8V;
+	else
+		return -EINVAL;
+
+	/*
+	 * This host is for eMMC, XENON self-defined
+	 * eMMC slot control register should be accessed
+	 * instead of Host Control 2
+	 */
+	ctrl = sdhci_readl(host, SDHC_SLOT_EMMC_CTRL);
+	ctrl &= ~EMMC_VCCQ_MASK;
+	ctrl |= voltage_code;
+	sdhci_writel(host, ctrl, SDHC_SLOT_EMMC_CTRL);
+
+	/* There is no standard to determine this waiting period */
+	usleep_range(1000, 2000);
+
+	/* Check whether io voltage switch is done */
+	ctrl = sdhci_readl(host, SDHC_SLOT_EMMC_CTRL);
+	ctrl &= EMMC_VCCQ_MASK;
+	/*
+	 * This bit is set only when regulator feeds back the voltage switch
+	 * results to Xenon SDHC.
+	 * However, in actaul implementation, regulator might not provide
+	 * this feedback.
+	 * Thus we shall not rely on this bit to determine if switch failed.
+	 * If the bit is not set, just throw a message.
+	 * Besides, error code should not be returned.
+	 */
+	if (ctrl != voltage_code)
+		dev_info(mmc_dev(mmc), "fail to detect eMMC signal voltage stable\n");
+	return 0;
+}
+
+static int xenon_emmc_signal_voltage_switch(struct mmc_host *mmc,
+					    struct mmc_ios *ios)
+{
+	unsigned char voltage = ios->signal_voltage;
+
+	if ((voltage == MMC_SIGNAL_VOLTAGE_330) ||
+	    (voltage == MMC_SIGNAL_VOLTAGE_180))
+		return __emmc_signal_voltage_switch(mmc, voltage);
+
+	dev_err(mmc_dev(mmc), "Unsupported signal voltage: %d\n",
+		voltage);
+	return -EINVAL;
+}
+
+static int xenon_start_signal_voltage_switch(struct mmc_host *mmc,
+					     struct mmc_ios *ios)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+	/*
+	 * Before SD/SDIO set signal voltage, SD bus clock should be
+	 * disabled. However, sdhci_set_clock will also disable the Internal
+	 * clock in mmc_set_signal_voltage().
+	 * If Internal clock is disabled, the 3.3V/1.8V bit can not be updated.
+	 * Thus here manually enable internal clock.
+	 *
+	 * After switch completes, it is unnecessary to disable internal clock,
+	 * since keeping internal clock active obeys SD spec.
+	 */
+	enable_xenon_internal_clk(host);
+
+	if (priv->card_candidate) {
+		if (mmc_card_mmc(priv->card_candidate))
+			return xenon_emmc_signal_voltage_switch(mmc, ios);
+	}
+
+	return sdhci_start_signal_voltage_switch(mmc, ios);
+}
+
+/*
+ * After determining which slot is used for SDIO,
+ * some additional task is required.
+ */
+static void xenon_init_card(struct mmc_host *mmc, struct mmc_card *card)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	u32 reg;
+	u8 slot_idx;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+	/* Link the card for delay adjustment */
+	priv->card_candidate = card;
+	/* Set tuning functionality of this slot */
+	xenon_slot_tuning_setup(host);
+
+	slot_idx = priv->slot_idx;
+	if (!mmc_card_sdio(card)) {
+		/* Re-enable the Auto-CMD12 cap flag. */
+		host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+		host->flags |= SDHCI_AUTO_CMD12;
+
+		/* Clear SDIO Card Inserted indication */
+		reg = sdhci_readl(host, SDHC_SYS_CFG_INFO);
+		reg &= ~(1 << (slot_idx + SLOT_TYPE_SDIO_SHIFT));
+		sdhci_writel(host, reg, SDHC_SYS_CFG_INFO);
+
+		if (mmc_card_mmc(card)) {
+			mmc->caps |= MMC_CAP_NONREMOVABLE;
+			if (!(host->quirks2 & SDHCI_QUIRK2_NO_1_8_V))
+				mmc->caps |= MMC_CAP_1_8V_DDR;
+			/*
+			 * Force to clear BUS_TEST to
+			 * skip bus_test_pre and bus_test_post
+			 */
+			mmc->caps &= ~MMC_CAP_BUS_WIDTH_TEST;
+			mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ |
+				      MMC_CAP2_PACKED_CMD;
+			if (mmc->caps & MMC_CAP_8_BIT_DATA)
+				mmc->caps2 |= MMC_CAP2_HS400_1_8V;
+		}
+	} else {
+		/*
+		 * Delete the Auto-CMD12 cap flag.
+		 * Otherwise, when sending multi-block CMD53,
+		 * Driver will set Transfer Mode Register to enable Auto CMD12.
+		 * However, SDIO device cannot recognize CMD12.
+		 * Thus SDHC will time-out for waiting for CMD12 response.
+		 */
+		host->quirks &= ~SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+		host->flags &= ~SDHCI_AUTO_CMD12;
+
+		/*
+		 * Set SDIO Card Inserted indication
+		 * to inform that the current slot is for SDIO
+		 */
+		reg = sdhci_readl(host, SDHC_SYS_CFG_INFO);
+		reg |= (1 << (slot_idx + SLOT_TYPE_SDIO_SHIFT));
+		sdhci_writel(host, reg, SDHC_SYS_CFG_INFO);
+	}
+}
+
+static int xenon_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+
+	if (host->timing == MMC_TIMING_UHS_DDR50)
+		return 0;
+
+	return sdhci_execute_tuning(mmc, opcode);
+}
+
+static void xenon_replace_mmc_host_ops(struct sdhci_host *host)
+{
+	host->mmc_host_ops.set_ios = xenon_set_ios;
+	host->mmc_host_ops.start_signal_voltage_switch =
+			xenon_start_signal_voltage_switch;
+	host->mmc_host_ops.init_card = xenon_init_card;
+	host->mmc_host_ops.execute_tuning = xenon_execute_tuning;
+}
+
+static int xenon_probe_dt(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+	struct mmc_host *mmc = host->mmc;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	int err;
+	u32 slot_idx, nr_slot;
+	u32 tuning_count;
+	u32 reg;
+
+	/* Standard MMC property */
+	err = mmc_of_parse(mmc);
+	if (err)
+		return err;
+
+	/* Standard SDHCI property */
+	sdhci_get_of_property(pdev);
+
+	/*
+	 * Xenon Specific property:
+	 * slotno: the index of slot. Refer to SDHC_SYS_CFG_INFO register
+	 * tuning-count: the interval between re-tuning
+	 * PHY type: "sdhc phy", "emmc phy 5.0" or "emmc phy 5.1"
+	 */
+	if (!of_property_read_u32(np, "xenon,slotno", &slot_idx)) {
+		nr_slot = sdhci_readl(host, SDHC_SYS_CFG_INFO);
+		nr_slot &= NR_SUPPORTED_SLOT_MASK;
+		if (unlikely(slot_idx > nr_slot)) {
+			dev_err(mmc_dev(mmc), "Slot Index %d exceeds Number of slots %d\n",
+				slot_idx, nr_slot);
+			return -EINVAL;
+		}
+	} else {
+		priv->slot_idx = 0x0;
+	}
+
+	if (!of_property_read_u32(np, "xenon,tuning-count", &tuning_count)) {
+		if (unlikely(tuning_count >= TMR_RETUN_NO_PRESENT)) {
+			dev_err(mmc_dev(mmc), "Wrong Re-tuning Count. Set default value %d\n",
+				DEF_TUNING_COUNT);
+			tuning_count = DEF_TUNING_COUNT;
+		}
+	} else {
+		priv->tuning_count = DEF_TUNING_COUNT;
+	}
+
+	if (of_property_read_bool(np, "xenon,mask-conflict-err")) {
+		reg = sdhci_readl(host, SDHC_SYS_EXT_OP_CTRL);
+		reg |= MASK_CMD_CONFLICT_ERROR;
+		sdhci_writel(host, reg, SDHC_SYS_EXT_OP_CTRL);
+	}
+
+	return err;
+}
+
+static int xenon_slot_probe(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	u8 slot_idx = priv->slot_idx;
+
+	/* Enable slot */
+	xenon_enable_slot(host, slot_idx);
+
+	/* Enable ACG */
+	xenon_set_acg(host, true);
+
+	/* Enable Parallel Transfer Mode */
+	xenon_enable_slot_parallel_tran(host, slot_idx);
+
+	priv->timing = MMC_TIMING_FAKE;
+	priv->clock = 0;
+
+	return 0;
+}
+
+static void xenon_slot_remove(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	u8 slot_idx = priv->slot_idx;
+
+	/* disable slot */
+	xenon_disable_slot(host, slot_idx);
+}
+
+static int sdhci_xenon_probe(struct platform_device *pdev)
+{
+	struct sdhci_pltfm_host *pltfm_host;
+	struct sdhci_host *host;
+	struct clk *clk, *axi_clk;
+	struct sdhci_xenon_priv *priv;
+	int err;
+
+	host = sdhci_pltfm_init(pdev, &sdhci_xenon_pdata,
+				sizeof(struct sdhci_xenon_priv));
+	if (IS_ERR(host))
+		return PTR_ERR(host);
+
+	pltfm_host = sdhci_priv(host);
+	priv = sdhci_pltfm_priv(pltfm_host);
+
+	xenon_set_acg(host, false);
+
+	/*
+	 * Link Xenon specific mmc_host_ops function,
+	 * to replace standard ones in sdhci_ops.
+	 */
+	xenon_replace_mmc_host_ops(host);
+
+	clk = devm_clk_get(&pdev->dev, "core");
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "Failed to setup input clk.\n");
+		err = PTR_ERR(clk);
+		goto free_pltfm;
+	}
+	clk_prepare_enable(clk);
+	pltfm_host->clk = clk;
+
+	/*
+	 * Some SOCs require additional clock to
+	 * manage AXI bus clock.
+	 * It is optional.
+	 */
+	axi_clk = devm_clk_get(&pdev->dev, "axi");
+	if (!IS_ERR(axi_clk)) {
+		clk_prepare_enable(axi_clk);
+		priv->axi_clk = axi_clk;
+	}
+
+	err = xenon_probe_dt(pdev);
+	if (err)
+		goto err_clk;
+
+	err = xenon_slot_probe(host);
+	if (err)
+		goto err_clk;
+
+	err = sdhci_add_host(host);
+	if (err)
+		goto remove_slot;
+
+	return 0;
+
+remove_slot:
+	xenon_slot_remove(host);
+err_clk:
+	clk_disable_unprepare(pltfm_host->clk);
+	if (!IS_ERR(axi_clk))
+		clk_disable_unprepare(axi_clk);
+free_pltfm:
+	sdhci_pltfm_free(pdev);
+	return err;
+}
+
+static int sdhci_xenon_remove(struct platform_device *pdev)
+{
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xFFFFFFFF);
+
+	xenon_slot_remove(host);
+
+	sdhci_remove_host(host, dead);
+
+	clk_disable_unprepare(pltfm_host->clk);
+	clk_disable_unprepare(priv->axi_clk);
+
+	sdhci_pltfm_free(pdev);
+
+	return 0;
+}
+
+static const struct of_device_id sdhci_xenon_dt_ids[] = {
+	{ .compatible = "marvell,sdhci-xenon",},
+	{ .compatible = "marvell,armada-3700-sdhci",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, sdhci_xenon_dt_ids);
+
+static struct platform_driver sdhci_xenon_driver = {
+	.driver	= {
+		.name	= "sdhci-xenon",
+		.of_match_table = sdhci_xenon_dt_ids,
+		.pm = &sdhci_pltfm_pmops,
+	},
+	.probe	= sdhci_xenon_probe,
+	.remove	= sdhci_xenon_remove,
+};
+
+module_platform_driver(sdhci_xenon_driver);
+
+MODULE_DESCRIPTION("SDHCI platform driver for Marvell Xenon SDHC");
+MODULE_AUTHOR("Hu Ziji <huziji@marvell.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-xenon.h b/drivers/mmc/host/sdhci-xenon.h
new file mode 100644
index 000000000000..c2370493fbe8
--- /dev/null
+++ b/drivers/mmc/host/sdhci-xenon.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2016 Marvell, All Rights Reserved.
+ *
+ * Author:	Hu Ziji <huziji@marvell.com>
+ * Date:	2016-8-24
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ */
+#ifndef SDHCI_XENON_H_
+#define SDHCI_XENON_H_
+
+#include <linux/clk.h>
+#include <linux/mmc/card.h>
+#include <linux/of.h>
+#include "sdhci.h"
+
+/* Register Offset of SD Host Controller SOCP self-defined register */
+#define SDHC_SYS_CFG_INFO			0x0104
+#define SLOT_TYPE_SDIO_SHIFT			24
+#define SLOT_TYPE_EMMC_MASK			0xFF
+#define SLOT_TYPE_EMMC_SHIFT			16
+#define SLOT_TYPE_SD_SDIO_MMC_MASK		0xFF
+#define SLOT_TYPE_SD_SDIO_MMC_SHIFT		8
+#define NR_SUPPORTED_SLOT_MASK			0x7
+
+#define SDHC_SYS_OP_CTRL			0x0108
+#define AUTO_CLKGATE_DISABLE_MASK		BIT(20)
+#define SDCLK_IDLEOFF_ENABLE_SHIFT		8
+#define SLOT_ENABLE_SHIFT			0
+
+#define SDHC_SYS_EXT_OP_CTRL			0x010C
+#define MASK_CMD_CONFLICT_ERROR			BIT(8)
+
+#define SDHC_SLOT_OP_STATUS_CTRL		0x0128
+#define DELAY_90_DEGREE_MASK_EMMC5		BIT(7)
+#define DELAY_90_DEGREE_SHIFT_EMMC5		7
+#define EMMC_5_0_PHY_FIXED_DELAY_MASK		0x7F
+#define EMMC_PHY_FIXED_DELAY_MASK		0xFF
+#define EMMC_PHY_FIXED_DELAY_WINDOW_MIN		(EMMC_PHY_FIXED_DELAY_MASK >> 3)
+#define SDH_PHY_FIXED_DELAY_MASK		0x1FF
+#define SDH_PHY_FIXED_DELAY_WINDOW_MIN		(SDH_PHY_FIXED_DELAY_MASK >> 4)
+
+#define TUN_CONSECUTIVE_TIMES_SHIFT		16
+#define TUN_CONSECUTIVE_TIMES_MASK		0x7
+#define TUN_CONSECUTIVE_TIMES			0x4
+#define TUNING_STEP_SHIFT			12
+#define TUNING_STEP_MASK			0xF
+#define TUNING_STEP_DIVIDER			BIT(6)
+
+#define FORCE_SEL_INVERSE_CLK_SHIFT		11
+
+#define SDHC_SLOT_EMMC_CTRL			0x0130
+#define ENABLE_DATA_STROBE			BIT(24)
+#define SET_EMMC_RSTN				BIT(16)
+#define DISABLE_RD_DATA_CRC			BIT(14)
+#define DISABLE_CRC_STAT_TOKEN			BIT(13)
+#define EMMC_VCCQ_MASK				0x3
+#define EMMC_VCCQ_1_8V				0x1
+#define EMMC_VCCQ_3_3V				0x3
+
+#define SDHC_SLOT_RETUNING_REQ_CTRL		0x0144
+/* retuning compatible */
+#define RETUNING_COMPATIBLE			0x1
+
+#define SDHC_SLOT_EXT_PRESENT_STATE		0x014C
+#define LOCK_STATE				0x1
+
+#define SDHC_SLOT_DLL_CUR_DLY_VAL		0x0150
+
+/* Tuning Parameter */
+#define TMR_RETUN_NO_PRESENT			0xF
+#define DEF_TUNING_COUNT			0x9
+
+#define MMC_TIMING_FAKE				0xFF
+
+#define DEFAULT_SDCLK_FREQ			(400000)
+
+/* Xenon specific Mode Select value */
+#define XENON_SDHCI_CTRL_HS200			0x5
+#define XENON_SDHCI_CTRL_HS400			0x6
+
+struct sdhci_xenon_priv {
+	/*
+	 * The bus_width, timing, and clock fields in below
+	 * record the current setting of Xenon SDHC.
+	 * Driver will call a Sampling Fixed Delay Adjustment
+	 * if any setting is changed.
+	 */
+	unsigned char	bus_width;
+	unsigned char	timing;
+	unsigned char	tuning_count;
+	unsigned int	clock;
+	struct clk	*axi_clk;
+
+	/* Slot idx */
+	u8		slot_idx;
+
+	/*
+	 * When initializing card, Xenon has to determine card type and
+	 * adjust Sampling Fixed delay.
+	 * However, at that time, card structure is not linked to mmc_host.
+	 * Thus a card pointer is added here to provide
+	 * the delay adjustment function with the card structure
+	 * of the card during initialization
+	 */
+	struct mmc_card *card_candidate;
+};
+
+static inline int enable_xenon_internal_clk(struct sdhci_host *host)
+{
+	u32 reg;
+	u8 timeout;
+
+	reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
+	reg |= SDHCI_CLOCK_INT_EN;
+	sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
+	/* Wait max 20 ms */
+	timeout = 20;
+	while (!((reg = 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 -ETIMEDOUT;
+		}
+		timeout--;
+		mdelay(1);
+	}
+
+	return 0;
+}
+#endif
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH 7/10] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement@free-electrons.com>

From: Ziji Hu <huziji@marvell.com>

Marvell Xenon eMMC/SD/SDIO Host Controller contains PHY.
Three types of PHYs are supported.

Add support to multiple types of PHYs init and configuration.
Add register definitions of PHYs.

Signed-off-by: Hu Ziji <huziji@marvell.com>
Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 MAINTAINERS                        |    1 +-
 drivers/mmc/host/Makefile          |    2 +-
 drivers/mmc/host/sdhci-xenon-phy.c | 1141 +++++++++++++++++++++++++++++-
 drivers/mmc/host/sdhci-xenon-phy.h |  157 ++++-
 drivers/mmc/host/sdhci-xenon.c     |    4 +-
 drivers/mmc/host/sdhci-xenon.h     |   17 +-
 6 files changed, 1321 insertions(+), 1 deletion(-)
 create mode 100644 drivers/mmc/host/sdhci-xenon-phy.c
 create mode 100644 drivers/mmc/host/sdhci-xenon-phy.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 859420e5dfd3..b5673c2ee5f2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7583,6 +7583,7 @@ M:	Ziji Hu <huziji@marvell.com>
 L:	linux-mmc at vger.kernel.org
 S:	Supported
 F:	drivers/mmc/host/sdhci-xenon.*
+F:	drivers/mmc/host/sdhci-xenon-phy.*
 F:	Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
 
 MATROX FRAMEBUFFER DRIVER
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 75eaf743486c..4f2854556ff7 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -82,4 +82,4 @@ ifeq ($(CONFIG_CB710_DEBUG),y)
 endif
 
 obj-$(CONFIG_MMC_SDHCI_XENON)	+= sdhci-xenon-driver.o
-sdhci-xenon-driver-y		+= sdhci-xenon.o
+sdhci-xenon-driver-y		+= sdhci-xenon.o sdhci-xenon-phy.o
diff --git a/drivers/mmc/host/sdhci-xenon-phy.c b/drivers/mmc/host/sdhci-xenon-phy.c
new file mode 100644
index 000000000000..4eb8fea1bec9
--- /dev/null
+++ b/drivers/mmc/host/sdhci-xenon-phy.c
@@ -0,0 +1,1141 @@
+/*
+ * PHY support for Xenon SDHC
+ *
+ * Copyright (C) 2016 Marvell, All Rights Reserved.
+ *
+ * Author:	Hu Ziji <huziji@marvell.com>
+ * Date:	2016-8-24
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/of_address.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+
+#include "sdhci.h"
+#include "sdhci-pltfm.h"
+#include "sdhci-xenon.h"
+
+static const char * const phy_types[] = {
+	"sdh phy",
+	"emmc 5.0 phy",
+	"emmc 5.1 phy"
+};
+
+enum phy_type_enum {
+	SDH_PHY,
+	EMMC_5_0_PHY,
+	EMMC_5_1_PHY,
+	NR_PHY_TYPES
+};
+
+struct soc_pad_ctrl_table {
+	const char *soc;
+	void (*set_soc_pad)(struct sdhci_host *host,
+			    unsigned char signal_voltage);
+};
+
+struct soc_pad_ctrl {
+	/* Register address of SOC PHY PAD ctrl */
+	void __iomem	*reg;
+	/* SOC PHY PAD ctrl type */
+	enum soc_pad_ctrl_type pad_type;
+	/* SOC specific operation to set SOC PHY PAD */
+	void (*set_soc_pad)(struct sdhci_host *host,
+			    unsigned char signal_voltage);
+};
+
+static struct xenon_emmc_phy_regs  xenon_emmc_5_0_phy_regs = {
+	.timing_adj	= EMMC_5_0_PHY_TIMING_ADJUST,
+	.func_ctrl	= EMMC_5_0_PHY_FUNC_CONTROL,
+	.pad_ctrl	= EMMC_5_0_PHY_PAD_CONTROL,
+	.pad_ctrl2	= EMMC_5_0_PHY_PAD_CONTROL2,
+	.dll_ctrl	= EMMC_5_0_PHY_DLL_CONTROL,
+	.logic_timing_adj = EMMC_5_0_PHY_LOGIC_TIMING_ADJUST,
+	.delay_mask	= EMMC_5_0_PHY_FIXED_DELAY_MASK,
+	.dll_update	= DLL_UPDATE_STROBE_5_0,
+};
+
+static struct xenon_emmc_phy_regs  xenon_emmc_5_1_phy_regs = {
+	.timing_adj	= EMMC_PHY_TIMING_ADJUST,
+	.func_ctrl	= EMMC_PHY_FUNC_CONTROL,
+	.pad_ctrl	= EMMC_PHY_PAD_CONTROL,
+	.pad_ctrl2	= EMMC_PHY_PAD_CONTROL2,
+	.dll_ctrl	= EMMC_PHY_DLL_CONTROL,
+	.logic_timing_adj = EMMC_PHY_LOGIC_TIMING_ADJUST,
+	.delay_mask	= EMMC_PHY_FIXED_DELAY_MASK,
+	.dll_update	= DLL_UPDATE,
+};
+
+static int xenon_delay_adj_test(struct mmc_card *card);
+
+/*
+ * eMMC PHY configuration and operations
+ */
+struct emmc_phy_params {
+	bool	slow_mode;
+
+	u8	znr;
+	u8	zpr;
+
+	/* Nr of consecutive Sampling Points of a Valid Sampling Window */
+	u8	nr_tun_times;
+	/* Divider for calculating Tuning Step */
+	u8	tun_step_divider;
+
+	struct soc_pad_ctrl pad_ctrl;
+};
+
+static void xenon_emmc_phy_strobe_delay_adj(struct sdhci_host *host,
+					    struct mmc_card *card);
+static int xenon_emmc_phy_fix_sampl_delay_adj(struct sdhci_host *host,
+					      struct mmc_card *card);
+static void xenon_emmc_phy_set(struct sdhci_host *host,
+			       unsigned char timing);
+static void xenon_emmc_set_soc_pad(struct sdhci_host *host,
+				   unsigned char signal_voltage);
+
+static const struct xenon_phy_ops emmc_phy_ops = {
+	.strobe_delay_adj = xenon_emmc_phy_strobe_delay_adj,
+	.fix_sampl_delay_adj = xenon_emmc_phy_fix_sampl_delay_adj,
+	.phy_set = xenon_emmc_phy_set,
+	.set_soc_pad = xenon_emmc_set_soc_pad,
+};
+
+static int alloc_emmc_phy(struct sdhci_xenon_priv *priv)
+{
+	struct emmc_phy_params *params;
+
+	params = kzalloc(sizeof(*params), GFP_KERNEL);
+	if (!params)
+		return -ENOMEM;
+
+	priv->phy_params = params;
+	priv->phy_ops = &emmc_phy_ops;
+	if (priv->phy_type == EMMC_5_0_PHY)
+		priv->emmc_phy_regs = &xenon_emmc_5_0_phy_regs;
+	else
+		priv->emmc_phy_regs = &xenon_emmc_5_1_phy_regs;
+
+	return 0;
+}
+
+static int xenon_emmc_phy_init(struct sdhci_host *host)
+{
+	u32 reg;
+	u32 wait, clock;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
+
+	reg = sdhci_readl(host, phy_regs->timing_adj);
+	reg |= PHY_INITIALIZAION;
+	sdhci_writel(host, reg, phy_regs->timing_adj);
+
+	/* Add duration of FC_SYNC_RST */
+	wait = ((reg >> FC_SYNC_RST_DURATION_SHIFT) &
+			FC_SYNC_RST_DURATION_MASK);
+	/* Add interval between FC_SYNC_EN and FC_SYNC_RST */
+	wait += ((reg >> FC_SYNC_RST_EN_DURATION_SHIFT) &
+			FC_SYNC_RST_EN_DURATION_MASK);
+	/* Add duration of asserting FC_SYNC_EN */
+	wait += ((reg >> FC_SYNC_EN_DURATION_SHIFT) &
+			FC_SYNC_EN_DURATION_MASK);
+	/* Add duration of waiting for PHY */
+	wait += ((reg >> WAIT_CYCLE_BEFORE_USING_SHIFT) &
+			WAIT_CYCLE_BEFORE_USING_MASK);
+	/* 4 addtional bus clock and 4 AXI bus clock are required */
+	wait += 8;
+	wait <<= 20;
+
+	clock = host->clock;
+	if (!clock)
+		/* Use the possibly slowest bus frequency value */
+		clock = LOWEST_SDCLK_FREQ;
+	/* get the wait time */
+	wait /= clock;
+	wait++;
+	/* wait for host eMMC PHY init completes */
+	udelay(wait);
+
+	reg = sdhci_readl(host, phy_regs->timing_adj);
+	reg &= PHY_INITIALIZAION;
+	if (reg) {
+		dev_err(mmc_dev(host->mmc), "eMMC PHY init cannot complete after %d us\n",
+			wait);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+#define ARMADA_3700_SOC_PAD_1_8V	0x1
+#define ARMADA_3700_SOC_PAD_3_3V	0x0
+
+static void armada_3700_soc_pad_voltage_set(struct sdhci_host *host,
+					    unsigned char signal_voltage)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	struct emmc_phy_params *params = priv->phy_params;
+
+	if (params->pad_ctrl.pad_type == SOC_PAD_FIXED_1_8V) {
+		writel(ARMADA_3700_SOC_PAD_1_8V, params->pad_ctrl.reg);
+	} else if (params->pad_ctrl.pad_type == SOC_PAD_SD) {
+		if (signal_voltage == MMC_SIGNAL_VOLTAGE_180)
+			writel(ARMADA_3700_SOC_PAD_1_8V, params->pad_ctrl.reg);
+		else if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
+			writel(ARMADA_3700_SOC_PAD_3_3V, params->pad_ctrl.reg);
+	}
+}
+
+static void xenon_emmc_set_soc_pad(struct sdhci_host *host,
+				   unsigned char signal_voltage)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	struct emmc_phy_params *params = priv->phy_params;
+
+	if (!params->pad_ctrl.reg)
+		return;
+
+	if (params->pad_ctrl.set_soc_pad)
+		params->pad_ctrl.set_soc_pad(host, signal_voltage);
+}
+
+static int emmc_phy_set_fix_sampl_delay(struct sdhci_host *host,
+					unsigned int delay,
+					bool invert,
+					bool delay_90_degree)
+{
+	u32 reg;
+	unsigned long flags;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
+	int ret = 0;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	/* Setup Sampling fix delay */
+	reg = sdhci_readl(host, SDHC_SLOT_OP_STATUS_CTRL);
+	reg &= ~phy_regs->delay_mask;
+	reg |= delay & phy_regs->delay_mask;
+	sdhci_writel(host, reg, SDHC_SLOT_OP_STATUS_CTRL);
+
+	if (priv->phy_type == EMMC_5_0_PHY) {
+		/* set 90 degree phase if necessary */
+		reg &= ~DELAY_90_DEGREE_MASK_EMMC5;
+		reg |= (delay_90_degree << DELAY_90_DEGREE_SHIFT_EMMC5);
+		sdhci_writel(host, reg, SDHC_SLOT_OP_STATUS_CTRL);
+	}
+
+	/* Disable SDCLK */
+	reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
+	reg &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN);
+	sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
+
+	udelay(200);
+
+	if (priv->phy_type == EMMC_5_1_PHY) {
+		/* set 90 degree phase if necessary */
+		reg = sdhci_readl(host, EMMC_PHY_FUNC_CONTROL);
+		reg &= ~ASYNC_DDRMODE_MASK;
+		reg |= (delay_90_degree << ASYNC_DDRMODE_SHIFT);
+		sdhci_writel(host, reg, EMMC_PHY_FUNC_CONTROL);
+	}
+
+	/* Setup Inversion of Sampling edge */
+	reg = sdhci_readl(host, phy_regs->timing_adj);
+	reg &= ~SAMPL_INV_QSP_PHASE_SELECT;
+	reg |= (invert << SAMPL_INV_QSP_PHASE_SELECT_SHIFT);
+	sdhci_writel(host, reg, phy_regs->timing_adj);
+
+	/* Enable SD internal clock */
+	ret = enable_xenon_internal_clk(host);
+	if (ret)
+		goto out;
+
+	/* Enable SDCLK */
+	reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
+	reg |= SDHCI_CLOCK_CARD_EN;
+	sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
+
+	udelay(200);
+
+	/*
+	 * Has to re-initialize eMMC PHY here to active PHY
+	 * because later get status cmd will be issued.
+	 */
+	ret = xenon_emmc_phy_init(host);
+
+out:
+	spin_unlock_irqrestore(&host->lock, flags);
+	return ret;
+}
+
+static int emmc_phy_do_fix_sampl_delay(struct sdhci_host *host,
+				       struct mmc_card *card,
+				       unsigned int delay,
+				       bool invert, bool quarter)
+{
+	int ret;
+
+	emmc_phy_set_fix_sampl_delay(host, delay, invert, quarter);
+
+	ret = xenon_delay_adj_test(card);
+	if (ret) {
+		dev_dbg(mmc_dev(host->mmc),
+			"fail when sampling fix delay = %d, phase = %d degree\n",
+			delay, invert * 180 + quarter * 90);
+		return -1;
+	}
+	return 0;
+}
+
+static int xenon_emmc_phy_fix_sampl_delay_adj(struct sdhci_host *host,
+					      struct mmc_card *card)
+{
+	enum sampl_fix_delay_phase phase;
+	int idx, nr_pair;
+	int ret;
+	unsigned int delay;
+	unsigned int min_delay, max_delay;
+	bool invert, quarter;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
+	u32 coarse_step, fine_step;
+	const enum sampl_fix_delay_phase delay_edge[] = {
+		PHASE_0_DEGREE,
+		PHASE_180_DEGREE,
+		PHASE_90_DEGREE,
+		PHASE_270_DEGREE
+	};
+
+	coarse_step = phy_regs->delay_mask >> 1;
+	fine_step = coarse_step >> 2;
+
+	nr_pair = ARRAY_SIZE(delay_edge);
+
+	for (idx = 0; idx < nr_pair; idx++) {
+		phase = delay_edge[idx];
+		invert = (phase & 0x2) ? true : false;
+		quarter = (phase & 0x1) ? true : false;
+
+		/* increase delay value to get fix delay */
+		for (min_delay = 0;
+		     min_delay <= phy_regs->delay_mask;
+		     min_delay += coarse_step) {
+			ret = emmc_phy_do_fix_sampl_delay(host, card, min_delay,
+							  invert, quarter);
+			if (!ret)
+				break;
+		}
+
+		if (ret) {
+			dev_dbg(mmc_dev(host->mmc),
+				"Fail to set Sampling Fixed Delay with phase = %d degree\n",
+				phase * 90);
+			continue;
+		}
+
+		for (max_delay = min_delay + fine_step;
+		     max_delay < phy_regs->delay_mask;
+		     max_delay += fine_step) {
+			ret = emmc_phy_do_fix_sampl_delay(host, card, max_delay,
+							  invert, quarter);
+			if (ret) {
+				max_delay -= fine_step;
+				break;
+			}
+		}
+
+		if (!ret) {
+			ret = emmc_phy_do_fix_sampl_delay(host, card,
+							  phy_regs->delay_mask,
+							  invert, quarter);
+			if (!ret)
+				max_delay = phy_regs->delay_mask;
+		}
+
+		/*
+		 * Sampling Fixed Delay line window should be large enough,
+		 * thus the sampling point (the middle of the window)
+		 * can work when environment varies.
+		 * However, there is no clear conclusion how large the window
+		 * should be.
+		 */
+		if ((max_delay - min_delay) <=
+		    EMMC_PHY_FIXED_DELAY_WINDOW_MIN) {
+			dev_info(mmc_dev(host->mmc),
+				 "The window size %d with phase = %d degree is too small\n",
+				 max_delay - min_delay, phase * 90);
+			continue;
+		}
+
+		delay = (min_delay + max_delay) / 2;
+		emmc_phy_set_fix_sampl_delay(host, delay, invert, quarter);
+		dev_dbg(mmc_dev(host->mmc),
+			"sampling fix delay = %d with phase = %d degree\n",
+			delay, phase * 90);
+		return 0;
+	}
+
+	return -EIO;
+}
+
+static int xenon_emmc_phy_enable_dll(struct sdhci_host *host)
+{
+	u32 reg;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
+	u8 timeout;
+
+	if (WARN_ON(host->clock <= MMC_HIGH_52_MAX_DTR))
+		return -EINVAL;
+
+	reg = sdhci_readl(host, phy_regs->dll_ctrl);
+	if (reg & DLL_ENABLE)
+		return 0;
+
+	/* Enable DLL */
+	reg = sdhci_readl(host, phy_regs->dll_ctrl);
+	reg |= (DLL_ENABLE | DLL_FAST_LOCK);
+
+	/*
+	 * Set Phase as 90 degree, which is most common value.
+	 * Might set another value if necessary.
+	 * The granularity is 1 degree.
+	 */
+	reg &= ~((DLL_PHASE_MASK << DLL_PHSEL0_SHIFT) |
+			(DLL_PHASE_MASK << DLL_PHSEL1_SHIFT));
+	reg |= ((DLL_PHASE_90_DEGREE << DLL_PHSEL0_SHIFT) |
+			(DLL_PHASE_90_DEGREE << DLL_PHSEL1_SHIFT));
+
+	reg &= ~DLL_BYPASS_EN;
+	reg |= phy_regs->dll_update;
+	if (priv->phy_type == EMMC_5_1_PHY)
+		reg &= ~DLL_REFCLK_SEL;
+	sdhci_writel(host, reg, phy_regs->dll_ctrl);
+
+	/* Wait max 32 ms */
+	timeout = 32;
+	while (!(sdhci_readw(host, SDHC_SLOT_EXT_PRESENT_STATE) & LOCK_STATE)) {
+		if (!timeout) {
+			dev_err(mmc_dev(host->mmc), "Wait for DLL Lock time-out\n");
+			return -ETIMEDOUT;
+		}
+		timeout--;
+		mdelay(1);
+	}
+	return 0;
+}
+
+static int __emmc_phy_config_tuning(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	struct emmc_phy_params *params = priv->phy_params;
+	u32 reg, tuning_step;
+	int ret;
+	unsigned long flags;
+
+	if (WARN_ON(host->clock <= MMC_HIGH_52_MAX_DTR))
+		return -EINVAL;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	ret = xenon_emmc_phy_enable_dll(host);
+	if (ret) {
+		spin_unlock_irqrestore(&host->lock, flags);
+		return ret;
+	}
+
+	reg = sdhci_readl(host, SDHC_SLOT_DLL_CUR_DLY_VAL);
+	tuning_step = reg / params->tun_step_divider;
+	if (unlikely(tuning_step > TUNING_STEP_MASK)) {
+		dev_warn(mmc_dev(host->mmc),
+			 "HS200 TUNING_STEP %d is larger than MAX value\n",
+			 tuning_step);
+		tuning_step = TUNING_STEP_MASK;
+	}
+
+	reg = sdhci_readl(host, SDHC_SLOT_OP_STATUS_CTRL);
+	reg &= ~(TUN_CONSECUTIVE_TIMES_MASK << TUN_CONSECUTIVE_TIMES_SHIFT);
+	reg |= (params->nr_tun_times << TUN_CONSECUTIVE_TIMES_SHIFT);
+	reg &= ~(TUNING_STEP_MASK << TUNING_STEP_SHIFT);
+	reg |= (tuning_step << TUNING_STEP_SHIFT);
+	sdhci_writel(host, reg, SDHC_SLOT_OP_STATUS_CTRL);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+	return 0;
+}
+
+static int xenon_emmc_phy_config_tuning(struct sdhci_host *host)
+{
+	return __emmc_phy_config_tuning(host);
+}
+
+static void xenon_emmc_phy_strobe_delay_adj(struct sdhci_host *host,
+					    struct mmc_card *card)
+{
+	u32 reg;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	unsigned long flags;
+
+	if (host->clock <= MMC_HIGH_52_MAX_DTR)
+		return;
+
+	dev_dbg(mmc_dev(host->mmc), "starts HS400 strobe delay adjustment\n");
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	xenon_emmc_phy_enable_dll(host);
+
+	/* Enable SDHC Data Strobe */
+	reg = sdhci_readl(host, SDHC_SLOT_EMMC_CTRL);
+	reg |= ENABLE_DATA_STROBE;
+	sdhci_writel(host, reg, SDHC_SLOT_EMMC_CTRL);
+
+	/* Set Data Strobe Pull down */
+	if (priv->phy_type == EMMC_5_0_PHY) {
+		reg = sdhci_readl(host, EMMC_5_0_PHY_PAD_CONTROL);
+		reg |= EMMC5_FC_QSP_PD;
+		reg &= ~EMMC5_FC_QSP_PU;
+		sdhci_writel(host, reg, EMMC_5_0_PHY_PAD_CONTROL);
+	} else {
+		reg = sdhci_readl(host, EMMC_PHY_PAD_CONTROL1);
+		reg |= EMMC5_1_FC_QSP_PD;
+		reg &= ~EMMC5_1_FC_QSP_PU;
+		sdhci_writel(host, reg, EMMC_PHY_PAD_CONTROL1);
+	}
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+#define LOGIC_TIMING_VALUE	0x00AA8977
+
+static void xenon_emmc_phy_set(struct sdhci_host *host,
+			       unsigned char timing)
+{
+	u32 reg;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	struct emmc_phy_params *params = priv->phy_params;
+	struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
+	struct mmc_card *card = priv->card_candidate;
+	unsigned long flags;
+
+	dev_dbg(mmc_dev(host->mmc), "eMMC PHY setting starts\n");
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	/* Setup pad, set bit[28] and bits[26:24] */
+	reg = sdhci_readl(host, phy_regs->pad_ctrl);
+	reg |= (FC_DQ_RECEN | FC_CMD_RECEN | FC_QSP_RECEN | OEN_QSN);
+	/*
+	 * All FC_XX_RECEIVCE should be set as CMOS Type
+	 */
+	reg |= FC_ALL_CMOS_RECEIVER;
+	sdhci_writel(host, reg, phy_regs->pad_ctrl);
+
+	/* Set CMD and DQ Pull Up */
+	if (priv->phy_type == EMMC_5_0_PHY) {
+		reg = sdhci_readl(host, EMMC_5_0_PHY_PAD_CONTROL);
+		reg |= (EMMC5_FC_CMD_PU | EMMC5_FC_DQ_PU);
+		reg &= ~(EMMC5_FC_CMD_PD | EMMC5_FC_DQ_PD);
+		sdhci_writel(host, reg, EMMC_5_0_PHY_PAD_CONTROL);
+	} else {
+		reg = sdhci_readl(host, EMMC_PHY_PAD_CONTROL1);
+		reg |= (EMMC5_1_FC_CMD_PU | EMMC5_1_FC_DQ_PU);
+		reg &= ~(EMMC5_1_FC_CMD_PD | EMMC5_1_FC_DQ_PD);
+		sdhci_writel(host, reg, EMMC_PHY_PAD_CONTROL1);
+	}
+
+	if ((timing == MMC_TIMING_LEGACY) || !card)
+		goto phy_init;
+
+	/*
+	 * FIXME: should depends on the specific board timing.
+	 */
+	if ((timing == MMC_TIMING_MMC_HS400) ||
+	    (timing == MMC_TIMING_MMC_HS200) ||
+	    (timing == MMC_TIMING_UHS_SDR50) ||
+	    (timing == MMC_TIMING_UHS_SDR104) ||
+	    (timing == MMC_TIMING_UHS_DDR50) ||
+	    (timing == MMC_TIMING_UHS_SDR25) ||
+	    (timing == MMC_TIMING_MMC_DDR52)) {
+		reg = sdhci_readl(host, phy_regs->timing_adj);
+		reg &= ~OUTPUT_QSN_PHASE_SELECT;
+		sdhci_writel(host, reg, phy_regs->timing_adj);
+	}
+
+	/*
+	 * If SDIO card, set SDIO Mode
+	 * Otherwise, clear SDIO Mode and Slow Mode
+	 */
+	if (mmc_card_sdio(card)) {
+		reg = sdhci_readl(host, phy_regs->timing_adj);
+		reg |= TIMING_ADJUST_SDIO_MODE;
+
+		if ((timing == MMC_TIMING_UHS_SDR25) ||
+		    (timing == MMC_TIMING_UHS_SDR12) ||
+		    (timing == MMC_TIMING_SD_HS) ||
+		    (timing == MMC_TIMING_LEGACY))
+			reg |= TIMING_ADJUST_SLOW_MODE;
+
+		sdhci_writel(host, reg, phy_regs->timing_adj);
+	} else {
+		reg = sdhci_readl(host, phy_regs->timing_adj);
+		reg &= ~(TIMING_ADJUST_SDIO_MODE | TIMING_ADJUST_SLOW_MODE);
+		sdhci_writel(host, reg, phy_regs->timing_adj);
+	}
+
+	if (((timing == MMC_TIMING_UHS_SDR50) ||
+	     (timing == MMC_TIMING_UHS_SDR25) ||
+	     (timing == MMC_TIMING_UHS_SDR12) ||
+	     (timing == MMC_TIMING_SD_HS) ||
+	     (timing == MMC_TIMING_MMC_HS) ||
+	     (timing == MMC_TIMING_LEGACY)) && params->slow_mode) {
+		reg = sdhci_readl(host, phy_regs->timing_adj);
+		reg |= TIMING_ADJUST_SLOW_MODE;
+		sdhci_writel(host, reg, phy_regs->timing_adj);
+	}
+
+	/*
+	 * Set preferred ZNR and ZPR value
+	 * The ZNR and ZPR value vary between different boards.
+	 * Define them both in sdhci-xenon-emmc-phy.h.
+	 */
+	reg = sdhci_readl(host, phy_regs->pad_ctrl2);
+	reg &= ~((ZNR_MASK << ZNR_SHIFT) | ZPR_MASK);
+	reg |= ((params->znr << ZNR_SHIFT) | params->zpr);
+	sdhci_writel(host, reg, phy_regs->pad_ctrl2);
+
+	/*
+	 * When setting EMMC_PHY_FUNC_CONTROL register,
+	 * SD clock should be disabled
+	 */
+	reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
+	reg &= ~SDHCI_CLOCK_CARD_EN;
+	sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
+
+	if ((timing == MMC_TIMING_UHS_DDR50) ||
+	    (timing == MMC_TIMING_MMC_HS400) ||
+	    (timing == MMC_TIMING_MMC_DDR52)) {
+		reg = sdhci_readl(host, phy_regs->func_ctrl);
+		reg |= (DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) | CMD_DDR_MODE;
+		sdhci_writel(host, reg, phy_regs->func_ctrl);
+	}
+
+	if (timing == MMC_TIMING_MMC_HS400) {
+		reg = sdhci_readl(host, phy_regs->func_ctrl);
+		reg &= ~DQ_ASYNC_MODE;
+		sdhci_writel(host, reg, phy_regs->func_ctrl);
+	}
+
+	/* Enable bus clock */
+	reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
+	reg |= SDHCI_CLOCK_CARD_EN;
+	sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
+
+	if (timing == MMC_TIMING_MMC_HS400)
+		/* Hardware team recommend a value for HS400 */
+		sdhci_writel(host, LOGIC_TIMING_VALUE,
+			     phy_regs->logic_timing_adj);
+
+phy_init:
+	xenon_emmc_phy_init(host);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	dev_dbg(mmc_dev(host->mmc), "eMMC PHY setting completes\n");
+}
+
+static int get_dt_pad_ctrl_data(struct sdhci_host *host,
+				struct device_node *np,
+				struct emmc_phy_params *params)
+{
+	int ret = 0;
+	const char *name;
+	struct resource iomem;
+
+	if (of_device_is_compatible(np, "marvell,armada-3700-sdhci"))
+		params->pad_ctrl.set_soc_pad = armada_3700_soc_pad_voltage_set;
+	else
+		return 0;
+
+	if (of_address_to_resource(np, 1, &iomem)) {
+		dev_err(mmc_dev(host->mmc), "Unable to find SOC PAD ctrl register address for %s\n",
+			np->name);
+		return -EINVAL;
+	}
+
+	params->pad_ctrl.reg = devm_ioremap_resource(mmc_dev(host->mmc),
+						     &iomem);
+	if (IS_ERR(params->pad_ctrl.reg)) {
+		dev_err(mmc_dev(host->mmc), "Unable to get SOC PHY PAD ctrl regiser for %s\n",
+			np->name);
+		return PTR_ERR(params->pad_ctrl.reg);
+	}
+
+	ret = of_property_read_string(np, "xenon,pad-type", &name);
+	if (ret) {
+		dev_err(mmc_dev(host->mmc), "Unable to determine SOC PHY PAD ctrl type\n");
+		return ret;
+	}
+	if (!strcmp(name, "sd")) {
+		params->pad_ctrl.pad_type = SOC_PAD_SD;
+	} else if (!strcmp(name, "fixed-1-8v")) {
+		params->pad_ctrl.pad_type = SOC_PAD_FIXED_1_8V;
+	} else {
+		dev_err(mmc_dev(host->mmc), "Unsupported SOC PHY PAD ctrl type %s\n",
+			name);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int emmc_phy_parse_param_dt(struct sdhci_host *host,
+				   struct device_node *np,
+				   struct emmc_phy_params *params)
+{
+	u32 value;
+
+	if (of_property_read_bool(np, "xenon,phy-slow-mode"))
+		params->slow_mode = true;
+	else
+		params->slow_mode = false;
+
+	if (!of_property_read_u32(np, "xenon,phy-znr", &value))
+		params->znr = value & ZNR_MASK;
+	else
+		params->znr = ZNR_DEF_VALUE;
+
+	if (!of_property_read_u32(np, "xenon,phy-zpr", &value))
+		params->zpr = value & ZPR_MASK;
+	else
+		params->zpr = ZPR_DEF_VALUE;
+
+	if (!of_property_read_u32(np, "xenon,phy-nr-tun-times", &value))
+		params->nr_tun_times = value & TUN_CONSECUTIVE_TIMES_MASK;
+	else
+		params->nr_tun_times = TUN_CONSECUTIVE_TIMES;
+
+	if (!of_property_read_u32(np, "xenon,phy-tun-step-divider", &value))
+		params->tun_step_divider = value & 0xFF;
+	else
+		params->tun_step_divider = TUNING_STEP_DIVIDER;
+
+	return get_dt_pad_ctrl_data(host, np, params);
+}
+
+/*
+ * SDH PHY configuration and operations
+ */
+static int xenon_sdh_phy_set_fix_sampl_delay(struct sdhci_host *host,
+					     unsigned int delay, bool invert)
+{
+	u32 reg;
+	unsigned long flags;
+	int ret;
+
+	if (invert)
+		invert = 0x1;
+	else
+		invert = 0x0;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	/* Disable SDCLK */
+	reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
+	reg &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN);
+	sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
+
+	udelay(200);
+
+	/* Setup Sampling fix delay */
+	reg = sdhci_readl(host, SDHC_SLOT_OP_STATUS_CTRL);
+	reg &= ~(SDH_PHY_FIXED_DELAY_MASK |
+			(0x1 << FORCE_SEL_INVERSE_CLK_SHIFT));
+	reg |= ((delay & SDH_PHY_FIXED_DELAY_MASK) |
+			(invert << FORCE_SEL_INVERSE_CLK_SHIFT));
+	sdhci_writel(host, reg, SDHC_SLOT_OP_STATUS_CTRL);
+
+	/* Enable SD internal clock */
+	ret = enable_xenon_internal_clk(host);
+
+	/* Enable SDCLK */
+	reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
+	reg |= SDHCI_CLOCK_CARD_EN;
+	sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
+
+	udelay(200);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+	return ret;
+}
+
+static int sdh_phy_do_fix_sampl_delay(struct sdhci_host *host,
+				      struct mmc_card *card,
+				      unsigned int delay, bool invert)
+{
+	int ret;
+
+	xenon_sdh_phy_set_fix_sampl_delay(host, delay, invert);
+
+	ret = xenon_delay_adj_test(card);
+	if (ret) {
+		dev_dbg(mmc_dev(host->mmc),
+			"fail when sampling fix delay = %d, phase = %d degree\n",
+			delay, invert * 180);
+		return -1;
+	}
+	return 0;
+}
+
+#define SDH_PHY_COARSE_FIX_DELAY	(SDH_PHY_FIXED_DELAY_MASK / 2)
+#define SDH_PHY_FINE_FIX_DELAY		(SDH_PHY_COARSE_FIX_DELAY / 4)
+
+static int xenon_sdh_phy_fix_sampl_delay_adj(struct sdhci_host *host,
+					     struct mmc_card *card)
+{
+	u32 reg;
+	bool dll_enable = false;
+	unsigned int min_delay, max_delay, delay;
+	const bool sampl_edge[] = {
+		false,
+		true,
+	};
+	int i, nr;
+	int ret;
+
+	if (host->clock > HIGH_SPEED_MAX_DTR) {
+		/* Enable DLL when SDCLK is higher than 50MHz */
+		reg = sdhci_readl(host, SDH_PHY_SLOT_DLL_CTRL);
+		if (!(reg & SDH_PHY_ENABLE_DLL)) {
+			reg |= (SDH_PHY_ENABLE_DLL | SDH_PHY_FAST_LOCK_EN);
+			sdhci_writel(host, reg, SDH_PHY_SLOT_DLL_CTRL);
+			mdelay(1);
+
+			reg = sdhci_readl(host, SDH_PHY_SLOT_DLL_PHASE_SEL);
+			reg |= SDH_PHY_DLL_UPDATE_TUNING;
+			sdhci_writel(host, reg, SDH_PHY_SLOT_DLL_PHASE_SEL);
+		}
+		dll_enable = true;
+	}
+
+	nr = dll_enable ? ARRAY_SIZE(sampl_edge) : 1;
+	for (i = 0; i < nr; i++) {
+		for (min_delay = 0; min_delay <= SDH_PHY_FIXED_DELAY_MASK;
+				min_delay += SDH_PHY_COARSE_FIX_DELAY) {
+			ret = sdh_phy_do_fix_sampl_delay(host, card, min_delay,
+							 sampl_edge[i]);
+			if (!ret)
+				break;
+		}
+
+		if (ret) {
+			dev_dbg(mmc_dev(host->mmc),
+				"Fail to set Fixed Sampling Delay with %s edge\n",
+				sampl_edge[i] ? "negative" : "positive");
+			continue;
+		}
+
+		for (max_delay = min_delay + SDH_PHY_FINE_FIX_DELAY;
+				max_delay < SDH_PHY_FIXED_DELAY_MASK;
+				max_delay += SDH_PHY_FINE_FIX_DELAY) {
+			ret = sdh_phy_do_fix_sampl_delay(host, card, max_delay,
+							 sampl_edge[i]);
+			if (ret) {
+				max_delay -= SDH_PHY_FINE_FIX_DELAY;
+				break;
+			}
+		}
+
+		if (!ret) {
+			delay = SDH_PHY_FIXED_DELAY_MASK;
+			ret = sdh_phy_do_fix_sampl_delay(host, card, delay,
+							 sampl_edge[i]);
+			if (!ret)
+				max_delay = SDH_PHY_FIXED_DELAY_MASK;
+		}
+
+		if ((max_delay - min_delay) <= SDH_PHY_FIXED_DELAY_WINDOW_MIN) {
+			dev_info(mmc_dev(host->mmc),
+				 "The window size %d with %s edge is too small\n",
+				 max_delay - min_delay,
+				 sampl_edge[i] ? "negative" : "positive");
+			continue;
+		}
+
+		delay = (min_delay + max_delay) / 2;
+		xenon_sdh_phy_set_fix_sampl_delay(host, delay, sampl_edge[i]);
+		dev_dbg(mmc_dev(host->mmc), "sampling fix delay = %d with %s edge\n",
+			delay, sampl_edge[i] ? "negative" : "positive");
+		return 0;
+	}
+	return -EIO;
+}
+
+static const struct xenon_phy_ops sdh_phy_ops = {
+	.fix_sampl_delay_adj = xenon_sdh_phy_fix_sampl_delay_adj,
+};
+
+static int alloc_sdh_phy(struct sdhci_xenon_priv *priv)
+{
+	priv->phy_params = NULL;
+	priv->phy_ops = &sdh_phy_ops;
+	return 0;
+}
+
+/*
+ * Common functions for all PHYs
+ */
+void xenon_soc_pad_ctrl(struct sdhci_host *host,
+			unsigned char signal_voltage)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+	if (priv->phy_ops->set_soc_pad)
+		priv->phy_ops->set_soc_pad(host, signal_voltage);
+}
+
+static int __xenon_emmc_delay_adj_test(struct mmc_card *card)
+{
+	int err;
+	u8 *ext_csd = NULL;
+
+	err = mmc_get_ext_csd(card, &ext_csd);
+	kfree(ext_csd);
+
+	return err;
+}
+
+static int __xenon_sdio_delay_adj_test(struct mmc_card *card)
+{
+	struct mmc_command cmd = {0};
+	int err;
+
+	cmd.opcode = SD_IO_RW_DIRECT;
+	cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+
+	err = mmc_wait_for_cmd(card->host, &cmd, 0);
+	if (err)
+		return err;
+
+	if (cmd.resp[0] & R5_ERROR)
+		return -EIO;
+	if (cmd.resp[0] & R5_FUNCTION_NUMBER)
+		return -EINVAL;
+	if (cmd.resp[0] & R5_OUT_OF_RANGE)
+		return -ERANGE;
+	return 0;
+}
+
+static int __xenon_sd_delay_adj_test(struct mmc_card *card)
+{
+	struct mmc_command cmd = {0};
+	int err;
+
+	cmd.opcode = MMC_SEND_STATUS;
+	cmd.arg = card->rca << 16;
+	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+	err = mmc_wait_for_cmd(card->host, &cmd, 0);
+	return err;
+}
+
+static int xenon_delay_adj_test(struct mmc_card *card)
+{
+	WARN_ON(!card);
+	WARN_ON(!card->host);
+
+	if (mmc_card_mmc(card))
+		return __xenon_emmc_delay_adj_test(card);
+	else if (mmc_card_sd(card))
+		return __xenon_sd_delay_adj_test(card);
+	else if (mmc_card_sdio(card))
+		return __xenon_sdio_delay_adj_test(card);
+	else
+		return -EINVAL;
+}
+
+static void xenon_phy_set(struct sdhci_host *host, unsigned char timing)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+	if (priv->phy_ops->phy_set)
+		priv->phy_ops->phy_set(host, timing);
+}
+
+static void xenon_hs400_strobe_delay_adj(struct sdhci_host *host,
+					 struct mmc_card *card)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+	if (WARN_ON(!mmc_card_hs400(card)))
+		return;
+
+	/* Enable the DLL to automatically adjust HS400 strobe delay.
+	 */
+	if (priv->phy_ops->strobe_delay_adj)
+		priv->phy_ops->strobe_delay_adj(host, card);
+}
+
+static int xenon_fix_sampl_delay_adj(struct sdhci_host *host,
+				     struct mmc_card *card)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+	if (priv->phy_ops->fix_sampl_delay_adj)
+		return priv->phy_ops->fix_sampl_delay_adj(host, card);
+
+	return 0;
+}
+
+/*
+ * xenon_delay_adj should not be called inside IRQ context,
+ * either Hard IRQ or Softirq.
+ */
+static int xenon_hs_delay_adj(struct sdhci_host *host,
+			      struct mmc_card *card)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	int ret = 0;
+
+	if (WARN_ON(host->clock <= DEFAULT_SDCLK_FREQ))
+		return -EINVAL;
+
+	if (mmc_card_hs400(card)) {
+		xenon_hs400_strobe_delay_adj(host, card);
+		return 0;
+	}
+
+	if (((priv->phy_type == EMMC_5_1_PHY) ||
+	     (priv->phy_type == EMMC_5_0_PHY)) &&
+	     (mmc_card_hs200(card) ||
+	     (host->timing == MMC_TIMING_UHS_SDR104))) {
+		ret = xenon_emmc_phy_config_tuning(host);
+		if (!ret)
+			return 0;
+	}
+
+	ret = xenon_fix_sampl_delay_adj(host, card);
+	if (ret)
+		dev_err(mmc_dev(host->mmc), "fails sampling fixed delay adjustment\n");
+	return ret;
+}
+
+int xenon_phy_adj(struct sdhci_host *host, struct mmc_ios *ios)
+{
+	struct mmc_host *mmc = host->mmc;
+	struct mmc_card *card;
+	int ret = 0;
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+	if (!host->clock) {
+		priv->clock = 0;
+		return 0;
+	}
+
+	/*
+	 * The timing, frequency or bus width is changed,
+	 * better to set eMMC PHY based on current setting
+	 * and adjust Xenon SDHC delay.
+	 */
+	if ((host->clock == priv->clock) &&
+	    (ios->bus_width == priv->bus_width) &&
+	    (ios->timing == priv->timing))
+		return 0;
+
+	xenon_phy_set(host, ios->timing);
+
+	/* Update the record */
+	priv->bus_width = ios->bus_width;
+	/* Temp stage from HS200 to HS400 */
+	if (((priv->timing == MMC_TIMING_MMC_HS200) &&
+	     (ios->timing == MMC_TIMING_MMC_HS)) ||
+	    ((ios->timing == MMC_TIMING_MMC_HS) &&
+	     (priv->clock > host->clock))) {
+		priv->timing = ios->timing;
+		priv->clock = host->clock;
+		return 0;
+	}
+	priv->timing = ios->timing;
+	priv->clock = host->clock;
+
+	/* Legacy mode is a special case */
+	if (ios->timing == MMC_TIMING_LEGACY)
+		return 0;
+
+	card = priv->card_candidate;
+	if (unlikely(!card)) {
+		dev_warn(mmc_dev(mmc), "card is not present\n");
+		return -EINVAL;
+	}
+
+	if (host->clock > DEFAULT_SDCLK_FREQ)
+		ret = xenon_hs_delay_adj(host, card);
+	return ret;
+}
+
+static int add_xenon_phy(struct device_node *np, struct sdhci_host *host,
+			 const char *phy_name)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	int i, ret;
+
+	for (i = 0; i < NR_PHY_TYPES; i++) {
+		if (!strcmp(phy_name, phy_types[i])) {
+			priv->phy_type = i;
+			break;
+		}
+	}
+	if (i == NR_PHY_TYPES) {
+		dev_err(mmc_dev(host->mmc),
+			"Unable to determine PHY name %s. Use default eMMC 5.1 PHY\n",
+			phy_name);
+		priv->phy_type = EMMC_5_1_PHY;
+	}
+
+	if (priv->phy_type == SDH_PHY) {
+		return alloc_sdh_phy(priv);
+	} else if ((priv->phy_type == EMMC_5_0_PHY) ||
+			(priv->phy_type == EMMC_5_1_PHY)) {
+		ret = alloc_emmc_phy(priv);
+		if (ret)
+			return ret;
+		return emmc_phy_parse_param_dt(host, np, priv->phy_params);
+	}
+
+	return -EINVAL;
+}
+
+int xenon_phy_parse_dt(struct device_node *np, struct sdhci_host *host)
+{
+	const char *phy_type = NULL;
+
+	if (!of_property_read_string(np, "xenon,phy-type", &phy_type))
+		return add_xenon_phy(np, host, phy_type);
+
+	dev_err(mmc_dev(host->mmc), "Fail to get Xenon PHY type. Use default eMMC 5.1 PHY\n");
+	return add_xenon_phy(np, host, "emmc 5.1 phy");
+}
diff --git a/drivers/mmc/host/sdhci-xenon-phy.h b/drivers/mmc/host/sdhci-xenon-phy.h
new file mode 100644
index 000000000000..4373c71d3b7b
--- /dev/null
+++ b/drivers/mmc/host/sdhci-xenon-phy.h
@@ -0,0 +1,157 @@
+/* linux/drivers/mmc/host/sdhci-xenon-phy.h
+ *
+ * Author:	Hu Ziji <huziji@marvell.com>
+ * Date:	2016-8-24
+ *
+ *  Copyright (C) 2016 Marvell, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+#ifndef SDHCI_XENON_PHY_H_
+#define SDHCI_XENON_PHY_H_
+
+#include <linux/types.h>
+#include "sdhci.h"
+
+/* Register base for eMMC PHY 5.0 Version */
+#define EMMC_5_0_PHY_REG_BASE			0x0160
+/* Register base for eMMC PHY 5.1 Version */
+#define EMMC_PHY_REG_BASE			0x0170
+
+#define EMMC_PHY_TIMING_ADJUST			EMMC_PHY_REG_BASE
+#define EMMC_5_0_PHY_TIMING_ADJUST		EMMC_5_0_PHY_REG_BASE
+#define TIMING_ADJUST_SLOW_MODE			BIT(29)
+#define TIMING_ADJUST_SDIO_MODE			BIT(28)
+#define OUTPUT_QSN_PHASE_SELECT			BIT(17)
+#define SAMPL_INV_QSP_PHASE_SELECT		BIT(18)
+#define SAMPL_INV_QSP_PHASE_SELECT_SHIFT	18
+#define PHY_INITIALIZAION			BIT(31)
+#define WAIT_CYCLE_BEFORE_USING_MASK		0xF
+#define WAIT_CYCLE_BEFORE_USING_SHIFT		12
+#define FC_SYNC_EN_DURATION_MASK		0xF
+#define FC_SYNC_EN_DURATION_SHIFT		8
+#define FC_SYNC_RST_EN_DURATION_MASK		0xF
+#define FC_SYNC_RST_EN_DURATION_SHIFT		4
+#define FC_SYNC_RST_DURATION_MASK		0xF
+#define FC_SYNC_RST_DURATION_SHIFT		0
+
+#define EMMC_PHY_FUNC_CONTROL			(EMMC_PHY_REG_BASE + 0x4)
+#define EMMC_5_0_PHY_FUNC_CONTROL		(EMMC_5_0_PHY_REG_BASE + 0x4)
+#define ASYNC_DDRMODE_MASK			BIT(23)
+#define ASYNC_DDRMODE_SHIFT			23
+#define CMD_DDR_MODE				BIT(16)
+#define DQ_DDR_MODE_SHIFT			8
+#define DQ_DDR_MODE_MASK			0xFF
+#define DQ_ASYNC_MODE				BIT(4)
+
+#define EMMC_PHY_PAD_CONTROL			(EMMC_PHY_REG_BASE + 0x8)
+#define EMMC_5_0_PHY_PAD_CONTROL		(EMMC_5_0_PHY_REG_BASE + 0x8)
+#define REC_EN_SHIFT				24
+#define REC_EN_MASK				0xF
+#define FC_DQ_RECEN				BIT(24)
+#define FC_CMD_RECEN				BIT(25)
+#define FC_QSP_RECEN				BIT(26)
+#define FC_QSN_RECEN				BIT(27)
+#define OEN_QSN					BIT(28)
+#define AUTO_RECEN_CTRL				BIT(30)
+#define FC_ALL_CMOS_RECEIVER			0xF000
+
+#define EMMC5_FC_QSP_PD				BIT(18)
+#define EMMC5_FC_QSP_PU				BIT(22)
+#define EMMC5_FC_CMD_PD				BIT(17)
+#define EMMC5_FC_CMD_PU				BIT(21)
+#define EMMC5_FC_DQ_PD				BIT(16)
+#define EMMC5_FC_DQ_PU				BIT(20)
+
+#define EMMC_PHY_PAD_CONTROL1			(EMMC_PHY_REG_BASE + 0xC)
+#define EMMC5_1_FC_QSP_PD			BIT(9)
+#define EMMC5_1_FC_QSP_PU			BIT(25)
+#define EMMC5_1_FC_CMD_PD			BIT(8)
+#define EMMC5_1_FC_CMD_PU			BIT(24)
+#define EMMC5_1_FC_DQ_PD			0xFF
+#define EMMC5_1_FC_DQ_PU			(0xFF << 16)
+
+#define EMMC_PHY_PAD_CONTROL2			(EMMC_PHY_REG_BASE + 0x10)
+#define EMMC_5_0_PHY_PAD_CONTROL2		(EMMC_5_0_PHY_REG_BASE + 0xC)
+#define ZNR_MASK				0x1F
+#define ZNR_SHIFT				8
+#define ZPR_MASK				0x1F
+/* Perferred ZNR and ZPR value vary between different boards.
+ * The specific ZNR and ZPR value should be defined here
+ * according to board actual timing.
+ */
+#define ZNR_DEF_VALUE				0xF
+#define ZPR_DEF_VALUE				0xF
+
+#define EMMC_PHY_DLL_CONTROL			(EMMC_PHY_REG_BASE + 0x14)
+#define EMMC_5_0_PHY_DLL_CONTROL		(EMMC_5_0_PHY_REG_BASE + 0x10)
+#define DLL_ENABLE				BIT(31)
+#define DLL_UPDATE_STROBE_5_0			BIT(30)
+#define DLL_REFCLK_SEL				BIT(30)
+#define DLL_UPDATE				BIT(23)
+#define DLL_PHSEL1_SHIFT			24
+#define DLL_PHSEL0_SHIFT			16
+#define DLL_PHASE_MASK				0x3F
+#define DLL_PHASE_90_DEGREE			0x1F
+#define DLL_FAST_LOCK				BIT(5)
+#define DLL_GAIN2X				BIT(3)
+#define DLL_BYPASS_EN				BIT(0)
+
+#define EMMC_5_0_PHY_LOGIC_TIMING_ADJUST	(EMMC_5_0_PHY_REG_BASE + 0x14)
+#define EMMC_PHY_LOGIC_TIMING_ADJUST		(EMMC_PHY_REG_BASE + 0x18)
+
+enum sampl_fix_delay_phase {
+	PHASE_0_DEGREE = 0x0,
+	PHASE_90_DEGREE = 0x1,
+	PHASE_180_DEGREE = 0x2,
+	PHASE_270_DEGREE = 0x3,
+};
+
+#define SDH_PHY_SLOT_DLL_CTRL			(0x0138)
+#define SDH_PHY_ENABLE_DLL			BIT(1)
+#define SDH_PHY_FAST_LOCK_EN			BIT(5)
+
+#define SDH_PHY_SLOT_DLL_PHASE_SEL		(0x013C)
+#define SDH_PHY_DLL_UPDATE_TUNING		BIT(15)
+
+enum soc_pad_ctrl_type {
+	SOC_PAD_SD,
+	SOC_PAD_FIXED_1_8V,
+};
+
+/*
+ * List offset of PHY registers and some special register values
+ * in eMMC PHY 5.0 or eMMC PHY 5.1
+ */
+struct xenon_emmc_phy_regs {
+	/* Offset of Timing Adjust register */
+	u16 timing_adj;
+	/* Offset of Func Control register */
+	u16 func_ctrl;
+	/* Offset of Pad Control register */
+	u16 pad_ctrl;
+	/* Offset of Pad Control register */
+	u16 pad_ctrl2;
+	/* Offset of DLL Control register */
+	u16 dll_ctrl;
+	/* Offset of Logic Timing Adjust register */
+	u16 logic_timing_adj;
+	/* Max value of eMMC Fixed Sampling Delay */
+	u32 delay_mask;
+	/* DLL Update Enable bit */
+	u32 dll_update;
+};
+
+struct xenon_phy_ops {
+	void (*strobe_delay_adj)(struct sdhci_host *host,
+				 struct mmc_card *card);
+	int (*fix_sampl_delay_adj)(struct sdhci_host *host,
+				   struct mmc_card *card);
+	void (*phy_set)(struct sdhci_host *host, unsigned char timing);
+	void (*set_soc_pad)(struct sdhci_host *host,
+			    unsigned char signal_voltage);
+};
+#endif
diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c
index 03ba183494d3..4d7d871544fc 100644
--- a/drivers/mmc/host/sdhci-xenon.c
+++ b/drivers/mmc/host/sdhci-xenon.c
@@ -224,6 +224,7 @@ static void xenon_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 	spin_unlock_irqrestore(&host->lock, flags);
 
 	sdhci_set_ios(mmc, ios);
+	xenon_phy_adj(host, ios);
 
 	if (host->clock > DEFAULT_SDCLK_FREQ) {
 		spin_lock_irqsave(&host->lock, flags);
@@ -309,6 +310,8 @@ static int xenon_start_signal_voltage_switch(struct mmc_host *mmc,
 	 */
 	enable_xenon_internal_clk(host);
 
+	xenon_soc_pad_ctrl(host, ios->signal_voltage);
+
 	if (priv->card_candidate) {
 		if (mmc_card_mmc(priv->card_candidate))
 			return xenon_emmc_signal_voltage_switch(mmc, ios);
@@ -453,6 +456,7 @@ static int xenon_probe_dt(struct platform_device *pdev)
 		sdhci_writel(host, reg, SDHC_SYS_EXT_OP_CTRL);
 	}
 
+	err = xenon_phy_parse_dt(np, host);
 	return err;
 }
 
diff --git a/drivers/mmc/host/sdhci-xenon.h b/drivers/mmc/host/sdhci-xenon.h
index c2370493fbe8..06e5261a563c 100644
--- a/drivers/mmc/host/sdhci-xenon.h
+++ b/drivers/mmc/host/sdhci-xenon.h
@@ -15,6 +15,7 @@
 #include <linux/mmc/card.h>
 #include <linux/of.h>
 #include "sdhci.h"
+#include "sdhci-xenon-phy.h"
 
 /* Register Offset of SD Host Controller SOCP self-defined register */
 #define SDHC_SYS_CFG_INFO			0x0104
@@ -76,6 +77,7 @@
 #define MMC_TIMING_FAKE				0xFF
 
 #define DEFAULT_SDCLK_FREQ			(400000)
+#define LOWEST_SDCLK_FREQ			(100000)
 
 /* Xenon specific Mode Select value */
 #define XENON_SDHCI_CTRL_HS200			0x5
@@ -97,6 +99,15 @@ struct sdhci_xenon_priv {
 	/* Slot idx */
 	u8		slot_idx;
 
+	int		phy_type;
+	/*
+	 * Contains board-specific PHY parameters
+	 * passed from device tree.
+	 */
+	void		*phy_params;
+	const struct xenon_phy_ops *phy_ops;
+	struct xenon_emmc_phy_regs *emmc_phy_regs;
+
 	/*
 	 * When initializing card, Xenon has to determine card type and
 	 * adjust Sampling Fixed delay.
@@ -131,4 +142,10 @@ static inline int enable_xenon_internal_clk(struct sdhci_host *host)
 
 	return 0;
 }
+
+int xenon_phy_adj(struct sdhci_host *host, struct mmc_ios *ios);
+int xenon_phy_parse_dt(struct device_node *np,
+		       struct sdhci_host *host);
+void xenon_soc_pad_ctrl(struct sdhci_host *host,
+			unsigned char signal_voltage);
 #endif
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH 8/10] arm64: dts: marvell: add eMMC support for Armada 37xx
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement@free-electrons.com>

Add the eMMC support for Armada 37xx SoC and enable it in the Armada 3720
DB board.

Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 arch/arm64/boot/dts/marvell/armada-3720-db.dts |  7 +++++++
 arch/arm64/boot/dts/marvell/armada-37xx.dtsi   | 11 +++++++++++
 2 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm64/boot/dts/marvell/armada-3720-db.dts b/arch/arm64/boot/dts/marvell/armada-3720-db.dts
index 1372e9a6aaa4..5b9cff4d5d7a 100644
--- a/arch/arm64/boot/dts/marvell/armada-3720-db.dts
+++ b/arch/arm64/boot/dts/marvell/armada-3720-db.dts
@@ -72,6 +72,13 @@
 	status = "okay";
 };
 
+&sdhci0 {
+	non-removable;
+	bus-width = <8>;
+	xenon,pad-type = "fixed-1-8v";
+	status = "okay";
+};
+
 /* CON31 */
 &usb3 {
 	status = "okay";
diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
index c4762538ec01..0c4cafe92e66 100644
--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
@@ -161,6 +161,17 @@
 				};
 			};
 
+			sdhci0: sdhci at d8000 {
+				compatible = "marvell,armada-3700-sdhci",
+				"marvell,sdhci-xenon";
+				reg = <0xd8000 0x300
+				       0x17808 0x4>;
+				interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
+				clocks = <&nb_perih_clk 0>;
+				clock-names = "core";
+				status = "disabled";
+			};
+
 			sata: sata at e0000 {
 				compatible = "marvell,armada-3700-ahci";
 				reg = <0xe0000 0x2000>;
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH 9/10] arm64: dts: marvell: add sdhci support for Armada 7K/8K
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement@free-electrons.com>

Also enable it on the Armada 7040 DB board

Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 arch/arm64/boot/dts/marvell/armada-7040-db.dts |  7 +++++++
 arch/arm64/boot/dts/marvell/armada-ap806.dtsi  |  9 +++++++++
 2 files changed, 16 insertions(+), 0 deletions(-)

diff --git a/arch/arm64/boot/dts/marvell/armada-7040-db.dts b/arch/arm64/boot/dts/marvell/armada-7040-db.dts
index 070b589680c5..f7f978a12a49 100644
--- a/arch/arm64/boot/dts/marvell/armada-7040-db.dts
+++ b/arch/arm64/boot/dts/marvell/armada-7040-db.dts
@@ -146,3 +146,10 @@
 &cpm_usb3_1 {
 	status = "okay";
 };
+
+&sdhci0 {
+	status = "okay";
+	bus-width = <4>;
+	no-1-8-v;
+	non-removable;
+};
diff --git a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
index 7b6136182ad0..ef2ce6be7205 100644
--- a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
@@ -229,6 +229,15 @@
 
 			};
 
+			sdhci0: sdhci at 6e0000 {
+				compatible = "marvell,sdhci-xenon";
+				reg = <0x6e0000 0x300>;
+				interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+				clock-names = "core";
+				clocks = <&cpm_syscon0 1 4>;
+				status = "disabled";
+			};
+
 			ap_syscon: system-controller at 6f4000 {
 				compatible = "marvell,ap806-system-controller",
 					     "syscon";
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCH 10/10] arm64: configs: enable SDHCI driver for Xenon
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement@free-electrons.com>

This patch enables the driver for the SDHCI controller found on the
Marvell Armada 3700 and 7K/8K ARM64 SoCs.

Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 arch/arm64/configs/defconfig | 1 +
 1 file changed, 1 insertion(+), 0 deletions(-)

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index dab2cb0c1f1c..2d1f5ee62b18 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -353,6 +353,7 @@ CONFIG_MMC_DW=y
 CONFIG_MMC_DW_EXYNOS=y
 CONFIG_MMC_DW_K3=y
 CONFIG_MMC_SUNXI=y
+CONFIG_MMC_SDHCI_XENON=y
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
 CONFIG_LEDS_GPIO=y
-- 
git-series 0.8.10

^ permalink raw reply related

* [PATCHv4 2/3] tty/serial: at91: fix hardware handshake with GPIOs
From: Richard Genoud @ 2016-10-07 15:26 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161004072525.josgoglunmvufcn2@pengutronix.de>

Le Tue, 4 Oct 2016 09:25:25 +0200,
Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de> a ?crit :

> On Fri, Sep 30, 2016 at 01:04:28PM +0200, Richard Genoud wrote:
> > 2016-09-30 11:12 GMT+02:00 Uwe Kleine-K?nig
> > <u.kleine-koenig@pengutronix.de>:
> > > Hello Richard,
> > >
> > > On Fri, Sep 30, 2016 at 10:58:00AM +0200, Richard Genoud wrote:
> > >> Commit 1cf6e8fc8341 ("tty/serial: at91: fix RTS line management
> > >> when hardware handshake is enabled") broke the hardware
> > >> handshake when GPIOs were used.
> > >>
> > >> Hardware handshake with GPIOs used to work before this commit
> > >> because the CRTSCTS flag (termios->c_cflag) was set, but not the
> > >> ATMEL_US_USMODE_HWHS flag (controller register) ; so hardware
> > >> handshake enabled, but not handled by the controller.
> > >
> > > What does the HWHS flag control? What if only RTS is a gpio and
> > > CTS is not? Or the other way round?
> > First, HWHS flag is used only in SAMA5D2. (if I correctly understood
> > Atmel HW guys,
> > all other platforms (sam9, sam9x5, sama5d3...) have this flag, but
> > it is unusable,
> > because they don't have Fifos nor PDC).
> > So, on SAMA5D2, the HWHS flag tells the controller to drive the RTS
> > pin according to
> > the number of char present in the rx fifo (cf Figure 44-29
> > ?44.7.3.15 p.1438 of
> > http://www.atmel.com/Images/Atmel-11267-32-bit-Cortex-A5-Microcontroller-SAMA5D2_Datasheet.pdf).
> > The controller will also start/stop the transmission on CTS
> > changes. But, as I haven't got this hard, I couldn't test it. (but
> > Cyrille did I guess). With this flag set, It's mandatory to have
> > CTS and RTS not handled via GPIO, because if they were, the
> > controller couldn't, well, control them.
> 
> Assuming the respective pin doesn't reach the hardware both are no
> problem though. And it would keep the driver simpler to just ignore
> this. There would be no need for patch 1 and also this patch could be
> dropped. So I guess there is really something to fix, otherwise you
> wouldn't start patching the driver. But I don't understand the issue,
> so I'd like to have a better picture.
> 
> Best regards
> Uwe
> 
Ok, so I'll try to explain what's going on.

I'm sure you're familiar with hardware handshaking (aka flow control),
but anyway, it doesn't hurt to explain it for the big picture
understanding.

On RS232, flow control is handled by 2 pins: RTS and CTS.
CTS is an input. When it's high (ttl), it indicates that we should stop
transmitting.
RTS is an output, we drive it at high level (ttl) to indicate that we
can't receive anymore data for now (because the rx buffer is full, or
the device is closed for instance).

The RTS and CTS management is done in serial_core.c which calls
atmel_set/get_mctrl to set/get RTS/CTS pin state.
And CTS changes are detected via an interrupt in atmel_interrupt().

Now, the atmel USART controller has a feature that can handle part of
the flow control job:
- disabling the transmitter when the CTS pin gets high
- drive the RTS pin high when the DMA buffer transfer is completed or
  PDC rx buffer full or rx FIFO is beyond threshold. (depending on the
  controller version).
This feature is enabled by setting the flag ATMEL_US_USMODE_HWHS.
And to be clear, this feature is *not* mandatory for the flow control to
work !

[ The fun part is that according to atmel designers, this feature is
broken for platforms with no PDC and no FIFO.
(source: https://lkml.org/lkml/2016/9/7/598 ) ]

Before commit 1cf6e8fc8341 ("tty/serial: at91: fix RTS line management
when hardware handshake is enabled"), this flag was never set.
Thus, the CTS/RTS where only handled by serial_core (and everything
worked just fine).

This commit introduced the use of the ATMEL_US_USMODE_HWHS (among other
things).
The logic introduced was to set the flag ATMEL_US_USMODE_HWHS for all
boards when the user space enables flow control.
This was clearly wrong because this feature can only be used when PDC
or FIFO are in use.

If ATMEL_US_USMODE_HWHS is enabled and PDC/FIFO/DMA are not used, the
RTS pin stays at high level no matter what, preventing any data to be
received.

Then, commit 5be605ac9af9 ("tty/serial: atmel: fix hardware handshake
selection") did something completely wrong:
Instead of removing the flag ATMEL_US_USMODE_HWHS for platforms with
no PDC nor FIFOs, and let serial_core happily handle the CTS/RTS, it
refused to set the flow control when the user space asked for it.
This was because of a misunderstanding:
It's not that platforms without PDC and FIFO can't do flow control at
all, they just can't enable the USART controller feature relative to
flag ATMEL_US_USMODE_HWHS.
And, again, this feature is *not* mandatory for the flow control to
work.

So, now, all atmel platforms with no PDC and no FIFO (SAM9G35, SAMA5D3,
etc.) can't use flow control anymore.


That's the reason of this patch series.


And as the controller flow control feature only works with PDC and
FIFOs, the logic would be to set the ATMEL_US_USMODE_HWHS like that:
if ((termios->c_cflag & CRTSCTS) &&
    !mctrl_gpio_use_rtscts(atmel_port->gpios) &&
    (atmel_use_pdc_rx(port) || atmel_use_fifo(port))) {
    mode |= ATMEL_US_USMODE_HWHS;
}

I added the mctrl_gpio_use_rtscts() test, because:
- There's no point using the ATMEL_US_USMODE_HWHS feature when CTS/RTS
  are driven by GPIOs, it doesn't make any sense.
- And it simply doesn't work because the transmitter is shut down.
(and that's the other reason of this patch series)

For the mctrl_gpio_use_rtscts() function, I agree that it may be atmel
specific, and I could implement as something like
atmel_use_{c,r}ts_gpio().

I'll have soon other hardware to test this on, and I'll resend a series
after that.

regards,
Richard

^ permalink raw reply

* [PATCH 1/2] ARM: imx: fix integer overflow in AV PLL round rate
From: Fabio Estevam @ 2016-10-07 15:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161007141224.GA18011@lime>

Hi Emil,

On Fri, Oct 7, 2016 at 11:12 AM, Emil Lundmark <emil@limesaudio.com> wrote:
> I realize that the two patches in this series does not actually depend on
> each other. This is my first contribution to Linux so I wonder if I should
> resubmit these as two separate patches instead?
>
> For example, what if the second patch in the series is not needed? Do you
> only accept the first patch then? Or what if I need to revise the second
> patch? It seems unnecessary to include the first patch in that case.
>
> I also got the threading wrong, but thats another story.

It is better to resend these two patches and mark them as v2:
[PATCH v2 1/2]
[PATCH v2 2/2]

Then put below the --- line what has changed from the previous one. If
nothing changed just put "None".

I am wondering if your patch series tries to fix the regression
reported by Ken Lin or is it unrelated?
http://lists.infradead.org/pipermail/linux-arm-kernel/2016-October/460451.html

^ permalink raw reply

* [PATCH V3 8/8] arm64: dma-mapping: Remove the notifier trick to handle early setting of dma_ops
From: Sricharan @ 2016-10-07 15:40 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475600632-21289-9-git-send-email-sricharan@codeaurora.org>

Hi,

>-----Original Message-----
>From: linux-arm-kernel [mailto:linux-arm-kernel-bounces at lists.infradead.org] On Behalf Of Sricharan R
>Sent: Tuesday, October 04, 2016 10:34 PM
>To: will.deacon at arm.com; robin.murphy at arm.com; joro at 8bytes.org; iommu at lists.linux-foundation.org; linux-arm-
>kernel at lists.infradead.org; linux-arm-msm at vger.kernel.org; laurent.pinchart at ideasonboard.com; m.szyprowski at samsung.com;
>tfiga at chromium.org; srinivas.kandagatla at linaro.org
>Cc: sricharan at codeaurora.org
>Subject: [PATCH V3 8/8] arm64: dma-mapping: Remove the notifier trick to handle early setting of dma_ops
>
>With arch_setup_dma_ops now being called late during device's probe after the
>device's iommu is probed, the notifier trick required to handle the early
>setup of dma_ops before the iommu group gets created is not required.
>So removing the notifier's here.
>
>Signed-off-by: Sricharan R <sricharan@codeaurora.org>
>---
> arch/arm64/mm/dma-mapping.c | 100 ++------------------------------------------
> 1 file changed, 3 insertions(+), 97 deletions(-)
>
>diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
>index faf4b92..eb593af 100644
>--- a/arch/arm64/mm/dma-mapping.c
>+++ b/arch/arm64/mm/dma-mapping.c
>@@ -799,24 +799,6 @@ static struct dma_map_ops iommu_dma_ops = {
> 	.mapping_error = iommu_dma_mapping_error,
> };
>
>-/*
>- * TODO: Right now __iommu_setup_dma_ops() gets called too early to do
>- * everything it needs to - the device is only partially created and the
>- * IOMMU driver hasn't seen it yet, so it can't have a group. Thus we
>- * need this delayed attachment dance. Once IOMMU probe ordering is sorted
>- * to move the arch_setup_dma_ops() call later, all the notifier bits below
>- * become unnecessary, and will go away.
>- */
>-struct iommu_dma_notifier_data {
>-	struct list_head list;
>-	struct device *dev;
>-	const struct iommu_ops *ops;
>-	u64 dma_base;
>-	u64 size;
>-};
>-static LIST_HEAD(iommu_dma_masters);
>-static DEFINE_MUTEX(iommu_dma_notifier_lock);
>-
> static bool do_iommu_attach(struct device *dev, const struct iommu_ops *ops,
> 			   u64 dma_base, u64 size)
> {
>@@ -837,79 +819,9 @@ static bool do_iommu_attach(struct device *dev, const struct iommu_ops *ops,
> 	return true;
> }
>
>-static void queue_iommu_attach(struct device *dev, const struct iommu_ops *ops,
>-			      u64 dma_base, u64 size)
>-{
>-	struct iommu_dma_notifier_data *iommudata;
>-
>-	iommudata = kzalloc(sizeof(*iommudata), GFP_KERNEL);
>-	if (!iommudata)
>-		return;
>-
>-	iommudata->dev = dev;
>-	iommudata->ops = ops;
>-	iommudata->dma_base = dma_base;
>-	iommudata->size = size;
>-
>-	mutex_lock(&iommu_dma_notifier_lock);
>-	list_add(&iommudata->list, &iommu_dma_masters);
>-	mutex_unlock(&iommu_dma_notifier_lock);
>-}
>-
>-static int __iommu_attach_notifier(struct notifier_block *nb,
>-				   unsigned long action, void *data)
>-{
>-	struct iommu_dma_notifier_data *master, *tmp;
>-
>-	if (action != BUS_NOTIFY_BIND_DRIVER)
>-		return 0;
>-
>-	mutex_lock(&iommu_dma_notifier_lock);
>-	list_for_each_entry_safe(master, tmp, &iommu_dma_masters, list) {
>-		if (data == master->dev && do_iommu_attach(master->dev,
>-				master->ops, master->dma_base, master->size)) {
>-			list_del(&master->list);
>-			kfree(master);
>-			break;
>-		}
>-	}
>-	mutex_unlock(&iommu_dma_notifier_lock);
>-	return 0;
>-}
>-
>-static int __init register_iommu_dma_ops_notifier(struct bus_type *bus)
>-{
>-	struct notifier_block *nb = kzalloc(sizeof(*nb), GFP_KERNEL);
>-	int ret;
>-
>-	if (!nb)
>-		return -ENOMEM;
>-
>-	nb->notifier_call = __iommu_attach_notifier;
>-
>-	ret = bus_register_notifier(bus, nb);
>-	if (ret) {
>-		pr_warn("Failed to register DMA domain notifier; IOMMU DMA ops unavailable on bus '%s'\n",
>-			bus->name);
>-		kfree(nb);
>-	}
>-	return ret;
>-}
>-
> static int __init __iommu_dma_init(void)
> {
>-	int ret;
>-
>-	ret = iommu_dma_init();
>-	if (!ret)
>-		ret = register_iommu_dma_ops_notifier(&platform_bus_type);
>-	if (!ret)
>-		ret = register_iommu_dma_ops_notifier(&amba_bustype);
>-#ifdef CONFIG_PCI
>-	if (!ret)
>-		ret = register_iommu_dma_ops_notifier(&pci_bus_type);
>-#endif
>-	return ret;
>+	return iommu_dma_init();
> }
> arch_initcall(__iommu_dma_init);
>
>@@ -920,18 +832,12 @@ static void __iommu_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
>
> 	if (!ops)
> 		return;
>-	/*
>-	 * TODO: As a concession to the future, we're ready to handle being
>-	 * called both early and late (i.e. after bus_add_device). Once all
>-	 * the platform bus code is reworked to call us late and the notifier
>-	 * junk above goes away, move the body of do_iommu_attach here.
>-	 */
>+
> 	group = iommu_group_get(dev);
>+
> 	if (group) {
> 		do_iommu_attach(dev, ops, dma_base, size);
> 		iommu_group_put(group);
>-	} else {
>-		queue_iommu_attach(dev, ops, dma_base, size);
> 	}
> }
>
I should have has this as well for being removed,

From: Sricharan R <sricharan@codeaurora.org>
Date: Fri, 7 Oct 2016 19:20:21 +0530
Subject: [PATCH] iommu:/arm-smmu: Avoid early iommu device registration

of_platform_device_create was called early in the init
to have the smmu probed before the master. But now with
the probe deferral, this is not needed.

Signed-off-by: Sricharan R <sricharan@codeaurora.org>
---
 drivers/iommu/arm-smmu.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index c841eb7..083489e4 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -2066,9 +2066,6 @@ static int __init arm_smmu_of_init(struct device_node *np)
 	if (ret)
 		return ret;
 
-	if (!of_platform_device_create(np, NULL, platform_bus_type.dev_root))
-		return -ENODEV;
-
 	return 0;
 }
 IOMMU_OF_DECLARE(arm_smmuv1, "arm,smmu-v1", arm_smmu_of_init);
-- 
1.8.2.1

Regards,
 Sricharan

^ permalink raw reply related

* [PATCH 1/3] mtd: s3c2410: make ecc mode configurable via platform data
From: Krzysztof Kozlowski @ 2016-10-07 15:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475711217-974-2-git-send-email-sergio.prado@e-labworks.com>

On Wed, Oct 05, 2016 at 08:46:55PM -0300, Sergio Prado wrote:
> Removing CONFIG_MTD_NAND_S3C2410_HWECC option and adding a ecc_mode
> field in the drivers's platform data structure so it can be selectable
> via platform data.
> 
> Also setting this field to NAND_ECC_SOFT in all boards using this
> driver since none of them had CONFIG_MTD_NAND_S3C2410_HWECC enabled.
> 
> Signed-off-by: Sergio Prado <sergio.prado@e-labworks.com>
> ---
>  arch/arm/mach-s3c24xx/common-smdk.c            |   1 +
>  arch/arm/mach-s3c24xx/mach-anubis.c            |   1 +
>  arch/arm/mach-s3c24xx/mach-at2440evb.c         |   1 +
>  arch/arm/mach-s3c24xx/mach-bast.c              |   1 +
>  arch/arm/mach-s3c24xx/mach-gta02.c             |   1 +
>  arch/arm/mach-s3c24xx/mach-jive.c              |   1 +
>  arch/arm/mach-s3c24xx/mach-mini2440.c          |   1 +
>  arch/arm/mach-s3c24xx/mach-osiris.c            |   1 +
>  arch/arm/mach-s3c24xx/mach-qt2410.c            |   1 +
>  arch/arm/mach-s3c24xx/mach-rx1950.c            |   1 +
>  arch/arm/mach-s3c24xx/mach-rx3715.c            |   1 +
>  arch/arm/mach-s3c24xx/mach-vstms.c             |   1 +
>  arch/arm/mach-s3c64xx/mach-hmt.c               |   1 +
>  arch/arm/mach-s3c64xx/mach-mini6410.c          |   1 +
>  arch/arm/mach-s3c64xx/mach-real6410.c          |   1 +
>  drivers/mtd/nand/Kconfig                       |   9 --
>  drivers/mtd/nand/s3c2410.c                     | 119 +++++++++++++------------
>  include/linux/platform_data/mtd-nand-s3c2410.h |   6 +-
>  18 files changed, 79 insertions(+), 70 deletions(-)
>


Acked-by: Krzysztof Kozlowski <krzk@kernel.org>

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH 1/2] ARM: imx: fix integer overflow in AV PLL round rate
From: Emil Lundmark @ 2016-10-07 16:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAOMZO5CYST3fw1q11RidmSMepsUpaHg91T5gD_c6XA6vsAsJLQ@mail.gmail.com>

On Fri, Oct 07, 2016 at 12:34:42PM -0300, Fabio Estevam wrote:
> Hi Emil,
> 
> On Fri, Oct 7, 2016 at 11:12 AM, Emil Lundmark <emil@limesaudio.com> wrote:
> > I realize that the two patches in this series does not actually depend on
> > each other. This is my first contribution to Linux so I wonder if I should
> > resubmit these as two separate patches instead?
> >
> > For example, what if the second patch in the series is not needed? Do you
> > only accept the first patch then? Or what if I need to revise the second
> > patch? It seems unnecessary to include the first patch in that case.
> >
> > I also got the threading wrong, but thats another story.
> 
> It is better to resend these two patches and mark them as v2:
> [PATCH v2 1/2]
> [PATCH v2 2/2]
> 
> Then put below the --- line what has changed from the previous one. If
> nothing changed just put "None".
> 
> I am wondering if your patch series tries to fix the regression
> reported by Ken Lin or is it unrelated?
> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-October/460451.html

I was not aware of that issue before but it seems related. Only the first
patch in the series is relevant for solving the issue I was experiencing.
I can't tell if it also solves theirs, but it's probable.

The second part is really only nitpicking I discovered when debugging. Its
intention is to allow a precision of 1 Hz instead of (most likely) 24 Hz.
But when is that important for MHz clocks anyway?

-- 
Emil Lundmark

^ permalink raw reply

* [PATCH 1/5] PCI: aardvark: Name private struct pointer "advk" consistently
From: Bjorn Helgaas @ 2016-10-07 16:20 UTC (permalink / raw)
  To: linux-arm-kernel

Use a device-specific name, "advk", for struct advk_pcie pointers to hint
that this is device-specific information.  No functional change intended.

Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
---
 drivers/pci/host/pci-aardvark.c |  370 +++++++++++++++++++--------------------
 1 file changed, 183 insertions(+), 187 deletions(-)

diff --git a/drivers/pci/host/pci-aardvark.c b/drivers/pci/host/pci-aardvark.c
index e4a5b7e..fd0e6af 100644
--- a/drivers/pci/host/pci-aardvark.c
+++ b/drivers/pci/host/pci-aardvark.c
@@ -209,40 +209,40 @@ struct advk_pcie {
 	int root_bus_nr;
 };
 
-static inline void advk_writel(struct advk_pcie *pcie, u32 val, u64 reg)
+static inline void advk_writel(struct advk_pcie *advk, u32 val, u64 reg)
 {
-	writel(val, pcie->base + reg);
+	writel(val, advk->base + reg);
 }
 
-static inline u32 advk_readl(struct advk_pcie *pcie, u64 reg)
+static inline u32 advk_readl(struct advk_pcie *advk, u64 reg)
 {
-	return readl(pcie->base + reg);
+	return readl(advk->base + reg);
 }
 
-static int advk_pcie_link_up(struct advk_pcie *pcie)
+static int advk_pcie_link_up(struct advk_pcie *advk)
 {
 	u32 val, ltssm_state;
 
-	val = advk_readl(pcie, CFG_REG);
+	val = advk_readl(advk, CFG_REG);
 	ltssm_state = (val >> LTSSM_SHIFT) & LTSSM_MASK;
 	return ltssm_state >= LTSSM_L0;
 }
 
-static int advk_pcie_wait_for_link(struct advk_pcie *pcie)
+static int advk_pcie_wait_for_link(struct advk_pcie *advk)
 {
 	int retries;
 
 	/* check if the link is up or not */
 	for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
-		if (advk_pcie_link_up(pcie)) {
-			dev_info(&pcie->pdev->dev, "link up\n");
+		if (advk_pcie_link_up(advk)) {
+			dev_info(&advk->pdev->dev, "link up\n");
 			return 0;
 		}
 
 		usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
 	}
 
-	dev_err(&pcie->pdev->dev, "link never came up\n");
+	dev_err(&advk->pdev->dev, "link never came up\n");
 
 	return -ETIMEDOUT;
 }
@@ -251,136 +251,136 @@ static int advk_pcie_wait_for_link(struct advk_pcie *pcie)
  * Set PCIe address window register which could be used for memory
  * mapping.
  */
-static void advk_pcie_set_ob_win(struct advk_pcie *pcie,
+static void advk_pcie_set_ob_win(struct advk_pcie *advk,
 				 u32 win_num, u32 match_ms,
 				 u32 match_ls, u32 mask_ms,
 				 u32 mask_ls, u32 remap_ms,
 				 u32 remap_ls, u32 action)
 {
-	advk_writel(pcie, match_ls, OB_WIN_MATCH_LS(win_num));
-	advk_writel(pcie, match_ms, OB_WIN_MATCH_MS(win_num));
-	advk_writel(pcie, mask_ms, OB_WIN_MASK_MS(win_num));
-	advk_writel(pcie, mask_ls, OB_WIN_MASK_LS(win_num));
-	advk_writel(pcie, remap_ms, OB_WIN_REMAP_MS(win_num));
-	advk_writel(pcie, remap_ls, OB_WIN_REMAP_LS(win_num));
-	advk_writel(pcie, action, OB_WIN_ACTIONS(win_num));
-	advk_writel(pcie, match_ls | BIT(0), OB_WIN_MATCH_LS(win_num));
+	advk_writel(advk, match_ls, OB_WIN_MATCH_LS(win_num));
+	advk_writel(advk, match_ms, OB_WIN_MATCH_MS(win_num));
+	advk_writel(advk, mask_ms, OB_WIN_MASK_MS(win_num));
+	advk_writel(advk, mask_ls, OB_WIN_MASK_LS(win_num));
+	advk_writel(advk, remap_ms, OB_WIN_REMAP_MS(win_num));
+	advk_writel(advk, remap_ls, OB_WIN_REMAP_LS(win_num));
+	advk_writel(advk, action, OB_WIN_ACTIONS(win_num));
+	advk_writel(advk, match_ls | BIT(0), OB_WIN_MATCH_LS(win_num));
 }
 
-static void advk_pcie_setup_hw(struct advk_pcie *pcie)
+static void advk_pcie_setup_hw(struct advk_pcie *advk)
 {
 	u32 reg;
 	int i;
 
 	/* Point PCIe unit MBUS decode windows to DRAM space */
 	for (i = 0; i < 8; i++)
-		advk_pcie_set_ob_win(pcie, i, 0, 0, 0, 0, 0, 0, 0);
+		advk_pcie_set_ob_win(advk, i, 0, 0, 0, 0, 0, 0, 0);
 
 	/* Set to Direct mode */
-	reg = advk_readl(pcie, CTRL_CONFIG_REG);
+	reg = advk_readl(advk, CTRL_CONFIG_REG);
 	reg &= ~(CTRL_MODE_MASK << CTRL_MODE_SHIFT);
 	reg |= ((PCIE_CORE_MODE_DIRECT & CTRL_MODE_MASK) << CTRL_MODE_SHIFT);
-	advk_writel(pcie, reg, CTRL_CONFIG_REG);
+	advk_writel(advk, reg, CTRL_CONFIG_REG);
 
 	/* Set PCI global control register to RC mode */
-	reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
+	reg = advk_readl(advk, PCIE_CORE_CTRL0_REG);
 	reg |= (IS_RC_MSK << IS_RC_SHIFT);
-	advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);
+	advk_writel(advk, reg, PCIE_CORE_CTRL0_REG);
 
 	/* Set Advanced Error Capabilities and Control PF0 register */
 	reg = PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX |
 		PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN |
 		PCIE_CORE_ERR_CAPCTL_ECRC_CHCK |
 		PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV;
-	advk_writel(pcie, reg, PCIE_CORE_ERR_CAPCTL_REG);
+	advk_writel(advk, reg, PCIE_CORE_ERR_CAPCTL_REG);
 
 	/* Set PCIe Device Control and Status 1 PF0 register */
 	reg = PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE |
 		(7 << PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT) |
 		PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE |
 		PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT;
-	advk_writel(pcie, reg, PCIE_CORE_DEV_CTRL_STATS_REG);
+	advk_writel(advk, reg, PCIE_CORE_DEV_CTRL_STATS_REG);
 
 	/* Program PCIe Control 2 to disable strict ordering */
 	reg = PCIE_CORE_CTRL2_RESERVED |
 		PCIE_CORE_CTRL2_TD_ENABLE;
-	advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG);
+	advk_writel(advk, reg, PCIE_CORE_CTRL2_REG);
 
 	/* Set GEN2 */
-	reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
+	reg = advk_readl(advk, PCIE_CORE_CTRL0_REG);
 	reg &= ~PCIE_GEN_SEL_MSK;
 	reg |= SPEED_GEN_2;
-	advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);
+	advk_writel(advk, reg, PCIE_CORE_CTRL0_REG);
 
 	/* Set lane X1 */
-	reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
+	reg = advk_readl(advk, PCIE_CORE_CTRL0_REG);
 	reg &= ~LANE_CNT_MSK;
 	reg |= LANE_COUNT_1;
-	advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);
+	advk_writel(advk, reg, PCIE_CORE_CTRL0_REG);
 
 	/* Enable link training */
-	reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
+	reg = advk_readl(advk, PCIE_CORE_CTRL0_REG);
 	reg |= LINK_TRAINING_EN;
-	advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);
+	advk_writel(advk, reg, PCIE_CORE_CTRL0_REG);
 
 	/* Enable MSI */
-	reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG);
+	reg = advk_readl(advk, PCIE_CORE_CTRL2_REG);
 	reg |= PCIE_CORE_CTRL2_MSI_ENABLE;
-	advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG);
+	advk_writel(advk, reg, PCIE_CORE_CTRL2_REG);
 
 	/* Clear all interrupts */
-	advk_writel(pcie, PCIE_ISR0_ALL_MASK, PCIE_ISR0_REG);
-	advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG);
-	advk_writel(pcie, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
+	advk_writel(advk, PCIE_ISR0_ALL_MASK, PCIE_ISR0_REG);
+	advk_writel(advk, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG);
+	advk_writel(advk, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
 
 	/* Disable All ISR0/1 Sources */
 	reg = PCIE_ISR0_ALL_MASK;
 	reg &= ~PCIE_ISR0_MSI_INT_PENDING;
-	advk_writel(pcie, reg, PCIE_ISR0_MASK_REG);
+	advk_writel(advk, reg, PCIE_ISR0_MASK_REG);
 
-	advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG);
+	advk_writel(advk, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG);
 
 	/* Unmask all MSI's */
-	advk_writel(pcie, 0, PCIE_MSI_MASK_REG);
+	advk_writel(advk, 0, PCIE_MSI_MASK_REG);
 
 	/* Enable summary interrupt for GIC SPI source */
 	reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK);
-	advk_writel(pcie, reg, HOST_CTRL_INT_MASK_REG);
+	advk_writel(advk, reg, HOST_CTRL_INT_MASK_REG);
 
-	reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG);
+	reg = advk_readl(advk, PCIE_CORE_CTRL2_REG);
 	reg |= PCIE_CORE_CTRL2_OB_WIN_ENABLE;
-	advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG);
+	advk_writel(advk, reg, PCIE_CORE_CTRL2_REG);
 
 	/* Bypass the address window mapping for PIO */
-	reg = advk_readl(pcie, PIO_CTRL);
+	reg = advk_readl(advk, PIO_CTRL);
 	reg |= PIO_CTRL_ADDR_WIN_DISABLE;
-	advk_writel(pcie, reg, PIO_CTRL);
+	advk_writel(advk, reg, PIO_CTRL);
 
 	/* Start link training */
-	reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG);
+	reg = advk_readl(advk, PCIE_CORE_LINK_CTRL_STAT_REG);
 	reg |= PCIE_CORE_LINK_TRAINING;
-	advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG);
+	advk_writel(advk, reg, PCIE_CORE_LINK_CTRL_STAT_REG);
 
-	advk_pcie_wait_for_link(pcie);
+	advk_pcie_wait_for_link(advk);
 
 	reg = PCIE_CORE_LINK_L0S_ENTRY |
 		(1 << PCIE_CORE_LINK_WIDTH_SHIFT);
-	advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG);
+	advk_writel(advk, reg, PCIE_CORE_LINK_CTRL_STAT_REG);
 
-	reg = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG);
+	reg = advk_readl(advk, PCIE_CORE_CMD_STATUS_REG);
 	reg |= PCIE_CORE_CMD_MEM_ACCESS_EN |
 		PCIE_CORE_CMD_IO_ACCESS_EN |
 		PCIE_CORE_CMD_MEM_IO_REQ_EN;
-	advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG);
+	advk_writel(advk, reg, PCIE_CORE_CMD_STATUS_REG);
 }
 
-static void advk_pcie_check_pio_status(struct advk_pcie *pcie)
+static void advk_pcie_check_pio_status(struct advk_pcie *advk)
 {
 	u32 reg;
 	unsigned int status;
 	char *strcomp_status, *str_posted;
 
-	reg = advk_readl(pcie, PIO_STAT);
+	reg = advk_readl(advk, PIO_STAT);
 	status = (reg & PIO_COMPLETION_STATUS_MASK) >>
 		PIO_COMPLETION_STATUS_SHIFT;
 
@@ -407,11 +407,11 @@ static void advk_pcie_check_pio_status(struct advk_pcie *pcie)
 	else
 		str_posted = "Posted";
 
-	dev_err(&pcie->pdev->dev, "%s PIO Response Status: %s, %#x @ %#x\n",
-		str_posted, strcomp_status, reg, advk_readl(pcie, PIO_ADDR_LS));
+	dev_err(&advk->pdev->dev, "%s PIO Response Status: %s, %#x @ %#x\n",
+		str_posted, strcomp_status, reg, advk_readl(advk, PIO_ADDR_LS));
 }
 
-static int advk_pcie_wait_pio(struct advk_pcie *pcie)
+static int advk_pcie_wait_pio(struct advk_pcie *advk)
 {
 	unsigned long timeout;
 
@@ -420,20 +420,20 @@ static int advk_pcie_wait_pio(struct advk_pcie *pcie)
 	while (time_before(jiffies, timeout)) {
 		u32 start, isr;
 
-		start = advk_readl(pcie, PIO_START);
-		isr = advk_readl(pcie, PIO_ISR);
+		start = advk_readl(advk, PIO_START);
+		isr = advk_readl(advk, PIO_ISR);
 		if (!start && isr)
 			return 0;
 	}
 
-	dev_err(&pcie->pdev->dev, "config read/write timed out\n");
+	dev_err(&advk->pdev->dev, "config read/write timed out\n");
 	return -ETIMEDOUT;
 }
 
 static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,
 			     int where, int size, u32 *val)
 {
-	struct advk_pcie *pcie = bus->sysdata;
+	struct advk_pcie *advk = bus->sysdata;
 	u32 reg;
 	int ret;
 
@@ -443,37 +443,37 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,
 	}
 
 	/* Start PIO */
-	advk_writel(pcie, 0, PIO_START);
-	advk_writel(pcie, 1, PIO_ISR);
+	advk_writel(advk, 0, PIO_START);
+	advk_writel(advk, 1, PIO_ISR);
 
 	/* Program the control register */
-	reg = advk_readl(pcie, PIO_CTRL);
+	reg = advk_readl(advk, PIO_CTRL);
 	reg &= ~PIO_CTRL_TYPE_MASK;
-	if (bus->number ==  pcie->root_bus_nr)
+	if (bus->number ==  advk->root_bus_nr)
 		reg |= PCIE_CONFIG_RD_TYPE0;
 	else
 		reg |= PCIE_CONFIG_RD_TYPE1;
-	advk_writel(pcie, reg, PIO_CTRL);
+	advk_writel(advk, reg, PIO_CTRL);
 
 	/* Program the address registers */
 	reg = PCIE_BDF(devfn) | PCIE_CONF_REG(where);
-	advk_writel(pcie, reg, PIO_ADDR_LS);
-	advk_writel(pcie, 0, PIO_ADDR_MS);
+	advk_writel(advk, reg, PIO_ADDR_LS);
+	advk_writel(advk, 0, PIO_ADDR_MS);
 
 	/* Program the data strobe */
-	advk_writel(pcie, 0xf, PIO_WR_DATA_STRB);
+	advk_writel(advk, 0xf, PIO_WR_DATA_STRB);
 
 	/* Start the transfer */
-	advk_writel(pcie, 1, PIO_START);
+	advk_writel(advk, 1, PIO_START);
 
-	ret = advk_pcie_wait_pio(pcie);
+	ret = advk_pcie_wait_pio(advk);
 	if (ret < 0)
 		return PCIBIOS_SET_FAILED;
 
-	advk_pcie_check_pio_status(pcie);
+	advk_pcie_check_pio_status(advk);
 
 	/* Get the read result */
-	*val = advk_readl(pcie, PIO_RD_DATA);
+	*val = advk_readl(advk, PIO_RD_DATA);
 	if (size == 1)
 		*val = (*val >> (8 * (where & 3))) & 0xff;
 	else if (size == 2)
@@ -485,7 +485,7 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,
 static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 				int where, int size, u32 val)
 {
-	struct advk_pcie *pcie = bus->sysdata;
+	struct advk_pcie *advk = bus->sysdata;
 	u32 reg;
 	u32 data_strobe = 0x0;
 	int offset;
@@ -498,22 +498,22 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 		return PCIBIOS_SET_FAILED;
 
 	/* Start PIO */
-	advk_writel(pcie, 0, PIO_START);
-	advk_writel(pcie, 1, PIO_ISR);
+	advk_writel(advk, 0, PIO_START);
+	advk_writel(advk, 1, PIO_ISR);
 
 	/* Program the control register */
-	reg = advk_readl(pcie, PIO_CTRL);
+	reg = advk_readl(advk, PIO_CTRL);
 	reg &= ~PIO_CTRL_TYPE_MASK;
-	if (bus->number == pcie->root_bus_nr)
+	if (bus->number == advk->root_bus_nr)
 		reg |= PCIE_CONFIG_WR_TYPE0;
 	else
 		reg |= PCIE_CONFIG_WR_TYPE1;
-	advk_writel(pcie, reg, PIO_CTRL);
+	advk_writel(advk, reg, PIO_CTRL);
 
 	/* Program the address registers */
 	reg = PCIE_CONF_ADDR(bus->number, devfn, where);
-	advk_writel(pcie, reg, PIO_ADDR_LS);
-	advk_writel(pcie, 0, PIO_ADDR_MS);
+	advk_writel(advk, reg, PIO_ADDR_LS);
+	advk_writel(advk, 0, PIO_ADDR_MS);
 
 	/* Calculate the write strobe */
 	offset      = where & 0x3;
@@ -521,19 +521,19 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 	data_strobe = GENMASK(size - 1, 0) << offset;
 
 	/* Program the data register */
-	advk_writel(pcie, reg, PIO_WR_DATA);
+	advk_writel(advk, reg, PIO_WR_DATA);
 
 	/* Program the data strobe */
-	advk_writel(pcie, data_strobe, PIO_WR_DATA_STRB);
+	advk_writel(advk, data_strobe, PIO_WR_DATA_STRB);
 
 	/* Start the transfer */
-	advk_writel(pcie, 1, PIO_START);
+	advk_writel(advk, 1, PIO_START);
 
-	ret = advk_pcie_wait_pio(pcie);
+	ret = advk_pcie_wait_pio(advk);
 	if (ret < 0)
 		return PCIBIOS_SET_FAILED;
 
-	advk_pcie_check_pio_status(pcie);
+	advk_pcie_check_pio_status(advk);
 
 	return PCIBIOS_SUCCESSFUL;
 }
@@ -543,37 +543,37 @@ static struct pci_ops advk_pcie_ops = {
 	.write = advk_pcie_wr_conf,
 };
 
-static int advk_pcie_alloc_msi(struct advk_pcie *pcie)
+static int advk_pcie_alloc_msi(struct advk_pcie *advk)
 {
 	int hwirq;
 
-	mutex_lock(&pcie->msi_used_lock);
-	hwirq = find_first_zero_bit(pcie->msi_irq_in_use, MSI_IRQ_NUM);
+	mutex_lock(&advk->msi_used_lock);
+	hwirq = find_first_zero_bit(advk->msi_irq_in_use, MSI_IRQ_NUM);
 	if (hwirq >= MSI_IRQ_NUM)
 		hwirq = -ENOSPC;
 	else
-		set_bit(hwirq, pcie->msi_irq_in_use);
-	mutex_unlock(&pcie->msi_used_lock);
+		set_bit(hwirq, advk->msi_irq_in_use);
+	mutex_unlock(&advk->msi_used_lock);
 
 	return hwirq;
 }
 
-static void advk_pcie_free_msi(struct advk_pcie *pcie, int hwirq)
+static void advk_pcie_free_msi(struct advk_pcie *advk, int hwirq)
 {
-	mutex_lock(&pcie->msi_used_lock);
-	if (!test_bit(hwirq, pcie->msi_irq_in_use))
-		dev_err(&pcie->pdev->dev, "trying to free unused MSI#%d\n",
+	mutex_lock(&advk->msi_used_lock);
+	if (!test_bit(hwirq, advk->msi_irq_in_use))
+		dev_err(&advk->pdev->dev, "trying to free unused MSI#%d\n",
 			hwirq);
 	else
-		clear_bit(hwirq, pcie->msi_irq_in_use);
-	mutex_unlock(&pcie->msi_used_lock);
+		clear_bit(hwirq, advk->msi_irq_in_use);
+	mutex_unlock(&advk->msi_used_lock);
 }
 
 static int advk_pcie_setup_msi_irq(struct msi_controller *chip,
 				   struct pci_dev *pdev,
 				   struct msi_desc *desc)
 {
-	struct advk_pcie *pcie = pdev->bus->sysdata;
+	struct advk_pcie *advk = pdev->bus->sysdata;
 	struct msi_msg msg;
 	int virq, hwirq;
 	phys_addr_t msi_msg_phys;
@@ -582,19 +582,19 @@ static int advk_pcie_setup_msi_irq(struct msi_controller *chip,
 	if (desc->msi_attrib.is_msix)
 		return -EINVAL;
 
-	hwirq = advk_pcie_alloc_msi(pcie);
+	hwirq = advk_pcie_alloc_msi(advk);
 	if (hwirq < 0)
 		return hwirq;
 
-	virq = irq_create_mapping(pcie->msi_domain, hwirq);
+	virq = irq_create_mapping(advk->msi_domain, hwirq);
 	if (!virq) {
-		advk_pcie_free_msi(pcie, hwirq);
+		advk_pcie_free_msi(advk, hwirq);
 		return -EINVAL;
 	}
 
 	irq_set_msi_desc(virq, desc);
 
-	msi_msg_phys = virt_to_phys(&pcie->msi_msg);
+	msi_msg_phys = virt_to_phys(&advk->msi_msg);
 
 	msg.address_lo = lower_32_bits(msi_msg_phys);
 	msg.address_hi = upper_32_bits(msi_msg_phys);
@@ -610,19 +610,19 @@ static void advk_pcie_teardown_msi_irq(struct msi_controller *chip,
 {
 	struct irq_data *d = irq_get_irq_data(irq);
 	struct msi_desc *msi = irq_data_get_msi_desc(d);
-	struct advk_pcie *pcie = msi_desc_to_pci_sysdata(msi);
+	struct advk_pcie *advk = msi_desc_to_pci_sysdata(msi);
 	unsigned long hwirq = d->hwirq;
 
 	irq_dispose_mapping(irq);
-	advk_pcie_free_msi(pcie, hwirq);
+	advk_pcie_free_msi(advk, hwirq);
 }
 
 static int advk_pcie_msi_map(struct irq_domain *domain,
 			     unsigned int virq, irq_hw_number_t hw)
 {
-	struct advk_pcie *pcie = domain->host_data;
+	struct advk_pcie *advk = domain->host_data;
 
-	irq_set_chip_and_handler(virq, &pcie->msi_irq_chip,
+	irq_set_chip_and_handler(virq, &advk->msi_irq_chip,
 				 handle_simple_irq);
 
 	return 0;
@@ -634,36 +634,35 @@ static const struct irq_domain_ops advk_pcie_msi_irq_ops = {
 
 static void advk_pcie_irq_mask(struct irq_data *d)
 {
-	struct advk_pcie *pcie = d->domain->host_data;
+	struct advk_pcie *advk = d->domain->host_data;
 	irq_hw_number_t hwirq = irqd_to_hwirq(d);
 	u32 mask;
 
-	mask = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+	mask = advk_readl(advk, PCIE_ISR0_MASK_REG);
 	mask |= PCIE_ISR0_INTX_ASSERT(hwirq);
-	advk_writel(pcie, mask, PCIE_ISR0_MASK_REG);
+	advk_writel(advk, mask, PCIE_ISR0_MASK_REG);
 }
 
 static void advk_pcie_irq_unmask(struct irq_data *d)
 {
-	struct advk_pcie *pcie = d->domain->host_data;
+	struct advk_pcie *advk = d->domain->host_data;
 	irq_hw_number_t hwirq = irqd_to_hwirq(d);
 	u32 mask;
 
-	mask = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+	mask = advk_readl(advk, PCIE_ISR0_MASK_REG);
 	mask &= ~PCIE_ISR0_INTX_ASSERT(hwirq);
-	advk_writel(pcie, mask, PCIE_ISR0_MASK_REG);
+	advk_writel(advk, mask, PCIE_ISR0_MASK_REG);
 }
 
 static int advk_pcie_irq_map(struct irq_domain *h,
 			     unsigned int virq, irq_hw_number_t hwirq)
 {
-	struct advk_pcie *pcie = h->host_data;
+	struct advk_pcie *advk = h->host_data;
 
 	advk_pcie_irq_mask(irq_get_irq_data(virq));
 	irq_set_status_flags(virq, IRQ_LEVEL);
-	irq_set_chip_and_handler(virq, &pcie->irq_chip,
-				 handle_level_irq);
-	irq_set_chip_data(virq, pcie);
+	irq_set_chip_and_handler(virq, &advk->irq_chip, handle_level_irq);
+	irq_set_chip_data(virq, advk);
 
 	return 0;
 }
@@ -673,16 +672,16 @@ static const struct irq_domain_ops advk_pcie_irq_domain_ops = {
 	.xlate = irq_domain_xlate_onecell,
 };
 
-static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
+static int advk_pcie_init_msi_irq_domain(struct advk_pcie *advk)
 {
-	struct device *dev = &pcie->pdev->dev;
+	struct device *dev = &advk->pdev->dev;
 	struct device_node *node = dev->of_node;
 	struct irq_chip *msi_irq_chip;
 	struct msi_controller *msi;
 	phys_addr_t msi_msg_phys;
 	int ret;
 
-	msi_irq_chip = &pcie->msi_irq_chip;
+	msi_irq_chip = &advk->msi_irq_chip;
 
 	msi_irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-msi",
 					    dev_name(dev));
@@ -694,45 +693,43 @@ static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
 	msi_irq_chip->irq_mask = pci_msi_mask_irq;
 	msi_irq_chip->irq_unmask = pci_msi_unmask_irq;
 
-	msi = &pcie->msi;
+	msi = &advk->msi;
 
 	msi->setup_irq = advk_pcie_setup_msi_irq;
 	msi->teardown_irq = advk_pcie_teardown_msi_irq;
 	msi->of_node = node;
 
-	mutex_init(&pcie->msi_used_lock);
+	mutex_init(&advk->msi_used_lock);
 
-	msi_msg_phys = virt_to_phys(&pcie->msi_msg);
+	msi_msg_phys = virt_to_phys(&advk->msi_msg);
 
-	advk_writel(pcie, lower_32_bits(msi_msg_phys),
-		    PCIE_MSI_ADDR_LOW_REG);
-	advk_writel(pcie, upper_32_bits(msi_msg_phys),
-		    PCIE_MSI_ADDR_HIGH_REG);
+	advk_writel(advk, lower_32_bits(msi_msg_phys), PCIE_MSI_ADDR_LOW_REG);
+	advk_writel(advk, upper_32_bits(msi_msg_phys), PCIE_MSI_ADDR_HIGH_REG);
 
-	pcie->msi_domain =
+	advk->msi_domain =
 		irq_domain_add_linear(NULL, MSI_IRQ_NUM,
-				      &advk_pcie_msi_irq_ops, pcie);
-	if (!pcie->msi_domain)
+				      &advk_pcie_msi_irq_ops, advk);
+	if (!advk->msi_domain)
 		return -ENOMEM;
 
 	ret = of_pci_msi_chip_add(msi);
 	if (ret < 0) {
-		irq_domain_remove(pcie->msi_domain);
+		irq_domain_remove(advk->msi_domain);
 		return ret;
 	}
 
 	return 0;
 }
 
-static void advk_pcie_remove_msi_irq_domain(struct advk_pcie *pcie)
+static void advk_pcie_remove_msi_irq_domain(struct advk_pcie *advk)
 {
-	of_pci_msi_chip_remove(&pcie->msi);
-	irq_domain_remove(pcie->msi_domain);
+	of_pci_msi_chip_remove(&advk->msi);
+	irq_domain_remove(advk->msi_domain);
 }
 
-static int advk_pcie_init_irq_domain(struct advk_pcie *pcie)
+static int advk_pcie_init_irq_domain(struct advk_pcie *advk)
 {
-	struct device *dev = &pcie->pdev->dev;
+	struct device *dev = &advk->pdev->dev;
 	struct device_node *node = dev->of_node;
 	struct device_node *pcie_intc_node;
 	struct irq_chip *irq_chip;
@@ -743,7 +740,7 @@ static int advk_pcie_init_irq_domain(struct advk_pcie *pcie)
 		return -ENODEV;
 	}
 
-	irq_chip = &pcie->irq_chip;
+	irq_chip = &advk->irq_chip;
 
 	irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq",
 					dev_name(dev));
@@ -756,10 +753,10 @@ static int advk_pcie_init_irq_domain(struct advk_pcie *pcie)
 	irq_chip->irq_mask_ack = advk_pcie_irq_mask;
 	irq_chip->irq_unmask = advk_pcie_irq_unmask;
 
-	pcie->irq_domain =
+	advk->irq_domain =
 		irq_domain_add_linear(pcie_intc_node, LEGACY_IRQ_NUM,
-				      &advk_pcie_irq_domain_ops, pcie);
-	if (!pcie->irq_domain) {
+				      &advk_pcie_irq_domain_ops, advk);
+	if (!advk->irq_domain) {
 		dev_err(dev, "Failed to get a INTx IRQ domain\n");
 		of_node_put(pcie_intc_node);
 		return -ENOMEM;
@@ -768,106 +765,105 @@ static int advk_pcie_init_irq_domain(struct advk_pcie *pcie)
 	return 0;
 }
 
-static void advk_pcie_remove_irq_domain(struct advk_pcie *pcie)
+static void advk_pcie_remove_irq_domain(struct advk_pcie *advk)
 {
-	irq_domain_remove(pcie->irq_domain);
+	irq_domain_remove(advk->irq_domain);
 }
 
-static void advk_pcie_handle_msi(struct advk_pcie *pcie)
+static void advk_pcie_handle_msi(struct advk_pcie *advk)
 {
 	u32 msi_val, msi_mask, msi_status, msi_idx;
 	u16 msi_data;
 
-	msi_mask = advk_readl(pcie, PCIE_MSI_MASK_REG);
-	msi_val = advk_readl(pcie, PCIE_MSI_STATUS_REG);
+	msi_mask = advk_readl(advk, PCIE_MSI_MASK_REG);
+	msi_val = advk_readl(advk, PCIE_MSI_STATUS_REG);
 	msi_status = msi_val & ~msi_mask;
 
 	for (msi_idx = 0; msi_idx < MSI_IRQ_NUM; msi_idx++) {
 		if (!(BIT(msi_idx) & msi_status))
 			continue;
 
-		advk_writel(pcie, BIT(msi_idx), PCIE_MSI_STATUS_REG);
-		msi_data = advk_readl(pcie, PCIE_MSI_PAYLOAD_REG) & 0xFF;
+		advk_writel(advk, BIT(msi_idx), PCIE_MSI_STATUS_REG);
+		msi_data = advk_readl(advk, PCIE_MSI_PAYLOAD_REG) & 0xFF;
 		generic_handle_irq(msi_data);
 	}
 
-	advk_writel(pcie, PCIE_ISR0_MSI_INT_PENDING,
-		    PCIE_ISR0_REG);
+	advk_writel(advk, PCIE_ISR0_MSI_INT_PENDING, PCIE_ISR0_REG);
 }
 
-static void advk_pcie_handle_int(struct advk_pcie *pcie)
+static void advk_pcie_handle_int(struct advk_pcie *advk)
 {
 	u32 val, mask, status;
 	int i, virq;
 
-	val = advk_readl(pcie, PCIE_ISR0_REG);
-	mask = advk_readl(pcie, PCIE_ISR0_MASK_REG);
+	val = advk_readl(advk, PCIE_ISR0_REG);
+	mask = advk_readl(advk, PCIE_ISR0_MASK_REG);
 	status = val & ((~mask) & PCIE_ISR0_ALL_MASK);
 
 	if (!status) {
-		advk_writel(pcie, val, PCIE_ISR0_REG);
+		advk_writel(advk, val, PCIE_ISR0_REG);
 		return;
 	}
 
 	/* Process MSI interrupts */
 	if (status & PCIE_ISR0_MSI_INT_PENDING)
-		advk_pcie_handle_msi(pcie);
+		advk_pcie_handle_msi(advk);
 
 	/* Process legacy interrupts */
 	for (i = 0; i < LEGACY_IRQ_NUM; i++) {
 		if (!(status & PCIE_ISR0_INTX_ASSERT(i)))
 			continue;
 
-		advk_writel(pcie, PCIE_ISR0_INTX_ASSERT(i),
+		advk_writel(advk, PCIE_ISR0_INTX_ASSERT(i),
 			    PCIE_ISR0_REG);
 
-		virq = irq_find_mapping(pcie->irq_domain, i);
+		virq = irq_find_mapping(advk->irq_domain, i);
 		generic_handle_irq(virq);
 	}
 }
 
 static irqreturn_t advk_pcie_irq_handler(int irq, void *arg)
 {
-	struct advk_pcie *pcie = arg;
+	struct advk_pcie *advk = arg;
 	u32 status;
 
-	status = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG);
+	status = advk_readl(advk, HOST_CTRL_INT_STATUS_REG);
 	if (!(status & PCIE_IRQ_CORE_INT))
 		return IRQ_NONE;
 
-	advk_pcie_handle_int(pcie);
+	advk_pcie_handle_int(advk);
 
 	/* Clear interrupt */
-	advk_writel(pcie, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG);
+	advk_writel(advk, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG);
 
 	return IRQ_HANDLED;
 }
 
-static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *pcie)
+static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *advk)
 {
 	int err, res_valid = 0;
-	struct device *dev = &pcie->pdev->dev;
+	struct device *dev = &advk->pdev->dev;
 	struct device_node *np = dev->of_node;
 	struct resource_entry *win, *tmp;
 	resource_size_t iobase;
 
-	INIT_LIST_HEAD(&pcie->resources);
+	INIT_LIST_HEAD(&advk->resources);
 
-	err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pcie->resources,
+	err = of_pci_get_host_bridge_resources(np, 0, 0xff, &advk->resources,
 					       &iobase);
 	if (err)
 		return err;
 
-	err = devm_request_pci_bus_resources(dev, &pcie->resources);
+	err = devm_request_pci_bus_resources(dev, &advk->resources);
 	if (err)
 		goto out_release_res;
 
-	resource_list_for_each_entry_safe(win, tmp, &pcie->resources) {
+	resource_list_for_each_entry_safe(win, tmp, &advk->resources) {
 		struct resource *res = win->res;
 
 		switch (resource_type(res)) {
 		case IORESOURCE_IO:
-			advk_pcie_set_ob_win(pcie, 1,
+			advk_pcie_set_ob_win(advk, 1,
 					     upper_32_bits(res->start),
 					     lower_32_bits(res->start),
 					     0,	0xF8000000, 0,
@@ -881,7 +877,7 @@ static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *pcie)
 			}
 			break;
 		case IORESOURCE_MEM:
-			advk_pcie_set_ob_win(pcie, 0,
+			advk_pcie_set_ob_win(advk, 0,
 					     upper_32_bits(res->start),
 					     lower_32_bits(res->start),
 					     0x0, 0xF8000000, 0,
@@ -890,7 +886,7 @@ static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *pcie)
 			res_valid |= !(res->flags & IORESOURCE_PREFETCH);
 			break;
 		case IORESOURCE_BUS:
-			pcie->root_bus_nr = res->start;
+			advk->root_bus_nr = res->start;
 			break;
 		}
 	}
@@ -904,59 +900,59 @@ static int advk_pcie_parse_request_of_pci_ranges(struct advk_pcie *pcie)
 	return 0;
 
 out_release_res:
-	pci_free_resource_list(&pcie->resources);
+	pci_free_resource_list(&advk->resources);
 	return err;
 }
 
 static int advk_pcie_probe(struct platform_device *pdev)
 {
-	struct advk_pcie *pcie;
+	struct advk_pcie *advk;
 	struct resource *res;
 	struct pci_bus *bus, *child;
 	struct msi_controller *msi;
 	struct device_node *msi_node;
 	int ret, irq;
 
-	pcie = devm_kzalloc(&pdev->dev, sizeof(struct advk_pcie),
+	advk = devm_kzalloc(&pdev->dev, sizeof(struct advk_pcie),
 			    GFP_KERNEL);
-	if (!pcie)
+	if (!advk)
 		return -ENOMEM;
 
-	pcie->pdev = pdev;
-	platform_set_drvdata(pdev, pcie);
+	advk->pdev = pdev;
+	platform_set_drvdata(pdev, advk);
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	pcie->base = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(pcie->base))
-		return PTR_ERR(pcie->base);
+	advk->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(advk->base))
+		return PTR_ERR(advk->base);
 
 	irq = platform_get_irq(pdev, 0);
 	ret = devm_request_irq(&pdev->dev, irq, advk_pcie_irq_handler,
 			       IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie",
-			       pcie);
+			       advk);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to register interrupt\n");
 		return ret;
 	}
 
-	ret = advk_pcie_parse_request_of_pci_ranges(pcie);
+	ret = advk_pcie_parse_request_of_pci_ranges(advk);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to parse resources\n");
 		return ret;
 	}
 
-	advk_pcie_setup_hw(pcie);
+	advk_pcie_setup_hw(advk);
 
-	ret = advk_pcie_init_irq_domain(pcie);
+	ret = advk_pcie_init_irq_domain(advk);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to initialize irq\n");
 		return ret;
 	}
 
-	ret = advk_pcie_init_msi_irq_domain(pcie);
+	ret = advk_pcie_init_msi_irq_domain(advk);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to initialize irq\n");
-		advk_pcie_remove_irq_domain(pcie);
+		advk_pcie_remove_irq_domain(advk);
 		return ret;
 	}
 
@@ -967,10 +963,10 @@ static int advk_pcie_probe(struct platform_device *pdev)
 		msi = NULL;
 
 	bus = pci_scan_root_bus_msi(&pdev->dev, 0, &advk_pcie_ops,
-				    pcie, &pcie->resources, &pcie->msi);
+				    advk, &advk->resources, &advk->msi);
 	if (!bus) {
-		advk_pcie_remove_msi_irq_domain(pcie);
-		advk_pcie_remove_irq_domain(pcie);
+		advk_pcie_remove_msi_irq_domain(advk);
+		advk_pcie_remove_irq_domain(advk);
 		return -ENOMEM;
 	}
 

^ permalink raw reply related

* [PATCH 2/5] PCI: aardvark: Reorder accessor functions
From: Bjorn Helgaas @ 2016-10-07 16:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161007162053.22668.91420.stgit@bhelgaas-glaptop2.roam.corp.google.com>

Reorder the accessors so the reader is first, as most other drivers do.
Uninline them.  No functional change intended.

Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
---
 drivers/pci/host/pci-aardvark.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/pci/host/pci-aardvark.c b/drivers/pci/host/pci-aardvark.c
index fd0e6af..3d629d4 100644
--- a/drivers/pci/host/pci-aardvark.c
+++ b/drivers/pci/host/pci-aardvark.c
@@ -209,14 +209,14 @@ struct advk_pcie {
 	int root_bus_nr;
 };
 
-static inline void advk_writel(struct advk_pcie *advk, u32 val, u64 reg)
+static u32 advk_readl(struct advk_pcie *advk, u64 reg)
 {
-	writel(val, advk->base + reg);
+	return readl(advk->base + reg);
 }
 
-static inline u32 advk_readl(struct advk_pcie *advk, u64 reg)
+static void advk_writel(struct advk_pcie *advk, u32 val, u64 reg)
 {
-	return readl(advk->base + reg);
+	writel(val, advk->base + reg);
 }
 
 static int advk_pcie_link_up(struct advk_pcie *advk)

^ permalink raw reply related

* [PATCH 3/5] PCI: aardvark: Swap order of advk_write() reg/val arguments
From: Bjorn Helgaas @ 2016-10-07 16:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161007162053.22668.91420.stgit@bhelgaas-glaptop2.roam.corp.google.com>

Swap order of advk_writel() arguments to match the "dev, pos, val" order
used by pci_write_config_word() and other drivers.  No functional change
intended.

Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
---
 drivers/pci/host/pci-aardvark.c |  109 +++++++++++++++++++--------------------
 1 file changed, 54 insertions(+), 55 deletions(-)

diff --git a/drivers/pci/host/pci-aardvark.c b/drivers/pci/host/pci-aardvark.c
index 3d629d4..af6312a 100644
--- a/drivers/pci/host/pci-aardvark.c
+++ b/drivers/pci/host/pci-aardvark.c
@@ -214,7 +214,7 @@ static u32 advk_readl(struct advk_pcie *advk, u64 reg)
 	return readl(advk->base + reg);
 }
 
-static void advk_writel(struct advk_pcie *advk, u32 val, u64 reg)
+static void advk_writel(struct advk_pcie *advk, u64 reg, u32 val)
 {
 	writel(val, advk->base + reg);
 }
@@ -257,14 +257,14 @@ static void advk_pcie_set_ob_win(struct advk_pcie *advk,
 				 u32 mask_ls, u32 remap_ms,
 				 u32 remap_ls, u32 action)
 {
-	advk_writel(advk, match_ls, OB_WIN_MATCH_LS(win_num));
-	advk_writel(advk, match_ms, OB_WIN_MATCH_MS(win_num));
-	advk_writel(advk, mask_ms, OB_WIN_MASK_MS(win_num));
-	advk_writel(advk, mask_ls, OB_WIN_MASK_LS(win_num));
-	advk_writel(advk, remap_ms, OB_WIN_REMAP_MS(win_num));
-	advk_writel(advk, remap_ls, OB_WIN_REMAP_LS(win_num));
-	advk_writel(advk, action, OB_WIN_ACTIONS(win_num));
-	advk_writel(advk, match_ls | BIT(0), OB_WIN_MATCH_LS(win_num));
+	advk_writel(advk, OB_WIN_MATCH_LS(win_num), match_ls);
+	advk_writel(advk, OB_WIN_MATCH_MS(win_num), match_ms);
+	advk_writel(advk, OB_WIN_MASK_MS(win_num), mask_ms);
+	advk_writel(advk, OB_WIN_MASK_LS(win_num), mask_ls);
+	advk_writel(advk, OB_WIN_REMAP_MS(win_num), remap_ms);
+	advk_writel(advk, OB_WIN_REMAP_LS(win_num), remap_ls);
+	advk_writel(advk, OB_WIN_ACTIONS(win_num), action);
+	advk_writel(advk, OB_WIN_MATCH_LS(win_num), match_ls | BIT(0));
 }
 
 static void advk_pcie_setup_hw(struct advk_pcie *advk)
@@ -280,98 +280,98 @@ static void advk_pcie_setup_hw(struct advk_pcie *advk)
 	reg = advk_readl(advk, CTRL_CONFIG_REG);
 	reg &= ~(CTRL_MODE_MASK << CTRL_MODE_SHIFT);
 	reg |= ((PCIE_CORE_MODE_DIRECT & CTRL_MODE_MASK) << CTRL_MODE_SHIFT);
-	advk_writel(advk, reg, CTRL_CONFIG_REG);
+	advk_writel(advk, CTRL_CONFIG_REG, reg);
 
 	/* Set PCI global control register to RC mode */
 	reg = advk_readl(advk, PCIE_CORE_CTRL0_REG);
 	reg |= (IS_RC_MSK << IS_RC_SHIFT);
-	advk_writel(advk, reg, PCIE_CORE_CTRL0_REG);
+	advk_writel(advk, PCIE_CORE_CTRL0_REG, reg);
 
 	/* Set Advanced Error Capabilities and Control PF0 register */
 	reg = PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX |
 		PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN |
 		PCIE_CORE_ERR_CAPCTL_ECRC_CHCK |
 		PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV;
-	advk_writel(advk, reg, PCIE_CORE_ERR_CAPCTL_REG);
+	advk_writel(advk, PCIE_CORE_ERR_CAPCTL_REG, reg);
 
 	/* Set PCIe Device Control and Status 1 PF0 register */
 	reg = PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE |
 		(7 << PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT) |
 		PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE |
 		PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT;
-	advk_writel(advk, reg, PCIE_CORE_DEV_CTRL_STATS_REG);
+	advk_writel(advk, PCIE_CORE_DEV_CTRL_STATS_REG, reg);
 
 	/* Program PCIe Control 2 to disable strict ordering */
 	reg = PCIE_CORE_CTRL2_RESERVED |
 		PCIE_CORE_CTRL2_TD_ENABLE;
-	advk_writel(advk, reg, PCIE_CORE_CTRL2_REG);
+	advk_writel(advk, PCIE_CORE_CTRL2_REG, reg);
 
 	/* Set GEN2 */
 	reg = advk_readl(advk, PCIE_CORE_CTRL0_REG);
 	reg &= ~PCIE_GEN_SEL_MSK;
 	reg |= SPEED_GEN_2;
-	advk_writel(advk, reg, PCIE_CORE_CTRL0_REG);
+	advk_writel(advk, PCIE_CORE_CTRL0_REG, reg);
 
 	/* Set lane X1 */
 	reg = advk_readl(advk, PCIE_CORE_CTRL0_REG);
 	reg &= ~LANE_CNT_MSK;
 	reg |= LANE_COUNT_1;
-	advk_writel(advk, reg, PCIE_CORE_CTRL0_REG);
+	advk_writel(advk, PCIE_CORE_CTRL0_REG, reg);
 
 	/* Enable link training */
 	reg = advk_readl(advk, PCIE_CORE_CTRL0_REG);
 	reg |= LINK_TRAINING_EN;
-	advk_writel(advk, reg, PCIE_CORE_CTRL0_REG);
+	advk_writel(advk, PCIE_CORE_CTRL0_REG, reg);
 
 	/* Enable MSI */
 	reg = advk_readl(advk, PCIE_CORE_CTRL2_REG);
 	reg |= PCIE_CORE_CTRL2_MSI_ENABLE;
-	advk_writel(advk, reg, PCIE_CORE_CTRL2_REG);
+	advk_writel(advk, PCIE_CORE_CTRL2_REG, reg);
 
 	/* Clear all interrupts */
-	advk_writel(advk, PCIE_ISR0_ALL_MASK, PCIE_ISR0_REG);
-	advk_writel(advk, PCIE_ISR1_ALL_MASK, PCIE_ISR1_REG);
-	advk_writel(advk, PCIE_IRQ_ALL_MASK, HOST_CTRL_INT_STATUS_REG);
+	advk_writel(advk, PCIE_ISR0_REG, PCIE_ISR0_ALL_MASK);
+	advk_writel(advk, PCIE_ISR1_REG, PCIE_ISR1_ALL_MASK);
+	advk_writel(advk, HOST_CTRL_INT_STATUS_REG, PCIE_IRQ_ALL_MASK);
 
 	/* Disable All ISR0/1 Sources */
 	reg = PCIE_ISR0_ALL_MASK;
 	reg &= ~PCIE_ISR0_MSI_INT_PENDING;
-	advk_writel(advk, reg, PCIE_ISR0_MASK_REG);
+	advk_writel(advk, PCIE_ISR0_MASK_REG, reg);
 
-	advk_writel(advk, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG);
+	advk_writel(advk, PCIE_ISR1_MASK_REG, PCIE_ISR1_ALL_MASK);
 
 	/* Unmask all MSI's */
-	advk_writel(advk, 0, PCIE_MSI_MASK_REG);
+	advk_writel(advk, PCIE_MSI_MASK_REG, 0);
 
 	/* Enable summary interrupt for GIC SPI source */
 	reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK);
-	advk_writel(advk, reg, HOST_CTRL_INT_MASK_REG);
+	advk_writel(advk, HOST_CTRL_INT_MASK_REG, reg);
 
 	reg = advk_readl(advk, PCIE_CORE_CTRL2_REG);
 	reg |= PCIE_CORE_CTRL2_OB_WIN_ENABLE;
-	advk_writel(advk, reg, PCIE_CORE_CTRL2_REG);
+	advk_writel(advk, PCIE_CORE_CTRL2_REG, reg);
 
 	/* Bypass the address window mapping for PIO */
 	reg = advk_readl(advk, PIO_CTRL);
 	reg |= PIO_CTRL_ADDR_WIN_DISABLE;
-	advk_writel(advk, reg, PIO_CTRL);
+	advk_writel(advk, PIO_CTRL, reg);
 
 	/* Start link training */
 	reg = advk_readl(advk, PCIE_CORE_LINK_CTRL_STAT_REG);
 	reg |= PCIE_CORE_LINK_TRAINING;
-	advk_writel(advk, reg, PCIE_CORE_LINK_CTRL_STAT_REG);
+	advk_writel(advk, PCIE_CORE_LINK_CTRL_STAT_REG, reg);
 
 	advk_pcie_wait_for_link(advk);
 
 	reg = PCIE_CORE_LINK_L0S_ENTRY |
 		(1 << PCIE_CORE_LINK_WIDTH_SHIFT);
-	advk_writel(advk, reg, PCIE_CORE_LINK_CTRL_STAT_REG);
+	advk_writel(advk, PCIE_CORE_LINK_CTRL_STAT_REG, reg);
 
 	reg = advk_readl(advk, PCIE_CORE_CMD_STATUS_REG);
 	reg |= PCIE_CORE_CMD_MEM_ACCESS_EN |
 		PCIE_CORE_CMD_IO_ACCESS_EN |
 		PCIE_CORE_CMD_MEM_IO_REQ_EN;
-	advk_writel(advk, reg, PCIE_CORE_CMD_STATUS_REG);
+	advk_writel(advk, PCIE_CORE_CMD_STATUS_REG, reg);
 }
 
 static void advk_pcie_check_pio_status(struct advk_pcie *advk)
@@ -443,8 +443,8 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,
 	}
 
 	/* Start PIO */
-	advk_writel(advk, 0, PIO_START);
-	advk_writel(advk, 1, PIO_ISR);
+	advk_writel(advk, PIO_START, 0);
+	advk_writel(advk, PIO_ISR, 1);
 
 	/* Program the control register */
 	reg = advk_readl(advk, PIO_CTRL);
@@ -453,18 +453,18 @@ static int advk_pcie_rd_conf(struct pci_bus *bus, u32 devfn,
 		reg |= PCIE_CONFIG_RD_TYPE0;
 	else
 		reg |= PCIE_CONFIG_RD_TYPE1;
-	advk_writel(advk, reg, PIO_CTRL);
+	advk_writel(advk, PIO_CTRL, reg);
 
 	/* Program the address registers */
 	reg = PCIE_BDF(devfn) | PCIE_CONF_REG(where);
-	advk_writel(advk, reg, PIO_ADDR_LS);
-	advk_writel(advk, 0, PIO_ADDR_MS);
+	advk_writel(advk, PIO_ADDR_LS, reg);
+	advk_writel(advk, PIO_ADDR_MS, 0);
 
 	/* Program the data strobe */
-	advk_writel(advk, 0xf, PIO_WR_DATA_STRB);
+	advk_writel(advk, PIO_WR_DATA_STRB, 0xf);
 
 	/* Start the transfer */
-	advk_writel(advk, 1, PIO_START);
+	advk_writel(advk, PIO_START, 1);
 
 	ret = advk_pcie_wait_pio(advk);
 	if (ret < 0)
@@ -498,8 +498,8 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 		return PCIBIOS_SET_FAILED;
 
 	/* Start PIO */
-	advk_writel(advk, 0, PIO_START);
-	advk_writel(advk, 1, PIO_ISR);
+	advk_writel(advk, PIO_START, 0);
+	advk_writel(advk, PIO_ISR, 1);
 
 	/* Program the control register */
 	reg = advk_readl(advk, PIO_CTRL);
@@ -508,12 +508,12 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 		reg |= PCIE_CONFIG_WR_TYPE0;
 	else
 		reg |= PCIE_CONFIG_WR_TYPE1;
-	advk_writel(advk, reg, PIO_CTRL);
+	advk_writel(advk, PIO_CTRL, reg);
 
 	/* Program the address registers */
 	reg = PCIE_CONF_ADDR(bus->number, devfn, where);
-	advk_writel(advk, reg, PIO_ADDR_LS);
-	advk_writel(advk, 0, PIO_ADDR_MS);
+	advk_writel(advk, PIO_ADDR_LS, reg);
+	advk_writel(advk, PIO_ADDR_MS, 0);
 
 	/* Calculate the write strobe */
 	offset      = where & 0x3;
@@ -521,13 +521,13 @@ static int advk_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 	data_strobe = GENMASK(size - 1, 0) << offset;
 
 	/* Program the data register */
-	advk_writel(advk, reg, PIO_WR_DATA);
+	advk_writel(advk, PIO_WR_DATA, reg);
 
 	/* Program the data strobe */
-	advk_writel(advk, data_strobe, PIO_WR_DATA_STRB);
+	advk_writel(advk, PIO_WR_DATA_STRB, data_strobe);
 
 	/* Start the transfer */
-	advk_writel(advk, 1, PIO_START);
+	advk_writel(advk, PIO_START, 1);
 
 	ret = advk_pcie_wait_pio(advk);
 	if (ret < 0)
@@ -640,7 +640,7 @@ static void advk_pcie_irq_mask(struct irq_data *d)
 
 	mask = advk_readl(advk, PCIE_ISR0_MASK_REG);
 	mask |= PCIE_ISR0_INTX_ASSERT(hwirq);
-	advk_writel(advk, mask, PCIE_ISR0_MASK_REG);
+	advk_writel(advk, PCIE_ISR0_MASK_REG, mask);
 }
 
 static void advk_pcie_irq_unmask(struct irq_data *d)
@@ -651,7 +651,7 @@ static void advk_pcie_irq_unmask(struct irq_data *d)
 
 	mask = advk_readl(advk, PCIE_ISR0_MASK_REG);
 	mask &= ~PCIE_ISR0_INTX_ASSERT(hwirq);
-	advk_writel(advk, mask, PCIE_ISR0_MASK_REG);
+	advk_writel(advk, PCIE_ISR0_MASK_REG, mask);
 }
 
 static int advk_pcie_irq_map(struct irq_domain *h,
@@ -703,8 +703,8 @@ static int advk_pcie_init_msi_irq_domain(struct advk_pcie *advk)
 
 	msi_msg_phys = virt_to_phys(&advk->msi_msg);
 
-	advk_writel(advk, lower_32_bits(msi_msg_phys), PCIE_MSI_ADDR_LOW_REG);
-	advk_writel(advk, upper_32_bits(msi_msg_phys), PCIE_MSI_ADDR_HIGH_REG);
+	advk_writel(advk, PCIE_MSI_ADDR_LOW_REG, lower_32_bits(msi_msg_phys));
+	advk_writel(advk, PCIE_MSI_ADDR_HIGH_REG, upper_32_bits(msi_msg_phys));
 
 	advk->msi_domain =
 		irq_domain_add_linear(NULL, MSI_IRQ_NUM,
@@ -783,12 +783,12 @@ static void advk_pcie_handle_msi(struct advk_pcie *advk)
 		if (!(BIT(msi_idx) & msi_status))
 			continue;
 
-		advk_writel(advk, BIT(msi_idx), PCIE_MSI_STATUS_REG);
+		advk_writel(advk, PCIE_MSI_STATUS_REG, BIT(msi_idx));
 		msi_data = advk_readl(advk, PCIE_MSI_PAYLOAD_REG) & 0xFF;
 		generic_handle_irq(msi_data);
 	}
 
-	advk_writel(advk, PCIE_ISR0_MSI_INT_PENDING, PCIE_ISR0_REG);
+	advk_writel(advk, PCIE_ISR0_REG, PCIE_ISR0_MSI_INT_PENDING);
 }
 
 static void advk_pcie_handle_int(struct advk_pcie *advk)
@@ -801,7 +801,7 @@ static void advk_pcie_handle_int(struct advk_pcie *advk)
 	status = val & ((~mask) & PCIE_ISR0_ALL_MASK);
 
 	if (!status) {
-		advk_writel(advk, val, PCIE_ISR0_REG);
+		advk_writel(advk, PCIE_ISR0_REG, val);
 		return;
 	}
 
@@ -814,8 +814,7 @@ static void advk_pcie_handle_int(struct advk_pcie *advk)
 		if (!(status & PCIE_ISR0_INTX_ASSERT(i)))
 			continue;
 
-		advk_writel(advk, PCIE_ISR0_INTX_ASSERT(i),
-			    PCIE_ISR0_REG);
+		advk_writel(advk, PCIE_ISR0_REG, PCIE_ISR0_INTX_ASSERT(i));
 
 		virq = irq_find_mapping(advk->irq_domain, i);
 		generic_handle_irq(virq);
@@ -834,7 +833,7 @@ static irqreturn_t advk_pcie_irq_handler(int irq, void *arg)
 	advk_pcie_handle_int(advk);
 
 	/* Clear interrupt */
-	advk_writel(advk, PCIE_IRQ_CORE_INT, HOST_CTRL_INT_STATUS_REG);
+	advk_writel(advk, HOST_CTRL_INT_STATUS_REG, PCIE_IRQ_CORE_INT);
 
 	return IRQ_HANDLED;
 }

^ permalink raw reply related

* [PATCH 4/5] PCI: aardvark: Add local struct device pointers
From: Bjorn Helgaas @ 2016-10-07 16:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161007162053.22668.91420.stgit@bhelgaas-glaptop2.roam.corp.google.com>

Use a local "struct device *dev" for brevity and consistency with other
drivers.  No functional change intended.

Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
---
 drivers/pci/host/pci-aardvark.c |   36 ++++++++++++++++++++----------------
 1 file changed, 20 insertions(+), 16 deletions(-)

diff --git a/drivers/pci/host/pci-aardvark.c b/drivers/pci/host/pci-aardvark.c
index af6312a..b255a08 100644
--- a/drivers/pci/host/pci-aardvark.c
+++ b/drivers/pci/host/pci-aardvark.c
@@ -230,19 +230,20 @@ static int advk_pcie_link_up(struct advk_pcie *advk)
 
 static int advk_pcie_wait_for_link(struct advk_pcie *advk)
 {
+	struct device *dev = &advk->pdev->dev;
 	int retries;
 
 	/* check if the link is up or not */
 	for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
 		if (advk_pcie_link_up(advk)) {
-			dev_info(&advk->pdev->dev, "link up\n");
+			dev_info(dev, "link up\n");
 			return 0;
 		}
 
 		usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
 	}
 
-	dev_err(&advk->pdev->dev, "link never came up\n");
+	dev_err(dev, "link never came up\n");
 
 	return -ETIMEDOUT;
 }
@@ -376,6 +377,7 @@ static void advk_pcie_setup_hw(struct advk_pcie *advk)
 
 static void advk_pcie_check_pio_status(struct advk_pcie *advk)
 {
+	struct device *dev = &advk->pdev->dev;
 	u32 reg;
 	unsigned int status;
 	char *strcomp_status, *str_posted;
@@ -407,12 +409,13 @@ static void advk_pcie_check_pio_status(struct advk_pcie *advk)
 	else
 		str_posted = "Posted";
 
-	dev_err(&advk->pdev->dev, "%s PIO Response Status: %s, %#x @ %#x\n",
+	dev_err(dev, "%s PIO Response Status: %s, %#x @ %#x\n",
 		str_posted, strcomp_status, reg, advk_readl(advk, PIO_ADDR_LS));
 }
 
 static int advk_pcie_wait_pio(struct advk_pcie *advk)
 {
+	struct device *dev = &advk->pdev->dev;
 	unsigned long timeout;
 
 	timeout = jiffies + msecs_to_jiffies(PIO_TIMEOUT_MS);
@@ -426,7 +429,7 @@ static int advk_pcie_wait_pio(struct advk_pcie *advk)
 			return 0;
 	}
 
-	dev_err(&advk->pdev->dev, "config read/write timed out\n");
+	dev_err(dev, "config read/write timed out\n");
 	return -ETIMEDOUT;
 }
 
@@ -560,10 +563,11 @@ static int advk_pcie_alloc_msi(struct advk_pcie *advk)
 
 static void advk_pcie_free_msi(struct advk_pcie *advk, int hwirq)
 {
+	struct device *dev = &advk->pdev->dev;
+
 	mutex_lock(&advk->msi_used_lock);
 	if (!test_bit(hwirq, advk->msi_irq_in_use))
-		dev_err(&advk->pdev->dev, "trying to free unused MSI#%d\n",
-			hwirq);
+		dev_err(dev, "trying to free unused MSI#%d\n", hwirq);
 	else
 		clear_bit(hwirq, advk->msi_irq_in_use);
 	mutex_unlock(&advk->msi_used_lock);
@@ -905,6 +909,7 @@ out_release_res:
 
 static int advk_pcie_probe(struct platform_device *pdev)
 {
+	struct device *dev = &pdev->dev;
 	struct advk_pcie *advk;
 	struct resource *res;
 	struct pci_bus *bus, *child;
@@ -912,8 +917,7 @@ static int advk_pcie_probe(struct platform_device *pdev)
 	struct device_node *msi_node;
 	int ret, irq;
 
-	advk = devm_kzalloc(&pdev->dev, sizeof(struct advk_pcie),
-			    GFP_KERNEL);
+	advk = devm_kzalloc(dev, sizeof(struct advk_pcie), GFP_KERNEL);
 	if (!advk)
 		return -ENOMEM;
 
@@ -921,22 +925,22 @@ static int advk_pcie_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, advk);
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	advk->base = devm_ioremap_resource(&pdev->dev, res);
+	advk->base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(advk->base))
 		return PTR_ERR(advk->base);
 
 	irq = platform_get_irq(pdev, 0);
-	ret = devm_request_irq(&pdev->dev, irq, advk_pcie_irq_handler,
+	ret = devm_request_irq(dev, irq, advk_pcie_irq_handler,
 			       IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie",
 			       advk);
 	if (ret) {
-		dev_err(&pdev->dev, "Failed to register interrupt\n");
+		dev_err(dev, "Failed to register interrupt\n");
 		return ret;
 	}
 
 	ret = advk_pcie_parse_request_of_pci_ranges(advk);
 	if (ret) {
-		dev_err(&pdev->dev, "Failed to parse resources\n");
+		dev_err(dev, "Failed to parse resources\n");
 		return ret;
 	}
 
@@ -944,24 +948,24 @@ static int advk_pcie_probe(struct platform_device *pdev)
 
 	ret = advk_pcie_init_irq_domain(advk);
 	if (ret) {
-		dev_err(&pdev->dev, "Failed to initialize irq\n");
+		dev_err(dev, "Failed to initialize irq\n");
 		return ret;
 	}
 
 	ret = advk_pcie_init_msi_irq_domain(advk);
 	if (ret) {
-		dev_err(&pdev->dev, "Failed to initialize irq\n");
+		dev_err(dev, "Failed to initialize irq\n");
 		advk_pcie_remove_irq_domain(advk);
 		return ret;
 	}
 
-	msi_node = of_parse_phandle(pdev->dev.of_node, "msi-parent", 0);
+	msi_node = of_parse_phandle(dev->of_node, "msi-parent", 0);
 	if (msi_node)
 		msi = of_pci_find_msi_chip_by_node(msi_node);
 	else
 		msi = NULL;
 
-	bus = pci_scan_root_bus_msi(&pdev->dev, 0, &advk_pcie_ops,
+	bus = pci_scan_root_bus_msi(dev, 0, &advk_pcie_ops,
 				    advk, &advk->resources, &advk->msi);
 	if (!bus) {
 		advk_pcie_remove_msi_irq_domain(advk);

^ permalink raw reply related

* [PATCH 5/5] PCI: aardvark: Remove unused platform data
From: Bjorn Helgaas @ 2016-10-07 16:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161007162053.22668.91420.stgit@bhelgaas-glaptop2.roam.corp.google.com>

The aardvark driver never uses the platform drvdata pointer, so don't
bother setting it.  No functional change intended.

Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
---
 drivers/pci/host/pci-aardvark.c |    1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/pci/host/pci-aardvark.c b/drivers/pci/host/pci-aardvark.c
index b255a08..5b12199 100644
--- a/drivers/pci/host/pci-aardvark.c
+++ b/drivers/pci/host/pci-aardvark.c
@@ -922,7 +922,6 @@ static int advk_pcie_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	advk->pdev = pdev;
-	platform_set_drvdata(pdev, advk);
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	advk->base = devm_ioremap_resource(dev, res);

^ permalink raw reply related

* [PATCH 3/3] mtd: s3c2410: parse the device configuration from OF node
From: Krzysztof Kozlowski @ 2016-10-07 16:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475711217-974-4-git-send-email-sergio.prado@e-labworks.com>

On Wed, Oct 05, 2016 at 08:46:57PM -0300, Sergio Prado wrote:
> Allows configuring Samsung's s3c2410 memory controller using a
> devicetree.
> 
> Signed-off-by: Sergio Prado <sergio.prado@e-labworks.com>
> ---
>  drivers/mtd/nand/s3c2410.c                     | 171 ++++++++++++++++++++++---
>  include/linux/platform_data/mtd-nand-s3c2410.h |   1 +
>  2 files changed, 156 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
> index 174ac9dc4265..352cf2656bc8 100644
> --- a/drivers/mtd/nand/s3c2410.c
> +++ b/drivers/mtd/nand/s3c2410.c
> @@ -39,6 +39,8 @@
>  #include <linux/slab.h>
>  #include <linux/clk.h>
>  #include <linux/cpufreq.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
>  
>  #include <linux/mtd/mtd.h>
>  #include <linux/mtd/nand.h>
> @@ -185,6 +187,26 @@ struct s3c2410_nand_info {
>  #endif
>  };
>  
> +struct s3c24XX_nand_devtype_data {
> +	enum s3c_cpu_type type;
> +};
> +
> +struct s3c24XX_nand_devtype_data s3c2410_nand_devtype_data = {
> +	.type = TYPE_S3C2410,
> +};
> +
> +struct s3c24XX_nand_devtype_data s3c2412_nand_devtype_data = {
> +	.type = TYPE_S3C2412,
> +};
> +
> +struct s3c24XX_nand_devtype_data s3c2440_nand_devtype_data = {
> +	.type = TYPE_S3C2440,
> +};
> +
> +struct s3c24XX_nand_devtype_data s3c6400_nand_devtype_data = {
> +	.type = TYPE_S3C2412,

All of these look like candidate for static const.

Additionally you are not actually differentiating between s3c2412 and
s3c64xx so I think there is not need of samsung,s3c6400-nand compatible.
Just use existing one.

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH 0/2] ARM: at91: properly handle LPDDR poweroff
From: Alexandre Belloni @ 2016-10-07 16:34 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

This patch set improves LPDDR support on SoCs using the Atmel MPDDR controller.

LPDDR memoris can only handle up to 400 uncontrolled power offs in their
life. The proper power off sequence has to be applied before shutting down the SoC.

I'm not too happy with the code duplication but this is a design choice
that has been made before because both shitdown controler are really
different appart from the shutdown itself.

I guess it is still better than slowly killing the LPDDR.

Alexandre Belloni (2):
  ARM: at91: define LPDDR types
  power/reset: at91-poweroff: timely shitdown LPDDR memories

 drivers/power/reset/at91-poweroff.c      | 52 +++++++++++++++++++++++++++++++-
 drivers/power/reset/at91-sama5d2_shdwc.c | 48 ++++++++++++++++++++++++++++-
 include/soc/at91/at91sam9_ddrsdr.h       |  3 ++
 3 files changed, 101 insertions(+), 2 deletions(-)

-- 
2.9.3

^ permalink raw reply

* [PATCH 1/2] ARM: at91: define LPDDR types
From: Alexandre Belloni @ 2016-10-07 16:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161007163427.11454-1-alexandre.belloni@free-electrons.com>

The Atmel MPDDR controller support LPDDR2 and LPDDR3 memories, add their
types.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 include/soc/at91/at91sam9_ddrsdr.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/soc/at91/at91sam9_ddrsdr.h b/include/soc/at91/at91sam9_ddrsdr.h
index dc10c52e0e91..393362bdb860 100644
--- a/include/soc/at91/at91sam9_ddrsdr.h
+++ b/include/soc/at91/at91sam9_ddrsdr.h
@@ -81,6 +81,7 @@
 #define			AT91_DDRSDRC_LPCB_POWER_DOWN		2
 #define			AT91_DDRSDRC_LPCB_DEEP_POWER_DOWN	3
 #define		AT91_DDRSDRC_CLKFR	(1 << 2)	/* Clock Frozen */
+#define		AT91_DDRSDRC_LPDDR2_PWOFF	(1 << 3)	/* LPDDR Power Off */
 #define		AT91_DDRSDRC_PASR	(7 << 4)	/* Partial Array Self Refresh */
 #define		AT91_DDRSDRC_TCSR	(3 << 8)	/* Temperature Compensated Self Refresh */
 #define		AT91_DDRSDRC_DS		(3 << 10)	/* Drive Strength */
@@ -96,7 +97,9 @@
 #define			AT91_DDRSDRC_MD_SDR		0
 #define			AT91_DDRSDRC_MD_LOW_POWER_SDR	1
 #define			AT91_DDRSDRC_MD_LOW_POWER_DDR	3
+#define			AT91_DDRSDRC_MD_LPDDR3		5
 #define			AT91_DDRSDRC_MD_DDR2		6	/* [SAM9 Only] */
+#define			AT91_DDRSDRC_MD_LPDDR2		7
 #define		AT91_DDRSDRC_DBW	(1 << 4)		/* Data Bus Width */
 #define			AT91_DDRSDRC_DBW_32BITS		(0 <<  4)
 #define			AT91_DDRSDRC_DBW_16BITS		(1 <<  4)
-- 
2.9.3

^ permalink raw reply related

* [PATCH 2/2] power/reset: at91-poweroff: timely shitdown LPDDR memories
From: Alexandre Belloni @ 2016-10-07 16:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161007163427.11454-1-alexandre.belloni@free-electrons.com>

LPDDR memories can only handle up to 400 uncontrolled power off. Ensure the
proper power off sequence is used before shutting down the platform.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 drivers/power/reset/at91-poweroff.c      | 52 +++++++++++++++++++++++++++++++-
 drivers/power/reset/at91-sama5d2_shdwc.c | 48 ++++++++++++++++++++++++++++-
 2 files changed, 98 insertions(+), 2 deletions(-)

diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c
index e9e24df35f26..bf97390e6cd7 100644
--- a/drivers/power/reset/at91-poweroff.c
+++ b/drivers/power/reset/at91-poweroff.c
@@ -14,9 +14,12 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/platform_device.h>
 #include <linux/printk.h>
 
+#include <soc/at91/at91sam9_ddrsdr.h>
+
 #define AT91_SHDW_CR	0x00		/* Shut Down Control Register */
 #define AT91_SHDW_SHDW		BIT(0)			/* Shut Down command */
 #define AT91_SHDW_KEY		(0xa5 << 24)		/* KEY Password */
@@ -50,6 +53,7 @@ static const char *shdwc_wakeup_modes[] = {
 
 static void __iomem *at91_shdwc_base;
 static struct clk *sclk;
+static void __iomem *mpddrc_base;
 
 static void __init at91_wakeup_status(void)
 {
@@ -73,6 +77,28 @@ static void at91_poweroff(void)
 	writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
 }
 
+static void at91_lpddr_poweroff(void)
+{
+	asm volatile(
+		/* Align to cache lines */
+		".balign 32\n\t"
+
+		"	ldr	r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
+
+		/* Power down SDRAM0 */
+		"	str	%1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
+		/* Shutdown CPU */
+		"	str	%3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
+
+		"	b	.\n\t"
+		:
+		: "r" (mpddrc_base),
+		  "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
+		  "r" (at91_shdwc_base),
+		  "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW)
+		: "r0");
+}
+
 static int at91_poweroff_get_wakeup_mode(struct device_node *np)
 {
 	const char *pm;
@@ -124,6 +150,8 @@ static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
 static int __init at91_poweroff_probe(struct platform_device *pdev)
 {
 	struct resource *res;
+	struct device_node *np;
+	u32 ddr_type;
 	int ret;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -150,12 +178,29 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
 
 	pm_power_off = at91_poweroff;
 
+	np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc");
+	if (!np)
+		return 0;
+
+	mpddrc_base = of_iomap(np, 0);
+	of_node_put(np);
+
+	if (!mpddrc_base)
+		return 0;
+
+	ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD;
+	if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) ||
+	    (ddr_type == AT91_DDRSDRC_MD_LPDDR3))
+	else
+		iounmap(mpddrc_base);
+
 	return 0;
 }
 
 static int __exit at91_poweroff_remove(struct platform_device *pdev)
 {
-	if (pm_power_off == at91_poweroff)
+	if (pm_power_off == at91_poweroff ||
+	    pm_power_off == at91_lpddr_poweroff)
 		pm_power_off = NULL;
 
 	clk_disable_unprepare(sclk);
@@ -163,6 +208,11 @@ static int __exit at91_poweroff_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct of_device_id at91_ramc_of_match[] = {
+	{ .compatible = "atmel,sama5d3-ddramc", },
+	{ /* sentinel */ }
+};
+
 static const struct of_device_id at91_poweroff_of_match[] = {
 	{ .compatible = "atmel,at91sam9260-shdwc", },
 	{ .compatible = "atmel,at91sam9rl-shdwc", },
diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c
index 8a5ac9706c9c..5736f360b374 100644
--- a/drivers/power/reset/at91-sama5d2_shdwc.c
+++ b/drivers/power/reset/at91-sama5d2_shdwc.c
@@ -22,9 +22,12 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/platform_device.h>
 #include <linux/printk.h>
 
+#include <soc/at91/at91sam9_ddrsdr.h>
+
 #define SLOW_CLOCK_FREQ	32768
 
 #define AT91_SHDW_CR	0x00		/* Shut Down Control Register */
@@ -75,6 +78,7 @@ struct shdwc {
  */
 static struct shdwc *at91_shdwc;
 static struct clk *sclk;
+static void __iomem *mpddrc_base;
 
 static const unsigned long long sdwc_dbc_period[] = {
 	0, 3, 32, 512, 4096, 32768,
@@ -108,6 +112,28 @@ static void at91_poweroff(void)
 	       at91_shdwc->at91_shdwc_base + AT91_SHDW_CR);
 }
 
+static void at91_lpddr_poweroff(void)
+{
+	asm volatile(
+		/* Align to cache lines */
+		".balign 32\n\t"
+
+		"	ldr	r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
+
+		/* Power down SDRAM0 */
+		"	str	%1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
+		/* Shutdown CPU */
+		"	str	%3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
+
+		"	b	.\n\t"
+		:
+		: "r" (mpddrc_base),
+		  "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
+		  "r" (at91_shdwc->at91_shdwc_base),
+		  "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW)
+		: "r0");
+}
+
 static u32 at91_shdwc_debouncer_value(struct platform_device *pdev,
 				      u32 in_period_us)
 {
@@ -212,6 +238,8 @@ static int __init at91_shdwc_probe(struct platform_device *pdev)
 {
 	struct resource *res;
 	const struct of_device_id *match;
+	struct device_node *np;
+	u32 ddr_type;
 	int ret;
 
 	if (!pdev->dev.of_node)
@@ -249,6 +277,23 @@ static int __init at91_shdwc_probe(struct platform_device *pdev)
 
 	pm_power_off = at91_poweroff;
 
+	np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc");
+	if (!np)
+		return 0;
+
+	mpddrc_base = of_iomap(np, 0);
+	of_node_put(np);
+
+	if (!mpddrc_base)
+		return 0;
+
+	ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD;
+	if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) ||
+	    (ddr_type == AT91_DDRSDRC_MD_LPDDR3))
+		pm_power_off = at91_lpddr_poweroff;
+	else
+		iounmap(mpddrc_base);
+
 	return 0;
 }
 
@@ -256,7 +301,8 @@ static int __exit at91_shdwc_remove(struct platform_device *pdev)
 {
 	struct shdwc *shdw = platform_get_drvdata(pdev);
 
-	if (pm_power_off == at91_poweroff)
+	if (pm_power_off == at91_poweroff ||
+	    pm_power_off == at91_lpddr_poweroff)
 		pm_power_off = NULL;
 
 	/* Reset values to disable wake-up features  */
-- 
2.9.3

^ permalink raw reply related

* [PATCH 2/3] arm64: hw_breakpoint: Handle inexact watchpoint addresses
From: Pratyush Anand @ 2016-10-07 16:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474643941-109020-2-git-send-email-labath@google.com>

On Fri, Sep 23, 2016 at 8:49 PM, Pavel Labath
<test.tberghammer@gmail.com> wrote:
> Arm64 hardware does not always report a watchpoint hit address that
> matches one of the watchpoints set. It can also report an address
> "near" the watchpoint if a single instruction access both watched and
> unwatched addresses. There is no straight-forward way, short of
> disassembling the offending instruction, to map that address back to
> the watchpoint.
>
> Previously, when the hardware reported a watchpoint hit on an address
> that did not match our watchpoint (this happens in case of instructions
> which access large chunks of memory such as "stp") the process would
> enter a loop where we would be continually resuming it (because we did
> not recognise that watchpoint hit) and it would keep hitting the
> watchpoint again and again. The tracing process would never get
> notified of the watchpoint hit.
>
> This commit fixes the problem by looking at the watchpoints near the
> address reported by the hardware. If the address does not exactly match
> one of the watchpoints we have set, it attributes the hit to the
> nearest watchpoint we have.  This heuristic is a bit dodgy, but I don't
> think we can do much more, given the hardware limitations.

IIUC, then you see an issue when an address watched is not the base
address accessed by the instruction. For example, if an address 'a+8'
is watched and an instruction accesses instruction from  a to a +16. I
tried to reproduce the issue with mustang using your test-case in
patch3 (after couple of syntax modifcations for resolving compilation
issue with gcc). All the test case did pass with existing code in
v4.8. I noticed that, watchpoint exception is generated if any of the
sub-location accessed from a single instruction is watched, provided
watchdpoint watches either a byte, half word, word or double word
from the base.

So, either I must be missing something or the problem is not related
to all arm64 platform.

However, I did notice that it does not work if we watch an address
which is at some offset from address programmed. For example, it works
when byte_mask is 0x3, but it does not work if byte_mask if 0x2 (which
is supported by hardware).

I do have some patches to resolve that.

https://github.com/pratyushanand/linux/commits/perf/upstream_arm64_devel

I will send them for review comment after some testing.

~Pratyush

>
> Signed-off-by: Pavel Labath <labath@google.com>
> ---
>  arch/arm64/kernel/hw_breakpoint.c | 98 +++++++++++++++++++++++++--------------
>  1 file changed, 64 insertions(+), 34 deletions(-)
>
> diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
> index 14562ae..3ce27ea 100644
> --- a/arch/arm64/kernel/hw_breakpoint.c
> +++ b/arch/arm64/kernel/hw_breakpoint.c
> @@ -664,49 +664,63 @@ unlock:
>  }
>  NOKPROBE_SYMBOL(breakpoint_handler);
>
> +/*
> + * Arm64 hardware does not always report a watchpoint hit address that matches
> + * one of the watchpoints set. It can also report an address "near" the
> + * watchpoint if a single instruction access both watched and unwatched
> + * addresses. There is no straight-forward way, short of disassembling the
> + * offending instruction, to map that address back to the watchpoint. This
> + * function computes the distance of the memory access from the watchpoint as a
> + * heuristic for the likelyhood that a given access triggered the watchpoint.
> + *
> + * See Section D2.10.5 "Determining the memory location that caused a Watchpoint
> + * exception" of ARMv8 Architecture Reference Manual for details.
> + *
> + * The function returns the distance of the address from the bytes watched by
> + * the watchpoint. In case of an exact match, it returns 0.
> + */
> +static u64 get_distance_from_watchpoint(unsigned long addr, int i,
> +                                       struct arch_hw_breakpoint *info)
> +{
> +       u64 wp_low, wp_high;
> +       int first_bit;
> +
> +       first_bit = ffs(info->ctrl.len);
> +       if (first_bit == 0)
> +               return -1;
> +
> +       wp_low = info->address + first_bit - 1;
> +       wp_high = info->address + fls(info->ctrl.len) - 1;
> +       if (addr < wp_low)
> +               return wp_low - addr;
> +       else if (addr > wp_high)
> +               return addr - wp_high;
> +       else
> +               return 0;
> +
> +}
> +
>  static int watchpoint_handler(unsigned long addr, unsigned int esr,
>                               struct pt_regs *regs)
>  {
> -       int i, step = 0, *kernel_step, access;
> -       u32 ctrl_reg;
> -       u64 val, alignment_mask;
> +       int i, step = 0, *kernel_step, access, closest_match = 0;
> +       u64 min_dist = -1, dist;
>         struct perf_event *wp, **slots;
>         struct debug_info *debug_info;
>         struct arch_hw_breakpoint *info;
> -       struct arch_hw_breakpoint_ctrl ctrl;
>
>         slots = this_cpu_ptr(wp_on_reg);
>         debug_info = &current->thread.debug;
>
> +       /*
> +        * Find all watchpoints that match the reported address. If no exact
> +        * match is found. Attribute the hit to the closest watchpoint.
> +        */
> +       rcu_read_lock();
>         for (i = 0; i < core_num_wrps; ++i) {
> -               rcu_read_lock();
> -
>                 wp = slots[i];
> -
>                 if (wp == NULL)
> -                       goto unlock;
> -
> -               info = counter_arch_bp(wp);
> -               /* AArch32 watchpoints are either 4 or 8 bytes aligned. */
> -               if (is_compat_task()) {
> -                       if (info->ctrl.len == ARM_BREAKPOINT_LEN_8)
> -                               alignment_mask = 0x7;
> -                       else
> -                               alignment_mask = 0x3;
> -               } else {
> -                       alignment_mask = 0x7;
> -               }
> -
> -               /* Check if the watchpoint value matches. */
> -               val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
> -               if (val != (addr & ~alignment_mask))
> -                       goto unlock;
> -
> -               /* Possible match, check the byte address select to confirm. */
> -               ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
> -               decode_ctrl_reg(ctrl_reg, &ctrl);
> -               if (!((1 << (addr & alignment_mask)) & ctrl.len))
> -                       goto unlock;
> +                       continue;
>
>                 /*
>                  * Check that the access type matches.
> @@ -715,7 +729,18 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,
>                 access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W :
>                          HW_BREAKPOINT_R;
>                 if (!(access & hw_breakpoint_type(wp)))
> -                       goto unlock;
> +                       continue;
> +
> +               info = counter_arch_bp(wp);
> +
> +               dist = get_distance_from_watchpoint(addr, i, info);
> +               if (dist < min_dist) {
> +                       min_dist = dist;
> +                       closest_match = i;
> +               }
> +               /* Is this an exact match? */
> +               if (dist != 0)
> +                       continue;
>
>                 info->trigger = addr;
>                 perf_bp_event(wp, regs);
> @@ -723,10 +748,15 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,
>                 /* Do we need to handle the stepping? */
>                 if (is_default_overflow_handler(wp))
>                         step = 1;
> -
> -unlock:
> -               rcu_read_unlock();
>         }
> +       if (min_dist > 0 && min_dist != -1) {
> +               /* No exact match found. */
> +               wp = slots[closest_match];
> +               info = counter_arch_bp(wp);
> +               info->trigger = addr;
> +               perf_bp_event(wp, regs);
> +       }
> +       rcu_read_unlock();
>
>         if (!step)
>                 return 0;
> --
> 2.8.0.rc3.226.g39d4020
>

^ permalink raw reply

* [PATCH v3 0/11] Add R8A7743/SK-RZG1M board support
From: Sergei Shtylyov @ 2016-10-07 16:40 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <2056698.ncAHq8vRQ3@wasted.cogentembedded.com>

On 10/06/2016 12:23 AM, Sergei Shtylyov wrote:

>    Here's the set of 11 patches against Simon Horman's 'renesas.git' repo's
> 'renesas-devel-20161003-v4.8' tag. I'm adding the device tree support for
> the R8A7743-based SK-RZG1M board. The SoC is close to R8A7791 and the board
> seems identical to the R8A7791/Porter board. The device tree patches depend on
> the R8A7743 CPG/MSSR driver series just posted in order to compile and work.

    Forgot to mention that this version causes a regression with the sh_eth 
driver (well, actually with phylib): since IRQC now gets a deferred probing, 
PHY IRQ doesn't work anymore -- phylib falls back to polling.

MBR, Sergei

^ permalink raw reply

* [PATCH/RFT 00/12] Add DT support for ohci-da8xx
From: ahaslam at baylibre.com @ 2016-10-07 16:42 UTC (permalink / raw)
  To: linux-arm-kernel

From: Axel Haslam <ahaslam@baylibre.com>

The purpose of this patch series is to add DT support to the ohci-da8xx
glue driver without breaking the non-DT boot, which is still used in
unconverted davinci devices.

To Achieve this, the first 8 patches make sure that the non-DT based
enumeration works, and prepares the stage for a DT migration by removing
dependencies on the board files (moving VBUS, and over current
handling to the driver). The last 4 patches actually add the DT
documentation and bindings for the ohci-da8xx driver.

Testing was done on a omap138-lcdk board, using DT, and non-DT boot,
and checking that in both cases the hub, usb mass storage and an input
device are correctly enumerated and working.

Since there have been some recent and ongoing efforts from David Lechner
to clean up davinci-mach code and the ochi-da8xx driver, this series
builds upon that work. Specifically:

* the accepted but soon to be reposted patch to remove mach code form
the ohci driver[1].

* The patch series to add phy nodes, and move usb clocks to a common
file [2].

A git branch based on tag: next-20161004 with the dependencies patches is
available in my github here [3].

The omap138-lcdk does not have gpios to control vbus and get over current
interrupt notifications, hence i was not able to test these and added
the RFT tag. If anyone has a da830-evm based board and could
confirm that ohci is correctly working, i would appreciate it.
(the OHCI option needs to be enabled in menuconfig)

P.D: It seems that the davinci-gpio driver is broken for DT based boot
and any gpio > 32. (luckly none of the DT based boards use gpios yet)
The probelm is that we have 144 gpios in the gpio controller as correctly
declared on the DT (they are not separate gpio controllers as in am3xx),
but the driver creates several gpio chips of 32 pins each, confusing the
"gpio chip to pin" matching logic of gpiolib-of. I think we might need to
fix this by creating a single gpio chip in gpio-davinci.c

[1] [PATCH v6 1/3] usb: ohci-da8xx: Remove code that references mach
    http://www.gossamer-threads.com/lists/linux/kernel/2518807
[2] [PATCH v5 0/5] da8xx USB PHY platform devices and clocks (was "da8xx UBS clocks")
    http://www.spinics.net/lists/linux-usb/msg140568.html
[3] github branch with all dependante patches
    https://github.com/axelhaslamx/linux-axel/commits/ohci-da8xx-dt

Axel Haslam (12):
  ARM: davinci: da8xx: Enable the usb20 "per" clk on phy_clk_enable
  ARM: davinci: hawk: add full constraints for ohci plat boot
  ARM: davinci: rename root_hub to platform_data
  USB: ohci-da8xx: Divide power up time in the ohci driver
  USB: ohci-da8xx: Fix probe for devices with no vbus/oci gpio
  ARM: davinci: hawk: Remove oci and vbus gpios
  USB: ohci-da8xx: Request gpios and handle interrupt in the driver
  ARM: davinci: register the usb20_phy clock on the SoC file
  usb: host: ohci-da8xx: Add devicetree bindings documentation
  USB: ohci-da8xx: Add device tree support
  ARM: dts: da850: Add the usb ohci device node
  ARM: dts: da850-lcdk: enable ohci usb

 .../devicetree/bindings/usb/ohci-da8xx.txt         |  32 +++++
 arch/arm/boot/dts/da850-lcdk.dts                   |   9 ++
 arch/arm/boot/dts/da850.dtsi                       |   8 ++
 arch/arm/mach-davinci/board-da830-evm.c            |  75 +---------
 arch/arm/mach-davinci/board-omapl138-hawk.c        | 105 +-------------
 arch/arm/mach-davinci/da850.c                      |   2 +
 arch/arm/mach-davinci/include/mach/da8xx.h         |   2 +-
 arch/arm/mach-davinci/usb-da8xx.c                  |  15 +-
 drivers/usb/host/ohci-da8xx.c                      | 158 +++++++++++++++++----
 include/linux/platform_data/usb-davinci.h          |  22 ++-
 10 files changed, 218 insertions(+), 210 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/usb/ohci-da8xx.txt

-- 
2.7.1

^ permalink raw reply


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