xref: /freebsd/sys/arm/nvidia/tegra_xhci.c (revision 289f133bd758b30ed14627a8e9e69a84c41ab989)
1ccf5e68eSMichal Meloun /*-
2ccf5e68eSMichal Meloun  * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3ccf5e68eSMichal Meloun  * All rights reserved.
4ccf5e68eSMichal Meloun  *
5ccf5e68eSMichal Meloun  * Redistribution and use in source and binary forms, with or without
6ccf5e68eSMichal Meloun  * modification, are permitted provided that the following conditions
7ccf5e68eSMichal Meloun  * are met:
8ccf5e68eSMichal Meloun  * 1. Redistributions of source code must retain the above copyright
9ccf5e68eSMichal Meloun  *    notice, this list of conditions and the following disclaimer.
10ccf5e68eSMichal Meloun  * 2. Redistributions in binary form must reproduce the above copyright
11ccf5e68eSMichal Meloun  *    notice, this list of conditions and the following disclaimer in the
12ccf5e68eSMichal Meloun  *    documentation and/or other materials provided with the distribution.
13ccf5e68eSMichal Meloun  *
14ccf5e68eSMichal Meloun  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15ccf5e68eSMichal Meloun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16ccf5e68eSMichal Meloun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17ccf5e68eSMichal Meloun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18ccf5e68eSMichal Meloun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19ccf5e68eSMichal Meloun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20ccf5e68eSMichal Meloun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21ccf5e68eSMichal Meloun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22ccf5e68eSMichal Meloun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23ccf5e68eSMichal Meloun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24ccf5e68eSMichal Meloun  * SUCH DAMAGE.
25ccf5e68eSMichal Meloun  */
26ccf5e68eSMichal Meloun 
27ccf5e68eSMichal Meloun #include <sys/cdefs.h>
28ccf5e68eSMichal Meloun __FBSDID("$FreeBSD$");
29ccf5e68eSMichal Meloun 
30ccf5e68eSMichal Meloun /*
31ccf5e68eSMichal Meloun  * XHCI driver for Tegra SoCs.
32ccf5e68eSMichal Meloun  */
33ccf5e68eSMichal Meloun #include "opt_bus.h"
34ccf5e68eSMichal Meloun #include "opt_platform.h"
35ccf5e68eSMichal Meloun 
36ccf5e68eSMichal Meloun #include <sys/param.h>
37ccf5e68eSMichal Meloun #include <sys/bus.h>
38ccf5e68eSMichal Meloun #include <sys/clock.h>
39ccf5e68eSMichal Meloun #include <sys/condvar.h>
40ccf5e68eSMichal Meloun #include <sys/firmware.h>
41e2e050c8SConrad Meyer #include <sys/kernel.h>
42e2e050c8SConrad Meyer #include <sys/lock.h>
43e2e050c8SConrad Meyer #include <sys/malloc.h>
44e2e050c8SConrad Meyer #include <sys/module.h>
45e2e050c8SConrad Meyer #include <sys/mutex.h>
46ccf5e68eSMichal Meloun #include <sys/rman.h>
47e2e050c8SConrad Meyer #include <sys/systm.h>
48ccf5e68eSMichal Meloun 
49ccf5e68eSMichal Meloun #include <vm/vm.h>
50ccf5e68eSMichal Meloun #include <vm/vm_extern.h>
51ccf5e68eSMichal Meloun #include <vm/vm_kern.h>
52ccf5e68eSMichal Meloun #include <vm/pmap.h>
53ccf5e68eSMichal Meloun 
54ccf5e68eSMichal Meloun #include <machine/bus.h>
55ccf5e68eSMichal Meloun #include <machine/resource.h>
56ccf5e68eSMichal Meloun 
57ccf5e68eSMichal Meloun #include <dev/extres/clk/clk.h>
58ccf5e68eSMichal Meloun #include <dev/extres/hwreset/hwreset.h>
59ccf5e68eSMichal Meloun #include <dev/extres/phy/phy.h>
60ccf5e68eSMichal Meloun #include <dev/extres/regulator/regulator.h>
61ccf5e68eSMichal Meloun #include <dev/ofw/ofw_bus.h>
62ccf5e68eSMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
63ccf5e68eSMichal Meloun #include <dev/usb/usb.h>
64ccf5e68eSMichal Meloun #include <dev/usb/usbdi.h>
65ccf5e68eSMichal Meloun #include <dev/usb/usb_busdma.h>
66ccf5e68eSMichal Meloun #include <dev/usb/usb_process.h>
67ccf5e68eSMichal Meloun #include <dev/usb/usb_controller.h>
68ccf5e68eSMichal Meloun #include <dev/usb/usb_bus.h>
69ccf5e68eSMichal Meloun #include <dev/usb/controller/xhci.h>
70ccf5e68eSMichal Meloun #include <dev/usb/controller/xhcireg.h>
71ccf5e68eSMichal Meloun 
72ccf5e68eSMichal Meloun #include <arm/nvidia/tegra_pmc.h>
73ccf5e68eSMichal Meloun 
74ccf5e68eSMichal Meloun #include "usbdevs.h"
75ccf5e68eSMichal Meloun 
76ccf5e68eSMichal Meloun /* FPCI address space */
77ccf5e68eSMichal Meloun #define	T_XUSB_CFG_0				0x000
78ccf5e68eSMichal Meloun #define	T_XUSB_CFG_1				0x004
79ccf5e68eSMichal Meloun #define	 CFG_1_BUS_MASTER				(1 << 2)
80ccf5e68eSMichal Meloun #define	 CFG_1_MEMORY_SPACE				(1 << 1)
81ccf5e68eSMichal Meloun #define	 CFG_1_IO_SPACE					(1 << 0)
82ccf5e68eSMichal Meloun 
83ccf5e68eSMichal Meloun #define	T_XUSB_CFG_2				0x008
84ccf5e68eSMichal Meloun #define	T_XUSB_CFG_3				0x00C
85ccf5e68eSMichal Meloun #define	T_XUSB_CFG_4				0x010
86ccf5e68eSMichal Meloun #define	 CFG_4_BASE_ADDRESS(x)				(((x) & 0x1FFFF) << 15)
87ccf5e68eSMichal Meloun 
88ccf5e68eSMichal Meloun #define	T_XUSB_CFG_5				0x014
89ccf5e68eSMichal Meloun #define	T_XUSB_CFG_ARU_MAILBOX_CMD		0x0E4
90ccf5e68eSMichal Meloun #define  ARU_MAILBOX_CMD_INT_EN				(1U << 31)
91ccf5e68eSMichal Meloun #define  ARU_MAILBOX_CMD_DEST_XHCI			(1  << 30)
92ccf5e68eSMichal Meloun #define  ARU_MAILBOX_CMD_DEST_SMI			(1  << 29)
93ccf5e68eSMichal Meloun #define  ARU_MAILBOX_CMD_DEST_PME			(1  << 28)
94ccf5e68eSMichal Meloun #define  ARU_MAILBOX_CMD_DEST_FALC			(1  << 27)
95ccf5e68eSMichal Meloun 
96ccf5e68eSMichal Meloun #define	T_XUSB_CFG_ARU_MAILBOX_DATA_IN		0x0E8
97ccf5e68eSMichal Meloun #define	 ARU_MAILBOX_DATA_IN_DATA(x)			(((x) & 0xFFFFFF) <<  0)
98ccf5e68eSMichal Meloun #define	 ARU_MAILBOX_DATA_IN_TYPE(x)			(((x) & 0x0000FF) << 24)
99ccf5e68eSMichal Meloun 
100ccf5e68eSMichal Meloun #define	T_XUSB_CFG_ARU_MAILBOX_DATA_OUT		0x0EC
101ccf5e68eSMichal Meloun #define	 ARU_MAILBOX_DATA_OUT_DATA(x)			(((x) >>  0) & 0xFFFFFF)
102ccf5e68eSMichal Meloun #define	 ARU_MAILBOX_DATA_OUT_TYPE(x)			(((x) >> 24) & 0x0000FF)
103ccf5e68eSMichal Meloun 
104ccf5e68eSMichal Meloun #define	T_XUSB_CFG_ARU_MAILBOX_OWNER		0x0F0
105ccf5e68eSMichal Meloun #define	 ARU_MAILBOX_OWNER_SW				2
106ccf5e68eSMichal Meloun #define	 ARU_MAILBOX_OWNER_FW				1
107ccf5e68eSMichal Meloun #define	 ARU_MAILBOX_OWNER_NONE				0
108ccf5e68eSMichal Meloun 
109ccf5e68eSMichal Meloun #define	XUSB_CFG_ARU_C11_CSBRANGE		0x41C	/* ! UNDOCUMENTED ! */
110ccf5e68eSMichal Meloun #define	 ARU_C11_CSBRANGE_PAGE(x)			((x) >> 9)
111ccf5e68eSMichal Meloun #define	 ARU_C11_CSBRANGE_ADDR(x)			(0x800 + ((x) & 0x1FF))
112ccf5e68eSMichal Meloun #define	XUSB_CFG_ARU_SMI_INTR			0x428	/* ! UNDOCUMENTED ! */
113ccf5e68eSMichal Meloun #define  ARU_SMI_INTR_EN				(1 << 3)
114ccf5e68eSMichal Meloun #define  ARU_SMI_INTR_FW_HANG				(1 << 1)
115ccf5e68eSMichal Meloun #define	XUSB_CFG_ARU_RST			0x42C	/* ! UNDOCUMENTED ! */
116ccf5e68eSMichal Meloun #define	 ARU_RST_RESET					(1 << 0)
117ccf5e68eSMichal Meloun 
118ccf5e68eSMichal Meloun #define	XUSB_HOST_CONFIGURATION			0x180
119ccf5e68eSMichal Meloun #define	 CONFIGURATION_CLKEN_OVERRIDE			(1U<< 31)
120ccf5e68eSMichal Meloun #define	 CONFIGURATION_PW_NO_DEVSEL_ERR_CYA		(1 << 19)
121ccf5e68eSMichal Meloun #define	 CONFIGURATION_INITIATOR_READ_IDLE		(1 << 18)
122ccf5e68eSMichal Meloun #define	 CONFIGURATION_INITIATOR_WRITE_IDLE		(1 << 17)
123ccf5e68eSMichal Meloun #define	 CONFIGURATION_WDATA_LEAD_CYA			(1 << 15)
124ccf5e68eSMichal Meloun #define	 CONFIGURATION_WR_INTRLV_CYA			(1 << 14)
125ccf5e68eSMichal Meloun #define	 CONFIGURATION_TARGET_READ_IDLE			(1 << 11)
126ccf5e68eSMichal Meloun #define	 CONFIGURATION_TARGET_WRITE_IDLE		(1 << 10)
127ccf5e68eSMichal Meloun #define	 CONFIGURATION_MSI_VEC_EMPTY			(1 <<  9)
128ccf5e68eSMichal Meloun #define	 CONFIGURATION_UFPCI_MSIAW			(1 <<  7)
129ccf5e68eSMichal Meloun #define	 CONFIGURATION_UFPCI_PWPASSPW			(1 <<  6)
130ccf5e68eSMichal Meloun #define	 CONFIGURATION_UFPCI_PASSPW			(1 <<  5)
131ccf5e68eSMichal Meloun #define	 CONFIGURATION_UFPCI_PWPASSNPW			(1 <<  4)
132ccf5e68eSMichal Meloun #define	 CONFIGURATION_DFPCI_PWPASSNPW			(1 <<  3)
133ccf5e68eSMichal Meloun #define	 CONFIGURATION_DFPCI_RSPPASSPW			(1 <<  2)
134ccf5e68eSMichal Meloun #define	 CONFIGURATION_DFPCI_PASSPW			(1 <<  1)
135ccf5e68eSMichal Meloun #define	 CONFIGURATION_EN_FPCI				(1 <<  0)
136ccf5e68eSMichal Meloun 
137ccf5e68eSMichal Meloun /* IPFS address space */
138ccf5e68eSMichal Meloun #define	XUSB_HOST_FPCI_ERROR_MASKS		0x184
139ccf5e68eSMichal Meloun #define	 FPCI_ERROR_MASTER_ABORT			(1 <<  2)
140ccf5e68eSMichal Meloun #define	 FPCI_ERRORI_DATA_ERROR				(1 <<  1)
141ccf5e68eSMichal Meloun #define	 FPCI_ERROR_TARGET_ABORT			(1 <<  0)
142ccf5e68eSMichal Meloun 
143ccf5e68eSMichal Meloun #define	XUSB_HOST_INTR_MASK			0x188
144ccf5e68eSMichal Meloun #define	 INTR_IP_INT_MASK				(1 << 16)
145ccf5e68eSMichal Meloun #define	 INTR_MSI_MASK					(1 <<  8)
146ccf5e68eSMichal Meloun #define	 INTR_INT_MASK					(1 <<  0)
147ccf5e68eSMichal Meloun 
148ccf5e68eSMichal Meloun #define	XUSB_HOST_CLKGATE_HYSTERESIS		0x1BC
149ccf5e68eSMichal Meloun 
150ccf5e68eSMichal Meloun  /* CSB Falcon CPU */
151ccf5e68eSMichal Meloun #define	XUSB_FALCON_CPUCTL			0x100
152ccf5e68eSMichal Meloun #define	 CPUCTL_STOPPED					(1 << 5)
153ccf5e68eSMichal Meloun #define	 CPUCTL_HALTED					(1 << 4)
154ccf5e68eSMichal Meloun #define	 CPUCTL_HRESET					(1 << 3)
155ccf5e68eSMichal Meloun #define	 CPUCTL_SRESET					(1 << 2)
156ccf5e68eSMichal Meloun #define	 CPUCTL_STARTCPU				(1 << 1)
157ccf5e68eSMichal Meloun #define	 CPUCTL_IINVAL					(1 << 0)
158ccf5e68eSMichal Meloun 
159ccf5e68eSMichal Meloun #define	XUSB_FALCON_BOOTVEC			0x104
160ccf5e68eSMichal Meloun #define	XUSB_FALCON_DMACTL			0x10C
161ccf5e68eSMichal Meloun #define	XUSB_FALCON_IMFILLRNG1			0x154
162ccf5e68eSMichal Meloun #define	 IMFILLRNG1_TAG_HI(x)				(((x) & 0xFFF) << 16)
163ccf5e68eSMichal Meloun #define	 IMFILLRNG1_TAG_LO(x)				(((x) & 0xFFF) <<  0)
164ccf5e68eSMichal Meloun #define	XUSB_FALCON_IMFILLCTL			0x158
165ccf5e68eSMichal Meloun 
166ccf5e68eSMichal Meloun /* CSB mempool */
167ccf5e68eSMichal Meloun #define	XUSB_CSB_MEMPOOL_APMAP			0x10181C
168ccf5e68eSMichal Meloun #define	 APMAP_BOOTPATH					(1U << 31)
169ccf5e68eSMichal Meloun 
170ccf5e68eSMichal Meloun #define	XUSB_CSB_MEMPOOL_ILOAD_ATTR		0x101A00
171ccf5e68eSMichal Meloun #define	XUSB_CSB_MEMPOOL_ILOAD_BASE_LO		0x101A04
172ccf5e68eSMichal Meloun #define	XUSB_CSB_MEMPOOL_ILOAD_BASE_HI		0x101A08
173ccf5e68eSMichal Meloun #define	XUSB_CSB_MEMPOOL_L2IMEMOP_SIZE		0x101A10
174ccf5e68eSMichal Meloun #define	 L2IMEMOP_SIZE_OFFSET(x)			(((x) & 0x3FF) <<  8)
175ccf5e68eSMichal Meloun #define	 L2IMEMOP_SIZE_SIZE(x)				(((x) & 0x0FF) << 24)
176ccf5e68eSMichal Meloun 
177ccf5e68eSMichal Meloun #define	XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG		0x101A14
178ccf5e68eSMichal Meloun #define	 L2IMEMOP_INVALIDATE_ALL			(0x40 << 24)
179ccf5e68eSMichal Meloun #define	 L2IMEMOP_LOAD_LOCKED_RESULT			(0x11 << 24)
180ccf5e68eSMichal Meloun 
181ccf5e68eSMichal Meloun #define	XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT        0x101A18
182ccf5e68eSMichal Meloun #define	 L2IMEMOP_RESULT_VLD       (1U << 31)
183ccf5e68eSMichal Meloun 
184ccf5e68eSMichal Meloun #define XUSB_CSB_IMEM_BLOCK_SIZE	256
185ccf5e68eSMichal Meloun 
186ccf5e68eSMichal Meloun #define	TEGRA_XHCI_SS_HIGH_SPEED	120000000
187ccf5e68eSMichal Meloun #define	TEGRA_XHCI_SS_LOW_SPEED		 12000000
188ccf5e68eSMichal Meloun 
189ccf5e68eSMichal Meloun /* MBOX commands. */
190ccf5e68eSMichal Meloun #define	MBOX_CMD_MSG_ENABLED			 1
191ccf5e68eSMichal Meloun #define	MBOX_CMD_INC_FALC_CLOCK			 2
192ccf5e68eSMichal Meloun #define	MBOX_CMD_DEC_FALC_CLOCK			 3
193ccf5e68eSMichal Meloun #define	MBOX_CMD_INC_SSPI_CLOCK			 4
194ccf5e68eSMichal Meloun #define	MBOX_CMD_DEC_SSPI_CLOCK			 5
195ccf5e68eSMichal Meloun #define	MBOX_CMD_SET_BW				 6
196ccf5e68eSMichal Meloun #define	MBOX_CMD_SET_SS_PWR_GATING		 7
197ccf5e68eSMichal Meloun #define	MBOX_CMD_SET_SS_PWR_UNGATING		 8
198ccf5e68eSMichal Meloun #define	MBOX_CMD_SAVE_DFE_CTLE_CTX		 9
199ccf5e68eSMichal Meloun #define	MBOX_CMD_AIRPLANE_MODE_ENABLED		10
200ccf5e68eSMichal Meloun #define	MBOX_CMD_AIRPLANE_MODE_DISABLED		11
201ccf5e68eSMichal Meloun #define	MBOX_CMD_START_HSIC_IDLE		12
202ccf5e68eSMichal Meloun #define	MBOX_CMD_STOP_HSIC_IDLE			13
203ccf5e68eSMichal Meloun #define	MBOX_CMD_DBC_WAKE_STACK			14
204ccf5e68eSMichal Meloun #define	MBOX_CMD_HSIC_PRETEND_CONNECT		15
205ccf5e68eSMichal Meloun #define	MBOX_CMD_RESET_SSPI			16
206ccf5e68eSMichal Meloun #define	MBOX_CMD_DISABLE_SS_LFPS_DETECTION	17
207ccf5e68eSMichal Meloun #define	MBOX_CMD_ENABLE_SS_LFPS_DETECTION	18
208ccf5e68eSMichal Meloun 
209ccf5e68eSMichal Meloun /* MBOX responses. */
210ccf5e68eSMichal Meloun #define	MBOX_CMD_ACK				(0x80 + 0)
211ccf5e68eSMichal Meloun #define	MBOX_CMD_NAK				(0x80 + 1)
212ccf5e68eSMichal Meloun 
213ccf5e68eSMichal Meloun #define	IPFS_WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res_ipfs, (_r), (_v))
214ccf5e68eSMichal Meloun #define	IPFS_RD4(_sc, _r)	bus_read_4((_sc)->mem_res_ipfs, (_r))
215ccf5e68eSMichal Meloun #define	FPCI_WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res_fpci, (_r), (_v))
216ccf5e68eSMichal Meloun #define	FPCI_RD4(_sc, _r)	bus_read_4((_sc)->mem_res_fpci, (_r))
217ccf5e68eSMichal Meloun 
218ccf5e68eSMichal Meloun #define	LOCK(_sc)		mtx_lock(&(_sc)->mtx)
219ccf5e68eSMichal Meloun #define	UNLOCK(_sc)		mtx_unlock(&(_sc)->mtx)
220ccf5e68eSMichal Meloun #define	SLEEP(_sc, timeout)						\
221ccf5e68eSMichal Meloun     mtx_sleep(sc, &sc->mtx, 0, "tegra_xhci", timeout);
222ccf5e68eSMichal Meloun #define	LOCK_INIT(_sc)							\
223ccf5e68eSMichal Meloun     mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_xhci", MTX_DEF)
224ccf5e68eSMichal Meloun #define	LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx)
225ccf5e68eSMichal Meloun #define	ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED)
226ccf5e68eSMichal Meloun #define	ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->mtx, MA_NOTOWNED)
227ccf5e68eSMichal Meloun 
228ccf5e68eSMichal Meloun struct tegra_xusb_fw_hdr {
229ccf5e68eSMichal Meloun 	uint32_t	boot_loadaddr_in_imem;
230ccf5e68eSMichal Meloun 	uint32_t	boot_codedfi_offset;
231ccf5e68eSMichal Meloun 	uint32_t	boot_codetag;
232ccf5e68eSMichal Meloun 	uint32_t	boot_codesize;
233ccf5e68eSMichal Meloun 
234ccf5e68eSMichal Meloun 	uint32_t	phys_memaddr;
235ccf5e68eSMichal Meloun 	uint16_t	reqphys_memsize;
236ccf5e68eSMichal Meloun 	uint16_t	alloc_phys_memsize;
237ccf5e68eSMichal Meloun 
238ccf5e68eSMichal Meloun 	uint32_t	rodata_img_offset;
239ccf5e68eSMichal Meloun 	uint32_t	rodata_section_start;
240ccf5e68eSMichal Meloun 	uint32_t	rodata_section_end;
241ccf5e68eSMichal Meloun 	uint32_t	main_fnaddr;
242ccf5e68eSMichal Meloun 
243ccf5e68eSMichal Meloun 	uint32_t	fwimg_cksum;
244ccf5e68eSMichal Meloun 	uint32_t	fwimg_created_time;
245ccf5e68eSMichal Meloun 
246ccf5e68eSMichal Meloun 	uint32_t	imem_resident_start;
247ccf5e68eSMichal Meloun 	uint32_t	imem_resident_end;
248ccf5e68eSMichal Meloun 	uint32_t	idirect_start;
249ccf5e68eSMichal Meloun 	uint32_t	idirect_end;
250ccf5e68eSMichal Meloun 	uint32_t	l2_imem_start;
251ccf5e68eSMichal Meloun 	uint32_t	l2_imem_end;
252ccf5e68eSMichal Meloun 	uint32_t	version_id;
253ccf5e68eSMichal Meloun 	uint8_t		init_ddirect;
254ccf5e68eSMichal Meloun 	uint8_t		reserved[3];
255ccf5e68eSMichal Meloun 	uint32_t	phys_addr_log_buffer;
256ccf5e68eSMichal Meloun 	uint32_t	total_log_entries;
257ccf5e68eSMichal Meloun 	uint32_t	dequeue_ptr;
258ccf5e68eSMichal Meloun 	uint32_t	dummy[2];
259ccf5e68eSMichal Meloun 	uint32_t	fwimg_len;
260ccf5e68eSMichal Meloun 	uint8_t		magic[8];
261ccf5e68eSMichal Meloun 	uint32_t	ss_low_power_entry_timeout;
262ccf5e68eSMichal Meloun 	uint8_t		num_hsic_port;
263ccf5e68eSMichal Meloun 	uint8_t		ss_portmap;
264ccf5e68eSMichal Meloun 	uint8_t		build;
265ccf5e68eSMichal Meloun 	uint8_t		padding[137]; /* Pad to 256 bytes */
266ccf5e68eSMichal Meloun };
267ccf5e68eSMichal Meloun 
268b9cbd68dSMichal Meloun struct xhci_soc;
269ccf5e68eSMichal Meloun struct tegra_xhci_softc {
270ccf5e68eSMichal Meloun 	struct xhci_softc 	xhci_softc;
271ccf5e68eSMichal Meloun 	device_t		dev;
272b9cbd68dSMichal Meloun 	struct xhci_soc		*soc;
273ccf5e68eSMichal Meloun 	struct mtx		mtx;
274ccf5e68eSMichal Meloun 	struct resource		*mem_res_fpci;
275ccf5e68eSMichal Meloun 	struct resource		*mem_res_ipfs;
276ccf5e68eSMichal Meloun 	struct resource		*irq_res_mbox;
277ccf5e68eSMichal Meloun 	void			*irq_hdl_mbox;
278ccf5e68eSMichal Meloun 
279ccf5e68eSMichal Meloun 	clk_t			clk_xusb_host;
280ccf5e68eSMichal Meloun 	clk_t			clk_xusb_gate;
281ccf5e68eSMichal Meloun 	clk_t			clk_xusb_falcon_src;
282ccf5e68eSMichal Meloun 	clk_t			clk_xusb_ss;
283ccf5e68eSMichal Meloun 	clk_t			clk_xusb_hs_src;
284ccf5e68eSMichal Meloun 	clk_t			clk_xusb_fs_src;
285ccf5e68eSMichal Meloun 	hwreset_t		hwreset_xusb_host;
286ccf5e68eSMichal Meloun 	hwreset_t		hwreset_xusb_ss;
287b9cbd68dSMichal Meloun 	regulator_t		regulators[16];		/* Safe maximum */
288b9cbd68dSMichal Meloun 	phy_t 			phys[8];		/* Safe maximum */
289ccf5e68eSMichal Meloun 
290ccf5e68eSMichal Meloun 	struct intr_config_hook	irq_hook;
291ccf5e68eSMichal Meloun 	bool			xhci_inited;
292ccf5e68eSMichal Meloun 	vm_offset_t		fw_vaddr;
293ccf5e68eSMichal Meloun 	vm_size_t		fw_size;
294ccf5e68eSMichal Meloun };
295ccf5e68eSMichal Meloun 
296b9cbd68dSMichal Meloun struct xhci_soc {
297b9cbd68dSMichal Meloun 	char		*fw_name;
298b9cbd68dSMichal Meloun 	char 		**regulator_names;
299b9cbd68dSMichal Meloun 	char 		**phy_names;
300b9cbd68dSMichal Meloun };
301b9cbd68dSMichal Meloun 
302b9cbd68dSMichal Meloun /* Tegra 124 config */
303b9cbd68dSMichal Meloun static char *tegra124_reg_names[] = {
304b9cbd68dSMichal Meloun 	"avddio-pex-supply",
305b9cbd68dSMichal Meloun 	"dvddio-pex-supply",
306b9cbd68dSMichal Meloun 	"avdd-usb-supply",
307b9cbd68dSMichal Meloun 	"avdd-pll-utmip-supply",
308b9cbd68dSMichal Meloun 	"avdd-pll-erefe-supply",
309b9cbd68dSMichal Meloun 	"avdd-usb-ss-pll-supply",
310b9cbd68dSMichal Meloun 	"hvdd-usb-ss-supply",
311b9cbd68dSMichal Meloun 	"hvdd-usb-ss-pll-e-supply",
312b9cbd68dSMichal Meloun 	NULL
313b9cbd68dSMichal Meloun };
314b9cbd68dSMichal Meloun 
315b9cbd68dSMichal Meloun static char *tegra124_phy_names[] = {
316b9cbd68dSMichal Meloun 	"usb2-0",
317b9cbd68dSMichal Meloun 	"usb2-1",
318b9cbd68dSMichal Meloun 	"usb2-2",
319b9cbd68dSMichal Meloun 	"usb3-0",
320b9cbd68dSMichal Meloun 	NULL
321b9cbd68dSMichal Meloun };
322b9cbd68dSMichal Meloun 
323b9cbd68dSMichal Meloun static struct xhci_soc tegra124_soc =
324b9cbd68dSMichal Meloun {
325b9cbd68dSMichal Meloun 	.fw_name = "tegra124_xusb_fw",
326b9cbd68dSMichal Meloun 	.regulator_names = tegra124_reg_names,
327b9cbd68dSMichal Meloun 	.phy_names = tegra124_phy_names,
328b9cbd68dSMichal Meloun };
329b9cbd68dSMichal Meloun 
330b9cbd68dSMichal Meloun /* Tegra 210 config */
331b9cbd68dSMichal Meloun static char *tegra210_reg_names[] = {
332b9cbd68dSMichal Meloun 	"dvddio-pex-supply",
333b9cbd68dSMichal Meloun 	"hvddio-pex-supply",
334b9cbd68dSMichal Meloun 	"avdd-usb-supply",
335b9cbd68dSMichal Meloun 	"avdd-pll-utmip-supply",
336b9cbd68dSMichal Meloun 	"avdd-pll-uerefe-supply",
337b9cbd68dSMichal Meloun 	"dvdd-usb-ss-pll-supply",
338b9cbd68dSMichal Meloun 	"hvdd-usb-ss-pll-e-supply",
339b9cbd68dSMichal Meloun 	NULL
340b9cbd68dSMichal Meloun };
341b9cbd68dSMichal Meloun 
342b9cbd68dSMichal Meloun static char *tegra210_phy_names[] = {
343b9cbd68dSMichal Meloun 	"usb2-0",
344b9cbd68dSMichal Meloun 	"usb2-1",
345b9cbd68dSMichal Meloun 	"usb2-2",
346b9cbd68dSMichal Meloun 	"usb2-3",
347b9cbd68dSMichal Meloun 	"usb3-0",
348b9cbd68dSMichal Meloun 	"usb3-1",
349b9cbd68dSMichal Meloun 	NULL
350b9cbd68dSMichal Meloun };
351b9cbd68dSMichal Meloun 
352b9cbd68dSMichal Meloun static struct xhci_soc tegra210_soc =
353b9cbd68dSMichal Meloun {
354b9cbd68dSMichal Meloun 	.fw_name = "tegra210_xusb_fw",
355b9cbd68dSMichal Meloun 	.regulator_names = tegra210_reg_names,
356b9cbd68dSMichal Meloun 	.phy_names = tegra210_phy_names,
357b9cbd68dSMichal Meloun };
358b9cbd68dSMichal Meloun 
359b9cbd68dSMichal Meloun /* Compatible devices. */
360b9cbd68dSMichal Meloun static struct ofw_compat_data compat_data[] = {
361b9cbd68dSMichal Meloun 	{"nvidia,tegra124-xusb", (uintptr_t)&tegra124_soc},
362b9cbd68dSMichal Meloun 	{"nvidia,tegra210-xusb", (uintptr_t)&tegra210_soc},
363b9cbd68dSMichal Meloun 	{NULL,		 	 0}
364b9cbd68dSMichal Meloun };
365b9cbd68dSMichal Meloun 
366b9cbd68dSMichal Meloun 
367ccf5e68eSMichal Meloun static uint32_t
368ccf5e68eSMichal Meloun CSB_RD4(struct tegra_xhci_softc *sc, uint32_t addr)
369ccf5e68eSMichal Meloun {
370ccf5e68eSMichal Meloun 
371ccf5e68eSMichal Meloun 	FPCI_WR4(sc, XUSB_CFG_ARU_C11_CSBRANGE, ARU_C11_CSBRANGE_PAGE(addr));
372ccf5e68eSMichal Meloun 	return (FPCI_RD4(sc, ARU_C11_CSBRANGE_ADDR(addr)));
373ccf5e68eSMichal Meloun }
374ccf5e68eSMichal Meloun 
375ccf5e68eSMichal Meloun static void
376ccf5e68eSMichal Meloun CSB_WR4(struct tegra_xhci_softc *sc, uint32_t addr, uint32_t val)
377ccf5e68eSMichal Meloun {
378ccf5e68eSMichal Meloun 
379ccf5e68eSMichal Meloun 	FPCI_WR4(sc, XUSB_CFG_ARU_C11_CSBRANGE, ARU_C11_CSBRANGE_PAGE(addr));
380ccf5e68eSMichal Meloun 	FPCI_WR4(sc, ARU_C11_CSBRANGE_ADDR(addr), val);
381ccf5e68eSMichal Meloun }
382ccf5e68eSMichal Meloun 
383ccf5e68eSMichal Meloun static int
384ccf5e68eSMichal Meloun get_fdt_resources(struct tegra_xhci_softc *sc, phandle_t node)
385ccf5e68eSMichal Meloun {
386b9cbd68dSMichal Meloun 	int i, rv;
387ccf5e68eSMichal Meloun 
388b9cbd68dSMichal Meloun 	/* Regulators. */
389b9cbd68dSMichal Meloun 	for (i = 0; sc->soc->regulator_names[i] != NULL; i++) {
390b9cbd68dSMichal Meloun 		if (i >= nitems(sc->regulators)) {
391ccf5e68eSMichal Meloun 			device_printf(sc->dev,
392b9cbd68dSMichal Meloun 			    "Too many regulators present in DT.\n");
393b9cbd68dSMichal Meloun 			return (EOVERFLOW);
394ccf5e68eSMichal Meloun 		}
395ccf5e68eSMichal Meloun 		rv = regulator_get_by_ofw_property(sc->dev, 0,
396b9cbd68dSMichal Meloun 		    sc->soc->regulator_names[i], sc->regulators + i);
397ccf5e68eSMichal Meloun 		if (rv != 0) {
398ccf5e68eSMichal Meloun 			device_printf(sc->dev,
399b9cbd68dSMichal Meloun 			    "Cannot get '%s' regulator\n",
400b9cbd68dSMichal Meloun 			    sc->soc->regulator_names[i]);
401ccf5e68eSMichal Meloun 			return (ENXIO);
402ccf5e68eSMichal Meloun 		}
403b9cbd68dSMichal Meloun 	}
404ccf5e68eSMichal Meloun 
405ccf5e68eSMichal Meloun 	rv = hwreset_get_by_ofw_name(sc->dev, 0, "xusb_host",
406ccf5e68eSMichal Meloun 	    &sc->hwreset_xusb_host);
407ccf5e68eSMichal Meloun 	if (rv != 0) {
408ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot get 'xusb_host' reset\n");
409ccf5e68eSMichal Meloun 		return (ENXIO);
410ccf5e68eSMichal Meloun 	}
411ccf5e68eSMichal Meloun 	rv = hwreset_get_by_ofw_name(sc->dev, 0, "xusb_ss",
412ccf5e68eSMichal Meloun 	    &sc->hwreset_xusb_ss);
413ccf5e68eSMichal Meloun 	if (rv != 0) {
414ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot get 'xusb_ss' reset\n");
415ccf5e68eSMichal Meloun 		return (ENXIO);
416ccf5e68eSMichal Meloun 	}
417ccf5e68eSMichal Meloun 
418b9cbd68dSMichal Meloun 	/* Phys. */
419b9cbd68dSMichal Meloun 	for (i = 0; sc->soc->phy_names[i] != NULL; i++) {
420b9cbd68dSMichal Meloun 		if (i >= nitems(sc->phys)) {
421b9cbd68dSMichal Meloun 			device_printf(sc->dev,
422b9cbd68dSMichal Meloun 			    "Too many phys present in DT.\n");
423b9cbd68dSMichal Meloun 			return (EOVERFLOW);
424b9cbd68dSMichal Meloun 		}
425b9cbd68dSMichal Meloun 		rv = phy_get_by_ofw_name(sc->dev, 0, sc->soc->phy_names[i],
426b9cbd68dSMichal Meloun 		    sc->phys + i);
427b9cbd68dSMichal Meloun 		if (rv != 0 && rv != ENOENT) {
428b9cbd68dSMichal Meloun 			device_printf(sc->dev, "Cannot get '%s' phy.\n",
429b9cbd68dSMichal Meloun 			    sc->soc->phy_names[i]);
430ccf5e68eSMichal Meloun 			return (ENXIO);
431ccf5e68eSMichal Meloun 		}
432ccf5e68eSMichal Meloun 	}
433ccf5e68eSMichal Meloun 
434ccf5e68eSMichal Meloun 	rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_host",
435ccf5e68eSMichal Meloun 	    &sc->clk_xusb_host);
436ccf5e68eSMichal Meloun 	if (rv != 0) {
437ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot get 'xusb_host' clock\n");
438ccf5e68eSMichal Meloun 		return (ENXIO);
439ccf5e68eSMichal Meloun 	}
440ccf5e68eSMichal Meloun 	rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_falcon_src",
441ccf5e68eSMichal Meloun 	    &sc->clk_xusb_falcon_src);
442ccf5e68eSMichal Meloun 	if (rv != 0) {
443ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot get 'xusb_falcon_src' clock\n");
444ccf5e68eSMichal Meloun 		return (ENXIO);
445ccf5e68eSMichal Meloun 	}
446ccf5e68eSMichal Meloun 	rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_ss",
447ccf5e68eSMichal Meloun 	    &sc->clk_xusb_ss);
448ccf5e68eSMichal Meloun 	if (rv != 0) {
449ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot get 'xusb_ss' clock\n");
450ccf5e68eSMichal Meloun 		return (ENXIO);
451ccf5e68eSMichal Meloun 	}
452ccf5e68eSMichal Meloun 	rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_hs_src",
453ccf5e68eSMichal Meloun 	    &sc->clk_xusb_hs_src);
454ccf5e68eSMichal Meloun 	if (rv != 0) {
455ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot get 'xusb_hs_src' clock\n");
456ccf5e68eSMichal Meloun 		return (ENXIO);
457ccf5e68eSMichal Meloun 	}
458ccf5e68eSMichal Meloun 	rv = clk_get_by_ofw_name(sc->dev, 0, "xusb_fs_src",
459ccf5e68eSMichal Meloun 	    &sc->clk_xusb_fs_src);
460ccf5e68eSMichal Meloun 	if (rv != 0) {
461ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot get 'xusb_fs_src' clock\n");
462ccf5e68eSMichal Meloun 		return (ENXIO);
463ccf5e68eSMichal Meloun 	}
464b9cbd68dSMichal Meloun 	/* Clock xusb_gate is missing in mainstream DT */
465b9cbd68dSMichal Meloun 	rv = clk_get_by_name(sc->dev, "xusb_gate", &sc->clk_xusb_gate);
466ccf5e68eSMichal Meloun 	if (rv != 0) {
467ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot get 'xusb_gate' clock\n");
468ccf5e68eSMichal Meloun 		return (ENXIO);
469ccf5e68eSMichal Meloun 	}
470ccf5e68eSMichal Meloun 	return (0);
471ccf5e68eSMichal Meloun }
472ccf5e68eSMichal Meloun 
473ccf5e68eSMichal Meloun static int
474ccf5e68eSMichal Meloun enable_fdt_resources(struct tegra_xhci_softc *sc)
475ccf5e68eSMichal Meloun {
476b9cbd68dSMichal Meloun 	int i, rv;
477ccf5e68eSMichal Meloun 
478ccf5e68eSMichal Meloun 	rv = hwreset_assert(sc->hwreset_xusb_host);
479ccf5e68eSMichal Meloun 	if (rv != 0) {
480ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot reset 'xusb_host' reset\n");
481ccf5e68eSMichal Meloun 		return (rv);
482ccf5e68eSMichal Meloun 	}
483ccf5e68eSMichal Meloun 	rv = hwreset_assert(sc->hwreset_xusb_ss);
484ccf5e68eSMichal Meloun 	if (rv != 0) {
485ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot reset 'xusb_ss' reset\n");
486ccf5e68eSMichal Meloun 		return (rv);
487ccf5e68eSMichal Meloun 	}
488ccf5e68eSMichal Meloun 
489b9cbd68dSMichal Meloun 	/* Regulators. */
490b9cbd68dSMichal Meloun 	for (i = 0; i < nitems(sc->regulators); i++) {
491b9cbd68dSMichal Meloun 		if (sc->regulators[i] == NULL)
492b9cbd68dSMichal Meloun 			continue;
493b9cbd68dSMichal Meloun 		rv = regulator_enable(sc->regulators[i]);
494ccf5e68eSMichal Meloun 		if (rv != 0) {
495ccf5e68eSMichal Meloun 			device_printf(sc->dev,
496b9cbd68dSMichal Meloun 			    "Cannot enable '%s' regulator\n",
497b9cbd68dSMichal Meloun 			    sc->soc->regulator_names[i]);
498ccf5e68eSMichal Meloun 			return (rv);
499ccf5e68eSMichal Meloun 		}
500ccf5e68eSMichal Meloun 	}
501ccf5e68eSMichal Meloun 
502ccf5e68eSMichal Meloun 	/* Power off XUSB host and XUSB SS domains. */
503ccf5e68eSMichal Meloun 	rv = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
504ccf5e68eSMichal Meloun 	if (rv != 0) {
505ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot powerdown  'xusba' domain\n");
506ccf5e68eSMichal Meloun 		return (rv);
507ccf5e68eSMichal Meloun 	}
508ccf5e68eSMichal Meloun 	rv = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
509ccf5e68eSMichal Meloun 	if (rv != 0) {
510ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot powerdown  'xusbc' domain\n");
511ccf5e68eSMichal Meloun 		return (rv);
512ccf5e68eSMichal Meloun 	}
513ccf5e68eSMichal Meloun 
514ccf5e68eSMichal Meloun 	/* Setup XUSB ss_src clock first */
515ccf5e68eSMichal Meloun 	clk_set_freq(sc->clk_xusb_ss, TEGRA_XHCI_SS_HIGH_SPEED, 0);
516ccf5e68eSMichal Meloun 	if (rv != 0)
517ccf5e68eSMichal Meloun 		return (rv);
518ccf5e68eSMichal Meloun 
519ccf5e68eSMichal Meloun 	/* The XUSB gate clock must be enabled before XUSBA can be powered. */
520ccf5e68eSMichal Meloun 	rv = clk_enable(sc->clk_xusb_gate);
521ccf5e68eSMichal Meloun 	if (rv != 0) {
522ccf5e68eSMichal Meloun 		device_printf(sc->dev,
523ccf5e68eSMichal Meloun 		    "Cannot enable 'xusb_gate' clock\n");
524ccf5e68eSMichal Meloun 		return (rv);
525ccf5e68eSMichal Meloun 	}
526ccf5e68eSMichal Meloun 
527ccf5e68eSMichal Meloun 	/* Power on XUSB host and XUSB SS domains. */
528ccf5e68eSMichal Meloun 	rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC,
529ccf5e68eSMichal Meloun 	    sc->clk_xusb_host, sc->hwreset_xusb_host);
530ccf5e68eSMichal Meloun 	if (rv != 0) {
531ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot powerup 'xusbc' domain\n");
532ccf5e68eSMichal Meloun 		return (rv);
533ccf5e68eSMichal Meloun 	}
534ccf5e68eSMichal Meloun 	rv = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA,
535ccf5e68eSMichal Meloun 	    sc->clk_xusb_ss, sc->hwreset_xusb_ss);
536ccf5e68eSMichal Meloun 	if (rv != 0) {
537ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot powerup 'xusba' domain\n");
538ccf5e68eSMichal Meloun 		return (rv);
539ccf5e68eSMichal Meloun 	}
540ccf5e68eSMichal Meloun 
541ccf5e68eSMichal Meloun 	/* Enable rest of clocks */
542ccf5e68eSMichal Meloun 	rv = clk_enable(sc->clk_xusb_falcon_src);
543ccf5e68eSMichal Meloun 	if (rv != 0) {
544ccf5e68eSMichal Meloun 		device_printf(sc->dev,
545ccf5e68eSMichal Meloun 		    "Cannot enable 'xusb_falcon_src' clock\n");
546ccf5e68eSMichal Meloun 		return (rv);
547ccf5e68eSMichal Meloun 	}
548ccf5e68eSMichal Meloun 	rv = clk_enable(sc->clk_xusb_fs_src);
549ccf5e68eSMichal Meloun 	if (rv != 0) {
550ccf5e68eSMichal Meloun 		device_printf(sc->dev,
551ccf5e68eSMichal Meloun 		    "Cannot enable 'xusb_fs_src' clock\n");
552ccf5e68eSMichal Meloun 		return (rv);
553ccf5e68eSMichal Meloun 	}
554ccf5e68eSMichal Meloun 	rv = clk_enable(sc->clk_xusb_hs_src);
555ccf5e68eSMichal Meloun 	if (rv != 0) {
556ccf5e68eSMichal Meloun 		device_printf(sc->dev,
557ccf5e68eSMichal Meloun 		    "Cannot enable 'xusb_hs_src' clock\n");
558ccf5e68eSMichal Meloun 		return (rv);
559ccf5e68eSMichal Meloun 	}
560ccf5e68eSMichal Meloun 
561b9cbd68dSMichal Meloun 	/* Phys. */
562b9cbd68dSMichal Meloun 	for (i = 0; i < nitems(sc->phys); i++) {
563b9cbd68dSMichal Meloun 		if (sc->phys[i] == NULL)
564b9cbd68dSMichal Meloun 			continue;
565b9cbd68dSMichal Meloun 		rv = phy_enable(sc->phys[i]);
566ccf5e68eSMichal Meloun 		if (rv != 0) {
567b9cbd68dSMichal Meloun 			device_printf(sc->dev, "Cannot enable '%s' phy\n",
568b9cbd68dSMichal Meloun 			    sc->soc->phy_names[i]);
569ccf5e68eSMichal Meloun 			return (rv);
570ccf5e68eSMichal Meloun 		}
571ccf5e68eSMichal Meloun 	}
572ccf5e68eSMichal Meloun 
573ccf5e68eSMichal Meloun 	return (0);
574ccf5e68eSMichal Meloun }
575ccf5e68eSMichal Meloun 
576ccf5e68eSMichal Meloun /* Respond by ACK/NAK back to FW */
577ccf5e68eSMichal Meloun static void
578ccf5e68eSMichal Meloun mbox_send_ack(struct tegra_xhci_softc *sc, uint32_t cmd, uint32_t data)
579ccf5e68eSMichal Meloun {
580ccf5e68eSMichal Meloun 	uint32_t reg;
581ccf5e68eSMichal Meloun 
582ccf5e68eSMichal Meloun 	reg = ARU_MAILBOX_DATA_IN_TYPE(cmd) | ARU_MAILBOX_DATA_IN_DATA(data);
583ccf5e68eSMichal Meloun 	FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_IN, reg);
584ccf5e68eSMichal Meloun 
585ccf5e68eSMichal Meloun 	reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD);
586ccf5e68eSMichal Meloun 	reg |= ARU_MAILBOX_CMD_DEST_FALC | ARU_MAILBOX_CMD_INT_EN;
587ccf5e68eSMichal Meloun 	FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg);
588ccf5e68eSMichal Meloun }
589ccf5e68eSMichal Meloun 
590ccf5e68eSMichal Meloun /* Sent command to FW */
591ccf5e68eSMichal Meloun static int
592ccf5e68eSMichal Meloun mbox_send_cmd(struct tegra_xhci_softc *sc, uint32_t cmd, uint32_t data)
593ccf5e68eSMichal Meloun {
594ccf5e68eSMichal Meloun 	uint32_t reg;
595ccf5e68eSMichal Meloun 	int i;
596ccf5e68eSMichal Meloun 
597ccf5e68eSMichal Meloun 	reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER);
598ccf5e68eSMichal Meloun 	if (reg != ARU_MAILBOX_OWNER_NONE) {
599ccf5e68eSMichal Meloun 		device_printf(sc->dev,
600ccf5e68eSMichal Meloun 		    "CPU mailbox is busy: 0x%08X\n", reg);
601ccf5e68eSMichal Meloun 		return (EBUSY);
602ccf5e68eSMichal Meloun 	}
603ccf5e68eSMichal Meloun 	/* XXX Is this right? Retry loop? Wait before send? */
604ccf5e68eSMichal Meloun 	FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER, ARU_MAILBOX_OWNER_SW);
605ccf5e68eSMichal Meloun 	reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER);
606ccf5e68eSMichal Meloun 	if (reg != ARU_MAILBOX_OWNER_SW) {
607ccf5e68eSMichal Meloun 		device_printf(sc->dev,
608ccf5e68eSMichal Meloun 		    "Cannot acquire CPU mailbox: 0x%08X\n", reg);
609ccf5e68eSMichal Meloun 		return (EBUSY);
610ccf5e68eSMichal Meloun 	}
611ccf5e68eSMichal Meloun 	reg = ARU_MAILBOX_DATA_IN_TYPE(cmd) | ARU_MAILBOX_DATA_IN_DATA(data);
612ccf5e68eSMichal Meloun 	FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_IN, reg);
613ccf5e68eSMichal Meloun 
614ccf5e68eSMichal Meloun 	reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD);
615ccf5e68eSMichal Meloun 	reg |= ARU_MAILBOX_CMD_DEST_FALC | ARU_MAILBOX_CMD_INT_EN;
616ccf5e68eSMichal Meloun 	FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg);
617ccf5e68eSMichal Meloun 
618ccf5e68eSMichal Meloun 	for (i = 250; i > 0; i--) {
619ccf5e68eSMichal Meloun 		reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER);
620ccf5e68eSMichal Meloun 		if (reg == ARU_MAILBOX_OWNER_NONE)
621ccf5e68eSMichal Meloun 			break;
622ccf5e68eSMichal Meloun 		DELAY(100);
623ccf5e68eSMichal Meloun 	}
624ccf5e68eSMichal Meloun 	if (i <= 0) {
625ccf5e68eSMichal Meloun 		device_printf(sc->dev,
626ccf5e68eSMichal Meloun 		    "Command response timeout: 0x%08X\n", reg);
627ccf5e68eSMichal Meloun 		return (ETIMEDOUT);
628ccf5e68eSMichal Meloun 	}
629ccf5e68eSMichal Meloun 
630ccf5e68eSMichal Meloun 	return(0);
631ccf5e68eSMichal Meloun }
632ccf5e68eSMichal Meloun 
633ccf5e68eSMichal Meloun static void
634ccf5e68eSMichal Meloun process_msg(struct tegra_xhci_softc *sc, uint32_t req_cmd, uint32_t req_data,
635ccf5e68eSMichal Meloun     uint32_t *resp_cmd, uint32_t *resp_data)
636ccf5e68eSMichal Meloun {
637ccf5e68eSMichal Meloun 	uint64_t freq;
638ccf5e68eSMichal Meloun 	int rv;
639ccf5e68eSMichal Meloun 
640ccf5e68eSMichal Meloun 	/* In most cases, data are echoed back. */
641ccf5e68eSMichal Meloun 	*resp_data = req_data;
642ccf5e68eSMichal Meloun 	switch (req_cmd) {
643ccf5e68eSMichal Meloun 	case MBOX_CMD_INC_FALC_CLOCK:
644ccf5e68eSMichal Meloun 	case MBOX_CMD_DEC_FALC_CLOCK:
645ccf5e68eSMichal Meloun 		rv = clk_set_freq(sc->clk_xusb_falcon_src, req_data * 1000ULL,
646ccf5e68eSMichal Meloun 		    0);
647ccf5e68eSMichal Meloun 		if (rv == 0) {
648ccf5e68eSMichal Meloun 			rv = clk_get_freq(sc->clk_xusb_falcon_src, &freq);
649ccf5e68eSMichal Meloun 			*resp_data = (uint32_t)(freq / 1000);
650ccf5e68eSMichal Meloun 		}
651ccf5e68eSMichal Meloun 		*resp_cmd = rv == 0 ? MBOX_CMD_ACK: MBOX_CMD_NAK;
652ccf5e68eSMichal Meloun 		break;
653ccf5e68eSMichal Meloun 
654ccf5e68eSMichal Meloun 	case MBOX_CMD_INC_SSPI_CLOCK:
655ccf5e68eSMichal Meloun 	case MBOX_CMD_DEC_SSPI_CLOCK:
656ccf5e68eSMichal Meloun 		rv = clk_set_freq(sc->clk_xusb_ss, req_data * 1000ULL,
657ccf5e68eSMichal Meloun 		    0);
658ccf5e68eSMichal Meloun 		if (rv == 0) {
659ccf5e68eSMichal Meloun 			rv = clk_get_freq(sc->clk_xusb_ss, &freq);
660ccf5e68eSMichal Meloun 			*resp_data = (uint32_t)(freq / 1000);
661ccf5e68eSMichal Meloun 		}
662ccf5e68eSMichal Meloun 		*resp_cmd = rv == 0 ? MBOX_CMD_ACK: MBOX_CMD_NAK;
663ccf5e68eSMichal Meloun 		break;
664ccf5e68eSMichal Meloun 
665ccf5e68eSMichal Meloun 	case MBOX_CMD_SET_BW:
666ccf5e68eSMichal Meloun 		/* No respense is expected. */
667ccf5e68eSMichal Meloun 		*resp_cmd = 0;
668ccf5e68eSMichal Meloun 		break;
669ccf5e68eSMichal Meloun 
670ccf5e68eSMichal Meloun 	case MBOX_CMD_SET_SS_PWR_GATING:
671ccf5e68eSMichal Meloun 	case MBOX_CMD_SET_SS_PWR_UNGATING:
672ccf5e68eSMichal Meloun 		*resp_cmd = MBOX_CMD_NAK;
673ccf5e68eSMichal Meloun 		break;
674ccf5e68eSMichal Meloun 
675ccf5e68eSMichal Meloun 	case MBOX_CMD_SAVE_DFE_CTLE_CTX:
676ccf5e68eSMichal Meloun 		/* Not implemented yet. */
677ccf5e68eSMichal Meloun 		*resp_cmd = MBOX_CMD_ACK;
678ccf5e68eSMichal Meloun 		break;
679ccf5e68eSMichal Meloun 
680ccf5e68eSMichal Meloun 	case MBOX_CMD_START_HSIC_IDLE:
681ccf5e68eSMichal Meloun 	case MBOX_CMD_STOP_HSIC_IDLE:
682ccf5e68eSMichal Meloun 		/* Not implemented yet. */
683ccf5e68eSMichal Meloun 		*resp_cmd = MBOX_CMD_NAK;
684ccf5e68eSMichal Meloun 		break;
685ccf5e68eSMichal Meloun 
686ccf5e68eSMichal Meloun 	case MBOX_CMD_DISABLE_SS_LFPS_DETECTION:
687ccf5e68eSMichal Meloun 	case MBOX_CMD_ENABLE_SS_LFPS_DETECTION:
688ccf5e68eSMichal Meloun 		/* Not implemented yet. */
689ccf5e68eSMichal Meloun 		*resp_cmd = MBOX_CMD_NAK;
690ccf5e68eSMichal Meloun 		break;
691ccf5e68eSMichal Meloun 
692ccf5e68eSMichal Meloun 	case MBOX_CMD_AIRPLANE_MODE_ENABLED:
693ccf5e68eSMichal Meloun 	case MBOX_CMD_AIRPLANE_MODE_DISABLED:
694ccf5e68eSMichal Meloun 	case MBOX_CMD_DBC_WAKE_STACK:
695ccf5e68eSMichal Meloun 	case MBOX_CMD_HSIC_PRETEND_CONNECT:
696ccf5e68eSMichal Meloun 	case MBOX_CMD_RESET_SSPI:
697ccf5e68eSMichal Meloun 		device_printf(sc->dev,
698ccf5e68eSMichal Meloun 		    "Received unused/unexpected command: %u\n", req_cmd);
699ccf5e68eSMichal Meloun 		*resp_cmd = 0;
700ccf5e68eSMichal Meloun 		break;
701ccf5e68eSMichal Meloun 
702ccf5e68eSMichal Meloun 	default:
703ccf5e68eSMichal Meloun 		device_printf(sc->dev,
704ccf5e68eSMichal Meloun 		    "Received unknown command: %u\n", req_cmd);
705ccf5e68eSMichal Meloun 	}
706ccf5e68eSMichal Meloun }
707ccf5e68eSMichal Meloun 
708ccf5e68eSMichal Meloun static void
709ccf5e68eSMichal Meloun intr_mbox(void *arg)
710ccf5e68eSMichal Meloun {
711ccf5e68eSMichal Meloun 	struct tegra_xhci_softc *sc;
712ccf5e68eSMichal Meloun 	uint32_t reg, msg, resp_cmd, resp_data;
713ccf5e68eSMichal Meloun 
714ccf5e68eSMichal Meloun 	sc = (struct tegra_xhci_softc *)arg;
715ccf5e68eSMichal Meloun 
716ccf5e68eSMichal Meloun 	/* Clear interrupt first */
717ccf5e68eSMichal Meloun 	reg = FPCI_RD4(sc, XUSB_CFG_ARU_SMI_INTR);
718ccf5e68eSMichal Meloun 	FPCI_WR4(sc, XUSB_CFG_ARU_SMI_INTR, reg);
719ccf5e68eSMichal Meloun 	if (reg & ARU_SMI_INTR_FW_HANG) {
720ccf5e68eSMichal Meloun 		device_printf(sc->dev,
721ccf5e68eSMichal Meloun 		    "XUSB CPU firmware hang!!! CPUCTL: 0x%08X\n",
722ccf5e68eSMichal Meloun 		    CSB_RD4(sc, XUSB_FALCON_CPUCTL));
723ccf5e68eSMichal Meloun 	}
724ccf5e68eSMichal Meloun 
725ccf5e68eSMichal Meloun 	msg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_DATA_OUT);
726ccf5e68eSMichal Meloun 	resp_cmd = 0;
727ccf5e68eSMichal Meloun 	process_msg(sc, ARU_MAILBOX_DATA_OUT_TYPE(msg),
728ccf5e68eSMichal Meloun 	   ARU_MAILBOX_DATA_OUT_DATA(msg), &resp_cmd, &resp_data);
729ccf5e68eSMichal Meloun 	if (resp_cmd != 0)
730ccf5e68eSMichal Meloun 		mbox_send_ack(sc, resp_cmd, resp_data);
731ccf5e68eSMichal Meloun 	else
732ccf5e68eSMichal Meloun 		FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_OWNER,
733ccf5e68eSMichal Meloun 		    ARU_MAILBOX_OWNER_NONE);
734ccf5e68eSMichal Meloun 
735ccf5e68eSMichal Meloun 	reg = FPCI_RD4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD);
736ccf5e68eSMichal Meloun 	reg &= ~ARU_MAILBOX_CMD_DEST_SMI;
737ccf5e68eSMichal Meloun 	FPCI_WR4(sc, T_XUSB_CFG_ARU_MAILBOX_CMD, reg);
738ccf5e68eSMichal Meloun 
739ccf5e68eSMichal Meloun }
740ccf5e68eSMichal Meloun 
741ccf5e68eSMichal Meloun static int
742ccf5e68eSMichal Meloun load_fw(struct tegra_xhci_softc *sc)
743ccf5e68eSMichal Meloun {
744ccf5e68eSMichal Meloun 	const struct firmware *fw;
745ccf5e68eSMichal Meloun 	const struct tegra_xusb_fw_hdr *fw_hdr;
746ccf5e68eSMichal Meloun 	vm_paddr_t fw_paddr, fw_base;
747ccf5e68eSMichal Meloun 	vm_offset_t fw_vaddr;
748ccf5e68eSMichal Meloun 	vm_size_t fw_size;
749ccf5e68eSMichal Meloun 	uint32_t code_tags, code_size;
750ccf5e68eSMichal Meloun 	struct clocktime fw_clock;
751ccf5e68eSMichal Meloun 	struct timespec	fw_timespec;
752ccf5e68eSMichal Meloun 	int i;
753ccf5e68eSMichal Meloun 
754ccf5e68eSMichal Meloun 	/* Reset ARU */
755ccf5e68eSMichal Meloun 	FPCI_WR4(sc, XUSB_CFG_ARU_RST, ARU_RST_RESET);
756ccf5e68eSMichal Meloun 	DELAY(3000);
757ccf5e68eSMichal Meloun 
758ccf5e68eSMichal Meloun 	/* Check if FALCON already runs */
759ccf5e68eSMichal Meloun 	if (CSB_RD4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_LO) != 0) {
760ccf5e68eSMichal Meloun 		device_printf(sc->dev,
761ccf5e68eSMichal Meloun 		    "XUSB CPU is already loaded, CPUCTL: 0x%08X\n",
762ccf5e68eSMichal Meloun 			 CSB_RD4(sc, XUSB_FALCON_CPUCTL));
763ccf5e68eSMichal Meloun 		return (0);
764ccf5e68eSMichal Meloun 	}
765ccf5e68eSMichal Meloun 
766b9cbd68dSMichal Meloun 	fw = firmware_get(sc->soc->fw_name);
767ccf5e68eSMichal Meloun 	if (fw == NULL) {
768ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Cannot read xusb firmware\n");
769ccf5e68eSMichal Meloun 		return (ENOENT);
770ccf5e68eSMichal Meloun 	}
771ccf5e68eSMichal Meloun 
772ccf5e68eSMichal Meloun 	/* Allocate uncached memory and copy firmware into. */
773ccf5e68eSMichal Meloun 	fw_hdr = (const struct tegra_xusb_fw_hdr *)fw->data;
774ccf5e68eSMichal Meloun 	fw_size = fw_hdr->fwimg_len;
775ccf5e68eSMichal Meloun 
77644d0efb2SAlan Cox 	fw_vaddr = kmem_alloc_contig(fw_size, M_WAITOK, 0, -1UL, PAGE_SIZE, 0,
77744d0efb2SAlan Cox 	    VM_MEMATTR_UNCACHEABLE);
778ccf5e68eSMichal Meloun 	fw_paddr = vtophys(fw_vaddr);
779ccf5e68eSMichal Meloun 	fw_hdr = (const struct tegra_xusb_fw_hdr *)fw_vaddr;
780ccf5e68eSMichal Meloun 	memcpy((void *)fw_vaddr, fw->data, fw_size);
781ccf5e68eSMichal Meloun 
782ccf5e68eSMichal Meloun 	firmware_put(fw, FIRMWARE_UNLOAD);
783ccf5e68eSMichal Meloun 	sc->fw_vaddr = fw_vaddr;
784ccf5e68eSMichal Meloun 	sc->fw_size = fw_size;
785ccf5e68eSMichal Meloun 
786ccf5e68eSMichal Meloun 	/* Setup firmware physical address and size. */
787ccf5e68eSMichal Meloun 	fw_base = fw_paddr + sizeof(*fw_hdr);
788ccf5e68eSMichal Meloun 	CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_ATTR, fw_size);
789ccf5e68eSMichal Meloun 	CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_LO, fw_base & 0xFFFFFFFF);
790ccf5e68eSMichal Meloun 	CSB_WR4(sc, XUSB_CSB_MEMPOOL_ILOAD_BASE_HI, (uint64_t)fw_base >> 32);
791ccf5e68eSMichal Meloun 	CSB_WR4(sc, XUSB_CSB_MEMPOOL_APMAP, APMAP_BOOTPATH);
792ccf5e68eSMichal Meloun 
793ccf5e68eSMichal Meloun 	/* Invalidate full L2IMEM context. */
794ccf5e68eSMichal Meloun 	CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG,
795ccf5e68eSMichal Meloun 	    L2IMEMOP_INVALIDATE_ALL);
796ccf5e68eSMichal Meloun 
797ccf5e68eSMichal Meloun 	/* Program load of L2IMEM by boot code. */
798ccf5e68eSMichal Meloun 	code_tags = howmany(fw_hdr->boot_codetag, XUSB_CSB_IMEM_BLOCK_SIZE);
799ccf5e68eSMichal Meloun 	code_size = howmany(fw_hdr->boot_codesize, XUSB_CSB_IMEM_BLOCK_SIZE);
800ccf5e68eSMichal Meloun 	CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_SIZE,
801ccf5e68eSMichal Meloun 	    L2IMEMOP_SIZE_OFFSET(code_tags) |
802ccf5e68eSMichal Meloun 	    L2IMEMOP_SIZE_SIZE(code_size));
803ccf5e68eSMichal Meloun 
804ccf5e68eSMichal Meloun 	/* Execute L2IMEM boot code fetch. */
805ccf5e68eSMichal Meloun 	CSB_WR4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_TRIG,
806ccf5e68eSMichal Meloun 	    L2IMEMOP_LOAD_LOCKED_RESULT);
807ccf5e68eSMichal Meloun 
808ccf5e68eSMichal Meloun 	/* Program FALCON auto-fill range and block count */
809ccf5e68eSMichal Meloun 	CSB_WR4(sc, XUSB_FALCON_IMFILLCTL, code_size);
810ccf5e68eSMichal Meloun 	CSB_WR4(sc, XUSB_FALCON_IMFILLRNG1,
811ccf5e68eSMichal Meloun 	    IMFILLRNG1_TAG_LO(code_tags) |
812ccf5e68eSMichal Meloun 	    IMFILLRNG1_TAG_HI(code_tags + code_size));
813ccf5e68eSMichal Meloun 
814ccf5e68eSMichal Meloun 	CSB_WR4(sc, XUSB_FALCON_DMACTL, 0);
815ccf5e68eSMichal Meloun 	/* Wait for CPU */
816ccf5e68eSMichal Meloun 	for (i = 500; i > 0; i--) {
817ccf5e68eSMichal Meloun 		if (CSB_RD4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT) &
818ccf5e68eSMichal Meloun 		     L2IMEMOP_RESULT_VLD)
819ccf5e68eSMichal Meloun 			break;
820ccf5e68eSMichal Meloun 		DELAY(100);
821ccf5e68eSMichal Meloun 	}
822ccf5e68eSMichal Meloun 	if (i <= 0) {
823ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Timedout while wating for DMA, "
824ccf5e68eSMichal Meloun 		    "state: 0x%08X\n",
825ccf5e68eSMichal Meloun 		    CSB_RD4(sc, XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT));
826ccf5e68eSMichal Meloun 		return (ETIMEDOUT);
827ccf5e68eSMichal Meloun 	}
828ccf5e68eSMichal Meloun 
829ccf5e68eSMichal Meloun 	/* Boot FALCON cpu */
830ccf5e68eSMichal Meloun 	CSB_WR4(sc, XUSB_FALCON_BOOTVEC, fw_hdr->boot_codetag);
831ccf5e68eSMichal Meloun 	CSB_WR4(sc, XUSB_FALCON_CPUCTL, CPUCTL_STARTCPU);
832ccf5e68eSMichal Meloun 
833ccf5e68eSMichal Meloun 	/* Wait for CPU */
834ccf5e68eSMichal Meloun 	for (i = 50; i > 0; i--) {
835ccf5e68eSMichal Meloun 		if (CSB_RD4(sc, XUSB_FALCON_CPUCTL) == CPUCTL_STOPPED)
836ccf5e68eSMichal Meloun 			break;
837ccf5e68eSMichal Meloun 		DELAY(100);
838ccf5e68eSMichal Meloun 	}
839ccf5e68eSMichal Meloun 	if (i <= 0) {
840ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Timedout while wating for FALCON cpu, "
841ccf5e68eSMichal Meloun 		    "state: 0x%08X\n", CSB_RD4(sc, XUSB_FALCON_CPUCTL));
842ccf5e68eSMichal Meloun 		return (ETIMEDOUT);
843ccf5e68eSMichal Meloun 	}
844ccf5e68eSMichal Meloun 
845ccf5e68eSMichal Meloun 	fw_timespec.tv_sec = fw_hdr->fwimg_created_time;
846ccf5e68eSMichal Meloun 	fw_timespec.tv_nsec = 0;
847ccf5e68eSMichal Meloun 	clock_ts_to_ct(&fw_timespec, &fw_clock);
848ccf5e68eSMichal Meloun 	device_printf(sc->dev,
849ccf5e68eSMichal Meloun 	    " Falcon firmware version: %02X.%02X.%04X,"
850ccf5e68eSMichal Meloun 	    " (%d/%d/%d %d:%02d:%02d UTC)\n",
851ccf5e68eSMichal Meloun 	    (fw_hdr->version_id >> 24) & 0xFF,(fw_hdr->version_id >> 15) & 0xFF,
852ccf5e68eSMichal Meloun 	    fw_hdr->version_id & 0xFFFF,
853ccf5e68eSMichal Meloun 	    fw_clock.day, fw_clock.mon, fw_clock.year,
854ccf5e68eSMichal Meloun 	    fw_clock.hour, fw_clock.min, fw_clock.sec);
855ccf5e68eSMichal Meloun 
856ccf5e68eSMichal Meloun 	return (0);
857ccf5e68eSMichal Meloun }
858ccf5e68eSMichal Meloun 
859ccf5e68eSMichal Meloun static int
860ccf5e68eSMichal Meloun init_hw(struct tegra_xhci_softc *sc)
861ccf5e68eSMichal Meloun {
862ccf5e68eSMichal Meloun 	int rv;
863ccf5e68eSMichal Meloun 	uint32_t reg;
864ccf5e68eSMichal Meloun 	rman_res_t base_addr;
865ccf5e68eSMichal Meloun 
866ccf5e68eSMichal Meloun 	base_addr = rman_get_start(sc->xhci_softc.sc_io_res);
867ccf5e68eSMichal Meloun 
868ccf5e68eSMichal Meloun 	/* Enable FPCI access */
869ccf5e68eSMichal Meloun 	reg = IPFS_RD4(sc, XUSB_HOST_CONFIGURATION);
870ccf5e68eSMichal Meloun 	reg |= CONFIGURATION_EN_FPCI;
871ccf5e68eSMichal Meloun 	IPFS_WR4(sc, XUSB_HOST_CONFIGURATION, reg);
872ccf5e68eSMichal Meloun 	IPFS_RD4(sc, XUSB_HOST_CONFIGURATION);
873ccf5e68eSMichal Meloun 
874ccf5e68eSMichal Meloun 	/* Program bar for XHCI base address */
875ccf5e68eSMichal Meloun 	reg = FPCI_RD4(sc, T_XUSB_CFG_4);
876ccf5e68eSMichal Meloun 	reg &= ~CFG_4_BASE_ADDRESS(~0);
877ccf5e68eSMichal Meloun 	reg |= CFG_4_BASE_ADDRESS((uint32_t)base_addr >> 15);
878ccf5e68eSMichal Meloun 	FPCI_WR4(sc, T_XUSB_CFG_4, reg);
879ccf5e68eSMichal Meloun 	FPCI_WR4(sc, T_XUSB_CFG_5, (uint32_t)((uint64_t)(base_addr) >> 32));
880ccf5e68eSMichal Meloun 
881ccf5e68eSMichal Meloun 	/* Enable bus master */
882ccf5e68eSMichal Meloun 	reg = FPCI_RD4(sc, T_XUSB_CFG_1);
883ccf5e68eSMichal Meloun 	reg |= CFG_1_IO_SPACE;
884ccf5e68eSMichal Meloun 	reg |= CFG_1_MEMORY_SPACE;
885ccf5e68eSMichal Meloun 	reg |= CFG_1_BUS_MASTER;
886ccf5e68eSMichal Meloun 	FPCI_WR4(sc, T_XUSB_CFG_1, reg);
887ccf5e68eSMichal Meloun 
888ccf5e68eSMichal Meloun 	/* Enable Interrupts */
889ccf5e68eSMichal Meloun 	reg = IPFS_RD4(sc, XUSB_HOST_INTR_MASK);
890ccf5e68eSMichal Meloun 	reg |= INTR_IP_INT_MASK;
891ccf5e68eSMichal Meloun 	IPFS_WR4(sc, XUSB_HOST_INTR_MASK, reg);
892ccf5e68eSMichal Meloun 
893ccf5e68eSMichal Meloun 	/* Set hysteresis */
894ccf5e68eSMichal Meloun 	IPFS_WR4(sc, XUSB_HOST_CLKGATE_HYSTERESIS, 128);
895ccf5e68eSMichal Meloun 
896ccf5e68eSMichal Meloun 	rv = load_fw(sc);
897ccf5e68eSMichal Meloun 	if (rv != 0)
898ccf5e68eSMichal Meloun 		return rv;
899ccf5e68eSMichal Meloun 	return (0);
900ccf5e68eSMichal Meloun }
901ccf5e68eSMichal Meloun 
902ccf5e68eSMichal Meloun static int
903ccf5e68eSMichal Meloun tegra_xhci_probe(device_t dev)
904ccf5e68eSMichal Meloun {
905ccf5e68eSMichal Meloun 
906ccf5e68eSMichal Meloun 	if (!ofw_bus_status_okay(dev))
907ccf5e68eSMichal Meloun 		return (ENXIO);
908ccf5e68eSMichal Meloun 
909ccf5e68eSMichal Meloun 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
910ccf5e68eSMichal Meloun 		device_set_desc(dev, "Nvidia Tegra XHCI controller");
911ccf5e68eSMichal Meloun 		return (BUS_PROBE_DEFAULT);
912ccf5e68eSMichal Meloun 	}
913ccf5e68eSMichal Meloun 	return (ENXIO);
914ccf5e68eSMichal Meloun }
915ccf5e68eSMichal Meloun 
916ccf5e68eSMichal Meloun static int
917ccf5e68eSMichal Meloun tegra_xhci_detach(device_t dev)
918ccf5e68eSMichal Meloun {
919ccf5e68eSMichal Meloun 	struct tegra_xhci_softc *sc;
920ccf5e68eSMichal Meloun 	struct xhci_softc *xsc;
921ccf5e68eSMichal Meloun 
922ccf5e68eSMichal Meloun 	sc = device_get_softc(dev);
923ccf5e68eSMichal Meloun 	xsc = &sc->xhci_softc;
924ccf5e68eSMichal Meloun 
925ccf5e68eSMichal Meloun 	/* during module unload there are lots of children leftover */
926ccf5e68eSMichal Meloun 	device_delete_children(dev);
927ccf5e68eSMichal Meloun 	if (sc->xhci_inited) {
928ccf5e68eSMichal Meloun 		usb_callout_drain(&xsc->sc_callout);
929ccf5e68eSMichal Meloun 		xhci_halt_controller(xsc);
930ccf5e68eSMichal Meloun 	}
931ccf5e68eSMichal Meloun 
932ccf5e68eSMichal Meloun 	if (xsc->sc_irq_res && xsc->sc_intr_hdl) {
933ccf5e68eSMichal Meloun 		bus_teardown_intr(dev, xsc->sc_irq_res, xsc->sc_intr_hdl);
934ccf5e68eSMichal Meloun 		xsc->sc_intr_hdl = NULL;
935ccf5e68eSMichal Meloun 	}
936ccf5e68eSMichal Meloun 	if (xsc->sc_irq_res) {
937ccf5e68eSMichal Meloun 		bus_release_resource(dev, SYS_RES_IRQ,
938ccf5e68eSMichal Meloun 		    rman_get_rid(xsc->sc_irq_res), xsc->sc_irq_res);
939ccf5e68eSMichal Meloun 		xsc->sc_irq_res = NULL;
940ccf5e68eSMichal Meloun 	}
941ccf5e68eSMichal Meloun 	if (xsc->sc_io_res != NULL) {
942ccf5e68eSMichal Meloun 		bus_release_resource(dev, SYS_RES_MEMORY,
943ccf5e68eSMichal Meloun 		    rman_get_rid(xsc->sc_io_res), xsc->sc_io_res);
944ccf5e68eSMichal Meloun 		xsc->sc_io_res = NULL;
945ccf5e68eSMichal Meloun 	}
946ccf5e68eSMichal Meloun 	if (sc->xhci_inited)
947ccf5e68eSMichal Meloun 		xhci_uninit(xsc);
948ccf5e68eSMichal Meloun 	if (sc->irq_hdl_mbox != NULL)
949ccf5e68eSMichal Meloun 		bus_teardown_intr(dev, sc->irq_res_mbox, sc->irq_hdl_mbox);
950ccf5e68eSMichal Meloun 	if (sc->fw_vaddr != 0)
95149bfa624SAlan Cox 		kmem_free(sc->fw_vaddr, sc->fw_size);
952ccf5e68eSMichal Meloun 	LOCK_DESTROY(sc);
953ccf5e68eSMichal Meloun 	return (0);
954ccf5e68eSMichal Meloun }
955ccf5e68eSMichal Meloun 
956ccf5e68eSMichal Meloun static int
957ccf5e68eSMichal Meloun tegra_xhci_attach(device_t dev)
958ccf5e68eSMichal Meloun {
959ccf5e68eSMichal Meloun 	struct tegra_xhci_softc *sc;
960ccf5e68eSMichal Meloun 	struct xhci_softc *xsc;
961ccf5e68eSMichal Meloun 	int rv, rid;
962ccf5e68eSMichal Meloun 	phandle_t node;
963ccf5e68eSMichal Meloun 
964ccf5e68eSMichal Meloun 	sc = device_get_softc(dev);
965ccf5e68eSMichal Meloun 	sc->dev = dev;
966b9cbd68dSMichal Meloun 	sc->soc = (struct xhci_soc *)ofw_bus_search_compatible(dev,
967b9cbd68dSMichal Meloun 	    compat_data)->ocd_data;
968ccf5e68eSMichal Meloun 	node = ofw_bus_get_node(dev);
969ccf5e68eSMichal Meloun 	xsc = &sc->xhci_softc;
970ccf5e68eSMichal Meloun 	LOCK_INIT(sc);
971ccf5e68eSMichal Meloun 
972ccf5e68eSMichal Meloun 	rv = get_fdt_resources(sc, node);
973ccf5e68eSMichal Meloun 	if (rv != 0) {
974ccf5e68eSMichal Meloun 		rv = ENXIO;
975ccf5e68eSMichal Meloun 		goto error;
976ccf5e68eSMichal Meloun 	}
977ccf5e68eSMichal Meloun 	rv = enable_fdt_resources(sc);
978ccf5e68eSMichal Meloun 	if (rv != 0) {
979ccf5e68eSMichal Meloun 		rv = ENXIO;
980ccf5e68eSMichal Meloun 		goto error;
981ccf5e68eSMichal Meloun 	}
982ccf5e68eSMichal Meloun 
983ccf5e68eSMichal Meloun 	/* Allocate resources. */
984ccf5e68eSMichal Meloun 	rid = 0;
985ccf5e68eSMichal Meloun 	xsc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
986ccf5e68eSMichal Meloun 	    RF_ACTIVE);
987ccf5e68eSMichal Meloun 	if (xsc->sc_io_res == NULL) {
988ccf5e68eSMichal Meloun 		device_printf(dev,
989ccf5e68eSMichal Meloun 		    "Could not allocate HCD memory resources\n");
990ccf5e68eSMichal Meloun 		rv = ENXIO;
991ccf5e68eSMichal Meloun 		goto error;
992ccf5e68eSMichal Meloun 	}
993ccf5e68eSMichal Meloun 	rid = 1;
994ccf5e68eSMichal Meloun 	sc->mem_res_fpci = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
995ccf5e68eSMichal Meloun 	    RF_ACTIVE);
996ccf5e68eSMichal Meloun 	if (sc->mem_res_fpci == NULL) {
997ccf5e68eSMichal Meloun 		device_printf(dev,
998ccf5e68eSMichal Meloun 		    "Could not allocate FPCI memory resources\n");
999ccf5e68eSMichal Meloun 		rv = ENXIO;
1000ccf5e68eSMichal Meloun 		goto error;
1001ccf5e68eSMichal Meloun 	}
1002ccf5e68eSMichal Meloun 	rid = 2;
1003ccf5e68eSMichal Meloun 	sc->mem_res_ipfs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
1004ccf5e68eSMichal Meloun 	    RF_ACTIVE);
1005ccf5e68eSMichal Meloun 	if (sc->mem_res_ipfs == NULL) {
1006ccf5e68eSMichal Meloun 		device_printf(dev,
1007ccf5e68eSMichal Meloun 		    "Could not allocate IPFS memory resources\n");
1008ccf5e68eSMichal Meloun 		rv = ENXIO;
1009ccf5e68eSMichal Meloun 		goto error;
1010ccf5e68eSMichal Meloun 	}
1011ccf5e68eSMichal Meloun 
1012ccf5e68eSMichal Meloun 	rid = 0;
1013ccf5e68eSMichal Meloun 	xsc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
1014ccf5e68eSMichal Meloun 	    RF_ACTIVE);
1015ccf5e68eSMichal Meloun 	if (xsc->sc_irq_res == NULL) {
1016ccf5e68eSMichal Meloun 		device_printf(dev, "Could not allocate HCD IRQ resources\n");
1017ccf5e68eSMichal Meloun 		rv = ENXIO;
1018ccf5e68eSMichal Meloun 		goto error;
1019ccf5e68eSMichal Meloun 	}
1020ccf5e68eSMichal Meloun 	rid = 1;
1021ccf5e68eSMichal Meloun 	sc->irq_res_mbox = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
1022ccf5e68eSMichal Meloun 	    RF_ACTIVE);
1023ccf5e68eSMichal Meloun 	if (sc->irq_res_mbox == NULL) {
1024ccf5e68eSMichal Meloun 		device_printf(dev, "Could not allocate MBOX IRQ resources\n");
1025ccf5e68eSMichal Meloun 		rv = ENXIO;
1026ccf5e68eSMichal Meloun 		goto error;
1027ccf5e68eSMichal Meloun 	}
1028ccf5e68eSMichal Meloun 
1029ccf5e68eSMichal Meloun 	rv = init_hw(sc);
1030ccf5e68eSMichal Meloun 	if (rv != 0) {
1031ccf5e68eSMichal Meloun 		device_printf(dev, "Could not initialize  XUSB hardware\n");
1032ccf5e68eSMichal Meloun 		goto error;
1033ccf5e68eSMichal Meloun 	}
1034ccf5e68eSMichal Meloun 
1035ccf5e68eSMichal Meloun 	/* Wakeup and enable firmaware */
1036ccf5e68eSMichal Meloun 	rv = mbox_send_cmd(sc, MBOX_CMD_MSG_ENABLED, 0);
1037ccf5e68eSMichal Meloun 	if (rv != 0) {
1038ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Could not enable XUSB firmware\n");
1039ccf5e68eSMichal Meloun 		goto error;
1040ccf5e68eSMichal Meloun 	}
1041ccf5e68eSMichal Meloun 
1042ccf5e68eSMichal Meloun 	/* Fill data for XHCI driver. */
1043ccf5e68eSMichal Meloun 	xsc->sc_bus.parent = dev;
1044ccf5e68eSMichal Meloun 	xsc->sc_bus.devices = xsc->sc_devices;
1045ccf5e68eSMichal Meloun 	xsc->sc_bus.devices_max = XHCI_MAX_DEVICES;
1046ccf5e68eSMichal Meloun 
1047ccf5e68eSMichal Meloun 	xsc->sc_io_tag = rman_get_bustag(xsc->sc_io_res);
1048ccf5e68eSMichal Meloun 	xsc->sc_io_hdl = rman_get_bushandle(xsc->sc_io_res);
1049ccf5e68eSMichal Meloun 	xsc->sc_io_size = rman_get_size(xsc->sc_io_res);
1050ccf5e68eSMichal Meloun 	strlcpy(xsc->sc_vendor, "Nvidia", sizeof(xsc->sc_vendor));
1051ccf5e68eSMichal Meloun 
1052ccf5e68eSMichal Meloun 	/* Add USB bus device. */
1053ccf5e68eSMichal Meloun 	xsc->sc_bus.bdev = device_add_child(sc->dev, "usbus", -1);
1054ccf5e68eSMichal Meloun 	if (xsc->sc_bus.bdev == NULL) {
1055ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Could not add USB device\n");
1056ccf5e68eSMichal Meloun 		rv = ENXIO;
1057ccf5e68eSMichal Meloun 		goto error;
1058ccf5e68eSMichal Meloun 	}
1059ccf5e68eSMichal Meloun 	device_set_ivars(xsc->sc_bus.bdev, &xsc->sc_bus);
1060ccf5e68eSMichal Meloun 	device_set_desc(xsc->sc_bus.bdev, "Nvidia USB 3.0 controller");
1061ccf5e68eSMichal Meloun 
1062ccf5e68eSMichal Meloun 	rv = xhci_init(xsc, sc->dev, 1);
1063ccf5e68eSMichal Meloun 	if (rv != 0) {
1064ccf5e68eSMichal Meloun 		device_printf(sc->dev, "USB init failed: %d\n", rv);
1065ccf5e68eSMichal Meloun 		goto error;
1066ccf5e68eSMichal Meloun 	}
1067ccf5e68eSMichal Meloun 	sc->xhci_inited = true;
1068ccf5e68eSMichal Meloun 	rv = xhci_start_controller(xsc);
1069ccf5e68eSMichal Meloun 	if (rv != 0) {
1070ccf5e68eSMichal Meloun 		device_printf(sc->dev,
1071ccf5e68eSMichal Meloun 		    "Could not start XHCI controller: %d\n", rv);
1072ccf5e68eSMichal Meloun 		goto error;
1073ccf5e68eSMichal Meloun 	}
1074ccf5e68eSMichal Meloun 
1075ccf5e68eSMichal Meloun 	rv = bus_setup_intr(dev, sc->irq_res_mbox, INTR_TYPE_MISC | INTR_MPSAFE,
1076ccf5e68eSMichal Meloun 	    NULL, intr_mbox, sc, &sc->irq_hdl_mbox);
1077ccf5e68eSMichal Meloun 	if (rv != 0) {
1078ccf5e68eSMichal Meloun 		device_printf(dev, "Could not setup error IRQ: %d\n",rv);
1079ccf5e68eSMichal Meloun 		xsc->sc_intr_hdl = NULL;
1080ccf5e68eSMichal Meloun 		goto error;
1081ccf5e68eSMichal Meloun 	}
1082ccf5e68eSMichal Meloun 
1083ccf5e68eSMichal Meloun 	rv = bus_setup_intr(dev, xsc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
1084ccf5e68eSMichal Meloun 	    NULL, (driver_intr_t *)xhci_interrupt, xsc, &xsc->sc_intr_hdl);
1085ccf5e68eSMichal Meloun 	if (rv != 0) {
1086ccf5e68eSMichal Meloun 		device_printf(dev, "Could not setup error IRQ: %d\n",rv);
1087ccf5e68eSMichal Meloun 		xsc->sc_intr_hdl = NULL;
1088ccf5e68eSMichal Meloun 		goto error;
1089ccf5e68eSMichal Meloun 	}
1090ccf5e68eSMichal Meloun 
1091ccf5e68eSMichal Meloun 	/* Probe the bus. */
1092ccf5e68eSMichal Meloun 	rv = device_probe_and_attach(xsc->sc_bus.bdev);
1093ccf5e68eSMichal Meloun 	if (rv != 0) {
1094ccf5e68eSMichal Meloun 		device_printf(sc->dev, "Could not initialize USB: %d\n", rv);
1095ccf5e68eSMichal Meloun 		goto error;
1096ccf5e68eSMichal Meloun 	}
1097ccf5e68eSMichal Meloun 
1098ccf5e68eSMichal Meloun 	return (0);
1099ccf5e68eSMichal Meloun 
1100ccf5e68eSMichal Meloun error:
1101ccf5e68eSMichal Meloun panic("XXXXX");
1102ccf5e68eSMichal Meloun 	tegra_xhci_detach(dev);
1103ccf5e68eSMichal Meloun 	return (rv);
1104ccf5e68eSMichal Meloun }
1105ccf5e68eSMichal Meloun 
1106ccf5e68eSMichal Meloun static device_method_t xhci_methods[] = {
1107ccf5e68eSMichal Meloun 	/* Device interface */
1108ccf5e68eSMichal Meloun 	DEVMETHOD(device_probe, tegra_xhci_probe),
1109ccf5e68eSMichal Meloun 	DEVMETHOD(device_attach, tegra_xhci_attach),
1110ccf5e68eSMichal Meloun 	DEVMETHOD(device_detach, tegra_xhci_detach),
1111ccf5e68eSMichal Meloun 	DEVMETHOD(device_suspend, bus_generic_suspend),
1112ccf5e68eSMichal Meloun 	DEVMETHOD(device_resume, bus_generic_resume),
1113ccf5e68eSMichal Meloun 	DEVMETHOD(device_shutdown, bus_generic_shutdown),
1114ccf5e68eSMichal Meloun 
1115ccf5e68eSMichal Meloun 	/* Bus interface */
1116ccf5e68eSMichal Meloun 	DEVMETHOD(bus_print_child, bus_generic_print_child),
1117ccf5e68eSMichal Meloun 
1118ccf5e68eSMichal Meloun 	DEVMETHOD_END
1119ccf5e68eSMichal Meloun };
1120ccf5e68eSMichal Meloun 
1121ccf5e68eSMichal Meloun static DEFINE_CLASS_0(xhci, xhci_driver, xhci_methods,
1122ccf5e68eSMichal Meloun     sizeof(struct tegra_xhci_softc));
1123*289f133bSJohn Baldwin DRIVER_MODULE(tegra_xhci, simplebus, xhci_driver, NULL, NULL);
1124ccf5e68eSMichal Meloun MODULE_DEPEND(tegra_xhci, usb, 1, 1, 1);
1125