Skip to content

Commit

Permalink
usb: xhci: Introduce Synopsys glue extension for DWC_usb3x
Browse files Browse the repository at this point in the history
Synopsys DWC_usb3x IPs are used on many different platforms. Since they
share the same IP, often the quirks are common across different
platforms and versions. This drives the need to find a way to handle all
the common (and platform specific) quirks and separate its logic from
dwc3 and xhci core logic. Hopefully this helps reduce introducing new
device properties while maintaining abstraction.

So, let's create a xhci-snps glue extension that can apply to xhci-plat
and xhci-pci glue drivers and teach it to handle DWC_usb3x hosts. For
this particular change, we'll start with xhci-plat glue driver.

Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
  • Loading branch information
Thinh Nguyen authored and intel-lab-lkp committed Jun 5, 2022
1 parent 726b0ba commit f2403a4
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 0 deletions.
8 changes: 8 additions & 0 deletions drivers/usb/host/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ config USB_XHCI_PLATFORM

If unsure, say N.

config USB_XHCI_SNPS
bool "xHCI fine tune for Synopsys platforms"
help
Say 'Y' to enable additional fine tune for Synopsys DWC_usb3x xHCI
controllers.

If unsure, say N.

config USB_XHCI_HISTB
tristate "xHCI support for HiSilicon STB SoCs"
depends on USB_XHCI_PLATFORM && (ARCH_HISI || COMPILE_TEST)
Expand Down
3 changes: 3 additions & 0 deletions drivers/usb/host/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ endif
ifneq ($(CONFIG_USB_XHCI_RCAR), )
xhci-plat-hcd-y += xhci-rcar.o
endif
ifneq ($(CONFIG_USB_XHCI_SNPS), )
xhci-plat-hcd-y += xhci-snps.o
endif

ifneq ($(CONFIG_DEBUG_FS),)
xhci-hcd-y += xhci-debugfs.o
Expand Down
40 changes: 40 additions & 0 deletions drivers/usb/host/xhci-plat.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,20 @@
#include "xhci-plat.h"
#include "xhci-mvebu.h"
#include "xhci-rcar.h"
#include "xhci-snps.h"

static struct hc_driver __read_mostly xhci_plat_hc_driver;

static int xhci_plat_setup(struct usb_hcd *hcd);
static int xhci_plat_start(struct usb_hcd *hcd);
static int xhci_plat_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep);

static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
.extra_priv_size = sizeof(struct xhci_plat_priv),
.reset = xhci_plat_setup,
.start = xhci_plat_start,
.add_endpoint = xhci_plat_add_endpoint,
};

static void xhci_priv_plat_start(struct usb_hcd *hcd)
Expand Down Expand Up @@ -105,6 +109,20 @@ static int xhci_plat_start(struct usb_hcd *hcd)
return xhci_run(hcd);
}

static int xhci_plat_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep)
{
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
int ret;

if (priv && priv->add_endpoint_quirk)
ret = priv->add_endpoint_quirk(hcd, udev, ep);
else
ret = xhci_add_endpoint(hcd, udev, ep);

return ret;
}

