linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] Add a new Rockchip usb2 phy driver
@ 2016-05-31  6:40 Frank Wang
  2016-05-31  6:40 ` [PATCH 1/2] Documentation: bindings: add DT documentation for Rockchip USB2PHY Frank Wang
  2016-05-31  6:40 ` [PATCH 2/2] phy: rockchip-inno-usb2: add a new driver for Rockchip usb2phy Frank Wang
  0 siblings, 2 replies; 10+ messages in thread
From: Frank Wang @ 2016-05-31  6:40 UTC (permalink / raw)
  To: heiko, dianders, kishon, robh+dt, pawel.moll, mark.rutland,
	ijc+devicetree, galak
  Cc: linux-kernel, linux-rockchip, linux-usb, kever.yang, huangtao,
	william.wu, frank.wang, Frank Wang

The newer SoCs (rk3366, rk3399) of Rock-chip take a different usb-phy
IP block than rk3288 and before, and most of phy-related registers are
also different from the past, so a new phy driver is required necessarily.

These series patches add phy-rockchip-inno-usb2.c and the corresponding
documentation.

Frank Wang (2):
  Documentation: bindings: add DT documentation for Rockchip USB2PHY
  phy: rockchip-inno-usb2: add a new driver for Rockchip usb2phy

 .../bindings/phy/phy-rockchip-inno-usb2.txt        |   48 ++
 drivers/phy/Kconfig                                |    7 +
 drivers/phy/Makefile                               |    1 +
 drivers/phy/phy-rockchip-inno-usb2.c               |  581 ++++++++++++++++++++
 4 files changed, 637 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
 create mode 100644 drivers/phy/phy-rockchip-inno-usb2.c

-- 
1.7.9.5

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

* [PATCH 1/2] Documentation: bindings: add DT documentation for Rockchip USB2PHY
  2016-05-31  6:40 [PATCH 0/2] Add a new Rockchip usb2 phy driver Frank Wang
@ 2016-05-31  6:40 ` Frank Wang
  2016-05-31  9:02   ` Heiko Stübner
  2016-06-01 22:02   ` Rob Herring
  2016-05-31  6:40 ` [PATCH 2/2] phy: rockchip-inno-usb2: add a new driver for Rockchip usb2phy Frank Wang
  1 sibling, 2 replies; 10+ messages in thread
From: Frank Wang @ 2016-05-31  6:40 UTC (permalink / raw)
  To: heiko, dianders, kishon, robh+dt, pawel.moll, mark.rutland,
	ijc+devicetree, galak
  Cc: linux-kernel, linux-rockchip, linux-usb, kever.yang, huangtao,
	william.wu, frank.wang, Frank Wang

Signed-off-by: Frank Wang <frank.wang@rock-chips.com>
---
 .../bindings/phy/phy-rockchip-inno-usb2.txt        |   48 ++++++++++++++++++++
 1 file changed, 48 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt

diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
new file mode 100644
index 0000000..4e537b2
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
@@ -0,0 +1,48 @@
+ROCKCHIP USB2.0 PHY WITH INNO IP BLOCK
+
+Required properties (phy (parent) node):
+ - compatible: should contain:
+	* "rockchip,rk3366-usb2phy"
+ - #clock-cells: should be 0.
+ - clock-names: specify the 480m output clk name.
+
+Optional properties:
+ - vbus_host-gpio: pull gpio high/low to control the host vbus power.
+
+Required nodes: a sub-node is required for each port the phy provides.
+		The sub-node name is used to identify host or otg port.
+
+Required properties (port (child) node):
+ - #phy-cells: must be 0. See ./phy-bindings.txt for details.
+ - interrupts: irq number for host/otg port.
+ - interrupt-names: interrupt name, in line with irq number.
+
+Example:
+
+grf: syscon@ff770000 {
+	compatible = "rockchip,rk3366-grf", "syscon", "simple-mfd";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+...
+
+	u2phy: usb2-phy {
+		compatible = "rockchip,rk3366-usb2phy";
+		#clock-cells = <0>;
+		clock-output-names = "sclk_otgphy0_480m";
+
+		u2phy_otg: otg-port {
+			#phy-cells = <0>;
+			interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "linestate";
+			status = "okay";
+		};
+
+		u2phy_host: host-port {
+			#phy-cells = <0>;
+			interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "linestate";
+			status = "okay";
+		};
+	};
+};
-- 
1.7.9.5

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

