diff mbox

usb: phy: samsung-usb2: Toggle HSIC GPIO from device tree

Message ID 1373416455-30358-1-git-send-email-jwerner@chromium.org (mailing list archive)
State New, archived
Headers show

Commit Message

Julius Werner July 10, 2013, 12:34 a.m. UTC
This patch adds support for a new 'samsung,hsic-reset-gpio' in the
device tree, which will be interpreted as an active-low reset pin during
PHY initialization when it exists. Useful for intergrated HSIC devices
like an SMSC 3503 hub. It is necessary to add this directly to the PHY
initialization to get the timing right, since resetting a HSIC device
after it has already been enumerated can confuse the USB stack.

Also fixes PHY semaphore code to make sure we always go through the
setup at least once, even if it was already turned on (e.g. by
firmware), and changes a spinlock to a mutex to allow sleeping in the
critical section.

Change-Id: Ieecac52c27daa7a17a7ed3b2863ddba3aeb8d16f
Signed-off-by: Julius Werner <jwerner@chromium.org>
---
 .../devicetree/bindings/usb/samsung-usbphy.txt     | 10 ++++++
 drivers/usb/phy/phy-samsung-usb.c                  | 17 ++++++++++
 drivers/usb/phy/phy-samsung-usb.h                  |  7 ++--
 drivers/usb/phy/phy-samsung-usb2.c                 | 38 ++++++++++------------
 drivers/usb/phy/phy-samsung-usb3.c                 | 12 +++----
 5 files changed, 55 insertions(+), 29 deletions(-)

Comments

Felipe Balbi July 10, 2013, 5:25 a.m. UTC | #1
On Tue, Jul 09, 2013 at 05:34:15PM -0700, Julius Werner wrote:
> This patch adds support for a new 'samsung,hsic-reset-gpio' in the
> device tree, which will be interpreted as an active-low reset pin during
> PHY initialization when it exists. Useful for intergrated HSIC devices
> like an SMSC 3503 hub. It is necessary to add this directly to the PHY
> initialization to get the timing right, since resetting a HSIC device
> after it has already been enumerated can confuse the USB stack.
> 
> Also fixes PHY semaphore code to make sure we always go through the
> setup at least once, even if it was already turned on (e.g. by
> firmware), and changes a spinlock to a mutex to allow sleeping in the
> critical section.
> 
> Change-Id: Ieecac52c27daa7a17a7ed3b2863ddba3aeb8d16f
> Signed-off-by: Julius Werner <jwerner@chromium.org>
> ---
>  .../devicetree/bindings/usb/samsung-usbphy.txt     | 10 ++++++
>  drivers/usb/phy/phy-samsung-usb.c                  | 17 ++++++++++
>  drivers/usb/phy/phy-samsung-usb.h                  |  7 ++--
>  drivers/usb/phy/phy-samsung-usb2.c                 | 38 ++++++++++------------
>  drivers/usb/phy/phy-samsung-usb3.c                 | 12 +++----
>  5 files changed, 55 insertions(+), 29 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt
> index 33fd354..82e2e16 100644
> --- a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt
> +++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt
> @@ -31,6 +31,12 @@ Optional properties:
>  - ranges: allows valid translation between child's address space and parent's
>  	  address space.
>  
> +- samsung,hsic-reset-gpio: an active low GPIO pin that resets a device
> +			connected to the HSIC port. Useful for things like
> +			an on-board SMSC3503 hub.
> +- pinctrl-0: Pin control group containing the HSIC reset GPIO pin.
> +- pinctrl-names: Should contain only one value - "default".
> +
>  - The child node 'usbphy-sys' to the node 'usbphy' is for the system controller
>    interface for usb-phy. It should provide the following information required by
>    usb-phy controller to control phy.
> @@ -56,6 +62,10 @@ Example:
>  		clocks = <&clock 2>, <&clock 305>;
>  		clock-names = "xusbxti", "otg";
>  
> +		samsung,hsic-reset-gpio = <&gpx2 4 1>;

looks like this should be modeled as a fixed-regulator ?
Julius Werner July 10, 2013, 5:42 p.m. UTC | #2
Hi Felipe,

This is intended to pull down a reset signal line, not to switch power
to the device. I could implement that with the regulator framework
too, but I think that would just be confusing and harder to understand
without providing any benefit. It's really just a plain old GPIO.
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jingoo Han July 10, 2013, 11:52 p.m. UTC | #3
On Wednesday, July 10, 2013 9:34 AM, Julius Werner wrote:
> 
> This patch adds support for a new 'samsung,hsic-reset-gpio' in the
> device tree, which will be interpreted as an active-low reset pin during
> PHY initialization when it exists. Useful for intergrated HSIC devices
> like an SMSC 3503 hub. It is necessary to add this directly to the PHY
> initialization to get the timing right, since resetting a HSIC device
> after it has already been enumerated can confuse the USB stack.
> 
> Also fixes PHY semaphore code to make sure we always go through the
> setup at least once, even if it was already turned on (e.g. by
> firmware), and changes a spinlock to a mutex to allow sleeping in the
> critical section.

CC'ed Thomas Abraham,

This reset signal pin seems 'HUB_RESET' pin to reset SMSC 3503 hub
on Arndale board. This reset pin is described that 'This active low signal
is used by the system to reset the chip' in SMSC 3503 hub datasheet.

