summaryrefslogtreecommitdiff
path: root/drivers/usb/host/xhci-fsl.c
blob: 3a16624713b4f16fda67df7fa7f10dc6bb2923f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/*
 * Copyright 2015,2016 Freescale Semiconductor, Inc.
 *
 * FSL USB HOST xHCI Controller
 *
 * Author: Ramneek Mehresh<ramneek.mehresh@freescale.com>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <usb.h>
#include <linux/errno.h>
#include <linux/compat.h>
#include <linux/usb/xhci-fsl.h>
#include <linux/usb/dwc3.h>
#include "xhci.h"
#include <fsl_errata.h>
#include <fsl_usb.h>
#include <dm.h>

/* Declare global data pointer */
DECLARE_GLOBAL_DATA_PTR;

#ifndef CONFIG_DM_USB
static struct fsl_xhci fsl_xhci;
unsigned long ctr_addr[] = FSL_USB_XHCI_ADDR;
#else
struct xhci_fsl_priv {
	struct xhci_ctrl xhci;
	fdt_addr_t hcd_base;
	struct fsl_xhci ctx;
};
#endif

__weak int __board_usb_init(int index, enum usb_init_type init)
{
	return 0;
}

static int erratum_a008751(void)
{
#if defined(CONFIG_TARGET_LS2080AQDS) || defined(CONFIG_TARGET_LS2080ARDB) ||\
					defined(CONFIG_TARGET_LS2080AQDS)
	u32 __iomem *scfg = (u32 __iomem *)SCFG_BASE;
	writel(SCFG_USB3PRM1CR_INIT, scfg + SCFG_USB3PRM1CR / 4);
	return 0;
#endif
	return 1;
}

static void fsl_apply_xhci_errata(void)
{
	int ret;
	if (has_erratum_a008751()) {
		ret = erratum_a008751();
		if (ret != 0)
			puts("Failed to apply erratum a008751\n");
	}
}

static void fsl_xhci_set_beat_burst_length(struct dwc3 *dwc3_reg)
{
	clrsetbits_le32(&dwc3_reg->g_sbuscfg0, USB3_ENABLE_BEAT_BURST_MASK,
			USB3_ENABLE_BEAT_BURST);
	setbits_le32(&dwc3_reg->g_sbuscfg1, USB3_SET_BEAT_BURST_LIMIT);
}

static int fsl_xhci_core_init(struct fsl_xhci *fsl_xhci)
{
	int ret = 0;

	ret = dwc3_core_init(fsl_xhci->dwc3_reg);
	if (ret) {
		debug("%s:failed to initialize core\n", __func__);
		return ret;
	}

	/* We are hard-coding DWC3 core to Host Mode */
	dwc3_set_mode(fsl_xhci->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);

	/* Set GFLADJ_30MHZ as 20h as per XHCI spec default value */
	dwc3_set_fladj(fsl_xhci->dwc3_reg, GFLADJ_30MHZ_DEFAULT);

	/* Change beat burst and outstanding pipelined transfers requests */
	fsl_xhci_set_beat_burst_length(fsl_xhci->dwc3_reg);

	/*
	 * A-010151: The dwc3 phy TSMC 28-nm HPM 0.9/1.8 V does not
	 * reliably support Rx Detect in P3 mode(P3 is the default
	 * setting). Therefore, some USB3.0 devices may not be detected
	 * reliably in Super Speed mode. So, USB controller to configure
	 * USB in P2 mode whenever the Receive Detect feature is required.
	 * whenever the Receive Detect feature is required.
	 */
	if (has_erratum_a010151())
		clrsetbits_le32(&fsl_xhci->dwc3_reg->g_usb3pipectl[0],
				DWC3_GUSB3PIPECTL_DISRXDETP3,
				DWC3_GUSB3PIPECTL_DISRXDETP3);

	return ret;
}

static int fsl_xhci_core_exit(struct fsl_xhci *fsl_xhci)
{
	/*
	 * Currently fsl socs do not support PHY shutdown from
	 * sw. But this support may be added in future socs.
	 */
	return 0;
}

#ifdef CONFIG_DM_USB
static int xhci_fsl_probe(struct udevice *dev)
{
	struct xhci_fsl_priv *priv = dev_get_priv(dev);
	struct xhci_hccr *hccr;
	struct xhci_hcor *hcor;

	int ret = 0;

	/*
	 * Get the base address for XHCI controller from the device node
	 */
	priv->hcd_base = dev_get_addr(dev);
	if (priv->hcd_base == FDT_ADDR_T_NONE) {
		debug("Can't get the XHCI register base address\n");
		return -ENXIO;
	}
	priv->ctx.hcd = (struct xhci_hccr *)priv->hcd_base;
	priv->ctx.dwc3_reg = (struct dwc3 *)((char *)(priv->hcd_base) +
			  DWC3_REG_OFFSET);

	fsl_apply_xhci_errata();

	ret = fsl_xhci_core_init(&priv->ctx);
	if (ret < 0) {
		puts("Failed to initialize xhci\n");
		return ret;
	}

	hccr = (struct xhci_hccr *)(priv->ctx.hcd);
	hcor = (struct xhci_hcor *)((uintptr_t) hccr
				+ HC_LENGTH(xhci_readl(&hccr->cr_capbase)));

	debug("xhci-fsl: init hccr %lx and hcor %lx hc_length %lx\n",
	      (uintptr_t)hccr, (uintptr_t)hcor,
	      (uintptr_t)HC_LENGTH(xhci_readl(&hccr->cr_capbase)));

	return xhci_register(dev, hccr, hcor);
}

static int xhci_fsl_remove(struct udevice *dev)
{
	struct xhci_fsl_priv *priv = dev_get_priv(dev);

	fsl_xhci_core_exit(&priv->ctx);

	return xhci_deregister(dev);
}

static const struct udevice_id xhci_usb_ids[] = {
	{ .compatible = "fsl,layerscape-dwc3", },
	{ }
};

U_BOOT_DRIVER(xhci_fsl) = {
	.name	= "xhci_fsl",
	.id	= UCLASS_USB,
	.of_match = xhci_usb_ids,
	.probe = xhci_fsl_probe,
	.remove = xhci_fsl_remove,
	.ops	= &xhci_usb_ops,
	.platdata_auto_alloc_size = sizeof(struct usb_platdata),
	.priv_auto_alloc_size = sizeof(struct xhci_fsl_priv),
	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
};
#else
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
{
	struct fsl_xhci *ctx = &fsl_xhci;
	int ret = 0;

	ctx->hcd = (struct xhci_hccr *)ctr_addr[index];
	ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET);

	ret = board_usb_init(index, USB_INIT_HOST);
	if (ret != 0) {
		puts("Failed to initialize board for USB\n");
		return ret;
	}

	fsl_apply_xhci_errata();

	ret = fsl_xhci_core_init(ctx);
	if (ret < 0) {
		puts("Failed to initialize xhci\n");
		return ret;
	}

	*hccr = (struct xhci_hccr *)ctx->hcd;
	*hcor = (struct xhci_hcor *)((uintptr_t) *hccr
				+ HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));

	debug("fsl-xhci: init hccr %lx and hcor %lx hc_length %lx\n",
	      (uintptr_t)*hccr, (uintptr_t)*hcor,
	      (uintptr_t)HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));

	return ret;
}

void xhci_hcd_stop(int index)
{
	struct fsl_xhci *ctx = &fsl_xhci;

	fsl_xhci_core_exit(ctx);
}
#endif