Devicetree
 help / color / mirror / Atom feed
* [PATCH v2 1/5] ARM: memory: da8xx-ddrctl: new driver
From: Bartosz Golaszewski @ 2016-10-31 14:45 UTC (permalink / raw)
  To: Kevin Hilman, Michael Turquette, Sekhar Nori, Rob Herring,
	Frank Rowand, Mark Rutland, Peter Ujfalusi, Russell King
  Cc: LKML, arm-soc, linux-drm, linux-devicetree, Jyri Sarha,
	Tomi Valkeinen, David Airlie, Laurent Pinchart,
	Bartosz Golaszewski
In-Reply-To: <1477925138-23457-1-git-send-email-bgolaszewski-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>

Create a new driver for the da8xx DDR2/mDDR controller and implement
support for writing to the Peripheral Bus Burst Priority Register.

Signed-off-by: Bartosz Golaszewski <bgolaszewski-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
---
 .../memory-controllers/ti-da8xx-ddrctl.txt         |  20 +++
 drivers/memory/Kconfig                             |   8 +
 drivers/memory/Makefile                            |   1 +
 drivers/memory/da8xx-ddrctl.c                      | 175 +++++++++++++++++++++
 4 files changed, 204 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/ti-da8xx-ddrctl.txt
 create mode 100644 drivers/memory/da8xx-ddrctl.c

diff --git a/Documentation/devicetree/bindings/memory-controllers/ti-da8xx-ddrctl.txt b/Documentation/devicetree/bindings/memory-controllers/ti-da8xx-ddrctl.txt
new file mode 100644
index 0000000..ec1dd40
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/ti-da8xx-ddrctl.txt
@@ -0,0 +1,20 @@
+* Device tree bindings for Texas Instruments da8xx DDR2/mDDR memory controller
+
+The DDR2/mDDR memory controller present on Texas Instruments da8xx SoCs features
+a set of registers which allow to tweak the controller's behavior.
+
+Documentation:
+OMAP-L138 (DA850) - http://www.ti.com/lit/ug/spruh82c/spruh82c.pdf
+
+Required properties:
+
+- compatible:		"ti,da850-ddr-controller" - for da850 SoC based boards
+- reg:			a tuple containing the base address of the memory
+			controller and the size of the memory area to map
+
+Example for da850 shown below.
+
+ddrctl {
+	compatible = "ti,da850-ddr-controller";
+	reg = <0xb0000000 0xe8>;
+};
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index 4b4c0c3..ec80e35 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -134,6 +134,14 @@ config MTK_SMI
 	  mainly help enable/disable iommu and control the power domain and
 	  clocks for each local arbiter.
 
+config DA8XX_DDRCTL
+	bool "Texas Instruments da8xx DDR2/mDDR driver"
+	depends on ARCH_DAVINCI_DA8XX
+	help
+	  This driver is for the DDR2/mDDR Memory Controller present on
+	  Texas Instruments da8xx SoCs. It's used to tweak various memory
+	  controller configuration options.
+
 source "drivers/memory/samsung/Kconfig"
 source "drivers/memory/tegra/Kconfig"
 
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index b20ae38..e88097fb 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_MVEBU_DEVBUS)	+= mvebu-devbus.o
 obj-$(CONFIG_TEGRA20_MC)	+= tegra20-mc.o
 obj-$(CONFIG_JZ4780_NEMC)	+= jz4780-nemc.o
 obj-$(CONFIG_MTK_SMI)		+= mtk-smi.o
+obj-$(CONFIG_DA8XX_DDRCTL)	+= da8xx-ddrctl.o
 
 obj-$(CONFIG_SAMSUNG_MC)	+= samsung/
 obj-$(CONFIG_TEGRA_MC)		+= tegra/
