All of lore.kernel.org
 help / color / mirror / Atom feed
From: Chunfeng Yun <chunfeng.yun@mediatek.com>
To: u-boot@lists.denx.de
Subject: [PATCH v6 10/14] xhci: mediatek: Add support for MTK xHCI host controller
Date: Mon, 20 Apr 2020 11:21:19 +0800	[thread overview]
Message-ID: <1587352883-8641-11-git-send-email-chunfeng.yun@mediatek.com> (raw)
In-Reply-To: <1587352883-8641-1-git-send-email-chunfeng.yun@mediatek.com>

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@mediatek.com>
Reviewed-by: Weijie Gao <weijie.gao@mediatek.com>
---
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 | 296 ++++++++++++++++++++++++++++++++++++
 drivers/usb/host/xhci.c     |  10 ++
 include/usb/xhci.h          |   3 +
 5 files changed, 316 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..93293ca180
--- /dev/null
+++ b/drivers/usb/host/xhci-mtk.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 MediaTek, Inc.
+ * Authors: Chunfeng Yun <chunfeng.yun@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) {
+		dev_err(dev, "failed to get phys %d!\n", ret);
+		return ret;
+	}
+
+	return generic_phy_enable_bulk(phys);
+}
+
+void xhci_mtk_phy_shutdown(struct mtk_xhci *mtk)
+{
+	generic_phy_disable_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
-- 
2.25.1

  parent reply	other threads:[~2020-04-20  3:21 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-04-20  3:21 [PATCH v6 00/14] Add support for MediaTek xHCI host controller Chunfeng Yun
2020-04-20  3:21 ` [PATCH v6 01/14] dm: core: Add function to get child count of ofnode or device Chunfeng Yun
2020-04-20  3:21 ` [PATCH v6 02/14] test: dm: add test item for ofnode_get_child_count() Chunfeng Yun
2020-04-20 13:10   ` Simon Glass
2020-04-20 14:07   ` Fabio Estevam
2020-04-21  1:39     ` Chunfeng Yun
2020-04-20  3:21 ` [PATCH v6 03/14] phy: Add get/enable/disable for a bulk of phys Chunfeng Yun
2020-04-25 13:28   ` Jagan Teki
2020-04-27  2:16     ` Chunfeng Yun
2020-04-28 19:45       ` Jagan Teki
2020-04-29  1:41         ` Chunfeng Yun
2020-04-20  3:21 ` [PATCH v6 04/14] test: dm: phy: add a test item for the phy_bulk API Chunfeng Yun
2020-04-20  3:21 ` [PATCH v6 05/14] usb: dwc3: use the phy bulk API to get phys Chunfeng Yun
2020-04-20  3:21 ` [PATCH v6 06/14] usb: dwc2_udc_otg: " Chunfeng Yun
2020-04-20  3:21 ` [PATCH v6 07/14] phy: phy-mtk-tphy: add support USB phys Chunfeng Yun
2020-04-20  3:21 ` [PATCH v6 08/14] phy: phy-mtk-tphy: add support new version Chunfeng Yun
2020-04-20  3:21 ` [PATCH v6 09/14] phy: phy-mtk-tphy: add a new reference clock Chunfeng Yun
2020-04-20  3:21 ` Chunfeng Yun [this message]
2020-04-20  3:21 ` [PATCH v6 11/14] arm: dts: mt7629: add usb related nodes Chunfeng Yun
2020-04-20  3:21 ` [PATCH v6 12/14] dt-bindings: phy-mtk-tphy: add properties of address mapping and clocks Chunfeng Yun
2020-04-20  3:21 ` [PATCH v6 13/14] dt-bindings: usb: mtk-xhci: Add binding for MediaTek xHCI host controller Chunfeng Yun
2020-04-20  3:21 ` [PATCH v6 14/14] MAINTAINERS: MediaTek: add USB related files Chunfeng Yun

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1587352883-8641-11-git-send-email-chunfeng.yun@mediatek.com \
    --to=chunfeng.yun@mediatek.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.