On Wed, Apr 29, 2020 at 11:46 AM Chunfeng Yun <chunfeng....@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....@mediatek.com> > Reviewed-by: Weijie Gao <weijie....@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....@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 <ja...@amarulasolutions.com>