usb: tegra: Move utmi-pads reset from ehci-tegra to tegra-phy

UTMI pads are shared by USB controllers and reset of UTMI pads is shared
with the reset of USB1 controller. Currently reset of UTMI pads is done by
the EHCI driver and ChipIdea UDC works because EHCI driver always happen
to be probed first. Move reset controls from ehci-tegra to tegra-phy in
order to resolve the problem.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
This commit is contained in:
Dmitry Osipenko 2018-04-10 01:02:58 +03:00 committed by Felipe Balbi
parent 3e1d333f6b
commit d2b9889f77
3 changed files with 117 additions and 55 deletions

View file

@ -36,7 +36,6 @@
#define DRV_NAME "tegra-ehci" #define DRV_NAME "tegra-ehci"
static struct hc_driver __read_mostly tegra_ehci_hc_driver; static struct hc_driver __read_mostly tegra_ehci_hc_driver;
static bool usb1_reset_attempted;
struct tegra_ehci_soc_config { struct tegra_ehci_soc_config {
bool has_hostpc; bool has_hostpc;
@ -51,68 +50,55 @@ struct tegra_ehci_hcd {
enum tegra_usb_phy_port_speed port_speed; enum tegra_usb_phy_port_speed port_speed;
}; };
/*
* The 1st USB controller contains some UTMI pad registers that are global for
* all the controllers on the chip. Those registers are also cleared when
* reset is asserted to the 1st controller. This means that the 1st controller
* can only be reset when no other controlled has finished probing. So we'll
* reset the 1st controller before doing any other setup on any of the
* controllers, and then never again.
*
* Since this is a PHY issue, the Tegra PHY driver should probably be doing
* the resetting of the USB controllers. But to keep compatibility with old
* device trees that don't have reset phandles in the PHYs, do it here.
* Those old DTs will be vulnerable to total USB breakage if the 1st EHCI
* device isn't the first one to finish probing, so warn them.
*/
static int tegra_reset_usb_controller(struct platform_device *pdev) static int tegra_reset_usb_controller(struct platform_device *pdev)
{ {
struct device_node *phy_np; struct device_node *phy_np;
struct usb_hcd *hcd = platform_get_drvdata(pdev); struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct tegra_ehci_hcd *tegra = struct tegra_ehci_hcd *tegra =
(struct tegra_ehci_hcd *)hcd_to_ehci(hcd)->priv; (struct tegra_ehci_hcd *)hcd_to_ehci(hcd)->priv;
bool has_utmi_pad_registers = false; struct reset_control *rst;
int err;
phy_np = of_parse_phandle(pdev->dev.of_node, "nvidia,phy", 0); phy_np = of_parse_phandle(pdev->dev.of_node, "nvidia,phy", 0);
if (!phy_np) if (!phy_np)
return -ENOENT; return -ENOENT;
if (of_property_read_bool(phy_np, "nvidia,has-utmi-pad-registers")) /*
has_utmi_pad_registers = true; * The 1st USB controller contains some UTMI pad registers that are
* global for all the controllers on the chip. Those registers are
if (!usb1_reset_attempted) { * also cleared when reset is asserted to the 1st controller.
struct reset_control *usb1_reset; */
rst = of_reset_control_get_shared(phy_np, "utmi-pads");
if (!has_utmi_pad_registers) if (IS_ERR(rst)) {
usb1_reset = of_reset_control_get(phy_np, "utmi-pads"); dev_warn(&pdev->dev,
else "can't get utmi-pads reset from the PHY\n");
usb1_reset = tegra->rst; dev_warn(&pdev->dev,
"continuing, but please update your DT\n");
if (IS_ERR(usb1_reset)) { } else {
dev_warn(&pdev->dev, /*
"can't get utmi-pads reset from the PHY\n"); * PHY driver performs UTMI-pads reset in a case of
dev_warn(&pdev->dev, * non-legacy DT.
"continuing, but please update your DT\n"); */
} else { reset_control_put(rst);
reset_control_assert(usb1_reset);
udelay(1);
reset_control_deassert(usb1_reset);
if (!has_utmi_pad_registers)
reset_control_put(usb1_reset);
}
usb1_reset_attempted = true;
}
if (!has_utmi_pad_registers) {
reset_control_assert(tegra->rst);
udelay(1);
reset_control_deassert(tegra->rst);
} }
of_node_put(phy_np); of_node_put(phy_np);
/* reset control is shared, hence initialize it first */
err = reset_control_deassert(tegra->rst);
if (err)
return err;
err = reset_control_assert(tegra->rst);
if (err)
return err;
udelay(1);
err = reset_control_deassert(tegra->rst);
if (err)
return err;
return 0; return 0;
} }
@ -440,7 +426,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
goto cleanup_hcd_create; goto cleanup_hcd_create;
} }
tegra->rst = devm_reset_control_get(&pdev->dev, "usb"); tegra->rst = devm_reset_control_get_shared(&pdev->dev, "usb");
if (IS_ERR(tegra->rst)) { if (IS_ERR(tegra->rst)) {
dev_err(&pdev->dev, "Can't get ehci reset\n"); dev_err(&pdev->dev, "Can't get ehci reset\n");
err = PTR_ERR(tegra->rst); err = PTR_ERR(tegra->rst);
@ -452,8 +438,10 @@ static int tegra_ehci_probe(struct platform_device *pdev)
goto cleanup_hcd_create; goto cleanup_hcd_create;
err = tegra_reset_usb_controller(pdev); err = tegra_reset_usb_controller(pdev);
if (err) if (err) {
dev_err(&pdev->dev, "Failed to reset controller\n");
goto cleanup_clk_en; goto cleanup_clk_en;
}
u_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0); u_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0);
if (IS_ERR(u_phy)) { if (IS_ERR(u_phy)) {
@ -538,6 +526,9 @@ static int tegra_ehci_remove(struct platform_device *pdev)
usb_phy_shutdown(hcd->usb_phy); usb_phy_shutdown(hcd->usb_phy);
usb_remove_hcd(hcd); usb_remove_hcd(hcd);
reset_control_assert(tegra->rst);
udelay(1);
clk_disable_unprepare(tegra->clk); clk_disable_unprepare(tegra->clk);
usb_put_hcd(hcd); usb_put_hcd(hcd);

View file

@ -236,17 +236,83 @@ static void set_phcd(struct tegra_usb_phy *phy, bool enable)
static int utmip_pad_open(struct tegra_usb_phy *phy) static int utmip_pad_open(struct tegra_usb_phy *phy)
{ {
int err; int ret;
phy->pad_clk = devm_clk_get(phy->u_phy.dev, "utmi-pads"); phy->pad_clk = devm_clk_get(phy->u_phy.dev, "utmi-pads");
if (IS_ERR(phy->pad_clk)) { if (IS_ERR(phy->pad_clk)) {
err = PTR_ERR(phy->pad_clk); ret = PTR_ERR(phy->pad_clk);
dev_err(phy->u_phy.dev, dev_err(phy->u_phy.dev,
"Failed to get UTMIP pad clock: %d\n", err); "Failed to get UTMIP pad clock: %d\n", ret);
return err; return ret;
} }
return 0; phy->pad_rst = devm_reset_control_get_optional_shared(
phy->u_phy.dev, "utmi-pads");
if (IS_ERR(phy->pad_rst)) {
ret = PTR_ERR(phy->pad_rst);
dev_err(phy->u_phy.dev,
"Failed to get UTMI-pads reset: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(phy->pad_clk);
if (ret) {
dev_err(phy->u_phy.dev,
"Failed to enable UTMI-pads clock: %d\n", ret);
return ret;
}
spin_lock(&utmip_pad_lock);
ret = reset_control_deassert(phy->pad_rst);
if (ret) {
dev_err(phy->u_phy.dev,
"Failed to initialize UTMI-pads reset: %d\n", ret);
goto unlock;
}
ret = reset_control_assert(phy->pad_rst);
if (ret) {
dev_err(phy->u_phy.dev,
"Failed to assert UTMI-pads reset: %d\n", ret);
goto unlock;
}
udelay(1);
ret = reset_control_deassert(phy->pad_rst);
if (ret)
dev_err(phy->u_phy.dev,
"Failed to deassert UTMI-pads reset: %d\n", ret);
unlock:
spin_unlock(&utmip_pad_lock);
clk_disable_unprepare(phy->pad_clk);
return ret;
}
static int utmip_pad_close(struct tegra_usb_phy *phy)
{
int ret;
ret = clk_prepare_enable(phy->pad_clk);
if (ret) {
dev_err(phy->u_phy.dev,
"Failed to enable UTMI-pads clock: %d\n", ret);
return ret;
}
ret = reset_control_assert(phy->pad_rst);
if (ret)
dev_err(phy->u_phy.dev,
"Failed to assert UTMI-pads reset: %d\n", ret);
udelay(1);
clk_disable_unprepare(phy->pad_clk);
return ret;
} }
static void utmip_pad_power_on(struct tegra_usb_phy *phy) static void utmip_pad_power_on(struct tegra_usb_phy *phy)
@ -700,6 +766,9 @@ static void tegra_usb_phy_close(struct tegra_usb_phy *phy)
if (!IS_ERR(phy->vbus)) if (!IS_ERR(phy->vbus))
regulator_disable(phy->vbus); regulator_disable(phy->vbus);
if (!phy->is_ulpi_phy)
utmip_pad_close(phy);
clk_disable_unprepare(phy->pll_u); clk_disable_unprepare(phy->pll_u);
} }

View file

@ -17,6 +17,7 @@
#define __TEGRA_USB_PHY_H #define __TEGRA_USB_PHY_H
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/reset.h>
#include <linux/usb/otg.h> #include <linux/usb/otg.h>
/* /*
@ -76,6 +77,7 @@ struct tegra_usb_phy {
bool is_legacy_phy; bool is_legacy_phy;
bool is_ulpi_phy; bool is_ulpi_phy;
int reset_gpio; int reset_gpio;
struct reset_control *pad_rst;
}; };
void tegra_usb_phy_preresume(struct usb_phy *phy); void tegra_usb_phy_preresume(struct usb_phy *phy);