* [PATCH 2/2] phy: rockchip-inno-usb2: add a new driver for Rockchip usb2phy
  2016-05-31  6:40 [PATCH 0/2] Add a new Rockchip usb2 phy driver Frank Wang
  2016-05-31  6:40 ` [PATCH 1/2] Documentation: bindings: add DT documentation for Rockchip USB2PHY Frank Wang
@ 2016-05-31  6:40 ` Frank Wang
  2016-06-01 23:46   ` Heiko Stübner
  1 sibling, 1 reply; 10+ messages in thread
From: Frank Wang @ 2016-05-31  6:40 UTC (permalink / raw)
  To: heiko, dianders, kishon, robh+dt, pawel.moll, mark.rutland,
	ijc+devicetree, galak
  Cc: linux-kernel, linux-rockchip, linux-usb, kever.yang, huangtao,
	william.wu, frank.wang, Frank Wang

The newer SoCs (rk3366, rk3399) take a different usb-phy IP block
than rk3288 and before, and most of phy-related registers are also
different from the past, so a new phy driver is required necessarily.

Signed-off-by: Frank Wang <frank.wang@rock-chips.com>
---
 drivers/phy/Kconfig                  |    7 +
 drivers/phy/Makefile                 |    1 +
 drivers/phy/phy-rockchip-inno-usb2.c |  581 ++++++++++++++++++++++++++++++++++
 3 files changed, 589 insertions(+)
 create mode 100644 drivers/phy/phy-rockchip-inno-usb2.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index b869b98..29ef15c 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -347,6 +347,13 @@ config PHY_ROCKCHIP_USB
 	help
 	  Enable this to support the Rockchip USB 2.0 PHY.
 
+config PHY_ROCKCHIP_INNO_USB2
+	tristate "Rockchip INNO USB2PHY Driver"
+	depends on ARCH_ROCKCHIP && OF
+	select GENERIC_PHY
+	help
+	  Support for Rockchip USB2.0 PHY with Innosilicon IP block.
+
 config PHY_ROCKCHIP_EMMC
 	tristate "Rockchip EMMC PHY Driver"
 	depends on ARCH_ROCKCHIP && OF
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 9c3e73c..4963fbc 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -38,6 +38,7 @@ phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2)	+= phy-s5pv210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS5_USBDRD)	+= phy-exynos5-usbdrd.o
 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
+obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2)	+= phy-rockchip-inno-usb2.o
 obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
 obj-$(CONFIG_PHY_ROCKCHIP_DP)		+= phy-rockchip-dp.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c
new file mode 100644
index 0000000..eeb5638
--- /dev/null
+++ b/drivers/phy/phy-rockchip-inno-usb2.c
@@ -0,0 +1,581 @@
+/*
+ * Rockchip USB2.0 PHY with Innosilicon IP block driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * 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.
+ *
+ * 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/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio/consumer.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define BIT_WRITEABLE_SHIFT	16
+#define SCHEDULE_DELAY	(60 * HZ)
+
+enum rockchip_usb2phy_port_id {
+	USB2PHY_PORT_OTG,
+	USB2PHY_PORT_HOST,
+	USB2PHY_NUM_PORTS,
+};
+
+enum rockchip_usb2phy_host_state {
+	PHY_STATE_HS_ONLINE	= 0,
+	PHY_STATE_DISCONNECT	= 1,
+	PHY_STATE_HS_CONNECT	= 2,
+	PHY_STATE_FS_CONNECT	= 4,
+};
+
+struct usb2phy_reg {
+	unsigned int	offset;
+	unsigned int	bitend;
+	unsigned int	bitstart;
+	unsigned int	disable;
+	unsigned int	enable;
+};
+
+/**
+ * struct rockchip_usb2phy_port_cfg: usb-phy port configuration.
+ * @phy_sus: phy suspend register.
+ * @ls_det_en: linestate detection enable register.
+ * @ls_det_st: linestate detection state register.
+ * @ls_det_clr: linestate detection clear register.
+ * @utmi_ls: utmi linestate state register.
+ * @utmi_hstdet: utmi host disconnect register.
+ */
+struct rockchip_usb2phy_port_cfg {
+	struct usb2phy_reg	phy_sus;
+	struct usb2phy_reg	ls_det_en;
+	struct usb2phy_reg	ls_det_st;
+	struct usb2phy_reg	ls_det_clr;
+	struct usb2phy_reg	utmi_ls;
+	struct usb2phy_reg	utmi_hstdet;
+};
+
+/**
+ * struct rockchip_usb2phy_cfg: usb-phy configuration.
+ * @num_ports: specify how many ports that the phy has.
+ * @clkout_ctl: keep on/turn off output clk of phy.
+ */
+struct rockchip_usb2phy_cfg {
+	unsigned int	num_ports;
+	struct usb2phy_reg	clkout_ctl;
+	const struct rockchip_usb2phy_port_cfg	*port_cfgs;
+};
+
+/**
+ * struct rockchip_usb2phy_port: usb-phy port data.
+ * @port_id: flag for otg port or host port.
+ * @suspended: phy suspended flag.
+ * @ls_irq: IRQ number assigned for linestate detection.
+ * @mutex: for register updating in sm_work.
+ * @sm_work: OTG state machine work.
+ * @phy_cfg: port register configuration, assigned by driver data.
+ */
+struct rockchip_usb2phy_port {
+	struct phy	*phy;
+	unsigned int	port_id;
+	bool		suspended;
+	int		ls_irq;
+	struct mutex	mutex;
+	struct		delayed_work sm_work;
+	const struct	rockchip_usb2phy_port_cfg *port_cfg;
+};
+
+/**
+ * struct rockchip_usb2phy: usb2.0 phy driver data.
+ * @grf: General Register Files regmap.
+ * @clk480m: clock struct of phy output clk.
+ * @clk_hw: clock struct of phy output clk management.
+ * @vbus_host_gpio: host VBUS direction output.
+ * @phy_cfg: phy register configuration, assigned by driver data.
+ * @ports: phy port instance.
+ */
+struct rockchip_usb2phy {
+	struct device	*dev;
+	struct regmap	*grf;
+	struct clk	*clk480m;
+	struct clk_hw	clk480m_hw;
+	struct gpio_desc	*vbus_host_gpio;
+	const struct rockchip_usb2phy_cfg	*phy_cfg;
+	struct rockchip_usb2phy_port	ports[USB2PHY_NUM_PORTS];
+};
+
+static inline int property_enable(struct rockchip_usb2phy *rphy,
+				  const struct usb2phy_reg *reg, bool en)
+{
+	unsigned int val, mask, tmp;
+
+	tmp = en ? reg->enable : reg->disable;
+	mask = GENMASK(reg->bitend, reg->bitstart);
+	val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
+
+	return regmap_write(rphy->grf, reg->offset, val);
+}
+
+static inline bool property_enabled(struct rockchip_usb2phy *rphy,
+				    const struct usb2phy_reg *reg)
+{
+	int ret;
+	unsigned int tmp, orig;
+	unsigned int mask = GENMASK(reg->bitend, reg->bitstart);
+
+	ret = regmap_read(rphy->grf, reg->offset, &orig);
+	if (ret)
+		return false;
+
+	tmp = (orig & mask) >> reg->bitstart;
+	return tmp == reg->enable;
+}
+
+static int rockchip_usb2phy_clk480m_enable(struct clk_hw *hw)
+{
+	struct rockchip_usb2phy *rphy =
+		container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+	int ret = 0;
+
+	/* turn on 480m clk output if it is off */
+	if (!property_enabled(rphy, &rphy->phy_cfg->clkout_ctl)) {
+		ret = property_enable(rphy, &rphy->phy_cfg->clkout_ctl, true);
+		if (ret)
+			return ret;
+
+		/* waitting for the clk become stable */
+		mdelay(1);
+	}
+
+	return ret;
+}
+
+static void rockchip_usb2phy_clk480m_disable(struct clk_hw *hw)
+{
+	struct rockchip_usb2phy *rphy =
+		container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+	int index;
+
+	/* make sure all ports in suspended mode */
+	for (index = 0; index != rphy->phy_cfg->num_ports; index++)
+		if (!rphy->ports[index].suspended)
+			return;
+
+	/* turn off 480m clk output */
+	property_enable(rphy, &rphy->phy_cfg->clkout_ctl, false);
+}
+
+static int rockchip_usb2phy_clk480m_enabled(struct clk_hw *hw)
+{
+	struct rockchip_usb2phy *rphy =
+		container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+
+	return property_enabled(rphy, &rphy->phy_cfg->clkout_ctl);
+}
+
+static unsigned long
+rockchip_usb2phy_clk480m_recalc_rate(struct clk_hw *hw,
+				     unsigned long parent_rate)
+{
+	return 480000000;
+}
+
+static const struct clk_ops rockchip_usb2phy_clkout_ops = {
+	.enable = rockchip_usb2phy_clk480m_enable,
+	.disable = rockchip_usb2phy_clk480m_disable,
+	.is_enabled = rockchip_usb2phy_clk480m_enabled,
+	.recalc_rate = rockchip_usb2phy_clk480m_recalc_rate,
+};
+
+static struct clk *
+rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
+{
+	struct device_node *node = rphy->dev->of_node;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	init.name = "clk_usbphy_480m";
+	init.ops = &rockchip_usb2phy_clkout_ops;
+	init.flags = CLK_IS_ROOT;
+	init.parent_names = NULL;
+	init.num_parents = 0;
+	rphy->clk480m_hw.init = &init;
+
+	/* optional override of the clockname */
+	of_property_read_string(node, "clock-output-names", &init.name);
+
+	/* register the clock */
+	clk = clk_register(rphy->dev, &rphy->clk480m_hw);
+	if (!IS_ERR(clk))
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+	return clk;
+}
+
+static int rockchip_usb2phy_init(struct phy *phy)
+{
+	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+	int ret = 0;
+
+	if (rport->port_id == USB2PHY_PORT_HOST) {
+		/* clear linestate and enable linestate detect irq */
+		mutex_lock(&rport->mutex);
+
+		ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+		if (ret) {
+			mutex_unlock(&rport->mutex);
+			return ret;
+		}
+
+		ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true);
+		if (ret) {
+			mutex_unlock(&rport->mutex);
+			return ret;
+		}
+
+		mutex_unlock(&rport->mutex);
+		schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
+	}
+
+	return ret;
+}
+
+static int rockchip_usb2phy_resume(struct phy *phy)
+{
+	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+	int ret;
+
+	dev_dbg(&rport->phy->dev, "port resume\n");
+
+	ret = clk_prepare_enable(rphy->clk480m);
+	if (ret)
+		return ret;
+
+	ret = property_enable(rphy, &rport->port_cfg->phy_sus, false);
+	if (ret)
+		return ret;
+
+	rport->suspended = false;
+	return 0;
+}
+
+static int rockchip_usb2phy_suspend(struct phy *phy)
+{
+	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+	int ret;
+
+	dev_dbg(&rport->phy->dev, "port suspend\n");
+
+	ret = property_enable(rphy, &rport->port_cfg->phy_sus, true);
+	if (ret)
+		return ret;
+
+	rport->suspended = true;
+	clk_disable_unprepare(rphy->clk480m);
+	return 0;
+}
+
+static int rockchip_usb2phy_exit(struct phy *phy)
+{
+	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+
+	if (rport->port_id == USB2PHY_PORT_HOST)
+		cancel_delayed_work_sync(&rport->sm_work);
+
+	return 0;
+}
+
+static const struct phy_ops rockchip_usb2phy_ops = {
+	.init		= rockchip_usb2phy_init,
+	.exit		= rockchip_usb2phy_exit,
+	.power_on	= rockchip_usb2phy_resume,
+	.power_off	= rockchip_usb2phy_suspend,
+	.owner		= THIS_MODULE,
+};
+
+/*
+ * The function manage host-phy port state and suspend/resume phy port
+ * to save power.
+ *
+ * we rely on utmi_linestate and utmi_hostdisconnect to identify whether
+ * FS/HS is disconnect or not. Besides, we do not need care it is FS
+ * disconnected or HS disconnected, actually, we just only need get the
+ * device is disconnected at last through rearm the delayed work,
+ * to suspend the phy port in _PHY_STATE_DISCONNECT_ case.
+ *
+ * NOTE: It will invoke some clk related APIs, so do not invoke it from
+ * interrupt context.
+ */
+static void rockchip_usb2phy_sm_work(struct work_struct *work)
+{
+	struct rockchip_usb2phy_port *rport =
+		container_of(work, struct rockchip_usb2phy_port, sm_work.work);
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+	unsigned int sh = rport->port_cfg->utmi_hstdet.bitend -
+			  rport->port_cfg->utmi_hstdet.bitstart + 1;
+	unsigned int ul, uhd, state;
+	unsigned int ul_mask, uhd_mask;
+	int ret;
+
+	mutex_lock(&rport->mutex);
+
+	ret = regmap_read(rphy->grf, rport->port_cfg->utmi_ls.offset, &ul);
+	if (ret < 0)
+		goto next_schedule;
+
+	ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset,
+			  &uhd);
+	if (ret < 0)
+		goto next_schedule;
+
+	uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend,
+			   rport->port_cfg->utmi_hstdet.bitstart);
+	ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend,
+			  rport->port_cfg->utmi_ls.bitstart);
+
+	/* stitch on utmi_ls and utmi_hstdet as phy state */
+	state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) |
+		(((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh);
+
+	switch (state) {
+	case PHY_STATE_HS_ONLINE:
+		dev_dbg(&rport->phy->dev, "HS online\n");
+		break;
+	case PHY_STATE_FS_CONNECT:
+		/*
+		 * For FS device, the online state share with connect state
+		 * from utmi_ls and utmi_hstdet register, so we distinguish
+		 * them via suspended flag.
+		 */
+		if (!rport->suspended) {
+			dev_dbg(&rport->phy->dev, "FS online\n");
+			break;
+		}
+		/* fall through */
+	case PHY_STATE_HS_CONNECT:
+		if (rport->suspended) {
+			dev_dbg(&rport->phy->dev, "HS/FS connected\n");
+			rockchip_usb2phy_resume(rport->phy);
+			rport->suspended = false;
+		}
+		break;
+	case PHY_STATE_DISCONNECT:
+		if (!rport->suspended) {
+			dev_dbg(&rport->phy->dev, "HS/FS disconnected\n");
+			rockchip_usb2phy_suspend(rport->phy);
+			rport->suspended = true;
+		}
+
+		/*
+		 * activate the linestate detection to get the next device
+		 * plug-in irq.
+		 */
+		property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+		property_enable(rphy, &rport->port_cfg->ls_det_en, true);
+
+		/*
+		 * we don't need to rearm the delayed work when the phy port
+		 * is suspended.
+		 */
+		mutex_unlock(&rport->mutex);
+		return;
+	default:
+		dev_dbg(&rport->phy->dev, "unknown phy state\n");
+		break;
+	}
+
+next_schedule:
+	mutex_unlock(&rport->mutex);
+	schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
+}
+
+static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data)
+{
+	struct rockchip_usb2phy_port *rport = data;
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+
+	if (!property_enabled(rphy, &rport->port_cfg->ls_det_st))
+		return IRQ_NONE;
+
+	mutex_lock(&rport->mutex);
+
+	/* disable linestate detect irq and clear its status */
+	property_enable(rphy, &rport->port_cfg->ls_det_en, false);
+	property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+
+	mutex_unlock(&rport->mutex);
+
+	/*
+	 * In this case for host phy port, a new device is plugged in,
+	 * meanwhile, if the phy port is suspended, we need rearm the work to
+	 * resume it and mange its states; otherwise, we do nothing about that.
+	 */
+	if (rport->suspended && rport->port_id == USB2PHY_PORT_HOST)
+		rockchip_usb2phy_sm_work(&rport->sm_work.work);
+
+	return IRQ_HANDLED;
+}
+
+static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
+					   struct rockchip_usb2phy_port *rport,
+					   struct device_node *child_np)
+{
+	int ret = 0;
+
+	rport->port_id = USB2PHY_PORT_HOST;
+	mutex_init(&rport->mutex);
+	INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work);
+
+	rport->ls_irq = of_irq_get_byname(child_np, "linestate");
+	if (rport->ls_irq < 0) {
+		dev_err(rphy->dev, "no linestate irq provided\n");
+		return rport->ls_irq;
+	}
+
+	ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
+					rockchip_usb2phy_linestate_irq,
+					IRQF_ONESHOT,
+					"rockchip_usb2phy", rport);
+	if (ret) {
+		dev_err(rphy->dev, "failed to request irq handle\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int rockchip_usb2phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *child_np;
+	struct phy_provider *provider;
+	struct rockchip_usb2phy *rphy;
+	const struct of_device_id *match;
+	int index, ret;
+
+	rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL);
+	if (!rphy)
+		return -ENOMEM;
+
+	match = of_match_device(dev->driver->of_match_table, dev);
+	if (!match || !match->data) {
+		dev_err(dev, "phy configs are not assigned!\n");
+		return -EINVAL;
+	}
+
+	if (!dev->parent || !dev->parent->of_node)
+		return -ENOMEM;
+
+	rphy->grf = syscon_node_to_regmap(dev->parent->of_node);
+	if (IS_ERR(rphy->grf))
+		return PTR_ERR(rphy->grf);
+
+	rphy->dev = dev;
+	rphy->phy_cfg = match->data;
+	platform_set_drvdata(pdev, rphy);
+
+	rphy->clk480m = rockchip_usb2phy_clk480m_register(rphy);
+	if (IS_ERR(rphy->clk480m))
+		return PTR_ERR(rphy->clk480m);
+
+	rphy->vbus_host_gpio =
+		devm_gpiod_get_optional(dev, "vbus_host", GPIOD_OUT_HIGH);
+	if (!rphy->vbus_host_gpio)
+		dev_info(dev, "host_vbus is not assigned!\n");
+	else if (IS_ERR(rphy->vbus_host_gpio))
+		return PTR_ERR(rphy->vbus_host_gpio);
+
+	index = 0;
+	for_each_child_of_node(np, child_np) {
+		struct rockchip_usb2phy_port *rport = &rphy->ports[index];
+		struct phy *phy;
+
+		phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);
+		if (IS_ERR(phy)) {
+			dev_err(dev, "failed to create phy\n");
+			ret = PTR_ERR(phy);
+			goto put_child;
+		}
+
+		rport->phy = phy;
+		rport->port_cfg = &rphy->phy_cfg->port_cfgs[index];
+
+		/* initialize otg/host port separately */
+		if (!of_node_cmp(child_np->name, "host-port")) {
+			ret = rockchip_usb2phy_host_port_init(rphy, rport,
+							      child_np);
+			if (ret)
+				goto put_child;
+		}
+
+		phy_set_drvdata(rport->phy, rport);
+		index++;
+	}
+
+	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	return PTR_ERR_OR_ZERO(provider);
+
+put_child:
+	of_node_put(child_np);
+	of_clk_del_provider(np);
+	clk_unregister(rphy->clk480m);
+	return ret;
+}
+
+static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs = {
+	.num_ports	= 1,
+	.clkout_ctl	= { 0x0724, 15, 15, 1, 0 },
+	.port_cfgs	= (struct rockchip_usb2phy_port_cfg[]) {
+		{
+			.phy_sus	= { 0x0728, 15, 0, 0, 0x1d1 },
+			.ls_det_en	= { 0x0680, 4, 4, 0, 1 },
+			.ls_det_st	= { 0x0690, 4, 4, 0, 1 },
+			.ls_det_clr	= { 0x06a0, 4, 4, 0, 1 },
+			.utmi_ls	= { 0x049c, 14, 13, 0, 1 },
+			.utmi_hstdet	= { 0x049c, 12, 12, 0, 1 }
+		},
+		{ /* sentinel */ }
+	},
+};
+
+static const struct of_device_id rockchip_usb2phy_dt_match[] = {
+	{ .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs },
+	{}
+};
+MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match);
+
+static struct platform_driver rockchip_usb2phy_driver = {
+	.probe		= rockchip_usb2phy_probe,
+	.driver		= {
+		.name	= "rockchip-usb2phy",
+		.of_match_table = rockchip_usb2phy_dt_match,
+	},
+};
+module_platform_driver(rockchip_usb2phy_driver);
+
+MODULE_AUTHOR("Frank Wang <frank.wang@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB2.0 PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5

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

* Re: [PATCH 1/2] Documentation: bindings: add DT documentation for Rockchip USB2PHY
  2016-05-31  6:40 ` [PATCH 1/2] Documentation: bindings: add DT documentation for Rockchip USB2PHY Frank Wang
