All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH 0/3] ARM: meson: Add G12A USB Support
@ 2019-03-26 15:15 ` Neil Armstrong
  0 siblings, 0 replies; 14+ messages in thread
From: Neil Armstrong @ 2019-03-26 15:15 UTC (permalink / raw)
  To: u-boot

This patchset adds support for USB on Amlogic G12A SoCs.

This patchset is composed with :
- PHY Drivers
- USB Control Glue driver
- G12A board setup for Device mode

Device Tree nodes will be added in a separate patchset when applied
on a tagged Linux tree.

The Amlogic G12A USB Complex is composed of :
- 2 USB Controllers :
 * DWC3 for USB2 and USB3 Host functionality
 * DWC2 for USB2 Peripheral functionality
- 2 USB2 OTG PHYs, only a single one will be routed to either DWC2 to DWC3
- 1 USB3 PHY shared with PCIE funcionnality
- A Glue to control PHY routing, setup and OTG detection

The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
routing of the OTG PHY between the DWC3 and DWC2 controllers, and
setups the on-chip OTG mode selection for this PHY.

This drivers supports the on-probe setup of the OTG mode, and manually
via a setup function in the G12A common board code. 

Neil Armstrong (3):
  usb: dwc3: Add Meson G12A USB Glue
  phy: meson: add Amlogic G12A USB2 and USB3+PCIE PHY drivers
  mach-meson: g12a: add DWC2 peripheral mode support

 arch/arm/include/asm/arch-meson/usb.h |  12 +
 arch/arm/mach-meson/board-g12a.c      | 126 +++++++
 drivers/phy/Kconfig                   |   8 +
 drivers/phy/Makefile                  |   1 +
 drivers/phy/meson-g12a-usb2.c         | 216 ++++++++++++
 drivers/phy/meson-g12a-usb3-pcie.c    | 345 +++++++++++++++++++
 drivers/usb/dwc3/Kconfig              |   8 +
 drivers/usb/dwc3/Makefile             |   1 +
 drivers/usb/dwc3/dwc3-meson-g12a.c    | 456 ++++++++++++++++++++++++++
 9 files changed, 1173 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-meson/usb.h
 create mode 100644 drivers/phy/meson-g12a-usb2.c
 create mode 100644 drivers/phy/meson-g12a-usb3-pcie.c
 create mode 100644 drivers/usb/dwc3/dwc3-meson-g12a.c

-- 
2.21.0

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 0/3] ARM: meson: Add G12A USB Support
@ 2019-03-26 15:15 ` Neil Armstrong
  0 siblings, 0 replies; 14+ messages in thread
From: Neil Armstrong @ 2019-03-26 15:15 UTC (permalink / raw)
  To: marex; +Cc: Neil Armstrong, u-boot-amlogic, u-boot

This patchset adds support for USB on Amlogic G12A SoCs.

This patchset is composed with :
- PHY Drivers
- USB Control Glue driver
- G12A board setup for Device mode

Device Tree nodes will be added in a separate patchset when applied
on a tagged Linux tree.

The Amlogic G12A USB Complex is composed of :
- 2 USB Controllers :
 * DWC3 for USB2 and USB3 Host functionality
 * DWC2 for USB2 Peripheral functionality
- 2 USB2 OTG PHYs, only a single one will be routed to either DWC2 to DWC3
- 1 USB3 PHY shared with PCIE funcionnality
- A Glue to control PHY routing, setup and OTG detection

The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
routing of the OTG PHY between the DWC3 and DWC2 controllers, and
setups the on-chip OTG mode selection for this PHY.

This drivers supports the on-probe setup of the OTG mode, and manually
via a setup function in the G12A common board code. 

Neil Armstrong (3):
  usb: dwc3: Add Meson G12A USB Glue
  phy: meson: add Amlogic G12A USB2 and USB3+PCIE PHY drivers
  mach-meson: g12a: add DWC2 peripheral mode support

 arch/arm/include/asm/arch-meson/usb.h |  12 +
 arch/arm/mach-meson/board-g12a.c      | 126 +++++++
 drivers/phy/Kconfig                   |   8 +
 drivers/phy/Makefile                  |   1 +
 drivers/phy/meson-g12a-usb2.c         | 216 ++++++++++++
 drivers/phy/meson-g12a-usb3-pcie.c    | 345 +++++++++++++++++++
 drivers/usb/dwc3/Kconfig              |   8 +
 drivers/usb/dwc3/Makefile             |   1 +
 drivers/usb/dwc3/dwc3-meson-g12a.c    | 456 ++++++++++++++++++++++++++
 9 files changed, 1173 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-meson/usb.h
 create mode 100644 drivers/phy/meson-g12a-usb2.c
 create mode 100644 drivers/phy/meson-g12a-usb3-pcie.c
 create mode 100644 drivers/usb/dwc3/dwc3-meson-g12a.c

-- 
2.21.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [U-Boot] [PATCH 1/3] usb: dwc3: Add Meson G12A USB Glue
  2019-03-26 15:15 ` Neil Armstrong
@ 2019-03-26 15:15   ` Neil Armstrong
  -1 siblings, 0 replies; 14+ messages in thread
From: Neil Armstrong @ 2019-03-26 15:15 UTC (permalink / raw)
  To: u-boot

Adds support for Amlogic G12A USB Control Glue HW.

The Amlogic G12A SoC Family embeds 2 USB Controllers :
- a DWC3 IP configured as Host for USB2 and USB3
- a DWC2 IP configured as Peripheral USB2 Only

A glue connects these both controllers to 2 USB2 PHYs, and optionnally
to an USB3+PCIE Combo PHY shared with the PCIE controller.

The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
routing of the OTG PHY between the DWC3 and DWC2 controllers, and
setups the on-chip OTG mode selection for this PHY.

This driver sets the OTG capable port as Host mode by default,
the switch to Device mode is to be done in a separate patch.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 drivers/usb/dwc3/Kconfig           |   8 +
 drivers/usb/dwc3/Makefile          |   1 +
 drivers/usb/dwc3/dwc3-meson-g12a.c | 456 +++++++++++++++++++++++++++++
 3 files changed, 465 insertions(+)
 create mode 100644 drivers/usb/dwc3/dwc3-meson-g12a.c

diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index bbd8105c06..25e1a38aee 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -44,6 +44,14 @@ config USB_DWC3_GENERIC
 	  Select this for Xilinx ZynqMP and similar Platforms.
 	  This wrapper supports Host and Peripheral operation modes.
 
+config USB_DWC3_MESON_G12A
+	bool "Amlogic Meson G12A USB wrapper"
+	depends on DM_USB && USB_DWC3 && ARCH_MESON
+	imply PHY
+	help
+	  Select this for Amlogic Meson G12A Platforms.
+	  This wrapper supports Host and Peripheral operation modes.
+
 config USB_DWC3_UNIPHIER
 	bool "DesignWare USB3 Host Support on UniPhier Platforms"
 	depends on ARCH_UNIPHIER && USB_XHCI_DWC3
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 60b5515a67..0b652a6f36 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -7,6 +7,7 @@ dwc3-y					:= core.o
 obj-$(CONFIG_USB_DWC3_GADGET)		+= gadget.o ep0.o
 
 obj-$(CONFIG_USB_DWC3_OMAP)		+= dwc3-omap.o
