Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753182AbcD2LAY (ORCPT ); Fri, 29 Apr 2016 07:00:24 -0400 Received: from mailout3.w1.samsung.com ([210.118.77.13]:57094 "EHLO mailout3.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752928AbcD2LAM (ORCPT ); Fri, 29 Apr 2016 07:00:12 -0400 X-AuditID: cbfec7f4-f796c6d000001486-8e-57233eb8e224 From: Krzysztof Kozlowski To: Kukjin Kim , Krzysztof Kozlowski , Chanwoo Choi , Liam Girdwood , Mark Brown , Greg Kroah-Hartman , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org, linux-usb@vger.kernel.org Cc: linux.amoon@gmail.com, tjakobi@math.uni-bielefeld.de, m.szyprowski@samsung.com, hverkuil@xs4all.nl, Bartlomiej Zolnierkiewicz Subject: [RFT PATCH 1/3] usb: misc: usb3503: Fix HUB mode after bootloader initialization Date: Fri, 29 Apr 2016 12:59:49 +0200 Message-id: <1461927591-7864-2-git-send-email-k.kozlowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1461927591-7864-1-git-send-email-k.kozlowski@samsung.com> References: <1461927591-7864-1-git-send-email-k.kozlowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrHLMWRmVeSWpSXmKPExsVy+t/xa7o77JTDDf4u5bHYOGM9q8XUh0/Y LK5/ec5qMf/IOVaL5sXr2SxOTX7GZPH6haFF/+PXzBbfrnQwWWx6fI3V4vKuOWwWM87vY7JY tKyV2WLdxlvsFmuP3GW3aFv9gdVBwGPnrLvsHptWdbJ57J+7ht1j85J6j3/H2D36tqxi9Pi8 Sc7j1NfP7AEcUVw2Kak5mWWpRfp2CVwZ+zfuYylYpVMx+fFdtgbGB8pdjJwcEgImEk1r/jND 2GISF+6tZ+ti5OIQEljKKNFxdgUjhNPIJDHt01uwKjYBY4nNy5eAVYkITGeW2P3iIAuIwyyw gFHiy/LZ7F2MHBzCAtESh84pgDSwCKhKrH1+mg3E5hVwk3g6ayETxDo5iZPHJrOC2JwC7hL3 Z30HWyAEVLPuyHu2CYy8CxgZVjGKppYmFxQnpeca6hUn5haX5qXrJefnbmKEhPKXHYyLj1kd YhTgYFTi4Z1xTylciDWxrLgy9xCjBAezkgjvUVvlcCHelMTKqtSi/Pii0pzU4kOM0hwsSuK8 c3e9DxESSE8sSc1OTS1ILYLJMnFwSjUwMhuxijDLvbvWrKjj22wxVfzdtvyf+R0xrleXnf78 4sOsLIuc/Ya3Gj3eLnie9qFpQ3hXRgqTTkuO39qo62vOOPEYfLVanhLH3LnwaeX1C8pnbJau sL4/dd92rYlKvNIzr/9ui9t9V23tfBkB/RhDA4vVl0/JFUe4/Ay9Wxc650+MbOjCNWluSizF GYmGWsxFxYkAE+PF/mECAAA= Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 5477 Lines: 195 On Odroid U3 (Exynos4412-based) board if USB was initialized by bootloader (in U-Boot "usb start" before tftpboot), the HUB after after successful probing was not visible in the system ("lsusb"). Connected devices were not visible neither. In such case the USB3503 has to be fully reset before configuring to HUB mode. Reset by GPIO (called RESET_N pin) and by RESET field in STCD register are not sufficient. Instead full reset has to be done by disabling and enabling regulator. The USB3503 can work with different regulator configurations, however toggling of only one is relevant in mentioned case: the VDD 3.3V. The patch adds: 1. New binding for optional regulator (VDD33), 2. Code for toggling the regulator on/off before doing reset by GPIO, 3. Initial reset during probing before configuring HUB mode. Patch is very loosely based on Tobias Jakobi's similar work for usb3503. Signed-off-by: Krzysztof Kozlowski --- Tested on Odroid U3 so far. Please kindly test on X2 or other configurations and bootloaders. --- Documentation/devicetree/bindings/usb/usb3503.txt | 1 + drivers/usb/misc/usb3503.c | 81 +++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/Documentation/devicetree/bindings/usb/usb3503.txt b/Documentation/devicetree/bindings/usb/usb3503.txt index c1a0a9191d26..36516ade9467 100644 --- a/Documentation/devicetree/bindings/usb/usb3503.txt +++ b/Documentation/devicetree/bindings/usb/usb3503.txt @@ -24,6 +24,7 @@ Optional properties: pins (optional, if not provided, driver will not set rate of the REFCLK signal and assume that a value from the primary reference clock frequencies table is used) +- vdd33-supply: Optional supply for VDD 3.3 V power source. Examples: usb3503@08 { diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index b45cb77c0744..8905e8b2439d 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -28,6 +28,7 @@ #include #include #include +#include #define USB3503_VIDL 0x00 #define USB3503_VIDM 0x01 @@ -59,6 +60,7 @@ struct usb3503 { struct regmap *regmap; struct device *dev; struct clk *clk; + struct regulator *vdd_reg; u8 port_off_mask; int gpio_intn; int gpio_reset; @@ -66,8 +68,31 @@ struct usb3503 { bool secondary_ref_clk; }; +static int usb3503_regulator(struct usb3503 *hub, int state) +{ + int ret; + + if (!hub->vdd_reg) + return 0; + + if (state) + ret = regulator_enable(hub->vdd_reg); + else + ret = regulator_disable(hub->vdd_reg); + + return ret; +} + static int usb3503_reset(struct usb3503 *hub, int state) { + int err; + + err = usb3503_regulator(hub, state); + if (err) { + dev_err(hub->dev, "unable to %s VDD33 regulator to (%d)\n", + (state ? "enable" : "disable"), err); + } + if (!state && gpio_is_valid(hub->gpio_connect)) gpio_set_value_cansleep(hub->gpio_connect, 0); @@ -260,6 +285,15 @@ static int usb3503_probe(struct usb3503 *hub) return -EPROBE_DEFER; of_property_read_u32(np, "initial-mode", &mode); hub->mode = mode; + + hub->vdd_reg = devm_regulator_get_optional(dev, "vdd33"); + if (IS_ERR(hub->vdd_reg)) { + if (PTR_ERR(hub->vdd_reg) == -EPROBE_DEFER) + return -EPROBE_DEFER; + hub->vdd_reg = NULL; + } else { + dev_dbg(dev, "Using VDD33 regulator for full reset\n"); + } } if (hub->port_off_mask && !hub->regmap) @@ -299,6 +333,21 @@ static int usb3503_probe(struct usb3503 *hub) return err; } } + err = usb3503_regulator(hub, true); + if (err) { + dev_err(dev, "unable to enable VDD33 regulator (%d)\n", err); + return err; + } + + /* + * Perform real full reset before configuring. + * On some boards (e.g. on Odroid U3 board with LAN9730/SMSC95xx) + * after enabling the USB by bootloader it has to be fully reset + * here to be visible. + */ + usb3503_reset(hub, 0); + /* Settle down before powering on again */ + usleep_range(4000, 10000); usb3503_switch_mode(hub, hub->mode); @@ -330,6 +379,21 @@ static int usb3503_i2c_probe(struct i2c_client *i2c, return usb3503_probe(hub); } +static int usb3503_i2c_remove(struct i2c_client *i2c) +{ + struct usb3503 *hub; + + hub = i2c_get_clientdata(i2c); + if (hub) { + if (hub->clk) + clk_disable_unprepare(hub->clk); + + usb3503_regulator(hub, false); + } + + return 0; +} + static int usb3503_platform_probe(struct platform_device *pdev) { struct usb3503 *hub; @@ -342,6 +406,21 @@ static int usb3503_platform_probe(struct platform_device *pdev) return usb3503_probe(hub); } +static int usb3503_platform_remove(struct platform_device *pdev) +{ + struct usb3503 *hub; + + hub = platform_get_drvdata(pdev); + if (hub) { + if (hub->clk) + clk_disable_unprepare(hub->clk); + + usb3503_regulator(hub, false); + } + + return 0; +} + #ifdef CONFIG_PM_SLEEP static int usb3503_i2c_suspend(struct device *dev) { @@ -395,6 +474,7 @@ static struct i2c_driver usb3503_i2c_driver = { .of_match_table = of_match_ptr(usb3503_of_match), }, .probe = usb3503_i2c_probe, + .remove = usb3503_i2c_remove, .id_table = usb3503_id, }; @@ -404,6 +484,7 @@ static struct platform_driver usb3503_platform_driver = { .of_match_table = of_match_ptr(usb3503_of_match), }, .probe = usb3503_platform_probe, + .remove = usb3503_platform_remove, }; static int __init usb3503_init(void) -- 1.9.1