@ 2016-05-31  9:02   ` Heiko Stübner
  2016-06-01  8:09     ` Frank Wang
  2016-06-01 22:02   ` Rob Herring
  1 sibling, 1 reply; 10+ messages in thread
From: Heiko Stübner @ 2016-05-31  9:02 UTC (permalink / raw)
  To: Frank Wang
  Cc: dianders, kishon, robh+dt, pawel.moll, mark.rutland,
	ijc+devicetree, galak, linux-kernel, linux-rockchip, linux-usb,
	kever.yang, huangtao, william.wu, frank.wang

Hi Frank,

Am Dienstag, 31. Mai 2016, 14:40:10 schrieb Frank Wang:
> Signed-off-by: Frank Wang <frank.wang@rock-chips.com>
> ---
>  .../bindings/phy/phy-rockchip-inno-usb2.txt        |   48
> ++++++++++++++++++++ 1 file changed, 48 insertions(+)
>  create mode 100644
> Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
> 
> diff --git
> a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
> b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt new file
> mode 100644
> index 0000000..4e537b2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
> @@ -0,0 +1,48 @@
> +ROCKCHIP USB2.0 PHY WITH INNO IP BLOCK
> +
> +Required properties (phy (parent) node):
> + - compatible: should contain:
> +	* "rockchip,rk3366-usb2phy"
> + - #clock-cells: should be 0.
> + - clock-names: specify the 480m output clk name.
> +
> +Optional properties:
> + - vbus_host-gpio: pull gpio high/low to control the host vbus power.

sorry for not catching that in our earlier talks, but I believe this should be 
a regulator instead. See for example vcc5_host1, vcc5v_otg in rk3288-veyron-
chromebook.dtsi .


> +Required nodes: a sub-node is required for each port the phy provides.
> +		The sub-node name is used to identify host or otg port.
> +
> +Required properties (port (child) node):
> + - #phy-cells: must be 0. See ./phy-bindings.txt for details.
> + - interrupts: irq number for host/otg port.

make that something like:
Specify an interrupt for each entry in interrupt-names.

> + - interrupt-names: interrupt name, in line with irq number.

make that something like:
Shall be "linestate" for the linestate interrupt.
---

You might want to add the bvalid and id interrupts for the otg phys as well 
already - would make handling legacy devicetree files easier. [= if they get 
specified later, the driver would always need to also handle devicetrees where 
they aren't specified].


Heiko

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

* Re: [PATCH 1/2] Documentation: bindings: add DT documentation for Rockchip USB2PHY
  2016-05-31  9:02   ` Heiko Stübner