diff --git a/drivers/memory/da8xx-ddrctl.c b/drivers/memory/da8xx-ddrctl.c
new file mode 100644
index 0000000..a20e7bb
--- /dev/null
+++ b/drivers/memory/da8xx-ddrctl.c
@@ -0,0 +1,175 @@
+/*
+ * TI da8xx DDR2/mDDR controller driver
+ *
+ * Copyright (C) 2016 BayLibre SAS
+ *
+ * Author:
+ *   Bartosz Golaszewski <bgolaszewski-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_fdt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+/*
+ * REVISIT: Linux doesn't have a good framework for the kind of performance
+ * knobs this driver controls. We can't use device tree properties as it deals
+ * with hardware configuration rather than description. We also don't want to
+ * commit to maintaining some random sysfs attributes.
+ *
+ * For now we just hardcode the register values for the boards that need
+ * some changes (as is the case for the LCD controller on da850-lcdk - the
+ * first board we support here). When linux gets an appropriate framework,
+ * we'll easily convert the driver to it.
+ */
+
+struct da8xx_ddrctl_config_knob {
+	const char *name;
+	u32 reg;
+	u32 mask;
+	u32 shift;
+};
+
+static const struct da8xx_ddrctl_config_knob da8xx_ddrctl_knobs[] = {
+	{
+		.name = "da850-pbbpr",
+		.reg = 0x20,
+		.mask = 0xffffff00,
+		.shift = 0,
+	},
+};
+
+struct da8xx_ddrctl_setting {
+	const char *name;
+	u32 val;
+};
+
+struct da8xx_ddrctl_board_settings {
+	const char *board;
+	const struct da8xx_ddrctl_setting *settings;
+};
+
+static const struct da8xx_ddrctl_setting da850_lcdk_ddrctl_settings[] = {
+	{
+		.name = "da850-pbbpr",
+		.val = 0x20,
+	},
+	{ }
+};
+
+static const struct da8xx_ddrctl_board_settings da8xx_ddrctl_board_confs[] = {
+	{
+		.board = "ti,da850-lcdk",
+		.settings = da850_lcdk_ddrctl_settings,
+	},
+};
+
+static const struct da8xx_ddrctl_config_knob *
+da8xx_ddrctl_match_knob(const struct da8xx_ddrctl_setting *setting)
+{
+	const struct da8xx_ddrctl_config_knob *knob;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_knobs); i++) {
+		knob = &da8xx_ddrctl_knobs[i];
+
+		if (strcmp(knob->name, setting->name) == 0)
+			return knob;
+	}
+
+	return NULL;
+}
+
+static const struct da8xx_ddrctl_setting *da8xx_ddrctl_get_board_settings(void)
+{
+	const struct da8xx_ddrctl_board_settings *board_settings;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(da8xx_ddrctl_board_confs); i++) {
+		board_settings = &da8xx_ddrctl_board_confs[i];
+
+		if (of_machine_is_compatible(board_settings->board))
+			return board_settings->settings;
+	}
+
+	return NULL;
+}
+
+static int da8xx_ddrctl_probe(struct platform_device *pdev)
+{
+	const struct da8xx_ddrctl_config_knob *knob;
+	const struct da8xx_ddrctl_setting *setting;
+	struct device_node *node;
+	struct resource *res;
+	void __iomem *ddrctl;
+	struct device *dev;
+	u32 reg;
+
+	dev = &pdev->dev;
+	node = dev->of_node;
+
+	setting = da8xx_ddrctl_get_board_settings();
+	if (!setting) {
+		dev_err(dev, "no settings for board '%s'\n",
+			of_flat_dt_get_machine_name());
+		return -EINVAL;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ddrctl = devm_ioremap_resource(dev, res);
+	if (IS_ERR(ddrctl)) {
+		dev_err(dev, "unable to map memory controller registers\n");
+		return PTR_ERR(ddrctl);
+	}
+
+	for (; setting->name; setting++) {
+		knob = da8xx_ddrctl_match_knob(setting);
+		if (!knob) {
+			dev_warn(dev,
+				 "no such config option: %s\n", setting->name);
+			continue;
+		}
+
+		if (knob->reg + sizeof(u32) > resource_size(res)) {
+			dev_warn(dev,
+				 "register offset of '%s' exceeds mapped memory size\n",
+				 knob->name);
+			continue;
+		}
+
+		reg = readl(ddrctl + knob->reg);
+		reg &= knob->mask;
+		reg |= setting->val << knob->shift;
+
+		dev_dbg(dev, "writing 0x%08x to %s\n", reg, setting->name);
+
+		writel(reg, ddrctl + knob->reg);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id da8xx_ddrctl_of_match[] = {
+	{ .compatible = "ti,da850-ddr-controller", },
+	{ },
+};
+
+static struct platform_driver da8xx_ddrctl_driver = {
+	.probe = da8xx_ddrctl_probe,
+	.driver = {
+		.name = "da850-ddr-controller",
+		.of_match_table = da8xx_ddrctl_of_match,
+	},
+};
+module_platform_driver(da8xx_ddrctl_driver);
+
+MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>");
+MODULE_DESCRIPTION("TI da8xx DDR2/mDDR controller driver");
+MODULE_LICENSE("GPL v2");
-- 
2.9.3

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

^ permalink raw reply related

* [PATCH v2 0/5] ARM: da850: new drivers for better LCDC support
From: Bartosz Golaszewski @ 2016-10-31 14:45 UTC (permalink / raw)
  To: Kevin Hilman, Michael Turquette, Sekhar Nori, Rob Herring,
	Frank Rowand, Mark Rutland, Peter Ujfalusi, Russell King
  Cc: LKML, arm-soc, linux-drm, linux-devicetree, Jyri Sarha,
	Tomi Valkeinen, David Airlie, Laurent Pinchart,
	Bartosz Golaszewski

This series adds two new drivers in order to better support the LCDC
rev1 present on the da850 boards.

The first patch adds a new memory driver which allows to write to the
DDR2/mDDR memory controller present on the da8xx SoCs.

The second patch adds a new bus driver which allows to interact with
the MSTPRI registers of the SYSCFG0 module

As is mentioned in the comments: we don't want to commit to supporting
stable interfaces (DT bindings or sysfs attributes) so we hardcode the
settings required by some boards (for now only da850-lcdk) with the
hope that linux gets an appropriate framework for performance knobs
in the future.

Potential extensions of these drivers should be straightforward in the
future.

Subsequent patches add DT nodes for the new drivers: disabled nodes
in da850.dtsi and enabled in da850-lcdk.dts.

The last patch adds a workaround for current lack of support for drm
bridges in tilcdc.

Tested on a da850-lcdk with a display connected over VGA and two
additional patches for tilcdc (sent to linux-drm): ran simple modetest
for supported resolutions, used X.org and fluxbox as graphical
environment, played video with mplayer.

v1 -> v2:
- used regular readl()/writel() instead of __raw_** versions
- used resource_size() instead of calculating the size by hand
- used ioremap instead of syscon in patch [2/5]
- added the DT nodes in patches [3/5]-[5/5]

Bartosz Golaszewski (5):
  ARM: memory: da8xx-ddrctl: new driver
  ARM: bus: da8xx-mstpri: new driver
  ARM: dts: da850: add the mstpri and ddrctl nodes
  ARM: dts: da850-lcdk: enable mstpri and ddrctl nodes
  ARM: dts: da850-lcdk: add tilcdc panel node

 .../devicetree/bindings/bus/ti,da850-mstpri.txt    |  20 ++
 .../memory-controllers/ti-da8xx-ddrctl.txt         |  20 ++
 arch/arm/boot/dts/da850-lcdk.dts                   |  71 ++++++
 arch/arm/boot/dts/da850.dtsi                       |  11 +
 drivers/bus/Kconfig                                |   9 +
 drivers/bus/Makefile                               |   2 +
 drivers/bus/da8xx-mstpri.c                         | 269 +++++++++++++++++++++
 drivers/memory/Kconfig                             |   8 +
 drivers/memory/Makefile                            |   1 +
 drivers/memory/da8xx-ddrctl.c                      | 175 ++++++++++++++
 10 files changed, 586 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/bus/ti,da850-mstpri.txt
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/ti-da8xx-ddrctl.txt
 create mode 100644 drivers/bus/da8xx-mstpri.c
 create mode 100644 drivers/memory/da8xx-ddrctl.c

-- 
2.9.3

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

^ permalink raw reply

* Re: [PATCH v2 5/7] [media] ir-lirc-codec: don't wait any transmitting time for tx only devices
From: David Härdeman @ 2016-10-31 14:31 UTC (permalink / raw)
  To: Sean Young, Andi Shyti
  Cc: Mauro Carvalho Chehab, Rob Herring, Mark Rutland, linux-media,
	devicetree, linux-kernel, Andi Shyti
In-Reply-To: <20161027143601.GA5103@gofer.mess.org>

October 27, 2016 4:36 PM, "Sean Young" <sean@mess.org> wrote:
> Since we have to be able to switch between waiting and not waiting,
> we need some sort of ABI for this. I think this warrants a new ioctl;
> I'm not sure how else it can be done. I'll be sending out a patch
> shortly.

Hi Sean,

have you considered using a module param for the LIRC bridge module instead? As far as I understand it, this is an issue which is entirely internal to LIRC, and it's also not something which really needs to be changed on a per-device level (either you have a modern LIRC daemon or you don't, and all drivers should behave the same, no?).

Another advantage is that the parameter would then go away if and when the lirc bridge ever goes away (yes, I can still dream, can't I?).

Regards,
David

^ permalink raw reply

* Re: [RFC PATCH 09/13] net: phy: Add Meson GXL Internal PHY driver
From: Neil Armstrong @ 2016-10-31 14:25 UTC (permalink / raw)
  To: Florian Fainelli, khilman-rdvid1DuHRBWk0Htik3J/w,
	carlo-KA+7E9HrN00dnm+yROfE0A,
	linus.walleij-QSEj5FYQhm4dnm+yROfE0A
  Cc: linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <0ea5a7a8-3913-0e6a-6ff2-9f34015ea071-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

On 10/21/2016 10:56 PM, Florian Fainelli wrote:
> On 10/21/2016 07:40 AM, Neil Armstrong wrote:
>> Add driver for the Internal RMII PHY found in the Amlogic Meson GXL SoCs.
>>
>> This PHY seems to only implement some standard registers and need some
>> workarounds to provide autoneg values from vendor registers.
>>
>> Some magic values are currently used to configure the PHY, and this a
>> temporary setup until clarification about these registers names and
>> registers fields are provided by Amlogic.
>>
>> Signed-off-by: Neil Armstrong <narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
>> ---
>> +
>> +static int meson_gxl_config_init(struct phy_device *phydev)
>> +{
>> +	int val;
>> +	u32 features;
>> +
>> +	meson_gxl_phy_config(phydev);
>> +
>> +	features = SUPPORTED_MII;
> 
> This does not really belong in the PHY driver, and this is statically
> assigned, I would just drop this.
> 

Ok

>> +
>> +	/* Do we support autonegotiation? */
>> +	val = phy_read(phydev, MII_BMSR);
>> +	if (val < 0)
>> +		return val;
>> +
>> +	if (val & BMSR_ANEGCAPABLE)
>> +		features |= SUPPORTED_Autoneg;
>> +	if (val & BMSR_100FULL)
>> +		features |= SUPPORTED_100baseT_Full;
>> +	if (val & BMSR_100HALF)
>> +		features |= SUPPORTED_100baseT_Half;
>> +	if (val & BMSR_10FULL)
>> +		features |= SUPPORTED_10baseT_Full;
>> +	if (val & BMSR_10HALF)
>> +		features |= SUPPORTED_10baseT_Half;
>> +
>> +	phydev->supported = features;
>> +	phydev->advertising = features;
> 
> This is redundant with what PHYLIB will determine for the PHY.
> 
ok

>> +
>> +	return 0;
>> +}
>> +
>> +static int meson_gxl_phy_read_status(struct phy_device *phydev)
>> +{
>> +	int err;
>> +
>> +	/* Update the link, but return if there was an error */
>> +	err = genphy_update_link(phydev);
>> +	if (err)
>> +		return err;
>> +
>> +	phydev->lp_advertising = 0;
>> +	phydev->pause = 0;
>> +	phydev->asym_pause = 0;
>> +
>> +	if (phydev->autoneg == AUTONEG_ENABLE) {
>> +		unsigned int speed;
>> +		int reg = phy_read(phydev, GXL_REG_ANEG);
> 
> Is all of this really necessary? This should all be reflected in the
> standard BMSR register, is not this the case here that we have to read
> this non-standard register?
> 

This is what I understood from the original driver code, but I will make some further tests
and see if the BMSR returns some good data.

> You use genphy_config_aneg(), so surely, the standard auto-negotiation
> part works somehow?

Yes, and the BMSR is also used in the config_init here...

> 
>> +
>> +		if (reg < 0)
>> +			return reg;
>> +
>> +		speed = reg & REG_ANEG_SPEED_MASK;
>> +
>> +		if (reg & REG_ANEG_FDUPLEX)
>> +			phydev->duplex = DUPLEX_FULL;
>> +		else
>> +			phydev->duplex = DUPLEX_HALF;
>> +
>> +		if ((reg & REG_ANEG_SPEED_MASK) == REG_ANEG_SPEED10)
>> +			phydev->speed = SPEED_10;
>> +		else if ((reg & REG_ANEG_SPEED_MASK) == REG_ANEG_SPEED100)
>> +			phydev->speed = SPEED_100;
>> +	} else {
>> +		int bmcr = phy_read(phydev, MII_BMCR);
>> +
>> +		if (bmcr < 0)
>> +			return bmcr;
>> +
>> +		if (bmcr & BMCR_FULLDPLX)
>> +			phydev->duplex = DUPLEX_FULL;
>> +		else
>> +			phydev->duplex = DUPLEX_HALF;
>> +
>> +		if (bmcr & BMCR_SPEED1000)
>> +			phydev->speed = SPEED_1000;
>> +		else if (bmcr & BMCR_SPEED100)
>> +			phydev->speed = SPEED_100;
>> +		else
>> +			phydev->speed = SPEED_10;
>> +	}
> 
>> +
>> +	return 0;
>> +}
>> +
>> +static struct phy_driver meson_gxl_phy = {
>> +	.phy_id		= 0x01814400,
>> +	.name		= "Meson GXL Internal PHY",
>> +	.phy_id_mask	= 0x0fffffff,
> 
> Usually the last 4 bits are 0, since that's where the revision part is
> located.
> 

Fixed

>> +	.features	= 0,
> 
> You should set PHY_GBIT_FEATURES and set .flags to PHY_IS_INTERNAL since
> this is an internal PHY?
> 

Ok

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

^ permalink raw reply

* Re: [RFC PATCH 08/13] dwmac-meson8b: add support for phy selection
From: Neil Armstrong @ 2016-10-31 14:23 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: khilman, carlo, linus.walleij, linux-gpio, devicetree,
	linux-arm-kernel, linux-amlogic, linux-kernel, netdev
In-Reply-To: <20161021160807.GT16974@lunn.ch>

On 10/21/2016 06:08 PM, Andrew Lunn wrote:
> Hi Neil
> 
>> Yes this would be a good idea if we were able to scan the internal
>> and external PHYs at the same time, but with our limited knowledge
>> the values we write in the register seems to switch a mux for the
>> whole RMII and MDIO signals to either interface.
> 
> Ah, O.K. So you need something like:
> 
> drivers/net/phy/mdio-mux-mmioreg.c
> 
> This seems to be limited to a single byte for the mux register, so you
> might need to make some extensions.
> 
>       Andrew
> 

Thanks for the hint, I will try to make use of this !

Neil

^ permalink raw reply

* Re: [PATCH v1 2/3] nvmem: Add the Broadcom OTP controller driver
From: Srinivas Kandagatla @ 2016-10-31 14:23 UTC (permalink / raw)
  To: Jonathan Richardson, Maxime Ripard
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Mark Rutland, Rob Herring,
	Scott Branden, Ray Jui,
	bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w,
	Jonathan Richardson, Scott Branden, Oza Pawandeep
In-Reply-To: <1477336324-10543-3-git-send-email-jonathan.richardson-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>


On 24/10/16 20:12, Jonathan Richardson wrote:
> From: Jonathan Richardson <jonathar-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
>
> Add support for 32 and 64-bit versions of Broadcom's On-Chip OTP
> controller. These controllers are used on SoC's such as Cygnus and
> Stingray.
>
> Reviewed-by: Ray Jui <ray.jui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
> Tested-by: Jonathan Richardson <jonathan.richardson-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
> Signed-off-by: Scott Branden <scott.branden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
> Signed-off-by: Oza Pawandeep <oza-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
> Signed-off-by: Jonathan Richardson <jonathan.richardson-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
> ---
>  drivers/nvmem/Kconfig     |  12 ++
>  drivers/nvmem/Makefile    |   2 +
>  drivers/nvmem/bcm-ocotp.c | 335 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 349 insertions(+)
>  create mode 100644 drivers/nvmem/bcm-ocotp.c


I can pick this patch along with dt bindings document, but dts patch has 
to go via arm-soc tree.

Thanks,
srini


>
> diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
> index ba140ea..06935a7 100644
> --- a/drivers/nvmem/Kconfig
> +++ b/drivers/nvmem/Kconfig
> @@ -80,6 +80,18 @@ config ROCKCHIP_EFUSE
>  	  This driver can also be built as a module. If so, the module
>  	  will be called nvmem_rockchip_efuse.
>
> +config NVMEM_BCM_OCOTP
> +	tristate "Broadcom On-Chip OTP Controller support"
> +	depends on ARCH_BCM_IPROC || COMPILE_TEST
> +	depends on HAS_IOMEM
> +	default ARCH_BCM_IPROC
?
> +	help
> +	  Say y here to enable read/write access to the Broadcom OTP
> +	  controller.
> +
> +	  This driver can also be built as a module. If so, the module
> +	  will be called nvmem-bcm-ocotp.
> +
>  config NVMEM_SUNXI_SID
>  	tristate "Allwinner SoCs SID support"
>  	depends on ARCH_SUNXI
> diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
> index 8f942a0..71781ca 100644
> --- a/drivers/nvmem/Makefile
> +++ b/drivers/nvmem/Makefile
> @@ -6,6 +6,8 @@ obj-$(CONFIG_NVMEM)		+= nvmem_core.o
>  nvmem_core-y			:= core.o
>
>  # Devices
> +obj-$(CONFIG_NVMEM_BCM_OCOTP)	+= nvmem-bcm-ocotp.o
> +nvmem-bcm-ocotp-y		:= bcm-ocotp.o
>  obj-$(CONFIG_NVMEM_IMX_OCOTP)	+= nvmem-imx-ocotp.o
>  nvmem-imx-ocotp-y		:= imx-ocotp.o
>  obj-$(CONFIG_NVMEM_LPC18XX_EEPROM)	+= nvmem_lpc18xx_eeprom.o
> diff --git a/drivers/nvmem/bcm-ocotp.c b/drivers/nvmem/bcm-ocotp.c
> new file mode 100644
> index 0000000..646cadb
> --- /dev/null
> +++ b/drivers/nvmem/bcm-ocotp.c
> @@ -0,0 +1,335 @@
> +/*
> + * Copyright (C) 2016 Broadcom
> + *
> + * 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.
> + *
> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any
> + * kind, whether express or implied; without even the implied warranty
> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/nvmem-provider.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +
> +/*
> + * # of tries for OTP Status. The time to execute a command varies. The slowest
> + * commands are writes which also vary based on the # of bits turned on. Writing
> + * 0xffffffff takes ~3800 us.
> + */
> +#define OTPC_RETRIES                 5000
> +
> +/* Sequence to enable OTP program */
> +#define OTPC_PROG_EN_SEQ             { 0xf, 0x4, 0x8, 0xd }
> +
> +/* OTPC Commands */
> +#define OTPC_CMD_READ                0x0
> +#define OTPC_CMD_OTP_PROG_ENABLE     0x2
> +#define OTPC_CMD_OTP_PROG_DISABLE    0x3
> +#define OTPC_CMD_PROGRAM             0xA
> +
> +/* OTPC Status Bits */
> +#define OTPC_STAT_CMD_DONE           BIT(1)
> +#define OTPC_STAT_PROG_OK            BIT(2)
> +
> +/* OTPC register definition */
> +#define OTPC_MODE_REG_OFFSET         0x0
> +#define OTPC_MODE_REG_OTPC_MODE      0
> +#define OTPC_COMMAND_OFFSET          0x4
> +#define OTPC_COMMAND_COMMAND_WIDTH   6
> +#define OTPC_CMD_START_OFFSET        0x8
> +#define OTPC_CMD_START_START         0
> +#define OTPC_CPU_STATUS_OFFSET       0xc
> +#define OTPC_CPUADDR_REG_OFFSET      0x28
> +#define OTPC_CPUADDR_REG_OTPC_CPU_ADDRESS_WIDTH 16
> +#define OTPC_CPU_WRITE_REG_OFFSET    0x2c
> +
> +#define OTPC_CMD_MASK  (BIT(OTPC_COMMAND_COMMAND_WIDTH) - 1)
> +#define OTPC_ADDR_MASK (BIT(OTPC_CPUADDR_REG_OTPC_CPU_ADDRESS_WIDTH) - 1)
> +
> +
> +struct otpc_map {
> +	/* in words. */
> +	u32 otpc_row_size;
> +	/* 128 bit row / 4 words support. */
> +	u16 data_r_offset[4];
> +	/* 128 bit row / 4 words support. */
> +	u16 data_w_offset[4];
> +};
> +
> +static struct otpc_map otp_map = {
> +	.otpc_row_size = 1,
> +	.data_r_offset = {0x10},
> +	.data_w_offset = {0x2c},
> +};
> +
> +static struct otpc_map otp_map_v2 = {
> +	.otpc_row_size = 2,
> +	.data_r_offset = {0x10, 0x5c},
> +	.data_w_offset = {0x2c, 0x64},
> +};
> +
> +struct otpc_priv {
> +	struct device       *dev;
> +	void __iomem        *base;
> +	struct otpc_map     *map;
> +	struct nvmem_config *config;
> +};
> +
> +static inline void set_command(void __iomem *base, u32 command)
> +{
> +	writel(command & OTPC_CMD_MASK, base + OTPC_COMMAND_OFFSET);
> +}
> +
> +static inline void set_cpu_address(void __iomem *base, u32 addr)
> +{
> +	writel(addr & OTPC_ADDR_MASK, base + OTPC_CPUADDR_REG_OFFSET);
> +}
> +
> +static inline void set_start_bit(void __iomem *base)
> +{
> +	writel(1 << OTPC_CMD_START_START, base + OTPC_CMD_START_OFFSET);
> +}
> +
> +static inline void reset_start_bit(void __iomem *base)
> +{
> +	writel(0, base + OTPC_CMD_START_OFFSET);
> +}
> +
> +static inline void write_cpu_data(void __iomem *base, u32 value)
> +{
> +	writel(value, base + OTPC_CPU_WRITE_REG_OFFSET);
> +}
> +
> +static int poll_cpu_status(void __iomem *base, u32 value)
> +{
> +	u32 status;
> +	u32 retries;
> +
> +	for (retries = 0; retries < OTPC_RETRIES; retries++) {
> +		status = readl(base + OTPC_CPU_STATUS_OFFSET);
> +		if (status & value)
> +			break;
> +		udelay(1);
> +	}
> +	if (retries == OTPC_RETRIES)
> +		return -EAGAIN;
> +
> +	return 0;
> +}
> +
> +static int enable_ocotp_program(void __iomem *base)
> +{
> +	static const u32 vals[] = OTPC_PROG_EN_SEQ;
> +	int i;
> +	int ret;
> +
> +	/* Write the magic sequence to enable programming */
> +	set_command(base, OTPC_CMD_OTP_PROG_ENABLE);
> +	for (i = 0; i < ARRAY_SIZE(vals); i++) {
> +		write_cpu_data(base, vals[i]);
> +		set_start_bit(base);
> +		ret = poll_cpu_status(base, OTPC_STAT_CMD_DONE);
> +		reset_start_bit(base);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return poll_cpu_status(base, OTPC_STAT_PROG_OK);
> +}
> +
> +static int disable_ocotp_program(void __iomem *base)
> +{
> +	int ret;
> +
> +	set_command(base, OTPC_CMD_OTP_PROG_DISABLE);
> +	set_start_bit(base);
> +	ret = poll_cpu_status(base, OTPC_STAT_PROG_OK);
> +	reset_start_bit(base);
> +
> +	return ret;
> +}
> +
> +static int bcm_otpc_read(void *context, unsigned int offset, void *val,
> +	size_t bytes)
> +{
> +	struct otpc_priv *priv = context;
> +	u32 *buf = val;
> +	u32 bytes_read;
> +	u32 address = offset / priv->config->word_size;
> +	int i, ret;
> +
> +	for (bytes_read = 0; bytes_read < bytes;) {
> +		set_command(priv->base, OTPC_CMD_READ);
> +		set_cpu_address(priv->base, address++);
> +		set_start_bit(priv->base);
> +		ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
> +		if (ret) {
> +			dev_err(priv->dev, "otp read error: 0x%x", ret);
> +			return -EIO;
> +		}
> +
> +		for (i = 0; i < priv->map->otpc_row_size; i++) {
> +			*buf++ = readl(priv->base +
> +					priv->map->data_r_offset[i]);
> +			bytes_read += sizeof(*buf);
> +		}
> +
> +		reset_start_bit(priv->base);
> +	}
> +
> +	return 0;
> +}
> +
> +static int bcm_otpc_write(void *context, unsigned int offset, void *val,
> +	size_t bytes)
> +{
> +	struct otpc_priv *priv = context;
> +	u32 *buf = val;
> +	u32 bytes_written;
> +	u32 address = offset / priv->config->word_size;
> +	int i, ret;
> +
> +	if (offset % priv->config->word_size)
> +		return -EINVAL;
> +
> +	ret = enable_ocotp_program(priv->base);
> +	if (ret)
> +		return -EIO;
> +
> +	for (bytes_written = 0; bytes_written < bytes;) {
> +		set_command(priv->base, OTPC_CMD_PROGRAM);
> +		set_cpu_address(priv->base, address++);
> +		for (i = 0; i < priv->map->otpc_row_size; i++) {
> +			writel(*buf, priv->base + priv->map->data_r_offset[i]);
> +			buf++;
> +			bytes_written += sizeof(*buf);
> +		}
> +		set_start_bit(priv->base);
> +		ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
> +		reset_start_bit(priv->base);
> +		if (ret) {
> +			dev_err(priv->dev, "otp write error: 0x%x", ret);
> +			return -EIO;
> +		}
> +	}
> +
> +	disable_ocotp_program(priv->base);
> +
> +	return 0;
> +}
> +
> +static struct nvmem_config bcm_otpc_nvmem_config = {
> +	.name = "bcm-ocotp",
> +	.read_only = false,
> +	.word_size = 4,
> +	.stride = 4,
> +	.owner = THIS_MODULE,
> +	.reg_read = bcm_otpc_read,
> +	.reg_write = bcm_otpc_write,
> +};
> +
> +static const struct of_device_id bcm_otpc_dt_ids[] = {
> +	{ .compatible = "brcm,ocotp" },
> +	{ .compatible = "brcm,ocotp-v2" },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, bcm_otpc_dt_ids);
> +
> +static int bcm_otpc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *dn = dev->of_node;
> +	struct resource *res;
> +	struct otpc_priv *priv;
> +	struct nvmem_device *nvmem;
> +	int err;
> +	u32 num_words;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	if (of_device_is_compatible(dev->of_node, "brcm,ocotp"))
> +		priv->map = &otp_map;
> +	else if (of_device_is_compatible(dev->of_node, "brcm,ocotp-v2"))
> +		priv->map = &otp_map_v2;
> +	else {
> +		dev_err(&pdev->dev,
> +			"%s otpc config map not defined\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* Get OTP base address register. */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(priv->base)) {
> +		dev_err(dev, "unable to map I/O memory\n");
> +		return PTR_ERR(priv->base);
> +	}
> +
> +	/* Enable CPU access to OTPC. */
> +	writel(readl(priv->base + OTPC_MODE_REG_OFFSET) |
> +		BIT(OTPC_MODE_REG_OTPC_MODE),
> +		priv->base + OTPC_MODE_REG_OFFSET);
> +	reset_start_bit(priv->base);
> +
> +	/* Read size of memory in words. */
> +	err = of_property_read_u32(dn, "brcm,ocotp-size", &num_words);
> +	if (err) {
> +		dev_err(dev, "size parameter not specified\n");
> +		return -EINVAL;
> +	} else if (num_words == 0) {
> +		dev_err(dev, "size must be > 0\n");
> +		return -EINVAL;
> +	}
> +
> +	bcm_otpc_nvmem_config.size = 4 * num_words;
> +	bcm_otpc_nvmem_config.dev = dev;
> +	bcm_otpc_nvmem_config.priv = priv;
> +
> +	if (of_device_is_compatible(dev->of_node, "brcm,ocotp-v2")) {
> +		bcm_otpc_nvmem_config.word_size = 8;
> +		bcm_otpc_nvmem_config.stride = 8;
> +	}
> +
> +	priv->config = &bcm_otpc_nvmem_config;
> +
> +	nvmem = nvmem_register(&bcm_otpc_nvmem_config);
> +	if (IS_ERR(nvmem)) {
> +		dev_err(dev, "error registering nvmem config\n");
> +		return PTR_ERR(nvmem);
> +	}
> +
> +	platform_set_drvdata(pdev, nvmem);
> +
> +	return 0;
> +}
> +
> +static int bcm_otpc_remove(struct platform_device *pdev)
> +{
> +	struct nvmem_device *nvmem = platform_get_drvdata(pdev);
> +
> +	return nvmem_unregister(nvmem);
> +}
> +
> +static struct platform_driver bcm_otpc_driver = {
> +	.probe	= bcm_otpc_probe,
> +	.remove	= bcm_otpc_remove,
> +	.driver = {
> +		.name	= "brcm-otpc",
> +		.of_match_table = bcm_otpc_dt_ids,
> +	},
> +};
> +module_platform_driver(bcm_otpc_driver);
> +
> +MODULE_DESCRIPTION("Broadcom OTPC driver");
> +MODULE_LICENSE("GPL v2");
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH] ARM: DT: stm32: move dma translation to board files
From: Radosław Pietrzyk @ 2016-10-31 14:14 UTC (permalink / raw)
  To: Bruno Herrera
  Cc: Alexandre Torgue, mark.rutland, devicetree, Arnd Bergmann, linux,
	>, >, Maxime Coquelin, Lee Jones, linux-arm-kernel
In-Reply-To: <CAF3+TqcFnXENmX4OpG9jzhPhuaq-EqNsRhhjKgFjHA96DLqK5g@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 9801 bytes --]

This is weird because dma ddresses are recalculated using parent's
dma-ranges property and soc already has it so there should be absolutely no
problem.

2016-10-31 11:27 GMT+01:00 Bruno Herrera <bruherrera@gmail.com>:

> On Fri, Oct 28, 2016 at 5:09 AM, Radosław Pietrzyk
> <radoslaw.pietrzyk@gmail.com> wrote:
> > Have you defined your sdio node within soc node ?
>
> It is in the SOC node of the DSTI file.
>
> >
> > 2016-10-27 14:57 GMT+02:00 Bruno Herrera <bruherrera@gmail.com>:
> >>
> >> Hi Alex,
> >>
> >> On Thu, Oct 27, 2016 at 10:21 AM, Alexandre Torgue
> >> <alexandre.torgue@st.com> wrote:
> >> > Hi Bruno,
> >> >
> >> >
> >> > On 10/27/2016 12:43 PM, Bruno Herrera wrote:
> >> >>
> >> >> Hi Alex,
> >> >>
> >> >> On Wed, Oct 26, 2016 at 7:09 AM, Alexandre Torgue
> >> >> <alexandre.torgue@st.com> wrote:
> >> >>>
> >> >>> Hi Bruno,
> >> >>>
> >> >>> On 10/25/2016 11:06 PM, Bruno Herrera wrote:
> >> >>>>
> >> >>>>
> >> >>>> Hi Alexandre,
> >> >>>>
> >> >>>>>
> >> >>>>> stm32f469-disco and stm32f429-eval boards use SDRAM start address
> >> >>>>> remapping
> >> >>>>> (to @0) to boost performances. A DMA translation through
> >> >>>>> "dma-ranges"
> >> >>>>> property was needed for other masters than the M4 CPU.
> >> >>>>> stm32f429-disco doesn't use remapping so doesn't need this DMA
> >> >>>>> translation.
> >> >>>>> This patches moves this DMA translation definition from stm32f429
> >> >>>>> soc
> >> >>>>> file
> >> >>>>> to board files.
> >> >>>>>
> >> >>>>> Signed-off-by: Alexandre TORGUE <alexandre.torgue@st.com>
> >> >>>>>
> >> >>>>> diff --git a/arch/arm/boot/dts/stm32429i-eval.dts
> >> >>>>> b/arch/arm/boot/dts/stm32429i-eval.dts
> >> >>>>> index 13c7cd2..a763c15 100644
> >> >>>>> --- a/arch/arm/boot/dts/stm32429i-eval.dts
> >> >>>>> +++ b/arch/arm/boot/dts/stm32429i-eval.dts
> >> >>>>> @@ -82,6 +82,10 @@
> >> >>>>>                 };
> >> >>>>>         };
> >> >>>>>
> >> >>>>> +       soc {
> >> >>>>> +               dma-ranges = <0xc0000000 0x0 0x10000000>;
> >> >>>>> +       };
> >> >>>>> +
> >> >>>>>         usbotg_hs_phy: usbphy {
> >> >>>>>                 #phy-cells = <0>;
> >> >>>>>                 compatible = "usb-nop-xceiv";
> >> >>>>
> >> >>>>
> >> >>>>
> >> >>>> Shouldn't also the peripheral dma-ranges property move to board
> >> >>>> specific
> >> >>>> too?
> >> >>>> I  had this patch for while but I didn't had the time to submit:
> >> >>>
> >> >>>
> >> >>>
> >> >>> Well spot I forgot it. Actually, discussing with Arnd ysterday on
> >> >>> IIRC,
> >> >>> empty dma-ranges is not needed. Can you test on your side by
> removing
> >> >>> dma-ranges in usb node please ?
> >> >>
> >> >> Unfortunately will take a time for me to set up this environment on
> >> >> the STM32F4-EVAL board.
> >> >> And on the discovery boards we dont have this scenario. That was the
> >> >> main reason I did not submit the patch right away.
> >> >> My conclusion and I might be wrong but is based on the my tests with
> >> >> SDIO device at STM32F469I-DISCO board.
> >> >>
> >> >> I started this issue as discussion at ST Forum but Maxime gave me the
> >> >> hint.
> >> >>
> >> >>
> >> >>
> >> >> https://my.st.com/public/STe2ecommunities/mcu/Lists/
> cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy%2est%2ecom%2fpublic%
> 2fSTe2ecommunities%2fmcu%2fLists%2fcortex_mx_stm32%
> 2fDMA2%20and%20SYSCFG_MEMRMP%20relationship&FolderCTID=
> 0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46
> A77F0FFD06506F5B&currentviews=44
> >> >>
> >> >>> I will push a v2 by removing empty dma-ranges if tests are ok in
> your
> >> >>> side.
> >> >>
> >> >>
> >> >> From my understating/conclusion is: when empty property(dma-ranges)
> is
> >> >> the device node, the mapping will be taken in consideration when
> using
> >> >> DMA otherwise the mapping is ignored.
> >> >> And in the SDIO case it is needed for DEV->MEM(SDRAM) and
> >> >> MEM(SDRAM)->DEV. If it is not the case for the devices in question so
> >> >> I suppose it can work without the property.
> >> >
> >> >
> >> > For sure translation has to be done but I'm not sure that an empty
> >> > "dma-ranges" is needed in device node to activate it. For Ethernet
> empty
> >> > "dma-ranges" is not needed. I will try with usb.
> >>
> >> In the case of SDIO it is needed. As example this is my working SDIO
> node:
> >>
> >> sdio: sdio@40012c00 {
> >> compatible = "arm,pl18x", "arm,primecell";
> >> arm,primecell-periphid = <0x00480181>;
> >> reg = <0x40012c00 0x400>;
> >> dmas =  <&dma2 6 4 0x10400 0x3>, /* Logical - DevToMem */
> >> <&dma2 3 4 0x10400 0x3>; /* Logical - MemToDev */
> >> dma-names = "rx", "tx";
> >> clocks = <&rcc 0 171>;
> >> clock-names = "apb_pclk";
> >> interrupts = <49>;
> >> status = "disabled";
> >> };
> >>
> >> &sdio {
> >> status = "okay";
> >> vmmc-supply = <&wlan_en>;
> >> bus-width = <4>;
> >> max-frequency = <24000000>;
> >> pinctrl-names = "default";
> >> pinctrl-0 = <&sdio_pins>;
> >> ti,non-removable;
> >> ti,needs-special-hs-handling;
> >> dma-ranges;
> >> cap-power-off-card;
> >> keep-power-in-suspend;
> >>
> >> #address-cells = <1>;
> >> #size-cells = <0>;
> >> wlcore: wlcore@0 {
> >> compatible = "ti,wl1835";
> >> reg = <2>;
> >> interrupt-parent = <&gpioa>;
> >> interrupts = <8 IRQ_TYPE_EDGE_RISING>;
> >> };
> >> };
> >>
> >> >
> >> > alex
> >> >
> >> >
> >> >>
> >> >>>
> >> >>> Thanks in advance
> >> >>> Alex
> >> >>>
> >> >>>
> >> >>>>
> >> >>>> Author: Bruno Herrera <bruherrera@gmail.com>
> >> >>>> Date:   Sun Oct 16 14:50:00 2016 -0200
> >> >>>>
> >> >>>>     ARM: DT: STM32: Use dma-ranges property per board not at dtsi
> >> >>>> file
> >> >>>>
> >> >>>> diff --git a/arch/arm/boot/dts/stm32429i-eval.dts
> >> >>>> b/arch/arm/boot/dts/stm32429i-eval.dts
> >> >>>> index 6bfc595..2a22a82 100644
> >> >>>> --- a/arch/arm/boot/dts/stm32429i-eval.dts
> >> >>>> +++ b/arch/arm/boot/dts/stm32429i-eval.dts
> >> >>>> @@ -52,6 +52,10 @@
> >> >>>>         model = "STMicroelectronics STM32429i-EVAL board";
> >> >>>>         compatible = "st,stm32429i-eval", "st,stm32f429";
> >> >>>>
> >> >>>> +       soc {
> >> >>>> +               dma-ranges = <0xC0000000 0x0 0x10000000>;
> >> >>>> +       };
> >> >>>> +
> >> >>>>         chosen {
> >> >>>>                 bootargs = "root=/dev/ram rdinit=/linuxrc";
> >> >>>>                 stdout-path = "serial0:115200n8";
> >> >>>> @@ -96,6 +100,7 @@
> >> >>>>
> >> >>>>  &ethernet0 {
> >> >>>>         status = "okay";
> >> >>>> +       dma-ranges;
> >> >>>>         pinctrl-0       = <&ethernet0_mii>;
> >> >>>>         pinctrl-names   = "default";
> >> >>>>         phy-mode        = "mii-id";
> >> >>>> @@ -116,6 +121,7 @@
> >> >>>>  };
> >> >>>>
> >> >>>>  &usbotg_hs {
> >> >>>> +       dma-ranges;
> >> >>>>         dr_mode = "host";
> >> >>>>         phys = <&usbotg_hs_phy>;
> >> >>>>         phy-names = "usb2-phy";
> >> >>>> diff --git a/arch/arm/boot/dts/stm32f429.dtsi
> >> >>>> b/arch/arm/boot/dts/stm32f429.dtsi
> >> >>>> index 7d624a2..697a133 100644
> >> >>>> --- a/arch/arm/boot/dts/stm32f429.dtsi
> >> >>>> +++ b/arch/arm/boot/dts/stm32f429.dtsi
> >> >>>> @@ -59,7 +59,6 @@
> >> >>>>         };
> >> >>>>
> >> >>>>         soc {
> >> >>>> -               dma-ranges = <0xc0000000 0x0 0x10000000>;
> >> >>>>
> >> >>>>                 timer2: timer@40000000 {
> >> >>>>                         compatible = "st,stm32-timer";
> >> >>>> @@ -472,13 +471,11 @@
> >> >>>>                         st,syscon = <&syscfg 0x4>;
> >> >>>>                         snps,pbl = <8>;
> >> >>>>                         snps,mixed-burst;
> >> >>>> -                       dma-ranges;
> >> >>>>                         status = "disabled";
> >> >>>>                 };
> >> >>>>
> >> >>>>                 usbotg_hs: usb@40040000 {
> >> >>>>                         compatible = "snps,dwc2";
> >> >>>> -                       dma-ranges;
> >> >>>>                         reg = <0x40040000 0x40000>;
> >> >>>>                         interrupts = <77>;
> >> >>>>                         clocks = <&rcc 0 29>;
> >> >>>>
> >> >>>>
> >> >>>>> diff --git a/arch/arm/boot/dts/stm32f429.dtsi
> >> >>>>> b/arch/arm/boot/dts/stm32f429.dtsi
> >> >>>>> index 0596d60..3a1cfdd 100644
> >> >>>>> --- a/arch/arm/boot/dts/stm32f429.dtsi
> >> >>>>> +++ b/arch/arm/boot/dts/stm32f429.dtsi
> >> >>>>> @@ -59,8 +59,6 @@
> >> >>>>>         };
> >> >>>>>
> >> >>>>>         soc {
> >> >>>>> -               dma-ranges = <0xc0000000 0x0 0x10000000>;
> >> >>>>> -
> >> >>>>>                 timer2: timer@40000000 {
> >> >>>>>                         compatible = "st,stm32-timer";
> >> >>>>>                         reg = <0x40000000 0x400>;
> >> >>>>> diff --git a/arch/arm/boot/dts/stm32f469-disco.dts
> >> >>>>> b/arch/arm/boot/dts/stm32f469-disco.dts
> >> >>>>> index 9e73656..c2213c0 100644
> >> >>>>> --- a/arch/arm/boot/dts/stm32f469-disco.dts
> >> >>>>> +++ b/arch/arm/boot/dts/stm32f469-disco.dts
> >> >>>>> @@ -64,6 +64,10 @@
> >> >>>>>         aliases {
> >> >>>>>                 serial0 = &usart3;
> >> >>>>>         };
> >> >>>>> +
> >> >>>>> +       soc {
> >> >>>>> +               dma-ranges = <0xc0000000 0x0 0x10000000>;
> >> >>>>> +       };
> >> >>>>>  };
> >> >>>>>
> >> >>>>>  &clk_hse {
> >> >>>>> --
> >> >>>>
> >> >>>>
> >> >>>>
> >> >>>>
> >> >>>> Br.,
> >> >>>> Bruno
> >> >>>>
> >> >>>
> >> >
> >>
> >> _______________________________________________
> >> linux-arm-kernel mailing list
> >> linux-arm-kernel@lists.infradead.org
> >> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> >
> >
>

[-- Attachment #2: Type: text/html, Size: 16590 bytes --]

^ permalink raw reply

* [PATCH] ARM: shmobile: r8a7779/marzen: Add board part number to DT bindings
From: Geert Uytterhoeven @ 2016-10-31 13:43 UTC (permalink / raw)
  To: Simon Horman, Magnus Damm
  Cc: devicetree, linux-renesas-soc, linux-arm-kernel,
	Geert Uytterhoeven

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
 Documentation/devicetree/bindings/arm/shmobile.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/arm/shmobile.txt b/Documentation/devicetree/bindings/arm/shmobile.txt
index 44b3129a8ded539b..d2e408795889fbec 100644
--- a/Documentation/devicetree/bindings/arm/shmobile.txt
+++ b/Documentation/devicetree/bindings/arm/shmobile.txt
@@ -63,7 +63,7 @@ Boards:
     compatible = "renesas,kzm9g", "renesas,sh73a0"
   - Lager (RTP0RC7790SEB00010S)
     compatible = "renesas,lager", "renesas,r8a7790"
-  - Marzen
+  - Marzen (R0P7779A00010S)
     compatible = "renesas,marzen", "renesas,r8a7779"
   - Porter (M2-LCDP)
     compatible = "renesas,porter", "renesas,r8a7791"
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH 1/3] Documentation: dt: Add TI SCI clock driver
From: Tero Kristo @ 2016-10-31 12:50 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-clk-u79uwXL29TY76Z2rM5mHXA,
	mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ,
	ssantosh-DgEjT+Ai2ygdnm+yROfE0A, nm-l0cyMroinI0,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161030204121.qvb5d33dh65awwzx@rob-hp-laptop>

On 30/10/16 22:41, Rob Herring wrote:
> On Fri, Oct 21, 2016 at 03:45:59PM +0300, Tero Kristo wrote:
>> Add a clock implementation, TI SCI clock, that will hook to the common
>> clock framework, and allow each clock to be controlled via TI SCI
>> protocol.
>>
>> Signed-off-by: Tero Kristo <t-kristo-l0cyMroinI0@public.gmane.org>
>> ---
>>  .../devicetree/bindings/clock/ti,sci-clk.txt       | 37 ++++++++++++++++++++++
>>  MAINTAINERS                                        |  1 +
>>  2 files changed, 38 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/clock/ti,sci-clk.txt
>>
>> diff --git a/Documentation/devicetree/bindings/clock/ti,sci-clk.txt b/Documentation/devicetree/bindings/clock/ti,sci-clk.txt
>> new file mode 100644
>> index 0000000..bfc3ca4
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/clock/ti,sci-clk.txt
>> @@ -0,0 +1,37 @@
>> +Texas Instruments TI-SCI Clocks
>> +===============================
>> +
>> +All clocks on Texas Instruments' SoCs that contain a System Controller,
>> +are only controlled by this entity. Communication between a host processor
>> +running an OS and the System Controller happens through a protocol known
>> +as TI-SCI[1]. This clock implementation plugs into the common clock
>> +framework and makes use of the TI-SCI protocol on clock API requests.
>> +
>> +[1] Documentation/devicetree/bindings/arm/keystone/ti,sci.txt
>> +
>> +Required properties:
>> +-------------------
>> +- compatible: Must be "ti,k2g-sci-clk"
>> +- #clock-cells: Shall be 2.
>> +  In clock consumers, this cell represents the device ID and clock ID
>> +  exposed by the PM firmware. The assignments can be found in the header
>> +  files <dt-bindings/genpd/<soc>.h> (which covers the device IDs) and
>> +  <dt-bindings/clock/<soc>.h> (which covers the clock IDs), where <soc>
>> +  is the SoC involved, for example 'k2g'.
>> +
>> +Examples:
>> +--------
>> +
>> +pmmc: pmmc {
>> +	compatible = "ti,k2g-sci";
>> +
>> +	k2g_clks: k2g_clks {
>
> Use "clocks" for node name instead.
>
>> +		compatible = "ti,k2g-sci-clk";
>
> I'm starting to think all these child nodes for SCI are pointless. Is
> there any reason why the parent node can't be the clock provider (along
> with all the other providers it acks as)?

I believe the only reason to keep them separate is to have kernel side 
of things modular. If we have separate nodes, the drivers can be probed 
separately.

If not, we need to build one huge blob with all the features in it, so 
the main driver can probe everything in one go, with annoying 
back-and-forth callbacks in place (assuming we still want to keep stuff 
somehow modular.)

-Tero

>
>> +		#clock-cells = <2>;
>> +	};
>> +};
>> +
>> +uart0: serial@2530c00 {
>> +	compatible = "ns16550a";
>> +	clocks = <&k2g_clks K2G_DEV_UART0 0>;
>> +};

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

^ permalink raw reply

* Re: [RFC 1/2] mmc: sdhci: dt: Add device tree properties sdhci-caps and sdhci-caps-mask
From: Adrian Hunter @ 2016-10-31 12:34 UTC (permalink / raw)
  To: Jaehoon Chung, Ulf Hansson, Zach Brown
  Cc: Rob Herring, Mark Rutland, linux-mmc,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <6031045c-0eb2-7bea-1efd-874dade0f009-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>

On 31/10/16 13:59, Jaehoon Chung wrote:
> On 10/28/2016 05:12 PM, Ulf Hansson wrote:
>> On 25 October 2016 at 21:58, Zach Brown <zach.brown-acOepvfBmUk@public.gmane.org> wrote:
>>> On some systems the sdhci capabilty registers are incorrect for one
>>> reason or another.
>>>
>>> The sdhci-caps-mask property specifies which bits in the registers
>>> are incorrect and should be turned off before using sdhci-caps to turn
>>> on bits.
>>>
>>> The sdhci-caps property specifies which bits should be turned on.
>>>
>>> Signed-off-by: Zach Brown <zach.brown-acOepvfBmUk@public.gmane.org>
>>> ---
>>>  Documentation/devicetree/bindings/mmc/mmc.txt | 7 +++++++
>>>  1 file changed, 7 insertions(+)
>>>
>>> diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt
>>> index 8a37782..1415aa0 100644
>>> --- a/Documentation/devicetree/bindings/mmc/mmc.txt
>>> +++ b/Documentation/devicetree/bindings/mmc/mmc.txt
>>
>> The bindings in this document are common mmc DT bindings, not bindings
>> specific to a mmc controller.
>>
>> So unless these bindings are applicable for another controller than
>> sdhci, I suggest we create a new file to document these.
>> How about Documentation/devicetree/bindings/mmc/sdhci.txt?
>>
>>> @@ -52,6 +52,13 @@ Optional properties:
>>>  - no-sdio: controller is limited to send sdio cmd during initialization
>>>  - no-sd: controller is limited to send sd cmd during initialization
>>>  - no-mmc: controller is limited to send mmc cmd during initialization
>>> +- sdhci-caps-mask: The sdhci capabilities registers are incorrect. This 64bit
>>
>> /s/registers/register
>>
>> This applies to some more places below as well.
>>
>>> +  property corresponds to the bits in the sdhci capabilty registers. If the bit
>>> +  is on in the mask then the bit is incorrect in the registers and should be
>>> +  turned off.
>>> +- sdhci-caps: The sdhci capabilities registers are incorrect. This 64bit
>>> +  property corresponds to the bits in the sdhci capability registers. If the
>>> +  bit is on in the property then the bit should be on in the reigsters.
>>
>> /s/reigsters/register
>>
>>>
>>>  *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
>>>  polarity properties, we have to fix the meaning of the "normal" and "inverted"
>>> --
>>> 2.7.4
>>>
>>
>> Overall, I like this idea as it gives us good flexibility. Thus it
>> should avoid us to having to add any further new similar "sdhci broken
>> cap" DT binding. We could also decide to start deprecate some of the
>> existing sdhci bindings, if we think that makes sense.
>>
>> The downside is that we get a "magic" hex value in the dts. Although,
>> people could address this issue by providing some comments about what
>> the bits it means in the dts files themselves.
> 
> I think it's not good about getting "magic" hex value.
> In my experience, it's too difficult what bits means and calculate..
> Because some people who i know had already used like this.(locally..)
> 
> It needs to consider this...otherwise..it should become really complex magic code.

The bits we use are listed in sdhci.h and how we use them can be determined
from the sdhci source code.  Also, from the hardware perspective, there is
the SDHCI specification.  So what the bits mean is readily available.

With regard to calculating the values, won't it be obvious from testing if
they are wrong?

> 
> Best Regards,
> Jaehoon Chung
> 
>>
>> Let's see what Rob thinks about this.
>>
>> Kind regards
>> Uffe
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>>
>>
> 
> 

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

^ permalink raw reply

* RE: [PATCH v2 1/3] soc: Support for NPS HW scheduling
From: Noam Camus @ 2016-10-31 12:26 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: robh+dt@kernel.org, mark.rutland@arm.com, tglx@linutronix.de,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20161031102659.GA1506@mai>

> From: Daniel Lezcano [mailto:daniel.lezcano@linaro.org] 
> Sent: Monday, October 31, 2016 12:27 PM
>> 
>> This new header file is for NPS400 SoC (part of ARC architecture).
>> The header file includes macros for save/restore of HW scheduling.
>> The control of HW scheduling is acheived by writing core registers.

>s/acheived/achieved/
Thanks will update in V4 of this patch set
...

>> +#ifndef SOC_NPS_MTM_H
>> +#define SOC_NPS_MTM_H
>> +
>> +#define CTOP_INST_HWSCHD_OFF_R3                 0x3B6F00BF
>> +#define CTOP_INST_HWSCHD_RESTORE_R3             0x3E6F70C3
>> +
>> +static inline void hw_schd_save(unsigned int *flags) {
>> +	__asm__ __volatile__(
>> +	"       .word %1\n"
>> +	"       st r3,[%0]\n"
>> +	:
>> +	: "r"(flags), "i"(CTOP_INST_HWSCHD_OFF_R3)
>> +	: "r3", "memory");
>> +}
>> +

>Wouldn't make sense to change the macro name to CTOP_INST_HWSCHD_SAVE_R3 ?
The save of state into register (R3) by this dedicated instruction is only part of action which main purpose
Is to turn off the HW scheduler within this core (we call CTOP).
We use it  (Off/On) at places we wish to keep consistency of data where more than one HW thread can access.
So I believe this way macro name reflects properly the functionality of this instruction and the API functions
schd_save()/schd_restore() provide similar format like with IRQs.

> +static inline void hw_schd_restore(unsigned int flags) {
> +	__asm__ __volatile__(
> +	"       mov r3, %0\n"
> +	"       .word %1\n"
> +	:
> +	: "r"(flags), "i"(CTOP_INST_HWSCHD_RESTORE_R3)
> +	: "r3");
> +}
> +
> +#endif /* SOC_NPS_MTM_H */
> --
> 1.7.1
> 

^ permalink raw reply

* Re: [RFC 1/2] mmc: sdhci: dt: Add device tree properties sdhci-caps and sdhci-caps-mask
From: Jaehoon Chung @ 2016-10-31 11:59 UTC (permalink / raw)
  To: Ulf Hansson, Zach Brown
  Cc: Adrian Hunter, Rob Herring, Mark Rutland, linux-mmc,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CAPDyKFpKoXd9yuX5vR-CACtXKGysmV0AqKnjxQ+hTGU+kgwceQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On 10/28/2016 05:12 PM, Ulf Hansson wrote:
> On 25 October 2016 at 21:58, Zach Brown <zach.brown-acOepvfBmUk@public.gmane.org> wrote:
>> On some systems the sdhci capabilty registers are incorrect for one
>> reason or another.
>>
>> The sdhci-caps-mask property specifies which bits in the registers
>> are incorrect and should be turned off before using sdhci-caps to turn
>> on bits.
>>
>> The sdhci-caps property specifies which bits should be turned on.
>>
>> Signed-off-by: Zach Brown <zach.brown-acOepvfBmUk@public.gmane.org>
>> ---
>>  Documentation/devicetree/bindings/mmc/mmc.txt | 7 +++++++
>>  1 file changed, 7 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt
>> index 8a37782..1415aa0 100644
>> --- a/Documentation/devicetree/bindings/mmc/mmc.txt
>> +++ b/Documentation/devicetree/bindings/mmc/mmc.txt
> 
> The bindings in this document are common mmc DT bindings, not bindings
> specific to a mmc controller.
> 
> So unless these bindings are applicable for another controller than
> sdhci, I suggest we create a new file to document these.
> How about Documentation/devicetree/bindings/mmc/sdhci.txt?
> 
>> @@ -52,6 +52,13 @@ Optional properties:
>>  - no-sdio: controller is limited to send sdio cmd during initialization
>>  - no-sd: controller is limited to send sd cmd during initialization
>>  - no-mmc: controller is limited to send mmc cmd during initialization
>> +- sdhci-caps-mask: The sdhci capabilities registers are incorrect. This 64bit
> 
> /s/registers/register
> 
> This applies to some more places below as well.
> 
>> +  property corresponds to the bits in the sdhci capabilty registers. If the bit
>> +  is on in the mask then the bit is incorrect in the registers and should be
>> +  turned off.
>> +- sdhci-caps: The sdhci capabilities registers are incorrect. This 64bit
>> +  property corresponds to the bits in the sdhci capability registers. If the
>> +  bit is on in the property then the bit should be on in the reigsters.
> 
> /s/reigsters/register
> 
>>
>>  *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
>>  polarity properties, we have to fix the meaning of the "normal" and "inverted"
>> --
>> 2.7.4
>>
> 
> Overall, I like this idea as it gives us good flexibility. Thus it
> should avoid us to having to add any further new similar "sdhci broken
> cap" DT binding. We could also decide to start deprecate some of the
> existing sdhci bindings, if we think that makes sense.
> 
> The downside is that we get a "magic" hex value in the dts. Although,
> people could address this issue by providing some comments about what
> the bits it means in the dts files themselves.

I think it's not good about getting "magic" hex value.
In my experience, it's too difficult what bits means and calculate..
Because some people who i know had already used like this.(locally..)

It needs to consider this...otherwise..it should become really complex magic code.

Best Regards,
Jaehoon Chung

> 
> Let's see what Rob thinks about this.
> 
> Kind regards
> Uffe
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 
> 

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

^ permalink raw reply

* [PATCH v2 7/7] soc: renesas: Identify SoC and register with the SoC bus
From: Geert Uytterhoeven @ 2016-10-31 11:30 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman, Yangbo Lu, Simon Horman,
	Magnus Damm, Rob Herring, Mark Rutland
  Cc: Dirk Behme, linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven
In-Reply-To: <1477913455-5314-1-git-send-email-geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>

Identify the SoC type and revision, and register this information with
the SoC bus, so it is available under /sys/devices/soc0/, and can be
checked where needed using soc_device_match().

Identification is done using the Product Register or Common Chip Code
Register, as declared in DT, or using a hardcoded fallback if missing.

Example:

    Detected Renesas r8a7791 (0x47) ES1.0
    ...
    # cat /sys/devices/soc0/{family,machine,soc_id,revision}
    Renesas
    Koelsch
    r8a7791
    ES1.0

Signed-off-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
---
v2:
  - Drop SoC families and family names; use fixed "Renesas" instead,
  - Drop EMEV2, which doesn't have a chip ID register, and doesn't share
    devices with other SoCs,
  - Drop RZ/A1H and R-CAR M1A, which don't have chip ID registers (for
    M1A: not accessible from the ARM core?),
  - On arm, move "select SOC_BUS" from ARCH_RENESAS to Kconfig symbols
    for SoCs that provide a chip ID register,
  - Build renesas-soc only if SOC_BUS is enabled,
  - Use "renesas,prr" and "renesas,cccr" device nodes in DT if
    available, else fall back to hardcoded addresses for compatibility
    with existing DTBs,
  - Remove verification of product IDs; just print the ID instead,
  - Don't register the SoC bus if the chip ID register is missing,
  - Change R-Mobile APE6 fallback to use PRR instead of CCCR (it has
    both).
---
 arch/arm/mach-shmobile/Kconfig    |   3 +
 arch/arm64/Kconfig.platforms      |   1 +
 drivers/soc/renesas/Makefile      |   2 +
 drivers/soc/renesas/renesas-soc.c | 130 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 136 insertions(+)
 create mode 100644 drivers/soc/renesas/renesas-soc.c

diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig
index 6fbd9b7d2d67a18f..dd4d52dd71b4eab2 100644
--- a/arch/arm/mach-shmobile/Kconfig
+++ b/arch/arm/mach-shmobile/Kconfig
@@ -21,11 +21,13 @@ config ARCH_RCAR_GEN2
 	select PM
 	select PM_GENERIC_DOMAINS
 	select RENESAS_IRQC
+	select SOC_BUS
 	select SYS_SUPPORTS_SH_CMT
 
 config ARCH_RMOBILE
 	bool
 	select PM_RMOBILE
+	select SOC_BUS
 	select SYS_SUPPORTS_SH_CMT
 	select SYS_SUPPORTS_SH_TMU
 
@@ -80,6 +82,7 @@ config ARCH_R8A7778
 config ARCH_R8A7779
 	bool "R-Car H1 (R8A77790)"
 	select ARCH_RCAR_GEN1
+	select SOC_BUS
 
 config ARCH_R8A7790
 	bool "R-Car H2 (R8A77900)"
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 101794f5ce1008b7..b751c6891c6a51ed 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -143,6 +143,7 @@ config ARCH_RENESAS
 	select PM
 	select PM_GENERIC_DOMAINS
 	select RENESAS_IRQC
+	select SOC_BUS
 	help
 	  This enables support for the ARMv8 based Renesas SoCs.
 
diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile
index 9e0bb329594c4fca..1652df037955e0e6 100644
--- a/drivers/soc/renesas/Makefile
+++ b/drivers/soc/renesas/Makefile
@@ -1,3 +1,5 @@
+obj-$(CONFIG_SOC_BUS)		+= renesas-soc.o
+
 obj-$(CONFIG_ARCH_R8A7743)	+= rcar-sysc.o r8a7743-sysc.o
 obj-$(CONFIG_ARCH_R8A7779)	+= rcar-sysc.o r8a7779-sysc.o
 obj-$(CONFIG_ARCH_R8A7790)	+= rcar-sysc.o r8a7790-sysc.o
diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c
new file mode 100644
index 0000000000000000..01104bfaefdb9f32
--- /dev/null
+++ b/drivers/soc/renesas/renesas-soc.c
@@ -0,0 +1,130 @@
+/*
+ * Renesas SoC Identification
+ *
+ * Copyright (C) 2014-2016 Glider bvba
+ *
+ * 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 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/sys_soc.h>
+
+
+#define CCCR	0xe600101c	/* Common Chip Code Register */
+#define PRR	0xff000044	/* Product Register */
+#define PRR3	0xfff00044	/* Product Register on R-Car Gen3 */
+
+static const struct of_device_id renesas_socs[] __initconst = {
+#ifdef CONFIG_ARCH_R8A73A4
+	{ .compatible = "renesas,r8a73a4",	.data = (void *)PRR, },
+#endif
+#ifdef CONFIG_ARCH_R8A7740
+	{ .compatible = "renesas,r8a7740",	.data = (void *)CCCR, },
+#endif
+#ifdef CONFIG_ARCH_R8A7743
+	{ .compatible = "renesas,r8a7743",	.data = (void *)PRR, },
+#endif
+#ifdef CONFIG_ARCH_R8A7745
+	{ .compatible = "renesas,r8a7745",	.data = (void *)PRR, },
+#endif
+#ifdef CONFIG_ARCH_R8A7779
+	{ .compatible = "renesas,r8a7779",	.data = (void *)PRR, },
+#endif
+#ifdef CONFIG_ARCH_R8A7790
+	{ .compatible = "renesas,r8a7790",	.data = (void *)PRR, },
+#endif
+#ifdef CONFIG_ARCH_R8A7791
+	{ .compatible = "renesas,r8a7791",	.data = (void *)PRR, },
+#endif
+#ifdef CONFIG_ARCH_R8A7792
+	{ .compatible = "renesas,r8a7792",	.data = (void *)PRR, },
+#endif
+#ifdef CONFIG_ARCH_R8A7793
+	{ .compatible = "renesas,r8a7793",	.data = (void *)PRR, },
+#endif
+#ifdef CONFIG_ARCH_R8A7794
+	{ .compatible = "renesas,r8a7794",	.data = (void *)PRR, },
+#endif
+#ifdef CONFIG_ARCH_R8A7795
+	{ .compatible = "renesas,r8a7795",	.data = (void *)PRR3, },
+#endif
+#ifdef CONFIG_ARCH_R8A7796
+	{ .compatible = "renesas,r8a7796",	.data = (void *)PRR3, },
+#endif
+#ifdef CONFIG_ARCH_SH73A0
+	{ .compatible = "renesas,sh73a0",	.data = (void *)CCCR, },
+#endif
+	{ /* sentinel */ }
+};
+
+static int __init renesas_soc_init(void)
+{
+	struct soc_device_attribute *soc_dev_attr;
+	const struct of_device_id *match;
+	void __iomem *chipid = NULL;
+	struct soc_device *soc_dev;
+	struct device_node *np;
+	unsigned int product;
+
+	np = of_find_matching_node_and_match(NULL, renesas_socs, &match);
+	if (!np)
+		return -ENODEV;
+
+	of_node_put(np);
+
+	/* Try PRR first, then CCCR, then hardcoded fallback */
+	np = of_find_compatible_node(NULL, NULL, "renesas,prr");
+	if (!np)
+		np = of_find_compatible_node(NULL, NULL, "renesas,cccr");
+	if (np) {
+		chipid = of_iomap(np, 0);
+		of_node_put(np);
+	} else if (match->data) {
+		chipid = ioremap((uintptr_t)match->data, 4);
+	}
+	if (!chipid)
+		return -ENODEV;
+
+	product = readl(chipid);
+	iounmap(chipid);
+
+	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+	if (!soc_dev_attr)
+		return -ENOMEM;
+
+	np = of_find_node_by_path("/");
+	of_property_read_string(np, "model", &soc_dev_attr->machine);
+	of_node_put(np);
+
+	soc_dev_attr->family = "Renesas";
+	soc_dev_attr->revision = kasprintf(GFP_KERNEL, "ES%u.%u",
+					   ((product >> 4) & 0xf) + 1,
+					   product & 0xf);
+	soc_dev_attr->soc_id = kstrdup_const(strchr(match->compatible, ',') + 1,
+					     GFP_KERNEL);
+	pr_info("Detected %s %s (0x%02x) %s\n", soc_dev_attr->family,
+		soc_dev_attr->soc_id, (product >> 8) & 0xff,
+		soc_dev_attr->revision);
+
+	soc_dev = soc_device_register(soc_dev_attr);
+	if (IS_ERR(soc_dev)) {
+		kfree(soc_dev_attr->revision);
+		kfree_const(soc_dev_attr->soc_id);
+		kfree(soc_dev_attr);
+		return PTR_ERR(soc_dev);
+	}
+
+	return 0;
+}
+core_initcall(renesas_soc_init);
-- 
1.9.1

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

^ permalink raw reply related

* [PATCH v2 6/7] arm64: dts: r8a7795: Add device node for PRR
From: Geert Uytterhoeven @ 2016-10-31 11:30 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman, Yangbo Lu, Simon Horman,
	Magnus Damm, Rob Herring, Mark Rutland
  Cc: Dirk Behme, linux-renesas-soc, linux-arm-kernel, linuxppc-dev,
	devicetree, linux-kernel, Geert Uytterhoeven
In-Reply-To: <1477913455-5314-1-git-send-email-geert+renesas@glider.be>

Add a device node for the Product Register, which provides SoC product
and revision information.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v2:
  - New.
---
 arch/arm64/boot/dts/renesas/r8a7795.dtsi | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
index 681f54422375f4f7..a39a702b904da73c 100644
--- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
@@ -321,6 +321,11 @@
 			#power-domain-cells = <0>;
 		};
 
+		prr: chipid@fff00044 {
+			compatible = "renesas,prr";
+			reg = <0 0xfff00044 0 4>;
+		};
+
 		sysc: system-controller@e6180000 {
 			compatible = "renesas,r8a7795-sysc";
 			reg = <0 0xe6180000 0 0x0400>;
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 5/7] ARM: shmobile: Document DT bindings for CCCR and PRR
From: Geert Uytterhoeven @ 2016-10-31 11:30 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman, Yangbo Lu, Simon Horman,
	Magnus Damm, Rob Herring, Mark Rutland
  Cc: Dirk Behme, linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven
In-Reply-To: <1477913455-5314-1-git-send-email-geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>

Add device tree binding documentation for the Common Chip Code Register
and Product Register, which provide SoC product and revision
information.

Signed-off-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
---
v2:
  - New.
---
 Documentation/devicetree/bindings/arm/shmobile.txt | 26 ++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/Documentation/devicetree/bindings/arm/shmobile.txt b/Documentation/devicetree/bindings/arm/shmobile.txt
index 15669642b5324412..bff1b596b235c535 100644
--- a/Documentation/devicetree/bindings/arm/shmobile.txt
+++ b/Documentation/devicetree/bindings/arm/shmobile.txt
@@ -79,3 +79,29 @@ Boards:
     compatible = "renesas,sk-rzg1m", "renesas,r8a7743"
   - Wheat
     compatible = "renesas,wheat", "renesas,r8a7792"
+
+
+Most Renesas ARM SoCs have one or two registers (Common Chip Code
+Register and/or Product Register) that allow to retrieve SoC product and
+revision information.  If present, device nodes for these devices should
+be added.
+
+Required properties:
+  - compatible: Must be one of:
+      - "renesas,cccr" for the Common Chip Code Register,
+      - "renesas,prr" for the Product Register.
+  - reg: Base address and length of the register block.
+
+
+Examples
+--------
+
+	cccr: chipid@e600101c {
+		compatible = "renesas,cccr";
+		reg = <0xe600101c 4>;
+	};
+
+	prr: chipid@ff000044 {
+		compatible = "renesas,prr";
+		reg = <0 0xff000044 0 4>;
+	};
-- 
1.9.1

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

^ permalink raw reply related

* [PATCH v2 4/7] base: soc: Provide a dummy implementation of soc_device_match()
From: Geert Uytterhoeven @ 2016-10-31 11:30 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman, Yangbo Lu, Simon Horman,
	Magnus Damm, Rob Herring, Mark Rutland
  Cc: Dirk Behme, linux-renesas-soc, linux-arm-kernel, linuxppc-dev,
	devicetree, linux-kernel, Geert Uytterhoeven
In-Reply-To: <1477913455-5314-1-git-send-email-geert+renesas@glider.be>

Provide a dummy implementation of soc_device_match(), to allow compiling
drivers that may be used on SoCs both with and without CONFIG_SOC_BUS,
and for compile testing.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v2:
  - New.
---
 include/linux/sys_soc.h | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/include/linux/sys_soc.h b/include/linux/sys_soc.h
index 9f5eb06f9fd87565..bed223b70217fd2d 100644
--- a/include/linux/sys_soc.h
+++ b/include/linux/sys_soc.h
@@ -35,6 +35,12 @@ struct soc_device *soc_device_register(
  */
 struct device *soc_device_to_device(struct soc_device *soc);
 
+#ifdef CONFIG_SOC_BUS
 const struct soc_device_attribute *soc_device_match(
 	const struct soc_device_attribute *matches);
+#else
+static inline const struct soc_device_attribute *soc_device_match(
+	const struct soc_device_attribute *matches) { return NULL; }
+#endif
+
 #endif /* __SOC_BUS_H */
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 3/7] base: soc: Check for NULL SoC device attributes
From: Geert Uytterhoeven @ 2016-10-31 11:30 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman, Yangbo Lu, Simon Horman,
	Magnus Damm, Rob Herring, Mark Rutland
  Cc: Dirk Behme, linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven
In-Reply-To: <1477913455-5314-1-git-send-email-geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>

If soc_device_match() is used to check the value of a specific
attribute that is not present for the current SoC, the kernel crashes
with a NULL pointer dereference.

Fix this by explicitly checking for the absence of a needed property,
and considering this a non-match.

Signed-off-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
Acked-by: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
Acked-by: Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>
---
v2:
  - Add Acked-by.
---
 drivers/base/soc.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/base/soc.c b/drivers/base/soc.c
index 04ee597fc3a3fda0..dc26e5949a320223 100644
--- a/drivers/base/soc.c
+++ b/drivers/base/soc.c
@@ -176,19 +176,23 @@ static int soc_device_match_one(struct device *dev, void *arg)
 	const struct soc_device_attribute *match = arg;
 
 	if (match->machine &&
-	    !glob_match(match->machine, soc_dev->attr->machine))
+	    (!soc_dev->attr->machine ||
+	     !glob_match(match->machine, soc_dev->attr->machine)))
 		return 0;
 
 	if (match->family &&
-	    !glob_match(match->family, soc_dev->attr->family))
+	    (!soc_dev->attr->family ||
+	     !glob_match(match->family, soc_dev->attr->family)))
 		return 0;
 
 	if (match->revision &&
-	    !glob_match(match->revision, soc_dev->attr->revision))
+	    (!soc_dev->attr->revision ||
+	     !glob_match(match->revision, soc_dev->attr->revision)))
 		return 0;
 
 	if (match->soc_id &&
-	    !glob_match(match->soc_id, soc_dev->attr->soc_id))
+	    (!soc_dev->attr->soc_id ||
+	     !glob_match(match->soc_id, soc_dev->attr->soc_id)))
 		return 0;
 
 	return 1;
