All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 05/25 v7][RFC] phy: rcar-gen3-usb2: Add R-Car Gen3 USB2 PHY driver
@ 2015-08-24  2:27 Kuninori Morimoto
  0 siblings, 0 replies; only message in thread
From: Kuninori Morimoto @ 2015-08-24  2:27 UTC (permalink / raw)
  To: linux-sh

From: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>

This patch adds support for R-Car generation 3 USB2 PHY driver.
This SoC has 3 EHCI/OHCI channels, and the channel 0 is shared
with the HSUSB (USB2.0 peripheral) device.

So, the purpose of this driver is:
 1) initializes some registers of SoC specific to use the
    {ehci,ohci}-platform driver.

 2) detects id pin to select host or peripheral on the channel 0.

For now, this driver only supports 1) above.

Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
v5 -> v7

 - based on v7 Gen3 SoC patch set


 .../devicetree/bindings/phy/rcar-gen3-phy-usb2.txt |  37 +++
 drivers/phy/Kconfig                                |   6 +
 drivers/phy/Makefile                               |   1 +
 drivers/phy/phy-rcar-gen3-usb2.c                   | 262 +++++++++++++++++++++
 4 files changed, 306 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
 create mode 100644 drivers/phy/phy-rcar-gen3-usb2.c

diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
new file mode 100644
index 0000000..1e8437f
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
@@ -0,0 +1,37 @@
+* Renesas R-Car generation 3 USB 2.0 PHY
+
+This file provides information on what the device node for the R-Car generation
+3 USB 2.0 PHY contains.
+
+Required properties:
+- compatible: "renesas,usb2-phy-r8a7795" if the device is a part of R8A7795 SoC.
+- reg: offset and length of the USB2.0 host register block.
+- reg-names: must be "usb2".
+- clocks: clock phandle and specifier pair.
+- clock-names: string, clock input name, must be "usb2", and optional "hsusb".
+- #phy-cells: see phy-bindings.txt in the same directory, must be <0>.
+
+Optional proparies:
+To use a USB channel which EHCI/OHCI and HSUSB are combined, the device tree
+node should set HSUSB proparies to reg and reg-names proparies:
+- reg: offset and length of the HSUSB register block.
+- reg-names: must be "hsusb".
+
+Example (R-Car H3):
+
+	usb-phy@ee080200 {
+		compatible = "renesas,usb2-phy-r8a7795";
+		reg = <0 0xee080200 0 0x6ff>, <0 0xe6590100 0 0x100>;
+		reg-names = "usb2", "hsusb";
+		clocks = <&mstp7_clks R8A7795_CLK_EHCI0>,
+			 <&mstp7_clks R8A7795_CLK_HSUSB>;
+		clock-names = "usb2", "hsusb";
+	};
+
+	usb-phy@ee0a0200 {
+		compatible = "renesas,usb2-phy-r8a7795";
+		reg = <0 0xee0a0200 0 0x6ff>;
+		reg-names = "usb2";
+		clocks = <&mstp7_clks R8A7795_CLK_EHCI0>;
+		clock-names = "usb2";
+	};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 47da573..ee300fa 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -118,6 +118,12 @@ config PHY_RCAR_GEN2
 	help
 	  Support for USB PHY found on Renesas R-Car generation 2 SoCs.
 
+config PHY_RCAR_GEN3_USB2
+	tristate "Renesas R-Car generation 3 USB 2.0 PHY driver"
+	depends on GENERIC_PHY
+	help
+	  Support for USB 2.0 PHY found on Renesas R-Car generation 3 SoCs.
+
 config OMAP_CONTROL_PHY
 	tristate "OMAP CONTROL PHY Driver"
 	depends on ARCH_OMAP2PLUS || COMPILE_TEST
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index a5b18c1..97e83bc 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_PHY_MVEBU_SATA)		+= phy-mvebu-sata.o
 obj-$(CONFIG_PHY_MIPHY28LP) 		+= phy-miphy28lp.o
 obj-$(CONFIG_PHY_MIPHY365X)		+= phy-miphy365x.o
 obj-$(CONFIG_PHY_RCAR_GEN2)		+= phy-rcar-gen2.o
+obj-$(CONFIG_PHY_RCAR_GEN3_USB2)	+= phy-rcar-gen3-usb2.o
 obj-$(CONFIG_OMAP_CONTROL_PHY)		+= phy-omap-control.o
 obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
 obj-$(CONFIG_TI_PIPE3)			+= phy-ti-pipe3.o
