[PATCH 1/5] IPQ40xx: Add SMEM support

There is already existing driver for SMEM so lets enable it for IPQ40xx as well.
Signed-off-by: Robert Marko robert.marko@sartura.hr --- arch/arm/Kconfig | 2 ++ arch/arm/dts/qcom-ipq4019.dtsi | 5 +++++ drivers/smem/Kconfig | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 8401851666..52c45292e9 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -767,8 +767,10 @@ config ARCH_IPQ40XX select DM select DM_GPIO select DM_SERIAL + select MSM_SMEM select PINCTRL select CLK + select SMEM select OF_CONTROL imply CMD_DM
diff --git a/arch/arm/dts/qcom-ipq4019.dtsi b/arch/arm/dts/qcom-ipq4019.dtsi index 5f78bc5ab9..9d21b15bb3 100644 --- a/arch/arm/dts/qcom-ipq4019.dtsi +++ b/arch/arm/dts/qcom-ipq4019.dtsi @@ -38,6 +38,11 @@ }; };
+ smem { + compatible = "qcom,smem"; + memory-region = <&smem_mem>; + }; + soc: soc { #address-cells = <1>; #size-cells = <1>; diff --git a/drivers/smem/Kconfig b/drivers/smem/Kconfig index 7169d0f205..73d51b3a7a 100644 --- a/drivers/smem/Kconfig +++ b/drivers/smem/Kconfig @@ -15,7 +15,7 @@ config SANDBOX_SMEM config MSM_SMEM bool "Qualcomm Shared Memory Manager (SMEM)" depends on DM - depends on ARCH_SNAPDRAGON + depends on ARCH_SNAPDRAGON || ARCH_IPQ40XX help Enable support for the Qualcomm Shared Memory Manager. The driver provides an interface to items in a heap shared among all

On Qualcomm IPQ40xx SoC series, GCC clock IP also handles the resets. So since this will be needed by further drivers, lets add a driver for the reset controller.
Signed-off-by: Robert Marko robert.marko@sartura.hr --- MAINTAINERS | 2 + drivers/reset/Kconfig | 8 + drivers/reset/Makefile | 1 + drivers/reset/reset-ipq40xx.c | 173 ++++++++++++++++++ .../dt-bindings/reset/qcom,ipq40xx-reset.h | 92 ++++++++++ 5 files changed, 276 insertions(+) create mode 100644 drivers/reset/reset-ipq40xx.c create mode 100644 include/dt-bindings/reset/qcom,ipq40xx-reset.h
diff --git a/MAINTAINERS b/MAINTAINERS index 17ac45587b..1a55327406 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -237,6 +237,8 @@ M: Luka Kovacic luka.kovacic@sartura.hr M: Luka Perkov luka.perkov@sartura.hr S: Maintained F: arch/arm/mach-ipq40xx/ +F: include/dt-bindings/reset/qcom,ipq40xx-reset.h +F: drivers/reset/reset-ipq40xx.c
ARM MARVELL KIRKWOOD ARMADA-XP ARMADA-38X ARMADA-37XX ARMADA-7K/8K M: Stefan Roese sr@denx.de diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 253902ff57..ab71ba0eae 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -148,6 +148,14 @@ config RESET_IMX7 help Support for reset controller on i.MX7/8 SoCs.
+config RESET_IPQ40XX + bool "Reset driver for Qualcomm IPQ40xx SoCs" + depends on DM_RESET && ARCH_IPQ40XX + default y + help + Support for reset controller on Qualcomm + IPQ40xx SoCs. + config RESET_SIFIVE bool "Reset Driver for SiFive SoC's" depends on DM_RESET && CLK_SIFIVE_FU540_PRCI && TARGET_SIFIVE_FU540 diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 3c7f066ae3..c0a3e5eff6 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_RESET_MTMIPS) += reset-mtmips.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o obj-$(CONFIG_RESET_HISILICON) += reset-hisilicon.o obj-$(CONFIG_RESET_IMX7) += reset-imx7.o +obj-$(CONFIG_RESET_IPQ40XX) += reset-ipq40xx.o obj-$(CONFIG_RESET_SIFIVE) += reset-sifive.o obj-$(CONFIG_RESET_SYSCON) += reset-syscon.o obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o diff --git a/drivers/reset/reset-ipq40xx.c b/drivers/reset/reset-ipq40xx.c new file mode 100644 index 0000000000..f5eafb0c09 --- /dev/null +++ b/drivers/reset/reset-ipq40xx.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Sartura Ltd. + * + * Author: Robert Marko robert.marko@sartura.hr + * + * Based on Linux driver + */ + +#include <asm/io.h> +#include <common.h> +#include <dm.h> +#include <dt-bindings/reset/qcom,ipq40xx-reset.h> +#include <reset-uclass.h> +#include <linux/bitops.h> +#include <malloc.h> + +struct ipq40xx_reset_priv { + phys_addr_t base; +}; + +struct qcom_reset_map { + unsigned int reg; + u8 bit; +}; + +static const struct qcom_reset_map gcc_ipq4019_resets[] = { + [WIFI0_CPU_INIT_RESET] = { 0x1f008, 5 }, + [WIFI0_RADIO_SRIF_RESET] = { 0x1f008, 4 }, + [WIFI0_RADIO_WARM_RESET] = { 0x1f008, 3 }, + [WIFI0_RADIO_COLD_RESET] = { 0x1f008, 2 }, + [WIFI0_CORE_WARM_RESET] = { 0x1f008, 1 }, + [WIFI0_CORE_COLD_RESET] = { 0x1f008, 0 }, + [WIFI1_CPU_INIT_RESET] = { 0x20008, 5 }, + [WIFI1_RADIO_SRIF_RESET] = { 0x20008, 4 }, + [WIFI1_RADIO_WARM_RESET] = { 0x20008, 3 }, + [WIFI1_RADIO_COLD_RESET] = { 0x20008, 2 }, + [WIFI1_CORE_WARM_RESET] = { 0x20008, 1 }, + [WIFI1_CORE_COLD_RESET] = { 0x20008, 0 }, + [USB3_UNIPHY_PHY_ARES] = { 0x1e038, 5 }, + [USB3_HSPHY_POR_ARES] = { 0x1e038, 4 }, + [USB3_HSPHY_S_ARES] = { 0x1e038, 2 }, + [USB2_HSPHY_POR_ARES] = { 0x1e01c, 4 }, + [USB2_HSPHY_S_ARES] = { 0x1e01c, 2 }, + [PCIE_PHY_AHB_ARES] = { 0x1d010, 11 }, + [PCIE_AHB_ARES] = { 0x1d010, 10 }, + [PCIE_PWR_ARES] = { 0x1d010, 9 }, + [PCIE_PIPE_STICKY_ARES] = { 0x1d010, 8 }, + [PCIE_AXI_M_STICKY_ARES] = { 0x1d010, 7 }, + [PCIE_PHY_ARES] = { 0x1d010, 6 }, + [PCIE_PARF_XPU_ARES] = { 0x1d010, 5 }, + [PCIE_AXI_S_XPU_ARES] = { 0x1d010, 4 }, + [PCIE_AXI_M_VMIDMT_ARES] = { 0x1d010, 3 }, + [PCIE_PIPE_ARES] = { 0x1d010, 2 }, + [PCIE_AXI_S_ARES] = { 0x1d010, 1 }, + [PCIE_AXI_M_ARES] = { 0x1d010, 0 }, + [ESS_RESET] = { 0x12008, 0}, + [GCC_BLSP1_BCR] = {0x01000, 0}, + [GCC_BLSP1_QUP1_BCR] = {0x02000, 0}, + [GCC_BLSP1_UART1_BCR] = {0x02038, 0}, + [GCC_BLSP1_QUP2_BCR] = {0x03008, 0}, + [GCC_BLSP1_UART2_BCR] = {0x03028, 0}, + [GCC_BIMC_BCR] = {0x04000, 0}, + [GCC_TLMM_BCR] = {0x05000, 0}, + [GCC_IMEM_BCR] = {0x0E000, 0}, + [GCC_ESS_BCR] = {0x12008, 0}, + [GCC_PRNG_BCR] = {0x13000, 0}, + [GCC_BOOT_ROM_BCR] = {0x13008, 0}, + [GCC_CRYPTO_BCR] = {0x16000, 0}, + [GCC_SDCC1_BCR] = {0x18000, 0}, + [GCC_SEC_CTRL_BCR] = {0x1A000, 0}, + [GCC_AUDIO_BCR] = {0x1B008, 0}, + [GCC_QPIC_BCR] = {0x1C000, 0}, + [GCC_PCIE_BCR] = {0x1D000, 0}, + [GCC_USB2_BCR] = {0x1E008, 0}, + [GCC_USB2_PHY_BCR] = {0x1E018, 0}, + [GCC_USB3_BCR] = {0x1E024, 0}, + [GCC_USB3_PHY_BCR] = {0x1E034, 0}, + [GCC_SYSTEM_NOC_BCR] = {0x21000, 0}, + [GCC_PCNOC_BCR] = {0x2102C, 0}, + [GCC_DCD_BCR] = {0x21038, 0}, + [GCC_SNOC_BUS_TIMEOUT0_BCR] = {0x21064, 0}, + [GCC_SNOC_BUS_TIMEOUT1_BCR] = {0x2106C, 0}, + [GCC_SNOC_BUS_TIMEOUT2_BCR] = {0x21074, 0}, + [GCC_SNOC_BUS_TIMEOUT3_BCR] = {0x2107C, 0}, + [GCC_PCNOC_BUS_TIMEOUT0_BCR] = {0x21084, 0}, + [GCC_PCNOC_BUS_TIMEOUT1_BCR] = {0x2108C, 0}, + [GCC_PCNOC_BUS_TIMEOUT2_BCR] = {0x21094, 0}, + [GCC_PCNOC_BUS_TIMEOUT3_BCR] = {0x2109C, 0}, + [GCC_PCNOC_BUS_TIMEOUT4_BCR] = {0x210A4, 0}, + [GCC_PCNOC_BUS_TIMEOUT5_BCR] = {0x210AC, 0}, + [GCC_PCNOC_BUS_TIMEOUT6_BCR] = {0x210B4, 0}, + [GCC_PCNOC_BUS_TIMEOUT7_BCR] = {0x210BC, 0}, + [GCC_PCNOC_BUS_TIMEOUT8_BCR] = {0x210C4, 0}, + [GCC_PCNOC_BUS_TIMEOUT9_BCR] = {0x210CC, 0}, + [GCC_TCSR_BCR] = {0x22000, 0}, + [GCC_MPM_BCR] = {0x24000, 0}, + [GCC_SPDM_BCR] = {0x25000, 0}, +}; + +static int ipq40xx_reset_assert(struct reset_ctl *rst) +{ + struct ipq40xx_reset_priv *priv = dev_get_priv(rst->dev); + const struct qcom_reset_map *reset_map = gcc_ipq4019_resets; + const struct qcom_reset_map *map; + u32 value; + + map = &reset_map[rst->id]; + + value = readl(priv->base + map->reg); + value |= BIT(map->bit); + writel(value, priv->base + map->reg); + + return 0; +} + +static int ipq40xx_reset_deassert(struct reset_ctl *rst) +{ + struct ipq40xx_reset_priv *priv = dev_get_priv(rst->dev); + const struct qcom_reset_map *reset_map = gcc_ipq4019_resets; + const struct qcom_reset_map *map; + u32 value; + + map = &reset_map[rst->id]; + + value = readl(priv->base + map->reg); + value &= ~BIT(map->bit); + writel(value, priv->base + map->reg); + + return 0; +} + +static int ipq40xx_reset_free(struct reset_ctl *rst) +{ + return 0; +} + +static int ipq40xx_reset_request(struct reset_ctl *rst) +{ + return 0; +} + +static const struct reset_ops ipq40xx_reset_ops = { + .request = ipq40xx_reset_request, + .rfree = ipq40xx_reset_free, + .rst_assert = ipq40xx_reset_assert, + .rst_deassert = ipq40xx_reset_deassert, +}; + +static const struct udevice_id ipq40xx_reset_ids[] = { + { .compatible = "qcom,gcc-reset-ipq4019" }, + { } +}; + +static int ipq40xx_reset_probe(struct udevice *dev) +{ + struct ipq40xx_reset_priv *priv = dev_get_priv(dev); + + priv->base = devfdt_get_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + return 0; +} + +U_BOOT_DRIVER(ipq40xx_reset) = { + .name = "ipq40xx_reset", + .id = UCLASS_RESET, + .of_match = ipq40xx_reset_ids, + .ops = &ipq40xx_reset_ops, + .probe = ipq40xx_reset_probe, + .priv_auto_alloc_size = sizeof(struct ipq40xx_reset_priv), +}; diff --git a/include/dt-bindings/reset/qcom,ipq40xx-reset.h b/include/dt-bindings/reset/qcom,ipq40xx-reset.h new file mode 100644 index 0000000000..e6c632f052 --- /dev/null +++ b/include/dt-bindings/reset/qcom,ipq40xx-reset.h @@ -0,0 +1,92 @@ +/* Copyright (c) 2015 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ +#ifndef __QCOM_CLK_IPQ4019_H__ +#define __QCOM_CLK_IPQ4019_H__ + +#define WIFI0_CPU_INIT_RESET 0 +#define WIFI0_RADIO_SRIF_RESET 1 +#define WIFI0_RADIO_WARM_RESET 2 +#define WIFI0_RADIO_COLD_RESET 3 +#define WIFI0_CORE_WARM_RESET 4 +#define WIFI0_CORE_COLD_RESET 5 +#define WIFI1_CPU_INIT_RESET 6 +#define WIFI1_RADIO_SRIF_RESET 7 +#define WIFI1_RADIO_WARM_RESET 8 +#define WIFI1_RADIO_COLD_RESET 9 +#define WIFI1_CORE_WARM_RESET 10 +#define WIFI1_CORE_COLD_RESET 11 +#define USB3_UNIPHY_PHY_ARES 12 +#define USB3_HSPHY_POR_ARES 13 +#define USB3_HSPHY_S_ARES 14 +#define USB2_HSPHY_POR_ARES 15 +#define USB2_HSPHY_S_ARES 16 +#define PCIE_PHY_AHB_ARES 17 +#define PCIE_AHB_ARES 18 +#define PCIE_PWR_ARES 19 +#define PCIE_PIPE_STICKY_ARES 20 +#define PCIE_AXI_M_STICKY_ARES 21 +#define PCIE_PHY_ARES 22 +#define PCIE_PARF_XPU_ARES 23 +#define PCIE_AXI_S_XPU_ARES 24 +#define PCIE_AXI_M_VMIDMT_ARES 25 +#define PCIE_PIPE_ARES 26 +#define PCIE_AXI_S_ARES 27 +#define PCIE_AXI_M_ARES 28 +#define ESS_RESET 29 +#define GCC_BLSP1_BCR 30 +#define GCC_BLSP1_QUP1_BCR 31 +#define GCC_BLSP1_UART1_BCR 32 +#define GCC_BLSP1_QUP2_BCR 33 +#define GCC_BLSP1_UART2_BCR 34 +#define GCC_BIMC_BCR 35 +#define GCC_TLMM_BCR 36 +#define GCC_IMEM_BCR 37 +#define GCC_ESS_BCR 38 +#define GCC_PRNG_BCR 39 +#define GCC_BOOT_ROM_BCR 40 +#define GCC_CRYPTO_BCR 41 +#define GCC_SDCC1_BCR 42 +#define GCC_SEC_CTRL_BCR 43 +#define GCC_AUDIO_BCR 44 +#define GCC_QPIC_BCR 45 +#define GCC_PCIE_BCR 46 +#define GCC_USB2_BCR 47 +#define GCC_USB2_PHY_BCR 48 +#define GCC_USB3_BCR 49 +#define GCC_USB3_PHY_BCR 50 +#define GCC_SYSTEM_NOC_BCR 51 +#define GCC_PCNOC_BCR 52 +#define GCC_DCD_BCR 53 +#define GCC_SNOC_BUS_TIMEOUT0_BCR 54 +#define GCC_SNOC_BUS_TIMEOUT1_BCR 55 +#define GCC_SNOC_BUS_TIMEOUT2_BCR 56 +#define GCC_SNOC_BUS_TIMEOUT3_BCR 57 +#define GCC_PCNOC_BUS_TIMEOUT0_BCR 58 +#define GCC_PCNOC_BUS_TIMEOUT1_BCR 59 +#define GCC_PCNOC_BUS_TIMEOUT2_BCR 60 +#define GCC_PCNOC_BUS_TIMEOUT3_BCR 61 +#define GCC_PCNOC_BUS_TIMEOUT4_BCR 62 +#define GCC_PCNOC_BUS_TIMEOUT5_BCR 63 +#define GCC_PCNOC_BUS_TIMEOUT6_BCR 64 +#define GCC_PCNOC_BUS_TIMEOUT7_BCR 65 +#define GCC_PCNOC_BUS_TIMEOUT8_BCR 66 +#define GCC_PCNOC_BUS_TIMEOUT9_BCR 67 +#define GCC_TCSR_BCR 68 +#define GCC_QDSS_BCR 69 +#define GCC_MPM_BCR 70 +#define GCC_SPDM_BCR 71 + +#endif