-- 
1.9.1

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

^ permalink raw reply related

* [PATCH v2 2/7] base: soc: Introduce soc_device_match() interface
From: Geert Uytterhoeven @ 2016-10-31 11:30 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman, Yangbo Lu, Simon Horman,
	Magnus Damm, Rob Herring, Mark Rutland
  Cc: Dirk Behme, linux-renesas-soc, linux-arm-kernel, linuxppc-dev,
	devicetree, linux-kernel, Geert Uytterhoeven
In-Reply-To: <1477913455-5314-1-git-send-email-geert+renesas@glider.be>

From: Arnd Bergmann <arnd@arndb.de>

We keep running into cases where device drivers want to know the exact
version of the a SoC they are currently running on. In the past, this has
usually been done through a vendor specific API that can be called by a
driver, or by directly accessing some kind of version register that is
not part of the device itself but that belongs to a global register area
of the chip.

Common reasons for doing this include:

- A machine is not using devicetree or similar for passing data about
  on-chip devices, but just announces their presence using boot-time
  platform devices, and the machine code itself does not care about the
  revision.

- There is existing firmware or boot loaders with existing DT binaries
  with generic compatible strings that do not identify the particular
  revision of each device, but the driver knows which SoC revisions
  include which part.

- A prerelease version of a chip has some quirks and we are using the same
  version of the bootloader and the DT blob on both the prerelease and the
  final version. An update of the DT binding seems inappropriate because
  that would involve maintaining multiple copies of the dts and/or
  bootloader.

