aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configs/uniphier_v7_defconfig1
-rw-r--r--configs/uniphier_v8_defconfig1
-rw-r--r--drivers/clk/uniphier/clk-uniphier-sys.c5
-rw-r--r--drivers/phy/socionext/Kconfig8
-rw-r--r--drivers/phy/socionext/Makefile1
-rw-r--r--drivers/phy/socionext/phy-uniphier-usb3.c168
-rw-r--r--drivers/reset/reset-uniphier.c78
-rw-r--r--drivers/usb/dwc3/Kconfig4
-rw-r--r--drivers/usb/dwc3/dwc3-generic.c132
-rw-r--r--drivers/usb/dwc3/dwc3-generic.h33
-rw-r--r--drivers/usb/dwc3/dwc3-uniphier.c116
11 files changed, 453 insertions, 94 deletions
diff --git a/configs/uniphier_v7_defconfig b/configs/uniphier_v7_defconfig
index d626968..03feb04 100644
--- a/configs/uniphier_v7_defconfig
+++ b/configs/uniphier_v7_defconfig
@@ -82,7 +82,6 @@ CONFIG_DM_SPI=y
CONFIG_UNIPHIER_SPI=y
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
-CONFIG_USB_XHCI_DWC3=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_GENERIC=y
CONFIG_USB_DWC3=y
diff --git a/configs/uniphier_v8_defconfig b/configs/uniphier_v8_defconfig
index 6a0e266..ed58b57 100644
--- a/configs/uniphier_v8_defconfig
+++ b/configs/uniphier_v8_defconfig
@@ -71,7 +71,6 @@ CONFIG_SYSRESET=y
CONFIG_SYSRESET_PSCI=y
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
-CONFIG_USB_XHCI_DWC3=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_GENERIC=y
CONFIG_USB_DWC3=y
diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c
index ff5d364..3b8595f 100644
--- a/drivers/clk/uniphier/clk-uniphier-sys.c
+++ b/drivers/clk/uniphier/clk-uniphier-sys.c
@@ -28,7 +28,10 @@ const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[] = {
UNIPHIER_CLK_GATE_SIMPLE(14, 0x2104, 16), /* usb30 (Pro4, Pro5, PXs2) */
UNIPHIER_CLK_GATE_SIMPLE(15, 0x2104, 17), /* usb31 (Pro4, Pro5, PXs2) */
UNIPHIER_CLK_GATE_SIMPLE(16, 0x2104, 19), /* usb30-phy (PXs2) */
+ UNIPHIER_CLK_RATE(17, 25000000), /* usb30-phy2 (PXs2) */
+ UNIPHIER_CLK_RATE(18, 25000000), /* usb30-phy3 (PXs2) */
UNIPHIER_CLK_GATE_SIMPLE(20, 0x2104, 20), /* usb31-phy (PXs2) */
+ UNIPHIER_CLK_RATE(21, 25000000), /* usb31-phy2 (PXs2) */
UNIPHIER_CLK_GATE_SIMPLE(24, 0x2108, 2), /* pcie (Pro5) */
{ /* sentinel */ }
#endif
@@ -44,6 +47,8 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = {
UNIPHIER_CLK_GATE_SIMPLE(14, 0x210c, 14), /* usb30 (LD20) */
UNIPHIER_CLK_GATE_SIMPLE(16, 0x210c, 12), /* usb30-phy0 (LD20) */
UNIPHIER_CLK_GATE_SIMPLE(17, 0x210c, 13), /* usb30-phy1 (LD20) */
+ UNIPHIER_CLK_RATE(18, 25000000), /* usb30-phy2 (LD20) */
+ UNIPHIER_CLK_RATE(19, 25000000), /* usb30-phy3 (LD20) */
UNIPHIER_CLK_GATE_SIMPLE(24, 0x210c, 4), /* pcie */
{ /* sentinel */ }
#endif
diff --git a/drivers/phy/socionext/Kconfig b/drivers/phy/socionext/Kconfig
index bcd579e..de87d5b0 100644
--- a/drivers/phy/socionext/Kconfig
+++ b/drivers/phy/socionext/Kconfig
@@ -10,3 +10,11 @@ config PHY_UNIPHIER_PCIE
help
Enable this to support PHY implemented in PCIe controller
on UniPhier SoCs.
+
+config PHY_UNIPHIER_USB3
+ bool "UniPhier USB3 PHY driver"
+ depends on PHY && ARCH_UNIPHIER
+ imply REGMAP
+ help
+ Enable this to support PHY implemented in USB3 controller
+ on UniPhier SoCs.
diff --git a/drivers/phy/socionext/Makefile b/drivers/phy/socionext/Makefile
index 5484360..94d3aa6 100644
--- a/drivers/phy/socionext/Makefile
+++ b/drivers/phy/socionext/Makefile
@@ -4,3 +4,4 @@
#
obj-$(CONFIG_PHY_UNIPHIER_PCIE) += phy-uniphier-pcie.o
+obj-$(CONFIG_PHY_UNIPHIER_USB3) += phy-uniphier-usb3.o
diff --git a/drivers/phy/socionext/phy-uniphier-usb3.c b/drivers/phy/socionext/phy-uniphier-usb3.c
new file mode 100644
index 0000000..1d65b0b
--- /dev/null
+++ b/drivers/phy/socionext/phy-uniphier-usb3.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * phy_uniphier_usb3.c - Socionext UniPhier Usb3 PHY driver
+ * Copyright 2019-2023 Socionext, Inc.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <generic-phy.h>
+
+#include <clk.h>
+#include <reset.h>
+
+struct uniphier_usb3phy_priv {
+ struct clk *clk_link, *clk_phy, *clk_parent, *clk_phyext;
+ struct reset_ctl *rst_link, *rst_phy, *rst_parent;
+};
+
+static int uniphier_usb3phy_init(struct phy *phy)
+{
+ struct uniphier_usb3phy_priv *priv = dev_get_priv(phy->dev);
+ int ret;
+
+ ret = clk_enable(priv->clk_phy);
+ if (ret)
+ return ret;
+
+ ret = reset_deassert(priv->rst_phy);
+ if (ret)
+ goto out_clk;
+
+ if (priv->clk_phyext) {
+ ret = clk_enable(priv->clk_phyext);
+ if (ret)
+ goto out_rst;
+ }
+
+ return 0;
+
+out_rst:
+ reset_assert(priv->rst_phy);
+out_clk:
+ clk_disable(priv->clk_phy);
+
+ return ret;
+}
+
+static int uniphier_usb3phy_exit(struct phy *phy)
+{
+ struct uniphier_usb3phy_priv *priv = dev_get_priv(phy->dev);
+
+ if (priv->clk_phyext)
+ clk_disable(priv->clk_phyext);
+
+ reset_assert(priv->rst_phy);
+ clk_disable(priv->clk_phy);
+
+ return 0;
+}
+
+static int uniphier_usb3phy_probe(struct udevice *dev)
+{
+ struct uniphier_usb3phy_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ priv->clk_link = devm_clk_get(dev, "link");
+ if (IS_ERR(priv->clk_link)) {
+ printf("Failed to get link clock\n");
+ return PTR_ERR(priv->clk_link);
+ }
+
+ priv->clk_phy = devm_clk_get(dev, "phy");
+ if (IS_ERR(priv->clk_link)) {
+ printf("Failed to get phy clock\n");
+ return PTR_ERR(priv->clk_link);
+ }
+
+ priv->clk_parent = devm_clk_get_optional(dev, "gio");
+ if (IS_ERR(priv->clk_parent)) {
+ printf("Failed to get parent clock\n");
+ return PTR_ERR(priv->clk_parent);
+ }
+
+ priv->clk_phyext = devm_clk_get_optional(dev, "phy-ext");
+ if (IS_ERR(priv->clk_phyext)) {
+ printf("Failed to get external phy clock\n");
+ return PTR_ERR(priv->clk_phyext);
+ }
+
+ priv->rst_link = devm_reset_control_get(dev, "link");
+ if (IS_ERR(priv->rst_link)) {
+ printf("Failed to get link reset\n");
+ return PTR_ERR(priv->rst_link);
+ }
+
+ priv->rst_phy = devm_reset_control_get(dev, "phy");
+ if (IS_ERR(priv->rst_phy)) {
+ printf("Failed to get phy reset\n");
+ return PTR_ERR(priv->rst_phy);
+ }
+
+ priv->rst_parent = devm_reset_control_get_optional(dev, "gio");
+ if (IS_ERR(priv->rst_parent)) {
+ printf("Failed to get parent reset\n");
+ return PTR_ERR(priv->rst_parent);
+ }
+
+ if (priv->clk_parent) {
+ ret = clk_enable(priv->clk_parent);
+ if (ret)
+ return ret;
+ }
+ if (priv->rst_parent) {
+ ret = reset_deassert(priv->rst_parent);
+ if (ret)
+ goto out_clk_parent;
+ }
+
+ ret = clk_enable(priv->clk_link);
+ if (ret)
+ goto out_rst_parent;
+
+ ret = reset_deassert(priv->rst_link);
+ if (ret)
+ goto out_clk;
+
+ return 0;
+
+out_clk:
+ clk_disable(priv->clk_link);
+out_rst_parent:
+ if (priv->rst_parent)
+ reset_assert(priv->rst_parent);
+out_clk_parent:
+ if (priv->clk_parent)
+ clk_disable(priv->clk_parent);
+
+ return ret;
+}
+
+static struct phy_ops uniphier_usb3phy_ops = {
+ .init = uniphier_usb3phy_init,
+ .exit = uniphier_usb3phy_exit,
+};
+
+static const struct udevice_id uniphier_usb3phy_ids[] = {
+ { .compatible = "socionext,uniphier-pro4-usb3-ssphy" },
+ { .compatible = "socionext,uniphier-pro5-usb3-hsphy" },
+ { .compatible = "socionext,uniphier-pro5-usb3-ssphy" },
+ { .compatible = "socionext,uniphier-pxs2-usb3-hsphy" },
+ { .compatible = "socionext,uniphier-pxs2-usb3-ssphy" },
+ { .compatible = "socionext,uniphier-ld20-usb3-hsphy" },
+ { .compatible = "socionext,uniphier-ld20-usb3-ssphy" },
+ { .compatible = "socionext,uniphier-pxs3-usb3-hsphy" },
+ { .compatible = "socionext,uniphier-pxs3-usb3-ssphy" },
+ { .compatible = "socionext,uniphier-nx1-usb3-hsphy" },
+ { .compatible = "socionext,uniphier-nx1-usb3-ssphy" },
+ { }
+};
+
+U_BOOT_DRIVER(uniphier_usb3_phy) = {
+ .name = "uniphier-usb3-phy",
+ .id = UCLASS_PHY,
+ .of_match = uniphier_usb3phy_ids,
+ .ops = &uniphier_usb3phy_ops,
+ .probe = uniphier_usb3phy_probe,
+ .priv_auto = sizeof(struct uniphier_usb3phy_priv),
+};
diff --git a/drivers/reset/reset-uniphier.c b/drivers/reset/reset-uniphier.c
index 7adae51..35e3cce 100644
--- a/drivers/reset/reset-uniphier.c
+++ b/drivers/reset/reset-uniphier.c
@@ -2,6 +2,7 @@
/*
* Copyright (C) 2016 Socionext Inc.
* Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ * Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
*/
#include <common.h>
@@ -9,6 +10,8 @@
#include <log.h>
#include <malloc.h>
#include <reset-uclass.h>
+#include <clk.h>
+#include <reset.h>
#include <dm/device_compat.h>
#include <linux/bitops.h>
#include <linux/io.h>
@@ -178,10 +181,17 @@ static const struct uniphier_reset_data uniphier_pro4_peri_reset_data[] = {
UNIPHIER_RESET_END,
};
+/* Glue reset data */
+static const struct uniphier_reset_data uniphier_pro4_usb3_reset_data[] = {
+ UNIPHIER_RESETX(15, 0, 15)
+};
+
/* core implementaton */
struct uniphier_reset_priv {
void __iomem *base;
const struct uniphier_reset_data *data;
+ struct clk_bulk clks;
+ struct reset_ctl_bulk rsts;
};
static int uniphier_reset_update(struct reset_ctl *reset_ctl, int assert)
@@ -233,10 +243,47 @@ static const struct reset_ops uniphier_reset_ops = {
.rst_deassert = uniphier_reset_deassert,
};
+static int uniphier_reset_rst_init(struct udevice *dev)
+{
+ struct uniphier_reset_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = reset_get_bulk(dev, &priv->rsts);
+ if (ret == -ENOSYS || ret == -ENOENT)
+ return 0;
+ else if (ret)
+ return ret;
+
+ ret = reset_deassert_bulk(&priv->rsts);
+ if (ret)
+ reset_release_bulk(&priv->rsts);
+
+ return ret;
+}
+
+static int uniphier_reset_clk_init(struct udevice *dev)
+{
+ struct uniphier_reset_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;
+}
+
static int uniphier_reset_probe(struct udevice *dev)
{
struct uniphier_reset_priv *priv = dev_get_priv(dev);
fdt_addr_t addr;
+ int ret;
addr = dev_read_addr(dev->parent);
if (addr == FDT_ADDR_T_NONE)
@@ -248,7 +295,11 @@ static int uniphier_reset_probe(struct udevice *dev)
priv->data = (void *)dev_get_driver_data(dev);
- return 0;
+ ret = uniphier_reset_clk_init(dev);
+ if (ret)
+ return ret;
+
+ return uniphier_reset_rst_init(dev);
}
static const struct udevice_id uniphier_reset_match[] = {
@@ -355,6 +406,31 @@ static const struct udevice_id uniphier_reset_match[] = {
.compatible = "socionext,uniphier-pxs3-peri-reset",
.data = (ulong)uniphier_pro4_peri_reset_data,
},
+ /* USB glue reset */
+ {
+ .compatible = "socionext,uniphier-pro4-usb3-reset",
+ .data = (ulong)uniphier_pro4_usb3_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro5-usb3-reset",
+ .data = (ulong)uniphier_pro4_usb3_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs2-usb3-reset",
+ .data = (ulong)uniphier_pro4_usb3_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld20-usb3-reset",
+ .data = (ulong)uniphier_pro4_usb3_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs3-usb3-reset",
+ .data = (ulong)uniphier_pro4_usb3_reset_data,
+ },
+ {
+ .compatible = "socionext,uniphier-nx1-usb3-reset",
+ .data = (ulong)uniphier_pro4_usb3_reset_data,
+ },
{ /* sentinel */ }
};
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index f010291..7ddfa94 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -55,7 +55,9 @@ config USB_DWC3_MESON_GXL
config USB_DWC3_UNIPHIER
bool "DesignWare USB3 Host Support on UniPhier Platforms"
- depends on ARCH_UNIPHIER && USB_XHCI_DWC3
+ depends on ARCH_UNIPHIER && USB_DWC3
+ select USB_DWC3_GENERIC
+ select PHY_UNIPHIER_USB3
help
Support of USB2/3 functionality in Socionext UniPhier platforms.
Say 'Y' here if you have one such device.
diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c
index 7896671..acbf7ac 100644
--- a/drivers/usb/dwc3/dwc3-generic.c
+++ b/drivers/usb/dwc3/dwc3-generic.c
@@ -28,11 +28,7 @@
#include <usb/xhci.h>
#include <asm/gpio.h>
-struct dwc3_glue_data {
- struct clk_bulk clks;
- struct reset_ctl_bulk resets;
- fdt_addr_t regs;
-};
+#include "dwc3-generic.h"
struct dwc3_generic_plat {
fdt_addr_t base;
@@ -68,10 +64,27 @@ static int dwc3_generic_probe(struct udevice *dev,
#if CONFIG_IS_ENABLED(OF_CONTROL)
dwc3_of_parse(dwc3);
+ /*
+ * There are currently four disparate placement possibilities of DWC3
+ * reference clock phandle in SoC DTs:
+ * - in top level glue node, with generic subnode without clock (ZynqMP)
+ * - in top level generic node, with no subnode (i.MX8MQ)
+ * - in generic subnode, with other clock in top level node (i.MX8MP)
+ * - in both top level node and generic subnode (Rockchip)
+ * Cover all the possibilities here by looking into both nodes, start
+ * with the top level node as that seems to be used in majority of DTs
+ * to reference the clock.
+ */
node = dev_ofnode(dev->parent);
index = ofnode_stringlist_search(node, "clock-names", "ref");
if (index < 0)
index = ofnode_stringlist_search(node, "clock-names", "ref_clk");
+ if (index < 0) {
+ node = dev_ofnode(dev);
+ index = ofnode_stringlist_search(node, "clock-names", "ref");
+ if (index < 0)
+ index = ofnode_stringlist_search(node, "clock-names", "ref_clk");
+ }
if (index >= 0)
dwc3->ref_clk = &glue->clks.clks[index];
#endif
@@ -258,11 +271,6 @@ U_BOOT_DRIVER(dwc3_generic_host) = {
};
#endif
-struct dwc3_glue_ops {
- void (*glue_configure)(struct udevice *dev, int index,
- enum usb_dr_mode mode);
-};
-
void dwc3_imx8mp_glue_configure(struct udevice *dev, int index,
enum usb_dr_mode mode)
{
@@ -398,54 +406,74 @@ struct dwc3_glue_ops ti_ops = {
.glue_configure = dwc3_ti_glue_configure,
};
-static int dwc3_glue_bind(struct udevice *parent)
+static int dwc3_glue_bind_common(struct udevice *parent, ofnode node)
{
- ofnode node;
- int ret;
+ const char *name = ofnode_get_name(node);
+ const char *driver = NULL;
enum usb_dr_mode dr_mode;
+ struct udevice *dev;
+ int ret;
- dr_mode = usb_get_dr_mode(dev_ofnode(parent));
-
- ofnode_for_each_subnode(node, dev_ofnode(parent)) {
- const char *name = ofnode_get_name(node);
- struct udevice *dev;
- const char *driver = NULL;
-
- debug("%s: subnode name: %s\n", __func__, name);
+ debug("%s: subnode name: %s\n", __func__, name);
- /* if the parent node doesn't have a mode check the leaf */
- if (!dr_mode)
- dr_mode = usb_get_dr_mode(node);
+ /* if the parent node doesn't have a mode check the leaf */
+ dr_mode = usb_get_dr_mode(dev_ofnode(parent));
+ if (!dr_mode)
+ dr_mode = usb_get_dr_mode(node);
- switch (dr_mode) {
- case USB_DR_MODE_PERIPHERAL:
- case USB_DR_MODE_OTG:
+ switch (dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ case USB_DR_MODE_OTG:
#if CONFIG_IS_ENABLED(DM_USB_GADGET)
- debug("%s: dr_mode: OTG or Peripheral\n", __func__);
- driver = "dwc3-generic-peripheral";
+ debug("%s: dr_mode: OTG or Peripheral\n", __func__);
+ driver = "dwc3-generic-peripheral";
#endif
- break;
+ break;
#if defined(CONFIG_SPL_USB_HOST) || !defined(CONFIG_SPL_BUILD)
- case USB_DR_MODE_HOST:
- debug("%s: dr_mode: HOST\n", __func__);
- driver = "dwc3-generic-host";
- break;
+ case USB_DR_MODE_HOST:
+ debug("%s: dr_mode: HOST\n", __func__);
+ driver = "dwc3-generic-host";
+ break;
#endif
- default:
- debug("%s: unsupported dr_mode\n", __func__);
- return -ENODEV;
- };
+ default:
+ debug("%s: unsupported dr_mode\n", __func__);
+ return -ENODEV;
+ };
- if (!driver)
- continue;
+ if (!driver)
+ return -ENXIO;
+
+ ret = device_bind_driver_to_node(parent, driver, name,
+ node, &dev);
+ if (ret) {
+ debug("%s: not able to bind usb device mode\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
- ret = device_bind_driver_to_node(parent, driver, name,
- node, &dev);
- if (ret) {
- debug("%s: not able to bind usb device mode\n",
- __func__);
+int dwc3_glue_bind(struct udevice *parent)
+{
+ struct dwc3_glue_ops *ops = (struct dwc3_glue_ops *)dev_get_driver_data(parent);
+ ofnode node;
+ int ret;
+
+ if (ops && ops->glue_get_ctrl_dev) {
+ ret = ops->glue_get_ctrl_dev(parent, &node);
+ if (ret)
+ return ret;
+
+ return dwc3_glue_bind_common(parent, node);
+ }
+
+ ofnode_for_each_subnode(node, dev_ofnode(parent)) {
+ ret = dwc3_glue_bind_common(parent, node);
+ if (ret == -ENXIO)
+ continue;
+ if (ret)
return ret;
- }
}
return 0;
@@ -493,7 +521,7 @@ static int dwc3_glue_clk_init(struct udevice *dev,
return 0;
}
-static int dwc3_glue_probe(struct udevice *dev)
+int dwc3_glue_probe(struct udevice *dev)
{
struct dwc3_glue_ops *ops = (struct dwc3_glue_ops *)dev_get_driver_data(dev);
struct dwc3_glue_data *glue = dev_get_plat(dev);
@@ -514,7 +542,7 @@ static int dwc3_glue_probe(struct udevice *dev)
phy.dev = NULL;
}
- glue->regs = dev_read_addr(dev);
+ glue->regs = dev_read_addr_size_index(dev, 0, &glue->size);
ret = dwc3_glue_clk_init(dev, glue);
if (ret)
@@ -534,6 +562,12 @@ static int dwc3_glue_probe(struct udevice *dev)
if (ret)
return ret;
+ if (glue->clks.count == 0) {
+ ret = dwc3_glue_clk_init(child, glue);
+ if (ret)
+ return ret;
+ }
+
if (glue->resets.count == 0) {
ret = dwc3_glue_reset_init(child, glue);
if (ret)
@@ -553,7 +587,7 @@ static int dwc3_glue_probe(struct udevice *dev)
return 0;
}
-static int dwc3_glue_remove(struct udevice *dev)
+int dwc3_glue_remove(struct udevice *dev)
{
struct dwc3_glue_data *glue = dev_get_plat(dev);
diff --git a/drivers/usb/dwc3/dwc3-generic.h b/drivers/usb/dwc3/dwc3-generic.h
new file mode 100644
index 0000000..40902c8
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-generic.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * dwc3-generic.h - Generic DWC3 Glue layer header
+ *
+ * Copyright (C) 2016 - 2018 Xilinx, Inc.
+ * Copyright (C) 2023 Socionext Inc.
+ */
+
+#ifndef __DRIVERS_USB_DWC3_GENERIC_H
+#define __DRIVERS_USB_DWC3_GENERIC_H
+
+#include <clk.h>
+#include <reset.h>
+#include <dwc3-uboot.h>
+
+struct dwc3_glue_data {
+ struct clk_bulk clks;
+ struct reset_ctl_bulk resets;
+ fdt_addr_t regs;
+ fdt_size_t size;
+};
+
+struct dwc3_glue_ops {
+ int (*glue_get_ctrl_dev)(struct udevice *parent, ofnode *node);
+ void (*glue_configure)(struct udevice *dev, int index,
+ enum usb_dr_mode mode);
+};
+
+int dwc3_glue_bind(struct udevice *parent);
+int dwc3_glue_probe(struct udevice *dev);
+int dwc3_glue_remove(struct udevice *dev);
+
+#endif
diff --git a/drivers/usb/dwc3/dwc3-uniphier.c b/drivers/usb/dwc3/dwc3-uniphier.c
index 54b52dc..ab85428 100644
--- a/drivers/usb/dwc3/dwc3-uniphier.c
+++ b/drivers/usb/dwc3/dwc3-uniphier.c
@@ -4,14 +4,17 @@
*
* Copyright (C) 2016-2017 Socionext Inc.
* Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ * Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
*/
#include <dm.h>
-#include <dm/device_compat.h>
+#include <dm/lists.h>
#include <linux/bitops.h>
-#include <linux/errno.h>
-#include <linux/io.h>
-#include <linux/sizes.h>
+#include <linux/usb/gadget.h>
+
+#include "core.h"
+#include "gadget.h"
+#include "dwc3-generic.h"
#define UNIPHIER_PRO4_DWC3_RESET 0x40
#define UNIPHIER_PRO4_DWC3_RESET_XIOMMU BIT(5)
@@ -27,8 +30,11 @@
#define UNIPHIER_PXS2_DWC3_RESET 0x00
#define UNIPHIER_PXS2_DWC3_RESET_XLINK BIT(15)
-static int uniphier_pro4_dwc3_init(void __iomem *regs)
+static void uniphier_pro4_dwc3_init(struct udevice *dev, int index,
+ enum usb_dr_mode mode)
{
+ struct dwc3_glue_data *glue = dev_get_plat(dev);
+ void *regs = map_physmem(glue->regs, glue->size, MAP_NOCACHE);
u32 tmp;
tmp = readl(regs + UNIPHIER_PRO4_DWC3_RESET);
@@ -36,11 +42,14 @@ static int uniphier_pro4_dwc3_init(void __iomem *regs)
tmp |= UNIPHIER_PRO4_DWC3_RESET_XIOMMU | UNIPHIER_PRO4_DWC3_RESET_XLINK;
writel(tmp, regs + UNIPHIER_PRO4_DWC3_RESET);
- return 0;
+ unmap_physmem(regs, MAP_NOCACHE);
}
-static int uniphier_pro5_dwc3_init(void __iomem *regs)
+static void uniphier_pro5_dwc3_init(struct udevice *dev, int index,
+ enum usb_dr_mode mode)
{
+ struct dwc3_glue_data *glue = dev_get_plat(dev);
+ void *regs = map_physmem(glue->regs, glue->size, MAP_NOCACHE);
u32 tmp;
tmp = readl(regs + UNIPHIER_PRO5_DWC3_RESET);
@@ -49,72 +58,97 @@ static int uniphier_pro5_dwc3_init(void __iomem *regs)
tmp |= UNIPHIER_PRO5_DWC3_RESET_XLINK | UNIPHIER_PRO5_DWC3_RESET_XIOMMU;
writel(tmp, regs + UNIPHIER_PRO5_DWC3_RESET);
- return 0;
+ unmap_physmem(regs, MAP_NOCACHE);
}
-static int uniphier_pxs2_dwc3_init(void __iomem *regs)
+static void uniphier_pxs2_dwc3_init(struct udevice *dev, int index,
+ enum usb_dr_mode mode)
{
+ struct dwc3_glue_data *glue = dev_get_plat(dev);
+ void *regs = map_physmem(glue->regs, glue->size, MAP_NOCACHE);
u32 tmp;
tmp = readl(regs + UNIPHIER_PXS2_DWC3_RESET);
tmp |= UNIPHIER_PXS2_DWC3_RESET_XLINK;
writel(tmp, regs + UNIPHIER_PXS2_DWC3_RESET);
- return 0;
+ unmap_physmem(regs, MAP_NOCACHE);
}
-static int uniphier_dwc3_probe(struct udevice *dev)
+static int dwc3_uniphier_glue_get_ctrl_dev(struct udevice *dev, ofnode *node)
{
- fdt_addr_t base;
- void __iomem *regs;
- int (*init)(void __iomem *regs);
- int ret;
+ struct udevice *child;
+ const char *name;
+ ofnode subnode;
+
+ /*
+ * "controller reset" belongs to glue logic, and it should be
+ * accessible in .glue_configure() before access to the controller
+ * begins.
+ */
+ ofnode_for_each_subnode(subnode, dev_ofnode(dev)) {
+ name = ofnode_get_name(subnode);
+ if (!strncmp(name, "reset", 5))
+ device_bind_driver_to_node(dev, "uniphier-reset",
+ name, subnode, &child);
+ }
+
+ /* Get controller node that is placed separately from the glue node */
+ *node = ofnode_by_compatible(dev_ofnode(dev->parent),
+ "socionext,uniphier-dwc3");
- base = dev_read_addr(dev);
- if (base == FDT_ADDR_T_NONE)
- return -EINVAL;
-
- regs = ioremap(base, SZ_32K);
- if (!regs)
- return -ENOMEM;
+ return 0;
+}
- init = (typeof(init))dev_get_driver_data(dev);
- ret = init(regs);
- if (ret)
- dev_err(dev, "failed to init glue layer\n");
+static const struct dwc3_glue_ops uniphier_pro4_dwc3_ops = {
+ .glue_get_ctrl_dev = dwc3_uniphier_glue_get_ctrl_dev,
+ .glue_configure = uniphier_pro4_dwc3_init,
+};
- iounmap(regs);
+static const struct dwc3_glue_ops uniphier_pro5_dwc3_ops = {
+ .glue_get_ctrl_dev = dwc3_uniphier_glue_get_ctrl_dev,
+ .glue_configure = uniphier_pro5_dwc3_init,
+};
- return ret;
-}
+static const struct dwc3_glue_ops uniphier_pxs2_dwc3_ops = {
+ .glue_get_ctrl_dev = dwc3_uniphier_glue_get_ctrl_dev,
+ .glue_configure = uniphier_pxs2_dwc3_init,
+};
static const struct udevice_id uniphier_dwc3_match[] = {
{
- .compatible = "socionext,uniphier-pro4-dwc3",
- .data = (ulong)uniphier_pro4_dwc3_init,
+ .compatible = "socionext,uniphier-pro4-dwc3-glue",
+ .data = (ulong)&uniphier_pro4_dwc3_ops,
+ },
+ {
+ .compatible = "socionext,uniphier-pro5-dwc3-glue",
+ .data = (ulong)&uniphier_pro5_dwc3_ops,
},
{
- .compatible = "socionext,uniphier-pro5-dwc3",
- .data = (ulong)uniphier_pro5_dwc3_init,
+ .compatible = "socionext,uniphier-pxs2-dwc3-glue",
+ .data = (ulong)&uniphier_pxs2_dwc3_ops,
},
{
- .compatible = "socionext,uniphier-pxs2-dwc3",
- .data = (ulong)uniphier_pxs2_dwc3_init,
+ .compatible = "socionext,uniphier-ld20-dwc3-glue",
+ .data = (ulong)&uniphier_pxs2_dwc3_ops,
},
{
- .compatible = "socionext,uniphier-ld20-dwc3",
- .data = (ulong)uniphier_pxs2_dwc3_init,
+ .compatible = "socionext,uniphier-pxs3-dwc3-glue",
+ .data = (ulong)&uniphier_pxs2_dwc3_ops,
},
{
- .compatible = "socionext,uniphier-pxs3-dwc3",
- .data = (ulong)uniphier_pxs2_dwc3_init,
+ .compatible = "socionext,uniphier-nx1-dwc3-glue",
+ .data = (ulong)&uniphier_pxs2_dwc3_ops,
},
{ /* sentinel */ }
};
-U_BOOT_DRIVER(usb_xhci) = {
+U_BOOT_DRIVER(dwc3_uniphier_wrapper) = {
.name = "uniphier-dwc3",
.id = UCLASS_SIMPLE_BUS,
.of_match = uniphier_dwc3_match,
- .probe = uniphier_dwc3_probe,
+ .bind = dwc3_glue_bind,
+ .probe = dwc3_glue_probe,
+ .remove = dwc3_glue_remove,
+ .plat_auto = sizeof(struct dwc3_glue_data),
};