Since we have a driver for the reset controller, lets add the necessary node.
Signed-off-by: Robert Marko robert.marko@sartura.hr --- arch/arm/Kconfig | 1 + arch/arm/dts/qcom-ipq4019.dtsi | 9 +++++++++ 2 files changed, 10 insertions(+)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 52c45292e9..a636049687 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -767,6 +767,7 @@ config ARCH_IPQ40XX select DM select DM_GPIO select DM_SERIAL + select DM_RESET select MSM_SMEM select PINCTRL select CLK diff --git a/arch/arm/dts/qcom-ipq4019.dtsi b/arch/arm/dts/qcom-ipq4019.dtsi index 9d21b15bb3..67122de9c0 100644 --- a/arch/arm/dts/qcom-ipq4019.dtsi +++ b/arch/arm/dts/qcom-ipq4019.dtsi @@ -10,6 +10,7 @@ #include "skeleton.dtsi" #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/pinctrl/pinctrl-snapdragon.h> +#include <dt-bindings/reset/qcom,ipq40xx-reset.h>
/ { #address-cells = <1>; @@ -57,6 +58,14 @@ u-boot,dm-pre-reloc; };
+ reset: gcc-reset@1800000 { + compatible = "qcom,gcc-reset-ipq4019"; + reg = <0x1800000 0x60000>; + #clock-cells = <1>; + #reset-cells = <1>; + u-boot,dm-pre-reloc; + }; + pinctrl: qcom,tlmm@1000000 { compatible = "qcom,tlmm-ipq4019"; reg = <0x1000000 0x300000>;

Add driver for Qualcomm DWC3 based dual role xHCI USB controller. Currently tested on IPQ40xx, but should support other Qualcomm SoC families as well.
Signed-off-by: Robert Marko robert.marko@sartura.hr --- MAINTAINERS | 2 + .../usb/qcom-dwc3-ipq.txt | 25 +++ drivers/usb/host/Kconfig | 6 + drivers/usb/host/Makefile | 1 + drivers/usb/host/xhci-ipq.c | 193 ++++++++++++++++++ 5 files changed, 227 insertions(+) create mode 100644 doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt create mode 100644 drivers/usb/host/xhci-ipq.c
diff --git a/MAINTAINERS b/MAINTAINERS index 1a55327406..0e4e281d9b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -239,6 +239,8 @@ S: Maintained F: arch/arm/mach-ipq40xx/ F: include/dt-bindings/reset/qcom,ipq40xx-reset.h F: drivers/reset/reset-ipq40xx.c +F: doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt +F: drivers/usb/host/xhci-ipq.c
ARM MARVELL KIRKWOOD ARMADA-XP ARMADA-38X ARMADA-37XX ARMADA-7K/8K M: Stefan Roese sr@denx.de diff --git a/doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt b/doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt new file mode 100644 index 0000000000..591683e520 --- /dev/null +++ b/doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt @@ -0,0 +1,25 @@ +Qualcomm SuperSpeed DWC3 USB SoC controller + +This controller is integrated in IPQ40xx SoC-s. +It is a dual role USB3.0/USB2.0 controller. + +Required properties : + - compatible: must be "qcom,dwc3-ipq" + - reg: should contain address and length of the standard XHCI + register set for the device. + +Optional properties: + - rst-ctrl: Magic value used to reset PHY-s properly + (PHY-s may not function properly without it) + - hs-only : If present, specifies that board only has USB2.0(HS) + port + +Example: + xhci@8a00000 { + compatible = "qcom,dwc3-ipq"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x8a00000 0xcd00>; + rst-ctrl = <0x181E038 0x4>; + status = "disabled"; + }; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 1c374a7bd8..320c77ead5 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -93,6 +93,12 @@ config USB_XHCI_BRCM USB controller based on the Broadcom USB3 IP Core. Supports USB2/3 functionality.
+config USB_XHCI_IPQ + bool "Support for Qualcomm IPQ on-chip DWC3 xHCI USB controller" + depends on DM_USB && USB_XHCI_DWC3 && ARCH_IPQ40XX + help + Enables support for the on-chip xHCI DWC3 controller on Qualcomm IPQ SoCs. + endif # USB_XHCI_HCD
config USB_EHCI_HCD diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 29d4f87e38..0fa9c8f32a 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_USB_XHCI_MVEBU) += xhci-mvebu.o obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o obj-$(CONFIG_USB_XHCI_RCAR) += xhci-rcar.o +obj-$(CONFIG_USB_XHCI_IPQ) += xhci-ipq.o obj-$(CONFIG_USB_XHCI_STI) += dwc3-sti-glue.o
# designware diff --git a/drivers/usb/host/xhci-ipq.c b/drivers/usb/host/xhci-ipq.c new file mode 100644 index 0000000000..b550cafac2 --- /dev/null +++ b/drivers/usb/host/xhci-ipq.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2015 Freescale Semiconductor, Inc. + * Copyright (c) 2020 Sartura Ltd. + * + * DWC3 controller driver + * + * Author: Ramneek Mehreshramneek.mehresh@freescale.com + * Author: Robert Marko robert.marko@sartura.hr + * + */ + +#include <common.h> +#include <dm.h> +#include <usb.h> +#include <linux/compat.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/usb/dwc3.h> +#include <usb/xhci.h> + +/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR; + +struct ipq_xhci_platdata { + fdt_addr_t hcd_base; + unsigned int rst_ctrl; + unsigned int hs_only; +}; + +struct ipq_xhci { + struct ipq_xhci_platdata usb_plat; + struct xhci_ctrl ctrl; + struct udevice* dev; + struct xhci_hccr *hcd; + struct dwc3 *dwc3_reg; +}; + +void ipq_reset_usb_phy(void *data) +{ + unsigned int gcc_rst_ctrl; + struct ipq_xhci_platdata *platdata; + struct ipq_xhci *ipq = (struct ipq_xhci *)data; + + platdata = dev_get_platdata(ipq->dev); + if (platdata == NULL) { + printf("Error: %s Failed\n", __func__); + return; + } + + gcc_rst_ctrl = platdata->rst_ctrl; + + if (gcc_rst_ctrl) { + /* assert HS PHY POR reset */ + setbits_le32(gcc_rst_ctrl, 0x10); + mdelay(10); + + /* assert HS PHY SRIF reset */ + setbits_le32(gcc_rst_ctrl, 0x4); + mdelay(10); + + /* deassert HS PHY SRIF reset and program HS PHY registers */ + clrbits_le32(gcc_rst_ctrl, 0x4); + mdelay(10); + + /* de-assert USB3 HS PHY POR reset */ + clrbits_le32(gcc_rst_ctrl, 0x10); + mdelay(10); + + if (!platdata->hs_only) { + /* assert SS PHY POR reset */ + setbits_le32(gcc_rst_ctrl, 0x20); + mdelay(10); + + /* deassert SS PHY POR reset */ + clrbits_le32(gcc_rst_ctrl, 0x20); + } + } +} + +static int ipq_xhci_core_init(struct ipq_xhci *ipq) +{ + int ret = 0; + + ipq_reset_usb_phy((void *)ipq); + + ret = dwc3_core_init(ipq->dwc3_reg); + if (ret) { + debug("%s:failed to initialize core\n", __func__); + return ret; + } + + /* We are hard-coding DWC3 core to Host Mode */ + dwc3_set_mode(ipq->dwc3_reg, DWC3_GCTL_PRTCAP_HOST); + + return ret; +} + +static void ipq_xhci_core_exit(struct ipq_xhci *ipq) +{ + +} + +static int xhci_usb_remove(struct udevice *dev) +{ + int ret; + ret = xhci_deregister(dev); + + if (ret != 0) { + debug("%s:xhci deregistration failed\n", __func__); + return ret; + } + + ipq_xhci_core_exit(dev_get_priv(dev)); + + return 0; +} + +static int xhci_usb_probe(struct udevice *dev) +{ + struct ipq_xhci *context; + struct ipq_xhci_platdata *platdata; + struct xhci_hcor *hcor; + int ret; + + platdata = dev_get_platdata(dev); + if (platdata == NULL) { + printf("Error: %s Failed\n", __func__); + return -ENODEV; + } + + context = dev_get_priv(dev); + if (context == NULL) { + printf("Error: %s Failed\n", __func__); + return -ENODEV; + } + + context->hcd = (struct xhci_hccr *)platdata->hcd_base; + context->dev = dev; + context->dwc3_reg = (struct dwc3 *)((char *)(context->hcd) + DWC3_REG_OFFSET); + hcor = (struct xhci_hcor *)((uint32_t)context->hcd + + HC_LENGTH(xhci_readl(&context->hcd->cr_capbase))); + + ret = ipq_xhci_core_init(context); + + if (ret) { + puts("Error initializing the XHCI controller\n"); + return -EINVAL; + } + + return xhci_register(dev, context->hcd, hcor); +} + +static int xhci_ofdata_to_platdata(struct udevice *dev) +{ + struct ipq_xhci_platdata *platdata; + const void *blob = gd->fdt_blob; + + platdata = dev_get_platdata(dev); + if (platdata == NULL) { + printf("Error: %s Failed\n", __func__); + return -ENODEV; + } + + platdata->hcd_base = devfdt_get_addr(dev); + if (platdata->hcd_base == FDT_ADDR_T_NONE) { + debug("Error getting DWC3 base address\n"); + return -ENXIO; + } + + platdata->rst_ctrl = fdtdec_get_int(blob, dev_of_offset(dev), "rst-ctrl", 0); + platdata->hs_only = fdtdec_get_int(blob, dev_of_offset(dev), "hs-only", 0); + + return 0; +} + +static const struct udevice_id xhci_match_ids[] = { + { .compatible = "qcom,dwc3-ipq" }, + {} +}; + +U_BOOT_DRIVER(usb_xhci) = { + .name = "xhci_ipq", + .id = UCLASS_USB, + .of_match = xhci_match_ids, + .ofdata_to_platdata = xhci_ofdata_to_platdata, + .probe = xhci_usb_probe, + .remove = xhci_usb_remove, + .ops = &xhci_usb_ops, + .platdata_auto_alloc_size = sizeof(struct ipq_xhci_platdata), + .priv_auto_alloc_size = sizeof(struct ipq_xhci), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +};

On Mon, Aug 17, 2020 at 12:36:05PM +0200, Robert Marko wrote:
Add driver for Qualcomm DWC3 based dual role xHCI USB controller. Currently tested on IPQ40xx, but should support other Qualcomm SoC families as well.
Signed-off-by: Robert Marko robert.marko@sartura.hr
MAINTAINERS | 2 + .../usb/qcom-dwc3-ipq.txt | 25 +++ drivers/usb/host/Kconfig | 6 + drivers/usb/host/Makefile | 1 + drivers/usb/host/xhci-ipq.c | 193 ++++++++++++++++++ 5 files changed, 227 insertions(+) create mode 100644 doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt create mode 100644 drivers/usb/host/xhci-ipq.c
diff --git a/MAINTAINERS b/MAINTAINERS index 1a55327406..0e4e281d9b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -239,6 +239,8 @@ S: Maintained F: arch/arm/mach-ipq40xx/ F: include/dt-bindings/reset/qcom,ipq40xx-reset.h F: drivers/reset/reset-ipq40xx.c +F: doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt +F: drivers/usb/host/xhci-ipq.c
ARM MARVELL KIRKWOOD ARMADA-XP ARMADA-38X ARMADA-37XX ARMADA-7K/8K M: Stefan Roese sr@denx.de diff --git a/doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt b/doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt new file mode 100644 index 0000000000..591683e520 --- /dev/null +++ b/doc/device-tree-bindings/usb/qcom-dwc3-ipq.txt @@ -0,0 +1,25 @@ +Qualcomm SuperSpeed DWC3 USB SoC controller
+This controller is integrated in IPQ40xx SoC-s. +It is a dual role USB3.0/USB2.0 controller.
+Required properties :
- compatible: must be "qcom,dwc3-ipq"
- reg: should contain address and length of the standard XHCI
- register set for the device.
+Optional properties:
- rst-ctrl: Magic value used to reset PHY-s properly
(PHY-s may not function properly without it)
- hs-only : If present, specifies that board only has USB2.0(HS)
port
+Example:
- xhci@8a00000 {
compatible = "qcom,dwc3-ipq";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x8a00000 0xcd00>;
rst-ctrl = <0x181E038 0x4>;
status = "disabled";
- };
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 1c374a7bd8..320c77ead5 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -93,6 +93,12 @@ config USB_XHCI_BRCM USB controller based on the Broadcom USB3 IP Core. Supports USB2/3 functionality.
+config USB_XHCI_IPQ
- bool "Support for Qualcomm IPQ on-chip DWC3 xHCI USB controller"
- depends on DM_USB && USB_XHCI_DWC3 && ARCH_IPQ40XX
- help
Enables support for the on-chip xHCI DWC3 controller on Qualcomm IPQ SoCs.
endif # USB_XHCI_HCD
config USB_EHCI_HCD diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 29d4f87e38..0fa9c8f32a 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_USB_XHCI_MVEBU) += xhci-mvebu.o obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o obj-$(CONFIG_USB_XHCI_RCAR) += xhci-rcar.o +obj-$(CONFIG_USB_XHCI_IPQ) += xhci-ipq.o obj-$(CONFIG_USB_XHCI_STI) += dwc3-sti-glue.o
# designware diff --git a/drivers/usb/host/xhci-ipq.c b/drivers/usb/host/xhci-ipq.c new file mode 100644 index 0000000000..b550cafac2 --- /dev/null +++ b/drivers/usb/host/xhci-ipq.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2015 Freescale Semiconductor, Inc.
- Copyright (c) 2020 Sartura Ltd.
- DWC3 controller driver
- Author: Ramneek Mehreshramneek.mehresh@freescale.com
- Author: Robert Marko robert.marko@sartura.hr
- */
+#include <common.h> +#include <dm.h> +#include <usb.h> +#include <linux/compat.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/usb/dwc3.h> +#include <usb/xhci.h>
+/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR;
+struct ipq_xhci_platdata {
- fdt_addr_t hcd_base;
- unsigned int rst_ctrl;
- unsigned int hs_only;
+};
+struct ipq_xhci {
- struct ipq_xhci_platdata usb_plat;
- struct xhci_ctrl ctrl;
- struct udevice* dev;
- struct xhci_hccr *hcd;
- struct dwc3 *dwc3_reg;
+};
+void ipq_reset_usb_phy(void *data) +{
- unsigned int gcc_rst_ctrl;
- struct ipq_xhci_platdata *platdata;
- struct ipq_xhci *ipq = (struct ipq_xhci *)data;
- platdata = dev_get_platdata(ipq->dev);
- if (platdata == NULL) {
printf("Error: %s Failed\n", __func__);
return;
- }
- gcc_rst_ctrl = platdata->rst_ctrl;
- if (gcc_rst_ctrl) {
/* assert HS PHY POR reset */
setbits_le32(gcc_rst_ctrl, 0x10);
mdelay(10);
/* assert HS PHY SRIF reset */
setbits_le32(gcc_rst_ctrl, 0x4);
mdelay(10);
/* deassert HS PHY SRIF reset and program HS PHY registers */
clrbits_le32(gcc_rst_ctrl, 0x4);
mdelay(10);
/* de-assert USB3 HS PHY POR reset */
clrbits_le32(gcc_rst_ctrl, 0x10);
mdelay(10);
if (!platdata->hs_only) {
/* assert SS PHY POR reset */
setbits_le32(gcc_rst_ctrl, 0x20);
mdelay(10);
/* deassert SS PHY POR reset */
clrbits_le32(gcc_rst_ctrl, 0x20);
}
- }
+}
+static int ipq_xhci_core_init(struct ipq_xhci *ipq) +{
- int ret = 0;
- ipq_reset_usb_phy((void *)ipq);
- ret = dwc3_core_init(ipq->dwc3_reg);
- if (ret) {
debug("%s:failed to initialize core\n", __func__);
return ret;
- }
- /* We are hard-coding DWC3 core to Host Mode */
- dwc3_set_mode(ipq->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
- return ret;
+}
+static void ipq_xhci_core_exit(struct ipq_xhci *ipq) +{
+}
+static int xhci_usb_remove(struct udevice *dev) +{
- int ret;
- ret = xhci_deregister(dev);
- if (ret != 0) {
debug("%s:xhci deregistration failed\n", __func__);
return ret;
- }
- ipq_xhci_core_exit(dev_get_priv(dev));
- return 0;
+}
+static int xhci_usb_probe(struct udevice *dev) +{
- struct ipq_xhci *context;
- struct ipq_xhci_platdata *platdata;
- struct xhci_hcor *hcor;
- int ret;
- platdata = dev_get_platdata(dev);
- if (platdata == NULL) {
printf("Error: %s Failed\n", __func__);
return -ENODEV;
- }
- context = dev_get_priv(dev);
- if (context == NULL) {
printf("Error: %s Failed\n", __func__);
return -ENODEV;
- }
- context->hcd = (struct xhci_hccr *)platdata->hcd_base;
- context->dev = dev;
- context->dwc3_reg = (struct dwc3 *)((char *)(context->hcd) + DWC3_REG_OFFSET);
- hcor = (struct xhci_hcor *)((uint32_t)context->hcd +
HC_LENGTH(xhci_readl(&context->hcd->cr_capbase)));
- ret = ipq_xhci_core_init(context);
- if (ret) {
puts("Error initializing the XHCI controller\n");
return -EINVAL;
- }
- return xhci_register(dev, context->hcd, hcor);
+}
+static int xhci_ofdata_to_platdata(struct udevice *dev) +{
- struct ipq_xhci_platdata *platdata;
- const void *blob = gd->fdt_blob;
- platdata = dev_get_platdata(dev);
- if (platdata == NULL) {
printf("Error: %s Failed\n", __func__);
return -ENODEV;
- }
- platdata->hcd_base = devfdt_get_addr(dev);
- if (platdata->hcd_base == FDT_ADDR_T_NONE) {
debug("Error getting DWC3 base address\n");
return -ENXIO;
- }
- platdata->rst_ctrl = fdtdec_get_int(blob, dev_of_offset(dev), "rst-ctrl", 0);
- platdata->hs_only = fdtdec_get_int(blob, dev_of_offset(dev), "hs-only", 0);
- return 0;
+}
+static const struct udevice_id xhci_match_ids[] = {
- { .compatible = "qcom,dwc3-ipq" },
- {}
+};
+U_BOOT_DRIVER(usb_xhci) = {
- .name = "xhci_ipq",
- .id = UCLASS_USB,
- .of_match = xhci_match_ids,
- .ofdata_to_platdata = xhci_ofdata_to_platdata,
- .probe = xhci_usb_probe,
- .remove = xhci_usb_remove,
- .ops = &xhci_usb_ops,
- .platdata_auto_alloc_size = sizeof(struct ipq_xhci_platdata),
- .priv_auto_alloc_size = sizeof(struct ipq_xhci),
- .flags = DM_FLAG_ALLOC_PRIV_DMA,
+};
Adding USB maintainer.

On 8/19/20 8:22 PM, Tom Rini wrote: [...]
+#include <common.h> +#include <dm.h> +#include <usb.h> +#include <linux/compat.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/usb/dwc3.h> +#include <usb/xhci.h>
Please keep the list sorted.
+/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR;
+struct ipq_xhci_platdata {
- fdt_addr_t hcd_base;
- unsigned int rst_ctrl;
- unsigned int hs_only;
bool ...
+};
+struct ipq_xhci {
- struct ipq_xhci_platdata usb_plat;
- struct xhci_ctrl ctrl;
- struct udevice* dev;
- struct xhci_hccr *hcd;
- struct dwc3 *dwc3_reg;
+};
+void ipq_reset_usb_phy(void *data) +{
- unsigned int gcc_rst_ctrl;
- struct ipq_xhci_platdata *platdata;
- struct ipq_xhci *ipq = (struct ipq_xhci *)data;
Pass struct ipg_xhci pointer in instead of void *.
- platdata = dev_get_platdata(ipq->dev);
- if (platdata == NULL) {
printf("Error: %s Failed\n", __func__);
dev_err() here.
return;
- }
Shouldn't this be part of a PHY driver ?
- gcc_rst_ctrl = platdata->rst_ctrl;
- if (gcc_rst_ctrl) {
/* assert HS PHY POR reset */
setbits_le32(gcc_rst_ctrl, 0x10);
mdelay(10);
Does it really need such lengthy delays here ?
/* assert HS PHY SRIF reset */
setbits_le32(gcc_rst_ctrl, 0x4);
mdelay(10);
/* deassert HS PHY SRIF reset and program HS PHY registers */
clrbits_le32(gcc_rst_ctrl, 0x4);
mdelay(10);
/* de-assert USB3 HS PHY POR reset */
clrbits_le32(gcc_rst_ctrl, 0x10);
This BIT(4) should likely be #define'd as a macro , same for the others.
mdelay(10);
if (!platdata->hs_only) {
/* assert SS PHY POR reset */
setbits_le32(gcc_rst_ctrl, 0x20);
mdelay(10);
/* deassert SS PHY POR reset */
clrbits_le32(gcc_rst_ctrl, 0x20);
}
- }
+}
+static int ipq_xhci_core_init(struct ipq_xhci *ipq) +{
- int ret = 0;
- ipq_reset_usb_phy((void *)ipq);
- ret = dwc3_core_init(ipq->dwc3_reg);
- if (ret) {
debug("%s:failed to initialize core\n", __func__);
dev_dbg()
return ret;
- }
- /* We are hard-coding DWC3 core to Host Mode */
- dwc3_set_mode(ipq->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
- return ret;
+}
+static void ipq_xhci_core_exit(struct ipq_xhci *ipq) +{
Is some code missing here ?
+}
+static int xhci_usb_remove(struct udevice *dev) +{
- int ret;
- ret = xhci_deregister(dev);
- if (ret != 0) {
debug("%s:xhci deregistration failed\n", __func__);
dev_dbg()
return ret;
- }
- ipq_xhci_core_exit(dev_get_priv(dev));
- return 0;
return ipg_xhci_core_exit() to propagate the error value (if any).
+}
[...]

On Wed, Aug 19, 2020 at 9:34 PM Marek Vasut marex@denx.de wrote:
On 8/19/20 8:22 PM, Tom Rini wrote: [...]
+#include <common.h> +#include <dm.h> +#include <usb.h> +#include <linux/compat.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/usb/dwc3.h> +#include <usb/xhci.h>
Please keep the list sorted.
+/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR;
+struct ipq_xhci_platdata {
- fdt_addr_t hcd_base;
- unsigned int rst_ctrl;
- unsigned int hs_only;
bool ...
+};
+struct ipq_xhci {
- struct ipq_xhci_platdata usb_plat;
- struct xhci_ctrl ctrl;
- struct udevice* dev;
- struct xhci_hccr *hcd;
- struct dwc3 *dwc3_reg;
+};
+void ipq_reset_usb_phy(void *data) +{
- unsigned int gcc_rst_ctrl;
- struct ipq_xhci_platdata *platdata;
- struct ipq_xhci *ipq = (struct ipq_xhci *)data;
Pass struct ipg_xhci pointer in instead of void *.
- platdata = dev_get_platdata(ipq->dev);
- if (platdata == NULL) {
printf("Error: %s Failed\n", __func__);
dev_err() here.
return;
- }
Shouldn't this be part of a PHY driver ?
- gcc_rst_ctrl = platdata->rst_ctrl;
- if (gcc_rst_ctrl) {
/* assert HS PHY POR reset */
setbits_le32(gcc_rst_ctrl, 0x10);
mdelay(10);
Does it really need such lengthy delays here ?
/* assert HS PHY SRIF reset */
setbits_le32(gcc_rst_ctrl, 0x4);
mdelay(10);
/* deassert HS PHY SRIF reset and program HS PHY registers */
clrbits_le32(gcc_rst_ctrl, 0x4);
mdelay(10);
/* de-assert USB3 HS PHY POR reset */
clrbits_le32(gcc_rst_ctrl, 0x10);
This BIT(4) should likely be #define'd as a macro , same for the others.
mdelay(10);
if (!platdata->hs_only) {
/* assert SS PHY POR reset */
setbits_le32(gcc_rst_ctrl, 0x20);
mdelay(10);
/* deassert SS PHY POR reset */
clrbits_le32(gcc_rst_ctrl, 0x20);
}
- }
+}
+static int ipq_xhci_core_init(struct ipq_xhci *ipq) +{
- int ret = 0;
- ipq_reset_usb_phy((void *)ipq);
- ret = dwc3_core_init(ipq->dwc3_reg);
- if (ret) {
debug("%s:failed to initialize core\n", __func__);
dev_dbg()
return ret;
- }
- /* We are hard-coding DWC3 core to Host Mode */
- dwc3_set_mode(ipq->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
- return ret;
+}
+static void ipq_xhci_core_exit(struct ipq_xhci *ipq) +{
Is some code missing here ?
+}
+static int xhci_usb_remove(struct udevice *dev) +{
- int ret;
- ret = xhci_deregister(dev);
- if (ret != 0) {
debug("%s:xhci deregistration failed\n", __func__);
dev_dbg()
return ret;
- }
- ipq_xhci_core_exit(dev_get_priv(dev));
- return 0;
return ipg_xhci_core_exit() to propagate the error value (if any).
+}
[...]
Hi Marek, thanks for the review.
I have moved the USB PHY-s into a separate driver as Linux does. I have also dropped this custom driver and moved to use of the DWC3 generic glue one, that way nodes and everything will match Linux as much as possible. Unfortunately, it will most likely take a while for me to send a v2 of this patchset as USB3.0 capable port is not working. USB2.0 one is working fine and detecting the TI CC2540 I have connected to it, but USB3.0 external Type-A port won't detect USB mass storage or WLAN adapters at all.
Do you maybe have advice on how to debug this?
Regards Robert

On 8/27/20 5:01 PM, Robert Marko wrote:
On Wed, Aug 19, 2020 at 9:34 PM Marek Vasut marex@denx.de wrote:
On 8/19/20 8:22 PM, Tom Rini wrote: [...]
+#include <common.h> +#include <dm.h> +#include <usb.h> +#include <linux/compat.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/usb/dwc3.h> +#include <usb/xhci.h>
Please keep the list sorted.
+/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR;
+struct ipq_xhci_platdata {
- fdt_addr_t hcd_base;
- unsigned int rst_ctrl;
- unsigned int hs_only;
bool ...
+};
+struct ipq_xhci {
- struct ipq_xhci_platdata usb_plat;
- struct xhci_ctrl ctrl;
- struct udevice* dev;
- struct xhci_hccr *hcd;
- struct dwc3 *dwc3_reg;
+};
+void ipq_reset_usb_phy(void *data) +{
- unsigned int gcc_rst_ctrl;
- struct ipq_xhci_platdata *platdata;
- struct ipq_xhci *ipq = (struct ipq_xhci *)data;
Pass struct ipg_xhci pointer in instead of void *.
- platdata = dev_get_platdata(ipq->dev);
- if (platdata == NULL) {
printf("Error: %s Failed\n", __func__);
dev_err() here.
return;
- }
Shouldn't this be part of a PHY driver ?
- gcc_rst_ctrl = platdata->rst_ctrl;
- if (gcc_rst_ctrl) {
/* assert HS PHY POR reset */
setbits_le32(gcc_rst_ctrl, 0x10);
mdelay(10);
Does it really need such lengthy delays here ?
/* assert HS PHY SRIF reset */
setbits_le32(gcc_rst_ctrl, 0x4);
mdelay(10);
/* deassert HS PHY SRIF reset and program HS PHY registers */
clrbits_le32(gcc_rst_ctrl, 0x4);
mdelay(10);
/* de-assert USB3 HS PHY POR reset */
clrbits_le32(gcc_rst_ctrl, 0x10);
This BIT(4) should likely be #define'd as a macro , same for the others.
mdelay(10);
if (!platdata->hs_only) {
/* assert SS PHY POR reset */
setbits_le32(gcc_rst_ctrl, 0x20);
mdelay(10);
/* deassert SS PHY POR reset */
clrbits_le32(gcc_rst_ctrl, 0x20);
}
- }
+}
+static int ipq_xhci_core_init(struct ipq_xhci *ipq) +{
- int ret = 0;
- ipq_reset_usb_phy((void *)ipq);
- ret = dwc3_core_init(ipq->dwc3_reg);
- if (ret) {
debug("%s:failed to initialize core\n", __func__);
dev_dbg()
return ret;
- }
- /* We are hard-coding DWC3 core to Host Mode */
- dwc3_set_mode(ipq->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
- return ret;
+}
+static void ipq_xhci_core_exit(struct ipq_xhci *ipq) +{
Is some code missing here ?
+}
+static int xhci_usb_remove(struct udevice *dev) +{
- int ret;
- ret = xhci_deregister(dev);
- if (ret != 0) {
debug("%s:xhci deregistration failed\n", __func__);
dev_dbg()
return ret;
- }
- ipq_xhci_core_exit(dev_get_priv(dev));
- return 0;
return ipg_xhci_core_exit() to propagate the error value (if any).
+}
[...]
Hi Marek, thanks for the review.
I have moved the USB PHY-s into a separate driver as Linux does. I have also dropped this custom driver and moved to use of the DWC3 generic glue one, that way nodes and everything will match Linux as much as possible. Unfortunately, it will most likely take a while for me to send a v2 of this patchset as USB3.0 capable port is not working. USB2.0 one is working fine and detecting the TI CC2540 I have connected to it, but USB3.0 external Type-A port won't detect USB mass storage or WLAN adapters at all.
Do you maybe have advice on how to debug this?
I don't know anything about the qualcomm hardware and I never worked with any, sorry.

I don't think that this has anything to do with Qualcomm HW. I find it weird that hooking into the non-DM DWC3 driver and simply calling core init has both ports working while DM version only has USB2.0 one working.
On Thu, Aug 27, 2020 at 8:06 PM Marek Vasut marex@denx.de wrote:
On 8/27/20 5:01 PM, Robert Marko wrote:
On Wed, Aug 19, 2020 at 9:34 PM Marek Vasut marex@denx.de wrote:
On 8/19/20 8:22 PM, Tom Rini wrote: [...]
+#include <common.h> +#include <dm.h> +#include <usb.h> +#include <linux/compat.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/usb/dwc3.h> +#include <usb/xhci.h>
Please keep the list sorted.
+/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR;
+struct ipq_xhci_platdata {
- fdt_addr_t hcd_base;
- unsigned int rst_ctrl;
- unsigned int hs_only;
bool ...
+};
+struct ipq_xhci {
- struct ipq_xhci_platdata usb_plat;
- struct xhci_ctrl ctrl;
- struct udevice* dev;
- struct xhci_hccr *hcd;
- struct dwc3 *dwc3_reg;
+};
+void ipq_reset_usb_phy(void *data) +{
- unsigned int gcc_rst_ctrl;
- struct ipq_xhci_platdata *platdata;
- struct ipq_xhci *ipq = (struct ipq_xhci *)data;
Pass struct ipg_xhci pointer in instead of void *.
- platdata = dev_get_platdata(ipq->dev);
- if (platdata == NULL) {
printf("Error: %s Failed\n", __func__);
dev_err() here.
return;
- }
Shouldn't this be part of a PHY driver ?
- gcc_rst_ctrl = platdata->rst_ctrl;
- if (gcc_rst_ctrl) {
/* assert HS PHY POR reset */
setbits_le32(gcc_rst_ctrl, 0x10);
mdelay(10);
Does it really need such lengthy delays here ?
/* assert HS PHY SRIF reset */
setbits_le32(gcc_rst_ctrl, 0x4);
mdelay(10);
/* deassert HS PHY SRIF reset and program HS PHY registers */
clrbits_le32(gcc_rst_ctrl, 0x4);
mdelay(10);
/* de-assert USB3 HS PHY POR reset */
clrbits_le32(gcc_rst_ctrl, 0x10);
This BIT(4) should likely be #define'd as a macro , same for the others.
mdelay(10);
if (!platdata->hs_only) {
/* assert SS PHY POR reset */
setbits_le32(gcc_rst_ctrl, 0x20);
mdelay(10);
/* deassert SS PHY POR reset */
clrbits_le32(gcc_rst_ctrl, 0x20);
}
- }
+}
+static int ipq_xhci_core_init(struct ipq_xhci *ipq) +{
- int ret = 0;
- ipq_reset_usb_phy((void *)ipq);
- ret = dwc3_core_init(ipq->dwc3_reg);
- if (ret) {
debug("%s:failed to initialize core\n", __func__);
dev_dbg()
return ret;
- }
- /* We are hard-coding DWC3 core to Host Mode */
- dwc3_set_mode(ipq->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
- return ret;
+}
+static void ipq_xhci_core_exit(struct ipq_xhci *ipq) +{
Is some code missing here ?
+}
+static int xhci_usb_remove(struct udevice *dev) +{
- int ret;
- ret = xhci_deregister(dev);
- if (ret != 0) {
debug("%s:xhci deregistration failed\n", __func__);
dev_dbg()
return ret;
- }
- ipq_xhci_core_exit(dev_get_priv(dev));
- return 0;
return ipg_xhci_core_exit() to propagate the error value (if any).
+}
[...]
Hi Marek, thanks for the review.
I have moved the USB PHY-s into a separate driver as Linux does. I have also dropped this custom driver and moved to use of the DWC3 generic glue one, that way nodes and everything will match Linux as much as possible. Unfortunately, it will most likely take a while for me to send a v2 of this patchset as USB3.0 capable port is not working. USB2.0 one is working fine and detecting the TI CC2540 I have connected to it, but USB3.0 external Type-A port won't detect USB mass storage or WLAN adapters at all.
Do you maybe have advice on how to debug this?
I don't know anything about the qualcomm hardware and I never worked with any, sorry.

On 8/27/20 9:08 PM, Robert Marko wrote:
I don't think that this has anything to do with Qualcomm HW. I find it weird that hooking into the non-DM DWC3 driver and simply calling core init has both ports working while DM version only has USB2.0 one working.
On Thu, Aug 27, 2020 at 8:06 PM Marek Vasut marex@denx.de wrote:
On 8/27/20 5:01 PM, Robert Marko wrote:
On Wed, Aug 19, 2020 at 9:34 PM Marek Vasut marex@denx.de wrote:
On 8/19/20 8:22 PM, Tom Rini wrote: [...]
+#include <common.h> +#include <dm.h> +#include <usb.h> +#include <linux/compat.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/usb/dwc3.h> +#include <usb/xhci.h>
Please keep the list sorted.
+/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR;
+struct ipq_xhci_platdata {
- fdt_addr_t hcd_base;
- unsigned int rst_ctrl;
- unsigned int hs_only;
bool ...
+};
+struct ipq_xhci {
- struct ipq_xhci_platdata usb_plat;
- struct xhci_ctrl ctrl;
- struct udevice* dev;
- struct xhci_hccr *hcd;
- struct dwc3 *dwc3_reg;
+};
+void ipq_reset_usb_phy(void *data) +{
- unsigned int gcc_rst_ctrl;
- struct ipq_xhci_platdata *platdata;
- struct ipq_xhci *ipq = (struct ipq_xhci *)data;
Pass struct ipg_xhci pointer in instead of void *.
- platdata = dev_get_platdata(ipq->dev);
- if (platdata == NULL) {
printf("Error: %s Failed\n", __func__);
dev_err() here.
return;
- }
Shouldn't this be part of a PHY driver ?
- gcc_rst_ctrl = platdata->rst_ctrl;
- if (gcc_rst_ctrl) {
/* assert HS PHY POR reset */
setbits_le32(gcc_rst_ctrl, 0x10);
mdelay(10);
Does it really need such lengthy delays here ?
/* assert HS PHY SRIF reset */
setbits_le32(gcc_rst_ctrl, 0x4);
mdelay(10);
/* deassert HS PHY SRIF reset and program HS PHY registers */
clrbits_le32(gcc_rst_ctrl, 0x4);
mdelay(10);
/* de-assert USB3 HS PHY POR reset */
clrbits_le32(gcc_rst_ctrl, 0x10);
This BIT(4) should likely be #define'd as a macro , same for the others.
mdelay(10);
if (!platdata->hs_only) {
/* assert SS PHY POR reset */
setbits_le32(gcc_rst_ctrl, 0x20);
mdelay(10);
/* deassert SS PHY POR reset */
clrbits_le32(gcc_rst_ctrl, 0x20);
}
- }
+}
+static int ipq_xhci_core_init(struct ipq_xhci *ipq) +{
- int ret = 0;
- ipq_reset_usb_phy((void *)ipq);
- ret = dwc3_core_init(ipq->dwc3_reg);
- if (ret) {
debug("%s:failed to initialize core\n", __func__);
dev_dbg()
return ret;
- }
- /* We are hard-coding DWC3 core to Host Mode */
- dwc3_set_mode(ipq->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
- return ret;
+}
+static void ipq_xhci_core_exit(struct ipq_xhci *ipq) +{
Is some code missing here ?
+}
+static int xhci_usb_remove(struct udevice *dev) +{
- int ret;
- ret = xhci_deregister(dev);
- if (ret != 0) {
debug("%s:xhci deregistration failed\n", __func__);
dev_dbg()
return ret;
- }
- ipq_xhci_core_exit(dev_get_priv(dev));
- return 0;
return ipg_xhci_core_exit() to propagate the error value (if any).
+}
[...]
Hi Marek, thanks for the review.
I have moved the USB PHY-s into a separate driver as Linux does. I have also dropped this custom driver and moved to use of the DWC3 generic glue one, that way nodes and everything will match Linux as much as possible. Unfortunately, it will most likely take a while for me to send a v2 of this patchset as USB3.0 capable port is not working. USB2.0 one is working fine and detecting the TI CC2540 I have connected to it, but USB3.0 external Type-A port won't detect USB mass storage or WLAN adapters at all.
Do you maybe have advice on how to debug this?
I don't know anything about the qualcomm hardware and I never worked with any, sorry.
In that case, +CC Bin.

Hi, I did some more testing and I can confirm that USB3.0 port works fine with the PHY driver and old XHCI-DWC3 while with DWC3 generic one it does not work. I have been unable to figure out why is that exactly.
Regards Robert
On Thu, Aug 27, 2020 at 9:13 PM Marek Vasut marex@denx.de wrote:
On 8/27/20 9:08 PM, Robert Marko wrote:
I don't think that this has anything to do with Qualcomm HW. I find it weird that hooking into the non-DM DWC3 driver and simply calling core init has both ports working while DM version only has USB2.0 one working.
On Thu, Aug 27, 2020 at 8:06 PM Marek Vasut marex@denx.de wrote:
On 8/27/20 5:01 PM, Robert Marko wrote:
On Wed, Aug 19, 2020 at 9:34 PM Marek Vasut marex@denx.de wrote:
On 8/19/20 8:22 PM, Tom Rini wrote: [...]
> +#include <common.h> > +#include <dm.h> > +#include <usb.h> > +#include <linux/compat.h> > +#include <linux/errno.h> > +#include <linux/delay.h> > +#include <linux/usb/dwc3.h> > +#include <usb/xhci.h>
Please keep the list sorted.
> +/* Declare global data pointer */ > +DECLARE_GLOBAL_DATA_PTR; > + > +struct ipq_xhci_platdata { > + fdt_addr_t hcd_base; > + unsigned int rst_ctrl; > + unsigned int hs_only;
bool ...
> +}; > + > +struct ipq_xhci { > + struct ipq_xhci_platdata usb_plat; > + struct xhci_ctrl ctrl; > + struct udevice* dev; > + struct xhci_hccr *hcd; > + struct dwc3 *dwc3_reg; > +}; > + > +void ipq_reset_usb_phy(void *data) > +{ > + unsigned int gcc_rst_ctrl; > + struct ipq_xhci_platdata *platdata; > + struct ipq_xhci *ipq = (struct ipq_xhci *)data;
Pass struct ipg_xhci pointer in instead of void *.
> + platdata = dev_get_platdata(ipq->dev); > + if (platdata == NULL) { > + printf("Error: %s Failed\n", __func__);
dev_err() here.
> + return; > + }
Shouldn't this be part of a PHY driver ?
> + gcc_rst_ctrl = platdata->rst_ctrl; > + > + if (gcc_rst_ctrl) { > + /* assert HS PHY POR reset */ > + setbits_le32(gcc_rst_ctrl, 0x10); > + mdelay(10);
Does it really need such lengthy delays here ?
> + /* assert HS PHY SRIF reset */ > + setbits_le32(gcc_rst_ctrl, 0x4); > + mdelay(10); > + > + /* deassert HS PHY SRIF reset and program HS PHY registers */ > + clrbits_le32(gcc_rst_ctrl, 0x4); > + mdelay(10); > + > + /* de-assert USB3 HS PHY POR reset */ > + clrbits_le32(gcc_rst_ctrl, 0x10);
This BIT(4) should likely be #define'd as a macro , same for the others.
> + mdelay(10); > + > + if (!platdata->hs_only) { > + /* assert SS PHY POR reset */ > + setbits_le32(gcc_rst_ctrl, 0x20); > + mdelay(10); > + > + /* deassert SS PHY POR reset */ > + clrbits_le32(gcc_rst_ctrl, 0x20); > + } > + } > +} > + > +static int ipq_xhci_core_init(struct ipq_xhci *ipq) > +{ > + int ret = 0; > + > + ipq_reset_usb_phy((void *)ipq); > + > + ret = dwc3_core_init(ipq->dwc3_reg); > + if (ret) { > + debug("%s:failed to initialize core\n", __func__);
dev_dbg()
> + return ret; > + } > + > + /* We are hard-coding DWC3 core to Host Mode */ > + dwc3_set_mode(ipq->dwc3_reg, DWC3_GCTL_PRTCAP_HOST); > + > + return ret; > +} > + > +static void ipq_xhci_core_exit(struct ipq_xhci *ipq) > +{
Is some code missing here ?
> +} > + > +static int xhci_usb_remove(struct udevice *dev) > +{ > + int ret; > + ret = xhci_deregister(dev); > + > + if (ret != 0) { > + debug("%s:xhci deregistration failed\n", __func__);
dev_dbg()
> + return ret; > + } > + > + ipq_xhci_core_exit(dev_get_priv(dev)); > + > + return 0;
return ipg_xhci_core_exit() to propagate the error value (if any).
> +}
[...]
Hi Marek, thanks for the review.
I have moved the USB PHY-s into a separate driver as Linux does. I have also dropped this custom driver and moved to use of the DWC3 generic glue one, that way nodes and everything will match Linux as much as possible. Unfortunately, it will most likely take a while for me to send a v2 of this patchset as USB3.0 capable port is not working. USB2.0 one is working fine and detecting the TI CC2540 I have connected to it, but USB3.0 external Type-A port won't detect USB mass storage or WLAN adapters at all.
Do you maybe have advice on how to debug this?
I don't know anything about the qualcomm hardware and I never worked with any, sorry.
In that case, +CC Bin.

On 8/30/20 10:51 PM, Robert Marko wrote:
Hi,
Hi,
I did some more testing and I can confirm that USB3.0 port works fine with the PHY driver and old XHCI-DWC3 while with DWC3 generic one it does not work. I have been unable to figure out why is that exactly.
Maybe it's the order of initialization in which the two IPs (PHY and controller) are enabled, or there is some difference in the register settings?
btw please stop top-posting, thanks.

There is a driver to support built in USB controller now, so lets add the USB nodes to DTSI.
Signed-off-by: Robert Marko robert.marko@sartura.hr --- arch/arm/dts/qcom-ipq4019.dtsi | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/arch/arm/dts/qcom-ipq4019.dtsi b/arch/arm/dts/qcom-ipq4019.dtsi index 67122de9c0..e82f1253a1 100644 --- a/arch/arm/dts/qcom-ipq4019.dtsi +++ b/arch/arm/dts/qcom-ipq4019.dtsi @@ -89,5 +89,23 @@ gpio-bank-name="soc"; #gpio-cells = <2>; }; + + usb3: xhci@8a00000 { + compatible = "qcom,dwc3-ipq"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x8a00000 0xcd00>; + rst-ctrl = <0x181E038 0x4>; + status = "disabled"; + }; + + usb2: xhci@6000000 { + compatible = "qcom,dwc3-ipq"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x6000000 0xcd00>; + rst-ctrl = <0x181E01C 0x4>; + status = "disabled"; + }; }; };
participants (3)
-
Marek Vasut
-
Robert Marko
-
Tom Rini