One more thing, 
'phy-samsung-usb*.c' files are used and designed to control USB PHY controller
in Exynos SoCs; thus, these files should control only internal USB PHY controller
in Exynos SoCs.

However, the reset pin is used for reset external SMSC 3503 hub chip
that is not placed in Exynos SoC.

Thus, there should not be HUB reset code
in ./drivers/usb/phy/phy-samsung-usb*.c

This topic was already discussed one month ago.
As Olof Johansson mentioned, 'drivers/platform/arm/' would be
a good alternative; thus, USB hub reset code should be moved 
to 'drivers/platform/arm/'. Please refer to the discussion.
(http://patches.linaro.org/16856/)


Best regards,
Jingoo Han

> 
> Change-Id: Ieecac52c27daa7a17a7ed3b2863ddba3aeb8d16f
> Signed-off-by: Julius Werner <jwerner@chromium.org>
> ---
>  .../devicetree/bindings/usb/samsung-usbphy.txt     | 10 ++++++
>  drivers/usb/phy/phy-samsung-usb.c                  | 17 ++++++++++
>  drivers/usb/phy/phy-samsung-usb.h                  |  7 ++--
>  drivers/usb/phy/phy-samsung-usb2.c                 | 38 ++++++++++------------
>  drivers/usb/phy/phy-samsung-usb3.c                 | 12 +++----
>  5 files changed, 55 insertions(+), 29 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt
> b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt
> index 33fd354..82e2e16 100644
> --- a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt
> +++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt
> @@ -31,6 +31,12 @@ Optional properties:
>  - ranges: allows valid translation between child's address space and parent's
>  	  address space.
> 
> +- samsung,hsic-reset-gpio: an active low GPIO pin that resets a device
> +			connected to the HSIC port. Useful for things like
> +			an on-board SMSC3503 hub.
> +- pinctrl-0: Pin control group containing the HSIC reset GPIO pin.
> +- pinctrl-names: Should contain only one value - "default".
> +
>  - The child node 'usbphy-sys' to the node 'usbphy' is for the system controller
>    interface for usb-phy. It should provide the following information required by
>    usb-phy controller to control phy.
> @@ -56,6 +62,10 @@ Example:
>  		clocks = <&clock 2>, <&clock 305>;
>  		clock-names = "xusbxti", "otg";
> 
> +		samsung,hsic-reset-gpio = <&gpx2 4 1>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&hsic_reset>;
> +
>  		usbphy-sys {
>  			/* USB device and host PHY_CONTROL registers */
>  			reg = <0x10020704 0x8>;
> diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c
> index ac025ca..23f1d70 100644
> --- a/drivers/usb/phy/phy-samsung-usb.c
> +++ b/drivers/usb/phy/phy-samsung-usb.c
> @@ -27,6 +27,7 @@
>  #include <linux/io.h>
>  #include <linux/of.h>
>  #include <linux/of_address.h>
> +#include <linux/of_gpio.h>
>  #include <linux/usb/samsung_usb_phy.h>
> 
>  #include "phy-samsung-usb.h"
> @@ -58,6 +59,22 @@ int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy)
>  	if (sphy->sysreg == NULL)
>  		dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n");
> 
> +	/*
> +	 * Some boards have a separate active-low reset GPIO for their HSIC USB
> +	 * devices. If they don't, this will just stay at an invalid value and
> +	 * the init code will ignore it.
> +	 */
> +	sphy->hsic_reset_gpio = of_get_named_gpio(sphy->dev->of_node,
> +						"samsung,hsic-reset-gpio", 0);
> +	if (gpio_is_valid(sphy->hsic_reset_gpio)) {
> +		if (devm_gpio_request_one(sphy->dev, sphy->hsic_reset_gpio,
> +				GPIOF_OUT_INIT_LOW, "samsung_hsic_reset")) {
> +			dev_err(sphy->dev, "can't request hsic reset gpio %d\n",
> +				sphy->hsic_reset_gpio);
> +			sphy->hsic_reset_gpio = -EINVAL;
> +		}
> +	}
> +
>  	of_node_put(usbphy_sys);
> 
>  	return 0;
> diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h
> index 68771bf..0703878 100644
> --- a/drivers/usb/phy/phy-samsung-usb.h
> +++ b/drivers/usb/phy/phy-samsung-usb.h
> @@ -16,6 +16,7 @@
>   * GNU General Public License for more details.
>   */
> 
> +#include <linux/mutex.h>
>  #include <linux/usb/phy.h>
> 
>  /* Register definitions */
> @@ -301,7 +302,8 @@ struct samsung_usbphy_drvdata {
>   * @phy_type: Samsung SoCs specific phy types:	#HOST
>   *						#DEVICE
>   * @phy_usage: usage count for phy
> - * @lock: lock for phy operations
> + * @mutex: mutex for phy operations (usb2phy must sleep, so no spinlock!)
> + * @hsic_reset_gpio: Active low GPIO that resets connected HSIC device
>   */
>  struct samsung_usbphy {
>  	struct usb_phy	phy;
> @@ -315,7 +317,8 @@ struct samsung_usbphy {
>  	const struct samsung_usbphy_drvdata *drv_data;
>  	enum samsung_usb_phy_type phy_type;
>  	atomic_t	phy_usage;
> -	spinlock_t	lock;
> +	struct mutex	mutex;
> +	int		hsic_reset_gpio;
>  };
> 
>  #define phy_to_sphy(x)		container_of((x), struct samsung_usbphy, phy)
> diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c
> index 1011c16..2db2113 100644
> --- a/drivers/usb/phy/phy-samsung-usb2.c
> +++ b/drivers/usb/phy/phy-samsung-usb2.c
> @@ -24,6 +24,7 @@
>  #include <linux/delay.h>
>  #include <linux/device.h>
>  #include <linux/err.h>
> +#include <linux/gpio.h>
>  #include <linux/io.h>
>  #include <linux/of.h>
>  #include <linux/usb/otg.h>
> @@ -43,15 +44,6 @@ static int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host)
>  	return 0;
>  }
> 
> -static bool exynos5_phyhost_is_on(void __iomem *regs)
> -{
> -	u32 reg;
> -
> -	reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0);
> -
> -	return !(reg & HOST_CTRL0_SIDDQ);
> -}
> -
>  static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy)
>  {
>  	void __iomem *regs = sphy->regs;
> @@ -68,10 +60,8 @@ static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy)
>  	 * the last consumer to disable it.
>  	 */
> 
> -	atomic_inc(&sphy->phy_usage);
> -
> -	if (exynos5_phyhost_is_on(regs)) {
> -		dev_info(sphy->dev, "Already power on PHY\n");
> +	if (atomic_inc_return(&sphy->phy_usage) != 1) {
> +		dev_info(sphy->dev, "USB PHY already initialized\n");
>  		return;
>  	}
> 
> @@ -132,6 +122,13 @@ static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy)
>  	writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS);
> 
>  	/* HSIC phy configuration */
> +	if (gpio_is_valid(sphy->hsic_reset_gpio)) {
> +		gpio_set_value(sphy->hsic_reset_gpio, 0);
> +		udelay(100);  /* Keep reset as active/low for 100us */
> +		gpio_set_value(sphy->hsic_reset_gpio, 1);
> +		usleep_range(4000, 10000);  /* wait for device init */
> +	}
> +
>  	phyhsic = (HSIC_CTRL_REFCLKDIV_12 |
>  			HSIC_CTRL_REFCLKSEL |
>  			HSIC_CTRL_PHYSWRST);
> @@ -220,6 +217,9 @@ static void samsung_exynos5_usb2phy_disable(struct samsung_usbphy *sphy)
>  	writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1);
>  	writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2);
> 
> +	if (gpio_is_valid(sphy->hsic_reset_gpio))
> +		gpio_set_value(sphy->hsic_reset_gpio, 0);
> +
>  	phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0);
>  	phyhost |= (HOST_CTRL0_SIDDQ |
>  			HOST_CTRL0_FORCESUSPEND |
> @@ -267,7 +267,6 @@ static int samsung_usb2phy_init(struct usb_phy *phy)
>  {
>  	struct samsung_usbphy *sphy;
>  	struct usb_bus *host = NULL;
> -	unsigned long flags;
>  	int ret = 0;
> 
>  	sphy = phy_to_sphy(phy);
> @@ -281,7 +280,7 @@ static int samsung_usb2phy_init(struct usb_phy *phy)
>  		return ret;
>  	}
> 
> -	spin_lock_irqsave(&sphy->lock, flags);
> +	mutex_lock(&sphy->mutex);
> 
>  	if (host) {
>  		/* setting default phy-type for USB 2.0 */
> @@ -304,7 +303,7 @@ static int samsung_usb2phy_init(struct usb_phy *phy)
>  	/* Initialize usb phy registers */
>  	sphy->drv_data->phy_enable(sphy);
> 
> -	spin_unlock_irqrestore(&sphy->lock, flags);
> +	mutex_unlock(&sphy->mutex);
> 
>  	/* Disable the phy clock */
>  	clk_disable_unprepare(sphy->clk);
> @@ -319,7 +318,6 @@ static void samsung_usb2phy_shutdown(struct usb_phy *phy)
>  {
>  	struct samsung_usbphy *sphy;
>  	struct usb_bus *host = NULL;
> -	unsigned long flags;
> 
>  	sphy = phy_to_sphy(phy);
> 
> @@ -330,7 +328,7 @@ static void samsung_usb2phy_shutdown(struct usb_phy *phy)
>  		return;
>  	}
> 
> -	spin_lock_irqsave(&sphy->lock, flags);
> +	mutex_lock(&sphy->mutex);
> 
>  	if (host) {
>  		/* setting default phy-type for USB 2.0 */
> @@ -350,7 +348,7 @@ static void samsung_usb2phy_shutdown(struct usb_phy *phy)
>  	else if (sphy->drv_data->set_isolation)
>  		sphy->drv_data->set_isolation(sphy, true);
> 
> -	spin_unlock_irqrestore(&sphy->lock, flags);
> +	mutex_unlock(&sphy->mutex);
> 
>  	clk_disable_unprepare(sphy->clk);
>  }
> @@ -422,7 +420,7 @@ static int samsung_usb2phy_probe(struct platform_device *pdev)
>  	sphy->phy.otg->phy	= &sphy->phy;
>  	sphy->phy.otg->set_host = samsung_usbphy_set_host;
> 
> -	spin_lock_init(&sphy->lock);
> +	mutex_init(&sphy->mutex);
> 
>  	platform_set_drvdata(pdev, sphy);
> 
> diff --git a/drivers/usb/phy/phy-samsung-usb3.c b/drivers/usb/phy/phy-samsung-usb3.c
> index 300e0cf..6ec3f08 100644
> --- a/drivers/usb/phy/phy-samsung-usb3.c
> +++ b/drivers/usb/phy/phy-samsung-usb3.c
> @@ -164,7 +164,6 @@ static void samsung_exynos5_usb3phy_disable(struct samsung_usbphy *sphy)
>  static int samsung_usb3phy_init(struct usb_phy *phy)
>  {
>  	struct samsung_usbphy *sphy;
> -	unsigned long flags;
>  	int ret = 0;
> 
>  	sphy = phy_to_sphy(phy);
> @@ -176,7 +175,7 @@ static int samsung_usb3phy_init(struct usb_phy *phy)
>  		return ret;
>  	}
> 
> -	spin_lock_irqsave(&sphy->lock, flags);
> +	mutex_lock(&sphy->mutex);
> 
>  	/* setting default phy-type for USB 3.0 */
>  	samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE);
> @@ -188,7 +187,7 @@ static int samsung_usb3phy_init(struct usb_phy *phy)
>  	/* Initialize usb phy registers */
>  	sphy->drv_data->phy_enable(sphy);
> 
> -	spin_unlock_irqrestore(&sphy->lock, flags);
> +	mutex_unlock(&sphy->mutex);
> 
>  	/* Disable the phy clock */
>  	clk_disable_unprepare(sphy->clk);
> @@ -202,7 +201,6 @@ static int samsung_usb3phy_init(struct usb_phy *phy)
>  static void samsung_usb3phy_shutdown(struct usb_phy *phy)
>  {
>  	struct samsung_usbphy *sphy;
> -	unsigned long flags;
> 
>  	sphy = phy_to_sphy(phy);
> 
> @@ -211,7 +209,7 @@ static void samsung_usb3phy_shutdown(struct usb_phy *phy)
>  		return;
>  	}
> 
> -	spin_lock_irqsave(&sphy->lock, flags);
> +	mutex_lock(&sphy->mutex);
> 
>  	/* setting default phy-type for USB 3.0 */
>  	samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE);
> @@ -223,7 +221,7 @@ static void samsung_usb3phy_shutdown(struct usb_phy *phy)
>  	if (sphy->drv_data->set_isolation)
>  		sphy->drv_data->set_isolation(sphy, true);
> 
> -	spin_unlock_irqrestore(&sphy->lock, flags);
> +	mutex_unlock(&sphy->mutex);
> 
>  	clk_disable_unprepare(sphy->clk);
>  }
> @@ -279,7 +277,7 @@ static int samsung_usb3phy_probe(struct platform_device *pdev)
>  	if (sphy->ref_clk_freq < 0)
>  		return -EINVAL;
> 
> -	spin_lock_init(&sphy->lock);
> +	mutex_init(&sphy->mutex);
> 
>  	platform_set_drvdata(pdev, sphy);
> 
> --
> 1.7.12.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Fabio Estevam July 11, 2013, 12:01 a.m. UTC | #4
Hi Julius,