@ 2016-06-01  8:09     ` Frank Wang
  2016-06-01 22:17       ` Heiko Stübner
  0 siblings, 1 reply; 10+ messages in thread
From: Frank Wang @ 2016-06-01  8:09 UTC (permalink / raw)
  To: Heiko Stübner
  Cc: dianders, kishon, robh+dt, pawel.moll, mark.rutland,
	ijc+devicetree, galak, linux-kernel, linux-rockchip, linux-usb,
	kever.yang, huangtao, william.wu, frank.wang

Hi Heiko,

On 05/31/2016 05:02 PM, Heiko Stübner wrote:
> Hi Frank,
>
> Am Dienstag, 31. Mai 2016, 14:40:10 schrieb Frank Wang:
>> Signed-off-by: Frank Wang <frank.wang@rock-chips.com>
>> ---
>>   .../bindings/phy/phy-rockchip-inno-usb2.txt        |   48
>> ++++++++++++++++++++ 1 file changed, 48 insertions(+)
>>   create mode 100644
>> Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
>>
>> diff --git
>> a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
>> b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt new file
>> mode 100644
>> index 0000000..4e537b2
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
>> @@ -0,0 +1,48 @@
>> +ROCKCHIP USB2.0 PHY WITH INNO IP BLOCK
>> +
>> +Required properties (phy (parent) node):
>> + - compatible: should contain:
>> +	* "rockchip,rk3366-usb2phy"
>> + - #clock-cells: should be 0.
>> + - clock-names: specify the 480m output clk name.
>> +
>> +Optional properties:
>> + - vbus_host-gpio: pull gpio high/low to control the host vbus power.
>
> sorry for not catching that in our earlier talks, but I believe this should be
> a regulator instead. See for example vcc5_host1, vcc5v_otg in rk3288-veyron-
> chromebook.dtsi .
>

