+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
* Copyright (C) 2014 Marek Vasut <marex@denx.de>
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <wait_bit.h>
#include <asm/io.h>
#include <power/regulator.h>
+#include <reset.h>
#include "dwc2.h"
-DECLARE_GLOBAL_DATA_PTR;
-
/* Use only HC channel 0. */
#define DWC2_HC_CHANNEL 0
#define DWC2_STATUS_BUF_SIZE 64
-#define DWC2_DATA_BUF_SIZE (64 * 1024)
+#define DWC2_DATA_BUF_SIZE (CONFIG_USB_DWC2_BUFFER_SIZE * 1024)
#define MAX_DEVICE 16
#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
#else
uint8_t *aligned_buffer;
uint8_t *status_buffer;
*/
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);
ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_TXFFLSH,
false, 1000, false);
if (ret)
- printf("%s: Timeout!\n", __func__);
+ dev_info(dev, "%s: Timeout!\n", __func__);
/* Wait for 3 PHY Clocks */
udelay(1);
ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_RXFFLSH,
false, 1000, false);
if (ret)
- printf("%s: Timeout!\n", __func__);
+ dev_info(dev, "%s: Timeout!\n", __func__);
/* Wait for 3 PHY Clocks */
udelay(1);
ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_AHBIDLE,
true, 1000, false);
if (ret)
- printf("%s: Timeout!\n", __func__);
+ dev_info(dev, "%s: Timeout!\n", __func__);
/* Core Soft Reset */
writel(DWC2_GRSTCTL_CSFTRST, ®s->grstctl);
ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_CSFTRST,
false, 1000, false);
if (ret)
- printf("%s: Timeout!\n", __func__);
+ dev_info(dev, "%s: Timeout!\n", __func__);
/*
* Wait for core to come out of reset.
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 udevice *vbus_supply;
+ struct dwc2_priv *priv = dev_get_priv(dev);
int ret;
- ret = device_get_supply_regulator(dev, "vbus-supply", &vbus_supply);
+ ret = device_get_supply_regulator(dev, "vbus-supply",
+ &priv->vbus_supply);
if (ret) {
debug("%s: No vbus supply\n", dev->name);
return 0;
}
- ret = regulator_set_enable(vbus_supply, true);
+ ret = regulator_set_enable(priv->vbus_supply, true);
if (ret) {
- pr_err("Error enabling vbus supply\n");
+ dev_err(dev, "Error enabling vbus supply\n");
return ret;
}
return 0;
}
+
+static int dwc_vbus_supply_exit(struct udevice *dev)
+{
+ struct dwc2_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ if (priv->vbus_supply) {
+ ret = regulator_set_enable(priv->vbus_supply, false);
+ if (ret) {
+ dev_err(dev, "Error disabling vbus supply\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
#else
static int dwc_vbus_supply_init(struct udevice *dev)
{
return 0;
}
+
+#if CONFIG_IS_ENABLED(DM_USB)
+static int dwc_vbus_supply_exit(struct udevice *dev)
+{
+ return 0;
+}
+#endif
#endif
/*
ret = wait_for_bit_le32(®s->hc_regs[i].hcchar,
DWC2_HCCHAR_CHEN, false, 1000, false);
if (ret)
- printf("%s: Timeout!\n", __func__);
+ dev_info("%s: Timeout!\n", __func__);
}
/* Turn on the vbus power. */
uint32_t hcint, hctsiz;
ret = wait_for_bit_le32(&hc_regs->hcint, DWC2_HCINT_CHHLTD, true,
- 1000, false);
+ 2000, false);
if (ret)
return ret;
timeout = get_timer(0) + USB_TIMEOUT_MS(pipe);
for (;;) {
if (get_timer(0) > timeout) {
- printf("Timeout poll on interrupt endpoint\n");
+ dev_err(dev, "Timeout poll on interrupt endpoint\n");
return -ETIMEDOUT;
}
ret = _submit_bulk_msg(priv, dev, pipe, buffer, len);
}
}
+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;
+ }
+
+ 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)
{
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);
- printf("Core Release: %x.%03x\n", snpsid >> 12 & 0xf, snpsid & 0xfff);
+ dev_info(dev, "Core Release: %x.%03x\n",
+ snpsid >> 12 & 0xf, snpsid & 0xfff);
if ((snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_2xx &&
(snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_3xx) {
- printf("SNPSID invalid (not DWC2 OTG device): %08x\n", snpsid);
+ dev_info(dev, "SNPSID invalid (not DWC2 OTG device): %08x\n",
+ snpsid);
return -ENODEV;
}
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)
{
}
#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)
static int dwc2_usb_remove(struct udevice *dev)
{
struct dwc2_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = dwc_vbus_supply_exit(dev);
+ if (ret)
+ return ret;
dwc2_uninit_common(priv->regs);
+ reset_release_bulk(&priv->resets);
+
return 0;
}
static const struct udevice_id dwc2_usb_ids[] = {
{ .compatible = "brcm,bcm2835-usb" },
+ { .compatible = "brcm,bcm2708-usb" },
{ .compatible = "snps,dwc2" },
{ }
};