All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/2] Remaining sunxi musb patches
@ 2015-05-31 16:10 ` Hans de Goede
  0 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-05-31 16:10 UTC (permalink / raw)
  To: Felipe Balbi, Kishon Vijay Abraham I
  Cc: Maxime Ripard, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi Kishon & Felipe,

Here is an updated version of the remaining (not yet merged in Felipe's tree)
sunxi musb patches.

The "phy-sun4i-usb: Add full support for usb0 phy / OTG" patch has been
updated with a small bug-fix and is ready for merging.

The "musb: Add support for the Allwinner sunxi musb" patch has been updated
to use the latest version of Maxime's sunxi sram controller driver. This
one has a compile-time dependency on Maxime's sunxi sram controller driver,
so it cannot be merged until that is merged.

I also have updated the dts patches enabling the sunxi musb controller on a
number of boards for the sram controller changes, you can look at these
here: https://github.com/jwrdegoede/linux-sunxi/commits/sunxi-wip

I'll post a new version of the dts patches for upstreaming as soon as the
other bits are merged.

Regards,

Hans

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

* [PATCH v4 0/2] Remaining sunxi musb patches
@ 2015-05-31 16:10 ` Hans de Goede
  0 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-05-31 16:10 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Kishon & Felipe,

Here is an updated version of the remaining (not yet merged in Felipe's tree)
sunxi musb patches.

The "phy-sun4i-usb: Add full support for usb0 phy / OTG" patch has been
updated with a small bug-fix and is ready for merging.

The "musb: Add support for the Allwinner sunxi musb" patch has been updated
to use the latest version of Maxime's sunxi sram controller driver. This
one has a compile-time dependency on Maxime's sunxi sram controller driver,
so it cannot be merged until that is merged.

I also have updated the dts patches enabling the sunxi musb controller on a
number of boards for the sram controller changes, you can look at these
here: https://github.com/jwrdegoede/linux-sunxi/commits/sunxi-wip

I'll post a new version of the dts patches for upstreaming as soon as the
other bits are merged.

Regards,

Hans

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

* [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-05-31 16:10 ` Hans de Goede
@ 2015-05-31 16:10     ` Hans de Goede
  -1 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-05-31 16:10 UTC (permalink / raw)
  To: Felipe Balbi, Kishon Vijay Abraham I
  Cc: Maxime Ripard, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Hans de Goede

The usb0 phy is connected to an OTG controller, and as such needs some special
handling:

1) It allows explicit control over the pullups, enable these on phy_init and
disable them on phy_exit.

2) It has bits to signal id and vbus detect to the musb-core, add support for
for monitoring id and vbus detect gpio-s for use in dual role mode, and set
these bits to the correct values for operating in host only mode when no
gpios are specified in the devicetree.

3) When in dual role mode the musb sunxi glue needs to know if the a host or
device cable is plugged in, so when in dual role mode register an extcon.

While updating the devicetree binding documentation also add documentation
for the sofar undocumented usage of regulators for vbus for all 3 phys.

Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
Changes in v2:
-Removed the sunxi specific phy functions, instead the id / vbus gpio polling
 has been moved to the phy-sun4i-usb driver and their status is exported
 through extcon for the sunxi-musb glue
Changes in v3:
-No changes
Changes in v4:
-Do not call regulator_disable in an unbalanced manner when an external vbus
 is present
---
 .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
 drivers/phy/Kconfig                                |   1 +
 drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
 3 files changed, 281 insertions(+), 11 deletions(-)

diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
index 16528b9..557fa99 100644
--- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
+++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
@@ -23,6 +23,13 @@ Required properties:
   * "usb1_reset"
   * "usb2_reset" for sun4i, sun6i or sun7i
 
+Optional properties:
+- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
+- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
+- usb0_vbus-supply : regulator phandle for controller usb0 vbus
+- usb1_vbus-supply : regulator phandle for controller usb1 vbus
+- usb2_vbus-supply : regulator phandle for controller usb2 vbus
+
 Example:
 	usbphy: phy@0x01c13400 {
 		#phy-cells = <1>;
@@ -32,6 +39,13 @@ Example:
 		reg-names = "phy_ctrl", "pmu1", "pmu2";
 		clocks = <&usb_clk 8>;
 		clock-names = "usb_phy";
-		resets = <&usb_clk 1>, <&usb_clk 2>;
-		reset-names = "usb1_reset", "usb2_reset";
+		resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
+		reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
+		pinctrl-names = "default";
+		pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
+		usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
+		usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
+		usb0_vbus-supply = <&reg_usb0_vbus>;
+		usb1_vbus-supply = <&reg_usb1_vbus>;
+		usb2_vbus-supply = <&reg_usb2_vbus>;
 	};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index a53bd5b..4614fba 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -173,6 +173,7 @@ config PHY_SUN4I_USB
 	tristate "Allwinner sunxi SoC USB PHY driver"
 	depends on ARCH_SUNXI && HAS_IOMEM && OF
 	depends on RESET_CONTROLLER
+	select EXTCON
 	select GENERIC_PHY
 	help
 	  Enable this to support the transceiver that is part of Allwinner
diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
index 91c5be4..b45d707 100644
--- a/drivers/phy/phy-sun4i-usb.c
+++ b/drivers/phy/phy-sun4i-usb.c
@@ -1,7 +1,7 @@
 /*
  * Allwinner sun4i USB phy driver
  *
- * Copyright (C) 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
+ * Copyright (C) 2014-2015 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
  *
  * Based on code from
  * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
@@ -23,17 +23,23 @@
 
 #include <linux/clk.h>
 #include <linux/err.h>
+#include <linux/extcon.h>
 #include <linux/io.h>
+#include <linux/interrupt.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_gpio.h>
 #include <linux/phy/phy.h>
 #include <linux/phy/phy-sun4i-usb.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
+#include <linux/workqueue.h>
+
+#define DRIVER_NAME "sun4i-usb-phy"
 
 #define REG_ISCR			0x00
 #define REG_PHYCTL			0x04
@@ -47,6 +53,17 @@
 #define SUNXI_AHB_INCRX_ALIGN_EN	BIT(8)
 #define SUNXI_ULPI_BYPASS_EN		BIT(0)
 
+/* ISCR, Interface Status and Control bits */
+#define ISCR_ID_PULLUP_EN		(1 << 17)
+#define ISCR_DPDM_PULLUP_EN	(1 << 16)
+/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
+#define ISCR_FORCE_ID_MASK	(3 << 14)
+#define ISCR_FORCE_ID_LOW		(2 << 14)
+#define ISCR_FORCE_ID_HIGH	(3 << 14)
+#define ISCR_FORCE_VBUS_MASK	(3 << 12)
+#define ISCR_FORCE_VBUS_LOW	(2 << 12)
+#define ISCR_FORCE_VBUS_HIGH	(3 << 12)
+
 /* Common Control Bits for Both PHYs */
 #define PHY_PLL_BW			0x03
 #define PHY_RES45_CAL_EN		0x0c
@@ -63,6 +80,13 @@
 
 #define MAX_PHYS			3
 
+/*
+ * Note do not raise the debounce time, we must report Vusb high within 100ms
+ * otherwise we get Vbus errors
+ */
+#define DEBOUNCE_TIME			msecs_to_jiffies(50)
+#define POLL_TIME			msecs_to_jiffies(250)
+
 struct sun4i_usb_phy_data {
 	void __iomem *base;
 	struct mutex mutex;
@@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
 		struct regulator *vbus;
 		struct reset_control *reset;
 		struct clk *clk;
+		bool regulator_on;
 		int index;
 	} phys[MAX_PHYS];
+	/* phy0 / otg related variables */
+	struct extcon_dev extcon;
+	const char *extcon_cable_names[3];
+	bool phy0_init;
+	bool phy0_poll;
+	struct gpio_desc *id_det_gpio;
+	struct gpio_desc *vbus_det_gpio;
+	int id_det_irq;
+	int vbus_det_irq;
+	int id_det;
+	int vbus_det;
+	struct delayed_work detect;
 };
 
 #define to_sun4i_usb_phy_data(phy) \
 	container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
 
+static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
+{
+	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+	u32 iscr;
+
+	iscr = readl(data->base + REG_ISCR);
+	iscr &= ~clr;
+	iscr |= set;
+	writel(iscr, data->base + REG_ISCR);
+}
+
+static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
+{
+	if (val)
+		val = ISCR_FORCE_ID_HIGH;
+	else
+		val = ISCR_FORCE_ID_LOW;
+
+	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
+}
+
+static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
+{
+	if (val)
+		val = ISCR_FORCE_VBUS_HIGH;
+	else
+		val = ISCR_FORCE_VBUS_LOW;
+
+	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
+}
+
 static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
 				int len)
 {
@@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
 
 	sun4i_usb_phy_passby(phy, 1);
 
+	if (phy->index == 0) {
+		data->phy0_init = true;
+
+		/* Enable pull-ups */
+		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
+		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
+
+		if (data->id_det_gpio) {
+			/* OTG mode, force ISCR and cable state updates */
+			data->id_det = -1;
+			data->vbus_det = -1;
+			queue_delayed_work(system_wq, &data->detect, 0);
+		} else {
+			/* Host only mode */
+			sun4i_usb_phy0_set_id_detect(_phy, 0);
+			sun4i_usb_phy0_set_vbus_detect(_phy, 1);
+		}
+	}
+
 	return 0;
 }
 
 static int sun4i_usb_phy_exit(struct phy *_phy)
 {
 	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+
+	if (phy->index == 0) {
+		/* Disable pull-ups */
+		sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
+		sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
+		data->phy0_init = false;
+	}
 
 	sun4i_usb_phy_passby(phy, 0);
 	reset_control_assert(phy->reset);
@@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
 static int sun4i_usb_phy_power_on(struct phy *_phy)
 {
 	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
-	int ret = 0;
+	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+	int ret;
+
+	if (!phy->vbus || phy->regulator_on)
+		return 0;
+
+	/* For phy0 only turn on Vbus if we don't have an ext. Vbus */
+	if (phy->index == 0 && data->vbus_det)
+		return 0;
 
-	if (phy->vbus)
-		ret = regulator_enable(phy->vbus);
+	ret = regulator_enable(phy->vbus);
+	if (ret)
+		return ret;
 
-	return ret;
+	phy->regulator_on = true;
+
+	/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
+	if (phy->index == 0 && data->phy0_poll)
+		mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
+
+	return 0;
 }
 
 static int sun4i_usb_phy_power_off(struct phy *_phy)
 {
 	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+
+	if (!phy->vbus || !phy->regulator_on)
+		return 0;
 
-	if (phy->vbus)
-		regulator_disable(phy->vbus);
+	regulator_disable(phy->vbus);
+	phy->regulator_on = false;
+
+	/*
+	 * phy0 vbus typically slowly discharges, sometimes this causes the
+	 * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
+	 */
+	if (phy->index == 0 && !data->phy0_poll)
+		mod_delayed_work(system_wq, &data->detect, POLL_TIME);
 
 	return 0;
 }
@@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
 	.owner		= THIS_MODULE,
 };
 
+static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
+{
+	struct sun4i_usb_phy_data *data =
+		container_of(work, struct sun4i_usb_phy_data, detect.work);
+	struct phy *phy0 = data->phys[0].phy;
+	int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
+
+	id_det = gpiod_get_value_cansleep(data->id_det_gpio);
+	vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
+
+	mutex_lock(&phy0->mutex);
+
+	if (!data->phy0_init) {
+		mutex_unlock(&phy0->mutex);
+		return;
+	}
+
+	if (id_det != data->id_det) {
+		sun4i_usb_phy0_set_id_detect(phy0, id_det);
+		data->id_det = id_det;
+		id_notify = 1;
+	}
+
+	if (vbus_det != data->vbus_det) {
+		sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
+		data->vbus_det = vbus_det;
+		vbus_notify = 1;
+	}
+
+	mutex_unlock(&phy0->mutex);
+
+	if (id_notify)
+		extcon_set_cable_state(&data->extcon,
+				       extcon_cable_name[EXTCON_USB_HOST],
+				       !id_det);
+
+	if (vbus_notify)
+		extcon_set_cable_state(&data->extcon,
+				       extcon_cable_name[EXTCON_USB],
+				       vbus_det);
+
+	if (data->phy0_poll)
+		queue_delayed_work(system_wq, &data->detect, POLL_TIME);
+}
+
+static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
+{
+	struct sun4i_usb_phy_data *data = dev_id;
+
+	/* vbus or id changed, let the pins settle and then scan them */
+	mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
+
+	return IRQ_HANDLED;
+}
+
 static struct phy *sun4i_usb_phy_xlate(struct device *dev,
 					struct of_phandle_args *args)
 {
@@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
 	struct phy_provider *phy_provider;
 	bool dedicated_clocks;
 	struct resource *res;
-	int i;
+	int i, ret;
 
 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
 
 	mutex_init(&data->mutex);
+	INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
+	data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
+	data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
+	data->extcon_cable_names[2] = NULL;
+	data->extcon.name = DRIVER_NAME;
+	data->extcon.supported_cable = data->extcon_cable_names;
+	data->extcon.dev.parent = dev;
 
 	if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
 		data->num_phys = 2;
@@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
 	if (IS_ERR(data->base))
 		return PTR_ERR(data->base);
 
+	data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
+	if (IS_ERR(data->id_det_gpio)) {
+		if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		data->id_det_gpio = NULL;
+	}
+
+	data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
+	if (IS_ERR(data->vbus_det_gpio)) {
+		if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		data->vbus_det_gpio = NULL;
+	}
+
+	/* We either want both gpio pins or neither (when in host mode) */
+	if (!data->id_det_gpio != !data->vbus_det_gpio) {
+		dev_err(dev, "failed to get id or vbus detect pin\n");
+		return -ENODEV;
+	}
+
+	if (data->id_det_gpio) {
+		ret = devm_extcon_dev_register(dev, &data->extcon);
+		if (ret) {
+			dev_err(dev, "failed to register extcon: %d\n", ret);
+			return ret;
+		}
+	}
+
 	for (i = 0; i < data->num_phys; i++) {
 		struct sun4i_usb_phy *phy = data->phys + i;
 		char name[16];
@@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
 		phy_set_drvdata(phy->phy, &data->phys[i]);
 	}
 
+	data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
+	data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
+	if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
+		data->phy0_poll = true;
+
+	if (data->id_det_irq >= 0) {
+		ret = devm_request_irq(dev, data->id_det_irq,
+				sun4i_usb_phy0_id_vbus_det_irq,
+				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				"usb0-id-det", data);
+		if (ret) {
+			dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
+			return ret;
+		}
+	}
+
+	if (data->vbus_det_irq >= 0) {
+		ret = devm_request_irq(dev, data->vbus_det_irq,
+				sun4i_usb_phy0_id_vbus_det_irq,
+				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				"usb0-vbus-det", data);
+		if (ret) {
+			dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
+			return ret;
+		}
+	}
+
 	dev_set_drvdata(dev, data);
 	phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
 
 	return PTR_ERR_OR_ZERO(phy_provider);
 }
 
+static int sun4i_usb_phy_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
+
+	if (data->id_det_irq >= 0)
+		devm_free_irq(dev, data->id_det_irq, data);
+	if (data->vbus_det_irq >= 0)
+		devm_free_irq(dev, data->vbus_det_irq, data);
+
+	cancel_delayed_work_sync(&data->detect);
+
+	return 0;
+}
+
 static const struct of_device_id sun4i_usb_phy_of_match[] = {
 	{ .compatible = "allwinner,sun4i-a10-usb-phy" },
 	{ .compatible = "allwinner,sun5i-a13-usb-phy" },
@@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
 
 static struct platform_driver sun4i_usb_phy_driver = {
 	.probe	= sun4i_usb_phy_probe,
+	.remove	= sun4i_usb_phy_remove,
 	.driver = {
 		.of_match_table	= sun4i_usb_phy_of_match,
-		.name  = "sun4i-usb-phy",
+		.name  = DRIVER_NAME,
 	}
 };
 module_platform_driver(sun4i_usb_phy_driver);
-- 
2.4.2

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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	[flat|nested] 57+ messages in thread

* [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-05-31 16:10     ` Hans de Goede
  0 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-05-31 16:10 UTC (permalink / raw)
  To: linux-arm-kernel

The usb0 phy is connected to an OTG controller, and as such needs some special
handling:

1) It allows explicit control over the pullups, enable these on phy_init and
disable them on phy_exit.

2) It has bits to signal id and vbus detect to the musb-core, add support for
for monitoring id and vbus detect gpio-s for use in dual role mode, and set
these bits to the correct values for operating in host only mode when no
gpios are specified in the devicetree.

3) When in dual role mode the musb sunxi glue needs to know if the a host or
device cable is plugged in, so when in dual role mode register an extcon.

While updating the devicetree binding documentation also add documentation
for the sofar undocumented usage of regulators for vbus for all 3 phys.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
Changes in v2:
-Removed the sunxi specific phy functions, instead the id / vbus gpio polling
 has been moved to the phy-sun4i-usb driver and their status is exported
 through extcon for the sunxi-musb glue
Changes in v3:
-No changes
Changes in v4:
-Do not call regulator_disable in an unbalanced manner when an external vbus
 is present
---
 .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
 drivers/phy/Kconfig                                |   1 +
 drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
 3 files changed, 281 insertions(+), 11 deletions(-)

diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
index 16528b9..557fa99 100644
--- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
+++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
@@ -23,6 +23,13 @@ Required properties:
   * "usb1_reset"
   * "usb2_reset" for sun4i, sun6i or sun7i
 
+Optional properties:
+- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
+- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
+- usb0_vbus-supply : regulator phandle for controller usb0 vbus
+- usb1_vbus-supply : regulator phandle for controller usb1 vbus
+- usb2_vbus-supply : regulator phandle for controller usb2 vbus
+
 Example:
 	usbphy: phy at 0x01c13400 {
 		#phy-cells = <1>;
@@ -32,6 +39,13 @@ Example:
 		reg-names = "phy_ctrl", "pmu1", "pmu2";
 		clocks = <&usb_clk 8>;
 		clock-names = "usb_phy";
-		resets = <&usb_clk 1>, <&usb_clk 2>;
-		reset-names = "usb1_reset", "usb2_reset";
+		resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
+		reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
+		pinctrl-names = "default";
+		pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
+		usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
+		usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
+		usb0_vbus-supply = <&reg_usb0_vbus>;
+		usb1_vbus-supply = <&reg_usb1_vbus>;
+		usb2_vbus-supply = <&reg_usb2_vbus>;
 	};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index a53bd5b..4614fba 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -173,6 +173,7 @@ config PHY_SUN4I_USB
 	tristate "Allwinner sunxi SoC USB PHY driver"
 	depends on ARCH_SUNXI && HAS_IOMEM && OF
 	depends on RESET_CONTROLLER
+	select EXTCON
 	select GENERIC_PHY
 	help
 	  Enable this to support the transceiver that is part of Allwinner
diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
index 91c5be4..b45d707 100644
--- a/drivers/phy/phy-sun4i-usb.c
+++ b/drivers/phy/phy-sun4i-usb.c
@@ -1,7 +1,7 @@
 /*
  * Allwinner sun4i USB phy driver
  *
- * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (C) 2014-2015 Hans de Goede <hdegoede@redhat.com>
  *
  * Based on code from
  * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
@@ -23,17 +23,23 @@
 
 #include <linux/clk.h>
 #include <linux/err.h>
+#include <linux/extcon.h>
 #include <linux/io.h>
+#include <linux/interrupt.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_gpio.h>
 #include <linux/phy/phy.h>
 #include <linux/phy/phy-sun4i-usb.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
+#include <linux/workqueue.h>
+
+#define DRIVER_NAME "sun4i-usb-phy"
 
 #define REG_ISCR			0x00
 #define REG_PHYCTL			0x04
@@ -47,6 +53,17 @@
 #define SUNXI_AHB_INCRX_ALIGN_EN	BIT(8)
 #define SUNXI_ULPI_BYPASS_EN		BIT(0)
 
+/* ISCR, Interface Status and Control bits */
+#define ISCR_ID_PULLUP_EN		(1 << 17)
+#define ISCR_DPDM_PULLUP_EN	(1 << 16)
+/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
+#define ISCR_FORCE_ID_MASK	(3 << 14)
+#define ISCR_FORCE_ID_LOW		(2 << 14)
+#define ISCR_FORCE_ID_HIGH	(3 << 14)
+#define ISCR_FORCE_VBUS_MASK	(3 << 12)
+#define ISCR_FORCE_VBUS_LOW	(2 << 12)
+#define ISCR_FORCE_VBUS_HIGH	(3 << 12)
+
 /* Common Control Bits for Both PHYs */
 #define PHY_PLL_BW			0x03
 #define PHY_RES45_CAL_EN		0x0c
@@ -63,6 +80,13 @@
 
 #define MAX_PHYS			3
 
+/*
+ * Note do not raise the debounce time, we must report Vusb high within 100ms
+ * otherwise we get Vbus errors
+ */
+#define DEBOUNCE_TIME			msecs_to_jiffies(50)
+#define POLL_TIME			msecs_to_jiffies(250)
+
 struct sun4i_usb_phy_data {
 	void __iomem *base;
 	struct mutex mutex;
@@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
 		struct regulator *vbus;
 		struct reset_control *reset;
 		struct clk *clk;
+		bool regulator_on;
 		int index;
 	} phys[MAX_PHYS];
+	/* phy0 / otg related variables */
+	struct extcon_dev extcon;
+	const char *extcon_cable_names[3];
+	bool phy0_init;
+	bool phy0_poll;
+	struct gpio_desc *id_det_gpio;
+	struct gpio_desc *vbus_det_gpio;
+	int id_det_irq;
+	int vbus_det_irq;
+	int id_det;
+	int vbus_det;
+	struct delayed_work detect;
 };
 
 #define to_sun4i_usb_phy_data(phy) \
 	container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
 
+static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
+{
+	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+	u32 iscr;
+
+	iscr = readl(data->base + REG_ISCR);
+	iscr &= ~clr;
+	iscr |= set;
+	writel(iscr, data->base + REG_ISCR);
+}
+
+static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
+{
+	if (val)
+		val = ISCR_FORCE_ID_HIGH;
+	else
+		val = ISCR_FORCE_ID_LOW;
+
+	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
+}
+
+static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
+{
+	if (val)
+		val = ISCR_FORCE_VBUS_HIGH;
+	else
+		val = ISCR_FORCE_VBUS_LOW;
+
+	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
+}
+
 static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
 				int len)
 {
@@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
 
 	sun4i_usb_phy_passby(phy, 1);
 
+	if (phy->index == 0) {
+		data->phy0_init = true;
+
+		/* Enable pull-ups */
+		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
+		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
+
+		if (data->id_det_gpio) {
+			/* OTG mode, force ISCR and cable state updates */
+			data->id_det = -1;
+			data->vbus_det = -1;
+			queue_delayed_work(system_wq, &data->detect, 0);
+		} else {
+			/* Host only mode */
+			sun4i_usb_phy0_set_id_detect(_phy, 0);
+			sun4i_usb_phy0_set_vbus_detect(_phy, 1);
+		}
+	}
+
 	return 0;
 }
 
 static int sun4i_usb_phy_exit(struct phy *_phy)
 {
 	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+
+	if (phy->index == 0) {
+		/* Disable pull-ups */
+		sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
+		sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
+		data->phy0_init = false;
+	}
 
 	sun4i_usb_phy_passby(phy, 0);
 	reset_control_assert(phy->reset);
@@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
 static int sun4i_usb_phy_power_on(struct phy *_phy)
 {
 	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
-	int ret = 0;
+	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+	int ret;
+
+	if (!phy->vbus || phy->regulator_on)
+		return 0;
+
+	/* For phy0 only turn on Vbus if we don't have an ext. Vbus */
+	if (phy->index == 0 && data->vbus_det)
+		return 0;
 
-	if (phy->vbus)
-		ret = regulator_enable(phy->vbus);
+	ret = regulator_enable(phy->vbus);
+	if (ret)
+		return ret;
 
-	return ret;
+	phy->regulator_on = true;
+
+	/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
+	if (phy->index == 0 && data->phy0_poll)
+		mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
+
+	return 0;
 }
 
 static int sun4i_usb_phy_power_off(struct phy *_phy)
 {
 	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+
+	if (!phy->vbus || !phy->regulator_on)
+		return 0;
 
-	if (phy->vbus)
-		regulator_disable(phy->vbus);
+	regulator_disable(phy->vbus);
+	phy->regulator_on = false;
+
+	/*
+	 * phy0 vbus typically slowly discharges, sometimes this causes the
+	 * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
+	 */
+	if (phy->index == 0 && !data->phy0_poll)
+		mod_delayed_work(system_wq, &data->detect, POLL_TIME);
 
 	return 0;
 }
@@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
 	.owner		= THIS_MODULE,
 };
 
+static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
+{
+	struct sun4i_usb_phy_data *data =
+		container_of(work, struct sun4i_usb_phy_data, detect.work);
+	struct phy *phy0 = data->phys[0].phy;
+	int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
+
+	id_det = gpiod_get_value_cansleep(data->id_det_gpio);
+	vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
+
+	mutex_lock(&phy0->mutex);
+
+	if (!data->phy0_init) {
+		mutex_unlock(&phy0->mutex);
+		return;
+	}
+
+	if (id_det != data->id_det) {
+		sun4i_usb_phy0_set_id_detect(phy0, id_det);
+		data->id_det = id_det;
+		id_notify = 1;
+	}
+
+	if (vbus_det != data->vbus_det) {
+		sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
+		data->vbus_det = vbus_det;
+		vbus_notify = 1;
+	}
+
+	mutex_unlock(&phy0->mutex);
+
+	if (id_notify)
+		extcon_set_cable_state(&data->extcon,
+				       extcon_cable_name[EXTCON_USB_HOST],
+				       !id_det);
+
+	if (vbus_notify)
+		extcon_set_cable_state(&data->extcon,
+				       extcon_cable_name[EXTCON_USB],
+				       vbus_det);
+
+	if (data->phy0_poll)
+		queue_delayed_work(system_wq, &data->detect, POLL_TIME);
+}
+
+static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
+{
+	struct sun4i_usb_phy_data *data = dev_id;
+
+	/* vbus or id changed, let the pins settle and then scan them */
+	mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
+
+	return IRQ_HANDLED;
+}
+
 static struct phy *sun4i_usb_phy_xlate(struct device *dev,
 					struct of_phandle_args *args)
 {
@@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
 	struct phy_provider *phy_provider;
 	bool dedicated_clocks;
 	struct resource *res;
-	int i;
+	int i, ret;
 
 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
 
 	mutex_init(&data->mutex);
+	INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
+	data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
+	data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
+	data->extcon_cable_names[2] = NULL;
+	data->extcon.name = DRIVER_NAME;
+	data->extcon.supported_cable = data->extcon_cable_names;
+	data->extcon.dev.parent = dev;
 
 	if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
 		data->num_phys = 2;
@@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
 	if (IS_ERR(data->base))
 		return PTR_ERR(data->base);
 
+	data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
+	if (IS_ERR(data->id_det_gpio)) {
+		if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		data->id_det_gpio = NULL;
+	}
+
+	data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
+	if (IS_ERR(data->vbus_det_gpio)) {
+		if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		data->vbus_det_gpio = NULL;
+	}
+
+	/* We either want both gpio pins or neither (when in host mode) */
+	if (!data->id_det_gpio != !data->vbus_det_gpio) {
+		dev_err(dev, "failed to get id or vbus detect pin\n");
+		return -ENODEV;
+	}
+
+	if (data->id_det_gpio) {
+		ret = devm_extcon_dev_register(dev, &data->extcon);
+		if (ret) {
+			dev_err(dev, "failed to register extcon: %d\n", ret);
+			return ret;
+		}
+	}
+
 	for (i = 0; i < data->num_phys; i++) {
 		struct sun4i_usb_phy *phy = data->phys + i;
 		char name[16];
@@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
 		phy_set_drvdata(phy->phy, &data->phys[i]);
 	}
 
+	data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
+	data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
+	if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
+		data->phy0_poll = true;
+
+	if (data->id_det_irq >= 0) {
+		ret = devm_request_irq(dev, data->id_det_irq,
+				sun4i_usb_phy0_id_vbus_det_irq,
+				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				"usb0-id-det", data);
+		if (ret) {
+			dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
+			return ret;
+		}
+	}
+
+	if (data->vbus_det_irq >= 0) {
+		ret = devm_request_irq(dev, data->vbus_det_irq,
+				sun4i_usb_phy0_id_vbus_det_irq,
+				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+				"usb0-vbus-det", data);
+		if (ret) {
+			dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
+			return ret;
+		}
+	}
+
 	dev_set_drvdata(dev, data);
 	phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
 
 	return PTR_ERR_OR_ZERO(phy_provider);
 }
 
+static int sun4i_usb_phy_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
+
+	if (data->id_det_irq >= 0)
+		devm_free_irq(dev, data->id_det_irq, data);
+	if (data->vbus_det_irq >= 0)
+		devm_free_irq(dev, data->vbus_det_irq, data);
+
+	cancel_delayed_work_sync(&data->detect);
+
+	return 0;
+}
+
 static const struct of_device_id sun4i_usb_phy_of_match[] = {
 	{ .compatible = "allwinner,sun4i-a10-usb-phy" },
 	{ .compatible = "allwinner,sun5i-a13-usb-phy" },
@@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
 
 static struct platform_driver sun4i_usb_phy_driver = {
 	.probe	= sun4i_usb_phy_probe,
+	.remove	= sun4i_usb_phy_remove,
 	.driver = {
 		.of_match_table	= sun4i_usb_phy_of_match,
-		.name  = "sun4i-usb-phy",
+		.name  = DRIVER_NAME,
 	}
 };
 module_platform_driver(sun4i_usb_phy_driver);
-- 
2.4.2

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

* [PATCH v4 2/2] musb: Add support for the Allwinner sunxi musb controller
  2015-05-31 16:10 ` Hans de Goede
@ 2015-05-31 16:10     ` Hans de Goede
  -1 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-05-31 16:10 UTC (permalink / raw)
  To: Felipe Balbi, Kishon Vijay Abraham I
  Cc: Maxime Ripard, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Hans de Goede

This is based on initial code to get the Allwinner sunxi musb controller
supported by Chen-Yu Tsai and Roman Byshko.

This adds support for the Allwinner sunxi musb controller in both host only
and otg mode. Peripheral only mode is not supported, as no boards use that.

This has been tested on a cubietruck (A20 SoC) and an UTOO P66 tablet
(A13 SoC) with a variety of devices in host mode and with the g_serial gadget
driver in peripheral mode, plugging otg / host cables in/out a lot of times
in all possible imaginable plug orders.

Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
Changes in v2:
-Move polling of id and vbus-det gpio-s to the phy driver
-Use extcon to get id (USB_HOST mode) status changes from the phy driver
-Stop using syscon, instead use Maxime Ripard's sunxi SRAM controller driver
Changes in v3:
-Check that USB_MUSB_FOO config is compatible with the dr_mode setting from dt
Changes in v4:
-Squash in "musb: sunxi: Add pre/post root reset end platform functions" patch
-Adjust for sunxi_sram controller driver changes
-Stop musb work from turning vbus off again when in host mode
---
 .../bindings/usb/allwinner,sun4i-a10-musb.txt      |  25 +
 drivers/usb/musb/Kconfig                           |  13 +-
 drivers/usb/musb/Makefile                          |   1 +
 drivers/usb/musb/musb_core.h                       |   1 +
 drivers/usb/musb/musb_gadget.c                     |   6 +
 drivers/usb/musb/musb_virthub.c                    |   6 +
 drivers/usb/musb/sunxi.c                           | 690 +++++++++++++++++++++
 7 files changed, 741 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt
 create mode 100644 drivers/usb/musb/sunxi.c

diff --git a/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt
new file mode 100644
index 0000000..21dfc7f
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt
@@ -0,0 +1,25 @@
+Allwinner sun4i A10 musb DRC/OTG controller
+-------------------------------------------
+
+Required properties:
+ - compatible      : "allwinner,sun4i-a10-musb"
+ - reg             : mmio address range of the musb controller
+ - clocks          : clock specifier for the musb controller ahb gate clock
+ - interrupts      : interrupt to which the musb controller is connected
+ - interrupt-names : must be "mc"
+ - phys            : phy specifier for the otg phy
+ - phy-names       : must be "usb"
+ - dr_mode         : Dual-Role mode must be "host" or "otg"
+
+Example:
+
+	usb_otg: usb@01c13000 {
+		compatible = "allwinner,sun4i-a10-musb";
+		reg = <0x01c13000 0x0400>;
+		clocks = <&ahb_gates 0>;
+		interrupts = <38>;
+		interrupt-names = "mc";
+		phys = <&usbphy 0>;
+		phy-names = "usb";
+		status = "disabled";
+	};
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 39db8b6..2c11cb4 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -5,7 +5,7 @@
 
 # (M)HDRC = (Multipoint) Highspeed Dual-Role Controller
 config USB_MUSB_HDRC
-	tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
+	tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, AW, ...)'
 	depends on (USB || USB_GADGET)
 	help
 	  Say Y here if your system has a dual role high speed USB
@@ -20,6 +20,8 @@ config USB_MUSB_HDRC
 	  Analog Devices parts using this IP include Blackfin BF54x,
 	  BF525 and BF527.
 
+	  Allwinner SoCs using this IP include A10, A13, A20, ...
+
 	  If you do not know what this is, please say N.
 
 	  To compile this driver as a module, choose M here; the
@@ -60,6 +62,15 @@ endchoice
 
 comment "Platform Glue Layer"
 
+config USB_MUSB_SUNXI
+	tristate "Allwinner (sunxi)"
+	depends on ARCH_SUNXI
+	depends on NOP_USB_XCEIV
+	depends on PHY_SUN4I_USB
+	select EXTCON
+	select GENERIC_PHY
+	select SUNXI_SRAM
+
 config USB_MUSB_DAVINCI
 	tristate "DaVinci"
 	depends on ARCH_DAVINCI_DMx
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile
index ba49501..f95befe 100644
--- a/drivers/usb/musb/Makefile
+++ b/drivers/usb/musb/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_USB_MUSB_DA8XX)			+= da8xx.o
 obj-$(CONFIG_USB_MUSB_BLACKFIN)			+= blackfin.o
 obj-$(CONFIG_USB_MUSB_UX500)			+= ux500.o
 obj-$(CONFIG_USB_MUSB_JZ4740)			+= jz4740.o
+obj-$(CONFIG_USB_MUSB_SUNXI)			+= sunxi.o
 
 
 obj-$(CONFIG_USB_MUSB_AM335X_CHILD)		+= musb_am335x.o
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index b1cb2c3..30a3123 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -169,6 +169,7 @@ struct musb_io;
  */
 struct musb_platform_ops {
 
+#define MUSB_SUN4I		BIT(7)
 #define MUSB_DMA_UX500		BIT(6)
 #define MUSB_DMA_CPPI41		BIT(5)
 #define MUSB_DMA_CPPI		BIT(4)
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 4c481cd..ec2e908 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -2003,6 +2003,12 @@ void musb_g_disconnect(struct musb *musb)
 		spin_lock(&musb->lock);
 	}
 
+	/* On sunxi ep0 FADDR must be 0 when (re)entering peripheral mode */
+	if (musb->io.quirks & MUSB_SUN4I) {
+		musb_ep_select(musb->mregs, 0);
+		musb_writeb(musb->mregs, MUSB_FADDR, 0);
+	}
+
 	switch (musb->xceiv->otg->state) {
 	default:
 		dev_dbg(musb->controller, "Unhandled disconnect %s, setting a_idle\n",
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 30842bc..fe98623 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -226,6 +226,12 @@ void musb_root_disconnect(struct musb *musb)
 	usb_hcd_poll_rh_status(musb->hcd);
 	musb->is_active = 0;
 
+	/* On sunxi ep0 FADDR must be 0 when (re)entering peripheral mode */
+	if (musb->io.quirks & MUSB_SUN4I) {
+		musb_ep_select(musb->mregs, 0);
+		musb_writeb(musb->mregs, MUSB_FADDR, 0);
+	}
+
 	switch (musb->xceiv->otg->state) {
 	case OTG_STATE_A_SUSPEND:
 		if (otg->host->b_hnp_enable) {
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
new file mode 100644
index 0000000..5db882f
--- /dev/null
+++ b/drivers/usb/musb/sunxi.c
@@ -0,0 +1,690 @@
+/*
+ * Allwinner sun4i MUSB Glue Layer
+ *
+ * Copyright (C) 2015 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
+ *
+ * Based on code from
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ *
+ * 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.
+ *
+ * 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/err.h>
+#include <linux/extcon.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy-sun4i-usb.h>
+#include <linux/platform_device.h>
+#include <linux/soc/sunxi/sunxi_sram.h>
+#include <linux/usb/musb.h>
+#include <linux/usb/of.h>
+#include <linux/usb/usb_phy_generic.h>
+#include <linux/workqueue.h>
+#include "musb_core.h"
+
+/*
+ * Register offsets, note sunxi musb has a different layout then most
+ * musb implementations, we translate the layout in musb_readb & friends.
+ */
+#define SUNXI_MUSB_POWER			0x0040
+#define SUNXI_MUSB_DEVCTL			0x0041
+#define SUNXI_MUSB_INDEX			0x0042
+#define SUNXI_MUSB_VEND0			0x0043
+#define SUNXI_MUSB_INTRTX			0x0044
+#define SUNXI_MUSB_INTRRX			0x0046
+#define SUNXI_MUSB_INTRTXE			0x0048
+#define SUNXI_MUSB_INTRRXE			0x004a
+#define SUNXI_MUSB_INTRUSB			0x004c
+#define SUNXI_MUSB_INTRUSBE			0x0050
+#define SUNXI_MUSB_FRAME			0x0054
+#define SUNXI_MUSB_TXFIFOSZ			0x0090
+#define SUNXI_MUSB_TXFIFOADD			0x0092
+#define SUNXI_MUSB_RXFIFOSZ			0x0094
+#define SUNXI_MUSB_RXFIFOADD			0x0096
+#define SUNXI_MUSB_FADDR			0x0098
+#define SUNXI_MUSB_TXFUNCADDR			0x0098
+#define SUNXI_MUSB_TXHUBADDR			0x009a
+#define SUNXI_MUSB_TXHUBPORT			0x009b
+#define SUNXI_MUSB_RXFUNCADDR			0x009c
+#define SUNXI_MUSB_RXHUBADDR			0x009e
+#define SUNXI_MUSB_RXHUBPORT			0x009f
+#define SUNXI_MUSB_CONFIGDATA			0x00c0
+
+/* VEND0 bits */
+#define SUNXI_MUSB_VEND0_PIO_MODE		0
+
+/* flags */
+#define SUNXI_MUSB_FL_ENABLED			0
+#define SUNXI_MUSB_FL_HOSTMODE			1
+#define SUNXI_MUSB_FL_HOSTMODE_PEND		2
+#define SUNXI_MUSB_FL_VBUS_ON			3
+#define SUNXI_MUSB_FL_PHY_ON			4
+
+/* Our read/write methods need access and do not get passed in a musb ref :| */
+struct musb *sunxi_musb;
+
+struct sunxi_glue {
+	struct device		*dev;
+	struct platform_device	*musb;
+	struct clk		*clk;
+	struct phy		*phy;
+	struct platform_device	*usb_phy;
+	struct usb_phy		*xceiv;
+	unsigned long		flags;
+	struct work_struct	work;
+	struct extcon_specific_cable_nb host_cable_nb;
+	struct notifier_block	host_nb;
+};
+
+/* phy_power_on / off may sleep, so we use a workqueue  */
+static void sunxi_musb_work(struct work_struct *work)
+{
+	struct sunxi_glue *glue = container_of(work, struct sunxi_glue, work);
+	bool vbus_on, phy_on;
+
+	if (!test_bit(SUNXI_MUSB_FL_ENABLED, &glue->flags))
+		return;
+
+	if (test_and_clear_bit(SUNXI_MUSB_FL_HOSTMODE_PEND, &glue->flags)) {
+		struct musb *musb = platform_get_drvdata(glue->musb);
+		unsigned long flags;
+		u8 devctl;
+
+		spin_lock_irqsave(&musb->lock, flags);
+
+		devctl = readb(musb->mregs + SUNXI_MUSB_DEVCTL);
+		if (test_bit(SUNXI_MUSB_FL_HOSTMODE, &glue->flags)) {
+			set_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+			musb->xceiv->otg->default_a = 1;
+			musb->xceiv->otg->state = OTG_STATE_A_IDLE;
+			MUSB_HST_MODE(musb);
+			devctl |= MUSB_DEVCTL_SESSION;
+		} else {
+			clear_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+			musb->xceiv->otg->default_a = 0;
+			musb->xceiv->otg->state = OTG_STATE_B_IDLE;
+			MUSB_DEV_MODE(musb);
+			devctl &= ~MUSB_DEVCTL_SESSION;
+		}
+		writeb(devctl, musb->mregs + SUNXI_MUSB_DEVCTL);
+
+		spin_unlock_irqrestore(&musb->lock, flags);
+	}
+
+	vbus_on = test_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+	phy_on = test_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags);
+
+	if (phy_on != vbus_on) {
+		if (vbus_on) {
+			phy_power_on(glue->phy);
+			set_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags);
+		} else {
+			phy_power_off(glue->phy);
+			clear_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags);
+		}
+	}
+}
+
+static void sunxi_musb_set_vbus(struct musb *musb, int is_on)
+{
+	struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+	if (is_on)
+		set_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+	else
+		clear_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+
+	schedule_work(&glue->work);
+}
+
+void sunxi_musb_pre_root_reset_end(struct musb *musb)
+{
+	struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+	sun4i_usb_phy_set_squelch_detect(glue->phy, false);
+}
+
+void sunxi_musb_post_root_reset_end(struct musb *musb)
+{
+	struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+	sun4i_usb_phy_set_squelch_detect(glue->phy, true);
+}
+
+static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci)
+{
+	struct musb *musb = __hci;
+	unsigned long flags;
+
+	spin_lock_irqsave(&musb->lock, flags);
+
+	musb->int_usb = readb(musb->mregs + SUNXI_MUSB_INTRUSB);
+	if (musb->int_usb)
+		writeb(musb->int_usb, musb->mregs + SUNXI_MUSB_INTRUSB);
+
+	/*
+	 * sunxi musb often signals babble on low / full speed device
+	 * disconnect, without ever raising MUSB_INTR_DISCONNECT, since
+	 * normally babble never happens treat it as disconnect.
+	 */
+	if ((musb->int_usb & MUSB_INTR_BABBLE) && is_host_active(musb)) {
+		musb->int_usb &= ~MUSB_INTR_BABBLE;
+		musb->int_usb |= MUSB_INTR_DISCONNECT;
+	}
+
+	musb->int_tx = readw(musb->mregs + SUNXI_MUSB_INTRTX);
+	if (musb->int_tx)
+		writew(musb->int_tx, musb->mregs + SUNXI_MUSB_INTRTX);
+
+	musb->int_rx = readw(musb->mregs + SUNXI_MUSB_INTRRX);
+	if (musb->int_rx)
+		writew(musb->int_rx, musb->mregs + SUNXI_MUSB_INTRRX);
+
+	musb_interrupt(musb);
+
+	spin_unlock_irqrestore(&musb->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static int sunxi_musb_host_notifier(struct notifier_block *nb,
+				    unsigned long event, void *ptr)
+{
+	struct sunxi_glue *glue = container_of(nb, struct sunxi_glue, host_nb);
+
+	if (event)
+		set_bit(SUNXI_MUSB_FL_HOSTMODE, &glue->flags);
+	else
+		clear_bit(SUNXI_MUSB_FL_HOSTMODE, &glue->flags);
+
+	set_bit(SUNXI_MUSB_FL_HOSTMODE_PEND, &glue->flags);
+	schedule_work(&glue->work);
+
+	return NOTIFY_DONE;
+}
+
+static int sunxi_musb_init(struct musb *musb)
+{
+	struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+	int ret;
+
+	sunxi_musb = musb;
+	musb->phy = glue->phy;
+	musb->xceiv = glue->xceiv;
+
+	ret = sunxi_sram_claim(musb->controller->parent);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(glue->clk);
+	if (ret)
+		goto error_sram_release;
+
+	writeb(SUNXI_MUSB_VEND0_PIO_MODE, musb->mregs + SUNXI_MUSB_VEND0);
+
+	/* Register interest before calling phy_init() */
+	if (musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) {
+		ret = extcon_register_interest(&glue->host_cable_nb,
+					"sun4i-usb-phy",
+					extcon_cable_name[EXTCON_USB_HOST],
+					&glue->host_nb);
+		if (ret)
+			goto error_clk_disable;
+	}
+
+	ret = phy_init(glue->phy);
+	if (ret)
+		goto error_unregister_interest;
+
+	if (musb->port_mode == MUSB_PORT_MODE_HOST) {
+		ret = phy_power_on(glue->phy);
+		if (ret)
+			goto error_phy_exit;
+		set_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags);
+		/* Stop musb work from turning vbus off again */
+		set_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+	}
+
+	musb->isr = sunxi_musb_interrupt;
+
+	/* Stop the musb-core from doing runtime pm (not supported on sunxi) */
+	pm_runtime_get(musb->controller);
+
+	return 0;
+
+error_phy_exit:
+	phy_exit(glue->phy);
+error_unregister_interest:
+	if (musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
+		extcon_unregister_interest(&glue->host_cable_nb);
+error_clk_disable:
+	clk_disable_unprepare(glue->clk);
+error_sram_release:
+	sunxi_sram_release(musb->controller->parent);
+	return ret;
+}
+
+static int sunxi_musb_exit(struct musb *musb)
+{
+	struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+	pm_runtime_put(musb->controller);
+
+	cancel_work_sync(&glue->work);
+	if (test_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags))
+		phy_power_off(glue->phy);
+
+	phy_exit(glue->phy);
+
+	if (musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
+		extcon_unregister_interest(&glue->host_cable_nb);
+
+	clk_disable_unprepare(glue->clk);
+	sunxi_sram_release(musb->controller->parent);
+
+	return 0;
+}
+
+static void sunxi_musb_enable(struct musb *musb)
+{
+	struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+	/* musb_core does not call us in a balanced manner */
+	if (test_and_set_bit(SUNXI_MUSB_FL_ENABLED, &glue->flags))
+		return;
+
+	schedule_work(&glue->work);
+}
+
+static void sunxi_musb_disable(struct musb *musb)
+{
+	struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+	clear_bit(SUNXI_MUSB_FL_ENABLED, &glue->flags);
+}
+
+/*
+ * sunxi musb register layout
+ * 0x00 - 0x17	fifo regs, 1 long per fifo
+ * 0x40 - 0x57	generic control regs (power - frame)
+ * 0x80 - 0x8f	ep control regs (addressed through hw_ep->regs, indexed)
+ * 0x90 - 0x97	fifo control regs (indexed)
+ * 0x98 - 0x9f	multipoint / busctl regs (indexed)
+ * 0xc0		configdata reg
+ */
+
+static u32 sunxi_musb_fifo_offset(u8 epnum)
+{
+	return (epnum * 4);
+}
+
+static u32 sunxi_musb_ep_offset(u8 epnum, u16 offset)
+{
+	WARN_ONCE(offset != 0,
+		  "sunxi_musb_ep_offset called with non 0 offset\n");
+
+	return 0x80; /* indexed, so ignore epnum */
+}
+
+static u32 sunxi_musb_busctl_offset(u8 epnum, u16 offset)
+{
+	return SUNXI_MUSB_TXFUNCADDR + offset;
+}
+
+static u8 sunxi_musb_readb(const void __iomem *addr, unsigned offset)
+{
+	if (addr == sunxi_musb->mregs) {
+		/* generic control or fifo control reg access */
+		switch (offset) {
+		case MUSB_FADDR:
+			return readb(addr + SUNXI_MUSB_FADDR);
+		case MUSB_POWER:
+			return readb(addr + SUNXI_MUSB_POWER);
+		case MUSB_INTRUSB:
+			return readb(addr + SUNXI_MUSB_INTRUSB);
+		case MUSB_INTRUSBE:
+			return readb(addr + SUNXI_MUSB_INTRUSBE);
+		case MUSB_INDEX:
+			return readb(addr + SUNXI_MUSB_INDEX);
+		case MUSB_TESTMODE:
+			return 0; /* No testmode on sunxi */
+		case MUSB_DEVCTL:
+			return readb(addr + SUNXI_MUSB_DEVCTL);
+		case MUSB_TXFIFOSZ:
+			return readb(addr + SUNXI_MUSB_TXFIFOSZ);
+		case MUSB_RXFIFOSZ:
+			return readb(addr + SUNXI_MUSB_RXFIFOSZ);
+		case MUSB_CONFIGDATA + 0x10: /* See musb_read_configdata() */
+			return readb(addr + SUNXI_MUSB_CONFIGDATA);
+		/* Offset for these is fixed by sunxi_musb_busctl_offset() */
+		case SUNXI_MUSB_TXFUNCADDR:
+		case SUNXI_MUSB_TXHUBADDR:
+		case SUNXI_MUSB_TXHUBPORT:
+		case SUNXI_MUSB_RXFUNCADDR:
+		case SUNXI_MUSB_RXHUBADDR:
+		case SUNXI_MUSB_RXHUBPORT:
+			/* multipoint / busctl reg access */
+			return readb(addr + offset);
+		default:
+			dev_err(sunxi_musb->controller->parent,
+				"Error unknown readb offset %u\n", offset);
+			return 0;
+		}
+	} else if (addr == (sunxi_musb->mregs + 0x80)) {
+		/* ep control reg access */
+		/* sunxi has a 2 byte hole before the txtype register */
+		if (offset >= MUSB_TXTYPE)
+			offset += 2;
+		return readb(addr + offset);
+	}
+
+	dev_err(sunxi_musb->controller->parent,
+		"Error unknown readb at 0x%x bytes offset\n",
+		(int)(addr - sunxi_musb->mregs));
+	return 0;
+}
+
+static void sunxi_musb_writeb(void __iomem *addr, unsigned offset, u8 data)
+{
+	if (addr == sunxi_musb->mregs) {
+		/* generic control or fifo control reg access */
+		switch (offset) {
+		case MUSB_FADDR:
+			return writeb(data, addr + SUNXI_MUSB_FADDR);
+		case MUSB_POWER:
+			return writeb(data, addr + SUNXI_MUSB_POWER);
+		case MUSB_INTRUSB:
+			return writeb(data, addr + SUNXI_MUSB_INTRUSB);
+		case MUSB_INTRUSBE:
+			return writeb(data, addr + SUNXI_MUSB_INTRUSBE);
+		case MUSB_INDEX:
+			return writeb(data, addr + SUNXI_MUSB_INDEX);
+		case MUSB_TESTMODE:
+			if (data)
+				dev_warn(sunxi_musb->controller->parent,
+					"sunxi-musb does not have testmode\n");
+			return;
+		case MUSB_DEVCTL:
+			return writeb(data, addr + SUNXI_MUSB_DEVCTL);
+		case MUSB_TXFIFOSZ:
+			return writeb(data, addr + SUNXI_MUSB_TXFIFOSZ);
+		case MUSB_RXFIFOSZ:
+			return writeb(data, addr + SUNXI_MUSB_RXFIFOSZ);
+		/* Offset for these is fixed by sunxi_musb_busctl_offset() */
+		case SUNXI_MUSB_TXFUNCADDR:
+		case SUNXI_MUSB_TXHUBADDR:
+		case SUNXI_MUSB_TXHUBPORT:
+		case SUNXI_MUSB_RXFUNCADDR:
+		case SUNXI_MUSB_RXHUBADDR:
+		case SUNXI_MUSB_RXHUBPORT:
+			/* multipoint / busctl reg access */
+			return writeb(data, addr + offset);
+		default:
+			dev_err(sunxi_musb->controller->parent,
+				"Error unknown writeb offset %u\n", offset);
+			return;
+		}
+	} else if (addr == (sunxi_musb->mregs + 0x80)) {
+		/* ep control reg access */
+		if (offset >= MUSB_TXTYPE)
+			offset += 2;
+		return writeb(data, addr + offset);
+	}
+
+	dev_err(sunxi_musb->controller->parent,
+		"Error unknown writeb at 0x%x bytes offset\n",
+		(int)(addr - sunxi_musb->mregs));
+}
+
+static u16 sunxi_musb_readw(const void __iomem *addr, unsigned offset)
+{
+	if (addr == sunxi_musb->mregs) {
+		/* generic control or fifo control reg access */
+		switch (offset) {
+		case MUSB_INTRTX:
+			return readw(addr + SUNXI_MUSB_INTRTX);
+		case MUSB_INTRRX:
+			return readw(addr + SUNXI_MUSB_INTRRX);
+		case MUSB_INTRTXE:
+			return readw(addr + SUNXI_MUSB_INTRTXE);
+		case MUSB_INTRRXE:
+			return readw(addr + SUNXI_MUSB_INTRRXE);
+		case MUSB_FRAME:
+			return readw(addr + SUNXI_MUSB_FRAME);
+		case MUSB_TXFIFOADD:
+			return readw(addr + SUNXI_MUSB_TXFIFOADD);
+		case MUSB_RXFIFOADD:
+			return readw(addr + SUNXI_MUSB_RXFIFOADD);
+		case MUSB_HWVERS:
+			return 0; /* sunxi musb version is not known */
+		default:
+			dev_err(sunxi_musb->controller->parent,
+				"Error unknown readw offset %u\n", offset);
+			return 0;
+		}
+	} else if (addr == (sunxi_musb->mregs + 0x80)) {
+		/* ep control reg access */
+		return readw(addr + offset);
+	}
+
+	dev_err(sunxi_musb->controller->parent,
+		"Error unknown readw at 0x%x bytes offset\n",
+		(int)(addr - sunxi_musb->mregs));
+	return 0;
+}
+
+static void sunxi_musb_writew(void __iomem *addr, unsigned offset, u16 data)
+{
+	if (addr == sunxi_musb->mregs) {
+		/* generic control or fifo control reg access */
+		switch (offset) {
+		case MUSB_INTRTX:
+			return writew(data, addr + SUNXI_MUSB_INTRTX);
+		case MUSB_INTRRX:
+			return writew(data, addr + SUNXI_MUSB_INTRRX);
+		case MUSB_INTRTXE:
+			return writew(data, addr + SUNXI_MUSB_INTRTXE);
+		case MUSB_INTRRXE:
+			return writew(data, addr + SUNXI_MUSB_INTRRXE);
+		case MUSB_FRAME:
+			return writew(data, addr + SUNXI_MUSB_FRAME);
+		case MUSB_TXFIFOADD:
+			return writew(data, addr + SUNXI_MUSB_TXFIFOADD);
+		case MUSB_RXFIFOADD:
+			return writew(data, addr + SUNXI_MUSB_RXFIFOADD);
+		default:
+			dev_err(sunxi_musb->controller->parent,
+				"Error unknown writew offset %u\n", offset);
+			return;
+		}
+	} else if (addr == (sunxi_musb->mregs + 0x80)) {
+		/* ep control reg access */
+		return writew(data, addr + offset);
+	}
+
+	dev_err(sunxi_musb->controller->parent,
+		"Error unknown writew at 0x%x bytes offset\n",
+		(int)(addr - sunxi_musb->mregs));
+}
+
+static const struct musb_platform_ops sunxi_musb_ops = {
+	.quirks		= MUSB_INDEXED_EP | MUSB_SUN4I,
+	.init		= sunxi_musb_init,
+	.exit		= sunxi_musb_exit,
+	.enable		= sunxi_musb_enable,
+	.disable	= sunxi_musb_disable,
+	.fifo_offset	= sunxi_musb_fifo_offset,
+	.ep_offset	= sunxi_musb_ep_offset,
+	.busctl_offset	= sunxi_musb_busctl_offset,
+	.readb		= sunxi_musb_readb,
+	.writeb		= sunxi_musb_writeb,
+	.readw		= sunxi_musb_readw,
+	.writew		= sunxi_musb_writew,
+	.set_vbus	= sunxi_musb_set_vbus,
+	.pre_root_reset_end = sunxi_musb_pre_root_reset_end,
+	.post_root_reset_end = sunxi_musb_post_root_reset_end,
+};
+
+/* Allwinner OTG supports up to 5 endpoints */
+#define SUNXI_MUSB_MAX_EP_NUM	6
+#define SUNXI_MUSB_RAM_BITS	11
+
+static struct musb_fifo_cfg sunxi_musb_mode_cfg[] = {
+	MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512),
+	MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512),
+	MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512),
+	MUSB_EP_FIFO_SINGLE(2, FIFO_RX, 512),
+	MUSB_EP_FIFO_SINGLE(3, FIFO_TX, 512),
+	MUSB_EP_FIFO_SINGLE(3, FIFO_RX, 512),
+	MUSB_EP_FIFO_SINGLE(4, FIFO_TX, 512),
+	MUSB_EP_FIFO_SINGLE(4, FIFO_RX, 512),
+	MUSB_EP_FIFO_SINGLE(5, FIFO_TX, 512),
+	MUSB_EP_FIFO_SINGLE(5, FIFO_RX, 512),
+};
+
+static struct musb_hdrc_config sunxi_musb_hdrc_config = {
+	.fifo_cfg       = sunxi_musb_mode_cfg,
+	.fifo_cfg_size  = ARRAY_SIZE(sunxi_musb_mode_cfg),
+	.multipoint	= true,
+	.dyn_fifo	= true,
+	.soft_con       = true,
+	.num_eps	= SUNXI_MUSB_MAX_EP_NUM,
+	.ram_bits	= SUNXI_MUSB_RAM_BITS,
+	.dma		= 0,
+};
+
+static int sunxi_musb_probe(struct platform_device *pdev)
+{
+	struct musb_hdrc_platform_data	pdata;
+	struct platform_device_info	pinfo;
+	struct sunxi_glue		*glue;
+	struct device_node		*np = pdev->dev.of_node;
+	int ret;
+
+	if (!np) {
+		dev_err(&pdev->dev, "Error no device tree node found\n");
+		return -EINVAL;
+	}
+
+	memset(&pdata, 0, sizeof(pdata));
+	switch (of_usb_get_dr_mode(np)) {
+#if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_HOST
+	case USB_DR_MODE_HOST:
+		pdata.mode = MUSB_PORT_MODE_HOST;
+		break;
+#endif
+#ifdef CONFIG_USB_MUSB_DUAL_ROLE
+	case USB_DR_MODE_OTG:
+		pdata.mode = MUSB_PORT_MODE_DUAL_ROLE;
+		break;
+#endif
+	default:
+		dev_err(&pdev->dev, "Invalid or missing 'dr_mode' property\n");
+		return -EINVAL;
+	}
+	pdata.platform_ops	= &sunxi_musb_ops;
+	pdata.config		= &sunxi_musb_hdrc_config;
+
+	glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
+	if (!glue)
+		return -ENOMEM;
+
+	glue->dev = &pdev->dev;
+	INIT_WORK(&glue->work, sunxi_musb_work);
+	glue->host_nb.notifier_call = sunxi_musb_host_notifier;
+
+	glue->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(glue->clk)) {
+		dev_err(&pdev->dev, "Error getting clock: %ld\n",
+			PTR_ERR(glue->clk));
+		return PTR_ERR(glue->clk);
+	}
+
+	glue->phy = devm_phy_get(&pdev->dev, "usb");
+	if (IS_ERR(glue->phy)) {
+		if (PTR_ERR(glue->phy) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_err(&pdev->dev, "Error getting phy %ld\n",
+			PTR_ERR(glue->phy));
+		return PTR_ERR(glue->phy);
+	}
+
+	glue->usb_phy = usb_phy_generic_register();
+	if (IS_ERR(glue->usb_phy)) {
+		dev_err(&pdev->dev, "Error registering usb-phy %ld\n",
+			PTR_ERR(glue->usb_phy));
+		return PTR_ERR(glue->usb_phy);
+	}
+
+	glue->xceiv = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
+	if (IS_ERR(glue->xceiv)) {
+		ret = PTR_ERR(glue->xceiv);
+		dev_err(&pdev->dev, "Error getting usb-phy %d\n", ret);
+		goto err_unregister_usb_phy;
+	}
+
+	platform_set_drvdata(pdev, glue);
+
+	memset(&pinfo, 0, sizeof(pinfo));
+	pinfo.name	 = "musb-hdrc";
+	pinfo.id	= PLATFORM_DEVID_AUTO;
+	pinfo.parent	= &pdev->dev;
+	pinfo.res	= pdev->resource;
+	pinfo.num_res	= pdev->num_resources;
+	pinfo.data	= &pdata;
+	pinfo.size_data = sizeof(pdata);
+
+	glue->musb = platform_device_register_full(&pinfo);
+	if (IS_ERR(glue->musb)) {
+		ret = PTR_ERR(glue->musb);
+		dev_err(&pdev->dev, "Error registering musb dev: %d\n", ret);
+		goto err_unregister_usb_phy;
+	}
+
+	return 0;
+
+err_unregister_usb_phy:
+	usb_phy_generic_unregister(glue->usb_phy);
+	return ret;
+}
+
+static int sunxi_musb_remove(struct platform_device *pdev)
+{
+	struct sunxi_glue *glue = platform_get_drvdata(pdev);
+	struct platform_device *usb_phy = glue->usb_phy;
+
+	platform_device_unregister(glue->musb); /* Frees glue ! */
+	usb_phy_generic_unregister(usb_phy);
+
+	return 0;
+}
+
+static const struct of_device_id sunxi_musb_match[] = {
+	{ .compatible = "allwinner,sun4i-a10-musb", },
+	{}
+};
+
+static struct platform_driver sunxi_musb_driver = {
+	.probe = sunxi_musb_probe,
+	.remove = sunxi_musb_remove,
+	.driver = {
+		.name = "musb-sunxi",
+		.of_match_table = sunxi_musb_match,
+	},
+};
+module_platform_driver(sunxi_musb_driver);
+
+MODULE_DESCRIPTION("Allwinner sunxi MUSB Glue Layer");
+MODULE_AUTHOR("Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
-- 
2.4.2

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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	[flat|nested] 57+ messages in thread

* [PATCH v4 2/2] musb: Add support for the Allwinner sunxi musb controller
@ 2015-05-31 16:10     ` Hans de Goede
  0 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-05-31 16:10 UTC (permalink / raw)
  To: linux-arm-kernel

This is based on initial code to get the Allwinner sunxi musb controller
supported by Chen-Yu Tsai and Roman Byshko.

This adds support for the Allwinner sunxi musb controller in both host only
and otg mode. Peripheral only mode is not supported, as no boards use that.

This has been tested on a cubietruck (A20 SoC) and an UTOO P66 tablet
(A13 SoC) with a variety of devices in host mode and with the g_serial gadget
driver in peripheral mode, plugging otg / host cables in/out a lot of times
in all possible imaginable plug orders.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
Changes in v2:
-Move polling of id and vbus-det gpio-s to the phy driver
-Use extcon to get id (USB_HOST mode) status changes from the phy driver
-Stop using syscon, instead use Maxime Ripard's sunxi SRAM controller driver
Changes in v3:
-Check that USB_MUSB_FOO config is compatible with the dr_mode setting from dt
Changes in v4:
-Squash in "musb: sunxi: Add pre/post root reset end platform functions" patch
-Adjust for sunxi_sram controller driver changes
-Stop musb work from turning vbus off again when in host mode
---
 .../bindings/usb/allwinner,sun4i-a10-musb.txt      |  25 +
 drivers/usb/musb/Kconfig                           |  13 +-
 drivers/usb/musb/Makefile                          |   1 +
 drivers/usb/musb/musb_core.h                       |   1 +
 drivers/usb/musb/musb_gadget.c                     |   6 +
 drivers/usb/musb/musb_virthub.c                    |   6 +
 drivers/usb/musb/sunxi.c                           | 690 +++++++++++++++++++++
 7 files changed, 741 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt
 create mode 100644 drivers/usb/musb/sunxi.c

diff --git a/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt
new file mode 100644
index 0000000..21dfc7f
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.txt
@@ -0,0 +1,25 @@
+Allwinner sun4i A10 musb DRC/OTG controller
+-------------------------------------------
+
+Required properties:
+ - compatible      : "allwinner,sun4i-a10-musb"
+ - reg             : mmio address range of the musb controller
+ - clocks          : clock specifier for the musb controller ahb gate clock
+ - interrupts      : interrupt to which the musb controller is connected
+ - interrupt-names : must be "mc"
+ - phys            : phy specifier for the otg phy
+ - phy-names       : must be "usb"
+ - dr_mode         : Dual-Role mode must be "host" or "otg"
+
+Example:
+
+	usb_otg: usb at 01c13000 {
+		compatible = "allwinner,sun4i-a10-musb";
+		reg = <0x01c13000 0x0400>;
+		clocks = <&ahb_gates 0>;
+		interrupts = <38>;
+		interrupt-names = "mc";
+		phys = <&usbphy 0>;
+		phy-names = "usb";
+		status = "disabled";
+	};
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 39db8b6..2c11cb4 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -5,7 +5,7 @@
 
 # (M)HDRC = (Multipoint) Highspeed Dual-Role Controller
 config USB_MUSB_HDRC
-	tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
+	tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, AW, ...)'
 	depends on (USB || USB_GADGET)
 	help
 	  Say Y here if your system has a dual role high speed USB
@@ -20,6 +20,8 @@ config USB_MUSB_HDRC
 	  Analog Devices parts using this IP include Blackfin BF54x,
 	  BF525 and BF527.
 
+	  Allwinner SoCs using this IP include A10, A13, A20, ...
+
 	  If you do not know what this is, please say N.
 
 	  To compile this driver as a module, choose M here; the
@@ -60,6 +62,15 @@ endchoice
 
 comment "Platform Glue Layer"
 
+config USB_MUSB_SUNXI
+	tristate "Allwinner (sunxi)"
+	depends on ARCH_SUNXI
+	depends on NOP_USB_XCEIV
+	depends on PHY_SUN4I_USB
+	select EXTCON
+	select GENERIC_PHY
+	select SUNXI_SRAM
+
 config USB_MUSB_DAVINCI
 	tristate "DaVinci"
 	depends on ARCH_DAVINCI_DMx
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile
index ba49501..f95befe 100644
--- a/drivers/usb/musb/Makefile
+++ b/drivers/usb/musb/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_USB_MUSB_DA8XX)			+= da8xx.o
 obj-$(CONFIG_USB_MUSB_BLACKFIN)			+= blackfin.o
 obj-$(CONFIG_USB_MUSB_UX500)			+= ux500.o
 obj-$(CONFIG_USB_MUSB_JZ4740)			+= jz4740.o
+obj-$(CONFIG_USB_MUSB_SUNXI)			+= sunxi.o
 
 
 obj-$(CONFIG_USB_MUSB_AM335X_CHILD)		+= musb_am335x.o
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index b1cb2c3..30a3123 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -169,6 +169,7 @@ struct musb_io;
  */
 struct musb_platform_ops {
 
+#define MUSB_SUN4I		BIT(7)
 #define MUSB_DMA_UX500		BIT(6)
 #define MUSB_DMA_CPPI41		BIT(5)
 #define MUSB_DMA_CPPI		BIT(4)
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 4c481cd..ec2e908 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -2003,6 +2003,12 @@ void musb_g_disconnect(struct musb *musb)
 		spin_lock(&musb->lock);
 	}
 
+	/* On sunxi ep0 FADDR must be 0 when (re)entering peripheral mode */
+	if (musb->io.quirks & MUSB_SUN4I) {
+		musb_ep_select(musb->mregs, 0);
+		musb_writeb(musb->mregs, MUSB_FADDR, 0);
+	}
+
 	switch (musb->xceiv->otg->state) {
 	default:
 		dev_dbg(musb->controller, "Unhandled disconnect %s, setting a_idle\n",
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 30842bc..fe98623 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -226,6 +226,12 @@ void musb_root_disconnect(struct musb *musb)
 	usb_hcd_poll_rh_status(musb->hcd);
 	musb->is_active = 0;
 
+	/* On sunxi ep0 FADDR must be 0 when (re)entering peripheral mode */
+	if (musb->io.quirks & MUSB_SUN4I) {
+		musb_ep_select(musb->mregs, 0);
+		musb_writeb(musb->mregs, MUSB_FADDR, 0);
+	}
+
 	switch (musb->xceiv->otg->state) {
 	case OTG_STATE_A_SUSPEND:
 		if (otg->host->b_hnp_enable) {
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
new file mode 100644
index 0000000..5db882f
--- /dev/null
+++ b/drivers/usb/musb/sunxi.c
@@ -0,0 +1,690 @@
+/*
+ * Allwinner sun4i MUSB Glue Layer
+ *
+ * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on code from
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ *
+ * 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.
+ *
+ * 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/err.h>
+#include <linux/extcon.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy-sun4i-usb.h>
+#include <linux/platform_device.h>
+#include <linux/soc/sunxi/sunxi_sram.h>
+#include <linux/usb/musb.h>
+#include <linux/usb/of.h>
+#include <linux/usb/usb_phy_generic.h>
+#include <linux/workqueue.h>
+#include "musb_core.h"
+
+/*
+ * Register offsets, note sunxi musb has a different layout then most
+ * musb implementations, we translate the layout in musb_readb & friends.
+ */
+#define SUNXI_MUSB_POWER			0x0040
+#define SUNXI_MUSB_DEVCTL			0x0041
+#define SUNXI_MUSB_INDEX			0x0042
+#define SUNXI_MUSB_VEND0			0x0043
+#define SUNXI_MUSB_INTRTX			0x0044
+#define SUNXI_MUSB_INTRRX			0x0046
+#define SUNXI_MUSB_INTRTXE			0x0048
+#define SUNXI_MUSB_INTRRXE			0x004a
+#define SUNXI_MUSB_INTRUSB			0x004c
+#define SUNXI_MUSB_INTRUSBE			0x0050
+#define SUNXI_MUSB_FRAME			0x0054
+#define SUNXI_MUSB_TXFIFOSZ			0x0090
+#define SUNXI_MUSB_TXFIFOADD			0x0092
+#define SUNXI_MUSB_RXFIFOSZ			0x0094
+#define SUNXI_MUSB_RXFIFOADD			0x0096
+#define SUNXI_MUSB_FADDR			0x0098
+#define SUNXI_MUSB_TXFUNCADDR			0x0098
+#define SUNXI_MUSB_TXHUBADDR			0x009a
+#define SUNXI_MUSB_TXHUBPORT			0x009b
+#define SUNXI_MUSB_RXFUNCADDR			0x009c
+#define SUNXI_MUSB_RXHUBADDR			0x009e
+#define SUNXI_MUSB_RXHUBPORT			0x009f
+#define SUNXI_MUSB_CONFIGDATA			0x00c0
+
+/* VEND0 bits */
+#define SUNXI_MUSB_VEND0_PIO_MODE		0
+
+/* flags */
+#define SUNXI_MUSB_FL_ENABLED			0
+#define SUNXI_MUSB_FL_HOSTMODE			1
+#define SUNXI_MUSB_FL_HOSTMODE_PEND		2
+#define SUNXI_MUSB_FL_VBUS_ON			3
+#define SUNXI_MUSB_FL_PHY_ON			4
+
+/* Our read/write methods need access and do not get passed in a musb ref :| */
+struct musb *sunxi_musb;
+
+struct sunxi_glue {
+	struct device		*dev;
+	struct platform_device	*musb;
+	struct clk		*clk;
+	struct phy		*phy;
+	struct platform_device	*usb_phy;
+	struct usb_phy		*xceiv;
+	unsigned long		flags;
+	struct work_struct	work;
+	struct extcon_specific_cable_nb host_cable_nb;
+	struct notifier_block	host_nb;
+};
+
+/* phy_power_on / off may sleep, so we use a workqueue  */
+static void sunxi_musb_work(struct work_struct *work)
+{
+	struct sunxi_glue *glue = container_of(work, struct sunxi_glue, work);
+	bool vbus_on, phy_on;
+
+	if (!test_bit(SUNXI_MUSB_FL_ENABLED, &glue->flags))
+		return;
+
+	if (test_and_clear_bit(SUNXI_MUSB_FL_HOSTMODE_PEND, &glue->flags)) {
+		struct musb *musb = platform_get_drvdata(glue->musb);
+		unsigned long flags;
+		u8 devctl;
+
+		spin_lock_irqsave(&musb->lock, flags);
+
+		devctl = readb(musb->mregs + SUNXI_MUSB_DEVCTL);
+		if (test_bit(SUNXI_MUSB_FL_HOSTMODE, &glue->flags)) {
+			set_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+			musb->xceiv->otg->default_a = 1;
+			musb->xceiv->otg->state = OTG_STATE_A_IDLE;
+			MUSB_HST_MODE(musb);
+			devctl |= MUSB_DEVCTL_SESSION;
+		} else {
+			clear_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+			musb->xceiv->otg->default_a = 0;
+			musb->xceiv->otg->state = OTG_STATE_B_IDLE;
+			MUSB_DEV_MODE(musb);
+			devctl &= ~MUSB_DEVCTL_SESSION;
+		}
+		writeb(devctl, musb->mregs + SUNXI_MUSB_DEVCTL);
+
+		spin_unlock_irqrestore(&musb->lock, flags);
+	}
+
+	vbus_on = test_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+	phy_on = test_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags);
+
+	if (phy_on != vbus_on) {
+		if (vbus_on) {
+			phy_power_on(glue->phy);
+			set_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags);
+		} else {
+			phy_power_off(glue->phy);
+			clear_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags);
+		}
+	}
+}
+
+static void sunxi_musb_set_vbus(struct musb *musb, int is_on)
+{
+	struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+	if (is_on)
+		set_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+	else
+		clear_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+
+	schedule_work(&glue->work);
+}
+
+void sunxi_musb_pre_root_reset_end(struct musb *musb)
+{
+	struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+	sun4i_usb_phy_set_squelch_detect(glue->phy, false);
+}
+
+void sunxi_musb_post_root_reset_end(struct musb *musb)
+{
+	struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+	sun4i_usb_phy_set_squelch_detect(glue->phy, true);
+}
+
+static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci)
+{
+	struct musb *musb = __hci;
+	unsigned long flags;
+
+	spin_lock_irqsave(&musb->lock, flags);
+
+	musb->int_usb = readb(musb->mregs + SUNXI_MUSB_INTRUSB);
+	if (musb->int_usb)
+		writeb(musb->int_usb, musb->mregs + SUNXI_MUSB_INTRUSB);
+
+	/*
+	 * sunxi musb often signals babble on low / full speed device
+	 * disconnect, without ever raising MUSB_INTR_DISCONNECT, since
+	 * normally babble never happens treat it as disconnect.
+	 */
+	if ((musb->int_usb & MUSB_INTR_BABBLE) && is_host_active(musb)) {
+		musb->int_usb &= ~MUSB_INTR_BABBLE;
+		musb->int_usb |= MUSB_INTR_DISCONNECT;
+	}
+
+	musb->int_tx = readw(musb->mregs + SUNXI_MUSB_INTRTX);
+	if (musb->int_tx)
+		writew(musb->int_tx, musb->mregs + SUNXI_MUSB_INTRTX);
+
+	musb->int_rx = readw(musb->mregs + SUNXI_MUSB_INTRRX);
+	if (musb->int_rx)
+		writew(musb->int_rx, musb->mregs + SUNXI_MUSB_INTRRX);
+
+	musb_interrupt(musb);
+
+	spin_unlock_irqrestore(&musb->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static int sunxi_musb_host_notifier(struct notifier_block *nb,
+				    unsigned long event, void *ptr)
+{
+	struct sunxi_glue *glue = container_of(nb, struct sunxi_glue, host_nb);
+
+	if (event)
+		set_bit(SUNXI_MUSB_FL_HOSTMODE, &glue->flags);
+	else
+		clear_bit(SUNXI_MUSB_FL_HOSTMODE, &glue->flags);
+
+	set_bit(SUNXI_MUSB_FL_HOSTMODE_PEND, &glue->flags);
+	schedule_work(&glue->work);
+
+	return NOTIFY_DONE;
+}
+
+static int sunxi_musb_init(struct musb *musb)
+{
+	struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+	int ret;
+
+	sunxi_musb = musb;
+	musb->phy = glue->phy;
+	musb->xceiv = glue->xceiv;
+
+	ret = sunxi_sram_claim(musb->controller->parent);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(glue->clk);
+	if (ret)
+		goto error_sram_release;
+
+	writeb(SUNXI_MUSB_VEND0_PIO_MODE, musb->mregs + SUNXI_MUSB_VEND0);
+
+	/* Register interest before calling phy_init() */
+	if (musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) {
+		ret = extcon_register_interest(&glue->host_cable_nb,
+					"sun4i-usb-phy",
+					extcon_cable_name[EXTCON_USB_HOST],
+					&glue->host_nb);
+		if (ret)
+			goto error_clk_disable;
+	}
+
+	ret = phy_init(glue->phy);
+	if (ret)
+		goto error_unregister_interest;
+
+	if (musb->port_mode == MUSB_PORT_MODE_HOST) {
+		ret = phy_power_on(glue->phy);
+		if (ret)
+			goto error_phy_exit;
+		set_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags);
+		/* Stop musb work from turning vbus off again */
+		set_bit(SUNXI_MUSB_FL_VBUS_ON, &glue->flags);
+	}
+
+	musb->isr = sunxi_musb_interrupt;
+
+	/* Stop the musb-core from doing runtime pm (not supported on sunxi) */
+	pm_runtime_get(musb->controller);
+
+	return 0;
+
+error_phy_exit:
+	phy_exit(glue->phy);
+error_unregister_interest:
+	if (musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
+		extcon_unregister_interest(&glue->host_cable_nb);
+error_clk_disable:
+	clk_disable_unprepare(glue->clk);
+error_sram_release:
+	sunxi_sram_release(musb->controller->parent);
+	return ret;
+}
+
+static int sunxi_musb_exit(struct musb *musb)
+{
+	struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+	pm_runtime_put(musb->controller);
+
+	cancel_work_sync(&glue->work);
+	if (test_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags))
+		phy_power_off(glue->phy);
+
+	phy_exit(glue->phy);
+
+	if (musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
+		extcon_unregister_interest(&glue->host_cable_nb);
+
+	clk_disable_unprepare(glue->clk);
+	sunxi_sram_release(musb->controller->parent);
+
+	return 0;
+}
+
+static void sunxi_musb_enable(struct musb *musb)
+{
+	struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+	/* musb_core does not call us in a balanced manner */
+	if (test_and_set_bit(SUNXI_MUSB_FL_ENABLED, &glue->flags))
+		return;
+
+	schedule_work(&glue->work);
+}
+
+static void sunxi_musb_disable(struct musb *musb)
+{
+	struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+	clear_bit(SUNXI_MUSB_FL_ENABLED, &glue->flags);
+}
+
+/*
+ * sunxi musb register layout
+ * 0x00 - 0x17	fifo regs, 1 long per fifo
+ * 0x40 - 0x57	generic control regs (power - frame)
+ * 0x80 - 0x8f	ep control regs (addressed through hw_ep->regs, indexed)
+ * 0x90 - 0x97	fifo control regs (indexed)
+ * 0x98 - 0x9f	multipoint / busctl regs (indexed)
+ * 0xc0		configdata reg
+ */
+
+static u32 sunxi_musb_fifo_offset(u8 epnum)
+{
+	return (epnum * 4);
+}
+
+static u32 sunxi_musb_ep_offset(u8 epnum, u16 offset)
+{
+	WARN_ONCE(offset != 0,
+		  "sunxi_musb_ep_offset called with non 0 offset\n");
+
+	return 0x80; /* indexed, so ignore epnum */
+}
+
+static u32 sunxi_musb_busctl_offset(u8 epnum, u16 offset)
+{
+	return SUNXI_MUSB_TXFUNCADDR + offset;
+}
+
+static u8 sunxi_musb_readb(const void __iomem *addr, unsigned offset)
+{
+	if (addr == sunxi_musb->mregs) {
+		/* generic control or fifo control reg access */
+		switch (offset) {
+		case MUSB_FADDR:
+			return readb(addr + SUNXI_MUSB_FADDR);
+		case MUSB_POWER:
+			return readb(addr + SUNXI_MUSB_POWER);
+		case MUSB_INTRUSB:
+			return readb(addr + SUNXI_MUSB_INTRUSB);
+		case MUSB_INTRUSBE:
+			return readb(addr + SUNXI_MUSB_INTRUSBE);
+		case MUSB_INDEX:
+			return readb(addr + SUNXI_MUSB_INDEX);
+		case MUSB_TESTMODE:
+			return 0; /* No testmode on sunxi */
+		case MUSB_DEVCTL:
+			return readb(addr + SUNXI_MUSB_DEVCTL);
+		case MUSB_TXFIFOSZ:
+			return readb(addr + SUNXI_MUSB_TXFIFOSZ);
+		case MUSB_RXFIFOSZ:
+			return readb(addr + SUNXI_MUSB_RXFIFOSZ);
+		case MUSB_CONFIGDATA + 0x10: /* See musb_read_configdata() */
+			return readb(addr + SUNXI_MUSB_CONFIGDATA);
+		/* Offset for these is fixed by sunxi_musb_busctl_offset() */
+		case SUNXI_MUSB_TXFUNCADDR:
+		case SUNXI_MUSB_TXHUBADDR:
+		case SUNXI_MUSB_TXHUBPORT:
+		case SUNXI_MUSB_RXFUNCADDR:
+		case SUNXI_MUSB_RXHUBADDR:
+		case SUNXI_MUSB_RXHUBPORT:
+			/* multipoint / busctl reg access */
+			return readb(addr + offset);
+		default:
+			dev_err(sunxi_musb->controller->parent,
+				"Error unknown readb offset %u\n", offset);
+			return 0;
+		}
+	} else if (addr == (sunxi_musb->mregs + 0x80)) {
+		/* ep control reg access */
+		/* sunxi has a 2 byte hole before the txtype register */
+		if (offset >= MUSB_TXTYPE)
+			offset += 2;
+		return readb(addr + offset);
+	}
+
+	dev_err(sunxi_musb->controller->parent,
+		"Error unknown readb at 0x%x bytes offset\n",
+		(int)(addr - sunxi_musb->mregs));
+	return 0;
+}
+
+static void sunxi_musb_writeb(void __iomem *addr, unsigned offset, u8 data)
+{
+	if (addr == sunxi_musb->mregs) {
+		/* generic control or fifo control reg access */
+		switch (offset) {
+		case MUSB_FADDR:
+			return writeb(data, addr + SUNXI_MUSB_FADDR);
+		case MUSB_POWER:
+			return writeb(data, addr + SUNXI_MUSB_POWER);
+		case MUSB_INTRUSB:
+			return writeb(data, addr + SUNXI_MUSB_INTRUSB);
+		case MUSB_INTRUSBE:
+			return writeb(data, addr + SUNXI_MUSB_INTRUSBE);
+		case MUSB_INDEX:
+			return writeb(data, addr + SUNXI_MUSB_INDEX);
+		case MUSB_TESTMODE:
+			if (data)
+				dev_warn(sunxi_musb->controller->parent,
+					"sunxi-musb does not have testmode\n");
+			return;
+		case MUSB_DEVCTL:
+			return writeb(data, addr + SUNXI_MUSB_DEVCTL);
+		case MUSB_TXFIFOSZ:
+			return writeb(data, addr + SUNXI_MUSB_TXFIFOSZ);
+		case MUSB_RXFIFOSZ:
+			return writeb(data, addr + SUNXI_MUSB_RXFIFOSZ);
+		/* Offset for these is fixed by sunxi_musb_busctl_offset() */
+		case SUNXI_MUSB_TXFUNCADDR:
+		case SUNXI_MUSB_TXHUBADDR:
+		case SUNXI_MUSB_TXHUBPORT:
+		case SUNXI_MUSB_RXFUNCADDR:
+		case SUNXI_MUSB_RXHUBADDR:
+		case SUNXI_MUSB_RXHUBPORT:
+			/* multipoint / busctl reg access */
+			return writeb(data, addr + offset);
+		default:
+			dev_err(sunxi_musb->controller->parent,
+				"Error unknown writeb offset %u\n", offset);
+			return;
+		}
+	} else if (addr == (sunxi_musb->mregs + 0x80)) {
+		/* ep control reg access */
+		if (offset >= MUSB_TXTYPE)
+			offset += 2;
+		return writeb(data, addr + offset);
+	}
+
+	dev_err(sunxi_musb->controller->parent,
+		"Error unknown writeb at 0x%x bytes offset\n",
+		(int)(addr - sunxi_musb->mregs));
+}
+
+static u16 sunxi_musb_readw(const void __iomem *addr, unsigned offset)
+{
+	if (addr == sunxi_musb->mregs) {
+		/* generic control or fifo control reg access */
+		switch (offset) {
+		case MUSB_INTRTX:
+			return readw(addr + SUNXI_MUSB_INTRTX);
+		case MUSB_INTRRX:
+			return readw(addr + SUNXI_MUSB_INTRRX);
+		case MUSB_INTRTXE:
+			return readw(addr + SUNXI_MUSB_INTRTXE);
+		case MUSB_INTRRXE:
+			return readw(addr + SUNXI_MUSB_INTRRXE);
+		case MUSB_FRAME:
+			return readw(addr + SUNXI_MUSB_FRAME);
+		case MUSB_TXFIFOADD:
+			return readw(addr + SUNXI_MUSB_TXFIFOADD);
+		case MUSB_RXFIFOADD:
+			return readw(addr + SUNXI_MUSB_RXFIFOADD);
+		case MUSB_HWVERS:
+			return 0; /* sunxi musb version is not known */
+		default:
+			dev_err(sunxi_musb->controller->parent,
+				"Error unknown readw offset %u\n", offset);
+			return 0;
+		}
+	} else if (addr == (sunxi_musb->mregs + 0x80)) {
+		/* ep control reg access */
+		return readw(addr + offset);
+	}
+
+	dev_err(sunxi_musb->controller->parent,
+		"Error unknown readw at 0x%x bytes offset\n",
+		(int)(addr - sunxi_musb->mregs));
+	return 0;
+}
+
+static void sunxi_musb_writew(void __iomem *addr, unsigned offset, u16 data)
+{
+	if (addr == sunxi_musb->mregs) {
+		/* generic control or fifo control reg access */
+		switch (offset) {
+		case MUSB_INTRTX:
+			return writew(data, addr + SUNXI_MUSB_INTRTX);
+		case MUSB_INTRRX:
+			return writew(data, addr + SUNXI_MUSB_INTRRX);
+		case MUSB_INTRTXE:
+			return writew(data, addr + SUNXI_MUSB_INTRTXE);
+		case MUSB_INTRRXE:
+			return writew(data, addr + SUNXI_MUSB_INTRRXE);
+		case MUSB_FRAME:
+			return writew(data, addr + SUNXI_MUSB_FRAME);
+		case MUSB_TXFIFOADD:
+			return writew(data, addr + SUNXI_MUSB_TXFIFOADD);
+		case MUSB_RXFIFOADD:
+			return writew(data, addr + SUNXI_MUSB_RXFIFOADD);
+		default:
+			dev_err(sunxi_musb->controller->parent,
+				"Error unknown writew offset %u\n", offset);
+			return;
+		}
+	} else if (addr == (sunxi_musb->mregs + 0x80)) {
+		/* ep control reg access */
+		return writew(data, addr + offset);
+	}
+
+	dev_err(sunxi_musb->controller->parent,
+		"Error unknown writew at 0x%x bytes offset\n",
+		(int)(addr - sunxi_musb->mregs));
+}
+
+static const struct musb_platform_ops sunxi_musb_ops = {
+	.quirks		= MUSB_INDEXED_EP | MUSB_SUN4I,
+	.init		= sunxi_musb_init,
+	.exit		= sunxi_musb_exit,
+	.enable		= sunxi_musb_enable,
+	.disable	= sunxi_musb_disable,
+	.fifo_offset	= sunxi_musb_fifo_offset,
+	.ep_offset	= sunxi_musb_ep_offset,
+	.busctl_offset	= sunxi_musb_busctl_offset,
+	.readb		= sunxi_musb_readb,
+	.writeb		= sunxi_musb_writeb,
+	.readw		= sunxi_musb_readw,
+	.writew		= sunxi_musb_writew,
+	.set_vbus	= sunxi_musb_set_vbus,
+	.pre_root_reset_end = sunxi_musb_pre_root_reset_end,
+	.post_root_reset_end = sunxi_musb_post_root_reset_end,
+};
+
+/* Allwinner OTG supports up to 5 endpoints */
+#define SUNXI_MUSB_MAX_EP_NUM	6
+#define SUNXI_MUSB_RAM_BITS	11
+
+static struct musb_fifo_cfg sunxi_musb_mode_cfg[] = {
+	MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512),
+	MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512),
+	MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512),
+	MUSB_EP_FIFO_SINGLE(2, FIFO_RX, 512),
+	MUSB_EP_FIFO_SINGLE(3, FIFO_TX, 512),
+	MUSB_EP_FIFO_SINGLE(3, FIFO_RX, 512),
+	MUSB_EP_FIFO_SINGLE(4, FIFO_TX, 512),
+	MUSB_EP_FIFO_SINGLE(4, FIFO_RX, 512),
+	MUSB_EP_FIFO_SINGLE(5, FIFO_TX, 512),
+	MUSB_EP_FIFO_SINGLE(5, FIFO_RX, 512),
+};
+
+static struct musb_hdrc_config sunxi_musb_hdrc_config = {
+	.fifo_cfg       = sunxi_musb_mode_cfg,
+	.fifo_cfg_size  = ARRAY_SIZE(sunxi_musb_mode_cfg),
+	.multipoint	= true,
+	.dyn_fifo	= true,
+	.soft_con       = true,
+	.num_eps	= SUNXI_MUSB_MAX_EP_NUM,
+	.ram_bits	= SUNXI_MUSB_RAM_BITS,
+	.dma		= 0,
+};
+
+static int sunxi_musb_probe(struct platform_device *pdev)
+{
+	struct musb_hdrc_platform_data	pdata;
+	struct platform_device_info	pinfo;
+	struct sunxi_glue		*glue;
+	struct device_node		*np = pdev->dev.of_node;
+	int ret;
+
+	if (!np) {
+		dev_err(&pdev->dev, "Error no device tree node found\n");
+		return -EINVAL;
+	}
+
+	memset(&pdata, 0, sizeof(pdata));
+	switch (of_usb_get_dr_mode(np)) {
+#if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_HOST
+	case USB_DR_MODE_HOST:
+		pdata.mode = MUSB_PORT_MODE_HOST;
+		break;
+#endif
+#ifdef CONFIG_USB_MUSB_DUAL_ROLE
+	case USB_DR_MODE_OTG:
+		pdata.mode = MUSB_PORT_MODE_DUAL_ROLE;
+		break;
+#endif
+	default:
+		dev_err(&pdev->dev, "Invalid or missing 'dr_mode' property\n");
+		return -EINVAL;
+	}
+	pdata.platform_ops	= &sunxi_musb_ops;
+	pdata.config		= &sunxi_musb_hdrc_config;
+
+	glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
+	if (!glue)
+		return -ENOMEM;
+
+	glue->dev = &pdev->dev;
+	INIT_WORK(&glue->work, sunxi_musb_work);
+	glue->host_nb.notifier_call = sunxi_musb_host_notifier;
+
+	glue->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(glue->clk)) {
+		dev_err(&pdev->dev, "Error getting clock: %ld\n",
+			PTR_ERR(glue->clk));
+		return PTR_ERR(glue->clk);
+	}
+
+	glue->phy = devm_phy_get(&pdev->dev, "usb");
+	if (IS_ERR(glue->phy)) {
+		if (PTR_ERR(glue->phy) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_err(&pdev->dev, "Error getting phy %ld\n",
+			PTR_ERR(glue->phy));
+		return PTR_ERR(glue->phy);
+	}
+
+	glue->usb_phy = usb_phy_generic_register();
+	if (IS_ERR(glue->usb_phy)) {
+		dev_err(&pdev->dev, "Error registering usb-phy %ld\n",
+			PTR_ERR(glue->usb_phy));
+		return PTR_ERR(glue->usb_phy);
+	}
+
+	glue->xceiv = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
+	if (IS_ERR(glue->xceiv)) {
+		ret = PTR_ERR(glue->xceiv);
+		dev_err(&pdev->dev, "Error getting usb-phy %d\n", ret);
+		goto err_unregister_usb_phy;
+	}
+
+	platform_set_drvdata(pdev, glue);
+
+	memset(&pinfo, 0, sizeof(pinfo));
+	pinfo.name	 = "musb-hdrc";
+	pinfo.id	= PLATFORM_DEVID_AUTO;
+	pinfo.parent	= &pdev->dev;
+	pinfo.res	= pdev->resource;
+	pinfo.num_res	= pdev->num_resources;
+	pinfo.data	= &pdata;
+	pinfo.size_data = sizeof(pdata);
+
+	glue->musb = platform_device_register_full(&pinfo);
+	if (IS_ERR(glue->musb)) {
+		ret = PTR_ERR(glue->musb);
+		dev_err(&pdev->dev, "Error registering musb dev: %d\n", ret);
+		goto err_unregister_usb_phy;
+	}
+
+	return 0;
+
+err_unregister_usb_phy:
+	usb_phy_generic_unregister(glue->usb_phy);
+	return ret;
+}
+
+static int sunxi_musb_remove(struct platform_device *pdev)
+{
+	struct sunxi_glue *glue = platform_get_drvdata(pdev);
+	struct platform_device *usb_phy = glue->usb_phy;
+
+	platform_device_unregister(glue->musb); /* Frees glue ! */
+	usb_phy_generic_unregister(usb_phy);
+
+	return 0;
+}
+
+static const struct of_device_id sunxi_musb_match[] = {
+	{ .compatible = "allwinner,sun4i-a10-musb", },
+	{}
+};
+
+static struct platform_driver sunxi_musb_driver = {
+	.probe = sunxi_musb_probe,
+	.remove = sunxi_musb_remove,
+	.driver = {
+		.name = "musb-sunxi",
+		.of_match_table = sunxi_musb_match,
+	},
+};
+module_platform_driver(sunxi_musb_driver);
+
+MODULE_DESCRIPTION("Allwinner sunxi MUSB Glue Layer");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.4.2

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

* Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-05-31 16:10     ` Hans de Goede
@ 2015-06-01  9:22         ` Maxime Ripard
  -1 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2015-06-01  9:22 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Felipe Balbi, Kishon Vijay Abraham I, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

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

On Sun, May 31, 2015 at 06:10:25PM +0200, Hans de Goede wrote:
> +	/* We either want both gpio pins or neither (when in host mode) */
> +	if (!data->id_det_gpio != !data->vbus_det_gpio) {
> +		dev_err(dev, "failed to get id or vbus detect pin\n");
> +		return -ENODEV;
> +	}

The fact that the driver expects both to be set if one is should be in
the binding documentation.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

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

* [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-01  9:22         ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2015-06-01  9:22 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, May 31, 2015 at 06:10:25PM +0200, Hans de Goede wrote:
> +	/* We either want both gpio pins or neither (when in host mode) */
> +	if (!data->id_det_gpio != !data->vbus_det_gpio) {
> +		dev_err(dev, "failed to get id or vbus detect pin\n");
> +		return -ENODEV;
> +	}

The fact that the driver expects both to be set if one is should be in
the binding documentation.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150601/3108a512/attachment.sig>

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

* Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-01  9:22         ` Maxime Ripard
@ 2015-06-01  9:28           ` Hans de Goede
  -1 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-01  9:28 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Felipe Balbi, Kishon Vijay Abraham I, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi,

On 01-06-15 11:22, Maxime Ripard wrote:
> On Sun, May 31, 2015 at 06:10:25PM +0200, Hans de Goede wrote:
>> +	/* We either want both gpio pins or neither (when in host mode) */
>> +	if (!data->id_det_gpio != !data->vbus_det_gpio) {
>> +		dev_err(dev, "failed to get id or vbus detect pin\n");
>> +		return -ENODEV;
>> +	}
>
> The fact that the driver expects both to be set if one is should be in
> the binding documentation.

That would be behavior description, and I've always been told that the
binding should only document the hardware description, not the behavior.

Also this may change in the future as we add support for more boards,
e.g. the cubieboard seems to be unable to turn vbus off ever, so vbus-det
will always read as 1 / high, making it rather useless. This is something
which I still need to figure out how to deal with.

Regards,

Hans

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

* [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-01  9:28           ` Hans de Goede
  0 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-01  9:28 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 01-06-15 11:22, Maxime Ripard wrote:
> On Sun, May 31, 2015 at 06:10:25PM +0200, Hans de Goede wrote:
>> +	/* We either want both gpio pins or neither (when in host mode) */
>> +	if (!data->id_det_gpio != !data->vbus_det_gpio) {
>> +		dev_err(dev, "failed to get id or vbus detect pin\n");
>> +		return -ENODEV;
>> +	}
>
> The fact that the driver expects both to be set if one is should be in
> the binding documentation.

That would be behavior description, and I've always been told that the
binding should only document the hardware description, not the behavior.

Also this may change in the future as we add support for more boards,
e.g. the cubieboard seems to be unable to turn vbus off ever, so vbus-det
will always read as 1 / high, making it rather useless. This is something
which I still need to figure out how to deal with.

Regards,

Hans

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

* Re: [PATCH v4 0/2] Remaining sunxi musb patches
       [not found] ` <1433088626-8858-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
  2015-05-31 16:10     ` Hans de Goede
  2015-05-31 16:10     ` Hans de Goede
@ 2015-06-01 18:30   ` Hans de Goede
       [not found]     ` <79fc4306-a621-43b8-b347-e42a27beb134-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org>
  2 siblings, 1 reply; 57+ messages in thread
From: Hans de Goede @ 2015-06-01 18:30 UTC (permalink / raw)
  To: linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
  Cc: balbi-l0cyMroinI0, rbyshko-Re5JQEeQqe8AvxtiuMwx3w,
	hdegoede-H+wXaHxf7aLQT0dZR+AlfA,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	kishon-l0cyMroinI0,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, wens-jdAy2FN1RRM


[-- Attachment #1.1: Type: text/plain, Size: 1108 bytes --]

Hi,

On Sunday, May 31, 2015 at 6:10:32 PM UTC+2, Hans de Goede wrote:
>
> Hi Kishon & Felipe, 
>
> Here is an updated version of the remaining (not yet merged in Felipe's 
> tree) 
> sunxi musb patches. 
>
> The "phy-sun4i-usb: Add full support for usb0 phy / OTG" patch has been 
> updated with a small bug-fix and is ready for merging. 
>
> The "musb: Add support for the Allwinner sunxi musb" patch has been 
> updated 
> to use the latest version of Maxime's sunxi sram controller driver. This 
> one has a compile-time dependency on Maxime's sunxi sram controller 
> driver, 
> so it cannot be merged until that is merged. 
>


Arnd has just merged the updated sram controller into the next branch
of the soc-drivers repo, so both these patches are now ready to merge.

Regards,

Hans

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.

[-- Attachment #1.2: Type: text/html, Size: 1481 bytes --]

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

* Re: [PATCH v4 0/2] Remaining sunxi musb patches
  2015-06-01 18:30   ` [PATCH v4 0/2] Remaining sunxi musb patches Hans de Goede
@ 2015-06-01 18:34         ` Felipe Balbi
  0 siblings, 0 replies; 57+ messages in thread
From: Felipe Balbi @ 2015-06-01 18:34 UTC (permalink / raw)
  To: Hans de Goede
  Cc: linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, balbi-l0cyMroinI0,
	rbyshko-Re5JQEeQqe8AvxtiuMwx3w, hdegoede-H+wXaHxf7aLQT0dZR+AlfA,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	kishon-l0cyMroinI0,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, wens-jdAy2FN1RRM

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

On Mon, Jun 01, 2015 at 11:30:45AM -0700, Hans de Goede wrote:
> Hi,
> 
> On Sunday, May 31, 2015 at 6:10:32 PM UTC+2, Hans de Goede wrote:
> >
> > Hi Kishon & Felipe, 
> >
> > Here is an updated version of the remaining (not yet merged in Felipe's 
> > tree) 
> > sunxi musb patches. 
> >
> > The "phy-sun4i-usb: Add full support for usb0 phy / OTG" patch has been 
> > updated with a small bug-fix and is ready for merging. 
> >
> > The "musb: Add support for the Allwinner sunxi musb" patch has been 
> > updated 
> > to use the latest version of Maxime's sunxi sram controller driver. This 
> > one has a compile-time dependency on Maxime's sunxi sram controller 
> > driver, 
> > so it cannot be merged until that is merged. 
> >
> 
> 
> Arnd has just merged the updated sram controller into the next branch
> of the soc-drivers repo, so both these patches are now ready to merge.

all right, so for v4.3 merge window there will be no issues in queues
the two remaining patches. Thanks

-- 
balbi

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

* [PATCH v4 0/2] Remaining sunxi musb patches
@ 2015-06-01 18:34         ` Felipe Balbi
  0 siblings, 0 replies; 57+ messages in thread
From: Felipe Balbi @ 2015-06-01 18:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jun 01, 2015 at 11:30:45AM -0700, Hans de Goede wrote:
> Hi,
> 
> On Sunday, May 31, 2015 at 6:10:32 PM UTC+2, Hans de Goede wrote:
> >
> > Hi Kishon & Felipe, 
> >
> > Here is an updated version of the remaining (not yet merged in Felipe's 
> > tree) 
> > sunxi musb patches. 
> >
> > The "phy-sun4i-usb: Add full support for usb0 phy / OTG" patch has been 
> > updated with a small bug-fix and is ready for merging. 
> >
> > The "musb: Add support for the Allwinner sunxi musb" patch has been 
> > updated 
> > to use the latest version of Maxime's sunxi sram controller driver. This 
> > one has a compile-time dependency on Maxime's sunxi sram controller 
> > driver, 
> > so it cannot be merged until that is merged. 
> >
> 
> 
> Arnd has just merged the updated sram controller into the next branch
> of the soc-drivers repo, so both these patches are now ready to merge.

all right, so for v4.3 merge window there will be no issues in queues
the two remaining patches. Thanks

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150601/29dfe505/attachment.sig>

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

* Re: [PATCH v4 0/2] Remaining sunxi musb patches
  2015-06-01 18:34         ` Felipe Balbi
@ 2015-06-02  7:14             ` Hans de Goede
  -1 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-02  7:14 UTC (permalink / raw)
  To: balbi-l0cyMroinI0, Hans de Goede
  Cc: linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	rbyshko-Re5JQEeQqe8AvxtiuMwx3w,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	kishon-l0cyMroinI0,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, wens-jdAy2FN1RRM

Hi,

On 01-06-15 20:34, Felipe Balbi wrote:
> On Mon, Jun 01, 2015 at 11:30:45AM -0700, Hans de Goede wrote:
>> Hi,
>>
>> On Sunday, May 31, 2015 at 6:10:32 PM UTC+2, Hans de Goede wrote:
>>>
>>> Hi Kishon & Felipe,
>>>
>>> Here is an updated version of the remaining (not yet merged in Felipe's
>>> tree)
>>> sunxi musb patches.
>>>
>>> The "phy-sun4i-usb: Add full support for usb0 phy / OTG" patch has been
>>> updated with a small bug-fix and is ready for merging.
>>>
>>> The "musb: Add support for the Allwinner sunxi musb" patch has been
>>> updated
>>> to use the latest version of Maxime's sunxi sram controller driver. This
>>> one has a compile-time dependency on Maxime's sunxi sram controller
>>> driver,
>>> so it cannot be merged until that is merged.
>>>
>>
>>
>> Arnd has just merged the updated sram controller into the next branch
>> of the soc-drivers repo, so both these patches are now ready to merge.
>
> all right, so for v4.3 merge window there will be no issues in queues
> the two remaining patches. Thanks

So I take it that it is too late to get these 2 into 4.2 ? I've a bunch
of (small) follow up patches coming up to enable support for the musb
controller on new boards, merging those would be easier if these were
already merged. And there also is a largish set of sunxi dts patches
actually enabling the musb code which is sorta waiting for merging
these 2 (waiting for the dt binding to be stable).

If this is not going to make 4.2, can you and Kishon at least review
them and let me know of they will get merged for 4.3 as is (baring
any merge issues) ?

Regards,

Hans
--
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	[flat|nested] 57+ messages in thread

* [PATCH v4 0/2] Remaining sunxi musb patches
@ 2015-06-02  7:14             ` Hans de Goede
  0 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-02  7:14 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 01-06-15 20:34, Felipe Balbi wrote:
> On Mon, Jun 01, 2015 at 11:30:45AM -0700, Hans de Goede wrote:
>> Hi,
>>
>> On Sunday, May 31, 2015 at 6:10:32 PM UTC+2, Hans de Goede wrote:
>>>
>>> Hi Kishon & Felipe,
>>>
>>> Here is an updated version of the remaining (not yet merged in Felipe's
>>> tree)
>>> sunxi musb patches.
>>>
>>> The "phy-sun4i-usb: Add full support for usb0 phy / OTG" patch has been
>>> updated with a small bug-fix and is ready for merging.
>>>
>>> The "musb: Add support for the Allwinner sunxi musb" patch has been
>>> updated
>>> to use the latest version of Maxime's sunxi sram controller driver. This
>>> one has a compile-time dependency on Maxime's sunxi sram controller
>>> driver,
>>> so it cannot be merged until that is merged.
>>>
>>
>>
>> Arnd has just merged the updated sram controller into the next branch
>> of the soc-drivers repo, so both these patches are now ready to merge.
>
> all right, so for v4.3 merge window there will be no issues in queues
> the two remaining patches. Thanks

So I take it that it is too late to get these 2 into 4.2 ? I've a bunch
of (small) follow up patches coming up to enable support for the musb
controller on new boards, merging those would be easier if these were
already merged. And there also is a largish set of sunxi dts patches
actually enabling the musb code which is sorta waiting for merging
these 2 (waiting for the dt binding to be stable).

If this is not going to make 4.2, can you and Kishon at least review
them and let me know of they will get merged for 4.3 as is (baring
any merge issues) ?

Regards,

Hans

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

* Re: [PATCH v4 0/2] Remaining sunxi musb patches
  2015-06-02  7:14             ` Hans de Goede
@ 2015-06-02 14:45                 ` Felipe Balbi
  -1 siblings, 0 replies; 57+ messages in thread
From: Felipe Balbi @ 2015-06-02 14:45 UTC (permalink / raw)
  To: Hans de Goede
  Cc: balbi-l0cyMroinI0, Hans de Goede,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	rbyshko-Re5JQEeQqe8AvxtiuMwx3w,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	kishon-l0cyMroinI0,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, wens-jdAy2FN1RRM

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

Hi,

On Tue, Jun 02, 2015 at 09:14:05AM +0200, Hans de Goede wrote:
> >>>Here is an updated version of the remaining (not yet merged in Felipe's
> >>>tree)
> >>>sunxi musb patches.
> >>>
> >>>The "phy-sun4i-usb: Add full support for usb0 phy / OTG" patch has been
> >>>updated with a small bug-fix and is ready for merging.
> >>>
> >>>The "musb: Add support for the Allwinner sunxi musb" patch has been
> >>>updated
> >>>to use the latest version of Maxime's sunxi sram controller driver. This
> >>>one has a compile-time dependency on Maxime's sunxi sram controller
> >>>driver,
> >>>so it cannot be merged until that is merged.
> >>>
> >>
> >>
> >>Arnd has just merged the updated sram controller into the next branch
> >>of the soc-drivers repo, so both these patches are now ready to merge.
> >
> >all right, so for v4.3 merge window there will be no issues in queues
> >the two remaining patches. Thanks
> 
> So I take it that it is too late to get these 2 into 4.2 ? I've a bunch
> of (small) follow up patches coming up to enable support for the musb
> controller on new boards, merging those would be easier if these were
> already merged. And there also is a largish set of sunxi dts patches
> actually enabling the musb code which is sorta waiting for merging
> these 2 (waiting for the dt binding to be stable).

DTS should not have to wait. If we know what the compatible should look
like, you can send your DTS upstream no problem. There will just be a
few devices which will not trigger a driver probe, but that's just fine.

> If this is not going to make 4.2, can you and Kishon at least review
> them and let me know of they will get merged for 4.3 as is (baring
> any merge issues) ?

Sure, that I can do, no issues.

-- 
balbi

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

* [PATCH v4 0/2] Remaining sunxi musb patches
@ 2015-06-02 14:45                 ` Felipe Balbi
  0 siblings, 0 replies; 57+ messages in thread
From: Felipe Balbi @ 2015-06-02 14:45 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Tue, Jun 02, 2015 at 09:14:05AM +0200, Hans de Goede wrote:
> >>>Here is an updated version of the remaining (not yet merged in Felipe's
> >>>tree)
> >>>sunxi musb patches.
> >>>
> >>>The "phy-sun4i-usb: Add full support for usb0 phy / OTG" patch has been
> >>>updated with a small bug-fix and is ready for merging.
> >>>
> >>>The "musb: Add support for the Allwinner sunxi musb" patch has been
> >>>updated
> >>>to use the latest version of Maxime's sunxi sram controller driver. This
> >>>one has a compile-time dependency on Maxime's sunxi sram controller
> >>>driver,
> >>>so it cannot be merged until that is merged.
> >>>
> >>
> >>
> >>Arnd has just merged the updated sram controller into the next branch
> >>of the soc-drivers repo, so both these patches are now ready to merge.
> >
> >all right, so for v4.3 merge window there will be no issues in queues
> >the two remaining patches. Thanks
> 
> So I take it that it is too late to get these 2 into 4.2 ? I've a bunch
> of (small) follow up patches coming up to enable support for the musb
> controller on new boards, merging those would be easier if these were
> already merged. And there also is a largish set of sunxi dts patches
> actually enabling the musb code which is sorta waiting for merging
> these 2 (waiting for the dt binding to be stable).

DTS should not have to wait. If we know what the compatible should look
like, you can send your DTS upstream no problem. There will just be a
few devices which will not trigger a driver probe, but that's just fine.

> If this is not going to make 4.2, can you and Kishon at least review
> them and let me know of they will get merged for 4.3 as is (baring
> any merge issues) ?

Sure, that I can do, no issues.

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150602/e443db59/attachment.sig>

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

* Re: [PATCH v4 2/2] musb: Add support for the Allwinner sunxi musb controller
  2015-05-31 16:10     ` Hans de Goede
@ 2015-06-02 15:02         ` Felipe Balbi
  -1 siblings, 0 replies; 57+ messages in thread
From: Felipe Balbi @ 2015-06-02 15:02 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Felipe Balbi, Kishon Vijay Abraham I, Maxime Ripard,
	Chen-Yu Tsai, Roman Byshko, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

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

On Sun, May 31, 2015 at 06:10:26PM +0200, Hans de Goede wrote:
> This is based on initial code to get the Allwinner sunxi musb controller
> supported by Chen-Yu Tsai and Roman Byshko.
> 
> This adds support for the Allwinner sunxi musb controller in both host only
> and otg mode. Peripheral only mode is not supported, as no boards use that.
> 
> This has been tested on a cubietruck (A20 SoC) and an UTOO P66 tablet
> (A13 SoC) with a variety of devices in host mode and with the g_serial gadget
> driver in peripheral mode, plugging otg / host cables in/out a lot of times
> in all possible imaginable plug orders.
> 
> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

this looks good, I'd say it's ready for v4.3

-- 
balbi

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

* [PATCH v4 2/2] musb: Add support for the Allwinner sunxi musb controller
@ 2015-06-02 15:02         ` Felipe Balbi
  0 siblings, 0 replies; 57+ messages in thread
From: Felipe Balbi @ 2015-06-02 15:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, May 31, 2015 at 06:10:26PM +0200, Hans de Goede wrote:
> This is based on initial code to get the Allwinner sunxi musb controller
> supported by Chen-Yu Tsai and Roman Byshko.
> 
> This adds support for the Allwinner sunxi musb controller in both host only
> and otg mode. Peripheral only mode is not supported, as no boards use that.
> 
> This has been tested on a cubietruck (A20 SoC) and an UTOO P66 tablet
> (A13 SoC) with a variety of devices in host mode and with the g_serial gadget
> driver in peripheral mode, plugging otg / host cables in/out a lot of times
> in all possible imaginable plug orders.
> 
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>

this looks good, I'd say it's ready for v4.3

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150602/30db435a/attachment-0001.sig>

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

* Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-01  9:28           ` Hans de Goede
@ 2015-06-02 15:22               ` Maxime Ripard
  -1 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2015-06-02 15:22 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Felipe Balbi, Kishon Vijay Abraham I, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

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

Hi,

On Mon, Jun 01, 2015 at 11:28:23AM +0200, Hans de Goede wrote:
> On 01-06-15 11:22, Maxime Ripard wrote:
> >On Sun, May 31, 2015 at 06:10:25PM +0200, Hans de Goede wrote:
> >>+	/* We either want both gpio pins or neither (when in host mode) */
> >>+	if (!data->id_det_gpio != !data->vbus_det_gpio) {
> >>+		dev_err(dev, "failed to get id or vbus detect pin\n");
> >>+		return -ENODEV;
> >>+	}
> >
> >The fact that the driver expects both to be set if one is should be in
> >the binding documentation.
> 
> That would be behavior description, and I've always been told that the
> binding should only document the hardware description, not the behavior.

Yes, that's true, but here it looks more like there's no point of
having one without the other.

Pretty much like interrupt-parent and interrupt. A single one doesn't
really make much sense without the other.

And I don't think saying that you would need some additional
properties when in device mode rather than in host mode is really
documenting a behaviour. It does change things at a hardware level
too.

> Also this may change in the future as we add support for more boards,
> e.g. the cubieboard seems to be unable to turn vbus off ever, so vbus-det
> will always read as 1 / high, making it rather useless. This is something
> which I still need to figure out how to deal with.

We can always figure it out in due time.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

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

* [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-02 15:22               ` Maxime Ripard
  0 siblings, 0 replies; 57+ messages in thread
From: Maxime Ripard @ 2015-06-02 15:22 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Mon, Jun 01, 2015 at 11:28:23AM +0200, Hans de Goede wrote:
> On 01-06-15 11:22, Maxime Ripard wrote:
> >On Sun, May 31, 2015 at 06:10:25PM +0200, Hans de Goede wrote:
> >>+	/* We either want both gpio pins or neither (when in host mode) */
> >>+	if (!data->id_det_gpio != !data->vbus_det_gpio) {
> >>+		dev_err(dev, "failed to get id or vbus detect pin\n");
> >>+		return -ENODEV;
> >>+	}
> >
> >The fact that the driver expects both to be set if one is should be in
> >the binding documentation.
> 
> That would be behavior description, and I've always been told that the
> binding should only document the hardware description, not the behavior.

Yes, that's true, but here it looks more like there's no point of
having one without the other.

Pretty much like interrupt-parent and interrupt. A single one doesn't
really make much sense without the other.

And I don't think saying that you would need some additional
properties when in device mode rather than in host mode is really
documenting a behaviour. It does change things at a hardware level
too.

> Also this may change in the future as we add support for more boards,
> e.g. the cubieboard seems to be unable to turn vbus off ever, so vbus-det
> will always read as 1 / high, making it rather useless. This is something
> which I still need to figure out how to deal with.

We can always figure it out in due time.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20150602/dde4cac1/attachment.sig>

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

* Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-05-31 16:10     ` Hans de Goede
@ 2015-06-11  5:48         ` Kishon Vijay Abraham I
  -1 siblings, 0 replies; 57+ messages in thread
From: Kishon Vijay Abraham I @ 2015-06-11  5:48 UTC (permalink / raw)
  To: Hans de Goede, Felipe Balbi
  Cc: Maxime Ripard, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Chanwoo Choi

+Chanwoo

Hi,

On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
> The usb0 phy is connected to an OTG controller, and as such needs some special
> handling:
>
> 1) It allows explicit control over the pullups, enable these on phy_init and
> disable them on phy_exit.
>
> 2) It has bits to signal id and vbus detect to the musb-core, add support for
> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
> these bits to the correct values for operating in host only mode when no
> gpios are specified in the devicetree.
>
> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
> device cable is plugged in, so when in dual role mode register an extcon.
>
> While updating the devicetree binding documentation also add documentation
> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>
> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> ---
> Changes in v2:
> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>   has been moved to the phy-sun4i-usb driver and their status is exported
>   through extcon for the sunxi-musb glue
> Changes in v3:
> -No changes
> Changes in v4:
> -Do not call regulator_disable in an unbalanced manner when an external vbus
>   is present
> ---
>   .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>   drivers/phy/Kconfig                                |   1 +
>   drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>   3 files changed, 281 insertions(+), 11 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> index 16528b9..557fa99 100644
> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> @@ -23,6 +23,13 @@ Required properties:
>     * "usb1_reset"
>     * "usb2_reset" for sun4i, sun6i or sun7i
>
> +Optional properties:
> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
> +
>   Example:
>   	usbphy: phy@0x01c13400 {
>   		#phy-cells = <1>;
> @@ -32,6 +39,13 @@ Example:
>   		reg-names = "phy_ctrl", "pmu1", "pmu2";
>   		clocks = <&usb_clk 8>;
>   		clock-names = "usb_phy";
> -		resets = <&usb_clk 1>, <&usb_clk 2>;
> -		reset-names = "usb1_reset", "usb2_reset";
> +		resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
> +		reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
> +		usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
> +		usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
> +		usb0_vbus-supply = <&reg_usb0_vbus>;
> +		usb1_vbus-supply = <&reg_usb1_vbus>;
> +		usb2_vbus-supply = <&reg_usb2_vbus>;
>   	};
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index a53bd5b..4614fba 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>   	tristate "Allwinner sunxi SoC USB PHY driver"
>   	depends on ARCH_SUNXI && HAS_IOMEM && OF
>   	depends on RESET_CONTROLLER
> +	select EXTCON

Avoid using 'select' on visible Kconfig symbols.

Also please split the patch to make the reviewing a bit easier.

Thanks
Kishon
>   	select GENERIC_PHY
>   	help
>   	  Enable this to support the transceiver that is part of Allwinner
> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
> index 91c5be4..b45d707 100644
> --- a/drivers/phy/phy-sun4i-usb.c
> +++ b/drivers/phy/phy-sun4i-usb.c
> @@ -1,7 +1,7 @@
>   /*
>    * Allwinner sun4i USB phy driver
>    *
> - * Copyright (C) 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>    *
>    * Based on code from
>    * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
> @@ -23,17 +23,23 @@
>
>   #include <linux/clk.h>
>   #include <linux/err.h>
> +#include <linux/extcon.h>
>   #include <linux/io.h>
> +#include <linux/interrupt.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_gpio.h>
>   #include <linux/phy/phy.h>
>   #include <linux/phy/phy-sun4i-usb.h>
>   #include <linux/platform_device.h>
>   #include <linux/regulator/consumer.h>
>   #include <linux/reset.h>
> +#include <linux/workqueue.h>
> +
> +#define DRIVER_NAME "sun4i-usb-phy"
>
>   #define REG_ISCR			0x00
>   #define REG_PHYCTL			0x04
> @@ -47,6 +53,17 @@
>   #define SUNXI_AHB_INCRX_ALIGN_EN	BIT(8)
>   #define SUNXI_ULPI_BYPASS_EN		BIT(0)
>
> +/* ISCR, Interface Status and Control bits */
> +#define ISCR_ID_PULLUP_EN		(1 << 17)
> +#define ISCR_DPDM_PULLUP_EN	(1 << 16)
> +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
> +#define ISCR_FORCE_ID_MASK	(3 << 14)
> +#define ISCR_FORCE_ID_LOW		(2 << 14)
> +#define ISCR_FORCE_ID_HIGH	(3 << 14)
> +#define ISCR_FORCE_VBUS_MASK	(3 << 12)
> +#define ISCR_FORCE_VBUS_LOW	(2 << 12)
> +#define ISCR_FORCE_VBUS_HIGH	(3 << 12)
> +
>   /* Common Control Bits for Both PHYs */
>   #define PHY_PLL_BW			0x03
>   #define PHY_RES45_CAL_EN		0x0c
> @@ -63,6 +80,13 @@
>
>   #define MAX_PHYS			3
>
> +/*
> + * Note do not raise the debounce time, we must report Vusb high within 100ms
> + * otherwise we get Vbus errors
> + */
> +#define DEBOUNCE_TIME			msecs_to_jiffies(50)
> +#define POLL_TIME			msecs_to_jiffies(250)
> +
>   struct sun4i_usb_phy_data {
>   	void __iomem *base;
>   	struct mutex mutex;
> @@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
>   		struct regulator *vbus;
>   		struct reset_control *reset;
>   		struct clk *clk;
> +		bool regulator_on;
>   		int index;
>   	} phys[MAX_PHYS];
> +	/* phy0 / otg related variables */
> +	struct extcon_dev extcon;
> +	const char *extcon_cable_names[3];
> +	bool phy0_init;
> +	bool phy0_poll;
> +	struct gpio_desc *id_det_gpio;
> +	struct gpio_desc *vbus_det_gpio;
> +	int id_det_irq;
> +	int vbus_det_irq;
> +	int id_det;
> +	int vbus_det;
> +	struct delayed_work detect;
>   };
>
>   #define to_sun4i_usb_phy_data(phy) \
>   	container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>
> +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
> +{
> +	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +	u32 iscr;
> +
> +	iscr = readl(data->base + REG_ISCR);
> +	iscr &= ~clr;
> +	iscr |= set;
> +	writel(iscr, data->base + REG_ISCR);
> +}
> +
> +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
> +{
> +	if (val)
> +		val = ISCR_FORCE_ID_HIGH;
> +	else
> +		val = ISCR_FORCE_ID_LOW;
> +
> +	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
> +}
> +
> +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
> +{
> +	if (val)
> +		val = ISCR_FORCE_VBUS_HIGH;
> +	else
> +		val = ISCR_FORCE_VBUS_LOW;
> +
> +	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
> +}
> +
>   static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>   				int len)
>   {
> @@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
>
>   	sun4i_usb_phy_passby(phy, 1);
>
> +	if (phy->index == 0) {
> +		data->phy0_init = true;
> +
> +		/* Enable pull-ups */
> +		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
> +		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
> +
> +		if (data->id_det_gpio) {
> +			/* OTG mode, force ISCR and cable state updates */
> +			data->id_det = -1;
> +			data->vbus_det = -1;
> +			queue_delayed_work(system_wq, &data->detect, 0);
> +		} else {
> +			/* Host only mode */
> +			sun4i_usb_phy0_set_id_detect(_phy, 0);
> +			sun4i_usb_phy0_set_vbus_detect(_phy, 1);
> +		}
> +	}
> +
>   	return 0;
>   }
>
>   static int sun4i_usb_phy_exit(struct phy *_phy)
>   {
>   	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +
> +	if (phy->index == 0) {
> +		/* Disable pull-ups */
> +		sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
> +		sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
> +		data->phy0_init = false;
> +	}
>
>   	sun4i_usb_phy_passby(phy, 0);
>   	reset_control_assert(phy->reset);
> @@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
>   static int sun4i_usb_phy_power_on(struct phy *_phy)
>   {
>   	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> -	int ret = 0;
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +	int ret;
> +
> +	if (!phy->vbus || phy->regulator_on)
> +		return 0;
> +
> +	/* For phy0 only turn on Vbus if we don't have an ext. Vbus */
> +	if (phy->index == 0 && data->vbus_det)
> +		return 0;
>
> -	if (phy->vbus)
> -		ret = regulator_enable(phy->vbus);
> +	ret = regulator_enable(phy->vbus);
> +	if (ret)
> +		return ret;
>
> -	return ret;
> +	phy->regulator_on = true;
> +
> +	/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
> +	if (phy->index == 0 && data->phy0_poll)
> +		mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
> +
> +	return 0;
>   }
>
>   static int sun4i_usb_phy_power_off(struct phy *_phy)
>   {
>   	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +
> +	if (!phy->vbus || !phy->regulator_on)
> +		return 0;
>
> -	if (phy->vbus)
> -		regulator_disable(phy->vbus);
> +	regulator_disable(phy->vbus);
> +	phy->regulator_on = false;
> +
> +	/*
> +	 * phy0 vbus typically slowly discharges, sometimes this causes the
> +	 * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
> +	 */
> +	if (phy->index == 0 && !data->phy0_poll)
> +		mod_delayed_work(system_wq, &data->detect, POLL_TIME);
>
>   	return 0;
>   }
> @@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
>   	.owner		= THIS_MODULE,
>   };
>
> +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
> +{
> +	struct sun4i_usb_phy_data *data =
> +		container_of(work, struct sun4i_usb_phy_data, detect.work);
> +	struct phy *phy0 = data->phys[0].phy;
> +	int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
> +
> +	id_det = gpiod_get_value_cansleep(data->id_det_gpio);
> +	vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
> +
> +	mutex_lock(&phy0->mutex);
> +
> +	if (!data->phy0_init) {
> +		mutex_unlock(&phy0->mutex);
> +		return;
> +	}
> +
> +	if (id_det != data->id_det) {
> +		sun4i_usb_phy0_set_id_detect(phy0, id_det);
> +		data->id_det = id_det;
> +		id_notify = 1;
> +	}
> +
> +	if (vbus_det != data->vbus_det) {
> +		sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
> +		data->vbus_det = vbus_det;
> +		vbus_notify = 1;
> +	}
> +
> +	mutex_unlock(&phy0->mutex);
> +
> +	if (id_notify)
> +		extcon_set_cable_state(&data->extcon,
> +				       extcon_cable_name[EXTCON_USB_HOST],
> +				       !id_det);
> +
> +	if (vbus_notify)
> +		extcon_set_cable_state(&data->extcon,
> +				       extcon_cable_name[EXTCON_USB],
> +				       vbus_det);
> +
> +	if (data->phy0_poll)
> +		queue_delayed_work(system_wq, &data->detect, POLL_TIME);
> +}
> +
> +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
> +{
> +	struct sun4i_usb_phy_data *data = dev_id;
> +
> +	/* vbus or id changed, let the pins settle and then scan them */
> +	mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
> +
> +	return IRQ_HANDLED;
> +}
> +
>   static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>   					struct of_phandle_args *args)
>   {
> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>   	struct phy_provider *phy_provider;
>   	bool dedicated_clocks;
>   	struct resource *res;
> -	int i;
> +	int i, ret;
>
>   	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>   	if (!data)
>   		return -ENOMEM;
>
>   	mutex_init(&data->mutex);
> +	INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
> +	data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
> +	data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
> +	data->extcon_cable_names[2] = NULL;
> +	data->extcon.name = DRIVER_NAME;
> +	data->extcon.supported_cable = data->extcon_cable_names;
> +	data->extcon.dev.parent = dev;
>
>   	if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>   		data->num_phys = 2;
> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>   	if (IS_ERR(data->base))
>   		return PTR_ERR(data->base);
>
> +	data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
> +	if (IS_ERR(data->id_det_gpio)) {
> +		if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		data->id_det_gpio = NULL;
> +	}
> +
> +	data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
> +	if (IS_ERR(data->vbus_det_gpio)) {
> +		if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		data->vbus_det_gpio = NULL;
> +	}
> +
> +	/* We either want both gpio pins or neither (when in host mode) */
> +	if (!data->id_det_gpio != !data->vbus_det_gpio) {
> +		dev_err(dev, "failed to get id or vbus detect pin\n");
> +		return -ENODEV;
> +	}
> +
> +	if (data->id_det_gpio) {
> +		ret = devm_extcon_dev_register(dev, &data->extcon);
> +		if (ret) {
> +			dev_err(dev, "failed to register extcon: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
>   	for (i = 0; i < data->num_phys; i++) {
>   		struct sun4i_usb_phy *phy = data->phys + i;
>   		char name[16];
> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>   		phy_set_drvdata(phy->phy, &data->phys[i]);
>   	}
>
> +	data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
> +	data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
> +	if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
> +		data->phy0_poll = true;
> +
> +	if (data->id_det_irq >= 0) {
> +		ret = devm_request_irq(dev, data->id_det_irq,
> +				sun4i_usb_phy0_id_vbus_det_irq,
> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
> +				"usb0-id-det", data);
> +		if (ret) {
> +			dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (data->vbus_det_irq >= 0) {
> +		ret = devm_request_irq(dev, data->vbus_det_irq,
> +				sun4i_usb_phy0_id_vbus_det_irq,
> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
> +				"usb0-vbus-det", data);
> +		if (ret) {
> +			dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
>   	dev_set_drvdata(dev, data);
>   	phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>
>   	return PTR_ERR_OR_ZERO(phy_provider);
>   }
>
> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
> +
> +	if (data->id_det_irq >= 0)
> +		devm_free_irq(dev, data->id_det_irq, data);
> +	if (data->vbus_det_irq >= 0)
> +		devm_free_irq(dev, data->vbus_det_irq, data);
> +
> +	cancel_delayed_work_sync(&data->detect);
> +
> +	return 0;
> +}
> +
>   static const struct of_device_id sun4i_usb_phy_of_match[] = {
>   	{ .compatible = "allwinner,sun4i-a10-usb-phy" },
>   	{ .compatible = "allwinner,sun5i-a13-usb-phy" },
> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>
>   static struct platform_driver sun4i_usb_phy_driver = {
>   	.probe	= sun4i_usb_phy_probe,
> +	.remove	= sun4i_usb_phy_remove,
>   	.driver = {
>   		.of_match_table	= sun4i_usb_phy_of_match,
> -		.name  = "sun4i-usb-phy",
> +		.name  = DRIVER_NAME,
>   	}
>   };
>   module_platform_driver(sun4i_usb_phy_driver);
>

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

* [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11  5:48         ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 57+ messages in thread
From: Kishon Vijay Abraham I @ 2015-06-11  5:48 UTC (permalink / raw)
  To: linux-arm-kernel

+Chanwoo

Hi,

On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
> The usb0 phy is connected to an OTG controller, and as such needs some special
> handling:
>
> 1) It allows explicit control over the pullups, enable these on phy_init and
> disable them on phy_exit.
>
> 2) It has bits to signal id and vbus detect to the musb-core, add support for
> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
> these bits to the correct values for operating in host only mode when no
> gpios are specified in the devicetree.
>
> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
> device cable is plugged in, so when in dual role mode register an extcon.
>
> While updating the devicetree binding documentation also add documentation
> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
> Changes in v2:
> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>   has been moved to the phy-sun4i-usb driver and their status is exported
>   through extcon for the sunxi-musb glue
> Changes in v3:
> -No changes
> Changes in v4:
> -Do not call regulator_disable in an unbalanced manner when an external vbus
>   is present
> ---
>   .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>   drivers/phy/Kconfig                                |   1 +
>   drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>   3 files changed, 281 insertions(+), 11 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> index 16528b9..557fa99 100644
> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> @@ -23,6 +23,13 @@ Required properties:
>     * "usb1_reset"
>     * "usb2_reset" for sun4i, sun6i or sun7i
>
> +Optional properties:
> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
> +
>   Example:
>   	usbphy: phy at 0x01c13400 {
>   		#phy-cells = <1>;
> @@ -32,6 +39,13 @@ Example:
>   		reg-names = "phy_ctrl", "pmu1", "pmu2";
>   		clocks = <&usb_clk 8>;
>   		clock-names = "usb_phy";
> -		resets = <&usb_clk 1>, <&usb_clk 2>;
> -		reset-names = "usb1_reset", "usb2_reset";
> +		resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
> +		reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
> +		usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
> +		usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
> +		usb0_vbus-supply = <&reg_usb0_vbus>;
> +		usb1_vbus-supply = <&reg_usb1_vbus>;
> +		usb2_vbus-supply = <&reg_usb2_vbus>;
>   	};
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index a53bd5b..4614fba 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>   	tristate "Allwinner sunxi SoC USB PHY driver"
>   	depends on ARCH_SUNXI && HAS_IOMEM && OF
>   	depends on RESET_CONTROLLER
> +	select EXTCON

Avoid using 'select' on visible Kconfig symbols.

Also please split the patch to make the reviewing a bit easier.

Thanks
Kishon
>   	select GENERIC_PHY
>   	help
>   	  Enable this to support the transceiver that is part of Allwinner
> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
> index 91c5be4..b45d707 100644
> --- a/drivers/phy/phy-sun4i-usb.c
> +++ b/drivers/phy/phy-sun4i-usb.c
> @@ -1,7 +1,7 @@
>   /*
>    * Allwinner sun4i USB phy driver
>    *
> - * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede@redhat.com>
>    *
>    * Based on code from
>    * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
> @@ -23,17 +23,23 @@
>
>   #include <linux/clk.h>
>   #include <linux/err.h>
> +#include <linux/extcon.h>
>   #include <linux/io.h>
> +#include <linux/interrupt.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_gpio.h>
>   #include <linux/phy/phy.h>
>   #include <linux/phy/phy-sun4i-usb.h>
>   #include <linux/platform_device.h>
>   #include <linux/regulator/consumer.h>
>   #include <linux/reset.h>
> +#include <linux/workqueue.h>
> +
> +#define DRIVER_NAME "sun4i-usb-phy"
>
>   #define REG_ISCR			0x00
>   #define REG_PHYCTL			0x04
> @@ -47,6 +53,17 @@
>   #define SUNXI_AHB_INCRX_ALIGN_EN	BIT(8)
>   #define SUNXI_ULPI_BYPASS_EN		BIT(0)
>
> +/* ISCR, Interface Status and Control bits */
> +#define ISCR_ID_PULLUP_EN		(1 << 17)
> +#define ISCR_DPDM_PULLUP_EN	(1 << 16)
> +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
> +#define ISCR_FORCE_ID_MASK	(3 << 14)
> +#define ISCR_FORCE_ID_LOW		(2 << 14)
> +#define ISCR_FORCE_ID_HIGH	(3 << 14)
> +#define ISCR_FORCE_VBUS_MASK	(3 << 12)
> +#define ISCR_FORCE_VBUS_LOW	(2 << 12)
> +#define ISCR_FORCE_VBUS_HIGH	(3 << 12)
> +
>   /* Common Control Bits for Both PHYs */
>   #define PHY_PLL_BW			0x03
>   #define PHY_RES45_CAL_EN		0x0c
> @@ -63,6 +80,13 @@
>
>   #define MAX_PHYS			3
>
> +/*
> + * Note do not raise the debounce time, we must report Vusb high within 100ms
> + * otherwise we get Vbus errors
> + */
> +#define DEBOUNCE_TIME			msecs_to_jiffies(50)
> +#define POLL_TIME			msecs_to_jiffies(250)
> +
>   struct sun4i_usb_phy_data {
>   	void __iomem *base;
>   	struct mutex mutex;
> @@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
>   		struct regulator *vbus;
>   		struct reset_control *reset;
>   		struct clk *clk;
> +		bool regulator_on;
>   		int index;
>   	} phys[MAX_PHYS];
> +	/* phy0 / otg related variables */
> +	struct extcon_dev extcon;
> +	const char *extcon_cable_names[3];
> +	bool phy0_init;
> +	bool phy0_poll;
> +	struct gpio_desc *id_det_gpio;
> +	struct gpio_desc *vbus_det_gpio;
> +	int id_det_irq;
> +	int vbus_det_irq;
> +	int id_det;
> +	int vbus_det;
> +	struct delayed_work detect;
>   };
>
>   #define to_sun4i_usb_phy_data(phy) \
>   	container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>
> +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
> +{
> +	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +	u32 iscr;
> +
> +	iscr = readl(data->base + REG_ISCR);
> +	iscr &= ~clr;
> +	iscr |= set;
> +	writel(iscr, data->base + REG_ISCR);
> +}
> +
> +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
> +{
> +	if (val)
> +		val = ISCR_FORCE_ID_HIGH;
> +	else
> +		val = ISCR_FORCE_ID_LOW;
> +
> +	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
> +}
> +
> +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
> +{
> +	if (val)
> +		val = ISCR_FORCE_VBUS_HIGH;
> +	else
> +		val = ISCR_FORCE_VBUS_LOW;
> +
> +	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
> +}
> +
>   static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>   				int len)
>   {
> @@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
>
>   	sun4i_usb_phy_passby(phy, 1);
>
> +	if (phy->index == 0) {
> +		data->phy0_init = true;
> +
> +		/* Enable pull-ups */
> +		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
> +		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
> +
> +		if (data->id_det_gpio) {
> +			/* OTG mode, force ISCR and cable state updates */
> +			data->id_det = -1;
> +			data->vbus_det = -1;
> +			queue_delayed_work(system_wq, &data->detect, 0);
> +		} else {
> +			/* Host only mode */
> +			sun4i_usb_phy0_set_id_detect(_phy, 0);
> +			sun4i_usb_phy0_set_vbus_detect(_phy, 1);
> +		}
> +	}
> +
>   	return 0;
>   }
>
>   static int sun4i_usb_phy_exit(struct phy *_phy)
>   {
>   	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +
> +	if (phy->index == 0) {
> +		/* Disable pull-ups */
> +		sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
> +		sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
> +		data->phy0_init = false;
> +	}
>
>   	sun4i_usb_phy_passby(phy, 0);
>   	reset_control_assert(phy->reset);
> @@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
>   static int sun4i_usb_phy_power_on(struct phy *_phy)
>   {
>   	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> -	int ret = 0;
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +	int ret;
> +
> +	if (!phy->vbus || phy->regulator_on)
> +		return 0;
> +
> +	/* For phy0 only turn on Vbus if we don't have an ext. Vbus */
> +	if (phy->index == 0 && data->vbus_det)
> +		return 0;
>
> -	if (phy->vbus)
> -		ret = regulator_enable(phy->vbus);
> +	ret = regulator_enable(phy->vbus);
> +	if (ret)
> +		return ret;
>
> -	return ret;
> +	phy->regulator_on = true;
> +
> +	/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
> +	if (phy->index == 0 && data->phy0_poll)
> +		mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
> +
> +	return 0;
>   }
>
>   static int sun4i_usb_phy_power_off(struct phy *_phy)
>   {
>   	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +
> +	if (!phy->vbus || !phy->regulator_on)
> +		return 0;
>
> -	if (phy->vbus)
> -		regulator_disable(phy->vbus);
> +	regulator_disable(phy->vbus);
> +	phy->regulator_on = false;
> +
> +	/*
> +	 * phy0 vbus typically slowly discharges, sometimes this causes the
> +	 * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
> +	 */
> +	if (phy->index == 0 && !data->phy0_poll)
> +		mod_delayed_work(system_wq, &data->detect, POLL_TIME);
>
>   	return 0;
>   }
> @@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
>   	.owner		= THIS_MODULE,
>   };
>
> +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
> +{
> +	struct sun4i_usb_phy_data *data =
> +		container_of(work, struct sun4i_usb_phy_data, detect.work);
> +	struct phy *phy0 = data->phys[0].phy;
> +	int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
> +
> +	id_det = gpiod_get_value_cansleep(data->id_det_gpio);
> +	vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
> +
> +	mutex_lock(&phy0->mutex);
> +
> +	if (!data->phy0_init) {
> +		mutex_unlock(&phy0->mutex);
> +		return;
> +	}
> +
> +	if (id_det != data->id_det) {
> +		sun4i_usb_phy0_set_id_detect(phy0, id_det);
> +		data->id_det = id_det;
> +		id_notify = 1;
> +	}
> +
> +	if (vbus_det != data->vbus_det) {
> +		sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
> +		data->vbus_det = vbus_det;
> +		vbus_notify = 1;
> +	}
> +
> +	mutex_unlock(&phy0->mutex);
> +
> +	if (id_notify)
> +		extcon_set_cable_state(&data->extcon,
> +				       extcon_cable_name[EXTCON_USB_HOST],
> +				       !id_det);
> +
> +	if (vbus_notify)
> +		extcon_set_cable_state(&data->extcon,
> +				       extcon_cable_name[EXTCON_USB],
> +				       vbus_det);
> +
> +	if (data->phy0_poll)
> +		queue_delayed_work(system_wq, &data->detect, POLL_TIME);
> +}
> +
> +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
> +{
> +	struct sun4i_usb_phy_data *data = dev_id;
> +
> +	/* vbus or id changed, let the pins settle and then scan them */
> +	mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
> +
> +	return IRQ_HANDLED;
> +}
> +
>   static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>   					struct of_phandle_args *args)
>   {
> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>   	struct phy_provider *phy_provider;
>   	bool dedicated_clocks;
>   	struct resource *res;
> -	int i;
> +	int i, ret;
>
>   	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>   	if (!data)
>   		return -ENOMEM;
>
>   	mutex_init(&data->mutex);
> +	INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
> +	data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
> +	data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
> +	data->extcon_cable_names[2] = NULL;
> +	data->extcon.name = DRIVER_NAME;
> +	data->extcon.supported_cable = data->extcon_cable_names;
> +	data->extcon.dev.parent = dev;
>
>   	if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>   		data->num_phys = 2;
> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>   	if (IS_ERR(data->base))
>   		return PTR_ERR(data->base);
>
> +	data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
> +	if (IS_ERR(data->id_det_gpio)) {
> +		if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		data->id_det_gpio = NULL;
> +	}
> +
> +	data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
> +	if (IS_ERR(data->vbus_det_gpio)) {
> +		if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		data->vbus_det_gpio = NULL;
> +	}
> +
> +	/* We either want both gpio pins or neither (when in host mode) */
> +	if (!data->id_det_gpio != !data->vbus_det_gpio) {
> +		dev_err(dev, "failed to get id or vbus detect pin\n");
> +		return -ENODEV;
> +	}
> +
> +	if (data->id_det_gpio) {
> +		ret = devm_extcon_dev_register(dev, &data->extcon);
> +		if (ret) {
> +			dev_err(dev, "failed to register extcon: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
>   	for (i = 0; i < data->num_phys; i++) {
>   		struct sun4i_usb_phy *phy = data->phys + i;
>   		char name[16];
> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>   		phy_set_drvdata(phy->phy, &data->phys[i]);
>   	}
>
> +	data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
> +	data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
> +	if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
> +		data->phy0_poll = true;
> +
> +	if (data->id_det_irq >= 0) {
> +		ret = devm_request_irq(dev, data->id_det_irq,
> +				sun4i_usb_phy0_id_vbus_det_irq,
> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
> +				"usb0-id-det", data);
> +		if (ret) {
> +			dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (data->vbus_det_irq >= 0) {
> +		ret = devm_request_irq(dev, data->vbus_det_irq,
> +				sun4i_usb_phy0_id_vbus_det_irq,
> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
> +				"usb0-vbus-det", data);
> +		if (ret) {
> +			dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
>   	dev_set_drvdata(dev, data);
>   	phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>
>   	return PTR_ERR_OR_ZERO(phy_provider);
>   }
>
> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
> +
> +	if (data->id_det_irq >= 0)
> +		devm_free_irq(dev, data->id_det_irq, data);
> +	if (data->vbus_det_irq >= 0)
> +		devm_free_irq(dev, data->vbus_det_irq, data);
> +
> +	cancel_delayed_work_sync(&data->detect);
> +
> +	return 0;
> +}
> +
>   static const struct of_device_id sun4i_usb_phy_of_match[] = {
>   	{ .compatible = "allwinner,sun4i-a10-usb-phy" },
>   	{ .compatible = "allwinner,sun5i-a13-usb-phy" },
> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>
>   static struct platform_driver sun4i_usb_phy_driver = {
>   	.probe	= sun4i_usb_phy_probe,
> +	.remove	= sun4i_usb_phy_remove,
>   	.driver = {
>   		.of_match_table	= sun4i_usb_phy_of_match,
> -		.name  = "sun4i-usb-phy",
> +		.name  = DRIVER_NAME,
>   	}
>   };
>   module_platform_driver(sun4i_usb_phy_driver);
>

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

* Re: [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-11  5:48         ` Kishon Vijay Abraham I
@ 2015-06-11  7:56             ` Hans de Goede
  -1 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-11  7:56 UTC (permalink / raw)
  To: kishon-l0cyMroinI0, Felipe Balbi
  Cc: Maxime Ripard, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Chanwoo Choi

Hi,

On 11-06-15 07:48, Kishon Vijay Abraham I wrote:
> +Chanwoo
>
> Hi,
>
> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>> The usb0 phy is connected to an OTG controller, and as such needs some special
>> handling:
>>
>> 1) It allows explicit control over the pullups, enable these on phy_init and
>> disable them on phy_exit.
>>
>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>> these bits to the correct values for operating in host only mode when no
>> gpios are specified in the devicetree.
>>
>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>> device cable is plugged in, so when in dual role mode register an extcon.
>>
>> While updating the devicetree binding documentation also add documentation
>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>
>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> ---
>> Changes in v2:
>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>   has been moved to the phy-sun4i-usb driver and their status is exported
>>   through extcon for the sunxi-musb glue
>> Changes in v3:
>> -No changes
>> Changes in v4:
>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>   is present
>> ---
>>   .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>   drivers/phy/Kconfig                                |   1 +
>>   drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>   3 files changed, 281 insertions(+), 11 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> index 16528b9..557fa99 100644
>> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> @@ -23,6 +23,13 @@ Required properties:
>>     * "usb1_reset"
>>     * "usb2_reset" for sun4i, sun6i or sun7i
>>
>> +Optional properties:
>> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
>> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
>> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
>> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
>> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
>> +
>>   Example:
>>       usbphy: phy@0x01c13400 {
>>           #phy-cells = <1>;
>> @@ -32,6 +39,13 @@ Example:
>>           reg-names = "phy_ctrl", "pmu1", "pmu2";
>>           clocks = <&usb_clk 8>;
>>           clock-names = "usb_phy";
>> -        resets = <&usb_clk 1>, <&usb_clk 2>;
>> -        reset-names = "usb1_reset", "usb2_reset";
>> +        resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
>> +        reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
>> +        pinctrl-names = "default";
>> +        pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
>> +        usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
>> +        usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
>> +        usb0_vbus-supply = <&reg_usb0_vbus>;
>> +        usb1_vbus-supply = <&reg_usb1_vbus>;
>> +        usb2_vbus-supply = <&reg_usb2_vbus>;
>>       };
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index a53bd5b..4614fba 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>>       tristate "Allwinner sunxi SoC USB PHY driver"
>>       depends on ARCH_SUNXI && HAS_IOMEM && OF
>>       depends on RESET_CONTROLLER
>> +    select EXTCON
>
> Avoid using 'select' on visible Kconfig symbols.

Ok, I'll do a v5 changing this to a depends on.

> Also please split the patch to make the reviewing a bit easier.

There really is not much which can be split out, the iscr register
updating is only done from the id / vbus det scanning, so the 2 are
intertwined with each other.

Also I must say I'm unhappy with the fact that you request this now,
after this patch has been posted in its current form a long time
ago already. The changes in v3 and v4 were very minimal, the
patch has existed in this for since v2, so you've had a long long
time to request changes like this already.

In the mean time I've build a whole set of related changes (enabling
the use of otg on newer versions of the sunxi soc), so sorry I'm
not going to split this up now as that is going to be a big pain
with all the extra patches which are sitting on top, and as said
there really is not that much to split in the first place.

I'll send out a v5 of this patch together with a resend of all the
patches adding support for new models which sit on top of this one
(and which I've already send before).

Regards,

Hans



>
> Thanks
> Kishon
>>       select GENERIC_PHY
>>       help
>>         Enable this to support the transceiver that is part of Allwinner
>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>> index 91c5be4..b45d707 100644
>> --- a/drivers/phy/phy-sun4i-usb.c
>> +++ b/drivers/phy/phy-sun4i-usb.c
>> @@ -1,7 +1,7 @@
>>   /*
>>    * Allwinner sun4i USB phy driver
>>    *
>> - * Copyright (C) 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>    *
>>    * Based on code from
>>    * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>> @@ -23,17 +23,23 @@
>>
>>   #include <linux/clk.h>
>>   #include <linux/err.h>
>> +#include <linux/extcon.h>
>>   #include <linux/io.h>
>> +#include <linux/interrupt.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_gpio.h>
>>   #include <linux/phy/phy.h>
>>   #include <linux/phy/phy-sun4i-usb.h>
>>   #include <linux/platform_device.h>
>>   #include <linux/regulator/consumer.h>
>>   #include <linux/reset.h>
>> +#include <linux/workqueue.h>
>> +
>> +#define DRIVER_NAME "sun4i-usb-phy"
>>
>>   #define REG_ISCR            0x00
>>   #define REG_PHYCTL            0x04
>> @@ -47,6 +53,17 @@
>>   #define SUNXI_AHB_INCRX_ALIGN_EN    BIT(8)
>>   #define SUNXI_ULPI_BYPASS_EN        BIT(0)
>>
>> +/* ISCR, Interface Status and Control bits */
>> +#define ISCR_ID_PULLUP_EN        (1 << 17)
>> +#define ISCR_DPDM_PULLUP_EN    (1 << 16)
>> +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
>> +#define ISCR_FORCE_ID_MASK    (3 << 14)
>> +#define ISCR_FORCE_ID_LOW        (2 << 14)
>> +#define ISCR_FORCE_ID_HIGH    (3 << 14)
>> +#define ISCR_FORCE_VBUS_MASK    (3 << 12)
>> +#define ISCR_FORCE_VBUS_LOW    (2 << 12)
>> +#define ISCR_FORCE_VBUS_HIGH    (3 << 12)
>> +
>>   /* Common Control Bits for Both PHYs */
>>   #define PHY_PLL_BW            0x03
>>   #define PHY_RES45_CAL_EN        0x0c
>> @@ -63,6 +80,13 @@
>>
>>   #define MAX_PHYS            3
>>
>> +/*
>> + * Note do not raise the debounce time, we must report Vusb high within 100ms
>> + * otherwise we get Vbus errors
>> + */
>> +#define DEBOUNCE_TIME            msecs_to_jiffies(50)
>> +#define POLL_TIME            msecs_to_jiffies(250)
>> +
>>   struct sun4i_usb_phy_data {
>>       void __iomem *base;
>>       struct mutex mutex;
>> @@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
>>           struct regulator *vbus;
>>           struct reset_control *reset;
>>           struct clk *clk;
>> +        bool regulator_on;
>>           int index;
>>       } phys[MAX_PHYS];
>> +    /* phy0 / otg related variables */
>> +    struct extcon_dev extcon;
>> +    const char *extcon_cable_names[3];
>> +    bool phy0_init;
>> +    bool phy0_poll;
>> +    struct gpio_desc *id_det_gpio;
>> +    struct gpio_desc *vbus_det_gpio;
>> +    int id_det_irq;
>> +    int vbus_det_irq;
>> +    int id_det;
>> +    int vbus_det;
>> +    struct delayed_work detect;
>>   };
>>
>>   #define to_sun4i_usb_phy_data(phy) \
>>       container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>>
>> +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
>> +{
>> +    struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +    u32 iscr;
>> +
>> +    iscr = readl(data->base + REG_ISCR);
>> +    iscr &= ~clr;
>> +    iscr |= set;
>> +    writel(iscr, data->base + REG_ISCR);
>> +}
>> +
>> +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
>> +{
>> +    if (val)
>> +        val = ISCR_FORCE_ID_HIGH;
>> +    else
>> +        val = ISCR_FORCE_ID_LOW;
>> +
>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
>> +}
>> +
>> +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
>> +{
>> +    if (val)
>> +        val = ISCR_FORCE_VBUS_HIGH;
>> +    else
>> +        val = ISCR_FORCE_VBUS_LOW;
>> +
>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
>> +}
>> +
>>   static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>>                   int len)
>>   {
>> @@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
>>
>>       sun4i_usb_phy_passby(phy, 1);
>>
>> +    if (phy->index == 0) {
>> +        data->phy0_init = true;
>> +
>> +        /* Enable pull-ups */
>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
>> +
>> +        if (data->id_det_gpio) {
>> +            /* OTG mode, force ISCR and cable state updates */
>> +            data->id_det = -1;
>> +            data->vbus_det = -1;
>> +            queue_delayed_work(system_wq, &data->detect, 0);
>> +        } else {
>> +            /* Host only mode */
>> +            sun4i_usb_phy0_set_id_detect(_phy, 0);
>> +            sun4i_usb_phy0_set_vbus_detect(_phy, 1);
>> +        }
>> +    }
>> +
>>       return 0;
>>   }
>>
>>   static int sun4i_usb_phy_exit(struct phy *_phy)
>>   {
>>       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> +    if (phy->index == 0) {
>> +        /* Disable pull-ups */
>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
>> +        data->phy0_init = false;
>> +    }
>>
>>       sun4i_usb_phy_passby(phy, 0);
>>       reset_control_assert(phy->reset);
>> @@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
>>   static int sun4i_usb_phy_power_on(struct phy *_phy)
>>   {
>>       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> -    int ret = 0;
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +    int ret;
>> +
>> +    if (!phy->vbus || phy->regulator_on)
>> +        return 0;
>> +
>> +    /* For phy0 only turn on Vbus if we don't have an ext. Vbus */
>> +    if (phy->index == 0 && data->vbus_det)
>> +        return 0;
>>
>> -    if (phy->vbus)
>> -        ret = regulator_enable(phy->vbus);
>> +    ret = regulator_enable(phy->vbus);
>> +    if (ret)
>> +        return ret;
>>
>> -    return ret;
>> +    phy->regulator_on = true;
>> +
>> +    /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
>> +    if (phy->index == 0 && data->phy0_poll)
>> +        mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>> +
>> +    return 0;
>>   }
>>
>>   static int sun4i_usb_phy_power_off(struct phy *_phy)
>>   {
>>       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> +    if (!phy->vbus || !phy->regulator_on)
>> +        return 0;
>>
>> -    if (phy->vbus)
>> -        regulator_disable(phy->vbus);
>> +    regulator_disable(phy->vbus);
>> +    phy->regulator_on = false;
>> +
>> +    /*
>> +     * phy0 vbus typically slowly discharges, sometimes this causes the
>> +     * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
>> +     */
>> +    if (phy->index == 0 && !data->phy0_poll)
>> +        mod_delayed_work(system_wq, &data->detect, POLL_TIME);
>>
>>       return 0;
>>   }
>> @@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
>>       .owner        = THIS_MODULE,
>>   };
>>
>> +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
>> +{
>> +    struct sun4i_usb_phy_data *data =
>> +        container_of(work, struct sun4i_usb_phy_data, detect.work);
>> +    struct phy *phy0 = data->phys[0].phy;
>> +    int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
>> +
>> +    id_det = gpiod_get_value_cansleep(data->id_det_gpio);
>> +    vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
>> +
>> +    mutex_lock(&phy0->mutex);
>> +
>> +    if (!data->phy0_init) {
>> +        mutex_unlock(&phy0->mutex);
>> +        return;
>> +    }
>> +
>> +    if (id_det != data->id_det) {
>> +        sun4i_usb_phy0_set_id_detect(phy0, id_det);
>> +        data->id_det = id_det;
>> +        id_notify = 1;
>> +    }
>> +
>> +    if (vbus_det != data->vbus_det) {
>> +        sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
>> +        data->vbus_det = vbus_det;
>> +        vbus_notify = 1;
>> +    }
>> +
>> +    mutex_unlock(&phy0->mutex);
>> +
>> +    if (id_notify)
>> +        extcon_set_cable_state(&data->extcon,
>> +                       extcon_cable_name[EXTCON_USB_HOST],
>> +                       !id_det);
>> +
>> +    if (vbus_notify)
>> +        extcon_set_cable_state(&data->extcon,
>> +                       extcon_cable_name[EXTCON_USB],
>> +                       vbus_det);
>> +
>> +    if (data->phy0_poll)
>> +        queue_delayed_work(system_wq, &data->detect, POLL_TIME);
>> +}
>> +
>> +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
>> +{
>> +    struct sun4i_usb_phy_data *data = dev_id;
>> +
>> +    /* vbus or id changed, let the pins settle and then scan them */
>> +    mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>>   static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>                       struct of_phandle_args *args)
>>   {
>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>       struct phy_provider *phy_provider;
>>       bool dedicated_clocks;
>>       struct resource *res;
>> -    int i;
>> +    int i, ret;
>>
>>       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>       if (!data)
>>           return -ENOMEM;
>>
>>       mutex_init(&data->mutex);
>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>> +    data->extcon_cable_names[2] = NULL;
>> +    data->extcon.name = DRIVER_NAME;
>> +    data->extcon.supported_cable = data->extcon_cable_names;
>> +    data->extcon.dev.parent = dev;
>>
>>       if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>           data->num_phys = 2;
>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>       if (IS_ERR(data->base))
>>           return PTR_ERR(data->base);
>>
>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>> +    if (IS_ERR(data->id_det_gpio)) {
>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>> +            return -EPROBE_DEFER;
>> +        data->id_det_gpio = NULL;
>> +    }
>> +
>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>> +    if (IS_ERR(data->vbus_det_gpio)) {
>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>> +            return -EPROBE_DEFER;
>> +        data->vbus_det_gpio = NULL;
>> +    }
>> +
>> +    /* We either want both gpio pins or neither (when in host mode) */
>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>> +        return -ENODEV;
>> +    }
>> +
>> +    if (data->id_det_gpio) {
>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>> +        if (ret) {
>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>>       for (i = 0; i < data->num_phys; i++) {
>>           struct sun4i_usb_phy *phy = data->phys + i;
>>           char name[16];
>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>           phy_set_drvdata(phy->phy, &data->phys[i]);
>>       }
>>
>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>> +        data->phy0_poll = true;
>> +
>> +    if (data->id_det_irq >= 0) {
>> +        ret = devm_request_irq(dev, data->id_det_irq,
>> +                sun4i_usb_phy0_id_vbus_det_irq,
>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +                "usb0-id-det", data);
>> +        if (ret) {
>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>> +    if (data->vbus_det_irq >= 0) {
>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>> +                sun4i_usb_phy0_id_vbus_det_irq,
>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +                "usb0-vbus-det", data);
>> +        if (ret) {
>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>>       dev_set_drvdata(dev, data);
>>       phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>
>>       return PTR_ERR_OR_ZERO(phy_provider);
>>   }
>>
>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>> +{
>> +    struct device *dev = &pdev->dev;
>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>> +
>> +    if (data->id_det_irq >= 0)
>> +        devm_free_irq(dev, data->id_det_irq, data);
>> +    if (data->vbus_det_irq >= 0)
>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>> +
>> +    cancel_delayed_work_sync(&data->detect);
>> +
>> +    return 0;
>> +}
>> +
>>   static const struct of_device_id sun4i_usb_phy_of_match[] = {
>>       { .compatible = "allwinner,sun4i-a10-usb-phy" },
>>       { .compatible = "allwinner,sun5i-a13-usb-phy" },
>> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>>
>>   static struct platform_driver sun4i_usb_phy_driver = {
>>       .probe    = sun4i_usb_phy_probe,
>> +    .remove    = sun4i_usb_phy_remove,
>>       .driver = {
>>           .of_match_table    = sun4i_usb_phy_of_match,
>> -        .name  = "sun4i-usb-phy",
>> +        .name  = DRIVER_NAME,
>>       }
>>   };
>>   module_platform_driver(sun4i_usb_phy_driver);
>>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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	[flat|nested] 57+ messages in thread

* [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11  7:56             ` Hans de Goede
  0 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-11  7:56 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 11-06-15 07:48, Kishon Vijay Abraham I wrote:
> +Chanwoo
>
> Hi,
>
> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>> The usb0 phy is connected to an OTG controller, and as such needs some special
>> handling:
>>
>> 1) It allows explicit control over the pullups, enable these on phy_init and
>> disable them on phy_exit.
>>
>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>> these bits to the correct values for operating in host only mode when no
>> gpios are specified in the devicetree.
>>
>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>> device cable is plugged in, so when in dual role mode register an extcon.
>>
>> While updating the devicetree binding documentation also add documentation
>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>
>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> ---
>> Changes in v2:
>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>   has been moved to the phy-sun4i-usb driver and their status is exported
>>   through extcon for the sunxi-musb glue
>> Changes in v3:
>> -No changes
>> Changes in v4:
>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>   is present
>> ---
>>   .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>   drivers/phy/Kconfig                                |   1 +
>>   drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>   3 files changed, 281 insertions(+), 11 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> index 16528b9..557fa99 100644
>> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> @@ -23,6 +23,13 @@ Required properties:
>>     * "usb1_reset"
>>     * "usb2_reset" for sun4i, sun6i or sun7i
>>
>> +Optional properties:
>> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
>> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
>> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
>> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
>> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
>> +
>>   Example:
>>       usbphy: phy at 0x01c13400 {
>>           #phy-cells = <1>;
>> @@ -32,6 +39,13 @@ Example:
>>           reg-names = "phy_ctrl", "pmu1", "pmu2";
>>           clocks = <&usb_clk 8>;
>>           clock-names = "usb_phy";
>> -        resets = <&usb_clk 1>, <&usb_clk 2>;
>> -        reset-names = "usb1_reset", "usb2_reset";
>> +        resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
>> +        reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
>> +        pinctrl-names = "default";
>> +        pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
>> +        usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
>> +        usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
>> +        usb0_vbus-supply = <&reg_usb0_vbus>;
>> +        usb1_vbus-supply = <&reg_usb1_vbus>;
>> +        usb2_vbus-supply = <&reg_usb2_vbus>;
>>       };
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index a53bd5b..4614fba 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>>       tristate "Allwinner sunxi SoC USB PHY driver"
>>       depends on ARCH_SUNXI && HAS_IOMEM && OF
>>       depends on RESET_CONTROLLER
>> +    select EXTCON
>
> Avoid using 'select' on visible Kconfig symbols.

Ok, I'll do a v5 changing this to a depends on.

> Also please split the patch to make the reviewing a bit easier.

There really is not much which can be split out, the iscr register
updating is only done from the id / vbus det scanning, so the 2 are
intertwined with each other.

Also I must say I'm unhappy with the fact that you request this now,
after this patch has been posted in its current form a long time
ago already. The changes in v3 and v4 were very minimal, the
patch has existed in this for since v2, so you've had a long long
time to request changes like this already.

In the mean time I've build a whole set of related changes (enabling
the use of otg on newer versions of the sunxi soc), so sorry I'm
not going to split this up now as that is going to be a big pain
with all the extra patches which are sitting on top, and as said
there really is not that much to split in the first place.

I'll send out a v5 of this patch together with a resend of all the
patches adding support for new models which sit on top of this one
(and which I've already send before).

Regards,

Hans



>
> Thanks
> Kishon
>>       select GENERIC_PHY
>>       help
>>         Enable this to support the transceiver that is part of Allwinner
>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>> index 91c5be4..b45d707 100644
>> --- a/drivers/phy/phy-sun4i-usb.c
>> +++ b/drivers/phy/phy-sun4i-usb.c
>> @@ -1,7 +1,7 @@
>>   /*
>>    * Allwinner sun4i USB phy driver
>>    *
>> - * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
>> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede@redhat.com>
>>    *
>>    * Based on code from
>>    * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>> @@ -23,17 +23,23 @@
>>
>>   #include <linux/clk.h>
>>   #include <linux/err.h>
>> +#include <linux/extcon.h>
>>   #include <linux/io.h>
>> +#include <linux/interrupt.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_gpio.h>
>>   #include <linux/phy/phy.h>
>>   #include <linux/phy/phy-sun4i-usb.h>
>>   #include <linux/platform_device.h>
>>   #include <linux/regulator/consumer.h>
>>   #include <linux/reset.h>
>> +#include <linux/workqueue.h>
>> +
>> +#define DRIVER_NAME "sun4i-usb-phy"
>>
>>   #define REG_ISCR            0x00
>>   #define REG_PHYCTL            0x04
>> @@ -47,6 +53,17 @@
>>   #define SUNXI_AHB_INCRX_ALIGN_EN    BIT(8)
>>   #define SUNXI_ULPI_BYPASS_EN        BIT(0)
>>
>> +/* ISCR, Interface Status and Control bits */
>> +#define ISCR_ID_PULLUP_EN        (1 << 17)
>> +#define ISCR_DPDM_PULLUP_EN    (1 << 16)
>> +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
>> +#define ISCR_FORCE_ID_MASK    (3 << 14)
>> +#define ISCR_FORCE_ID_LOW        (2 << 14)
>> +#define ISCR_FORCE_ID_HIGH    (3 << 14)
>> +#define ISCR_FORCE_VBUS_MASK    (3 << 12)
>> +#define ISCR_FORCE_VBUS_LOW    (2 << 12)
>> +#define ISCR_FORCE_VBUS_HIGH    (3 << 12)
>> +
>>   /* Common Control Bits for Both PHYs */
>>   #define PHY_PLL_BW            0x03
>>   #define PHY_RES45_CAL_EN        0x0c
>> @@ -63,6 +80,13 @@
>>
>>   #define MAX_PHYS            3
>>
>> +/*
>> + * Note do not raise the debounce time, we must report Vusb high within 100ms
>> + * otherwise we get Vbus errors
>> + */
>> +#define DEBOUNCE_TIME            msecs_to_jiffies(50)
>> +#define POLL_TIME            msecs_to_jiffies(250)
>> +
>>   struct sun4i_usb_phy_data {
>>       void __iomem *base;
>>       struct mutex mutex;
>> @@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
>>           struct regulator *vbus;
>>           struct reset_control *reset;
>>           struct clk *clk;
>> +        bool regulator_on;
>>           int index;
>>       } phys[MAX_PHYS];
>> +    /* phy0 / otg related variables */
>> +    struct extcon_dev extcon;
>> +    const char *extcon_cable_names[3];
>> +    bool phy0_init;
>> +    bool phy0_poll;
>> +    struct gpio_desc *id_det_gpio;
>> +    struct gpio_desc *vbus_det_gpio;
>> +    int id_det_irq;
>> +    int vbus_det_irq;
>> +    int id_det;
>> +    int vbus_det;
>> +    struct delayed_work detect;
>>   };
>>
>>   #define to_sun4i_usb_phy_data(phy) \
>>       container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>>
>> +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
>> +{
>> +    struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +    u32 iscr;
>> +
>> +    iscr = readl(data->base + REG_ISCR);
>> +    iscr &= ~clr;
>> +    iscr |= set;
>> +    writel(iscr, data->base + REG_ISCR);
>> +}
>> +
>> +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
>> +{
>> +    if (val)
>> +        val = ISCR_FORCE_ID_HIGH;
>> +    else
>> +        val = ISCR_FORCE_ID_LOW;
>> +
>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
>> +}
>> +
>> +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
>> +{
>> +    if (val)
>> +        val = ISCR_FORCE_VBUS_HIGH;
>> +    else
>> +        val = ISCR_FORCE_VBUS_LOW;
>> +
>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
>> +}
>> +
>>   static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>>                   int len)
>>   {
>> @@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
>>
>>       sun4i_usb_phy_passby(phy, 1);
>>
>> +    if (phy->index == 0) {
>> +        data->phy0_init = true;
>> +
>> +        /* Enable pull-ups */
>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
>> +
>> +        if (data->id_det_gpio) {
>> +            /* OTG mode, force ISCR and cable state updates */
>> +            data->id_det = -1;
>> +            data->vbus_det = -1;
>> +            queue_delayed_work(system_wq, &data->detect, 0);
>> +        } else {
>> +            /* Host only mode */
>> +            sun4i_usb_phy0_set_id_detect(_phy, 0);
>> +            sun4i_usb_phy0_set_vbus_detect(_phy, 1);
>> +        }
>> +    }
>> +
>>       return 0;
>>   }
>>
>>   static int sun4i_usb_phy_exit(struct phy *_phy)
>>   {
>>       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> +    if (phy->index == 0) {
>> +        /* Disable pull-ups */
>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
>> +        data->phy0_init = false;
>> +    }
>>
>>       sun4i_usb_phy_passby(phy, 0);
>>       reset_control_assert(phy->reset);
>> @@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
>>   static int sun4i_usb_phy_power_on(struct phy *_phy)
>>   {
>>       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> -    int ret = 0;
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +    int ret;
>> +
>> +    if (!phy->vbus || phy->regulator_on)
>> +        return 0;
>> +
>> +    /* For phy0 only turn on Vbus if we don't have an ext. Vbus */
>> +    if (phy->index == 0 && data->vbus_det)
>> +        return 0;
>>
>> -    if (phy->vbus)
>> -        ret = regulator_enable(phy->vbus);
>> +    ret = regulator_enable(phy->vbus);
>> +    if (ret)
>> +        return ret;
>>
>> -    return ret;
>> +    phy->regulator_on = true;
>> +
>> +    /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
>> +    if (phy->index == 0 && data->phy0_poll)
>> +        mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>> +
>> +    return 0;
>>   }
>>
>>   static int sun4i_usb_phy_power_off(struct phy *_phy)
>>   {
>>       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> +    if (!phy->vbus || !phy->regulator_on)
>> +        return 0;
>>
>> -    if (phy->vbus)
>> -        regulator_disable(phy->vbus);
>> +    regulator_disable(phy->vbus);
>> +    phy->regulator_on = false;
>> +
>> +    /*
>> +     * phy0 vbus typically slowly discharges, sometimes this causes the
>> +     * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
>> +     */
>> +    if (phy->index == 0 && !data->phy0_poll)
>> +        mod_delayed_work(system_wq, &data->detect, POLL_TIME);
>>
>>       return 0;
>>   }
>> @@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
>>       .owner        = THIS_MODULE,
>>   };
>>
>> +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
>> +{
>> +    struct sun4i_usb_phy_data *data =
>> +        container_of(work, struct sun4i_usb_phy_data, detect.work);
>> +    struct phy *phy0 = data->phys[0].phy;
>> +    int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
>> +
>> +    id_det = gpiod_get_value_cansleep(data->id_det_gpio);
>> +    vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
>> +
>> +    mutex_lock(&phy0->mutex);
>> +
>> +    if (!data->phy0_init) {
>> +        mutex_unlock(&phy0->mutex);
>> +        return;
>> +    }
>> +
>> +    if (id_det != data->id_det) {
>> +        sun4i_usb_phy0_set_id_detect(phy0, id_det);
>> +        data->id_det = id_det;
>> +        id_notify = 1;
>> +    }
>> +
>> +    if (vbus_det != data->vbus_det) {
>> +        sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
>> +        data->vbus_det = vbus_det;
>> +        vbus_notify = 1;
>> +    }
>> +
>> +    mutex_unlock(&phy0->mutex);
>> +
>> +    if (id_notify)
>> +        extcon_set_cable_state(&data->extcon,
>> +                       extcon_cable_name[EXTCON_USB_HOST],
>> +                       !id_det);
>> +
>> +    if (vbus_notify)
>> +        extcon_set_cable_state(&data->extcon,
>> +                       extcon_cable_name[EXTCON_USB],
>> +                       vbus_det);
>> +
>> +    if (data->phy0_poll)
>> +        queue_delayed_work(system_wq, &data->detect, POLL_TIME);
>> +}
>> +
>> +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
>> +{
>> +    struct sun4i_usb_phy_data *data = dev_id;
>> +
>> +    /* vbus or id changed, let the pins settle and then scan them */
>> +    mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>>   static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>                       struct of_phandle_args *args)
>>   {
>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>       struct phy_provider *phy_provider;
>>       bool dedicated_clocks;
>>       struct resource *res;
>> -    int i;
>> +    int i, ret;
>>
>>       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>       if (!data)
>>           return -ENOMEM;
>>
>>       mutex_init(&data->mutex);
>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>> +    data->extcon_cable_names[2] = NULL;
>> +    data->extcon.name = DRIVER_NAME;
>> +    data->extcon.supported_cable = data->extcon_cable_names;
>> +    data->extcon.dev.parent = dev;
>>
>>       if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>           data->num_phys = 2;
>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>       if (IS_ERR(data->base))
>>           return PTR_ERR(data->base);
>>
>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>> +    if (IS_ERR(data->id_det_gpio)) {
>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>> +            return -EPROBE_DEFER;
>> +        data->id_det_gpio = NULL;
>> +    }
>> +
>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>> +    if (IS_ERR(data->vbus_det_gpio)) {
>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>> +            return -EPROBE_DEFER;
>> +        data->vbus_det_gpio = NULL;
>> +    }
>> +
>> +    /* We either want both gpio pins or neither (when in host mode) */
>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>> +        return -ENODEV;
>> +    }
>> +
>> +    if (data->id_det_gpio) {
>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>> +        if (ret) {
>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>>       for (i = 0; i < data->num_phys; i++) {
>>           struct sun4i_usb_phy *phy = data->phys + i;
>>           char name[16];
>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>           phy_set_drvdata(phy->phy, &data->phys[i]);
>>       }
>>
>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>> +        data->phy0_poll = true;
>> +
>> +    if (data->id_det_irq >= 0) {
>> +        ret = devm_request_irq(dev, data->id_det_irq,
>> +                sun4i_usb_phy0_id_vbus_det_irq,
>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +                "usb0-id-det", data);
>> +        if (ret) {
>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>> +    if (data->vbus_det_irq >= 0) {
>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>> +                sun4i_usb_phy0_id_vbus_det_irq,
>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +                "usb0-vbus-det", data);
>> +        if (ret) {
>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>>       dev_set_drvdata(dev, data);
>>       phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>
>>       return PTR_ERR_OR_ZERO(phy_provider);
>>   }
>>
>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>> +{
>> +    struct device *dev = &pdev->dev;
>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>> +
>> +    if (data->id_det_irq >= 0)
>> +        devm_free_irq(dev, data->id_det_irq, data);
>> +    if (data->vbus_det_irq >= 0)
>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>> +
>> +    cancel_delayed_work_sync(&data->detect);
>> +
>> +    return 0;
>> +}
>> +
>>   static const struct of_device_id sun4i_usb_phy_of_match[] = {
>>       { .compatible = "allwinner,sun4i-a10-usb-phy" },
>>       { .compatible = "allwinner,sun5i-a13-usb-phy" },
>> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>>
>>   static struct platform_driver sun4i_usb_phy_driver = {
>>       .probe    = sun4i_usb_phy_probe,
>> +    .remove    = sun4i_usb_phy_remove,
>>       .driver = {
>>           .of_match_table    = sun4i_usb_phy_of_match,
>> -        .name  = "sun4i-usb-phy",
>> +        .name  = DRIVER_NAME,
>>       }
>>   };
>>   module_platform_driver(sun4i_usb_phy_driver);
>>
>

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

* Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-11  5:48         ` Kishon Vijay Abraham I
@ 2015-06-11  8:07             ` Chanwoo Choi
  -1 siblings, 0 replies; 57+ messages in thread
From: Chanwoo Choi @ 2015-06-11  8:07 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Hans de Goede, Felipe Balbi, Maxime Ripard, Chen-Yu Tsai,
	Roman Byshko, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi Hans,

I add the comment about extcon-related code.

Firstly,
I'd like you to implment the extcon driver for phy-sun4i-usb device
in drivers/extcon/ directoryby using MFD because there are both extcon
provider driver and extcon client driver. I think that all extcon
provider driver better to be included in drivers/extcon/ directory.
extcon_set_cable_state() function should be handled in extcon provider
driver which is incluced in drivers/extcon/ directory.

On 06/11/2015 02:48 PM, Kishon Vijay Abraham I wrote:
> +Chanwoo
> 
> Hi,
> 
> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>> The usb0 phy is connected to an OTG controller, and as such needs some special
>> handling:
>>
>> 1) It allows explicit control over the pullups, enable these on phy_init and
>> disable them on phy_exit.
>>
>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>> these bits to the correct values for operating in host only mode when no
>> gpios are specified in the devicetree.
>>
>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>> device cable is plugged in, so when in dual role mode register an extcon.
>>
>> While updating the devicetree binding documentation also add documentation
>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>
>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> ---
>> Changes in v2:
>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>   has been moved to the phy-sun4i-usb driver and their status is exported
>>   through extcon for the sunxi-musb glue
>> Changes in v3:
>> -No changes
>> Changes in v4:
>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>   is present
>> ---
>>   .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>   drivers/phy/Kconfig                                |   1 +
>>   drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>   3 files changed, 281 insertions(+), 11 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> index 16528b9..557fa99 100644
>> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> @@ -23,6 +23,13 @@ Required properties:
>>     * "usb1_reset"
>>     * "usb2_reset" for sun4i, sun6i or sun7i
>>
>> +Optional properties:
>> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
>> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
>> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
>> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
>> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
>> +
>>   Example:
>>       usbphy: phy@0x01c13400 {
>>           #phy-cells = <1>;
>> @@ -32,6 +39,13 @@ Example:
>>           reg-names = "phy_ctrl", "pmu1", "pmu2";
>>           clocks = <&usb_clk 8>;
>>           clock-names = "usb_phy";
>> -        resets = <&usb_clk 1>, <&usb_clk 2>;
>> -        reset-names = "usb1_reset", "usb2_reset";
>> +        resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
>> +        reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
>> +        pinctrl-names = "default";
>> +        pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
>> +        usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
>> +        usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
>> +        usb0_vbus-supply = <&reg_usb0_vbus>;
>> +        usb1_vbus-supply = <&reg_usb1_vbus>;
>> +        usb2_vbus-supply = <&reg_usb2_vbus>;
>>       };
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index a53bd5b..4614fba 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>>       tristate "Allwinner sunxi SoC USB PHY driver"
>>       depends on ARCH_SUNXI && HAS_IOMEM && OF
>>       depends on RESET_CONTROLLER
>> +    select EXTCON
> 
> Avoid using 'select' on visible Kconfig symbols.
> 
> Also please split the patch to make the reviewing a bit easier.
> 
> Thanks
> Kishon
>>       select GENERIC_PHY
>>       help
>>         Enable this to support the transceiver that is part of Allwinner
>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>> index 91c5be4..b45d707 100644
>> --- a/drivers/phy/phy-sun4i-usb.c
>> +++ b/drivers/phy/phy-sun4i-usb.c
>> @@ -1,7 +1,7 @@
>>   /*
>>    * Allwinner sun4i USB phy driver
>>    *
>> - * Copyright (C) 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>    *
>>    * Based on code from
>>    * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>> @@ -23,17 +23,23 @@
>>
>>   #include <linux/clk.h>
>>   #include <linux/err.h>
>> +#include <linux/extcon.h>
>>   #include <linux/io.h>
>> +#include <linux/interrupt.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_gpio.h>
>>   #include <linux/phy/phy.h>
>>   #include <linux/phy/phy-sun4i-usb.h>
>>   #include <linux/platform_device.h>
>>   #include <linux/regulator/consumer.h>
>>   #include <linux/reset.h>
>> +#include <linux/workqueue.h>
>> +
>> +#define DRIVER_NAME "sun4i-usb-phy"
>>
>>   #define REG_ISCR            0x00
>>   #define REG_PHYCTL            0x04
>> @@ -47,6 +53,17 @@
>>   #define SUNXI_AHB_INCRX_ALIGN_EN    BIT(8)
>>   #define SUNXI_ULPI_BYPASS_EN        BIT(0)
>>
>> +/* ISCR, Interface Status and Control bits */
>> +#define ISCR_ID_PULLUP_EN        (1 << 17)
>> +#define ISCR_DPDM_PULLUP_EN    (1 << 16)
>> +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
>> +#define ISCR_FORCE_ID_MASK    (3 << 14)
>> +#define ISCR_FORCE_ID_LOW        (2 << 14)
>> +#define ISCR_FORCE_ID_HIGH    (3 << 14)
>> +#define ISCR_FORCE_VBUS_MASK    (3 << 12)
>> +#define ISCR_FORCE_VBUS_LOW    (2 << 12)
>> +#define ISCR_FORCE_VBUS_HIGH    (3 << 12)
>> +
>>   /* Common Control Bits for Both PHYs */
>>   #define PHY_PLL_BW            0x03
>>   #define PHY_RES45_CAL_EN        0x0c
>> @@ -63,6 +80,13 @@
>>
>>   #define MAX_PHYS            3
>>
>> +/*
>> + * Note do not raise the debounce time, we must report Vusb high within 100ms
>> + * otherwise we get Vbus errors
>> + */
>> +#define DEBOUNCE_TIME            msecs_to_jiffies(50)
>> +#define POLL_TIME            msecs_to_jiffies(250)
>> +
>>   struct sun4i_usb_phy_data {
>>       void __iomem *base;
>>       struct mutex mutex;
>> @@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
>>           struct regulator *vbus;
>>           struct reset_control *reset;
>>           struct clk *clk;
>> +        bool regulator_on;
>>           int index;
>>       } phys[MAX_PHYS];
>> +    /* phy0 / otg related variables */
>> +    struct extcon_dev extcon;
>> +    const char *extcon_cable_names[3];

The latest extcon use the unique id to indicate the each external connector
instead of legacy string. The related patch[1] and patch[2] will be merged on v4.2-rc1.

[1] extcon: Use the unique id for external connector instead of string
- http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=2a9de9c0f08d61fbe3764a21d22d0b72df97d6ae
[2] extcon: Redefine the unique id of supported external connectors without 'enum extcon' type
- http://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/commit/?h=extcon-next-v4.3&id=bbd8d8b4244bb2799143c959fde57b1f034ec838

For exmaple in drivers/extcon/extcon-usb-gpio.c:
Maybe you can add the similiar array which include the supported external connector.

static const unsigned int usb_extcon_cable[] = {
	EXTCON_USB,
	EXTCON_USB_HOST,
	EXTCON_NONE,
};

>> +    bool phy0_init;
>> +    bool phy0_poll;
>> +    struct gpio_desc *id_det_gpio;
>> +    struct gpio_desc *vbus_det_gpio;
>> +    int id_det_irq;
>> +    int vbus_det_irq;
>> +    int id_det;
>> +    int vbus_det;
>> +    struct delayed_work detect;
>>   };
>>
>>   #define to_sun4i_usb_phy_data(phy) \
>>       container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>>
>> +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
>> +{
>> +    struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +    u32 iscr;
>> +
>> +    iscr = readl(data->base + REG_ISCR);
>> +    iscr &= ~clr;
>> +    iscr |= set;
>> +    writel(iscr, data->base + REG_ISCR);
>> +}
>> +
>> +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
>> +{
>> +    if (val)
>> +        val = ISCR_FORCE_ID_HIGH;
>> +    else
>> +        val = ISCR_FORCE_ID_LOW;
>> +
>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
>> +}
>> +
>> +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
>> +{
>> +    if (val)
>> +        val = ISCR_FORCE_VBUS_HIGH;
>> +    else
>> +        val = ISCR_FORCE_VBUS_LOW;
>> +
>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
>> +}
>> +
>>   static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>>                   int len)
>>   {
>> @@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
>>
>>       sun4i_usb_phy_passby(phy, 1);
>>
>> +    if (phy->index == 0) {
>> +        data->phy0_init = true;
>> +
>> +        /* Enable pull-ups */
>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
>> +
>> +        if (data->id_det_gpio) {
>> +            /* OTG mode, force ISCR and cable state updates */
>> +            data->id_det = -1;
>> +            data->vbus_det = -1;
>> +            queue_delayed_work(system_wq, &data->detect, 0);
>> +        } else {
>> +            /* Host only mode */
>> +            sun4i_usb_phy0_set_id_detect(_phy, 0);
>> +            sun4i_usb_phy0_set_vbus_detect(_phy, 1);
>> +        }
>> +    }
>> +
>>       return 0;
>>   }
>>
>>   static int sun4i_usb_phy_exit(struct phy *_phy)
>>   {
>>       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> +    if (phy->index == 0) {
>> +        /* Disable pull-ups */
>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
>> +        data->phy0_init = false;
>> +    }
>>
>>       sun4i_usb_phy_passby(phy, 0);
>>       reset_control_assert(phy->reset);
>> @@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
>>   static int sun4i_usb_phy_power_on(struct phy *_phy)
>>   {
>>       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> -    int ret = 0;
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +    int ret;
>> +
>> +    if (!phy->vbus || phy->regulator_on)
>> +        return 0;
>> +
>> +    /* For phy0 only turn on Vbus if we don't have an ext. Vbus */
>> +    if (phy->index == 0 && data->vbus_det)
>> +        return 0;
>>
>> -    if (phy->vbus)
>> -        ret = regulator_enable(phy->vbus);
>> +    ret = regulator_enable(phy->vbus);
>> +    if (ret)
>> +        return ret;
>>
>> -    return ret;
>> +    phy->regulator_on = true;
>> +
>> +    /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
>> +    if (phy->index == 0 && data->phy0_poll)
>> +        mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>> +
>> +    return 0;
>>   }
>>
>>   static int sun4i_usb_phy_power_off(struct phy *_phy)
>>   {
>>       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> +    if (!phy->vbus || !phy->regulator_on)
>> +        return 0;
>>
>> -    if (phy->vbus)
>> -        regulator_disable(phy->vbus);
>> +    regulator_disable(phy->vbus);
>> +    phy->regulator_on = false;
>> +
>> +    /*
>> +     * phy0 vbus typically slowly discharges, sometimes this causes the
>> +     * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
>> +     */
>> +    if (phy->index == 0 && !data->phy0_poll)
>> +        mod_delayed_work(system_wq, &data->detect, POLL_TIME);
>>
>>       return 0;
>>   }
>> @@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
>>       .owner        = THIS_MODULE,
>>   };
>>
>> +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
>> +{
>> +    struct sun4i_usb_phy_data *data =
>> +        container_of(work, struct sun4i_usb_phy_data, detect.work);
>> +    struct phy *phy0 = data->phys[0].phy;
>> +    int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
>> +
>> +    id_det = gpiod_get_value_cansleep(data->id_det_gpio);
>> +    vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
>> +
>> +    mutex_lock(&phy0->mutex);
>> +
>> +    if (!data->phy0_init) {
>> +        mutex_unlock(&phy0->mutex);
>> +        return;
>> +    }
>> +
>> +    if (id_det != data->id_det) {
>> +        sun4i_usb_phy0_set_id_detect(phy0, id_det);
>> +        data->id_det = id_det;
>> +        id_notify = 1;
>> +    }
>> +
>> +    if (vbus_det != data->vbus_det) {
>> +        sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
>> +        data->vbus_det = vbus_det;
>> +        vbus_notify = 1;
>> +    }
>> +
>> +    mutex_unlock(&phy0->mutex);
>> +
>> +    if (id_notify)
>> +        extcon_set_cable_state(&data->extcon,
>> +                       extcon_cable_name[EXTCON_USB_HOST],
>> +                       !id_det);
>> +
>> +    if (vbus_notify)
>> +        extcon_set_cable_state(&data->extcon,
>> +                       extcon_cable_name[EXTCON_USB],
>> +                       vbus_det);

I don't want to use the legacy extcon API with string name.
Instead, you can use the recommeded extcon_set_cable_state_() API as following:

- Legacy extcon API
: extern int extcon_set_cable_state(struct extcon_dev *edev,
				  const char *cable_name, bool cable_state);
- Recommended extcon API
: extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
				   bool cable_state);

[1] extcon: Use the unique id for external connector instead of string
- http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=2a9de9c0f08d61fbe3764a21d22d0b72df97d6ae

>> +
>> +    if (data->phy0_poll)
>> +        queue_delayed_work(system_wq, &data->detect, POLL_TIME);
>> +}
>> +
>> +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
>> +{
>> +    struct sun4i_usb_phy_data *data = dev_id;
>> +
>> +    /* vbus or id changed, let the pins settle and then scan them */
>> +    mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>>   static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>                       struct of_phandle_args *args)
>>   {
>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>       struct phy_provider *phy_provider;
>>       bool dedicated_clocks;
>>       struct resource *res;
>> -    int i;
>> +    int i, ret;
>>
>>       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>       if (!data)
>>           return -ENOMEM;
>>
>>       mutex_init(&data->mutex);
>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>> +    data->extcon_cable_names[2] = NULL;
>> +    data->extcon.name = DRIVER_NAME;

Don't need it because extcon_dev_register() set the device name automatically.
You can check it on patch[3].
[3] http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=71c3ffa5d23af0554c27010cf12710da9bf85950

>> +    data->extcon.supported_cable = data->extcon_cable_names;

You can use the devm_extcon_dev_allocate() to set the supported external connectors.

>> +    data->extcon.dev.parent = dev;

Don't need it because extcon_dev_register() set the parent device automatically.

The field of 'struct extcon_dev' shold be filled with extcon API without direct allocation.

>>
>>       if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>           data->num_phys = 2;
>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>       if (IS_ERR(data->base))
>>           return PTR_ERR(data->base);
>>
>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>> +    if (IS_ERR(data->id_det_gpio)) {
>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>> +            return -EPROBE_DEFER;
>> +        data->id_det_gpio = NULL;
>> +    }
>> +
>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>> +    if (IS_ERR(data->vbus_det_gpio)) {
>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>> +            return -EPROBE_DEFER;
>> +        data->vbus_det_gpio = NULL;
>> +    }
>> +
>> +    /* We either want both gpio pins or neither (when in host mode) */
>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>> +        return -ENODEV;
>> +    }
>> +
>> +    if (data->id_det_gpio) {
>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>> +        if (ret) {
>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>>       for (i = 0; i < data->num_phys; i++) {
>>           struct sun4i_usb_phy *phy = data->phys + i;
>>           char name[16];
>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>           phy_set_drvdata(phy->phy, &data->phys[i]);
>>       }
>>
>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>> +        data->phy0_poll = true;
>> +
>> +    if (data->id_det_irq >= 0) {
>> +        ret = devm_request_irq(dev, data->id_det_irq,
>> +                sun4i_usb_phy0_id_vbus_det_irq,
>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +                "usb0-id-det", data);
>> +        if (ret) {
>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>> +    if (data->vbus_det_irq >= 0) {
>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>> +                sun4i_usb_phy0_id_vbus_det_irq,
>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +                "usb0-vbus-det", data);
>> +        if (ret) {
>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>>       dev_set_drvdata(dev, data);
>>       phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>
>>       return PTR_ERR_OR_ZERO(phy_provider);
>>   }
>>
>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>> +{
>> +    struct device *dev = &pdev->dev;
>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>> +
>> +    if (data->id_det_irq >= 0)
>> +        devm_free_irq(dev, data->id_det_irq, data);
>> +    if (data->vbus_det_irq >= 0)
>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>> +
>> +    cancel_delayed_work_sync(&data->detect);
>> +
>> +    return 0;
>> +}
>> +
>>   static const struct of_device_id sun4i_usb_phy_of_match[] = {
>>       { .compatible = "allwinner,sun4i-a10-usb-phy" },
>>       { .compatible = "allwinner,sun5i-a13-usb-phy" },
>> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>>
>>   static struct platform_driver sun4i_usb_phy_driver = {
>>       .probe    = sun4i_usb_phy_probe,
>> +    .remove    = sun4i_usb_phy_remove,
>>       .driver = {
>>           .of_match_table    = sun4i_usb_phy_of_match,
>> -        .name  = "sun4i-usb-phy",
>> +        .name  = DRIVER_NAME,
>>       }
>>   };
>>   module_platform_driver(sun4i_usb_phy_driver);
>>
> 

Thanks,
Chanwoo Choi

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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	[flat|nested] 57+ messages in thread

* [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11  8:07             ` Chanwoo Choi
  0 siblings, 0 replies; 57+ messages in thread
From: Chanwoo Choi @ 2015-06-11  8:07 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Hans,

I add the comment about extcon-related code.

Firstly,
I'd like you to implment the extcon driver for phy-sun4i-usb device
in drivers/extcon/ directoryby using MFD because there are both extcon
provider driver and extcon client driver. I think that all extcon
provider driver better to be included in drivers/extcon/ directory.
extcon_set_cable_state() function should be handled in extcon provider
driver which is incluced in drivers/extcon/ directory.

On 06/11/2015 02:48 PM, Kishon Vijay Abraham I wrote:
> +Chanwoo
> 
> Hi,
> 
> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>> The usb0 phy is connected to an OTG controller, and as such needs some special
>> handling:
>>
>> 1) It allows explicit control over the pullups, enable these on phy_init and
>> disable them on phy_exit.
>>
>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>> these bits to the correct values for operating in host only mode when no
>> gpios are specified in the devicetree.
>>
>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>> device cable is plugged in, so when in dual role mode register an extcon.
>>
>> While updating the devicetree binding documentation also add documentation
>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>
>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> ---
>> Changes in v2:
>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>   has been moved to the phy-sun4i-usb driver and their status is exported
>>   through extcon for the sunxi-musb glue
>> Changes in v3:
>> -No changes
>> Changes in v4:
>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>   is present
>> ---
>>   .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>   drivers/phy/Kconfig                                |   1 +
>>   drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>   3 files changed, 281 insertions(+), 11 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> index 16528b9..557fa99 100644
>> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> @@ -23,6 +23,13 @@ Required properties:
>>     * "usb1_reset"
>>     * "usb2_reset" for sun4i, sun6i or sun7i
>>
>> +Optional properties:
>> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
>> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
>> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
>> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
>> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
>> +
>>   Example:
>>       usbphy: phy at 0x01c13400 {
>>           #phy-cells = <1>;
>> @@ -32,6 +39,13 @@ Example:
>>           reg-names = "phy_ctrl", "pmu1", "pmu2";
>>           clocks = <&usb_clk 8>;
>>           clock-names = "usb_phy";
>> -        resets = <&usb_clk 1>, <&usb_clk 2>;
>> -        reset-names = "usb1_reset", "usb2_reset";
>> +        resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
>> +        reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
>> +        pinctrl-names = "default";
>> +        pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
>> +        usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
>> +        usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
>> +        usb0_vbus-supply = <&reg_usb0_vbus>;
>> +        usb1_vbus-supply = <&reg_usb1_vbus>;
>> +        usb2_vbus-supply = <&reg_usb2_vbus>;
>>       };
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index a53bd5b..4614fba 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>>       tristate "Allwinner sunxi SoC USB PHY driver"
>>       depends on ARCH_SUNXI && HAS_IOMEM && OF
>>       depends on RESET_CONTROLLER
>> +    select EXTCON
> 
> Avoid using 'select' on visible Kconfig symbols.
> 
> Also please split the patch to make the reviewing a bit easier.
> 
> Thanks
> Kishon
>>       select GENERIC_PHY
>>       help
>>         Enable this to support the transceiver that is part of Allwinner
>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>> index 91c5be4..b45d707 100644
>> --- a/drivers/phy/phy-sun4i-usb.c
>> +++ b/drivers/phy/phy-sun4i-usb.c
>> @@ -1,7 +1,7 @@
>>   /*
>>    * Allwinner sun4i USB phy driver
>>    *
>> - * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
>> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede@redhat.com>
>>    *
>>    * Based on code from
>>    * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>> @@ -23,17 +23,23 @@
>>
>>   #include <linux/clk.h>
>>   #include <linux/err.h>
>> +#include <linux/extcon.h>
>>   #include <linux/io.h>
>> +#include <linux/interrupt.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_gpio.h>
>>   #include <linux/phy/phy.h>
>>   #include <linux/phy/phy-sun4i-usb.h>
>>   #include <linux/platform_device.h>
>>   #include <linux/regulator/consumer.h>
>>   #include <linux/reset.h>
>> +#include <linux/workqueue.h>
>> +
>> +#define DRIVER_NAME "sun4i-usb-phy"
>>
>>   #define REG_ISCR            0x00
>>   #define REG_PHYCTL            0x04
>> @@ -47,6 +53,17 @@
>>   #define SUNXI_AHB_INCRX_ALIGN_EN    BIT(8)
>>   #define SUNXI_ULPI_BYPASS_EN        BIT(0)
>>
>> +/* ISCR, Interface Status and Control bits */
>> +#define ISCR_ID_PULLUP_EN        (1 << 17)
>> +#define ISCR_DPDM_PULLUP_EN    (1 << 16)
>> +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
>> +#define ISCR_FORCE_ID_MASK    (3 << 14)
>> +#define ISCR_FORCE_ID_LOW        (2 << 14)
>> +#define ISCR_FORCE_ID_HIGH    (3 << 14)
>> +#define ISCR_FORCE_VBUS_MASK    (3 << 12)
>> +#define ISCR_FORCE_VBUS_LOW    (2 << 12)
>> +#define ISCR_FORCE_VBUS_HIGH    (3 << 12)
>> +
>>   /* Common Control Bits for Both PHYs */
>>   #define PHY_PLL_BW            0x03
>>   #define PHY_RES45_CAL_EN        0x0c
>> @@ -63,6 +80,13 @@
>>
>>   #define MAX_PHYS            3
>>
>> +/*
>> + * Note do not raise the debounce time, we must report Vusb high within 100ms
>> + * otherwise we get Vbus errors
>> + */
>> +#define DEBOUNCE_TIME            msecs_to_jiffies(50)
>> +#define POLL_TIME            msecs_to_jiffies(250)
>> +
>>   struct sun4i_usb_phy_data {
>>       void __iomem *base;
>>       struct mutex mutex;
>> @@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
>>           struct regulator *vbus;
>>           struct reset_control *reset;
>>           struct clk *clk;
>> +        bool regulator_on;
>>           int index;
>>       } phys[MAX_PHYS];
>> +    /* phy0 / otg related variables */
>> +    struct extcon_dev extcon;
>> +    const char *extcon_cable_names[3];

The latest extcon use the unique id to indicate the each external connector
instead of legacy string. The related patch[1] and patch[2] will be merged on v4.2-rc1.

[1] extcon: Use the unique id for external connector instead of string
- http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=2a9de9c0f08d61fbe3764a21d22d0b72df97d6ae
[2] extcon: Redefine the unique id of supported external connectors without 'enum extcon' type
- http://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/commit/?h=extcon-next-v4.3&id=bbd8d8b4244bb2799143c959fde57b1f034ec838

For exmaple in drivers/extcon/extcon-usb-gpio.c:
Maybe you can add the similiar array which include the supported external connector.

static const unsigned int usb_extcon_cable[] = {
	EXTCON_USB,
	EXTCON_USB_HOST,
	EXTCON_NONE,
};

>> +    bool phy0_init;
>> +    bool phy0_poll;
>> +    struct gpio_desc *id_det_gpio;
>> +    struct gpio_desc *vbus_det_gpio;
>> +    int id_det_irq;
>> +    int vbus_det_irq;
>> +    int id_det;
>> +    int vbus_det;
>> +    struct delayed_work detect;
>>   };
>>
>>   #define to_sun4i_usb_phy_data(phy) \
>>       container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>>
>> +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
>> +{
>> +    struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +    u32 iscr;
>> +
>> +    iscr = readl(data->base + REG_ISCR);
>> +    iscr &= ~clr;
>> +    iscr |= set;
>> +    writel(iscr, data->base + REG_ISCR);
>> +}
>> +
>> +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
>> +{
>> +    if (val)
>> +        val = ISCR_FORCE_ID_HIGH;
>> +    else
>> +        val = ISCR_FORCE_ID_LOW;
>> +
>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
>> +}
>> +
>> +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
>> +{
>> +    if (val)
>> +        val = ISCR_FORCE_VBUS_HIGH;
>> +    else
>> +        val = ISCR_FORCE_VBUS_LOW;
>> +
>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
>> +}
>> +
>>   static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>>                   int len)
>>   {
>> @@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
>>
>>       sun4i_usb_phy_passby(phy, 1);
>>
>> +    if (phy->index == 0) {
>> +        data->phy0_init = true;
>> +
>> +        /* Enable pull-ups */
>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
>> +
>> +        if (data->id_det_gpio) {
>> +            /* OTG mode, force ISCR and cable state updates */
>> +            data->id_det = -1;
>> +            data->vbus_det = -1;
>> +            queue_delayed_work(system_wq, &data->detect, 0);
>> +        } else {
>> +            /* Host only mode */
>> +            sun4i_usb_phy0_set_id_detect(_phy, 0);
>> +            sun4i_usb_phy0_set_vbus_detect(_phy, 1);
>> +        }
>> +    }
>> +
>>       return 0;
>>   }
>>
>>   static int sun4i_usb_phy_exit(struct phy *_phy)
>>   {
>>       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> +    if (phy->index == 0) {
>> +        /* Disable pull-ups */
>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
>> +        data->phy0_init = false;
>> +    }
>>
>>       sun4i_usb_phy_passby(phy, 0);
>>       reset_control_assert(phy->reset);
>> @@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
>>   static int sun4i_usb_phy_power_on(struct phy *_phy)
>>   {
>>       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> -    int ret = 0;
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +    int ret;
>> +
>> +    if (!phy->vbus || phy->regulator_on)
>> +        return 0;
>> +
>> +    /* For phy0 only turn on Vbus if we don't have an ext. Vbus */
>> +    if (phy->index == 0 && data->vbus_det)
>> +        return 0;
>>
>> -    if (phy->vbus)
>> -        ret = regulator_enable(phy->vbus);
>> +    ret = regulator_enable(phy->vbus);
>> +    if (ret)
>> +        return ret;
>>
>> -    return ret;
>> +    phy->regulator_on = true;
>> +
>> +    /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
>> +    if (phy->index == 0 && data->phy0_poll)
>> +        mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>> +
>> +    return 0;
>>   }
>>
>>   static int sun4i_usb_phy_power_off(struct phy *_phy)
>>   {
>>       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> +    if (!phy->vbus || !phy->regulator_on)
>> +        return 0;
>>
>> -    if (phy->vbus)
>> -        regulator_disable(phy->vbus);
>> +    regulator_disable(phy->vbus);
>> +    phy->regulator_on = false;
>> +
>> +    /*
>> +     * phy0 vbus typically slowly discharges, sometimes this causes the
>> +     * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
>> +     */
>> +    if (phy->index == 0 && !data->phy0_poll)
>> +        mod_delayed_work(system_wq, &data->detect, POLL_TIME);
>>
>>       return 0;
>>   }
>> @@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
>>       .owner        = THIS_MODULE,
>>   };
>>
>> +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
>> +{
>> +    struct sun4i_usb_phy_data *data =
>> +        container_of(work, struct sun4i_usb_phy_data, detect.work);
>> +    struct phy *phy0 = data->phys[0].phy;
>> +    int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
>> +
>> +    id_det = gpiod_get_value_cansleep(data->id_det_gpio);
>> +    vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
>> +
>> +    mutex_lock(&phy0->mutex);
>> +
>> +    if (!data->phy0_init) {
>> +        mutex_unlock(&phy0->mutex);
>> +        return;
>> +    }
>> +
>> +    if (id_det != data->id_det) {
>> +        sun4i_usb_phy0_set_id_detect(phy0, id_det);
>> +        data->id_det = id_det;
>> +        id_notify = 1;
>> +    }
>> +
>> +    if (vbus_det != data->vbus_det) {
>> +        sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
>> +        data->vbus_det = vbus_det;
>> +        vbus_notify = 1;
>> +    }
>> +
>> +    mutex_unlock(&phy0->mutex);
>> +
>> +    if (id_notify)
>> +        extcon_set_cable_state(&data->extcon,
>> +                       extcon_cable_name[EXTCON_USB_HOST],
>> +                       !id_det);
>> +
>> +    if (vbus_notify)
>> +        extcon_set_cable_state(&data->extcon,
>> +                       extcon_cable_name[EXTCON_USB],
>> +                       vbus_det);

I don't want to use the legacy extcon API with string name.
Instead, you can use the recommeded extcon_set_cable_state_() API as following:

- Legacy extcon API
: extern int extcon_set_cable_state(struct extcon_dev *edev,
				  const char *cable_name, bool cable_state);
- Recommended extcon API
: extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
				   bool cable_state);

[1] extcon: Use the unique id for external connector instead of string
- http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=2a9de9c0f08d61fbe3764a21d22d0b72df97d6ae

>> +
>> +    if (data->phy0_poll)
>> +        queue_delayed_work(system_wq, &data->detect, POLL_TIME);
>> +}
>> +
>> +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
>> +{
>> +    struct sun4i_usb_phy_data *data = dev_id;
>> +
>> +    /* vbus or id changed, let the pins settle and then scan them */
>> +    mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>>   static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>                       struct of_phandle_args *args)
>>   {
>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>       struct phy_provider *phy_provider;
>>       bool dedicated_clocks;
>>       struct resource *res;
>> -    int i;
>> +    int i, ret;
>>
>>       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>       if (!data)
>>           return -ENOMEM;
>>
>>       mutex_init(&data->mutex);
>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>> +    data->extcon_cable_names[2] = NULL;
>> +    data->extcon.name = DRIVER_NAME;

Don't need it because extcon_dev_register() set the device name automatically.
You can check it on patch[3].
[3] http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=71c3ffa5d23af0554c27010cf12710da9bf85950

>> +    data->extcon.supported_cable = data->extcon_cable_names;

You can use the devm_extcon_dev_allocate() to set the supported external connectors.

>> +    data->extcon.dev.parent = dev;

Don't need it because extcon_dev_register() set the parent device automatically.

The field of 'struct extcon_dev' shold be filled with extcon API without direct allocation.

>>
>>       if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>           data->num_phys = 2;
>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>       if (IS_ERR(data->base))
>>           return PTR_ERR(data->base);
>>
>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>> +    if (IS_ERR(data->id_det_gpio)) {
>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>> +            return -EPROBE_DEFER;
>> +        data->id_det_gpio = NULL;
>> +    }
>> +
>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>> +    if (IS_ERR(data->vbus_det_gpio)) {
>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>> +            return -EPROBE_DEFER;
>> +        data->vbus_det_gpio = NULL;
>> +    }
>> +
>> +    /* We either want both gpio pins or neither (when in host mode) */
>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>> +        return -ENODEV;
>> +    }
>> +
>> +    if (data->id_det_gpio) {
>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>> +        if (ret) {
>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>>       for (i = 0; i < data->num_phys; i++) {
>>           struct sun4i_usb_phy *phy = data->phys + i;
>>           char name[16];
>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>           phy_set_drvdata(phy->phy, &data->phys[i]);
>>       }
>>
>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>> +        data->phy0_poll = true;
>> +
>> +    if (data->id_det_irq >= 0) {
>> +        ret = devm_request_irq(dev, data->id_det_irq,
>> +                sun4i_usb_phy0_id_vbus_det_irq,
>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +                "usb0-id-det", data);
>> +        if (ret) {
>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>> +    if (data->vbus_det_irq >= 0) {
>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>> +                sun4i_usb_phy0_id_vbus_det_irq,
>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +                "usb0-vbus-det", data);
>> +        if (ret) {
>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>>       dev_set_drvdata(dev, data);
>>       phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>
>>       return PTR_ERR_OR_ZERO(phy_provider);
>>   }
>>
>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>> +{
>> +    struct device *dev = &pdev->dev;
>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>> +
>> +    if (data->id_det_irq >= 0)
>> +        devm_free_irq(dev, data->id_det_irq, data);
>> +    if (data->vbus_det_irq >= 0)
>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>> +
>> +    cancel_delayed_work_sync(&data->detect);
>> +
>> +    return 0;
>> +}
>> +
>>   static const struct of_device_id sun4i_usb_phy_of_match[] = {
>>       { .compatible = "allwinner,sun4i-a10-usb-phy" },
>>       { .compatible = "allwinner,sun5i-a13-usb-phy" },
>> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>>
>>   static struct platform_driver sun4i_usb_phy_driver = {
>>       .probe    = sun4i_usb_phy_probe,
>> +    .remove    = sun4i_usb_phy_remove,
>>       .driver = {
>>           .of_match_table    = sun4i_usb_phy_of_match,
>> -        .name  = "sun4i-usb-phy",
>> +        .name  = DRIVER_NAME,
>>       }
>>   };
>>   module_platform_driver(sun4i_usb_phy_driver);
>>
> 

Thanks,
Chanwoo Choi

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

* Re: [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-11  8:07             ` Chanwoo Choi
@ 2015-06-11  8:21                 ` Hans de Goede
  -1 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-11  8:21 UTC (permalink / raw)
  To: cw00.choi-Sze3O3UU22JBDgjK7y7TUQ, Kishon Vijay Abraham I
  Cc: Felipe Balbi, Maxime Ripard, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi Chanwoo,

Thanks for the quick review.

On 11-06-15 10:07, Chanwoo Choi wrote:
> Hi Hans,
>
> I add the comment about extcon-related code.
>
> Firstly,
> I'd like you to implment the extcon driver for phy-sun4i-usb device
> in drivers/extcon/ directoryby using MFD

No, just no, this is not what the MFD framework is for, the usb-phy
in question here is not a multifunction device. The MFD framework
is intended for true multi-function devices like i2c attached
PMICs which have regulators, gpios, pwm, input (power button),
chargers, power-supply, etc. That is NOT the case here.

Also moving this to the MFD framework would very likely requiring
the devicetree binding for the usb-phy to change which we cannot
do as that would break the devicetree ABI.

> because there are both extcon
> provider driver and extcon client driver. I think that all extcon
> provider driver better to be included in drivers/extcon/ directory.
> extcon_set_cable_state() function should be handled in extcon provider
> driver which is incluced in drivers/extcon/ directory.

I do not find this a compelling reason, there are plenty of subsystems
where not all implementations of the subsystem class live in the subsystem
directory, e.g. input and hwmon devices are often also found outside of
the input and hwmon driver directories.

>
> On 06/11/2015 02:48 PM, Kishon Vijay Abraham I wrote:
>> +Chanwoo
>>
>> Hi,
>>
>> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>>> The usb0 phy is connected to an OTG controller, and as such needs some special
>>> handling:
>>>
>>> 1) It allows explicit control over the pullups, enable these on phy_init and
>>> disable them on phy_exit.
>>>
>>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>>> these bits to the correct values for operating in host only mode when no
>>> gpios are specified in the devicetree.
>>>
>>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>>> device cable is plugged in, so when in dual role mode register an extcon.
>>>
>>> While updating the devicetree binding documentation also add documentation
>>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>>
>>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>> ---
>>> Changes in v2:
>>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>>    has been moved to the phy-sun4i-usb driver and their status is exported
>>>    through extcon for the sunxi-musb glue
>>> Changes in v3:
>>> -No changes
>>> Changes in v4:
>>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>>    is present
>>> ---
>>>    .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>>    drivers/phy/Kconfig                                |   1 +
>>>    drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>>    3 files changed, 281 insertions(+), 11 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>> index 16528b9..557fa99 100644
>>> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>> @@ -23,6 +23,13 @@ Required properties:
>>>      * "usb1_reset"
>>>      * "usb2_reset" for sun4i, sun6i or sun7i
>>>
>>> +Optional properties:
>>> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
>>> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
>>> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
>>> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
>>> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
>>> +
>>>    Example:
>>>        usbphy: phy@0x01c13400 {
>>>            #phy-cells = <1>;
>>> @@ -32,6 +39,13 @@ Example:
>>>            reg-names = "phy_ctrl", "pmu1", "pmu2";
>>>            clocks = <&usb_clk 8>;
>>>            clock-names = "usb_phy";
>>> -        resets = <&usb_clk 1>, <&usb_clk 2>;
>>> -        reset-names = "usb1_reset", "usb2_reset";
>>> +        resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
>>> +        reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
>>> +        pinctrl-names = "default";
>>> +        pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
>>> +        usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
>>> +        usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
>>> +        usb0_vbus-supply = <&reg_usb0_vbus>;
>>> +        usb1_vbus-supply = <&reg_usb1_vbus>;
>>> +        usb2_vbus-supply = <&reg_usb2_vbus>;
>>>        };
>>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>>> index a53bd5b..4614fba 100644
>>> --- a/drivers/phy/Kconfig
>>> +++ b/drivers/phy/Kconfig
>>> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>>>        tristate "Allwinner sunxi SoC USB PHY driver"
>>>        depends on ARCH_SUNXI && HAS_IOMEM && OF
>>>        depends on RESET_CONTROLLER
>>> +    select EXTCON
>>
>> Avoid using 'select' on visible Kconfig symbols.
>>
>> Also please split the patch to make the reviewing a bit easier.
>>
>> Thanks
>> Kishon
>>>        select GENERIC_PHY
>>>        help
>>>          Enable this to support the transceiver that is part of Allwinner
>>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>>> index 91c5be4..b45d707 100644
>>> --- a/drivers/phy/phy-sun4i-usb.c
>>> +++ b/drivers/phy/phy-sun4i-usb.c
>>> @@ -1,7 +1,7 @@
>>>    /*
>>>     * Allwinner sun4i USB phy driver
>>>     *
>>> - * Copyright (C) 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>>     *
>>>     * Based on code from
>>>     * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>>> @@ -23,17 +23,23 @@
>>>
>>>    #include <linux/clk.h>
>>>    #include <linux/err.h>
>>> +#include <linux/extcon.h>
>>>    #include <linux/io.h>
>>> +#include <linux/interrupt.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_gpio.h>
>>>    #include <linux/phy/phy.h>
>>>    #include <linux/phy/phy-sun4i-usb.h>
>>>    #include <linux/platform_device.h>
>>>    #include <linux/regulator/consumer.h>
>>>    #include <linux/reset.h>
>>> +#include <linux/workqueue.h>
>>> +
>>> +#define DRIVER_NAME "sun4i-usb-phy"
>>>
>>>    #define REG_ISCR            0x00
>>>    #define REG_PHYCTL            0x04
>>> @@ -47,6 +53,17 @@
>>>    #define SUNXI_AHB_INCRX_ALIGN_EN    BIT(8)
>>>    #define SUNXI_ULPI_BYPASS_EN        BIT(0)
>>>
>>> +/* ISCR, Interface Status and Control bits */
>>> +#define ISCR_ID_PULLUP_EN        (1 << 17)
>>> +#define ISCR_DPDM_PULLUP_EN    (1 << 16)
>>> +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
>>> +#define ISCR_FORCE_ID_MASK    (3 << 14)
>>> +#define ISCR_FORCE_ID_LOW        (2 << 14)
>>> +#define ISCR_FORCE_ID_HIGH    (3 << 14)
>>> +#define ISCR_FORCE_VBUS_MASK    (3 << 12)
>>> +#define ISCR_FORCE_VBUS_LOW    (2 << 12)
>>> +#define ISCR_FORCE_VBUS_HIGH    (3 << 12)
>>> +
>>>    /* Common Control Bits for Both PHYs */
>>>    #define PHY_PLL_BW            0x03
>>>    #define PHY_RES45_CAL_EN        0x0c
>>> @@ -63,6 +80,13 @@
>>>
>>>    #define MAX_PHYS            3
>>>
>>> +/*
>>> + * Note do not raise the debounce time, we must report Vusb high within 100ms
>>> + * otherwise we get Vbus errors
>>> + */
>>> +#define DEBOUNCE_TIME            msecs_to_jiffies(50)
>>> +#define POLL_TIME            msecs_to_jiffies(250)
>>> +
>>>    struct sun4i_usb_phy_data {
>>>        void __iomem *base;
>>>        struct mutex mutex;
>>> @@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
>>>            struct regulator *vbus;
>>>            struct reset_control *reset;
>>>            struct clk *clk;
>>> +        bool regulator_on;
>>>            int index;
>>>        } phys[MAX_PHYS];
>>> +    /* phy0 / otg related variables */
>>> +    struct extcon_dev extcon;
>>> +    const char *extcon_cable_names[3];
>
> The latest extcon use the unique id to indicate the each external connector
> instead of legacy string. The related patch[1] and patch[2] will be merged on v4.2-rc1.
>
> [1] extcon: Use the unique id for external connector instead of string
> - http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=2a9de9c0f08d61fbe3764a21d22d0b72df97d6ae
> [2] extcon: Redefine the unique id of supported external connectors without 'enum extcon' type
> - http://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/commit/?h=extcon-next-v4.3&id=bbd8d8b4244bb2799143c959fde57b1f034ec838
>
> For exmaple in drivers/extcon/extcon-usb-gpio.c:
> Maybe you can add the similiar array which include the supported external connector.
>
> static const unsigned int usb_extcon_cable[] = {
> 	EXTCON_USB,
> 	EXTCON_USB_HOST,
> 	EXTCON_NONE,
> };


Ah, that is good to know, I'll port the extcon code in phy-sun4i-usb.c to
this new API for the next version of the patch.

Thanks & Regards,

Hans


>
>>> +    bool phy0_init;
>>> +    bool phy0_poll;
>>> +    struct gpio_desc *id_det_gpio;
>>> +    struct gpio_desc *vbus_det_gpio;
>>> +    int id_det_irq;
>>> +    int vbus_det_irq;
>>> +    int id_det;
>>> +    int vbus_det;
>>> +    struct delayed_work detect;
>>>    };
>>>
>>>    #define to_sun4i_usb_phy_data(phy) \
>>>        container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>>>
>>> +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
>>> +{
>>> +    struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>> +    u32 iscr;
>>> +
>>> +    iscr = readl(data->base + REG_ISCR);
>>> +    iscr &= ~clr;
>>> +    iscr |= set;
>>> +    writel(iscr, data->base + REG_ISCR);
>>> +}
>>> +
>>> +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
>>> +{
>>> +    if (val)
>>> +        val = ISCR_FORCE_ID_HIGH;
>>> +    else
>>> +        val = ISCR_FORCE_ID_LOW;
>>> +
>>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
>>> +}
>>> +
>>> +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
>>> +{
>>> +    if (val)
>>> +        val = ISCR_FORCE_VBUS_HIGH;
>>> +    else
>>> +        val = ISCR_FORCE_VBUS_LOW;
>>> +
>>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
>>> +}
>>> +
>>>    static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>>>                    int len)
>>>    {
>>> @@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
>>>
>>>        sun4i_usb_phy_passby(phy, 1);
>>>
>>> +    if (phy->index == 0) {
>>> +        data->phy0_init = true;
>>> +
>>> +        /* Enable pull-ups */
>>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
>>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
>>> +
>>> +        if (data->id_det_gpio) {
>>> +            /* OTG mode, force ISCR and cable state updates */
>>> +            data->id_det = -1;
>>> +            data->vbus_det = -1;
>>> +            queue_delayed_work(system_wq, &data->detect, 0);
>>> +        } else {
>>> +            /* Host only mode */
>>> +            sun4i_usb_phy0_set_id_detect(_phy, 0);
>>> +            sun4i_usb_phy0_set_vbus_detect(_phy, 1);
>>> +        }
>>> +    }
>>> +
>>>        return 0;
>>>    }
>>>
>>>    static int sun4i_usb_phy_exit(struct phy *_phy)
>>>    {
>>>        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>> +
>>> +    if (phy->index == 0) {
>>> +        /* Disable pull-ups */
>>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
>>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
>>> +        data->phy0_init = false;
>>> +    }
>>>
>>>        sun4i_usb_phy_passby(phy, 0);
>>>        reset_control_assert(phy->reset);
>>> @@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
>>>    static int sun4i_usb_phy_power_on(struct phy *_phy)
>>>    {
>>>        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>> -    int ret = 0;
>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>> +    int ret;
>>> +
>>> +    if (!phy->vbus || phy->regulator_on)
>>> +        return 0;
>>> +
>>> +    /* For phy0 only turn on Vbus if we don't have an ext. Vbus */
>>> +    if (phy->index == 0 && data->vbus_det)
>>> +        return 0;
>>>
>>> -    if (phy->vbus)
>>> -        ret = regulator_enable(phy->vbus);
>>> +    ret = regulator_enable(phy->vbus);
>>> +    if (ret)
>>> +        return ret;
>>>
>>> -    return ret;
>>> +    phy->regulator_on = true;
>>> +
>>> +    /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
>>> +    if (phy->index == 0 && data->phy0_poll)
>>> +        mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>>> +
>>> +    return 0;
>>>    }
>>>
>>>    static int sun4i_usb_phy_power_off(struct phy *_phy)
>>>    {
>>>        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>> +
>>> +    if (!phy->vbus || !phy->regulator_on)
>>> +        return 0;
>>>
>>> -    if (phy->vbus)
>>> -        regulator_disable(phy->vbus);
>>> +    regulator_disable(phy->vbus);
>>> +    phy->regulator_on = false;
>>> +
>>> +    /*
>>> +     * phy0 vbus typically slowly discharges, sometimes this causes the
>>> +     * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
>>> +     */
>>> +    if (phy->index == 0 && !data->phy0_poll)
>>> +        mod_delayed_work(system_wq, &data->detect, POLL_TIME);
>>>
>>>        return 0;
>>>    }
>>> @@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
>>>        .owner        = THIS_MODULE,
>>>    };
>>>
>>> +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
>>> +{
>>> +    struct sun4i_usb_phy_data *data =
>>> +        container_of(work, struct sun4i_usb_phy_data, detect.work);
>>> +    struct phy *phy0 = data->phys[0].phy;
>>> +    int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
>>> +
>>> +    id_det = gpiod_get_value_cansleep(data->id_det_gpio);
>>> +    vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
>>> +
>>> +    mutex_lock(&phy0->mutex);
>>> +
>>> +    if (!data->phy0_init) {
>>> +        mutex_unlock(&phy0->mutex);
>>> +        return;
>>> +    }
>>> +
>>> +    if (id_det != data->id_det) {
>>> +        sun4i_usb_phy0_set_id_detect(phy0, id_det);
>>> +        data->id_det = id_det;
>>> +        id_notify = 1;
>>> +    }
>>> +
>>> +    if (vbus_det != data->vbus_det) {
>>> +        sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
>>> +        data->vbus_det = vbus_det;
>>> +        vbus_notify = 1;
>>> +    }
>>> +
>>> +    mutex_unlock(&phy0->mutex);
>>> +
>>> +    if (id_notify)
>>> +        extcon_set_cable_state(&data->extcon,
>>> +                       extcon_cable_name[EXTCON_USB_HOST],
>>> +                       !id_det);
>>> +
>>> +    if (vbus_notify)
>>> +        extcon_set_cable_state(&data->extcon,
>>> +                       extcon_cable_name[EXTCON_USB],
>>> +                       vbus_det);
>
> I don't want to use the legacy extcon API with string name.
> Instead, you can use the recommeded extcon_set_cable_state_() API as following:
>
> - Legacy extcon API
> : extern int extcon_set_cable_state(struct extcon_dev *edev,
> 				  const char *cable_name, bool cable_state);
> - Recommended extcon API
> : extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
> 				   bool cable_state);
>
> [1] extcon: Use the unique id for external connector instead of string
> - http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=2a9de9c0f08d61fbe3764a21d22d0b72df97d6ae
>
>>> +
>>> +    if (data->phy0_poll)
>>> +        queue_delayed_work(system_wq, &data->detect, POLL_TIME);
>>> +}
>>> +
>>> +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
>>> +{
>>> +    struct sun4i_usb_phy_data *data = dev_id;
>>> +
>>> +    /* vbus or id changed, let the pins settle and then scan them */
>>> +    mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>>> +
>>> +    return IRQ_HANDLED;
>>> +}
>>> +
>>>    static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>>                        struct of_phandle_args *args)
>>>    {
>>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>        struct phy_provider *phy_provider;
>>>        bool dedicated_clocks;
>>>        struct resource *res;
>>> -    int i;
>>> +    int i, ret;
>>>
>>>        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>>        if (!data)
>>>            return -ENOMEM;
>>>
>>>        mutex_init(&data->mutex);
>>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>>> +    data->extcon_cable_names[2] = NULL;
>>> +    data->extcon.name = DRIVER_NAME;
>
> Don't need it because extcon_dev_register() set the device name automatically.
> You can check it on patch[3].
> [3] http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=71c3ffa5d23af0554c27010cf12710da9bf85950
>
>>> +    data->extcon.supported_cable = data->extcon_cable_names;
>
> You can use the devm_extcon_dev_allocate() to set the supported external connectors.
>
>>> +    data->extcon.dev.parent = dev;
>
> Don't need it because extcon_dev_register() set the parent device automatically.
>
> The field of 'struct extcon_dev' shold be filled with extcon API without direct allocation.
>
>>>
>>>        if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>>            data->num_phys = 2;
>>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>        if (IS_ERR(data->base))
>>>            return PTR_ERR(data->base);
>>>
>>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>>> +    if (IS_ERR(data->id_det_gpio)) {
>>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>>> +            return -EPROBE_DEFER;
>>> +        data->id_det_gpio = NULL;
>>> +    }
>>> +
>>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>>> +    if (IS_ERR(data->vbus_det_gpio)) {
>>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>>> +            return -EPROBE_DEFER;
>>> +        data->vbus_det_gpio = NULL;
>>> +    }
>>> +
>>> +    /* We either want both gpio pins or neither (when in host mode) */
>>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>>> +        return -ENODEV;
>>> +    }
>>> +
>>> +    if (data->id_det_gpio) {
>>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>>> +        if (ret) {
>>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>>        for (i = 0; i < data->num_phys; i++) {
>>>            struct sun4i_usb_phy *phy = data->phys + i;
>>>            char name[16];
>>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>            phy_set_drvdata(phy->phy, &data->phys[i]);
>>>        }
>>>
>>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>>> +        data->phy0_poll = true;
>>> +
>>> +    if (data->id_det_irq >= 0) {
>>> +        ret = devm_request_irq(dev, data->id_det_irq,
>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>> +                "usb0-id-det", data);
>>> +        if (ret) {
>>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>> +    if (data->vbus_det_irq >= 0) {
>>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>> +                "usb0-vbus-det", data);
>>> +        if (ret) {
>>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>>        dev_set_drvdata(dev, data);
>>>        phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>>
>>>        return PTR_ERR_OR_ZERO(phy_provider);
>>>    }
>>>
>>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>>> +{
>>> +    struct device *dev = &pdev->dev;
>>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>>> +
>>> +    if (data->id_det_irq >= 0)
>>> +        devm_free_irq(dev, data->id_det_irq, data);
>>> +    if (data->vbus_det_irq >= 0)
>>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>>> +
>>> +    cancel_delayed_work_sync(&data->detect);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>>    static const struct of_device_id sun4i_usb_phy_of_match[] = {
>>>        { .compatible = "allwinner,sun4i-a10-usb-phy" },
>>>        { .compatible = "allwinner,sun5i-a13-usb-phy" },
>>> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>>>
>>>    static struct platform_driver sun4i_usb_phy_driver = {
>>>        .probe    = sun4i_usb_phy_probe,
>>> +    .remove    = sun4i_usb_phy_remove,
>>>        .driver = {
>>>            .of_match_table    = sun4i_usb_phy_of_match,
>>> -        .name  = "sun4i-usb-phy",
>>> +        .name  = DRIVER_NAME,
>>>        }
>>>    };
>>>    module_platform_driver(sun4i_usb_phy_driver);
>>>
>>
>
> Thanks,
> Chanwoo Choi
>
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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	[flat|nested] 57+ messages in thread

* [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11  8:21                 ` Hans de Goede
  0 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-11  8:21 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Chanwoo,

Thanks for the quick review.

On 11-06-15 10:07, Chanwoo Choi wrote:
> Hi Hans,
>
> I add the comment about extcon-related code.
>
> Firstly,
> I'd like you to implment the extcon driver for phy-sun4i-usb device
> in drivers/extcon/ directoryby using MFD

No, just no, this is not what the MFD framework is for, the usb-phy
in question here is not a multifunction device. The MFD framework
is intended for true multi-function devices like i2c attached
PMICs which have regulators, gpios, pwm, input (power button),
chargers, power-supply, etc. That is NOT the case here.

Also moving this to the MFD framework would very likely requiring
the devicetree binding for the usb-phy to change which we cannot
do as that would break the devicetree ABI.

> because there are both extcon
> provider driver and extcon client driver. I think that all extcon
> provider driver better to be included in drivers/extcon/ directory.
> extcon_set_cable_state() function should be handled in extcon provider
> driver which is incluced in drivers/extcon/ directory.

I do not find this a compelling reason, there are plenty of subsystems
where not all implementations of the subsystem class live in the subsystem
directory, e.g. input and hwmon devices are often also found outside of
the input and hwmon driver directories.

>
> On 06/11/2015 02:48 PM, Kishon Vijay Abraham I wrote:
>> +Chanwoo
>>
>> Hi,
>>
>> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>>> The usb0 phy is connected to an OTG controller, and as such needs some special
>>> handling:
>>>
>>> 1) It allows explicit control over the pullups, enable these on phy_init and
>>> disable them on phy_exit.
>>>
>>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>>> these bits to the correct values for operating in host only mode when no
>>> gpios are specified in the devicetree.
>>>
>>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>>> device cable is plugged in, so when in dual role mode register an extcon.
>>>
>>> While updating the devicetree binding documentation also add documentation
>>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>>
>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>>> ---
>>> Changes in v2:
>>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>>    has been moved to the phy-sun4i-usb driver and their status is exported
>>>    through extcon for the sunxi-musb glue
>>> Changes in v3:
>>> -No changes
>>> Changes in v4:
>>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>>    is present
>>> ---
>>>    .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>>    drivers/phy/Kconfig                                |   1 +
>>>    drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>>    3 files changed, 281 insertions(+), 11 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>> index 16528b9..557fa99 100644
>>> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>> @@ -23,6 +23,13 @@ Required properties:
>>>      * "usb1_reset"
>>>      * "usb2_reset" for sun4i, sun6i or sun7i
>>>
>>> +Optional properties:
>>> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
>>> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
>>> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
>>> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
>>> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
>>> +
>>>    Example:
>>>        usbphy: phy at 0x01c13400 {
>>>            #phy-cells = <1>;
>>> @@ -32,6 +39,13 @@ Example:
>>>            reg-names = "phy_ctrl", "pmu1", "pmu2";
>>>            clocks = <&usb_clk 8>;
>>>            clock-names = "usb_phy";
>>> -        resets = <&usb_clk 1>, <&usb_clk 2>;
>>> -        reset-names = "usb1_reset", "usb2_reset";
>>> +        resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
>>> +        reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
>>> +        pinctrl-names = "default";
>>> +        pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
>>> +        usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
>>> +        usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
>>> +        usb0_vbus-supply = <&reg_usb0_vbus>;
>>> +        usb1_vbus-supply = <&reg_usb1_vbus>;
>>> +        usb2_vbus-supply = <&reg_usb2_vbus>;
>>>        };
>>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>>> index a53bd5b..4614fba 100644
>>> --- a/drivers/phy/Kconfig
>>> +++ b/drivers/phy/Kconfig
>>> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>>>        tristate "Allwinner sunxi SoC USB PHY driver"
>>>        depends on ARCH_SUNXI && HAS_IOMEM && OF
>>>        depends on RESET_CONTROLLER
>>> +    select EXTCON
>>
>> Avoid using 'select' on visible Kconfig symbols.
>>
>> Also please split the patch to make the reviewing a bit easier.
>>
>> Thanks
>> Kishon
>>>        select GENERIC_PHY
>>>        help
>>>          Enable this to support the transceiver that is part of Allwinner
>>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>>> index 91c5be4..b45d707 100644
>>> --- a/drivers/phy/phy-sun4i-usb.c
>>> +++ b/drivers/phy/phy-sun4i-usb.c
>>> @@ -1,7 +1,7 @@
>>>    /*
>>>     * Allwinner sun4i USB phy driver
>>>     *
>>> - * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
>>> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede@redhat.com>
>>>     *
>>>     * Based on code from
>>>     * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>>> @@ -23,17 +23,23 @@
>>>
>>>    #include <linux/clk.h>
>>>    #include <linux/err.h>
>>> +#include <linux/extcon.h>
>>>    #include <linux/io.h>
>>> +#include <linux/interrupt.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_gpio.h>
>>>    #include <linux/phy/phy.h>
>>>    #include <linux/phy/phy-sun4i-usb.h>
>>>    #include <linux/platform_device.h>
>>>    #include <linux/regulator/consumer.h>
>>>    #include <linux/reset.h>
>>> +#include <linux/workqueue.h>
>>> +
>>> +#define DRIVER_NAME "sun4i-usb-phy"
>>>
>>>    #define REG_ISCR            0x00
>>>    #define REG_PHYCTL            0x04
>>> @@ -47,6 +53,17 @@
>>>    #define SUNXI_AHB_INCRX_ALIGN_EN    BIT(8)
>>>    #define SUNXI_ULPI_BYPASS_EN        BIT(0)
>>>
>>> +/* ISCR, Interface Status and Control bits */
>>> +#define ISCR_ID_PULLUP_EN        (1 << 17)
>>> +#define ISCR_DPDM_PULLUP_EN    (1 << 16)
>>> +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
>>> +#define ISCR_FORCE_ID_MASK    (3 << 14)
>>> +#define ISCR_FORCE_ID_LOW        (2 << 14)
>>> +#define ISCR_FORCE_ID_HIGH    (3 << 14)
>>> +#define ISCR_FORCE_VBUS_MASK    (3 << 12)
>>> +#define ISCR_FORCE_VBUS_LOW    (2 << 12)
>>> +#define ISCR_FORCE_VBUS_HIGH    (3 << 12)
>>> +
>>>    /* Common Control Bits for Both PHYs */
>>>    #define PHY_PLL_BW            0x03
>>>    #define PHY_RES45_CAL_EN        0x0c
>>> @@ -63,6 +80,13 @@
>>>
>>>    #define MAX_PHYS            3
>>>
>>> +/*
>>> + * Note do not raise the debounce time, we must report Vusb high within 100ms
>>> + * otherwise we get Vbus errors
>>> + */
>>> +#define DEBOUNCE_TIME            msecs_to_jiffies(50)
>>> +#define POLL_TIME            msecs_to_jiffies(250)
>>> +
>>>    struct sun4i_usb_phy_data {
>>>        void __iomem *base;
>>>        struct mutex mutex;
>>> @@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
>>>            struct regulator *vbus;
>>>            struct reset_control *reset;
>>>            struct clk *clk;
>>> +        bool regulator_on;
>>>            int index;
>>>        } phys[MAX_PHYS];
>>> +    /* phy0 / otg related variables */
>>> +    struct extcon_dev extcon;
>>> +    const char *extcon_cable_names[3];
>
> The latest extcon use the unique id to indicate the each external connector
> instead of legacy string. The related patch[1] and patch[2] will be merged on v4.2-rc1.
>
> [1] extcon: Use the unique id for external connector instead of string
> - http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=2a9de9c0f08d61fbe3764a21d22d0b72df97d6ae
> [2] extcon: Redefine the unique id of supported external connectors without 'enum extcon' type
> - http://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/commit/?h=extcon-next-v4.3&id=bbd8d8b4244bb2799143c959fde57b1f034ec838
>
> For exmaple in drivers/extcon/extcon-usb-gpio.c:
> Maybe you can add the similiar array which include the supported external connector.
>
> static const unsigned int usb_extcon_cable[] = {
> 	EXTCON_USB,
> 	EXTCON_USB_HOST,
> 	EXTCON_NONE,
> };


Ah, that is good to know, I'll port the extcon code in phy-sun4i-usb.c to
this new API for the next version of the patch.

Thanks & Regards,

Hans


>
>>> +    bool phy0_init;
>>> +    bool phy0_poll;
>>> +    struct gpio_desc *id_det_gpio;
>>> +    struct gpio_desc *vbus_det_gpio;
>>> +    int id_det_irq;
>>> +    int vbus_det_irq;
>>> +    int id_det;
>>> +    int vbus_det;
>>> +    struct delayed_work detect;
>>>    };
>>>
>>>    #define to_sun4i_usb_phy_data(phy) \
>>>        container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>>>
>>> +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
>>> +{
>>> +    struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>> +    u32 iscr;
>>> +
>>> +    iscr = readl(data->base + REG_ISCR);
>>> +    iscr &= ~clr;
>>> +    iscr |= set;
>>> +    writel(iscr, data->base + REG_ISCR);
>>> +}
>>> +
>>> +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
>>> +{
>>> +    if (val)
>>> +        val = ISCR_FORCE_ID_HIGH;
>>> +    else
>>> +        val = ISCR_FORCE_ID_LOW;
>>> +
>>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
>>> +}
>>> +
>>> +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
>>> +{
>>> +    if (val)
>>> +        val = ISCR_FORCE_VBUS_HIGH;
>>> +    else
>>> +        val = ISCR_FORCE_VBUS_LOW;
>>> +
>>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
>>> +}
>>> +
>>>    static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>>>                    int len)
>>>    {
>>> @@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
>>>
>>>        sun4i_usb_phy_passby(phy, 1);
>>>
>>> +    if (phy->index == 0) {
>>> +        data->phy0_init = true;
>>> +
>>> +        /* Enable pull-ups */
>>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
>>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
>>> +
>>> +        if (data->id_det_gpio) {
>>> +            /* OTG mode, force ISCR and cable state updates */
>>> +            data->id_det = -1;
>>> +            data->vbus_det = -1;
>>> +            queue_delayed_work(system_wq, &data->detect, 0);
>>> +        } else {
>>> +            /* Host only mode */
>>> +            sun4i_usb_phy0_set_id_detect(_phy, 0);
>>> +            sun4i_usb_phy0_set_vbus_detect(_phy, 1);
>>> +        }
>>> +    }
>>> +
>>>        return 0;
>>>    }
>>>
>>>    static int sun4i_usb_phy_exit(struct phy *_phy)
>>>    {
>>>        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>> +
>>> +    if (phy->index == 0) {
>>> +        /* Disable pull-ups */
>>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
>>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
>>> +        data->phy0_init = false;
>>> +    }
>>>
>>>        sun4i_usb_phy_passby(phy, 0);
>>>        reset_control_assert(phy->reset);
>>> @@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
>>>    static int sun4i_usb_phy_power_on(struct phy *_phy)
>>>    {
>>>        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>> -    int ret = 0;
>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>> +    int ret;
>>> +
>>> +    if (!phy->vbus || phy->regulator_on)
>>> +        return 0;
>>> +
>>> +    /* For phy0 only turn on Vbus if we don't have an ext. Vbus */
>>> +    if (phy->index == 0 && data->vbus_det)
>>> +        return 0;
>>>
>>> -    if (phy->vbus)
>>> -        ret = regulator_enable(phy->vbus);
>>> +    ret = regulator_enable(phy->vbus);
>>> +    if (ret)
>>> +        return ret;
>>>
>>> -    return ret;
>>> +    phy->regulator_on = true;
>>> +
>>> +    /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
>>> +    if (phy->index == 0 && data->phy0_poll)
>>> +        mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>>> +
>>> +    return 0;
>>>    }
>>>
>>>    static int sun4i_usb_phy_power_off(struct phy *_phy)
>>>    {
>>>        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>> +
>>> +    if (!phy->vbus || !phy->regulator_on)
>>> +        return 0;
>>>
>>> -    if (phy->vbus)
>>> -        regulator_disable(phy->vbus);
>>> +    regulator_disable(phy->vbus);
>>> +    phy->regulator_on = false;
>>> +
>>> +    /*
>>> +     * phy0 vbus typically slowly discharges, sometimes this causes the
>>> +     * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
>>> +     */
>>> +    if (phy->index == 0 && !data->phy0_poll)
>>> +        mod_delayed_work(system_wq, &data->detect, POLL_TIME);
>>>
>>>        return 0;
>>>    }
>>> @@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
>>>        .owner        = THIS_MODULE,
>>>    };
>>>
>>> +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
>>> +{
>>> +    struct sun4i_usb_phy_data *data =
>>> +        container_of(work, struct sun4i_usb_phy_data, detect.work);
>>> +    struct phy *phy0 = data->phys[0].phy;
>>> +    int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
>>> +
>>> +    id_det = gpiod_get_value_cansleep(data->id_det_gpio);
>>> +    vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
>>> +
>>> +    mutex_lock(&phy0->mutex);
>>> +
>>> +    if (!data->phy0_init) {
>>> +        mutex_unlock(&phy0->mutex);
>>> +        return;
>>> +    }
>>> +
>>> +    if (id_det != data->id_det) {
>>> +        sun4i_usb_phy0_set_id_detect(phy0, id_det);
>>> +        data->id_det = id_det;
>>> +        id_notify = 1;
>>> +    }
>>> +
>>> +    if (vbus_det != data->vbus_det) {
>>> +        sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
>>> +        data->vbus_det = vbus_det;
>>> +        vbus_notify = 1;
>>> +    }
>>> +
>>> +    mutex_unlock(&phy0->mutex);
>>> +
>>> +    if (id_notify)
>>> +        extcon_set_cable_state(&data->extcon,
>>> +                       extcon_cable_name[EXTCON_USB_HOST],
>>> +                       !id_det);
>>> +
>>> +    if (vbus_notify)
>>> +        extcon_set_cable_state(&data->extcon,
>>> +                       extcon_cable_name[EXTCON_USB],
>>> +                       vbus_det);
>
> I don't want to use the legacy extcon API with string name.
> Instead, you can use the recommeded extcon_set_cable_state_() API as following:
>
> - Legacy extcon API
> : extern int extcon_set_cable_state(struct extcon_dev *edev,
> 				  const char *cable_name, bool cable_state);
> - Recommended extcon API
> : extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
> 				   bool cable_state);
>
> [1] extcon: Use the unique id for external connector instead of string
> - http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=2a9de9c0f08d61fbe3764a21d22d0b72df97d6ae
>
>>> +
>>> +    if (data->phy0_poll)
>>> +        queue_delayed_work(system_wq, &data->detect, POLL_TIME);
>>> +}
>>> +
>>> +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
>>> +{
>>> +    struct sun4i_usb_phy_data *data = dev_id;
>>> +
>>> +    /* vbus or id changed, let the pins settle and then scan them */
>>> +    mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>>> +
>>> +    return IRQ_HANDLED;
>>> +}
>>> +
>>>    static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>>                        struct of_phandle_args *args)
>>>    {
>>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>        struct phy_provider *phy_provider;
>>>        bool dedicated_clocks;
>>>        struct resource *res;
>>> -    int i;
>>> +    int i, ret;
>>>
>>>        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>>        if (!data)
>>>            return -ENOMEM;
>>>
>>>        mutex_init(&data->mutex);
>>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>>> +    data->extcon_cable_names[2] = NULL;
>>> +    data->extcon.name = DRIVER_NAME;
>
> Don't need it because extcon_dev_register() set the device name automatically.
> You can check it on patch[3].
> [3] http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=71c3ffa5d23af0554c27010cf12710da9bf85950
>
>>> +    data->extcon.supported_cable = data->extcon_cable_names;
>
> You can use the devm_extcon_dev_allocate() to set the supported external connectors.
>
>>> +    data->extcon.dev.parent = dev;
>
> Don't need it because extcon_dev_register() set the parent device automatically.
>
> The field of 'struct extcon_dev' shold be filled with extcon API without direct allocation.
>
>>>
>>>        if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>>            data->num_phys = 2;
>>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>        if (IS_ERR(data->base))
>>>            return PTR_ERR(data->base);
>>>
>>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>>> +    if (IS_ERR(data->id_det_gpio)) {
>>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>>> +            return -EPROBE_DEFER;
>>> +        data->id_det_gpio = NULL;
>>> +    }
>>> +
>>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>>> +    if (IS_ERR(data->vbus_det_gpio)) {
>>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>>> +            return -EPROBE_DEFER;
>>> +        data->vbus_det_gpio = NULL;
>>> +    }
>>> +
>>> +    /* We either want both gpio pins or neither (when in host mode) */
>>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>>> +        return -ENODEV;
>>> +    }
>>> +
>>> +    if (data->id_det_gpio) {
>>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>>> +        if (ret) {
>>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>>        for (i = 0; i < data->num_phys; i++) {
>>>            struct sun4i_usb_phy *phy = data->phys + i;
>>>            char name[16];
>>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>            phy_set_drvdata(phy->phy, &data->phys[i]);
>>>        }
>>>
>>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>>> +        data->phy0_poll = true;
>>> +
>>> +    if (data->id_det_irq >= 0) {
>>> +        ret = devm_request_irq(dev, data->id_det_irq,
>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>> +                "usb0-id-det", data);
>>> +        if (ret) {
>>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>> +    if (data->vbus_det_irq >= 0) {
>>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>> +                "usb0-vbus-det", data);
>>> +        if (ret) {
>>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>>        dev_set_drvdata(dev, data);
>>>        phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>>
>>>        return PTR_ERR_OR_ZERO(phy_provider);
>>>    }
>>>
>>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>>> +{
>>> +    struct device *dev = &pdev->dev;
>>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>>> +
>>> +    if (data->id_det_irq >= 0)
>>> +        devm_free_irq(dev, data->id_det_irq, data);
>>> +    if (data->vbus_det_irq >= 0)
>>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>>> +
>>> +    cancel_delayed_work_sync(&data->detect);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>>    static const struct of_device_id sun4i_usb_phy_of_match[] = {
>>>        { .compatible = "allwinner,sun4i-a10-usb-phy" },
>>>        { .compatible = "allwinner,sun5i-a13-usb-phy" },
>>> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>>>
>>>    static struct platform_driver sun4i_usb_phy_driver = {
>>>        .probe    = sun4i_usb_phy_probe,
>>> +    .remove    = sun4i_usb_phy_remove,
>>>        .driver = {
>>>            .of_match_table    = sun4i_usb_phy_of_match,
>>> -        .name  = "sun4i-usb-phy",
>>> +        .name  = DRIVER_NAME,
>>>        }
>>>    };
>>>    module_platform_driver(sun4i_usb_phy_driver);
>>>
>>
>
> Thanks,
> Chanwoo Choi
>

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

* Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-11  7:56             ` Hans de Goede
@ 2015-06-11  8:28                 ` Hans de Goede
  -1 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-11  8:28 UTC (permalink / raw)
  To: kishon-l0cyMroinI0, Felipe Balbi
  Cc: Maxime Ripard, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Chanwoo Choi

Hi,

On 11-06-15 09:56, Hans de Goede wrote:
> Hi,
>
> On 11-06-15 07:48, Kishon Vijay Abraham I wrote:
>> +Chanwoo
>>
>> Hi,
>>
>> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>>> The usb0 phy is connected to an OTG controller, and as such needs some special
>>> handling:
>>>
>>> 1) It allows explicit control over the pullups, enable these on phy_init and
>>> disable them on phy_exit.
>>>
>>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>>> these bits to the correct values for operating in host only mode when no
>>> gpios are specified in the devicetree.
>>>
>>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>>> device cable is plugged in, so when in dual role mode register an extcon.
>>>
>>> While updating the devicetree binding documentation also add documentation
>>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>>
>>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>> ---
>>> Changes in v2:
>>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>>   has been moved to the phy-sun4i-usb driver and their status is exported
>>>   through extcon for the sunxi-musb glue
>>> Changes in v3:
>>> -No changes
>>> Changes in v4:
>>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>>   is present
>>> ---
>>>   .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>>   drivers/phy/Kconfig                                |   1 +
>>>   drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>>   3 files changed, 281 insertions(+), 11 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>> index 16528b9..557fa99 100644
>>> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>> @@ -23,6 +23,13 @@ Required properties:
>>>     * "usb1_reset"
>>>     * "usb2_reset" for sun4i, sun6i or sun7i
>>>
>>> +Optional properties:
>>> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
>>> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
>>> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
>>> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
>>> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
>>> +
>>>   Example:
>>>       usbphy: phy@0x01c13400 {
>>>           #phy-cells = <1>;
>>> @@ -32,6 +39,13 @@ Example:
>>>           reg-names = "phy_ctrl", "pmu1", "pmu2";
>>>           clocks = <&usb_clk 8>;
>>>           clock-names = "usb_phy";
>>> -        resets = <&usb_clk 1>, <&usb_clk 2>;
>>> -        reset-names = "usb1_reset", "usb2_reset";
>>> +        resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
>>> +        reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
>>> +        pinctrl-names = "default";
>>> +        pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
>>> +        usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
>>> +        usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
>>> +        usb0_vbus-supply = <&reg_usb0_vbus>;
>>> +        usb1_vbus-supply = <&reg_usb1_vbus>;
>>> +        usb2_vbus-supply = <&reg_usb2_vbus>;
>>>       };
>>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>>> index a53bd5b..4614fba 100644
>>> --- a/drivers/phy/Kconfig
>>> +++ b/drivers/phy/Kconfig
>>> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>>>       tristate "Allwinner sunxi SoC USB PHY driver"
>>>       depends on ARCH_SUNXI && HAS_IOMEM && OF
>>>       depends on RESET_CONTROLLER
>>> +    select EXTCON
>>
>> Avoid using 'select' on visible Kconfig symbols.
>
> Ok, I'll do a v5 changing this to a depends on.
>
>> Also please split the patch to make the reviewing a bit easier.
>
> There really is not much which can be split out, the iscr register
> updating is only done from the id / vbus det scanning, so the 2 are
> intertwined with each other.
>
> Also I must say I'm unhappy with the fact that you request this now,
> after this patch has been posted in its current form a long time
> ago already. The changes in v3 and v4 were very minimal, the
> patch has existed in this for since v2, so you've had a long long
> time to request changes like this already.
>
> In the mean time I've build a whole set of related changes (enabling
> the use of otg on newer versions of the sunxi soc), so sorry I'm
> not going to split this up now as that is going to be a big pain
> with all the extra patches which are sitting on top, and as said
> there really is not that much to split in the first place.
>
> I'll send out a v5 of this patch together with a resend of all the
> patches adding support for new models which sit on top of this one
> (and which I've already send before).

Correction: Since I need to rework the extcon bits for the new extcon
API in 4.2 anyways, I will split out the extcon bits, so you will
get 2 patches in v5, one adding the id / vbus det gpio scanning +
iscr register updating to reflect the detected status and one on
top adding extcon support so that the musb glue can respond
to id-pin changes.


Regards,

Hans

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

* [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11  8:28                 ` Hans de Goede
  0 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-11  8:28 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 11-06-15 09:56, Hans de Goede wrote:
> Hi,
>
> On 11-06-15 07:48, Kishon Vijay Abraham I wrote:
>> +Chanwoo
>>
>> Hi,
>>
>> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>>> The usb0 phy is connected to an OTG controller, and as such needs some special
>>> handling:
>>>
>>> 1) It allows explicit control over the pullups, enable these on phy_init and
>>> disable them on phy_exit.
>>>
>>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>>> these bits to the correct values for operating in host only mode when no
>>> gpios are specified in the devicetree.
>>>
>>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>>> device cable is plugged in, so when in dual role mode register an extcon.
>>>
>>> While updating the devicetree binding documentation also add documentation
>>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>>
>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>>> ---
>>> Changes in v2:
>>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>>   has been moved to the phy-sun4i-usb driver and their status is exported
>>>   through extcon for the sunxi-musb glue
>>> Changes in v3:
>>> -No changes
>>> Changes in v4:
>>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>>   is present
>>> ---
>>>   .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>>   drivers/phy/Kconfig                                |   1 +
>>>   drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>>   3 files changed, 281 insertions(+), 11 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>> index 16528b9..557fa99 100644
>>> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>> @@ -23,6 +23,13 @@ Required properties:
>>>     * "usb1_reset"
>>>     * "usb2_reset" for sun4i, sun6i or sun7i
>>>
>>> +Optional properties:
>>> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
>>> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
>>> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
>>> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
>>> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
>>> +
>>>   Example:
>>>       usbphy: phy at 0x01c13400 {
>>>           #phy-cells = <1>;
>>> @@ -32,6 +39,13 @@ Example:
>>>           reg-names = "phy_ctrl", "pmu1", "pmu2";
>>>           clocks = <&usb_clk 8>;
>>>           clock-names = "usb_phy";
>>> -        resets = <&usb_clk 1>, <&usb_clk 2>;
>>> -        reset-names = "usb1_reset", "usb2_reset";
>>> +        resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
>>> +        reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
>>> +        pinctrl-names = "default";
>>> +        pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
>>> +        usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
>>> +        usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
>>> +        usb0_vbus-supply = <&reg_usb0_vbus>;
>>> +        usb1_vbus-supply = <&reg_usb1_vbus>;
>>> +        usb2_vbus-supply = <&reg_usb2_vbus>;
>>>       };
>>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>>> index a53bd5b..4614fba 100644
>>> --- a/drivers/phy/Kconfig
>>> +++ b/drivers/phy/Kconfig
>>> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>>>       tristate "Allwinner sunxi SoC USB PHY driver"
>>>       depends on ARCH_SUNXI && HAS_IOMEM && OF
>>>       depends on RESET_CONTROLLER
>>> +    select EXTCON
>>
>> Avoid using 'select' on visible Kconfig symbols.
>
> Ok, I'll do a v5 changing this to a depends on.
>
>> Also please split the patch to make the reviewing a bit easier.
>
> There really is not much which can be split out, the iscr register
> updating is only done from the id / vbus det scanning, so the 2 are
> intertwined with each other.
>
> Also I must say I'm unhappy with the fact that you request this now,
> after this patch has been posted in its current form a long time
> ago already. The changes in v3 and v4 were very minimal, the
> patch has existed in this for since v2, so you've had a long long
> time to request changes like this already.
>
> In the mean time I've build a whole set of related changes (enabling
> the use of otg on newer versions of the sunxi soc), so sorry I'm
> not going to split this up now as that is going to be a big pain
> with all the extra patches which are sitting on top, and as said
> there really is not that much to split in the first place.
>
> I'll send out a v5 of this patch together with a resend of all the
> patches adding support for new models which sit on top of this one
> (and which I've already send before).

Correction: Since I need to rework the extcon bits for the new extcon
API in 4.2 anyways, I will split out the extcon bits, so you will
get 2 patches in v5, one adding the id / vbus det gpio scanning +
iscr register updating to reflect the detected status and one on
top adding extcon support so that the musb glue can respond
to id-pin changes.


Regards,

Hans

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

* Re: Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-11  8:21                 ` Hans de Goede
@ 2015-06-11  8:29                     ` Chanwoo Choi
  -1 siblings, 0 replies; 57+ messages in thread
From: Chanwoo Choi @ 2015-06-11  8:29 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Kishon Vijay Abraham I, Felipe Balbi, Maxime Ripard,
	Chen-Yu Tsai, Roman Byshko, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi Hans,

On 06/11/2015 05:21 PM, Hans de Goede wrote:
> Hi Chanwoo,
> 
> Thanks for the quick review.
> 
> On 11-06-15 10:07, Chanwoo Choi wrote:
>> Hi Hans,
>>
>> I add the comment about extcon-related code.
>>
>> Firstly,
>> I'd like you to implment the extcon driver for phy-sun4i-usb device
>> in drivers/extcon/ directoryby using MFD
> 
> No, just no, this is not what the MFD framework is for, the usb-phy
> in question here is not a multifunction device. The MFD framework
> is intended for true multi-function devices like i2c attached
> PMICs which have regulators, gpios, pwm, input (power button),
> chargers, power-supply, etc. That is NOT the case here.
> 
> Also moving this to the MFD framework would very likely requiring
> the devicetree binding for the usb-phy to change which we cannot
> do as that would break the devicetree ABI.
> 
>> because there are both extcon
>> provider driver and extcon client driver. I think that all extcon
>> provider driver better to be included in drivers/extcon/ directory.
>> extcon_set_cable_state() function should be handled in extcon provider
>> driver which is incluced in drivers/extcon/ directory.
> 
> I do not find this a compelling reason, there are plenty of subsystems
> where not all implementations of the subsystem class live in the subsystem
> directory, e.g. input and hwmon devices are often also found outside of
> the input and hwmon driver directories.

There are difference on between input/hwmon and extcon.

Because input and hwmon driver implement the only one type driver as provider driver.
But, extcon implement the two type driver of both extcon provider and extcon client driver.
The extcon is similiar with regulator and clock framework as resource.

extcon provider driver to provider the event when the state of external connector is changed.
- devm_extcon_dev_register()
- e.g., almost extcon provider driver are included in 'drivers/extcon/' directory.

extcon client driver to receive the event when the state of external connector is changed.
- extcon_register_notifier() or extcon_register_interest()(deprecated API)
- e.g., drivers/power/charger-manager.c, drivers/usb/dwc3/dwc3-omap.c etc.

Thanks,
Chanwoo Choi

> 
>>
>> On 06/11/2015 02:48 PM, Kishon Vijay Abraham I wrote:
>>> +Chanwoo
>>>
>>> Hi,
>>>
>>> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>>>> The usb0 phy is connected to an OTG controller, and as such needs some special
>>>> handling:
>>>>
>>>> 1) It allows explicit control over the pullups, enable these on phy_init and
>>>> disable them on phy_exit.
>>>>
>>>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>>>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>>>> these bits to the correct values for operating in host only mode when no
>>>> gpios are specified in the devicetree.
>>>>
>>>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>>>> device cable is plugged in, so when in dual role mode register an extcon.
>>>>
>>>> While updating the devicetree binding documentation also add documentation
>>>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>>>
>>>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>>> ---
>>>> Changes in v2:
>>>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>>>    has been moved to the phy-sun4i-usb driver and their status is exported
>>>>    through extcon for the sunxi-musb glue
>>>> Changes in v3:
>>>> -No changes
>>>> Changes in v4:
>>>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>>>    is present
>>>> ---
>>>>    .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>>>    drivers/phy/Kconfig                                |   1 +
>>>>    drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>>>    3 files changed, 281 insertions(+), 11 deletions(-)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>>> index 16528b9..557fa99 100644
>>>> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>>> @@ -23,6 +23,13 @@ Required properties:
>>>>      * "usb1_reset"
>>>>      * "usb2_reset" for sun4i, sun6i or sun7i
>>>>
>>>> +Optional properties:
>>>> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
>>>> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
>>>> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
>>>> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
>>>> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
>>>> +
>>>>    Example:
>>>>        usbphy: phy@0x01c13400 {
>>>>            #phy-cells = <1>;
>>>> @@ -32,6 +39,13 @@ Example:
>>>>            reg-names = "phy_ctrl", "pmu1", "pmu2";
>>>>            clocks = <&usb_clk 8>;
>>>>            clock-names = "usb_phy";
>>>> -        resets = <&usb_clk 1>, <&usb_clk 2>;
>>>> -        reset-names = "usb1_reset", "usb2_reset";
>>>> +        resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
>>>> +        reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
>>>> +        pinctrl-names = "default";
>>>> +        pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
>>>> +        usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
>>>> +        usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
>>>> +        usb0_vbus-supply = <&reg_usb0_vbus>;
>>>> +        usb1_vbus-supply = <&reg_usb1_vbus>;
>>>> +        usb2_vbus-supply = <&reg_usb2_vbus>;
>>>>        };
>>>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>>>> index a53bd5b..4614fba 100644
>>>> --- a/drivers/phy/Kconfig
>>>> +++ b/drivers/phy/Kconfig
>>>> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>>>>        tristate "Allwinner sunxi SoC USB PHY driver"
>>>>        depends on ARCH_SUNXI && HAS_IOMEM && OF
>>>>        depends on RESET_CONTROLLER
>>>> +    select EXTCON
>>>
>>> Avoid using 'select' on visible Kconfig symbols.
>>>
>>> Also please split the patch to make the reviewing a bit easier.
>>>
>>> Thanks
>>> Kishon
>>>>        select GENERIC_PHY
>>>>        help
>>>>          Enable this to support the transceiver that is part of Allwinner
>>>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>>>> index 91c5be4..b45d707 100644
>>>> --- a/drivers/phy/phy-sun4i-usb.c
>>>> +++ b/drivers/phy/phy-sun4i-usb.c
>>>> @@ -1,7 +1,7 @@
>>>>    /*
>>>>     * Allwinner sun4i USB phy driver
>>>>     *
>>>> - * Copyright (C) 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>>> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>>>     *
>>>>     * Based on code from
>>>>     * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>>>> @@ -23,17 +23,23 @@
>>>>
>>>>    #include <linux/clk.h>
>>>>    #include <linux/err.h>
>>>> +#include <linux/extcon.h>
>>>>    #include <linux/io.h>
>>>> +#include <linux/interrupt.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_gpio.h>
>>>>    #include <linux/phy/phy.h>
>>>>    #include <linux/phy/phy-sun4i-usb.h>
>>>>    #include <linux/platform_device.h>
>>>>    #include <linux/regulator/consumer.h>
>>>>    #include <linux/reset.h>
>>>> +#include <linux/workqueue.h>
>>>> +
>>>> +#define DRIVER_NAME "sun4i-usb-phy"
>>>>
>>>>    #define REG_ISCR            0x00
>>>>    #define REG_PHYCTL            0x04
>>>> @@ -47,6 +53,17 @@
>>>>    #define SUNXI_AHB_INCRX_ALIGN_EN    BIT(8)
>>>>    #define SUNXI_ULPI_BYPASS_EN        BIT(0)
>>>>
>>>> +/* ISCR, Interface Status and Control bits */
>>>> +#define ISCR_ID_PULLUP_EN        (1 << 17)
>>>> +#define ISCR_DPDM_PULLUP_EN    (1 << 16)
>>>> +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
>>>> +#define ISCR_FORCE_ID_MASK    (3 << 14)
>>>> +#define ISCR_FORCE_ID_LOW        (2 << 14)
>>>> +#define ISCR_FORCE_ID_HIGH    (3 << 14)
>>>> +#define ISCR_FORCE_VBUS_MASK    (3 << 12)
>>>> +#define ISCR_FORCE_VBUS_LOW    (2 << 12)
>>>> +#define ISCR_FORCE_VBUS_HIGH    (3 << 12)
>>>> +
>>>>    /* Common Control Bits for Both PHYs */
>>>>    #define PHY_PLL_BW            0x03
>>>>    #define PHY_RES45_CAL_EN        0x0c
>>>> @@ -63,6 +80,13 @@
>>>>
>>>>    #define MAX_PHYS            3
>>>>
>>>> +/*
>>>> + * Note do not raise the debounce time, we must report Vusb high within 100ms
>>>> + * otherwise we get Vbus errors
>>>> + */
>>>> +#define DEBOUNCE_TIME            msecs_to_jiffies(50)
>>>> +#define POLL_TIME            msecs_to_jiffies(250)
>>>> +
>>>>    struct sun4i_usb_phy_data {
>>>>        void __iomem *base;
>>>>        struct mutex mutex;
>>>> @@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
>>>>            struct regulator *vbus;
>>>>            struct reset_control *reset;
>>>>            struct clk *clk;
>>>> +        bool regulator_on;
>>>>            int index;
>>>>        } phys[MAX_PHYS];
>>>> +    /* phy0 / otg related variables */
>>>> +    struct extcon_dev extcon;
>>>> +    const char *extcon_cable_names[3];
>>
>> The latest extcon use the unique id to indicate the each external connector
>> instead of legacy string. The related patch[1] and patch[2] will be merged on v4.2-rc1.
>>
>> [1] extcon: Use the unique id for external connector instead of string
>> - http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=2a9de9c0f08d61fbe3764a21d22d0b72df97d6ae
>> [2] extcon: Redefine the unique id of supported external connectors without 'enum extcon' type
>> - http://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/commit/?h=extcon-next-v4.3&id=bbd8d8b4244bb2799143c959fde57b1f034ec838
>>
>> For exmaple in drivers/extcon/extcon-usb-gpio.c:
>> Maybe you can add the similiar array which include the supported external connector.
>>
>> static const unsigned int usb_extcon_cable[] = {
>>     EXTCON_USB,
>>     EXTCON_USB_HOST,
>>     EXTCON_NONE,
>> };
> 
> 
> Ah, that is good to know, I'll port the extcon code in phy-sun4i-usb.c to
> this new API for the next version of the patch.
> 
> Thanks & Regards,
> 
> Hans
> 
> 
>>
>>>> +    bool phy0_init;
>>>> +    bool phy0_poll;
>>>> +    struct gpio_desc *id_det_gpio;
>>>> +    struct gpio_desc *vbus_det_gpio;
>>>> +    int id_det_irq;
>>>> +    int vbus_det_irq;
>>>> +    int id_det;
>>>> +    int vbus_det;
>>>> +    struct delayed_work detect;
>>>>    };
>>>>
>>>>    #define to_sun4i_usb_phy_data(phy) \
>>>>        container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>>>>
>>>> +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
>>>> +{
>>>> +    struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>>> +    u32 iscr;
>>>> +
>>>> +    iscr = readl(data->base + REG_ISCR);
>>>> +    iscr &= ~clr;
>>>> +    iscr |= set;
>>>> +    writel(iscr, data->base + REG_ISCR);
>>>> +}
>>>> +
>>>> +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
>>>> +{
>>>> +    if (val)
>>>> +        val = ISCR_FORCE_ID_HIGH;
>>>> +    else
>>>> +        val = ISCR_FORCE_ID_LOW;
>>>> +
>>>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
>>>> +}
>>>> +
>>>> +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
>>>> +{
>>>> +    if (val)
>>>> +        val = ISCR_FORCE_VBUS_HIGH;
>>>> +    else
>>>> +        val = ISCR_FORCE_VBUS_LOW;
>>>> +
>>>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
>>>> +}
>>>> +
>>>>    static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>>>>                    int len)
>>>>    {
>>>> @@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
>>>>
>>>>        sun4i_usb_phy_passby(phy, 1);
>>>>
>>>> +    if (phy->index == 0) {
>>>> +        data->phy0_init = true;
>>>> +
>>>> +        /* Enable pull-ups */
>>>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
>>>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
>>>> +
>>>> +        if (data->id_det_gpio) {
>>>> +            /* OTG mode, force ISCR and cable state updates */
>>>> +            data->id_det = -1;
>>>> +            data->vbus_det = -1;
>>>> +            queue_delayed_work(system_wq, &data->detect, 0);
>>>> +        } else {
>>>> +            /* Host only mode */
>>>> +            sun4i_usb_phy0_set_id_detect(_phy, 0);
>>>> +            sun4i_usb_phy0_set_vbus_detect(_phy, 1);
>>>> +        }
>>>> +    }
>>>> +
>>>>        return 0;
>>>>    }
>>>>
>>>>    static int sun4i_usb_phy_exit(struct phy *_phy)
>>>>    {
>>>>        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>>> +
>>>> +    if (phy->index == 0) {
>>>> +        /* Disable pull-ups */
>>>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
>>>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
>>>> +        data->phy0_init = false;
>>>> +    }
>>>>
>>>>        sun4i_usb_phy_passby(phy, 0);
>>>>        reset_control_assert(phy->reset);
>>>> @@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
>>>>    static int sun4i_usb_phy_power_on(struct phy *_phy)
>>>>    {
>>>>        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>>> -    int ret = 0;
>>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>>> +    int ret;
>>>> +
>>>> +    if (!phy->vbus || phy->regulator_on)
>>>> +        return 0;
>>>> +
>>>> +    /* For phy0 only turn on Vbus if we don't have an ext. Vbus */
>>>> +    if (phy->index == 0 && data->vbus_det)
>>>> +        return 0;
>>>>
>>>> -    if (phy->vbus)
>>>> -        ret = regulator_enable(phy->vbus);
>>>> +    ret = regulator_enable(phy->vbus);
>>>> +    if (ret)
>>>> +        return ret;
>>>>
>>>> -    return ret;
>>>> +    phy->regulator_on = true;
>>>> +
>>>> +    /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
>>>> +    if (phy->index == 0 && data->phy0_poll)
>>>> +        mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>>>> +
>>>> +    return 0;
>>>>    }
>>>>
>>>>    static int sun4i_usb_phy_power_off(struct phy *_phy)
>>>>    {
>>>>        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>>> +
>>>> +    if (!phy->vbus || !phy->regulator_on)
>>>> +        return 0;
>>>>
>>>> -    if (phy->vbus)
>>>> -        regulator_disable(phy->vbus);
>>>> +    regulator_disable(phy->vbus);
>>>> +    phy->regulator_on = false;
>>>> +
>>>> +    /*
>>>> +     * phy0 vbus typically slowly discharges, sometimes this causes the
>>>> +     * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
>>>> +     */
>>>> +    if (phy->index == 0 && !data->phy0_poll)
>>>> +        mod_delayed_work(system_wq, &data->detect, POLL_TIME);
>>>>
>>>>        return 0;
>>>>    }
>>>> @@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
>>>>        .owner        = THIS_MODULE,
>>>>    };
>>>>
>>>> +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
>>>> +{
>>>> +    struct sun4i_usb_phy_data *data =
>>>> +        container_of(work, struct sun4i_usb_phy_data, detect.work);
>>>> +    struct phy *phy0 = data->phys[0].phy;
>>>> +    int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
>>>> +
>>>> +    id_det = gpiod_get_value_cansleep(data->id_det_gpio);
>>>> +    vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
>>>> +
>>>> +    mutex_lock(&phy0->mutex);
>>>> +
>>>> +    if (!data->phy0_init) {
>>>> +        mutex_unlock(&phy0->mutex);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    if (id_det != data->id_det) {
>>>> +        sun4i_usb_phy0_set_id_detect(phy0, id_det);
>>>> +        data->id_det = id_det;
>>>> +        id_notify = 1;
>>>> +    }
>>>> +
>>>> +    if (vbus_det != data->vbus_det) {
>>>> +        sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
>>>> +        data->vbus_det = vbus_det;
>>>> +        vbus_notify = 1;
>>>> +    }
>>>> +
>>>> +    mutex_unlock(&phy0->mutex);
>>>> +
>>>> +    if (id_notify)
>>>> +        extcon_set_cable_state(&data->extcon,
>>>> +                       extcon_cable_name[EXTCON_USB_HOST],
>>>> +                       !id_det);
>>>> +
>>>> +    if (vbus_notify)
>>>> +        extcon_set_cable_state(&data->extcon,
>>>> +                       extcon_cable_name[EXTCON_USB],
>>>> +                       vbus_det);
>>
>> I don't want to use the legacy extcon API with string name.
>> Instead, you can use the recommeded extcon_set_cable_state_() API as following:
>>
>> - Legacy extcon API
>> : extern int extcon_set_cable_state(struct extcon_dev *edev,
>>                   const char *cable_name, bool cable_state);
>> - Recommended extcon API
>> : extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
>>                    bool cable_state);
>>
>> [1] extcon: Use the unique id for external connector instead of string
>> - http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=2a9de9c0f08d61fbe3764a21d22d0b72df97d6ae
>>
>>>> +
>>>> +    if (data->phy0_poll)
>>>> +        queue_delayed_work(system_wq, &data->detect, POLL_TIME);
>>>> +}
>>>> +
>>>> +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
>>>> +{
>>>> +    struct sun4i_usb_phy_data *data = dev_id;
>>>> +
>>>> +    /* vbus or id changed, let the pins settle and then scan them */
>>>> +    mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>>>> +
>>>> +    return IRQ_HANDLED;
>>>> +}
>>>> +
>>>>    static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>>>                        struct of_phandle_args *args)
>>>>    {
>>>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>        struct phy_provider *phy_provider;
>>>>        bool dedicated_clocks;
>>>>        struct resource *res;
>>>> -    int i;
>>>> +    int i, ret;
>>>>
>>>>        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>>>        if (!data)
>>>>            return -ENOMEM;
>>>>
>>>>        mutex_init(&data->mutex);
>>>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>>>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>>>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>>>> +    data->extcon_cable_names[2] = NULL;
>>>> +    data->extcon.name = DRIVER_NAME;
>>
>> Don't need it because extcon_dev_register() set the device name automatically.
>> You can check it on patch[3].
>> [3] http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=71c3ffa5d23af0554c27010cf12710da9bf85950
>>
>>>> +    data->extcon.supported_cable = data->extcon_cable_names;
>>
>> You can use the devm_extcon_dev_allocate() to set the supported external connectors.
>>
>>>> +    data->extcon.dev.parent = dev;
>>
>> Don't need it because extcon_dev_register() set the parent device automatically.
>>
>> The field of 'struct extcon_dev' shold be filled with extcon API without direct allocation.
>>
>>>>
>>>>        if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>>>            data->num_phys = 2;
>>>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>        if (IS_ERR(data->base))
>>>>            return PTR_ERR(data->base);
>>>>
>>>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>>>> +    if (IS_ERR(data->id_det_gpio)) {
>>>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>>>> +            return -EPROBE_DEFER;
>>>> +        data->id_det_gpio = NULL;
>>>> +    }
>>>> +
>>>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>>>> +    if (IS_ERR(data->vbus_det_gpio)) {
>>>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>>>> +            return -EPROBE_DEFER;
>>>> +        data->vbus_det_gpio = NULL;
>>>> +    }
>>>> +
>>>> +    /* We either want both gpio pins or neither (when in host mode) */
>>>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>>>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>>>> +        return -ENODEV;
>>>> +    }
>>>> +
>>>> +    if (data->id_det_gpio) {
>>>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>>>> +        if (ret) {
>>>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>>>> +            return ret;
>>>> +        }
>>>> +    }
>>>> +
>>>>        for (i = 0; i < data->num_phys; i++) {
>>>>            struct sun4i_usb_phy *phy = data->phys + i;
>>>>            char name[16];
>>>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>            phy_set_drvdata(phy->phy, &data->phys[i]);
>>>>        }
>>>>
>>>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>>>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>>>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>>>> +        data->phy0_poll = true;
>>>> +
>>>> +    if (data->id_det_irq >= 0) {
>>>> +        ret = devm_request_irq(dev, data->id_det_irq,
>>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>>> +                "usb0-id-det", data);
>>>> +        if (ret) {
>>>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>>>> +            return ret;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    if (data->vbus_det_irq >= 0) {
>>>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>>> +                "usb0-vbus-det", data);
>>>> +        if (ret) {
>>>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>>>> +            return ret;
>>>> +        }
>>>> +    }
>>>> +
>>>>        dev_set_drvdata(dev, data);
>>>>        phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>>>
>>>>        return PTR_ERR_OR_ZERO(phy_provider);
>>>>    }
>>>>
>>>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>>>> +{
>>>> +    struct device *dev = &pdev->dev;
>>>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>>>> +
>>>> +    if (data->id_det_irq >= 0)
>>>> +        devm_free_irq(dev, data->id_det_irq, data);
>>>> +    if (data->vbus_det_irq >= 0)
>>>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>>>> +
>>>> +    cancel_delayed_work_sync(&data->detect);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>>    static const struct of_device_id sun4i_usb_phy_of_match[] = {
>>>>        { .compatible = "allwinner,sun4i-a10-usb-phy" },
>>>>        { .compatible = "allwinner,sun5i-a13-usb-phy" },
>>>> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>>>>
>>>>    static struct platform_driver sun4i_usb_phy_driver = {
>>>>        .probe    = sun4i_usb_phy_probe,
>>>> +    .remove    = sun4i_usb_phy_remove,
>>>>        .driver = {
>>>>            .of_match_table    = sun4i_usb_phy_of_match,
>>>> -        .name  = "sun4i-usb-phy",
>>>> +        .name  = DRIVER_NAME,
>>>>        }
>>>>    };
>>>>    module_platform_driver(sun4i_usb_phy_driver);
>>>>
>>>
>>
>> Thanks,
>> Chanwoo Choi
>>
> 

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

* [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11  8:29                     ` Chanwoo Choi
  0 siblings, 0 replies; 57+ messages in thread
From: Chanwoo Choi @ 2015-06-11  8:29 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Hans,

On 06/11/2015 05:21 PM, Hans de Goede wrote:
> Hi Chanwoo,
> 
> Thanks for the quick review.
> 
> On 11-06-15 10:07, Chanwoo Choi wrote:
>> Hi Hans,
>>
>> I add the comment about extcon-related code.
>>
>> Firstly,
>> I'd like you to implment the extcon driver for phy-sun4i-usb device
>> in drivers/extcon/ directoryby using MFD
> 
> No, just no, this is not what the MFD framework is for, the usb-phy
> in question here is not a multifunction device. The MFD framework
> is intended for true multi-function devices like i2c attached
> PMICs which have regulators, gpios, pwm, input (power button),
> chargers, power-supply, etc. That is NOT the case here.
> 
> Also moving this to the MFD framework would very likely requiring
> the devicetree binding for the usb-phy to change which we cannot
> do as that would break the devicetree ABI.
> 
>> because there are both extcon
>> provider driver and extcon client driver. I think that all extcon
>> provider driver better to be included in drivers/extcon/ directory.
>> extcon_set_cable_state() function should be handled in extcon provider
>> driver which is incluced in drivers/extcon/ directory.
> 
> I do not find this a compelling reason, there are plenty of subsystems
> where not all implementations of the subsystem class live in the subsystem
> directory, e.g. input and hwmon devices are often also found outside of
> the input and hwmon driver directories.

There are difference on between input/hwmon and extcon.

Because input and hwmon driver implement the only one type driver as provider driver.
But, extcon implement the two type driver of both extcon provider and extcon client driver.
The extcon is similiar with regulator and clock framework as resource.

extcon provider driver to provider the event when the state of external connector is changed.
- devm_extcon_dev_register()
- e.g., almost extcon provider driver are included in 'drivers/extcon/' directory.

extcon client driver to receive the event when the state of external connector is changed.
- extcon_register_notifier() or extcon_register_interest()(deprecated API)
- e.g., drivers/power/charger-manager.c, drivers/usb/dwc3/dwc3-omap.c etc.

Thanks,
Chanwoo Choi

> 
>>
>> On 06/11/2015 02:48 PM, Kishon Vijay Abraham I wrote:
>>> +Chanwoo
>>>
>>> Hi,
>>>
>>> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>>>> The usb0 phy is connected to an OTG controller, and as such needs some special
>>>> handling:
>>>>
>>>> 1) It allows explicit control over the pullups, enable these on phy_init and
>>>> disable them on phy_exit.
>>>>
>>>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>>>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>>>> these bits to the correct values for operating in host only mode when no
>>>> gpios are specified in the devicetree.
>>>>
>>>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>>>> device cable is plugged in, so when in dual role mode register an extcon.
>>>>
>>>> While updating the devicetree binding documentation also add documentation
>>>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>>>
>>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>>>> ---
>>>> Changes in v2:
>>>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>>>    has been moved to the phy-sun4i-usb driver and their status is exported
>>>>    through extcon for the sunxi-musb glue
>>>> Changes in v3:
>>>> -No changes
>>>> Changes in v4:
>>>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>>>    is present
>>>> ---
>>>>    .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>>>    drivers/phy/Kconfig                                |   1 +
>>>>    drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>>>    3 files changed, 281 insertions(+), 11 deletions(-)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>>> index 16528b9..557fa99 100644
>>>> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>>>> @@ -23,6 +23,13 @@ Required properties:
>>>>      * "usb1_reset"
>>>>      * "usb2_reset" for sun4i, sun6i or sun7i
>>>>
>>>> +Optional properties:
>>>> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
>>>> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
>>>> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
>>>> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
>>>> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
>>>> +
>>>>    Example:
>>>>        usbphy: phy at 0x01c13400 {
>>>>            #phy-cells = <1>;
>>>> @@ -32,6 +39,13 @@ Example:
>>>>            reg-names = "phy_ctrl", "pmu1", "pmu2";
>>>>            clocks = <&usb_clk 8>;
>>>>            clock-names = "usb_phy";
>>>> -        resets = <&usb_clk 1>, <&usb_clk 2>;
>>>> -        reset-names = "usb1_reset", "usb2_reset";
>>>> +        resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
>>>> +        reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
>>>> +        pinctrl-names = "default";
>>>> +        pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
>>>> +        usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
>>>> +        usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
>>>> +        usb0_vbus-supply = <&reg_usb0_vbus>;
>>>> +        usb1_vbus-supply = <&reg_usb1_vbus>;
>>>> +        usb2_vbus-supply = <&reg_usb2_vbus>;
>>>>        };
>>>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>>>> index a53bd5b..4614fba 100644
>>>> --- a/drivers/phy/Kconfig
>>>> +++ b/drivers/phy/Kconfig
>>>> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>>>>        tristate "Allwinner sunxi SoC USB PHY driver"
>>>>        depends on ARCH_SUNXI && HAS_IOMEM && OF
>>>>        depends on RESET_CONTROLLER
>>>> +    select EXTCON
>>>
>>> Avoid using 'select' on visible Kconfig symbols.
>>>
>>> Also please split the patch to make the reviewing a bit easier.
>>>
>>> Thanks
>>> Kishon
>>>>        select GENERIC_PHY
>>>>        help
>>>>          Enable this to support the transceiver that is part of Allwinner
>>>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>>>> index 91c5be4..b45d707 100644
>>>> --- a/drivers/phy/phy-sun4i-usb.c
>>>> +++ b/drivers/phy/phy-sun4i-usb.c
>>>> @@ -1,7 +1,7 @@
>>>>    /*
>>>>     * Allwinner sun4i USB phy driver
>>>>     *
>>>> - * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
>>>> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede@redhat.com>
>>>>     *
>>>>     * Based on code from
>>>>     * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>>>> @@ -23,17 +23,23 @@
>>>>
>>>>    #include <linux/clk.h>
>>>>    #include <linux/err.h>
>>>> +#include <linux/extcon.h>
>>>>    #include <linux/io.h>
>>>> +#include <linux/interrupt.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_gpio.h>
>>>>    #include <linux/phy/phy.h>
>>>>    #include <linux/phy/phy-sun4i-usb.h>
>>>>    #include <linux/platform_device.h>
>>>>    #include <linux/regulator/consumer.h>
>>>>    #include <linux/reset.h>
>>>> +#include <linux/workqueue.h>
>>>> +
>>>> +#define DRIVER_NAME "sun4i-usb-phy"
>>>>
>>>>    #define REG_ISCR            0x00
>>>>    #define REG_PHYCTL            0x04
>>>> @@ -47,6 +53,17 @@
>>>>    #define SUNXI_AHB_INCRX_ALIGN_EN    BIT(8)
>>>>    #define SUNXI_ULPI_BYPASS_EN        BIT(0)
>>>>
>>>> +/* ISCR, Interface Status and Control bits */
>>>> +#define ISCR_ID_PULLUP_EN        (1 << 17)
>>>> +#define ISCR_DPDM_PULLUP_EN    (1 << 16)
>>>> +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
>>>> +#define ISCR_FORCE_ID_MASK    (3 << 14)
>>>> +#define ISCR_FORCE_ID_LOW        (2 << 14)
>>>> +#define ISCR_FORCE_ID_HIGH    (3 << 14)
>>>> +#define ISCR_FORCE_VBUS_MASK    (3 << 12)
>>>> +#define ISCR_FORCE_VBUS_LOW    (2 << 12)
>>>> +#define ISCR_FORCE_VBUS_HIGH    (3 << 12)
>>>> +
>>>>    /* Common Control Bits for Both PHYs */
>>>>    #define PHY_PLL_BW            0x03
>>>>    #define PHY_RES45_CAL_EN        0x0c
>>>> @@ -63,6 +80,13 @@
>>>>
>>>>    #define MAX_PHYS            3
>>>>
>>>> +/*
>>>> + * Note do not raise the debounce time, we must report Vusb high within 100ms
>>>> + * otherwise we get Vbus errors
>>>> + */
>>>> +#define DEBOUNCE_TIME            msecs_to_jiffies(50)
>>>> +#define POLL_TIME            msecs_to_jiffies(250)
>>>> +
>>>>    struct sun4i_usb_phy_data {
>>>>        void __iomem *base;
>>>>        struct mutex mutex;
>>>> @@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
>>>>            struct regulator *vbus;
>>>>            struct reset_control *reset;
>>>>            struct clk *clk;
>>>> +        bool regulator_on;
>>>>            int index;
>>>>        } phys[MAX_PHYS];
>>>> +    /* phy0 / otg related variables */
>>>> +    struct extcon_dev extcon;
>>>> +    const char *extcon_cable_names[3];
>>
>> The latest extcon use the unique id to indicate the each external connector
>> instead of legacy string. The related patch[1] and patch[2] will be merged on v4.2-rc1.
>>
>> [1] extcon: Use the unique id for external connector instead of string
>> - http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=2a9de9c0f08d61fbe3764a21d22d0b72df97d6ae
>> [2] extcon: Redefine the unique id of supported external connectors without 'enum extcon' type
>> - http://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/commit/?h=extcon-next-v4.3&id=bbd8d8b4244bb2799143c959fde57b1f034ec838
>>
>> For exmaple in drivers/extcon/extcon-usb-gpio.c:
>> Maybe you can add the similiar array which include the supported external connector.
>>
>> static const unsigned int usb_extcon_cable[] = {
>>     EXTCON_USB,
>>     EXTCON_USB_HOST,
>>     EXTCON_NONE,
>> };
> 
> 
> Ah, that is good to know, I'll port the extcon code in phy-sun4i-usb.c to
> this new API for the next version of the patch.
> 
> Thanks & Regards,
> 
> Hans
> 
> 
>>
>>>> +    bool phy0_init;
>>>> +    bool phy0_poll;
>>>> +    struct gpio_desc *id_det_gpio;
>>>> +    struct gpio_desc *vbus_det_gpio;
>>>> +    int id_det_irq;
>>>> +    int vbus_det_irq;
>>>> +    int id_det;
>>>> +    int vbus_det;
>>>> +    struct delayed_work detect;
>>>>    };
>>>>
>>>>    #define to_sun4i_usb_phy_data(phy) \
>>>>        container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>>>>
>>>> +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
>>>> +{
>>>> +    struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>>> +    u32 iscr;
>>>> +
>>>> +    iscr = readl(data->base + REG_ISCR);
>>>> +    iscr &= ~clr;
>>>> +    iscr |= set;
>>>> +    writel(iscr, data->base + REG_ISCR);
>>>> +}
>>>> +
>>>> +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
>>>> +{
>>>> +    if (val)
>>>> +        val = ISCR_FORCE_ID_HIGH;
>>>> +    else
>>>> +        val = ISCR_FORCE_ID_LOW;
>>>> +
>>>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
>>>> +}
>>>> +
>>>> +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
>>>> +{
>>>> +    if (val)
>>>> +        val = ISCR_FORCE_VBUS_HIGH;
>>>> +    else
>>>> +        val = ISCR_FORCE_VBUS_LOW;
>>>> +
>>>> +    sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
>>>> +}
>>>> +
>>>>    static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>>>>                    int len)
>>>>    {
>>>> @@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
>>>>
>>>>        sun4i_usb_phy_passby(phy, 1);
>>>>
>>>> +    if (phy->index == 0) {
>>>> +        data->phy0_init = true;
>>>> +
>>>> +        /* Enable pull-ups */
>>>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
>>>> +        sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
>>>> +
>>>> +        if (data->id_det_gpio) {
>>>> +            /* OTG mode, force ISCR and cable state updates */
>>>> +            data->id_det = -1;
>>>> +            data->vbus_det = -1;
>>>> +            queue_delayed_work(system_wq, &data->detect, 0);
>>>> +        } else {
>>>> +            /* Host only mode */
>>>> +            sun4i_usb_phy0_set_id_detect(_phy, 0);
>>>> +            sun4i_usb_phy0_set_vbus_detect(_phy, 1);
>>>> +        }
>>>> +    }
>>>> +
>>>>        return 0;
>>>>    }
>>>>
>>>>    static int sun4i_usb_phy_exit(struct phy *_phy)
>>>>    {
>>>>        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>>> +
>>>> +    if (phy->index == 0) {
>>>> +        /* Disable pull-ups */
>>>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
>>>> +        sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
>>>> +        data->phy0_init = false;
>>>> +    }
>>>>
>>>>        sun4i_usb_phy_passby(phy, 0);
>>>>        reset_control_assert(phy->reset);
>>>> @@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
>>>>    static int sun4i_usb_phy_power_on(struct phy *_phy)
>>>>    {
>>>>        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>>> -    int ret = 0;
>>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>>> +    int ret;
>>>> +
>>>> +    if (!phy->vbus || phy->regulator_on)
>>>> +        return 0;
>>>> +
>>>> +    /* For phy0 only turn on Vbus if we don't have an ext. Vbus */
>>>> +    if (phy->index == 0 && data->vbus_det)
>>>> +        return 0;
>>>>
>>>> -    if (phy->vbus)
>>>> -        ret = regulator_enable(phy->vbus);
>>>> +    ret = regulator_enable(phy->vbus);
>>>> +    if (ret)
>>>> +        return ret;
>>>>
>>>> -    return ret;
>>>> +    phy->regulator_on = true;
>>>> +
>>>> +    /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
>>>> +    if (phy->index == 0 && data->phy0_poll)
>>>> +        mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>>>> +
>>>> +    return 0;
>>>>    }
>>>>
>>>>    static int sun4i_usb_phy_power_off(struct phy *_phy)
>>>>    {
>>>>        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>>>> +    struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>>>> +
>>>> +    if (!phy->vbus || !phy->regulator_on)
>>>> +        return 0;
>>>>
>>>> -    if (phy->vbus)
>>>> -        regulator_disable(phy->vbus);
>>>> +    regulator_disable(phy->vbus);
>>>> +    phy->regulator_on = false;
>>>> +
>>>> +    /*
>>>> +     * phy0 vbus typically slowly discharges, sometimes this causes the
>>>> +     * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
>>>> +     */
>>>> +    if (phy->index == 0 && !data->phy0_poll)
>>>> +        mod_delayed_work(system_wq, &data->detect, POLL_TIME);
>>>>
>>>>        return 0;
>>>>    }
>>>> @@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
>>>>        .owner        = THIS_MODULE,
>>>>    };
>>>>
>>>> +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
>>>> +{
>>>> +    struct sun4i_usb_phy_data *data =
>>>> +        container_of(work, struct sun4i_usb_phy_data, detect.work);
>>>> +    struct phy *phy0 = data->phys[0].phy;
>>>> +    int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
>>>> +
>>>> +    id_det = gpiod_get_value_cansleep(data->id_det_gpio);
>>>> +    vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
>>>> +
>>>> +    mutex_lock(&phy0->mutex);
>>>> +
>>>> +    if (!data->phy0_init) {
>>>> +        mutex_unlock(&phy0->mutex);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    if (id_det != data->id_det) {
>>>> +        sun4i_usb_phy0_set_id_detect(phy0, id_det);
>>>> +        data->id_det = id_det;
>>>> +        id_notify = 1;
>>>> +    }
>>>> +
>>>> +    if (vbus_det != data->vbus_det) {
>>>> +        sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
>>>> +        data->vbus_det = vbus_det;
>>>> +        vbus_notify = 1;
>>>> +    }
>>>> +
>>>> +    mutex_unlock(&phy0->mutex);
>>>> +
>>>> +    if (id_notify)
>>>> +        extcon_set_cable_state(&data->extcon,
>>>> +                       extcon_cable_name[EXTCON_USB_HOST],
>>>> +                       !id_det);
>>>> +
>>>> +    if (vbus_notify)
>>>> +        extcon_set_cable_state(&data->extcon,
>>>> +                       extcon_cable_name[EXTCON_USB],
>>>> +                       vbus_det);
>>
>> I don't want to use the legacy extcon API with string name.
>> Instead, you can use the recommeded extcon_set_cable_state_() API as following:
>>
>> - Legacy extcon API
>> : extern int extcon_set_cable_state(struct extcon_dev *edev,
>>                   const char *cable_name, bool cable_state);
>> - Recommended extcon API
>> : extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
>>                    bool cable_state);
>>
>> [1] extcon: Use the unique id for external connector instead of string
>> - http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=2a9de9c0f08d61fbe3764a21d22d0b72df97d6ae
>>
>>>> +
>>>> +    if (data->phy0_poll)
>>>> +        queue_delayed_work(system_wq, &data->detect, POLL_TIME);
>>>> +}
>>>> +
>>>> +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
>>>> +{
>>>> +    struct sun4i_usb_phy_data *data = dev_id;
>>>> +
>>>> +    /* vbus or id changed, let the pins settle and then scan them */
>>>> +    mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>>>> +
>>>> +    return IRQ_HANDLED;
>>>> +}
>>>> +
>>>>    static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>>>                        struct of_phandle_args *args)
>>>>    {
>>>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>        struct phy_provider *phy_provider;
>>>>        bool dedicated_clocks;
>>>>        struct resource *res;
>>>> -    int i;
>>>> +    int i, ret;
>>>>
>>>>        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>>>        if (!data)
>>>>            return -ENOMEM;
>>>>
>>>>        mutex_init(&data->mutex);
>>>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>>>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>>>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>>>> +    data->extcon_cable_names[2] = NULL;
>>>> +    data->extcon.name = DRIVER_NAME;
>>
>> Don't need it because extcon_dev_register() set the device name automatically.
>> You can check it on patch[3].
>> [3] http://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/commit/?h=char-misc-next&id=71c3ffa5d23af0554c27010cf12710da9bf85950
>>
>>>> +    data->extcon.supported_cable = data->extcon_cable_names;
>>
>> You can use the devm_extcon_dev_allocate() to set the supported external connectors.
>>
>>>> +    data->extcon.dev.parent = dev;
>>
>> Don't need it because extcon_dev_register() set the parent device automatically.
>>
>> The field of 'struct extcon_dev' shold be filled with extcon API without direct allocation.
>>
>>>>
>>>>        if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>>>            data->num_phys = 2;
>>>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>        if (IS_ERR(data->base))
>>>>            return PTR_ERR(data->base);
>>>>
>>>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>>>> +    if (IS_ERR(data->id_det_gpio)) {
>>>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>>>> +            return -EPROBE_DEFER;
>>>> +        data->id_det_gpio = NULL;
>>>> +    }
>>>> +
>>>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>>>> +    if (IS_ERR(data->vbus_det_gpio)) {
>>>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>>>> +            return -EPROBE_DEFER;
>>>> +        data->vbus_det_gpio = NULL;
>>>> +    }
>>>> +
>>>> +    /* We either want both gpio pins or neither (when in host mode) */
>>>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>>>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>>>> +        return -ENODEV;
>>>> +    }
>>>> +
>>>> +    if (data->id_det_gpio) {
>>>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>>>> +        if (ret) {
>>>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>>>> +            return ret;
>>>> +        }
>>>> +    }
>>>> +
>>>>        for (i = 0; i < data->num_phys; i++) {
>>>>            struct sun4i_usb_phy *phy = data->phys + i;
>>>>            char name[16];
>>>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>            phy_set_drvdata(phy->phy, &data->phys[i]);
>>>>        }
>>>>
>>>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>>>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>>>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>>>> +        data->phy0_poll = true;
>>>> +
>>>> +    if (data->id_det_irq >= 0) {
>>>> +        ret = devm_request_irq(dev, data->id_det_irq,
>>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>>> +                "usb0-id-det", data);
>>>> +        if (ret) {
>>>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>>>> +            return ret;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    if (data->vbus_det_irq >= 0) {
>>>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>>> +                "usb0-vbus-det", data);
>>>> +        if (ret) {
>>>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>>>> +            return ret;
>>>> +        }
>>>> +    }
>>>> +
>>>>        dev_set_drvdata(dev, data);
>>>>        phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>>>
>>>>        return PTR_ERR_OR_ZERO(phy_provider);
>>>>    }
>>>>
>>>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>>>> +{
>>>> +    struct device *dev = &pdev->dev;
>>>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>>>> +
>>>> +    if (data->id_det_irq >= 0)
>>>> +        devm_free_irq(dev, data->id_det_irq, data);
>>>> +    if (data->vbus_det_irq >= 0)
>>>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>>>> +
>>>> +    cancel_delayed_work_sync(&data->detect);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>>    static const struct of_device_id sun4i_usb_phy_of_match[] = {
>>>>        { .compatible = "allwinner,sun4i-a10-usb-phy" },
>>>>        { .compatible = "allwinner,sun5i-a13-usb-phy" },
>>>> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>>>>
>>>>    static struct platform_driver sun4i_usb_phy_driver = {
>>>>        .probe    = sun4i_usb_phy_probe,
>>>> +    .remove    = sun4i_usb_phy_remove,
>>>>        .driver = {
>>>>            .of_match_table    = sun4i_usb_phy_of_match,
>>>> -        .name  = "sun4i-usb-phy",
>>>> +        .name  = DRIVER_NAME,
>>>>        }
>>>>    };
>>>>    module_platform_driver(sun4i_usb_phy_driver);
>>>>
>>>
>>
>> Thanks,
>> Chanwoo Choi
>>
> 

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

* Re: [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-11  8:29                     ` [linux-sunxi] " Chanwoo Choi
@ 2015-06-11  8:59                         ` Hans de Goede
  -1 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-11  8:59 UTC (permalink / raw)
  To: Chanwoo Choi
  Cc: Kishon Vijay Abraham I, Felipe Balbi, Maxime Ripard,
	Chen-Yu Tsai, Roman Byshko, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi,

On 11-06-15 10:29, Chanwoo Choi wrote:
> Hi Hans,
>
> On 06/11/2015 05:21 PM, Hans de Goede wrote:
>> Hi Chanwoo,
>>
>> Thanks for the quick review.
>>
>> On 11-06-15 10:07, Chanwoo Choi wrote:
>>> Hi Hans,
>>>
>>> I add the comment about extcon-related code.
>>>
>>> Firstly,
>>> I'd like you to implment the extcon driver for phy-sun4i-usb device
>>> in drivers/extcon/ directoryby using MFD
>>
>> No, just no, this is not what the MFD framework is for, the usb-phy
>> in question here is not a multifunction device. The MFD framework
>> is intended for true multi-function devices like i2c attached
>> PMICs which have regulators, gpios, pwm, input (power button),
>> chargers, power-supply, etc. That is NOT the case here.
>>
>> Also moving this to the MFD framework would very likely requiring
>> the devicetree binding for the usb-phy to change which we cannot
>> do as that would break the devicetree ABI.
>>
>>> because there are both extcon
>>> provider driver and extcon client driver. I think that all extcon
>>> provider driver better to be included in drivers/extcon/ directory.
>>> extcon_set_cable_state() function should be handled in extcon provider
>>> driver which is incluced in drivers/extcon/ directory.
>>
>> I do not find this a compelling reason, there are plenty of subsystems
>> where not all implementations of the subsystem class live in the subsystem
>> directory, e.g. input and hwmon devices are often also found outside of
>> the input and hwmon driver directories.
>
> There are difference on between input/hwmon and extcon.
>
> Because input and hwmon driver implement the only one type driver as provider driver.
> But, extcon implement the two type driver of both extcon provider and extcon client driver.
> The extcon is similiar with regulator and clock framework as resource.
>
> extcon provider driver to provider the event when the state of external connector is changed.
> - devm_extcon_dev_register()
> - e.g., almost extcon provider driver are included in 'drivers/extcon/' directory.

I understand, but that does not change my first argument, that the usb-phy is not
a MFD device. And although it may be desirable to keep extcon provider drivers
in the drivers/extcon, there are no technical reasons to do so.

The whole reason why Kishon asked me to start using the extcon framework is to avoid
adding a private API to the phy-sun4i-usb code for notifying the musb-sunxi code
about otg-id-pin status changes. Adding a separate driver for just the extcon bits
means re-adding a private api to the phy-sun4i-usb code but this time for the
extcon code, at which point we might just as well skip extcon and have the
musb-sunxi glue code call directly into the phy-sun4i-usb code...

Needing a private API for a separate extcn driver actually is a good argument to
NOT have a separate extcon driver and keep the extcon code in the phy-sun4i-usb code,
where as I see no technical arguments in favor of a separate extcon driver.

Regards,

Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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	[flat|nested] 57+ messages in thread

* [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11  8:59                         ` Hans de Goede
  0 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-11  8:59 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 11-06-15 10:29, Chanwoo Choi wrote:
> Hi Hans,
>
> On 06/11/2015 05:21 PM, Hans de Goede wrote:
>> Hi Chanwoo,
>>
>> Thanks for the quick review.
>>
>> On 11-06-15 10:07, Chanwoo Choi wrote:
>>> Hi Hans,
>>>
>>> I add the comment about extcon-related code.
>>>
>>> Firstly,
>>> I'd like you to implment the extcon driver for phy-sun4i-usb device
>>> in drivers/extcon/ directoryby using MFD
>>
>> No, just no, this is not what the MFD framework is for, the usb-phy
>> in question here is not a multifunction device. The MFD framework
>> is intended for true multi-function devices like i2c attached
>> PMICs which have regulators, gpios, pwm, input (power button),
>> chargers, power-supply, etc. That is NOT the case here.
>>
>> Also moving this to the MFD framework would very likely requiring
>> the devicetree binding for the usb-phy to change which we cannot
>> do as that would break the devicetree ABI.
>>
>>> because there are both extcon
>>> provider driver and extcon client driver. I think that all extcon
>>> provider driver better to be included in drivers/extcon/ directory.
>>> extcon_set_cable_state() function should be handled in extcon provider
>>> driver which is incluced in drivers/extcon/ directory.
>>
>> I do not find this a compelling reason, there are plenty of subsystems
>> where not all implementations of the subsystem class live in the subsystem
>> directory, e.g. input and hwmon devices are often also found outside of
>> the input and hwmon driver directories.
>
> There are difference on between input/hwmon and extcon.
>
> Because input and hwmon driver implement the only one type driver as provider driver.
> But, extcon implement the two type driver of both extcon provider and extcon client driver.
> The extcon is similiar with regulator and clock framework as resource.
>
> extcon provider driver to provider the event when the state of external connector is changed.
> - devm_extcon_dev_register()
> - e.g., almost extcon provider driver are included in 'drivers/extcon/' directory.

I understand, but that does not change my first argument, that the usb-phy is not
a MFD device. And although it may be desirable to keep extcon provider drivers
in the drivers/extcon, there are no technical reasons to do so.

The whole reason why Kishon asked me to start using the extcon framework is to avoid
adding a private API to the phy-sun4i-usb code for notifying the musb-sunxi code
about otg-id-pin status changes. Adding a separate driver for just the extcon bits
means re-adding a private api to the phy-sun4i-usb code but this time for the
extcon code, at which point we might just as well skip extcon and have the
musb-sunxi glue code call directly into the phy-sun4i-usb code...

Needing a private API for a separate extcn driver actually is a good argument to
NOT have a separate extcon driver and keep the extcon code in the phy-sun4i-usb code,
where as I see no technical arguments in favor of a separate extcon driver.

Regards,

Hans

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

* Re: Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-11  8:59                         ` Hans de Goede
@ 2015-06-11  9:33                             ` Chanwoo Choi
  -1 siblings, 0 replies; 57+ messages in thread
From: Chanwoo Choi @ 2015-06-11  9:33 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Kishon Vijay Abraham I, Felipe Balbi, Maxime Ripard,
	Chen-Yu Tsai, Roman Byshko, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi,

On 06/11/2015 05:59 PM, Hans de Goede wrote:
> Hi,
> 
> On 11-06-15 10:29, Chanwoo Choi wrote:
>> Hi Hans,
>>
>> On 06/11/2015 05:21 PM, Hans de Goede wrote:
>>> Hi Chanwoo,
>>>
>>> Thanks for the quick review.
>>>
>>> On 11-06-15 10:07, Chanwoo Choi wrote:
>>>> Hi Hans,
>>>>
>>>> I add the comment about extcon-related code.
>>>>
>>>> Firstly,
>>>> I'd like you to implment the extcon driver for phy-sun4i-usb device
>>>> in drivers/extcon/ directoryby using MFD
>>>
>>> No, just no, this is not what the MFD framework is for, the usb-phy
>>> in question here is not a multifunction device. The MFD framework
>>> is intended for true multi-function devices like i2c attached
>>> PMICs which have regulators, gpios, pwm, input (power button),
>>> chargers, power-supply, etc. That is NOT the case here.
>>>
>>> Also moving this to the MFD framework would very likely requiring
>>> the devicetree binding for the usb-phy to change which we cannot
>>> do as that would break the devicetree ABI.
>>>
>>>> because there are both extcon
>>>> provider driver and extcon client driver. I think that all extcon
>>>> provider driver better to be included in drivers/extcon/ directory.
>>>> extcon_set_cable_state() function should be handled in extcon provider
>>>> driver which is incluced in drivers/extcon/ directory.
>>>
>>> I do not find this a compelling reason, there are plenty of subsystems
>>> where not all implementations of the subsystem class live in the subsystem
>>> directory, e.g. input and hwmon devices are often also found outside of
>>> the input and hwmon driver directories.
>>
>> There are difference on between input/hwmon and extcon.
>>
>> Because input and hwmon driver implement the only one type driver as provider driver.
>> But, extcon implement the two type driver of both extcon provider and extcon client driver.
>> The extcon is similiar with regulator and clock framework as resource.
>>
>> extcon provider driver to provider the event when the state of external connector is changed.
>> - devm_extcon_dev_register()
>> - e.g., almost extcon provider driver are included in 'drivers/extcon/' directory.
> 
> I understand, but that does not change my first argument, that the usb-phy is not
> a MFD device. And although it may be desirable to keep extcon provider drivers
> in the drivers/extcon, there are no technical reasons to do so.
> 
> The whole reason why Kishon asked me to start using the extcon framework is to avoid
> adding a private API to the phy-sun4i-usb code for notifying the musb-sunxi code
> about otg-id-pin status changes. Adding a separate driver for just the extcon bits
> means re-adding a private api to the phy-sun4i-usb code but this time for the
> extcon code, at which point we might just as well skip extcon and have the
> musb-sunxi glue code call directly into the phy-sun4i-usb code...
> 
> Needing a private API for a separate extcn driver actually is a good argument to
> NOT have a separate extcon driver and keep the extcon code in the phy-sun4i-usb code,
> where as I see no technical arguments in favor of a separate extcon driver.

There is one technical issue.

The extcon_set_cable_state() should be handled by extcon provider driver.
because extcon_set_cable_state() inform the extcon client driver of the event
when detecting the change of h/w line (gpio line) or register of peripheral device.

But, extcon client driver can now get the instance of extcon_dev structure
by extcon_get_edev_by_phandle() and then can change the cable state by using the extcon_set_cable_state().

I think that these issue have to be protected by framework level.

Thanks,
Chanwoo Choi

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

* [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11  9:33                             ` Chanwoo Choi
  0 siblings, 0 replies; 57+ messages in thread
From: Chanwoo Choi @ 2015-06-11  9:33 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 06/11/2015 05:59 PM, Hans de Goede wrote:
> Hi,
> 
> On 11-06-15 10:29, Chanwoo Choi wrote:
>> Hi Hans,
>>
>> On 06/11/2015 05:21 PM, Hans de Goede wrote:
>>> Hi Chanwoo,
>>>
>>> Thanks for the quick review.
>>>
>>> On 11-06-15 10:07, Chanwoo Choi wrote:
>>>> Hi Hans,
>>>>
>>>> I add the comment about extcon-related code.
>>>>
>>>> Firstly,
>>>> I'd like you to implment the extcon driver for phy-sun4i-usb device
>>>> in drivers/extcon/ directoryby using MFD
>>>
>>> No, just no, this is not what the MFD framework is for, the usb-phy
>>> in question here is not a multifunction device. The MFD framework
>>> is intended for true multi-function devices like i2c attached
>>> PMICs which have regulators, gpios, pwm, input (power button),
>>> chargers, power-supply, etc. That is NOT the case here.
>>>
>>> Also moving this to the MFD framework would very likely requiring
>>> the devicetree binding for the usb-phy to change which we cannot
>>> do as that would break the devicetree ABI.
>>>
>>>> because there are both extcon
>>>> provider driver and extcon client driver. I think that all extcon
>>>> provider driver better to be included in drivers/extcon/ directory.
>>>> extcon_set_cable_state() function should be handled in extcon provider
>>>> driver which is incluced in drivers/extcon/ directory.
>>>
>>> I do not find this a compelling reason, there are plenty of subsystems
>>> where not all implementations of the subsystem class live in the subsystem
>>> directory, e.g. input and hwmon devices are often also found outside of
>>> the input and hwmon driver directories.
>>
>> There are difference on between input/hwmon and extcon.
>>
>> Because input and hwmon driver implement the only one type driver as provider driver.
>> But, extcon implement the two type driver of both extcon provider and extcon client driver.
>> The extcon is similiar with regulator and clock framework as resource.
>>
>> extcon provider driver to provider the event when the state of external connector is changed.
>> - devm_extcon_dev_register()
>> - e.g., almost extcon provider driver are included in 'drivers/extcon/' directory.
> 
> I understand, but that does not change my first argument, that the usb-phy is not
> a MFD device. And although it may be desirable to keep extcon provider drivers
> in the drivers/extcon, there are no technical reasons to do so.
> 
> The whole reason why Kishon asked me to start using the extcon framework is to avoid
> adding a private API to the phy-sun4i-usb code for notifying the musb-sunxi code
> about otg-id-pin status changes. Adding a separate driver for just the extcon bits
> means re-adding a private api to the phy-sun4i-usb code but this time for the
> extcon code, at which point we might just as well skip extcon and have the
> musb-sunxi glue code call directly into the phy-sun4i-usb code...
> 
> Needing a private API for a separate extcn driver actually is a good argument to
> NOT have a separate extcon driver and keep the extcon code in the phy-sun4i-usb code,
> where as I see no technical arguments in favor of a separate extcon driver.

There is one technical issue.

The extcon_set_cable_state() should be handled by extcon provider driver.
because extcon_set_cable_state() inform the extcon client driver of the event
when detecting the change of h/w line (gpio line) or register of peripheral device.

But, extcon client driver can now get the instance of extcon_dev structure
by extcon_get_edev_by_phandle() and then can change the cable state by using the extcon_set_cable_state().

I think that these issue have to be protected by framework level.

Thanks,
Chanwoo Choi

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

* Re: [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-11  9:33                             ` [linux-sunxi] " Chanwoo Choi
@ 2015-06-11  9:37                                 ` Chanwoo Choi
  -1 siblings, 0 replies; 57+ messages in thread
From: Chanwoo Choi @ 2015-06-11  9:37 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Kishon Vijay Abraham I, Felipe Balbi, Maxime Ripard,
	Chen-Yu Tsai, Roman Byshko, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

On 06/11/2015 06:33 PM, Chanwoo Choi wrote:
> Hi,
> 
> On 06/11/2015 05:59 PM, Hans de Goede wrote:
>> Hi,
>>
>> On 11-06-15 10:29, Chanwoo Choi wrote:
>>> Hi Hans,
>>>
>>> On 06/11/2015 05:21 PM, Hans de Goede wrote:
>>>> Hi Chanwoo,
>>>>
>>>> Thanks for the quick review.
>>>>
>>>> On 11-06-15 10:07, Chanwoo Choi wrote:
>>>>> Hi Hans,
>>>>>
>>>>> I add the comment about extcon-related code.
>>>>>
>>>>> Firstly,
>>>>> I'd like you to implment the extcon driver for phy-sun4i-usb device
>>>>> in drivers/extcon/ directoryby using MFD
>>>>
>>>> No, just no, this is not what the MFD framework is for, the usb-phy
>>>> in question here is not a multifunction device. The MFD framework
>>>> is intended for true multi-function devices like i2c attached
>>>> PMICs which have regulators, gpios, pwm, input (power button),
>>>> chargers, power-supply, etc. That is NOT the case here.
>>>>
>>>> Also moving this to the MFD framework would very likely requiring
>>>> the devicetree binding for the usb-phy to change which we cannot
>>>> do as that would break the devicetree ABI.
>>>>
>>>>> because there are both extcon
>>>>> provider driver and extcon client driver. I think that all extcon
>>>>> provider driver better to be included in drivers/extcon/ directory.
>>>>> extcon_set_cable_state() function should be handled in extcon provider
>>>>> driver which is incluced in drivers/extcon/ directory.
>>>>
>>>> I do not find this a compelling reason, there are plenty of subsystems
>>>> where not all implementations of the subsystem class live in the subsystem
>>>> directory, e.g. input and hwmon devices are often also found outside of
>>>> the input and hwmon driver directories.
>>>
>>> There are difference on between input/hwmon and extcon.
>>>
>>> Because input and hwmon driver implement the only one type driver as provider driver.
>>> But, extcon implement the two type driver of both extcon provider and extcon client driver.
>>> The extcon is similiar with regulator and clock framework as resource.
>>>
>>> extcon provider driver to provider the event when the state of external connector is changed.
>>> - devm_extcon_dev_register()
>>> - e.g., almost extcon provider driver are included in 'drivers/extcon/' directory.
>>
>> I understand, but that does not change my first argument, that the usb-phy is not
>> a MFD device. And although it may be desirable to keep extcon provider drivers
>> in the drivers/extcon, there are no technical reasons to do so.
>>
>> The whole reason why Kishon asked me to start using the extcon framework is to avoid
>> adding a private API to the phy-sun4i-usb code for notifying the musb-sunxi code
>> about otg-id-pin status changes. Adding a separate driver for just the extcon bits
>> means re-adding a private api to the phy-sun4i-usb code but this time for the
>> extcon code, at which point we might just as well skip extcon and have the
>> musb-sunxi glue code call directly into the phy-sun4i-usb code...
>>
>> Needing a private API for a separate extcn driver actually is a good argument to
>> NOT have a separate extcon driver and keep the extcon code in the phy-sun4i-usb code,
>> where as I see no technical arguments in favor of a separate extcon driver.
> 
> There is one technical issue.
> 
> The extcon_set_cable_state() should be handled by extcon provider driver.
> because extcon_set_cable_state() inform the extcon client driver of the event
> when detecting the change of h/w line (gpio line) or register of peripheral device.
> 
> But, extcon client driver can now get the instance of extcon_dev structure
> by extcon_get_edev_by_phandle() and then can change the cable state by using the extcon_set_cable_state().
> 
> I think that these issue have to be protected by framework level.

I fix wrong word. (protected -> prevented)
- I think that these issue have to be prevented by framework level.

Thanks,
Chanwoo Choi
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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	[flat|nested] 57+ messages in thread

* [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11  9:37                                 ` Chanwoo Choi
  0 siblings, 0 replies; 57+ messages in thread
From: Chanwoo Choi @ 2015-06-11  9:37 UTC (permalink / raw)
  To: linux-arm-kernel

On 06/11/2015 06:33 PM, Chanwoo Choi wrote:
> Hi,
> 
> On 06/11/2015 05:59 PM, Hans de Goede wrote:
>> Hi,
>>
>> On 11-06-15 10:29, Chanwoo Choi wrote:
>>> Hi Hans,
>>>
>>> On 06/11/2015 05:21 PM, Hans de Goede wrote:
>>>> Hi Chanwoo,
>>>>
>>>> Thanks for the quick review.
>>>>
>>>> On 11-06-15 10:07, Chanwoo Choi wrote:
>>>>> Hi Hans,
>>>>>
>>>>> I add the comment about extcon-related code.
>>>>>
>>>>> Firstly,
>>>>> I'd like you to implment the extcon driver for phy-sun4i-usb device
>>>>> in drivers/extcon/ directoryby using MFD
>>>>
>>>> No, just no, this is not what the MFD framework is for, the usb-phy
>>>> in question here is not a multifunction device. The MFD framework
>>>> is intended for true multi-function devices like i2c attached
>>>> PMICs which have regulators, gpios, pwm, input (power button),
>>>> chargers, power-supply, etc. That is NOT the case here.
>>>>
>>>> Also moving this to the MFD framework would very likely requiring
>>>> the devicetree binding for the usb-phy to change which we cannot
>>>> do as that would break the devicetree ABI.
>>>>
>>>>> because there are both extcon
>>>>> provider driver and extcon client driver. I think that all extcon
>>>>> provider driver better to be included in drivers/extcon/ directory.
>>>>> extcon_set_cable_state() function should be handled in extcon provider
>>>>> driver which is incluced in drivers/extcon/ directory.
>>>>
>>>> I do not find this a compelling reason, there are plenty of subsystems
>>>> where not all implementations of the subsystem class live in the subsystem
>>>> directory, e.g. input and hwmon devices are often also found outside of
>>>> the input and hwmon driver directories.
>>>
>>> There are difference on between input/hwmon and extcon.
>>>
>>> Because input and hwmon driver implement the only one type driver as provider driver.
>>> But, extcon implement the two type driver of both extcon provider and extcon client driver.
>>> The extcon is similiar with regulator and clock framework as resource.
>>>
>>> extcon provider driver to provider the event when the state of external connector is changed.
>>> - devm_extcon_dev_register()
>>> - e.g., almost extcon provider driver are included in 'drivers/extcon/' directory.
>>
>> I understand, but that does not change my first argument, that the usb-phy is not
>> a MFD device. And although it may be desirable to keep extcon provider drivers
>> in the drivers/extcon, there are no technical reasons to do so.
>>
>> The whole reason why Kishon asked me to start using the extcon framework is to avoid
>> adding a private API to the phy-sun4i-usb code for notifying the musb-sunxi code
>> about otg-id-pin status changes. Adding a separate driver for just the extcon bits
>> means re-adding a private api to the phy-sun4i-usb code but this time for the
>> extcon code, at which point we might just as well skip extcon and have the
>> musb-sunxi glue code call directly into the phy-sun4i-usb code...
>>
>> Needing a private API for a separate extcn driver actually is a good argument to
>> NOT have a separate extcon driver and keep the extcon code in the phy-sun4i-usb code,
>> where as I see no technical arguments in favor of a separate extcon driver.
> 
> There is one technical issue.
> 
> The extcon_set_cable_state() should be handled by extcon provider driver.
> because extcon_set_cable_state() inform the extcon client driver of the event
> when detecting the change of h/w line (gpio line) or register of peripheral device.
> 
> But, extcon client driver can now get the instance of extcon_dev structure
> by extcon_get_edev_by_phandle() and then can change the cable state by using the extcon_set_cable_state().
> 
> I think that these issue have to be protected by framework level.

I fix wrong word. (protected -> prevented)
- I think that these issue have to be prevented by framework level.

Thanks,
Chanwoo Choi

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

* Re: [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-11  9:33                             ` [linux-sunxi] " Chanwoo Choi
@ 2015-06-11  9:38                                 ` Hans de Goede
  -1 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-11  9:38 UTC (permalink / raw)
  To: Chanwoo Choi
  Cc: Kishon Vijay Abraham I, Felipe Balbi, Maxime Ripard,
	Chen-Yu Tsai, Roman Byshko, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi,

On 11-06-15 11:33, Chanwoo Choi wrote:
> Hi,
>
> On 06/11/2015 05:59 PM, Hans de Goede wrote:
>> Hi,
>>
>> On 11-06-15 10:29, Chanwoo Choi wrote:
>>> Hi Hans,
>>>
>>> On 06/11/2015 05:21 PM, Hans de Goede wrote:
>>>> Hi Chanwoo,
>>>>
>>>> Thanks for the quick review.
>>>>
>>>> On 11-06-15 10:07, Chanwoo Choi wrote:
>>>>> Hi Hans,
>>>>>
>>>>> I add the comment about extcon-related code.
>>>>>
>>>>> Firstly,
>>>>> I'd like you to implment the extcon driver for phy-sun4i-usb device
>>>>> in drivers/extcon/ directoryby using MFD
>>>>
>>>> No, just no, this is not what the MFD framework is for, the usb-phy
>>>> in question here is not a multifunction device. The MFD framework
>>>> is intended for true multi-function devices like i2c attached
>>>> PMICs which have regulators, gpios, pwm, input (power button),
>>>> chargers, power-supply, etc. That is NOT the case here.
>>>>
>>>> Also moving this to the MFD framework would very likely requiring
>>>> the devicetree binding for the usb-phy to change which we cannot
>>>> do as that would break the devicetree ABI.
>>>>
>>>>> because there are both extcon
>>>>> provider driver and extcon client driver. I think that all extcon
>>>>> provider driver better to be included in drivers/extcon/ directory.
>>>>> extcon_set_cable_state() function should be handled in extcon provider
>>>>> driver which is incluced in drivers/extcon/ directory.
>>>>
>>>> I do not find this a compelling reason, there are plenty of subsystems
>>>> where not all implementations of the subsystem class live in the subsystem
>>>> directory, e.g. input and hwmon devices are often also found outside of
>>>> the input and hwmon driver directories.
>>>
>>> There are difference on between input/hwmon and extcon.
>>>
>>> Because input and hwmon driver implement the only one type driver as provider driver.
>>> But, extcon implement the two type driver of both extcon provider and extcon client driver.
>>> The extcon is similiar with regulator and clock framework as resource.
>>>
>>> extcon provider driver to provider the event when the state of external connector is changed.
>>> - devm_extcon_dev_register()
>>> - e.g., almost extcon provider driver are included in 'drivers/extcon/' directory.
>>
>> I understand, but that does not change my first argument, that the usb-phy is not
>> a MFD device. And although it may be desirable to keep extcon provider drivers
>> in the drivers/extcon, there are no technical reasons to do so.
>>
>> The whole reason why Kishon asked me to start using the extcon framework is to avoid
>> adding a private API to the phy-sun4i-usb code for notifying the musb-sunxi code
>> about otg-id-pin status changes. Adding a separate driver for just the extcon bits
>> means re-adding a private api to the phy-sun4i-usb code but this time for the
>> extcon code, at which point we might just as well skip extcon and have the
>> musb-sunxi glue code call directly into the phy-sun4i-usb code...
>>
>> Needing a private API for a separate extcn driver actually is a good argument to
>> NOT have a separate extcon driver and keep the extcon code in the phy-sun4i-usb code,
>> where as I see no technical arguments in favor of a separate extcon driver.
>
> There is one technical issue.
>
> The extcon_set_cable_state() should be handled by extcon provider driver.

That is something which can be done regardless of where the extcon provider
driver code is located in the kernel tree...

> because extcon_set_cable_state() inform the extcon client driver of the event
> when detecting the change of h/w line (gpio line) or register of peripheral device.
>
> But, extcon client driver can now get the instance of extcon_dev structure
> by extcon_get_edev_by_phandle() and then can change the cable state by using the extcon_set_cable_state().
>
> I think that these issue have to be protected by framework level.

Protecting this at the framework level would mean protecting it with code
in drivers/extcon/extcon.c, that code will be used (and thus can protect
things) regardless of where the extcon provider code lives.

I really still see no technical reasons why all extcon provider code
MUST be under drivers/extcon.

As for the whole provider <-> client relation as argument, the same goes
for irq-chips and any code with irq handlers, yet we have irc-chip drivers
(irq providers) all over the place.

Regards,

Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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	[flat|nested] 57+ messages in thread

* [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11  9:38                                 ` Hans de Goede
  0 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-11  9:38 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 11-06-15 11:33, Chanwoo Choi wrote:
> Hi,
>
> On 06/11/2015 05:59 PM, Hans de Goede wrote:
>> Hi,
>>
>> On 11-06-15 10:29, Chanwoo Choi wrote:
>>> Hi Hans,
>>>
>>> On 06/11/2015 05:21 PM, Hans de Goede wrote:
>>>> Hi Chanwoo,
>>>>
>>>> Thanks for the quick review.
>>>>
>>>> On 11-06-15 10:07, Chanwoo Choi wrote:
>>>>> Hi Hans,
>>>>>
>>>>> I add the comment about extcon-related code.
>>>>>
>>>>> Firstly,
>>>>> I'd like you to implment the extcon driver for phy-sun4i-usb device
>>>>> in drivers/extcon/ directoryby using MFD
>>>>
>>>> No, just no, this is not what the MFD framework is for, the usb-phy
>>>> in question here is not a multifunction device. The MFD framework
>>>> is intended for true multi-function devices like i2c attached
>>>> PMICs which have regulators, gpios, pwm, input (power button),
>>>> chargers, power-supply, etc. That is NOT the case here.
>>>>
>>>> Also moving this to the MFD framework would very likely requiring
>>>> the devicetree binding for the usb-phy to change which we cannot
>>>> do as that would break the devicetree ABI.
>>>>
>>>>> because there are both extcon
>>>>> provider driver and extcon client driver. I think that all extcon
>>>>> provider driver better to be included in drivers/extcon/ directory.
>>>>> extcon_set_cable_state() function should be handled in extcon provider
>>>>> driver which is incluced in drivers/extcon/ directory.
>>>>
>>>> I do not find this a compelling reason, there are plenty of subsystems
>>>> where not all implementations of the subsystem class live in the subsystem
>>>> directory, e.g. input and hwmon devices are often also found outside of
>>>> the input and hwmon driver directories.
>>>
>>> There are difference on between input/hwmon and extcon.
>>>
>>> Because input and hwmon driver implement the only one type driver as provider driver.
>>> But, extcon implement the two type driver of both extcon provider and extcon client driver.
>>> The extcon is similiar with regulator and clock framework as resource.
>>>
>>> extcon provider driver to provider the event when the state of external connector is changed.
>>> - devm_extcon_dev_register()
>>> - e.g., almost extcon provider driver are included in 'drivers/extcon/' directory.
>>
>> I understand, but that does not change my first argument, that the usb-phy is not
>> a MFD device. And although it may be desirable to keep extcon provider drivers
>> in the drivers/extcon, there are no technical reasons to do so.
>>
>> The whole reason why Kishon asked me to start using the extcon framework is to avoid
>> adding a private API to the phy-sun4i-usb code for notifying the musb-sunxi code
>> about otg-id-pin status changes. Adding a separate driver for just the extcon bits
>> means re-adding a private api to the phy-sun4i-usb code but this time for the
>> extcon code, at which point we might just as well skip extcon and have the
>> musb-sunxi glue code call directly into the phy-sun4i-usb code...
>>
>> Needing a private API for a separate extcn driver actually is a good argument to
>> NOT have a separate extcon driver and keep the extcon code in the phy-sun4i-usb code,
>> where as I see no technical arguments in favor of a separate extcon driver.
>
> There is one technical issue.
>
> The extcon_set_cable_state() should be handled by extcon provider driver.

That is something which can be done regardless of where the extcon provider
driver code is located in the kernel tree...

> because extcon_set_cable_state() inform the extcon client driver of the event
> when detecting the change of h/w line (gpio line) or register of peripheral device.
>
> But, extcon client driver can now get the instance of extcon_dev structure
> by extcon_get_edev_by_phandle() and then can change the cable state by using the extcon_set_cable_state().
>
> I think that these issue have to be protected by framework level.

Protecting this at the framework level would mean protecting it with code
in drivers/extcon/extcon.c, that code will be used (and thus can protect
things) regardless of where the extcon provider code lives.

I really still see no technical reasons why all extcon provider code
MUST be under drivers/extcon.

As for the whole provider <-> client relation as argument, the same goes
for irq-chips and any code with irq handlers, yet we have irc-chip drivers
(irq providers) all over the place.

Regards,

Hans

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

* Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-05-31 16:10     ` Hans de Goede
@ 2015-06-11  9:42         ` Kishon Vijay Abraham I
  -1 siblings, 0 replies; 57+ messages in thread
From: Kishon Vijay Abraham I @ 2015-06-11  9:42 UTC (permalink / raw)
  To: Hans de Goede, Felipe Balbi
  Cc: Maxime Ripard, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi,

On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
> The usb0 phy is connected to an OTG controller, and as such needs some special
> handling:
>
> 1) It allows explicit control over the pullups, enable these on phy_init and
> disable them on phy_exit.
>
> 2) It has bits to signal id and vbus detect to the musb-core, add support for
> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
> these bits to the correct values for operating in host only mode when no
> gpios are specified in the devicetree.
>
> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
> device cable is plugged in, so when in dual role mode register an extcon.
>
> While updating the devicetree binding documentation also add documentation
> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>
> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> ---
> Changes in v2:
> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>   has been moved to the phy-sun4i-usb driver and their status is exported
>   through extcon for the sunxi-musb glue
> Changes in v3:
> -No changes
> Changes in v4:
> -Do not call regulator_disable in an unbalanced manner when an external vbus
>   is present
> ---
>   .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>   drivers/phy/Kconfig                                |   1 +
>   drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>   3 files changed, 281 insertions(+), 11 deletions(-)
>
.
.
<snip>
.
.
> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
> index 91c5be4..b45d707 100644
> --- a/drivers/phy/phy-sun4i-usb.c
> +++ b/drivers/phy/phy-sun4i-usb.c
> @@ -1,7 +1,7 @@

.
.
<snip>
.
.
>   static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>   					struct of_phandle_args *args)
>   {
> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>   	struct phy_provider *phy_provider;
>   	bool dedicated_clocks;
>   	struct resource *res;
> -	int i;
> +	int i, ret;
>
>   	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>   	if (!data)
>   		return -ENOMEM;
>
>   	mutex_init(&data->mutex);
> +	INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
> +	data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
> +	data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
> +	data->extcon_cable_names[2] = NULL;
> +	data->extcon.name = DRIVER_NAME;
> +	data->extcon.supported_cable = data->extcon_cable_names;
> +	data->extcon.dev.parent = dev;
>
>   	if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>   		data->num_phys = 2;
> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>   	if (IS_ERR(data->base))
>   		return PTR_ERR(data->base);
>
> +	data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
> +	if (IS_ERR(data->id_det_gpio)) {
> +		if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		data->id_det_gpio = NULL;
> +	}
> +
> +	data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
> +	if (IS_ERR(data->vbus_det_gpio)) {
> +		if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		data->vbus_det_gpio = NULL;
> +	}
> +
> +	/* We either want both gpio pins or neither (when in host mode) */
> +	if (!data->id_det_gpio != !data->vbus_det_gpio) {
> +		dev_err(dev, "failed to get id or vbus detect pin\n");
> +		return -ENODEV;
> +	}
> +
> +	if (data->id_det_gpio) {
> +		ret = devm_extcon_dev_register(dev, &data->extcon);
> +		if (ret) {
> +			dev_err(dev, "failed to register extcon: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
>   	for (i = 0; i < data->num_phys; i++) {
>   		struct sun4i_usb_phy *phy = data->phys + i;
>   		char name[16];
> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>   		phy_set_drvdata(phy->phy, &data->phys[i]);
>   	}
>
> +	data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
> +	data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
> +	if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
> +		data->phy0_poll = true;

if polling is enabled, we shouldn't enable irq at all?
> +
> +	if (data->id_det_irq >= 0) {
> +		ret = devm_request_irq(dev, data->id_det_irq,
> +				sun4i_usb_phy0_id_vbus_det_irq,
> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
> +				"usb0-id-det", data);
> +		if (ret) {
> +			dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (data->vbus_det_irq >= 0) {
> +		ret = devm_request_irq(dev, data->vbus_det_irq,
> +				sun4i_usb_phy0_id_vbus_det_irq,
> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
> +				"usb0-vbus-det", data);
> +		if (ret) {
> +			dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
>   	dev_set_drvdata(dev, data);
>   	phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>
>   	return PTR_ERR_OR_ZERO(phy_provider);
>   }
>
> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
> +
> +	if (data->id_det_irq >= 0)
> +		devm_free_irq(dev, data->id_det_irq, data);
> +	if (data->vbus_det_irq >= 0)
> +		devm_free_irq(dev, data->vbus_det_irq, data);

This shouldn't be needed since you already use devm_* in probe.

Thanks
Kishon

> +
> +	cancel_delayed_work_sync(&data->detect);
> +
> +	return 0;
> +}
> +
>   static const struct of_device_id sun4i_usb_phy_of_match[] = {
>   	{ .compatible = "allwinner,sun4i-a10-usb-phy" },
>   	{ .compatible = "allwinner,sun5i-a13-usb-phy" },
> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>
>   static struct platform_driver sun4i_usb_phy_driver = {
>   	.probe	= sun4i_usb_phy_probe,
> +	.remove	= sun4i_usb_phy_remove,
>   	.driver = {
>   		.of_match_table	= sun4i_usb_phy_of_match,
> -		.name  = "sun4i-usb-phy",
> +		.name  = DRIVER_NAME,
>   	}
>   };
>   module_platform_driver(sun4i_usb_phy_driver);
>

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

* [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11  9:42         ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 57+ messages in thread
From: Kishon Vijay Abraham I @ 2015-06-11  9:42 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
> The usb0 phy is connected to an OTG controller, and as such needs some special
> handling:
>
> 1) It allows explicit control over the pullups, enable these on phy_init and
> disable them on phy_exit.
>
> 2) It has bits to signal id and vbus detect to the musb-core, add support for
> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
> these bits to the correct values for operating in host only mode when no
> gpios are specified in the devicetree.
>
> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
> device cable is plugged in, so when in dual role mode register an extcon.
>
> While updating the devicetree binding documentation also add documentation
> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
> Changes in v2:
> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>   has been moved to the phy-sun4i-usb driver and their status is exported
>   through extcon for the sunxi-musb glue
> Changes in v3:
> -No changes
> Changes in v4:
> -Do not call regulator_disable in an unbalanced manner when an external vbus
>   is present
> ---
>   .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>   drivers/phy/Kconfig                                |   1 +
>   drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>   3 files changed, 281 insertions(+), 11 deletions(-)
>
.
.
<snip>
.
.
> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
> index 91c5be4..b45d707 100644
> --- a/drivers/phy/phy-sun4i-usb.c
> +++ b/drivers/phy/phy-sun4i-usb.c
> @@ -1,7 +1,7 @@

.
.
<snip>
.
.
>   static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>   					struct of_phandle_args *args)
>   {
> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>   	struct phy_provider *phy_provider;
>   	bool dedicated_clocks;
>   	struct resource *res;
> -	int i;
> +	int i, ret;
>
>   	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>   	if (!data)
>   		return -ENOMEM;
>
>   	mutex_init(&data->mutex);
> +	INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
> +	data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
> +	data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
> +	data->extcon_cable_names[2] = NULL;
> +	data->extcon.name = DRIVER_NAME;
> +	data->extcon.supported_cable = data->extcon_cable_names;
> +	data->extcon.dev.parent = dev;
>
>   	if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>   		data->num_phys = 2;
> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>   	if (IS_ERR(data->base))
>   		return PTR_ERR(data->base);
>
> +	data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
> +	if (IS_ERR(data->id_det_gpio)) {
> +		if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		data->id_det_gpio = NULL;
> +	}
> +
> +	data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
> +	if (IS_ERR(data->vbus_det_gpio)) {
> +		if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		data->vbus_det_gpio = NULL;
> +	}
> +
> +	/* We either want both gpio pins or neither (when in host mode) */
> +	if (!data->id_det_gpio != !data->vbus_det_gpio) {
> +		dev_err(dev, "failed to get id or vbus detect pin\n");
> +		return -ENODEV;
> +	}
> +
> +	if (data->id_det_gpio) {
> +		ret = devm_extcon_dev_register(dev, &data->extcon);
> +		if (ret) {
> +			dev_err(dev, "failed to register extcon: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
>   	for (i = 0; i < data->num_phys; i++) {
>   		struct sun4i_usb_phy *phy = data->phys + i;
>   		char name[16];
> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>   		phy_set_drvdata(phy->phy, &data->phys[i]);
>   	}
>
> +	data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
> +	data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
> +	if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
> +		data->phy0_poll = true;

if polling is enabled, we shouldn't enable irq at all?
> +
> +	if (data->id_det_irq >= 0) {
> +		ret = devm_request_irq(dev, data->id_det_irq,
> +				sun4i_usb_phy0_id_vbus_det_irq,
> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
> +				"usb0-id-det", data);
> +		if (ret) {
> +			dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (data->vbus_det_irq >= 0) {
> +		ret = devm_request_irq(dev, data->vbus_det_irq,
> +				sun4i_usb_phy0_id_vbus_det_irq,
> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
> +				"usb0-vbus-det", data);
> +		if (ret) {
> +			dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
>   	dev_set_drvdata(dev, data);
>   	phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>
>   	return PTR_ERR_OR_ZERO(phy_provider);
>   }
>
> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
> +
> +	if (data->id_det_irq >= 0)
> +		devm_free_irq(dev, data->id_det_irq, data);
> +	if (data->vbus_det_irq >= 0)
> +		devm_free_irq(dev, data->vbus_det_irq, data);

This shouldn't be needed since you already use devm_* in probe.

Thanks
Kishon

> +
> +	cancel_delayed_work_sync(&data->detect);
> +
> +	return 0;
> +}
> +
>   static const struct of_device_id sun4i_usb_phy_of_match[] = {
>   	{ .compatible = "allwinner,sun4i-a10-usb-phy" },
>   	{ .compatible = "allwinner,sun5i-a13-usb-phy" },
> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>
>   static struct platform_driver sun4i_usb_phy_driver = {
>   	.probe	= sun4i_usb_phy_probe,
> +	.remove	= sun4i_usb_phy_remove,
>   	.driver = {
>   		.of_match_table	= sun4i_usb_phy_of_match,
> -		.name  = "sun4i-usb-phy",
> +		.name  = DRIVER_NAME,
>   	}
>   };
>   module_platform_driver(sun4i_usb_phy_driver);
>

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

* Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-11  9:42         ` Kishon Vijay Abraham I
@ 2015-06-11  9:53             ` Hans de Goede
  -1 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-11  9:53 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Felipe Balbi
  Cc: Maxime Ripard, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi,

On 11-06-15 11:42, Kishon Vijay Abraham I wrote:
> Hi,
>
> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>> The usb0 phy is connected to an OTG controller, and as such needs some special
>> handling:
>>
>> 1) It allows explicit control over the pullups, enable these on phy_init and
>> disable them on phy_exit.
>>
>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>> these bits to the correct values for operating in host only mode when no
>> gpios are specified in the devicetree.
>>
>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>> device cable is plugged in, so when in dual role mode register an extcon.
>>
>> While updating the devicetree binding documentation also add documentation
>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>
>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> ---
>> Changes in v2:
>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>   has been moved to the phy-sun4i-usb driver and their status is exported
>>   through extcon for the sunxi-musb glue
>> Changes in v3:
>> -No changes
>> Changes in v4:
>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>   is present
>> ---
>>   .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>   drivers/phy/Kconfig                                |   1 +
>>   drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>   3 files changed, 281 insertions(+), 11 deletions(-)
>>
> .
> .
> <snip>
> .
> .
>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>> index 91c5be4..b45d707 100644
>> --- a/drivers/phy/phy-sun4i-usb.c
>> +++ b/drivers/phy/phy-sun4i-usb.c
>> @@ -1,7 +1,7 @@
>
> .
> .
> <snip>
> .
> .
>>   static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>                       struct of_phandle_args *args)
>>   {
>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>       struct phy_provider *phy_provider;
>>       bool dedicated_clocks;
>>       struct resource *res;
>> -    int i;
>> +    int i, ret;
>>
>>       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>       if (!data)
>>           return -ENOMEM;
>>
>>       mutex_init(&data->mutex);
>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>> +    data->extcon_cable_names[2] = NULL;
>> +    data->extcon.name = DRIVER_NAME;
>> +    data->extcon.supported_cable = data->extcon_cable_names;
>> +    data->extcon.dev.parent = dev;
>>
>>       if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>           data->num_phys = 2;
>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>       if (IS_ERR(data->base))
>>           return PTR_ERR(data->base);
>>
>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>> +    if (IS_ERR(data->id_det_gpio)) {
>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>> +            return -EPROBE_DEFER;
>> +        data->id_det_gpio = NULL;
>> +    }
>> +
>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>> +    if (IS_ERR(data->vbus_det_gpio)) {
>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>> +            return -EPROBE_DEFER;
>> +        data->vbus_det_gpio = NULL;
>> +    }
>> +
>> +    /* We either want both gpio pins or neither (when in host mode) */
>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>> +        return -ENODEV;
>> +    }
>> +
>> +    if (data->id_det_gpio) {
>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>> +        if (ret) {
>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>>       for (i = 0; i < data->num_phys; i++) {
>>           struct sun4i_usb_phy *phy = data->phys + i;
>>           char name[16];
>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>           phy_set_drvdata(phy->phy, &data->phys[i]);
>>       }
>>
>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>> +        data->phy0_poll = true;
>
> if polling is enabled, we shouldn't enable irq at all?

Thanks for the review.

One some boards one of the gpio-s is irq capable and the other
is not, in which case the current code indeed enables both
irq handling for the one gpio which is irq capable and enables
polling. This is done this way deliberately as the irq path
has much better latency then polling and the 2 can co-exist.

>> +
>> +    if (data->id_det_irq >= 0) {
>> +        ret = devm_request_irq(dev, data->id_det_irq,
>> +                sun4i_usb_phy0_id_vbus_det_irq,
>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +                "usb0-id-det", data);
>> +        if (ret) {
>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>> +    if (data->vbus_det_irq >= 0) {
>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>> +                sun4i_usb_phy0_id_vbus_det_irq,
>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +                "usb0-vbus-det", data);
>> +        if (ret) {
>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>>       dev_set_drvdata(dev, data);
>>       phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>
>>       return PTR_ERR_OR_ZERO(phy_provider);
>>   }
>>
>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>> +{
>> +    struct device *dev = &pdev->dev;
>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>> +
>> +    if (data->id_det_irq >= 0)
>> +        devm_free_irq(dev, data->id_det_irq, data);
>> +    if (data->vbus_det_irq >= 0)
>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>
> This shouldn't be needed since you already use devm_* in probe.

The use of devm_* in probe helps to keep the error handling in
probe sane, but we must explicitly free the irqs here, otherwise
they may trigger after this:

>> +    cancel_delayed_work_sync(&data->detect);

call causing the workqueue to get run after (parts of) the
phy resources have been freed and then bad things will happen.

>> +
>> +    return 0;
>> +}
>> +
>>   static const struct of_device_id sun4i_usb_phy_of_match[] = {
>>       { .compatible = "allwinner,sun4i-a10-usb-phy" },
>>       { .compatible = "allwinner,sun5i-a13-usb-phy" },
>> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>>
>>   static struct platform_driver sun4i_usb_phy_driver = {
>>       .probe    = sun4i_usb_phy_probe,
>> +    .remove    = sun4i_usb_phy_remove,
>>       .driver = {
>>           .of_match_table    = sun4i_usb_phy_of_match,
>> -        .name  = "sun4i-usb-phy",
>> +        .name  = DRIVER_NAME,
>>       }
>>   };
>>   module_platform_driver(sun4i_usb_phy_driver);
>>

Regards,

Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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	[flat|nested] 57+ messages in thread

* [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11  9:53             ` Hans de Goede
  0 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-11  9:53 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 11-06-15 11:42, Kishon Vijay Abraham I wrote:
> Hi,
>
> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>> The usb0 phy is connected to an OTG controller, and as such needs some special
>> handling:
>>
>> 1) It allows explicit control over the pullups, enable these on phy_init and
>> disable them on phy_exit.
>>
>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>> these bits to the correct values for operating in host only mode when no
>> gpios are specified in the devicetree.
>>
>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>> device cable is plugged in, so when in dual role mode register an extcon.
>>
>> While updating the devicetree binding documentation also add documentation
>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>
>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> ---
>> Changes in v2:
>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>   has been moved to the phy-sun4i-usb driver and their status is exported
>>   through extcon for the sunxi-musb glue
>> Changes in v3:
>> -No changes
>> Changes in v4:
>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>   is present
>> ---
>>   .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>   drivers/phy/Kconfig                                |   1 +
>>   drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>   3 files changed, 281 insertions(+), 11 deletions(-)
>>
> .
> .
> <snip>
> .
> .
>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>> index 91c5be4..b45d707 100644
>> --- a/drivers/phy/phy-sun4i-usb.c
>> +++ b/drivers/phy/phy-sun4i-usb.c
>> @@ -1,7 +1,7 @@
>
> .
> .
> <snip>
> .
> .
>>   static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>                       struct of_phandle_args *args)
>>   {
>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>       struct phy_provider *phy_provider;
>>       bool dedicated_clocks;
>>       struct resource *res;
>> -    int i;
>> +    int i, ret;
>>
>>       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>       if (!data)
>>           return -ENOMEM;
>>
>>       mutex_init(&data->mutex);
>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>> +    data->extcon_cable_names[2] = NULL;
>> +    data->extcon.name = DRIVER_NAME;
>> +    data->extcon.supported_cable = data->extcon_cable_names;
>> +    data->extcon.dev.parent = dev;
>>
>>       if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>           data->num_phys = 2;
>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>       if (IS_ERR(data->base))
>>           return PTR_ERR(data->base);
>>
>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>> +    if (IS_ERR(data->id_det_gpio)) {
>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>> +            return -EPROBE_DEFER;
>> +        data->id_det_gpio = NULL;
>> +    }
>> +
>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>> +    if (IS_ERR(data->vbus_det_gpio)) {
>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>> +            return -EPROBE_DEFER;
>> +        data->vbus_det_gpio = NULL;
>> +    }
>> +
>> +    /* We either want both gpio pins or neither (when in host mode) */
>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>> +        return -ENODEV;
>> +    }
>> +
>> +    if (data->id_det_gpio) {
>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>> +        if (ret) {
>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>>       for (i = 0; i < data->num_phys; i++) {
>>           struct sun4i_usb_phy *phy = data->phys + i;
>>           char name[16];
>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>           phy_set_drvdata(phy->phy, &data->phys[i]);
>>       }
>>
>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>> +        data->phy0_poll = true;
>
> if polling is enabled, we shouldn't enable irq at all?

Thanks for the review.

One some boards one of the gpio-s is irq capable and the other
is not, in which case the current code indeed enables both
irq handling for the one gpio which is irq capable and enables
polling. This is done this way deliberately as the irq path
has much better latency then polling and the 2 can co-exist.

>> +
>> +    if (data->id_det_irq >= 0) {
>> +        ret = devm_request_irq(dev, data->id_det_irq,
>> +                sun4i_usb_phy0_id_vbus_det_irq,
>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +                "usb0-id-det", data);
>> +        if (ret) {
>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>> +    if (data->vbus_det_irq >= 0) {
>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>> +                sun4i_usb_phy0_id_vbus_det_irq,
>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +                "usb0-vbus-det", data);
>> +        if (ret) {
>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>> +            return ret;
>> +        }
>> +    }
>> +
>>       dev_set_drvdata(dev, data);
>>       phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>
>>       return PTR_ERR_OR_ZERO(phy_provider);
>>   }
>>
>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>> +{
>> +    struct device *dev = &pdev->dev;
>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>> +
>> +    if (data->id_det_irq >= 0)
>> +        devm_free_irq(dev, data->id_det_irq, data);
>> +    if (data->vbus_det_irq >= 0)
>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>
> This shouldn't be needed since you already use devm_* in probe.

The use of devm_* in probe helps to keep the error handling in
probe sane, but we must explicitly free the irqs here, otherwise
they may trigger after this:

>> +    cancel_delayed_work_sync(&data->detect);

call causing the workqueue to get run after (parts of) the
phy resources have been freed and then bad things will happen.

>> +
>> +    return 0;
>> +}
>> +
>>   static const struct of_device_id sun4i_usb_phy_of_match[] = {
>>       { .compatible = "allwinner,sun4i-a10-usb-phy" },
>>       { .compatible = "allwinner,sun5i-a13-usb-phy" },
>> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>>
>>   static struct platform_driver sun4i_usb_phy_driver = {
>>       .probe    = sun4i_usb_phy_probe,
>> +    .remove    = sun4i_usb_phy_remove,
>>       .driver = {
>>           .of_match_table    = sun4i_usb_phy_of_match,
>> -        .name  = "sun4i-usb-phy",
>> +        .name  = DRIVER_NAME,
>>       }
>>   };
>>   module_platform_driver(sun4i_usb_phy_driver);
>>

Regards,

Hans

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

* Re: Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-11  9:38                                 ` Hans de Goede
@ 2015-06-11 10:30                                     ` Chanwoo Choi
  -1 siblings, 0 replies; 57+ messages in thread
From: Chanwoo Choi @ 2015-06-11 10:30 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Kishon Vijay Abraham I, Felipe Balbi, Maxime Ripard,
	Chen-Yu Tsai, Roman Byshko, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi,

On 06/11/2015 06:38 PM, Hans de Goede wrote:
> Hi,
> 
> On 11-06-15 11:33, Chanwoo Choi wrote:
>> Hi,
>>
>> On 06/11/2015 05:59 PM, Hans de Goede wrote:
>>> Hi,
>>>
>>> On 11-06-15 10:29, Chanwoo Choi wrote:
>>>> Hi Hans,
>>>>
>>>> On 06/11/2015 05:21 PM, Hans de Goede wrote:
>>>>> Hi Chanwoo,
>>>>>
>>>>> Thanks for the quick review.
>>>>>
>>>>> On 11-06-15 10:07, Chanwoo Choi wrote:
>>>>>> Hi Hans,
>>>>>>
>>>>>> I add the comment about extcon-related code.
>>>>>>
>>>>>> Firstly,
>>>>>> I'd like you to implment the extcon driver for phy-sun4i-usb device
>>>>>> in drivers/extcon/ directoryby using MFD
>>>>>
>>>>> No, just no, this is not what the MFD framework is for, the usb-phy
>>>>> in question here is not a multifunction device. The MFD framework
>>>>> is intended for true multi-function devices like i2c attached
>>>>> PMICs which have regulators, gpios, pwm, input (power button),
>>>>> chargers, power-supply, etc. That is NOT the case here.
>>>>>
>>>>> Also moving this to the MFD framework would very likely requiring
>>>>> the devicetree binding for the usb-phy to change which we cannot
>>>>> do as that would break the devicetree ABI.
>>>>>
>>>>>> because there are both extcon
>>>>>> provider driver and extcon client driver. I think that all extcon
>>>>>> provider driver better to be included in drivers/extcon/ directory.
>>>>>> extcon_set_cable_state() function should be handled in extcon provider
>>>>>> driver which is incluced in drivers/extcon/ directory.
>>>>>
>>>>> I do not find this a compelling reason, there are plenty of subsystems
>>>>> where not all implementations of the subsystem class live in the subsystem
>>>>> directory, e.g. input and hwmon devices are often also found outside of
>>>>> the input and hwmon driver directories.
>>>>
>>>> There are difference on between input/hwmon and extcon.
>>>>
>>>> Because input and hwmon driver implement the only one type driver as provider driver.
>>>> But, extcon implement the two type driver of both extcon provider and extcon client driver.
>>>> The extcon is similiar with regulator and clock framework as resource.
>>>>
>>>> extcon provider driver to provider the event when the state of external connector is changed.
>>>> - devm_extcon_dev_register()
>>>> - e.g., almost extcon provider driver are included in 'drivers/extcon/' directory.
>>>
>>> I understand, but that does not change my first argument, that the usb-phy is not
>>> a MFD device. And although it may be desirable to keep extcon provider drivers
>>> in the drivers/extcon, there are no technical reasons to do so.
>>>
>>> The whole reason why Kishon asked me to start using the extcon framework is to avoid
>>> adding a private API to the phy-sun4i-usb code for notifying the musb-sunxi code
>>> about otg-id-pin status changes. Adding a separate driver for just the extcon bits
>>> means re-adding a private api to the phy-sun4i-usb code but this time for the
>>> extcon code, at which point we might just as well skip extcon and have the
>>> musb-sunxi glue code call directly into the phy-sun4i-usb code...
>>>
>>> Needing a private API for a separate extcn driver actually is a good argument to
>>> NOT have a separate extcon driver and keep the extcon code in the phy-sun4i-usb code,
>>> where as I see no technical arguments in favor of a separate extcon driver.
>>
>> There is one technical issue.
>>
>> The extcon_set_cable_state() should be handled by extcon provider driver.
> 
> That is something which can be done regardless of where the extcon provider
> driver code is located in the kernel tree...
> 
>> because extcon_set_cable_state() inform the extcon client driver of the event
>> when detecting the change of h/w line (gpio line) or register of peripheral device.
>>
>> But, extcon client driver can now get the instance of extcon_dev structure
>> by extcon_get_edev_by_phandle() and then can change the cable state by using the extcon_set_cable_state().
>>
>> I think that these issue have to be protected by framework level.
> 
> Protecting this at the framework level would mean protecting it with code
> in drivers/extcon/extcon.c, that code will be used (and thus can protect
> things) regardless of where the extcon provider code lives.
> 
> I really still see no technical reasons why all extcon provider code
> MUST be under drivers/extcon.

OK. As you said, this issue shold be prevented on framework.
I'm considering what is appropriate method to resolve this issue.

Thanks,
Chanwoo Choi

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

* [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11 10:30                                     ` Chanwoo Choi
  0 siblings, 0 replies; 57+ messages in thread
From: Chanwoo Choi @ 2015-06-11 10:30 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 06/11/2015 06:38 PM, Hans de Goede wrote:
> Hi,
> 
> On 11-06-15 11:33, Chanwoo Choi wrote:
>> Hi,
>>
>> On 06/11/2015 05:59 PM, Hans de Goede wrote:
>>> Hi,
>>>
>>> On 11-06-15 10:29, Chanwoo Choi wrote:
>>>> Hi Hans,
>>>>
>>>> On 06/11/2015 05:21 PM, Hans de Goede wrote:
>>>>> Hi Chanwoo,
>>>>>
>>>>> Thanks for the quick review.
>>>>>
>>>>> On 11-06-15 10:07, Chanwoo Choi wrote:
>>>>>> Hi Hans,
>>>>>>
>>>>>> I add the comment about extcon-related code.
>>>>>>
>>>>>> Firstly,
>>>>>> I'd like you to implment the extcon driver for phy-sun4i-usb device
>>>>>> in drivers/extcon/ directoryby using MFD
>>>>>
>>>>> No, just no, this is not what the MFD framework is for, the usb-phy
>>>>> in question here is not a multifunction device. The MFD framework
>>>>> is intended for true multi-function devices like i2c attached
>>>>> PMICs which have regulators, gpios, pwm, input (power button),
>>>>> chargers, power-supply, etc. That is NOT the case here.
>>>>>
>>>>> Also moving this to the MFD framework would very likely requiring
>>>>> the devicetree binding for the usb-phy to change which we cannot
>>>>> do as that would break the devicetree ABI.
>>>>>
>>>>>> because there are both extcon
>>>>>> provider driver and extcon client driver. I think that all extcon
>>>>>> provider driver better to be included in drivers/extcon/ directory.
>>>>>> extcon_set_cable_state() function should be handled in extcon provider
>>>>>> driver which is incluced in drivers/extcon/ directory.
>>>>>
>>>>> I do not find this a compelling reason, there are plenty of subsystems
>>>>> where not all implementations of the subsystem class live in the subsystem
>>>>> directory, e.g. input and hwmon devices are often also found outside of
>>>>> the input and hwmon driver directories.
>>>>
>>>> There are difference on between input/hwmon and extcon.
>>>>
>>>> Because input and hwmon driver implement the only one type driver as provider driver.
>>>> But, extcon implement the two type driver of both extcon provider and extcon client driver.
>>>> The extcon is similiar with regulator and clock framework as resource.
>>>>
>>>> extcon provider driver to provider the event when the state of external connector is changed.
>>>> - devm_extcon_dev_register()
>>>> - e.g., almost extcon provider driver are included in 'drivers/extcon/' directory.
>>>
>>> I understand, but that does not change my first argument, that the usb-phy is not
>>> a MFD device. And although it may be desirable to keep extcon provider drivers
>>> in the drivers/extcon, there are no technical reasons to do so.
>>>
>>> The whole reason why Kishon asked me to start using the extcon framework is to avoid
>>> adding a private API to the phy-sun4i-usb code for notifying the musb-sunxi code
>>> about otg-id-pin status changes. Adding a separate driver for just the extcon bits
>>> means re-adding a private api to the phy-sun4i-usb code but this time for the
>>> extcon code, at which point we might just as well skip extcon and have the
>>> musb-sunxi glue code call directly into the phy-sun4i-usb code...
>>>
>>> Needing a private API for a separate extcn driver actually is a good argument to
>>> NOT have a separate extcon driver and keep the extcon code in the phy-sun4i-usb code,
>>> where as I see no technical arguments in favor of a separate extcon driver.
>>
>> There is one technical issue.
>>
>> The extcon_set_cable_state() should be handled by extcon provider driver.
> 
> That is something which can be done regardless of where the extcon provider
> driver code is located in the kernel tree...
> 
>> because extcon_set_cable_state() inform the extcon client driver of the event
>> when detecting the change of h/w line (gpio line) or register of peripheral device.
>>
>> But, extcon client driver can now get the instance of extcon_dev structure
>> by extcon_get_edev_by_phandle() and then can change the cable state by using the extcon_set_cable_state().
>>
>> I think that these issue have to be protected by framework level.
> 
> Protecting this at the framework level would mean protecting it with code
> in drivers/extcon/extcon.c, that code will be used (and thus can protect
> things) regardless of where the extcon provider code lives.
> 
> I really still see no technical reasons why all extcon provider code
> MUST be under drivers/extcon.

OK. As you said, this issue shold be prevented on framework.
I'm considering what is appropriate method to resolve this issue.

Thanks,
Chanwoo Choi

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

* Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-11  9:53             ` Hans de Goede
@ 2015-06-11 11:16                 ` Kishon Vijay Abraham I
  -1 siblings, 0 replies; 57+ messages in thread
From: Kishon Vijay Abraham I @ 2015-06-11 11:16 UTC (permalink / raw)
  To: Hans de Goede, Felipe Balbi
  Cc: Maxime Ripard, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi,

On Thursday 11 June 2015 03:23 PM, Hans de Goede wrote:
> Hi,
>
> On 11-06-15 11:42, Kishon Vijay Abraham I wrote:
>> Hi,
>>
>> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>>> The usb0 phy is connected to an OTG controller, and as such needs some special
>>> handling:
>>>
>>> 1) It allows explicit control over the pullups, enable these on phy_init and
>>> disable them on phy_exit.
>>>
>>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>>> these bits to the correct values for operating in host only mode when no
>>> gpios are specified in the devicetree.
>>>
>>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>>> device cable is plugged in, so when in dual role mode register an extcon.
>>>
>>> While updating the devicetree binding documentation also add documentation
>>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>>
>>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>> ---
>>> Changes in v2:
>>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>>    has been moved to the phy-sun4i-usb driver and their status is exported
>>>    through extcon for the sunxi-musb glue
>>> Changes in v3:
>>> -No changes
>>> Changes in v4:
>>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>>    is present
>>> ---
>>>    .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>>    drivers/phy/Kconfig                                |   1 +
>>>    drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>>    3 files changed, 281 insertions(+), 11 deletions(-)
>>>
>> .
>> .
>> <snip>
>> .
>> .
>>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>>> index 91c5be4..b45d707 100644
>>> --- a/drivers/phy/phy-sun4i-usb.c
>>> +++ b/drivers/phy/phy-sun4i-usb.c
>>> @@ -1,7 +1,7 @@
>>
>> .
>> .
>> <snip>
>> .
>> .
>>>    static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>>                        struct of_phandle_args *args)
>>>    {
>>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>        struct phy_provider *phy_provider;
>>>        bool dedicated_clocks;
>>>        struct resource *res;
>>> -    int i;
>>> +    int i, ret;
>>>
>>>        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>>        if (!data)
>>>            return -ENOMEM;
>>>
>>>        mutex_init(&data->mutex);
>>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>>> +    data->extcon_cable_names[2] = NULL;
>>> +    data->extcon.name = DRIVER_NAME;
>>> +    data->extcon.supported_cable = data->extcon_cable_names;
>>> +    data->extcon.dev.parent = dev;
>>>
>>>        if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>>            data->num_phys = 2;
>>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>        if (IS_ERR(data->base))
>>>            return PTR_ERR(data->base);
>>>
>>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>>> +    if (IS_ERR(data->id_det_gpio)) {
>>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>>> +            return -EPROBE_DEFER;
>>> +        data->id_det_gpio = NULL;
>>> +    }
>>> +
>>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>>> +    if (IS_ERR(data->vbus_det_gpio)) {
>>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>>> +            return -EPROBE_DEFER;
>>> +        data->vbus_det_gpio = NULL;
>>> +    }
>>> +
>>> +    /* We either want both gpio pins or neither (when in host mode) */
>>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>>> +        return -ENODEV;
>>> +    }
>>> +
>>> +    if (data->id_det_gpio) {
>>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>>> +        if (ret) {
>>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>>        for (i = 0; i < data->num_phys; i++) {
>>>            struct sun4i_usb_phy *phy = data->phys + i;
>>>            char name[16];
>>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>            phy_set_drvdata(phy->phy, &data->phys[i]);
>>>        }
>>>
>>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>>> +        data->phy0_poll = true;
>>
>> if polling is enabled, we shouldn't enable irq at all?
>
> Thanks for the review.
>
> One some boards one of the gpio-s is irq capable and the other
> is not, in which case the current code indeed enables both
> irq handling for the one gpio which is irq capable and enables
> polling. This is done this way deliberately as the irq path
> has much better latency then polling and the 2 can co-exist.

okay. Would be good to have this as a comment.
>
>>> +
>>> +    if (data->id_det_irq >= 0) {
>>> +        ret = devm_request_irq(dev, data->id_det_irq,
>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>> +                "usb0-id-det", data);
>>> +        if (ret) {
>>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>> +    if (data->vbus_det_irq >= 0) {
>>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>> +                "usb0-vbus-det", data);
>>> +        if (ret) {
>>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>>        dev_set_drvdata(dev, data);
>>>        phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>>
>>>        return PTR_ERR_OR_ZERO(phy_provider);
>>>    }
>>>
>>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>>> +{
>>> +    struct device *dev = &pdev->dev;
>>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>>> +
>>> +    if (data->id_det_irq >= 0)
>>> +        devm_free_irq(dev, data->id_det_irq, data);
>>> +    if (data->vbus_det_irq >= 0)
>>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>>
>> This shouldn't be needed since you already use devm_* in probe.
>
> The use of devm_* in probe helps to keep the error handling in
> probe sane, but we must explicitly free the irqs here, otherwise
> they may trigger after this:

 From whatever I know free irq will be invoked in remove if devm_request_irq is 
used in probe. I have to check though.

Thanks
Kishon

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

* [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11 11:16                 ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 57+ messages in thread
From: Kishon Vijay Abraham I @ 2015-06-11 11:16 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Thursday 11 June 2015 03:23 PM, Hans de Goede wrote:
> Hi,
>
> On 11-06-15 11:42, Kishon Vijay Abraham I wrote:
>> Hi,
>>
>> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>>> The usb0 phy is connected to an OTG controller, and as such needs some special
>>> handling:
>>>
>>> 1) It allows explicit control over the pullups, enable these on phy_init and
>>> disable them on phy_exit.
>>>
>>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>>> these bits to the correct values for operating in host only mode when no
>>> gpios are specified in the devicetree.
>>>
>>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>>> device cable is plugged in, so when in dual role mode register an extcon.
>>>
>>> While updating the devicetree binding documentation also add documentation
>>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>>
>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>>> ---
>>> Changes in v2:
>>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>>    has been moved to the phy-sun4i-usb driver and their status is exported
>>>    through extcon for the sunxi-musb glue
>>> Changes in v3:
>>> -No changes
>>> Changes in v4:
>>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>>    is present
>>> ---
>>>    .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>>    drivers/phy/Kconfig                                |   1 +
>>>    drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>>    3 files changed, 281 insertions(+), 11 deletions(-)
>>>
>> .
>> .
>> <snip>
>> .
>> .
>>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>>> index 91c5be4..b45d707 100644
>>> --- a/drivers/phy/phy-sun4i-usb.c
>>> +++ b/drivers/phy/phy-sun4i-usb.c
>>> @@ -1,7 +1,7 @@
>>
>> .
>> .
>> <snip>
>> .
>> .
>>>    static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>>                        struct of_phandle_args *args)
>>>    {
>>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>        struct phy_provider *phy_provider;
>>>        bool dedicated_clocks;
>>>        struct resource *res;
>>> -    int i;
>>> +    int i, ret;
>>>
>>>        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>>        if (!data)
>>>            return -ENOMEM;
>>>
>>>        mutex_init(&data->mutex);
>>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>>> +    data->extcon_cable_names[2] = NULL;
>>> +    data->extcon.name = DRIVER_NAME;
>>> +    data->extcon.supported_cable = data->extcon_cable_names;
>>> +    data->extcon.dev.parent = dev;
>>>
>>>        if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>>            data->num_phys = 2;
>>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>        if (IS_ERR(data->base))
>>>            return PTR_ERR(data->base);
>>>
>>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>>> +    if (IS_ERR(data->id_det_gpio)) {
>>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>>> +            return -EPROBE_DEFER;
>>> +        data->id_det_gpio = NULL;
>>> +    }
>>> +
>>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>>> +    if (IS_ERR(data->vbus_det_gpio)) {
>>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>>> +            return -EPROBE_DEFER;
>>> +        data->vbus_det_gpio = NULL;
>>> +    }
>>> +
>>> +    /* We either want both gpio pins or neither (when in host mode) */
>>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>>> +        return -ENODEV;
>>> +    }
>>> +
>>> +    if (data->id_det_gpio) {
>>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>>> +        if (ret) {
>>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>>        for (i = 0; i < data->num_phys; i++) {
>>>            struct sun4i_usb_phy *phy = data->phys + i;
>>>            char name[16];
>>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>            phy_set_drvdata(phy->phy, &data->phys[i]);
>>>        }
>>>
>>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>>> +        data->phy0_poll = true;
>>
>> if polling is enabled, we shouldn't enable irq at all?
>
> Thanks for the review.
>
> One some boards one of the gpio-s is irq capable and the other
> is not, in which case the current code indeed enables both
> irq handling for the one gpio which is irq capable and enables
> polling. This is done this way deliberately as the irq path
> has much better latency then polling and the 2 can co-exist.

okay. Would be good to have this as a comment.
>
>>> +
>>> +    if (data->id_det_irq >= 0) {
>>> +        ret = devm_request_irq(dev, data->id_det_irq,
>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>> +                "usb0-id-det", data);
>>> +        if (ret) {
>>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>> +    if (data->vbus_det_irq >= 0) {
>>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>> +                "usb0-vbus-det", data);
>>> +        if (ret) {
>>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>>> +            return ret;
>>> +        }
>>> +    }
>>> +
>>>        dev_set_drvdata(dev, data);
>>>        phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>>
>>>        return PTR_ERR_OR_ZERO(phy_provider);
>>>    }
>>>
>>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>>> +{
>>> +    struct device *dev = &pdev->dev;
>>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>>> +
>>> +    if (data->id_det_irq >= 0)
>>> +        devm_free_irq(dev, data->id_det_irq, data);
>>> +    if (data->vbus_det_irq >= 0)
>>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>>
>> This shouldn't be needed since you already use devm_* in probe.
>
> The use of devm_* in probe helps to keep the error handling in
> probe sane, but we must explicitly free the irqs here, otherwise
> they may trigger after this:

 From whatever I know free irq will be invoked in remove if devm_request_irq is 
used in probe. I have to check though.

Thanks
Kishon

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

* Re: [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-11 11:16                 ` Kishon Vijay Abraham I
@ 2015-06-11 12:35                     ` Hans de Goede
  -1 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-11 12:35 UTC (permalink / raw)
  To: kishon-l0cyMroinI0, Felipe Balbi
  Cc: Maxime Ripard, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi,

On 11-06-15 13:16, Kishon Vijay Abraham I wrote:
> Hi,
>
> On Thursday 11 June 2015 03:23 PM, Hans de Goede wrote:
>> Hi,
>>
>> On 11-06-15 11:42, Kishon Vijay Abraham I wrote:
>>> Hi,
>>>
>>> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>>>> The usb0 phy is connected to an OTG controller, and as such needs some special
>>>> handling:
>>>>
>>>> 1) It allows explicit control over the pullups, enable these on phy_init and
>>>> disable them on phy_exit.
>>>>
>>>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>>>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>>>> these bits to the correct values for operating in host only mode when no
>>>> gpios are specified in the devicetree.
>>>>
>>>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>>>> device cable is plugged in, so when in dual role mode register an extcon.
>>>>
>>>> While updating the devicetree binding documentation also add documentation
>>>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>>>
>>>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>>> ---
>>>> Changes in v2:
>>>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>>>    has been moved to the phy-sun4i-usb driver and their status is exported
>>>>    through extcon for the sunxi-musb glue
>>>> Changes in v3:
>>>> -No changes
>>>> Changes in v4:
>>>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>>>    is present
>>>> ---
>>>>    .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>>>    drivers/phy/Kconfig                                |   1 +
>>>>    drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>>>    3 files changed, 281 insertions(+), 11 deletions(-)
>>>>
>>> .
>>> .
>>> <snip>
>>> .
>>> .
>>>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>>>> index 91c5be4..b45d707 100644
>>>> --- a/drivers/phy/phy-sun4i-usb.c
>>>> +++ b/drivers/phy/phy-sun4i-usb.c
>>>> @@ -1,7 +1,7 @@
>>>
>>> .
>>> .
>>> <snip>
>>> .
>>> .
>>>>    static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>>>                        struct of_phandle_args *args)
>>>>    {
>>>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>        struct phy_provider *phy_provider;
>>>>        bool dedicated_clocks;
>>>>        struct resource *res;
>>>> -    int i;
>>>> +    int i, ret;
>>>>
>>>>        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>>>        if (!data)
>>>>            return -ENOMEM;
>>>>
>>>>        mutex_init(&data->mutex);
>>>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>>>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>>>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>>>> +    data->extcon_cable_names[2] = NULL;
>>>> +    data->extcon.name = DRIVER_NAME;
>>>> +    data->extcon.supported_cable = data->extcon_cable_names;
>>>> +    data->extcon.dev.parent = dev;
>>>>
>>>>        if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>>>            data->num_phys = 2;
>>>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>        if (IS_ERR(data->base))
>>>>            return PTR_ERR(data->base);
>>>>
>>>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>>>> +    if (IS_ERR(data->id_det_gpio)) {
>>>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>>>> +            return -EPROBE_DEFER;
>>>> +        data->id_det_gpio = NULL;
>>>> +    }
>>>> +
>>>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>>>> +    if (IS_ERR(data->vbus_det_gpio)) {
>>>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>>>> +            return -EPROBE_DEFER;
>>>> +        data->vbus_det_gpio = NULL;
>>>> +    }
>>>> +
>>>> +    /* We either want both gpio pins or neither (when in host mode) */
>>>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>>>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>>>> +        return -ENODEV;
>>>> +    }
>>>> +
>>>> +    if (data->id_det_gpio) {
>>>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>>>> +        if (ret) {
>>>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>>>> +            return ret;
>>>> +        }
>>>> +    }
>>>> +
>>>>        for (i = 0; i < data->num_phys; i++) {
>>>>            struct sun4i_usb_phy *phy = data->phys + i;
>>>>            char name[16];
>>>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>            phy_set_drvdata(phy->phy, &data->phys[i]);
>>>>        }
>>>>
>>>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>>>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>>>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>>>> +        data->phy0_poll = true;
>>>
>>> if polling is enabled, we shouldn't enable irq at all?
>>
>> Thanks for the review.
>>
>> One some boards one of the gpio-s is irq capable and the other
>> is not, in which case the current code indeed enables both
>> irq handling for the one gpio which is irq capable and enables
>> polling. This is done this way deliberately as the irq path
>> has much better latency then polling and the 2 can co-exist.
>
> okay. Would be good to have this as a comment.
>>
>>>> +
>>>> +    if (data->id_det_irq >= 0) {
>>>> +        ret = devm_request_irq(dev, data->id_det_irq,
>>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>>> +                "usb0-id-det", data);
>>>> +        if (ret) {
>>>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>>>> +            return ret;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    if (data->vbus_det_irq >= 0) {
>>>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>>> +                "usb0-vbus-det", data);
>>>> +        if (ret) {
>>>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>>>> +            return ret;
>>>> +        }
>>>> +    }
>>>> +
>>>>        dev_set_drvdata(dev, data);
>>>>        phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>>>
>>>>        return PTR_ERR_OR_ZERO(phy_provider);
>>>>    }
>>>>
>>>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>>>> +{
>>>> +    struct device *dev = &pdev->dev;
>>>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>>>> +
>>>> +    if (data->id_det_irq >= 0)
>>>> +        devm_free_irq(dev, data->id_det_irq, data);
>>>> +    if (data->vbus_det_irq >= 0)
>>>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>>>
>>> This shouldn't be needed since you already use devm_* in probe.
>>
>> The use of devm_* in probe helps to keep the error handling in
>> probe sane, but we must explicitly free the irqs here, otherwise
>> they may trigger after this:
>
>  From whatever I know free irq will be invoked in remove if devm_request_irq is used in probe. I have to check though.

Yes it will be invoked, but after we've stopped our workqueue function,
and in the mean time the irq handler can trigger re-scheduling the
workqueue function.

All drivers using a workqueue explicitly free irqs on remove before
stopping the workqueue because of this reason. The auto freeing
of the irq happens after the remove function has been called, and
thus after the workqueue has been stopped/cancelled which is too
late.

Regards,

Hans
--
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	[flat|nested] 57+ messages in thread

* [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11 12:35                     ` Hans de Goede
  0 siblings, 0 replies; 57+ messages in thread
From: Hans de Goede @ 2015-06-11 12:35 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 11-06-15 13:16, Kishon Vijay Abraham I wrote:
> Hi,
>
> On Thursday 11 June 2015 03:23 PM, Hans de Goede wrote:
>> Hi,
>>
>> On 11-06-15 11:42, Kishon Vijay Abraham I wrote:
>>> Hi,
>>>
>>> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>>>> The usb0 phy is connected to an OTG controller, and as such needs some special
>>>> handling:
>>>>
>>>> 1) It allows explicit control over the pullups, enable these on phy_init and
>>>> disable them on phy_exit.
>>>>
>>>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>>>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>>>> these bits to the correct values for operating in host only mode when no
>>>> gpios are specified in the devicetree.
>>>>
>>>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>>>> device cable is plugged in, so when in dual role mode register an extcon.
>>>>
>>>> While updating the devicetree binding documentation also add documentation
>>>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>>>
>>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>>>> ---
>>>> Changes in v2:
>>>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>>>    has been moved to the phy-sun4i-usb driver and their status is exported
>>>>    through extcon for the sunxi-musb glue
>>>> Changes in v3:
>>>> -No changes
>>>> Changes in v4:
>>>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>>>    is present
>>>> ---
>>>>    .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>>>    drivers/phy/Kconfig                                |   1 +
>>>>    drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>>>    3 files changed, 281 insertions(+), 11 deletions(-)
>>>>
>>> .
>>> .
>>> <snip>
>>> .
>>> .
>>>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>>>> index 91c5be4..b45d707 100644
>>>> --- a/drivers/phy/phy-sun4i-usb.c
>>>> +++ b/drivers/phy/phy-sun4i-usb.c
>>>> @@ -1,7 +1,7 @@
>>>
>>> .
>>> .
>>> <snip>
>>> .
>>> .
>>>>    static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>>>                        struct of_phandle_args *args)
>>>>    {
>>>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>        struct phy_provider *phy_provider;
>>>>        bool dedicated_clocks;
>>>>        struct resource *res;
>>>> -    int i;
>>>> +    int i, ret;
>>>>
>>>>        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>>>        if (!data)
>>>>            return -ENOMEM;
>>>>
>>>>        mutex_init(&data->mutex);
>>>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>>>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>>>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>>>> +    data->extcon_cable_names[2] = NULL;
>>>> +    data->extcon.name = DRIVER_NAME;
>>>> +    data->extcon.supported_cable = data->extcon_cable_names;
>>>> +    data->extcon.dev.parent = dev;
>>>>
>>>>        if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>>>            data->num_phys = 2;
>>>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>        if (IS_ERR(data->base))
>>>>            return PTR_ERR(data->base);
>>>>
>>>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>>>> +    if (IS_ERR(data->id_det_gpio)) {
>>>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>>>> +            return -EPROBE_DEFER;
>>>> +        data->id_det_gpio = NULL;
>>>> +    }
>>>> +
>>>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>>>> +    if (IS_ERR(data->vbus_det_gpio)) {
>>>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>>>> +            return -EPROBE_DEFER;
>>>> +        data->vbus_det_gpio = NULL;
>>>> +    }
>>>> +
>>>> +    /* We either want both gpio pins or neither (when in host mode) */
>>>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>>>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>>>> +        return -ENODEV;
>>>> +    }
>>>> +
>>>> +    if (data->id_det_gpio) {
>>>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>>>> +        if (ret) {
>>>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>>>> +            return ret;
>>>> +        }
>>>> +    }
>>>> +
>>>>        for (i = 0; i < data->num_phys; i++) {
>>>>            struct sun4i_usb_phy *phy = data->phys + i;
>>>>            char name[16];
>>>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>            phy_set_drvdata(phy->phy, &data->phys[i]);
>>>>        }
>>>>
>>>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>>>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>>>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>>>> +        data->phy0_poll = true;
>>>
>>> if polling is enabled, we shouldn't enable irq at all?
>>
>> Thanks for the review.
>>
>> One some boards one of the gpio-s is irq capable and the other
>> is not, in which case the current code indeed enables both
>> irq handling for the one gpio which is irq capable and enables
>> polling. This is done this way deliberately as the irq path
>> has much better latency then polling and the 2 can co-exist.
>
> okay. Would be good to have this as a comment.
>>
>>>> +
>>>> +    if (data->id_det_irq >= 0) {
>>>> +        ret = devm_request_irq(dev, data->id_det_irq,
>>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>>> +                "usb0-id-det", data);
>>>> +        if (ret) {
>>>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>>>> +            return ret;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    if (data->vbus_det_irq >= 0) {
>>>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>>> +                "usb0-vbus-det", data);
>>>> +        if (ret) {
>>>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>>>> +            return ret;
>>>> +        }
>>>> +    }
>>>> +
>>>>        dev_set_drvdata(dev, data);
>>>>        phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>>>
>>>>        return PTR_ERR_OR_ZERO(phy_provider);
>>>>    }
>>>>
>>>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>>>> +{
>>>> +    struct device *dev = &pdev->dev;
>>>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>>>> +
>>>> +    if (data->id_det_irq >= 0)
>>>> +        devm_free_irq(dev, data->id_det_irq, data);
>>>> +    if (data->vbus_det_irq >= 0)
>>>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>>>
>>> This shouldn't be needed since you already use devm_* in probe.
>>
>> The use of devm_* in probe helps to keep the error handling in
>> probe sane, but we must explicitly free the irqs here, otherwise
>> they may trigger after this:
>
>  From whatever I know free irq will be invoked in remove if devm_request_irq is used in probe. I have to check though.

Yes it will be invoked, but after we've stopped our workqueue function,
and in the mean time the irq handler can trigger re-scheduling the
workqueue function.

All drivers using a workqueue explicitly free irqs on remove before
stopping the workqueue because of this reason. The auto freeing
of the irq happens after the remove function has been called, and
thus after the workqueue has been stopped/cancelled which is too
late.

Regards,

Hans

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

* Re: Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-06-11 12:35                     ` Hans de Goede
@ 2015-06-11 12:59                         ` Kishon Vijay Abraham I
  -1 siblings, 0 replies; 57+ messages in thread
From: Kishon Vijay Abraham I @ 2015-06-11 12:59 UTC (permalink / raw)
  To: Hans de Goede, Felipe Balbi
  Cc: Maxime Ripard, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw



On Thursday 11 June 2015 06:05 PM, Hans de Goede wrote:
> Hi,
>
> On 11-06-15 13:16, Kishon Vijay Abraham I wrote:
>> Hi,
>>
>> On Thursday 11 June 2015 03:23 PM, Hans de Goede wrote:
>>> Hi,
>>>
>>> On 11-06-15 11:42, Kishon Vijay Abraham I wrote:
>>>> Hi,
>>>>
>>>> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>>>>> The usb0 phy is connected to an OTG controller, and as such needs some special
>>>>> handling:
>>>>>
>>>>> 1) It allows explicit control over the pullups, enable these on phy_init and
>>>>> disable them on phy_exit.
>>>>>
>>>>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>>>>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>>>>> these bits to the correct values for operating in host only mode when no
>>>>> gpios are specified in the devicetree.
>>>>>
>>>>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>>>>> device cable is plugged in, so when in dual role mode register an extcon.
>>>>>
>>>>> While updating the devicetree binding documentation also add documentation
>>>>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>>>>
>>>>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>>>> ---
>>>>> Changes in v2:
>>>>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>>>>     has been moved to the phy-sun4i-usb driver and their status is exported
>>>>>     through extcon for the sunxi-musb glue
>>>>> Changes in v3:
>>>>> -No changes
>>>>> Changes in v4:
>>>>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>>>>     is present
>>>>> ---
>>>>>     .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>>>>     drivers/phy/Kconfig                                |   1 +
>>>>>     drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>>>>     3 files changed, 281 insertions(+), 11 deletions(-)
>>>>>
>>>> .
>>>> .
>>>> <snip>
>>>> .
>>>> .
>>>>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>>>>> index 91c5be4..b45d707 100644
>>>>> --- a/drivers/phy/phy-sun4i-usb.c
>>>>> +++ b/drivers/phy/phy-sun4i-usb.c
>>>>> @@ -1,7 +1,7 @@
>>>>
>>>> .
>>>> .
>>>> <snip>
>>>> .
>>>> .
>>>>>     static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>>>>                         struct of_phandle_args *args)
>>>>>     {
>>>>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>>         struct phy_provider *phy_provider;
>>>>>         bool dedicated_clocks;
>>>>>         struct resource *res;
>>>>> -    int i;
>>>>> +    int i, ret;
>>>>>
>>>>>         data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>>>>         if (!data)
>>>>>             return -ENOMEM;
>>>>>
>>>>>         mutex_init(&data->mutex);
>>>>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>>>>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>>>>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>>>>> +    data->extcon_cable_names[2] = NULL;
>>>>> +    data->extcon.name = DRIVER_NAME;
>>>>> +    data->extcon.supported_cable = data->extcon_cable_names;
>>>>> +    data->extcon.dev.parent = dev;
>>>>>
>>>>>         if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>>>>             data->num_phys = 2;
>>>>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>>         if (IS_ERR(data->base))
>>>>>             return PTR_ERR(data->base);
>>>>>
>>>>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>>>>> +    if (IS_ERR(data->id_det_gpio)) {
>>>>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>>>>> +            return -EPROBE_DEFER;
>>>>> +        data->id_det_gpio = NULL;
>>>>> +    }
>>>>> +
>>>>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>>>>> +    if (IS_ERR(data->vbus_det_gpio)) {
>>>>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>>>>> +            return -EPROBE_DEFER;
>>>>> +        data->vbus_det_gpio = NULL;
>>>>> +    }
>>>>> +
>>>>> +    /* We either want both gpio pins or neither (when in host mode) */
>>>>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>>>>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>>>>> +        return -ENODEV;
>>>>> +    }
>>>>> +
>>>>> +    if (data->id_det_gpio) {
>>>>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>>>>> +        if (ret) {
>>>>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>>>>> +            return ret;
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>>         for (i = 0; i < data->num_phys; i++) {
>>>>>             struct sun4i_usb_phy *phy = data->phys + i;
>>>>>             char name[16];
>>>>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>>             phy_set_drvdata(phy->phy, &data->phys[i]);
>>>>>         }
>>>>>
>>>>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>>>>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>>>>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>>>>> +        data->phy0_poll = true;
>>>>
>>>> if polling is enabled, we shouldn't enable irq at all?
>>>
>>> Thanks for the review.
>>>
>>> One some boards one of the gpio-s is irq capable and the other
>>> is not, in which case the current code indeed enables both
>>> irq handling for the one gpio which is irq capable and enables
>>> polling. This is done this way deliberately as the irq path
>>> has much better latency then polling and the 2 can co-exist.
>>
>> okay. Would be good to have this as a comment.
>>>
>>>>> +
>>>>> +    if (data->id_det_irq >= 0) {
>>>>> +        ret = devm_request_irq(dev, data->id_det_irq,
>>>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>>>> +                "usb0-id-det", data);
>>>>> +        if (ret) {
>>>>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>>>>> +            return ret;
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>> +    if (data->vbus_det_irq >= 0) {
>>>>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>>>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>>>> +                "usb0-vbus-det", data);
>>>>> +        if (ret) {
>>>>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>>>>> +            return ret;
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>>         dev_set_drvdata(dev, data);
>>>>>         phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>>>>
>>>>>         return PTR_ERR_OR_ZERO(phy_provider);
>>>>>     }
>>>>>
>>>>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>>>>> +{
>>>>> +    struct device *dev = &pdev->dev;
>>>>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>>>>> +
>>>>> +    if (data->id_det_irq >= 0)
>>>>> +        devm_free_irq(dev, data->id_det_irq, data);
>>>>> +    if (data->vbus_det_irq >= 0)
>>>>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>>>>
>>>> This shouldn't be needed since you already use devm_* in probe.
>>>
>>> The use of devm_* in probe helps to keep the error handling in
>>> probe sane, but we must explicitly free the irqs here, otherwise
>>> they may trigger after this:
>>
>>   From whatever I know free irq will be invoked in remove if devm_request_irq is used in probe. I have to check though.
>
> Yes it will be invoked, but after we've stopped our workqueue function,
> and in the mean time the irq handler can trigger re-scheduling the
> workqueue function.
>
> All drivers using a workqueue explicitly free irqs on remove before
> stopping the workqueue because of this reason. The auto freeing
> of the irq happens after the remove function has been called, and
> thus after the workqueue has been stopped/cancelled which is too
> late.

Fine then. Thanks for that detail.

Thanks
Kishon

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

* [linux-sunxi] Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-06-11 12:59                         ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 57+ messages in thread
From: Kishon Vijay Abraham I @ 2015-06-11 12:59 UTC (permalink / raw)
  To: linux-arm-kernel



On Thursday 11 June 2015 06:05 PM, Hans de Goede wrote:
> Hi,
>
> On 11-06-15 13:16, Kishon Vijay Abraham I wrote:
>> Hi,
>>
>> On Thursday 11 June 2015 03:23 PM, Hans de Goede wrote:
>>> Hi,
>>>
>>> On 11-06-15 11:42, Kishon Vijay Abraham I wrote:
>>>> Hi,
>>>>
>>>> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>>>>> The usb0 phy is connected to an OTG controller, and as such needs some special
>>>>> handling:
>>>>>
>>>>> 1) It allows explicit control over the pullups, enable these on phy_init and
>>>>> disable them on phy_exit.
>>>>>
>>>>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>>>>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>>>>> these bits to the correct values for operating in host only mode when no
>>>>> gpios are specified in the devicetree.
>>>>>
>>>>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>>>>> device cable is plugged in, so when in dual role mode register an extcon.
>>>>>
>>>>> While updating the devicetree binding documentation also add documentation
>>>>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
>>>>>
>>>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>>>>> ---
>>>>> Changes in v2:
>>>>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>>>>     has been moved to the phy-sun4i-usb driver and their status is exported
>>>>>     through extcon for the sunxi-musb glue
>>>>> Changes in v3:
>>>>> -No changes
>>>>> Changes in v4:
>>>>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>>>>     is present
>>>>> ---
>>>>>     .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>>>>     drivers/phy/Kconfig                                |   1 +
>>>>>     drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>>>>     3 files changed, 281 insertions(+), 11 deletions(-)
>>>>>
>>>> .
>>>> .
>>>> <snip>
>>>> .
>>>> .
>>>>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>>>>> index 91c5be4..b45d707 100644
>>>>> --- a/drivers/phy/phy-sun4i-usb.c
>>>>> +++ b/drivers/phy/phy-sun4i-usb.c
>>>>> @@ -1,7 +1,7 @@
>>>>
>>>> .
>>>> .
>>>> <snip>
>>>> .
>>>> .
>>>>>     static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>>>>                         struct of_phandle_args *args)
>>>>>     {
>>>>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>>         struct phy_provider *phy_provider;
>>>>>         bool dedicated_clocks;
>>>>>         struct resource *res;
>>>>> -    int i;
>>>>> +    int i, ret;
>>>>>
>>>>>         data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>>>>         if (!data)
>>>>>             return -ENOMEM;
>>>>>
>>>>>         mutex_init(&data->mutex);
>>>>> +    INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>>>>> +    data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>>>>> +    data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>>>>> +    data->extcon_cable_names[2] = NULL;
>>>>> +    data->extcon.name = DRIVER_NAME;
>>>>> +    data->extcon.supported_cable = data->extcon_cable_names;
>>>>> +    data->extcon.dev.parent = dev;
>>>>>
>>>>>         if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>>>>             data->num_phys = 2;
>>>>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>>         if (IS_ERR(data->base))
>>>>>             return PTR_ERR(data->base);
>>>>>
>>>>> +    data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>>>>> +    if (IS_ERR(data->id_det_gpio)) {
>>>>> +        if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>>>>> +            return -EPROBE_DEFER;
>>>>> +        data->id_det_gpio = NULL;
>>>>> +    }
>>>>> +
>>>>> +    data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>>>>> +    if (IS_ERR(data->vbus_det_gpio)) {
>>>>> +        if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>>>>> +            return -EPROBE_DEFER;
>>>>> +        data->vbus_det_gpio = NULL;
>>>>> +    }
>>>>> +
>>>>> +    /* We either want both gpio pins or neither (when in host mode) */
>>>>> +    if (!data->id_det_gpio != !data->vbus_det_gpio) {
>>>>> +        dev_err(dev, "failed to get id or vbus detect pin\n");
>>>>> +        return -ENODEV;
>>>>> +    }
>>>>> +
>>>>> +    if (data->id_det_gpio) {
>>>>> +        ret = devm_extcon_dev_register(dev, &data->extcon);
>>>>> +        if (ret) {
>>>>> +            dev_err(dev, "failed to register extcon: %d\n", ret);
>>>>> +            return ret;
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>>         for (i = 0; i < data->num_phys; i++) {
>>>>>             struct sun4i_usb_phy *phy = data->phys + i;
>>>>>             char name[16];
>>>>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>>>>             phy_set_drvdata(phy->phy, &data->phys[i]);
>>>>>         }
>>>>>
>>>>> +    data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>>>>> +    data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>>>>> +    if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>>>>> +        data->phy0_poll = true;
>>>>
>>>> if polling is enabled, we shouldn't enable irq at all?
>>>
>>> Thanks for the review.
>>>
>>> One some boards one of the gpio-s is irq capable and the other
>>> is not, in which case the current code indeed enables both
>>> irq handling for the one gpio which is irq capable and enables
>>> polling. This is done this way deliberately as the irq path
>>> has much better latency then polling and the 2 can co-exist.
>>
>> okay. Would be good to have this as a comment.
>>>
>>>>> +
>>>>> +    if (data->id_det_irq >= 0) {
>>>>> +        ret = devm_request_irq(dev, data->id_det_irq,
>>>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>>>> +                "usb0-id-det", data);
>>>>> +        if (ret) {
>>>>> +            dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>>>>> +            return ret;
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>> +    if (data->vbus_det_irq >= 0) {
>>>>> +        ret = devm_request_irq(dev, data->vbus_det_irq,
>>>>> +                sun4i_usb_phy0_id_vbus_det_irq,
>>>>> +                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>>>>> +                "usb0-vbus-det", data);
>>>>> +        if (ret) {
>>>>> +            dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>>>>> +            return ret;
>>>>> +        }
>>>>> +    }
>>>>> +
>>>>>         dev_set_drvdata(dev, data);
>>>>>         phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>>>>
>>>>>         return PTR_ERR_OR_ZERO(phy_provider);
>>>>>     }
>>>>>
>>>>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>>>>> +{
>>>>> +    struct device *dev = &pdev->dev;
>>>>> +    struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>>>>> +
>>>>> +    if (data->id_det_irq >= 0)
>>>>> +        devm_free_irq(dev, data->id_det_irq, data);
>>>>> +    if (data->vbus_det_irq >= 0)
>>>>> +        devm_free_irq(dev, data->vbus_det_irq, data);
>>>>
>>>> This shouldn't be needed since you already use devm_* in probe.
>>>
>>> The use of devm_* in probe helps to keep the error handling in
>>> probe sane, but we must explicitly free the irqs here, otherwise
>>> they may trigger after this:
>>
>>   From whatever I know free irq will be invoked in remove if devm_request_irq is used in probe. I have to check though.
>
> Yes it will be invoked, but after we've stopped our workqueue function,
> and in the mean time the irq handler can trigger re-scheduling the
> workqueue function.
>
> All drivers using a workqueue explicitly free irqs on remove before
> stopping the workqueue because of this reason. The auto freeing
> of the irq happens after the remove function has been called, and
> thus after the workqueue has been stopped/cancelled which is too
> late.

Fine then. Thanks for that detail.

Thanks
Kishon

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

* Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-05-31 16:10     ` Hans de Goede
@ 2015-07-15 10:55         ` Kishon Vijay Abraham I
  -1 siblings, 0 replies; 57+ messages in thread
From: Kishon Vijay Abraham I @ 2015-07-15 10:55 UTC (permalink / raw)
  To: Hans de Goede, Felipe Balbi
  Cc: Maxime Ripard, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi,

On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
> The usb0 phy is connected to an OTG controller, and as such needs some special
> handling:
> 
> 1) It allows explicit control over the pullups, enable these on phy_init and
> disable them on phy_exit.
> 
> 2) It has bits to signal id and vbus detect to the musb-core, add support for
> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
> these bits to the correct values for operating in host only mode when no
> gpios are specified in the devicetree.
> 
> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
> device cable is plugged in, so when in dual role mode register an extcon.
> 
> While updating the devicetree binding documentation also add documentation
> for the sofar undocumented usage of regulators for vbus for all 3 phys.

merged this to linux-phy tree.

Cheers
Kishon
> 
> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> ---
> Changes in v2:
> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>  has been moved to the phy-sun4i-usb driver and their status is exported
>  through extcon for the sunxi-musb glue
> Changes in v3:
> -No changes
> Changes in v4:
> -Do not call regulator_disable in an unbalanced manner when an external vbus
>  is present
> ---
>  .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>  drivers/phy/Kconfig                                |   1 +
>  drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>  3 files changed, 281 insertions(+), 11 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> index 16528b9..557fa99 100644
> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> @@ -23,6 +23,13 @@ Required properties:
>    * "usb1_reset"
>    * "usb2_reset" for sun4i, sun6i or sun7i
>  
> +Optional properties:
> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
> +
>  Example:
>  	usbphy: phy@0x01c13400 {
>  		#phy-cells = <1>;
> @@ -32,6 +39,13 @@ Example:
>  		reg-names = "phy_ctrl", "pmu1", "pmu2";
>  		clocks = <&usb_clk 8>;
>  		clock-names = "usb_phy";
> -		resets = <&usb_clk 1>, <&usb_clk 2>;
> -		reset-names = "usb1_reset", "usb2_reset";
> +		resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
> +		reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
> +		usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
> +		usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
> +		usb0_vbus-supply = <&reg_usb0_vbus>;
> +		usb1_vbus-supply = <&reg_usb1_vbus>;
> +		usb2_vbus-supply = <&reg_usb2_vbus>;
>  	};
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index a53bd5b..4614fba 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>  	tristate "Allwinner sunxi SoC USB PHY driver"
>  	depends on ARCH_SUNXI && HAS_IOMEM && OF
>  	depends on RESET_CONTROLLER
> +	select EXTCON
>  	select GENERIC_PHY
>  	help
>  	  Enable this to support the transceiver that is part of Allwinner
> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
> index 91c5be4..b45d707 100644
> --- a/drivers/phy/phy-sun4i-usb.c
> +++ b/drivers/phy/phy-sun4i-usb.c
> @@ -1,7 +1,7 @@
>  /*
>   * Allwinner sun4i USB phy driver
>   *
> - * Copyright (C) 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>   *
>   * Based on code from
>   * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
> @@ -23,17 +23,23 @@
>  
>  #include <linux/clk.h>
>  #include <linux/err.h>
> +#include <linux/extcon.h>
>  #include <linux/io.h>
> +#include <linux/interrupt.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_gpio.h>
>  #include <linux/phy/phy.h>
>  #include <linux/phy/phy-sun4i-usb.h>
>  #include <linux/platform_device.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/reset.h>
> +#include <linux/workqueue.h>
> +
> +#define DRIVER_NAME "sun4i-usb-phy"
>  
>  #define REG_ISCR			0x00
>  #define REG_PHYCTL			0x04
> @@ -47,6 +53,17 @@
>  #define SUNXI_AHB_INCRX_ALIGN_EN	BIT(8)
>  #define SUNXI_ULPI_BYPASS_EN		BIT(0)
>  
> +/* ISCR, Interface Status and Control bits */
> +#define ISCR_ID_PULLUP_EN		(1 << 17)
> +#define ISCR_DPDM_PULLUP_EN	(1 << 16)
> +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
> +#define ISCR_FORCE_ID_MASK	(3 << 14)
> +#define ISCR_FORCE_ID_LOW		(2 << 14)
> +#define ISCR_FORCE_ID_HIGH	(3 << 14)
> +#define ISCR_FORCE_VBUS_MASK	(3 << 12)
> +#define ISCR_FORCE_VBUS_LOW	(2 << 12)
> +#define ISCR_FORCE_VBUS_HIGH	(3 << 12)
> +
>  /* Common Control Bits for Both PHYs */
>  #define PHY_PLL_BW			0x03
>  #define PHY_RES45_CAL_EN		0x0c
> @@ -63,6 +80,13 @@
>  
>  #define MAX_PHYS			3
>  
> +/*
> + * Note do not raise the debounce time, we must report Vusb high within 100ms
> + * otherwise we get Vbus errors
> + */
> +#define DEBOUNCE_TIME			msecs_to_jiffies(50)
> +#define POLL_TIME			msecs_to_jiffies(250)
> +
>  struct sun4i_usb_phy_data {
>  	void __iomem *base;
>  	struct mutex mutex;
> @@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
>  		struct regulator *vbus;
>  		struct reset_control *reset;
>  		struct clk *clk;
> +		bool regulator_on;
>  		int index;
>  	} phys[MAX_PHYS];
> +	/* phy0 / otg related variables */
> +	struct extcon_dev extcon;
> +	const char *extcon_cable_names[3];
> +	bool phy0_init;
> +	bool phy0_poll;
> +	struct gpio_desc *id_det_gpio;
> +	struct gpio_desc *vbus_det_gpio;
> +	int id_det_irq;
> +	int vbus_det_irq;
> +	int id_det;
> +	int vbus_det;
> +	struct delayed_work detect;
>  };
>  
>  #define to_sun4i_usb_phy_data(phy) \
>  	container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>  
> +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
> +{
> +	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +	u32 iscr;
> +
> +	iscr = readl(data->base + REG_ISCR);
> +	iscr &= ~clr;
> +	iscr |= set;
> +	writel(iscr, data->base + REG_ISCR);
> +}
> +
> +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
> +{
> +	if (val)
> +		val = ISCR_FORCE_ID_HIGH;
> +	else
> +		val = ISCR_FORCE_ID_LOW;
> +
> +	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
> +}
> +
> +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
> +{
> +	if (val)
> +		val = ISCR_FORCE_VBUS_HIGH;
> +	else
> +		val = ISCR_FORCE_VBUS_LOW;
> +
> +	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
> +}
> +
>  static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>  				int len)
>  {
> @@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
>  
>  	sun4i_usb_phy_passby(phy, 1);
>  
> +	if (phy->index == 0) {
> +		data->phy0_init = true;
> +
> +		/* Enable pull-ups */
> +		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
> +		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
> +
> +		if (data->id_det_gpio) {
> +			/* OTG mode, force ISCR and cable state updates */
> +			data->id_det = -1;
> +			data->vbus_det = -1;
> +			queue_delayed_work(system_wq, &data->detect, 0);
> +		} else {
> +			/* Host only mode */
> +			sun4i_usb_phy0_set_id_detect(_phy, 0);
> +			sun4i_usb_phy0_set_vbus_detect(_phy, 1);
> +		}
> +	}
> +
>  	return 0;
>  }
>  
>  static int sun4i_usb_phy_exit(struct phy *_phy)
>  {
>  	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +
> +	if (phy->index == 0) {
> +		/* Disable pull-ups */
> +		sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
> +		sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
> +		data->phy0_init = false;
> +	}
>  
>  	sun4i_usb_phy_passby(phy, 0);
>  	reset_control_assert(phy->reset);
> @@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
>  static int sun4i_usb_phy_power_on(struct phy *_phy)
>  {
>  	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> -	int ret = 0;
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +	int ret;
> +
> +	if (!phy->vbus || phy->regulator_on)
> +		return 0;
> +
> +	/* For phy0 only turn on Vbus if we don't have an ext. Vbus */
> +	if (phy->index == 0 && data->vbus_det)
> +		return 0;
>  
> -	if (phy->vbus)
> -		ret = regulator_enable(phy->vbus);
> +	ret = regulator_enable(phy->vbus);
> +	if (ret)
> +		return ret;
>  
> -	return ret;
> +	phy->regulator_on = true;
> +
> +	/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
> +	if (phy->index == 0 && data->phy0_poll)
> +		mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
> +
> +	return 0;
>  }
>  
>  static int sun4i_usb_phy_power_off(struct phy *_phy)
>  {
>  	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +
> +	if (!phy->vbus || !phy->regulator_on)
> +		return 0;
>  
> -	if (phy->vbus)
> -		regulator_disable(phy->vbus);
> +	regulator_disable(phy->vbus);
> +	phy->regulator_on = false;
> +
> +	/*
> +	 * phy0 vbus typically slowly discharges, sometimes this causes the
> +	 * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
> +	 */
> +	if (phy->index == 0 && !data->phy0_poll)
> +		mod_delayed_work(system_wq, &data->detect, POLL_TIME);
>  
>  	return 0;
>  }
> @@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
>  	.owner		= THIS_MODULE,
>  };
>  
> +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
> +{
> +	struct sun4i_usb_phy_data *data =
> +		container_of(work, struct sun4i_usb_phy_data, detect.work);
> +	struct phy *phy0 = data->phys[0].phy;
> +	int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
> +
> +	id_det = gpiod_get_value_cansleep(data->id_det_gpio);
> +	vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
> +
> +	mutex_lock(&phy0->mutex);
> +
> +	if (!data->phy0_init) {
> +		mutex_unlock(&phy0->mutex);
> +		return;
> +	}
> +
> +	if (id_det != data->id_det) {
> +		sun4i_usb_phy0_set_id_detect(phy0, id_det);
> +		data->id_det = id_det;
> +		id_notify = 1;
> +	}
> +
> +	if (vbus_det != data->vbus_det) {
> +		sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
> +		data->vbus_det = vbus_det;
> +		vbus_notify = 1;
> +	}
> +
> +	mutex_unlock(&phy0->mutex);
> +
> +	if (id_notify)
> +		extcon_set_cable_state(&data->extcon,
> +				       extcon_cable_name[EXTCON_USB_HOST],
> +				       !id_det);
> +
> +	if (vbus_notify)
> +		extcon_set_cable_state(&data->extcon,
> +				       extcon_cable_name[EXTCON_USB],
> +				       vbus_det);
> +
> +	if (data->phy0_poll)
> +		queue_delayed_work(system_wq, &data->detect, POLL_TIME);
> +}
> +
> +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
> +{
> +	struct sun4i_usb_phy_data *data = dev_id;
> +
> +	/* vbus or id changed, let the pins settle and then scan them */
> +	mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
> +
> +	return IRQ_HANDLED;
> +}
> +
>  static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>  					struct of_phandle_args *args)
>  {
> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>  	struct phy_provider *phy_provider;
>  	bool dedicated_clocks;
>  	struct resource *res;
> -	int i;
> +	int i, ret;
>  
>  	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>  	if (!data)
>  		return -ENOMEM;
>  
>  	mutex_init(&data->mutex);
> +	INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
> +	data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
> +	data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
> +	data->extcon_cable_names[2] = NULL;
> +	data->extcon.name = DRIVER_NAME;
> +	data->extcon.supported_cable = data->extcon_cable_names;
> +	data->extcon.dev.parent = dev;
>  
>  	if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>  		data->num_phys = 2;
> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>  	if (IS_ERR(data->base))
>  		return PTR_ERR(data->base);
>  
> +	data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
> +	if (IS_ERR(data->id_det_gpio)) {
> +		if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		data->id_det_gpio = NULL;
> +	}
> +
> +	data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
> +	if (IS_ERR(data->vbus_det_gpio)) {
> +		if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		data->vbus_det_gpio = NULL;
> +	}
> +
> +	/* We either want both gpio pins or neither (when in host mode) */
> +	if (!data->id_det_gpio != !data->vbus_det_gpio) {
> +		dev_err(dev, "failed to get id or vbus detect pin\n");
> +		return -ENODEV;
> +	}
> +
> +	if (data->id_det_gpio) {
> +		ret = devm_extcon_dev_register(dev, &data->extcon);
> +		if (ret) {
> +			dev_err(dev, "failed to register extcon: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
>  	for (i = 0; i < data->num_phys; i++) {
>  		struct sun4i_usb_phy *phy = data->phys + i;
>  		char name[16];
> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>  		phy_set_drvdata(phy->phy, &data->phys[i]);
>  	}
>  
> +	data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
> +	data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
> +	if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
> +		data->phy0_poll = true;
> +
> +	if (data->id_det_irq >= 0) {
> +		ret = devm_request_irq(dev, data->id_det_irq,
> +				sun4i_usb_phy0_id_vbus_det_irq,
> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
> +				"usb0-id-det", data);
> +		if (ret) {
> +			dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (data->vbus_det_irq >= 0) {
> +		ret = devm_request_irq(dev, data->vbus_det_irq,
> +				sun4i_usb_phy0_id_vbus_det_irq,
> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
> +				"usb0-vbus-det", data);
> +		if (ret) {
> +			dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
>  	dev_set_drvdata(dev, data);
>  	phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>  
>  	return PTR_ERR_OR_ZERO(phy_provider);
>  }
>  
> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
> +
> +	if (data->id_det_irq >= 0)
> +		devm_free_irq(dev, data->id_det_irq, data);
> +	if (data->vbus_det_irq >= 0)
> +		devm_free_irq(dev, data->vbus_det_irq, data);
> +
> +	cancel_delayed_work_sync(&data->detect);
> +
> +	return 0;
> +}
> +
>  static const struct of_device_id sun4i_usb_phy_of_match[] = {
>  	{ .compatible = "allwinner,sun4i-a10-usb-phy" },
>  	{ .compatible = "allwinner,sun5i-a13-usb-phy" },
> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>  
>  static struct platform_driver sun4i_usb_phy_driver = {
>  	.probe	= sun4i_usb_phy_probe,
> +	.remove	= sun4i_usb_phy_remove,
>  	.driver = {
>  		.of_match_table	= sun4i_usb_phy_of_match,
> -		.name  = "sun4i-usb-phy",
> +		.name  = DRIVER_NAME,
>  	}
>  };
>  module_platform_driver(sun4i_usb_phy_driver);
> 

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

* [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-07-15 10:55         ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 57+ messages in thread
From: Kishon Vijay Abraham I @ 2015-07-15 10:55 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
> The usb0 phy is connected to an OTG controller, and as such needs some special
> handling:
> 
> 1) It allows explicit control over the pullups, enable these on phy_init and
> disable them on phy_exit.
> 
> 2) It has bits to signal id and vbus detect to the musb-core, add support for
> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
> these bits to the correct values for operating in host only mode when no
> gpios are specified in the devicetree.
> 
> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
> device cable is plugged in, so when in dual role mode register an extcon.
> 
> While updating the devicetree binding documentation also add documentation
> for the sofar undocumented usage of regulators for vbus for all 3 phys.

merged this to linux-phy tree.

Cheers
Kishon
> 
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
> Changes in v2:
> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>  has been moved to the phy-sun4i-usb driver and their status is exported
>  through extcon for the sunxi-musb glue
> Changes in v3:
> -No changes
> Changes in v4:
> -Do not call regulator_disable in an unbalanced manner when an external vbus
>  is present
> ---
>  .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>  drivers/phy/Kconfig                                |   1 +
>  drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>  3 files changed, 281 insertions(+), 11 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> index 16528b9..557fa99 100644
> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
> @@ -23,6 +23,13 @@ Required properties:
>    * "usb1_reset"
>    * "usb2_reset" for sun4i, sun6i or sun7i
>  
> +Optional properties:
> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
> +
>  Example:
>  	usbphy: phy at 0x01c13400 {
>  		#phy-cells = <1>;
> @@ -32,6 +39,13 @@ Example:
>  		reg-names = "phy_ctrl", "pmu1", "pmu2";
>  		clocks = <&usb_clk 8>;
>  		clock-names = "usb_phy";
> -		resets = <&usb_clk 1>, <&usb_clk 2>;
> -		reset-names = "usb1_reset", "usb2_reset";
> +		resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
> +		reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
> +		usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
> +		usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
> +		usb0_vbus-supply = <&reg_usb0_vbus>;
> +		usb1_vbus-supply = <&reg_usb1_vbus>;
> +		usb2_vbus-supply = <&reg_usb2_vbus>;
>  	};
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index a53bd5b..4614fba 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>  	tristate "Allwinner sunxi SoC USB PHY driver"
>  	depends on ARCH_SUNXI && HAS_IOMEM && OF
>  	depends on RESET_CONTROLLER
> +	select EXTCON
>  	select GENERIC_PHY
>  	help
>  	  Enable this to support the transceiver that is part of Allwinner
> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
> index 91c5be4..b45d707 100644
> --- a/drivers/phy/phy-sun4i-usb.c
> +++ b/drivers/phy/phy-sun4i-usb.c
> @@ -1,7 +1,7 @@
>  /*
>   * Allwinner sun4i USB phy driver
>   *
> - * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede@redhat.com>
>   *
>   * Based on code from
>   * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
> @@ -23,17 +23,23 @@
>  
>  #include <linux/clk.h>
>  #include <linux/err.h>
> +#include <linux/extcon.h>
>  #include <linux/io.h>
> +#include <linux/interrupt.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_gpio.h>
>  #include <linux/phy/phy.h>
>  #include <linux/phy/phy-sun4i-usb.h>
>  #include <linux/platform_device.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/reset.h>
> +#include <linux/workqueue.h>
> +
> +#define DRIVER_NAME "sun4i-usb-phy"
>  
>  #define REG_ISCR			0x00
>  #define REG_PHYCTL			0x04
> @@ -47,6 +53,17 @@
>  #define SUNXI_AHB_INCRX_ALIGN_EN	BIT(8)
>  #define SUNXI_ULPI_BYPASS_EN		BIT(0)
>  
> +/* ISCR, Interface Status and Control bits */
> +#define ISCR_ID_PULLUP_EN		(1 << 17)
> +#define ISCR_DPDM_PULLUP_EN	(1 << 16)
> +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
> +#define ISCR_FORCE_ID_MASK	(3 << 14)
> +#define ISCR_FORCE_ID_LOW		(2 << 14)
> +#define ISCR_FORCE_ID_HIGH	(3 << 14)
> +#define ISCR_FORCE_VBUS_MASK	(3 << 12)
> +#define ISCR_FORCE_VBUS_LOW	(2 << 12)
> +#define ISCR_FORCE_VBUS_HIGH	(3 << 12)
> +
>  /* Common Control Bits for Both PHYs */
>  #define PHY_PLL_BW			0x03
>  #define PHY_RES45_CAL_EN		0x0c
> @@ -63,6 +80,13 @@
>  
>  #define MAX_PHYS			3
>  
> +/*
> + * Note do not raise the debounce time, we must report Vusb high within 100ms
> + * otherwise we get Vbus errors
> + */
> +#define DEBOUNCE_TIME			msecs_to_jiffies(50)
> +#define POLL_TIME			msecs_to_jiffies(250)
> +
>  struct sun4i_usb_phy_data {
>  	void __iomem *base;
>  	struct mutex mutex;
> @@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
>  		struct regulator *vbus;
>  		struct reset_control *reset;
>  		struct clk *clk;
> +		bool regulator_on;
>  		int index;
>  	} phys[MAX_PHYS];
> +	/* phy0 / otg related variables */
> +	struct extcon_dev extcon;
> +	const char *extcon_cable_names[3];
> +	bool phy0_init;
> +	bool phy0_poll;
> +	struct gpio_desc *id_det_gpio;
> +	struct gpio_desc *vbus_det_gpio;
> +	int id_det_irq;
> +	int vbus_det_irq;
> +	int id_det;
> +	int vbus_det;
> +	struct delayed_work detect;
>  };
>  
>  #define to_sun4i_usb_phy_data(phy) \
>  	container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>  
> +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
> +{
> +	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +	u32 iscr;
> +
> +	iscr = readl(data->base + REG_ISCR);
> +	iscr &= ~clr;
> +	iscr |= set;
> +	writel(iscr, data->base + REG_ISCR);
> +}
> +
> +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
> +{
> +	if (val)
> +		val = ISCR_FORCE_ID_HIGH;
> +	else
> +		val = ISCR_FORCE_ID_LOW;
> +
> +	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
> +}
> +
> +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
> +{
> +	if (val)
> +		val = ISCR_FORCE_VBUS_HIGH;
> +	else
> +		val = ISCR_FORCE_VBUS_LOW;
> +
> +	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
> +}
> +
>  static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>  				int len)
>  {
> @@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
>  
>  	sun4i_usb_phy_passby(phy, 1);
>  
> +	if (phy->index == 0) {
> +		data->phy0_init = true;
> +
> +		/* Enable pull-ups */
> +		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
> +		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
> +
> +		if (data->id_det_gpio) {
> +			/* OTG mode, force ISCR and cable state updates */
> +			data->id_det = -1;
> +			data->vbus_det = -1;
> +			queue_delayed_work(system_wq, &data->detect, 0);
> +		} else {
> +			/* Host only mode */
> +			sun4i_usb_phy0_set_id_detect(_phy, 0);
> +			sun4i_usb_phy0_set_vbus_detect(_phy, 1);
> +		}
> +	}
> +
>  	return 0;
>  }
>  
>  static int sun4i_usb_phy_exit(struct phy *_phy)
>  {
>  	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +
> +	if (phy->index == 0) {
> +		/* Disable pull-ups */
> +		sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
> +		sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
> +		data->phy0_init = false;
> +	}
>  
>  	sun4i_usb_phy_passby(phy, 0);
>  	reset_control_assert(phy->reset);
> @@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
>  static int sun4i_usb_phy_power_on(struct phy *_phy)
>  {
>  	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> -	int ret = 0;
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +	int ret;
> +
> +	if (!phy->vbus || phy->regulator_on)
> +		return 0;
> +
> +	/* For phy0 only turn on Vbus if we don't have an ext. Vbus */
> +	if (phy->index == 0 && data->vbus_det)
> +		return 0;
>  
> -	if (phy->vbus)
> -		ret = regulator_enable(phy->vbus);
> +	ret = regulator_enable(phy->vbus);
> +	if (ret)
> +		return ret;
>  
> -	return ret;
> +	phy->regulator_on = true;
> +
> +	/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
> +	if (phy->index == 0 && data->phy0_poll)
> +		mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
> +
> +	return 0;
>  }
>  
>  static int sun4i_usb_phy_power_off(struct phy *_phy)
>  {
>  	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
> +
> +	if (!phy->vbus || !phy->regulator_on)
> +		return 0;
>  
> -	if (phy->vbus)
> -		regulator_disable(phy->vbus);
> +	regulator_disable(phy->vbus);
> +	phy->regulator_on = false;
> +
> +	/*
> +	 * phy0 vbus typically slowly discharges, sometimes this causes the
> +	 * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
> +	 */
> +	if (phy->index == 0 && !data->phy0_poll)
> +		mod_delayed_work(system_wq, &data->detect, POLL_TIME);
>  
>  	return 0;
>  }
> @@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
>  	.owner		= THIS_MODULE,
>  };
>  
> +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
> +{
> +	struct sun4i_usb_phy_data *data =
> +		container_of(work, struct sun4i_usb_phy_data, detect.work);
> +	struct phy *phy0 = data->phys[0].phy;
> +	int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
> +
> +	id_det = gpiod_get_value_cansleep(data->id_det_gpio);
> +	vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
> +
> +	mutex_lock(&phy0->mutex);
> +
> +	if (!data->phy0_init) {
> +		mutex_unlock(&phy0->mutex);
> +		return;
> +	}
> +
> +	if (id_det != data->id_det) {
> +		sun4i_usb_phy0_set_id_detect(phy0, id_det);
> +		data->id_det = id_det;
> +		id_notify = 1;
> +	}
> +
> +	if (vbus_det != data->vbus_det) {
> +		sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
> +		data->vbus_det = vbus_det;
> +		vbus_notify = 1;
> +	}
> +
> +	mutex_unlock(&phy0->mutex);
> +
> +	if (id_notify)
> +		extcon_set_cable_state(&data->extcon,
> +				       extcon_cable_name[EXTCON_USB_HOST],
> +				       !id_det);
> +
> +	if (vbus_notify)
> +		extcon_set_cable_state(&data->extcon,
> +				       extcon_cable_name[EXTCON_USB],
> +				       vbus_det);
> +
> +	if (data->phy0_poll)
> +		queue_delayed_work(system_wq, &data->detect, POLL_TIME);
> +}
> +
> +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
> +{
> +	struct sun4i_usb_phy_data *data = dev_id;
> +
> +	/* vbus or id changed, let the pins settle and then scan them */
> +	mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
> +
> +	return IRQ_HANDLED;
> +}
> +
>  static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>  					struct of_phandle_args *args)
>  {
> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>  	struct phy_provider *phy_provider;
>  	bool dedicated_clocks;
>  	struct resource *res;
> -	int i;
> +	int i, ret;
>  
>  	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>  	if (!data)
>  		return -ENOMEM;
>  
>  	mutex_init(&data->mutex);
> +	INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
> +	data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
> +	data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
> +	data->extcon_cable_names[2] = NULL;
> +	data->extcon.name = DRIVER_NAME;
> +	data->extcon.supported_cable = data->extcon_cable_names;
> +	data->extcon.dev.parent = dev;
>  
>  	if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>  		data->num_phys = 2;
> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>  	if (IS_ERR(data->base))
>  		return PTR_ERR(data->base);
>  
> +	data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
> +	if (IS_ERR(data->id_det_gpio)) {
> +		if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		data->id_det_gpio = NULL;
> +	}
> +
> +	data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
> +	if (IS_ERR(data->vbus_det_gpio)) {
> +		if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		data->vbus_det_gpio = NULL;
> +	}
> +
> +	/* We either want both gpio pins or neither (when in host mode) */
> +	if (!data->id_det_gpio != !data->vbus_det_gpio) {
> +		dev_err(dev, "failed to get id or vbus detect pin\n");
> +		return -ENODEV;
> +	}
> +
> +	if (data->id_det_gpio) {
> +		ret = devm_extcon_dev_register(dev, &data->extcon);
> +		if (ret) {
> +			dev_err(dev, "failed to register extcon: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
>  	for (i = 0; i < data->num_phys; i++) {
>  		struct sun4i_usb_phy *phy = data->phys + i;
>  		char name[16];
> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>  		phy_set_drvdata(phy->phy, &data->phys[i]);
>  	}
>  
> +	data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
> +	data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
> +	if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
> +		data->phy0_poll = true;
> +
> +	if (data->id_det_irq >= 0) {
> +		ret = devm_request_irq(dev, data->id_det_irq,
> +				sun4i_usb_phy0_id_vbus_det_irq,
> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
> +				"usb0-id-det", data);
> +		if (ret) {
> +			dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (data->vbus_det_irq >= 0) {
> +		ret = devm_request_irq(dev, data->vbus_det_irq,
> +				sun4i_usb_phy0_id_vbus_det_irq,
> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
> +				"usb0-vbus-det", data);
> +		if (ret) {
> +			dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
>  	dev_set_drvdata(dev, data);
>  	phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>  
>  	return PTR_ERR_OR_ZERO(phy_provider);
>  }
>  
> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
> +
> +	if (data->id_det_irq >= 0)
> +		devm_free_irq(dev, data->id_det_irq, data);
> +	if (data->vbus_det_irq >= 0)
> +		devm_free_irq(dev, data->vbus_det_irq, data);
> +
> +	cancel_delayed_work_sync(&data->detect);
> +
> +	return 0;
> +}
> +
>  static const struct of_device_id sun4i_usb_phy_of_match[] = {
>  	{ .compatible = "allwinner,sun4i-a10-usb-phy" },
>  	{ .compatible = "allwinner,sun5i-a13-usb-phy" },
> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>  
>  static struct platform_driver sun4i_usb_phy_driver = {
>  	.probe	= sun4i_usb_phy_probe,
> +	.remove	= sun4i_usb_phy_remove,
>  	.driver = {
>  		.of_match_table	= sun4i_usb_phy_of_match,
> -		.name  = "sun4i-usb-phy",
> +		.name  = DRIVER_NAME,
>  	}
>  };
>  module_platform_driver(sun4i_usb_phy_driver);
> 

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

* Re: [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
  2015-07-15 10:55         ` Kishon Vijay Abraham I
@ 2015-07-15 10:58             ` Kishon Vijay Abraham I
  -1 siblings, 0 replies; 57+ messages in thread
From: Kishon Vijay Abraham I @ 2015-07-15 10:58 UTC (permalink / raw)
  To: Hans de Goede, Felipe Balbi
  Cc: Maxime Ripard, Chen-Yu Tsai, Roman Byshko,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw



On Wednesday 15 July 2015 04:25 PM, Kishon Vijay Abraham I wrote:
> Hi,
> 
> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>> The usb0 phy is connected to an OTG controller, and as such needs some special
>> handling:
>>
>> 1) It allows explicit control over the pullups, enable these on phy_init and
>> disable them on phy_exit.
>>
>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>> these bits to the correct values for operating in host only mode when no
>> gpios are specified in the devicetree.
>>
>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>> device cable is plugged in, so when in dual role mode register an extcon.
>>
>> While updating the devicetree binding documentation also add documentation
>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
> 
> merged this to linux-phy tree.

Please ignore. I merged the v5 of the patch series.

Cheers
Kishon
> 
> Cheers
> Kishon
>>
>> Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> ---
>> Changes in v2:
>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>  has been moved to the phy-sun4i-usb driver and their status is exported
>>  through extcon for the sunxi-musb glue
>> Changes in v3:
>> -No changes
>> Changes in v4:
>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>  is present
>> ---
>>  .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>  drivers/phy/Kconfig                                |   1 +
>>  drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>  3 files changed, 281 insertions(+), 11 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> index 16528b9..557fa99 100644
>> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> @@ -23,6 +23,13 @@ Required properties:
>>    * "usb1_reset"
>>    * "usb2_reset" for sun4i, sun6i or sun7i
>>  
>> +Optional properties:
>> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
>> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
>> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
>> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
>> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
>> +
>>  Example:
>>  	usbphy: phy@0x01c13400 {
>>  		#phy-cells = <1>;
>> @@ -32,6 +39,13 @@ Example:
>>  		reg-names = "phy_ctrl", "pmu1", "pmu2";
>>  		clocks = <&usb_clk 8>;
>>  		clock-names = "usb_phy";
>> -		resets = <&usb_clk 1>, <&usb_clk 2>;
>> -		reset-names = "usb1_reset", "usb2_reset";
>> +		resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
>> +		reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
>> +		pinctrl-names = "default";
>> +		pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
>> +		usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
>> +		usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
>> +		usb0_vbus-supply = <&reg_usb0_vbus>;
>> +		usb1_vbus-supply = <&reg_usb1_vbus>;
>> +		usb2_vbus-supply = <&reg_usb2_vbus>;
>>  	};
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index a53bd5b..4614fba 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>>  	tristate "Allwinner sunxi SoC USB PHY driver"
>>  	depends on ARCH_SUNXI && HAS_IOMEM && OF
>>  	depends on RESET_CONTROLLER
>> +	select EXTCON
>>  	select GENERIC_PHY
>>  	help
>>  	  Enable this to support the transceiver that is part of Allwinner
>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>> index 91c5be4..b45d707 100644
>> --- a/drivers/phy/phy-sun4i-usb.c
>> +++ b/drivers/phy/phy-sun4i-usb.c
>> @@ -1,7 +1,7 @@
>>  /*
>>   * Allwinner sun4i USB phy driver
>>   *
>> - * Copyright (C) 2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>   *
>>   * Based on code from
>>   * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>> @@ -23,17 +23,23 @@
>>  
>>  #include <linux/clk.h>
>>  #include <linux/err.h>
>> +#include <linux/extcon.h>
>>  #include <linux/io.h>
>> +#include <linux/interrupt.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_gpio.h>
>>  #include <linux/phy/phy.h>
>>  #include <linux/phy/phy-sun4i-usb.h>
>>  #include <linux/platform_device.h>
>>  #include <linux/regulator/consumer.h>
>>  #include <linux/reset.h>
>> +#include <linux/workqueue.h>
>> +
>> +#define DRIVER_NAME "sun4i-usb-phy"
>>  
>>  #define REG_ISCR			0x00
>>  #define REG_PHYCTL			0x04
>> @@ -47,6 +53,17 @@
>>  #define SUNXI_AHB_INCRX_ALIGN_EN	BIT(8)
>>  #define SUNXI_ULPI_BYPASS_EN		BIT(0)
>>  
>> +/* ISCR, Interface Status and Control bits */
>> +#define ISCR_ID_PULLUP_EN		(1 << 17)
>> +#define ISCR_DPDM_PULLUP_EN	(1 << 16)
>> +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
>> +#define ISCR_FORCE_ID_MASK	(3 << 14)
>> +#define ISCR_FORCE_ID_LOW		(2 << 14)
>> +#define ISCR_FORCE_ID_HIGH	(3 << 14)
>> +#define ISCR_FORCE_VBUS_MASK	(3 << 12)
>> +#define ISCR_FORCE_VBUS_LOW	(2 << 12)
>> +#define ISCR_FORCE_VBUS_HIGH	(3 << 12)
>> +
>>  /* Common Control Bits for Both PHYs */
>>  #define PHY_PLL_BW			0x03
>>  #define PHY_RES45_CAL_EN		0x0c
>> @@ -63,6 +80,13 @@
>>  
>>  #define MAX_PHYS			3
>>  
>> +/*
>> + * Note do not raise the debounce time, we must report Vusb high within 100ms
>> + * otherwise we get Vbus errors
>> + */
>> +#define DEBOUNCE_TIME			msecs_to_jiffies(50)
>> +#define POLL_TIME			msecs_to_jiffies(250)
>> +
>>  struct sun4i_usb_phy_data {
>>  	void __iomem *base;
>>  	struct mutex mutex;
>> @@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
>>  		struct regulator *vbus;
>>  		struct reset_control *reset;
>>  		struct clk *clk;
>> +		bool regulator_on;
>>  		int index;
>>  	} phys[MAX_PHYS];
>> +	/* phy0 / otg related variables */
>> +	struct extcon_dev extcon;
>> +	const char *extcon_cable_names[3];
>> +	bool phy0_init;
>> +	bool phy0_poll;
>> +	struct gpio_desc *id_det_gpio;
>> +	struct gpio_desc *vbus_det_gpio;
>> +	int id_det_irq;
>> +	int vbus_det_irq;
>> +	int id_det;
>> +	int vbus_det;
>> +	struct delayed_work detect;
>>  };
>>  
>>  #define to_sun4i_usb_phy_data(phy) \
>>  	container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>>  
>> +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
>> +{
>> +	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +	u32 iscr;
>> +
>> +	iscr = readl(data->base + REG_ISCR);
>> +	iscr &= ~clr;
>> +	iscr |= set;
>> +	writel(iscr, data->base + REG_ISCR);
>> +}
>> +
>> +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
>> +{
>> +	if (val)
>> +		val = ISCR_FORCE_ID_HIGH;
>> +	else
>> +		val = ISCR_FORCE_ID_LOW;
>> +
>> +	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
>> +}
>> +
>> +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
>> +{
>> +	if (val)
>> +		val = ISCR_FORCE_VBUS_HIGH;
>> +	else
>> +		val = ISCR_FORCE_VBUS_LOW;
>> +
>> +	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
>> +}
>> +
>>  static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>>  				int len)
>>  {
>> @@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
>>  
>>  	sun4i_usb_phy_passby(phy, 1);
>>  
>> +	if (phy->index == 0) {
>> +		data->phy0_init = true;
>> +
>> +		/* Enable pull-ups */
>> +		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
>> +		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
>> +
>> +		if (data->id_det_gpio) {
>> +			/* OTG mode, force ISCR and cable state updates */
>> +			data->id_det = -1;
>> +			data->vbus_det = -1;
>> +			queue_delayed_work(system_wq, &data->detect, 0);
>> +		} else {
>> +			/* Host only mode */
>> +			sun4i_usb_phy0_set_id_detect(_phy, 0);
>> +			sun4i_usb_phy0_set_vbus_detect(_phy, 1);
>> +		}
>> +	}
>> +
>>  	return 0;
>>  }
>>  
>>  static int sun4i_usb_phy_exit(struct phy *_phy)
>>  {
>>  	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> +	if (phy->index == 0) {
>> +		/* Disable pull-ups */
>> +		sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
>> +		sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
>> +		data->phy0_init = false;
>> +	}
>>  
>>  	sun4i_usb_phy_passby(phy, 0);
>>  	reset_control_assert(phy->reset);
>> @@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
>>  static int sun4i_usb_phy_power_on(struct phy *_phy)
>>  {
>>  	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> -	int ret = 0;
>> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +	int ret;
>> +
>> +	if (!phy->vbus || phy->regulator_on)
>> +		return 0;
>> +
>> +	/* For phy0 only turn on Vbus if we don't have an ext. Vbus */
>> +	if (phy->index == 0 && data->vbus_det)
>> +		return 0;
>>  
>> -	if (phy->vbus)
>> -		ret = regulator_enable(phy->vbus);
>> +	ret = regulator_enable(phy->vbus);
>> +	if (ret)
>> +		return ret;
>>  
>> -	return ret;
>> +	phy->regulator_on = true;
>> +
>> +	/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
>> +	if (phy->index == 0 && data->phy0_poll)
>> +		mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>> +
>> +	return 0;
>>  }
>>  
>>  static int sun4i_usb_phy_power_off(struct phy *_phy)
>>  {
>>  	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> +	if (!phy->vbus || !phy->regulator_on)
>> +		return 0;
>>  
>> -	if (phy->vbus)
>> -		regulator_disable(phy->vbus);
>> +	regulator_disable(phy->vbus);
>> +	phy->regulator_on = false;
>> +
>> +	/*
>> +	 * phy0 vbus typically slowly discharges, sometimes this causes the
>> +	 * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
>> +	 */
>> +	if (phy->index == 0 && !data->phy0_poll)
>> +		mod_delayed_work(system_wq, &data->detect, POLL_TIME);
>>  
>>  	return 0;
>>  }
>> @@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
>>  	.owner		= THIS_MODULE,
>>  };
>>  
>> +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
>> +{
>> +	struct sun4i_usb_phy_data *data =
>> +		container_of(work, struct sun4i_usb_phy_data, detect.work);
>> +	struct phy *phy0 = data->phys[0].phy;
>> +	int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
>> +
>> +	id_det = gpiod_get_value_cansleep(data->id_det_gpio);
>> +	vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
>> +
>> +	mutex_lock(&phy0->mutex);
>> +
>> +	if (!data->phy0_init) {
>> +		mutex_unlock(&phy0->mutex);
>> +		return;
>> +	}
>> +
>> +	if (id_det != data->id_det) {
>> +		sun4i_usb_phy0_set_id_detect(phy0, id_det);
>> +		data->id_det = id_det;
>> +		id_notify = 1;
>> +	}
>> +
>> +	if (vbus_det != data->vbus_det) {
>> +		sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
>> +		data->vbus_det = vbus_det;
>> +		vbus_notify = 1;
>> +	}
>> +
>> +	mutex_unlock(&phy0->mutex);
>> +
>> +	if (id_notify)
>> +		extcon_set_cable_state(&data->extcon,
>> +				       extcon_cable_name[EXTCON_USB_HOST],
>> +				       !id_det);
>> +
>> +	if (vbus_notify)
>> +		extcon_set_cable_state(&data->extcon,
>> +				       extcon_cable_name[EXTCON_USB],
>> +				       vbus_det);
>> +
>> +	if (data->phy0_poll)
>> +		queue_delayed_work(system_wq, &data->detect, POLL_TIME);
>> +}
>> +
>> +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
>> +{
>> +	struct sun4i_usb_phy_data *data = dev_id;
>> +
>> +	/* vbus or id changed, let the pins settle and then scan them */
>> +	mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>>  static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>  					struct of_phandle_args *args)
>>  {
>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>  	struct phy_provider *phy_provider;
>>  	bool dedicated_clocks;
>>  	struct resource *res;
>> -	int i;
>> +	int i, ret;
>>  
>>  	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>  	if (!data)
>>  		return -ENOMEM;
>>  
>>  	mutex_init(&data->mutex);
>> +	INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>> +	data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>> +	data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>> +	data->extcon_cable_names[2] = NULL;
>> +	data->extcon.name = DRIVER_NAME;
>> +	data->extcon.supported_cable = data->extcon_cable_names;
>> +	data->extcon.dev.parent = dev;
>>  
>>  	if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>  		data->num_phys = 2;
>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>  	if (IS_ERR(data->base))
>>  		return PTR_ERR(data->base);
>>  
>> +	data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>> +	if (IS_ERR(data->id_det_gpio)) {
>> +		if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>> +			return -EPROBE_DEFER;
>> +		data->id_det_gpio = NULL;
>> +	}
>> +
>> +	data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>> +	if (IS_ERR(data->vbus_det_gpio)) {
>> +		if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>> +			return -EPROBE_DEFER;
>> +		data->vbus_det_gpio = NULL;
>> +	}
>> +
>> +	/* We either want both gpio pins or neither (when in host mode) */
>> +	if (!data->id_det_gpio != !data->vbus_det_gpio) {
>> +		dev_err(dev, "failed to get id or vbus detect pin\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	if (data->id_det_gpio) {
>> +		ret = devm_extcon_dev_register(dev, &data->extcon);
>> +		if (ret) {
>> +			dev_err(dev, "failed to register extcon: %d\n", ret);
>> +			return ret;
>> +		}
>> +	}
>> +
>>  	for (i = 0; i < data->num_phys; i++) {
>>  		struct sun4i_usb_phy *phy = data->phys + i;
>>  		char name[16];
>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>  		phy_set_drvdata(phy->phy, &data->phys[i]);
>>  	}
>>  
>> +	data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>> +	data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>> +	if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>> +		data->phy0_poll = true;
>> +
>> +	if (data->id_det_irq >= 0) {
>> +		ret = devm_request_irq(dev, data->id_det_irq,
>> +				sun4i_usb_phy0_id_vbus_det_irq,
>> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +				"usb0-id-det", data);
>> +		if (ret) {
>> +			dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>> +			return ret;
>> +		}
>> +	}
>> +
>> +	if (data->vbus_det_irq >= 0) {
>> +		ret = devm_request_irq(dev, data->vbus_det_irq,
>> +				sun4i_usb_phy0_id_vbus_det_irq,
>> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +				"usb0-vbus-det", data);
>> +		if (ret) {
>> +			dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>> +			return ret;
>> +		}
>> +	}
>> +
>>  	dev_set_drvdata(dev, data);
>>  	phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>  
>>  	return PTR_ERR_OR_ZERO(phy_provider);
>>  }
>>  
>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>> +
>> +	if (data->id_det_irq >= 0)
>> +		devm_free_irq(dev, data->id_det_irq, data);
>> +	if (data->vbus_det_irq >= 0)
>> +		devm_free_irq(dev, data->vbus_det_irq, data);
>> +
>> +	cancel_delayed_work_sync(&data->detect);
>> +
>> +	return 0;
>> +}
>> +
>>  static const struct of_device_id sun4i_usb_phy_of_match[] = {
>>  	{ .compatible = "allwinner,sun4i-a10-usb-phy" },
>>  	{ .compatible = "allwinner,sun5i-a13-usb-phy" },
>> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>>  
>>  static struct platform_driver sun4i_usb_phy_driver = {
>>  	.probe	= sun4i_usb_phy_probe,
>> +	.remove	= sun4i_usb_phy_remove,
>>  	.driver = {
>>  		.of_match_table	= sun4i_usb_phy_of_match,
>> -		.name  = "sun4i-usb-phy",
>> +		.name  = DRIVER_NAME,
>>  	}
>>  };
>>  module_platform_driver(sun4i_usb_phy_driver);
>>

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

* [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG
@ 2015-07-15 10:58             ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 57+ messages in thread
From: Kishon Vijay Abraham I @ 2015-07-15 10:58 UTC (permalink / raw)
  To: linux-arm-kernel



On Wednesday 15 July 2015 04:25 PM, Kishon Vijay Abraham I wrote:
> Hi,
> 
> On Sunday 31 May 2015 09:40 PM, Hans de Goede wrote:
>> The usb0 phy is connected to an OTG controller, and as such needs some special
>> handling:
>>
>> 1) It allows explicit control over the pullups, enable these on phy_init and
>> disable them on phy_exit.
>>
>> 2) It has bits to signal id and vbus detect to the musb-core, add support for
>> for monitoring id and vbus detect gpio-s for use in dual role mode, and set
>> these bits to the correct values for operating in host only mode when no
>> gpios are specified in the devicetree.
>>
>> 3) When in dual role mode the musb sunxi glue needs to know if the a host or
>> device cable is plugged in, so when in dual role mode register an extcon.
>>
>> While updating the devicetree binding documentation also add documentation
>> for the sofar undocumented usage of regulators for vbus for all 3 phys.
> 
> merged this to linux-phy tree.

Please ignore. I merged the v5 of the patch series.

Cheers
Kishon
> 
> Cheers
> Kishon
>>
>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> ---
>> Changes in v2:
>> -Removed the sunxi specific phy functions, instead the id / vbus gpio polling
>>  has been moved to the phy-sun4i-usb driver and their status is exported
>>  through extcon for the sunxi-musb glue
>> Changes in v3:
>> -No changes
>> Changes in v4:
>> -Do not call regulator_disable in an unbalanced manner when an external vbus
>>  is present
>> ---
>>  .../devicetree/bindings/phy/sun4i-usb-phy.txt      |  18 +-
>>  drivers/phy/Kconfig                                |   1 +
>>  drivers/phy/phy-sun4i-usb.c                        | 273 ++++++++++++++++++++-
>>  3 files changed, 281 insertions(+), 11 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> index 16528b9..557fa99 100644
>> --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
>> @@ -23,6 +23,13 @@ Required properties:
>>    * "usb1_reset"
>>    * "usb2_reset" for sun4i, sun6i or sun7i
>>  
>> +Optional properties:
>> +- usb0_id_det-gpios : gpio phandle for reading the otg id pin value
>> +- usb0_vbus_det-gpios : gpio phandle for detecting the presence of usb0 vbus
>> +- usb0_vbus-supply : regulator phandle for controller usb0 vbus
>> +- usb1_vbus-supply : regulator phandle for controller usb1 vbus
>> +- usb2_vbus-supply : regulator phandle for controller usb2 vbus
>> +
>>  Example:
>>  	usbphy: phy at 0x01c13400 {
>>  		#phy-cells = <1>;
>> @@ -32,6 +39,13 @@ Example:
>>  		reg-names = "phy_ctrl", "pmu1", "pmu2";
>>  		clocks = <&usb_clk 8>;
>>  		clock-names = "usb_phy";
>> -		resets = <&usb_clk 1>, <&usb_clk 2>;
>> -		reset-names = "usb1_reset", "usb2_reset";
>> +		resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
>> +		reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
>> +		pinctrl-names = "default";
>> +		pinctrl-0 = <&usb0_id_detect_pin>, <&usb0_vbus_detect_pin>;
>> +		usb0_id_det-gpios = <&pio 7 19 GPIO_ACTIVE_HIGH>; /* PH19 */
>> +		usb0_vbus_det-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
>> +		usb0_vbus-supply = <&reg_usb0_vbus>;
>> +		usb1_vbus-supply = <&reg_usb1_vbus>;
>> +		usb2_vbus-supply = <&reg_usb2_vbus>;
>>  	};
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index a53bd5b..4614fba 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -173,6 +173,7 @@ config PHY_SUN4I_USB
>>  	tristate "Allwinner sunxi SoC USB PHY driver"
>>  	depends on ARCH_SUNXI && HAS_IOMEM && OF
>>  	depends on RESET_CONTROLLER
>> +	select EXTCON
>>  	select GENERIC_PHY
>>  	help
>>  	  Enable this to support the transceiver that is part of Allwinner
>> diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
>> index 91c5be4..b45d707 100644
>> --- a/drivers/phy/phy-sun4i-usb.c
>> +++ b/drivers/phy/phy-sun4i-usb.c
>> @@ -1,7 +1,7 @@
>>  /*
>>   * Allwinner sun4i USB phy driver
>>   *
>> - * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
>> + * Copyright (C) 2014-2015 Hans de Goede <hdegoede@redhat.com>
>>   *
>>   * Based on code from
>>   * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
>> @@ -23,17 +23,23 @@
>>  
>>  #include <linux/clk.h>
>>  #include <linux/err.h>
>> +#include <linux/extcon.h>
>>  #include <linux/io.h>
>> +#include <linux/interrupt.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_gpio.h>
>>  #include <linux/phy/phy.h>
>>  #include <linux/phy/phy-sun4i-usb.h>
>>  #include <linux/platform_device.h>
>>  #include <linux/regulator/consumer.h>
>>  #include <linux/reset.h>
>> +#include <linux/workqueue.h>
>> +
>> +#define DRIVER_NAME "sun4i-usb-phy"
>>  
>>  #define REG_ISCR			0x00
>>  #define REG_PHYCTL			0x04
>> @@ -47,6 +53,17 @@
>>  #define SUNXI_AHB_INCRX_ALIGN_EN	BIT(8)
>>  #define SUNXI_ULPI_BYPASS_EN		BIT(0)
>>  
>> +/* ISCR, Interface Status and Control bits */
>> +#define ISCR_ID_PULLUP_EN		(1 << 17)
>> +#define ISCR_DPDM_PULLUP_EN	(1 << 16)
>> +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
>> +#define ISCR_FORCE_ID_MASK	(3 << 14)
>> +#define ISCR_FORCE_ID_LOW		(2 << 14)
>> +#define ISCR_FORCE_ID_HIGH	(3 << 14)
>> +#define ISCR_FORCE_VBUS_MASK	(3 << 12)
>> +#define ISCR_FORCE_VBUS_LOW	(2 << 12)
>> +#define ISCR_FORCE_VBUS_HIGH	(3 << 12)
>> +
>>  /* Common Control Bits for Both PHYs */
>>  #define PHY_PLL_BW			0x03
>>  #define PHY_RES45_CAL_EN		0x0c
>> @@ -63,6 +80,13 @@
>>  
>>  #define MAX_PHYS			3
>>  
>> +/*
>> + * Note do not raise the debounce time, we must report Vusb high within 100ms
>> + * otherwise we get Vbus errors
>> + */
>> +#define DEBOUNCE_TIME			msecs_to_jiffies(50)
>> +#define POLL_TIME			msecs_to_jiffies(250)
>> +
>>  struct sun4i_usb_phy_data {
>>  	void __iomem *base;
>>  	struct mutex mutex;
>> @@ -74,13 +98,58 @@ struct sun4i_usb_phy_data {
>>  		struct regulator *vbus;
>>  		struct reset_control *reset;
>>  		struct clk *clk;
>> +		bool regulator_on;
>>  		int index;
>>  	} phys[MAX_PHYS];
>> +	/* phy0 / otg related variables */
>> +	struct extcon_dev extcon;
>> +	const char *extcon_cable_names[3];
>> +	bool phy0_init;
>> +	bool phy0_poll;
>> +	struct gpio_desc *id_det_gpio;
>> +	struct gpio_desc *vbus_det_gpio;
>> +	int id_det_irq;
>> +	int vbus_det_irq;
>> +	int id_det;
>> +	int vbus_det;
>> +	struct delayed_work detect;
>>  };
>>  
>>  #define to_sun4i_usb_phy_data(phy) \
>>  	container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
>>  
>> +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
>> +{
>> +	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +	u32 iscr;
>> +
>> +	iscr = readl(data->base + REG_ISCR);
>> +	iscr &= ~clr;
>> +	iscr |= set;
>> +	writel(iscr, data->base + REG_ISCR);
>> +}
>> +
>> +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
>> +{
>> +	if (val)
>> +		val = ISCR_FORCE_ID_HIGH;
>> +	else
>> +		val = ISCR_FORCE_ID_LOW;
>> +
>> +	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
>> +}
>> +
>> +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
>> +{
>> +	if (val)
>> +		val = ISCR_FORCE_VBUS_HIGH;
>> +	else
>> +		val = ISCR_FORCE_VBUS_LOW;
>> +
>> +	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
>> +}
>> +
>>  static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
>>  				int len)
>>  {
>> @@ -171,12 +240,39 @@ static int sun4i_usb_phy_init(struct phy *_phy)
>>  
>>  	sun4i_usb_phy_passby(phy, 1);
>>  
>> +	if (phy->index == 0) {
>> +		data->phy0_init = true;
>> +
>> +		/* Enable pull-ups */
>> +		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
>> +		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
>> +
>> +		if (data->id_det_gpio) {
>> +			/* OTG mode, force ISCR and cable state updates */
>> +			data->id_det = -1;
>> +			data->vbus_det = -1;
>> +			queue_delayed_work(system_wq, &data->detect, 0);
>> +		} else {
>> +			/* Host only mode */
>> +			sun4i_usb_phy0_set_id_detect(_phy, 0);
>> +			sun4i_usb_phy0_set_vbus_detect(_phy, 1);
>> +		}
>> +	}
>> +
>>  	return 0;
>>  }
>>  
>>  static int sun4i_usb_phy_exit(struct phy *_phy)
>>  {
>>  	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> +	if (phy->index == 0) {
>> +		/* Disable pull-ups */
>> +		sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
>> +		sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
>> +		data->phy0_init = false;
>> +	}
>>  
>>  	sun4i_usb_phy_passby(phy, 0);
>>  	reset_control_assert(phy->reset);
>> @@ -188,20 +284,46 @@ static int sun4i_usb_phy_exit(struct phy *_phy)
>>  static int sun4i_usb_phy_power_on(struct phy *_phy)
>>  {
>>  	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> -	int ret = 0;
>> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +	int ret;
>> +
>> +	if (!phy->vbus || phy->regulator_on)
>> +		return 0;
>> +
>> +	/* For phy0 only turn on Vbus if we don't have an ext. Vbus */
>> +	if (phy->index == 0 && data->vbus_det)
>> +		return 0;
>>  
>> -	if (phy->vbus)
>> -		ret = regulator_enable(phy->vbus);
>> +	ret = regulator_enable(phy->vbus);
>> +	if (ret)
>> +		return ret;
>>  
>> -	return ret;
>> +	phy->regulator_on = true;
>> +
>> +	/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
>> +	if (phy->index == 0 && data->phy0_poll)
>> +		mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>> +
>> +	return 0;
>>  }
>>  
>>  static int sun4i_usb_phy_power_off(struct phy *_phy)
>>  {
>>  	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
>> +	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
>> +
>> +	if (!phy->vbus || !phy->regulator_on)
>> +		return 0;
>>  
>> -	if (phy->vbus)
>> -		regulator_disable(phy->vbus);
>> +	regulator_disable(phy->vbus);
>> +	phy->regulator_on = false;
>> +
>> +	/*
>> +	 * phy0 vbus typically slowly discharges, sometimes this causes the
>> +	 * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
>> +	 */
>> +	if (phy->index == 0 && !data->phy0_poll)
>> +		mod_delayed_work(system_wq, &data->detect, POLL_TIME);
>>  
>>  	return 0;
>>  }
>> @@ -221,6 +343,61 @@ static struct phy_ops sun4i_usb_phy_ops = {
>>  	.owner		= THIS_MODULE,
>>  };
>>  
>> +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
>> +{
>> +	struct sun4i_usb_phy_data *data =
>> +		container_of(work, struct sun4i_usb_phy_data, detect.work);
>> +	struct phy *phy0 = data->phys[0].phy;
>> +	int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
>> +
>> +	id_det = gpiod_get_value_cansleep(data->id_det_gpio);
>> +	vbus_det = gpiod_get_value_cansleep(data->vbus_det_gpio);
>> +
>> +	mutex_lock(&phy0->mutex);
>> +
>> +	if (!data->phy0_init) {
>> +		mutex_unlock(&phy0->mutex);
>> +		return;
>> +	}
>> +
>> +	if (id_det != data->id_det) {
>> +		sun4i_usb_phy0_set_id_detect(phy0, id_det);
>> +		data->id_det = id_det;
>> +		id_notify = 1;
>> +	}
>> +
>> +	if (vbus_det != data->vbus_det) {
>> +		sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
>> +		data->vbus_det = vbus_det;
>> +		vbus_notify = 1;
>> +	}
>> +
>> +	mutex_unlock(&phy0->mutex);
>> +
>> +	if (id_notify)
>> +		extcon_set_cable_state(&data->extcon,
>> +				       extcon_cable_name[EXTCON_USB_HOST],
>> +				       !id_det);
>> +
>> +	if (vbus_notify)
>> +		extcon_set_cable_state(&data->extcon,
>> +				       extcon_cable_name[EXTCON_USB],
>> +				       vbus_det);
>> +
>> +	if (data->phy0_poll)
>> +		queue_delayed_work(system_wq, &data->detect, POLL_TIME);
>> +}
>> +
>> +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
>> +{
>> +	struct sun4i_usb_phy_data *data = dev_id;
>> +
>> +	/* vbus or id changed, let the pins settle and then scan them */
>> +	mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>>  static struct phy *sun4i_usb_phy_xlate(struct device *dev,
>>  					struct of_phandle_args *args)
>>  {
>> @@ -240,13 +417,20 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>  	struct phy_provider *phy_provider;
>>  	bool dedicated_clocks;
>>  	struct resource *res;
>> -	int i;
>> +	int i, ret;
>>  
>>  	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
>>  	if (!data)
>>  		return -ENOMEM;
>>  
>>  	mutex_init(&data->mutex);
>> +	INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
>> +	data->extcon_cable_names[0] = extcon_cable_name[EXTCON_USB_HOST];
>> +	data->extcon_cable_names[1] = extcon_cable_name[EXTCON_USB];
>> +	data->extcon_cable_names[2] = NULL;
>> +	data->extcon.name = DRIVER_NAME;
>> +	data->extcon.supported_cable = data->extcon_cable_names;
>> +	data->extcon.dev.parent = dev;
>>  
>>  	if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
>>  		data->num_phys = 2;
>> @@ -269,6 +453,34 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>  	if (IS_ERR(data->base))
>>  		return PTR_ERR(data->base);
>>  
>> +	data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN);
>> +	if (IS_ERR(data->id_det_gpio)) {
>> +		if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER)
>> +			return -EPROBE_DEFER;
>> +		data->id_det_gpio = NULL;
>> +	}
>> +
>> +	data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN);
>> +	if (IS_ERR(data->vbus_det_gpio)) {
>> +		if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER)
>> +			return -EPROBE_DEFER;
>> +		data->vbus_det_gpio = NULL;
>> +	}
>> +
>> +	/* We either want both gpio pins or neither (when in host mode) */
>> +	if (!data->id_det_gpio != !data->vbus_det_gpio) {
>> +		dev_err(dev, "failed to get id or vbus detect pin\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	if (data->id_det_gpio) {
>> +		ret = devm_extcon_dev_register(dev, &data->extcon);
>> +		if (ret) {
>> +			dev_err(dev, "failed to register extcon: %d\n", ret);
>> +			return ret;
>> +		}
>> +	}
>> +
>>  	for (i = 0; i < data->num_phys; i++) {
>>  		struct sun4i_usb_phy *phy = data->phys + i;
>>  		char name[16];
>> @@ -318,12 +530,54 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
>>  		phy_set_drvdata(phy->phy, &data->phys[i]);
>>  	}
>>  
>> +	data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
>> +	data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
>> +	if (data->id_det_irq  < 0 || data->vbus_det_irq < 0)
>> +		data->phy0_poll = true;
>> +
>> +	if (data->id_det_irq >= 0) {
>> +		ret = devm_request_irq(dev, data->id_det_irq,
>> +				sun4i_usb_phy0_id_vbus_det_irq,
>> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +				"usb0-id-det", data);
>> +		if (ret) {
>> +			dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
>> +			return ret;
>> +		}
>> +	}
>> +
>> +	if (data->vbus_det_irq >= 0) {
>> +		ret = devm_request_irq(dev, data->vbus_det_irq,
>> +				sun4i_usb_phy0_id_vbus_det_irq,
>> +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
>> +				"usb0-vbus-det", data);
>> +		if (ret) {
>> +			dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
>> +			return ret;
>> +		}
>> +	}
>> +
>>  	dev_set_drvdata(dev, data);
>>  	phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
>>  
>>  	return PTR_ERR_OR_ZERO(phy_provider);
>>  }
>>  
>> +static int sun4i_usb_phy_remove(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
>> +
>> +	if (data->id_det_irq >= 0)
>> +		devm_free_irq(dev, data->id_det_irq, data);
>> +	if (data->vbus_det_irq >= 0)
>> +		devm_free_irq(dev, data->vbus_det_irq, data);
>> +
>> +	cancel_delayed_work_sync(&data->detect);
>> +
>> +	return 0;
>> +}
>> +
>>  static const struct of_device_id sun4i_usb_phy_of_match[] = {
>>  	{ .compatible = "allwinner,sun4i-a10-usb-phy" },
>>  	{ .compatible = "allwinner,sun5i-a13-usb-phy" },
>> @@ -335,9 +589,10 @@ MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
>>  
>>  static struct platform_driver sun4i_usb_phy_driver = {
>>  	.probe	= sun4i_usb_phy_probe,
>> +	.remove	= sun4i_usb_phy_remove,
>>  	.driver = {
>>  		.of_match_table	= sun4i_usb_phy_of_match,
>> -		.name  = "sun4i-usb-phy",
>> +		.name  = DRIVER_NAME,
>>  	}
>>  };
>>  module_platform_driver(sun4i_usb_phy_driver);
>>

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

end of thread, other threads:[~2015-07-15 10:58 UTC | newest]

Thread overview: 57+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-05-31 16:10 [PATCH v4 0/2] Remaining sunxi musb patches Hans de Goede
2015-05-31 16:10 ` Hans de Goede
     [not found] ` <1433088626-8858-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2015-05-31 16:10   ` [PATCH v4 1/2] phy-sun4i-usb: Add full support for usb0 phy / OTG Hans de Goede
2015-05-31 16:10     ` Hans de Goede
     [not found]     ` <1433088626-8858-2-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2015-06-01  9:22       ` Maxime Ripard
2015-06-01  9:22         ` Maxime Ripard
2015-06-01  9:28         ` Hans de Goede
2015-06-01  9:28           ` Hans de Goede
     [not found]           ` <556C25B7.80708-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2015-06-02 15:22             ` Maxime Ripard
2015-06-02 15:22               ` Maxime Ripard
2015-06-11  5:48       ` Kishon Vijay Abraham I
2015-06-11  5:48         ` Kishon Vijay Abraham I
     [not found]         ` <55792122.20607-l0cyMroinI0@public.gmane.org>
2015-06-11  7:56           ` [linux-sunxi] " Hans de Goede
2015-06-11  7:56             ` Hans de Goede
     [not found]             ` <55793F41.7000304-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2015-06-11  8:28               ` Hans de Goede
2015-06-11  8:28                 ` Hans de Goede
2015-06-11  8:07           ` Chanwoo Choi
2015-06-11  8:07             ` Chanwoo Choi
     [not found]             ` <557941A6.5000306-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2015-06-11  8:21               ` [linux-sunxi] " Hans de Goede
2015-06-11  8:21                 ` Hans de Goede
     [not found]                 ` <557944EE.1020303-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2015-06-11  8:29                   ` Chanwoo Choi
2015-06-11  8:29                     ` [linux-sunxi] " Chanwoo Choi
     [not found]                     ` <557946EC.3060607-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2015-06-11  8:59                       ` Hans de Goede
2015-06-11  8:59                         ` Hans de Goede
     [not found]                         ` <55794DEA.1050604-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2015-06-11  9:33                           ` Chanwoo Choi
2015-06-11  9:33                             ` [linux-sunxi] " Chanwoo Choi
     [not found]                             ` <557955D4.4030908-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2015-06-11  9:37                               ` Chanwoo Choi
2015-06-11  9:37                                 ` Chanwoo Choi
2015-06-11  9:38                               ` Hans de Goede
2015-06-11  9:38                                 ` Hans de Goede
     [not found]                                 ` <5579572D.4070006-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2015-06-11 10:30                                   ` Chanwoo Choi
2015-06-11 10:30                                     ` [linux-sunxi] " Chanwoo Choi
2015-06-11  9:42       ` Kishon Vijay Abraham I
2015-06-11  9:42         ` Kishon Vijay Abraham I
     [not found]         ` <55795800.4070405-l0cyMroinI0@public.gmane.org>
2015-06-11  9:53           ` Hans de Goede
2015-06-11  9:53             ` Hans de Goede
     [not found]             ` <55795A98.8090303-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2015-06-11 11:16               ` Kishon Vijay Abraham I
2015-06-11 11:16                 ` Kishon Vijay Abraham I
     [not found]                 ` <55796E23.90509-l0cyMroinI0@public.gmane.org>
2015-06-11 12:35                   ` [linux-sunxi] " Hans de Goede
2015-06-11 12:35                     ` Hans de Goede
     [not found]                     ` <5579808C.2080208-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2015-06-11 12:59                       ` Kishon Vijay Abraham I
2015-06-11 12:59                         ` [linux-sunxi] " Kishon Vijay Abraham I
2015-07-15 10:55       ` Kishon Vijay Abraham I
2015-07-15 10:55         ` Kishon Vijay Abraham I
     [not found]         ` <55A63C1B.1010503-l0cyMroinI0@public.gmane.org>
2015-07-15 10:58           ` Kishon Vijay Abraham I
2015-07-15 10:58             ` Kishon Vijay Abraham I
2015-05-31 16:10   ` [PATCH v4 2/2] musb: Add support for the Allwinner sunxi musb controller Hans de Goede
2015-05-31 16:10     ` Hans de Goede
     [not found]     ` <1433088626-8858-3-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2015-06-02 15:02       ` Felipe Balbi
2015-06-02 15:02         ` Felipe Balbi
2015-06-01 18:30   ` [PATCH v4 0/2] Remaining sunxi musb patches Hans de Goede
     [not found]     ` <79fc4306-a621-43b8-b347-e42a27beb134-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org>
2015-06-01 18:34       ` Felipe Balbi
2015-06-01 18:34         ` Felipe Balbi
     [not found]         ` <20150601183447.GF26081-HgARHv6XitJaoMGHk7MhZQC/G2K4zDHf@public.gmane.org>
2015-06-02  7:14           ` Hans de Goede
2015-06-02  7:14             ` Hans de Goede
     [not found]             ` <556D57BD.90502-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2015-06-02 14:45               ` Felipe Balbi
2015-06-02 14:45                 ` Felipe Balbi

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.