X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fdwc2.c;h=cefe9d83b1dc860f6616ac5e36ae5282c3b82f5f;hb=c05ed00afb95fa5237f16962fccf5810437317bf;hp=4862ab0e7db541b2c1b90d972dfea0fcadd3f38d;hpb=ac6c796c3f5c7b7594cf4ecede8cd8fb8d2611ba;p=oweals%2Fu-boot.git diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index 4862ab0e7d..cefe9d83b1 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -1,26 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2012 Oleksandr Tymoshenko * Copyright (C) 2014 Marek Vasut - * - * SPDX-License-Identifier: GPL-2.0+ */ #include +#include +#include #include #include -#include +#include +#include #include #include #include +#include #include #include +#include #include +#include +#include #include +#include #include "dwc2.h" -DECLARE_GLOBAL_DATA_PTR; - /* Use only HC channel 0. */ #define DWC2_HC_CHANNEL 0 @@ -31,12 +36,14 @@ DECLARE_GLOBAL_DATA_PTR; #define MAX_ENDPOINT 16 struct dwc2_priv { -#ifdef CONFIG_DM_USB +#if CONFIG_IS_ENABLED(DM_USB) uint8_t aligned_buffer[DWC2_DATA_BUF_SIZE] __aligned(ARCH_DMA_MINALIGN); uint8_t status_buffer[DWC2_STATUS_BUF_SIZE] __aligned(ARCH_DMA_MINALIGN); #ifdef CONFIG_DM_REGULATOR struct udevice *vbus_supply; #endif + struct phy phy; + struct clk_bulk clks; #else uint8_t *aligned_buffer; uint8_t *status_buffer; @@ -52,9 +59,11 @@ struct dwc2_priv { */ bool hnp_srp_disable; bool oc_disable; + + struct reset_ctl_bulk resets; }; -#ifndef CONFIG_DM_USB +#if !CONFIG_IS_ENABLED(DM_USB) /* We need cacheline-aligned buffers for DMA transfers and dcache support */ DEFINE_ALIGN_BUFFER(uint8_t, aligned_buffer_addr, DWC2_DATA_BUF_SIZE, ARCH_DMA_MINALIGN); @@ -168,7 +177,7 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs) mdelay(100); } -#if defined(CONFIG_DM_USB) && defined(CONFIG_DM_REGULATOR) +#if CONFIG_IS_ENABLED(DM_USB) && defined(CONFIG_DM_REGULATOR) static int dwc_vbus_supply_init(struct udevice *dev) { struct dwc2_priv *priv = dev_get_priv(dev); @@ -211,7 +220,7 @@ static int dwc_vbus_supply_init(struct udevice *dev) return 0; } -#if defined(CONFIG_DM_USB) +#if CONFIG_IS_ENABLED(DM_USB) static int dwc_vbus_supply_exit(struct udevice *dev) { return 0; @@ -1108,7 +1117,8 @@ static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev, } int _submit_int_msg(struct dwc2_priv *priv, struct usb_device *dev, - unsigned long pipe, void *buffer, int len, int interval) + unsigned long pipe, void *buffer, int len, int interval, + bool nonblock) { unsigned long timeout; int ret; @@ -1122,9 +1132,38 @@ int _submit_int_msg(struct dwc2_priv *priv, struct usb_device *dev, return -ETIMEDOUT; } ret = _submit_bulk_msg(priv, dev, pipe, buffer, len); - if (ret != -EAGAIN) + if ((ret != -EAGAIN) || nonblock) + return ret; + } +} + +static int dwc2_reset(struct udevice *dev) +{ + int ret; + struct dwc2_priv *priv = dev_get_priv(dev); + + ret = reset_get_bulk(dev, &priv->resets); + if (ret) { + dev_warn(dev, "Can't get reset: %d\n", ret); + /* Return 0 if error due to !CONFIG_DM_RESET and reset + * DT property is not present. + */ + if (ret == -ENOENT || ret == -ENOTSUPP) + return 0; + else return ret; } + + /* force reset to clear all IP register */ + reset_assert_bulk(&priv->resets); + ret = reset_deassert_bulk(&priv->resets); + if (ret) { + reset_release_bulk(&priv->resets); + dev_err(dev, "Failed to reset: %d\n", ret); + return ret; + } + + return 0; } static int dwc2_init_common(struct udevice *dev, struct dwc2_priv *priv) @@ -1132,6 +1171,11 @@ static int dwc2_init_common(struct udevice *dev, struct dwc2_priv *priv) struct dwc2_core_regs *regs = priv->regs; uint32_t snpsid; int i, j; + int ret; + + ret = dwc2_reset(dev); + if (ret) + return ret; snpsid = readl(®s->gsnpsid); dev_info(dev, "Core Release: %x.%03x\n", @@ -1178,6 +1222,8 @@ static int dwc2_init_common(struct udevice *dev, struct dwc2_priv *priv) if (readl(®s->gintsts) & DWC2_GINTSTS_CURMODE_HOST) mdelay(1000); + printf("USB DWC2\n"); + return 0; } @@ -1190,7 +1236,7 @@ static void dwc2_uninit_common(struct dwc2_core_regs *regs) DWC2_HPRT0_PRTRST); } -#ifndef CONFIG_DM_USB +#if !CONFIG_IS_ENABLED(DM_USB) int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int len, struct devrequest *setup) { @@ -1204,9 +1250,10 @@ int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, } int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, - int len, int interval) + int len, int interval, bool nonblock) { - return _submit_int_msg(&local, dev, pipe, buffer, len, interval); + return _submit_int_msg(&local, dev, pipe, buffer, len, interval, + nonblock); } /* U-Boot USB control interface */ @@ -1235,7 +1282,7 @@ int usb_lowlevel_stop(int index) } #endif -#ifdef CONFIG_DM_USB +#if CONFIG_IS_ENABLED(DM_USB) static int dwc2_submit_control_msg(struct udevice *dev, struct usb_device *udev, unsigned long pipe, void *buffer, int length, struct devrequest *setup) @@ -1260,13 +1307,14 @@ static int dwc2_submit_bulk_msg(struct udevice *dev, struct usb_device *udev, static int dwc2_submit_int_msg(struct udevice *dev, struct usb_device *udev, unsigned long pipe, void *buffer, int length, - int interval) + int interval, bool nonblock) { struct dwc2_priv *priv = dev_get_priv(dev); debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); - return _submit_int_msg(priv, udev, pipe, buffer, length, interval); + return _submit_int_msg(priv, udev, pipe, buffer, length, interval, + nonblock); } static int dwc2_usb_ofdata_to_platdata(struct udevice *dev) @@ -1285,13 +1333,95 @@ static int dwc2_usb_ofdata_to_platdata(struct udevice *dev) return 0; } +static int dwc2_setup_phy(struct udevice *dev) +{ + struct dwc2_priv *priv = dev_get_priv(dev); + int ret; + + ret = generic_phy_get_by_index(dev, 0, &priv->phy); + if (ret) { + if (ret == -ENOENT) + return 0; /* no PHY, nothing to do */ + dev_err(dev, "Failed to get USB PHY: %d.\n", ret); + return ret; + } + + ret = generic_phy_init(&priv->phy); + if (ret) { + dev_dbg(dev, "Failed to init USB PHY: %d.\n", ret); + return ret; + } + + ret = generic_phy_power_on(&priv->phy); + if (ret) { + dev_dbg(dev, "Failed to power on USB PHY: %d.\n", ret); + generic_phy_exit(&priv->phy); + return ret; + } + + return 0; +} + +static int dwc2_shutdown_phy(struct udevice *dev) +{ + struct dwc2_priv *priv = dev_get_priv(dev); + int ret; + + /* PHY is not valid when generic_phy_get_by_index() = -ENOENT */ + if (!generic_phy_valid(&priv->phy)) + return 0; /* no PHY, nothing to do */ + + ret = generic_phy_power_off(&priv->phy); + if (ret) { + dev_dbg(dev, "Failed to power off USB PHY: %d.\n", ret); + return ret; + } + + ret = generic_phy_exit(&priv->phy); + if (ret) { + dev_dbg(dev, "Failed to power off USB PHY: %d.\n", ret); + return ret; + } + + return 0; +} + +static int dwc2_clk_init(struct udevice *dev) +{ + struct dwc2_priv *priv = dev_get_priv(dev); + int ret; + + ret = clk_get_bulk(dev, &priv->clks); + if (ret == -ENOSYS || ret == -ENOENT) + return 0; + if (ret) + return ret; + + ret = clk_enable_bulk(&priv->clks); + if (ret) { + clk_release_bulk(&priv->clks); + return ret; + } + + return 0; +} + static int dwc2_usb_probe(struct udevice *dev) { struct dwc2_priv *priv = dev_get_priv(dev); struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev); + int ret; bus_priv->desc_before_addr = true; + ret = dwc2_clk_init(dev); + if (ret) + return ret; + + ret = dwc2_setup_phy(dev); + if (ret) + return ret; + return dwc2_init_common(dev, priv); } @@ -1304,8 +1434,18 @@ static int dwc2_usb_remove(struct udevice *dev) if (ret) return ret; + ret = dwc2_shutdown_phy(dev); + if (ret) { + dev_dbg(dev, "Failed to shutdown USB PHY: %d.\n", ret); + return ret; + } + dwc2_uninit_common(priv->regs); + reset_release_bulk(&priv->resets); + clk_disable_bulk(&priv->clks); + clk_release_bulk(&priv->clks); + return 0; } @@ -1317,6 +1457,7 @@ struct dm_usb_ops dwc2_usb_ops = { static const struct udevice_id dwc2_usb_ids[] = { { .compatible = "brcm,bcm2835-usb" }, + { .compatible = "brcm,bcm2708-usb" }, { .compatible = "snps,dwc2" }, { } };