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