summaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc2/dwc2.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc2/dwc2.c')
-rw-r--r--drivers/usb/dwc2/dwc2.c214
1 files changed, 214 insertions, 0 deletions
diff --git a/drivers/usb/dwc2/dwc2.c b/drivers/usb/dwc2/dwc2.c
new file mode 100644
index 0000000000..a35fd0e717
--- /dev/null
+++ b/drivers/usb/dwc2/dwc2.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org>
+ * Copyright (C) 2014 Marek Vasut <marex@denx.de>
+ *
+ * Copied from u-Boot
+ */
+#include <common.h>
+#include <of.h>
+#include <dma.h>
+#include <init.h>
+#include <errno.h>
+#include <driver.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <of_device.h>
+
+#include "dwc2.h"
+
+static void dwc2_set_stm32mp15_fsotg_params(struct dwc2 *dwc2)
+{
+ struct dwc2_core_params *p = &dwc2->params;
+
+ p->otg_cap &= ~(DWC2_CAP_PARAM_HNP_SRP_CAPABLE
+ | DWC2_CAP_PARAM_SRP_ONLY_CAPABLE);
+ p->otg_cap |= DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
+ p->speed = DWC2_SPEED_PARAM_FULL;
+ p->host_rx_fifo_size = 128;
+ p->host_nperio_tx_fifo_size = 96;
+ p->host_perio_tx_fifo_size = 96;
+ p->max_packet_count = 256;
+ p->phy_type = DWC2_PHY_TYPE_PARAM_FS;
+ p->i2c_enable = false;
+ p->activate_stm_fs_transceiver = true;
+ p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
+ p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+ p->host_support_fs_ls_low_power = true;
+ p->host_ls_low_power_phy_clk = true;
+}
+
+static void dwc2_set_stm32mp15_hsotg_params(struct dwc2 *dwc2)
+{
+ struct dwc2_core_params *p = &dwc2->params;
+
+ p->otg_cap &= ~(DWC2_CAP_PARAM_HNP_SRP_CAPABLE
+ | DWC2_CAP_PARAM_SRP_ONLY_CAPABLE);
+ p->otg_cap |= DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
+ p->host_rx_fifo_size = 440;
+ p->host_nperio_tx_fifo_size = 256;
+ p->host_perio_tx_fifo_size = 256;
+ p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
+ p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+ p->lpm = false;
+ p->lpm_clock_gating = false;
+ p->besl = false;
+ p->hird_threshold_en = false;
+}
+
+static int dwc2_set_mode(void *ctx, enum usb_dr_mode mode)
+{
+ struct dwc2 *dwc2 = ctx;
+ int ret = -ENODEV;
+ int oldmode = dwc2->dr_mode;
+
+ dwc2->dr_mode = mode;
+
+ if (mode == USB_DR_MODE_HOST || mode == USB_DR_MODE_OTG) {
+ if (IS_ENABLED(CONFIG_USB_DWC2_HOST))
+ ret = dwc2_register_host(dwc2);
+ else
+ dwc2_err(dwc2, "Host support not available\n");
+ }
+ if (mode == USB_DR_MODE_PERIPHERAL || mode == USB_DR_MODE_OTG) {
+ if (IS_ENABLED(CONFIG_USB_DWC2_GADGET))
+ ret = dwc2_gadget_init(dwc2);
+ else
+ dwc2_err(dwc2, "Peripheral support not available\n");
+ }
+
+ if (ret)
+ dwc2->dr_mode = oldmode;
+
+ return ret;
+}
+
+typedef void (*set_params_cb)(struct dwc2 *dwc2);
+
+static int dwc2_probe(struct device *dev)
+{
+ struct resource *iores;
+ struct dwc2 *dwc2;
+ set_params_cb set_params;
+ int ret;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+ dwc2 = xzalloc(sizeof(*dwc2));
+ dwc2->regs = IOMEM(iores->start);
+ dwc2->dev = dev;
+
+ dwc2->clk = clk_get(dev, "otg");
+ if (IS_ERR(dwc2->clk)) {
+ ret = PTR_ERR(dwc2->clk);
+ dev_err(dev, "Failed to get USB clock %d\n", ret);
+ goto release_region;
+ }
+
+ ret = clk_enable(dwc2->clk);
+ if (ret)
+ goto clk_put;
+
+ ret = device_reset_us(dev, 2);
+ if (ret)
+ goto clk_disable;
+
+ dwc2->phy = phy_optional_get(dev, "usb2-phy");
+ if (IS_ERR(dwc2->phy)) {
+ ret = PTR_ERR(dwc2->phy);
+ goto clk_disable;
+ }
+
+ ret = phy_power_on(dwc2->phy);
+ if (ret)
+ goto clk_disable;
+
+ ret = phy_init(dwc2->phy);
+ if (ret)
+ goto phy_power_off;
+
+ ret = dwc2_check_core_version(dwc2);
+ if (ret)
+ goto error;
+
+ /*
+ * Reset before dwc2_get_hwparams() then it could get power-on real
+ * reset value form registers.
+ */
+ ret = dwc2_core_reset(dwc2);
+ if (ret)
+ goto error;
+
+ /* Detect config values from hardware */
+ dwc2_get_hwparams(dwc2);
+
+ ret = dwc2_get_dr_mode(dwc2);
+ if (ret)
+ goto error;
+
+ dwc2_set_default_params(dwc2);
+ dwc2_get_device_properties(dwc2);
+
+ set_params = of_device_get_match_data(dev);
+ if (set_params)
+ set_params(dwc2);
+
+ dma_set_mask(dev, DMA_BIT_MASK(32));
+ dev->priv = dwc2;
+
+ if (dwc2->dr_mode == USB_DR_MODE_OTG)
+ ret = usb_register_otg_device(dwc2->dev, dwc2_set_mode, dwc2);
+ else
+ ret = dwc2_set_mode(dwc2, dwc2->dr_mode);
+
+ if (ret)
+ goto error;
+
+ return 0;
+error:
+ phy_exit(dwc2->phy);
+phy_power_off:
+ phy_power_off(dwc2->phy);
+clk_disable:
+ clk_disable(dwc2->clk);
+clk_put:
+ clk_put(dwc2->clk);
+release_region:
+ release_region(iores);
+
+ free(dwc2);
+
+ return ret;
+}
+
+static void dwc2_remove(struct device *dev)
+{
+ struct dwc2 *dwc2 = dev->priv;
+
+ dwc2_host_uninit(dwc2);
+ dwc2_gadget_uninit(dwc2);
+
+ phy_exit(dwc2->phy);
+ phy_power_off(dwc2->phy);
+}
+
+static const struct of_device_id dwc2_platform_dt_ids[] = {
+ { .compatible = "brcm,bcm2835-usb", },
+ { .compatible = "brcm,bcm2708-usb", },
+ { .compatible = "snps,dwc2", },
+ { .compatible = "st,stm32mp15-fsotg",
+ .data = dwc2_set_stm32mp15_fsotg_params },
+ { .compatible = "st,stm32mp15-hsotg",
+ .data = dwc2_set_stm32mp15_hsotg_params },
+ { }
+};
+MODULE_DEVICE_TABLE(of, dwc2_platform_dt_ids);
+
+static struct driver dwc2_driver = {
+ .name = "dwc2",
+ .probe = dwc2_probe,
+ .remove = dwc2_remove,
+ .of_compatible = DRV_OF_COMPAT(dwc2_platform_dt_ids),
+};
+device_platform_driver(dwc2_driver);