diff mbox series

[4/5] usb: xhci: Add Qualcomm DWC3 xHCI driver

Message ID 20200817103606.22417-4-robert.marko@sartura.hr
State Superseded
Delegated to: Tom Rini
Headers show
Series [1/5] IPQ40xx: Add SMEM support | expand

Commit Message

Robert Marko Aug. 17, 2020, 10:36 a.m. UTC
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

Comments

Tom Rini Aug. 19, 2020, 6:22 p.m. UTC | #1
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 Mehresh<ramneek.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.
Marek Vasut Aug. 19, 2020, 7:34 p.m. UTC | #2
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).

>> +}

[...]
Robert Marko Aug. 27, 2020, 3:01 p.m. UTC | #3
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
Marek Vasut Aug. 27, 2020, 6:03 p.m. UTC | #4
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.
Robert Marko Aug. 27, 2020, 7:08 p.m. UTC | #5
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.
Marek Vasut Aug. 27, 2020, 7:13 p.m. UTC | #6
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.
Robert Marko Aug. 30, 2020, 8:51 p.m. UTC | #7
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.
Marek Vasut Aug. 30, 2020, 9:12 p.m. UTC | #8
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.
diff mbox series

Patch

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 Mehresh<ramneek.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,
+};