This patch introduces the soc_device_match() interface that is meant to
work like of_match_node() but instead of identifying the version of a
device, it identifies the SoC itself using a vendor-agnostic interface.

Unlike of_match_node(), we do not do an exact string compare but instead
use glob_match() to allow wildcards in strings.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
v2:
  - Add Acked-by.
---
 drivers/base/Kconfig    |  1 +
 drivers/base/soc.c      | 66 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/sys_soc.h |  3 +++
 3 files changed, 70 insertions(+)

diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index d02e7c0f5bfdff1c..2abea876c0a0c2c2 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -237,6 +237,7 @@ config GENERIC_CPU_AUTOPROBE
 
 config SOC_BUS
 	bool
+	select GLOB
 
 source "drivers/base/regmap/Kconfig"
 
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
index 028cef377fd49959..04ee597fc3a3fda0 100644
--- a/drivers/base/soc.c
+++ b/drivers/base/soc.c
@@ -13,6 +13,7 @@
 #include <linux/spinlock.h>
 #include <linux/sys_soc.h>
 #include <linux/err.h>
+#include <linux/glob.h>
 
 static DEFINE_IDA(soc_ida);
 
@@ -168,3 +169,68 @@ static int __init soc_bus_register(void)
 	return bus_register(&soc_bus_type);
 }
 core_initcall(soc_bus_register);
