18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * NVIDIA Tegra XUSB device mode controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2013-2019, NVIDIA CORPORATION. All rights reserved. 68c2ecf20Sopenharmony_ci * Copyright (c) 2015, Google Inc. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/completion.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 138c2ecf20Sopenharmony_ci#include <linux/dmapool.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_device.h> 208c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 218c2ecf20Sopenharmony_ci#include <linux/phy/tegra/xusb.h> 228c2ecf20Sopenharmony_ci#include <linux/pm_domain.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 258c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 268c2ecf20Sopenharmony_ci#include <linux/reset.h> 278c2ecf20Sopenharmony_ci#include <linux/usb/ch9.h> 288c2ecf20Sopenharmony_ci#include <linux/usb/gadget.h> 298c2ecf20Sopenharmony_ci#include <linux/usb/otg.h> 308c2ecf20Sopenharmony_ci#include <linux/usb/role.h> 318c2ecf20Sopenharmony_ci#include <linux/usb/phy.h> 328c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* XUSB_DEV registers */ 358c2ecf20Sopenharmony_ci#define DB 0x004 368c2ecf20Sopenharmony_ci#define DB_TARGET_MASK GENMASK(15, 8) 378c2ecf20Sopenharmony_ci#define DB_TARGET(x) (((x) << 8) & DB_TARGET_MASK) 388c2ecf20Sopenharmony_ci#define DB_STREAMID_MASK GENMASK(31, 16) 398c2ecf20Sopenharmony_ci#define DB_STREAMID(x) (((x) << 16) & DB_STREAMID_MASK) 408c2ecf20Sopenharmony_ci#define ERSTSZ 0x008 418c2ecf20Sopenharmony_ci#define ERSTSZ_ERSTXSZ_SHIFT(x) ((x) * 16) 428c2ecf20Sopenharmony_ci#define ERSTSZ_ERSTXSZ_MASK GENMASK(15, 0) 438c2ecf20Sopenharmony_ci#define ERSTXBALO(x) (0x010 + 8 * (x)) 448c2ecf20Sopenharmony_ci#define ERSTXBAHI(x) (0x014 + 8 * (x)) 458c2ecf20Sopenharmony_ci#define ERDPLO 0x020 468c2ecf20Sopenharmony_ci#define ERDPLO_EHB BIT(3) 478c2ecf20Sopenharmony_ci#define ERDPHI 0x024 488c2ecf20Sopenharmony_ci#define EREPLO 0x028 498c2ecf20Sopenharmony_ci#define EREPLO_ECS BIT(0) 508c2ecf20Sopenharmony_ci#define EREPLO_SEGI BIT(1) 518c2ecf20Sopenharmony_ci#define EREPHI 0x02c 528c2ecf20Sopenharmony_ci#define CTRL 0x030 538c2ecf20Sopenharmony_ci#define CTRL_RUN BIT(0) 548c2ecf20Sopenharmony_ci#define CTRL_LSE BIT(1) 558c2ecf20Sopenharmony_ci#define CTRL_IE BIT(4) 568c2ecf20Sopenharmony_ci#define CTRL_SMI_EVT BIT(5) 578c2ecf20Sopenharmony_ci#define CTRL_SMI_DSE BIT(6) 588c2ecf20Sopenharmony_ci#define CTRL_EWE BIT(7) 598c2ecf20Sopenharmony_ci#define CTRL_DEVADDR_MASK GENMASK(30, 24) 608c2ecf20Sopenharmony_ci#define CTRL_DEVADDR(x) (((x) << 24) & CTRL_DEVADDR_MASK) 618c2ecf20Sopenharmony_ci#define CTRL_ENABLE BIT(31) 628c2ecf20Sopenharmony_ci#define ST 0x034 638c2ecf20Sopenharmony_ci#define ST_RC BIT(0) 648c2ecf20Sopenharmony_ci#define ST_IP BIT(4) 658c2ecf20Sopenharmony_ci#define RT_IMOD 0x038 668c2ecf20Sopenharmony_ci#define RT_IMOD_IMODI_MASK GENMASK(15, 0) 678c2ecf20Sopenharmony_ci#define RT_IMOD_IMODI(x) ((x) & RT_IMOD_IMODI_MASK) 688c2ecf20Sopenharmony_ci#define RT_IMOD_IMODC_MASK GENMASK(31, 16) 698c2ecf20Sopenharmony_ci#define RT_IMOD_IMODC(x) (((x) << 16) & RT_IMOD_IMODC_MASK) 708c2ecf20Sopenharmony_ci#define PORTSC 0x03c 718c2ecf20Sopenharmony_ci#define PORTSC_CCS BIT(0) 728c2ecf20Sopenharmony_ci#define PORTSC_PED BIT(1) 738c2ecf20Sopenharmony_ci#define PORTSC_PR BIT(4) 748c2ecf20Sopenharmony_ci#define PORTSC_PLS_SHIFT 5 758c2ecf20Sopenharmony_ci#define PORTSC_PLS_MASK GENMASK(8, 5) 768c2ecf20Sopenharmony_ci#define PORTSC_PLS_U0 0x0 778c2ecf20Sopenharmony_ci#define PORTSC_PLS_U2 0x2 788c2ecf20Sopenharmony_ci#define PORTSC_PLS_U3 0x3 798c2ecf20Sopenharmony_ci#define PORTSC_PLS_DISABLED 0x4 808c2ecf20Sopenharmony_ci#define PORTSC_PLS_RXDETECT 0x5 818c2ecf20Sopenharmony_ci#define PORTSC_PLS_INACTIVE 0x6 828c2ecf20Sopenharmony_ci#define PORTSC_PLS_RESUME 0xf 838c2ecf20Sopenharmony_ci#define PORTSC_PLS(x) (((x) << PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK) 848c2ecf20Sopenharmony_ci#define PORTSC_PS_SHIFT 10 858c2ecf20Sopenharmony_ci#define PORTSC_PS_MASK GENMASK(13, 10) 868c2ecf20Sopenharmony_ci#define PORTSC_PS_UNDEFINED 0x0 878c2ecf20Sopenharmony_ci#define PORTSC_PS_FS 0x1 888c2ecf20Sopenharmony_ci#define PORTSC_PS_LS 0x2 898c2ecf20Sopenharmony_ci#define PORTSC_PS_HS 0x3 908c2ecf20Sopenharmony_ci#define PORTSC_PS_SS 0x4 918c2ecf20Sopenharmony_ci#define PORTSC_LWS BIT(16) 928c2ecf20Sopenharmony_ci#define PORTSC_CSC BIT(17) 938c2ecf20Sopenharmony_ci#define PORTSC_WRC BIT(19) 948c2ecf20Sopenharmony_ci#define PORTSC_PRC BIT(21) 958c2ecf20Sopenharmony_ci#define PORTSC_PLC BIT(22) 968c2ecf20Sopenharmony_ci#define PORTSC_CEC BIT(23) 978c2ecf20Sopenharmony_ci#define PORTSC_WPR BIT(30) 988c2ecf20Sopenharmony_ci#define PORTSC_CHANGE_MASK (PORTSC_CSC | PORTSC_WRC | PORTSC_PRC | \ 998c2ecf20Sopenharmony_ci PORTSC_PLC | PORTSC_CEC) 1008c2ecf20Sopenharmony_ci#define ECPLO 0x040 1018c2ecf20Sopenharmony_ci#define ECPHI 0x044 1028c2ecf20Sopenharmony_ci#define MFINDEX 0x048 1038c2ecf20Sopenharmony_ci#define MFINDEX_FRAME_SHIFT 3 1048c2ecf20Sopenharmony_ci#define MFINDEX_FRAME_MASK GENMASK(13, 3) 1058c2ecf20Sopenharmony_ci#define PORTPM 0x04c 1068c2ecf20Sopenharmony_ci#define PORTPM_L1S_MASK GENMASK(1, 0) 1078c2ecf20Sopenharmony_ci#define PORTPM_L1S_DROP 0x0 1088c2ecf20Sopenharmony_ci#define PORTPM_L1S_ACCEPT 0x1 1098c2ecf20Sopenharmony_ci#define PORTPM_L1S_NYET 0x2 1108c2ecf20Sopenharmony_ci#define PORTPM_L1S_STALL 0x3 1118c2ecf20Sopenharmony_ci#define PORTPM_L1S(x) ((x) & PORTPM_L1S_MASK) 1128c2ecf20Sopenharmony_ci#define PORTPM_RWE BIT(3) 1138c2ecf20Sopenharmony_ci#define PORTPM_U2TIMEOUT_MASK GENMASK(15, 8) 1148c2ecf20Sopenharmony_ci#define PORTPM_U1TIMEOUT_MASK GENMASK(23, 16) 1158c2ecf20Sopenharmony_ci#define PORTPM_FLA BIT(24) 1168c2ecf20Sopenharmony_ci#define PORTPM_VBA BIT(25) 1178c2ecf20Sopenharmony_ci#define PORTPM_WOC BIT(26) 1188c2ecf20Sopenharmony_ci#define PORTPM_WOD BIT(27) 1198c2ecf20Sopenharmony_ci#define PORTPM_U1E BIT(28) 1208c2ecf20Sopenharmony_ci#define PORTPM_U2E BIT(29) 1218c2ecf20Sopenharmony_ci#define PORTPM_FRWE BIT(30) 1228c2ecf20Sopenharmony_ci#define PORTPM_PNG_CYA BIT(31) 1238c2ecf20Sopenharmony_ci#define EP_HALT 0x050 1248c2ecf20Sopenharmony_ci#define EP_PAUSE 0x054 1258c2ecf20Sopenharmony_ci#define EP_RELOAD 0x058 1268c2ecf20Sopenharmony_ci#define EP_STCHG 0x05c 1278c2ecf20Sopenharmony_ci#define DEVNOTIF_LO 0x064 1288c2ecf20Sopenharmony_ci#define DEVNOTIF_LO_TRIG BIT(0) 1298c2ecf20Sopenharmony_ci#define DEVNOTIF_LO_TYPE_MASK GENMASK(7, 4) 1308c2ecf20Sopenharmony_ci#define DEVNOTIF_LO_TYPE(x) (((x) << 4) & DEVNOTIF_LO_TYPE_MASK) 1318c2ecf20Sopenharmony_ci#define DEVNOTIF_LO_TYPE_FUNCTION_WAKE 0x1 1328c2ecf20Sopenharmony_ci#define DEVNOTIF_HI 0x068 1338c2ecf20Sopenharmony_ci#define PORTHALT 0x06c 1348c2ecf20Sopenharmony_ci#define PORTHALT_HALT_LTSSM BIT(0) 1358c2ecf20Sopenharmony_ci#define PORTHALT_HALT_REJECT BIT(1) 1368c2ecf20Sopenharmony_ci#define PORTHALT_STCHG_REQ BIT(20) 1378c2ecf20Sopenharmony_ci#define PORTHALT_STCHG_INTR_EN BIT(24) 1388c2ecf20Sopenharmony_ci#define PORT_TM 0x070 1398c2ecf20Sopenharmony_ci#define EP_THREAD_ACTIVE 0x074 1408c2ecf20Sopenharmony_ci#define EP_STOPPED 0x078 1418c2ecf20Sopenharmony_ci#define HSFSPI_COUNT0 0x100 1428c2ecf20Sopenharmony_ci#define HSFSPI_COUNT13 0x134 1438c2ecf20Sopenharmony_ci#define HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK GENMASK(29, 0) 1448c2ecf20Sopenharmony_ci#define HSFSPI_COUNT13_U2_RESUME_K_DURATION(x) ((x) & \ 1458c2ecf20Sopenharmony_ci HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK) 1468c2ecf20Sopenharmony_ci#define BLCG 0x840 1478c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT0 0x610 1488c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT0_PING_TBURST_MASK GENMASK(7, 0) 1498c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT0_PING_TBURST(x) ((x) & SSPX_CORE_CNT0_PING_TBURST_MASK) 1508c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT30 0x688 1518c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT30_LMPITP_TIMER_MASK GENMASK(19, 0) 1528c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT30_LMPITP_TIMER(x) ((x) & \ 1538c2ecf20Sopenharmony_ci SSPX_CORE_CNT30_LMPITP_TIMER_MASK) 1548c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT32 0x690 1558c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK GENMASK(7, 0) 1568c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT32_POLL_TBURST_MAX(x) ((x) & \ 1578c2ecf20Sopenharmony_ci SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK) 1588c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT56 0x6fc 1598c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK GENMASK(19, 0) 1608c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(x) ((x) & \ 1618c2ecf20Sopenharmony_ci SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK) 1628c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT57 0x700 1638c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK GENMASK(19, 0) 1648c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(x) ((x) & \ 1658c2ecf20Sopenharmony_ci SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK) 1668c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT65 0x720 1678c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK GENMASK(19, 0) 1688c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(x) ((x) & \ 1698c2ecf20Sopenharmony_ci SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK) 1708c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT66 0x724 1718c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK GENMASK(19, 0) 1728c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(x) ((x) & \ 1738c2ecf20Sopenharmony_ci SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK) 1748c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT67 0x728 1758c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK GENMASK(19, 0) 1768c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(x) ((x) & \ 1778c2ecf20Sopenharmony_ci SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK) 1788c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT72 0x73c 1798c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK GENMASK(19, 0) 1808c2ecf20Sopenharmony_ci#define SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(x) ((x) & \ 1818c2ecf20Sopenharmony_ci SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK) 1828c2ecf20Sopenharmony_ci#define SSPX_CORE_PADCTL4 0x750 1838c2ecf20Sopenharmony_ci#define SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK GENMASK(19, 0) 1848c2ecf20Sopenharmony_ci#define SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3(x) ((x) & \ 1858c2ecf20Sopenharmony_ci SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK) 1868c2ecf20Sopenharmony_ci#define BLCG_DFPCI BIT(0) 1878c2ecf20Sopenharmony_ci#define BLCG_UFPCI BIT(1) 1888c2ecf20Sopenharmony_ci#define BLCG_FE BIT(2) 1898c2ecf20Sopenharmony_ci#define BLCG_COREPLL_PWRDN BIT(8) 1908c2ecf20Sopenharmony_ci#define BLCG_IOPLL_0_PWRDN BIT(9) 1918c2ecf20Sopenharmony_ci#define BLCG_IOPLL_1_PWRDN BIT(10) 1928c2ecf20Sopenharmony_ci#define BLCG_IOPLL_2_PWRDN BIT(11) 1938c2ecf20Sopenharmony_ci#define BLCG_ALL 0x1ff 1948c2ecf20Sopenharmony_ci#define CFG_DEV_SSPI_XFER 0x858 1958c2ecf20Sopenharmony_ci#define CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK GENMASK(31, 0) 1968c2ecf20Sopenharmony_ci#define CFG_DEV_SSPI_XFER_ACKTIMEOUT(x) ((x) & \ 1978c2ecf20Sopenharmony_ci CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK) 1988c2ecf20Sopenharmony_ci#define CFG_DEV_FE 0x85c 1998c2ecf20Sopenharmony_ci#define CFG_DEV_FE_PORTREGSEL_MASK GENMASK(1, 0) 2008c2ecf20Sopenharmony_ci#define CFG_DEV_FE_PORTREGSEL_SS_PI 1 2018c2ecf20Sopenharmony_ci#define CFG_DEV_FE_PORTREGSEL_HSFS_PI 2 2028c2ecf20Sopenharmony_ci#define CFG_DEV_FE_PORTREGSEL(x) ((x) & CFG_DEV_FE_PORTREGSEL_MASK) 2038c2ecf20Sopenharmony_ci#define CFG_DEV_FE_INFINITE_SS_RETRY BIT(29) 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* FPCI registers */ 2068c2ecf20Sopenharmony_ci#define XUSB_DEV_CFG_1 0x004 2078c2ecf20Sopenharmony_ci#define XUSB_DEV_CFG_1_IO_SPACE_EN BIT(0) 2088c2ecf20Sopenharmony_ci#define XUSB_DEV_CFG_1_MEMORY_SPACE_EN BIT(1) 2098c2ecf20Sopenharmony_ci#define XUSB_DEV_CFG_1_BUS_MASTER_EN BIT(2) 2108c2ecf20Sopenharmony_ci#define XUSB_DEV_CFG_4 0x010 2118c2ecf20Sopenharmony_ci#define XUSB_DEV_CFG_4_BASE_ADDR_MASK GENMASK(31, 15) 2128c2ecf20Sopenharmony_ci#define XUSB_DEV_CFG_5 0x014 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/* IPFS registers */ 2158c2ecf20Sopenharmony_ci#define XUSB_DEV_CONFIGURATION_0 0x180 2168c2ecf20Sopenharmony_ci#define XUSB_DEV_CONFIGURATION_0_EN_FPCI BIT(0) 2178c2ecf20Sopenharmony_ci#define XUSB_DEV_INTR_MASK_0 0x188 2188c2ecf20Sopenharmony_ci#define XUSB_DEV_INTR_MASK_0_IP_INT_MASK BIT(16) 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistruct tegra_xudc_ep_context { 2218c2ecf20Sopenharmony_ci __le32 info0; 2228c2ecf20Sopenharmony_ci __le32 info1; 2238c2ecf20Sopenharmony_ci __le32 deq_lo; 2248c2ecf20Sopenharmony_ci __le32 deq_hi; 2258c2ecf20Sopenharmony_ci __le32 tx_info; 2268c2ecf20Sopenharmony_ci __le32 rsvd[11]; 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci#define EP_STATE_DISABLED 0 2308c2ecf20Sopenharmony_ci#define EP_STATE_RUNNING 1 2318c2ecf20Sopenharmony_ci#define EP_STATE_HALTED 2 2328c2ecf20Sopenharmony_ci#define EP_STATE_STOPPED 3 2338c2ecf20Sopenharmony_ci#define EP_STATE_ERROR 4 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci#define EP_TYPE_INVALID 0 2368c2ecf20Sopenharmony_ci#define EP_TYPE_ISOCH_OUT 1 2378c2ecf20Sopenharmony_ci#define EP_TYPE_BULK_OUT 2 2388c2ecf20Sopenharmony_ci#define EP_TYPE_INTERRUPT_OUT 3 2398c2ecf20Sopenharmony_ci#define EP_TYPE_CONTROL 4 2408c2ecf20Sopenharmony_ci#define EP_TYPE_ISCOH_IN 5 2418c2ecf20Sopenharmony_ci#define EP_TYPE_BULK_IN 6 2428c2ecf20Sopenharmony_ci#define EP_TYPE_INTERRUPT_IN 7 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci#define BUILD_EP_CONTEXT_RW(name, member, shift, mask) \ 2458c2ecf20Sopenharmony_cistatic inline u32 ep_ctx_read_##name(struct tegra_xudc_ep_context *ctx) \ 2468c2ecf20Sopenharmony_ci{ \ 2478c2ecf20Sopenharmony_ci return (le32_to_cpu(ctx->member) >> (shift)) & (mask); \ 2488c2ecf20Sopenharmony_ci} \ 2498c2ecf20Sopenharmony_cistatic inline void \ 2508c2ecf20Sopenharmony_ciep_ctx_write_##name(struct tegra_xudc_ep_context *ctx, u32 val) \ 2518c2ecf20Sopenharmony_ci{ \ 2528c2ecf20Sopenharmony_ci u32 tmp; \ 2538c2ecf20Sopenharmony_ci \ 2548c2ecf20Sopenharmony_ci tmp = le32_to_cpu(ctx->member) & ~((mask) << (shift)); \ 2558c2ecf20Sopenharmony_ci tmp |= (val & (mask)) << (shift); \ 2568c2ecf20Sopenharmony_ci ctx->member = cpu_to_le32(tmp); \ 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(state, info0, 0, 0x7) 2608c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(mult, info0, 8, 0x3) 2618c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(max_pstreams, info0, 10, 0x1f) 2628c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(lsa, info0, 15, 0x1) 2638c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(interval, info0, 16, 0xff) 2648c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(cerr, info1, 1, 0x3) 2658c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(type, info1, 3, 0x7) 2668c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(hid, info1, 7, 0x1) 2678c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(max_burst_size, info1, 8, 0xff) 2688c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(max_packet_size, info1, 16, 0xffff) 2698c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(dcs, deq_lo, 0, 0x1) 2708c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(deq_lo, deq_lo, 4, 0xfffffff) 2718c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(deq_hi, deq_hi, 0, 0xffffffff) 2728c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(avg_trb_len, tx_info, 0, 0xffff) 2738c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(max_esit_payload, tx_info, 16, 0xffff) 2748c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(edtla, rsvd[0], 0, 0xffffff) 2758c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(rsvd, rsvd[0], 24, 0x1) 2768c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(partial_td, rsvd[0], 25, 0x1) 2778c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(splitxstate, rsvd[0], 26, 0x1) 2788c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(seq_num, rsvd[0], 27, 0x1f) 2798c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(cerrcnt, rsvd[1], 18, 0x3) 2808c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(data_offset, rsvd[2], 0, 0x1ffff) 2818c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(numtrbs, rsvd[2], 22, 0x1f) 2828c2ecf20Sopenharmony_ciBUILD_EP_CONTEXT_RW(devaddr, rsvd[6], 0, 0x7f) 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic inline u64 ep_ctx_read_deq_ptr(struct tegra_xudc_ep_context *ctx) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci return ((u64)ep_ctx_read_deq_hi(ctx) << 32) | 2878c2ecf20Sopenharmony_ci (ep_ctx_read_deq_lo(ctx) << 4); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic inline void 2918c2ecf20Sopenharmony_ciep_ctx_write_deq_ptr(struct tegra_xudc_ep_context *ctx, u64 addr) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci ep_ctx_write_deq_lo(ctx, lower_32_bits(addr) >> 4); 2948c2ecf20Sopenharmony_ci ep_ctx_write_deq_hi(ctx, upper_32_bits(addr)); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistruct tegra_xudc_trb { 2988c2ecf20Sopenharmony_ci __le32 data_lo; 2998c2ecf20Sopenharmony_ci __le32 data_hi; 3008c2ecf20Sopenharmony_ci __le32 status; 3018c2ecf20Sopenharmony_ci __le32 control; 3028c2ecf20Sopenharmony_ci}; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci#define TRB_TYPE_RSVD 0 3058c2ecf20Sopenharmony_ci#define TRB_TYPE_NORMAL 1 3068c2ecf20Sopenharmony_ci#define TRB_TYPE_SETUP_STAGE 2 3078c2ecf20Sopenharmony_ci#define TRB_TYPE_DATA_STAGE 3 3088c2ecf20Sopenharmony_ci#define TRB_TYPE_STATUS_STAGE 4 3098c2ecf20Sopenharmony_ci#define TRB_TYPE_ISOCH 5 3108c2ecf20Sopenharmony_ci#define TRB_TYPE_LINK 6 3118c2ecf20Sopenharmony_ci#define TRB_TYPE_TRANSFER_EVENT 32 3128c2ecf20Sopenharmony_ci#define TRB_TYPE_PORT_STATUS_CHANGE_EVENT 34 3138c2ecf20Sopenharmony_ci#define TRB_TYPE_STREAM 48 3148c2ecf20Sopenharmony_ci#define TRB_TYPE_SETUP_PACKET_EVENT 63 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_INVALID 0 3178c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_SUCCESS 1 3188c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_DATA_BUFFER_ERR 2 3198c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_BABBLE_DETECTED_ERR 3 3208c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_USB_TRANS_ERR 4 3218c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_TRB_ERR 5 3228c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_STALL 6 3238c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_INVALID_STREAM_TYPE_ERR 10 3248c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_SHORT_PACKET 13 3258c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_RING_UNDERRUN 14 3268c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_RING_OVERRUN 15 3278c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_EVENT_RING_FULL_ERR 21 3288c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_STOPPED 26 3298c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_ISOCH_BUFFER_OVERRUN 31 3308c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_STREAM_NUMP_ERROR 219 3318c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_PRIME_PIPE_RECEIVED 220 3328c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_HOST_REJECTED 221 3338c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_CTRL_DIR_ERR 222 3348c2ecf20Sopenharmony_ci#define TRB_CMPL_CODE_CTRL_SEQNUM_ERR 223 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci#define BUILD_TRB_RW(name, member, shift, mask) \ 3378c2ecf20Sopenharmony_cistatic inline u32 trb_read_##name(struct tegra_xudc_trb *trb) \ 3388c2ecf20Sopenharmony_ci{ \ 3398c2ecf20Sopenharmony_ci return (le32_to_cpu(trb->member) >> (shift)) & (mask); \ 3408c2ecf20Sopenharmony_ci} \ 3418c2ecf20Sopenharmony_cistatic inline void \ 3428c2ecf20Sopenharmony_citrb_write_##name(struct tegra_xudc_trb *trb, u32 val) \ 3438c2ecf20Sopenharmony_ci{ \ 3448c2ecf20Sopenharmony_ci u32 tmp; \ 3458c2ecf20Sopenharmony_ci \ 3468c2ecf20Sopenharmony_ci tmp = le32_to_cpu(trb->member) & ~((mask) << (shift)); \ 3478c2ecf20Sopenharmony_ci tmp |= (val & (mask)) << (shift); \ 3488c2ecf20Sopenharmony_ci trb->member = cpu_to_le32(tmp); \ 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ciBUILD_TRB_RW(data_lo, data_lo, 0, 0xffffffff) 3528c2ecf20Sopenharmony_ciBUILD_TRB_RW(data_hi, data_hi, 0, 0xffffffff) 3538c2ecf20Sopenharmony_ciBUILD_TRB_RW(seq_num, status, 0, 0xffff) 3548c2ecf20Sopenharmony_ciBUILD_TRB_RW(transfer_len, status, 0, 0xffffff) 3558c2ecf20Sopenharmony_ciBUILD_TRB_RW(td_size, status, 17, 0x1f) 3568c2ecf20Sopenharmony_ciBUILD_TRB_RW(cmpl_code, status, 24, 0xff) 3578c2ecf20Sopenharmony_ciBUILD_TRB_RW(cycle, control, 0, 0x1) 3588c2ecf20Sopenharmony_ciBUILD_TRB_RW(toggle_cycle, control, 1, 0x1) 3598c2ecf20Sopenharmony_ciBUILD_TRB_RW(isp, control, 2, 0x1) 3608c2ecf20Sopenharmony_ciBUILD_TRB_RW(chain, control, 4, 0x1) 3618c2ecf20Sopenharmony_ciBUILD_TRB_RW(ioc, control, 5, 0x1) 3628c2ecf20Sopenharmony_ciBUILD_TRB_RW(type, control, 10, 0x3f) 3638c2ecf20Sopenharmony_ciBUILD_TRB_RW(stream_id, control, 16, 0xffff) 3648c2ecf20Sopenharmony_ciBUILD_TRB_RW(endpoint_id, control, 16, 0x1f) 3658c2ecf20Sopenharmony_ciBUILD_TRB_RW(tlbpc, control, 16, 0xf) 3668c2ecf20Sopenharmony_ciBUILD_TRB_RW(data_stage_dir, control, 16, 0x1) 3678c2ecf20Sopenharmony_ciBUILD_TRB_RW(frame_id, control, 20, 0x7ff) 3688c2ecf20Sopenharmony_ciBUILD_TRB_RW(sia, control, 31, 0x1) 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic inline u64 trb_read_data_ptr(struct tegra_xudc_trb *trb) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci return ((u64)trb_read_data_hi(trb) << 32) | 3738c2ecf20Sopenharmony_ci trb_read_data_lo(trb); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic inline void trb_write_data_ptr(struct tegra_xudc_trb *trb, u64 addr) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci trb_write_data_lo(trb, lower_32_bits(addr)); 3798c2ecf20Sopenharmony_ci trb_write_data_hi(trb, upper_32_bits(addr)); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistruct tegra_xudc_request { 3838c2ecf20Sopenharmony_ci struct usb_request usb_req; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci size_t buf_queued; 3868c2ecf20Sopenharmony_ci unsigned int trbs_queued; 3878c2ecf20Sopenharmony_ci unsigned int trbs_needed; 3888c2ecf20Sopenharmony_ci bool need_zlp; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci struct tegra_xudc_trb *first_trb; 3918c2ecf20Sopenharmony_ci struct tegra_xudc_trb *last_trb; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci struct list_head list; 3948c2ecf20Sopenharmony_ci}; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistruct tegra_xudc_ep { 3978c2ecf20Sopenharmony_ci struct tegra_xudc *xudc; 3988c2ecf20Sopenharmony_ci struct usb_ep usb_ep; 3998c2ecf20Sopenharmony_ci unsigned int index; 4008c2ecf20Sopenharmony_ci char name[8]; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci struct tegra_xudc_ep_context *context; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci#define XUDC_TRANSFER_RING_SIZE 64 4058c2ecf20Sopenharmony_ci struct tegra_xudc_trb *transfer_ring; 4068c2ecf20Sopenharmony_ci dma_addr_t transfer_ring_phys; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci unsigned int enq_ptr; 4098c2ecf20Sopenharmony_ci unsigned int deq_ptr; 4108c2ecf20Sopenharmony_ci bool pcs; 4118c2ecf20Sopenharmony_ci bool ring_full; 4128c2ecf20Sopenharmony_ci bool stream_rejected; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci struct list_head queue; 4158c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *desc; 4168c2ecf20Sopenharmony_ci const struct usb_ss_ep_comp_descriptor *comp_desc; 4178c2ecf20Sopenharmony_ci}; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistruct tegra_xudc_sel_timing { 4208c2ecf20Sopenharmony_ci __u8 u1sel; 4218c2ecf20Sopenharmony_ci __u8 u1pel; 4228c2ecf20Sopenharmony_ci __le16 u2sel; 4238c2ecf20Sopenharmony_ci __le16 u2pel; 4248c2ecf20Sopenharmony_ci}; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cienum tegra_xudc_setup_state { 4278c2ecf20Sopenharmony_ci WAIT_FOR_SETUP, 4288c2ecf20Sopenharmony_ci DATA_STAGE_XFER, 4298c2ecf20Sopenharmony_ci DATA_STAGE_RECV, 4308c2ecf20Sopenharmony_ci STATUS_STAGE_XFER, 4318c2ecf20Sopenharmony_ci STATUS_STAGE_RECV, 4328c2ecf20Sopenharmony_ci}; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistruct tegra_xudc_setup_packet { 4358c2ecf20Sopenharmony_ci struct usb_ctrlrequest ctrl_req; 4368c2ecf20Sopenharmony_ci unsigned int seq_num; 4378c2ecf20Sopenharmony_ci}; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistruct tegra_xudc_save_regs { 4408c2ecf20Sopenharmony_ci u32 ctrl; 4418c2ecf20Sopenharmony_ci u32 portpm; 4428c2ecf20Sopenharmony_ci}; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistruct tegra_xudc { 4458c2ecf20Sopenharmony_ci struct device *dev; 4468c2ecf20Sopenharmony_ci const struct tegra_xudc_soc *soc; 4478c2ecf20Sopenharmony_ci struct tegra_xusb_padctl *padctl; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci spinlock_t lock; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci struct usb_gadget gadget; 4528c2ecf20Sopenharmony_ci struct usb_gadget_driver *driver; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci#define XUDC_NR_EVENT_RINGS 2 4558c2ecf20Sopenharmony_ci#define XUDC_EVENT_RING_SIZE 4096 4568c2ecf20Sopenharmony_ci struct tegra_xudc_trb *event_ring[XUDC_NR_EVENT_RINGS]; 4578c2ecf20Sopenharmony_ci dma_addr_t event_ring_phys[XUDC_NR_EVENT_RINGS]; 4588c2ecf20Sopenharmony_ci unsigned int event_ring_index; 4598c2ecf20Sopenharmony_ci unsigned int event_ring_deq_ptr; 4608c2ecf20Sopenharmony_ci bool ccs; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci#define XUDC_NR_EPS 32 4638c2ecf20Sopenharmony_ci struct tegra_xudc_ep ep[XUDC_NR_EPS]; 4648c2ecf20Sopenharmony_ci struct tegra_xudc_ep_context *ep_context; 4658c2ecf20Sopenharmony_ci dma_addr_t ep_context_phys; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci struct device *genpd_dev_device; 4688c2ecf20Sopenharmony_ci struct device *genpd_dev_ss; 4698c2ecf20Sopenharmony_ci struct device_link *genpd_dl_device; 4708c2ecf20Sopenharmony_ci struct device_link *genpd_dl_ss; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci struct dma_pool *transfer_ring_pool; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci bool queued_setup_packet; 4758c2ecf20Sopenharmony_ci struct tegra_xudc_setup_packet setup_packet; 4768c2ecf20Sopenharmony_ci enum tegra_xudc_setup_state setup_state; 4778c2ecf20Sopenharmony_ci u16 setup_seq_num; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci u16 dev_addr; 4808c2ecf20Sopenharmony_ci u16 isoch_delay; 4818c2ecf20Sopenharmony_ci struct tegra_xudc_sel_timing sel_timing; 4828c2ecf20Sopenharmony_ci u8 test_mode_pattern; 4838c2ecf20Sopenharmony_ci u16 status_buf; 4848c2ecf20Sopenharmony_ci struct tegra_xudc_request *ep0_req; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci bool pullup; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci unsigned int nr_enabled_eps; 4898c2ecf20Sopenharmony_ci unsigned int nr_isoch_eps; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci unsigned int device_state; 4928c2ecf20Sopenharmony_ci unsigned int resume_state; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci int irq; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci void __iomem *base; 4978c2ecf20Sopenharmony_ci resource_size_t phys_base; 4988c2ecf20Sopenharmony_ci void __iomem *ipfs; 4998c2ecf20Sopenharmony_ci void __iomem *fpci; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci struct regulator_bulk_data *supplies; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci struct clk_bulk_data *clks; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci bool device_mode; 5068c2ecf20Sopenharmony_ci struct work_struct usb_role_sw_work; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci struct phy **usb3_phy; 5098c2ecf20Sopenharmony_ci struct phy *curr_usb3_phy; 5108c2ecf20Sopenharmony_ci struct phy **utmi_phy; 5118c2ecf20Sopenharmony_ci struct phy *curr_utmi_phy; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci struct tegra_xudc_save_regs saved_regs; 5148c2ecf20Sopenharmony_ci bool suspended; 5158c2ecf20Sopenharmony_ci bool powergated; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci struct usb_phy **usbphy; 5188c2ecf20Sopenharmony_ci struct usb_phy *curr_usbphy; 5198c2ecf20Sopenharmony_ci struct notifier_block vbus_nb; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci struct completion disconnect_complete; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci bool selfpowered; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci#define TOGGLE_VBUS_WAIT_MS 100 5268c2ecf20Sopenharmony_ci struct delayed_work plc_reset_work; 5278c2ecf20Sopenharmony_ci bool wait_csc; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci struct delayed_work port_reset_war_work; 5308c2ecf20Sopenharmony_ci bool wait_for_sec_prc; 5318c2ecf20Sopenharmony_ci}; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci#define XUDC_TRB_MAX_BUFFER_SIZE 65536 5348c2ecf20Sopenharmony_ci#define XUDC_MAX_ISOCH_EPS 4 5358c2ecf20Sopenharmony_ci#define XUDC_INTERRUPT_MODERATION_US 0 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic struct usb_endpoint_descriptor tegra_xudc_ep0_desc = { 5388c2ecf20Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 5398c2ecf20Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 5408c2ecf20Sopenharmony_ci .bEndpointAddress = 0, 5418c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_CONTROL, 5428c2ecf20Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(64), 5438c2ecf20Sopenharmony_ci}; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistruct tegra_xudc_soc { 5468c2ecf20Sopenharmony_ci const char * const *supply_names; 5478c2ecf20Sopenharmony_ci unsigned int num_supplies; 5488c2ecf20Sopenharmony_ci const char * const *clock_names; 5498c2ecf20Sopenharmony_ci unsigned int num_clks; 5508c2ecf20Sopenharmony_ci unsigned int num_phys; 5518c2ecf20Sopenharmony_ci bool u1_enable; 5528c2ecf20Sopenharmony_ci bool u2_enable; 5538c2ecf20Sopenharmony_ci bool lpm_enable; 5548c2ecf20Sopenharmony_ci bool invalid_seq_num; 5558c2ecf20Sopenharmony_ci bool pls_quirk; 5568c2ecf20Sopenharmony_ci bool port_reset_quirk; 5578c2ecf20Sopenharmony_ci bool port_speed_quirk; 5588c2ecf20Sopenharmony_ci bool has_ipfs; 5598c2ecf20Sopenharmony_ci}; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic inline u32 fpci_readl(struct tegra_xudc *xudc, unsigned int offset) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci return readl(xudc->fpci + offset); 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic inline void fpci_writel(struct tegra_xudc *xudc, u32 val, 5678c2ecf20Sopenharmony_ci unsigned int offset) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci writel(val, xudc->fpci + offset); 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic inline u32 ipfs_readl(struct tegra_xudc *xudc, unsigned int offset) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci return readl(xudc->ipfs + offset); 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic inline void ipfs_writel(struct tegra_xudc *xudc, u32 val, 5788c2ecf20Sopenharmony_ci unsigned int offset) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci writel(val, xudc->ipfs + offset); 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic inline u32 xudc_readl(struct tegra_xudc *xudc, unsigned int offset) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci return readl(xudc->base + offset); 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic inline void xudc_writel(struct tegra_xudc *xudc, u32 val, 5898c2ecf20Sopenharmony_ci unsigned int offset) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci writel(val, xudc->base + offset); 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic inline int xudc_readl_poll(struct tegra_xudc *xudc, 5958c2ecf20Sopenharmony_ci unsigned int offset, u32 mask, u32 val) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci u32 regval; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci return readl_poll_timeout_atomic(xudc->base + offset, regval, 6008c2ecf20Sopenharmony_ci (regval & mask) == val, 1, 100); 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic inline struct tegra_xudc *to_xudc(struct usb_gadget *gadget) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci return container_of(gadget, struct tegra_xudc, gadget); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic inline struct tegra_xudc_ep *to_xudc_ep(struct usb_ep *ep) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci return container_of(ep, struct tegra_xudc_ep, usb_ep); 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic inline struct tegra_xudc_request *to_xudc_req(struct usb_request *req) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci return container_of(req, struct tegra_xudc_request, usb_req); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic inline void dump_trb(struct tegra_xudc *xudc, const char *type, 6198c2ecf20Sopenharmony_ci struct tegra_xudc_trb *trb) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, 6228c2ecf20Sopenharmony_ci "%s: %p, lo = %#x, hi = %#x, status = %#x, control = %#x\n", 6238c2ecf20Sopenharmony_ci type, trb, trb->data_lo, trb->data_hi, trb->status, 6248c2ecf20Sopenharmony_ci trb->control); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic void tegra_xudc_limit_port_speed(struct tegra_xudc *xudc) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci u32 val; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci /* limit port speed to gen 1 */ 6328c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT56); 6338c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK); 6348c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(0x260); 6358c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT56); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT57); 6388c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK); 6398c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(0x6D6); 6408c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT57); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT65); 6438c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK); 6448c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(0x4B0); 6458c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT66); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT66); 6488c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK); 6498c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(0x4B0); 6508c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT66); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT67); 6538c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK); 6548c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(0x4B0); 6558c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT67); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT72); 6588c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK); 6598c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(0x10); 6608c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT72); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic void tegra_xudc_restore_port_speed(struct tegra_xudc *xudc) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci u32 val; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* restore port speed to gen2 */ 6688c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT56); 6698c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK); 6708c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(0x438); 6718c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT56); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT57); 6748c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK); 6758c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(0x528); 6768c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT57); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT65); 6798c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK); 6808c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(0xE10); 6818c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT66); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT66); 6848c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK); 6858c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(0x348); 6868c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT66); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT67); 6898c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK); 6908c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(0x5a0); 6918c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT67); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT72); 6948c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK); 6958c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(0x1c21); 6968c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT72); 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic void tegra_xudc_device_mode_on(struct tegra_xudc *xudc) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci int err; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci pm_runtime_get_sync(xudc->dev); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci err = phy_power_on(xudc->curr_utmi_phy); 7068c2ecf20Sopenharmony_ci if (err < 0) 7078c2ecf20Sopenharmony_ci dev_err(xudc->dev, "UTMI power on failed: %d\n", err); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci err = phy_power_on(xudc->curr_usb3_phy); 7108c2ecf20Sopenharmony_ci if (err < 0) 7118c2ecf20Sopenharmony_ci dev_err(xudc->dev, "USB3 PHY power on failed: %d\n", err); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "device mode on\n"); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, 7168c2ecf20Sopenharmony_ci USB_ROLE_DEVICE); 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic void tegra_xudc_device_mode_off(struct tegra_xudc *xudc) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci bool connected = false; 7228c2ecf20Sopenharmony_ci u32 pls, val; 7238c2ecf20Sopenharmony_ci int err; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "device mode off\n"); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci connected = !!(xudc_readl(xudc, PORTSC) & PORTSC_CCS); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci reinit_completion(&xudc->disconnect_complete); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (xudc->soc->port_speed_quirk) 7328c2ecf20Sopenharmony_ci tegra_xudc_restore_port_speed(xudc); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_NONE); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >> 7378c2ecf20Sopenharmony_ci PORTSC_PLS_SHIFT; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* Direct link to U0 if disconnected in RESUME or U2. */ 7408c2ecf20Sopenharmony_ci if (xudc->soc->pls_quirk && xudc->gadget.speed == USB_SPEED_SUPER && 7418c2ecf20Sopenharmony_ci (pls == PORTSC_PLS_RESUME || pls == PORTSC_PLS_U2)) { 7428c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTPM); 7438c2ecf20Sopenharmony_ci val |= PORTPM_FRWE; 7448c2ecf20Sopenharmony_ci xudc_writel(xudc, val, PORTPM); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTSC); 7478c2ecf20Sopenharmony_ci val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK); 7488c2ecf20Sopenharmony_ci val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_U0); 7498c2ecf20Sopenharmony_ci xudc_writel(xudc, val, PORTSC); 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* Wait for disconnect event. */ 7538c2ecf20Sopenharmony_ci if (connected) 7548c2ecf20Sopenharmony_ci wait_for_completion(&xudc->disconnect_complete); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* Make sure interrupt handler has completed before powergating. */ 7578c2ecf20Sopenharmony_ci synchronize_irq(xudc->irq); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci err = phy_power_off(xudc->curr_utmi_phy); 7608c2ecf20Sopenharmony_ci if (err < 0) 7618c2ecf20Sopenharmony_ci dev_err(xudc->dev, "UTMI PHY power off failed: %d\n", err); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci err = phy_power_off(xudc->curr_usb3_phy); 7648c2ecf20Sopenharmony_ci if (err < 0) 7658c2ecf20Sopenharmony_ci dev_err(xudc->dev, "USB3 PHY power off failed: %d\n", err); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci pm_runtime_put(xudc->dev); 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic void tegra_xudc_usb_role_sw_work(struct work_struct *work) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = container_of(work, struct tegra_xudc, 7738c2ecf20Sopenharmony_ci usb_role_sw_work); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (xudc->device_mode) 7768c2ecf20Sopenharmony_ci tegra_xudc_device_mode_on(xudc); 7778c2ecf20Sopenharmony_ci else 7788c2ecf20Sopenharmony_ci tegra_xudc_device_mode_off(xudc); 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic int tegra_xudc_get_phy_index(struct tegra_xudc *xudc, 7828c2ecf20Sopenharmony_ci struct usb_phy *usbphy) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci unsigned int i; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci for (i = 0; i < xudc->soc->num_phys; i++) { 7878c2ecf20Sopenharmony_ci if (xudc->usbphy[i] && usbphy == xudc->usbphy[i]) 7888c2ecf20Sopenharmony_ci return i; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci dev_info(xudc->dev, "phy index could not be found for shared USB PHY"); 7928c2ecf20Sopenharmony_ci return -1; 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_cistatic int tegra_xudc_vbus_notify(struct notifier_block *nb, 7968c2ecf20Sopenharmony_ci unsigned long action, void *data) 7978c2ecf20Sopenharmony_ci{ 7988c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = container_of(nb, struct tegra_xudc, 7998c2ecf20Sopenharmony_ci vbus_nb); 8008c2ecf20Sopenharmony_ci struct usb_phy *usbphy = (struct usb_phy *)data; 8018c2ecf20Sopenharmony_ci int phy_index; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "%s(): event is %d\n", __func__, usbphy->last_event); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if ((xudc->device_mode && usbphy->last_event == USB_EVENT_VBUS) || 8068c2ecf20Sopenharmony_ci (!xudc->device_mode && usbphy->last_event != USB_EVENT_VBUS)) { 8078c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "Same role(%d) received. Ignore", 8088c2ecf20Sopenharmony_ci xudc->device_mode); 8098c2ecf20Sopenharmony_ci return NOTIFY_OK; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci xudc->device_mode = (usbphy->last_event == USB_EVENT_VBUS) ? true : 8138c2ecf20Sopenharmony_ci false; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci phy_index = tegra_xudc_get_phy_index(xudc, usbphy); 8168c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "%s(): current phy index is %d\n", __func__, 8178c2ecf20Sopenharmony_ci phy_index); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci if (!xudc->suspended && phy_index != -1) { 8208c2ecf20Sopenharmony_ci xudc->curr_utmi_phy = xudc->utmi_phy[phy_index]; 8218c2ecf20Sopenharmony_ci xudc->curr_usb3_phy = xudc->usb3_phy[phy_index]; 8228c2ecf20Sopenharmony_ci xudc->curr_usbphy = usbphy; 8238c2ecf20Sopenharmony_ci schedule_work(&xudc->usb_role_sw_work); 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci return NOTIFY_OK; 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_cistatic void tegra_xudc_plc_reset_work(struct work_struct *work) 8308c2ecf20Sopenharmony_ci{ 8318c2ecf20Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 8328c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = container_of(dwork, struct tegra_xudc, 8338c2ecf20Sopenharmony_ci plc_reset_work); 8348c2ecf20Sopenharmony_ci unsigned long flags; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (xudc->wait_csc) { 8398c2ecf20Sopenharmony_ci u32 pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >> 8408c2ecf20Sopenharmony_ci PORTSC_PLS_SHIFT; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (pls == PORTSC_PLS_INACTIVE) { 8438c2ecf20Sopenharmony_ci dev_info(xudc->dev, "PLS = Inactive. Toggle VBUS\n"); 8448c2ecf20Sopenharmony_ci phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, 8458c2ecf20Sopenharmony_ci USB_ROLE_NONE); 8468c2ecf20Sopenharmony_ci phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, 8478c2ecf20Sopenharmony_ci USB_ROLE_DEVICE); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci xudc->wait_csc = false; 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 8548c2ecf20Sopenharmony_ci} 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_cistatic void tegra_xudc_port_reset_war_work(struct work_struct *work) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 8598c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = 8608c2ecf20Sopenharmony_ci container_of(dwork, struct tegra_xudc, port_reset_war_work); 8618c2ecf20Sopenharmony_ci unsigned long flags; 8628c2ecf20Sopenharmony_ci u32 pls; 8638c2ecf20Sopenharmony_ci int ret; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (xudc->device_mode && xudc->wait_for_sec_prc) { 8688c2ecf20Sopenharmony_ci pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >> 8698c2ecf20Sopenharmony_ci PORTSC_PLS_SHIFT; 8708c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "pls = %x\n", pls); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci if (pls == PORTSC_PLS_DISABLED) { 8738c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "toggle vbus\n"); 8748c2ecf20Sopenharmony_ci /* PRC doesn't complete in 100ms, toggle the vbus */ 8758c2ecf20Sopenharmony_ci ret = tegra_phy_xusb_utmi_port_reset( 8768c2ecf20Sopenharmony_ci xudc->curr_utmi_phy); 8778c2ecf20Sopenharmony_ci if (ret == 1) 8788c2ecf20Sopenharmony_ci xudc->wait_for_sec_prc = 0; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic dma_addr_t trb_virt_to_phys(struct tegra_xudc_ep *ep, 8868c2ecf20Sopenharmony_ci struct tegra_xudc_trb *trb) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci unsigned int index; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci index = trb - ep->transfer_ring; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (WARN_ON(index >= XUDC_TRANSFER_RING_SIZE)) 8938c2ecf20Sopenharmony_ci return 0; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci return (ep->transfer_ring_phys + index * sizeof(*trb)); 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic struct tegra_xudc_trb *trb_phys_to_virt(struct tegra_xudc_ep *ep, 8998c2ecf20Sopenharmony_ci dma_addr_t addr) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci struct tegra_xudc_trb *trb; 9028c2ecf20Sopenharmony_ci unsigned int index; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci index = (addr - ep->transfer_ring_phys) / sizeof(*trb); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (WARN_ON(index >= XUDC_TRANSFER_RING_SIZE)) 9078c2ecf20Sopenharmony_ci return NULL; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci trb = &ep->transfer_ring[index]; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci return trb; 9128c2ecf20Sopenharmony_ci} 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_cistatic void ep_reload(struct tegra_xudc *xudc, unsigned int ep) 9158c2ecf20Sopenharmony_ci{ 9168c2ecf20Sopenharmony_ci xudc_writel(xudc, BIT(ep), EP_RELOAD); 9178c2ecf20Sopenharmony_ci xudc_readl_poll(xudc, EP_RELOAD, BIT(ep), 0); 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cistatic void ep_pause(struct tegra_xudc *xudc, unsigned int ep) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci u32 val; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci val = xudc_readl(xudc, EP_PAUSE); 9258c2ecf20Sopenharmony_ci if (val & BIT(ep)) 9268c2ecf20Sopenharmony_ci return; 9278c2ecf20Sopenharmony_ci val |= BIT(ep); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci xudc_writel(xudc, val, EP_PAUSE); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep)); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci xudc_writel(xudc, BIT(ep), EP_STCHG); 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_cistatic void ep_unpause(struct tegra_xudc *xudc, unsigned int ep) 9378c2ecf20Sopenharmony_ci{ 9388c2ecf20Sopenharmony_ci u32 val; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci val = xudc_readl(xudc, EP_PAUSE); 9418c2ecf20Sopenharmony_ci if (!(val & BIT(ep))) 9428c2ecf20Sopenharmony_ci return; 9438c2ecf20Sopenharmony_ci val &= ~BIT(ep); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci xudc_writel(xudc, val, EP_PAUSE); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep)); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci xudc_writel(xudc, BIT(ep), EP_STCHG); 9508c2ecf20Sopenharmony_ci} 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cistatic void ep_unpause_all(struct tegra_xudc *xudc) 9538c2ecf20Sopenharmony_ci{ 9548c2ecf20Sopenharmony_ci u32 val; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci val = xudc_readl(xudc, EP_PAUSE); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci xudc_writel(xudc, 0, EP_PAUSE); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci xudc_readl_poll(xudc, EP_STCHG, val, val); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci xudc_writel(xudc, val, EP_STCHG); 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_cistatic void ep_halt(struct tegra_xudc *xudc, unsigned int ep) 9668c2ecf20Sopenharmony_ci{ 9678c2ecf20Sopenharmony_ci u32 val; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci val = xudc_readl(xudc, EP_HALT); 9708c2ecf20Sopenharmony_ci if (val & BIT(ep)) 9718c2ecf20Sopenharmony_ci return; 9728c2ecf20Sopenharmony_ci val |= BIT(ep); 9738c2ecf20Sopenharmony_ci xudc_writel(xudc, val, EP_HALT); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep)); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci xudc_writel(xudc, BIT(ep), EP_STCHG); 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic void ep_unhalt(struct tegra_xudc *xudc, unsigned int ep) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci u32 val; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci val = xudc_readl(xudc, EP_HALT); 9858c2ecf20Sopenharmony_ci if (!(val & BIT(ep))) 9868c2ecf20Sopenharmony_ci return; 9878c2ecf20Sopenharmony_ci val &= ~BIT(ep); 9888c2ecf20Sopenharmony_ci xudc_writel(xudc, val, EP_HALT); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep)); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci xudc_writel(xudc, BIT(ep), EP_STCHG); 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cistatic void ep_unhalt_all(struct tegra_xudc *xudc) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci u32 val; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci val = xudc_readl(xudc, EP_HALT); 10008c2ecf20Sopenharmony_ci if (!val) 10018c2ecf20Sopenharmony_ci return; 10028c2ecf20Sopenharmony_ci xudc_writel(xudc, 0, EP_HALT); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci xudc_readl_poll(xudc, EP_STCHG, val, val); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci xudc_writel(xudc, val, EP_STCHG); 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_cistatic void ep_wait_for_stopped(struct tegra_xudc *xudc, unsigned int ep) 10108c2ecf20Sopenharmony_ci{ 10118c2ecf20Sopenharmony_ci xudc_readl_poll(xudc, EP_STOPPED, BIT(ep), BIT(ep)); 10128c2ecf20Sopenharmony_ci xudc_writel(xudc, BIT(ep), EP_STOPPED); 10138c2ecf20Sopenharmony_ci} 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_cistatic void ep_wait_for_inactive(struct tegra_xudc *xudc, unsigned int ep) 10168c2ecf20Sopenharmony_ci{ 10178c2ecf20Sopenharmony_ci xudc_readl_poll(xudc, EP_THREAD_ACTIVE, BIT(ep), 0); 10188c2ecf20Sopenharmony_ci} 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_cistatic void tegra_xudc_req_done(struct tegra_xudc_ep *ep, 10218c2ecf20Sopenharmony_ci struct tegra_xudc_request *req, int status) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = ep->xudc; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "completing request %p on EP %u with status %d\n", 10268c2ecf20Sopenharmony_ci req, ep->index, status); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (likely(req->usb_req.status == -EINPROGRESS)) 10298c2ecf20Sopenharmony_ci req->usb_req.status = status; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci list_del_init(&req->list); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_control(ep->desc)) { 10348c2ecf20Sopenharmony_ci usb_gadget_unmap_request(&xudc->gadget, &req->usb_req, 10358c2ecf20Sopenharmony_ci (xudc->setup_state == 10368c2ecf20Sopenharmony_ci DATA_STAGE_XFER)); 10378c2ecf20Sopenharmony_ci } else { 10388c2ecf20Sopenharmony_ci usb_gadget_unmap_request(&xudc->gadget, &req->usb_req, 10398c2ecf20Sopenharmony_ci usb_endpoint_dir_in(ep->desc)); 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci spin_unlock(&xudc->lock); 10438c2ecf20Sopenharmony_ci usb_gadget_giveback_request(&ep->usb_ep, &req->usb_req); 10448c2ecf20Sopenharmony_ci spin_lock(&xudc->lock); 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cistatic void tegra_xudc_ep_nuke(struct tegra_xudc_ep *ep, int status) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci struct tegra_xudc_request *req; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci while (!list_empty(&ep->queue)) { 10528c2ecf20Sopenharmony_ci req = list_first_entry(&ep->queue, struct tegra_xudc_request, 10538c2ecf20Sopenharmony_ci list); 10548c2ecf20Sopenharmony_ci tegra_xudc_req_done(ep, req, status); 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_cistatic unsigned int ep_available_trbs(struct tegra_xudc_ep *ep) 10598c2ecf20Sopenharmony_ci{ 10608c2ecf20Sopenharmony_ci if (ep->ring_full) 10618c2ecf20Sopenharmony_ci return 0; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci if (ep->deq_ptr > ep->enq_ptr) 10648c2ecf20Sopenharmony_ci return ep->deq_ptr - ep->enq_ptr - 1; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci return XUDC_TRANSFER_RING_SIZE - (ep->enq_ptr - ep->deq_ptr) - 2; 10678c2ecf20Sopenharmony_ci} 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_cistatic void tegra_xudc_queue_one_trb(struct tegra_xudc_ep *ep, 10708c2ecf20Sopenharmony_ci struct tegra_xudc_request *req, 10718c2ecf20Sopenharmony_ci struct tegra_xudc_trb *trb, 10728c2ecf20Sopenharmony_ci bool ioc) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = ep->xudc; 10758c2ecf20Sopenharmony_ci dma_addr_t buf_addr; 10768c2ecf20Sopenharmony_ci size_t len; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci len = min_t(size_t, XUDC_TRB_MAX_BUFFER_SIZE, req->usb_req.length - 10798c2ecf20Sopenharmony_ci req->buf_queued); 10808c2ecf20Sopenharmony_ci if (len > 0) 10818c2ecf20Sopenharmony_ci buf_addr = req->usb_req.dma + req->buf_queued; 10828c2ecf20Sopenharmony_ci else 10838c2ecf20Sopenharmony_ci buf_addr = 0; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci trb_write_data_ptr(trb, buf_addr); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci trb_write_transfer_len(trb, len); 10888c2ecf20Sopenharmony_ci trb_write_td_size(trb, req->trbs_needed - req->trbs_queued - 1); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci if (req->trbs_queued == req->trbs_needed - 1 || 10918c2ecf20Sopenharmony_ci (req->need_zlp && req->trbs_queued == req->trbs_needed - 2)) 10928c2ecf20Sopenharmony_ci trb_write_chain(trb, 0); 10938c2ecf20Sopenharmony_ci else 10948c2ecf20Sopenharmony_ci trb_write_chain(trb, 1); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci trb_write_ioc(trb, ioc); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci if (usb_endpoint_dir_out(ep->desc) || 10998c2ecf20Sopenharmony_ci (usb_endpoint_xfer_control(ep->desc) && 11008c2ecf20Sopenharmony_ci (xudc->setup_state == DATA_STAGE_RECV))) 11018c2ecf20Sopenharmony_ci trb_write_isp(trb, 1); 11028c2ecf20Sopenharmony_ci else 11038c2ecf20Sopenharmony_ci trb_write_isp(trb, 0); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_control(ep->desc)) { 11068c2ecf20Sopenharmony_ci if (xudc->setup_state == DATA_STAGE_XFER || 11078c2ecf20Sopenharmony_ci xudc->setup_state == DATA_STAGE_RECV) 11088c2ecf20Sopenharmony_ci trb_write_type(trb, TRB_TYPE_DATA_STAGE); 11098c2ecf20Sopenharmony_ci else 11108c2ecf20Sopenharmony_ci trb_write_type(trb, TRB_TYPE_STATUS_STAGE); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci if (xudc->setup_state == DATA_STAGE_XFER || 11138c2ecf20Sopenharmony_ci xudc->setup_state == STATUS_STAGE_XFER) 11148c2ecf20Sopenharmony_ci trb_write_data_stage_dir(trb, 1); 11158c2ecf20Sopenharmony_ci else 11168c2ecf20Sopenharmony_ci trb_write_data_stage_dir(trb, 0); 11178c2ecf20Sopenharmony_ci } else if (usb_endpoint_xfer_isoc(ep->desc)) { 11188c2ecf20Sopenharmony_ci trb_write_type(trb, TRB_TYPE_ISOCH); 11198c2ecf20Sopenharmony_ci trb_write_sia(trb, 1); 11208c2ecf20Sopenharmony_ci trb_write_frame_id(trb, 0); 11218c2ecf20Sopenharmony_ci trb_write_tlbpc(trb, 0); 11228c2ecf20Sopenharmony_ci } else if (usb_ss_max_streams(ep->comp_desc)) { 11238c2ecf20Sopenharmony_ci trb_write_type(trb, TRB_TYPE_STREAM); 11248c2ecf20Sopenharmony_ci trb_write_stream_id(trb, req->usb_req.stream_id); 11258c2ecf20Sopenharmony_ci } else { 11268c2ecf20Sopenharmony_ci trb_write_type(trb, TRB_TYPE_NORMAL); 11278c2ecf20Sopenharmony_ci trb_write_stream_id(trb, 0); 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci trb_write_cycle(trb, ep->pcs); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci req->trbs_queued++; 11338c2ecf20Sopenharmony_ci req->buf_queued += len; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci dump_trb(xudc, "TRANSFER", trb); 11368c2ecf20Sopenharmony_ci} 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_cistatic unsigned int tegra_xudc_queue_trbs(struct tegra_xudc_ep *ep, 11398c2ecf20Sopenharmony_ci struct tegra_xudc_request *req) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci unsigned int i, count, available; 11428c2ecf20Sopenharmony_ci bool wait_td = false; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci available = ep_available_trbs(ep); 11458c2ecf20Sopenharmony_ci count = req->trbs_needed - req->trbs_queued; 11468c2ecf20Sopenharmony_ci if (available < count) { 11478c2ecf20Sopenharmony_ci count = available; 11488c2ecf20Sopenharmony_ci ep->ring_full = true; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci /* 11528c2ecf20Sopenharmony_ci * To generate zero-length packet on USB bus, SW needs schedule a 11538c2ecf20Sopenharmony_ci * standalone zero-length TD. According to HW's behavior, SW needs 11548c2ecf20Sopenharmony_ci * to schedule TDs in different ways for different endpoint types. 11558c2ecf20Sopenharmony_ci * 11568c2ecf20Sopenharmony_ci * For control endpoint: 11578c2ecf20Sopenharmony_ci * - Data stage TD (IOC = 1, CH = 0) 11588c2ecf20Sopenharmony_ci * - Ring doorbell and wait transfer event 11598c2ecf20Sopenharmony_ci * - Data stage TD for ZLP (IOC = 1, CH = 0) 11608c2ecf20Sopenharmony_ci * - Ring doorbell 11618c2ecf20Sopenharmony_ci * 11628c2ecf20Sopenharmony_ci * For bulk and interrupt endpoints: 11638c2ecf20Sopenharmony_ci * - Normal transfer TD (IOC = 0, CH = 0) 11648c2ecf20Sopenharmony_ci * - Normal transfer TD for ZLP (IOC = 1, CH = 0) 11658c2ecf20Sopenharmony_ci * - Ring doorbell 11668c2ecf20Sopenharmony_ci */ 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci if (req->need_zlp && usb_endpoint_xfer_control(ep->desc) && count > 1) 11698c2ecf20Sopenharmony_ci wait_td = true; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (!req->first_trb) 11728c2ecf20Sopenharmony_ci req->first_trb = &ep->transfer_ring[ep->enq_ptr]; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 11758c2ecf20Sopenharmony_ci struct tegra_xudc_trb *trb = &ep->transfer_ring[ep->enq_ptr]; 11768c2ecf20Sopenharmony_ci bool ioc = false; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci if ((i == count - 1) || (wait_td && i == count - 2)) 11798c2ecf20Sopenharmony_ci ioc = true; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci tegra_xudc_queue_one_trb(ep, req, trb, ioc); 11828c2ecf20Sopenharmony_ci req->last_trb = trb; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci ep->enq_ptr++; 11858c2ecf20Sopenharmony_ci if (ep->enq_ptr == XUDC_TRANSFER_RING_SIZE - 1) { 11868c2ecf20Sopenharmony_ci trb = &ep->transfer_ring[ep->enq_ptr]; 11878c2ecf20Sopenharmony_ci trb_write_cycle(trb, ep->pcs); 11888c2ecf20Sopenharmony_ci ep->pcs = !ep->pcs; 11898c2ecf20Sopenharmony_ci ep->enq_ptr = 0; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci if (ioc) 11938c2ecf20Sopenharmony_ci break; 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci return count; 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic void tegra_xudc_ep_ring_doorbell(struct tegra_xudc_ep *ep) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = ep->xudc; 12028c2ecf20Sopenharmony_ci u32 val; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci if (list_empty(&ep->queue)) 12058c2ecf20Sopenharmony_ci return; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci val = DB_TARGET(ep->index); 12088c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_control(ep->desc)) { 12098c2ecf20Sopenharmony_ci val |= DB_STREAMID(xudc->setup_seq_num); 12108c2ecf20Sopenharmony_ci } else if (usb_ss_max_streams(ep->comp_desc) > 0) { 12118c2ecf20Sopenharmony_ci struct tegra_xudc_request *req; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* Don't ring doorbell if the stream has been rejected. */ 12148c2ecf20Sopenharmony_ci if (ep->stream_rejected) 12158c2ecf20Sopenharmony_ci return; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci req = list_first_entry(&ep->queue, struct tegra_xudc_request, 12188c2ecf20Sopenharmony_ci list); 12198c2ecf20Sopenharmony_ci val |= DB_STREAMID(req->usb_req.stream_id); 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "ring doorbell: %#x\n", val); 12238c2ecf20Sopenharmony_ci xudc_writel(xudc, val, DB); 12248c2ecf20Sopenharmony_ci} 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_cistatic void tegra_xudc_ep_kick_queue(struct tegra_xudc_ep *ep) 12278c2ecf20Sopenharmony_ci{ 12288c2ecf20Sopenharmony_ci struct tegra_xudc_request *req; 12298c2ecf20Sopenharmony_ci bool trbs_queued = false; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci list_for_each_entry(req, &ep->queue, list) { 12328c2ecf20Sopenharmony_ci if (ep->ring_full) 12338c2ecf20Sopenharmony_ci break; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci if (tegra_xudc_queue_trbs(ep, req) > 0) 12368c2ecf20Sopenharmony_ci trbs_queued = true; 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci if (trbs_queued) 12408c2ecf20Sopenharmony_ci tegra_xudc_ep_ring_doorbell(ep); 12418c2ecf20Sopenharmony_ci} 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_cistatic int 12448c2ecf20Sopenharmony_ci__tegra_xudc_ep_queue(struct tegra_xudc_ep *ep, struct tegra_xudc_request *req) 12458c2ecf20Sopenharmony_ci{ 12468c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = ep->xudc; 12478c2ecf20Sopenharmony_ci int err; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_control(ep->desc) && !list_empty(&ep->queue)) { 12508c2ecf20Sopenharmony_ci dev_err(xudc->dev, "control EP has pending transfers\n"); 12518c2ecf20Sopenharmony_ci return -EINVAL; 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_control(ep->desc)) { 12558c2ecf20Sopenharmony_ci err = usb_gadget_map_request(&xudc->gadget, &req->usb_req, 12568c2ecf20Sopenharmony_ci (xudc->setup_state == 12578c2ecf20Sopenharmony_ci DATA_STAGE_XFER)); 12588c2ecf20Sopenharmony_ci } else { 12598c2ecf20Sopenharmony_ci err = usb_gadget_map_request(&xudc->gadget, &req->usb_req, 12608c2ecf20Sopenharmony_ci usb_endpoint_dir_in(ep->desc)); 12618c2ecf20Sopenharmony_ci } 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci if (err < 0) { 12648c2ecf20Sopenharmony_ci dev_err(xudc->dev, "failed to map request: %d\n", err); 12658c2ecf20Sopenharmony_ci return err; 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci req->first_trb = NULL; 12698c2ecf20Sopenharmony_ci req->last_trb = NULL; 12708c2ecf20Sopenharmony_ci req->buf_queued = 0; 12718c2ecf20Sopenharmony_ci req->trbs_queued = 0; 12728c2ecf20Sopenharmony_ci req->need_zlp = false; 12738c2ecf20Sopenharmony_ci req->trbs_needed = DIV_ROUND_UP(req->usb_req.length, 12748c2ecf20Sopenharmony_ci XUDC_TRB_MAX_BUFFER_SIZE); 12758c2ecf20Sopenharmony_ci if (req->usb_req.length == 0) 12768c2ecf20Sopenharmony_ci req->trbs_needed++; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci if (!usb_endpoint_xfer_isoc(ep->desc) && 12798c2ecf20Sopenharmony_ci req->usb_req.zero && req->usb_req.length && 12808c2ecf20Sopenharmony_ci ((req->usb_req.length % ep->usb_ep.maxpacket) == 0)) { 12818c2ecf20Sopenharmony_ci req->trbs_needed++; 12828c2ecf20Sopenharmony_ci req->need_zlp = true; 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci req->usb_req.status = -EINPROGRESS; 12868c2ecf20Sopenharmony_ci req->usb_req.actual = 0; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci list_add_tail(&req->list, &ep->queue); 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci tegra_xudc_ep_kick_queue(ep); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci return 0; 12938c2ecf20Sopenharmony_ci} 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_cistatic int 12968c2ecf20Sopenharmony_citegra_xudc_ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req, 12978c2ecf20Sopenharmony_ci gfp_t gfp) 12988c2ecf20Sopenharmony_ci{ 12998c2ecf20Sopenharmony_ci struct tegra_xudc_request *req; 13008c2ecf20Sopenharmony_ci struct tegra_xudc_ep *ep; 13018c2ecf20Sopenharmony_ci struct tegra_xudc *xudc; 13028c2ecf20Sopenharmony_ci unsigned long flags; 13038c2ecf20Sopenharmony_ci int ret; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci if (!usb_ep || !usb_req) 13068c2ecf20Sopenharmony_ci return -EINVAL; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci ep = to_xudc_ep(usb_ep); 13098c2ecf20Sopenharmony_ci req = to_xudc_req(usb_req); 13108c2ecf20Sopenharmony_ci xudc = ep->xudc; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 13138c2ecf20Sopenharmony_ci if (xudc->powergated || !ep->desc) { 13148c2ecf20Sopenharmony_ci ret = -ESHUTDOWN; 13158c2ecf20Sopenharmony_ci goto unlock; 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci ret = __tegra_xudc_ep_queue(ep, req); 13198c2ecf20Sopenharmony_ciunlock: 13208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci return ret; 13238c2ecf20Sopenharmony_ci} 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_cistatic void squeeze_transfer_ring(struct tegra_xudc_ep *ep, 13268c2ecf20Sopenharmony_ci struct tegra_xudc_request *req) 13278c2ecf20Sopenharmony_ci{ 13288c2ecf20Sopenharmony_ci struct tegra_xudc_trb *trb = req->first_trb; 13298c2ecf20Sopenharmony_ci bool pcs_enq = trb_read_cycle(trb); 13308c2ecf20Sopenharmony_ci bool pcs; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci /* 13338c2ecf20Sopenharmony_ci * Clear out all the TRBs part of or after the cancelled request, 13348c2ecf20Sopenharmony_ci * and must correct trb cycle bit to the last un-enqueued state. 13358c2ecf20Sopenharmony_ci */ 13368c2ecf20Sopenharmony_ci while (trb != &ep->transfer_ring[ep->enq_ptr]) { 13378c2ecf20Sopenharmony_ci pcs = trb_read_cycle(trb); 13388c2ecf20Sopenharmony_ci memset(trb, 0, sizeof(*trb)); 13398c2ecf20Sopenharmony_ci trb_write_cycle(trb, !pcs); 13408c2ecf20Sopenharmony_ci trb++; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci if (trb_read_type(trb) == TRB_TYPE_LINK) 13438c2ecf20Sopenharmony_ci trb = ep->transfer_ring; 13448c2ecf20Sopenharmony_ci } 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci /* Requests will be re-queued at the start of the cancelled request. */ 13478c2ecf20Sopenharmony_ci ep->enq_ptr = req->first_trb - ep->transfer_ring; 13488c2ecf20Sopenharmony_ci /* 13498c2ecf20Sopenharmony_ci * Retrieve the correct cycle bit state from the first trb of 13508c2ecf20Sopenharmony_ci * the cancelled request. 13518c2ecf20Sopenharmony_ci */ 13528c2ecf20Sopenharmony_ci ep->pcs = pcs_enq; 13538c2ecf20Sopenharmony_ci ep->ring_full = false; 13548c2ecf20Sopenharmony_ci list_for_each_entry_continue(req, &ep->queue, list) { 13558c2ecf20Sopenharmony_ci req->usb_req.status = -EINPROGRESS; 13568c2ecf20Sopenharmony_ci req->usb_req.actual = 0; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci req->first_trb = NULL; 13598c2ecf20Sopenharmony_ci req->last_trb = NULL; 13608c2ecf20Sopenharmony_ci req->buf_queued = 0; 13618c2ecf20Sopenharmony_ci req->trbs_queued = 0; 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci} 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci/* 13668c2ecf20Sopenharmony_ci * Determine if the given TRB is in the range [first trb, last trb] for the 13678c2ecf20Sopenharmony_ci * given request. 13688c2ecf20Sopenharmony_ci */ 13698c2ecf20Sopenharmony_cistatic bool trb_in_request(struct tegra_xudc_ep *ep, 13708c2ecf20Sopenharmony_ci struct tegra_xudc_request *req, 13718c2ecf20Sopenharmony_ci struct tegra_xudc_trb *trb) 13728c2ecf20Sopenharmony_ci{ 13738c2ecf20Sopenharmony_ci dev_dbg(ep->xudc->dev, "%s: request %p -> %p; trb %p\n", __func__, 13748c2ecf20Sopenharmony_ci req->first_trb, req->last_trb, trb); 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci if (trb >= req->first_trb && (trb <= req->last_trb || 13778c2ecf20Sopenharmony_ci req->last_trb < req->first_trb)) 13788c2ecf20Sopenharmony_ci return true; 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci if (trb < req->first_trb && trb <= req->last_trb && 13818c2ecf20Sopenharmony_ci req->last_trb < req->first_trb) 13828c2ecf20Sopenharmony_ci return true; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci return false; 13858c2ecf20Sopenharmony_ci} 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci/* 13888c2ecf20Sopenharmony_ci * Determine if the given TRB is in the range [EP enqueue pointer, first TRB) 13898c2ecf20Sopenharmony_ci * for the given endpoint and request. 13908c2ecf20Sopenharmony_ci */ 13918c2ecf20Sopenharmony_cistatic bool trb_before_request(struct tegra_xudc_ep *ep, 13928c2ecf20Sopenharmony_ci struct tegra_xudc_request *req, 13938c2ecf20Sopenharmony_ci struct tegra_xudc_trb *trb) 13948c2ecf20Sopenharmony_ci{ 13958c2ecf20Sopenharmony_ci struct tegra_xudc_trb *enq_trb = &ep->transfer_ring[ep->enq_ptr]; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci dev_dbg(ep->xudc->dev, "%s: request %p -> %p; enq ptr: %p; trb %p\n", 13988c2ecf20Sopenharmony_ci __func__, req->first_trb, req->last_trb, enq_trb, trb); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci if (trb < req->first_trb && (enq_trb <= trb || 14018c2ecf20Sopenharmony_ci req->first_trb < enq_trb)) 14028c2ecf20Sopenharmony_ci return true; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci if (trb > req->first_trb && req->first_trb < enq_trb && enq_trb <= trb) 14058c2ecf20Sopenharmony_ci return true; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci return false; 14088c2ecf20Sopenharmony_ci} 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_cistatic int 14118c2ecf20Sopenharmony_ci__tegra_xudc_ep_dequeue(struct tegra_xudc_ep *ep, 14128c2ecf20Sopenharmony_ci struct tegra_xudc_request *req) 14138c2ecf20Sopenharmony_ci{ 14148c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = ep->xudc; 14158c2ecf20Sopenharmony_ci struct tegra_xudc_request *r; 14168c2ecf20Sopenharmony_ci struct tegra_xudc_trb *deq_trb; 14178c2ecf20Sopenharmony_ci bool busy, kick_queue = false; 14188c2ecf20Sopenharmony_ci int ret = 0; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci /* Make sure the request is actually queued to this endpoint. */ 14218c2ecf20Sopenharmony_ci list_for_each_entry(r, &ep->queue, list) { 14228c2ecf20Sopenharmony_ci if (r == req) 14238c2ecf20Sopenharmony_ci break; 14248c2ecf20Sopenharmony_ci } 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci if (r != req) 14278c2ecf20Sopenharmony_ci return -EINVAL; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci /* Request hasn't been queued in the transfer ring yet. */ 14308c2ecf20Sopenharmony_ci if (!req->trbs_queued) { 14318c2ecf20Sopenharmony_ci tegra_xudc_req_done(ep, req, -ECONNRESET); 14328c2ecf20Sopenharmony_ci return 0; 14338c2ecf20Sopenharmony_ci } 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci /* Halt DMA for this endpiont. */ 14368c2ecf20Sopenharmony_ci if (ep_ctx_read_state(ep->context) == EP_STATE_RUNNING) { 14378c2ecf20Sopenharmony_ci ep_pause(xudc, ep->index); 14388c2ecf20Sopenharmony_ci ep_wait_for_inactive(xudc, ep->index); 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci deq_trb = trb_phys_to_virt(ep, ep_ctx_read_deq_ptr(ep->context)); 14428c2ecf20Sopenharmony_ci /* Is the hardware processing the TRB at the dequeue pointer? */ 14438c2ecf20Sopenharmony_ci busy = (trb_read_cycle(deq_trb) == ep_ctx_read_dcs(ep->context)); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci if (trb_in_request(ep, req, deq_trb) && busy) { 14468c2ecf20Sopenharmony_ci /* 14478c2ecf20Sopenharmony_ci * Request has been partially completed or it hasn't 14488c2ecf20Sopenharmony_ci * started processing yet. 14498c2ecf20Sopenharmony_ci */ 14508c2ecf20Sopenharmony_ci dma_addr_t deq_ptr; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci squeeze_transfer_ring(ep, req); 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci req->usb_req.actual = ep_ctx_read_edtla(ep->context); 14558c2ecf20Sopenharmony_ci tegra_xudc_req_done(ep, req, -ECONNRESET); 14568c2ecf20Sopenharmony_ci kick_queue = true; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci /* EDTLA is > 0: request has been partially completed */ 14598c2ecf20Sopenharmony_ci if (req->usb_req.actual > 0) { 14608c2ecf20Sopenharmony_ci /* 14618c2ecf20Sopenharmony_ci * Abort the pending transfer and update the dequeue 14628c2ecf20Sopenharmony_ci * pointer 14638c2ecf20Sopenharmony_ci */ 14648c2ecf20Sopenharmony_ci ep_ctx_write_edtla(ep->context, 0); 14658c2ecf20Sopenharmony_ci ep_ctx_write_partial_td(ep->context, 0); 14668c2ecf20Sopenharmony_ci ep_ctx_write_data_offset(ep->context, 0); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci deq_ptr = trb_virt_to_phys(ep, 14698c2ecf20Sopenharmony_ci &ep->transfer_ring[ep->enq_ptr]); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (dma_mapping_error(xudc->dev, deq_ptr)) { 14728c2ecf20Sopenharmony_ci ret = -EINVAL; 14738c2ecf20Sopenharmony_ci } else { 14748c2ecf20Sopenharmony_ci ep_ctx_write_deq_ptr(ep->context, deq_ptr); 14758c2ecf20Sopenharmony_ci ep_ctx_write_dcs(ep->context, ep->pcs); 14768c2ecf20Sopenharmony_ci ep_reload(xudc, ep->index); 14778c2ecf20Sopenharmony_ci } 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci } else if (trb_before_request(ep, req, deq_trb) && busy) { 14808c2ecf20Sopenharmony_ci /* Request hasn't started processing yet. */ 14818c2ecf20Sopenharmony_ci squeeze_transfer_ring(ep, req); 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci tegra_xudc_req_done(ep, req, -ECONNRESET); 14848c2ecf20Sopenharmony_ci kick_queue = true; 14858c2ecf20Sopenharmony_ci } else { 14868c2ecf20Sopenharmony_ci /* 14878c2ecf20Sopenharmony_ci * Request has completed, but we haven't processed the 14888c2ecf20Sopenharmony_ci * completion event yet. 14898c2ecf20Sopenharmony_ci */ 14908c2ecf20Sopenharmony_ci tegra_xudc_req_done(ep, req, -ECONNRESET); 14918c2ecf20Sopenharmony_ci ret = -EINVAL; 14928c2ecf20Sopenharmony_ci } 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci /* Resume the endpoint. */ 14958c2ecf20Sopenharmony_ci ep_unpause(xudc, ep->index); 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci if (kick_queue) 14988c2ecf20Sopenharmony_ci tegra_xudc_ep_kick_queue(ep); 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci return ret; 15018c2ecf20Sopenharmony_ci} 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_cistatic int 15048c2ecf20Sopenharmony_citegra_xudc_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) 15058c2ecf20Sopenharmony_ci{ 15068c2ecf20Sopenharmony_ci struct tegra_xudc_request *req; 15078c2ecf20Sopenharmony_ci struct tegra_xudc_ep *ep; 15088c2ecf20Sopenharmony_ci struct tegra_xudc *xudc; 15098c2ecf20Sopenharmony_ci unsigned long flags; 15108c2ecf20Sopenharmony_ci int ret; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci if (!usb_ep || !usb_req) 15138c2ecf20Sopenharmony_ci return -EINVAL; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci ep = to_xudc_ep(usb_ep); 15168c2ecf20Sopenharmony_ci req = to_xudc_req(usb_req); 15178c2ecf20Sopenharmony_ci xudc = ep->xudc; 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci if (xudc->powergated || !ep->desc) { 15228c2ecf20Sopenharmony_ci ret = -ESHUTDOWN; 15238c2ecf20Sopenharmony_ci goto unlock; 15248c2ecf20Sopenharmony_ci } 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci ret = __tegra_xudc_ep_dequeue(ep, req); 15278c2ecf20Sopenharmony_ciunlock: 15288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci return ret; 15318c2ecf20Sopenharmony_ci} 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_cistatic int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt) 15348c2ecf20Sopenharmony_ci{ 15358c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = ep->xudc; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci if (!ep->desc) 15388c2ecf20Sopenharmony_ci return -EINVAL; 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_isoc(ep->desc)) { 15418c2ecf20Sopenharmony_ci dev_err(xudc->dev, "can't halt isochronous EP\n"); 15428c2ecf20Sopenharmony_ci return -ENOTSUPP; 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci if (!!(xudc_readl(xudc, EP_HALT) & BIT(ep->index)) == halt) { 15468c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "EP %u already %s\n", ep->index, 15478c2ecf20Sopenharmony_ci halt ? "halted" : "not halted"); 15488c2ecf20Sopenharmony_ci return 0; 15498c2ecf20Sopenharmony_ci } 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci if (halt) { 15528c2ecf20Sopenharmony_ci ep_halt(xudc, ep->index); 15538c2ecf20Sopenharmony_ci } else { 15548c2ecf20Sopenharmony_ci ep_ctx_write_state(ep->context, EP_STATE_DISABLED); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci ep_reload(xudc, ep->index); 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci ep_ctx_write_state(ep->context, EP_STATE_RUNNING); 15598c2ecf20Sopenharmony_ci ep_ctx_write_rsvd(ep->context, 0); 15608c2ecf20Sopenharmony_ci ep_ctx_write_partial_td(ep->context, 0); 15618c2ecf20Sopenharmony_ci ep_ctx_write_splitxstate(ep->context, 0); 15628c2ecf20Sopenharmony_ci ep_ctx_write_seq_num(ep->context, 0); 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci ep_reload(xudc, ep->index); 15658c2ecf20Sopenharmony_ci ep_unpause(xudc, ep->index); 15668c2ecf20Sopenharmony_ci ep_unhalt(xudc, ep->index); 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci tegra_xudc_ep_ring_doorbell(ep); 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci return 0; 15728c2ecf20Sopenharmony_ci} 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_cistatic int tegra_xudc_ep_set_halt(struct usb_ep *usb_ep, int value) 15758c2ecf20Sopenharmony_ci{ 15768c2ecf20Sopenharmony_ci struct tegra_xudc_ep *ep; 15778c2ecf20Sopenharmony_ci struct tegra_xudc *xudc; 15788c2ecf20Sopenharmony_ci unsigned long flags; 15798c2ecf20Sopenharmony_ci int ret; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci if (!usb_ep) 15828c2ecf20Sopenharmony_ci return -EINVAL; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci ep = to_xudc_ep(usb_ep); 15858c2ecf20Sopenharmony_ci xudc = ep->xudc; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 15888c2ecf20Sopenharmony_ci if (xudc->powergated) { 15898c2ecf20Sopenharmony_ci ret = -ESHUTDOWN; 15908c2ecf20Sopenharmony_ci goto unlock; 15918c2ecf20Sopenharmony_ci } 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci if (value && usb_endpoint_dir_in(ep->desc) && 15948c2ecf20Sopenharmony_ci !list_empty(&ep->queue)) { 15958c2ecf20Sopenharmony_ci dev_err(xudc->dev, "can't halt EP with requests pending\n"); 15968c2ecf20Sopenharmony_ci ret = -EAGAIN; 15978c2ecf20Sopenharmony_ci goto unlock; 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci ret = __tegra_xudc_ep_set_halt(ep, value); 16018c2ecf20Sopenharmony_ciunlock: 16028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci return ret; 16058c2ecf20Sopenharmony_ci} 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_cistatic void tegra_xudc_ep_context_setup(struct tegra_xudc_ep *ep) 16088c2ecf20Sopenharmony_ci{ 16098c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *desc = ep->desc; 16108c2ecf20Sopenharmony_ci const struct usb_ss_ep_comp_descriptor *comp_desc = ep->comp_desc; 16118c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = ep->xudc; 16128c2ecf20Sopenharmony_ci u16 maxpacket, maxburst = 0, esit = 0; 16138c2ecf20Sopenharmony_ci u32 val; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci maxpacket = usb_endpoint_maxp(desc); 16168c2ecf20Sopenharmony_ci if (xudc->gadget.speed == USB_SPEED_SUPER) { 16178c2ecf20Sopenharmony_ci if (!usb_endpoint_xfer_control(desc)) 16188c2ecf20Sopenharmony_ci maxburst = comp_desc->bMaxBurst; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) 16218c2ecf20Sopenharmony_ci esit = le16_to_cpu(comp_desc->wBytesPerInterval); 16228c2ecf20Sopenharmony_ci } else if ((xudc->gadget.speed < USB_SPEED_SUPER) && 16238c2ecf20Sopenharmony_ci (usb_endpoint_xfer_int(desc) || 16248c2ecf20Sopenharmony_ci usb_endpoint_xfer_isoc(desc))) { 16258c2ecf20Sopenharmony_ci if (xudc->gadget.speed == USB_SPEED_HIGH) { 16268c2ecf20Sopenharmony_ci maxburst = usb_endpoint_maxp_mult(desc) - 1; 16278c2ecf20Sopenharmony_ci if (maxburst == 0x3) { 16288c2ecf20Sopenharmony_ci dev_warn(xudc->dev, 16298c2ecf20Sopenharmony_ci "invalid endpoint maxburst\n"); 16308c2ecf20Sopenharmony_ci maxburst = 0x2; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci esit = maxpacket * (maxburst + 1); 16348c2ecf20Sopenharmony_ci } 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci memset(ep->context, 0, sizeof(*ep->context)); 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci ep_ctx_write_state(ep->context, EP_STATE_RUNNING); 16398c2ecf20Sopenharmony_ci ep_ctx_write_interval(ep->context, desc->bInterval); 16408c2ecf20Sopenharmony_ci if (xudc->gadget.speed == USB_SPEED_SUPER) { 16418c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_isoc(desc)) { 16428c2ecf20Sopenharmony_ci ep_ctx_write_mult(ep->context, 16438c2ecf20Sopenharmony_ci comp_desc->bmAttributes & 0x3); 16448c2ecf20Sopenharmony_ci } 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_bulk(desc)) { 16478c2ecf20Sopenharmony_ci ep_ctx_write_max_pstreams(ep->context, 16488c2ecf20Sopenharmony_ci comp_desc->bmAttributes & 16498c2ecf20Sopenharmony_ci 0x1f); 16508c2ecf20Sopenharmony_ci ep_ctx_write_lsa(ep->context, 1); 16518c2ecf20Sopenharmony_ci } 16528c2ecf20Sopenharmony_ci } 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci if (!usb_endpoint_xfer_control(desc) && usb_endpoint_dir_out(desc)) 16558c2ecf20Sopenharmony_ci val = usb_endpoint_type(desc); 16568c2ecf20Sopenharmony_ci else 16578c2ecf20Sopenharmony_ci val = usb_endpoint_type(desc) + EP_TYPE_CONTROL; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci ep_ctx_write_type(ep->context, val); 16608c2ecf20Sopenharmony_ci ep_ctx_write_cerr(ep->context, 0x3); 16618c2ecf20Sopenharmony_ci ep_ctx_write_max_packet_size(ep->context, maxpacket); 16628c2ecf20Sopenharmony_ci ep_ctx_write_max_burst_size(ep->context, maxburst); 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci ep_ctx_write_deq_ptr(ep->context, ep->transfer_ring_phys); 16658c2ecf20Sopenharmony_ci ep_ctx_write_dcs(ep->context, ep->pcs); 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci /* Select a reasonable average TRB length based on endpoint type. */ 16688c2ecf20Sopenharmony_ci switch (usb_endpoint_type(desc)) { 16698c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_CONTROL: 16708c2ecf20Sopenharmony_ci val = 8; 16718c2ecf20Sopenharmony_ci break; 16728c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_INT: 16738c2ecf20Sopenharmony_ci val = 1024; 16748c2ecf20Sopenharmony_ci break; 16758c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_BULK: 16768c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_ISOC: 16778c2ecf20Sopenharmony_ci default: 16788c2ecf20Sopenharmony_ci val = 3072; 16798c2ecf20Sopenharmony_ci break; 16808c2ecf20Sopenharmony_ci } 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci ep_ctx_write_avg_trb_len(ep->context, val); 16838c2ecf20Sopenharmony_ci ep_ctx_write_max_esit_payload(ep->context, esit); 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci ep_ctx_write_cerrcnt(ep->context, 0x3); 16868c2ecf20Sopenharmony_ci} 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_cistatic void setup_link_trb(struct tegra_xudc_ep *ep, 16898c2ecf20Sopenharmony_ci struct tegra_xudc_trb *trb) 16908c2ecf20Sopenharmony_ci{ 16918c2ecf20Sopenharmony_ci trb_write_data_ptr(trb, ep->transfer_ring_phys); 16928c2ecf20Sopenharmony_ci trb_write_type(trb, TRB_TYPE_LINK); 16938c2ecf20Sopenharmony_ci trb_write_toggle_cycle(trb, 1); 16948c2ecf20Sopenharmony_ci} 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_cistatic int __tegra_xudc_ep_disable(struct tegra_xudc_ep *ep) 16978c2ecf20Sopenharmony_ci{ 16988c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = ep->xudc; 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci if (ep_ctx_read_state(ep->context) == EP_STATE_DISABLED) { 17018c2ecf20Sopenharmony_ci dev_err(xudc->dev, "endpoint %u already disabled\n", 17028c2ecf20Sopenharmony_ci ep->index); 17038c2ecf20Sopenharmony_ci return -EINVAL; 17048c2ecf20Sopenharmony_ci } 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci ep_ctx_write_state(ep->context, EP_STATE_DISABLED); 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci ep_reload(xudc, ep->index); 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci tegra_xudc_ep_nuke(ep, -ESHUTDOWN); 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci xudc->nr_enabled_eps--; 17138c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_isoc(ep->desc)) 17148c2ecf20Sopenharmony_ci xudc->nr_isoch_eps--; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci ep->desc = NULL; 17178c2ecf20Sopenharmony_ci ep->comp_desc = NULL; 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci memset(ep->context, 0, sizeof(*ep->context)); 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci ep_unpause(xudc, ep->index); 17228c2ecf20Sopenharmony_ci ep_unhalt(xudc, ep->index); 17238c2ecf20Sopenharmony_ci if (xudc_readl(xudc, EP_STOPPED) & BIT(ep->index)) 17248c2ecf20Sopenharmony_ci xudc_writel(xudc, BIT(ep->index), EP_STOPPED); 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci /* 17278c2ecf20Sopenharmony_ci * If this is the last endpoint disabled in a de-configure request, 17288c2ecf20Sopenharmony_ci * switch back to address state. 17298c2ecf20Sopenharmony_ci */ 17308c2ecf20Sopenharmony_ci if ((xudc->device_state == USB_STATE_CONFIGURED) && 17318c2ecf20Sopenharmony_ci (xudc->nr_enabled_eps == 1)) { 17328c2ecf20Sopenharmony_ci u32 val; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci xudc->device_state = USB_STATE_ADDRESS; 17358c2ecf20Sopenharmony_ci usb_gadget_set_state(&xudc->gadget, xudc->device_state); 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci val = xudc_readl(xudc, CTRL); 17388c2ecf20Sopenharmony_ci val &= ~CTRL_RUN; 17398c2ecf20Sopenharmony_ci xudc_writel(xudc, val, CTRL); 17408c2ecf20Sopenharmony_ci } 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci dev_info(xudc->dev, "ep %u disabled\n", ep->index); 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci return 0; 17458c2ecf20Sopenharmony_ci} 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_cistatic int tegra_xudc_ep_disable(struct usb_ep *usb_ep) 17488c2ecf20Sopenharmony_ci{ 17498c2ecf20Sopenharmony_ci struct tegra_xudc_ep *ep; 17508c2ecf20Sopenharmony_ci struct tegra_xudc *xudc; 17518c2ecf20Sopenharmony_ci unsigned long flags; 17528c2ecf20Sopenharmony_ci int ret; 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci if (!usb_ep) 17558c2ecf20Sopenharmony_ci return -EINVAL; 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci ep = to_xudc_ep(usb_ep); 17588c2ecf20Sopenharmony_ci xudc = ep->xudc; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 17618c2ecf20Sopenharmony_ci if (xudc->powergated) { 17628c2ecf20Sopenharmony_ci ret = -ESHUTDOWN; 17638c2ecf20Sopenharmony_ci goto unlock; 17648c2ecf20Sopenharmony_ci } 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci ret = __tegra_xudc_ep_disable(ep); 17678c2ecf20Sopenharmony_ciunlock: 17688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci return ret; 17718c2ecf20Sopenharmony_ci} 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_cistatic int __tegra_xudc_ep_enable(struct tegra_xudc_ep *ep, 17748c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *desc) 17758c2ecf20Sopenharmony_ci{ 17768c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = ep->xudc; 17778c2ecf20Sopenharmony_ci unsigned int i; 17788c2ecf20Sopenharmony_ci u32 val; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci if (xudc->gadget.speed == USB_SPEED_SUPER && 17818c2ecf20Sopenharmony_ci !usb_endpoint_xfer_control(desc) && !ep->usb_ep.comp_desc) 17828c2ecf20Sopenharmony_ci return -EINVAL; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci /* Disable the EP if it is not disabled */ 17858c2ecf20Sopenharmony_ci if (ep_ctx_read_state(ep->context) != EP_STATE_DISABLED) 17868c2ecf20Sopenharmony_ci __tegra_xudc_ep_disable(ep); 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci ep->desc = desc; 17898c2ecf20Sopenharmony_ci ep->comp_desc = ep->usb_ep.comp_desc; 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_isoc(desc)) { 17928c2ecf20Sopenharmony_ci if (xudc->nr_isoch_eps > XUDC_MAX_ISOCH_EPS) { 17938c2ecf20Sopenharmony_ci dev_err(xudc->dev, "too many isochronous endpoints\n"); 17948c2ecf20Sopenharmony_ci return -EBUSY; 17958c2ecf20Sopenharmony_ci } 17968c2ecf20Sopenharmony_ci xudc->nr_isoch_eps++; 17978c2ecf20Sopenharmony_ci } 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci memset(ep->transfer_ring, 0, XUDC_TRANSFER_RING_SIZE * 18008c2ecf20Sopenharmony_ci sizeof(*ep->transfer_ring)); 18018c2ecf20Sopenharmony_ci setup_link_trb(ep, &ep->transfer_ring[XUDC_TRANSFER_RING_SIZE - 1]); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci ep->enq_ptr = 0; 18048c2ecf20Sopenharmony_ci ep->deq_ptr = 0; 18058c2ecf20Sopenharmony_ci ep->pcs = true; 18068c2ecf20Sopenharmony_ci ep->ring_full = false; 18078c2ecf20Sopenharmony_ci xudc->nr_enabled_eps++; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci tegra_xudc_ep_context_setup(ep); 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci /* 18128c2ecf20Sopenharmony_ci * No need to reload and un-halt EP0. This will be done automatically 18138c2ecf20Sopenharmony_ci * once a valid SETUP packet is received. 18148c2ecf20Sopenharmony_ci */ 18158c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_control(desc)) 18168c2ecf20Sopenharmony_ci goto out; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci /* 18198c2ecf20Sopenharmony_ci * Transition to configured state once the first non-control 18208c2ecf20Sopenharmony_ci * endpoint is enabled. 18218c2ecf20Sopenharmony_ci */ 18228c2ecf20Sopenharmony_ci if (xudc->device_state == USB_STATE_ADDRESS) { 18238c2ecf20Sopenharmony_ci val = xudc_readl(xudc, CTRL); 18248c2ecf20Sopenharmony_ci val |= CTRL_RUN; 18258c2ecf20Sopenharmony_ci xudc_writel(xudc, val, CTRL); 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci xudc->device_state = USB_STATE_CONFIGURED; 18288c2ecf20Sopenharmony_ci usb_gadget_set_state(&xudc->gadget, xudc->device_state); 18298c2ecf20Sopenharmony_ci } 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_isoc(desc)) { 18328c2ecf20Sopenharmony_ci /* 18338c2ecf20Sopenharmony_ci * Pause all bulk endpoints when enabling an isoch endpoint 18348c2ecf20Sopenharmony_ci * to ensure the isoch endpoint is allocated enough bandwidth. 18358c2ecf20Sopenharmony_ci */ 18368c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) { 18378c2ecf20Sopenharmony_ci if (xudc->ep[i].desc && 18388c2ecf20Sopenharmony_ci usb_endpoint_xfer_bulk(xudc->ep[i].desc)) 18398c2ecf20Sopenharmony_ci ep_pause(xudc, i); 18408c2ecf20Sopenharmony_ci } 18418c2ecf20Sopenharmony_ci } 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci ep_reload(xudc, ep->index); 18448c2ecf20Sopenharmony_ci ep_unpause(xudc, ep->index); 18458c2ecf20Sopenharmony_ci ep_unhalt(xudc, ep->index); 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_isoc(desc)) { 18488c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) { 18498c2ecf20Sopenharmony_ci if (xudc->ep[i].desc && 18508c2ecf20Sopenharmony_ci usb_endpoint_xfer_bulk(xudc->ep[i].desc)) 18518c2ecf20Sopenharmony_ci ep_unpause(xudc, i); 18528c2ecf20Sopenharmony_ci } 18538c2ecf20Sopenharmony_ci } 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ciout: 18568c2ecf20Sopenharmony_ci dev_info(xudc->dev, "EP %u (type: %s, dir: %s) enabled\n", ep->index, 18578c2ecf20Sopenharmony_ci usb_ep_type_string(usb_endpoint_type(ep->desc)), 18588c2ecf20Sopenharmony_ci usb_endpoint_dir_in(ep->desc) ? "in" : "out"); 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci return 0; 18618c2ecf20Sopenharmony_ci} 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_cistatic int tegra_xudc_ep_enable(struct usb_ep *usb_ep, 18648c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *desc) 18658c2ecf20Sopenharmony_ci{ 18668c2ecf20Sopenharmony_ci struct tegra_xudc_ep *ep; 18678c2ecf20Sopenharmony_ci struct tegra_xudc *xudc; 18688c2ecf20Sopenharmony_ci unsigned long flags; 18698c2ecf20Sopenharmony_ci int ret; 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci if (!usb_ep || !desc || (desc->bDescriptorType != USB_DT_ENDPOINT)) 18728c2ecf20Sopenharmony_ci return -EINVAL; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci ep = to_xudc_ep(usb_ep); 18758c2ecf20Sopenharmony_ci xudc = ep->xudc; 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 18788c2ecf20Sopenharmony_ci if (xudc->powergated) { 18798c2ecf20Sopenharmony_ci ret = -ESHUTDOWN; 18808c2ecf20Sopenharmony_ci goto unlock; 18818c2ecf20Sopenharmony_ci } 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci ret = __tegra_xudc_ep_enable(ep, desc); 18848c2ecf20Sopenharmony_ciunlock: 18858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci return ret; 18888c2ecf20Sopenharmony_ci} 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_cistatic struct usb_request * 18918c2ecf20Sopenharmony_citegra_xudc_ep_alloc_request(struct usb_ep *usb_ep, gfp_t gfp) 18928c2ecf20Sopenharmony_ci{ 18938c2ecf20Sopenharmony_ci struct tegra_xudc_request *req; 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci req = kzalloc(sizeof(*req), gfp); 18968c2ecf20Sopenharmony_ci if (!req) 18978c2ecf20Sopenharmony_ci return NULL; 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&req->list); 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci return &req->usb_req; 19028c2ecf20Sopenharmony_ci} 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_cistatic void tegra_xudc_ep_free_request(struct usb_ep *usb_ep, 19058c2ecf20Sopenharmony_ci struct usb_request *usb_req) 19068c2ecf20Sopenharmony_ci{ 19078c2ecf20Sopenharmony_ci struct tegra_xudc_request *req = to_xudc_req(usb_req); 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci kfree(req); 19108c2ecf20Sopenharmony_ci} 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_cistatic struct usb_ep_ops tegra_xudc_ep_ops = { 19138c2ecf20Sopenharmony_ci .enable = tegra_xudc_ep_enable, 19148c2ecf20Sopenharmony_ci .disable = tegra_xudc_ep_disable, 19158c2ecf20Sopenharmony_ci .alloc_request = tegra_xudc_ep_alloc_request, 19168c2ecf20Sopenharmony_ci .free_request = tegra_xudc_ep_free_request, 19178c2ecf20Sopenharmony_ci .queue = tegra_xudc_ep_queue, 19188c2ecf20Sopenharmony_ci .dequeue = tegra_xudc_ep_dequeue, 19198c2ecf20Sopenharmony_ci .set_halt = tegra_xudc_ep_set_halt, 19208c2ecf20Sopenharmony_ci}; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_cistatic int tegra_xudc_ep0_enable(struct usb_ep *usb_ep, 19238c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *desc) 19248c2ecf20Sopenharmony_ci{ 19258c2ecf20Sopenharmony_ci return -EBUSY; 19268c2ecf20Sopenharmony_ci} 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_cistatic int tegra_xudc_ep0_disable(struct usb_ep *usb_ep) 19298c2ecf20Sopenharmony_ci{ 19308c2ecf20Sopenharmony_ci return -EBUSY; 19318c2ecf20Sopenharmony_ci} 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_cistatic struct usb_ep_ops tegra_xudc_ep0_ops = { 19348c2ecf20Sopenharmony_ci .enable = tegra_xudc_ep0_enable, 19358c2ecf20Sopenharmony_ci .disable = tegra_xudc_ep0_disable, 19368c2ecf20Sopenharmony_ci .alloc_request = tegra_xudc_ep_alloc_request, 19378c2ecf20Sopenharmony_ci .free_request = tegra_xudc_ep_free_request, 19388c2ecf20Sopenharmony_ci .queue = tegra_xudc_ep_queue, 19398c2ecf20Sopenharmony_ci .dequeue = tegra_xudc_ep_dequeue, 19408c2ecf20Sopenharmony_ci .set_halt = tegra_xudc_ep_set_halt, 19418c2ecf20Sopenharmony_ci}; 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_cistatic int tegra_xudc_gadget_get_frame(struct usb_gadget *gadget) 19448c2ecf20Sopenharmony_ci{ 19458c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = to_xudc(gadget); 19468c2ecf20Sopenharmony_ci unsigned long flags; 19478c2ecf20Sopenharmony_ci int ret; 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 19508c2ecf20Sopenharmony_ci if (xudc->powergated) { 19518c2ecf20Sopenharmony_ci ret = -ESHUTDOWN; 19528c2ecf20Sopenharmony_ci goto unlock; 19538c2ecf20Sopenharmony_ci } 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci ret = (xudc_readl(xudc, MFINDEX) & MFINDEX_FRAME_MASK) >> 19568c2ecf20Sopenharmony_ci MFINDEX_FRAME_SHIFT; 19578c2ecf20Sopenharmony_ciunlock: 19588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci return ret; 19618c2ecf20Sopenharmony_ci} 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_cistatic void tegra_xudc_resume_device_state(struct tegra_xudc *xudc) 19648c2ecf20Sopenharmony_ci{ 19658c2ecf20Sopenharmony_ci unsigned int i; 19668c2ecf20Sopenharmony_ci u32 val; 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci ep_unpause_all(xudc); 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci /* Direct link to U0. */ 19718c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTSC); 19728c2ecf20Sopenharmony_ci if (((val & PORTSC_PLS_MASK) >> PORTSC_PLS_SHIFT) != PORTSC_PLS_U0) { 19738c2ecf20Sopenharmony_ci val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK); 19748c2ecf20Sopenharmony_ci val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_U0); 19758c2ecf20Sopenharmony_ci xudc_writel(xudc, val, PORTSC); 19768c2ecf20Sopenharmony_ci } 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci if (xudc->device_state == USB_STATE_SUSPENDED) { 19798c2ecf20Sopenharmony_ci xudc->device_state = xudc->resume_state; 19808c2ecf20Sopenharmony_ci usb_gadget_set_state(&xudc->gadget, xudc->device_state); 19818c2ecf20Sopenharmony_ci xudc->resume_state = 0; 19828c2ecf20Sopenharmony_ci } 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci /* 19858c2ecf20Sopenharmony_ci * Doorbells may be dropped if they are sent too soon (< ~200ns) 19868c2ecf20Sopenharmony_ci * after unpausing the endpoint. Wait for 500ns just to be safe. 19878c2ecf20Sopenharmony_ci */ 19888c2ecf20Sopenharmony_ci ndelay(500); 19898c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) 19908c2ecf20Sopenharmony_ci tegra_xudc_ep_ring_doorbell(&xudc->ep[i]); 19918c2ecf20Sopenharmony_ci} 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_cistatic int tegra_xudc_gadget_wakeup(struct usb_gadget *gadget) 19948c2ecf20Sopenharmony_ci{ 19958c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = to_xudc(gadget); 19968c2ecf20Sopenharmony_ci unsigned long flags; 19978c2ecf20Sopenharmony_ci int ret = 0; 19988c2ecf20Sopenharmony_ci u32 val; 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci if (xudc->powergated) { 20038c2ecf20Sopenharmony_ci ret = -ESHUTDOWN; 20048c2ecf20Sopenharmony_ci goto unlock; 20058c2ecf20Sopenharmony_ci } 20068c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTPM); 20078c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "%s: PORTPM=%#x, speed=%x\n", __func__, 20088c2ecf20Sopenharmony_ci val, gadget->speed); 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci if (((xudc->gadget.speed <= USB_SPEED_HIGH) && 20118c2ecf20Sopenharmony_ci (val & PORTPM_RWE)) || 20128c2ecf20Sopenharmony_ci ((xudc->gadget.speed == USB_SPEED_SUPER) && 20138c2ecf20Sopenharmony_ci (val & PORTPM_FRWE))) { 20148c2ecf20Sopenharmony_ci tegra_xudc_resume_device_state(xudc); 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci /* Send Device Notification packet. */ 20178c2ecf20Sopenharmony_ci if (xudc->gadget.speed == USB_SPEED_SUPER) { 20188c2ecf20Sopenharmony_ci val = DEVNOTIF_LO_TYPE(DEVNOTIF_LO_TYPE_FUNCTION_WAKE) 20198c2ecf20Sopenharmony_ci | DEVNOTIF_LO_TRIG; 20208c2ecf20Sopenharmony_ci xudc_writel(xudc, 0, DEVNOTIF_HI); 20218c2ecf20Sopenharmony_ci xudc_writel(xudc, val, DEVNOTIF_LO); 20228c2ecf20Sopenharmony_ci } 20238c2ecf20Sopenharmony_ci } 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ciunlock: 20268c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "%s: ret value is %d", __func__, ret); 20278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci return ret; 20308c2ecf20Sopenharmony_ci} 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_cistatic int tegra_xudc_gadget_pullup(struct usb_gadget *gadget, int is_on) 20338c2ecf20Sopenharmony_ci{ 20348c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = to_xudc(gadget); 20358c2ecf20Sopenharmony_ci unsigned long flags; 20368c2ecf20Sopenharmony_ci u32 val; 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci pm_runtime_get_sync(xudc->dev); 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci if (is_on != xudc->pullup) { 20438c2ecf20Sopenharmony_ci val = xudc_readl(xudc, CTRL); 20448c2ecf20Sopenharmony_ci if (is_on) 20458c2ecf20Sopenharmony_ci val |= CTRL_ENABLE; 20468c2ecf20Sopenharmony_ci else 20478c2ecf20Sopenharmony_ci val &= ~CTRL_ENABLE; 20488c2ecf20Sopenharmony_ci xudc_writel(xudc, val, CTRL); 20498c2ecf20Sopenharmony_ci } 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci xudc->pullup = is_on; 20528c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "%s: pullup:%d", __func__, is_on); 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci pm_runtime_put(xudc->dev); 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci return 0; 20598c2ecf20Sopenharmony_ci} 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_cistatic int tegra_xudc_gadget_start(struct usb_gadget *gadget, 20628c2ecf20Sopenharmony_ci struct usb_gadget_driver *driver) 20638c2ecf20Sopenharmony_ci{ 20648c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = to_xudc(gadget); 20658c2ecf20Sopenharmony_ci unsigned long flags; 20668c2ecf20Sopenharmony_ci u32 val; 20678c2ecf20Sopenharmony_ci int ret; 20688c2ecf20Sopenharmony_ci unsigned int i; 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci if (!driver) 20718c2ecf20Sopenharmony_ci return -EINVAL; 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci pm_runtime_get_sync(xudc->dev); 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci if (xudc->driver) { 20788c2ecf20Sopenharmony_ci ret = -EBUSY; 20798c2ecf20Sopenharmony_ci goto unlock; 20808c2ecf20Sopenharmony_ci } 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci xudc->setup_state = WAIT_FOR_SETUP; 20838c2ecf20Sopenharmony_ci xudc->device_state = USB_STATE_DEFAULT; 20848c2ecf20Sopenharmony_ci usb_gadget_set_state(&xudc->gadget, xudc->device_state); 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci ret = __tegra_xudc_ep_enable(&xudc->ep[0], &tegra_xudc_ep0_desc); 20878c2ecf20Sopenharmony_ci if (ret < 0) 20888c2ecf20Sopenharmony_ci goto unlock; 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci val = xudc_readl(xudc, CTRL); 20918c2ecf20Sopenharmony_ci val |= CTRL_IE | CTRL_LSE; 20928c2ecf20Sopenharmony_ci xudc_writel(xudc, val, CTRL); 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTHALT); 20958c2ecf20Sopenharmony_ci val |= PORTHALT_STCHG_INTR_EN; 20968c2ecf20Sopenharmony_ci xudc_writel(xudc, val, PORTHALT); 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci if (xudc->pullup) { 20998c2ecf20Sopenharmony_ci val = xudc_readl(xudc, CTRL); 21008c2ecf20Sopenharmony_ci val |= CTRL_ENABLE; 21018c2ecf20Sopenharmony_ci xudc_writel(xudc, val, CTRL); 21028c2ecf20Sopenharmony_ci } 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci for (i = 0; i < xudc->soc->num_phys; i++) 21058c2ecf20Sopenharmony_ci if (xudc->usbphy[i]) 21068c2ecf20Sopenharmony_ci otg_set_peripheral(xudc->usbphy[i]->otg, gadget); 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci xudc->driver = driver; 21098c2ecf20Sopenharmony_ciunlock: 21108c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "%s: ret value is %d", __func__, ret); 21118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci pm_runtime_put(xudc->dev); 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci return ret; 21168c2ecf20Sopenharmony_ci} 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_cistatic int tegra_xudc_gadget_stop(struct usb_gadget *gadget) 21198c2ecf20Sopenharmony_ci{ 21208c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = to_xudc(gadget); 21218c2ecf20Sopenharmony_ci unsigned long flags; 21228c2ecf20Sopenharmony_ci u32 val; 21238c2ecf20Sopenharmony_ci unsigned int i; 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci pm_runtime_get_sync(xudc->dev); 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci for (i = 0; i < xudc->soc->num_phys; i++) 21308c2ecf20Sopenharmony_ci if (xudc->usbphy[i]) 21318c2ecf20Sopenharmony_ci otg_set_peripheral(xudc->usbphy[i]->otg, NULL); 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci val = xudc_readl(xudc, CTRL); 21348c2ecf20Sopenharmony_ci val &= ~(CTRL_IE | CTRL_ENABLE); 21358c2ecf20Sopenharmony_ci xudc_writel(xudc, val, CTRL); 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci __tegra_xudc_ep_disable(&xudc->ep[0]); 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci xudc->driver = NULL; 21408c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "Gadget stopped"); 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci pm_runtime_put(xudc->dev); 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci return 0; 21478c2ecf20Sopenharmony_ci} 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_cistatic int tegra_xudc_gadget_vbus_draw(struct usb_gadget *gadget, 21508c2ecf20Sopenharmony_ci unsigned int m_a) 21518c2ecf20Sopenharmony_ci{ 21528c2ecf20Sopenharmony_ci int ret = 0; 21538c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = to_xudc(gadget); 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "%s: %u mA\n", __func__, m_a); 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci if (xudc->curr_usbphy && xudc->curr_usbphy->chg_type == SDP_TYPE) 21588c2ecf20Sopenharmony_ci ret = usb_phy_set_power(xudc->curr_usbphy, m_a); 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci return ret; 21618c2ecf20Sopenharmony_ci} 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_cistatic int tegra_xudc_set_selfpowered(struct usb_gadget *gadget, int is_on) 21648c2ecf20Sopenharmony_ci{ 21658c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = to_xudc(gadget); 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "%s: %d\n", __func__, is_on); 21688c2ecf20Sopenharmony_ci xudc->selfpowered = !!is_on; 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci return 0; 21718c2ecf20Sopenharmony_ci} 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_cistatic struct usb_gadget_ops tegra_xudc_gadget_ops = { 21748c2ecf20Sopenharmony_ci .get_frame = tegra_xudc_gadget_get_frame, 21758c2ecf20Sopenharmony_ci .wakeup = tegra_xudc_gadget_wakeup, 21768c2ecf20Sopenharmony_ci .pullup = tegra_xudc_gadget_pullup, 21778c2ecf20Sopenharmony_ci .udc_start = tegra_xudc_gadget_start, 21788c2ecf20Sopenharmony_ci .udc_stop = tegra_xudc_gadget_stop, 21798c2ecf20Sopenharmony_ci .vbus_draw = tegra_xudc_gadget_vbus_draw, 21808c2ecf20Sopenharmony_ci .set_selfpowered = tegra_xudc_set_selfpowered, 21818c2ecf20Sopenharmony_ci}; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_cistatic void no_op_complete(struct usb_ep *ep, struct usb_request *req) 21848c2ecf20Sopenharmony_ci{ 21858c2ecf20Sopenharmony_ci} 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_cistatic int 21888c2ecf20Sopenharmony_citegra_xudc_ep0_queue_status(struct tegra_xudc *xudc, 21898c2ecf20Sopenharmony_ci void (*cmpl)(struct usb_ep *, struct usb_request *)) 21908c2ecf20Sopenharmony_ci{ 21918c2ecf20Sopenharmony_ci xudc->ep0_req->usb_req.buf = NULL; 21928c2ecf20Sopenharmony_ci xudc->ep0_req->usb_req.dma = 0; 21938c2ecf20Sopenharmony_ci xudc->ep0_req->usb_req.length = 0; 21948c2ecf20Sopenharmony_ci xudc->ep0_req->usb_req.complete = cmpl; 21958c2ecf20Sopenharmony_ci xudc->ep0_req->usb_req.context = xudc; 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci return __tegra_xudc_ep_queue(&xudc->ep[0], xudc->ep0_req); 21988c2ecf20Sopenharmony_ci} 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_cistatic int 22018c2ecf20Sopenharmony_citegra_xudc_ep0_queue_data(struct tegra_xudc *xudc, void *buf, size_t len, 22028c2ecf20Sopenharmony_ci void (*cmpl)(struct usb_ep *, struct usb_request *)) 22038c2ecf20Sopenharmony_ci{ 22048c2ecf20Sopenharmony_ci xudc->ep0_req->usb_req.buf = buf; 22058c2ecf20Sopenharmony_ci xudc->ep0_req->usb_req.length = len; 22068c2ecf20Sopenharmony_ci xudc->ep0_req->usb_req.complete = cmpl; 22078c2ecf20Sopenharmony_ci xudc->ep0_req->usb_req.context = xudc; 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci return __tegra_xudc_ep_queue(&xudc->ep[0], xudc->ep0_req); 22108c2ecf20Sopenharmony_ci} 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_cistatic void tegra_xudc_ep0_req_done(struct tegra_xudc *xudc) 22138c2ecf20Sopenharmony_ci{ 22148c2ecf20Sopenharmony_ci switch (xudc->setup_state) { 22158c2ecf20Sopenharmony_ci case DATA_STAGE_XFER: 22168c2ecf20Sopenharmony_ci xudc->setup_state = STATUS_STAGE_RECV; 22178c2ecf20Sopenharmony_ci tegra_xudc_ep0_queue_status(xudc, no_op_complete); 22188c2ecf20Sopenharmony_ci break; 22198c2ecf20Sopenharmony_ci case DATA_STAGE_RECV: 22208c2ecf20Sopenharmony_ci xudc->setup_state = STATUS_STAGE_XFER; 22218c2ecf20Sopenharmony_ci tegra_xudc_ep0_queue_status(xudc, no_op_complete); 22228c2ecf20Sopenharmony_ci break; 22238c2ecf20Sopenharmony_ci default: 22248c2ecf20Sopenharmony_ci xudc->setup_state = WAIT_FOR_SETUP; 22258c2ecf20Sopenharmony_ci break; 22268c2ecf20Sopenharmony_ci } 22278c2ecf20Sopenharmony_ci} 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_cistatic int tegra_xudc_ep0_delegate_req(struct tegra_xudc *xudc, 22308c2ecf20Sopenharmony_ci struct usb_ctrlrequest *ctrl) 22318c2ecf20Sopenharmony_ci{ 22328c2ecf20Sopenharmony_ci int ret; 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci spin_unlock(&xudc->lock); 22358c2ecf20Sopenharmony_ci ret = xudc->driver->setup(&xudc->gadget, ctrl); 22368c2ecf20Sopenharmony_ci spin_lock(&xudc->lock); 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci return ret; 22398c2ecf20Sopenharmony_ci} 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_cistatic void set_feature_complete(struct usb_ep *ep, struct usb_request *req) 22428c2ecf20Sopenharmony_ci{ 22438c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = req->context; 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci if (xudc->test_mode_pattern) { 22468c2ecf20Sopenharmony_ci xudc_writel(xudc, xudc->test_mode_pattern, PORT_TM); 22478c2ecf20Sopenharmony_ci xudc->test_mode_pattern = 0; 22488c2ecf20Sopenharmony_ci } 22498c2ecf20Sopenharmony_ci} 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_cistatic int tegra_xudc_ep0_set_feature(struct tegra_xudc *xudc, 22528c2ecf20Sopenharmony_ci struct usb_ctrlrequest *ctrl) 22538c2ecf20Sopenharmony_ci{ 22548c2ecf20Sopenharmony_ci bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); 22558c2ecf20Sopenharmony_ci u32 feature = le16_to_cpu(ctrl->wValue); 22568c2ecf20Sopenharmony_ci u32 index = le16_to_cpu(ctrl->wIndex); 22578c2ecf20Sopenharmony_ci u32 val, ep; 22588c2ecf20Sopenharmony_ci int ret; 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_ci if (le16_to_cpu(ctrl->wLength) != 0) 22618c2ecf20Sopenharmony_ci return -EINVAL; 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci switch (ctrl->bRequestType & USB_RECIP_MASK) { 22648c2ecf20Sopenharmony_ci case USB_RECIP_DEVICE: 22658c2ecf20Sopenharmony_ci switch (feature) { 22668c2ecf20Sopenharmony_ci case USB_DEVICE_REMOTE_WAKEUP: 22678c2ecf20Sopenharmony_ci if ((xudc->gadget.speed == USB_SPEED_SUPER) || 22688c2ecf20Sopenharmony_ci (xudc->device_state == USB_STATE_DEFAULT)) 22698c2ecf20Sopenharmony_ci return -EINVAL; 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTPM); 22728c2ecf20Sopenharmony_ci if (set) 22738c2ecf20Sopenharmony_ci val |= PORTPM_RWE; 22748c2ecf20Sopenharmony_ci else 22758c2ecf20Sopenharmony_ci val &= ~PORTPM_RWE; 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci xudc_writel(xudc, val, PORTPM); 22788c2ecf20Sopenharmony_ci break; 22798c2ecf20Sopenharmony_ci case USB_DEVICE_U1_ENABLE: 22808c2ecf20Sopenharmony_ci case USB_DEVICE_U2_ENABLE: 22818c2ecf20Sopenharmony_ci if ((xudc->device_state != USB_STATE_CONFIGURED) || 22828c2ecf20Sopenharmony_ci (xudc->gadget.speed != USB_SPEED_SUPER)) 22838c2ecf20Sopenharmony_ci return -EINVAL; 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTPM); 22868c2ecf20Sopenharmony_ci if ((feature == USB_DEVICE_U1_ENABLE) && 22878c2ecf20Sopenharmony_ci xudc->soc->u1_enable) { 22888c2ecf20Sopenharmony_ci if (set) 22898c2ecf20Sopenharmony_ci val |= PORTPM_U1E; 22908c2ecf20Sopenharmony_ci else 22918c2ecf20Sopenharmony_ci val &= ~PORTPM_U1E; 22928c2ecf20Sopenharmony_ci } 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci if ((feature == USB_DEVICE_U2_ENABLE) && 22958c2ecf20Sopenharmony_ci xudc->soc->u2_enable) { 22968c2ecf20Sopenharmony_ci if (set) 22978c2ecf20Sopenharmony_ci val |= PORTPM_U2E; 22988c2ecf20Sopenharmony_ci else 22998c2ecf20Sopenharmony_ci val &= ~PORTPM_U2E; 23008c2ecf20Sopenharmony_ci } 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci xudc_writel(xudc, val, PORTPM); 23038c2ecf20Sopenharmony_ci break; 23048c2ecf20Sopenharmony_ci case USB_DEVICE_TEST_MODE: 23058c2ecf20Sopenharmony_ci if (xudc->gadget.speed != USB_SPEED_HIGH) 23068c2ecf20Sopenharmony_ci return -EINVAL; 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci if (!set) 23098c2ecf20Sopenharmony_ci return -EINVAL; 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci xudc->test_mode_pattern = index >> 8; 23128c2ecf20Sopenharmony_ci break; 23138c2ecf20Sopenharmony_ci default: 23148c2ecf20Sopenharmony_ci return -EINVAL; 23158c2ecf20Sopenharmony_ci } 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci break; 23188c2ecf20Sopenharmony_ci case USB_RECIP_INTERFACE: 23198c2ecf20Sopenharmony_ci if (xudc->device_state != USB_STATE_CONFIGURED) 23208c2ecf20Sopenharmony_ci return -EINVAL; 23218c2ecf20Sopenharmony_ci 23228c2ecf20Sopenharmony_ci switch (feature) { 23238c2ecf20Sopenharmony_ci case USB_INTRF_FUNC_SUSPEND: 23248c2ecf20Sopenharmony_ci if (set) { 23258c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTPM); 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci if (index & USB_INTRF_FUNC_SUSPEND_RW) 23288c2ecf20Sopenharmony_ci val |= PORTPM_FRWE; 23298c2ecf20Sopenharmony_ci else 23308c2ecf20Sopenharmony_ci val &= ~PORTPM_FRWE; 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_ci xudc_writel(xudc, val, PORTPM); 23338c2ecf20Sopenharmony_ci } 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci return tegra_xudc_ep0_delegate_req(xudc, ctrl); 23368c2ecf20Sopenharmony_ci default: 23378c2ecf20Sopenharmony_ci return -EINVAL; 23388c2ecf20Sopenharmony_ci } 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci break; 23418c2ecf20Sopenharmony_ci case USB_RECIP_ENDPOINT: 23428c2ecf20Sopenharmony_ci ep = (index & USB_ENDPOINT_NUMBER_MASK) * 2 + 23438c2ecf20Sopenharmony_ci ((index & USB_DIR_IN) ? 1 : 0); 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ci if ((xudc->device_state == USB_STATE_DEFAULT) || 23468c2ecf20Sopenharmony_ci ((xudc->device_state == USB_STATE_ADDRESS) && 23478c2ecf20Sopenharmony_ci (index != 0))) 23488c2ecf20Sopenharmony_ci return -EINVAL; 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_ci ret = __tegra_xudc_ep_set_halt(&xudc->ep[ep], set); 23518c2ecf20Sopenharmony_ci if (ret < 0) 23528c2ecf20Sopenharmony_ci return ret; 23538c2ecf20Sopenharmony_ci break; 23548c2ecf20Sopenharmony_ci default: 23558c2ecf20Sopenharmony_ci return -EINVAL; 23568c2ecf20Sopenharmony_ci } 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci return tegra_xudc_ep0_queue_status(xudc, set_feature_complete); 23598c2ecf20Sopenharmony_ci} 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_cistatic int tegra_xudc_ep0_get_status(struct tegra_xudc *xudc, 23628c2ecf20Sopenharmony_ci struct usb_ctrlrequest *ctrl) 23638c2ecf20Sopenharmony_ci{ 23648c2ecf20Sopenharmony_ci struct tegra_xudc_ep_context *ep_ctx; 23658c2ecf20Sopenharmony_ci u32 val, ep, index = le16_to_cpu(ctrl->wIndex); 23668c2ecf20Sopenharmony_ci u16 status = 0; 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_ci if (!(ctrl->bRequestType & USB_DIR_IN)) 23698c2ecf20Sopenharmony_ci return -EINVAL; 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci if ((le16_to_cpu(ctrl->wValue) != 0) || 23728c2ecf20Sopenharmony_ci (le16_to_cpu(ctrl->wLength) != 2)) 23738c2ecf20Sopenharmony_ci return -EINVAL; 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci switch (ctrl->bRequestType & USB_RECIP_MASK) { 23768c2ecf20Sopenharmony_ci case USB_RECIP_DEVICE: 23778c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTPM); 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci if (xudc->selfpowered) 23808c2ecf20Sopenharmony_ci status |= BIT(USB_DEVICE_SELF_POWERED); 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci if ((xudc->gadget.speed < USB_SPEED_SUPER) && 23838c2ecf20Sopenharmony_ci (val & PORTPM_RWE)) 23848c2ecf20Sopenharmony_ci status |= BIT(USB_DEVICE_REMOTE_WAKEUP); 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci if (xudc->gadget.speed == USB_SPEED_SUPER) { 23878c2ecf20Sopenharmony_ci if (val & PORTPM_U1E) 23888c2ecf20Sopenharmony_ci status |= BIT(USB_DEV_STAT_U1_ENABLED); 23898c2ecf20Sopenharmony_ci if (val & PORTPM_U2E) 23908c2ecf20Sopenharmony_ci status |= BIT(USB_DEV_STAT_U2_ENABLED); 23918c2ecf20Sopenharmony_ci } 23928c2ecf20Sopenharmony_ci break; 23938c2ecf20Sopenharmony_ci case USB_RECIP_INTERFACE: 23948c2ecf20Sopenharmony_ci if (xudc->gadget.speed == USB_SPEED_SUPER) { 23958c2ecf20Sopenharmony_ci status |= USB_INTRF_STAT_FUNC_RW_CAP; 23968c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTPM); 23978c2ecf20Sopenharmony_ci if (val & PORTPM_FRWE) 23988c2ecf20Sopenharmony_ci status |= USB_INTRF_STAT_FUNC_RW; 23998c2ecf20Sopenharmony_ci } 24008c2ecf20Sopenharmony_ci break; 24018c2ecf20Sopenharmony_ci case USB_RECIP_ENDPOINT: 24028c2ecf20Sopenharmony_ci ep = (index & USB_ENDPOINT_NUMBER_MASK) * 2 + 24038c2ecf20Sopenharmony_ci ((index & USB_DIR_IN) ? 1 : 0); 24048c2ecf20Sopenharmony_ci ep_ctx = &xudc->ep_context[ep]; 24058c2ecf20Sopenharmony_ci 24068c2ecf20Sopenharmony_ci if ((xudc->device_state != USB_STATE_CONFIGURED) && 24078c2ecf20Sopenharmony_ci ((xudc->device_state != USB_STATE_ADDRESS) || (ep != 0))) 24088c2ecf20Sopenharmony_ci return -EINVAL; 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci if (ep_ctx_read_state(ep_ctx) == EP_STATE_DISABLED) 24118c2ecf20Sopenharmony_ci return -EINVAL; 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci if (xudc_readl(xudc, EP_HALT) & BIT(ep)) 24148c2ecf20Sopenharmony_ci status |= BIT(USB_ENDPOINT_HALT); 24158c2ecf20Sopenharmony_ci break; 24168c2ecf20Sopenharmony_ci default: 24178c2ecf20Sopenharmony_ci return -EINVAL; 24188c2ecf20Sopenharmony_ci } 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci xudc->status_buf = cpu_to_le16(status); 24218c2ecf20Sopenharmony_ci return tegra_xudc_ep0_queue_data(xudc, &xudc->status_buf, 24228c2ecf20Sopenharmony_ci sizeof(xudc->status_buf), 24238c2ecf20Sopenharmony_ci no_op_complete); 24248c2ecf20Sopenharmony_ci} 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_cistatic void set_sel_complete(struct usb_ep *ep, struct usb_request *req) 24278c2ecf20Sopenharmony_ci{ 24288c2ecf20Sopenharmony_ci /* Nothing to do with SEL values */ 24298c2ecf20Sopenharmony_ci} 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_cistatic int tegra_xudc_ep0_set_sel(struct tegra_xudc *xudc, 24328c2ecf20Sopenharmony_ci struct usb_ctrlrequest *ctrl) 24338c2ecf20Sopenharmony_ci{ 24348c2ecf20Sopenharmony_ci if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE | 24358c2ecf20Sopenharmony_ci USB_TYPE_STANDARD)) 24368c2ecf20Sopenharmony_ci return -EINVAL; 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci if (xudc->device_state == USB_STATE_DEFAULT) 24398c2ecf20Sopenharmony_ci return -EINVAL; 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci if ((le16_to_cpu(ctrl->wIndex) != 0) || 24428c2ecf20Sopenharmony_ci (le16_to_cpu(ctrl->wValue) != 0) || 24438c2ecf20Sopenharmony_ci (le16_to_cpu(ctrl->wLength) != 6)) 24448c2ecf20Sopenharmony_ci return -EINVAL; 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci return tegra_xudc_ep0_queue_data(xudc, &xudc->sel_timing, 24478c2ecf20Sopenharmony_ci sizeof(xudc->sel_timing), 24488c2ecf20Sopenharmony_ci set_sel_complete); 24498c2ecf20Sopenharmony_ci} 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_cistatic void set_isoch_delay_complete(struct usb_ep *ep, struct usb_request *req) 24528c2ecf20Sopenharmony_ci{ 24538c2ecf20Sopenharmony_ci /* Nothing to do with isoch delay */ 24548c2ecf20Sopenharmony_ci} 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_cistatic int tegra_xudc_ep0_set_isoch_delay(struct tegra_xudc *xudc, 24578c2ecf20Sopenharmony_ci struct usb_ctrlrequest *ctrl) 24588c2ecf20Sopenharmony_ci{ 24598c2ecf20Sopenharmony_ci u32 delay = le16_to_cpu(ctrl->wValue); 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE | 24628c2ecf20Sopenharmony_ci USB_TYPE_STANDARD)) 24638c2ecf20Sopenharmony_ci return -EINVAL; 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci if ((delay > 65535) || (le16_to_cpu(ctrl->wIndex) != 0) || 24668c2ecf20Sopenharmony_ci (le16_to_cpu(ctrl->wLength) != 0)) 24678c2ecf20Sopenharmony_ci return -EINVAL; 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci xudc->isoch_delay = delay; 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci return tegra_xudc_ep0_queue_status(xudc, set_isoch_delay_complete); 24728c2ecf20Sopenharmony_ci} 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_cistatic void set_address_complete(struct usb_ep *ep, struct usb_request *req) 24758c2ecf20Sopenharmony_ci{ 24768c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = req->context; 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_ci if ((xudc->device_state == USB_STATE_DEFAULT) && 24798c2ecf20Sopenharmony_ci (xudc->dev_addr != 0)) { 24808c2ecf20Sopenharmony_ci xudc->device_state = USB_STATE_ADDRESS; 24818c2ecf20Sopenharmony_ci usb_gadget_set_state(&xudc->gadget, xudc->device_state); 24828c2ecf20Sopenharmony_ci } else if ((xudc->device_state == USB_STATE_ADDRESS) && 24838c2ecf20Sopenharmony_ci (xudc->dev_addr == 0)) { 24848c2ecf20Sopenharmony_ci xudc->device_state = USB_STATE_DEFAULT; 24858c2ecf20Sopenharmony_ci usb_gadget_set_state(&xudc->gadget, xudc->device_state); 24868c2ecf20Sopenharmony_ci } 24878c2ecf20Sopenharmony_ci} 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_cistatic int tegra_xudc_ep0_set_address(struct tegra_xudc *xudc, 24908c2ecf20Sopenharmony_ci struct usb_ctrlrequest *ctrl) 24918c2ecf20Sopenharmony_ci{ 24928c2ecf20Sopenharmony_ci struct tegra_xudc_ep *ep0 = &xudc->ep[0]; 24938c2ecf20Sopenharmony_ci u32 val, addr = le16_to_cpu(ctrl->wValue); 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE | 24968c2ecf20Sopenharmony_ci USB_TYPE_STANDARD)) 24978c2ecf20Sopenharmony_ci return -EINVAL; 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_ci if ((addr > 127) || (le16_to_cpu(ctrl->wIndex) != 0) || 25008c2ecf20Sopenharmony_ci (le16_to_cpu(ctrl->wLength) != 0)) 25018c2ecf20Sopenharmony_ci return -EINVAL; 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci if (xudc->device_state == USB_STATE_CONFIGURED) 25048c2ecf20Sopenharmony_ci return -EINVAL; 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "set address: %u\n", addr); 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci xudc->dev_addr = addr; 25098c2ecf20Sopenharmony_ci val = xudc_readl(xudc, CTRL); 25108c2ecf20Sopenharmony_ci val &= ~(CTRL_DEVADDR_MASK); 25118c2ecf20Sopenharmony_ci val |= CTRL_DEVADDR(addr); 25128c2ecf20Sopenharmony_ci xudc_writel(xudc, val, CTRL); 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci ep_ctx_write_devaddr(ep0->context, addr); 25158c2ecf20Sopenharmony_ci 25168c2ecf20Sopenharmony_ci return tegra_xudc_ep0_queue_status(xudc, set_address_complete); 25178c2ecf20Sopenharmony_ci} 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_cistatic int tegra_xudc_ep0_standard_req(struct tegra_xudc *xudc, 25208c2ecf20Sopenharmony_ci struct usb_ctrlrequest *ctrl) 25218c2ecf20Sopenharmony_ci{ 25228c2ecf20Sopenharmony_ci int ret; 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci switch (ctrl->bRequest) { 25258c2ecf20Sopenharmony_ci case USB_REQ_GET_STATUS: 25268c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "USB_REQ_GET_STATUS\n"); 25278c2ecf20Sopenharmony_ci ret = tegra_xudc_ep0_get_status(xudc, ctrl); 25288c2ecf20Sopenharmony_ci break; 25298c2ecf20Sopenharmony_ci case USB_REQ_SET_ADDRESS: 25308c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "USB_REQ_SET_ADDRESS\n"); 25318c2ecf20Sopenharmony_ci ret = tegra_xudc_ep0_set_address(xudc, ctrl); 25328c2ecf20Sopenharmony_ci break; 25338c2ecf20Sopenharmony_ci case USB_REQ_SET_SEL: 25348c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "USB_REQ_SET_SEL\n"); 25358c2ecf20Sopenharmony_ci ret = tegra_xudc_ep0_set_sel(xudc, ctrl); 25368c2ecf20Sopenharmony_ci break; 25378c2ecf20Sopenharmony_ci case USB_REQ_SET_ISOCH_DELAY: 25388c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "USB_REQ_SET_ISOCH_DELAY\n"); 25398c2ecf20Sopenharmony_ci ret = tegra_xudc_ep0_set_isoch_delay(xudc, ctrl); 25408c2ecf20Sopenharmony_ci break; 25418c2ecf20Sopenharmony_ci case USB_REQ_CLEAR_FEATURE: 25428c2ecf20Sopenharmony_ci case USB_REQ_SET_FEATURE: 25438c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "USB_REQ_CLEAR/SET_FEATURE\n"); 25448c2ecf20Sopenharmony_ci ret = tegra_xudc_ep0_set_feature(xudc, ctrl); 25458c2ecf20Sopenharmony_ci break; 25468c2ecf20Sopenharmony_ci case USB_REQ_SET_CONFIGURATION: 25478c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "USB_REQ_SET_CONFIGURATION\n"); 25488c2ecf20Sopenharmony_ci /* 25498c2ecf20Sopenharmony_ci * In theory we need to clear RUN bit before status stage of 25508c2ecf20Sopenharmony_ci * deconfig request sent, but this seems to be causing problems. 25518c2ecf20Sopenharmony_ci * Clear RUN once all endpoints are disabled instead. 25528c2ecf20Sopenharmony_ci */ 25538c2ecf20Sopenharmony_ci fallthrough; 25548c2ecf20Sopenharmony_ci default: 25558c2ecf20Sopenharmony_ci ret = tegra_xudc_ep0_delegate_req(xudc, ctrl); 25568c2ecf20Sopenharmony_ci break; 25578c2ecf20Sopenharmony_ci } 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci return ret; 25608c2ecf20Sopenharmony_ci} 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_cistatic void tegra_xudc_handle_ep0_setup_packet(struct tegra_xudc *xudc, 25638c2ecf20Sopenharmony_ci struct usb_ctrlrequest *ctrl, 25648c2ecf20Sopenharmony_ci u16 seq_num) 25658c2ecf20Sopenharmony_ci{ 25668c2ecf20Sopenharmony_ci int ret; 25678c2ecf20Sopenharmony_ci 25688c2ecf20Sopenharmony_ci xudc->setup_seq_num = seq_num; 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci /* Ensure EP0 is unhalted. */ 25718c2ecf20Sopenharmony_ci ep_unhalt(xudc, 0); 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci /* 25748c2ecf20Sopenharmony_ci * On Tegra210, setup packets with sequence numbers 0xfffe or 0xffff 25758c2ecf20Sopenharmony_ci * are invalid. Halt EP0 until we get a valid packet. 25768c2ecf20Sopenharmony_ci */ 25778c2ecf20Sopenharmony_ci if (xudc->soc->invalid_seq_num && 25788c2ecf20Sopenharmony_ci (seq_num == 0xfffe || seq_num == 0xffff)) { 25798c2ecf20Sopenharmony_ci dev_warn(xudc->dev, "invalid sequence number detected\n"); 25808c2ecf20Sopenharmony_ci ep_halt(xudc, 0); 25818c2ecf20Sopenharmony_ci return; 25828c2ecf20Sopenharmony_ci } 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ci if (ctrl->wLength) 25858c2ecf20Sopenharmony_ci xudc->setup_state = (ctrl->bRequestType & USB_DIR_IN) ? 25868c2ecf20Sopenharmony_ci DATA_STAGE_XFER : DATA_STAGE_RECV; 25878c2ecf20Sopenharmony_ci else 25888c2ecf20Sopenharmony_ci xudc->setup_state = STATUS_STAGE_XFER; 25898c2ecf20Sopenharmony_ci 25908c2ecf20Sopenharmony_ci if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) 25918c2ecf20Sopenharmony_ci ret = tegra_xudc_ep0_standard_req(xudc, ctrl); 25928c2ecf20Sopenharmony_ci else 25938c2ecf20Sopenharmony_ci ret = tegra_xudc_ep0_delegate_req(xudc, ctrl); 25948c2ecf20Sopenharmony_ci 25958c2ecf20Sopenharmony_ci if (ret < 0) { 25968c2ecf20Sopenharmony_ci dev_warn(xudc->dev, "setup request failed: %d\n", ret); 25978c2ecf20Sopenharmony_ci xudc->setup_state = WAIT_FOR_SETUP; 25988c2ecf20Sopenharmony_ci ep_halt(xudc, 0); 25998c2ecf20Sopenharmony_ci } 26008c2ecf20Sopenharmony_ci} 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_cistatic void tegra_xudc_handle_ep0_event(struct tegra_xudc *xudc, 26038c2ecf20Sopenharmony_ci struct tegra_xudc_trb *event) 26048c2ecf20Sopenharmony_ci{ 26058c2ecf20Sopenharmony_ci struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *)event; 26068c2ecf20Sopenharmony_ci u16 seq_num = trb_read_seq_num(event); 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_ci if (xudc->setup_state != WAIT_FOR_SETUP) { 26098c2ecf20Sopenharmony_ci /* 26108c2ecf20Sopenharmony_ci * The controller is in the process of handling another 26118c2ecf20Sopenharmony_ci * setup request. Queue subsequent requests and handle 26128c2ecf20Sopenharmony_ci * the last one once the controller reports a sequence 26138c2ecf20Sopenharmony_ci * number error. 26148c2ecf20Sopenharmony_ci */ 26158c2ecf20Sopenharmony_ci memcpy(&xudc->setup_packet.ctrl_req, ctrl, sizeof(*ctrl)); 26168c2ecf20Sopenharmony_ci xudc->setup_packet.seq_num = seq_num; 26178c2ecf20Sopenharmony_ci xudc->queued_setup_packet = true; 26188c2ecf20Sopenharmony_ci } else { 26198c2ecf20Sopenharmony_ci tegra_xudc_handle_ep0_setup_packet(xudc, ctrl, seq_num); 26208c2ecf20Sopenharmony_ci } 26218c2ecf20Sopenharmony_ci} 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_cistatic struct tegra_xudc_request * 26248c2ecf20Sopenharmony_citrb_to_request(struct tegra_xudc_ep *ep, struct tegra_xudc_trb *trb) 26258c2ecf20Sopenharmony_ci{ 26268c2ecf20Sopenharmony_ci struct tegra_xudc_request *req; 26278c2ecf20Sopenharmony_ci 26288c2ecf20Sopenharmony_ci list_for_each_entry(req, &ep->queue, list) { 26298c2ecf20Sopenharmony_ci if (!req->trbs_queued) 26308c2ecf20Sopenharmony_ci break; 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci if (trb_in_request(ep, req, trb)) 26338c2ecf20Sopenharmony_ci return req; 26348c2ecf20Sopenharmony_ci } 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci return NULL; 26378c2ecf20Sopenharmony_ci} 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_cistatic void tegra_xudc_handle_transfer_completion(struct tegra_xudc *xudc, 26408c2ecf20Sopenharmony_ci struct tegra_xudc_ep *ep, 26418c2ecf20Sopenharmony_ci struct tegra_xudc_trb *event) 26428c2ecf20Sopenharmony_ci{ 26438c2ecf20Sopenharmony_ci struct tegra_xudc_request *req; 26448c2ecf20Sopenharmony_ci struct tegra_xudc_trb *trb; 26458c2ecf20Sopenharmony_ci bool short_packet; 26468c2ecf20Sopenharmony_ci 26478c2ecf20Sopenharmony_ci short_packet = (trb_read_cmpl_code(event) == 26488c2ecf20Sopenharmony_ci TRB_CMPL_CODE_SHORT_PACKET); 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_ci trb = trb_phys_to_virt(ep, trb_read_data_ptr(event)); 26518c2ecf20Sopenharmony_ci req = trb_to_request(ep, trb); 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_ci /* 26548c2ecf20Sopenharmony_ci * TDs are complete on short packet or when the completed TRB is the 26558c2ecf20Sopenharmony_ci * last TRB in the TD (the CHAIN bit is unset). 26568c2ecf20Sopenharmony_ci */ 26578c2ecf20Sopenharmony_ci if (req && (short_packet || (!trb_read_chain(trb) && 26588c2ecf20Sopenharmony_ci (req->trbs_needed == req->trbs_queued)))) { 26598c2ecf20Sopenharmony_ci struct tegra_xudc_trb *last = req->last_trb; 26608c2ecf20Sopenharmony_ci unsigned int residual; 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_ci residual = trb_read_transfer_len(event); 26638c2ecf20Sopenharmony_ci req->usb_req.actual = req->usb_req.length - residual; 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "bytes transferred %u / %u\n", 26668c2ecf20Sopenharmony_ci req->usb_req.actual, req->usb_req.length); 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_ci tegra_xudc_req_done(ep, req, 0); 26698c2ecf20Sopenharmony_ci 26708c2ecf20Sopenharmony_ci if (ep->desc && usb_endpoint_xfer_control(ep->desc)) 26718c2ecf20Sopenharmony_ci tegra_xudc_ep0_req_done(xudc); 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci /* 26748c2ecf20Sopenharmony_ci * Advance the dequeue pointer past the end of the current TD 26758c2ecf20Sopenharmony_ci * on short packet completion. 26768c2ecf20Sopenharmony_ci */ 26778c2ecf20Sopenharmony_ci if (short_packet) { 26788c2ecf20Sopenharmony_ci ep->deq_ptr = (last - ep->transfer_ring) + 1; 26798c2ecf20Sopenharmony_ci if (ep->deq_ptr == XUDC_TRANSFER_RING_SIZE - 1) 26808c2ecf20Sopenharmony_ci ep->deq_ptr = 0; 26818c2ecf20Sopenharmony_ci } 26828c2ecf20Sopenharmony_ci } else if (!req) { 26838c2ecf20Sopenharmony_ci dev_warn(xudc->dev, "transfer event on dequeued request\n"); 26848c2ecf20Sopenharmony_ci } 26858c2ecf20Sopenharmony_ci 26868c2ecf20Sopenharmony_ci if (ep->desc) 26878c2ecf20Sopenharmony_ci tegra_xudc_ep_kick_queue(ep); 26888c2ecf20Sopenharmony_ci} 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_cistatic void tegra_xudc_handle_transfer_event(struct tegra_xudc *xudc, 26918c2ecf20Sopenharmony_ci struct tegra_xudc_trb *event) 26928c2ecf20Sopenharmony_ci{ 26938c2ecf20Sopenharmony_ci unsigned int ep_index = trb_read_endpoint_id(event); 26948c2ecf20Sopenharmony_ci struct tegra_xudc_ep *ep = &xudc->ep[ep_index]; 26958c2ecf20Sopenharmony_ci struct tegra_xudc_trb *trb; 26968c2ecf20Sopenharmony_ci u16 comp_code; 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci if (ep_ctx_read_state(ep->context) == EP_STATE_DISABLED) { 26998c2ecf20Sopenharmony_ci dev_warn(xudc->dev, "transfer event on disabled EP %u\n", 27008c2ecf20Sopenharmony_ci ep_index); 27018c2ecf20Sopenharmony_ci return; 27028c2ecf20Sopenharmony_ci } 27038c2ecf20Sopenharmony_ci 27048c2ecf20Sopenharmony_ci /* Update transfer ring dequeue pointer. */ 27058c2ecf20Sopenharmony_ci trb = trb_phys_to_virt(ep, trb_read_data_ptr(event)); 27068c2ecf20Sopenharmony_ci comp_code = trb_read_cmpl_code(event); 27078c2ecf20Sopenharmony_ci if (comp_code != TRB_CMPL_CODE_BABBLE_DETECTED_ERR) { 27088c2ecf20Sopenharmony_ci ep->deq_ptr = (trb - ep->transfer_ring) + 1; 27098c2ecf20Sopenharmony_ci 27108c2ecf20Sopenharmony_ci if (ep->deq_ptr == XUDC_TRANSFER_RING_SIZE - 1) 27118c2ecf20Sopenharmony_ci ep->deq_ptr = 0; 27128c2ecf20Sopenharmony_ci ep->ring_full = false; 27138c2ecf20Sopenharmony_ci } 27148c2ecf20Sopenharmony_ci 27158c2ecf20Sopenharmony_ci switch (comp_code) { 27168c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_SUCCESS: 27178c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_SHORT_PACKET: 27188c2ecf20Sopenharmony_ci tegra_xudc_handle_transfer_completion(xudc, ep, event); 27198c2ecf20Sopenharmony_ci break; 27208c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_HOST_REJECTED: 27218c2ecf20Sopenharmony_ci dev_info(xudc->dev, "stream rejected on EP %u\n", ep_index); 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ci ep->stream_rejected = true; 27248c2ecf20Sopenharmony_ci break; 27258c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_PRIME_PIPE_RECEIVED: 27268c2ecf20Sopenharmony_ci dev_info(xudc->dev, "prime pipe received on EP %u\n", ep_index); 27278c2ecf20Sopenharmony_ci 27288c2ecf20Sopenharmony_ci if (ep->stream_rejected) { 27298c2ecf20Sopenharmony_ci ep->stream_rejected = false; 27308c2ecf20Sopenharmony_ci /* 27318c2ecf20Sopenharmony_ci * An EP is stopped when a stream is rejected. Wait 27328c2ecf20Sopenharmony_ci * for the EP to report that it is stopped and then 27338c2ecf20Sopenharmony_ci * un-stop it. 27348c2ecf20Sopenharmony_ci */ 27358c2ecf20Sopenharmony_ci ep_wait_for_stopped(xudc, ep_index); 27368c2ecf20Sopenharmony_ci } 27378c2ecf20Sopenharmony_ci tegra_xudc_ep_ring_doorbell(ep); 27388c2ecf20Sopenharmony_ci break; 27398c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_BABBLE_DETECTED_ERR: 27408c2ecf20Sopenharmony_ci /* 27418c2ecf20Sopenharmony_ci * Wait for the EP to be stopped so the controller stops 27428c2ecf20Sopenharmony_ci * processing doorbells. 27438c2ecf20Sopenharmony_ci */ 27448c2ecf20Sopenharmony_ci ep_wait_for_stopped(xudc, ep_index); 27458c2ecf20Sopenharmony_ci ep->enq_ptr = ep->deq_ptr; 27468c2ecf20Sopenharmony_ci tegra_xudc_ep_nuke(ep, -EIO); 27478c2ecf20Sopenharmony_ci fallthrough; 27488c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_STREAM_NUMP_ERROR: 27498c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_CTRL_DIR_ERR: 27508c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_INVALID_STREAM_TYPE_ERR: 27518c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_RING_UNDERRUN: 27528c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_RING_OVERRUN: 27538c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_ISOCH_BUFFER_OVERRUN: 27548c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_USB_TRANS_ERR: 27558c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_TRB_ERR: 27568c2ecf20Sopenharmony_ci dev_err(xudc->dev, "completion error %#x on EP %u\n", 27578c2ecf20Sopenharmony_ci comp_code, ep_index); 27588c2ecf20Sopenharmony_ci 27598c2ecf20Sopenharmony_ci ep_halt(xudc, ep_index); 27608c2ecf20Sopenharmony_ci break; 27618c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_CTRL_SEQNUM_ERR: 27628c2ecf20Sopenharmony_ci dev_info(xudc->dev, "sequence number error\n"); 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_ci /* 27658c2ecf20Sopenharmony_ci * Kill any queued control request and skip to the last 27668c2ecf20Sopenharmony_ci * setup packet we received. 27678c2ecf20Sopenharmony_ci */ 27688c2ecf20Sopenharmony_ci tegra_xudc_ep_nuke(ep, -EINVAL); 27698c2ecf20Sopenharmony_ci xudc->setup_state = WAIT_FOR_SETUP; 27708c2ecf20Sopenharmony_ci if (!xudc->queued_setup_packet) 27718c2ecf20Sopenharmony_ci break; 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci tegra_xudc_handle_ep0_setup_packet(xudc, 27748c2ecf20Sopenharmony_ci &xudc->setup_packet.ctrl_req, 27758c2ecf20Sopenharmony_ci xudc->setup_packet.seq_num); 27768c2ecf20Sopenharmony_ci xudc->queued_setup_packet = false; 27778c2ecf20Sopenharmony_ci break; 27788c2ecf20Sopenharmony_ci case TRB_CMPL_CODE_STOPPED: 27798c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "stop completion code on EP %u\n", 27808c2ecf20Sopenharmony_ci ep_index); 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_ci /* Disconnected. */ 27838c2ecf20Sopenharmony_ci tegra_xudc_ep_nuke(ep, -ECONNREFUSED); 27848c2ecf20Sopenharmony_ci break; 27858c2ecf20Sopenharmony_ci default: 27868c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "completion event %#x on EP %u\n", 27878c2ecf20Sopenharmony_ci comp_code, ep_index); 27888c2ecf20Sopenharmony_ci break; 27898c2ecf20Sopenharmony_ci } 27908c2ecf20Sopenharmony_ci} 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_cistatic void tegra_xudc_reset(struct tegra_xudc *xudc) 27938c2ecf20Sopenharmony_ci{ 27948c2ecf20Sopenharmony_ci struct tegra_xudc_ep *ep0 = &xudc->ep[0]; 27958c2ecf20Sopenharmony_ci dma_addr_t deq_ptr; 27968c2ecf20Sopenharmony_ci unsigned int i; 27978c2ecf20Sopenharmony_ci 27988c2ecf20Sopenharmony_ci xudc->setup_state = WAIT_FOR_SETUP; 27998c2ecf20Sopenharmony_ci xudc->device_state = USB_STATE_DEFAULT; 28008c2ecf20Sopenharmony_ci usb_gadget_set_state(&xudc->gadget, xudc->device_state); 28018c2ecf20Sopenharmony_ci 28028c2ecf20Sopenharmony_ci ep_unpause_all(xudc); 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) 28058c2ecf20Sopenharmony_ci tegra_xudc_ep_nuke(&xudc->ep[i], -ESHUTDOWN); 28068c2ecf20Sopenharmony_ci 28078c2ecf20Sopenharmony_ci /* 28088c2ecf20Sopenharmony_ci * Reset sequence number and dequeue pointer to flush the transfer 28098c2ecf20Sopenharmony_ci * ring. 28108c2ecf20Sopenharmony_ci */ 28118c2ecf20Sopenharmony_ci ep0->deq_ptr = ep0->enq_ptr; 28128c2ecf20Sopenharmony_ci ep0->ring_full = false; 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_ci xudc->setup_seq_num = 0; 28158c2ecf20Sopenharmony_ci xudc->queued_setup_packet = false; 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ci ep_ctx_write_rsvd(ep0->context, 0); 28188c2ecf20Sopenharmony_ci ep_ctx_write_partial_td(ep0->context, 0); 28198c2ecf20Sopenharmony_ci ep_ctx_write_splitxstate(ep0->context, 0); 28208c2ecf20Sopenharmony_ci ep_ctx_write_seq_num(ep0->context, 0); 28218c2ecf20Sopenharmony_ci 28228c2ecf20Sopenharmony_ci deq_ptr = trb_virt_to_phys(ep0, &ep0->transfer_ring[ep0->deq_ptr]); 28238c2ecf20Sopenharmony_ci 28248c2ecf20Sopenharmony_ci if (!dma_mapping_error(xudc->dev, deq_ptr)) { 28258c2ecf20Sopenharmony_ci ep_ctx_write_deq_ptr(ep0->context, deq_ptr); 28268c2ecf20Sopenharmony_ci ep_ctx_write_dcs(ep0->context, ep0->pcs); 28278c2ecf20Sopenharmony_ci } 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_ci ep_unhalt_all(xudc); 28308c2ecf20Sopenharmony_ci ep_reload(xudc, 0); 28318c2ecf20Sopenharmony_ci ep_unpause(xudc, 0); 28328c2ecf20Sopenharmony_ci} 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_cistatic void tegra_xudc_port_connect(struct tegra_xudc *xudc) 28358c2ecf20Sopenharmony_ci{ 28368c2ecf20Sopenharmony_ci struct tegra_xudc_ep *ep0 = &xudc->ep[0]; 28378c2ecf20Sopenharmony_ci u16 maxpacket; 28388c2ecf20Sopenharmony_ci u32 val; 28398c2ecf20Sopenharmony_ci 28408c2ecf20Sopenharmony_ci val = (xudc_readl(xudc, PORTSC) & PORTSC_PS_MASK) >> PORTSC_PS_SHIFT; 28418c2ecf20Sopenharmony_ci switch (val) { 28428c2ecf20Sopenharmony_ci case PORTSC_PS_LS: 28438c2ecf20Sopenharmony_ci xudc->gadget.speed = USB_SPEED_LOW; 28448c2ecf20Sopenharmony_ci break; 28458c2ecf20Sopenharmony_ci case PORTSC_PS_FS: 28468c2ecf20Sopenharmony_ci xudc->gadget.speed = USB_SPEED_FULL; 28478c2ecf20Sopenharmony_ci break; 28488c2ecf20Sopenharmony_ci case PORTSC_PS_HS: 28498c2ecf20Sopenharmony_ci xudc->gadget.speed = USB_SPEED_HIGH; 28508c2ecf20Sopenharmony_ci break; 28518c2ecf20Sopenharmony_ci case PORTSC_PS_SS: 28528c2ecf20Sopenharmony_ci xudc->gadget.speed = USB_SPEED_SUPER; 28538c2ecf20Sopenharmony_ci break; 28548c2ecf20Sopenharmony_ci default: 28558c2ecf20Sopenharmony_ci xudc->gadget.speed = USB_SPEED_UNKNOWN; 28568c2ecf20Sopenharmony_ci break; 28578c2ecf20Sopenharmony_ci } 28588c2ecf20Sopenharmony_ci 28598c2ecf20Sopenharmony_ci xudc->device_state = USB_STATE_DEFAULT; 28608c2ecf20Sopenharmony_ci usb_gadget_set_state(&xudc->gadget, xudc->device_state); 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci xudc->setup_state = WAIT_FOR_SETUP; 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci if (xudc->gadget.speed == USB_SPEED_SUPER) 28658c2ecf20Sopenharmony_ci maxpacket = 512; 28668c2ecf20Sopenharmony_ci else 28678c2ecf20Sopenharmony_ci maxpacket = 64; 28688c2ecf20Sopenharmony_ci 28698c2ecf20Sopenharmony_ci ep_ctx_write_max_packet_size(ep0->context, maxpacket); 28708c2ecf20Sopenharmony_ci tegra_xudc_ep0_desc.wMaxPacketSize = cpu_to_le16(maxpacket); 28718c2ecf20Sopenharmony_ci usb_ep_set_maxpacket_limit(&ep0->usb_ep, maxpacket); 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci if (!xudc->soc->u1_enable) { 28748c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTPM); 28758c2ecf20Sopenharmony_ci val &= ~(PORTPM_U1TIMEOUT_MASK); 28768c2ecf20Sopenharmony_ci xudc_writel(xudc, val, PORTPM); 28778c2ecf20Sopenharmony_ci } 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_ci if (!xudc->soc->u2_enable) { 28808c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTPM); 28818c2ecf20Sopenharmony_ci val &= ~(PORTPM_U2TIMEOUT_MASK); 28828c2ecf20Sopenharmony_ci xudc_writel(xudc, val, PORTPM); 28838c2ecf20Sopenharmony_ci } 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_ci if (xudc->gadget.speed <= USB_SPEED_HIGH) { 28868c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTPM); 28878c2ecf20Sopenharmony_ci val &= ~(PORTPM_L1S_MASK); 28888c2ecf20Sopenharmony_ci if (xudc->soc->lpm_enable) 28898c2ecf20Sopenharmony_ci val |= PORTPM_L1S(PORTPM_L1S_ACCEPT); 28908c2ecf20Sopenharmony_ci else 28918c2ecf20Sopenharmony_ci val |= PORTPM_L1S(PORTPM_L1S_NYET); 28928c2ecf20Sopenharmony_ci xudc_writel(xudc, val, PORTPM); 28938c2ecf20Sopenharmony_ci } 28948c2ecf20Sopenharmony_ci 28958c2ecf20Sopenharmony_ci val = xudc_readl(xudc, ST); 28968c2ecf20Sopenharmony_ci if (val & ST_RC) 28978c2ecf20Sopenharmony_ci xudc_writel(xudc, ST_RC, ST); 28988c2ecf20Sopenharmony_ci} 28998c2ecf20Sopenharmony_ci 29008c2ecf20Sopenharmony_cistatic void tegra_xudc_port_disconnect(struct tegra_xudc *xudc) 29018c2ecf20Sopenharmony_ci{ 29028c2ecf20Sopenharmony_ci tegra_xudc_reset(xudc); 29038c2ecf20Sopenharmony_ci 29048c2ecf20Sopenharmony_ci if (xudc->driver && xudc->driver->disconnect) { 29058c2ecf20Sopenharmony_ci spin_unlock(&xudc->lock); 29068c2ecf20Sopenharmony_ci xudc->driver->disconnect(&xudc->gadget); 29078c2ecf20Sopenharmony_ci spin_lock(&xudc->lock); 29088c2ecf20Sopenharmony_ci } 29098c2ecf20Sopenharmony_ci 29108c2ecf20Sopenharmony_ci xudc->device_state = USB_STATE_NOTATTACHED; 29118c2ecf20Sopenharmony_ci usb_gadget_set_state(&xudc->gadget, xudc->device_state); 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_ci complete(&xudc->disconnect_complete); 29148c2ecf20Sopenharmony_ci} 29158c2ecf20Sopenharmony_ci 29168c2ecf20Sopenharmony_cistatic void tegra_xudc_port_reset(struct tegra_xudc *xudc) 29178c2ecf20Sopenharmony_ci{ 29188c2ecf20Sopenharmony_ci tegra_xudc_reset(xudc); 29198c2ecf20Sopenharmony_ci 29208c2ecf20Sopenharmony_ci if (xudc->driver) { 29218c2ecf20Sopenharmony_ci spin_unlock(&xudc->lock); 29228c2ecf20Sopenharmony_ci usb_gadget_udc_reset(&xudc->gadget, xudc->driver); 29238c2ecf20Sopenharmony_ci spin_lock(&xudc->lock); 29248c2ecf20Sopenharmony_ci } 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_ci tegra_xudc_port_connect(xudc); 29278c2ecf20Sopenharmony_ci} 29288c2ecf20Sopenharmony_ci 29298c2ecf20Sopenharmony_cistatic void tegra_xudc_port_suspend(struct tegra_xudc *xudc) 29308c2ecf20Sopenharmony_ci{ 29318c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "port suspend\n"); 29328c2ecf20Sopenharmony_ci 29338c2ecf20Sopenharmony_ci xudc->resume_state = xudc->device_state; 29348c2ecf20Sopenharmony_ci xudc->device_state = USB_STATE_SUSPENDED; 29358c2ecf20Sopenharmony_ci usb_gadget_set_state(&xudc->gadget, xudc->device_state); 29368c2ecf20Sopenharmony_ci 29378c2ecf20Sopenharmony_ci if (xudc->driver->suspend) { 29388c2ecf20Sopenharmony_ci spin_unlock(&xudc->lock); 29398c2ecf20Sopenharmony_ci xudc->driver->suspend(&xudc->gadget); 29408c2ecf20Sopenharmony_ci spin_lock(&xudc->lock); 29418c2ecf20Sopenharmony_ci } 29428c2ecf20Sopenharmony_ci} 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_cistatic void tegra_xudc_port_resume(struct tegra_xudc *xudc) 29458c2ecf20Sopenharmony_ci{ 29468c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "port resume\n"); 29478c2ecf20Sopenharmony_ci 29488c2ecf20Sopenharmony_ci tegra_xudc_resume_device_state(xudc); 29498c2ecf20Sopenharmony_ci 29508c2ecf20Sopenharmony_ci if (xudc->driver->resume) { 29518c2ecf20Sopenharmony_ci spin_unlock(&xudc->lock); 29528c2ecf20Sopenharmony_ci xudc->driver->resume(&xudc->gadget); 29538c2ecf20Sopenharmony_ci spin_lock(&xudc->lock); 29548c2ecf20Sopenharmony_ci } 29558c2ecf20Sopenharmony_ci} 29568c2ecf20Sopenharmony_ci 29578c2ecf20Sopenharmony_cistatic inline void clear_port_change(struct tegra_xudc *xudc, u32 flag) 29588c2ecf20Sopenharmony_ci{ 29598c2ecf20Sopenharmony_ci u32 val; 29608c2ecf20Sopenharmony_ci 29618c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTSC); 29628c2ecf20Sopenharmony_ci val &= ~PORTSC_CHANGE_MASK; 29638c2ecf20Sopenharmony_ci val |= flag; 29648c2ecf20Sopenharmony_ci xudc_writel(xudc, val, PORTSC); 29658c2ecf20Sopenharmony_ci} 29668c2ecf20Sopenharmony_ci 29678c2ecf20Sopenharmony_cistatic void __tegra_xudc_handle_port_status(struct tegra_xudc *xudc) 29688c2ecf20Sopenharmony_ci{ 29698c2ecf20Sopenharmony_ci u32 portsc, porthalt; 29708c2ecf20Sopenharmony_ci 29718c2ecf20Sopenharmony_ci porthalt = xudc_readl(xudc, PORTHALT); 29728c2ecf20Sopenharmony_ci if ((porthalt & PORTHALT_STCHG_REQ) && 29738c2ecf20Sopenharmony_ci (porthalt & PORTHALT_HALT_LTSSM)) { 29748c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "STCHG_REQ, PORTHALT = %#x\n", porthalt); 29758c2ecf20Sopenharmony_ci porthalt &= ~PORTHALT_HALT_LTSSM; 29768c2ecf20Sopenharmony_ci xudc_writel(xudc, porthalt, PORTHALT); 29778c2ecf20Sopenharmony_ci } 29788c2ecf20Sopenharmony_ci 29798c2ecf20Sopenharmony_ci portsc = xudc_readl(xudc, PORTSC); 29808c2ecf20Sopenharmony_ci if ((portsc & PORTSC_PRC) && (portsc & PORTSC_PR)) { 29818c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "PRC, PR, PORTSC = %#x\n", portsc); 29828c2ecf20Sopenharmony_ci clear_port_change(xudc, PORTSC_PRC | PORTSC_PED); 29838c2ecf20Sopenharmony_ci#define TOGGLE_VBUS_WAIT_MS 100 29848c2ecf20Sopenharmony_ci if (xudc->soc->port_reset_quirk) { 29858c2ecf20Sopenharmony_ci schedule_delayed_work(&xudc->port_reset_war_work, 29868c2ecf20Sopenharmony_ci msecs_to_jiffies(TOGGLE_VBUS_WAIT_MS)); 29878c2ecf20Sopenharmony_ci xudc->wait_for_sec_prc = 1; 29888c2ecf20Sopenharmony_ci } 29898c2ecf20Sopenharmony_ci } 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_ci if ((portsc & PORTSC_PRC) && !(portsc & PORTSC_PR)) { 29928c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "PRC, Not PR, PORTSC = %#x\n", portsc); 29938c2ecf20Sopenharmony_ci clear_port_change(xudc, PORTSC_PRC | PORTSC_PED); 29948c2ecf20Sopenharmony_ci tegra_xudc_port_reset(xudc); 29958c2ecf20Sopenharmony_ci cancel_delayed_work(&xudc->port_reset_war_work); 29968c2ecf20Sopenharmony_ci xudc->wait_for_sec_prc = 0; 29978c2ecf20Sopenharmony_ci } 29988c2ecf20Sopenharmony_ci 29998c2ecf20Sopenharmony_ci portsc = xudc_readl(xudc, PORTSC); 30008c2ecf20Sopenharmony_ci if (portsc & PORTSC_WRC) { 30018c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "WRC, PORTSC = %#x\n", portsc); 30028c2ecf20Sopenharmony_ci clear_port_change(xudc, PORTSC_WRC | PORTSC_PED); 30038c2ecf20Sopenharmony_ci if (!(xudc_readl(xudc, PORTSC) & PORTSC_WPR)) 30048c2ecf20Sopenharmony_ci tegra_xudc_port_reset(xudc); 30058c2ecf20Sopenharmony_ci } 30068c2ecf20Sopenharmony_ci 30078c2ecf20Sopenharmony_ci portsc = xudc_readl(xudc, PORTSC); 30088c2ecf20Sopenharmony_ci if (portsc & PORTSC_CSC) { 30098c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "CSC, PORTSC = %#x\n", portsc); 30108c2ecf20Sopenharmony_ci clear_port_change(xudc, PORTSC_CSC); 30118c2ecf20Sopenharmony_ci 30128c2ecf20Sopenharmony_ci if (portsc & PORTSC_CCS) 30138c2ecf20Sopenharmony_ci tegra_xudc_port_connect(xudc); 30148c2ecf20Sopenharmony_ci else 30158c2ecf20Sopenharmony_ci tegra_xudc_port_disconnect(xudc); 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_ci if (xudc->wait_csc) { 30188c2ecf20Sopenharmony_ci cancel_delayed_work(&xudc->plc_reset_work); 30198c2ecf20Sopenharmony_ci xudc->wait_csc = false; 30208c2ecf20Sopenharmony_ci } 30218c2ecf20Sopenharmony_ci } 30228c2ecf20Sopenharmony_ci 30238c2ecf20Sopenharmony_ci portsc = xudc_readl(xudc, PORTSC); 30248c2ecf20Sopenharmony_ci if (portsc & PORTSC_PLC) { 30258c2ecf20Sopenharmony_ci u32 pls = (portsc & PORTSC_PLS_MASK) >> PORTSC_PLS_SHIFT; 30268c2ecf20Sopenharmony_ci 30278c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "PLC, PORTSC = %#x\n", portsc); 30288c2ecf20Sopenharmony_ci clear_port_change(xudc, PORTSC_PLC); 30298c2ecf20Sopenharmony_ci switch (pls) { 30308c2ecf20Sopenharmony_ci case PORTSC_PLS_U3: 30318c2ecf20Sopenharmony_ci tegra_xudc_port_suspend(xudc); 30328c2ecf20Sopenharmony_ci break; 30338c2ecf20Sopenharmony_ci case PORTSC_PLS_U0: 30348c2ecf20Sopenharmony_ci if (xudc->gadget.speed < USB_SPEED_SUPER) 30358c2ecf20Sopenharmony_ci tegra_xudc_port_resume(xudc); 30368c2ecf20Sopenharmony_ci break; 30378c2ecf20Sopenharmony_ci case PORTSC_PLS_RESUME: 30388c2ecf20Sopenharmony_ci if (xudc->gadget.speed == USB_SPEED_SUPER) 30398c2ecf20Sopenharmony_ci tegra_xudc_port_resume(xudc); 30408c2ecf20Sopenharmony_ci break; 30418c2ecf20Sopenharmony_ci case PORTSC_PLS_INACTIVE: 30428c2ecf20Sopenharmony_ci schedule_delayed_work(&xudc->plc_reset_work, 30438c2ecf20Sopenharmony_ci msecs_to_jiffies(TOGGLE_VBUS_WAIT_MS)); 30448c2ecf20Sopenharmony_ci xudc->wait_csc = true; 30458c2ecf20Sopenharmony_ci break; 30468c2ecf20Sopenharmony_ci default: 30478c2ecf20Sopenharmony_ci break; 30488c2ecf20Sopenharmony_ci } 30498c2ecf20Sopenharmony_ci } 30508c2ecf20Sopenharmony_ci 30518c2ecf20Sopenharmony_ci if (portsc & PORTSC_CEC) { 30528c2ecf20Sopenharmony_ci dev_warn(xudc->dev, "CEC, PORTSC = %#x\n", portsc); 30538c2ecf20Sopenharmony_ci clear_port_change(xudc, PORTSC_CEC); 30548c2ecf20Sopenharmony_ci } 30558c2ecf20Sopenharmony_ci 30568c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "PORTSC = %#x\n", xudc_readl(xudc, PORTSC)); 30578c2ecf20Sopenharmony_ci} 30588c2ecf20Sopenharmony_ci 30598c2ecf20Sopenharmony_cistatic void tegra_xudc_handle_port_status(struct tegra_xudc *xudc) 30608c2ecf20Sopenharmony_ci{ 30618c2ecf20Sopenharmony_ci while ((xudc_readl(xudc, PORTSC) & PORTSC_CHANGE_MASK) || 30628c2ecf20Sopenharmony_ci (xudc_readl(xudc, PORTHALT) & PORTHALT_STCHG_REQ)) 30638c2ecf20Sopenharmony_ci __tegra_xudc_handle_port_status(xudc); 30648c2ecf20Sopenharmony_ci} 30658c2ecf20Sopenharmony_ci 30668c2ecf20Sopenharmony_cistatic void tegra_xudc_handle_event(struct tegra_xudc *xudc, 30678c2ecf20Sopenharmony_ci struct tegra_xudc_trb *event) 30688c2ecf20Sopenharmony_ci{ 30698c2ecf20Sopenharmony_ci u32 type = trb_read_type(event); 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_ci dump_trb(xudc, "EVENT", event); 30728c2ecf20Sopenharmony_ci 30738c2ecf20Sopenharmony_ci switch (type) { 30748c2ecf20Sopenharmony_ci case TRB_TYPE_PORT_STATUS_CHANGE_EVENT: 30758c2ecf20Sopenharmony_ci tegra_xudc_handle_port_status(xudc); 30768c2ecf20Sopenharmony_ci break; 30778c2ecf20Sopenharmony_ci case TRB_TYPE_TRANSFER_EVENT: 30788c2ecf20Sopenharmony_ci tegra_xudc_handle_transfer_event(xudc, event); 30798c2ecf20Sopenharmony_ci break; 30808c2ecf20Sopenharmony_ci case TRB_TYPE_SETUP_PACKET_EVENT: 30818c2ecf20Sopenharmony_ci tegra_xudc_handle_ep0_event(xudc, event); 30828c2ecf20Sopenharmony_ci break; 30838c2ecf20Sopenharmony_ci default: 30848c2ecf20Sopenharmony_ci dev_info(xudc->dev, "Unrecognized TRB type = %#x\n", type); 30858c2ecf20Sopenharmony_ci break; 30868c2ecf20Sopenharmony_ci } 30878c2ecf20Sopenharmony_ci} 30888c2ecf20Sopenharmony_ci 30898c2ecf20Sopenharmony_cistatic void tegra_xudc_process_event_ring(struct tegra_xudc *xudc) 30908c2ecf20Sopenharmony_ci{ 30918c2ecf20Sopenharmony_ci struct tegra_xudc_trb *event; 30928c2ecf20Sopenharmony_ci dma_addr_t erdp; 30938c2ecf20Sopenharmony_ci 30948c2ecf20Sopenharmony_ci while (true) { 30958c2ecf20Sopenharmony_ci event = xudc->event_ring[xudc->event_ring_index] + 30968c2ecf20Sopenharmony_ci xudc->event_ring_deq_ptr; 30978c2ecf20Sopenharmony_ci 30988c2ecf20Sopenharmony_ci if (trb_read_cycle(event) != xudc->ccs) 30998c2ecf20Sopenharmony_ci break; 31008c2ecf20Sopenharmony_ci 31018c2ecf20Sopenharmony_ci tegra_xudc_handle_event(xudc, event); 31028c2ecf20Sopenharmony_ci 31038c2ecf20Sopenharmony_ci xudc->event_ring_deq_ptr++; 31048c2ecf20Sopenharmony_ci if (xudc->event_ring_deq_ptr == XUDC_EVENT_RING_SIZE) { 31058c2ecf20Sopenharmony_ci xudc->event_ring_deq_ptr = 0; 31068c2ecf20Sopenharmony_ci xudc->event_ring_index++; 31078c2ecf20Sopenharmony_ci } 31088c2ecf20Sopenharmony_ci 31098c2ecf20Sopenharmony_ci if (xudc->event_ring_index == XUDC_NR_EVENT_RINGS) { 31108c2ecf20Sopenharmony_ci xudc->event_ring_index = 0; 31118c2ecf20Sopenharmony_ci xudc->ccs = !xudc->ccs; 31128c2ecf20Sopenharmony_ci } 31138c2ecf20Sopenharmony_ci } 31148c2ecf20Sopenharmony_ci 31158c2ecf20Sopenharmony_ci erdp = xudc->event_ring_phys[xudc->event_ring_index] + 31168c2ecf20Sopenharmony_ci xudc->event_ring_deq_ptr * sizeof(*event); 31178c2ecf20Sopenharmony_ci 31188c2ecf20Sopenharmony_ci xudc_writel(xudc, upper_32_bits(erdp), ERDPHI); 31198c2ecf20Sopenharmony_ci xudc_writel(xudc, lower_32_bits(erdp) | ERDPLO_EHB, ERDPLO); 31208c2ecf20Sopenharmony_ci} 31218c2ecf20Sopenharmony_ci 31228c2ecf20Sopenharmony_cistatic irqreturn_t tegra_xudc_irq(int irq, void *data) 31238c2ecf20Sopenharmony_ci{ 31248c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = data; 31258c2ecf20Sopenharmony_ci unsigned long flags; 31268c2ecf20Sopenharmony_ci u32 val; 31278c2ecf20Sopenharmony_ci 31288c2ecf20Sopenharmony_ci val = xudc_readl(xudc, ST); 31298c2ecf20Sopenharmony_ci if (!(val & ST_IP)) 31308c2ecf20Sopenharmony_ci return IRQ_NONE; 31318c2ecf20Sopenharmony_ci xudc_writel(xudc, ST_IP, ST); 31328c2ecf20Sopenharmony_ci 31338c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 31348c2ecf20Sopenharmony_ci tegra_xudc_process_event_ring(xudc); 31358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 31368c2ecf20Sopenharmony_ci 31378c2ecf20Sopenharmony_ci return IRQ_HANDLED; 31388c2ecf20Sopenharmony_ci} 31398c2ecf20Sopenharmony_ci 31408c2ecf20Sopenharmony_cistatic int tegra_xudc_alloc_ep(struct tegra_xudc *xudc, unsigned int index) 31418c2ecf20Sopenharmony_ci{ 31428c2ecf20Sopenharmony_ci struct tegra_xudc_ep *ep = &xudc->ep[index]; 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_ci ep->xudc = xudc; 31458c2ecf20Sopenharmony_ci ep->index = index; 31468c2ecf20Sopenharmony_ci ep->context = &xudc->ep_context[index]; 31478c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ep->queue); 31488c2ecf20Sopenharmony_ci 31498c2ecf20Sopenharmony_ci /* 31508c2ecf20Sopenharmony_ci * EP1 would be the input endpoint corresponding to EP0, but since 31518c2ecf20Sopenharmony_ci * EP0 is bi-directional, EP1 is unused. 31528c2ecf20Sopenharmony_ci */ 31538c2ecf20Sopenharmony_ci if (index == 1) 31548c2ecf20Sopenharmony_ci return 0; 31558c2ecf20Sopenharmony_ci 31568c2ecf20Sopenharmony_ci ep->transfer_ring = dma_pool_alloc(xudc->transfer_ring_pool, 31578c2ecf20Sopenharmony_ci GFP_KERNEL, 31588c2ecf20Sopenharmony_ci &ep->transfer_ring_phys); 31598c2ecf20Sopenharmony_ci if (!ep->transfer_ring) 31608c2ecf20Sopenharmony_ci return -ENOMEM; 31618c2ecf20Sopenharmony_ci 31628c2ecf20Sopenharmony_ci if (index) { 31638c2ecf20Sopenharmony_ci snprintf(ep->name, sizeof(ep->name), "ep%u%s", index / 2, 31648c2ecf20Sopenharmony_ci (index % 2 == 0) ? "out" : "in"); 31658c2ecf20Sopenharmony_ci ep->usb_ep.name = ep->name; 31668c2ecf20Sopenharmony_ci usb_ep_set_maxpacket_limit(&ep->usb_ep, 1024); 31678c2ecf20Sopenharmony_ci ep->usb_ep.max_streams = 16; 31688c2ecf20Sopenharmony_ci ep->usb_ep.ops = &tegra_xudc_ep_ops; 31698c2ecf20Sopenharmony_ci ep->usb_ep.caps.type_bulk = true; 31708c2ecf20Sopenharmony_ci ep->usb_ep.caps.type_int = true; 31718c2ecf20Sopenharmony_ci if (index & 1) 31728c2ecf20Sopenharmony_ci ep->usb_ep.caps.dir_in = true; 31738c2ecf20Sopenharmony_ci else 31748c2ecf20Sopenharmony_ci ep->usb_ep.caps.dir_out = true; 31758c2ecf20Sopenharmony_ci list_add_tail(&ep->usb_ep.ep_list, &xudc->gadget.ep_list); 31768c2ecf20Sopenharmony_ci } else { 31778c2ecf20Sopenharmony_ci strscpy(ep->name, "ep0", 3); 31788c2ecf20Sopenharmony_ci ep->usb_ep.name = ep->name; 31798c2ecf20Sopenharmony_ci usb_ep_set_maxpacket_limit(&ep->usb_ep, 512); 31808c2ecf20Sopenharmony_ci ep->usb_ep.ops = &tegra_xudc_ep0_ops; 31818c2ecf20Sopenharmony_ci ep->usb_ep.caps.type_control = true; 31828c2ecf20Sopenharmony_ci ep->usb_ep.caps.dir_in = true; 31838c2ecf20Sopenharmony_ci ep->usb_ep.caps.dir_out = true; 31848c2ecf20Sopenharmony_ci } 31858c2ecf20Sopenharmony_ci 31868c2ecf20Sopenharmony_ci return 0; 31878c2ecf20Sopenharmony_ci} 31888c2ecf20Sopenharmony_ci 31898c2ecf20Sopenharmony_cistatic void tegra_xudc_free_ep(struct tegra_xudc *xudc, unsigned int index) 31908c2ecf20Sopenharmony_ci{ 31918c2ecf20Sopenharmony_ci struct tegra_xudc_ep *ep = &xudc->ep[index]; 31928c2ecf20Sopenharmony_ci 31938c2ecf20Sopenharmony_ci /* 31948c2ecf20Sopenharmony_ci * EP1 would be the input endpoint corresponding to EP0, but since 31958c2ecf20Sopenharmony_ci * EP0 is bi-directional, EP1 is unused. 31968c2ecf20Sopenharmony_ci */ 31978c2ecf20Sopenharmony_ci if (index == 1) 31988c2ecf20Sopenharmony_ci return; 31998c2ecf20Sopenharmony_ci 32008c2ecf20Sopenharmony_ci dma_pool_free(xudc->transfer_ring_pool, ep->transfer_ring, 32018c2ecf20Sopenharmony_ci ep->transfer_ring_phys); 32028c2ecf20Sopenharmony_ci} 32038c2ecf20Sopenharmony_ci 32048c2ecf20Sopenharmony_cistatic int tegra_xudc_alloc_eps(struct tegra_xudc *xudc) 32058c2ecf20Sopenharmony_ci{ 32068c2ecf20Sopenharmony_ci struct usb_request *req; 32078c2ecf20Sopenharmony_ci unsigned int i; 32088c2ecf20Sopenharmony_ci int err; 32098c2ecf20Sopenharmony_ci 32108c2ecf20Sopenharmony_ci xudc->ep_context = 32118c2ecf20Sopenharmony_ci dma_alloc_coherent(xudc->dev, XUDC_NR_EPS * 32128c2ecf20Sopenharmony_ci sizeof(*xudc->ep_context), 32138c2ecf20Sopenharmony_ci &xudc->ep_context_phys, GFP_KERNEL); 32148c2ecf20Sopenharmony_ci if (!xudc->ep_context) 32158c2ecf20Sopenharmony_ci return -ENOMEM; 32168c2ecf20Sopenharmony_ci 32178c2ecf20Sopenharmony_ci xudc->transfer_ring_pool = 32188c2ecf20Sopenharmony_ci dmam_pool_create(dev_name(xudc->dev), xudc->dev, 32198c2ecf20Sopenharmony_ci XUDC_TRANSFER_RING_SIZE * 32208c2ecf20Sopenharmony_ci sizeof(struct tegra_xudc_trb), 32218c2ecf20Sopenharmony_ci sizeof(struct tegra_xudc_trb), 0); 32228c2ecf20Sopenharmony_ci if (!xudc->transfer_ring_pool) { 32238c2ecf20Sopenharmony_ci err = -ENOMEM; 32248c2ecf20Sopenharmony_ci goto free_ep_context; 32258c2ecf20Sopenharmony_ci } 32268c2ecf20Sopenharmony_ci 32278c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&xudc->gadget.ep_list); 32288c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) { 32298c2ecf20Sopenharmony_ci err = tegra_xudc_alloc_ep(xudc, i); 32308c2ecf20Sopenharmony_ci if (err < 0) 32318c2ecf20Sopenharmony_ci goto free_eps; 32328c2ecf20Sopenharmony_ci } 32338c2ecf20Sopenharmony_ci 32348c2ecf20Sopenharmony_ci req = tegra_xudc_ep_alloc_request(&xudc->ep[0].usb_ep, GFP_KERNEL); 32358c2ecf20Sopenharmony_ci if (!req) { 32368c2ecf20Sopenharmony_ci err = -ENOMEM; 32378c2ecf20Sopenharmony_ci goto free_eps; 32388c2ecf20Sopenharmony_ci } 32398c2ecf20Sopenharmony_ci xudc->ep0_req = to_xudc_req(req); 32408c2ecf20Sopenharmony_ci 32418c2ecf20Sopenharmony_ci return 0; 32428c2ecf20Sopenharmony_ci 32438c2ecf20Sopenharmony_cifree_eps: 32448c2ecf20Sopenharmony_ci for (; i > 0; i--) 32458c2ecf20Sopenharmony_ci tegra_xudc_free_ep(xudc, i - 1); 32468c2ecf20Sopenharmony_cifree_ep_context: 32478c2ecf20Sopenharmony_ci dma_free_coherent(xudc->dev, XUDC_NR_EPS * sizeof(*xudc->ep_context), 32488c2ecf20Sopenharmony_ci xudc->ep_context, xudc->ep_context_phys); 32498c2ecf20Sopenharmony_ci return err; 32508c2ecf20Sopenharmony_ci} 32518c2ecf20Sopenharmony_ci 32528c2ecf20Sopenharmony_cistatic void tegra_xudc_init_eps(struct tegra_xudc *xudc) 32538c2ecf20Sopenharmony_ci{ 32548c2ecf20Sopenharmony_ci xudc_writel(xudc, lower_32_bits(xudc->ep_context_phys), ECPLO); 32558c2ecf20Sopenharmony_ci xudc_writel(xudc, upper_32_bits(xudc->ep_context_phys), ECPHI); 32568c2ecf20Sopenharmony_ci} 32578c2ecf20Sopenharmony_ci 32588c2ecf20Sopenharmony_cistatic void tegra_xudc_free_eps(struct tegra_xudc *xudc) 32598c2ecf20Sopenharmony_ci{ 32608c2ecf20Sopenharmony_ci unsigned int i; 32618c2ecf20Sopenharmony_ci 32628c2ecf20Sopenharmony_ci tegra_xudc_ep_free_request(&xudc->ep[0].usb_ep, 32638c2ecf20Sopenharmony_ci &xudc->ep0_req->usb_req); 32648c2ecf20Sopenharmony_ci 32658c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) 32668c2ecf20Sopenharmony_ci tegra_xudc_free_ep(xudc, i); 32678c2ecf20Sopenharmony_ci 32688c2ecf20Sopenharmony_ci dma_free_coherent(xudc->dev, XUDC_NR_EPS * sizeof(*xudc->ep_context), 32698c2ecf20Sopenharmony_ci xudc->ep_context, xudc->ep_context_phys); 32708c2ecf20Sopenharmony_ci} 32718c2ecf20Sopenharmony_ci 32728c2ecf20Sopenharmony_cistatic int tegra_xudc_alloc_event_ring(struct tegra_xudc *xudc) 32738c2ecf20Sopenharmony_ci{ 32748c2ecf20Sopenharmony_ci unsigned int i; 32758c2ecf20Sopenharmony_ci 32768c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) { 32778c2ecf20Sopenharmony_ci xudc->event_ring[i] = 32788c2ecf20Sopenharmony_ci dma_alloc_coherent(xudc->dev, XUDC_EVENT_RING_SIZE * 32798c2ecf20Sopenharmony_ci sizeof(*xudc->event_ring[i]), 32808c2ecf20Sopenharmony_ci &xudc->event_ring_phys[i], 32818c2ecf20Sopenharmony_ci GFP_KERNEL); 32828c2ecf20Sopenharmony_ci if (!xudc->event_ring[i]) 32838c2ecf20Sopenharmony_ci goto free_dma; 32848c2ecf20Sopenharmony_ci } 32858c2ecf20Sopenharmony_ci 32868c2ecf20Sopenharmony_ci return 0; 32878c2ecf20Sopenharmony_ci 32888c2ecf20Sopenharmony_cifree_dma: 32898c2ecf20Sopenharmony_ci for (; i > 0; i--) { 32908c2ecf20Sopenharmony_ci dma_free_coherent(xudc->dev, XUDC_EVENT_RING_SIZE * 32918c2ecf20Sopenharmony_ci sizeof(*xudc->event_ring[i - 1]), 32928c2ecf20Sopenharmony_ci xudc->event_ring[i - 1], 32938c2ecf20Sopenharmony_ci xudc->event_ring_phys[i - 1]); 32948c2ecf20Sopenharmony_ci } 32958c2ecf20Sopenharmony_ci return -ENOMEM; 32968c2ecf20Sopenharmony_ci} 32978c2ecf20Sopenharmony_ci 32988c2ecf20Sopenharmony_cistatic void tegra_xudc_init_event_ring(struct tegra_xudc *xudc) 32998c2ecf20Sopenharmony_ci{ 33008c2ecf20Sopenharmony_ci unsigned int i; 33018c2ecf20Sopenharmony_ci u32 val; 33028c2ecf20Sopenharmony_ci 33038c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) { 33048c2ecf20Sopenharmony_ci memset(xudc->event_ring[i], 0, XUDC_EVENT_RING_SIZE * 33058c2ecf20Sopenharmony_ci sizeof(*xudc->event_ring[i])); 33068c2ecf20Sopenharmony_ci 33078c2ecf20Sopenharmony_ci val = xudc_readl(xudc, ERSTSZ); 33088c2ecf20Sopenharmony_ci val &= ~(ERSTSZ_ERSTXSZ_MASK << ERSTSZ_ERSTXSZ_SHIFT(i)); 33098c2ecf20Sopenharmony_ci val |= XUDC_EVENT_RING_SIZE << ERSTSZ_ERSTXSZ_SHIFT(i); 33108c2ecf20Sopenharmony_ci xudc_writel(xudc, val, ERSTSZ); 33118c2ecf20Sopenharmony_ci 33128c2ecf20Sopenharmony_ci xudc_writel(xudc, lower_32_bits(xudc->event_ring_phys[i]), 33138c2ecf20Sopenharmony_ci ERSTXBALO(i)); 33148c2ecf20Sopenharmony_ci xudc_writel(xudc, upper_32_bits(xudc->event_ring_phys[i]), 33158c2ecf20Sopenharmony_ci ERSTXBAHI(i)); 33168c2ecf20Sopenharmony_ci } 33178c2ecf20Sopenharmony_ci 33188c2ecf20Sopenharmony_ci val = lower_32_bits(xudc->event_ring_phys[0]); 33198c2ecf20Sopenharmony_ci xudc_writel(xudc, val, ERDPLO); 33208c2ecf20Sopenharmony_ci val |= EREPLO_ECS; 33218c2ecf20Sopenharmony_ci xudc_writel(xudc, val, EREPLO); 33228c2ecf20Sopenharmony_ci 33238c2ecf20Sopenharmony_ci val = upper_32_bits(xudc->event_ring_phys[0]); 33248c2ecf20Sopenharmony_ci xudc_writel(xudc, val, ERDPHI); 33258c2ecf20Sopenharmony_ci xudc_writel(xudc, val, EREPHI); 33268c2ecf20Sopenharmony_ci 33278c2ecf20Sopenharmony_ci xudc->ccs = true; 33288c2ecf20Sopenharmony_ci xudc->event_ring_index = 0; 33298c2ecf20Sopenharmony_ci xudc->event_ring_deq_ptr = 0; 33308c2ecf20Sopenharmony_ci} 33318c2ecf20Sopenharmony_ci 33328c2ecf20Sopenharmony_cistatic void tegra_xudc_free_event_ring(struct tegra_xudc *xudc) 33338c2ecf20Sopenharmony_ci{ 33348c2ecf20Sopenharmony_ci unsigned int i; 33358c2ecf20Sopenharmony_ci 33368c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) { 33378c2ecf20Sopenharmony_ci dma_free_coherent(xudc->dev, XUDC_EVENT_RING_SIZE * 33388c2ecf20Sopenharmony_ci sizeof(*xudc->event_ring[i]), 33398c2ecf20Sopenharmony_ci xudc->event_ring[i], 33408c2ecf20Sopenharmony_ci xudc->event_ring_phys[i]); 33418c2ecf20Sopenharmony_ci } 33428c2ecf20Sopenharmony_ci} 33438c2ecf20Sopenharmony_ci 33448c2ecf20Sopenharmony_cistatic void tegra_xudc_fpci_ipfs_init(struct tegra_xudc *xudc) 33458c2ecf20Sopenharmony_ci{ 33468c2ecf20Sopenharmony_ci u32 val; 33478c2ecf20Sopenharmony_ci 33488c2ecf20Sopenharmony_ci if (xudc->soc->has_ipfs) { 33498c2ecf20Sopenharmony_ci val = ipfs_readl(xudc, XUSB_DEV_CONFIGURATION_0); 33508c2ecf20Sopenharmony_ci val |= XUSB_DEV_CONFIGURATION_0_EN_FPCI; 33518c2ecf20Sopenharmony_ci ipfs_writel(xudc, val, XUSB_DEV_CONFIGURATION_0); 33528c2ecf20Sopenharmony_ci usleep_range(10, 15); 33538c2ecf20Sopenharmony_ci } 33548c2ecf20Sopenharmony_ci 33558c2ecf20Sopenharmony_ci /* Enable bus master */ 33568c2ecf20Sopenharmony_ci val = XUSB_DEV_CFG_1_IO_SPACE_EN | XUSB_DEV_CFG_1_MEMORY_SPACE_EN | 33578c2ecf20Sopenharmony_ci XUSB_DEV_CFG_1_BUS_MASTER_EN; 33588c2ecf20Sopenharmony_ci fpci_writel(xudc, val, XUSB_DEV_CFG_1); 33598c2ecf20Sopenharmony_ci 33608c2ecf20Sopenharmony_ci /* Program BAR0 space */ 33618c2ecf20Sopenharmony_ci val = fpci_readl(xudc, XUSB_DEV_CFG_4); 33628c2ecf20Sopenharmony_ci val &= ~(XUSB_DEV_CFG_4_BASE_ADDR_MASK); 33638c2ecf20Sopenharmony_ci val |= xudc->phys_base & (XUSB_DEV_CFG_4_BASE_ADDR_MASK); 33648c2ecf20Sopenharmony_ci 33658c2ecf20Sopenharmony_ci fpci_writel(xudc, val, XUSB_DEV_CFG_4); 33668c2ecf20Sopenharmony_ci fpci_writel(xudc, upper_32_bits(xudc->phys_base), XUSB_DEV_CFG_5); 33678c2ecf20Sopenharmony_ci 33688c2ecf20Sopenharmony_ci usleep_range(100, 200); 33698c2ecf20Sopenharmony_ci 33708c2ecf20Sopenharmony_ci if (xudc->soc->has_ipfs) { 33718c2ecf20Sopenharmony_ci /* Enable interrupt assertion */ 33728c2ecf20Sopenharmony_ci val = ipfs_readl(xudc, XUSB_DEV_INTR_MASK_0); 33738c2ecf20Sopenharmony_ci val |= XUSB_DEV_INTR_MASK_0_IP_INT_MASK; 33748c2ecf20Sopenharmony_ci ipfs_writel(xudc, val, XUSB_DEV_INTR_MASK_0); 33758c2ecf20Sopenharmony_ci } 33768c2ecf20Sopenharmony_ci} 33778c2ecf20Sopenharmony_ci 33788c2ecf20Sopenharmony_cistatic void tegra_xudc_device_params_init(struct tegra_xudc *xudc) 33798c2ecf20Sopenharmony_ci{ 33808c2ecf20Sopenharmony_ci u32 val, imod; 33818c2ecf20Sopenharmony_ci 33828c2ecf20Sopenharmony_ci if (xudc->soc->has_ipfs) { 33838c2ecf20Sopenharmony_ci val = xudc_readl(xudc, BLCG); 33848c2ecf20Sopenharmony_ci val |= BLCG_ALL; 33858c2ecf20Sopenharmony_ci val &= ~(BLCG_DFPCI | BLCG_UFPCI | BLCG_FE | 33868c2ecf20Sopenharmony_ci BLCG_COREPLL_PWRDN); 33878c2ecf20Sopenharmony_ci val |= BLCG_IOPLL_0_PWRDN; 33888c2ecf20Sopenharmony_ci val |= BLCG_IOPLL_1_PWRDN; 33898c2ecf20Sopenharmony_ci val |= BLCG_IOPLL_2_PWRDN; 33908c2ecf20Sopenharmony_ci 33918c2ecf20Sopenharmony_ci xudc_writel(xudc, val, BLCG); 33928c2ecf20Sopenharmony_ci } 33938c2ecf20Sopenharmony_ci 33948c2ecf20Sopenharmony_ci if (xudc->soc->port_speed_quirk) 33958c2ecf20Sopenharmony_ci tegra_xudc_limit_port_speed(xudc); 33968c2ecf20Sopenharmony_ci 33978c2ecf20Sopenharmony_ci /* Set a reasonable U3 exit timer value. */ 33988c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_PADCTL4); 33998c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK); 34008c2ecf20Sopenharmony_ci val |= SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3(0x5dc0); 34018c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_PADCTL4); 34028c2ecf20Sopenharmony_ci 34038c2ecf20Sopenharmony_ci /* Default ping LFPS tBurst is too large. */ 34048c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT0); 34058c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT0_PING_TBURST_MASK); 34068c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT0_PING_TBURST(0xa); 34078c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT0); 34088c2ecf20Sopenharmony_ci 34098c2ecf20Sopenharmony_ci /* Default tPortConfiguration timeout is too small. */ 34108c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT30); 34118c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT30_LMPITP_TIMER_MASK); 34128c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT30_LMPITP_TIMER(0x978); 34138c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT30); 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_ci if (xudc->soc->lpm_enable) { 34168c2ecf20Sopenharmony_ci /* Set L1 resume duration to 95 us. */ 34178c2ecf20Sopenharmony_ci val = xudc_readl(xudc, HSFSPI_COUNT13); 34188c2ecf20Sopenharmony_ci val &= ~(HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK); 34198c2ecf20Sopenharmony_ci val |= HSFSPI_COUNT13_U2_RESUME_K_DURATION(0x2c88); 34208c2ecf20Sopenharmony_ci xudc_writel(xudc, val, HSFSPI_COUNT13); 34218c2ecf20Sopenharmony_ci } 34228c2ecf20Sopenharmony_ci 34238c2ecf20Sopenharmony_ci /* 34248c2ecf20Sopenharmony_ci * Compliacne suite appears to be violating polling LFPS tBurst max 34258c2ecf20Sopenharmony_ci * of 1.4us. Send 1.45us instead. 34268c2ecf20Sopenharmony_ci */ 34278c2ecf20Sopenharmony_ci val = xudc_readl(xudc, SSPX_CORE_CNT32); 34288c2ecf20Sopenharmony_ci val &= ~(SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK); 34298c2ecf20Sopenharmony_ci val |= SSPX_CORE_CNT32_POLL_TBURST_MAX(0xb0); 34308c2ecf20Sopenharmony_ci xudc_writel(xudc, val, SSPX_CORE_CNT32); 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_ci /* Direct HS/FS port instance to RxDetect. */ 34338c2ecf20Sopenharmony_ci val = xudc_readl(xudc, CFG_DEV_FE); 34348c2ecf20Sopenharmony_ci val &= ~(CFG_DEV_FE_PORTREGSEL_MASK); 34358c2ecf20Sopenharmony_ci val |= CFG_DEV_FE_PORTREGSEL(CFG_DEV_FE_PORTREGSEL_HSFS_PI); 34368c2ecf20Sopenharmony_ci xudc_writel(xudc, val, CFG_DEV_FE); 34378c2ecf20Sopenharmony_ci 34388c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTSC); 34398c2ecf20Sopenharmony_ci val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK); 34408c2ecf20Sopenharmony_ci val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_RXDETECT); 34418c2ecf20Sopenharmony_ci xudc_writel(xudc, val, PORTSC); 34428c2ecf20Sopenharmony_ci 34438c2ecf20Sopenharmony_ci /* Direct SS port instance to RxDetect. */ 34448c2ecf20Sopenharmony_ci val = xudc_readl(xudc, CFG_DEV_FE); 34458c2ecf20Sopenharmony_ci val &= ~(CFG_DEV_FE_PORTREGSEL_MASK); 34468c2ecf20Sopenharmony_ci val |= CFG_DEV_FE_PORTREGSEL_SS_PI & CFG_DEV_FE_PORTREGSEL_MASK; 34478c2ecf20Sopenharmony_ci xudc_writel(xudc, val, CFG_DEV_FE); 34488c2ecf20Sopenharmony_ci 34498c2ecf20Sopenharmony_ci val = xudc_readl(xudc, PORTSC); 34508c2ecf20Sopenharmony_ci val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK); 34518c2ecf20Sopenharmony_ci val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_RXDETECT); 34528c2ecf20Sopenharmony_ci xudc_writel(xudc, val, PORTSC); 34538c2ecf20Sopenharmony_ci 34548c2ecf20Sopenharmony_ci /* Restore port instance. */ 34558c2ecf20Sopenharmony_ci val = xudc_readl(xudc, CFG_DEV_FE); 34568c2ecf20Sopenharmony_ci val &= ~(CFG_DEV_FE_PORTREGSEL_MASK); 34578c2ecf20Sopenharmony_ci xudc_writel(xudc, val, CFG_DEV_FE); 34588c2ecf20Sopenharmony_ci 34598c2ecf20Sopenharmony_ci /* 34608c2ecf20Sopenharmony_ci * Enable INFINITE_SS_RETRY to prevent device from entering 34618c2ecf20Sopenharmony_ci * Disabled.Error when attached to buggy SuperSpeed hubs. 34628c2ecf20Sopenharmony_ci */ 34638c2ecf20Sopenharmony_ci val = xudc_readl(xudc, CFG_DEV_FE); 34648c2ecf20Sopenharmony_ci val |= CFG_DEV_FE_INFINITE_SS_RETRY; 34658c2ecf20Sopenharmony_ci xudc_writel(xudc, val, CFG_DEV_FE); 34668c2ecf20Sopenharmony_ci 34678c2ecf20Sopenharmony_ci /* Set interrupt moderation. */ 34688c2ecf20Sopenharmony_ci imod = XUDC_INTERRUPT_MODERATION_US * 4; 34698c2ecf20Sopenharmony_ci val = xudc_readl(xudc, RT_IMOD); 34708c2ecf20Sopenharmony_ci val &= ~((RT_IMOD_IMODI_MASK) | (RT_IMOD_IMODC_MASK)); 34718c2ecf20Sopenharmony_ci val |= (RT_IMOD_IMODI(imod) | RT_IMOD_IMODC(imod)); 34728c2ecf20Sopenharmony_ci xudc_writel(xudc, val, RT_IMOD); 34738c2ecf20Sopenharmony_ci 34748c2ecf20Sopenharmony_ci /* increase SSPI transaction timeout from 32us to 512us */ 34758c2ecf20Sopenharmony_ci val = xudc_readl(xudc, CFG_DEV_SSPI_XFER); 34768c2ecf20Sopenharmony_ci val &= ~(CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK); 34778c2ecf20Sopenharmony_ci val |= CFG_DEV_SSPI_XFER_ACKTIMEOUT(0xf000); 34788c2ecf20Sopenharmony_ci xudc_writel(xudc, val, CFG_DEV_SSPI_XFER); 34798c2ecf20Sopenharmony_ci} 34808c2ecf20Sopenharmony_ci 34818c2ecf20Sopenharmony_cistatic int tegra_xudc_phy_get(struct tegra_xudc *xudc) 34828c2ecf20Sopenharmony_ci{ 34838c2ecf20Sopenharmony_ci int err = 0, usb3; 34848c2ecf20Sopenharmony_ci unsigned int i; 34858c2ecf20Sopenharmony_ci 34868c2ecf20Sopenharmony_ci xudc->utmi_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys, 34878c2ecf20Sopenharmony_ci sizeof(*xudc->utmi_phy), GFP_KERNEL); 34888c2ecf20Sopenharmony_ci if (!xudc->utmi_phy) 34898c2ecf20Sopenharmony_ci return -ENOMEM; 34908c2ecf20Sopenharmony_ci 34918c2ecf20Sopenharmony_ci xudc->usb3_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys, 34928c2ecf20Sopenharmony_ci sizeof(*xudc->usb3_phy), GFP_KERNEL); 34938c2ecf20Sopenharmony_ci if (!xudc->usb3_phy) 34948c2ecf20Sopenharmony_ci return -ENOMEM; 34958c2ecf20Sopenharmony_ci 34968c2ecf20Sopenharmony_ci xudc->usbphy = devm_kcalloc(xudc->dev, xudc->soc->num_phys, 34978c2ecf20Sopenharmony_ci sizeof(*xudc->usbphy), GFP_KERNEL); 34988c2ecf20Sopenharmony_ci if (!xudc->usbphy) 34998c2ecf20Sopenharmony_ci return -ENOMEM; 35008c2ecf20Sopenharmony_ci 35018c2ecf20Sopenharmony_ci xudc->vbus_nb.notifier_call = tegra_xudc_vbus_notify; 35028c2ecf20Sopenharmony_ci 35038c2ecf20Sopenharmony_ci for (i = 0; i < xudc->soc->num_phys; i++) { 35048c2ecf20Sopenharmony_ci char phy_name[] = "usb.-."; 35058c2ecf20Sopenharmony_ci 35068c2ecf20Sopenharmony_ci /* Get USB2 phy */ 35078c2ecf20Sopenharmony_ci snprintf(phy_name, sizeof(phy_name), "usb2-%d", i); 35088c2ecf20Sopenharmony_ci xudc->utmi_phy[i] = devm_phy_optional_get(xudc->dev, phy_name); 35098c2ecf20Sopenharmony_ci if (IS_ERR(xudc->utmi_phy[i])) { 35108c2ecf20Sopenharmony_ci err = PTR_ERR(xudc->utmi_phy[i]); 35118c2ecf20Sopenharmony_ci if (err != -EPROBE_DEFER) 35128c2ecf20Sopenharmony_ci dev_err(xudc->dev, "failed to get usb2-%d PHY: %d\n", 35138c2ecf20Sopenharmony_ci i, err); 35148c2ecf20Sopenharmony_ci 35158c2ecf20Sopenharmony_ci goto clean_up; 35168c2ecf20Sopenharmony_ci } else if (xudc->utmi_phy[i]) { 35178c2ecf20Sopenharmony_ci /* Get usb-phy, if utmi phy is available */ 35188c2ecf20Sopenharmony_ci xudc->usbphy[i] = devm_usb_get_phy_by_node(xudc->dev, 35198c2ecf20Sopenharmony_ci xudc->utmi_phy[i]->dev.of_node, 35208c2ecf20Sopenharmony_ci &xudc->vbus_nb); 35218c2ecf20Sopenharmony_ci if (IS_ERR(xudc->usbphy[i])) { 35228c2ecf20Sopenharmony_ci err = PTR_ERR(xudc->usbphy[i]); 35238c2ecf20Sopenharmony_ci dev_err(xudc->dev, "failed to get usbphy-%d: %d\n", 35248c2ecf20Sopenharmony_ci i, err); 35258c2ecf20Sopenharmony_ci goto clean_up; 35268c2ecf20Sopenharmony_ci } 35278c2ecf20Sopenharmony_ci } else if (!xudc->utmi_phy[i]) { 35288c2ecf20Sopenharmony_ci /* if utmi phy is not available, ignore USB3 phy get */ 35298c2ecf20Sopenharmony_ci continue; 35308c2ecf20Sopenharmony_ci } 35318c2ecf20Sopenharmony_ci 35328c2ecf20Sopenharmony_ci /* Get USB3 phy */ 35338c2ecf20Sopenharmony_ci usb3 = tegra_xusb_padctl_get_usb3_companion(xudc->padctl, i); 35348c2ecf20Sopenharmony_ci if (usb3 < 0) 35358c2ecf20Sopenharmony_ci continue; 35368c2ecf20Sopenharmony_ci 35378c2ecf20Sopenharmony_ci snprintf(phy_name, sizeof(phy_name), "usb3-%d", usb3); 35388c2ecf20Sopenharmony_ci xudc->usb3_phy[i] = devm_phy_optional_get(xudc->dev, phy_name); 35398c2ecf20Sopenharmony_ci if (IS_ERR(xudc->usb3_phy[i])) { 35408c2ecf20Sopenharmony_ci err = PTR_ERR(xudc->usb3_phy[i]); 35418c2ecf20Sopenharmony_ci if (err != -EPROBE_DEFER) 35428c2ecf20Sopenharmony_ci dev_err(xudc->dev, "failed to get usb3-%d PHY: %d\n", 35438c2ecf20Sopenharmony_ci usb3, err); 35448c2ecf20Sopenharmony_ci 35458c2ecf20Sopenharmony_ci goto clean_up; 35468c2ecf20Sopenharmony_ci } else if (xudc->usb3_phy[i]) 35478c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "usb3-%d PHY registered", usb3); 35488c2ecf20Sopenharmony_ci } 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_ci return err; 35518c2ecf20Sopenharmony_ci 35528c2ecf20Sopenharmony_ciclean_up: 35538c2ecf20Sopenharmony_ci for (i = 0; i < xudc->soc->num_phys; i++) { 35548c2ecf20Sopenharmony_ci xudc->usb3_phy[i] = NULL; 35558c2ecf20Sopenharmony_ci xudc->utmi_phy[i] = NULL; 35568c2ecf20Sopenharmony_ci xudc->usbphy[i] = NULL; 35578c2ecf20Sopenharmony_ci } 35588c2ecf20Sopenharmony_ci 35598c2ecf20Sopenharmony_ci return err; 35608c2ecf20Sopenharmony_ci} 35618c2ecf20Sopenharmony_ci 35628c2ecf20Sopenharmony_cistatic void tegra_xudc_phy_exit(struct tegra_xudc *xudc) 35638c2ecf20Sopenharmony_ci{ 35648c2ecf20Sopenharmony_ci unsigned int i; 35658c2ecf20Sopenharmony_ci 35668c2ecf20Sopenharmony_ci for (i = 0; i < xudc->soc->num_phys; i++) { 35678c2ecf20Sopenharmony_ci phy_exit(xudc->usb3_phy[i]); 35688c2ecf20Sopenharmony_ci phy_exit(xudc->utmi_phy[i]); 35698c2ecf20Sopenharmony_ci } 35708c2ecf20Sopenharmony_ci} 35718c2ecf20Sopenharmony_ci 35728c2ecf20Sopenharmony_cistatic int tegra_xudc_phy_init(struct tegra_xudc *xudc) 35738c2ecf20Sopenharmony_ci{ 35748c2ecf20Sopenharmony_ci int err; 35758c2ecf20Sopenharmony_ci unsigned int i; 35768c2ecf20Sopenharmony_ci 35778c2ecf20Sopenharmony_ci for (i = 0; i < xudc->soc->num_phys; i++) { 35788c2ecf20Sopenharmony_ci err = phy_init(xudc->utmi_phy[i]); 35798c2ecf20Sopenharmony_ci if (err < 0) { 35808c2ecf20Sopenharmony_ci dev_err(xudc->dev, "UTMI PHY #%u initialization failed: %d\n", i, err); 35818c2ecf20Sopenharmony_ci goto exit_phy; 35828c2ecf20Sopenharmony_ci } 35838c2ecf20Sopenharmony_ci 35848c2ecf20Sopenharmony_ci err = phy_init(xudc->usb3_phy[i]); 35858c2ecf20Sopenharmony_ci if (err < 0) { 35868c2ecf20Sopenharmony_ci dev_err(xudc->dev, "USB3 PHY #%u initialization failed: %d\n", i, err); 35878c2ecf20Sopenharmony_ci goto exit_phy; 35888c2ecf20Sopenharmony_ci } 35898c2ecf20Sopenharmony_ci } 35908c2ecf20Sopenharmony_ci return 0; 35918c2ecf20Sopenharmony_ci 35928c2ecf20Sopenharmony_ciexit_phy: 35938c2ecf20Sopenharmony_ci tegra_xudc_phy_exit(xudc); 35948c2ecf20Sopenharmony_ci return err; 35958c2ecf20Sopenharmony_ci} 35968c2ecf20Sopenharmony_ci 35978c2ecf20Sopenharmony_cistatic const char * const tegra210_xudc_supply_names[] = { 35988c2ecf20Sopenharmony_ci "hvdd-usb", 35998c2ecf20Sopenharmony_ci "avddio-usb", 36008c2ecf20Sopenharmony_ci}; 36018c2ecf20Sopenharmony_ci 36028c2ecf20Sopenharmony_cistatic const char * const tegra210_xudc_clock_names[] = { 36038c2ecf20Sopenharmony_ci "dev", 36048c2ecf20Sopenharmony_ci "ss", 36058c2ecf20Sopenharmony_ci "ss_src", 36068c2ecf20Sopenharmony_ci "hs_src", 36078c2ecf20Sopenharmony_ci "fs_src", 36088c2ecf20Sopenharmony_ci}; 36098c2ecf20Sopenharmony_ci 36108c2ecf20Sopenharmony_cistatic const char * const tegra186_xudc_clock_names[] = { 36118c2ecf20Sopenharmony_ci "dev", 36128c2ecf20Sopenharmony_ci "ss", 36138c2ecf20Sopenharmony_ci "ss_src", 36148c2ecf20Sopenharmony_ci "fs_src", 36158c2ecf20Sopenharmony_ci}; 36168c2ecf20Sopenharmony_ci 36178c2ecf20Sopenharmony_cistatic struct tegra_xudc_soc tegra210_xudc_soc_data = { 36188c2ecf20Sopenharmony_ci .supply_names = tegra210_xudc_supply_names, 36198c2ecf20Sopenharmony_ci .num_supplies = ARRAY_SIZE(tegra210_xudc_supply_names), 36208c2ecf20Sopenharmony_ci .clock_names = tegra210_xudc_clock_names, 36218c2ecf20Sopenharmony_ci .num_clks = ARRAY_SIZE(tegra210_xudc_clock_names), 36228c2ecf20Sopenharmony_ci .num_phys = 4, 36238c2ecf20Sopenharmony_ci .u1_enable = false, 36248c2ecf20Sopenharmony_ci .u2_enable = true, 36258c2ecf20Sopenharmony_ci .lpm_enable = false, 36268c2ecf20Sopenharmony_ci .invalid_seq_num = true, 36278c2ecf20Sopenharmony_ci .pls_quirk = true, 36288c2ecf20Sopenharmony_ci .port_reset_quirk = true, 36298c2ecf20Sopenharmony_ci .port_speed_quirk = false, 36308c2ecf20Sopenharmony_ci .has_ipfs = true, 36318c2ecf20Sopenharmony_ci}; 36328c2ecf20Sopenharmony_ci 36338c2ecf20Sopenharmony_cistatic struct tegra_xudc_soc tegra186_xudc_soc_data = { 36348c2ecf20Sopenharmony_ci .clock_names = tegra186_xudc_clock_names, 36358c2ecf20Sopenharmony_ci .num_clks = ARRAY_SIZE(tegra186_xudc_clock_names), 36368c2ecf20Sopenharmony_ci .num_phys = 4, 36378c2ecf20Sopenharmony_ci .u1_enable = true, 36388c2ecf20Sopenharmony_ci .u2_enable = true, 36398c2ecf20Sopenharmony_ci .lpm_enable = false, 36408c2ecf20Sopenharmony_ci .invalid_seq_num = false, 36418c2ecf20Sopenharmony_ci .pls_quirk = false, 36428c2ecf20Sopenharmony_ci .port_reset_quirk = false, 36438c2ecf20Sopenharmony_ci .port_speed_quirk = false, 36448c2ecf20Sopenharmony_ci .has_ipfs = false, 36458c2ecf20Sopenharmony_ci}; 36468c2ecf20Sopenharmony_ci 36478c2ecf20Sopenharmony_cistatic struct tegra_xudc_soc tegra194_xudc_soc_data = { 36488c2ecf20Sopenharmony_ci .clock_names = tegra186_xudc_clock_names, 36498c2ecf20Sopenharmony_ci .num_clks = ARRAY_SIZE(tegra186_xudc_clock_names), 36508c2ecf20Sopenharmony_ci .num_phys = 4, 36518c2ecf20Sopenharmony_ci .u1_enable = true, 36528c2ecf20Sopenharmony_ci .u2_enable = true, 36538c2ecf20Sopenharmony_ci .lpm_enable = true, 36548c2ecf20Sopenharmony_ci .invalid_seq_num = false, 36558c2ecf20Sopenharmony_ci .pls_quirk = false, 36568c2ecf20Sopenharmony_ci .port_reset_quirk = false, 36578c2ecf20Sopenharmony_ci .port_speed_quirk = true, 36588c2ecf20Sopenharmony_ci .has_ipfs = false, 36598c2ecf20Sopenharmony_ci}; 36608c2ecf20Sopenharmony_ci 36618c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_xudc_of_match[] = { 36628c2ecf20Sopenharmony_ci { 36638c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra210-xudc", 36648c2ecf20Sopenharmony_ci .data = &tegra210_xudc_soc_data 36658c2ecf20Sopenharmony_ci }, 36668c2ecf20Sopenharmony_ci { 36678c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra186-xudc", 36688c2ecf20Sopenharmony_ci .data = &tegra186_xudc_soc_data 36698c2ecf20Sopenharmony_ci }, 36708c2ecf20Sopenharmony_ci { 36718c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra194-xudc", 36728c2ecf20Sopenharmony_ci .data = &tegra194_xudc_soc_data 36738c2ecf20Sopenharmony_ci }, 36748c2ecf20Sopenharmony_ci { } 36758c2ecf20Sopenharmony_ci}; 36768c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_xudc_of_match); 36778c2ecf20Sopenharmony_ci 36788c2ecf20Sopenharmony_cistatic void tegra_xudc_powerdomain_remove(struct tegra_xudc *xudc) 36798c2ecf20Sopenharmony_ci{ 36808c2ecf20Sopenharmony_ci if (xudc->genpd_dl_ss) 36818c2ecf20Sopenharmony_ci device_link_del(xudc->genpd_dl_ss); 36828c2ecf20Sopenharmony_ci if (xudc->genpd_dl_device) 36838c2ecf20Sopenharmony_ci device_link_del(xudc->genpd_dl_device); 36848c2ecf20Sopenharmony_ci if (xudc->genpd_dev_ss) 36858c2ecf20Sopenharmony_ci dev_pm_domain_detach(xudc->genpd_dev_ss, true); 36868c2ecf20Sopenharmony_ci if (xudc->genpd_dev_device) 36878c2ecf20Sopenharmony_ci dev_pm_domain_detach(xudc->genpd_dev_device, true); 36888c2ecf20Sopenharmony_ci} 36898c2ecf20Sopenharmony_ci 36908c2ecf20Sopenharmony_cistatic int tegra_xudc_powerdomain_init(struct tegra_xudc *xudc) 36918c2ecf20Sopenharmony_ci{ 36928c2ecf20Sopenharmony_ci struct device *dev = xudc->dev; 36938c2ecf20Sopenharmony_ci int err; 36948c2ecf20Sopenharmony_ci 36958c2ecf20Sopenharmony_ci xudc->genpd_dev_device = dev_pm_domain_attach_by_name(dev, "dev"); 36968c2ecf20Sopenharmony_ci if (IS_ERR(xudc->genpd_dev_device)) { 36978c2ecf20Sopenharmony_ci err = PTR_ERR(xudc->genpd_dev_device); 36988c2ecf20Sopenharmony_ci dev_err(dev, "failed to get device power domain: %d\n", err); 36998c2ecf20Sopenharmony_ci return err; 37008c2ecf20Sopenharmony_ci } 37018c2ecf20Sopenharmony_ci 37028c2ecf20Sopenharmony_ci xudc->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "ss"); 37038c2ecf20Sopenharmony_ci if (IS_ERR(xudc->genpd_dev_ss)) { 37048c2ecf20Sopenharmony_ci err = PTR_ERR(xudc->genpd_dev_ss); 37058c2ecf20Sopenharmony_ci dev_err(dev, "failed to get SuperSpeed power domain: %d\n", err); 37068c2ecf20Sopenharmony_ci return err; 37078c2ecf20Sopenharmony_ci } 37088c2ecf20Sopenharmony_ci 37098c2ecf20Sopenharmony_ci xudc->genpd_dl_device = device_link_add(dev, xudc->genpd_dev_device, 37108c2ecf20Sopenharmony_ci DL_FLAG_PM_RUNTIME | 37118c2ecf20Sopenharmony_ci DL_FLAG_STATELESS); 37128c2ecf20Sopenharmony_ci if (!xudc->genpd_dl_device) { 37138c2ecf20Sopenharmony_ci dev_err(dev, "failed to add USB device link\n"); 37148c2ecf20Sopenharmony_ci return -ENODEV; 37158c2ecf20Sopenharmony_ci } 37168c2ecf20Sopenharmony_ci 37178c2ecf20Sopenharmony_ci xudc->genpd_dl_ss = device_link_add(dev, xudc->genpd_dev_ss, 37188c2ecf20Sopenharmony_ci DL_FLAG_PM_RUNTIME | 37198c2ecf20Sopenharmony_ci DL_FLAG_STATELESS); 37208c2ecf20Sopenharmony_ci if (!xudc->genpd_dl_ss) { 37218c2ecf20Sopenharmony_ci dev_err(dev, "failed to add SuperSpeed device link\n"); 37228c2ecf20Sopenharmony_ci return -ENODEV; 37238c2ecf20Sopenharmony_ci } 37248c2ecf20Sopenharmony_ci 37258c2ecf20Sopenharmony_ci return 0; 37268c2ecf20Sopenharmony_ci} 37278c2ecf20Sopenharmony_ci 37288c2ecf20Sopenharmony_cistatic int tegra_xudc_probe(struct platform_device *pdev) 37298c2ecf20Sopenharmony_ci{ 37308c2ecf20Sopenharmony_ci struct tegra_xudc *xudc; 37318c2ecf20Sopenharmony_ci struct resource *res; 37328c2ecf20Sopenharmony_ci unsigned int i; 37338c2ecf20Sopenharmony_ci int err; 37348c2ecf20Sopenharmony_ci 37358c2ecf20Sopenharmony_ci xudc = devm_kzalloc(&pdev->dev, sizeof(*xudc), GFP_KERNEL); 37368c2ecf20Sopenharmony_ci if (!xudc) 37378c2ecf20Sopenharmony_ci return -ENOMEM; 37388c2ecf20Sopenharmony_ci 37398c2ecf20Sopenharmony_ci xudc->dev = &pdev->dev; 37408c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, xudc); 37418c2ecf20Sopenharmony_ci 37428c2ecf20Sopenharmony_ci xudc->soc = of_device_get_match_data(&pdev->dev); 37438c2ecf20Sopenharmony_ci if (!xudc->soc) 37448c2ecf20Sopenharmony_ci return -ENODEV; 37458c2ecf20Sopenharmony_ci 37468c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); 37478c2ecf20Sopenharmony_ci xudc->base = devm_ioremap_resource(&pdev->dev, res); 37488c2ecf20Sopenharmony_ci if (IS_ERR(xudc->base)) 37498c2ecf20Sopenharmony_ci return PTR_ERR(xudc->base); 37508c2ecf20Sopenharmony_ci xudc->phys_base = res->start; 37518c2ecf20Sopenharmony_ci 37528c2ecf20Sopenharmony_ci xudc->fpci = devm_platform_ioremap_resource_byname(pdev, "fpci"); 37538c2ecf20Sopenharmony_ci if (IS_ERR(xudc->fpci)) 37548c2ecf20Sopenharmony_ci return PTR_ERR(xudc->fpci); 37558c2ecf20Sopenharmony_ci 37568c2ecf20Sopenharmony_ci if (xudc->soc->has_ipfs) { 37578c2ecf20Sopenharmony_ci xudc->ipfs = devm_platform_ioremap_resource_byname(pdev, "ipfs"); 37588c2ecf20Sopenharmony_ci if (IS_ERR(xudc->ipfs)) 37598c2ecf20Sopenharmony_ci return PTR_ERR(xudc->ipfs); 37608c2ecf20Sopenharmony_ci } 37618c2ecf20Sopenharmony_ci 37628c2ecf20Sopenharmony_ci xudc->irq = platform_get_irq(pdev, 0); 37638c2ecf20Sopenharmony_ci if (xudc->irq < 0) 37648c2ecf20Sopenharmony_ci return xudc->irq; 37658c2ecf20Sopenharmony_ci 37668c2ecf20Sopenharmony_ci err = devm_request_irq(&pdev->dev, xudc->irq, tegra_xudc_irq, 0, 37678c2ecf20Sopenharmony_ci dev_name(&pdev->dev), xudc); 37688c2ecf20Sopenharmony_ci if (err < 0) { 37698c2ecf20Sopenharmony_ci dev_err(xudc->dev, "failed to claim IRQ#%u: %d\n", xudc->irq, 37708c2ecf20Sopenharmony_ci err); 37718c2ecf20Sopenharmony_ci return err; 37728c2ecf20Sopenharmony_ci } 37738c2ecf20Sopenharmony_ci 37748c2ecf20Sopenharmony_ci xudc->clks = devm_kcalloc(&pdev->dev, xudc->soc->num_clks, sizeof(*xudc->clks), 37758c2ecf20Sopenharmony_ci GFP_KERNEL); 37768c2ecf20Sopenharmony_ci if (!xudc->clks) 37778c2ecf20Sopenharmony_ci return -ENOMEM; 37788c2ecf20Sopenharmony_ci 37798c2ecf20Sopenharmony_ci for (i = 0; i < xudc->soc->num_clks; i++) 37808c2ecf20Sopenharmony_ci xudc->clks[i].id = xudc->soc->clock_names[i]; 37818c2ecf20Sopenharmony_ci 37828c2ecf20Sopenharmony_ci err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks, xudc->clks); 37838c2ecf20Sopenharmony_ci if (err) { 37848c2ecf20Sopenharmony_ci if (err != -EPROBE_DEFER) 37858c2ecf20Sopenharmony_ci dev_err(xudc->dev, "failed to request clocks: %d\n", err); 37868c2ecf20Sopenharmony_ci 37878c2ecf20Sopenharmony_ci return err; 37888c2ecf20Sopenharmony_ci } 37898c2ecf20Sopenharmony_ci 37908c2ecf20Sopenharmony_ci xudc->supplies = devm_kcalloc(&pdev->dev, xudc->soc->num_supplies, 37918c2ecf20Sopenharmony_ci sizeof(*xudc->supplies), GFP_KERNEL); 37928c2ecf20Sopenharmony_ci if (!xudc->supplies) 37938c2ecf20Sopenharmony_ci return -ENOMEM; 37948c2ecf20Sopenharmony_ci 37958c2ecf20Sopenharmony_ci for (i = 0; i < xudc->soc->num_supplies; i++) 37968c2ecf20Sopenharmony_ci xudc->supplies[i].supply = xudc->soc->supply_names[i]; 37978c2ecf20Sopenharmony_ci 37988c2ecf20Sopenharmony_ci err = devm_regulator_bulk_get(&pdev->dev, xudc->soc->num_supplies, 37998c2ecf20Sopenharmony_ci xudc->supplies); 38008c2ecf20Sopenharmony_ci if (err) { 38018c2ecf20Sopenharmony_ci if (err != -EPROBE_DEFER) 38028c2ecf20Sopenharmony_ci dev_err(xudc->dev, "failed to request regulators: %d\n", err); 38038c2ecf20Sopenharmony_ci 38048c2ecf20Sopenharmony_ci return err; 38058c2ecf20Sopenharmony_ci } 38068c2ecf20Sopenharmony_ci 38078c2ecf20Sopenharmony_ci xudc->padctl = tegra_xusb_padctl_get(&pdev->dev); 38088c2ecf20Sopenharmony_ci if (IS_ERR(xudc->padctl)) 38098c2ecf20Sopenharmony_ci return PTR_ERR(xudc->padctl); 38108c2ecf20Sopenharmony_ci 38118c2ecf20Sopenharmony_ci err = regulator_bulk_enable(xudc->soc->num_supplies, xudc->supplies); 38128c2ecf20Sopenharmony_ci if (err) { 38138c2ecf20Sopenharmony_ci dev_err(xudc->dev, "failed to enable regulators: %d\n", err); 38148c2ecf20Sopenharmony_ci goto put_padctl; 38158c2ecf20Sopenharmony_ci } 38168c2ecf20Sopenharmony_ci 38178c2ecf20Sopenharmony_ci err = tegra_xudc_phy_get(xudc); 38188c2ecf20Sopenharmony_ci if (err) 38198c2ecf20Sopenharmony_ci goto disable_regulator; 38208c2ecf20Sopenharmony_ci 38218c2ecf20Sopenharmony_ci err = tegra_xudc_powerdomain_init(xudc); 38228c2ecf20Sopenharmony_ci if (err) 38238c2ecf20Sopenharmony_ci goto put_powerdomains; 38248c2ecf20Sopenharmony_ci 38258c2ecf20Sopenharmony_ci err = tegra_xudc_phy_init(xudc); 38268c2ecf20Sopenharmony_ci if (err) 38278c2ecf20Sopenharmony_ci goto put_powerdomains; 38288c2ecf20Sopenharmony_ci 38298c2ecf20Sopenharmony_ci err = tegra_xudc_alloc_event_ring(xudc); 38308c2ecf20Sopenharmony_ci if (err) 38318c2ecf20Sopenharmony_ci goto disable_phy; 38328c2ecf20Sopenharmony_ci 38338c2ecf20Sopenharmony_ci err = tegra_xudc_alloc_eps(xudc); 38348c2ecf20Sopenharmony_ci if (err) 38358c2ecf20Sopenharmony_ci goto free_event_ring; 38368c2ecf20Sopenharmony_ci 38378c2ecf20Sopenharmony_ci spin_lock_init(&xudc->lock); 38388c2ecf20Sopenharmony_ci 38398c2ecf20Sopenharmony_ci init_completion(&xudc->disconnect_complete); 38408c2ecf20Sopenharmony_ci 38418c2ecf20Sopenharmony_ci INIT_WORK(&xudc->usb_role_sw_work, tegra_xudc_usb_role_sw_work); 38428c2ecf20Sopenharmony_ci 38438c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&xudc->plc_reset_work, tegra_xudc_plc_reset_work); 38448c2ecf20Sopenharmony_ci 38458c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&xudc->port_reset_war_work, 38468c2ecf20Sopenharmony_ci tegra_xudc_port_reset_war_work); 38478c2ecf20Sopenharmony_ci 38488c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 38498c2ecf20Sopenharmony_ci 38508c2ecf20Sopenharmony_ci xudc->gadget.ops = &tegra_xudc_gadget_ops; 38518c2ecf20Sopenharmony_ci xudc->gadget.ep0 = &xudc->ep[0].usb_ep; 38528c2ecf20Sopenharmony_ci xudc->gadget.name = "tegra-xudc"; 38538c2ecf20Sopenharmony_ci xudc->gadget.max_speed = USB_SPEED_SUPER; 38548c2ecf20Sopenharmony_ci 38558c2ecf20Sopenharmony_ci err = usb_add_gadget_udc(&pdev->dev, &xudc->gadget); 38568c2ecf20Sopenharmony_ci if (err) { 38578c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to add USB gadget: %d\n", err); 38588c2ecf20Sopenharmony_ci goto free_eps; 38598c2ecf20Sopenharmony_ci } 38608c2ecf20Sopenharmony_ci 38618c2ecf20Sopenharmony_ci return 0; 38628c2ecf20Sopenharmony_ci 38638c2ecf20Sopenharmony_cifree_eps: 38648c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 38658c2ecf20Sopenharmony_ci tegra_xudc_free_eps(xudc); 38668c2ecf20Sopenharmony_cifree_event_ring: 38678c2ecf20Sopenharmony_ci tegra_xudc_free_event_ring(xudc); 38688c2ecf20Sopenharmony_cidisable_phy: 38698c2ecf20Sopenharmony_ci tegra_xudc_phy_exit(xudc); 38708c2ecf20Sopenharmony_ciput_powerdomains: 38718c2ecf20Sopenharmony_ci tegra_xudc_powerdomain_remove(xudc); 38728c2ecf20Sopenharmony_cidisable_regulator: 38738c2ecf20Sopenharmony_ci regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); 38748c2ecf20Sopenharmony_ciput_padctl: 38758c2ecf20Sopenharmony_ci tegra_xusb_padctl_put(xudc->padctl); 38768c2ecf20Sopenharmony_ci 38778c2ecf20Sopenharmony_ci return err; 38788c2ecf20Sopenharmony_ci} 38798c2ecf20Sopenharmony_ci 38808c2ecf20Sopenharmony_cistatic int tegra_xudc_remove(struct platform_device *pdev) 38818c2ecf20Sopenharmony_ci{ 38828c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = platform_get_drvdata(pdev); 38838c2ecf20Sopenharmony_ci unsigned int i; 38848c2ecf20Sopenharmony_ci 38858c2ecf20Sopenharmony_ci pm_runtime_get_sync(xudc->dev); 38868c2ecf20Sopenharmony_ci 38878c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&xudc->plc_reset_work); 38888c2ecf20Sopenharmony_ci cancel_work_sync(&xudc->usb_role_sw_work); 38898c2ecf20Sopenharmony_ci 38908c2ecf20Sopenharmony_ci usb_del_gadget_udc(&xudc->gadget); 38918c2ecf20Sopenharmony_ci 38928c2ecf20Sopenharmony_ci tegra_xudc_free_eps(xudc); 38938c2ecf20Sopenharmony_ci tegra_xudc_free_event_ring(xudc); 38948c2ecf20Sopenharmony_ci 38958c2ecf20Sopenharmony_ci tegra_xudc_powerdomain_remove(xudc); 38968c2ecf20Sopenharmony_ci 38978c2ecf20Sopenharmony_ci regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); 38988c2ecf20Sopenharmony_ci 38998c2ecf20Sopenharmony_ci for (i = 0; i < xudc->soc->num_phys; i++) { 39008c2ecf20Sopenharmony_ci phy_power_off(xudc->utmi_phy[i]); 39018c2ecf20Sopenharmony_ci phy_power_off(xudc->usb3_phy[i]); 39028c2ecf20Sopenharmony_ci } 39038c2ecf20Sopenharmony_ci 39048c2ecf20Sopenharmony_ci tegra_xudc_phy_exit(xudc); 39058c2ecf20Sopenharmony_ci 39068c2ecf20Sopenharmony_ci pm_runtime_disable(xudc->dev); 39078c2ecf20Sopenharmony_ci pm_runtime_put(xudc->dev); 39088c2ecf20Sopenharmony_ci 39098c2ecf20Sopenharmony_ci tegra_xusb_padctl_put(xudc->padctl); 39108c2ecf20Sopenharmony_ci 39118c2ecf20Sopenharmony_ci return 0; 39128c2ecf20Sopenharmony_ci} 39138c2ecf20Sopenharmony_ci 39148c2ecf20Sopenharmony_cistatic int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc) 39158c2ecf20Sopenharmony_ci{ 39168c2ecf20Sopenharmony_ci unsigned long flags; 39178c2ecf20Sopenharmony_ci 39188c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "entering ELPG\n"); 39198c2ecf20Sopenharmony_ci 39208c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 39218c2ecf20Sopenharmony_ci 39228c2ecf20Sopenharmony_ci xudc->powergated = true; 39238c2ecf20Sopenharmony_ci xudc->saved_regs.ctrl = xudc_readl(xudc, CTRL); 39248c2ecf20Sopenharmony_ci xudc->saved_regs.portpm = xudc_readl(xudc, PORTPM); 39258c2ecf20Sopenharmony_ci xudc_writel(xudc, 0, CTRL); 39268c2ecf20Sopenharmony_ci 39278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 39288c2ecf20Sopenharmony_ci 39298c2ecf20Sopenharmony_ci clk_bulk_disable_unprepare(xudc->soc->num_clks, xudc->clks); 39308c2ecf20Sopenharmony_ci 39318c2ecf20Sopenharmony_ci regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); 39328c2ecf20Sopenharmony_ci 39338c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "entering ELPG done\n"); 39348c2ecf20Sopenharmony_ci return 0; 39358c2ecf20Sopenharmony_ci} 39368c2ecf20Sopenharmony_ci 39378c2ecf20Sopenharmony_cistatic int __maybe_unused tegra_xudc_unpowergate(struct tegra_xudc *xudc) 39388c2ecf20Sopenharmony_ci{ 39398c2ecf20Sopenharmony_ci unsigned long flags; 39408c2ecf20Sopenharmony_ci int err; 39418c2ecf20Sopenharmony_ci 39428c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "exiting ELPG\n"); 39438c2ecf20Sopenharmony_ci 39448c2ecf20Sopenharmony_ci err = regulator_bulk_enable(xudc->soc->num_supplies, 39458c2ecf20Sopenharmony_ci xudc->supplies); 39468c2ecf20Sopenharmony_ci if (err < 0) 39478c2ecf20Sopenharmony_ci return err; 39488c2ecf20Sopenharmony_ci 39498c2ecf20Sopenharmony_ci err = clk_bulk_prepare_enable(xudc->soc->num_clks, xudc->clks); 39508c2ecf20Sopenharmony_ci if (err < 0) 39518c2ecf20Sopenharmony_ci return err; 39528c2ecf20Sopenharmony_ci 39538c2ecf20Sopenharmony_ci tegra_xudc_fpci_ipfs_init(xudc); 39548c2ecf20Sopenharmony_ci 39558c2ecf20Sopenharmony_ci tegra_xudc_device_params_init(xudc); 39568c2ecf20Sopenharmony_ci 39578c2ecf20Sopenharmony_ci tegra_xudc_init_event_ring(xudc); 39588c2ecf20Sopenharmony_ci 39598c2ecf20Sopenharmony_ci tegra_xudc_init_eps(xudc); 39608c2ecf20Sopenharmony_ci 39618c2ecf20Sopenharmony_ci xudc_writel(xudc, xudc->saved_regs.portpm, PORTPM); 39628c2ecf20Sopenharmony_ci xudc_writel(xudc, xudc->saved_regs.ctrl, CTRL); 39638c2ecf20Sopenharmony_ci 39648c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 39658c2ecf20Sopenharmony_ci xudc->powergated = false; 39668c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 39678c2ecf20Sopenharmony_ci 39688c2ecf20Sopenharmony_ci dev_dbg(xudc->dev, "exiting ELPG done\n"); 39698c2ecf20Sopenharmony_ci return 0; 39708c2ecf20Sopenharmony_ci} 39718c2ecf20Sopenharmony_ci 39728c2ecf20Sopenharmony_cistatic int __maybe_unused tegra_xudc_suspend(struct device *dev) 39738c2ecf20Sopenharmony_ci{ 39748c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = dev_get_drvdata(dev); 39758c2ecf20Sopenharmony_ci unsigned long flags; 39768c2ecf20Sopenharmony_ci 39778c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 39788c2ecf20Sopenharmony_ci xudc->suspended = true; 39798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 39808c2ecf20Sopenharmony_ci 39818c2ecf20Sopenharmony_ci flush_work(&xudc->usb_role_sw_work); 39828c2ecf20Sopenharmony_ci 39838c2ecf20Sopenharmony_ci if (!pm_runtime_status_suspended(dev)) { 39848c2ecf20Sopenharmony_ci /* Forcibly disconnect before powergating. */ 39858c2ecf20Sopenharmony_ci tegra_xudc_device_mode_off(xudc); 39868c2ecf20Sopenharmony_ci tegra_xudc_powergate(xudc); 39878c2ecf20Sopenharmony_ci } 39888c2ecf20Sopenharmony_ci 39898c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 39908c2ecf20Sopenharmony_ci 39918c2ecf20Sopenharmony_ci return 0; 39928c2ecf20Sopenharmony_ci} 39938c2ecf20Sopenharmony_ci 39948c2ecf20Sopenharmony_cistatic int __maybe_unused tegra_xudc_resume(struct device *dev) 39958c2ecf20Sopenharmony_ci{ 39968c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = dev_get_drvdata(dev); 39978c2ecf20Sopenharmony_ci unsigned long flags; 39988c2ecf20Sopenharmony_ci int err; 39998c2ecf20Sopenharmony_ci 40008c2ecf20Sopenharmony_ci err = tegra_xudc_unpowergate(xudc); 40018c2ecf20Sopenharmony_ci if (err < 0) 40028c2ecf20Sopenharmony_ci return err; 40038c2ecf20Sopenharmony_ci 40048c2ecf20Sopenharmony_ci spin_lock_irqsave(&xudc->lock, flags); 40058c2ecf20Sopenharmony_ci xudc->suspended = false; 40068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xudc->lock, flags); 40078c2ecf20Sopenharmony_ci 40088c2ecf20Sopenharmony_ci schedule_work(&xudc->usb_role_sw_work); 40098c2ecf20Sopenharmony_ci 40108c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 40118c2ecf20Sopenharmony_ci 40128c2ecf20Sopenharmony_ci return 0; 40138c2ecf20Sopenharmony_ci} 40148c2ecf20Sopenharmony_ci 40158c2ecf20Sopenharmony_cistatic int __maybe_unused tegra_xudc_runtime_suspend(struct device *dev) 40168c2ecf20Sopenharmony_ci{ 40178c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = dev_get_drvdata(dev); 40188c2ecf20Sopenharmony_ci 40198c2ecf20Sopenharmony_ci return tegra_xudc_powergate(xudc); 40208c2ecf20Sopenharmony_ci} 40218c2ecf20Sopenharmony_ci 40228c2ecf20Sopenharmony_cistatic int __maybe_unused tegra_xudc_runtime_resume(struct device *dev) 40238c2ecf20Sopenharmony_ci{ 40248c2ecf20Sopenharmony_ci struct tegra_xudc *xudc = dev_get_drvdata(dev); 40258c2ecf20Sopenharmony_ci 40268c2ecf20Sopenharmony_ci return tegra_xudc_unpowergate(xudc); 40278c2ecf20Sopenharmony_ci} 40288c2ecf20Sopenharmony_ci 40298c2ecf20Sopenharmony_cistatic const struct dev_pm_ops tegra_xudc_pm_ops = { 40308c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(tegra_xudc_suspend, tegra_xudc_resume) 40318c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(tegra_xudc_runtime_suspend, 40328c2ecf20Sopenharmony_ci tegra_xudc_runtime_resume, NULL) 40338c2ecf20Sopenharmony_ci}; 40348c2ecf20Sopenharmony_ci 40358c2ecf20Sopenharmony_cistatic struct platform_driver tegra_xudc_driver = { 40368c2ecf20Sopenharmony_ci .probe = tegra_xudc_probe, 40378c2ecf20Sopenharmony_ci .remove = tegra_xudc_remove, 40388c2ecf20Sopenharmony_ci .driver = { 40398c2ecf20Sopenharmony_ci .name = "tegra-xudc", 40408c2ecf20Sopenharmony_ci .pm = &tegra_xudc_pm_ops, 40418c2ecf20Sopenharmony_ci .of_match_table = tegra_xudc_of_match, 40428c2ecf20Sopenharmony_ci }, 40438c2ecf20Sopenharmony_ci}; 40448c2ecf20Sopenharmony_cimodule_platform_driver(tegra_xudc_driver); 40458c2ecf20Sopenharmony_ci 40468c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NVIDIA Tegra XUSB Device Controller"); 40478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrew Bresticker <abrestic@chromium.org>"); 40488c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hui Fu <hfu@nvidia.com>"); 40498c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nagarjuna Kristam <nkristam@nvidia.com>"); 40508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 4051