1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com>
4 *
5 * Renesas RCar USB HOST xHCI Controller
6 */
7
8#include <common.h>
9#include <clk.h>
10#include <dm.h>
11#include <fdtdec.h>
12#include <log.h>
13#include <malloc.h>
14#include <usb.h>
15#include <wait_bit.h>
16#include <dm/device_compat.h>
17#include <linux/bitops.h>
18
19#include <usb/xhci.h>
20#include "xhci-rcar-r8a779x_usb3_v3.h"
21
22/* Register Offset */
23#define RCAR_USB3_DL_CTRL	0x250	/* FW Download Control & Status */
24#define RCAR_USB3_FW_DATA0	0x258	/* FW Data0 */
25
26/* Register Settings */
27/* FW Download Control & Status */
28#define RCAR_USB3_DL_CTRL_ENABLE	BIT(0)
29#define RCAR_USB3_DL_CTRL_FW_SUCCESS	BIT(4)
30#define RCAR_USB3_DL_CTRL_FW_SET_DATA0	BIT(8)
31
32struct rcar_xhci_plat {
33	fdt_addr_t	hcd_base;
34	struct clk	clk;
35};
36
37/**
38 * Contains pointers to register base addresses
39 * for the usb controller.
40 */
41struct rcar_xhci {
42	struct xhci_ctrl ctrl;	/* Needs to come first in this struct! */
43	struct usb_plat usb_plat;
44	struct xhci_hccr *hcd;
45};
46
47static int xhci_rcar_download_fw(struct rcar_xhci *ctx, const u32 *fw_data,
48				 const size_t fw_array_size)
49{
50	void __iomem *regs = (void __iomem *)ctx->hcd;
51	int i, ret;
52
53	/* Download R-Car USB3.0 firmware */
54	setbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_ENABLE);
55
56	for (i = 0; i < fw_array_size; i++) {
57		writel(fw_data[i], regs + RCAR_USB3_FW_DATA0);
58		setbits_le32(regs + RCAR_USB3_DL_CTRL,
59			     RCAR_USB3_DL_CTRL_FW_SET_DATA0);
60
61		ret = wait_for_bit_le32(regs + RCAR_USB3_DL_CTRL,
62					RCAR_USB3_DL_CTRL_FW_SET_DATA0, false,
63					10, false);
64		if (ret)
65			break;
66	}
67
68	clrbits_le32(regs + RCAR_USB3_DL_CTRL, RCAR_USB3_DL_CTRL_ENABLE);
69
70	ret = wait_for_bit_le32(regs + RCAR_USB3_DL_CTRL,
71				RCAR_USB3_DL_CTRL_FW_SUCCESS, true,
72				10, false);
73
74	return ret;
75}
76
77static int xhci_rcar_probe(struct udevice *dev)
78{
79	struct rcar_xhci_plat *plat = dev_get_plat(dev);
80	struct rcar_xhci *ctx = dev_get_priv(dev);
81	struct xhci_hcor *hcor;
82	int len, ret;
83
84	ret = clk_get_by_index(dev, 0, &plat->clk);
85	if (ret < 0) {
86		dev_err(dev, "Failed to get USB3 clock\n");
87		return ret;
88	}
89
90	ret = clk_enable(&plat->clk);
91	if (ret) {
92		dev_err(dev, "Failed to enable USB3 clock\n");
93		return ret;
94	}
95
96	ctx->hcd = (struct xhci_hccr *)plat->hcd_base;
97	len = HC_LENGTH(xhci_readl(&ctx->hcd->cr_capbase));
98	hcor = (struct xhci_hcor *)((uintptr_t)ctx->hcd + len);
99
100	ret = xhci_rcar_download_fw(ctx, firmware_r8a779x_usb3_v3,
101				    ARRAY_SIZE(firmware_r8a779x_usb3_v3));
102	if (ret) {
103		dev_err(dev, "Failed to download firmware\n");
104		goto err_fw;
105	}
106
107	ret = xhci_register(dev, ctx->hcd, hcor);
108	if (ret) {
109		dev_err(dev, "Failed to register xHCI\n");
110		goto err_fw;
111	}
112
113	return 0;
114
115err_fw:
116	clk_disable(&plat->clk);
117	return ret;
118}
119
120static int xhci_rcar_deregister(struct udevice *dev)
121{
122	int ret;
123	struct rcar_xhci_plat *plat = dev_get_plat(dev);
124
125	ret = xhci_deregister(dev);
126
127	clk_disable(&plat->clk);
128
129	return ret;
130}
131
132static int xhci_rcar_of_to_plat(struct udevice *dev)
133{
134	struct rcar_xhci_plat *plat = dev_get_plat(dev);
135
136	plat->hcd_base = dev_read_addr(dev);
137	if (plat->hcd_base == FDT_ADDR_T_NONE) {
138		debug("Can't get the XHCI register base address\n");
139		return -ENXIO;
140	}
141
142	return 0;
143}
144
145static const struct udevice_id xhci_rcar_ids[] = {
146	{ .compatible = "renesas,rcar-gen3-xhci" },
147	{ .compatible = "renesas,xhci-r8a7795" },
148	{ .compatible = "renesas,xhci-r8a7796" },
149	{ .compatible = "renesas,xhci-r8a77965" },
150	{ }
151};
152
153U_BOOT_DRIVER(usb_xhci) = {
154	.name		= "xhci_rcar",
155	.id		= UCLASS_USB,
156	.probe		= xhci_rcar_probe,
157	.remove		= xhci_rcar_deregister,
158	.ops		= &xhci_usb_ops,
159	.of_match	= xhci_rcar_ids,
160	.of_to_plat = xhci_rcar_of_to_plat,
161	.plat_auto	= sizeof(struct rcar_xhci_plat),
162	.priv_auto	= sizeof(struct rcar_xhci),
163	.flags		= DM_FLAG_ALLOC_PRIV_DMA,
164};
165