+
+static int soc_device_match_one(struct device *dev, void *arg)
+{
+	struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
+	const struct soc_device_attribute *match = arg;
+
+	if (match->machine &&
+	    !glob_match(match->machine, soc_dev->attr->machine))
+		return 0;
+
+	if (match->family &&
+	    !glob_match(match->family, soc_dev->attr->family))
+		return 0;
+
+	if (match->revision &&
+	    !glob_match(match->revision, soc_dev->attr->revision))
+		return 0;
+
+	if (match->soc_id &&
+	    !glob_match(match->soc_id, soc_dev->attr->soc_id))
+		return 0;
+
+	return 1;
+}
+
+/*
+ * soc_device_match - identify the SoC in the machine
+ * @matches: zero-terminated array of possible matches
+ *
+ * returns the first matching entry of the argument array, or NULL
+ * if none of them match.
+ *
+ * This function is meant as a helper in place of of_match_node()
+ * in cases where either no device tree is available or the information
+ * in a device node is insufficient to identify a particular variant
+ * by its compatible strings or other properties. For new devices,
+ * the DT binding should always provide unique compatible strings
+ * that allow the use of of_match_node() instead.
+ *
+ * The calling function can use the .data entry of the
+ * soc_device_attribute to pass a structure or function pointer for
+ * each entry.
+ */
+const struct soc_device_attribute *soc_device_match(
+	const struct soc_device_attribute *matches)
+{
+	int ret = 0;
+
+	if (!matches)
+		return NULL;
+
+	while (!ret) {
+		if (!(matches->machine || matches->family ||
+		      matches->revision || matches->soc_id))
+			break;
+		ret = bus_for_each_dev(&soc_bus_type, NULL, (void *)matches,
+				       soc_device_match_one);
+		if (!ret)
+			matches++;
+		else
+			return matches;
+	}
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(soc_device_match);
diff --git a/include/linux/sys_soc.h b/include/linux/sys_soc.h
index 2739ccb69571e444..9f5eb06f9fd87565 100644
--- a/include/linux/sys_soc.h
+++ b/include/linux/sys_soc.h
@@ -13,6 +13,7 @@ struct soc_device_attribute {
 	const char *family;
 	const char *revision;
 	const char *soc_id;
+	const void *data;
 };
 
 /**
@@ -34,4 +35,6 @@ struct soc_device *soc_device_register(
  */
 struct device *soc_device_to_device(struct soc_device *soc);
 
+const struct soc_device_attribute *soc_device_match(
+	const struct soc_device_attribute *matches);
 #endif /* __SOC_BUS_H */
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 1/7] base: soc: Early register bus when needed
From: Geert Uytterhoeven @ 2016-10-31 11:30 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman, Yangbo Lu, Simon Horman,
	Magnus Damm, Rob Herring, Mark Rutland
  Cc: Dirk Behme, linux-renesas-soc, linux-arm-kernel, linuxppc-dev,
	devicetree, linux-kernel, Geert Uytterhoeven