#ifdef CONFIG_OF
static const struct xhci_plat_priv xhci_plat_marvell_armada = {
.init_quirk = xhci_mvebu_mbus_init_quirk,
Expand Down Expand Up @@ -173,6 +191,25 @@ static const struct of_device_id usb_xhci_of_match[] = {
MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
#endif

static int xhci_plat_setup_snps_quirks(struct usb_hcd *hcd)
{
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
int ret;

if (!IS_ENABLED(CONFIG_USB_XHCI_SNPS))
return 0;

ret = xhci_snps_init(hcd);
if (ret) {
dev_err(hcd->self.controller, "SNPS extension setup fails\n");
return ret;
}

priv->init_quirk = &xhci_snps_setup;
priv->add_endpoint_quirk = &xhci_snps_add_endpoint;
return 0;
}

static int xhci_plat_probe(struct platform_device *pdev)
{
const struct xhci_plat_priv *priv_match;
Expand Down Expand Up @@ -301,6 +338,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
if (device_property_read_bool(tmpdev, "quirk-broken-port-ped"))
xhci->quirks |= XHCI_BROKEN_PORT_PED;

if (device_property_read_bool(tmpdev, "xhci-snps-quirks"))
xhci_plat_setup_snps_quirks(hcd);

device_property_read_u32(tmpdev, "imod-interval-ns",
&xhci->imod_interval);
}
Expand Down
3 changes: 3 additions & 0 deletions drivers/usb/host/xhci-plat.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ struct xhci_plat_priv {
int (*init_quirk)(struct usb_hcd *);
int (*suspend_quirk)(struct usb_hcd *);
int (*resume_quirk)(struct usb_hcd *);
int (*add_endpoint_quirk)(struct usb_hcd *hcd,
struct usb_device *udev,
struct usb_host_endpoint *ep);
};

#define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv)
Expand Down
185 changes: 185 additions & 0 deletions drivers/usb/host/xhci-snps.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// SPDX-License-Identifier: GPL-2.0
/*
* xhci-snps.c - xHCI glue extension for Synopsys DWC_usb3x IPs.
*
* Copyright (C) 2022 Synopsys Inc.
*
* Author: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
*
* Contains some borrowed code from dwc3 driver.
*/

#include "xhci.h"
#include "xhci-snps.h"

#define DWC3_GSNPSID 0xc120
#define DWC3_VER_NUMBER 0xc1a0
#define DWC3_VER_TYPE 0xc1a4

#define DWC3_GLOBALS_REGS_END 0xc6ff

#define DWC3_GSNPSID_MASK 0xffff0000
#define DWC3_GSNPS_ID(p) (((p) & DWC3_GSNPSID_MASK) >> 16)

#define DWC3_IP 0x5533
#define DWC31_IP 0x3331
#define DWC32_IP 0x3332

#define DWC3_REVISION_ANY 0x0
#define DWC3_REVISION_173A 0x5533173a
#define DWC3_REVISION_175A 0x5533175a
#define DWC3_REVISION_180A 0x5533180a
#define DWC3_REVISION_183A 0x5533183a
#define DWC3_REVISION_185A 0x5533185a
#define DWC3_REVISION_187A 0x5533187a
#define DWC3_REVISION_188A 0x5533188a
#define DWC3_REVISION_190A 0x5533190a
#define DWC3_REVISION_194A 0x5533194a
#define DWC3_REVISION_200A 0x5533200a
#define DWC3_REVISION_202A 0x5533202a
#define DWC3_REVISION_210A 0x5533210a
#define DWC3_REVISION_220A 0x5533220a
#define DWC3_REVISION_230A 0x5533230a
#define DWC3_REVISION_240A 0x5533240a
#define DWC3_REVISION_250A 0x5533250a
#define DWC3_REVISION_260A 0x5533260a
#define DWC3_REVISION_270A 0x5533270a
#define DWC3_REVISION_280A 0x5533280a
#define DWC3_REVISION_290A 0x5533290a
#define DWC3_REVISION_300A 0x5533300a
#define DWC3_REVISION_310A 0x5533310a
#define DWC3_REVISION_330A 0x5533330a

#define DWC31_REVISION_ANY 0x0
#define DWC31_REVISION_110A 0x3131302a
#define DWC31_REVISION_120A 0x3132302a
#define DWC31_REVISION_160A 0x3136302a
#define DWC31_REVISION_170A 0x3137302a
#define DWC31_REVISION_180A 0x3138302a
#define DWC31_REVISION_190A 0x3139302a

#define DWC32_REVISION_ANY 0x0
#define DWC32_REVISION_100A 0x3130302a

#define DWC31_VERSIONTYPE_ANY 0x0
#define DWC31_VERSIONTYPE_EA01 0x65613031
#define DWC31_VERSIONTYPE_EA02 0x65613032
#define DWC31_VERSIONTYPE_EA03 0x65613033
#define DWC31_VERSIONTYPE_EA04 0x65613034
#define DWC31_VERSIONTYPE_EA05 0x65613035
#define DWC31_VERSIONTYPE_EA06 0x65613036

#define DWC3_IP_IS(_ip) \
(snps->ip == _ip##_IP)

#define DWC3_VER_IS(_ip, _ver) \
(DWC3_IP_IS(_ip) && snps->revision == _ip##_REVISION_##_ver)

#define DWC3_VER_IS_PRIOR(_ip, _ver) \
(DWC3_IP_IS(_ip) && snps->revision < _ip##_REVISION_##_ver)

#define DWC3_VER_IS_WITHIN(_ip, _from, _to) \
(DWC3_IP_IS(_ip) && \
snps->revision >= _ip##_REVISION_##_from && \
(!(_ip##_REVISION_##_to) || \
snps->revision <= _ip##_REVISION_##_to))

#define DWC3_VER_TYPE_IS_WITHIN(_ip, _ver, _from, _to) \
(DWC3_VER_IS(_ip, _ver) && \
snps->version_type >= _ip##_VERSIONTYPE_##_from && \
(!(_ip##_VERSIONTYPE_##_to) || \
snps->version_type <= _ip##_VERSIONTYPE_##_to))

/**
* struct xhci_snps - Wrapper for DWC_usb3x host controller
* @hcd: the main host device
* @ip: controller's ID
* @revision: controller's version of an IP
* @version_type: VERSIONTYPE register contents, a sub release of a revision
*/
struct xhci_snps {
struct usb_hcd *hcd;
u32 ip;
u32 revision;
u32 version_type;
};

#define dev_to_xhci_snps(p) (container_of((p), struct xhci_snps, dev))

int xhci_snps_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep)
{
struct xhci_snps *snps = dev_get_drvdata(hcd->self.controller);

if (DWC3_VER_IS_WITHIN(DWC31, ANY, 190A) &&
usb_endpoint_xfer_int(&ep->desc) &&
udev->speed == USB_SPEED_FULL && ep->desc.bInterval == 6)
ep->desc.bInterval = 5;

return xhci_add_endpoint(hcd, udev, ep);
}

static bool xhci_is_snps(struct usb_hcd *hcd)
{
u32 id;

if (hcd->rsrc_len < DWC3_GLOBALS_REGS_END)
return false;

id = DWC3_GSNPS_ID(readl(hcd->regs + DWC3_GSNPSID));

return (id == DWC3_IP || id == DWC31_IP || id == DWC32_IP);
}

static int xhci_snps_init_version(struct xhci_snps *snps)
{
void __iomem *regs = snps->hcd->regs;
u32 reg;

reg = readl(regs + DWC3_GSNPSID);
snps->ip = DWC3_GSNPS_ID(reg);

if (DWC3_IP_IS(DWC3)) {
snps->revision = reg;
} else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) {
snps->revision = readl(regs + DWC3_VER_NUMBER);
snps->version_type = readl(regs + DWC3_VER_TYPE);
} else {
return -EINVAL;
}

return 0;
}

int xhci_snps_setup(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);

/* These quirks are applicable to all DWC_usb3x IPs and versions */
xhci->quirks |= XHCI_SKIP_PHY_INIT | XHCI_SG_TRB_CACHE_SIZE_QUIRK;

return 0;
}

int xhci_snps_init(struct usb_hcd *hcd)
{
struct device *dev = hcd->self.controller;
struct xhci_snps *snps;
int ret;

if (!xhci_is_snps(hcd))
return -EINVAL;

snps = devm_kzalloc(dev, sizeof(*snps), GFP_KERNEL);
if (!snps)
return -ENOMEM;

snps->hcd = hcd;

ret = xhci_snps_init_version(snps);
if (ret)
return ret;

dev_set_drvdata(dev, snps);
return 0;
}
32 changes: 32 additions & 0 deletions drivers/usb/host/xhci-snps.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2022 Synopsys Inc.
*/

#ifndef _XHCI_SNPS_H
#define _XHCI_SNPS_H

#if IS_ENABLED(CONFIG_USB_XHCI_SNPS)
int xhci_snps_init(struct usb_hcd *hcd);
int xhci_snps_setup(struct usb_hcd *hcd);
int xhci_snps_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep);
#else
static inline int xhci_snps_init(struct usb_hcd *hcd)
{
return 0;
}

static inline int xhci_snps_setup(struct usb_hcd *hcd)
{
return 0;
}

static int xhci_snps_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep)
{
return 0;
}
#endif

#endif /* _XHCI_SNPS_H */

0 comments on commit f2403a4

Please sign in to comment.