diff --git a/drivers/phy/phy-rcar-gen3-usb2.c b/drivers/phy/phy-rcar-gen3-usb2.c
new file mode 100644
index 0000000..e319eaf
--- /dev/null
+++ b/drivers/phy/phy-rcar-gen3-usb2.c
@@ -0,0 +1,262 @@
+/*
+ * Renesas R-Car Gen3 for USB2.0 PHY driver
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * This is based on the phy-rcar-gen2 driver:
+ * Copyright (C) 2014 Renesas Solutions Corp.
+ * Copyright (C) 2014 Cogent Embedded, Inc.
+ *
+ * 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/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include <asm/cmpxchg.h>
+
+/******* USB2.0 Host registers (original offset is +0x200) *******/
+#define USB2_INT_ENABLE		0x000
+#define USB2_USBCTR		0x00c
+#define USB2_SPD_RSM_TIMSET	0x10c
+#define USB2_OC_TIMSET		0x110
+
+/* INT_ENABLE */
+#define USB2_INT_ENABLE_USBH_INTB_EN	BIT(2)
+#define USB2_INT_ENABLE_USBH_INTA_EN	BIT(1)
+#define USB2_INT_ENABLE_INIT		(USB2_INT_ENABLE_USBH_INTB_EN | \
+					 USB2_INT_ENABLE_USBH_INTA_EN)
+
+/* USBCTR */
+#define USB2_USBCTR_DIRPD	BIT(2)
+#define USB2_USBCTR_PLL_RST	BIT(1)
+
+/* SPD_RSM_TIMSET */
+#define USB2_SPD_RSM_TIMSET_INIT	0x014e029b
+
+/* OC_TIMSET */
+#define USB2_OC_TIMSET_INIT		0x000209ab
+
+/******* HSUSB registers (original offset is +0x100) *******/
+#define HSUSB_LPSTS			0x02
+#define HSUSB_UGCTRL2			0x84
+
+/* Low Power Status register (LPSTS) */
+#define HSUSB_LPSTS_SUSPM		0x4000
+
+/* USB General control register 2 (UGCTRL2) */
+#define HSUSB_UGCTRL2_MASK		0x00000031 /* bit[31:6] should be 0 */
+#define HSUSB_UGCTRL2_USB0SEL		0x00000030
+#define HSUSB_UGCTRL2_USB0SEL_HOST	0x00000010
+#define HSUSB_UGCTRL2_USB0SEL_HS_USB	0x00000020
+#define HSUSB_UGCTRL2_USB0SEL_OTG	0x00000030
+
+struct rcar_gen3_phy_usb2_data {
+	void __iomem *base;
+	struct clk *clk;
+};
+
+struct rcar_gen3_phy_usb2_channel {
+	struct rcar_gen3_phy_usb2_data usb2;
+	struct rcar_gen3_phy_usb2_data hsusb;
+	struct phy *phy;
+	spinlock_t lock;
+};
+
+static int rcar_gen3_phy_usb2_init(struct phy *p)
+{
+	struct rcar_gen3_phy_usb2_channel *channel = phy_get_drvdata(p);
+	unsigned long flags;
+	void __iomem *usb2_base = channel->usb2.base;
+	void __iomem *hsusb_base = channel->hsusb.base;
+	u32 tmp;
+
+	/* Since ops->init() is called once, this driver enables both clocks */
+	clk_prepare_enable(channel->usb2.clk);
+	clk_prepare_enable(channel->hsusb.clk);
+
+	spin_lock_irqsave(&channel->lock, flags);
+
+	/* Initialize USB2 part */
+	writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE);
+	writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET);
+	writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
+
+	/* Initialize HSUSB part */
+	if (hsusb_base) {
+		/* TODO: support "OTG" mode */
+		tmp = readl(hsusb_base + HSUSB_UGCTRL2);
+		tmp = (tmp & ~HSUSB_UGCTRL2_USB0SEL) |
+		      HSUSB_UGCTRL2_USB0SEL_HOST;
+		writel(tmp & HSUSB_UGCTRL2_MASK, hsusb_base + HSUSB_UGCTRL2);
+	}
+
+	spin_unlock_irqrestore(&channel->lock, flags);
+
+	return 0;
+}
+
+static int rcar_gen3_phy_usb2_exit(struct phy *p)
+{
+	struct rcar_gen3_phy_usb2_channel *channel = phy_get_drvdata(p);
+
+	writel(0, channel->usb2.base + USB2_INT_ENABLE);
+
+	clk_disable_unprepare(channel->hsusb.clk);
+	clk_disable_unprepare(channel->usb2.clk);
+
+	return 0;
+}
+
+static int rcar_gen3_phy_usb2_power_on(struct phy *p)
+{
+	struct rcar_gen3_phy_usb2_channel *channel = phy_get_drvdata(p);
+	void __iomem *usb2_base = channel->usb2.base;
+	void __iomem *hsusb_base = channel->hsusb.base;
+	unsigned long flags;
+	u32 tmp;
+
+	spin_lock_irqsave(&channel->lock, flags);
+
+	tmp = readl(usb2_base + USB2_USBCTR);
+	tmp |= USB2_USBCTR_PLL_RST;
+	writel(tmp, usb2_base + USB2_USBCTR);
+	tmp &= ~USB2_USBCTR_PLL_RST;
+	writel(tmp, usb2_base + USB2_USBCTR);
+
+	/*
+	 * TODO: To reduce power consuming, this driver should set the SUSPM
+	 *	after the PHY detects ID pin as peripherai.
+	 */
+	if (hsusb_base) {
+		/* Power on HSUSB PHY */
+		tmp = readw(hsusb_base + HSUSB_LPSTS);
+		tmp |= HSUSB_LPSTS_SUSPM;
+		writew(tmp, hsusb_base + HSUSB_LPSTS);
+	}
+
+	spin_unlock_irqrestore(&channel->lock, flags);
+
+	return 0;
+}
+
+static int rcar_gen3_phy_usb2_power_off(struct phy *p)
+{
+	struct rcar_gen3_phy_usb2_channel *channel = phy_get_drvdata(p);
+	void __iomem *hsusb_base = channel->hsusb.base;
+	unsigned long flags;
+	u32 tmp;
+
+	spin_lock_irqsave(&channel->lock, flags);
+
+	if (hsusb_base) {
+		/* Power off HSUSB PHY */
+		tmp = readw(hsusb_base + HSUSB_LPSTS);
+		tmp &= ~HSUSB_LPSTS_SUSPM;
+		writew(tmp, hsusb_base + HSUSB_LPSTS);
+	}
+
+	spin_unlock_irqrestore(&channel->lock, flags);
+
+	return 0;
+}
+
+static struct phy_ops rcar_gen3_phy_usb2_ops = {
+	.init		= rcar_gen3_phy_usb2_init,
+	.exit		= rcar_gen3_phy_usb2_exit,
+	.power_on	= rcar_gen3_phy_usb2_power_on,
+	.power_off	= rcar_gen3_phy_usb2_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
+	{ .compatible = "renesas,usb2-phy-r8a7795" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table);
+
+static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rcar_gen3_phy_usb2_channel *channel;
+	struct phy_provider *provider;
+	struct resource *res;
+
+	if (!dev->of_node) {
+		dev_err(dev, "This driver needs device tree\n");
+		return -EINVAL;
+	}
+
+	channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL);
+	if (!channel)
+		return -ENOMEM;
+
+	spin_lock_init(&channel->lock);
+
+	channel->usb2.clk = devm_clk_get(dev, "usb2");
+	if (IS_ERR(channel->usb2.clk)) {
+		dev_err(dev, "Can't get USB2 clock\n");
+		return PTR_ERR(channel->usb2.clk);
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb2");
+	channel->usb2.base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(channel->usb2.base))
+		return PTR_ERR(channel->usb2.base);
+
+	/* "hsusb" memory resource is optional */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsusb");
+
+	/* To avoid error message by devm_ioremap_resource() */
+	if (res) {
+		channel->hsusb.base = devm_ioremap_resource(dev, res);
+		if (!IS_ERR(channel->hsusb.base)) {
+			channel->hsusb.clk = devm_clk_get(dev, "hsusb");
+			if (IS_ERR(channel->hsusb.clk)) {
+				dev_err(dev, "Can't get HSUSB clock\n");
+				return PTR_ERR(channel->hsusb.clk);
+			}
+		} else {
+			channel->hsusb.base = NULL;
+		}
+	}
+
+	channel->phy = devm_phy_create(dev, NULL, &rcar_gen3_phy_usb2_ops);
+	if (IS_ERR(channel->phy)) {
+		dev_err(dev, "Failed to create USB2 PHY\n");
+		return PTR_ERR(channel->phy);
+	}
+
+	phy_set_drvdata(channel->phy, channel);
+
+	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(provider)) {
+		dev_err(dev, "Failed to register PHY provider\n");
+		return PTR_ERR(provider);
+	}
+
+	dev_set_drvdata(dev, channel);
+
+	return 0;
+}
+
+static struct platform_driver rcar_gen3_phy_usb2_driver = {
+	.driver = {
+		.name		= "phy_rcar_gen3_usb2",
+		.of_match_table	= rcar_gen3_phy_usb2_match_table,
+	},
+	.probe	= rcar_gen3_phy_usb2_probe,
+};
+module_platform_driver(rcar_gen3_phy_usb2_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Renesas R-Car Gen3 USB 2.0 PHY");
+MODULE_AUTHOR("Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>");
-- 
1.9.1


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2015-08-24  2:27 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-24  2:27 [PATCH 05/25 v7][RFC] phy: rcar-gen3-usb2: Add R-Car Gen3 USB2 PHY driver Kuninori Morimoto

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