In-Reply-To: <1477913455-5314-1-git-send-email-geert+renesas@glider.be>

If soc_device_register() is called before soc_bus_register(), it crashes
with a NULL pointer dereference.

soc_bus_register() is already a core_initcall(), but drivers/base/ is
entered later than e.g. drivers/pinctrl/ and drivers/soc/. Hence there
are several subsystems that may need to know SoC revision information,
while it's not so easy to initialize the SoC bus even earlier using an
initcall.

To fix this, let soc_device_register() register the bus early if that
hasn't happened yet.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
v2:
  - Add Acked-by.
---
 drivers/base/soc.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/base/soc.c b/drivers/base/soc.c
index b63f23e6ad61b647..028cef377fd49959 100644
--- a/drivers/base/soc.c
+++ b/drivers/base/soc.c
@@ -113,6 +113,12 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr
 	struct soc_device *soc_dev;
 	int ret;
 
+	if (!soc_bus_type.p) {
+		ret = bus_register(&soc_bus_type);
+		if (ret)
+			goto out1;
+	}
+
 	soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL);
 	if (!soc_dev) {
 		ret = -ENOMEM;
@@ -156,6 +162,9 @@ void soc_device_unregister(struct soc_device *soc_dev)
 
 static int __init soc_bus_register(void)
 {
+	if (soc_bus_type.p)
+		return 0;
+
 	return bus_register(&soc_bus_type);
 }
 core_initcall(soc_bus_register);
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 0/7] soc: renesas: Identify SoC and register with the SoC bus
From: Geert Uytterhoeven @ 2016-10-31 11:30 UTC (permalink / raw)
  To: Arnd Bergmann, Greg Kroah-Hartman, Yangbo Lu, Simon Horman,
	Magnus Damm, Rob Herring, Mark Rutland
  Cc: Dirk Behme, linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven

	Hi all,