On Wed, Jul 10, 2013 at 2:42 PM, Julius Werner <jwerner@chromium.org> wrote:
> Hi Felipe,
>
> This is intended to pull down a reset signal line, not to switch power
> to the device. I could implement that with the regulator framework
> too, but I think that would just be confusing and harder to understand
> without providing any benefit. It's really just a plain old GPIO.

It seems that the reset gpio driver from Phillip Zabel would help in this case:
http://permalink.gmane.org/gmane.linux.drivers.devicetree/36830
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Julius Werner July 11, 2013, 9:46 p.m. UTC | #5
Hi Jingoo,

Yeah, I followed that discussion back then, but it seems to have
stalled a little (at least the HSIC patches haven't been picked up in
any kernel.org repo yet to my knowledge).

The problem is that I think these approaches cannot work reliably. I
agree that it would be nice to control the HSIC device from its own
driver, and have spent quite some time playing around with the
usb/misc/usb3503.c driver to try to make this work... but there's a
timing dependency here that you just can't model correctly with
independent drivers.

If the HSIC device is already active during boot (e.g. because it was
used by firmware), there's always a chance that the USB stack will
come up before the driver that resets it does. The device will be
enumerated as normal, and when the other driver later pulls the reset
signal the USB stack will not notice because there is no real
disconnect detection on HSIC. Only when you eventually try to send
another transfer to the device will you start to get timeouts, and the
newly reset device will not be able to reenumerate because the host
never asks it to.