+obj-$(CONFIG_USB_DWC3_MESON_G12A)	+= dwc3-meson-g12a.o
 obj-$(CONFIG_USB_DWC3_GENERIC)		+= dwc3-generic.o
 obj-$(CONFIG_USB_DWC3_UNIPHIER)		+= dwc3-uniphier.o
 obj-$(CONFIG_USB_DWC3_PHY_OMAP)		+= ti_usb_phy.o
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
new file mode 100644
index 0000000000..f59a8c0261
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
@@ -0,0 +1,456 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Amlogic G12A DWC3 Glue layer
+ *
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#include <common.h>
+#include <asm-generic/io.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dwc3-uboot.h>
+#include <generic-phy.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <malloc.h>
+#include <regmap.h>
+#include <usb.h>
+#include "core.h"
+#include "gadget.h"
+#include <reset.h>
+#include <clk.h>
+#include <power/regulator.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/compat.h>
+
+/* USB2 Ports Control Registers */
+
+#define U2P_REG_SIZE						0x20
+
+#define U2P_R0							0x0
+	#define U2P_R0_HOST_DEVICE				BIT(0)
+	#define U2P_R0_POWER_OK					BIT(1)
+	#define U2P_R0_HAST_MODE				BIT(2)
+	#define U2P_R0_POWER_ON_RESET				BIT(3)
+	#define U2P_R0_ID_PULLUP				BIT(4)
+	#define U2P_R0_DRV_VBUS					BIT(5)
+
+#define U2P_R1							0x4
+	#define U2P_R1_PHY_READY				BIT(0)
+	#define U2P_R1_ID_DIG					BIT(1)
+	#define U2P_R1_OTG_SESSION_VALID			BIT(2)
+	#define U2P_R1_VBUS_VALID				BIT(3)
+
+/* USB Glue Control Registers */
+
+#define USB_R0							0x80
+	#define USB_R0_P30_LANE0_TX2RX_LOOPBACK			BIT(17)
+	#define USB_R0_P30_LANE0_EXT_PCLK_REQ			BIT(18)
+	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK		GENMASK(28, 19)
+	#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK		GENMASK(30, 29)
+	#define USB_R0_U2D_ACT					BIT(31)
+
+#define USB_R1							0x84
+	#define USB_R1_U3H_BIGENDIAN_GS				BIT(0)
+	#define USB_R1_U3H_PME_ENABLE				BIT(1)
+	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK		GENMASK(4, 2)
+	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK		GENMASK(9, 7)
+	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK		GENMASK(13, 12)
+	#define USB_R1_U3H_HOST_U3_PORT_DISABLE			BIT(16)
+	#define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT	BIT(17)
+	#define USB_R1_U3H_HOST_MSI_ENABLE			BIT(18)
+	#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK			GENMASK(24, 19)
+	#define USB_R1_P30_PCS_TX_SWING_FULL_MASK		GENMASK(31, 25)
+
+#define USB_R2							0x88
+	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK		GENMASK(25, 20)
+	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK		GENMASK(31, 26)
+
+#define USB_R3							0x8c
+	#define USB_R3_P30_SSC_ENABLE				BIT(0)
+	#define USB_R3_P30_SSC_RANGE_MASK			GENMASK(3, 1)
+	#define USB_R3_P30_SSC_REF_CLK_SEL_MASK			GENMASK(12, 4)
+	#define USB_R3_P30_REF_SSP_EN				BIT(13)
+
+#define USB_R4							0x90
+	#define USB_R4_P21_PORT_RESET_0				BIT(0)
+	#define USB_R4_P21_SLEEP_M0				BIT(1)
+	#define USB_R4_MEM_PD_MASK				GENMASK(3, 2)
+	#define USB_R4_P21_ONLY					BIT(4)
+
+#define USB_R5							0x94
+	#define USB_R5_ID_DIG_SYNC				BIT(0)
+	#define USB_R5_ID_DIG_REG				BIT(1)
+	#define USB_R5_ID_DIG_CFG_MASK				GENMASK(3, 2)
+	#define USB_R5_ID_DIG_EN_0				BIT(4)
+	#define USB_R5_ID_DIG_EN_1				BIT(5)
+	#define USB_R5_ID_DIG_CURR				BIT(6)
+	#define USB_R5_ID_DIG_IRQ				BIT(7)
+	#define USB_R5_ID_DIG_TH_MASK				GENMASK(15, 8)
+	#define USB_R5_ID_DIG_CNT_MASK				GENMASK(23, 16)
+
+enum {
+	USB2_HOST_PHY = 0,
+	USB2_OTG_PHY,
+	USB3_HOST_PHY,
+	PHY_COUNT,
+};
+
+static const char *phy_names[PHY_COUNT] = {
+	"usb2-phy0", "usb2-phy1", "usb3-phy0",
+};
+
+struct dwc3_meson_g12a {
+	struct udevice		*dev;
+	struct regmap           *regmap;
+	struct clk		clk;
+	struct reset_ctl	reset;
+	struct phy		phys[PHY_COUNT];
+	enum usb_dr_mode	otg_mode;
+	enum usb_dr_mode	otg_phy_mode;
+	unsigned int		usb2_ports;
+	unsigned int		usb3_ports;
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	struct udevice		*vbus_supply;
+#endif
+};
+
+#define U2P_REG_SIZE						0x20
+#define USB_REG_OFFSET						0x80
+
+static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
+					  int i, enum usb_dr_mode mode)
+{
+	switch (mode) {
+	case USB_DR_MODE_HOST:
+	case USB_DR_MODE_OTG:
+	case USB_DR_MODE_UNKNOWN:
+		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+				U2P_R0_HOST_DEVICE,
+				U2P_R0_HOST_DEVICE);
+		break;
+
+	case USB_DR_MODE_PERIPHERAL:
+		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+				U2P_R0_HOST_DEVICE, 0);
+		break;
+	}
+}
+
+static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
+{
+	int i;
+
+	if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
+		priv->otg_phy_mode = USB_DR_MODE_PERIPHERAL;
+	else
+		priv->otg_phy_mode = USB_DR_MODE_HOST;
+
+	for (i = 0 ; i < USB3_HOST_PHY ; ++i) {
+		if (!priv->phys[i].dev)
+			continue;
+
+		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+				   U2P_R0_POWER_ON_RESET,
+				   U2P_R0_POWER_ON_RESET);
+
+		if (i == USB2_OTG_PHY) {
+			regmap_update_bits(priv->regmap,
+					   U2P_R0 + (U2P_REG_SIZE * i),
+					   U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
+					   U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
+
+			dwc3_meson_g12a_usb2_set_mode(priv, i,
+						      priv->otg_phy_mode);
+		} else
+			dwc3_meson_g12a_usb2_set_mode(priv, i,
+						      USB_DR_MODE_HOST);
+
+		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+				   U2P_R0_POWER_ON_RESET, 0);
+	}
+
+	return 0;
+}
+
+static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
+{
+	regmap_update_bits(priv->regmap, USB_R3,
+			USB_R3_P30_SSC_RANGE_MASK |
+			USB_R3_P30_REF_SSP_EN,
+			USB_R3_P30_SSC_ENABLE |
+			FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) |
+			USB_R3_P30_REF_SSP_EN);
+	udelay(2);
+
+	regmap_update_bits(priv->regmap, USB_R2,
+			USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
+			FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
+
+	regmap_update_bits(priv->regmap, USB_R2,
+			USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
+			FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
+
+	udelay(2);
+
+	regmap_update_bits(priv->regmap, USB_R1,
+			USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
+			USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
+
+	regmap_update_bits(priv->regmap, USB_R1,
+			USB_R1_P30_PCS_TX_SWING_FULL_MASK,
+			FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
+}
+
+static void dwc3_meson_g12a_usb_init_mode(struct dwc3_meson_g12a *priv)
+{
+	if (priv->otg_phy_mode == USB_DR_MODE_PERIPHERAL) {
+		regmap_update_bits(priv->regmap, USB_R0,
+				USB_R0_U2D_ACT, USB_R0_U2D_ACT);
+		regmap_update_bits(priv->regmap, USB_R0,
+				USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
+		regmap_update_bits(priv->regmap, USB_R4,
+				USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
+	} else {
+		regmap_update_bits(priv->regmap, USB_R0,
+				USB_R0_U2D_ACT, 0);
+		regmap_update_bits(priv->regmap, USB_R4,
+				USB_R4_P21_SLEEP_M0, 0);
+	}
+}
+
+static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
+{
+	int ret;
+
+	ret = dwc3_meson_g12a_usb2_init(priv);
+	if (ret)
+		return ret;
+
+	regmap_update_bits(priv->regmap, USB_R1,
+			USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
+			FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
+
+	regmap_update_bits(priv->regmap, USB_R5,
+			USB_R5_ID_DIG_EN_0,
+			USB_R5_ID_DIG_EN_0);
+	regmap_update_bits(priv->regmap, USB_R5,
+			USB_R5_ID_DIG_EN_1,
+			USB_R5_ID_DIG_EN_1);
+	regmap_update_bits(priv->regmap, USB_R5,
+			USB_R5_ID_DIG_TH_MASK,
+			FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
+
+	/* If we have an actual SuperSpeed port, initialize it */
+	if (priv->usb3_ports)
+		dwc3_meson_g12a_usb3_init(priv);
+
+	dwc3_meson_g12a_usb_init_mode(priv);
+
+	return 0;
+}
+
+int dwc3_meson_g12a_force_mode(struct udevice *dev, enum usb_dr_mode mode)
+{
+	struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
+
+	if (!priv)
+		return -EINVAL;
+
+	if (mode != USB_DR_MODE_HOST && mode != USB_DR_MODE_PERIPHERAL)
+		return -EINVAL;
+
+	if (!priv->phys[USB2_OTG_PHY].dev)
+		return -EINVAL;
+
+	if (mode == priv->otg_mode)
+		return 0;
+
+	if (mode == USB_DR_MODE_HOST)
+		debug("%s: switching to Host Mode\n", __func__);
+	else
+		debug("%s: switching to Device Mode\n", __func__);
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	if (priv->vbus_supply) {
+		int ret = regulator_set_enable(priv->vbus_supply,
+					(mode == USB_DR_MODE_PERIPHERAL));
+		if (ret)
+			return ret;
+	}
+#endif
+	priv->otg_phy_mode = mode;
+
+	dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
+
+	dwc3_meson_g12a_usb_init_mode(priv);
+
+	return 0;
+}
+
+static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
+{
+	int i, ret;
+
+	for (i = 0 ; i < PHY_COUNT ; ++i) {
+		ret = generic_phy_get_by_name(priv->dev, phy_names[i],
+					      &priv->phys[i]);
+		if (ret == -ENOENT)
+			continue;
+
+		if (ret)
+			return ret;
+
+		if (i == USB3_HOST_PHY)
+			priv->usb3_ports++;
+		else
+			priv->usb2_ports++;
+	}
+
+	printf("%s: usb2 ports: %d\n", __func__, priv->usb2_ports);
+	printf("%s: usb3 ports: %d\n", __func__, priv->usb3_ports);
+
+	return 0;
+}
+
+static int dwc3_meson_g12a_reset_init(struct dwc3_meson_g12a *priv)
+{
+	int ret;
+
+	ret = reset_get_by_index(priv->dev, 0, &priv->reset);
+	if (ret)
+		return ret;
+
+	ret = reset_assert(&priv->reset);
+	udelay(1);
+	ret |= reset_deassert(&priv->reset);
+	if (ret) {
+		reset_free(&priv->reset);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dwc3_meson_g12a_clk_init(struct dwc3_meson_g12a *priv)
+{
+	int ret;
+
+	ret = clk_get_by_index(priv->dev, 0, &priv->clk);
+	if (ret)
+		return ret;
+
+#if CONFIG_IS_ENABLED(CLK)
+	ret = clk_enable(&priv->clk);
+	if (ret) {
+		clk_free(&priv->clk);
+		return ret;
+	}
+#endif
+
+	return 0;
+}
+
+static int dwc3_meson_g12a_probe(struct udevice *dev)
+{
+	struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
+	int ret, i;
+
+	priv->dev = dev;
+
+	ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
+	if (ret)
+		return ret;
+
+	ret = dwc3_meson_g12a_clk_init(priv);
+	if (ret)
+		return ret;
+
+	ret = dwc3_meson_g12a_reset_init(priv);
+	if (ret)
+		return ret;
+
+	ret = dwc3_meson_g12a_get_phys(priv);
+	if (ret)
+		return ret;
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	ret = device_get_supply_regulator(dev, "vbus-supply",
+					  &priv->vbus_supply);
+	if (ret && ret != -ENOENT) {
+		pr_err("Failed to get PHY regulator\n");
+		return ret;
+	}
+
+	if (priv->vbus_supply) {
+		ret = regulator_set_enable(priv->vbus_supply, true);
+		if (ret)
+			return ret;
+	}
+#endif
+
+	priv->otg_mode = usb_get_dr_mode(dev_of_offset(dev));
+
+	ret = dwc3_meson_g12a_usb_init(priv);
+	if (ret)
+		return ret;
+
+	for (i = 0 ; i < PHY_COUNT ; ++i) {
+		if (!priv->phys[i].dev)
+			continue;
+
+		ret = generic_phy_init(&priv->phys[i]);
+		if (ret)
+			goto err_phy_init;
+	}
+
+	return 0;
+
+err_phy_init:
+	for (i = 0 ; i < PHY_COUNT ; ++i) {
+		if (!priv->phys[i].dev)
+			continue;
+
+		 generic_phy_exit(&priv->phys[i]);
+	}
+
+	return ret;
+}
+
+static int dwc3_meson_g12a_remove(struct udevice *dev)
+{
+	struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
+	int i;
+
+	reset_release_all(&priv->reset, 1);
+
+	clk_release_all(&priv->clk, 1);
+
+	for (i = 0 ; i < PHY_COUNT ; ++i) {
+		if (!priv->phys[i].dev)
+			continue;
+
+		 generic_phy_exit(&priv->phys[i]);
+	}
+
+	return dm_scan_fdt_dev(dev);
+}
+
+static const struct udevice_id dwc3_meson_g12a_ids[] = {
+	{ .compatible = "amlogic,meson-g12a-usb-ctrl" },
+	{ }
+};
+
+U_BOOT_DRIVER(dwc3_generic_wrapper) = {
+	.name	= "dwc3-meson-g12a",
+	.id	= UCLASS_SIMPLE_BUS,
+	.of_match = dwc3_meson_g12a_ids,
+	.probe = dwc3_meson_g12a_probe,
+	.remove = dwc3_meson_g12a_remove,
+	.platdata_auto_alloc_size = sizeof(struct dwc3_meson_g12a),
+
+};
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 1/3] usb: dwc3: Add Meson G12A USB Glue
@ 2019-03-26 15:15   ` Neil Armstrong
  0 siblings, 0 replies; 14+ messages in thread
From: Neil Armstrong @ 2019-03-26 15:15 UTC (permalink / raw)
  To: marex; +Cc: Neil Armstrong, u-boot-amlogic, u-boot

Adds support for Amlogic G12A USB Control Glue HW.

The Amlogic G12A SoC Family embeds 2 USB Controllers :
- a DWC3 IP configured as Host for USB2 and USB3
- a DWC2 IP configured as Peripheral USB2 Only

A glue connects these both controllers to 2 USB2 PHYs, and optionnally
to an USB3+PCIE Combo PHY shared with the PCIE controller.

The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
routing of the OTG PHY between the DWC3 and DWC2 controllers, and
setups the on-chip OTG mode selection for this PHY.

This driver sets the OTG capable port as Host mode by default,
the switch to Device mode is to be done in a separate patch.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 drivers/usb/dwc3/Kconfig           |   8 +
 drivers/usb/dwc3/Makefile          |   1 +
 drivers/usb/dwc3/dwc3-meson-g12a.c | 456 +++++++++++++++++++++++++++++
 3 files changed, 465 insertions(+)
 create mode 100644 drivers/usb/dwc3/dwc3-meson-g12a.c

diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index bbd8105c06..25e1a38aee 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -44,6 +44,14 @@ config USB_DWC3_GENERIC
 	  Select this for Xilinx ZynqMP and similar Platforms.
 	  This wrapper supports Host and Peripheral operation modes.
 
+config USB_DWC3_MESON_G12A
+	bool "Amlogic Meson G12A USB wrapper"
+	depends on DM_USB && USB_DWC3 && ARCH_MESON
+	imply PHY
+	help
+	  Select this for Amlogic Meson G12A Platforms.
+	  This wrapper supports Host and Peripheral operation modes.
+
 config USB_DWC3_UNIPHIER
 	bool "DesignWare USB3 Host Support on UniPhier Platforms"
 	depends on ARCH_UNIPHIER && USB_XHCI_DWC3
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 60b5515a67..0b652a6f36 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -7,6 +7,7 @@ dwc3-y					:= core.o
 obj-$(CONFIG_USB_DWC3_GADGET)		+= gadget.o ep0.o
 
 obj-$(CONFIG_USB_DWC3_OMAP)		+= dwc3-omap.o
+obj-$(CONFIG_USB_DWC3_MESON_G12A)	+= dwc3-meson-g12a.o
 obj-$(CONFIG_USB_DWC3_GENERIC)		+= dwc3-generic.o
 obj-$(CONFIG_USB_DWC3_UNIPHIER)		+= dwc3-uniphier.o
 obj-$(CONFIG_USB_DWC3_PHY_OMAP)		+= ti_usb_phy.o
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
new file mode 100644
index 0000000000..f59a8c0261
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
@@ -0,0 +1,456 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Amlogic G12A DWC3 Glue layer
+ *
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#include <common.h>
+#include <asm-generic/io.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dwc3-uboot.h>
+#include <generic-phy.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <malloc.h>
+#include <regmap.h>
+#include <usb.h>
+#include "core.h"
+#include "gadget.h"
+#include <reset.h>
+#include <clk.h>
+#include <power/regulator.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/compat.h>
+
+/* USB2 Ports Control Registers */
+
+#define U2P_REG_SIZE						0x20
+
+#define U2P_R0							0x0
+	#define U2P_R0_HOST_DEVICE				BIT(0)
+	#define U2P_R0_POWER_OK					BIT(1)
+	#define U2P_R0_HAST_MODE				BIT(2)
+	#define U2P_R0_POWER_ON_RESET				BIT(3)
+	#define U2P_R0_ID_PULLUP				BIT(4)
+	#define U2P_R0_DRV_VBUS					BIT(5)
+
+#define U2P_R1							0x4
+	#define U2P_R1_PHY_READY				BIT(0)
+	#define U2P_R1_ID_DIG					BIT(1)
+	#define U2P_R1_OTG_SESSION_VALID			BIT(2)
+	#define U2P_R1_VBUS_VALID				BIT(3)
+
+/* USB Glue Control Registers */
+
+#define USB_R0							0x80
+	#define USB_R0_P30_LANE0_TX2RX_LOOPBACK			BIT(17)
+	#define USB_R0_P30_LANE0_EXT_PCLK_REQ			BIT(18)
+	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK		GENMASK(28, 19)
+	#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK		GENMASK(30, 29)
+	#define USB_R0_U2D_ACT					BIT(31)
+
+#define USB_R1							0x84
+	#define USB_R1_U3H_BIGENDIAN_GS				BIT(0)
+	#define USB_R1_U3H_PME_ENABLE				BIT(1)
+	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK		GENMASK(4, 2)
+	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK		GENMASK(9, 7)
+	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK		GENMASK(13, 12)
+	#define USB_R1_U3H_HOST_U3_PORT_DISABLE			BIT(16)
+	#define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT	BIT(17)
+	#define USB_R1_U3H_HOST_MSI_ENABLE			BIT(18)
+	#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK			GENMASK(24, 19)
+	#define USB_R1_P30_PCS_TX_SWING_FULL_MASK		GENMASK(31, 25)
+
+#define USB_R2							0x88
+	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK		GENMASK(25, 20)
+	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK		GENMASK(31, 26)
+
+#define USB_R3							0x8c
+	#define USB_R3_P30_SSC_ENABLE				BIT(0)
+	#define USB_R3_P30_SSC_RANGE_MASK			GENMASK(3, 1)
+	#define USB_R3_P30_SSC_REF_CLK_SEL_MASK			GENMASK(12, 4)
+	#define USB_R3_P30_REF_SSP_EN				BIT(13)
+
+#define USB_R4							0x90
+	#define USB_R4_P21_PORT_RESET_0				BIT(0)
+	#define USB_R4_P21_SLEEP_M0				BIT(1)
+	#define USB_R4_MEM_PD_MASK				GENMASK(3, 2)
+	#define USB_R4_P21_ONLY					BIT(4)
+
+#define USB_R5							0x94
+	#define USB_R5_ID_DIG_SYNC				BIT(0)
+	#define USB_R5_ID_DIG_REG				BIT(1)
+	#define USB_R5_ID_DIG_CFG_MASK				GENMASK(3, 2)
+	#define USB_R5_ID_DIG_EN_0				BIT(4)
+	#define USB_R5_ID_DIG_EN_1				BIT(5)
+	#define USB_R5_ID_DIG_CURR				BIT(6)
+	#define USB_R5_ID_DIG_IRQ				BIT(7)
+	#define USB_R5_ID_DIG_TH_MASK				GENMASK(15, 8)
+	#define USB_R5_ID_DIG_CNT_MASK				GENMASK(23, 16)
+
+enum {
+	USB2_HOST_PHY = 0,
+	USB2_OTG_PHY,
+	USB3_HOST_PHY,
+	PHY_COUNT,
+};
+
+static const char *phy_names[PHY_COUNT] = {
+	"usb2-phy0", "usb2-phy1", "usb3-phy0",
+};
+
+struct dwc3_meson_g12a {
+	struct udevice		*dev;
+	struct regmap           *regmap;
+	struct clk		clk;
+	struct reset_ctl	reset;
+	struct phy		phys[PHY_COUNT];
+	enum usb_dr_mode	otg_mode;
+	enum usb_dr_mode	otg_phy_mode;
+	unsigned int		usb2_ports;
+	unsigned int		usb3_ports;
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	struct udevice		*vbus_supply;
+#endif
+};
+
+#define U2P_REG_SIZE						0x20
+#define USB_REG_OFFSET						0x80
+
+static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
+					  int i, enum usb_dr_mode mode)
+{
+	switch (mode) {
+	case USB_DR_MODE_HOST:
+	case USB_DR_MODE_OTG:
+	case USB_DR_MODE_UNKNOWN:
+		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+				U2P_R0_HOST_DEVICE,
+				U2P_R0_HOST_DEVICE);
+		break;
+
+	case USB_DR_MODE_PERIPHERAL:
+		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+				U2P_R0_HOST_DEVICE, 0);
+		break;
+	}
+}
+
+static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
+{
+	int i;
+
+	if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
+		priv->otg_phy_mode = USB_DR_MODE_PERIPHERAL;
+	else
+		priv->otg_phy_mode = USB_DR_MODE_HOST;
+
+	for (i = 0 ; i < USB3_HOST_PHY ; ++i) {
+		if (!priv->phys[i].dev)
+			continue;
+
+		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+				   U2P_R0_POWER_ON_RESET,
+				   U2P_R0_POWER_ON_RESET);
+
+		if (i == USB2_OTG_PHY) {
+			regmap_update_bits(priv->regmap,
+					   U2P_R0 + (U2P_REG_SIZE * i),
+					   U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
+					   U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
+
+			dwc3_meson_g12a_usb2_set_mode(priv, i,
+						      priv->otg_phy_mode);
+		} else
+			dwc3_meson_g12a_usb2_set_mode(priv, i,
+						      USB_DR_MODE_HOST);
+
+		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+				   U2P_R0_POWER_ON_RESET, 0);
+	}
+
+	return 0;
+}
+
+static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
+{
+	regmap_update_bits(priv->regmap, USB_R3,
+			USB_R3_P30_SSC_RANGE_MASK |
+			USB_R3_P30_REF_SSP_EN,
+			USB_R3_P30_SSC_ENABLE |
+			FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) |
+			USB_R3_P30_REF_SSP_EN);
+	udelay(2);
+
+	regmap_update_bits(priv->regmap, USB_R2,
+			USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
+			FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
+
+	regmap_update_bits(priv->regmap, USB_R2,
+			USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
+			FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
+
+	udelay(2);
+
+	regmap_update_bits(priv->regmap, USB_R1,
+			USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
+			USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
+
+	regmap_update_bits(priv->regmap, USB_R1,
+			USB_R1_P30_PCS_TX_SWING_FULL_MASK,
+			FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
+}
+
+static void dwc3_meson_g12a_usb_init_mode(struct dwc3_meson_g12a *priv)
+{
+	if (priv->otg_phy_mode == USB_DR_MODE_PERIPHERAL) {
+		regmap_update_bits(priv->regmap, USB_R0,
+				USB_R0_U2D_ACT, USB_R0_U2D_ACT);
+		regmap_update_bits(priv->regmap, USB_R0,
+				USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
+		regmap_update_bits(priv->regmap, USB_R4,
+				USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
+	} else {
+		regmap_update_bits(priv->regmap, USB_R0,
+				USB_R0_U2D_ACT, 0);
+		regmap_update_bits(priv->regmap, USB_R4,
+				USB_R4_P21_SLEEP_M0, 0);
+	}
+}
+
+static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
+{
+	int ret;
+
+	ret = dwc3_meson_g12a_usb2_init(priv);
+	if (ret)
+		return ret;
+
+	regmap_update_bits(priv->regmap, USB_R1,
+			USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
+			FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
+
+	regmap_update_bits(priv->regmap, USB_R5,
+			USB_R5_ID_DIG_EN_0,
+			USB_R5_ID_DIG_EN_0);
+	regmap_update_bits(priv->regmap, USB_R5,
+			USB_R5_ID_DIG_EN_1,
+			USB_R5_ID_DIG_EN_1);
+	regmap_update_bits(priv->regmap, USB_R5,
+			USB_R5_ID_DIG_TH_MASK,
+			FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
+
+	/* If we have an actual SuperSpeed port, initialize it */
+	if (priv->usb3_ports)
+		dwc3_meson_g12a_usb3_init(priv);
+
+	dwc3_meson_g12a_usb_init_mode(priv);
+
+	return 0;
+}
+
+int dwc3_meson_g12a_force_mode(struct udevice *dev, enum usb_dr_mode mode)
+{
+	struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
+
+	if (!priv)
+		return -EINVAL;
+
+	if (mode != USB_DR_MODE_HOST && mode != USB_DR_MODE_PERIPHERAL)
+		return -EINVAL;
+
+	if (!priv->phys[USB2_OTG_PHY].dev)
+		return -EINVAL;
+
+	if (mode == priv->otg_mode)
+		return 0;
+
+	if (mode == USB_DR_MODE_HOST)
+		debug("%s: switching to Host Mode\n", __func__);
+	else
+		debug("%s: switching to Device Mode\n", __func__);
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	if (priv->vbus_supply) {
+		int ret = regulator_set_enable(priv->vbus_supply,
+					(mode == USB_DR_MODE_PERIPHERAL));
+		if (ret)
+			return ret;
+	}
+#endif
+	priv->otg_phy_mode = mode;
+
+	dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
+
+	dwc3_meson_g12a_usb_init_mode(priv);
+
+	return 0;
+}
+
+static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
+{
+	int i, ret;
+
+	for (i = 0 ; i < PHY_COUNT ; ++i) {
+		ret = generic_phy_get_by_name(priv->dev, phy_names[i],
+					      &priv->phys[i]);
+		if (ret == -ENOENT)
+			continue;
+
+		if (ret)
+			return ret;
+
+		if (i == USB3_HOST_PHY)
+			priv->usb3_ports++;
+		else
+			priv->usb2_ports++;
+	}
+
+	printf("%s: usb2 ports: %d\n", __func__, priv->usb2_ports);
+	printf("%s: usb3 ports: %d\n", __func__, priv->usb3_ports);
+
+	return 0;
+}
+
+static int dwc3_meson_g12a_reset_init(struct dwc3_meson_g12a *priv)
+{
+	int ret;
+
+	ret = reset_get_by_index(priv->dev, 0, &priv->reset);
+	if (ret)
+		return ret;
+
+	ret = reset_assert(&priv->reset);
+	udelay(1);
+	ret |= reset_deassert(&priv->reset);
+	if (ret) {
+		reset_free(&priv->reset);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dwc3_meson_g12a_clk_init(struct dwc3_meson_g12a *priv)
+{
+	int ret;
+
+	ret = clk_get_by_index(priv->dev, 0, &priv->clk);
+	if (ret)
+		return ret;
+
+#if CONFIG_IS_ENABLED(CLK)
+	ret = clk_enable(&priv->clk);
+	if (ret) {
+		clk_free(&priv->clk);
+		return ret;
+	}
+#endif
+
+	return 0;
+}
+
+static int dwc3_meson_g12a_probe(struct udevice *dev)
+{
+	struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
+	int ret, i;
+
+	priv->dev = dev;
+
+	ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
+	if (ret)
+		return ret;
+
+	ret = dwc3_meson_g12a_clk_init(priv);
+	if (ret)
+		return ret;
+
+	ret = dwc3_meson_g12a_reset_init(priv);
+	if (ret)
+		return ret;
+
+	ret = dwc3_meson_g12a_get_phys(priv);
+	if (ret)
+		return ret;
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	ret = device_get_supply_regulator(dev, "vbus-supply",
+					  &priv->vbus_supply);
+	if (ret && ret != -ENOENT) {
+		pr_err("Failed to get PHY regulator\n");
+		return ret;
+	}
+
+	if (priv->vbus_supply) {
+		ret = regulator_set_enable(priv->vbus_supply, true);
+		if (ret)
+			return ret;
+	}
+#endif
+
+	priv->otg_mode = usb_get_dr_mode(dev_of_offset(dev));
+
+	ret = dwc3_meson_g12a_usb_init(priv);
+	if (ret)
+		return ret;
+
+	for (i = 0 ; i < PHY_COUNT ; ++i) {
+		if (!priv->phys[i].dev)
+			continue;
+
+		ret = generic_phy_init(&priv->phys[i]);
+		if (ret)
+			goto err_phy_init;
+	}
+
+	return 0;
+
+err_phy_init:
+	for (i = 0 ; i < PHY_COUNT ; ++i) {
+		if (!priv->phys[i].dev)
+			continue;
+
+		 generic_phy_exit(&priv->phys[i]);
+	}
+
+	return ret;
+}
+
+static int dwc3_meson_g12a_remove(struct udevice *dev)
+{
+	struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
+	int i;
+
+	reset_release_all(&priv->reset, 1);
+
+	clk_release_all(&priv->clk, 1);
+
+	for (i = 0 ; i < PHY_COUNT ; ++i) {
+		if (!priv->phys[i].dev)
+			continue;
+
+		 generic_phy_exit(&priv->phys[i]);
+	}
+
+	return dm_scan_fdt_dev(dev);
+}
+
+static const struct udevice_id dwc3_meson_g12a_ids[] = {
+	{ .compatible = "amlogic,meson-g12a-usb-ctrl" },
+	{ }
+};
+
+U_BOOT_DRIVER(dwc3_generic_wrapper) = {
+	.name	= "dwc3-meson-g12a",
+	.id	= UCLASS_SIMPLE_BUS,
+	.of_match = dwc3_meson_g12a_ids,
+	.probe = dwc3_meson_g12a_probe,
+	.remove = dwc3_meson_g12a_remove,
+	.platdata_auto_alloc_size = sizeof(struct dwc3_meson_g12a),
+
+};
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [U-Boot] [PATCH 2/3] phy: meson: add Amlogic G12A USB2 and USB3+PCIE PHY drivers
  2019-03-26 15:15 ` Neil Armstrong
@ 2019-03-26 15:15   ` Neil Armstrong
  -1 siblings, 0 replies; 14+ messages in thread
From: Neil Armstrong @ 2019-03-26 15:15 UTC (permalink / raw)
  To: u-boot

This adds support for the USB PHYs found in the Amlogic G12A SoC Family.

The USB2 PHY supports Host and/or Peripheral mode, depending on it's position.
The first PHY is only used as Host, but the second supports Dual modes
defined by the USB Control Glue HW in front of the USB Controllers.

The second driver supports USB3 Host mode or PCIE 2.0 mode, depending on
the layout of the board.
Selection is done by the #phy-cells, making the mode static and exclusive.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 drivers/phy/Kconfig                |   8 +
 drivers/phy/Makefile               |   1 +
 drivers/phy/meson-g12a-usb2.c      | 216 ++++++++++++++++++
 drivers/phy/meson-g12a-usb3-pcie.c | 345 +++++++++++++++++++++++++++++
 4 files changed, 570 insertions(+)
 create mode 100644 drivers/phy/meson-g12a-usb2.c
 create mode 100644 drivers/phy/meson-g12a-usb3-pcie.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 825ee7c3be..7d45bc46a5 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -147,6 +147,14 @@ config MESON_GXL_USB_PHY
 	  This is the generic phy driver for the Amlogic Meson GXL
 	  USB2 and USB3 PHYS.
 
+config MESON_G12A_USB_PHY
+	bool "Amlogic Meson G12A USB PHYs"
+	depends on PHY && ARCH_MESON && MESON_G12A
+	imply REGMAP
+	help
+	  This is the generic phy driver for the Amlogic Meson G12A
+	  USB2 and USB3 PHYS.
+
 config MSM8916_USB_PHY
 	bool "Qualcomm MSM8916 USB PHY support"
 	depends on PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 099551d693..12602dbd33 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o
 obj-$(CONFIG_PHY_RCAR_GEN3) += phy-rcar-gen3.o
 obj-$(CONFIG_PHY_STM32_USBPHYC) += phy-stm32-usbphyc.o
 obj-$(CONFIG_MESON_GXL_USB_PHY) += meson-gxl-usb2.o meson-gxl-usb3.o
+obj-$(CONFIG_MESON_G12A_USB_PHY) += meson-g12a-usb2.o meson-g12a-usb3-pcie.o
 obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o
 obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o
 obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o
diff --git a/drivers/phy/meson-g12a-usb2.c b/drivers/phy/meson-g12a-usb2.c
new file mode 100644
index 0000000000..ad1a77fcfc
--- /dev/null
+++ b/drivers/phy/meson-g12a-usb2.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Meson G12A USB2 PHY driver
+ *
+ * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstron@baylibre.com>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <bitfield.h>
+#include <dm.h>
+#include <errno.h>
+#include <generic-phy.h>
+#include <regmap.h>
+#include <power/regulator.h>
+#include <reset.h>
+#include <clk.h>
+
+#include <linux/bitops.h>
+#include <linux/compat.h>
+
+#define PHY_CTRL_R0						0x0
+#define PHY_CTRL_R1						0x4
+#define PHY_CTRL_R2						0x8
+#define PHY_CTRL_R3						0xc
+#define PHY_CTRL_R4						0x10
+#define PHY_CTRL_R5						0x14
+#define PHY_CTRL_R6						0x18
+#define PHY_CTRL_R7						0x1c
+#define PHY_CTRL_R8						0x20
+#define PHY_CTRL_R9						0x24
+#define PHY_CTRL_R10						0x28
+#define PHY_CTRL_R11						0x2c
+#define PHY_CTRL_R12						0x30
+#define PHY_CTRL_R13						0x34
+#define PHY_CTRL_R14						0x38
+#define PHY_CTRL_R15						0x3c
+#define PHY_CTRL_R16						0x40
+#define PHY_CTRL_R17						0x44
+#define PHY_CTRL_R18						0x48
+#define PHY_CTRL_R19						0x4c
+#define PHY_CTRL_R20						0x50
+#define PHY_CTRL_R21						0x54
+#define PHY_CTRL_R22						0x58
+#define PHY_CTRL_R23						0x5c
+
+#define RESET_COMPLETE_TIME					1000
+#define PLL_RESET_COMPLETE_TIME					100
+
+struct phy_meson_g12a_usb2_priv {
+	struct regmap		*regmap;
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	struct udevice		*phy_supply;
+#endif
+#if CONFIG_IS_ENABLED(CLK)
+	struct clk		clk;
+#endif
+	struct reset_ctl	reset;
+};
+
+
+static int phy_meson_g12a_usb2_power_on(struct phy *phy)
+{
+	struct udevice *dev = phy->dev;
+	struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	if (priv->phy_supply) {
+		int ret = regulator_set_enable(priv->phy_supply, true);
+		if (ret)
+			return ret;
+	}
+#endif
+
+	return 0;
+}
+
+static int phy_meson_g12a_usb2_power_off(struct phy *phy)
+{
+	struct udevice *dev = phy->dev;
+	struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	if (priv->phy_supply) {
+		int ret = regulator_set_enable(priv->phy_supply, false);
+		if (ret) {
+			pr_err("Error disabling PHY supply\n");
+			return ret;
+		}
+	}
+#endif
+
+	return 0;
+}
+
+static int phy_meson_g12a_usb2_init(struct phy *phy)
+{
+	struct udevice *dev = phy->dev;
+	struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = reset_assert(&priv->reset);
+	udelay(1);
+	ret |= reset_deassert(&priv->reset);
+	if (ret)
+		return ret;
+
+	udelay(RESET_COMPLETE_TIME);
+
+	/* usb2_otg_aca_en == 0 */
+	regmap_update_bits(priv->regmap, PHY_CTRL_R21, BIT(2), 0);
+
+	/* PLL Setup : 24MHz * 20 / 1 = 480MHz */
+	regmap_write(priv->regmap, PHY_CTRL_R16, 0x39400414);
+	regmap_write(priv->regmap, PHY_CTRL_R17, 0x927e0000);
+	regmap_write(priv->regmap, PHY_CTRL_R18, 0xac5f49e5);
+
+	udelay(PLL_RESET_COMPLETE_TIME);
+
+	/* UnReset PLL */
+	regmap_write(priv->regmap, PHY_CTRL_R16, 0x19400414);
+
+	/* PHY Tuning */
+	regmap_write(priv->regmap, PHY_CTRL_R20, 0xfe18);
+	regmap_write(priv->regmap, PHY_CTRL_R4, 0x8000fff);
+
+	/* Tuning Disconnect Threshold */
+	regmap_write(priv->regmap, PHY_CTRL_R3, 0x34);
+
+	/* Analog Settings */
+	regmap_write(priv->regmap, PHY_CTRL_R14, 0);
+	regmap_write(priv->regmap, PHY_CTRL_R13, 0x78000);
+
+	return 0;
+}
+
+static int phy_meson_g12a_usb2_exit(struct phy *phy)
+{
+	struct udevice *dev = phy->dev;
+	struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = reset_assert(&priv->reset);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+struct phy_ops meson_g12a_usb2_phy_ops = {
+	.init = phy_meson_g12a_usb2_init,
+	.exit = phy_meson_g12a_usb2_exit,
+	.power_on = phy_meson_g12a_usb2_power_on,
+	.power_off = phy_meson_g12a_usb2_power_off,
+};
+
+int meson_g12a_usb2_phy_probe(struct udevice *dev)
+{
+	struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
+	if (ret)
+		return ret;
+
+	ret = reset_get_by_index(dev, 0, &priv->reset);
+	if (ret == -ENOTSUPP)
+		return 0;
+	else if (ret)
+		return ret;
+
+	ret = reset_deassert(&priv->reset);
+	if (ret) {
+		reset_release_all(&priv->reset, 1);
+		return ret;
+	}
+
+#if CONFIG_IS_ENABLED(CLK)
+	ret = clk_get_by_index(dev, 0, &priv->clk);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_enable(&priv->clk);
+	if (ret && ret != -ENOSYS && ret != -ENOTSUPP) {
+		pr_err("failed to enable PHY clock\n");
+		clk_free(&priv->clk);
+		return ret;
+	}
+#endif
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	ret = device_get_supply_regulator(dev, "phy-supply", &priv->phy_supply);
+	if (ret && ret != -ENOENT) {
+		pr_err("Failed to get PHY regulator\n");
+		return ret;
+	}
+#endif
+
+	return 0;
+}
+
+static const struct udevice_id meson_g12a_usb2_phy_ids[] = {
+	{ .compatible = "amlogic,g12a-usb2-phy" },
+	{ }
+};
+
+U_BOOT_DRIVER(meson_g12a_usb2_phy) = {
+	.name = "meson_g12a_usb2_phy",
+	.id = UCLASS_PHY,
+	.of_match = meson_g12a_usb2_phy_ids,
+	.probe = meson_g12a_usb2_phy_probe,
+	.ops = &meson_g12a_usb2_phy_ops,
+	.priv_auto_alloc_size = sizeof(struct phy_meson_g12a_usb2_priv),
+};
diff --git a/drivers/phy/meson-g12a-usb3-pcie.c b/drivers/phy/meson-g12a-usb3-pcie.c
new file mode 100644
index 0000000000..920675dc99
--- /dev/null
+++ b/drivers/phy/meson-g12a-usb3-pcie.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Meson G12A USB3+PCIE Combo PHY driver
+ *
+ * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstron@baylibre.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <regmap.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <reset.h>
+#include <bitfield.h>
+#include <generic-phy.h>
+
+#include <linux/bitops.h>
+#include <linux/compat.h>
+#include <linux/bitfield.h>
+
+#define PHY_R0							0x00
+	#define PHY_R0_PCIE_POWER_STATE				GENMASK(4, 0)
+	#define PHY_R0_PCIE_USB3_SWITCH				GENMASK(6, 5)
+
+#define PHY_R1							0x04
+	#define PHY_R1_PHY_TX1_TERM_OFFSET			GENMASK(4, 0)
+	#define PHY_R1_PHY_TX0_TERM_OFFSET			GENMASK(9, 5)
+	#define PHY_R1_PHY_RX1_EQ				GENMASK(12, 10)
+	#define PHY_R1_PHY_RX0_EQ				GENMASK(15, 13)
+	#define PHY_R1_PHY_LOS_LEVEL				GENMASK(20, 16)
+	#define PHY_R1_PHY_LOS_BIAS				GENMASK(23, 21)
+	#define PHY_R1_PHY_REF_CLKDIV2				BIT(24)
+	#define PHY_R1_PHY_MPLL_MULTIPLIER			GENMASK(31, 25)
+
+#define PHY_R2							0x08
+	#define PHY_R2_PCS_TX_DEEMPH_GEN2_6DB			GENMASK(5, 0)
+	#define PHY_R2_PCS_TX_DEEMPH_GEN2_3P5DB			GENMASK(11, 6)
+	#define PHY_R2_PCS_TX_DEEMPH_GEN1			GENMASK(17, 12)
+	#define PHY_R2_PHY_TX_VBOOST_LVL			GENMASK(20, 18)
+
+#define PHY_R4							0x10
+	#define PHY_R4_PHY_CR_WRITE				BIT(0)
+	#define PHY_R4_PHY_CR_READ				BIT(1)
+	#define PHY_R4_PHY_CR_DATA_IN				GENMASK(17, 2)
+	#define PHY_R4_PHY_CR_CAP_DATA				BIT(18)
+	#define PHY_R4_PHY_CR_CAP_ADDR				BIT(19)
+
+#define PHY_R5							0x14
+	#define PHY_R5_PHY_CR_DATA_OUT				GENMASK(15, 0)
+	#define PHY_R5_PHY_CR_ACK				BIT(16)
+	#define PHY_R5_PHY_BS_OUT				BIT(17)
+
+struct phy_g12a_usb3_pcie_priv {
+	struct regmap		*regmap;
+#if CONFIG_IS_ENABLED(CLK)
+	struct clk		clk;
+#endif
+	struct reset_ctl_bulk	resets;
+};
+
+static int phy_g12a_usb3_pcie_cr_bus_addr(struct phy_g12a_usb3_pcie_priv *priv,
+					  unsigned int addr)
+{
+	unsigned int val, reg;
+	int ret;
+
+	reg = FIELD_PREP(PHY_R4_PHY_CR_DATA_IN, addr);
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_CAP_ADDR);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       !(val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int
+phy_g12a_usb3_pcie_cr_bus_read(struct phy_g12a_usb3_pcie_priv *priv,
+			       unsigned int addr, unsigned int *data)
+{
+	unsigned int val;
+	int ret;
+
+	ret = phy_g12a_usb3_pcie_cr_bus_addr(priv, addr);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, 0);
+	regmap_write(priv->regmap, PHY_R4, PHY_R4_PHY_CR_READ);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	*data = FIELD_GET(PHY_R5_PHY_CR_DATA_OUT, val);
+
+	regmap_write(priv->regmap, PHY_R4, 0);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       !(val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int
+phy_g12a_usb3_pcie_cr_bus_write(struct phy_g12a_usb3_pcie_priv *priv,
+			        unsigned int addr, unsigned int data)
+{
+	unsigned int val, reg;
+	int ret;
+
+	ret = phy_g12a_usb3_pcie_cr_bus_addr(priv, addr);
+	if (ret)
+		return ret;
+
+	reg = FIELD_PREP(PHY_R4_PHY_CR_DATA_IN, data);
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_CAP_DATA);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK) == 0,
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_WRITE);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK) == 0,
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int
+phy_g12a_usb3_pcie_cr_bus_update_bits(struct phy_g12a_usb3_pcie_priv *priv,
+				      uint offset, uint mask, uint val)
+{
+	uint reg;
+	int ret;
+
+	ret = phy_g12a_usb3_pcie_cr_bus_read(priv, offset, &reg);
+	if (ret)
+		return ret;
+
+	reg &= ~mask;
+
+	return phy_g12a_usb3_pcie_cr_bus_write(priv, offset, reg | val);
+}
+
+static int phy_meson_g12a_usb3_init(struct phy *phy)
+{
+	struct udevice *dev = phy->dev;
+	struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(dev);
+	unsigned int data;
+	int ret;
+
+	/* TOFIX Handle PCIE mode */
+
+	ret = reset_assert_bulk(&priv->resets);
+	udelay(1);
+	ret |= reset_deassert_bulk(&priv->resets);
+	if (ret)
+		return ret;
+
+	/* Switch PHY to USB3 */
+	regmap_update_bits(priv->regmap, PHY_R0,
+			   PHY_R0_PCIE_USB3_SWITCH,
+			   PHY_R0_PCIE_USB3_SWITCH);
+
+	/*
+	 * WORKAROUND: There is SSPHY suspend bug due to
+	 * which USB enumerates
+	 * in HS mode instead of SS mode. Workaround it by asserting
+	 * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus
+	 * mode
+	 */
+	ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x102d,
+						    BIT(7), BIT(7));
+	if (ret)
+		return ret;
+
+	ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x1010, 0xff0, 20);
+	if (ret)
+		return ret;
+
+	/*
+	 * Fix RX Equalization setting as follows
+	 * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
+	 * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
+	 * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
+	 * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
+	 */
+	ret = phy_g12a_usb3_pcie_cr_bus_read(priv, 0x1006, &data);
+	if (ret)
+		return ret;
+
+	data &= ~BIT(6);
+	data |= BIT(7);
+	data &= ~(0x7 << 8);
+	data |= (0x3 << 8);
+	data |= (1 << 11);
+	ret = phy_g12a_usb3_pcie_cr_bus_write(priv, 0x1006, data);
+	if (ret)
+		return ret;
+
+	/*
+	 * Set EQ and TX launch amplitudes as follows
+	 * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
+	 * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
+	 * LANE0.TX_OVRD_DRV_LO.EN set to 1.
+	 */
+	ret = phy_g12a_usb3_pcie_cr_bus_read(priv, 0x1002, &data);
+	if (ret)
+		return ret;
+
+	data &= ~0x3f80;
+	data |= (0x16 << 7);
+	data &= ~0x7f;
+	data |= (0x7f | BIT(14));
+	ret = phy_g12a_usb3_pcie_cr_bus_write(priv, 0x1002, data);
+	if (ret)
+		return ret;
+
+	/*
+	 * MPLL_LOOP_CTL.PROP_CNTRL = 8
+	 */
+	ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x30,
+						    0xf << 4, 8 << 4);
+	if (ret)
+		return ret;
+
+	regmap_update_bits(priv->regmap, PHY_R2,
+			PHY_R2_PHY_TX_VBOOST_LVL,
+			FIELD_PREP(PHY_R2_PHY_TX_VBOOST_LVL, 0x4));
+
+	regmap_update_bits(priv->regmap, PHY_R1,
+			PHY_R1_PHY_LOS_BIAS | PHY_R1_PHY_LOS_LEVEL,
+			FIELD_PREP(PHY_R1_PHY_LOS_BIAS, 4) |
+			FIELD_PREP(PHY_R1_PHY_LOS_LEVEL, 9));
+
+	return ret;
+}
+
+static int phy_meson_g12a_usb3_exit(struct phy *phy)
+{
+	struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(phy->dev);
+
+	return reset_assert_bulk(&priv->resets);
+}
+
+struct phy_ops meson_g12a_usb3_pcie_phy_ops = {
+	.init = phy_meson_g12a_usb3_init,
+	.exit = phy_meson_g12a_usb3_exit,
+};
+
+int meson_g12a_usb3_pcie_phy_probe(struct udevice *dev)
+{
+	struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
+	if (ret)
+		return ret;
+
+	ret = reset_get_bulk(dev, &priv->resets);
+	if (ret == -ENOTSUPP)
+		return 0;
+	else if (ret)
+		return ret;
+
+#if CONFIG_IS_ENABLED(CLK)
+	ret = clk_get_by_index(dev, 0, &priv->clk);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_enable(&priv->clk);
+	if (ret && ret != -ENOENT && ret != -ENOTSUPP) {
+		pr_err("failed to enable PHY clock\n");
+		clk_free(&priv->clk);
+		return ret;
+	}
+#endif
+
+	return 0;
+}
+
+static const struct udevice_id meson_g12a_usb3_pcie_phy_ids[] = {
+	{ .compatible = "amlogic,g12a-usb3-pcie-phy" },
+	{ }
+};
+
+U_BOOT_DRIVER(meson_g12a_usb3_pcie_phy) = {
+	.name = "meson_g12a_usb3_pcie_phy",
+	.id = UCLASS_PHY,
+	.of_match = meson_g12a_usb3_pcie_phy_ids,
+	.probe = meson_g12a_usb3_pcie_phy_probe,
+	.ops = &meson_g12a_usb3_pcie_phy_ops,
+	.priv_auto_alloc_size = sizeof(struct phy_g12a_usb3_pcie_priv),
+};
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 2/3] phy: meson: add Amlogic G12A USB2 and USB3+PCIE PHY drivers
@ 2019-03-26 15:15   ` Neil Armstrong
  0 siblings, 0 replies; 14+ messages in thread
From: Neil Armstrong @ 2019-03-26 15:15 UTC (permalink / raw)
  To: marex; +Cc: Neil Armstrong, u-boot-amlogic, u-boot

This adds support for the USB PHYs found in the Amlogic G12A SoC Family.

The USB2 PHY supports Host and/or Peripheral mode, depending on it's position.
The first PHY is only used as Host, but the second supports Dual modes
defined by the USB Control Glue HW in front of the USB Controllers.

The second driver supports USB3 Host mode or PCIE 2.0 mode, depending on
the layout of the board.
Selection is done by the #phy-cells, making the mode static and exclusive.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 drivers/phy/Kconfig                |   8 +
 drivers/phy/Makefile               |   1 +
 drivers/phy/meson-g12a-usb2.c      | 216 ++++++++++++++++++
 drivers/phy/meson-g12a-usb3-pcie.c | 345 +++++++++++++++++++++++++++++
 4 files changed, 570 insertions(+)
 create mode 100644 drivers/phy/meson-g12a-usb2.c
 create mode 100644 drivers/phy/meson-g12a-usb3-pcie.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 825ee7c3be..7d45bc46a5 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -147,6 +147,14 @@ config MESON_GXL_USB_PHY
 	  This is the generic phy driver for the Amlogic Meson GXL
 	  USB2 and USB3 PHYS.
 
+config MESON_G12A_USB_PHY
+	bool "Amlogic Meson G12A USB PHYs"
+	depends on PHY && ARCH_MESON && MESON_G12A
+	imply REGMAP
+	help
+	  This is the generic phy driver for the Amlogic Meson G12A
+	  USB2 and USB3 PHYS.
+
 config MSM8916_USB_PHY
 	bool "Qualcomm MSM8916 USB PHY support"
 	depends on PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 099551d693..12602dbd33 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o
 obj-$(CONFIG_PHY_RCAR_GEN3) += phy-rcar-gen3.o
 obj-$(CONFIG_PHY_STM32_USBPHYC) += phy-stm32-usbphyc.o
 obj-$(CONFIG_MESON_GXL_USB_PHY) += meson-gxl-usb2.o meson-gxl-usb3.o
+obj-$(CONFIG_MESON_G12A_USB_PHY) += meson-g12a-usb2.o meson-g12a-usb3-pcie.o
 obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o
 obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o
 obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o
diff --git a/drivers/phy/meson-g12a-usb2.c b/drivers/phy/meson-g12a-usb2.c
new file mode 100644
index 0000000000..ad1a77fcfc
--- /dev/null
+++ b/drivers/phy/meson-g12a-usb2.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Meson G12A USB2 PHY driver
+ *
+ * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstron@baylibre.com>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <bitfield.h>
+#include <dm.h>
+#include <errno.h>
+#include <generic-phy.h>
+#include <regmap.h>
+#include <power/regulator.h>
+#include <reset.h>
+#include <clk.h>
+
+#include <linux/bitops.h>
+#include <linux/compat.h>
+
+#define PHY_CTRL_R0						0x0
+#define PHY_CTRL_R1						0x4
+#define PHY_CTRL_R2						0x8
+#define PHY_CTRL_R3						0xc
+#define PHY_CTRL_R4						0x10
+#define PHY_CTRL_R5						0x14
+#define PHY_CTRL_R6						0x18
+#define PHY_CTRL_R7						0x1c
+#define PHY_CTRL_R8						0x20
+#define PHY_CTRL_R9						0x24
+#define PHY_CTRL_R10						0x28
+#define PHY_CTRL_R11						0x2c
+#define PHY_CTRL_R12						0x30
+#define PHY_CTRL_R13						0x34
+#define PHY_CTRL_R14						0x38
+#define PHY_CTRL_R15						0x3c
+#define PHY_CTRL_R16						0x40
+#define PHY_CTRL_R17						0x44
+#define PHY_CTRL_R18						0x48
+#define PHY_CTRL_R19						0x4c
+#define PHY_CTRL_R20						0x50
+#define PHY_CTRL_R21						0x54
+#define PHY_CTRL_R22						0x58
+#define PHY_CTRL_R23						0x5c
+
+#define RESET_COMPLETE_TIME					1000
+#define PLL_RESET_COMPLETE_TIME					100
+
+struct phy_meson_g12a_usb2_priv {
+	struct regmap		*regmap;
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	struct udevice		*phy_supply;
+#endif
+#if CONFIG_IS_ENABLED(CLK)
+	struct clk		clk;
+#endif
+	struct reset_ctl	reset;
+};
+
+
+static int phy_meson_g12a_usb2_power_on(struct phy *phy)
+{
+	struct udevice *dev = phy->dev;
+	struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	if (priv->phy_supply) {
+		int ret = regulator_set_enable(priv->phy_supply, true);
+		if (ret)
+			return ret;
+	}
+#endif
+
+	return 0;
+}
+
+static int phy_meson_g12a_usb2_power_off(struct phy *phy)
+{
+	struct udevice *dev = phy->dev;
+	struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	if (priv->phy_supply) {
+		int ret = regulator_set_enable(priv->phy_supply, false);
+		if (ret) {
+			pr_err("Error disabling PHY supply\n");
+			return ret;
+		}
+	}
+#endif
+
+	return 0;
+}
+
+static int phy_meson_g12a_usb2_init(struct phy *phy)
+{
+	struct udevice *dev = phy->dev;
+	struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = reset_assert(&priv->reset);
+	udelay(1);
+	ret |= reset_deassert(&priv->reset);
+	if (ret)
+		return ret;
+
+	udelay(RESET_COMPLETE_TIME);
+
+	/* usb2_otg_aca_en == 0 */
+	regmap_update_bits(priv->regmap, PHY_CTRL_R21, BIT(2), 0);
+
+	/* PLL Setup : 24MHz * 20 / 1 = 480MHz */
+	regmap_write(priv->regmap, PHY_CTRL_R16, 0x39400414);
+	regmap_write(priv->regmap, PHY_CTRL_R17, 0x927e0000);
+	regmap_write(priv->regmap, PHY_CTRL_R18, 0xac5f49e5);
+
+	udelay(PLL_RESET_COMPLETE_TIME);
+
+	/* UnReset PLL */
+	regmap_write(priv->regmap, PHY_CTRL_R16, 0x19400414);
+
+	/* PHY Tuning */
+	regmap_write(priv->regmap, PHY_CTRL_R20, 0xfe18);
+	regmap_write(priv->regmap, PHY_CTRL_R4, 0x8000fff);
+
+	/* Tuning Disconnect Threshold */
+	regmap_write(priv->regmap, PHY_CTRL_R3, 0x34);
+
+	/* Analog Settings */
+	regmap_write(priv->regmap, PHY_CTRL_R14, 0);
+	regmap_write(priv->regmap, PHY_CTRL_R13, 0x78000);
+
+	return 0;
+}
+
+static int phy_meson_g12a_usb2_exit(struct phy *phy)
+{
+	struct udevice *dev = phy->dev;
+	struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = reset_assert(&priv->reset);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+struct phy_ops meson_g12a_usb2_phy_ops = {
+	.init = phy_meson_g12a_usb2_init,
+	.exit = phy_meson_g12a_usb2_exit,
+	.power_on = phy_meson_g12a_usb2_power_on,
+	.power_off = phy_meson_g12a_usb2_power_off,
+};
+
+int meson_g12a_usb2_phy_probe(struct udevice *dev)
+{
+	struct phy_meson_g12a_usb2_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
+	if (ret)
+		return ret;
+
+	ret = reset_get_by_index(dev, 0, &priv->reset);
+	if (ret == -ENOTSUPP)
+		return 0;
+	else if (ret)
+		return ret;
+
+	ret = reset_deassert(&priv->reset);
+	if (ret) {
+		reset_release_all(&priv->reset, 1);
+		return ret;
+	}
+
+#if CONFIG_IS_ENABLED(CLK)
+	ret = clk_get_by_index(dev, 0, &priv->clk);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_enable(&priv->clk);
+	if (ret && ret != -ENOSYS && ret != -ENOTSUPP) {
+		pr_err("failed to enable PHY clock\n");
+		clk_free(&priv->clk);
+		return ret;
+	}
+#endif
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+	ret = device_get_supply_regulator(dev, "phy-supply", &priv->phy_supply);
+	if (ret && ret != -ENOENT) {
+		pr_err("Failed to get PHY regulator\n");
+		return ret;
+	}
+#endif
+
+	return 0;
+}
+
+static const struct udevice_id meson_g12a_usb2_phy_ids[] = {
+	{ .compatible = "amlogic,g12a-usb2-phy" },
+	{ }
+};
+
+U_BOOT_DRIVER(meson_g12a_usb2_phy) = {
+	.name = "meson_g12a_usb2_phy",
+	.id = UCLASS_PHY,
+	.of_match = meson_g12a_usb2_phy_ids,
+	.probe = meson_g12a_usb2_phy_probe,
+	.ops = &meson_g12a_usb2_phy_ops,
+	.priv_auto_alloc_size = sizeof(struct phy_meson_g12a_usb2_priv),
+};
diff --git a/drivers/phy/meson-g12a-usb3-pcie.c b/drivers/phy/meson-g12a-usb3-pcie.c
new file mode 100644
index 0000000000..920675dc99
--- /dev/null
+++ b/drivers/phy/meson-g12a-usb3-pcie.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Meson G12A USB3+PCIE Combo PHY driver
+ *
+ * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstron@baylibre.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <regmap.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <reset.h>
+#include <bitfield.h>
+#include <generic-phy.h>
+
+#include <linux/bitops.h>
+#include <linux/compat.h>
+#include <linux/bitfield.h>
+
+#define PHY_R0							0x00
+	#define PHY_R0_PCIE_POWER_STATE				GENMASK(4, 0)
+	#define PHY_R0_PCIE_USB3_SWITCH				GENMASK(6, 5)
+
+#define PHY_R1							0x04
+	#define PHY_R1_PHY_TX1_TERM_OFFSET			GENMASK(4, 0)
+	#define PHY_R1_PHY_TX0_TERM_OFFSET			GENMASK(9, 5)
+	#define PHY_R1_PHY_RX1_EQ				GENMASK(12, 10)
+	#define PHY_R1_PHY_RX0_EQ				GENMASK(15, 13)
+	#define PHY_R1_PHY_LOS_LEVEL				GENMASK(20, 16)
+	#define PHY_R1_PHY_LOS_BIAS				GENMASK(23, 21)
+	#define PHY_R1_PHY_REF_CLKDIV2				BIT(24)
+	#define PHY_R1_PHY_MPLL_MULTIPLIER			GENMASK(31, 25)
+
+#define PHY_R2							0x08
+	#define PHY_R2_PCS_TX_DEEMPH_GEN2_6DB			GENMASK(5, 0)
+	#define PHY_R2_PCS_TX_DEEMPH_GEN2_3P5DB			GENMASK(11, 6)
+	#define PHY_R2_PCS_TX_DEEMPH_GEN1			GENMASK(17, 12)
+	#define PHY_R2_PHY_TX_VBOOST_LVL			GENMASK(20, 18)
+
+#define PHY_R4							0x10
+	#define PHY_R4_PHY_CR_WRITE				BIT(0)
+	#define PHY_R4_PHY_CR_READ				BIT(1)
+	#define PHY_R4_PHY_CR_DATA_IN				GENMASK(17, 2)
+	#define PHY_R4_PHY_CR_CAP_DATA				BIT(18)
+	#define PHY_R4_PHY_CR_CAP_ADDR				BIT(19)
+
+#define PHY_R5							0x14
+	#define PHY_R5_PHY_CR_DATA_OUT				GENMASK(15, 0)
+	#define PHY_R5_PHY_CR_ACK				BIT(16)
+	#define PHY_R5_PHY_BS_OUT				BIT(17)
+
+struct phy_g12a_usb3_pcie_priv {
+	struct regmap		*regmap;
+#if CONFIG_IS_ENABLED(CLK)
+	struct clk		clk;
+#endif
+	struct reset_ctl_bulk	resets;
+};
+
+static int phy_g12a_usb3_pcie_cr_bus_addr(struct phy_g12a_usb3_pcie_priv *priv,
+					  unsigned int addr)
+{
+	unsigned int val, reg;
+	int ret;
+
+	reg = FIELD_PREP(PHY_R4_PHY_CR_DATA_IN, addr);
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_CAP_ADDR);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       !(val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int
+phy_g12a_usb3_pcie_cr_bus_read(struct phy_g12a_usb3_pcie_priv *priv,
+			       unsigned int addr, unsigned int *data)
+{
+	unsigned int val;
+	int ret;
+
+	ret = phy_g12a_usb3_pcie_cr_bus_addr(priv, addr);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, 0);
+	regmap_write(priv->regmap, PHY_R4, PHY_R4_PHY_CR_READ);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	*data = FIELD_GET(PHY_R5_PHY_CR_DATA_OUT, val);
+
+	regmap_write(priv->regmap, PHY_R4, 0);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       !(val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int
+phy_g12a_usb3_pcie_cr_bus_write(struct phy_g12a_usb3_pcie_priv *priv,
+			        unsigned int addr, unsigned int data)
+{
+	unsigned int val, reg;
+	int ret;
+
+	ret = phy_g12a_usb3_pcie_cr_bus_addr(priv, addr);
+	if (ret)
+		return ret;
+
+	reg = FIELD_PREP(PHY_R4_PHY_CR_DATA_IN, data);
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_CAP_DATA);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK) == 0,
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_WRITE);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK) == 0,
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int
+phy_g12a_usb3_pcie_cr_bus_update_bits(struct phy_g12a_usb3_pcie_priv *priv,
+				      uint offset, uint mask, uint val)
+{
+	uint reg;
+	int ret;
+
+	ret = phy_g12a_usb3_pcie_cr_bus_read(priv, offset, &reg);
+	if (ret)
+		return ret;
+
+	reg &= ~mask;
+
+	return phy_g12a_usb3_pcie_cr_bus_write(priv, offset, reg | val);
+}
+
+static int phy_meson_g12a_usb3_init(struct phy *phy)
+{
+	struct udevice *dev = phy->dev;
+	struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(dev);
+	unsigned int data;
+	int ret;
+
+	/* TOFIX Handle PCIE mode */
+
+	ret = reset_assert_bulk(&priv->resets);
+	udelay(1);
+	ret |= reset_deassert_bulk(&priv->resets);
+	if (ret)
+		return ret;
+
+	/* Switch PHY to USB3 */
+	regmap_update_bits(priv->regmap, PHY_R0,
+			   PHY_R0_PCIE_USB3_SWITCH,
+			   PHY_R0_PCIE_USB3_SWITCH);
+
+	/*
+	 * WORKAROUND: There is SSPHY suspend bug due to
+	 * which USB enumerates
+	 * in HS mode instead of SS mode. Workaround it by asserting
+	 * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus
+	 * mode
+	 */
+	ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x102d,
+						    BIT(7), BIT(7));
+	if (ret)
+		return ret;
+
+	ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x1010, 0xff0, 20);
+	if (ret)
+		return ret;
+
+	/*
+	 * Fix RX Equalization setting as follows
+	 * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
+	 * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
+	 * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
+	 * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
+	 */
+	ret = phy_g12a_usb3_pcie_cr_bus_read(priv, 0x1006, &data);
+	if (ret)
+		return ret;
+
+	data &= ~BIT(6);
+	data |= BIT(7);
+	data &= ~(0x7 << 8);
+	data |= (0x3 << 8);
+	data |= (1 << 11);
+	ret = phy_g12a_usb3_pcie_cr_bus_write(priv, 0x1006, data);
+	if (ret)
+		return ret;
+
+	/*
+	 * Set EQ and TX launch amplitudes as follows
+	 * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
+	 * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
+	 * LANE0.TX_OVRD_DRV_LO.EN set to 1.
+	 */
+	ret = phy_g12a_usb3_pcie_cr_bus_read(priv, 0x1002, &data);
+	if (ret)
+		return ret;
+
+	data &= ~0x3f80;
+	data |= (0x16 << 7);
+	data &= ~0x7f;
+	data |= (0x7f | BIT(14));
+	ret = phy_g12a_usb3_pcie_cr_bus_write(priv, 0x1002, data);
+	if (ret)
+		return ret;
+
+	/*
+	 * MPLL_LOOP_CTL.PROP_CNTRL = 8
+	 */
+	ret = phy_g12a_usb3_pcie_cr_bus_update_bits(priv, 0x30,
+						    0xf << 4, 8 << 4);
+	if (ret)
+		return ret;
+
+	regmap_update_bits(priv->regmap, PHY_R2,
+			PHY_R2_PHY_TX_VBOOST_LVL,
+			FIELD_PREP(PHY_R2_PHY_TX_VBOOST_LVL, 0x4));
+
+	regmap_update_bits(priv->regmap, PHY_R1,
+			PHY_R1_PHY_LOS_BIAS | PHY_R1_PHY_LOS_LEVEL,
+			FIELD_PREP(PHY_R1_PHY_LOS_BIAS, 4) |
+			FIELD_PREP(PHY_R1_PHY_LOS_LEVEL, 9));
+
+	return ret;
+}
+
+static int phy_meson_g12a_usb3_exit(struct phy *phy)
+{
+	struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(phy->dev);
+
+	return reset_assert_bulk(&priv->resets);
+}
+
+struct phy_ops meson_g12a_usb3_pcie_phy_ops = {
+	.init = phy_meson_g12a_usb3_init,
+	.exit = phy_meson_g12a_usb3_exit,
+};
+
+int meson_g12a_usb3_pcie_phy_probe(struct udevice *dev)
+{
+	struct phy_g12a_usb3_pcie_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
+	if (ret)
+		return ret;
+
+	ret = reset_get_bulk(dev, &priv->resets);
+	if (ret == -ENOTSUPP)
+		return 0;
+	else if (ret)
+		return ret;
+
+#if CONFIG_IS_ENABLED(CLK)
+	ret = clk_get_by_index(dev, 0, &priv->clk);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_enable(&priv->clk);
+	if (ret && ret != -ENOENT && ret != -ENOTSUPP) {
+		pr_err("failed to enable PHY clock\n");
+		clk_free(&priv->clk);
+		return ret;
+	}
+#endif
+
+	return 0;
+}
+
+static const struct udevice_id meson_g12a_usb3_pcie_phy_ids[] = {
+	{ .compatible = "amlogic,g12a-usb3-pcie-phy" },
+	{ }
+};
+
+U_BOOT_DRIVER(meson_g12a_usb3_pcie_phy) = {
+	.name = "meson_g12a_usb3_pcie_phy",
+	.id = UCLASS_PHY,
+	.of_match = meson_g12a_usb3_pcie_phy_ids,
+	.probe = meson_g12a_usb3_pcie_phy_probe,
+	.ops = &meson_g12a_usb3_pcie_phy_ops,
+	.priv_auto_alloc_size = sizeof(struct phy_g12a_usb3_pcie_priv),
+};
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [U-Boot] [PATCH 3/3] mach-meson: g12a: add DWC2 peripheral mode support
  2019-03-26 15:15 ` Neil Armstrong
@ 2019-03-26 15:15   ` Neil Armstrong
  -1 siblings, 0 replies; 14+ messages in thread
From: Neil Armstrong @ 2019-03-26 15:15 UTC (permalink / raw)
  To: u-boot

Adds support for Amlogic G12A USB Device mode.

The DWC2 Controller behind the Glue can be connected to an OTG
capable PHY. The Glue setups the PHY mode.

This patch implements Device mode support by adding a board_usb_init/cleanup
setting up the DWC2 controller and switch the OTG capable port to Device
before starting the DWC2 controller in Device mode.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 arch/arm/include/asm/arch-meson/usb.h |  12 +++
 arch/arm/mach-meson/board-g12a.c      | 126 ++++++++++++++++++++++++++
 2 files changed, 138 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-meson/usb.h

diff --git a/arch/arm/include/asm/arch-meson/usb.h b/arch/arm/include/asm/arch-meson/usb.h
new file mode 100644
index 0000000000..b794b5ce77
--- /dev/null
+++ b/arch/arm/include/asm/arch-meson/usb.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#ifndef __MESON_USB_H__
+#define __MESON_USB_H__
+
+int dwc3_meson_g12a_force_mode(struct udevice *dev, enum usb_dr_mode mode);
+
+#endif /* __MESON_USB_H__ */
diff --git a/arch/arm/mach-meson/board-g12a.c b/arch/arm/mach-meson/board-g12a.c
index fc3764b960..1652970fbd 100644
--- a/arch/arm/mach-meson/board-g12a.c
+++ b/arch/arm/mach-meson/board-g12a.c
@@ -12,7 +12,12 @@
 #include <asm/io.h>
 #include <asm/armv8/mmu.h>
 #include <linux/sizes.h>
+#include <usb.h>
+#include <linux/usb/otg.h>
+#include <asm/arch/usb.h>
+#include <usb/dwc2_udc.h>
 #include <phy.h>
+#include <clk.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -148,3 +153,124 @@ void meson_eth_init(phy_interface_t mode, unsigned int flags)
 	/* Enable power gate */
 	clrbits_le32(G12A_MEM_PD_REG_0, G12A_MEM_PD_REG_0_ETH_MASK);
 }
+
+#if CONFIG_IS_ENABLED(USB_DWC3_MESON_G12A) && \
+	CONFIG_IS_ENABLED(USB_GADGET_DWC2_OTG)
+static struct dwc2_plat_otg_data meson_g12a_dwc2_data;
+
+int board_usb_init(int index, enum usb_init_type init)
+{
+	struct fdtdec_phandle_args args;
+	const void *blob = gd->fdt_blob;
+	int node, dwc2_node;
+	struct udevice *dev, *clk_dev;
+	struct clk clk;
+	int ret;
+
+	/* find the usb glue node */
+	node = fdt_node_offset_by_compatible(blob, -1,
+					     "amlogic,meson-g12a-usb-ctrl");
+	if (node < 0) {
+		debug("Not found usb-control node\n");
+		return -ENODEV;
+	}
+
+	if (!fdtdec_get_is_enabled(blob, node)) {
+		debug("usb is disabled in the device tree\n");
+		return -ENODEV;
+	}
+
+	ret = uclass_get_device_by_of_offset(UCLASS_SIMPLE_BUS, node, &dev);
+	if (ret) {
+		debug("Not found usb-control device\n");
+		return ret;
+	}
+
+	/* find the dwc2 node */
+	dwc2_node = fdt_node_offset_by_compatible(blob, node,
+						  "amlogic,meson-g12a-usb");
+	if (dwc2_node < 0) {
+		debug("Not found dwc2 node\n");
+		return -ENODEV;
+	}
+
+	if (!fdtdec_get_is_enabled(blob, dwc2_node)) {
+		debug("dwc2 is disabled in the device tree\n");
+		return -ENODEV;
+	}
+
+	meson_g12a_dwc2_data.regs_otg = fdtdec_get_addr(blob, dwc2_node, "reg");
+	if (meson_g12a_dwc2_data.regs_otg == FDT_ADDR_T_NONE) {
+		debug("usbotg: can't get base address\n");
+		return -ENODATA;
+	}
+
+	/* Enable clock */
+	ret = fdtdec_parse_phandle_with_args(blob, dwc2_node, "clocks",
+					     "#clock-cells", 0, 0, &args);
+	if (ret) {
+		debug("usbotg has no clocks defined in the device tree\n");
+		return ret;
+	}
+
+	ret = uclass_get_device_by_of_offset(UCLASS_CLK, args.node, &clk_dev);
+	if (ret)
+		return ret;
+
+	if (args.args_count != 1) {
+		debug("Can't find clock ID in the device tree\n");
+		return -ENODATA;
+	}
+
+	clk.dev = clk_dev;
+	clk.id = args.args[0];
+
+	ret = clk_enable(&clk);
+	if (ret) {
+		debug("Failed to enable usbotg clock\n");
+		return ret;
+	}
+
+	meson_g12a_dwc2_data.rx_fifo_sz = fdtdec_get_int(blob, dwc2_node,
+						     "g-rx-fifo-size", 0);
+	meson_g12a_dwc2_data.np_tx_fifo_sz = fdtdec_get_int(blob, dwc2_node,
+							"g-np-tx-fifo-size", 0);
+	meson_g12a_dwc2_data.tx_fifo_sz = fdtdec_get_int(blob, dwc2_node,
+						     "g-tx-fifo-size", 0);
+
+	/* Switch to peripheral mode */
+	ret = dwc3_meson_g12a_force_mode(dev, USB_DR_MODE_PERIPHERAL);
+	if (ret)
+		return ret;
+
+	return dwc2_udc_probe(&meson_g12a_dwc2_data);
+}
+
+int board_usb_cleanup(int index, enum usb_init_type init)
+{
+	const void *blob = gd->fdt_blob;
+	struct udevice *dev;
+	int node;
+	int ret;
+
+	/* find the usb glue node */
+	node = fdt_node_offset_by_compatible(blob, -1,
+					     "amlogic,meson-g12a-usb-ctrl");
+	if (node < 0)
+		return -ENODEV;
+
+	if (!fdtdec_get_is_enabled(blob, node))
+		return -ENODEV;
+
+	ret = uclass_get_device_by_of_offset(UCLASS_SIMPLE_BUS, node, &dev);
+	if (ret)
+		return ret;
+
+	/* Switch to OTG mode */
+	ret = dwc3_meson_g12a_force_mode(dev, USB_DR_MODE_HOST);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+#endif
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 3/3] mach-meson: g12a: add DWC2 peripheral mode support
@ 2019-03-26 15:15   ` Neil Armstrong
  0 siblings, 0 replies; 14+ messages in thread
From: Neil Armstrong @ 2019-03-26 15:15 UTC (permalink / raw)
  To: marex; +Cc: Neil Armstrong, u-boot-amlogic, u-boot

Adds support for Amlogic G12A USB Device mode.

The DWC2 Controller behind the Glue can be connected to an OTG
capable PHY. The Glue setups the PHY mode.

This patch implements Device mode support by adding a board_usb_init/cleanup
setting up the DWC2 controller and switch the OTG capable port to Device
before starting the DWC2 controller in Device mode.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 arch/arm/include/asm/arch-meson/usb.h |  12 +++
 arch/arm/mach-meson/board-g12a.c      | 126 ++++++++++++++++++++++++++
 2 files changed, 138 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-meson/usb.h

diff --git a/arch/arm/include/asm/arch-meson/usb.h b/arch/arm/include/asm/arch-meson/usb.h
new file mode 100644
index 0000000000..b794b5ce77
--- /dev/null
+++ b/arch/arm/include/asm/arch-meson/usb.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#ifndef __MESON_USB_H__
+#define __MESON_USB_H__
+
+int dwc3_meson_g12a_force_mode(struct udevice *dev, enum usb_dr_mode mode);
+
+#endif /* __MESON_USB_H__ */
diff --git a/arch/arm/mach-meson/board-g12a.c b/arch/arm/mach-meson/board-g12a.c
index fc3764b960..1652970fbd 100644
--- a/arch/arm/mach-meson/board-g12a.c
+++ b/arch/arm/mach-meson/board-g12a.c
@@ -12,7 +12,12 @@
 #include <asm/io.h>
 #include <asm/armv8/mmu.h>
 #include <linux/sizes.h>
+#include <usb.h>
+#include <linux/usb/otg.h>
+#include <asm/arch/usb.h>
+#include <usb/dwc2_udc.h>
 #include <phy.h>
+#include <clk.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -148,3 +153,124 @@ void meson_eth_init(phy_interface_t mode, unsigned int flags)
 	/* Enable power gate */
 	clrbits_le32(G12A_MEM_PD_REG_0, G12A_MEM_PD_REG_0_ETH_MASK);
 }
+
+#if CONFIG_IS_ENABLED(USB_DWC3_MESON_G12A) && \
+	CONFIG_IS_ENABLED(USB_GADGET_DWC2_OTG)
+static struct dwc2_plat_otg_data meson_g12a_dwc2_data;
+
+int board_usb_init(int index, enum usb_init_type init)
+{
+	struct fdtdec_phandle_args args;
+	const void *blob = gd->fdt_blob;
+	int node, dwc2_node;
+	struct udevice *dev, *clk_dev;
+	struct clk clk;
+	int ret;
+
+	/* find the usb glue node */
+	node = fdt_node_offset_by_compatible(blob, -1,
+					     "amlogic,meson-g12a-usb-ctrl");
+	if (node < 0) {
+		debug("Not found usb-control node\n");
+		return -ENODEV;
+	}
+
+	if (!fdtdec_get_is_enabled(blob, node)) {
+		debug("usb is disabled in the device tree\n");
+		return -ENODEV;
+	}
+
+	ret = uclass_get_device_by_of_offset(UCLASS_SIMPLE_BUS, node, &dev);
+	if (ret) {
+		debug("Not found usb-control device\n");
+		return ret;
+	}
+
+	/* find the dwc2 node */
+	dwc2_node = fdt_node_offset_by_compatible(blob, node,
+						  "amlogic,meson-g12a-usb");
+	if (dwc2_node < 0) {
+		debug("Not found dwc2 node\n");
+		return -ENODEV;
+	}
+
+	if (!fdtdec_get_is_enabled(blob, dwc2_node)) {
+		debug("dwc2 is disabled in the device tree\n");
+		return -ENODEV;
+	}
+
+	meson_g12a_dwc2_data.regs_otg = fdtdec_get_addr(blob, dwc2_node, "reg");
+	if (meson_g12a_dwc2_data.regs_otg == FDT_ADDR_T_NONE) {
+		debug("usbotg: can't get base address\n");
+		return -ENODATA;
+	}
+
+	/* Enable clock */
+	ret = fdtdec_parse_phandle_with_args(blob, dwc2_node, "clocks",
+					     "#clock-cells", 0, 0, &args);
+	if (ret) {
+		debug("usbotg has no clocks defined in the device tree\n");
+		return ret;
+	}
+
+	ret = uclass_get_device_by_of_offset(UCLASS_CLK, args.node, &clk_dev);
+	if (ret)
+		return ret;
+
+	if (args.args_count != 1) {
+		debug("Can't find clock ID in the device tree\n");
+		return -ENODATA;
+	}
+
+	clk.dev = clk_dev;
+	clk.id = args.args[0];
+
+	ret = clk_enable(&clk);
+	if (ret) {
+		debug("Failed to enable usbotg clock\n");
+		return ret;
+	}
+
+	meson_g12a_dwc2_data.rx_fifo_sz = fdtdec_get_int(blob, dwc2_node,
+						     "g-rx-fifo-size", 0);
+	meson_g12a_dwc2_data.np_tx_fifo_sz = fdtdec_get_int(blob, dwc2_node,
+							"g-np-tx-fifo-size", 0);
+	meson_g12a_dwc2_data.tx_fifo_sz = fdtdec_get_int(blob, dwc2_node,
+						     "g-tx-fifo-size", 0);
+
+	/* Switch to peripheral mode */
+	ret = dwc3_meson_g12a_force_mode(dev, USB_DR_MODE_PERIPHERAL);
+	if (ret)
+		return ret;
+
+	return dwc2_udc_probe(&meson_g12a_dwc2_data);
+}
+
+int board_usb_cleanup(int index, enum usb_init_type init)
+{
+	const void *blob = gd->fdt_blob;
+	struct udevice *dev;
+	int node;
+	int ret;
+
+	/* find the usb glue node */
+	node = fdt_node_offset_by_compatible(blob, -1,
+					     "amlogic,meson-g12a-usb-ctrl");
+	if (node < 0)
+		return -ENODEV;
+
+	if (!fdtdec_get_is_enabled(blob, node))
+		return -ENODEV;
+
+	ret = uclass_get_device_by_of_offset(UCLASS_SIMPLE_BUS, node, &dev);
+	if (ret)
+		return ret;
+
+	/* Switch to OTG mode */
+	ret = dwc3_meson_g12a_force_mode(dev, USB_DR_MODE_HOST);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+#endif
-- 
2.21.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [U-Boot] [PATCH 1/3] usb: dwc3: Add Meson G12A USB Glue
  2019-03-26 15:15   ` Neil Armstrong
@ 2019-03-26 15:53     ` Marek Vasut
  -1 siblings, 0 replies; 14+ messages in thread
From: Marek Vasut @ 2019-03-26 15:53 UTC (permalink / raw)
  To: u-boot

On 3/26/19 4:15 PM, Neil Armstrong wrote:
> Adds support for Amlogic G12A USB Control Glue HW.
> 
> The Amlogic G12A SoC Family embeds 2 USB Controllers :
> - a DWC3 IP configured as Host for USB2 and USB3
> - a DWC2 IP configured as Peripheral USB2 Only
> 
> A glue connects these both controllers to 2 USB2 PHYs, and optionnally
> to an USB3+PCIE Combo PHY shared with the PCIE controller.
> 
> The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
> routing of the OTG PHY between the DWC3 and DWC2 controllers, and
> setups the on-chip OTG mode selection for this PHY.
> 
> This driver sets the OTG capable port as Host mode by default,
> the switch to Device mode is to be done in a separate patch.
> 
> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
> ---

[...]

> +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
> +{
> +	int i, ret;
> +
> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
> +		ret = generic_phy_get_by_name(priv->dev, phy_names[i],
> +					      &priv->phys[i]);
> +		if (ret == -ENOENT)
> +			continue;
> +
> +		if (ret)
> +			return ret;
> +
> +		if (i == USB3_HOST_PHY)
> +			priv->usb3_ports++;
> +		else
> +			priv->usb2_ports++;
> +	}
> +
> +	printf("%s: usb2 ports: %d\n", __func__, priv->usb2_ports);
> +	printf("%s: usb3 ports: %d\n", __func__, priv->usb3_ports);

Are these prints needed ?

> +	return 0;
> +}

[...]
> +static int dwc3_meson_g12a_probe(struct udevice *dev)
> +{
> +	struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
> +	int ret, i;
> +
> +	priv->dev = dev;
> +
> +	ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
> +	if (ret)
> +		return ret;
> +
> +	ret = dwc3_meson_g12a_clk_init(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = dwc3_meson_g12a_reset_init(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = dwc3_meson_g12a_get_phys(priv);
> +	if (ret)
> +		return ret;
> +
> +#if CONFIG_IS_ENABLED(DM_REGULATOR)
> +	ret = device_get_supply_regulator(dev, "vbus-supply",
> +					  &priv->vbus_supply);
> +	if (ret && ret != -ENOENT) {
> +		pr_err("Failed to get PHY regulator\n");
> +		return ret;
> +	}
> +
> +	if (priv->vbus_supply) {
> +		ret = regulator_set_enable(priv->vbus_supply, true);
> +		if (ret)
> +			return ret;
> +	}
> +#endif
> +
> +	priv->otg_mode = usb_get_dr_mode(dev_of_offset(dev));
> +
> +	ret = dwc3_meson_g12a_usb_init(priv);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
> +		if (!priv->phys[i].dev)
> +			continue;
> +
> +		ret = generic_phy_init(&priv->phys[i]);
> +		if (ret)
> +			goto err_phy_init;
> +	}

Isn't there some function to init all the PHYs already ?

-- 
Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/3] usb: dwc3: Add Meson G12A USB Glue
@ 2019-03-26 15:53     ` Marek Vasut
  0 siblings, 0 replies; 14+ messages in thread
From: Marek Vasut @ 2019-03-26 15:53 UTC (permalink / raw)
  To: Neil Armstrong; +Cc: u-boot-amlogic, u-boot

On 3/26/19 4:15 PM, Neil Armstrong wrote:
> Adds support for Amlogic G12A USB Control Glue HW.
> 
> The Amlogic G12A SoC Family embeds 2 USB Controllers :
> - a DWC3 IP configured as Host for USB2 and USB3
> - a DWC2 IP configured as Peripheral USB2 Only
> 
> A glue connects these both controllers to 2 USB2 PHYs, and optionnally
> to an USB3+PCIE Combo PHY shared with the PCIE controller.
> 
> The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
> routing of the OTG PHY between the DWC3 and DWC2 controllers, and
> setups the on-chip OTG mode selection for this PHY.
> 
> This driver sets the OTG capable port as Host mode by default,
> the switch to Device mode is to be done in a separate patch.
> 
> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
> ---

[...]

> +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
> +{
> +	int i, ret;
> +
> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
> +		ret = generic_phy_get_by_name(priv->dev, phy_names[i],
> +					      &priv->phys[i]);
> +		if (ret == -ENOENT)
> +			continue;
> +
> +		if (ret)
> +			return ret;
> +
> +		if (i == USB3_HOST_PHY)
> +			priv->usb3_ports++;
> +		else
> +			priv->usb2_ports++;
> +	}
> +
> +	printf("%s: usb2 ports: %d\n", __func__, priv->usb2_ports);
> +	printf("%s: usb3 ports: %d\n", __func__, priv->usb3_ports);

Are these prints needed ?

> +	return 0;
> +}

[...]
> +static int dwc3_meson_g12a_probe(struct udevice *dev)
> +{
> +	struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
> +	int ret, i;
> +
> +	priv->dev = dev;
> +
> +	ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
> +	if (ret)
> +		return ret;
> +
> +	ret = dwc3_meson_g12a_clk_init(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = dwc3_meson_g12a_reset_init(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = dwc3_meson_g12a_get_phys(priv);
> +	if (ret)
> +		return ret;
> +
> +#if CONFIG_IS_ENABLED(DM_REGULATOR)
> +	ret = device_get_supply_regulator(dev, "vbus-supply",
> +					  &priv->vbus_supply);
> +	if (ret && ret != -ENOENT) {
> +		pr_err("Failed to get PHY regulator\n");
> +		return ret;
> +	}
> +
> +	if (priv->vbus_supply) {
> +		ret = regulator_set_enable(priv->vbus_supply, true);
> +		if (ret)
> +			return ret;
> +	}
> +#endif
> +
> +	priv->otg_mode = usb_get_dr_mode(dev_of_offset(dev));
> +
> +	ret = dwc3_meson_g12a_usb_init(priv);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
> +		if (!priv->phys[i].dev)
> +			continue;
> +
> +		ret = generic_phy_init(&priv->phys[i]);
> +		if (ret)
> +			goto err_phy_init;
> +	}

Isn't there some function to init all the PHYs already ?

-- 
Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [U-Boot] [PATCH 1/3] usb: dwc3: Add Meson G12A USB Glue
  2019-03-26 15:53     ` Marek Vasut
@ 2019-03-27  9:12       ` Neil Armstrong
  -1 siblings, 0 replies; 14+ messages in thread
From: Neil Armstrong @ 2019-03-27  9:12 UTC (permalink / raw)
  To: u-boot

On 26/03/2019 16:53, Marek Vasut wrote:
> On 3/26/19 4:15 PM, Neil Armstrong wrote:
>> Adds support for Amlogic G12A USB Control Glue HW.
>>
>> The Amlogic G12A SoC Family embeds 2 USB Controllers :
>> - a DWC3 IP configured as Host for USB2 and USB3
>> - a DWC2 IP configured as Peripheral USB2 Only
>>
>> A glue connects these both controllers to 2 USB2 PHYs, and optionnally
>> to an USB3+PCIE Combo PHY shared with the PCIE controller.
>>
>> The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
>> routing of the OTG PHY between the DWC3 and DWC2 controllers, and
>> setups the on-chip OTG mode selection for this PHY.
>>
>> This driver sets the OTG capable port as Host mode by default,
>> the switch to Device mode is to be done in a separate patch.
>>
>> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
>> ---
> 
> [...]
> 
>> +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
>> +{
>> +	int i, ret;
>> +
>> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
>> +		ret = generic_phy_get_by_name(priv->dev, phy_names[i],
>> +					      &priv->phys[i]);
>> +		if (ret == -ENOENT)
>> +			continue;
>> +
>> +		if (ret)
>> +			return ret;
>> +
>> +		if (i == USB3_HOST_PHY)
>> +			priv->usb3_ports++;
>> +		else
>> +			priv->usb2_ports++;
>> +	}
>> +
>> +	printf("%s: usb2 ports: %d\n", __func__, priv->usb2_ports);
>> +	printf("%s: usb3 ports: %d\n", __func__, priv->usb3_ports);
> 
> Are these prints needed ?

Nop, forgot to switch them to debug

> 
>> +	return 0;
>> +}
> 
> [...]
>> +static int dwc3_meson_g12a_probe(struct udevice *dev)
>> +{
>> +	struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
>> +	int ret, i;
>> +
>> +	priv->dev = dev;
>> +
>> +	ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = dwc3_meson_g12a_clk_init(priv);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = dwc3_meson_g12a_reset_init(priv);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = dwc3_meson_g12a_get_phys(priv);
>> +	if (ret)
>> +		return ret;
>> +
>> +#if CONFIG_IS_ENABLED(DM_REGULATOR)
>> +	ret = device_get_supply_regulator(dev, "vbus-supply",
>> +					  &priv->vbus_supply);
>> +	if (ret && ret != -ENOENT) {
>> +		pr_err("Failed to get PHY regulator\n");
>> +		return ret;
>> +	}
>> +
>> +	if (priv->vbus_supply) {
>> +		ret = regulator_set_enable(priv->vbus_supply, true);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +#endif
>> +
>> +	priv->otg_mode = usb_get_dr_mode(dev_of_offset(dev));
>> +
>> +	ret = dwc3_meson_g12a_usb_init(priv);
>> +	if (ret)
>> +		return ret;
>> +
>> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
>> +		if (!priv->phys[i].dev)
>> +			continue;
>> +
>> +		ret = generic_phy_init(&priv->phys[i]);
>> +		if (ret)
>> +			goto err_phy_init;
>> +	}
> 
> Isn't there some function to init all the PHYs already ?
> 

I don't see any in u-boot master !
Unlike dwc3, we need to get the phys with the names to check if
the USB3 phy is enabled, so we can't get all possible phys.

Neil

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/3] usb: dwc3: Add Meson G12A USB Glue
@ 2019-03-27  9:12       ` Neil Armstrong
  0 siblings, 0 replies; 14+ messages in thread
From: Neil Armstrong @ 2019-03-27  9:12 UTC (permalink / raw)
  To: Marek Vasut; +Cc: u-boot-amlogic, u-boot

On 26/03/2019 16:53, Marek Vasut wrote:
> On 3/26/19 4:15 PM, Neil Armstrong wrote:
>> Adds support for Amlogic G12A USB Control Glue HW.
>>
>> The Amlogic G12A SoC Family embeds 2 USB Controllers :
>> - a DWC3 IP configured as Host for USB2 and USB3
>> - a DWC2 IP configured as Peripheral USB2 Only
>>
>> A glue connects these both controllers to 2 USB2 PHYs, and optionnally
>> to an USB3+PCIE Combo PHY shared with the PCIE controller.
>>
>> The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
>> routing of the OTG PHY between the DWC3 and DWC2 controllers, and
>> setups the on-chip OTG mode selection for this PHY.
>>
>> This driver sets the OTG capable port as Host mode by default,
>> the switch to Device mode is to be done in a separate patch.
>>
>> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
>> ---
> 
> [...]
> 
>> +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
>> +{
>> +	int i, ret;
>> +
>> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
>> +		ret = generic_phy_get_by_name(priv->dev, phy_names[i],
>> +					      &priv->phys[i]);
>> +		if (ret == -ENOENT)
>> +			continue;
>> +
>> +		if (ret)
>> +			return ret;
>> +
>> +		if (i == USB3_HOST_PHY)
>> +			priv->usb3_ports++;
>> +		else
>> +			priv->usb2_ports++;
>> +	}
>> +
>> +	printf("%s: usb2 ports: %d\n", __func__, priv->usb2_ports);
>> +	printf("%s: usb3 ports: %d\n", __func__, priv->usb3_ports);
> 
> Are these prints needed ?

Nop, forgot to switch them to debug

> 
>> +	return 0;
>> +}
> 
> [...]
>> +static int dwc3_meson_g12a_probe(struct udevice *dev)
>> +{
>> +	struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
>> +	int ret, i;
>> +
>> +	priv->dev = dev;
>> +
>> +	ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = dwc3_meson_g12a_clk_init(priv);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = dwc3_meson_g12a_reset_init(priv);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = dwc3_meson_g12a_get_phys(priv);
>> +	if (ret)
>> +		return ret;
>> +
>> +#if CONFIG_IS_ENABLED(DM_REGULATOR)
>> +	ret = device_get_supply_regulator(dev, "vbus-supply",
>> +					  &priv->vbus_supply);
>> +	if (ret && ret != -ENOENT) {
>> +		pr_err("Failed to get PHY regulator\n");
>> +		return ret;
>> +	}
>> +
>> +	if (priv->vbus_supply) {
>> +		ret = regulator_set_enable(priv->vbus_supply, true);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +#endif
>> +
>> +	priv->otg_mode = usb_get_dr_mode(dev_of_offset(dev));
>> +
>> +	ret = dwc3_meson_g12a_usb_init(priv);
>> +	if (ret)
>> +		return ret;
>> +
>> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
>> +		if (!priv->phys[i].dev)
>> +			continue;
>> +
>> +		ret = generic_phy_init(&priv->phys[i]);
>> +		if (ret)
>> +			goto err_phy_init;
>> +	}
> 
> Isn't there some function to init all the PHYs already ?
> 

I don't see any in u-boot master !
Unlike dwc3, we need to get the phys with the names to check if
the USB3 phy is enabled, so we can't get all possible phys.

Neil



^ permalink raw reply	[flat|nested] 14+ messages in thread

* [U-Boot] [PATCH 1/3] usb: dwc3: Add Meson G12A USB Glue
  2019-03-27  9:12       ` Neil Armstrong
@ 2019-03-28  3:27         ` Marek Vasut
  -1 siblings, 0 replies; 14+ messages in thread
From: Marek Vasut @ 2019-03-28  3:27 UTC (permalink / raw)
  To: u-boot

On 3/27/19 10:12 AM, Neil Armstrong wrote:
> On 26/03/2019 16:53, Marek Vasut wrote:
>> On 3/26/19 4:15 PM, Neil Armstrong wrote:
>>> Adds support for Amlogic G12A USB Control Glue HW.
>>>
>>> The Amlogic G12A SoC Family embeds 2 USB Controllers :
>>> - a DWC3 IP configured as Host for USB2 and USB3
>>> - a DWC2 IP configured as Peripheral USB2 Only
>>>
>>> A glue connects these both controllers to 2 USB2 PHYs, and optionnally
>>> to an USB3+PCIE Combo PHY shared with the PCIE controller.
>>>
>>> The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
>>> routing of the OTG PHY between the DWC3 and DWC2 controllers, and
>>> setups the on-chip OTG mode selection for this PHY.
>>>
>>> This driver sets the OTG capable port as Host mode by default,
>>> the switch to Device mode is to be done in a separate patch.
>>>
>>> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
>>> ---
>>
>> [...]
>>
>>> +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
>>> +{
>>> +	int i, ret;
>>> +
>>> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
>>> +		ret = generic_phy_get_by_name(priv->dev, phy_names[i],
>>> +					      &priv->phys[i]);
>>> +		if (ret == -ENOENT)
>>> +			continue;
>>> +
>>> +		if (ret)
>>> +			return ret;
>>> +
>>> +		if (i == USB3_HOST_PHY)
>>> +			priv->usb3_ports++;
>>> +		else
>>> +			priv->usb2_ports++;
>>> +	}
>>> +
>>> +	printf("%s: usb2 ports: %d\n", __func__, priv->usb2_ports);
>>> +	printf("%s: usb3 ports: %d\n", __func__, priv->usb3_ports);
>>
>> Are these prints needed ?
> 
> Nop, forgot to switch them to debug
> 
>>
>>> +	return 0;
>>> +}
>>
>> [...]
>>> +static int dwc3_meson_g12a_probe(struct udevice *dev)
>>> +{
>>> +	struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
>>> +	int ret, i;
>>> +
>>> +	priv->dev = dev;
>>> +
>>> +	ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	ret = dwc3_meson_g12a_clk_init(priv);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	ret = dwc3_meson_g12a_reset_init(priv);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	ret = dwc3_meson_g12a_get_phys(priv);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +#if CONFIG_IS_ENABLED(DM_REGULATOR)
>>> +	ret = device_get_supply_regulator(dev, "vbus-supply",
>>> +					  &priv->vbus_supply);
>>> +	if (ret && ret != -ENOENT) {
>>> +		pr_err("Failed to get PHY regulator\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	if (priv->vbus_supply) {
>>> +		ret = regulator_set_enable(priv->vbus_supply, true);
>>> +		if (ret)
>>> +			return ret;
>>> +	}
>>> +#endif
>>> +
>>> +	priv->otg_mode = usb_get_dr_mode(dev_of_offset(dev));
>>> +
>>> +	ret = dwc3_meson_g12a_usb_init(priv);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
>>> +		if (!priv->phys[i].dev)
>>> +			continue;
>>> +
>>> +		ret = generic_phy_init(&priv->phys[i]);
>>> +		if (ret)
>>> +			goto err_phy_init;
>>> +	}
>>
>> Isn't there some function to init all the PHYs already ?
>>
> 
> I don't see any in u-boot master !
> Unlike dwc3, we need to get the phys with the names to check if
> the USB3 phy is enabled, so we can't get all possible phys.

I see, so thus far my only comment is about the missed debug prints.
Thanks!

-- 
Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/3] usb: dwc3: Add Meson G12A USB Glue
@ 2019-03-28  3:27         ` Marek Vasut
  0 siblings, 0 replies; 14+ messages in thread
From: Marek Vasut @ 2019-03-28  3:27 UTC (permalink / raw)
  To: Neil Armstrong; +Cc: u-boot-amlogic, u-boot

On 3/27/19 10:12 AM, Neil Armstrong wrote:
> On 26/03/2019 16:53, Marek Vasut wrote:
>> On 3/26/19 4:15 PM, Neil Armstrong wrote:
>>> Adds support for Amlogic G12A USB Control Glue HW.
>>>
>>> The Amlogic G12A SoC Family embeds 2 USB Controllers :
>>> - a DWC3 IP configured as Host for USB2 and USB3
>>> - a DWC2 IP configured as Peripheral USB2 Only
>>>
>>> A glue connects these both controllers to 2 USB2 PHYs, and optionnally
>>> to an USB3+PCIE Combo PHY shared with the PCIE controller.
>>>
>>> The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
>>> routing of the OTG PHY between the DWC3 and DWC2 controllers, and
>>> setups the on-chip OTG mode selection for this PHY.
>>>
>>> This driver sets the OTG capable port as Host mode by default,
>>> the switch to Device mode is to be done in a separate patch.
>>>
>>> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
>>> ---
>>
>> [...]
>>
>>> +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
>>> +{
>>> +	int i, ret;
>>> +
>>> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
>>> +		ret = generic_phy_get_by_name(priv->dev, phy_names[i],
>>> +					      &priv->phys[i]);
>>> +		if (ret == -ENOENT)
>>> +			continue;
>>> +
>>> +		if (ret)
>>> +			return ret;
>>> +
>>> +		if (i == USB3_HOST_PHY)
>>> +			priv->usb3_ports++;
>>> +		else
>>> +			priv->usb2_ports++;
>>> +	}
>>> +
>>> +	printf("%s: usb2 ports: %d\n", __func__, priv->usb2_ports);
>>> +	printf("%s: usb3 ports: %d\n", __func__, priv->usb3_ports);
>>
>> Are these prints needed ?
> 
> Nop, forgot to switch them to debug
> 
>>
>>> +	return 0;
>>> +}
>>
>> [...]
>>> +static int dwc3_meson_g12a_probe(struct udevice *dev)
>>> +{
>>> +	struct dwc3_meson_g12a *priv = dev_get_platdata(dev);
>>> +	int ret, i;
>>> +
>>> +	priv->dev = dev;
>>> +
>>> +	ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	ret = dwc3_meson_g12a_clk_init(priv);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	ret = dwc3_meson_g12a_reset_init(priv);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	ret = dwc3_meson_g12a_get_phys(priv);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +#if CONFIG_IS_ENABLED(DM_REGULATOR)
>>> +	ret = device_get_supply_regulator(dev, "vbus-supply",
>>> +					  &priv->vbus_supply);
>>> +	if (ret && ret != -ENOENT) {
>>> +		pr_err("Failed to get PHY regulator\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	if (priv->vbus_supply) {
>>> +		ret = regulator_set_enable(priv->vbus_supply, true);
>>> +		if (ret)
>>> +			return ret;
>>> +	}
>>> +#endif
>>> +
>>> +	priv->otg_mode = usb_get_dr_mode(dev_of_offset(dev));
>>> +
>>> +	ret = dwc3_meson_g12a_usb_init(priv);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
>>> +		if (!priv->phys[i].dev)
>>> +			continue;
>>> +
>>> +		ret = generic_phy_init(&priv->phys[i]);
>>> +		if (ret)
>>> +			goto err_phy_init;
>>> +	}
>>
>> Isn't there some function to init all the PHYs already ?
>>
> 
> I don't see any in u-boot master !
> Unlike dwc3, we need to get the phys with the names to check if
> the USB3 phy is enabled, so we can't get all possible phys.

I see, so thus far my only comment is about the missed debug prints.
Thanks!

-- 
Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2019-03-28  3:27 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-26 15:15 [U-Boot] [PATCH 0/3] ARM: meson: Add G12A USB Support Neil Armstrong
2019-03-26 15:15 ` Neil Armstrong
2019-03-26 15:15 ` [U-Boot] [PATCH 1/3] usb: dwc3: Add Meson G12A USB Glue Neil Armstrong
2019-03-26 15:15   ` Neil Armstrong
2019-03-26 15:53   ` [U-Boot] " Marek Vasut
2019-03-26 15:53     ` Marek Vasut
2019-03-27  9:12     ` [U-Boot] " Neil Armstrong
2019-03-27  9:12       ` Neil Armstrong
2019-03-28  3:27       ` [U-Boot] " Marek Vasut
2019-03-28  3:27         ` Marek Vasut
2019-03-26 15:15 ` [U-Boot] [PATCH 2/3] phy: meson: add Amlogic G12A USB2 and USB3+PCIE PHY drivers Neil Armstrong
2019-03-26 15:15   ` Neil Armstrong
2019-03-26 15:15 ` [U-Boot] [PATCH 3/3] mach-meson: g12a: add DWC2 peripheral mode support Neil Armstrong
2019-03-26 15:15   ` Neil Armstrong

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.