Some Renesas SoCs may exist in different revisions, providing slightly
different functionalities (e.g. R-Car H3 ES1.x and ES2.0), and behavior
(errate and quirks).  This needs to be catered for by drivers and/or
platform code.  The recently proposed soc_device_match() API seems like
a good fit to handle this.

This patch series implements the core infrastructure to provide SoC and
revision information through the SoC bus for Renesas ARM SoCs. It
consists of 7 patches:
  - Patches 1-4 provide soc_device_match(), with some related fixes,
  - Patches 5-7 implement identification of Renesas SoCs and
    registration with the SoC bus,

Changes compared to v1:
  - Add Acked-by,
  - New patches:
      - "[4/7] base: soc: Provide a dummy implementation of
	       soc_device_match()",
      - "[5/7] ARM: shmobile: Document DT bindings for CCCR and PRR",
      - "[6/7] arm64: dts: r8a7795: Add device node for PRR"
        (more similar patches available, I'm not yet spamming you all
	 with them),
  - Drop SoC families and family names; use fixed "Renesas" instead,
  - Drop EMEV2, which doesn't have a chip ID register, and doesn't share
    devices with other SoCs,
  - Drop RZ/A1H and R-CAR M1A, which don't have chip ID registers (for
    M1A: not accessible from the ARM core?),
  - On arm, move "select SOC_BUS" from ARCH_RENESAS to Kconfig symbols
    for SoCs that provide a chip ID register,
  - Build renesas-soc only if SOC_BUS is enabled,
  - Use "renesas,prr" and "renesas,cccr" device nodes in DT if
    available, else fall back to hardcoded addresses for compatibility
    with existing DTBs,
  - Remove verification of product IDs; just print the ID instead,
  - Don't register the SoC bus if the chip ID register is missing,
  - Change R-Mobile APE6 fallback to use PRR instead of CCCR (it has
    both).

Merge strategy:
  - In theory, patches 1-4 should go through Greg's driver core tree.
    But it's a hard dependency for all users.
    If people agree, I can provide an immutable branch in my
    renesas-drivers repository, to be merged by all interested parties.
    So far I'm aware of Freescale/NXP, and Renesas.
  - Patches 5-7 obviously have to go through Simon's Renesas tree (after
    merging the soc_device_match() core), and arm-soc.

Tested on (machine, soc_id, optional revision):
    EMEV2 KZM9D Board, emev2
    Genmai, r7s72100
    APE6EVM, r8a73a4, ES1.0
    armadillo 800 eva, r8a7740, ES2.0
    bockw, r8a7778
    marzen, r8a7779, ES1.0
    Lager, r8a7790, ES1.0
    Koelsch, r8a7791, ES1.0
    Porter, r8a7791, ES3.0
    Blanche, r8a7792, ES1.1
    Gose, r8a7793, ES1.0
    Alt, r8a7794, ES1.0
    Renesas Salvator-X board based on r8a7795, r8a7795, ES1.0
    Renesas Salvator-X board based on r8a7795, r8a7795, ES1.1
    Renesas Salvator-X board based on r8a7796, r8a7796, ES1.0
    KZM-A9-GT, sh73a0, ES2.0

For your convenience, this series (incl. more DT updates to add device
nodes for CCCR and PRR to all other Renesas ARM SoCs) is also available
in the topic/renesas-soc-id-v2 branch of my renesas-drivers git
repository at
git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers.git
Its first user is support for R-Car H3 ES2.0 in branch
topic/r8a7795-es2-v1-rebased2.

Thanks for your comments!

Arnd Bergmann (1):
  base: soc: Introduce soc_device_match() interface

Geert Uytterhoeven (6):
  base: soc: Early register bus when needed
  base: soc: Check for NULL SoC device attributes
  base: soc: Provide a dummy implementation of soc_device_match()
  ARM: shmobile: Document DT bindings for CCCR and PRR
  arm64: dts: r8a7795: Add device node for PRR
  soc: renesas: Identify SoC and register with the SoC bus

 Documentation/devicetree/bindings/arm/shmobile.txt |  26 +++++
 arch/arm/mach-shmobile/Kconfig                     |   3 +
 arch/arm64/Kconfig.platforms                       |   1 +
 arch/arm64/boot/dts/renesas/r8a7795.dtsi           |   5 +
 drivers/base/Kconfig                               |   1 +
 drivers/base/soc.c                                 |  79 +++++++++++++
 drivers/soc/renesas/Makefile                       |   2 +
 drivers/soc/renesas/renesas-soc.c                  | 130 +++++++++++++++++++++
 include/linux/sys_soc.h                            |   9 ++
 9 files changed, 256 insertions(+)
 create mode 100644 drivers/soc/renesas/renesas-soc.c

