Message ID | 1588140330-9910-11-git-send-email-chunfeng.yun@mediatek.com |
---|---|
State | Accepted |
Commit | 74102834154c2e575c14e050a62f63e2e52f96de |
Headers | show |
Series | Add support for MediaTek xHCI host controller | expand |
On Wed, Apr 29, 2020 at 11:46 AM Chunfeng Yun <chunfeng.yun at mediatek.com> wrote: > > This patch is used to support the on-chip xHCI controller on > MediaTek SoCs, currently control/bulk/interrupt transfers are > supported. > > Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com> > Reviewed-by: Weijie Gao <weijie.gao at mediatek.com> > --- > v7: use new API of phy bulk > > v6: add Reviewed-by Weijie > > v5: > 1. print error number suggested by Marek > 2. support interrupt transfer > > v4: > 1. use phy_bulk API > > v3: > 1. use macro approach to access registers suggested by Marek > > v2: > 1. use clk_bulk to get clocks suggested by Marek > 2. use clrsetbits_le32() etc suggeseted by Marek > --- > drivers/usb/host/Kconfig | 6 + > drivers/usb/host/Makefile | 1 + > drivers/usb/host/xhci-mtk.c | 303 ++++++++++++++++++++++++++++++++++++ > drivers/usb/host/xhci.c | 10 ++ > include/usb/xhci.h | 3 + > 5 files changed, 323 insertions(+) > create mode 100644 drivers/usb/host/xhci-mtk.c > > diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig > index 94ac969058..2f381dc958 100644 > --- a/drivers/usb/host/Kconfig > +++ b/drivers/usb/host/Kconfig > @@ -30,6 +30,12 @@ config USB_XHCI_DWC3_OF_SIMPLE > Support USB2/3 functionality in simple SoC integrations with > USB controller based on the DesignWare USB3 IP Core. > > +config USB_XHCI_MTK > + bool "Support for MediaTek on-chip xHCI USB controller" > + depends on ARCH_MEDIATEK > + help > + Enables support for the on-chip xHCI controller on MediaTek SoCs. > + > config USB_XHCI_MVEBU > bool "MVEBU USB 3.0 support" > default y > diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile > index b62f346094..e8e3b17e42 100644 > --- a/drivers/usb/host/Makefile > +++ b/drivers/usb/host/Makefile > @@ -51,6 +51,7 @@ obj-$(CONFIG_USB_XHCI_DWC3_OF_SIMPLE) += dwc3-of-simple.o > obj-$(CONFIG_USB_XHCI_ROCKCHIP) += xhci-rockchip.o > obj-$(CONFIG_USB_XHCI_EXYNOS) += xhci-exynos5.o > obj-$(CONFIG_USB_XHCI_FSL) += xhci-fsl.o > +obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o > obj-$(CONFIG_USB_XHCI_MVEBU) += xhci-mvebu.o > obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o > obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o > diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c > new file mode 100644 > index 0000000000..7c71fe728d > --- /dev/null > +++ b/drivers/usb/host/xhci-mtk.c > @@ -0,0 +1,303 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (c) 2019 MediaTek, Inc. > + * Authors: Chunfeng Yun <chunfeng.yun at mediatek.com> > + */ > + > +#include <clk.h> > +#include <common.h> > +#include <dm.h> > +#include <dm/devres.h> > +#include <generic-phy.h> > +#include <malloc.h> > +#include <usb.h> > +#include <linux/errno.h> > +#include <linux/compat.h> > +#include <power/regulator.h> > +#include <linux/iopoll.h> > +#include <usb/xhci.h> > + > +/* IPPC (IP Port Control) registers */ > +#define IPPC_IP_PW_CTRL0 0x00 > +#define CTRL0_IP_SW_RST BIT(0) > + > +#define IPPC_IP_PW_CTRL1 0x04 > +#define CTRL1_IP_HOST_PDN BIT(0) > + > +#define IPPC_IP_PW_STS1 0x10 > +#define STS1_IP_SLEEP_STS BIT(30) > +#define STS1_U3_MAC_RST BIT(16) > +#define STS1_XHCI_RST BIT(11) > +#define STS1_SYS125_RST BIT(10) > +#define STS1_REF_RST BIT(8) > +#define STS1_SYSPLL_STABLE BIT(0) > + > +#define IPPC_IP_XHCI_CAP 0x24 > +#define CAP_U3_PORT_NUM(p) ((p) & 0xff) > +#define CAP_U2_PORT_NUM(p) (((p) >> 8) & 0xff) > + > +#define IPPC_U3_CTRL_0P 0x30 > +#define CTRL_U3_PORT_HOST_SEL BIT(2) > +#define CTRL_U3_PORT_PDN BIT(1) > +#define CTRL_U3_PORT_DIS BIT(0) > + > +#define IPPC_U2_CTRL_0P 0x50 > +#define CTRL_U2_PORT_HOST_SEL BIT(2) > +#define CTRL_U2_PORT_PDN BIT(1) > +#define CTRL_U2_PORT_DIS BIT(0) > + > +#define IPPC_U3_CTRL(p) (IPPC_U3_CTRL_0P + ((p) * 0x08)) > +#define IPPC_U2_CTRL(p) (IPPC_U2_CTRL_0P + ((p) * 0x08)) > + > +struct mtk_xhci { > + struct xhci_ctrl ctrl; /* Needs to come first in this struct! */ > + struct xhci_hccr *hcd; > + void __iomem *ippc; > + struct udevice *dev; > + struct udevice *vusb33_supply; > + struct udevice *vbus_supply; > + struct clk_bulk clks; > + struct phy_bulk phys; > + int num_u2ports; > + int num_u3ports; > +}; > + > +static int xhci_mtk_host_enable(struct mtk_xhci *mtk) > +{ > + u32 value; > + u32 check_val; > + int ret; > + int i; > + > + /* power on host ip */ > + clrbits_le32(mtk->ippc + IPPC_IP_PW_CTRL1, CTRL1_IP_HOST_PDN); > + > + /* power on and enable all u3 ports */ > + for (i = 0; i < mtk->num_u3ports; i++) { > + clrsetbits_le32(mtk->ippc + IPPC_U3_CTRL(i), > + CTRL_U3_PORT_PDN | CTRL_U3_PORT_DIS, > + CTRL_U3_PORT_HOST_SEL); > + } > + > + /* power on and enable all u2 ports */ > + for (i = 0; i < mtk->num_u2ports; i++) { > + clrsetbits_le32(mtk->ippc + IPPC_U2_CTRL(i), > + CTRL_U2_PORT_PDN | CTRL_U2_PORT_DIS, > + CTRL_U2_PORT_HOST_SEL); > + } > + > + /* > + * wait for clocks to be stable, and clock domains reset to > + * be inactive after power on and enable ports > + */ > + check_val = STS1_SYSPLL_STABLE | STS1_REF_RST | > + STS1_SYS125_RST | STS1_XHCI_RST; > + > + if (mtk->num_u3ports) > + check_val |= STS1_U3_MAC_RST; > + > + ret = readl_poll_timeout(mtk->ippc + IPPC_IP_PW_STS1, value, > + (check_val == (value & check_val)), 20000); > + if (ret) > + dev_err(mtk->dev, "clocks are not stable 0x%x!\n", value); > + > + return ret; > +} > + > +static int xhci_mtk_host_disable(struct mtk_xhci *mtk) > +{ > + int i; > + > + /* power down all u3 ports */ > + for (i = 0; i < mtk->num_u3ports; i++) > + setbits_le32(mtk->ippc + IPPC_U3_CTRL(i), CTRL_U3_PORT_PDN); > + > + /* power down all u2 ports */ > + for (i = 0; i < mtk->num_u2ports; i++) > + setbits_le32(mtk->ippc + IPPC_U2_CTRL(i), CTRL_U2_PORT_PDN); > + > + /* power down host ip */ > + setbits_le32(mtk->ippc + IPPC_IP_PW_CTRL1, CTRL1_IP_HOST_PDN); > + > + return 0; > +} > + > +static int xhci_mtk_ssusb_init(struct mtk_xhci *mtk) > +{ > + u32 value; > + > + /* reset whole ip */ > + setbits_le32(mtk->ippc + IPPC_IP_PW_CTRL0, CTRL0_IP_SW_RST); > + udelay(1); > + clrbits_le32(mtk->ippc + IPPC_IP_PW_CTRL0, CTRL0_IP_SW_RST); > + > + value = readl(mtk->ippc + IPPC_IP_XHCI_CAP); > + mtk->num_u3ports = CAP_U3_PORT_NUM(value); > + mtk->num_u2ports = CAP_U2_PORT_NUM(value); > + dev_info(mtk->dev, "u2p:%d, u3p:%d\n", > + mtk->num_u2ports, mtk->num_u3ports); > + > + return xhci_mtk_host_enable(mtk); > +} > + > +static int xhci_mtk_ofdata_get(struct mtk_xhci *mtk) > +{ > + struct udevice *dev = mtk->dev; > + int ret = 0; > + > + mtk->hcd = devfdt_remap_addr_name(dev, "mac"); > + if (!mtk->hcd) { > + dev_err(dev, "failed to get xHCI base address\n"); > + return -ENXIO; > + } > + > + mtk->ippc = devfdt_remap_addr_name(dev, "ippc"); > + if (!mtk->ippc) { > + dev_err(dev, "failed to get IPPC base address\n"); > + return -ENXIO; > + } > + > + dev_info(dev, "hcd: 0x%p, ippc: 0x%p\n", mtk->hcd, mtk->ippc); > + > + ret = clk_get_bulk(dev, &mtk->clks); > + if (ret) { > + dev_err(dev, "failed to get clocks %d!\n", ret); > + return ret; > + } > + > + ret = device_get_supply_regulator(dev, "vusb33-supply", > + &mtk->vusb33_supply); > + if (ret) > + debug("can't get vusb33 regulator %d!\n", ret); > + > + ret = device_get_supply_regulator(dev, "vbus-supply", > + &mtk->vbus_supply); > + if (ret) > + debug("can't get vbus regulator %d!\n", ret); > + > + return 0; > +} > + > +static int xhci_mtk_ldos_enable(struct mtk_xhci *mtk) > +{ > + int ret; > + > + ret = regulator_set_enable(mtk->vusb33_supply, true); > + if (ret < 0 && ret != -ENOSYS) { > + dev_err(mtk->dev, "failed to enable vusb33 %d!\n", ret); > + return ret; > + } > + > + ret = regulator_set_enable(mtk->vbus_supply, true); > + if (ret < 0 && ret != -ENOSYS) { > + dev_err(mtk->dev, "failed to enable vbus %d!\n", ret); > + regulator_set_enable(mtk->vusb33_supply, false); > + return ret; > + } > + > + return 0; > +} > + > +static void xhci_mtk_ldos_disable(struct mtk_xhci *mtk) > +{ > + regulator_set_enable(mtk->vbus_supply, false); > + regulator_set_enable(mtk->vusb33_supply, false); > +} > + > +int xhci_mtk_phy_setup(struct mtk_xhci *mtk) > +{ > + struct udevice *dev = mtk->dev; > + struct phy_bulk *phys = &mtk->phys; > + int ret; > + > + ret = generic_phy_get_bulk(dev, phys); > + if (ret) > + return ret; > + > + ret = generic_phy_init_bulk(phys); > + if (ret) > + return ret; > + > + ret = generic_phy_power_on_bulk(phys); > + if (ret) > + generic_phy_exit_bulk(phys); > + > + return ret; > +} > + > +void xhci_mtk_phy_shutdown(struct mtk_xhci *mtk) > +{ > + generic_phy_power_off_bulk(&mtk->phys); > + generic_phy_exit_bulk(&mtk->phys); > +} Hope these are local functions, if yes mark it as static. otherwise, Reviewed-by: Jagan Teki <jagan at amarulasolutions.com>
On Fri, 2020-05-01 at 23:00 +0530, Jagan Teki wrote: > On Wed, Apr 29, 2020 at 11:46 AM Chunfeng Yun <chunfeng.yun at mediatek.com> wrote: > > > > This patch is used to support the on-chip xHCI controller on > > MediaTek SoCs, currently control/bulk/interrupt transfers are > > supported. > > > > Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com> > > Reviewed-by: Weijie Gao <weijie.gao at mediatek.com> ... > > +int xhci_mtk_phy_setup(struct mtk_xhci *mtk) > > +{ > > + struct udevice *dev = mtk->dev; > > + struct phy_bulk *phys = &mtk->phys; > > + int ret; > > + > > + ret = generic_phy_get_bulk(dev, phys); > > + if (ret) > > + return ret; > > + > > + ret = generic_phy_init_bulk(phys); > > + if (ret) > > + return ret; > > + > > + ret = generic_phy_power_on_bulk(phys); > > + if (ret) > > + generic_phy_exit_bulk(phys); > > + > > + return ret; > > +} > > + > > +void xhci_mtk_phy_shutdown(struct mtk_xhci *mtk) > > +{ > > + generic_phy_power_off_bulk(&mtk->phys); > > + generic_phy_exit_bulk(&mtk->phys); > > +} > > Hope these are local functions, if yes mark it as static. Ok, thanks > > otherwise, > > Reviewed-by: Jagan Teki <jagan at amarulasolutions.com>
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 94ac969058..2f381dc958 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -30,6 +30,12 @@ config USB_XHCI_DWC3_OF_SIMPLE Support USB2/3 functionality in simple SoC integrations with USB controller based on the DesignWare USB3 IP Core. +config USB_XHCI_MTK + bool "Support for MediaTek on-chip xHCI USB controller" + depends on ARCH_MEDIATEK + help + Enables support for the on-chip xHCI controller on MediaTek SoCs. + config USB_XHCI_MVEBU bool "MVEBU USB 3.0 support" default y diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index b62f346094..e8e3b17e42 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_USB_XHCI_DWC3_OF_SIMPLE) += dwc3-of-simple.o obj-$(CONFIG_USB_XHCI_ROCKCHIP) += xhci-rockchip.o obj-$(CONFIG_USB_XHCI_EXYNOS) += xhci-exynos5.o obj-$(CONFIG_USB_XHCI_FSL) += xhci-fsl.o +obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o obj-$(CONFIG_USB_XHCI_MVEBU) += xhci-mvebu.o obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c new file mode 100644 index 0000000000..7c71fe728d --- /dev/null +++ b/drivers/usb/host/xhci-mtk.c @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 MediaTek, Inc. + * Authors: Chunfeng Yun <chunfeng.yun at mediatek.com> + */ + +#include <clk.h> +#include <common.h> +#include <dm.h> +#include <dm/devres.h> +#include <generic-phy.h> +#include <malloc.h> +#include <usb.h> +#include <linux/errno.h> +#include <linux/compat.h> +#include <power/regulator.h> +#include <linux/iopoll.h> +#include <usb/xhci.h> + +/* IPPC (IP Port Control) registers */ +#define IPPC_IP_PW_CTRL0 0x00 +#define CTRL0_IP_SW_RST BIT(0) + +#define IPPC_IP_PW_CTRL1 0x04 +#define CTRL1_IP_HOST_PDN BIT(0) + +#define IPPC_IP_PW_STS1 0x10 +#define STS1_IP_SLEEP_STS BIT(30) +#define STS1_U3_MAC_RST BIT(16) +#define STS1_XHCI_RST BIT(11) +#define STS1_SYS125_RST BIT(10) +#define STS1_REF_RST BIT(8) +#define STS1_SYSPLL_STABLE BIT(0) + +#define IPPC_IP_XHCI_CAP 0x24 +#define CAP_U3_PORT_NUM(p) ((p) & 0xff) +#define CAP_U2_PORT_NUM(p) (((p) >> 8) & 0xff) + +#define IPPC_U3_CTRL_0P 0x30 +#define CTRL_U3_PORT_HOST_SEL BIT(2) +#define CTRL_U3_PORT_PDN BIT(1) +#define CTRL_U3_PORT_DIS BIT(0) + +#define IPPC_U2_CTRL_0P 0x50 +#define CTRL_U2_PORT_HOST_SEL BIT(2) +#define CTRL_U2_PORT_PDN BIT(1) +#define CTRL_U2_PORT_DIS BIT(0) + +#define IPPC_U3_CTRL(p) (IPPC_U3_CTRL_0P + ((p) * 0x08)) +#define IPPC_U2_CTRL(p) (IPPC_U2_CTRL_0P + ((p) * 0x08)) + +struct mtk_xhci { + struct xhci_ctrl ctrl; /* Needs to come first in this struct! */ + struct xhci_hccr *hcd; + void __iomem *ippc; + struct udevice *dev; + struct udevice *vusb33_supply; + struct udevice *vbus_supply; + struct clk_bulk clks; + struct phy_bulk phys; + int num_u2ports; + int num_u3ports; +}; + +static int xhci_mtk_host_enable(struct mtk_xhci *mtk) +{ + u32 value; + u32 check_val; + int ret; + int i; + + /* power on host ip */ + clrbits_le32(mtk->ippc + IPPC_IP_PW_CTRL1, CTRL1_IP_HOST_PDN); + + /* power on and enable all u3 ports */ + for (i = 0; i < mtk->num_u3ports; i++) { + clrsetbits_le32(mtk->ippc + IPPC_U3_CTRL(i), + CTRL_U3_PORT_PDN | CTRL_U3_PORT_DIS, + CTRL_U3_PORT_HOST_SEL); + } + + /* power on and enable all u2 ports */ + for (i = 0; i < mtk->num_u2ports; i++) { + clrsetbits_le32(mtk->ippc + IPPC_U2_CTRL(i), + CTRL_U2_PORT_PDN | CTRL_U2_PORT_DIS, + CTRL_U2_PORT_HOST_SEL); + } + + /* + * wait for clocks to be stable, and clock domains reset to + * be inactive after power on and enable ports + */ + check_val = STS1_SYSPLL_STABLE | STS1_REF_RST | + STS1_SYS125_RST | STS1_XHCI_RST; + + if (mtk->num_u3ports) + check_val |= STS1_U3_MAC_RST; + + ret = readl_poll_timeout(mtk->ippc + IPPC_IP_PW_STS1, value, + (check_val == (value & check_val)), 20000); + if (ret) + dev_err(mtk->dev, "clocks are not stable 0x%x!\n", value); + + return ret; +} + +static int xhci_mtk_host_disable(struct mtk_xhci *mtk) +{ + int i; + + /* power down all u3 ports */ + for (i = 0; i < mtk->num_u3ports; i++) + setbits_le32(mtk->ippc + IPPC_U3_CTRL(i), CTRL_U3_PORT_PDN); + + /* power down all u2 ports */ + for (i = 0; i < mtk->num_u2ports; i++) + setbits_le32(mtk->ippc + IPPC_U2_CTRL(i), CTRL_U2_PORT_PDN); + + /* power down host ip */ + setbits_le32(mtk->ippc + IPPC_IP_PW_CTRL1, CTRL1_IP_HOST_PDN); + + return 0; +} + +static int xhci_mtk_ssusb_init(struct mtk_xhci *mtk) +{ + u32 value; + + /* reset whole ip */ + setbits_le32(mtk->ippc + IPPC_IP_PW_CTRL0, CTRL0_IP_SW_RST); + udelay(1); + clrbits_le32(mtk->ippc + IPPC_IP_PW_CTRL0, CTRL0_IP_SW_RST); + + value = readl(mtk->ippc + IPPC_IP_XHCI_CAP); + mtk->num_u3ports = CAP_U3_PORT_NUM(value); + mtk->num_u2ports = CAP_U2_PORT_NUM(value); + dev_info(mtk->dev, "u2p:%d, u3p:%d\n", + mtk->num_u2ports, mtk->num_u3ports); + + return xhci_mtk_host_enable(mtk); +} + +static int xhci_mtk_ofdata_get(struct mtk_xhci *mtk) +{ + struct udevice *dev = mtk->dev; + int ret = 0; + + mtk->hcd = devfdt_remap_addr_name(dev, "mac"); + if (!mtk->hcd) { + dev_err(dev, "failed to get xHCI base address\n"); + return -ENXIO; + } + + mtk->ippc = devfdt_remap_addr_name(dev, "ippc"); + if (!mtk->ippc) { + dev_err(dev, "failed to get IPPC base address\n"); + return -ENXIO; + } + + dev_info(dev, "hcd: 0x%p, ippc: 0x%p\n", mtk->hcd, mtk->ippc); + + ret = clk_get_bulk(dev, &mtk->clks); + if (ret) { + dev_err(dev, "failed to get clocks %d!\n", ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vusb33-supply", + &mtk->vusb33_supply); + if (ret) + debug("can't get vusb33 regulator %d!\n", ret); + + ret = device_get_supply_regulator(dev, "vbus-supply", + &mtk->vbus_supply); + if (ret) + debug("can't get vbus regulator %d!\n", ret); + + return 0; +} + +static int xhci_mtk_ldos_enable(struct mtk_xhci *mtk) +{ + int ret; + + ret = regulator_set_enable(mtk->vusb33_supply, true); + if (ret < 0 && ret != -ENOSYS) { + dev_err(mtk->dev, "failed to enable vusb33 %d!\n", ret); + return ret; + } + + ret = regulator_set_enable(mtk->vbus_supply, true); + if (ret < 0 && ret != -ENOSYS) { + dev_err(mtk->dev, "failed to enable vbus %d!\n", ret); + regulator_set_enable(mtk->vusb33_supply, false); + return ret; + } + + return 0; +} + +static void xhci_mtk_ldos_disable(struct mtk_xhci *mtk) +{ + regulator_set_enable(mtk->vbus_supply, false); + regulator_set_enable(mtk->vusb33_supply, false); +} + +int xhci_mtk_phy_setup(struct mtk_xhci *mtk) +{ + struct udevice *dev = mtk->dev; + struct phy_bulk *phys = &mtk->phys; + int ret; + + ret = generic_phy_get_bulk(dev, phys); + if (ret) + return ret; + + ret = generic_phy_init_bulk(phys); + if (ret) + return ret; + + ret = generic_phy_power_on_bulk(phys); + if (ret) + generic_phy_exit_bulk(phys); + + return ret; +} + +void xhci_mtk_phy_shutdown(struct mtk_xhci *mtk) +{ + generic_phy_power_off_bulk(&mtk->phys); + generic_phy_exit_bulk(&mtk->phys); +} + +static int xhci_mtk_probe(struct udevice *dev) +{ + struct mtk_xhci *mtk = dev_get_priv(dev); + struct xhci_hcor *hcor; + int ret; + + mtk->dev = dev; + ret = xhci_mtk_ofdata_get(mtk); + if (ret) + return ret; + + ret = xhci_mtk_ldos_enable(mtk); + if (ret) + goto ldos_err; + + ret = clk_enable_bulk(&mtk->clks); + if (ret) + goto clks_err; + + ret = xhci_mtk_phy_setup(mtk); + if (ret) + goto phys_err; + + ret = xhci_mtk_ssusb_init(mtk); + if (ret) + goto ssusb_init_err; + + hcor = (struct xhci_hcor *)((uintptr_t)mtk->hcd + + HC_LENGTH(xhci_readl(&mtk->hcd->cr_capbase))); + + return xhci_register(dev, mtk->hcd, hcor); + +ssusb_init_err: + xhci_mtk_phy_shutdown(mtk); +phys_err: + clk_disable_bulk(&mtk->clks); +clks_err: + xhci_mtk_ldos_disable(mtk); +ldos_err: + return ret; +} + +static int xhci_mtk_remove(struct udevice *dev) +{ + struct mtk_xhci *mtk = dev_get_priv(dev); + + xhci_deregister(dev); + xhci_mtk_host_disable(mtk); + xhci_mtk_ldos_disable(mtk); + clk_disable_bulk(&mtk->clks); + + return 0; +} + +static const struct udevice_id xhci_mtk_ids[] = { + { .compatible = "mediatek,mtk-xhci" }, + { } +}; + +U_BOOT_DRIVER(usb_xhci) = { + .name = "xhci-mtk", + .id = UCLASS_USB, + .of_match = xhci_mtk_ids, + .probe = xhci_mtk_probe, + .remove = xhci_mtk_remove, + .ops = &xhci_usb_ops, + .bind = dm_scan_fdt_dev, + .priv_auto_alloc_size = sizeof(struct mtk_xhci), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 40dee2e6d9..c370eb6394 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -610,6 +610,16 @@ static int xhci_set_configuration(struct usb_device *udev) ep_ctx[ep_index]->tx_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_LO(max_esit_payload) | EP_AVG_TRB_LENGTH(avg_trb_len)); + + /* + * The MediaTek xHCI defines some extra SW parameters which + * are put into reserved DWs in Slot and Endpoint Contexts + * for synchronous endpoints. + */ + if (IS_ENABLED(CONFIG_USB_XHCI_MTK)) { + ep_ctx[ep_index]->reserved[0] = + cpu_to_le32(EP_BPKTS(1) | EP_BBM(1)); + } } return xhci_configure_endpoints(udev, false); diff --git a/include/usb/xhci.h b/include/usb/xhci.h index 6017504488..20e4a21066 100644 --- a/include/usb/xhci.h +++ b/include/usb/xhci.h @@ -670,6 +670,9 @@ struct xhci_ep_ctx { /* deq bitmasks */ #define EP_CTX_CYCLE_MASK (1 << 0) +/* reserved[0] bitmasks, MediaTek xHCI used */ +#define EP_BPKTS(p) (((p) & 0x7f) << 0) +#define EP_BBM(p) (((p) & 0x1) << 11) /** * struct xhci_input_control_context