That is OK, I will correct it in the next version.

>
>> +Required nodes: a sub-node is required for each port the phy provides.
>> +		The sub-node name is used to identify host or otg port.
>> +
>> +Required properties (port (child) node):
>> + - #phy-cells: must be 0. See ./phy-bindings.txt for details.
>> + - interrupts: irq number for host/otg port.
>
> make that something like:
> Specify an interrupt for each entry in interrupt-names.
>
>> + - interrupt-names: interrupt name, in line with irq number.
>
> make that something like:
> Shall be "linestate" for the linestate interrupt.

Yeah, Got it.

> ---
>
> You might want to add the bvalid and id interrupts for the otg phys as well
> already - would make handling legacy devicetree files easier. [= if they get
> specified later, the driver would always need to also handle devicetrees where
> they aren't specified].
>

Hmmm! you mean that I can specify these properties into documentation, 
even if the driver have not handled (implemented) them in current?

BR.
Frank

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

* Re: [PATCH 1/2] Documentation: bindings: add DT documentation for Rockchip USB2PHY
  2016-05-31  6:40 ` [PATCH 1/2] Documentation: bindings: add DT documentation for Rockchip USB2PHY Frank Wang
  2016-05-31  9:02   ` Heiko Stübner
@ 2016-06-01 22:02   ` Rob Herring
  1 sibling, 0 replies; 10+ messages in thread
From: Rob Herring @ 2016-06-01 22:02 UTC (permalink / raw)
  To: Frank Wang
  Cc: Heiko Stübner, Doug Anderson, Kishon Vijay Abraham I,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, linux-kernel,
	open list:ARM/Rockchip SoC...,
	Linux USB List, Kever Yang, Tao Huang, william.wu, frank.wang

On Tue, May 31, 2016 at 1:40 AM, Frank Wang <frank.wang@rock-chips.com> wrote:
> Signed-off-by: Frank Wang <frank.wang@rock-chips.com>

Please resend to DT list.

> ---
>  .../bindings/phy/phy-rockchip-inno-usb2.txt        |   48 ++++++++++++++++++++
>  1 file changed, 48 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
>
> diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
> new file mode 100644
> index 0000000..4e537b2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
> @@ -0,0 +1,48 @@
> +ROCKCHIP USB2.0 PHY WITH INNO IP BLOCK
> +
> +Required properties (phy (parent) node):
> + - compatible: should contain:
> +       * "rockchip,rk3366-usb2phy"
> + - #clock-cells: should be 0.
> + - clock-names: specify the 480m output clk name.
> +
> +Optional properties:
> + - vbus_host-gpio: pull gpio high/low to control the host vbus power.
> +
> +Required nodes: a sub-node is required for each port the phy provides.
> +               The sub-node name is used to identify host or otg port.
> +
> +Required properties (port (child) node):
> + - #phy-cells: must be 0. See ./phy-bindings.txt for details.
> + - interrupts: irq number for host/otg port.
> + - interrupt-names: interrupt name, in line with irq number.
> +
> +Example:
> +
> +grf: syscon@ff770000 {
> +       compatible = "rockchip,rk3366-grf", "syscon", "simple-mfd";
> +       #address-cells = <1>;
> +       #size-cells = <1>;
> +
> +...
> +
> +       u2phy: usb2-phy {
> +               compatible = "rockchip,rk3366-usb2phy";
> +               #clock-cells = <0>;
> +               clock-output-names = "sclk_otgphy0_480m";
> +
> +               u2phy_otg: otg-port {
> +                       #phy-cells = <0>;
> +                       interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
> +                       interrupt-names = "linestate";
> +                       status = "okay";
> +               };
> +
> +               u2phy_host: host-port {
> +                       #phy-cells = <0>;
> +                       interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
> +                       interrupt-names = "linestate";
> +                       status = "okay";
> +               };
> +       };
> +};
> --
> 1.7.9.5
>
>

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

* Re: [PATCH 1/2] Documentation: bindings: add DT documentation for Rockchip USB2PHY
  2016-06-01  8:09     ` Frank Wang
@ 2016-06-01 22:17       ` Heiko Stübner
  2016-06-02  2:53         ` Frank Wang
  0 siblings, 1 reply; 10+ messages in thread
From: Heiko Stübner @ 2016-06-01 22:17 UTC (permalink / raw)
  To: Frank Wang
  Cc: dianders, kishon, robh+dt, pawel.moll, mark.rutland,
	ijc+devicetree, galak, linux-kernel, linux-rockchip, linux-usb,
	kever.yang, huangtao, william.wu, frank.wang

Hi Frank,

Am Mittwoch, 1. Juni 2016, 16:09:41 schrieb Frank Wang:
> > You might want to add the bvalid and id interrupts for the otg phys as
> > well
> > already - would make handling legacy devicetree files easier. [= if they
> > get specified later, the driver would always need to also handle
> > devicetrees where they aren't specified].
> 
> Hmmm! you mean that I can specify these properties into documentation,
> even if the driver have not handled (implemented) them in current?

The devicetree bindings are supposed to be a generic hardware-description.
And a driver then simply implements that binding. So if the interrupt is part 
of the hardware it can be part of the binding, independent of the driver.

I guess it really comes down to, will you need those interrupts later in the 
driver, then they should definitly be specified now, as later on you cannot 
require them anymore and always need to also support devicetrees not having 
them.


Heiko

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

* Re: [PATCH 2/2] phy: rockchip-inno-usb2: add a new driver for Rockchip usb2phy
  2016-05-31  6:40 ` [PATCH 2/2] phy: rockchip-inno-usb2: add a new driver for Rockchip usb2phy Frank Wang
@ 2016-06-01 23:46   ` Heiko Stübner
  2016-06-02  3:19     ` Frank Wang
  0 siblings, 1 reply; 10+ messages in thread
From: Heiko Stübner @ 2016-06-01 23:46 UTC (permalink / raw)
  To: Frank Wang
  Cc: dianders, kishon, robh+dt, pawel.moll, mark.rutland,
	ijc+devicetree, galak, linux-kernel, linux-rockchip, linux-usb,
	kever.yang, huangtao, william.wu, frank.wang

Hi Frank,

Am Dienstag, 31. Mai 2016, 14:40:11 schrieb Frank Wang:
> The newer SoCs (rk3366, rk3399) take a different usb-phy IP block
> than rk3288 and before, and most of phy-related registers are also
> different from the past, so a new phy driver is required necessarily.
> 
> Signed-off-by: Frank Wang <frank.wang@rock-chips.com>
> ---

[...]

> +static void rockchip_usb2phy_clk480m_disable(struct clk_hw *hw)
> +{
> +	struct rockchip_usb2phy *rphy =
> +		container_of(hw, struct rockchip_usb2phy, clk480m_hw);
> +	int index;
> +
> +	/* make sure all ports in suspended mode */
> +	for (index = 0; index != rphy->phy_cfg->num_ports; index++)
> +		if (!rphy->ports[index].suspended)
> +			return;

This function can only get called when all clk-references have disabled the 
clock, so you should never reach that point that one phy port is not 
suspended?

> +
> +	/* turn off 480m clk output */
> +	property_enable(rphy, &rphy->phy_cfg->clkout_ctl, false);
> +}
> +

[...]

add something like:

static void rockchip_usb2phy_clk480m_unregister(void *data)
{
	struct rockchip_usb2phy *rphy = data;

	of_clk_del_provider(rphy->dev->of_node);
	clk_unregister(rphy->clk480m);
}

> +static struct clk *
> +rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
> +{
> +	struct device_node *node = rphy->dev->of_node;
> +	struct clk *clk;
> +	struct clk_init_data init;
	int ret;

> +
> +	init.name = "clk_usbphy_480m";
> +	init.ops = &rockchip_usb2phy_clkout_ops;
> +	init.flags = CLK_IS_ROOT;
> +	init.parent_names = NULL;
> +	init.num_parents = 0;
> +	rphy->clk480m_hw.init = &init;
> +
> +	/* optional override of the clockname */
> +	of_property_read_string(node, "clock-output-names", &init.name);
> +
> +	/* register the clock */
> +	clk = clk_register(rphy->dev, &rphy->clk480m_hw);
	if (IS_ERR(clk))
		return clk:

	ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
	if (ret < 0)
		goto err_clk_provider;

	ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister, 
rphy);
	if (ret < 0)
		goto err_unreg_action;

	return clk;

err_unreg_action:
	of_clk_del_provider(node);
err_clk_provider:
	clk_unregister(clk);
	return ERR_PTR(ret);

> +}

[...]

> +/*
> + * The function manage host-phy port state and suspend/resume phy port
> + * to save power.
> + *
> + * we rely on utmi_linestate and utmi_hostdisconnect to identify whether
> + * FS/HS is disconnect or not. Besides, we do not need care it is FS
> + * disconnected or HS disconnected, actually, we just only need get the
> + * device is disconnected at last through rearm the delayed work,
> + * to suspend the phy port in _PHY_STATE_DISCONNECT_ case.
> + *
> + * NOTE: It will invoke some clk related APIs, so do not invoke it from
> + * interrupt context.

This does not seem to match the code, as I don't see any clk_* calls,

> + */
> +static void rockchip_usb2phy_sm_work(struct work_struct *work)
> +{
> +	struct rockchip_usb2phy_port *rport =
> +		container_of(work, struct rockchip_usb2phy_port, sm_work.work);
> +	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
> +	unsigned int sh = rport->port_cfg->utmi_hstdet.bitend -
> +			  rport->port_cfg->utmi_hstdet.bitstart + 1;
> +	unsigned int ul, uhd, state;
> +	unsigned int ul_mask, uhd_mask;
> +	int ret;
> +
> +	mutex_lock(&rport->mutex);
> +
> +	ret = regmap_read(rphy->grf, rport->port_cfg->utmi_ls.offset, &ul);
> +	if (ret < 0)
> +		goto next_schedule;
> +
> +	ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset,
> +			  &uhd);
> +	if (ret < 0)
> +		goto next_schedule;
> +
> +	uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend,
> +			   rport->port_cfg->utmi_hstdet.bitstart);
> +	ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend,
> +			  rport->port_cfg->utmi_ls.bitstart);
> +
> +	/* stitch on utmi_ls and utmi_hstdet as phy state */
> +	state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) |
> +		(((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh);
> +
> +	switch (state) {
> +	case PHY_STATE_HS_ONLINE:
> +		dev_dbg(&rport->phy->dev, "HS online\n");
> +		break;
> +	case PHY_STATE_FS_CONNECT:
> +		/*
> +		 * For FS device, the online state share with connect state
> +		 * from utmi_ls and utmi_hstdet register, so we distinguish
> +		 * them via suspended flag.
> +		 */
> +		if (!rport->suspended) {
> +			dev_dbg(&rport->phy->dev, "FS online\n");
> +			break;
> +		}
> +		/* fall through */
> +	case PHY_STATE_HS_CONNECT:
> +		if (rport->suspended) {
> +			dev_dbg(&rport->phy->dev, "HS/FS connected\n");
> +			rockchip_usb2phy_resume(rport->phy);
> +			rport->suspended = false;
> +		}
> +		break;
> +	case PHY_STATE_DISCONNECT:
> +		if (!rport->suspended) {
> +			dev_dbg(&rport->phy->dev, "HS/FS disconnected\n");
> +			rockchip_usb2phy_suspend(rport->phy);
> +			rport->suspended = true;
> +		}
> +
> +		/*
> +		 * activate the linestate detection to get the next device
> +		 * plug-in irq.
> +		 */
> +		property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
> +		property_enable(rphy, &rport->port_cfg->ls_det_en, true);
> +
> +		/*
> +		 * we don't need to rearm the delayed work when the phy port
> +		 * is suspended.
> +		 */
> +		mutex_unlock(&rport->mutex);
> +		return;
> +	default:
> +		dev_dbg(&rport->phy->dev, "unknown phy state\n");
> +		break;
> +	}
> +
> +next_schedule:
> +	mutex_unlock(&rport->mutex);
> +	schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
> +}

[...]

> +static int rockchip_usb2phy_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	struct device_node *child_np;
> +	struct phy_provider *provider;
> +	struct rockchip_usb2phy *rphy;
> +	const struct of_device_id *match;
> +	int index, ret;
> +
> +	rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL);
> +	if (!rphy)
> +		return -ENOMEM;
> +
> +	match = of_match_device(dev->driver->of_match_table, dev);
> +	if (!match || !match->data) {
> +		dev_err(dev, "phy configs are not assigned!\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!dev->parent || !dev->parent->of_node)
> +		return -ENOMEM;
> +
> +	rphy->grf = syscon_node_to_regmap(dev->parent->of_node);
> +	if (IS_ERR(rphy->grf))
> +		return PTR_ERR(rphy->grf);
> +
> +	rphy->dev = dev;
> +	rphy->phy_cfg = match->data;
> +	platform_set_drvdata(pdev, rphy);
> +
> +	rphy->clk480m = rockchip_usb2phy_clk480m_register(rphy);
> +	if (IS_ERR(rphy->clk480m))
> +		return PTR_ERR(rphy->clk480m);
> +
> +	rphy->vbus_host_gpio =
> +		devm_gpiod_get_optional(dev, "vbus_host", GPIOD_OUT_HIGH);
> +	if (!rphy->vbus_host_gpio)
> +		dev_info(dev, "host_vbus is not assigned!\n");
> +	else if (IS_ERR(rphy->vbus_host_gpio))
> +		return PTR_ERR(rphy->vbus_host_gpio);
> +
> +	index = 0;
> +	for_each_child_of_node(np, child_np) {
> +		struct rockchip_usb2phy_port *rport = &rphy->ports[index];
> +		struct phy *phy;
> +
> +		phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);
> +		if (IS_ERR(phy)) {
> +			dev_err(dev, "failed to create phy\n");
> +			ret = PTR_ERR(phy);
> +			goto put_child;
> +		}
> +
> +		rport->phy = phy;
> +		rport->port_cfg = &rphy->phy_cfg->port_cfgs[index];
> +
> +		/* initialize otg/host port separately */
> +		if (!of_node_cmp(child_np->name, "host-port")) {
> +			ret = rockchip_usb2phy_host_port_init(rphy, rport,
> +							      child_np);
> +			if (ret)
> +				goto put_child;
> +		}
> +
> +		phy_set_drvdata(rport->phy, rport);
> +		index++;
> +	}
> +
> +	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> +	return PTR_ERR_OR_ZERO(provider);
> +
> +put_child:
> +	of_node_put(child_np);

> +	of_clk_del_provider(np);
> +	clk_unregister(rphy->clk480m);

these two can go away, once you have the devm_action described
near your clk_register function.

> +	return ret;
> +}


Heiko

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

* Re: [PATCH 1/2] Documentation: bindings: add DT documentation for Rockchip USB2PHY
  2016-06-01 22:17       ` Heiko Stübner
@ 2016-06-02  2:53         ` Frank Wang
  0 siblings, 0 replies; 10+ messages in thread
From: Frank Wang @ 2016-06-02  2:53 UTC (permalink / raw)
  To: Heiko Stübner
  Cc: dianders, kishon, robh+dt, pawel.moll, mark.rutland,
	ijc+devicetree, galak, linux-kernel, linux-rockchip, linux-usb,
	kever.yang, huangtao, william.wu, frank.wang

Hi Heiko,

On 06/02/2016 06:17 AM, Heiko Stübner wrote:
> Hi Frank,
>
> Am Mittwoch, 1. Juni 2016, 16:09:41 schrieb Frank Wang:
>>> You might want to add the bvalid and id interrupts for the otg phys as
>>> well
>>> already - would make handling legacy devicetree files easier. [= if they
>>> get specified later, the driver would always need to also handle
>>> devicetrees where they aren't specified].
>>
>> Hmmm! you mean that I can specify these properties into documentation,
>> even if the driver have not handled (implemented) them in current?
>
> The devicetree bindings are supposed to be a generic hardware-description.
> And a driver then simply implements that binding. So if the interrupt is part
> of the hardware it can be part of the binding, independent of the driver.
>
> I guess it really comes down to, will you need those interrupts later in the
> driver, then they should definitly be specified now, as later on you cannot
> require them anymore and always need to also support devicetrees not having
> them.
>

Got it, I have already added them in the new patches which I will hand 
out later.

BR.
Frank

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

* Re: [PATCH 2/2] phy: rockchip-inno-usb2: add a new driver for Rockchip usb2phy
  2016-06-01 23:46   ` Heiko Stübner
@ 2016-06-02  3:19     ` Frank Wang
  0 siblings, 0 replies; 10+ messages in thread
From: Frank Wang @ 2016-06-02  3:19 UTC (permalink / raw)
  To: Heiko Stübner
  Cc: dianders, kishon, robh+dt, pawel.moll, mark.rutland,
	ijc+devicetree, galak, linux-kernel, linux-rockchip, linux-usb,
	kever.yang, huangtao, william.wu, frank.wang

Hi Heiko,

On 06/02/2016 07:46 AM, Heiko Stübner wrote:
> Hi Frank,
>
> Am Dienstag, 31. Mai 2016, 14:40:11 schrieb Frank Wang:
>> The newer SoCs (rk3366, rk3399) take a different usb-phy IP block
>> than rk3288 and before, and most of phy-related registers are also
>> different from the past, so a new phy driver is required necessarily.
>>
>> Signed-off-by: Frank Wang <frank.wang@rock-chips.com>
>> ---
>
> [...]
>
>> +static void rockchip_usb2phy_clk480m_disable(struct clk_hw *hw)
>> +{
>> +	struct rockchip_usb2phy *rphy =
>> +		container_of(hw, struct rockchip_usb2phy, clk480m_hw);
>> +	int index;
>> +
>> +	/* make sure all ports in suspended mode */
>> +	for (index = 0; index != rphy->phy_cfg->num_ports; index++)
>> +		if (!rphy->ports[index].suspended)
>> +			return;
>
> This function can only get called when all clk-references have disabled the
> clock, so you should never reach that point that one phy port is not
> suspended?
>

Yeah, you are right, I will clean them up in the next patches.

>> +
>> +	/* turn off 480m clk output */
>> +	property_enable(rphy, &rphy->phy_cfg->clkout_ctl, false);
>> +}
>> +
>
> [...]
>
> add something like:
>
> static void rockchip_usb2phy_clk480m_unregister(void *data)
> {
> 	struct rockchip_usb2phy *rphy = data;
>
> 	of_clk_del_provider(rphy->dev->of_node);
> 	clk_unregister(rphy->clk480m);
> }
>
>> +static struct clk *
>> +rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
>> +{
>> +	struct device_node *node = rphy->dev->of_node;
>> +	struct clk *clk;
>> +	struct clk_init_data init;
> 	int ret;
>
>> +
>> +	init.name = "clk_usbphy_480m";
>> +	init.ops = &rockchip_usb2phy_clkout_ops;
>> +	init.flags = CLK_IS_ROOT;
>> +	init.parent_names = NULL;
>> +	init.num_parents = 0;
>> +	rphy->clk480m_hw.init = &init;
>> +
>> +	/* optional override of the clockname */
>> +	of_property_read_string(node, "clock-output-names", &init.name);
>> +
>> +	/* register the clock */
>> +	clk = clk_register(rphy->dev, &rphy->clk480m_hw);
> 	if (IS_ERR(clk))
> 		return clk:
>
> 	ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
> 	if (ret < 0)
> 		goto err_clk_provider;
>
> 	ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister,
> rphy);
> 	if (ret < 0)
> 		goto err_unreg_action;
>
> 	return clk;
>
> err_unreg_action:
> 	of_clk_del_provider(node);
> err_clk_provider:
> 	clk_unregister(clk);
> 	return ERR_PTR(ret);
>
>> +}
>

Good comments! Those codes will be included in the next.

> [...]
>
>> +/*
>> + * The function manage host-phy port state and suspend/resume phy port
>> + * to save power.
>> + *
>> + * we rely on utmi_linestate and utmi_hostdisconnect to identify whether
>> + * FS/HS is disconnect or not. Besides, we do not need care it is FS
>> + * disconnected or HS disconnected, actually, we just only need get the
>> + * device is disconnected at last through rearm the delayed work,
>> + * to suspend the phy port in _PHY_STATE_DISCONNECT_ case.
>> + *
>> + * NOTE: It will invoke some clk related APIs, so do not invoke it from
>> + * interrupt context.
>
> This does not seem to match the code, as I don't see any clk_* calls,

Sorry for not describing the comment clearly. In a fact, *_sm_work will 
invoke *phy_suspend or *phy_resume which will invoke some *clk APIs.

Anyway, I will correct it to be more clear.

>
>> + */
>> +static void rockchip_usb2phy_sm_work(struct work_struct *work)
>> +{
 >> +
>>  [...]
 >> +
>> +		/* fall through */
>> +	case PHY_STATE_HS_CONNECT:
>> +		if (rport->suspended) {
>> +			dev_dbg(&rport->phy->dev, "HS/FS connected\n");
>> +			rockchip_usb2phy_resume(rport->phy);
>> +			rport->suspended = false;
>> +		}
>> +		break;
>> +	case PHY_STATE_DISCONNECT:
>> +		if (!rport->suspended) {
>> +			dev_dbg(&rport->phy->dev, "HS/FS disconnected\n");
>> +			rockchip_usb2phy_suspend(rport->phy);
>> +			rport->suspended = true;
>> +		}
>>  [...]
 >> +
>> +	}
>> +
>> +next_schedule:
>> +	mutex_unlock(&rport->mutex);
>> +	schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
>> +}
>
> [...]
>
>> +static int rockchip_usb2phy_probe(struct platform_device *pdev)
>> +{
 >> +
 >>  [...]
 >> +
>> +
>> +	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
>> +	return PTR_ERR_OR_ZERO(provider);
>> +
>> +put_child:
>> +	of_node_put(child_np);
>
>> +	of_clk_del_provider(np);
>> +	clk_unregister(rphy->clk480m);
>
> these two can go away, once you have the devm_action described
> near your clk_register function.

Done

>
>> +	return ret;
>> +}

BR.
Frank

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

end of thread, other threads:[~2016-06-02  3:20 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-31  6:40 [PATCH 0/2] Add a new Rockchip usb2 phy driver Frank Wang
2016-05-31  6:40 ` [PATCH 1/2] Documentation: bindings: add DT documentation for Rockchip USB2PHY Frank Wang
2016-05-31  9:02   ` Heiko Stübner
2016-06-01  8:09     ` Frank Wang
2016-06-01 22:17       ` Heiko Stübner
2016-06-02  2:53         ` Frank Wang
2016-06-01 22:02   ` Rob Herring
2016-05-31  6:40 ` [PATCH 2/2] phy: rockchip-inno-usb2: add a new driver for Rockchip usb2phy Frank Wang
2016-06-01 23:46   ` Heiko Stübner
2016-06-02  3:19     ` Frank Wang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).