I really don't see how you could solve this without putting some kind
of synchronization mechanism in the USB drivers. So this leaves
ehci-s5p and phy-samsung-usb2 as the only possible places, and I chose
the latter since all the host-side HSIC initialization is also there
already. I think if you think of it as "reset whatever is on the other
side of this PHY", it's okay to put it as an optional feature into the
PHY driver.
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jingoo Han July 12, 2013, 12:48 a.m. UTC | #6
On Friday, July 12, 2013 6:46 AM, Julius Werner wrote:
> 
> Hi Jingoo,
> 
> Yeah, I followed that discussion back then, but it seems to have
> stalled a little (at least the HSIC patches haven't been picked up in
> any kernel.org repo yet to my knowledge).
> 
> The problem is that I think these approaches cannot work reliably. I
> agree that it would be nice to control the HSIC device from its own
> driver, and have spent quite some time playing around with the
> usb/misc/usb3503.c driver to try to make this work... but there's a
> timing dependency here that you just can't model correctly with
> independent drivers.
> 
> If the HSIC device is already active during boot (e.g. because it was
> used by firmware), there's always a chance that the USB stack will
> come up before the driver that resets it does. The device will be
> enumerated as normal, and when the other driver later pulls the reset
> signal the USB stack will not notice because there is no real
> disconnect detection on HSIC. Only when you eventually try to send
> another transfer to the device will you start to get timeouts, and the
> newly reset device will not be able to reenumerate because the host
> never asks it to.
> 
> I really don't see how you could solve this without putting some kind
> of synchronization mechanism in the USB drivers. So this leaves
> ehci-s5p and phy-samsung-usb2 as the only possible places, and I chose
> the latter since all the host-side HSIC initialization is also there
> already. I think if you think of it as "reset whatever is on the other
> side of this PHY", it's okay to put it as an optional feature into the
> PHY driver.

