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