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.ma...@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 <s...@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.mehr...@freescale.com>
> + * Author: Robert Marko <robert.ma...@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.

-- 
Tom

Attachment: signature.asc
Description: PGP signature

Reply via email to