CC'ed Tomasz Figa, Dongjin Kim, Yulgon Kim


Hi Tomasz, Dongjin,

Julius Werner wants to put 'SMSC 3503 hub reset on Arndale board'
to 'phy-samsung-usb*.c' files, because there is a timing dependency
above mentioned.
The following is the original patch sent by Julius Werner two day ago.
(http://www.spinics.net/lists/linux-samsung-soc/msg20250.html)

Previously, Olof mentioned that 'drivers/platform/arm/' would be used.
(http://patches.linaro.org/16856/)

Also, another way was mentioned by Fabio Estevam, using 
'drivers/reset/gpio-reset.c' which is not merged yet.
(http://permalink.gmane.org/gmane.linux.drivers.devicetree/36830)

I think that 'phy-samsung-usb*.c' files are not a good place.
However, Julius Werner's comment looks reasonable enough.

If you have a comment, please feel free to share it. :)
Thank you.

Best regards,
Jingoo Han


--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Felipe Balbi July 12, 2013, 6:57 a.m. UTC | #7
Hi,

On Wed, Jul 10, 2013 at 10:42:27AM -0700, Julius Werner wrote:
> Hi Felipe,
> 
> This is intended to pull down a reset signal line, not to switch power
> to the device. I could implement that with the regulator framework
> too, but I think that would just be confusing and harder to understand
> without providing any benefit. It's really just a plain old GPIO.

alright, in that case how about using drivers/reset/ ?
Tomasz Figa July 12, 2013, 10:34 a.m. UTC | #8
Hi,

On Friday 12 of July 2013 09:48:54 Jingoo Han wrote:
> On Friday, July 12, 2013 6:46 AM, Julius Werner wrote:
> > Hi Jingoo,
> > 
> > Yeah, I followed that discussion back then, but it seems to have
> > stalled a little (at least the HSIC patches haven't been picked up in
> > any kernel.org repo yet to my knowledge).
> > 
> > The problem is that I think these approaches cannot work reliably. I
> > agree that it would be nice to control the HSIC device from its own
> > driver, and have spent quite some time playing around with the
> > usb/misc/usb3503.c driver to try to make this work... but there's a
> > timing dependency here that you just can't model correctly with
> > independent drivers.
> > 
> > If the HSIC device is already active during boot (e.g. because it was
> > used by firmware), there's always a chance that the USB stack will
> > come up before the driver that resets it does. The device will be
> > enumerated as normal, and when the other driver later pulls the reset
> > signal the USB stack will not notice because there is no real
> > disconnect detection on HSIC. Only when you eventually try to send
> > another transfer to the device will you start to get timeouts, and the
> > newly reset device will not be able to reenumerate because the host
> > never asks it to.
> > 
> > I really don't see how you could solve this without putting some kind
> > of synchronization mechanism in the USB drivers. So this leaves
> > ehci-s5p and phy-samsung-usb2 as the only possible places, and I chose
> > the latter since all the host-side HSIC initialization is also there
> > already. I think if you think of it as "reset whatever is on the other
> > side of this PHY", it's okay to put it as an optional feature into the
> > PHY driver.
> 
> CC'ed Tomasz Figa, Dongjin Kim, Yulgon Kim
> 
> 
> Hi Tomasz, Dongjin,
> 
> Julius Werner wants to put 'SMSC 3503 hub reset on Arndale board'
> to 'phy-samsung-usb*.c' files, because there is a timing dependency
> above mentioned.
> The following is the original patch sent by Julius Werner two day ago.
> (http://www.spinics.net/lists/linux-samsung-soc/msg20250.html)

Well, I think this is simply broken. Following are the reasons why I think so:
 a) you can use the smsc3503 chip on any USB/HSIC controller and with any 
USB/HSIC PHY, so you would have to add such reset GPIO to _every_ PHY or 
controller driver,
 b) you might want to use other USB/HSIC hubs like this one that requires some 
initialization sequence, other than just toggling a GPIO, so you would have to 
add cases for all of those hubs or other chips in _every_ PHY or controller 
driver.

> Previously, Olof mentioned that 'drivers/platform/arm/' would be used.
> (http://patches.linaro.org/16856/)
> 
> Also, another way was mentioned by Fabio Estevam, using
> 'drivers/reset/gpio-reset.c' which is not merged yet.
> (http://permalink.gmane.org/gmane.linux.drivers.devicetree/36830)
> 
> I think that 'phy-samsung-usb*.c' files are not a good place.
> However, Julius Werner's comment looks reasonable enough.

I can see this problem almost equivalent to the problem with on-board WLAN 
adapters connected using SDIO. Those adapters require some kind of power on 
sequence before they can be enumerated using standard MMC/SDIO mechanisms and 
so does this USB/HSIC hub.

So basically this is a thing that we should consider on a more generic level, 
not just for this particular single chip.

CCing some extra people to increase chance of getting more opinions on this.

Best regards,
Tomasz

--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Tushar Behera Aug. 13, 2013, 8:41 a.m. UTC | #9
On 12 July 2013 12:27, Felipe Balbi <balbi@ti.com> wrote:
> Hi,
>
> On Wed, Jul 10, 2013 at 10:42:27AM -0700, Julius Werner wrote:
>> Hi Felipe,
>>
>> This is intended to pull down a reset signal line, not to switch power
>> to the device. I could implement that with the regulator framework
>> too, but I think that would just be confusing and harder to understand
>> without providing any benefit. It's really just a plain old GPIO.
>
> alright, in that case how about using drivers/reset/ ?
>

IIUC, reset-gpio driver only provides APIs for reset controls (reset,
assert, deassert). We still need to find out the location from where
we would be calling the reset function.
Felipe Balbi Aug. 27, 2013, 6:44 p.m. UTC | #10
On Tue, Aug 13, 2013 at 02:11:27PM +0530, Tushar Behera wrote:
> On 12 July 2013 12:27, Felipe Balbi <balbi@ti.com> wrote:
> > Hi,
> >
> > On Wed, Jul 10, 2013 at 10:42:27AM -0700, Julius Werner wrote:
> >> Hi Felipe,
> >>
> >> This is intended to pull down a reset signal line, not to switch power
> >> to the device. I could implement that with the regulator framework
> >> too, but I think that would just be confusing and harder to understand
> >> without providing any benefit. It's really just a plain old GPIO.
> >
> > alright, in that case how about using drivers/reset/ ?
> >
> 
> IIUC, reset-gpio driver only provides APIs for reset controls (reset,
> assert, deassert). We still need to find out the location from where
> we would be calling the reset function.

that's fair, but at least you reuse a bunch of boilerplate code to claim
the GPIO, set proper direction and value. No need to duplicate that in
your driver.
Tushar Behera Aug. 28, 2013, 3:55 a.m. UTC | #11
On 28 August 2013 00:14, Felipe Balbi <balbi@ti.com> wrote:
> On Tue, Aug 13, 2013 at 02:11:27PM +0530, Tushar Behera wrote:
>> On 12 July 2013 12:27, Felipe Balbi <balbi@ti.com> wrote:
>> > Hi,
>> >
>> > On Wed, Jul 10, 2013 at 10:42:27AM -0700, Julius Werner wrote:
>> >> Hi Felipe,
>> >>
>> >> This is intended to pull down a reset signal line, not to switch power
>> >> to the device. I could implement that with the regulator framework
>> >> too, but I think that would just be confusing and harder to understand
>> >> without providing any benefit. It's really just a plain old GPIO.
>> >
>> > alright, in that case how about using drivers/reset/ ?
>> >
>>
>> IIUC, reset-gpio driver only provides APIs for reset controls (reset,
>> assert, deassert). We still need to find out the location from where
>> we would be calling the reset function.
>
> that's fair, but at least you reuse a bunch of boilerplate code to claim
> the GPIO, set proper direction and value. No need to duplicate that in
> your driver.
>

SMSC3503 driver code was recently updated by Mark Brown [1] which
allows the device to work even if it is not connected to I2C bus. The
timing is still an issue though [2]. With this USB works on
linux-next, but surely that [2] is not the solution.

[1] http://comments.gmane.org/gmane.linux.usb.general/92061
[2] https://lkml.org/lkml/2013/8/14/814
Julius Werner Aug. 28, 2013, 6:24 p.m. UTC | #12
I've tried to get the 3503 driver to work in my case for quite some
time, but ultimately gave up. For me, playing around with the load
order was not enough to solve all issues. When you try to build a
permanent, clean solution for this, you should definitely also test
the case where the hub has already been initialized and configured by
firmware before the kernel booted, because that brought on another
bunch of issues for me.

I think it ultimately only works reliably when you first reset the
hub, then reset the HSIC port on the PHY side before the USB core gets
a chance to talk to it again (thus one of the drivers needs to be
directly connected to and call the other).
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt
index 33fd354..82e2e16 100644
--- a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt
+++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt
@@ -31,6 +31,12 @@  Optional properties:
 - ranges: allows valid translation between child's address space and parent's
 	  address space.
 
+- samsung,hsic-reset-gpio: an active low GPIO pin that resets a device
+			connected to the HSIC port. Useful for things like
+			an on-board SMSC3503 hub.
+- pinctrl-0: Pin control group containing the HSIC reset GPIO pin.
+- pinctrl-names: Should contain only one value - "default".
+
 - The child node 'usbphy-sys' to the node 'usbphy' is for the system controller
   interface for usb-phy. It should provide the following information required by
   usb-phy controller to control phy.
@@ -56,6 +62,10 @@  Example:
 		clocks = <&clock 2>, <&clock 305>;
 		clock-names = "xusbxti", "otg";
 
+		samsung,hsic-reset-gpio = <&gpx2 4 1>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&hsic_reset>;
+
 		usbphy-sys {
 			/* USB device and host PHY_CONTROL registers */
 			reg = <0x10020704 0x8>;
diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c
index ac025ca..23f1d70 100644
--- a/drivers/usb/phy/phy-samsung-usb.c
+++ b/drivers/usb/phy/phy-samsung-usb.c
@@ -27,6 +27,7 @@ 
 #include <linux/io.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_gpio.h>
 #include <linux/usb/samsung_usb_phy.h>
 
 #include "phy-samsung-usb.h"
@@ -58,6 +59,22 @@  int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy)
 	if (sphy->sysreg == NULL)
 		dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n");
 
+	/*
+	 * Some boards have a separate active-low reset GPIO for their HSIC USB
+	 * devices. If they don't, this will just stay at an invalid value and
+	 * the init code will ignore it.
+	 */
+	sphy->hsic_reset_gpio = of_get_named_gpio(sphy->dev->of_node,
+						"samsung,hsic-reset-gpio", 0);
+	if (gpio_is_valid(sphy->hsic_reset_gpio)) {
+		if (devm_gpio_request_one(sphy->dev, sphy->hsic_reset_gpio,
+				GPIOF_OUT_INIT_LOW, "samsung_hsic_reset")) {
+			dev_err(sphy->dev, "can't request hsic reset gpio %d\n",
+				sphy->hsic_reset_gpio);
+			sphy->hsic_reset_gpio = -EINVAL;
+		}
+	}
+
 	of_node_put(usbphy_sys);
 
 	return 0;
diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h
index 68771bf..0703878 100644
--- a/drivers/usb/phy/phy-samsung-usb.h
+++ b/drivers/usb/phy/phy-samsung-usb.h
@@ -16,6 +16,7 @@ 
  * GNU General Public License for more details.
  */
 
+#include <linux/mutex.h>
 #include <linux/usb/phy.h>
 
 /* Register definitions */
@@ -301,7 +302,8 @@  struct samsung_usbphy_drvdata {
  * @phy_type: Samsung SoCs specific phy types:	#HOST
  *						#DEVICE
  * @phy_usage: usage count for phy
- * @lock: lock for phy operations
+ * @mutex: mutex for phy operations (usb2phy must sleep, so no spinlock!)
+ * @hsic_reset_gpio: Active low GPIO that resets connected HSIC device
  */
 struct samsung_usbphy {
 	struct usb_phy	phy;
@@ -315,7 +317,8 @@  struct samsung_usbphy {
 	const struct samsung_usbphy_drvdata *drv_data;
 	enum samsung_usb_phy_type phy_type;
 	atomic_t	phy_usage;
-	spinlock_t	lock;
+	struct mutex	mutex;
+	int		hsic_reset_gpio;
 };
 
 #define phy_to_sphy(x)		container_of((x), struct samsung_usbphy, phy)
diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c
index 1011c16..2db2113 100644
--- a/drivers/usb/phy/phy-samsung-usb2.c
+++ b/drivers/usb/phy/phy-samsung-usb2.c
@@ -24,6 +24,7 @@ 
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/err.h>
+#include <linux/gpio.h>
 #include <linux/io.h>
 #include <linux/of.h>
 #include <linux/usb/otg.h>
@@ -43,15 +44,6 @@  static int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host)
 	return 0;
 }
 
-static bool exynos5_phyhost_is_on(void __iomem *regs)
-{
-	u32 reg;
-
-	reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0);
-
-	return !(reg & HOST_CTRL0_SIDDQ);
-}
-
 static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy)
 {
 	void __iomem *regs = sphy->regs;
@@ -68,10 +60,8 @@  static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy)
 	 * the last consumer to disable it.
 	 */
 
-	atomic_inc(&sphy->phy_usage);
-
-	if (exynos5_phyhost_is_on(regs)) {
-		dev_info(sphy->dev, "Already power on PHY\n");
+	if (atomic_inc_return(&sphy->phy_usage) != 1) {
+		dev_info(sphy->dev, "USB PHY already initialized\n");
 		return;
 	}
 
@@ -132,6 +122,13 @@  static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy)
 	writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS);
 
 	/* HSIC phy configuration */
