162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * xHCI host controller driver for R-Car SoCs
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014 Renesas Electronics Corporation
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/firmware.h>
962306a36Sopenharmony_ci#include <linux/iopoll.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/usb/phy.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "xhci.h"
1662306a36Sopenharmony_ci#include "xhci-plat.h"
1762306a36Sopenharmony_ci#include "xhci-rzv2m.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define XHCI_RCAR_FIRMWARE_NAME_V1	"r8a779x_usb3_v1.dlmem"
2062306a36Sopenharmony_ci#define XHCI_RCAR_FIRMWARE_NAME_V3	"r8a779x_usb3_v3.dlmem"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*
2362306a36Sopenharmony_ci* - The V3 firmware is for all R-Car Gen3
2462306a36Sopenharmony_ci* - The V2 firmware is possible to use on R-Car Gen2. However, the V2 causes
2562306a36Sopenharmony_ci*   performance degradation. So, this driver continues to use the V1 if R-Car
2662306a36Sopenharmony_ci*   Gen2.
2762306a36Sopenharmony_ci* - The V1 firmware is impossible to use on R-Car Gen3.
2862306a36Sopenharmony_ci*/
2962306a36Sopenharmony_ciMODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V1);
3062306a36Sopenharmony_ciMODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V3);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*** Register Offset ***/
3362306a36Sopenharmony_ci#define RCAR_USB3_AXH_STA	0x104	/* AXI Host Control Status */
3462306a36Sopenharmony_ci#define RCAR_USB3_INT_ENA	0x224	/* Interrupt Enable */
3562306a36Sopenharmony_ci#define RCAR_USB3_DL_CTRL	0x250	/* FW Download Control & Status */
3662306a36Sopenharmony_ci#define RCAR_USB3_FW_DATA0	0x258	/* FW Data0 */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define RCAR_USB3_LCLK		0xa44	/* LCLK Select */
3962306a36Sopenharmony_ci#define RCAR_USB3_CONF1		0xa48	/* USB3.0 Configuration1 */
4062306a36Sopenharmony_ci#define RCAR_USB3_CONF2		0xa5c	/* USB3.0 Configuration2 */
4162306a36Sopenharmony_ci#define RCAR_USB3_CONF3		0xaa8	/* USB3.0 Configuration3 */
4262306a36Sopenharmony_ci#define RCAR_USB3_RX_POL	0xab0	/* USB3.0 RX Polarity */
4362306a36Sopenharmony_ci#define RCAR_USB3_TX_POL	0xab8	/* USB3.0 TX Polarity */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*** Register Settings ***/
4662306a36Sopenharmony_ci/* AXI Host Control Status */
4762306a36Sopenharmony_ci#define RCAR_USB3_AXH_STA_B3_PLL_ACTIVE		0x00010000
4862306a36Sopenharmony_ci#define RCAR_USB3_AXH_STA_B2_PLL_ACTIVE		0x00000001
4962306a36Sopenharmony_ci#define RCAR_USB3_AXH_STA_PLL_ACTIVE_MASK (RCAR_USB3_AXH_STA_B3_PLL_ACTIVE | \
5062306a36Sopenharmony_ci					   RCAR_USB3_AXH_STA_B2_PLL_ACTIVE)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/* Interrupt Enable */
5362306a36Sopenharmony_ci#define RCAR_USB3_INT_XHC_ENA	0x00000001
5462306a36Sopenharmony_ci#define RCAR_USB3_INT_PME_ENA	0x00000002
5562306a36Sopenharmony_ci#define RCAR_USB3_INT_HSE_ENA	0x00000004
5662306a36Sopenharmony_ci#define RCAR_USB3_INT_ENA_VAL	(RCAR_USB3_INT_XHC_ENA | \
5762306a36Sopenharmony_ci				RCAR_USB3_INT_PME_ENA | RCAR_USB3_INT_HSE_ENA)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/* FW Download Control & Status */
6062306a36Sopenharmony_ci#define RCAR_USB3_DL_CTRL_ENABLE	0x00000001
6162306a36Sopenharmony_ci#define RCAR_USB3_DL_CTRL_FW_SUCCESS	0x00000010
6262306a36Sopenharmony_ci#define RCAR_USB3_DL_CTRL_FW_SET_DATA0	0x00000100
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* LCLK Select */
6562306a36Sopenharmony_ci#define RCAR_USB3_LCLK_ENA_VAL	0x01030001
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* USB3.0 Configuration */
6862306a36Sopenharmony_ci#define RCAR_USB3_CONF1_VAL	0x00030204
6962306a36Sopenharmony_ci#define RCAR_USB3_CONF2_VAL	0x00030300
7062306a36Sopenharmony_ci#define RCAR_USB3_CONF3_VAL	0x13802007
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* USB3.0 Polarity */
7362306a36Sopenharmony_ci#define RCAR_USB3_RX_POL_VAL	BIT(21)
7462306a36Sopenharmony_ci#define RCAR_USB3_TX_POL_VAL	BIT(4)
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic void xhci_rcar_start_gen2(struct usb_hcd *hcd)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	/* LCLK Select */
7962306a36Sopenharmony_ci	writel(RCAR_USB3_LCLK_ENA_VAL, hcd->regs + RCAR_USB3_LCLK);
8062306a36Sopenharmony_ci	/* USB3.0 Configuration */
8162306a36Sopenharmony_ci	writel(RCAR_USB3_CONF1_VAL, hcd->regs + RCAR_USB3_CONF1);
8262306a36Sopenharmony_ci	writel(RCAR_USB3_CONF2_VAL, hcd->regs + RCAR_USB3_CONF2);
8362306a36Sopenharmony_ci	writel(RCAR_USB3_CONF3_VAL, hcd->regs + RCAR_USB3_CONF3);
8462306a36Sopenharmony_ci	/* USB3.0 Polarity */
8562306a36Sopenharmony_ci	writel(RCAR_USB3_RX_POL_VAL, hcd->regs + RCAR_USB3_RX_POL);
8662306a36Sopenharmony_ci	writel(RCAR_USB3_TX_POL_VAL, hcd->regs + RCAR_USB3_TX_POL);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int xhci_rcar_is_gen2(struct device *dev)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct device_node *node = dev->of_node;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return of_device_is_compatible(node, "renesas,xhci-r8a7790") ||
9462306a36Sopenharmony_ci		of_device_is_compatible(node, "renesas,xhci-r8a7791") ||
9562306a36Sopenharmony_ci		of_device_is_compatible(node, "renesas,xhci-r8a7793") ||
9662306a36Sopenharmony_ci		of_device_is_compatible(node, "renesas,rcar-gen2-xhci");
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void xhci_rcar_start(struct usb_hcd *hcd)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	u32 temp;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (hcd->regs != NULL) {
10462306a36Sopenharmony_ci		/* Interrupt Enable */
10562306a36Sopenharmony_ci		temp = readl(hcd->regs + RCAR_USB3_INT_ENA);
10662306a36Sopenharmony_ci		temp |= RCAR_USB3_INT_ENA_VAL;
10762306a36Sopenharmony_ci		writel(temp, hcd->regs + RCAR_USB3_INT_ENA);
10862306a36Sopenharmony_ci		if (xhci_rcar_is_gen2(hcd->self.controller))
10962306a36Sopenharmony_ci			xhci_rcar_start_gen2(hcd);
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int xhci_rcar_download_firmware(struct usb_hcd *hcd)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct device *dev = hcd->self.controller;
11662306a36Sopenharmony_ci	void __iomem *regs = hcd->regs;
11762306a36Sopenharmony_ci	struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
11862306a36Sopenharmony_ci	const struct firmware *fw;
11962306a36Sopenharmony_ci	int retval, index, j;
12062306a36Sopenharmony_ci	u32 data, val, temp;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/*
12362306a36Sopenharmony_ci	 * According to the datasheet, "Upon the completion of FW Download,
12462306a36Sopenharmony_ci	 * there is no need to write or reload FW".
12562306a36Sopenharmony_ci	 */
12662306a36Sopenharmony_ci	if (readl(regs + RCAR_USB3_DL_CTRL) & RCAR_USB3_DL_CTRL_FW_SUCCESS)
12762306a36Sopenharmony_ci		return 0;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* request R-Car USB3.0 firmware */
13062306a36Sopenharmony_ci	retval = request_firmware(&fw, priv->firmware_name, dev);
13162306a36Sopenharmony_ci	if (retval)
13262306a36Sopenharmony_ci		return retval;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* download R-Car USB3.0 firmware */
13562306a36Sopenharmony_ci	temp = readl(regs + RCAR_USB3_DL_CTRL);
13662306a36Sopenharmony_ci	temp |= RCAR_USB3_DL_CTRL_ENABLE;
13762306a36Sopenharmony_ci	writel(temp, regs + RCAR_USB3_DL_CTRL);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	for (index = 0; index < fw->size; index += 4) {
14062306a36Sopenharmony_ci		/* to avoid reading beyond the end of the buffer */
14162306a36Sopenharmony_ci		for (data = 0, j = 3; j >= 0; j--) {
14262306a36Sopenharmony_ci			if ((j + index) < fw->size)
14362306a36Sopenharmony_ci				data |= fw->data[index + j] << (8 * j);
14462306a36Sopenharmony_ci		}
14562306a36Sopenharmony_ci		writel(data, regs + RCAR_USB3_FW_DATA0);
14662306a36Sopenharmony_ci		temp = readl(regs + RCAR_USB3_DL_CTRL);
14762306a36Sopenharmony_ci		temp |= RCAR_USB3_DL_CTRL_FW_SET_DATA0;
14862306a36Sopenharmony_ci		writel(temp, regs + RCAR_USB3_DL_CTRL);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		retval = readl_poll_timeout_atomic(regs + RCAR_USB3_DL_CTRL,
15162306a36Sopenharmony_ci				val, !(val & RCAR_USB3_DL_CTRL_FW_SET_DATA0),
15262306a36Sopenharmony_ci				1, 10000);
15362306a36Sopenharmony_ci		if (retval < 0)
15462306a36Sopenharmony_ci			break;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	temp = readl(regs + RCAR_USB3_DL_CTRL);
15862306a36Sopenharmony_ci	temp &= ~RCAR_USB3_DL_CTRL_ENABLE;
15962306a36Sopenharmony_ci	writel(temp, regs + RCAR_USB3_DL_CTRL);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	retval = readl_poll_timeout_atomic((regs + RCAR_USB3_DL_CTRL),
16262306a36Sopenharmony_ci			val, val & RCAR_USB3_DL_CTRL_FW_SUCCESS, 1, 10000);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	release_firmware(fw);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return retval;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic bool xhci_rcar_wait_for_pll_active(struct usb_hcd *hcd)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	int retval;
17262306a36Sopenharmony_ci	u32 val, mask = RCAR_USB3_AXH_STA_PLL_ACTIVE_MASK;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	retval = readl_poll_timeout_atomic(hcd->regs + RCAR_USB3_AXH_STA,
17562306a36Sopenharmony_ci			val, (val & mask) == mask, 1, 1000);
17662306a36Sopenharmony_ci	return !retval;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/* This function needs to initialize a "phy" of usb before */
18062306a36Sopenharmony_cistatic int xhci_rcar_init_quirk(struct usb_hcd *hcd)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	/* If hcd->regs is NULL, we don't just call the following function */
18362306a36Sopenharmony_ci	if (!hcd->regs)
18462306a36Sopenharmony_ci		return 0;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (!xhci_rcar_wait_for_pll_active(hcd))
18762306a36Sopenharmony_ci		return -ETIMEDOUT;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return xhci_rcar_download_firmware(hcd);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int xhci_rcar_resume_quirk(struct usb_hcd *hcd)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	int ret;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	ret = xhci_rcar_download_firmware(hcd);
19762306a36Sopenharmony_ci	if (!ret)
19862306a36Sopenharmony_ci		xhci_rcar_start(hcd);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	return ret;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/*
20462306a36Sopenharmony_ci * On R-Car Gen2 and Gen3, the AC64 bit (bit 0) of HCCPARAMS1 is set
20562306a36Sopenharmony_ci * to 1. However, these SoCs don't support 64-bit address memory
20662306a36Sopenharmony_ci * pointers. So, this driver clears the AC64 bit of xhci->hcc_params
20762306a36Sopenharmony_ci * to call dma_set_coherent_mask(dev, DMA_BIT_MASK(32)) in
20862306a36Sopenharmony_ci * xhci_gen_setup() by using the XHCI_NO_64BIT_SUPPORT quirk.
20962306a36Sopenharmony_ci *
21062306a36Sopenharmony_ci * And, since the firmware/internal CPU control the USBSTS.STS_HALT
21162306a36Sopenharmony_ci * and the process speed is down when the roothub port enters U3,
21262306a36Sopenharmony_ci * long delay for the handshake of STS_HALT is neeed in xhci_suspend()
21362306a36Sopenharmony_ci * by using the XHCI_SLOW_SUSPEND quirk.
21462306a36Sopenharmony_ci */
21562306a36Sopenharmony_ci#define SET_XHCI_PLAT_PRIV_FOR_RCAR(firmware)				\
21662306a36Sopenharmony_ci	.firmware_name = firmware,					\
21762306a36Sopenharmony_ci	.quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH |	\
21862306a36Sopenharmony_ci		  XHCI_SLOW_SUSPEND,					\
21962306a36Sopenharmony_ci	.init_quirk = xhci_rcar_init_quirk,				\
22062306a36Sopenharmony_ci	.plat_start = xhci_rcar_start,					\
22162306a36Sopenharmony_ci	.resume_quirk = xhci_rcar_resume_quirk,
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = {
22462306a36Sopenharmony_ci	SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V1)
22562306a36Sopenharmony_ci};
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
22862306a36Sopenharmony_ci	SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V3)
22962306a36Sopenharmony_ci};
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic const struct xhci_plat_priv xhci_plat_renesas_rzv2m = {
23262306a36Sopenharmony_ci	.quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH |
23362306a36Sopenharmony_ci		  XHCI_SLOW_SUSPEND,
23462306a36Sopenharmony_ci	.init_quirk = xhci_rzv2m_init_quirk,
23562306a36Sopenharmony_ci	.plat_start = xhci_rzv2m_start,
23662306a36Sopenharmony_ci};
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic const struct of_device_id usb_xhci_of_match[] = {
23962306a36Sopenharmony_ci	{
24062306a36Sopenharmony_ci		.compatible = "renesas,xhci-r8a7790",
24162306a36Sopenharmony_ci		.data = &xhci_plat_renesas_rcar_gen2,
24262306a36Sopenharmony_ci	}, {
24362306a36Sopenharmony_ci		.compatible = "renesas,xhci-r8a7791",
24462306a36Sopenharmony_ci		.data = &xhci_plat_renesas_rcar_gen2,
24562306a36Sopenharmony_ci	}, {
24662306a36Sopenharmony_ci		.compatible = "renesas,xhci-r8a7793",
24762306a36Sopenharmony_ci		.data = &xhci_plat_renesas_rcar_gen2,
24862306a36Sopenharmony_ci	}, {
24962306a36Sopenharmony_ci		.compatible = "renesas,xhci-r8a7795",
25062306a36Sopenharmony_ci		.data = &xhci_plat_renesas_rcar_gen3,
25162306a36Sopenharmony_ci	}, {
25262306a36Sopenharmony_ci		.compatible = "renesas,xhci-r8a7796",
25362306a36Sopenharmony_ci		.data = &xhci_plat_renesas_rcar_gen3,
25462306a36Sopenharmony_ci	}, {
25562306a36Sopenharmony_ci		.compatible = "renesas,rcar-gen2-xhci",
25662306a36Sopenharmony_ci		.data = &xhci_plat_renesas_rcar_gen2,
25762306a36Sopenharmony_ci	}, {
25862306a36Sopenharmony_ci		.compatible = "renesas,rcar-gen3-xhci",
25962306a36Sopenharmony_ci		.data = &xhci_plat_renesas_rcar_gen3,
26062306a36Sopenharmony_ci	}, {
26162306a36Sopenharmony_ci		.compatible = "renesas,rzv2m-xhci",
26262306a36Sopenharmony_ci		.data = &xhci_plat_renesas_rzv2m,
26362306a36Sopenharmony_ci	},
26462306a36Sopenharmony_ci	{ },
26562306a36Sopenharmony_ci};
26662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, usb_xhci_of_match);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic int xhci_renesas_probe(struct platform_device *pdev)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	const struct xhci_plat_priv *priv_match;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	priv_match = of_device_get_match_data(&pdev->dev);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return xhci_plat_probe(pdev, NULL, priv_match);
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic struct platform_driver usb_xhci_renesas_driver = {
27862306a36Sopenharmony_ci	.probe = xhci_renesas_probe,
27962306a36Sopenharmony_ci	.remove_new = xhci_plat_remove,
28062306a36Sopenharmony_ci	.shutdown = usb_hcd_platform_shutdown,
28162306a36Sopenharmony_ci	.driver = {
28262306a36Sopenharmony_ci		.name = "xhci-renesas-hcd",
28362306a36Sopenharmony_ci		.pm = &xhci_plat_pm_ops,
28462306a36Sopenharmony_ci		.of_match_table = usb_xhci_of_match,
28562306a36Sopenharmony_ci	},
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_cimodule_platform_driver(usb_xhci_renesas_driver);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ciMODULE_DESCRIPTION("xHCI Platform Host Controller Driver for Renesas R-Car and RZ");
29062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
291