-- 
1.9.1

Gr{oetje,eeting}s,

						Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
							    -- Linus Torvalds
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH 10/10] arm64: configs: enable SDHCI driver for Xenon
From: Gregory CLEMENT @ 2016-10-31 11:09 UTC (permalink / raw)
  To: Ulf Hansson, Adrian Hunter, linux-mmc-u79uwXL29TY76Z2rM5mHXA
  Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
	Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Petazzoni,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Ziji Hu,
	Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai, Ryan Gao,
	Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu, Wilson Ding,
	Xueping Liu, Hilbert Zhang
In-Reply-To: <cover.86006f271b60cf7c0b4c5a51762a9dacca4c4718.1477911954.git-series.gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

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-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 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
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 9/10] arm64: dts: marvell: add sdhci support for Armada 7K/8K
From: Gregory CLEMENT @ 2016-10-31 11:09 UTC (permalink / raw)
  To: Ulf Hansson, Adrian Hunter, linux-mmc-u79uwXL29TY76Z2rM5mHXA
  Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
	Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Petazzoni,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Ziji Hu,
	Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai, Ryan Gao,
	Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu, Wilson Ding,
	Xueping Liu, Hilbert Zhang
In-Reply-To: <cover.86006f271b60cf7c0b4c5a51762a9dacca4c4718.1477911954.git-series.gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

Also enable it on the Armada 7040 DB board

Signed-off-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 arch/arm64/boot/dts/marvell/armada-7040-db.dts |  8 ++++++++
 arch/arm64/boot/dts/marvell/armada-ap806.dtsi  |  9 +++++++++
 2 files changed, 17 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..f8bdabdbd864 100644
--- a/arch/arm64/boot/dts/marvell/armada-7040-db.dts
+++ b/arch/arm64/boot/dts/marvell/armada-7040-db.dts
@@ -146,3 +146,11 @@
 &cpm_usb3_1 {
 	status = "okay";
 };
+
+&sdhci0 {
+	status = "okay";
+	bus-width = <4>;
+	no-1-8-v;
+	non-removable;
+	marvell,xenon-emmc;
+};
diff --git a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
index 7b6136182ad0..174c41b24d4c 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@6e0000 {
+				compatible = "marvell,xenon-sdhci";
+				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@6f4000 {
 				compatible = "marvell,ap806-system-controller",
 					     "syscon";
-- 
git-series 0.8.10
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 8/10] arm64: dts: marvell: add eMMC support for Armada 37xx
From: Gregory CLEMENT @ 2016-10-31 11:09 UTC (permalink / raw)
  To: Ulf Hansson, Adrian Hunter, linux-mmc-u79uwXL29TY76Z2rM5mHXA
  Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
	Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Petazzoni,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Ziji Hu,
	Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai, Ryan Gao,
	Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu, Wilson Ding,
	Xueping Liu, Hilbert Zhang
In-Reply-To: <cover.86006f271b60cf7c0b4c5a51762a9dacca4c4718.1477911954.git-series.gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

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

Signed-off-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 arch/arm64/boot/dts/marvell/armada-3720-db.dts |  8 ++++++++
 arch/arm64/boot/dts/marvell/armada-37xx.dtsi   | 11 +++++++++++
 2 files changed, 19 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..9107dd3e2a44 100644
--- a/arch/arm64/boot/dts/marvell/armada-3720-db.dts
+++ b/arch/arm64/boot/dts/marvell/armada-3720-db.dts
@@ -72,6 +72,14 @@
 	status = "okay";
 };
 
+&sdhci0 {
+	non-removable;
+	bus-width = <8>;
+	marvell,xenon-emmc;
+	marvell,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@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@e0000 {
 				compatible = "marvell,armada-3700-ahci";
 				reg = <0xe0000 0x2000>;
-- 
git-series 0.8.10
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 7/10] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC
From: Gregory CLEMENT @ 2016-10-31 11:09 UTC (permalink / raw)
  To: Ulf Hansson, Adrian Hunter, linux-mmc
  Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
	Rob Herring, devicetree, Thomas Petazzoni, linux-arm-kernel,
	Ziji Hu, Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai,
	Ryan Gao, Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu,
	Wilson Ding, Xueping Liu, Hilbert Zhang
In-Reply-To: <cover.86006f271b60cf7c0b4c5a51762a9dacca4c4718.1477911954.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>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
 MAINTAINERS                        |    2 +-
 drivers/mmc/host/Makefile          |    2 +-
 drivers/mmc/host/sdhci-xenon-phy.c | 1181 +++++++++++++++++++++++++++++-
 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, 1361 insertions(+), 2 deletions(-)
 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 d92f4175574b..bb33286aeb48 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7608,7 +7608,7 @@ MARVELL XENON MMC/SD/SDIO HOST CONTROLLER DRIVER
 M:	Ziji Hu <huziji@marvell.com>
 L:	linux-mmc@vger.kernel.org
 S:	Supported
-F:	drivers/mmc/host/sdhci-xenon.*
+F:	drivers/mmc/host/sdhci-xenon*
 F:	Documentation/devicetree/bindings/mmc/marvell,xenon-sdhci.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..af32f8842e0b
--- /dev/null
+++ b/drivers/mmc/host/sdhci-xenon-phy.c
@@ -0,0 +1,1181 @@
+/*
+ * 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);
+}
+
+static void __emmc_phy_disable_data_strobe(struct sdhci_host *host)
+{
+	u32 reg;
+
+	/* Disable SDHC Data Strobe */
+	reg = sdhci_readl(host, SDHC_SLOT_EMMC_CTRL);
+	reg &= ~ENABLE_DATA_STROBE;
+	sdhci_writel(host, reg, SDHC_SLOT_EMMC_CTRL);
+}
+
+#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);
+
+	reg = sdhci_readl(host, phy_regs->func_ctrl);
+	if ((timing == MMC_TIMING_UHS_DDR50) ||
+	    (timing == MMC_TIMING_MMC_HS400) ||
+	    (timing == MMC_TIMING_MMC_DDR52))
+		reg |= (DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) | CMD_DDR_MODE;
+	else
+		reg &= ~((DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) |
+			 CMD_DDR_MODE);
+
+	if (timing == MMC_TIMING_MMC_HS400)
+		reg &= ~DQ_ASYNC_MODE;
+	else
+		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);
+	else
+		__emmc_phy_disable_data_strobe(host);
+
+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, "marvell,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, "marvell,xenon-phy-slow-mode"))
+		params->slow_mode = true;
+	else
+		params->slow_mode = false;
+
+	if (!of_property_read_u32(np, "marvell,xenon-phy-znr", &value))
+		params->znr = value & ZNR_MASK;
+	else
+		params->znr = ZNR_DEF_VALUE;
+
+	if (!of_property_read_u32(np, "marvell,xenon-phy-zpr", &value))
+		params->zpr = value & ZPR_MASK;
+	else
+		params->zpr = ZPR_DEF_VALUE;
+
+	if (!of_property_read_u32(np, "marvell,xenon-phy-nr-success-tun",
+				  &value))
+		params->nr_tun_times = value & TUN_CONSECUTIVE_TIMES_MASK;
+	else
+		params->nr_tun_times = TUN_CONSECUTIVE_TIMES;
+
+	if (!of_property_read_u32(np, "marvell,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;
+	}
+	/*
+	 * Skip temp stages from HS400 t0 HS200:
+	 * from 200MHz to 52MHz in HS400
+	 * from HS400 to HS DDR in 52MHz
+	 * from HS DDR to HS in 52MHz
+	 * from HS to HS200 in 52MHz
+	 */
+	if (((priv->timing == MMC_TIMING_MMC_HS400) &&
+	     ((host->clock == MMC_HIGH_52_MAX_DTR) ||
+	      (ios->timing == MMC_TIMING_MMC_DDR52))) ||
+	    ((priv->timing == MMC_TIMING_MMC_DDR52) &&
+	     (ios->timing == MMC_TIMING_MMC_HS)) ||
+	    ((ios->timing == MMC_TIMING_MMC_HS200) &&
+	     (ios->clock == MMC_HIGH_52_MAX_DTR))) {
+		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;
+
+	if (mmc->card)
+		card = mmc->card;
+	else
+		/*
+		 * Only valid during initialization
+		 * before mmc->card is set
+		 */
+		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, "marvell,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 3ea059f2aaab..ee02014a2917 100644
--- a/drivers/mmc/host/sdhci-xenon.c
+++ b/drivers/mmc/host/sdhci-xenon.c
@@ -228,6 +228,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);
@@ -313,6 +314,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->emmc_slot)
 		return xenon_emmc_signal_voltage_switch(mmc, ios);
 
@@ -448,6 +451,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 4601d0a4b22f..e6ee47c227aa 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
@@ -99,6 +101,15 @@ struct sdhci_xenon_priv {
 	/* Whether this slot is for eMMC */
 	bool		emmc_slot;
 
+	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 for the speed mode in which
@@ -139,4 +150,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 6/10] mmc: sdhci-xenon: Add Marvell Xenon SDHC core functionality
From: Gregory CLEMENT @ 2016-10-31 11:09 UTC (permalink / raw)
  To: Ulf Hansson, Adrian Hunter, linux-mmc-u79uwXL29TY76Z2rM5mHXA
  Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
	Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Petazzoni,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Ziji Hu,
	Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai, Ryan Gao,
	Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu, Wilson Ding,
	Xueping Liu, Hilbert Zhang
In-Reply-To: <cover.86006f271b60cf7c0b4c5a51762a9dacca4c4718.1477911954.git-series.gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

From: Ziji Hu <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>

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-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
Signed-off-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 MAINTAINERS                    |   1 +-
 drivers/mmc/host/Kconfig       |   9 +-
 drivers/mmc/host/Makefile      |   3 +-
 drivers/mmc/host/sdhci-xenon.c | 594 ++++++++++++++++++++++++++++++++++-
 drivers/mmc/host/sdhci-xenon.h | 142 ++++++++-
 5 files changed, 749 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 850a0afb0c8d..d92f4175574b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7608,6 +7608,7 @@ MARVELL XENON MMC/SD/SDIO HOST CONTROLLER DRIVER
 M:	Ziji Hu <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
 L:	linux-mmc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
 S:	Supported
+F:	drivers/mmc/host/sdhci-xenon.*
 F:	Documentation/devicetree/bindings/mmc/marvell,xenon-sdhci.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..3ea059f2aaab
--- /dev/null
+++ b/drivers/mmc/host/sdhci-xenon.c
@@ -0,0 +1,594 @@
+/*
+ * Driver for Marvell SOC Platform Group Xenon SDHC as a platform device
+ *
+ * Copyright (C) 2016 Marvell, All Rights Reserved.
+ *
+ * Author:	Hu Ziji <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
+ * 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-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
+ * 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);
+
+	/* Disable 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-retuning 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_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;
+	u32 reg;
+
+	/*
+	 * 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;
+
+		reg = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+		reg &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
+		sdhci_writew(host, reg, SDHCI_HOST_CONTROL2);
+	} 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->emmc_slot)
+		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)) {
+		/* 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 {
+		/*
+		 * 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:
+	 * emmc: explicitly indicate whether this slot is for eMMC
+	 * slotno: the index of slot. Refer to SDHC_SYS_CFG_INFO register
+	 * tun-count: the interval between re-tuning
+	 * PHY type: "sdhc phy", "emmc phy 5.0" or "emmc phy 5.1"
+	 */
+	if (of_property_read_bool(np, "marvell,xenon-emmc"))
+		priv->emmc_slot = true;
+	else
+		priv->emmc_slot = false;
+
+	if (!of_property_read_u32(np, "marvell,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, "marvell,xenon-tun-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, "marvell,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,xenon-sdhci",},
+	{ .compatible = "marvell,armada-3700-sdhci",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, sdhci_xenon_dt_ids);
+
+static struct platform_driver sdhci_xenon_driver = {
+	.driver	= {
+		.name	= "xenon-sdhci",
+		.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-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>");
+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..4601d0a4b22f
--- /dev/null
+++ b/drivers/mmc/host/sdhci-xenon.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 Marvell, All Rights Reserved.
+ *
+ * Author:	Hu Ziji <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
+ * 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;
+	/* Whether this slot is for eMMC */
+	bool		emmc_slot;
+
+	/*
+	 * When initializing card, Xenon has to determine card type and
+	 * adjust Sampling Fixed delay for the speed mode in which
+	 * DLL tuning is not support.
+	 * 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.
+	 *
+	 * It is only valid during initialization after it is updated in
+	 * xenon_init_card().
+	 * Do not access this variable in normal transfers after
+	 * initialization completes.
+	 */
+	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
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related


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