+	if (gpio_is_valid(sphy->hsic_reset_gpio)) {
+		gpio_set_value(sphy->hsic_reset_gpio, 0);
+		udelay(100);  /* Keep reset as active/low for 100us */
+		gpio_set_value(sphy->hsic_reset_gpio, 1);
+		usleep_range(4000, 10000);  /* wait for device init */
+	}
+
 	phyhsic = (HSIC_CTRL_REFCLKDIV_12 |
 			HSIC_CTRL_REFCLKSEL |
 			HSIC_CTRL_PHYSWRST);
@@ -220,6 +217,9 @@  static void samsung_exynos5_usb2phy_disable(struct samsung_usbphy *sphy)
 	writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1);
 	writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2);
 
+	if (gpio_is_valid(sphy->hsic_reset_gpio))
+		gpio_set_value(sphy->hsic_reset_gpio, 0);
+
 	phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0);
 	phyhost |= (HOST_CTRL0_SIDDQ |
 			HOST_CTRL0_FORCESUSPEND |
@@ -267,7 +267,6 @@  static int samsung_usb2phy_init(struct usb_phy *phy)
 {
 	struct samsung_usbphy *sphy;
 	struct usb_bus *host = NULL;
-	unsigned long flags;
 	int ret = 0;
 
 	sphy = phy_to_sphy(phy);
@@ -281,7 +280,7 @@  static int samsung_usb2phy_init(struct usb_phy *phy)
 		return ret;
 	}
 
-	spin_lock_irqsave(&sphy->lock, flags);
+	mutex_lock(&sphy->mutex);
 
 	if (host) {
 		/* setting default phy-type for USB 2.0 */
@@ -304,7 +303,7 @@  static int samsung_usb2phy_init(struct usb_phy *phy)
 	/* Initialize usb phy registers */
 	sphy->drv_data->phy_enable(sphy);
 
-	spin_unlock_irqrestore(&sphy->lock, flags);
+	mutex_unlock(&sphy->mutex);
 
 	/* Disable the phy clock */
 	clk_disable_unprepare(sphy->clk);
@@ -319,7 +318,6 @@  static void samsung_usb2phy_shutdown(struct usb_phy *phy)
 {
 	struct samsung_usbphy *sphy;
 	struct usb_bus *host = NULL;
-	unsigned long flags;
 
 	sphy = phy_to_sphy(phy);
 
@@ -330,7 +328,7 @@  static void samsung_usb2phy_shutdown(struct usb_phy *phy)
 		return;
 	}
 
-	spin_lock_irqsave(&sphy->lock, flags);
+	mutex_lock(&sphy->mutex);
 
 	if (host) {
 		/* setting default phy-type for USB 2.0 */
@@ -350,7 +348,7 @@  static void samsung_usb2phy_shutdown(struct usb_phy *phy)
 	else if (sphy->drv_data->set_isolation)
 		sphy->drv_data->set_isolation(sphy, true);
 
-	spin_unlock_irqrestore(&sphy->lock, flags);
+	mutex_unlock(&sphy->mutex);
 
 	clk_disable_unprepare(sphy->clk);
 }
@@ -422,7 +420,7 @@  static int samsung_usb2phy_probe(struct platform_device *pdev)
 	sphy->phy.otg->phy	= &sphy->phy;
 	sphy->phy.otg->set_host = samsung_usbphy_set_host;
 
-	spin_lock_init(&sphy->lock);
+	mutex_init(&sphy->mutex);
 
 	platform_set_drvdata(pdev, sphy);
 
diff --git a/drivers/usb/phy/phy-samsung-usb3.c b/drivers/usb/phy/phy-samsung-usb3.c
index 300e0cf..6ec3f08 100644
--- a/drivers/usb/phy/phy-samsung-usb3.c
+++ b/drivers/usb/phy/phy-samsung-usb3.c
@@ -164,7 +164,6 @@  static void samsung_exynos5_usb3phy_disable(struct samsung_usbphy *sphy)
 static int samsung_usb3phy_init(struct usb_phy *phy)
 {
 	struct samsung_usbphy *sphy;
-	unsigned long flags;
 	int ret = 0;
 
 	sphy = phy_to_sphy(phy);
@@ -176,7 +175,7 @@  static int samsung_usb3phy_init(struct usb_phy *phy)
 		return ret;
 	}
 
-	spin_lock_irqsave(&sphy->lock, flags);
+	mutex_lock(&sphy->mutex);
 
 	/* setting default phy-type for USB 3.0 */
 	samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE);
@@ -188,7 +187,7 @@  static int samsung_usb3phy_init(struct usb_phy *phy)
 	/* Initialize usb phy registers */
 	sphy->drv_data->phy_enable(sphy);
 
-	spin_unlock_irqrestore(&sphy->lock, flags);
+	mutex_unlock(&sphy->mutex);
 
 	/* Disable the phy clock */
 	clk_disable_unprepare(sphy->clk);
@@ -202,7 +201,6 @@  static int samsung_usb3phy_init(struct usb_phy *phy)
 static void samsung_usb3phy_shutdown(struct usb_phy *phy)
 {
 	struct samsung_usbphy *sphy;
-	unsigned long flags;
 
 	sphy = phy_to_sphy(phy);
 
@@ -211,7 +209,7 @@  static void samsung_usb3phy_shutdown(struct usb_phy *phy)
 		return;
 	}
 
-	spin_lock_irqsave(&sphy->lock, flags);
+	mutex_lock(&sphy->mutex);
 
 	/* setting default phy-type for USB 3.0 */
 	samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE);
@@ -223,7 +221,7 @@  static void samsung_usb3phy_shutdown(struct usb_phy *phy)
 	if (sphy->drv_data->set_isolation)
 		sphy->drv_data->set_isolation(sphy, true);
 
-	spin_unlock_irqrestore(&sphy->lock, flags);
+	mutex_unlock(&sphy->mutex);
 
 	clk_disable_unprepare(sphy->clk);
 }
@@ -279,7 +277,7 @@  static int samsung_usb3phy_probe(struct platform_device *pdev)
 	if (sphy->ref_clk_freq < 0)
 		return -EINVAL;
 
-	spin_lock_init(&sphy->lock);
+	mutex_init(&sphy->mutex);
 
 	platform_set_drvdata(pdev, sphy);