162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NVIDIA Tegra XUSB device mode controller
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2013-2022, NVIDIA CORPORATION.  All rights reserved.
662306a36Sopenharmony_ci * Copyright (c) 2015, Google Inc.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/clk.h>
1062306a36Sopenharmony_ci#include <linux/completion.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1362306a36Sopenharmony_ci#include <linux/dmapool.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/iopoll.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci#include <linux/phy/phy.h>
2062306a36Sopenharmony_ci#include <linux/phy/tegra/xusb.h>
2162306a36Sopenharmony_ci#include <linux/pm_domain.h>
2262306a36Sopenharmony_ci#include <linux/platform_device.h>
2362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2462306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
2562306a36Sopenharmony_ci#include <linux/reset.h>
2662306a36Sopenharmony_ci#include <linux/usb/ch9.h>
2762306a36Sopenharmony_ci#include <linux/usb/gadget.h>
2862306a36Sopenharmony_ci#include <linux/usb/otg.h>
2962306a36Sopenharmony_ci#include <linux/usb/role.h>
3062306a36Sopenharmony_ci#include <linux/usb/phy.h>
3162306a36Sopenharmony_ci#include <linux/workqueue.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* XUSB_DEV registers */
3462306a36Sopenharmony_ci#define DB 0x004
3562306a36Sopenharmony_ci#define  DB_TARGET_MASK GENMASK(15, 8)
3662306a36Sopenharmony_ci#define  DB_TARGET(x) (((x) << 8) & DB_TARGET_MASK)
3762306a36Sopenharmony_ci#define  DB_STREAMID_MASK GENMASK(31, 16)
3862306a36Sopenharmony_ci#define  DB_STREAMID(x) (((x) << 16) & DB_STREAMID_MASK)
3962306a36Sopenharmony_ci#define ERSTSZ 0x008
4062306a36Sopenharmony_ci#define  ERSTSZ_ERSTXSZ_SHIFT(x) ((x) * 16)
4162306a36Sopenharmony_ci#define  ERSTSZ_ERSTXSZ_MASK GENMASK(15, 0)
4262306a36Sopenharmony_ci#define ERSTXBALO(x) (0x010 + 8 * (x))
4362306a36Sopenharmony_ci#define ERSTXBAHI(x) (0x014 + 8 * (x))
4462306a36Sopenharmony_ci#define ERDPLO 0x020
4562306a36Sopenharmony_ci#define  ERDPLO_EHB BIT(3)
4662306a36Sopenharmony_ci#define ERDPHI 0x024
4762306a36Sopenharmony_ci#define EREPLO 0x028
4862306a36Sopenharmony_ci#define  EREPLO_ECS BIT(0)
4962306a36Sopenharmony_ci#define  EREPLO_SEGI BIT(1)
5062306a36Sopenharmony_ci#define EREPHI 0x02c
5162306a36Sopenharmony_ci#define CTRL 0x030
5262306a36Sopenharmony_ci#define  CTRL_RUN BIT(0)
5362306a36Sopenharmony_ci#define  CTRL_LSE BIT(1)
5462306a36Sopenharmony_ci#define  CTRL_IE BIT(4)
5562306a36Sopenharmony_ci#define  CTRL_SMI_EVT BIT(5)
5662306a36Sopenharmony_ci#define  CTRL_SMI_DSE BIT(6)
5762306a36Sopenharmony_ci#define  CTRL_EWE BIT(7)
5862306a36Sopenharmony_ci#define  CTRL_DEVADDR_MASK GENMASK(30, 24)
5962306a36Sopenharmony_ci#define  CTRL_DEVADDR(x) (((x) << 24) & CTRL_DEVADDR_MASK)
6062306a36Sopenharmony_ci#define  CTRL_ENABLE BIT(31)
6162306a36Sopenharmony_ci#define ST 0x034
6262306a36Sopenharmony_ci#define  ST_RC BIT(0)
6362306a36Sopenharmony_ci#define  ST_IP BIT(4)
6462306a36Sopenharmony_ci#define RT_IMOD	0x038
6562306a36Sopenharmony_ci#define  RT_IMOD_IMODI_MASK GENMASK(15, 0)
6662306a36Sopenharmony_ci#define  RT_IMOD_IMODI(x) ((x) & RT_IMOD_IMODI_MASK)
6762306a36Sopenharmony_ci#define  RT_IMOD_IMODC_MASK GENMASK(31, 16)
6862306a36Sopenharmony_ci#define  RT_IMOD_IMODC(x) (((x) << 16) & RT_IMOD_IMODC_MASK)
6962306a36Sopenharmony_ci#define PORTSC 0x03c
7062306a36Sopenharmony_ci#define  PORTSC_CCS BIT(0)
7162306a36Sopenharmony_ci#define  PORTSC_PED BIT(1)
7262306a36Sopenharmony_ci#define  PORTSC_PR BIT(4)
7362306a36Sopenharmony_ci#define  PORTSC_PLS_SHIFT 5
7462306a36Sopenharmony_ci#define  PORTSC_PLS_MASK GENMASK(8, 5)
7562306a36Sopenharmony_ci#define  PORTSC_PLS_U0 0x0
7662306a36Sopenharmony_ci#define  PORTSC_PLS_U2 0x2
7762306a36Sopenharmony_ci#define  PORTSC_PLS_U3 0x3
7862306a36Sopenharmony_ci#define  PORTSC_PLS_DISABLED 0x4
7962306a36Sopenharmony_ci#define  PORTSC_PLS_RXDETECT 0x5
8062306a36Sopenharmony_ci#define  PORTSC_PLS_INACTIVE 0x6
8162306a36Sopenharmony_ci#define  PORTSC_PLS_RESUME 0xf
8262306a36Sopenharmony_ci#define  PORTSC_PLS(x) (((x) << PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK)
8362306a36Sopenharmony_ci#define  PORTSC_PS_SHIFT 10
8462306a36Sopenharmony_ci#define  PORTSC_PS_MASK GENMASK(13, 10)
8562306a36Sopenharmony_ci#define  PORTSC_PS_UNDEFINED 0x0
8662306a36Sopenharmony_ci#define  PORTSC_PS_FS 0x1
8762306a36Sopenharmony_ci#define  PORTSC_PS_LS 0x2
8862306a36Sopenharmony_ci#define  PORTSC_PS_HS 0x3
8962306a36Sopenharmony_ci#define  PORTSC_PS_SS 0x4
9062306a36Sopenharmony_ci#define  PORTSC_LWS BIT(16)
9162306a36Sopenharmony_ci#define  PORTSC_CSC BIT(17)
9262306a36Sopenharmony_ci#define  PORTSC_WRC BIT(19)
9362306a36Sopenharmony_ci#define  PORTSC_PRC BIT(21)
9462306a36Sopenharmony_ci#define  PORTSC_PLC BIT(22)
9562306a36Sopenharmony_ci#define  PORTSC_CEC BIT(23)
9662306a36Sopenharmony_ci#define  PORTSC_WPR BIT(30)
9762306a36Sopenharmony_ci#define  PORTSC_CHANGE_MASK (PORTSC_CSC | PORTSC_WRC | PORTSC_PRC | \
9862306a36Sopenharmony_ci			     PORTSC_PLC | PORTSC_CEC)
9962306a36Sopenharmony_ci#define ECPLO 0x040
10062306a36Sopenharmony_ci#define ECPHI 0x044
10162306a36Sopenharmony_ci#define MFINDEX 0x048
10262306a36Sopenharmony_ci#define  MFINDEX_FRAME_SHIFT 3
10362306a36Sopenharmony_ci#define  MFINDEX_FRAME_MASK GENMASK(13, 3)
10462306a36Sopenharmony_ci#define PORTPM 0x04c
10562306a36Sopenharmony_ci#define  PORTPM_L1S_MASK GENMASK(1, 0)
10662306a36Sopenharmony_ci#define  PORTPM_L1S_DROP 0x0
10762306a36Sopenharmony_ci#define  PORTPM_L1S_ACCEPT 0x1
10862306a36Sopenharmony_ci#define  PORTPM_L1S_NYET 0x2
10962306a36Sopenharmony_ci#define  PORTPM_L1S_STALL 0x3
11062306a36Sopenharmony_ci#define  PORTPM_L1S(x) ((x) & PORTPM_L1S_MASK)
11162306a36Sopenharmony_ci#define  PORTPM_RWE BIT(3)
11262306a36Sopenharmony_ci#define  PORTPM_U2TIMEOUT_MASK GENMASK(15, 8)
11362306a36Sopenharmony_ci#define  PORTPM_U1TIMEOUT_MASK GENMASK(23, 16)
11462306a36Sopenharmony_ci#define  PORTPM_FLA BIT(24)
11562306a36Sopenharmony_ci#define  PORTPM_VBA BIT(25)
11662306a36Sopenharmony_ci#define  PORTPM_WOC BIT(26)
11762306a36Sopenharmony_ci#define  PORTPM_WOD BIT(27)
11862306a36Sopenharmony_ci#define  PORTPM_U1E BIT(28)
11962306a36Sopenharmony_ci#define  PORTPM_U2E BIT(29)
12062306a36Sopenharmony_ci#define  PORTPM_FRWE BIT(30)
12162306a36Sopenharmony_ci#define  PORTPM_PNG_CYA BIT(31)
12262306a36Sopenharmony_ci#define EP_HALT 0x050
12362306a36Sopenharmony_ci#define EP_PAUSE 0x054
12462306a36Sopenharmony_ci#define EP_RELOAD 0x058
12562306a36Sopenharmony_ci#define EP_STCHG 0x05c
12662306a36Sopenharmony_ci#define DEVNOTIF_LO 0x064
12762306a36Sopenharmony_ci#define  DEVNOTIF_LO_TRIG BIT(0)
12862306a36Sopenharmony_ci#define  DEVNOTIF_LO_TYPE_MASK GENMASK(7, 4)
12962306a36Sopenharmony_ci#define  DEVNOTIF_LO_TYPE(x) (((x) << 4)  & DEVNOTIF_LO_TYPE_MASK)
13062306a36Sopenharmony_ci#define  DEVNOTIF_LO_TYPE_FUNCTION_WAKE 0x1
13162306a36Sopenharmony_ci#define DEVNOTIF_HI 0x068
13262306a36Sopenharmony_ci#define PORTHALT 0x06c
13362306a36Sopenharmony_ci#define  PORTHALT_HALT_LTSSM BIT(0)
13462306a36Sopenharmony_ci#define  PORTHALT_HALT_REJECT BIT(1)
13562306a36Sopenharmony_ci#define  PORTHALT_STCHG_REQ BIT(20)
13662306a36Sopenharmony_ci#define  PORTHALT_STCHG_INTR_EN BIT(24)
13762306a36Sopenharmony_ci#define PORT_TM	0x070
13862306a36Sopenharmony_ci#define EP_THREAD_ACTIVE 0x074
13962306a36Sopenharmony_ci#define EP_STOPPED 0x078
14062306a36Sopenharmony_ci#define HSFSPI_COUNT0 0x100
14162306a36Sopenharmony_ci#define HSFSPI_COUNT13 0x134
14262306a36Sopenharmony_ci#define  HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK GENMASK(29, 0)
14362306a36Sopenharmony_ci#define  HSFSPI_COUNT13_U2_RESUME_K_DURATION(x) ((x) & \
14462306a36Sopenharmony_ci				HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK)
14562306a36Sopenharmony_ci#define BLCG 0x840
14662306a36Sopenharmony_ci#define SSPX_CORE_CNT0 0x610
14762306a36Sopenharmony_ci#define  SSPX_CORE_CNT0_PING_TBURST_MASK GENMASK(7, 0)
14862306a36Sopenharmony_ci#define  SSPX_CORE_CNT0_PING_TBURST(x) ((x) & SSPX_CORE_CNT0_PING_TBURST_MASK)
14962306a36Sopenharmony_ci#define SSPX_CORE_CNT30 0x688
15062306a36Sopenharmony_ci#define  SSPX_CORE_CNT30_LMPITP_TIMER_MASK GENMASK(19, 0)
15162306a36Sopenharmony_ci#define  SSPX_CORE_CNT30_LMPITP_TIMER(x) ((x) & \
15262306a36Sopenharmony_ci					SSPX_CORE_CNT30_LMPITP_TIMER_MASK)
15362306a36Sopenharmony_ci#define SSPX_CORE_CNT32 0x690
15462306a36Sopenharmony_ci#define  SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK GENMASK(7, 0)
15562306a36Sopenharmony_ci#define  SSPX_CORE_CNT32_POLL_TBURST_MAX(x) ((x) & \
15662306a36Sopenharmony_ci					SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK)
15762306a36Sopenharmony_ci#define SSPX_CORE_CNT56 0x6fc
15862306a36Sopenharmony_ci#define  SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK GENMASK(19, 0)
15962306a36Sopenharmony_ci#define  SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(x) ((x) & \
16062306a36Sopenharmony_ci				SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK)
16162306a36Sopenharmony_ci#define SSPX_CORE_CNT57 0x700
16262306a36Sopenharmony_ci#define  SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK GENMASK(19, 0)
16362306a36Sopenharmony_ci#define  SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(x) ((x) & \
16462306a36Sopenharmony_ci				SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK)
16562306a36Sopenharmony_ci#define SSPX_CORE_CNT65 0x720
16662306a36Sopenharmony_ci#define  SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK GENMASK(19, 0)
16762306a36Sopenharmony_ci#define  SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(x) ((x) & \
16862306a36Sopenharmony_ci				SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK)
16962306a36Sopenharmony_ci#define SSPX_CORE_CNT66 0x724
17062306a36Sopenharmony_ci#define  SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK GENMASK(19, 0)
17162306a36Sopenharmony_ci#define  SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(x) ((x) & \
17262306a36Sopenharmony_ci				SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK)
17362306a36Sopenharmony_ci#define SSPX_CORE_CNT67 0x728
17462306a36Sopenharmony_ci#define  SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK GENMASK(19, 0)
17562306a36Sopenharmony_ci#define  SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(x) ((x) & \
17662306a36Sopenharmony_ci				SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK)
17762306a36Sopenharmony_ci#define SSPX_CORE_CNT72 0x73c
17862306a36Sopenharmony_ci#define  SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK GENMASK(19, 0)
17962306a36Sopenharmony_ci#define  SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(x) ((x) & \
18062306a36Sopenharmony_ci				SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK)
18162306a36Sopenharmony_ci#define SSPX_CORE_PADCTL4 0x750
18262306a36Sopenharmony_ci#define  SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK GENMASK(19, 0)
18362306a36Sopenharmony_ci#define  SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3(x) ((x) & \
18462306a36Sopenharmony_ci				SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK)
18562306a36Sopenharmony_ci#define  BLCG_DFPCI BIT(0)
18662306a36Sopenharmony_ci#define  BLCG_UFPCI BIT(1)
18762306a36Sopenharmony_ci#define  BLCG_FE BIT(2)
18862306a36Sopenharmony_ci#define  BLCG_COREPLL_PWRDN BIT(8)
18962306a36Sopenharmony_ci#define  BLCG_IOPLL_0_PWRDN BIT(9)
19062306a36Sopenharmony_ci#define  BLCG_IOPLL_1_PWRDN BIT(10)
19162306a36Sopenharmony_ci#define  BLCG_IOPLL_2_PWRDN BIT(11)
19262306a36Sopenharmony_ci#define  BLCG_ALL 0x1ff
19362306a36Sopenharmony_ci#define CFG_DEV_SSPI_XFER 0x858
19462306a36Sopenharmony_ci#define  CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK GENMASK(31, 0)
19562306a36Sopenharmony_ci#define  CFG_DEV_SSPI_XFER_ACKTIMEOUT(x) ((x) & \
19662306a36Sopenharmony_ci					CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK)
19762306a36Sopenharmony_ci#define CFG_DEV_FE 0x85c
19862306a36Sopenharmony_ci#define  CFG_DEV_FE_PORTREGSEL_MASK GENMASK(1, 0)
19962306a36Sopenharmony_ci#define  CFG_DEV_FE_PORTREGSEL_SS_PI 1
20062306a36Sopenharmony_ci#define  CFG_DEV_FE_PORTREGSEL_HSFS_PI 2
20162306a36Sopenharmony_ci#define  CFG_DEV_FE_PORTREGSEL(x) ((x) & CFG_DEV_FE_PORTREGSEL_MASK)
20262306a36Sopenharmony_ci#define  CFG_DEV_FE_INFINITE_SS_RETRY BIT(29)
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/* FPCI registers */
20562306a36Sopenharmony_ci#define XUSB_DEV_CFG_1 0x004
20662306a36Sopenharmony_ci#define  XUSB_DEV_CFG_1_IO_SPACE_EN BIT(0)
20762306a36Sopenharmony_ci#define  XUSB_DEV_CFG_1_MEMORY_SPACE_EN BIT(1)
20862306a36Sopenharmony_ci#define  XUSB_DEV_CFG_1_BUS_MASTER_EN BIT(2)
20962306a36Sopenharmony_ci#define XUSB_DEV_CFG_4 0x010
21062306a36Sopenharmony_ci#define  XUSB_DEV_CFG_4_BASE_ADDR_MASK GENMASK(31, 15)
21162306a36Sopenharmony_ci#define XUSB_DEV_CFG_5 0x014
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/* IPFS registers */
21462306a36Sopenharmony_ci#define XUSB_DEV_CONFIGURATION_0 0x180
21562306a36Sopenharmony_ci#define  XUSB_DEV_CONFIGURATION_0_EN_FPCI BIT(0)
21662306a36Sopenharmony_ci#define XUSB_DEV_INTR_MASK_0 0x188
21762306a36Sopenharmony_ci#define  XUSB_DEV_INTR_MASK_0_IP_INT_MASK BIT(16)
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistruct tegra_xudc_ep_context {
22062306a36Sopenharmony_ci	__le32 info0;
22162306a36Sopenharmony_ci	__le32 info1;
22262306a36Sopenharmony_ci	__le32 deq_lo;
22362306a36Sopenharmony_ci	__le32 deq_hi;
22462306a36Sopenharmony_ci	__le32 tx_info;
22562306a36Sopenharmony_ci	__le32 rsvd[11];
22662306a36Sopenharmony_ci};
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci#define EP_STATE_DISABLED 0
22962306a36Sopenharmony_ci#define EP_STATE_RUNNING 1
23062306a36Sopenharmony_ci#define EP_STATE_HALTED 2
23162306a36Sopenharmony_ci#define EP_STATE_STOPPED 3
23262306a36Sopenharmony_ci#define EP_STATE_ERROR 4
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci#define EP_TYPE_INVALID 0
23562306a36Sopenharmony_ci#define EP_TYPE_ISOCH_OUT 1
23662306a36Sopenharmony_ci#define EP_TYPE_BULK_OUT 2
23762306a36Sopenharmony_ci#define EP_TYPE_INTERRUPT_OUT 3
23862306a36Sopenharmony_ci#define EP_TYPE_CONTROL 4
23962306a36Sopenharmony_ci#define EP_TYPE_ISCOH_IN 5
24062306a36Sopenharmony_ci#define EP_TYPE_BULK_IN 6
24162306a36Sopenharmony_ci#define EP_TYPE_INTERRUPT_IN 7
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci#define BUILD_EP_CONTEXT_RW(name, member, shift, mask)			\
24462306a36Sopenharmony_cistatic inline u32 ep_ctx_read_##name(struct tegra_xudc_ep_context *ctx)	\
24562306a36Sopenharmony_ci{									\
24662306a36Sopenharmony_ci	return (le32_to_cpu(ctx->member) >> (shift)) & (mask);		\
24762306a36Sopenharmony_ci}									\
24862306a36Sopenharmony_cistatic inline void							\
24962306a36Sopenharmony_ciep_ctx_write_##name(struct tegra_xudc_ep_context *ctx, u32 val)		\
25062306a36Sopenharmony_ci{									\
25162306a36Sopenharmony_ci	u32 tmp;							\
25262306a36Sopenharmony_ci									\
25362306a36Sopenharmony_ci	tmp = le32_to_cpu(ctx->member) & ~((mask) << (shift));		\
25462306a36Sopenharmony_ci	tmp |= (val & (mask)) << (shift);				\
25562306a36Sopenharmony_ci	ctx->member = cpu_to_le32(tmp);					\
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(state, info0, 0, 0x7)
25962306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(mult, info0, 8, 0x3)
26062306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(max_pstreams, info0, 10, 0x1f)
26162306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(lsa, info0, 15, 0x1)
26262306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(interval, info0, 16, 0xff)
26362306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(cerr, info1, 1, 0x3)
26462306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(type, info1, 3, 0x7)
26562306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(hid, info1, 7, 0x1)
26662306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(max_burst_size, info1, 8, 0xff)
26762306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(max_packet_size, info1, 16, 0xffff)
26862306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(dcs, deq_lo, 0, 0x1)
26962306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(deq_lo, deq_lo, 4, 0xfffffff)
27062306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(deq_hi, deq_hi, 0, 0xffffffff)
27162306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(avg_trb_len, tx_info, 0, 0xffff)
27262306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(max_esit_payload, tx_info, 16, 0xffff)
27362306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(edtla, rsvd[0], 0, 0xffffff)
27462306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(rsvd, rsvd[0], 24, 0x1)
27562306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(partial_td, rsvd[0], 25, 0x1)
27662306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(splitxstate, rsvd[0], 26, 0x1)
27762306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(seq_num, rsvd[0], 27, 0x1f)
27862306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(cerrcnt, rsvd[1], 18, 0x3)
27962306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(data_offset, rsvd[2], 0, 0x1ffff)
28062306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(numtrbs, rsvd[2], 22, 0x1f)
28162306a36Sopenharmony_ciBUILD_EP_CONTEXT_RW(devaddr, rsvd[6], 0, 0x7f)
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic inline u64 ep_ctx_read_deq_ptr(struct tegra_xudc_ep_context *ctx)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	return ((u64)ep_ctx_read_deq_hi(ctx) << 32) |
28662306a36Sopenharmony_ci		(ep_ctx_read_deq_lo(ctx) << 4);
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic inline void
29062306a36Sopenharmony_ciep_ctx_write_deq_ptr(struct tegra_xudc_ep_context *ctx, u64 addr)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	ep_ctx_write_deq_lo(ctx, lower_32_bits(addr) >> 4);
29362306a36Sopenharmony_ci	ep_ctx_write_deq_hi(ctx, upper_32_bits(addr));
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistruct tegra_xudc_trb {
29762306a36Sopenharmony_ci	__le32 data_lo;
29862306a36Sopenharmony_ci	__le32 data_hi;
29962306a36Sopenharmony_ci	__le32 status;
30062306a36Sopenharmony_ci	__le32 control;
30162306a36Sopenharmony_ci};
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci#define TRB_TYPE_RSVD 0
30462306a36Sopenharmony_ci#define TRB_TYPE_NORMAL 1
30562306a36Sopenharmony_ci#define TRB_TYPE_SETUP_STAGE 2
30662306a36Sopenharmony_ci#define TRB_TYPE_DATA_STAGE 3
30762306a36Sopenharmony_ci#define TRB_TYPE_STATUS_STAGE 4
30862306a36Sopenharmony_ci#define TRB_TYPE_ISOCH 5
30962306a36Sopenharmony_ci#define TRB_TYPE_LINK 6
31062306a36Sopenharmony_ci#define TRB_TYPE_TRANSFER_EVENT 32
31162306a36Sopenharmony_ci#define TRB_TYPE_PORT_STATUS_CHANGE_EVENT 34
31262306a36Sopenharmony_ci#define TRB_TYPE_STREAM 48
31362306a36Sopenharmony_ci#define TRB_TYPE_SETUP_PACKET_EVENT 63
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci#define TRB_CMPL_CODE_INVALID 0
31662306a36Sopenharmony_ci#define TRB_CMPL_CODE_SUCCESS 1
31762306a36Sopenharmony_ci#define TRB_CMPL_CODE_DATA_BUFFER_ERR 2
31862306a36Sopenharmony_ci#define TRB_CMPL_CODE_BABBLE_DETECTED_ERR 3
31962306a36Sopenharmony_ci#define TRB_CMPL_CODE_USB_TRANS_ERR 4
32062306a36Sopenharmony_ci#define TRB_CMPL_CODE_TRB_ERR 5
32162306a36Sopenharmony_ci#define TRB_CMPL_CODE_STALL 6
32262306a36Sopenharmony_ci#define TRB_CMPL_CODE_INVALID_STREAM_TYPE_ERR 10
32362306a36Sopenharmony_ci#define TRB_CMPL_CODE_SHORT_PACKET 13
32462306a36Sopenharmony_ci#define TRB_CMPL_CODE_RING_UNDERRUN 14
32562306a36Sopenharmony_ci#define TRB_CMPL_CODE_RING_OVERRUN 15
32662306a36Sopenharmony_ci#define TRB_CMPL_CODE_EVENT_RING_FULL_ERR 21
32762306a36Sopenharmony_ci#define TRB_CMPL_CODE_STOPPED 26
32862306a36Sopenharmony_ci#define TRB_CMPL_CODE_ISOCH_BUFFER_OVERRUN 31
32962306a36Sopenharmony_ci#define TRB_CMPL_CODE_STREAM_NUMP_ERROR 219
33062306a36Sopenharmony_ci#define TRB_CMPL_CODE_PRIME_PIPE_RECEIVED 220
33162306a36Sopenharmony_ci#define TRB_CMPL_CODE_HOST_REJECTED 221
33262306a36Sopenharmony_ci#define TRB_CMPL_CODE_CTRL_DIR_ERR 222
33362306a36Sopenharmony_ci#define TRB_CMPL_CODE_CTRL_SEQNUM_ERR 223
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci#define BUILD_TRB_RW(name, member, shift, mask)				\
33662306a36Sopenharmony_cistatic inline u32 trb_read_##name(struct tegra_xudc_trb *trb)		\
33762306a36Sopenharmony_ci{									\
33862306a36Sopenharmony_ci	return (le32_to_cpu(trb->member) >> (shift)) & (mask);		\
33962306a36Sopenharmony_ci}									\
34062306a36Sopenharmony_cistatic inline void							\
34162306a36Sopenharmony_citrb_write_##name(struct tegra_xudc_trb *trb, u32 val)			\
34262306a36Sopenharmony_ci{									\
34362306a36Sopenharmony_ci	u32 tmp;							\
34462306a36Sopenharmony_ci									\
34562306a36Sopenharmony_ci	tmp = le32_to_cpu(trb->member) & ~((mask) << (shift));		\
34662306a36Sopenharmony_ci	tmp |= (val & (mask)) << (shift);				\
34762306a36Sopenharmony_ci	trb->member = cpu_to_le32(tmp);					\
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ciBUILD_TRB_RW(data_lo, data_lo, 0, 0xffffffff)
35162306a36Sopenharmony_ciBUILD_TRB_RW(data_hi, data_hi, 0, 0xffffffff)
35262306a36Sopenharmony_ciBUILD_TRB_RW(seq_num, status, 0, 0xffff)
35362306a36Sopenharmony_ciBUILD_TRB_RW(transfer_len, status, 0, 0xffffff)
35462306a36Sopenharmony_ciBUILD_TRB_RW(td_size, status, 17, 0x1f)
35562306a36Sopenharmony_ciBUILD_TRB_RW(cmpl_code, status, 24, 0xff)
35662306a36Sopenharmony_ciBUILD_TRB_RW(cycle, control, 0, 0x1)
35762306a36Sopenharmony_ciBUILD_TRB_RW(toggle_cycle, control, 1, 0x1)
35862306a36Sopenharmony_ciBUILD_TRB_RW(isp, control, 2, 0x1)
35962306a36Sopenharmony_ciBUILD_TRB_RW(chain, control, 4, 0x1)
36062306a36Sopenharmony_ciBUILD_TRB_RW(ioc, control, 5, 0x1)
36162306a36Sopenharmony_ciBUILD_TRB_RW(type, control, 10, 0x3f)
36262306a36Sopenharmony_ciBUILD_TRB_RW(stream_id, control, 16, 0xffff)
36362306a36Sopenharmony_ciBUILD_TRB_RW(endpoint_id, control, 16, 0x1f)
36462306a36Sopenharmony_ciBUILD_TRB_RW(tlbpc, control, 16, 0xf)
36562306a36Sopenharmony_ciBUILD_TRB_RW(data_stage_dir, control, 16, 0x1)
36662306a36Sopenharmony_ciBUILD_TRB_RW(frame_id, control, 20, 0x7ff)
36762306a36Sopenharmony_ciBUILD_TRB_RW(sia, control, 31, 0x1)
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic inline u64 trb_read_data_ptr(struct tegra_xudc_trb *trb)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	return ((u64)trb_read_data_hi(trb) << 32) |
37262306a36Sopenharmony_ci		trb_read_data_lo(trb);
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic inline void trb_write_data_ptr(struct tegra_xudc_trb *trb, u64 addr)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	trb_write_data_lo(trb, lower_32_bits(addr));
37862306a36Sopenharmony_ci	trb_write_data_hi(trb, upper_32_bits(addr));
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistruct tegra_xudc_request {
38262306a36Sopenharmony_ci	struct usb_request usb_req;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	size_t buf_queued;
38562306a36Sopenharmony_ci	unsigned int trbs_queued;
38662306a36Sopenharmony_ci	unsigned int trbs_needed;
38762306a36Sopenharmony_ci	bool need_zlp;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	struct tegra_xudc_trb *first_trb;
39062306a36Sopenharmony_ci	struct tegra_xudc_trb *last_trb;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	struct list_head list;
39362306a36Sopenharmony_ci};
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistruct tegra_xudc_ep {
39662306a36Sopenharmony_ci	struct tegra_xudc *xudc;
39762306a36Sopenharmony_ci	struct usb_ep usb_ep;
39862306a36Sopenharmony_ci	unsigned int index;
39962306a36Sopenharmony_ci	char name[8];
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	struct tegra_xudc_ep_context *context;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci#define XUDC_TRANSFER_RING_SIZE 64
40462306a36Sopenharmony_ci	struct tegra_xudc_trb *transfer_ring;
40562306a36Sopenharmony_ci	dma_addr_t transfer_ring_phys;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	unsigned int enq_ptr;
40862306a36Sopenharmony_ci	unsigned int deq_ptr;
40962306a36Sopenharmony_ci	bool pcs;
41062306a36Sopenharmony_ci	bool ring_full;
41162306a36Sopenharmony_ci	bool stream_rejected;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	struct list_head queue;
41462306a36Sopenharmony_ci	const struct usb_endpoint_descriptor *desc;
41562306a36Sopenharmony_ci	const struct usb_ss_ep_comp_descriptor *comp_desc;
41662306a36Sopenharmony_ci};
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistruct tegra_xudc_sel_timing {
41962306a36Sopenharmony_ci	__u8 u1sel;
42062306a36Sopenharmony_ci	__u8 u1pel;
42162306a36Sopenharmony_ci	__le16 u2sel;
42262306a36Sopenharmony_ci	__le16 u2pel;
42362306a36Sopenharmony_ci};
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cienum tegra_xudc_setup_state {
42662306a36Sopenharmony_ci	WAIT_FOR_SETUP,
42762306a36Sopenharmony_ci	DATA_STAGE_XFER,
42862306a36Sopenharmony_ci	DATA_STAGE_RECV,
42962306a36Sopenharmony_ci	STATUS_STAGE_XFER,
43062306a36Sopenharmony_ci	STATUS_STAGE_RECV,
43162306a36Sopenharmony_ci};
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistruct tegra_xudc_setup_packet {
43462306a36Sopenharmony_ci	struct usb_ctrlrequest ctrl_req;
43562306a36Sopenharmony_ci	unsigned int seq_num;
43662306a36Sopenharmony_ci};
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistruct tegra_xudc_save_regs {
43962306a36Sopenharmony_ci	u32 ctrl;
44062306a36Sopenharmony_ci	u32 portpm;
44162306a36Sopenharmony_ci};
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistruct tegra_xudc {
44462306a36Sopenharmony_ci	struct device *dev;
44562306a36Sopenharmony_ci	const struct tegra_xudc_soc *soc;
44662306a36Sopenharmony_ci	struct tegra_xusb_padctl *padctl;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	spinlock_t lock;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	struct usb_gadget gadget;
45162306a36Sopenharmony_ci	struct usb_gadget_driver *driver;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci#define XUDC_NR_EVENT_RINGS 2
45462306a36Sopenharmony_ci#define XUDC_EVENT_RING_SIZE 4096
45562306a36Sopenharmony_ci	struct tegra_xudc_trb *event_ring[XUDC_NR_EVENT_RINGS];
45662306a36Sopenharmony_ci	dma_addr_t event_ring_phys[XUDC_NR_EVENT_RINGS];
45762306a36Sopenharmony_ci	unsigned int event_ring_index;
45862306a36Sopenharmony_ci	unsigned int event_ring_deq_ptr;
45962306a36Sopenharmony_ci	bool ccs;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci#define XUDC_NR_EPS 32
46262306a36Sopenharmony_ci	struct tegra_xudc_ep ep[XUDC_NR_EPS];
46362306a36Sopenharmony_ci	struct tegra_xudc_ep_context *ep_context;
46462306a36Sopenharmony_ci	dma_addr_t ep_context_phys;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	struct device *genpd_dev_device;
46762306a36Sopenharmony_ci	struct device *genpd_dev_ss;
46862306a36Sopenharmony_ci	struct device_link *genpd_dl_device;
46962306a36Sopenharmony_ci	struct device_link *genpd_dl_ss;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	struct dma_pool *transfer_ring_pool;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	bool queued_setup_packet;
47462306a36Sopenharmony_ci	struct tegra_xudc_setup_packet setup_packet;
47562306a36Sopenharmony_ci	enum tegra_xudc_setup_state setup_state;
47662306a36Sopenharmony_ci	u16 setup_seq_num;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	u16 dev_addr;
47962306a36Sopenharmony_ci	u16 isoch_delay;
48062306a36Sopenharmony_ci	struct tegra_xudc_sel_timing sel_timing;
48162306a36Sopenharmony_ci	u8 test_mode_pattern;
48262306a36Sopenharmony_ci	u16 status_buf;
48362306a36Sopenharmony_ci	struct tegra_xudc_request *ep0_req;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	bool pullup;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	unsigned int nr_enabled_eps;
48862306a36Sopenharmony_ci	unsigned int nr_isoch_eps;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	unsigned int device_state;
49162306a36Sopenharmony_ci	unsigned int resume_state;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	int irq;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	void __iomem *base;
49662306a36Sopenharmony_ci	resource_size_t phys_base;
49762306a36Sopenharmony_ci	void __iomem *ipfs;
49862306a36Sopenharmony_ci	void __iomem *fpci;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	struct regulator_bulk_data *supplies;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	struct clk_bulk_data *clks;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	bool device_mode;
50562306a36Sopenharmony_ci	struct work_struct usb_role_sw_work;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	struct phy **usb3_phy;
50862306a36Sopenharmony_ci	struct phy *curr_usb3_phy;
50962306a36Sopenharmony_ci	struct phy **utmi_phy;
51062306a36Sopenharmony_ci	struct phy *curr_utmi_phy;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	struct tegra_xudc_save_regs saved_regs;
51362306a36Sopenharmony_ci	bool suspended;
51462306a36Sopenharmony_ci	bool powergated;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	struct usb_phy **usbphy;
51762306a36Sopenharmony_ci	struct usb_phy *curr_usbphy;
51862306a36Sopenharmony_ci	struct notifier_block vbus_nb;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	struct completion disconnect_complete;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	bool selfpowered;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci#define TOGGLE_VBUS_WAIT_MS 100
52562306a36Sopenharmony_ci	struct delayed_work plc_reset_work;
52662306a36Sopenharmony_ci	bool wait_csc;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	struct delayed_work port_reset_war_work;
52962306a36Sopenharmony_ci	bool wait_for_sec_prc;
53062306a36Sopenharmony_ci};
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci#define XUDC_TRB_MAX_BUFFER_SIZE 65536
53362306a36Sopenharmony_ci#define XUDC_MAX_ISOCH_EPS 4
53462306a36Sopenharmony_ci#define XUDC_INTERRUPT_MODERATION_US 0
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic struct usb_endpoint_descriptor tegra_xudc_ep0_desc = {
53762306a36Sopenharmony_ci	.bLength = USB_DT_ENDPOINT_SIZE,
53862306a36Sopenharmony_ci	.bDescriptorType = USB_DT_ENDPOINT,
53962306a36Sopenharmony_ci	.bEndpointAddress = 0,
54062306a36Sopenharmony_ci	.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
54162306a36Sopenharmony_ci	.wMaxPacketSize = cpu_to_le16(64),
54262306a36Sopenharmony_ci};
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistruct tegra_xudc_soc {
54562306a36Sopenharmony_ci	const char * const *supply_names;
54662306a36Sopenharmony_ci	unsigned int num_supplies;
54762306a36Sopenharmony_ci	const char * const *clock_names;
54862306a36Sopenharmony_ci	unsigned int num_clks;
54962306a36Sopenharmony_ci	unsigned int num_phys;
55062306a36Sopenharmony_ci	bool u1_enable;
55162306a36Sopenharmony_ci	bool u2_enable;
55262306a36Sopenharmony_ci	bool lpm_enable;
55362306a36Sopenharmony_ci	bool invalid_seq_num;
55462306a36Sopenharmony_ci	bool pls_quirk;
55562306a36Sopenharmony_ci	bool port_reset_quirk;
55662306a36Sopenharmony_ci	bool port_speed_quirk;
55762306a36Sopenharmony_ci	bool has_ipfs;
55862306a36Sopenharmony_ci};
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic inline u32 fpci_readl(struct tegra_xudc *xudc, unsigned int offset)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	return readl(xudc->fpci + offset);
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic inline void fpci_writel(struct tegra_xudc *xudc, u32 val,
56662306a36Sopenharmony_ci			       unsigned int offset)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	writel(val, xudc->fpci + offset);
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cistatic inline u32 ipfs_readl(struct tegra_xudc *xudc, unsigned int offset)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	return readl(xudc->ipfs + offset);
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic inline void ipfs_writel(struct tegra_xudc *xudc, u32 val,
57762306a36Sopenharmony_ci			       unsigned int offset)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	writel(val, xudc->ipfs + offset);
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic inline u32 xudc_readl(struct tegra_xudc *xudc, unsigned int offset)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	return readl(xudc->base + offset);
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic inline void xudc_writel(struct tegra_xudc *xudc, u32 val,
58862306a36Sopenharmony_ci			       unsigned int offset)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	writel(val, xudc->base + offset);
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cistatic inline int xudc_readl_poll(struct tegra_xudc *xudc,
59462306a36Sopenharmony_ci				  unsigned int offset, u32 mask, u32 val)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	u32 regval;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	return readl_poll_timeout_atomic(xudc->base + offset, regval,
59962306a36Sopenharmony_ci					 (regval & mask) == val, 1, 100);
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic inline struct tegra_xudc *to_xudc(struct usb_gadget *gadget)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	return container_of(gadget, struct tegra_xudc, gadget);
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic inline struct tegra_xudc_ep *to_xudc_ep(struct usb_ep *ep)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	return container_of(ep, struct tegra_xudc_ep, usb_ep);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic inline struct tegra_xudc_request *to_xudc_req(struct usb_request *req)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	return container_of(req, struct tegra_xudc_request, usb_req);
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic inline void dump_trb(struct tegra_xudc *xudc, const char *type,
61862306a36Sopenharmony_ci			    struct tegra_xudc_trb *trb)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	dev_dbg(xudc->dev,
62162306a36Sopenharmony_ci		"%s: %p, lo = %#x, hi = %#x, status = %#x, control = %#x\n",
62262306a36Sopenharmony_ci		type, trb, trb->data_lo, trb->data_hi, trb->status,
62362306a36Sopenharmony_ci		trb->control);
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic void tegra_xudc_limit_port_speed(struct tegra_xudc *xudc)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	u32 val;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	/* limit port speed to gen 1 */
63162306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT56);
63262306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK);
63362306a36Sopenharmony_ci	val |= SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(0x260);
63462306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT56);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT57);
63762306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK);
63862306a36Sopenharmony_ci	val |= SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(0x6D6);
63962306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT57);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT65);
64262306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK);
64362306a36Sopenharmony_ci	val |= SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(0x4B0);
64462306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT66);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT66);
64762306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK);
64862306a36Sopenharmony_ci	val |= SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(0x4B0);
64962306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT66);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT67);
65262306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK);
65362306a36Sopenharmony_ci	val |= SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(0x4B0);
65462306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT67);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT72);
65762306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK);
65862306a36Sopenharmony_ci	val |= SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(0x10);
65962306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT72);
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic void tegra_xudc_restore_port_speed(struct tegra_xudc *xudc)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	u32 val;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	/* restore port speed to gen2 */
66762306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT56);
66862306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK);
66962306a36Sopenharmony_ci	val |= SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(0x438);
67062306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT56);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT57);
67362306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK);
67462306a36Sopenharmony_ci	val |= SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(0x528);
67562306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT57);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT65);
67862306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK);
67962306a36Sopenharmony_ci	val |= SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(0xE10);
68062306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT66);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT66);
68362306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK);
68462306a36Sopenharmony_ci	val |= SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(0x348);
68562306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT66);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT67);
68862306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK);
68962306a36Sopenharmony_ci	val |= SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(0x5a0);
69062306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT67);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT72);
69362306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK);
69462306a36Sopenharmony_ci	val |= SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(0x1c21);
69562306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT72);
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	int err;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	pm_runtime_get_sync(xudc->dev);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	tegra_phy_xusb_utmi_pad_power_on(xudc->curr_utmi_phy);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	err = phy_power_on(xudc->curr_utmi_phy);
70762306a36Sopenharmony_ci	if (err < 0)
70862306a36Sopenharmony_ci		dev_err(xudc->dev, "UTMI power on failed: %d\n", err);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	err = phy_power_on(xudc->curr_usb3_phy);
71162306a36Sopenharmony_ci	if (err < 0)
71262306a36Sopenharmony_ci		dev_err(xudc->dev, "USB3 PHY power on failed: %d\n", err);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	dev_dbg(xudc->dev, "device mode on\n");
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
71762306a36Sopenharmony_ci			 USB_ROLE_DEVICE);
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	bool connected = false;
72362306a36Sopenharmony_ci	u32 pls, val;
72462306a36Sopenharmony_ci	int err;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	dev_dbg(xudc->dev, "device mode off\n");
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	connected = !!(xudc_readl(xudc, PORTSC) & PORTSC_CCS);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	reinit_completion(&xudc->disconnect_complete);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (xudc->soc->port_speed_quirk)
73362306a36Sopenharmony_ci		tegra_xudc_restore_port_speed(xudc);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >>
73862306a36Sopenharmony_ci		PORTSC_PLS_SHIFT;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	/* Direct link to U0 if disconnected in RESUME or U2. */
74162306a36Sopenharmony_ci	if (xudc->soc->pls_quirk && xudc->gadget.speed == USB_SPEED_SUPER &&
74262306a36Sopenharmony_ci	    (pls == PORTSC_PLS_RESUME || pls == PORTSC_PLS_U2)) {
74362306a36Sopenharmony_ci		val = xudc_readl(xudc, PORTPM);
74462306a36Sopenharmony_ci		val |= PORTPM_FRWE;
74562306a36Sopenharmony_ci		xudc_writel(xudc, val, PORTPM);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci		val = xudc_readl(xudc, PORTSC);
74862306a36Sopenharmony_ci		val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK);
74962306a36Sopenharmony_ci		val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_U0);
75062306a36Sopenharmony_ci		xudc_writel(xudc, val, PORTSC);
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	/* Wait for disconnect event. */
75462306a36Sopenharmony_ci	if (connected)
75562306a36Sopenharmony_ci		wait_for_completion(&xudc->disconnect_complete);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	/* Make sure interrupt handler has completed before powergating. */
75862306a36Sopenharmony_ci	synchronize_irq(xudc->irq);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	tegra_phy_xusb_utmi_pad_power_down(xudc->curr_utmi_phy);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	err = phy_power_off(xudc->curr_utmi_phy);
76362306a36Sopenharmony_ci	if (err < 0)
76462306a36Sopenharmony_ci		dev_err(xudc->dev, "UTMI PHY power off failed: %d\n", err);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	err = phy_power_off(xudc->curr_usb3_phy);
76762306a36Sopenharmony_ci	if (err < 0)
76862306a36Sopenharmony_ci		dev_err(xudc->dev, "USB3 PHY power off failed: %d\n", err);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	pm_runtime_put(xudc->dev);
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic void tegra_xudc_usb_role_sw_work(struct work_struct *work)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	struct tegra_xudc *xudc = container_of(work, struct tegra_xudc,
77662306a36Sopenharmony_ci					       usb_role_sw_work);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	if (xudc->device_mode)
77962306a36Sopenharmony_ci		tegra_xudc_device_mode_on(xudc);
78062306a36Sopenharmony_ci	else
78162306a36Sopenharmony_ci		tegra_xudc_device_mode_off(xudc);
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_cistatic int tegra_xudc_get_phy_index(struct tegra_xudc *xudc,
78562306a36Sopenharmony_ci					      struct usb_phy *usbphy)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	unsigned int i;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	for (i = 0; i < xudc->soc->num_phys; i++) {
79062306a36Sopenharmony_ci		if (xudc->usbphy[i] && usbphy == xudc->usbphy[i])
79162306a36Sopenharmony_ci			return i;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	dev_info(xudc->dev, "phy index could not be found for shared USB PHY");
79562306a36Sopenharmony_ci	return -1;
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic void tegra_xudc_update_data_role(struct tegra_xudc *xudc,
79962306a36Sopenharmony_ci					      struct usb_phy *usbphy)
80062306a36Sopenharmony_ci{
80162306a36Sopenharmony_ci	int phy_index;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	if ((xudc->device_mode && usbphy->last_event == USB_EVENT_VBUS) ||
80462306a36Sopenharmony_ci	    (!xudc->device_mode && usbphy->last_event != USB_EVENT_VBUS)) {
80562306a36Sopenharmony_ci		dev_dbg(xudc->dev, "Same role(%d) received. Ignore",
80662306a36Sopenharmony_ci			xudc->device_mode);
80762306a36Sopenharmony_ci		return;
80862306a36Sopenharmony_ci	}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	xudc->device_mode = (usbphy->last_event == USB_EVENT_VBUS) ? true :
81162306a36Sopenharmony_ci								     false;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	phy_index = tegra_xudc_get_phy_index(xudc, usbphy);
81462306a36Sopenharmony_ci	dev_dbg(xudc->dev, "%s(): current phy index is %d\n", __func__,
81562306a36Sopenharmony_ci		phy_index);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	if (!xudc->suspended && phy_index != -1) {
81862306a36Sopenharmony_ci		xudc->curr_utmi_phy = xudc->utmi_phy[phy_index];
81962306a36Sopenharmony_ci		xudc->curr_usb3_phy = xudc->usb3_phy[phy_index];
82062306a36Sopenharmony_ci		xudc->curr_usbphy = usbphy;
82162306a36Sopenharmony_ci		schedule_work(&xudc->usb_role_sw_work);
82262306a36Sopenharmony_ci	}
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic int tegra_xudc_vbus_notify(struct notifier_block *nb,
82662306a36Sopenharmony_ci					 unsigned long action, void *data)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct tegra_xudc *xudc = container_of(nb, struct tegra_xudc,
82962306a36Sopenharmony_ci					       vbus_nb);
83062306a36Sopenharmony_ci	struct usb_phy *usbphy = (struct usb_phy *)data;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	dev_dbg(xudc->dev, "%s(): event is %d\n", __func__, usbphy->last_event);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	tegra_xudc_update_data_role(xudc, usbphy);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	return NOTIFY_OK;
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cistatic void tegra_xudc_plc_reset_work(struct work_struct *work)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	struct delayed_work *dwork = to_delayed_work(work);
84262306a36Sopenharmony_ci	struct tegra_xudc *xudc = container_of(dwork, struct tegra_xudc,
84362306a36Sopenharmony_ci					       plc_reset_work);
84462306a36Sopenharmony_ci	unsigned long flags;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	if (xudc->wait_csc) {
84962306a36Sopenharmony_ci		u32 pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >>
85062306a36Sopenharmony_ci			PORTSC_PLS_SHIFT;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci		if (pls == PORTSC_PLS_INACTIVE) {
85362306a36Sopenharmony_ci			dev_info(xudc->dev, "PLS = Inactive. Toggle VBUS\n");
85462306a36Sopenharmony_ci			phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
85562306a36Sopenharmony_ci					 USB_ROLE_NONE);
85662306a36Sopenharmony_ci			phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
85762306a36Sopenharmony_ci					 USB_ROLE_DEVICE);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci			xudc->wait_csc = false;
86062306a36Sopenharmony_ci		}
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_cistatic void tegra_xudc_port_reset_war_work(struct work_struct *work)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	struct delayed_work *dwork = to_delayed_work(work);
86962306a36Sopenharmony_ci	struct tegra_xudc *xudc =
87062306a36Sopenharmony_ci		container_of(dwork, struct tegra_xudc, port_reset_war_work);
87162306a36Sopenharmony_ci	unsigned long flags;
87262306a36Sopenharmony_ci	u32 pls;
87362306a36Sopenharmony_ci	int ret;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (xudc->device_mode && xudc->wait_for_sec_prc) {
87862306a36Sopenharmony_ci		pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >>
87962306a36Sopenharmony_ci			PORTSC_PLS_SHIFT;
88062306a36Sopenharmony_ci		dev_dbg(xudc->dev, "pls = %x\n", pls);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci		if (pls == PORTSC_PLS_DISABLED) {
88362306a36Sopenharmony_ci			dev_dbg(xudc->dev, "toggle vbus\n");
88462306a36Sopenharmony_ci			/* PRC doesn't complete in 100ms, toggle the vbus */
88562306a36Sopenharmony_ci			ret = tegra_phy_xusb_utmi_port_reset(
88662306a36Sopenharmony_ci				xudc->curr_utmi_phy);
88762306a36Sopenharmony_ci			if (ret == 1)
88862306a36Sopenharmony_ci				xudc->wait_for_sec_prc = 0;
88962306a36Sopenharmony_ci		}
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cistatic dma_addr_t trb_virt_to_phys(struct tegra_xudc_ep *ep,
89662306a36Sopenharmony_ci				   struct tegra_xudc_trb *trb)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	unsigned int index;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	index = trb - ep->transfer_ring;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	if (WARN_ON(index >= XUDC_TRANSFER_RING_SIZE))
90362306a36Sopenharmony_ci		return 0;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	return (ep->transfer_ring_phys + index * sizeof(*trb));
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic struct tegra_xudc_trb *trb_phys_to_virt(struct tegra_xudc_ep *ep,
90962306a36Sopenharmony_ci					       dma_addr_t addr)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	struct tegra_xudc_trb *trb;
91262306a36Sopenharmony_ci	unsigned int index;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	index = (addr - ep->transfer_ring_phys) / sizeof(*trb);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	if (WARN_ON(index >= XUDC_TRANSFER_RING_SIZE))
91762306a36Sopenharmony_ci		return NULL;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	trb = &ep->transfer_ring[index];
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	return trb;
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_cistatic void ep_reload(struct tegra_xudc *xudc, unsigned int ep)
92562306a36Sopenharmony_ci{
92662306a36Sopenharmony_ci	xudc_writel(xudc, BIT(ep), EP_RELOAD);
92762306a36Sopenharmony_ci	xudc_readl_poll(xudc, EP_RELOAD, BIT(ep), 0);
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_cistatic void ep_pause(struct tegra_xudc *xudc, unsigned int ep)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	u32 val;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	val = xudc_readl(xudc, EP_PAUSE);
93562306a36Sopenharmony_ci	if (val & BIT(ep))
93662306a36Sopenharmony_ci		return;
93762306a36Sopenharmony_ci	val |= BIT(ep);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	xudc_writel(xudc, val, EP_PAUSE);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep));
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	xudc_writel(xudc, BIT(ep), EP_STCHG);
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_cistatic void ep_unpause(struct tegra_xudc *xudc, unsigned int ep)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	u32 val;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	val = xudc_readl(xudc, EP_PAUSE);
95162306a36Sopenharmony_ci	if (!(val & BIT(ep)))
95262306a36Sopenharmony_ci		return;
95362306a36Sopenharmony_ci	val &= ~BIT(ep);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	xudc_writel(xudc, val, EP_PAUSE);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep));
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	xudc_writel(xudc, BIT(ep), EP_STCHG);
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_cistatic void ep_unpause_all(struct tegra_xudc *xudc)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	u32 val;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	val = xudc_readl(xudc, EP_PAUSE);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	xudc_writel(xudc, 0, EP_PAUSE);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	xudc_readl_poll(xudc, EP_STCHG, val, val);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	xudc_writel(xudc, val, EP_STCHG);
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic void ep_halt(struct tegra_xudc *xudc, unsigned int ep)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	u32 val;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	val = xudc_readl(xudc, EP_HALT);
98062306a36Sopenharmony_ci	if (val & BIT(ep))
98162306a36Sopenharmony_ci		return;
98262306a36Sopenharmony_ci	val |= BIT(ep);
98362306a36Sopenharmony_ci	xudc_writel(xudc, val, EP_HALT);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep));
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	xudc_writel(xudc, BIT(ep), EP_STCHG);
98862306a36Sopenharmony_ci}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_cistatic void ep_unhalt(struct tegra_xudc *xudc, unsigned int ep)
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	u32 val;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	val = xudc_readl(xudc, EP_HALT);
99562306a36Sopenharmony_ci	if (!(val & BIT(ep)))
99662306a36Sopenharmony_ci		return;
99762306a36Sopenharmony_ci	val &= ~BIT(ep);
99862306a36Sopenharmony_ci	xudc_writel(xudc, val, EP_HALT);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep));
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	xudc_writel(xudc, BIT(ep), EP_STCHG);
100362306a36Sopenharmony_ci}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_cistatic void ep_unhalt_all(struct tegra_xudc *xudc)
100662306a36Sopenharmony_ci{
100762306a36Sopenharmony_ci	u32 val;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	val = xudc_readl(xudc, EP_HALT);
101062306a36Sopenharmony_ci	if (!val)
101162306a36Sopenharmony_ci		return;
101262306a36Sopenharmony_ci	xudc_writel(xudc, 0, EP_HALT);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	xudc_readl_poll(xudc, EP_STCHG, val, val);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	xudc_writel(xudc, val, EP_STCHG);
101762306a36Sopenharmony_ci}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_cistatic void ep_wait_for_stopped(struct tegra_xudc *xudc, unsigned int ep)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	xudc_readl_poll(xudc, EP_STOPPED, BIT(ep), BIT(ep));
102262306a36Sopenharmony_ci	xudc_writel(xudc, BIT(ep), EP_STOPPED);
102362306a36Sopenharmony_ci}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_cistatic void ep_wait_for_inactive(struct tegra_xudc *xudc, unsigned int ep)
102662306a36Sopenharmony_ci{
102762306a36Sopenharmony_ci	xudc_readl_poll(xudc, EP_THREAD_ACTIVE, BIT(ep), 0);
102862306a36Sopenharmony_ci}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_cistatic void tegra_xudc_req_done(struct tegra_xudc_ep *ep,
103162306a36Sopenharmony_ci				struct tegra_xudc_request *req, int status)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	struct tegra_xudc *xudc = ep->xudc;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	dev_dbg(xudc->dev, "completing request %p on EP %u with status %d\n",
103662306a36Sopenharmony_ci		 req, ep->index, status);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (likely(req->usb_req.status == -EINPROGRESS))
103962306a36Sopenharmony_ci		req->usb_req.status = status;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	list_del_init(&req->list);
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	if (usb_endpoint_xfer_control(ep->desc)) {
104462306a36Sopenharmony_ci		usb_gadget_unmap_request(&xudc->gadget, &req->usb_req,
104562306a36Sopenharmony_ci					 (xudc->setup_state ==
104662306a36Sopenharmony_ci					  DATA_STAGE_XFER));
104762306a36Sopenharmony_ci	} else {
104862306a36Sopenharmony_ci		usb_gadget_unmap_request(&xudc->gadget, &req->usb_req,
104962306a36Sopenharmony_ci					 usb_endpoint_dir_in(ep->desc));
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	spin_unlock(&xudc->lock);
105362306a36Sopenharmony_ci	usb_gadget_giveback_request(&ep->usb_ep, &req->usb_req);
105462306a36Sopenharmony_ci	spin_lock(&xudc->lock);
105562306a36Sopenharmony_ci}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_cistatic void tegra_xudc_ep_nuke(struct tegra_xudc_ep *ep, int status)
105862306a36Sopenharmony_ci{
105962306a36Sopenharmony_ci	struct tegra_xudc_request *req;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	while (!list_empty(&ep->queue)) {
106262306a36Sopenharmony_ci		req = list_first_entry(&ep->queue, struct tegra_xudc_request,
106362306a36Sopenharmony_ci				       list);
106462306a36Sopenharmony_ci		tegra_xudc_req_done(ep, req, status);
106562306a36Sopenharmony_ci	}
106662306a36Sopenharmony_ci}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_cistatic unsigned int ep_available_trbs(struct tegra_xudc_ep *ep)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	if (ep->ring_full)
107162306a36Sopenharmony_ci		return 0;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	if (ep->deq_ptr > ep->enq_ptr)
107462306a36Sopenharmony_ci		return ep->deq_ptr - ep->enq_ptr - 1;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	return XUDC_TRANSFER_RING_SIZE - (ep->enq_ptr - ep->deq_ptr) - 2;
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_cistatic void tegra_xudc_queue_one_trb(struct tegra_xudc_ep *ep,
108062306a36Sopenharmony_ci				     struct tegra_xudc_request *req,
108162306a36Sopenharmony_ci				     struct tegra_xudc_trb *trb,
108262306a36Sopenharmony_ci				     bool ioc)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	struct tegra_xudc *xudc = ep->xudc;
108562306a36Sopenharmony_ci	dma_addr_t buf_addr;
108662306a36Sopenharmony_ci	size_t len;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	len = min_t(size_t, XUDC_TRB_MAX_BUFFER_SIZE, req->usb_req.length -
108962306a36Sopenharmony_ci		    req->buf_queued);
109062306a36Sopenharmony_ci	if (len > 0)
109162306a36Sopenharmony_ci		buf_addr = req->usb_req.dma + req->buf_queued;
109262306a36Sopenharmony_ci	else
109362306a36Sopenharmony_ci		buf_addr = 0;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	trb_write_data_ptr(trb, buf_addr);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	trb_write_transfer_len(trb, len);
109862306a36Sopenharmony_ci	trb_write_td_size(trb, req->trbs_needed - req->trbs_queued - 1);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	if (req->trbs_queued == req->trbs_needed - 1 ||
110162306a36Sopenharmony_ci		(req->need_zlp && req->trbs_queued == req->trbs_needed - 2))
110262306a36Sopenharmony_ci		trb_write_chain(trb, 0);
110362306a36Sopenharmony_ci	else
110462306a36Sopenharmony_ci		trb_write_chain(trb, 1);
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	trb_write_ioc(trb, ioc);
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	if (usb_endpoint_dir_out(ep->desc) ||
110962306a36Sopenharmony_ci	    (usb_endpoint_xfer_control(ep->desc) &&
111062306a36Sopenharmony_ci	     (xudc->setup_state == DATA_STAGE_RECV)))
111162306a36Sopenharmony_ci		trb_write_isp(trb, 1);
111262306a36Sopenharmony_ci	else
111362306a36Sopenharmony_ci		trb_write_isp(trb, 0);
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	if (usb_endpoint_xfer_control(ep->desc)) {
111662306a36Sopenharmony_ci		if (xudc->setup_state == DATA_STAGE_XFER ||
111762306a36Sopenharmony_ci		    xudc->setup_state == DATA_STAGE_RECV)
111862306a36Sopenharmony_ci			trb_write_type(trb, TRB_TYPE_DATA_STAGE);
111962306a36Sopenharmony_ci		else
112062306a36Sopenharmony_ci			trb_write_type(trb, TRB_TYPE_STATUS_STAGE);
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci		if (xudc->setup_state == DATA_STAGE_XFER ||
112362306a36Sopenharmony_ci		    xudc->setup_state == STATUS_STAGE_XFER)
112462306a36Sopenharmony_ci			trb_write_data_stage_dir(trb, 1);
112562306a36Sopenharmony_ci		else
112662306a36Sopenharmony_ci			trb_write_data_stage_dir(trb, 0);
112762306a36Sopenharmony_ci	} else if (usb_endpoint_xfer_isoc(ep->desc)) {
112862306a36Sopenharmony_ci		trb_write_type(trb, TRB_TYPE_ISOCH);
112962306a36Sopenharmony_ci		trb_write_sia(trb, 1);
113062306a36Sopenharmony_ci		trb_write_frame_id(trb, 0);
113162306a36Sopenharmony_ci		trb_write_tlbpc(trb, 0);
113262306a36Sopenharmony_ci	} else if (usb_ss_max_streams(ep->comp_desc)) {
113362306a36Sopenharmony_ci		trb_write_type(trb, TRB_TYPE_STREAM);
113462306a36Sopenharmony_ci		trb_write_stream_id(trb, req->usb_req.stream_id);
113562306a36Sopenharmony_ci	} else {
113662306a36Sopenharmony_ci		trb_write_type(trb, TRB_TYPE_NORMAL);
113762306a36Sopenharmony_ci		trb_write_stream_id(trb, 0);
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	trb_write_cycle(trb, ep->pcs);
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	req->trbs_queued++;
114362306a36Sopenharmony_ci	req->buf_queued += len;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	dump_trb(xudc, "TRANSFER", trb);
114662306a36Sopenharmony_ci}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_cistatic unsigned int tegra_xudc_queue_trbs(struct tegra_xudc_ep *ep,
114962306a36Sopenharmony_ci					  struct tegra_xudc_request *req)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	unsigned int i, count, available;
115262306a36Sopenharmony_ci	bool wait_td = false;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	available = ep_available_trbs(ep);
115562306a36Sopenharmony_ci	count = req->trbs_needed - req->trbs_queued;
115662306a36Sopenharmony_ci	if (available < count) {
115762306a36Sopenharmony_ci		count = available;
115862306a36Sopenharmony_ci		ep->ring_full = true;
115962306a36Sopenharmony_ci	}
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	/*
116262306a36Sopenharmony_ci	 * To generate zero-length packet on USB bus, SW needs schedule a
116362306a36Sopenharmony_ci	 * standalone zero-length TD. According to HW's behavior, SW needs
116462306a36Sopenharmony_ci	 * to schedule TDs in different ways for different endpoint types.
116562306a36Sopenharmony_ci	 *
116662306a36Sopenharmony_ci	 * For control endpoint:
116762306a36Sopenharmony_ci	 * - Data stage TD (IOC = 1, CH = 0)
116862306a36Sopenharmony_ci	 * - Ring doorbell and wait transfer event
116962306a36Sopenharmony_ci	 * - Data stage TD for ZLP (IOC = 1, CH = 0)
117062306a36Sopenharmony_ci	 * - Ring doorbell
117162306a36Sopenharmony_ci	 *
117262306a36Sopenharmony_ci	 * For bulk and interrupt endpoints:
117362306a36Sopenharmony_ci	 * - Normal transfer TD (IOC = 0, CH = 0)
117462306a36Sopenharmony_ci	 * - Normal transfer TD for ZLP (IOC = 1, CH = 0)
117562306a36Sopenharmony_ci	 * - Ring doorbell
117662306a36Sopenharmony_ci	 */
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	if (req->need_zlp && usb_endpoint_xfer_control(ep->desc) && count > 1)
117962306a36Sopenharmony_ci		wait_td = true;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	if (!req->first_trb)
118262306a36Sopenharmony_ci		req->first_trb = &ep->transfer_ring[ep->enq_ptr];
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
118562306a36Sopenharmony_ci		struct tegra_xudc_trb *trb = &ep->transfer_ring[ep->enq_ptr];
118662306a36Sopenharmony_ci		bool ioc = false;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		if ((i == count - 1) || (wait_td && i == count - 2))
118962306a36Sopenharmony_ci			ioc = true;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci		tegra_xudc_queue_one_trb(ep, req, trb, ioc);
119262306a36Sopenharmony_ci		req->last_trb = trb;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci		ep->enq_ptr++;
119562306a36Sopenharmony_ci		if (ep->enq_ptr == XUDC_TRANSFER_RING_SIZE - 1) {
119662306a36Sopenharmony_ci			trb = &ep->transfer_ring[ep->enq_ptr];
119762306a36Sopenharmony_ci			trb_write_cycle(trb, ep->pcs);
119862306a36Sopenharmony_ci			ep->pcs = !ep->pcs;
119962306a36Sopenharmony_ci			ep->enq_ptr = 0;
120062306a36Sopenharmony_ci		}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci		if (ioc)
120362306a36Sopenharmony_ci			break;
120462306a36Sopenharmony_ci	}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	return count;
120762306a36Sopenharmony_ci}
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_cistatic void tegra_xudc_ep_ring_doorbell(struct tegra_xudc_ep *ep)
121062306a36Sopenharmony_ci{
121162306a36Sopenharmony_ci	struct tegra_xudc *xudc = ep->xudc;
121262306a36Sopenharmony_ci	u32 val;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	if (list_empty(&ep->queue))
121562306a36Sopenharmony_ci		return;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	val = DB_TARGET(ep->index);
121862306a36Sopenharmony_ci	if (usb_endpoint_xfer_control(ep->desc)) {
121962306a36Sopenharmony_ci		val |= DB_STREAMID(xudc->setup_seq_num);
122062306a36Sopenharmony_ci	} else if (usb_ss_max_streams(ep->comp_desc) > 0) {
122162306a36Sopenharmony_ci		struct tegra_xudc_request *req;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci		/* Don't ring doorbell if the stream has been rejected. */
122462306a36Sopenharmony_ci		if (ep->stream_rejected)
122562306a36Sopenharmony_ci			return;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci		req = list_first_entry(&ep->queue, struct tegra_xudc_request,
122862306a36Sopenharmony_ci				       list);
122962306a36Sopenharmony_ci		val |= DB_STREAMID(req->usb_req.stream_id);
123062306a36Sopenharmony_ci	}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	dev_dbg(xudc->dev, "ring doorbell: %#x\n", val);
123362306a36Sopenharmony_ci	xudc_writel(xudc, val, DB);
123462306a36Sopenharmony_ci}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_cistatic void tegra_xudc_ep_kick_queue(struct tegra_xudc_ep *ep)
123762306a36Sopenharmony_ci{
123862306a36Sopenharmony_ci	struct tegra_xudc_request *req;
123962306a36Sopenharmony_ci	bool trbs_queued = false;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	list_for_each_entry(req, &ep->queue, list) {
124262306a36Sopenharmony_ci		if (ep->ring_full)
124362306a36Sopenharmony_ci			break;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci		if (tegra_xudc_queue_trbs(ep, req) > 0)
124662306a36Sopenharmony_ci			trbs_queued = true;
124762306a36Sopenharmony_ci	}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	if (trbs_queued)
125062306a36Sopenharmony_ci		tegra_xudc_ep_ring_doorbell(ep);
125162306a36Sopenharmony_ci}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_cistatic int
125462306a36Sopenharmony_ci__tegra_xudc_ep_queue(struct tegra_xudc_ep *ep, struct tegra_xudc_request *req)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	struct tegra_xudc *xudc = ep->xudc;
125762306a36Sopenharmony_ci	int err;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	if (usb_endpoint_xfer_control(ep->desc) && !list_empty(&ep->queue)) {
126062306a36Sopenharmony_ci		dev_err(xudc->dev, "control EP has pending transfers\n");
126162306a36Sopenharmony_ci		return -EINVAL;
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	if (usb_endpoint_xfer_control(ep->desc)) {
126562306a36Sopenharmony_ci		err = usb_gadget_map_request(&xudc->gadget, &req->usb_req,
126662306a36Sopenharmony_ci					     (xudc->setup_state ==
126762306a36Sopenharmony_ci					      DATA_STAGE_XFER));
126862306a36Sopenharmony_ci	} else {
126962306a36Sopenharmony_ci		err = usb_gadget_map_request(&xudc->gadget, &req->usb_req,
127062306a36Sopenharmony_ci					     usb_endpoint_dir_in(ep->desc));
127162306a36Sopenharmony_ci	}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	if (err < 0) {
127462306a36Sopenharmony_ci		dev_err(xudc->dev, "failed to map request: %d\n", err);
127562306a36Sopenharmony_ci		return err;
127662306a36Sopenharmony_ci	}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	req->first_trb = NULL;
127962306a36Sopenharmony_ci	req->last_trb = NULL;
128062306a36Sopenharmony_ci	req->buf_queued = 0;
128162306a36Sopenharmony_ci	req->trbs_queued = 0;
128262306a36Sopenharmony_ci	req->need_zlp = false;
128362306a36Sopenharmony_ci	req->trbs_needed = DIV_ROUND_UP(req->usb_req.length,
128462306a36Sopenharmony_ci					XUDC_TRB_MAX_BUFFER_SIZE);
128562306a36Sopenharmony_ci	if (req->usb_req.length == 0)
128662306a36Sopenharmony_ci		req->trbs_needed++;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	if (!usb_endpoint_xfer_isoc(ep->desc) &&
128962306a36Sopenharmony_ci	    req->usb_req.zero && req->usb_req.length &&
129062306a36Sopenharmony_ci	    ((req->usb_req.length % ep->usb_ep.maxpacket) == 0)) {
129162306a36Sopenharmony_ci		req->trbs_needed++;
129262306a36Sopenharmony_ci		req->need_zlp = true;
129362306a36Sopenharmony_ci	}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	req->usb_req.status = -EINPROGRESS;
129662306a36Sopenharmony_ci	req->usb_req.actual = 0;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	list_add_tail(&req->list, &ep->queue);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	tegra_xudc_ep_kick_queue(ep);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	return 0;
130362306a36Sopenharmony_ci}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_cistatic int
130662306a36Sopenharmony_citegra_xudc_ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req,
130762306a36Sopenharmony_ci		    gfp_t gfp)
130862306a36Sopenharmony_ci{
130962306a36Sopenharmony_ci	struct tegra_xudc_request *req;
131062306a36Sopenharmony_ci	struct tegra_xudc_ep *ep;
131162306a36Sopenharmony_ci	struct tegra_xudc *xudc;
131262306a36Sopenharmony_ci	unsigned long flags;
131362306a36Sopenharmony_ci	int ret;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	if (!usb_ep || !usb_req)
131662306a36Sopenharmony_ci		return -EINVAL;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	ep = to_xudc_ep(usb_ep);
131962306a36Sopenharmony_ci	req = to_xudc_req(usb_req);
132062306a36Sopenharmony_ci	xudc = ep->xudc;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
132362306a36Sopenharmony_ci	if (xudc->powergated || !ep->desc) {
132462306a36Sopenharmony_ci		ret = -ESHUTDOWN;
132562306a36Sopenharmony_ci		goto unlock;
132662306a36Sopenharmony_ci	}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	ret = __tegra_xudc_ep_queue(ep, req);
132962306a36Sopenharmony_ciunlock:
133062306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	return ret;
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_cistatic void squeeze_transfer_ring(struct tegra_xudc_ep *ep,
133662306a36Sopenharmony_ci				  struct tegra_xudc_request *req)
133762306a36Sopenharmony_ci{
133862306a36Sopenharmony_ci	struct tegra_xudc_trb *trb = req->first_trb;
133962306a36Sopenharmony_ci	bool pcs_enq = trb_read_cycle(trb);
134062306a36Sopenharmony_ci	bool pcs;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	/*
134362306a36Sopenharmony_ci	 * Clear out all the TRBs part of or after the cancelled request,
134462306a36Sopenharmony_ci	 * and must correct trb cycle bit to the last un-enqueued state.
134562306a36Sopenharmony_ci	 */
134662306a36Sopenharmony_ci	while (trb != &ep->transfer_ring[ep->enq_ptr]) {
134762306a36Sopenharmony_ci		pcs = trb_read_cycle(trb);
134862306a36Sopenharmony_ci		memset(trb, 0, sizeof(*trb));
134962306a36Sopenharmony_ci		trb_write_cycle(trb, !pcs);
135062306a36Sopenharmony_ci		trb++;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci		if (trb_read_type(trb) == TRB_TYPE_LINK)
135362306a36Sopenharmony_ci			trb = ep->transfer_ring;
135462306a36Sopenharmony_ci	}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	/* Requests will be re-queued at the start of the cancelled request. */
135762306a36Sopenharmony_ci	ep->enq_ptr = req->first_trb - ep->transfer_ring;
135862306a36Sopenharmony_ci	/*
135962306a36Sopenharmony_ci	 * Retrieve the correct cycle bit state from the first trb of
136062306a36Sopenharmony_ci	 * the cancelled request.
136162306a36Sopenharmony_ci	 */
136262306a36Sopenharmony_ci	ep->pcs = pcs_enq;
136362306a36Sopenharmony_ci	ep->ring_full = false;
136462306a36Sopenharmony_ci	list_for_each_entry_continue(req, &ep->queue, list) {
136562306a36Sopenharmony_ci		req->usb_req.status = -EINPROGRESS;
136662306a36Sopenharmony_ci		req->usb_req.actual = 0;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci		req->first_trb = NULL;
136962306a36Sopenharmony_ci		req->last_trb = NULL;
137062306a36Sopenharmony_ci		req->buf_queued = 0;
137162306a36Sopenharmony_ci		req->trbs_queued = 0;
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci}
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci/*
137662306a36Sopenharmony_ci * Determine if the given TRB is in the range [first trb, last trb] for the
137762306a36Sopenharmony_ci * given request.
137862306a36Sopenharmony_ci */
137962306a36Sopenharmony_cistatic bool trb_in_request(struct tegra_xudc_ep *ep,
138062306a36Sopenharmony_ci			   struct tegra_xudc_request *req,
138162306a36Sopenharmony_ci			   struct tegra_xudc_trb *trb)
138262306a36Sopenharmony_ci{
138362306a36Sopenharmony_ci	dev_dbg(ep->xudc->dev, "%s: request %p -> %p; trb %p\n", __func__,
138462306a36Sopenharmony_ci		req->first_trb, req->last_trb, trb);
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	if (trb >= req->first_trb && (trb <= req->last_trb ||
138762306a36Sopenharmony_ci				      req->last_trb < req->first_trb))
138862306a36Sopenharmony_ci		return true;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	if (trb < req->first_trb && trb <= req->last_trb &&
139162306a36Sopenharmony_ci	    req->last_trb < req->first_trb)
139262306a36Sopenharmony_ci		return true;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	return false;
139562306a36Sopenharmony_ci}
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci/*
139862306a36Sopenharmony_ci * Determine if the given TRB is in the range [EP enqueue pointer, first TRB)
139962306a36Sopenharmony_ci * for the given endpoint and request.
140062306a36Sopenharmony_ci */
140162306a36Sopenharmony_cistatic bool trb_before_request(struct tegra_xudc_ep *ep,
140262306a36Sopenharmony_ci			       struct tegra_xudc_request *req,
140362306a36Sopenharmony_ci			       struct tegra_xudc_trb *trb)
140462306a36Sopenharmony_ci{
140562306a36Sopenharmony_ci	struct tegra_xudc_trb *enq_trb = &ep->transfer_ring[ep->enq_ptr];
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	dev_dbg(ep->xudc->dev, "%s: request %p -> %p; enq ptr: %p; trb %p\n",
140862306a36Sopenharmony_ci		__func__, req->first_trb, req->last_trb, enq_trb, trb);
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	if (trb < req->first_trb && (enq_trb <= trb ||
141162306a36Sopenharmony_ci				     req->first_trb < enq_trb))
141262306a36Sopenharmony_ci		return true;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	if (trb > req->first_trb && req->first_trb < enq_trb && enq_trb <= trb)
141562306a36Sopenharmony_ci		return true;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	return false;
141862306a36Sopenharmony_ci}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_cistatic int
142162306a36Sopenharmony_ci__tegra_xudc_ep_dequeue(struct tegra_xudc_ep *ep,
142262306a36Sopenharmony_ci			struct tegra_xudc_request *req)
142362306a36Sopenharmony_ci{
142462306a36Sopenharmony_ci	struct tegra_xudc *xudc = ep->xudc;
142562306a36Sopenharmony_ci	struct tegra_xudc_request *r = NULL, *iter;
142662306a36Sopenharmony_ci	struct tegra_xudc_trb *deq_trb;
142762306a36Sopenharmony_ci	bool busy, kick_queue = false;
142862306a36Sopenharmony_ci	int ret = 0;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	/* Make sure the request is actually queued to this endpoint. */
143162306a36Sopenharmony_ci	list_for_each_entry(iter, &ep->queue, list) {
143262306a36Sopenharmony_ci		if (iter != req)
143362306a36Sopenharmony_ci			continue;
143462306a36Sopenharmony_ci		r = iter;
143562306a36Sopenharmony_ci		break;
143662306a36Sopenharmony_ci	}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	if (!r)
143962306a36Sopenharmony_ci		return -EINVAL;
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	/* Request hasn't been queued in the transfer ring yet. */
144262306a36Sopenharmony_ci	if (!req->trbs_queued) {
144362306a36Sopenharmony_ci		tegra_xudc_req_done(ep, req, -ECONNRESET);
144462306a36Sopenharmony_ci		return 0;
144562306a36Sopenharmony_ci	}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	/* Halt DMA for this endpoint. */
144862306a36Sopenharmony_ci	if (ep_ctx_read_state(ep->context) == EP_STATE_RUNNING) {
144962306a36Sopenharmony_ci		ep_pause(xudc, ep->index);
145062306a36Sopenharmony_ci		ep_wait_for_inactive(xudc, ep->index);
145162306a36Sopenharmony_ci	}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	deq_trb = trb_phys_to_virt(ep, ep_ctx_read_deq_ptr(ep->context));
145462306a36Sopenharmony_ci	/* Is the hardware processing the TRB at the dequeue pointer? */
145562306a36Sopenharmony_ci	busy = (trb_read_cycle(deq_trb) == ep_ctx_read_dcs(ep->context));
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	if (trb_in_request(ep, req, deq_trb) && busy) {
145862306a36Sopenharmony_ci		/*
145962306a36Sopenharmony_ci		 * Request has been partially completed or it hasn't
146062306a36Sopenharmony_ci		 * started processing yet.
146162306a36Sopenharmony_ci		 */
146262306a36Sopenharmony_ci		dma_addr_t deq_ptr;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci		squeeze_transfer_ring(ep, req);
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci		req->usb_req.actual = ep_ctx_read_edtla(ep->context);
146762306a36Sopenharmony_ci		tegra_xudc_req_done(ep, req, -ECONNRESET);
146862306a36Sopenharmony_ci		kick_queue = true;
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci		/* EDTLA is > 0: request has been partially completed */
147162306a36Sopenharmony_ci		if (req->usb_req.actual > 0) {
147262306a36Sopenharmony_ci			/*
147362306a36Sopenharmony_ci			 * Abort the pending transfer and update the dequeue
147462306a36Sopenharmony_ci			 * pointer
147562306a36Sopenharmony_ci			 */
147662306a36Sopenharmony_ci			ep_ctx_write_edtla(ep->context, 0);
147762306a36Sopenharmony_ci			ep_ctx_write_partial_td(ep->context, 0);
147862306a36Sopenharmony_ci			ep_ctx_write_data_offset(ep->context, 0);
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci			deq_ptr = trb_virt_to_phys(ep,
148162306a36Sopenharmony_ci					&ep->transfer_ring[ep->enq_ptr]);
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci			if (dma_mapping_error(xudc->dev, deq_ptr)) {
148462306a36Sopenharmony_ci				ret = -EINVAL;
148562306a36Sopenharmony_ci			} else {
148662306a36Sopenharmony_ci				ep_ctx_write_deq_ptr(ep->context, deq_ptr);
148762306a36Sopenharmony_ci				ep_ctx_write_dcs(ep->context, ep->pcs);
148862306a36Sopenharmony_ci				ep_reload(xudc, ep->index);
148962306a36Sopenharmony_ci			}
149062306a36Sopenharmony_ci		}
149162306a36Sopenharmony_ci	} else if (trb_before_request(ep, req, deq_trb) && busy) {
149262306a36Sopenharmony_ci		/* Request hasn't started processing yet. */
149362306a36Sopenharmony_ci		squeeze_transfer_ring(ep, req);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci		tegra_xudc_req_done(ep, req, -ECONNRESET);
149662306a36Sopenharmony_ci		kick_queue = true;
149762306a36Sopenharmony_ci	} else {
149862306a36Sopenharmony_ci		/*
149962306a36Sopenharmony_ci		 * Request has completed, but we haven't processed the
150062306a36Sopenharmony_ci		 * completion event yet.
150162306a36Sopenharmony_ci		 */
150262306a36Sopenharmony_ci		tegra_xudc_req_done(ep, req, -ECONNRESET);
150362306a36Sopenharmony_ci		ret = -EINVAL;
150462306a36Sopenharmony_ci	}
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	/* Resume the endpoint. */
150762306a36Sopenharmony_ci	ep_unpause(xudc, ep->index);
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	if (kick_queue)
151062306a36Sopenharmony_ci		tegra_xudc_ep_kick_queue(ep);
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	return ret;
151362306a36Sopenharmony_ci}
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_cistatic int
151662306a36Sopenharmony_citegra_xudc_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req)
151762306a36Sopenharmony_ci{
151862306a36Sopenharmony_ci	struct tegra_xudc_request *req;
151962306a36Sopenharmony_ci	struct tegra_xudc_ep *ep;
152062306a36Sopenharmony_ci	struct tegra_xudc *xudc;
152162306a36Sopenharmony_ci	unsigned long flags;
152262306a36Sopenharmony_ci	int ret;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	if (!usb_ep || !usb_req)
152562306a36Sopenharmony_ci		return -EINVAL;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	ep = to_xudc_ep(usb_ep);
152862306a36Sopenharmony_ci	req = to_xudc_req(usb_req);
152962306a36Sopenharmony_ci	xudc = ep->xudc;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	if (xudc->powergated || !ep->desc) {
153462306a36Sopenharmony_ci		ret = -ESHUTDOWN;
153562306a36Sopenharmony_ci		goto unlock;
153662306a36Sopenharmony_ci	}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	ret = __tegra_xudc_ep_dequeue(ep, req);
153962306a36Sopenharmony_ciunlock:
154062306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	return ret;
154362306a36Sopenharmony_ci}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_cistatic int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt)
154662306a36Sopenharmony_ci{
154762306a36Sopenharmony_ci	struct tegra_xudc *xudc = ep->xudc;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	if (!ep->desc)
155062306a36Sopenharmony_ci		return -EINVAL;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	if (usb_endpoint_xfer_isoc(ep->desc)) {
155362306a36Sopenharmony_ci		dev_err(xudc->dev, "can't halt isochronous EP\n");
155462306a36Sopenharmony_ci		return -ENOTSUPP;
155562306a36Sopenharmony_ci	}
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	if (!!(xudc_readl(xudc, EP_HALT) & BIT(ep->index)) == halt) {
155862306a36Sopenharmony_ci		dev_dbg(xudc->dev, "EP %u already %s\n", ep->index,
155962306a36Sopenharmony_ci			halt ? "halted" : "not halted");
156062306a36Sopenharmony_ci		return 0;
156162306a36Sopenharmony_ci	}
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	if (halt) {
156462306a36Sopenharmony_ci		ep_halt(xudc, ep->index);
156562306a36Sopenharmony_ci	} else {
156662306a36Sopenharmony_ci		ep_ctx_write_state(ep->context, EP_STATE_DISABLED);
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci		ep_reload(xudc, ep->index);
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci		ep_ctx_write_state(ep->context, EP_STATE_RUNNING);
157162306a36Sopenharmony_ci		ep_ctx_write_rsvd(ep->context, 0);
157262306a36Sopenharmony_ci		ep_ctx_write_partial_td(ep->context, 0);
157362306a36Sopenharmony_ci		ep_ctx_write_splitxstate(ep->context, 0);
157462306a36Sopenharmony_ci		ep_ctx_write_seq_num(ep->context, 0);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci		ep_reload(xudc, ep->index);
157762306a36Sopenharmony_ci		ep_unpause(xudc, ep->index);
157862306a36Sopenharmony_ci		ep_unhalt(xudc, ep->index);
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci		tegra_xudc_ep_ring_doorbell(ep);
158162306a36Sopenharmony_ci	}
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	return 0;
158462306a36Sopenharmony_ci}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_cistatic int tegra_xudc_ep_set_halt(struct usb_ep *usb_ep, int value)
158762306a36Sopenharmony_ci{
158862306a36Sopenharmony_ci	struct tegra_xudc_ep *ep;
158962306a36Sopenharmony_ci	struct tegra_xudc *xudc;
159062306a36Sopenharmony_ci	unsigned long flags;
159162306a36Sopenharmony_ci	int ret;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	if (!usb_ep)
159462306a36Sopenharmony_ci		return -EINVAL;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	ep = to_xudc_ep(usb_ep);
159762306a36Sopenharmony_ci	xudc = ep->xudc;
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
160062306a36Sopenharmony_ci	if (xudc->powergated) {
160162306a36Sopenharmony_ci		ret = -ESHUTDOWN;
160262306a36Sopenharmony_ci		goto unlock;
160362306a36Sopenharmony_ci	}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	if (value && usb_endpoint_dir_in(ep->desc) &&
160662306a36Sopenharmony_ci	    !list_empty(&ep->queue)) {
160762306a36Sopenharmony_ci		dev_err(xudc->dev, "can't halt EP with requests pending\n");
160862306a36Sopenharmony_ci		ret = -EAGAIN;
160962306a36Sopenharmony_ci		goto unlock;
161062306a36Sopenharmony_ci	}
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	ret = __tegra_xudc_ep_set_halt(ep, value);
161362306a36Sopenharmony_ciunlock:
161462306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	return ret;
161762306a36Sopenharmony_ci}
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_cistatic void tegra_xudc_ep_context_setup(struct tegra_xudc_ep *ep)
162062306a36Sopenharmony_ci{
162162306a36Sopenharmony_ci	const struct usb_endpoint_descriptor *desc = ep->desc;
162262306a36Sopenharmony_ci	const struct usb_ss_ep_comp_descriptor *comp_desc = ep->comp_desc;
162362306a36Sopenharmony_ci	struct tegra_xudc *xudc = ep->xudc;
162462306a36Sopenharmony_ci	u16 maxpacket, maxburst = 0, esit = 0;
162562306a36Sopenharmony_ci	u32 val;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	maxpacket = usb_endpoint_maxp(desc);
162862306a36Sopenharmony_ci	if (xudc->gadget.speed == USB_SPEED_SUPER) {
162962306a36Sopenharmony_ci		if (!usb_endpoint_xfer_control(desc))
163062306a36Sopenharmony_ci			maxburst = comp_desc->bMaxBurst;
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci		if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc))
163362306a36Sopenharmony_ci			esit = le16_to_cpu(comp_desc->wBytesPerInterval);
163462306a36Sopenharmony_ci	} else if ((xudc->gadget.speed < USB_SPEED_SUPER) &&
163562306a36Sopenharmony_ci		   (usb_endpoint_xfer_int(desc) ||
163662306a36Sopenharmony_ci		    usb_endpoint_xfer_isoc(desc))) {
163762306a36Sopenharmony_ci		if (xudc->gadget.speed == USB_SPEED_HIGH) {
163862306a36Sopenharmony_ci			maxburst = usb_endpoint_maxp_mult(desc) - 1;
163962306a36Sopenharmony_ci			if (maxburst == 0x3) {
164062306a36Sopenharmony_ci				dev_warn(xudc->dev,
164162306a36Sopenharmony_ci					 "invalid endpoint maxburst\n");
164262306a36Sopenharmony_ci				maxburst = 0x2;
164362306a36Sopenharmony_ci			}
164462306a36Sopenharmony_ci		}
164562306a36Sopenharmony_ci		esit = maxpacket * (maxburst + 1);
164662306a36Sopenharmony_ci	}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	memset(ep->context, 0, sizeof(*ep->context));
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	ep_ctx_write_state(ep->context, EP_STATE_RUNNING);
165162306a36Sopenharmony_ci	ep_ctx_write_interval(ep->context, desc->bInterval);
165262306a36Sopenharmony_ci	if (xudc->gadget.speed == USB_SPEED_SUPER) {
165362306a36Sopenharmony_ci		if (usb_endpoint_xfer_isoc(desc)) {
165462306a36Sopenharmony_ci			ep_ctx_write_mult(ep->context,
165562306a36Sopenharmony_ci					  comp_desc->bmAttributes & 0x3);
165662306a36Sopenharmony_ci		}
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci		if (usb_endpoint_xfer_bulk(desc)) {
165962306a36Sopenharmony_ci			ep_ctx_write_max_pstreams(ep->context,
166062306a36Sopenharmony_ci						  comp_desc->bmAttributes &
166162306a36Sopenharmony_ci						  0x1f);
166262306a36Sopenharmony_ci			ep_ctx_write_lsa(ep->context, 1);
166362306a36Sopenharmony_ci		}
166462306a36Sopenharmony_ci	}
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	if (!usb_endpoint_xfer_control(desc) && usb_endpoint_dir_out(desc))
166762306a36Sopenharmony_ci		val = usb_endpoint_type(desc);
166862306a36Sopenharmony_ci	else
166962306a36Sopenharmony_ci		val = usb_endpoint_type(desc) + EP_TYPE_CONTROL;
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	ep_ctx_write_type(ep->context, val);
167262306a36Sopenharmony_ci	ep_ctx_write_cerr(ep->context, 0x3);
167362306a36Sopenharmony_ci	ep_ctx_write_max_packet_size(ep->context, maxpacket);
167462306a36Sopenharmony_ci	ep_ctx_write_max_burst_size(ep->context, maxburst);
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	ep_ctx_write_deq_ptr(ep->context, ep->transfer_ring_phys);
167762306a36Sopenharmony_ci	ep_ctx_write_dcs(ep->context, ep->pcs);
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	/* Select a reasonable average TRB length based on endpoint type. */
168062306a36Sopenharmony_ci	switch (usb_endpoint_type(desc)) {
168162306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_CONTROL:
168262306a36Sopenharmony_ci		val = 8;
168362306a36Sopenharmony_ci		break;
168462306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_INT:
168562306a36Sopenharmony_ci		val = 1024;
168662306a36Sopenharmony_ci		break;
168762306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_BULK:
168862306a36Sopenharmony_ci	case USB_ENDPOINT_XFER_ISOC:
168962306a36Sopenharmony_ci	default:
169062306a36Sopenharmony_ci		val = 3072;
169162306a36Sopenharmony_ci		break;
169262306a36Sopenharmony_ci	}
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	ep_ctx_write_avg_trb_len(ep->context, val);
169562306a36Sopenharmony_ci	ep_ctx_write_max_esit_payload(ep->context, esit);
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	ep_ctx_write_cerrcnt(ep->context, 0x3);
169862306a36Sopenharmony_ci}
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_cistatic void setup_link_trb(struct tegra_xudc_ep *ep,
170162306a36Sopenharmony_ci			   struct tegra_xudc_trb *trb)
170262306a36Sopenharmony_ci{
170362306a36Sopenharmony_ci	trb_write_data_ptr(trb, ep->transfer_ring_phys);
170462306a36Sopenharmony_ci	trb_write_type(trb, TRB_TYPE_LINK);
170562306a36Sopenharmony_ci	trb_write_toggle_cycle(trb, 1);
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_cistatic int __tegra_xudc_ep_disable(struct tegra_xudc_ep *ep)
170962306a36Sopenharmony_ci{
171062306a36Sopenharmony_ci	struct tegra_xudc *xudc = ep->xudc;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	if (ep_ctx_read_state(ep->context) == EP_STATE_DISABLED) {
171362306a36Sopenharmony_ci		dev_err(xudc->dev, "endpoint %u already disabled\n",
171462306a36Sopenharmony_ci			ep->index);
171562306a36Sopenharmony_ci		return -EINVAL;
171662306a36Sopenharmony_ci	}
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	ep_ctx_write_state(ep->context, EP_STATE_DISABLED);
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	ep_reload(xudc, ep->index);
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	tegra_xudc_ep_nuke(ep, -ESHUTDOWN);
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	xudc->nr_enabled_eps--;
172562306a36Sopenharmony_ci	if (usb_endpoint_xfer_isoc(ep->desc))
172662306a36Sopenharmony_ci		xudc->nr_isoch_eps--;
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	ep->desc = NULL;
172962306a36Sopenharmony_ci	ep->comp_desc = NULL;
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	memset(ep->context, 0, sizeof(*ep->context));
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	ep_unpause(xudc, ep->index);
173462306a36Sopenharmony_ci	ep_unhalt(xudc, ep->index);
173562306a36Sopenharmony_ci	if (xudc_readl(xudc, EP_STOPPED) & BIT(ep->index))
173662306a36Sopenharmony_ci		xudc_writel(xudc, BIT(ep->index), EP_STOPPED);
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	/*
173962306a36Sopenharmony_ci	 * If this is the last endpoint disabled in a de-configure request,
174062306a36Sopenharmony_ci	 * switch back to address state.
174162306a36Sopenharmony_ci	 */
174262306a36Sopenharmony_ci	if ((xudc->device_state == USB_STATE_CONFIGURED) &&
174362306a36Sopenharmony_ci	    (xudc->nr_enabled_eps == 1)) {
174462306a36Sopenharmony_ci		u32 val;
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci		xudc->device_state = USB_STATE_ADDRESS;
174762306a36Sopenharmony_ci		usb_gadget_set_state(&xudc->gadget, xudc->device_state);
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci		val = xudc_readl(xudc, CTRL);
175062306a36Sopenharmony_ci		val &= ~CTRL_RUN;
175162306a36Sopenharmony_ci		xudc_writel(xudc, val, CTRL);
175262306a36Sopenharmony_ci	}
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	dev_info(xudc->dev, "ep %u disabled\n", ep->index);
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	return 0;
175762306a36Sopenharmony_ci}
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_cistatic int tegra_xudc_ep_disable(struct usb_ep *usb_ep)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	struct tegra_xudc_ep *ep;
176262306a36Sopenharmony_ci	struct tegra_xudc *xudc;
176362306a36Sopenharmony_ci	unsigned long flags;
176462306a36Sopenharmony_ci	int ret;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	if (!usb_ep)
176762306a36Sopenharmony_ci		return -EINVAL;
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	ep = to_xudc_ep(usb_ep);
177062306a36Sopenharmony_ci	xudc = ep->xudc;
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
177362306a36Sopenharmony_ci	if (xudc->powergated) {
177462306a36Sopenharmony_ci		ret = -ESHUTDOWN;
177562306a36Sopenharmony_ci		goto unlock;
177662306a36Sopenharmony_ci	}
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	ret = __tegra_xudc_ep_disable(ep);
177962306a36Sopenharmony_ciunlock:
178062306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	return ret;
178362306a36Sopenharmony_ci}
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_cistatic int __tegra_xudc_ep_enable(struct tegra_xudc_ep *ep,
178662306a36Sopenharmony_ci				  const struct usb_endpoint_descriptor *desc)
178762306a36Sopenharmony_ci{
178862306a36Sopenharmony_ci	struct tegra_xudc *xudc = ep->xudc;
178962306a36Sopenharmony_ci	unsigned int i;
179062306a36Sopenharmony_ci	u32 val;
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	if (xudc->gadget.speed == USB_SPEED_SUPER &&
179362306a36Sopenharmony_ci		!usb_endpoint_xfer_control(desc) && !ep->usb_ep.comp_desc)
179462306a36Sopenharmony_ci		return -EINVAL;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	/* Disable the EP if it is not disabled */
179762306a36Sopenharmony_ci	if (ep_ctx_read_state(ep->context) != EP_STATE_DISABLED)
179862306a36Sopenharmony_ci		__tegra_xudc_ep_disable(ep);
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	ep->desc = desc;
180162306a36Sopenharmony_ci	ep->comp_desc = ep->usb_ep.comp_desc;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	if (usb_endpoint_xfer_isoc(desc)) {
180462306a36Sopenharmony_ci		if (xudc->nr_isoch_eps > XUDC_MAX_ISOCH_EPS) {
180562306a36Sopenharmony_ci			dev_err(xudc->dev, "too many isochronous endpoints\n");
180662306a36Sopenharmony_ci			return -EBUSY;
180762306a36Sopenharmony_ci		}
180862306a36Sopenharmony_ci		xudc->nr_isoch_eps++;
180962306a36Sopenharmony_ci	}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	memset(ep->transfer_ring, 0, XUDC_TRANSFER_RING_SIZE *
181262306a36Sopenharmony_ci	       sizeof(*ep->transfer_ring));
181362306a36Sopenharmony_ci	setup_link_trb(ep, &ep->transfer_ring[XUDC_TRANSFER_RING_SIZE - 1]);
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	ep->enq_ptr = 0;
181662306a36Sopenharmony_ci	ep->deq_ptr = 0;
181762306a36Sopenharmony_ci	ep->pcs = true;
181862306a36Sopenharmony_ci	ep->ring_full = false;
181962306a36Sopenharmony_ci	xudc->nr_enabled_eps++;
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	tegra_xudc_ep_context_setup(ep);
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	/*
182462306a36Sopenharmony_ci	 * No need to reload and un-halt EP0.  This will be done automatically
182562306a36Sopenharmony_ci	 * once a valid SETUP packet is received.
182662306a36Sopenharmony_ci	 */
182762306a36Sopenharmony_ci	if (usb_endpoint_xfer_control(desc))
182862306a36Sopenharmony_ci		goto out;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	/*
183162306a36Sopenharmony_ci	 * Transition to configured state once the first non-control
183262306a36Sopenharmony_ci	 * endpoint is enabled.
183362306a36Sopenharmony_ci	 */
183462306a36Sopenharmony_ci	if (xudc->device_state == USB_STATE_ADDRESS) {
183562306a36Sopenharmony_ci		val = xudc_readl(xudc, CTRL);
183662306a36Sopenharmony_ci		val |= CTRL_RUN;
183762306a36Sopenharmony_ci		xudc_writel(xudc, val, CTRL);
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci		xudc->device_state = USB_STATE_CONFIGURED;
184062306a36Sopenharmony_ci		usb_gadget_set_state(&xudc->gadget, xudc->device_state);
184162306a36Sopenharmony_ci	}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	if (usb_endpoint_xfer_isoc(desc)) {
184462306a36Sopenharmony_ci		/*
184562306a36Sopenharmony_ci		 * Pause all bulk endpoints when enabling an isoch endpoint
184662306a36Sopenharmony_ci		 * to ensure the isoch endpoint is allocated enough bandwidth.
184762306a36Sopenharmony_ci		 */
184862306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) {
184962306a36Sopenharmony_ci			if (xudc->ep[i].desc &&
185062306a36Sopenharmony_ci			    usb_endpoint_xfer_bulk(xudc->ep[i].desc))
185162306a36Sopenharmony_ci				ep_pause(xudc, i);
185262306a36Sopenharmony_ci		}
185362306a36Sopenharmony_ci	}
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	ep_reload(xudc, ep->index);
185662306a36Sopenharmony_ci	ep_unpause(xudc, ep->index);
185762306a36Sopenharmony_ci	ep_unhalt(xudc, ep->index);
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	if (usb_endpoint_xfer_isoc(desc)) {
186062306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) {
186162306a36Sopenharmony_ci			if (xudc->ep[i].desc &&
186262306a36Sopenharmony_ci			    usb_endpoint_xfer_bulk(xudc->ep[i].desc))
186362306a36Sopenharmony_ci				ep_unpause(xudc, i);
186462306a36Sopenharmony_ci		}
186562306a36Sopenharmony_ci	}
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ciout:
186862306a36Sopenharmony_ci	dev_info(xudc->dev, "EP %u (type: %s, dir: %s) enabled\n", ep->index,
186962306a36Sopenharmony_ci		 usb_ep_type_string(usb_endpoint_type(ep->desc)),
187062306a36Sopenharmony_ci		 usb_endpoint_dir_in(ep->desc) ? "in" : "out");
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	return 0;
187362306a36Sopenharmony_ci}
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_cistatic int tegra_xudc_ep_enable(struct usb_ep *usb_ep,
187662306a36Sopenharmony_ci				const struct usb_endpoint_descriptor *desc)
187762306a36Sopenharmony_ci{
187862306a36Sopenharmony_ci	struct tegra_xudc_ep *ep;
187962306a36Sopenharmony_ci	struct tegra_xudc *xudc;
188062306a36Sopenharmony_ci	unsigned long flags;
188162306a36Sopenharmony_ci	int ret;
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	if  (!usb_ep || !desc || (desc->bDescriptorType != USB_DT_ENDPOINT))
188462306a36Sopenharmony_ci		return -EINVAL;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	ep = to_xudc_ep(usb_ep);
188762306a36Sopenharmony_ci	xudc = ep->xudc;
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
189062306a36Sopenharmony_ci	if (xudc->powergated) {
189162306a36Sopenharmony_ci		ret = -ESHUTDOWN;
189262306a36Sopenharmony_ci		goto unlock;
189362306a36Sopenharmony_ci	}
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	ret = __tegra_xudc_ep_enable(ep, desc);
189662306a36Sopenharmony_ciunlock:
189762306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	return ret;
190062306a36Sopenharmony_ci}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_cistatic struct usb_request *
190362306a36Sopenharmony_citegra_xudc_ep_alloc_request(struct usb_ep *usb_ep, gfp_t gfp)
190462306a36Sopenharmony_ci{
190562306a36Sopenharmony_ci	struct tegra_xudc_request *req;
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	req = kzalloc(sizeof(*req), gfp);
190862306a36Sopenharmony_ci	if (!req)
190962306a36Sopenharmony_ci		return NULL;
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci	INIT_LIST_HEAD(&req->list);
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	return &req->usb_req;
191462306a36Sopenharmony_ci}
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_cistatic void tegra_xudc_ep_free_request(struct usb_ep *usb_ep,
191762306a36Sopenharmony_ci				       struct usb_request *usb_req)
191862306a36Sopenharmony_ci{
191962306a36Sopenharmony_ci	struct tegra_xudc_request *req = to_xudc_req(usb_req);
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	kfree(req);
192262306a36Sopenharmony_ci}
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_cistatic const struct usb_ep_ops tegra_xudc_ep_ops = {
192562306a36Sopenharmony_ci	.enable = tegra_xudc_ep_enable,
192662306a36Sopenharmony_ci	.disable = tegra_xudc_ep_disable,
192762306a36Sopenharmony_ci	.alloc_request = tegra_xudc_ep_alloc_request,
192862306a36Sopenharmony_ci	.free_request = tegra_xudc_ep_free_request,
192962306a36Sopenharmony_ci	.queue = tegra_xudc_ep_queue,
193062306a36Sopenharmony_ci	.dequeue = tegra_xudc_ep_dequeue,
193162306a36Sopenharmony_ci	.set_halt = tegra_xudc_ep_set_halt,
193262306a36Sopenharmony_ci};
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_cistatic int tegra_xudc_ep0_enable(struct usb_ep *usb_ep,
193562306a36Sopenharmony_ci				 const struct usb_endpoint_descriptor *desc)
193662306a36Sopenharmony_ci{
193762306a36Sopenharmony_ci	return -EBUSY;
193862306a36Sopenharmony_ci}
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_cistatic int tegra_xudc_ep0_disable(struct usb_ep *usb_ep)
194162306a36Sopenharmony_ci{
194262306a36Sopenharmony_ci	return -EBUSY;
194362306a36Sopenharmony_ci}
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_cistatic const struct usb_ep_ops tegra_xudc_ep0_ops = {
194662306a36Sopenharmony_ci	.enable = tegra_xudc_ep0_enable,
194762306a36Sopenharmony_ci	.disable = tegra_xudc_ep0_disable,
194862306a36Sopenharmony_ci	.alloc_request = tegra_xudc_ep_alloc_request,
194962306a36Sopenharmony_ci	.free_request = tegra_xudc_ep_free_request,
195062306a36Sopenharmony_ci	.queue = tegra_xudc_ep_queue,
195162306a36Sopenharmony_ci	.dequeue = tegra_xudc_ep_dequeue,
195262306a36Sopenharmony_ci	.set_halt = tegra_xudc_ep_set_halt,
195362306a36Sopenharmony_ci};
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_cistatic int tegra_xudc_gadget_get_frame(struct usb_gadget *gadget)
195662306a36Sopenharmony_ci{
195762306a36Sopenharmony_ci	struct tegra_xudc *xudc = to_xudc(gadget);
195862306a36Sopenharmony_ci	unsigned long flags;
195962306a36Sopenharmony_ci	int ret;
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
196262306a36Sopenharmony_ci	if (xudc->powergated) {
196362306a36Sopenharmony_ci		ret = -ESHUTDOWN;
196462306a36Sopenharmony_ci		goto unlock;
196562306a36Sopenharmony_ci	}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	ret = (xudc_readl(xudc, MFINDEX) & MFINDEX_FRAME_MASK) >>
196862306a36Sopenharmony_ci		MFINDEX_FRAME_SHIFT;
196962306a36Sopenharmony_ciunlock:
197062306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	return ret;
197362306a36Sopenharmony_ci}
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_cistatic void tegra_xudc_resume_device_state(struct tegra_xudc *xudc)
197662306a36Sopenharmony_ci{
197762306a36Sopenharmony_ci	unsigned int i;
197862306a36Sopenharmony_ci	u32 val;
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci	ep_unpause_all(xudc);
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci	/* Direct link to U0. */
198362306a36Sopenharmony_ci	val = xudc_readl(xudc, PORTSC);
198462306a36Sopenharmony_ci	if (((val & PORTSC_PLS_MASK) >> PORTSC_PLS_SHIFT) != PORTSC_PLS_U0) {
198562306a36Sopenharmony_ci		val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK);
198662306a36Sopenharmony_ci		val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_U0);
198762306a36Sopenharmony_ci		xudc_writel(xudc, val, PORTSC);
198862306a36Sopenharmony_ci	}
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	if (xudc->device_state == USB_STATE_SUSPENDED) {
199162306a36Sopenharmony_ci		xudc->device_state = xudc->resume_state;
199262306a36Sopenharmony_ci		usb_gadget_set_state(&xudc->gadget, xudc->device_state);
199362306a36Sopenharmony_ci		xudc->resume_state = 0;
199462306a36Sopenharmony_ci	}
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	/*
199762306a36Sopenharmony_ci	 * Doorbells may be dropped if they are sent too soon (< ~200ns)
199862306a36Sopenharmony_ci	 * after unpausing the endpoint.  Wait for 500ns just to be safe.
199962306a36Sopenharmony_ci	 */
200062306a36Sopenharmony_ci	ndelay(500);
200162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(xudc->ep); i++)
200262306a36Sopenharmony_ci		tegra_xudc_ep_ring_doorbell(&xudc->ep[i]);
200362306a36Sopenharmony_ci}
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_cistatic int tegra_xudc_gadget_wakeup(struct usb_gadget *gadget)
200662306a36Sopenharmony_ci{
200762306a36Sopenharmony_ci	struct tegra_xudc *xudc = to_xudc(gadget);
200862306a36Sopenharmony_ci	unsigned long flags;
200962306a36Sopenharmony_ci	int ret = 0;
201062306a36Sopenharmony_ci	u32 val;
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	if (xudc->powergated) {
201562306a36Sopenharmony_ci		ret = -ESHUTDOWN;
201662306a36Sopenharmony_ci		goto unlock;
201762306a36Sopenharmony_ci	}
201862306a36Sopenharmony_ci	val = xudc_readl(xudc, PORTPM);
201962306a36Sopenharmony_ci	dev_dbg(xudc->dev, "%s: PORTPM=%#x, speed=%x\n", __func__,
202062306a36Sopenharmony_ci			val, gadget->speed);
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	if (((xudc->gadget.speed <= USB_SPEED_HIGH) &&
202362306a36Sopenharmony_ci	     (val & PORTPM_RWE)) ||
202462306a36Sopenharmony_ci	    ((xudc->gadget.speed == USB_SPEED_SUPER) &&
202562306a36Sopenharmony_ci	     (val & PORTPM_FRWE))) {
202662306a36Sopenharmony_ci		tegra_xudc_resume_device_state(xudc);
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci		/* Send Device Notification packet. */
202962306a36Sopenharmony_ci		if (xudc->gadget.speed == USB_SPEED_SUPER) {
203062306a36Sopenharmony_ci			val = DEVNOTIF_LO_TYPE(DEVNOTIF_LO_TYPE_FUNCTION_WAKE)
203162306a36Sopenharmony_ci					     | DEVNOTIF_LO_TRIG;
203262306a36Sopenharmony_ci			xudc_writel(xudc, 0, DEVNOTIF_HI);
203362306a36Sopenharmony_ci			xudc_writel(xudc, val, DEVNOTIF_LO);
203462306a36Sopenharmony_ci		}
203562306a36Sopenharmony_ci	}
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ciunlock:
203862306a36Sopenharmony_ci	dev_dbg(xudc->dev, "%s: ret value is %d", __func__, ret);
203962306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	return ret;
204262306a36Sopenharmony_ci}
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_cistatic int tegra_xudc_gadget_pullup(struct usb_gadget *gadget, int is_on)
204562306a36Sopenharmony_ci{
204662306a36Sopenharmony_ci	struct tegra_xudc *xudc = to_xudc(gadget);
204762306a36Sopenharmony_ci	unsigned long flags;
204862306a36Sopenharmony_ci	u32 val;
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	pm_runtime_get_sync(xudc->dev);
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	if (is_on != xudc->pullup) {
205562306a36Sopenharmony_ci		val = xudc_readl(xudc, CTRL);
205662306a36Sopenharmony_ci		if (is_on)
205762306a36Sopenharmony_ci			val |= CTRL_ENABLE;
205862306a36Sopenharmony_ci		else
205962306a36Sopenharmony_ci			val &= ~CTRL_ENABLE;
206062306a36Sopenharmony_ci		xudc_writel(xudc, val, CTRL);
206162306a36Sopenharmony_ci	}
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	xudc->pullup = is_on;
206462306a36Sopenharmony_ci	dev_dbg(xudc->dev, "%s: pullup:%d", __func__, is_on);
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	pm_runtime_put(xudc->dev);
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	return 0;
207162306a36Sopenharmony_ci}
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_cistatic int tegra_xudc_gadget_start(struct usb_gadget *gadget,
207462306a36Sopenharmony_ci				   struct usb_gadget_driver *driver)
207562306a36Sopenharmony_ci{
207662306a36Sopenharmony_ci	struct tegra_xudc *xudc = to_xudc(gadget);
207762306a36Sopenharmony_ci	unsigned long flags;
207862306a36Sopenharmony_ci	u32 val;
207962306a36Sopenharmony_ci	int ret;
208062306a36Sopenharmony_ci	unsigned int i;
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	if (!driver)
208362306a36Sopenharmony_ci		return -EINVAL;
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	pm_runtime_get_sync(xudc->dev);
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	if (xudc->driver) {
209062306a36Sopenharmony_ci		ret = -EBUSY;
209162306a36Sopenharmony_ci		goto unlock;
209262306a36Sopenharmony_ci	}
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	xudc->setup_state = WAIT_FOR_SETUP;
209562306a36Sopenharmony_ci	xudc->device_state = USB_STATE_DEFAULT;
209662306a36Sopenharmony_ci	usb_gadget_set_state(&xudc->gadget, xudc->device_state);
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	ret = __tegra_xudc_ep_enable(&xudc->ep[0], &tegra_xudc_ep0_desc);
209962306a36Sopenharmony_ci	if (ret < 0)
210062306a36Sopenharmony_ci		goto unlock;
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci	val = xudc_readl(xudc, CTRL);
210362306a36Sopenharmony_ci	val |= CTRL_IE | CTRL_LSE;
210462306a36Sopenharmony_ci	xudc_writel(xudc, val, CTRL);
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	val = xudc_readl(xudc, PORTHALT);
210762306a36Sopenharmony_ci	val |= PORTHALT_STCHG_INTR_EN;
210862306a36Sopenharmony_ci	xudc_writel(xudc, val, PORTHALT);
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	if (xudc->pullup) {
211162306a36Sopenharmony_ci		val = xudc_readl(xudc, CTRL);
211262306a36Sopenharmony_ci		val |= CTRL_ENABLE;
211362306a36Sopenharmony_ci		xudc_writel(xudc, val, CTRL);
211462306a36Sopenharmony_ci	}
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	for (i = 0; i < xudc->soc->num_phys; i++)
211762306a36Sopenharmony_ci		if (xudc->usbphy[i])
211862306a36Sopenharmony_ci			otg_set_peripheral(xudc->usbphy[i]->otg, gadget);
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	xudc->driver = driver;
212162306a36Sopenharmony_ciunlock:
212262306a36Sopenharmony_ci	dev_dbg(xudc->dev, "%s: ret value is %d", __func__, ret);
212362306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	pm_runtime_put(xudc->dev);
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci	return ret;
212862306a36Sopenharmony_ci}
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_cistatic int tegra_xudc_gadget_stop(struct usb_gadget *gadget)
213162306a36Sopenharmony_ci{
213262306a36Sopenharmony_ci	struct tegra_xudc *xudc = to_xudc(gadget);
213362306a36Sopenharmony_ci	unsigned long flags;
213462306a36Sopenharmony_ci	u32 val;
213562306a36Sopenharmony_ci	unsigned int i;
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	pm_runtime_get_sync(xudc->dev);
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	for (i = 0; i < xudc->soc->num_phys; i++)
214262306a36Sopenharmony_ci		if (xudc->usbphy[i])
214362306a36Sopenharmony_ci			otg_set_peripheral(xudc->usbphy[i]->otg, NULL);
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci	val = xudc_readl(xudc, CTRL);
214662306a36Sopenharmony_ci	val &= ~(CTRL_IE | CTRL_ENABLE);
214762306a36Sopenharmony_ci	xudc_writel(xudc, val, CTRL);
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	__tegra_xudc_ep_disable(&xudc->ep[0]);
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	xudc->driver = NULL;
215262306a36Sopenharmony_ci	dev_dbg(xudc->dev, "Gadget stopped");
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	pm_runtime_put(xudc->dev);
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	return 0;
215962306a36Sopenharmony_ci}
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_cistatic int tegra_xudc_gadget_vbus_draw(struct usb_gadget *gadget,
216262306a36Sopenharmony_ci						unsigned int m_a)
216362306a36Sopenharmony_ci{
216462306a36Sopenharmony_ci	struct tegra_xudc *xudc = to_xudc(gadget);
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	dev_dbg(xudc->dev, "%s: %u mA\n", __func__, m_a);
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci	if (xudc->curr_usbphy && xudc->curr_usbphy->chg_type == SDP_TYPE)
216962306a36Sopenharmony_ci		return usb_phy_set_power(xudc->curr_usbphy, m_a);
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	return 0;
217262306a36Sopenharmony_ci}
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_cistatic int tegra_xudc_set_selfpowered(struct usb_gadget *gadget, int is_on)
217562306a36Sopenharmony_ci{
217662306a36Sopenharmony_ci	struct tegra_xudc *xudc = to_xudc(gadget);
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	dev_dbg(xudc->dev, "%s: %d\n", __func__, is_on);
217962306a36Sopenharmony_ci	xudc->selfpowered = !!is_on;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	return 0;
218262306a36Sopenharmony_ci}
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_cistatic const struct usb_gadget_ops tegra_xudc_gadget_ops = {
218562306a36Sopenharmony_ci	.get_frame = tegra_xudc_gadget_get_frame,
218662306a36Sopenharmony_ci	.wakeup = tegra_xudc_gadget_wakeup,
218762306a36Sopenharmony_ci	.pullup = tegra_xudc_gadget_pullup,
218862306a36Sopenharmony_ci	.udc_start = tegra_xudc_gadget_start,
218962306a36Sopenharmony_ci	.udc_stop = tegra_xudc_gadget_stop,
219062306a36Sopenharmony_ci	.vbus_draw = tegra_xudc_gadget_vbus_draw,
219162306a36Sopenharmony_ci	.set_selfpowered = tegra_xudc_set_selfpowered,
219262306a36Sopenharmony_ci};
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_cistatic void no_op_complete(struct usb_ep *ep, struct usb_request *req)
219562306a36Sopenharmony_ci{
219662306a36Sopenharmony_ci}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_cistatic int
219962306a36Sopenharmony_citegra_xudc_ep0_queue_status(struct tegra_xudc *xudc,
220062306a36Sopenharmony_ci		void (*cmpl)(struct usb_ep *, struct usb_request *))
220162306a36Sopenharmony_ci{
220262306a36Sopenharmony_ci	xudc->ep0_req->usb_req.buf = NULL;
220362306a36Sopenharmony_ci	xudc->ep0_req->usb_req.dma = 0;
220462306a36Sopenharmony_ci	xudc->ep0_req->usb_req.length = 0;
220562306a36Sopenharmony_ci	xudc->ep0_req->usb_req.complete = cmpl;
220662306a36Sopenharmony_ci	xudc->ep0_req->usb_req.context = xudc;
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	return __tegra_xudc_ep_queue(&xudc->ep[0], xudc->ep0_req);
220962306a36Sopenharmony_ci}
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_cistatic int
221262306a36Sopenharmony_citegra_xudc_ep0_queue_data(struct tegra_xudc *xudc, void *buf, size_t len,
221362306a36Sopenharmony_ci		void (*cmpl)(struct usb_ep *, struct usb_request *))
221462306a36Sopenharmony_ci{
221562306a36Sopenharmony_ci	xudc->ep0_req->usb_req.buf = buf;
221662306a36Sopenharmony_ci	xudc->ep0_req->usb_req.length = len;
221762306a36Sopenharmony_ci	xudc->ep0_req->usb_req.complete = cmpl;
221862306a36Sopenharmony_ci	xudc->ep0_req->usb_req.context = xudc;
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci	return __tegra_xudc_ep_queue(&xudc->ep[0], xudc->ep0_req);
222162306a36Sopenharmony_ci}
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_cistatic void tegra_xudc_ep0_req_done(struct tegra_xudc *xudc)
222462306a36Sopenharmony_ci{
222562306a36Sopenharmony_ci	switch (xudc->setup_state) {
222662306a36Sopenharmony_ci	case DATA_STAGE_XFER:
222762306a36Sopenharmony_ci		xudc->setup_state = STATUS_STAGE_RECV;
222862306a36Sopenharmony_ci		tegra_xudc_ep0_queue_status(xudc, no_op_complete);
222962306a36Sopenharmony_ci		break;
223062306a36Sopenharmony_ci	case DATA_STAGE_RECV:
223162306a36Sopenharmony_ci		xudc->setup_state = STATUS_STAGE_XFER;
223262306a36Sopenharmony_ci		tegra_xudc_ep0_queue_status(xudc, no_op_complete);
223362306a36Sopenharmony_ci		break;
223462306a36Sopenharmony_ci	default:
223562306a36Sopenharmony_ci		xudc->setup_state = WAIT_FOR_SETUP;
223662306a36Sopenharmony_ci		break;
223762306a36Sopenharmony_ci	}
223862306a36Sopenharmony_ci}
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_cistatic int tegra_xudc_ep0_delegate_req(struct tegra_xudc *xudc,
224162306a36Sopenharmony_ci				       struct usb_ctrlrequest *ctrl)
224262306a36Sopenharmony_ci{
224362306a36Sopenharmony_ci	int ret;
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci	spin_unlock(&xudc->lock);
224662306a36Sopenharmony_ci	ret = xudc->driver->setup(&xudc->gadget, ctrl);
224762306a36Sopenharmony_ci	spin_lock(&xudc->lock);
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci	return ret;
225062306a36Sopenharmony_ci}
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_cistatic void set_feature_complete(struct usb_ep *ep, struct usb_request *req)
225362306a36Sopenharmony_ci{
225462306a36Sopenharmony_ci	struct tegra_xudc *xudc = req->context;
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	if (xudc->test_mode_pattern) {
225762306a36Sopenharmony_ci		xudc_writel(xudc, xudc->test_mode_pattern, PORT_TM);
225862306a36Sopenharmony_ci		xudc->test_mode_pattern = 0;
225962306a36Sopenharmony_ci	}
226062306a36Sopenharmony_ci}
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_cistatic int tegra_xudc_ep0_set_feature(struct tegra_xudc *xudc,
226362306a36Sopenharmony_ci				      struct usb_ctrlrequest *ctrl)
226462306a36Sopenharmony_ci{
226562306a36Sopenharmony_ci	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
226662306a36Sopenharmony_ci	u32 feature = le16_to_cpu(ctrl->wValue);
226762306a36Sopenharmony_ci	u32 index = le16_to_cpu(ctrl->wIndex);
226862306a36Sopenharmony_ci	u32 val, ep;
226962306a36Sopenharmony_ci	int ret;
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci	if (le16_to_cpu(ctrl->wLength) != 0)
227262306a36Sopenharmony_ci		return -EINVAL;
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci	switch (ctrl->bRequestType & USB_RECIP_MASK) {
227562306a36Sopenharmony_ci	case USB_RECIP_DEVICE:
227662306a36Sopenharmony_ci		switch (feature) {
227762306a36Sopenharmony_ci		case USB_DEVICE_REMOTE_WAKEUP:
227862306a36Sopenharmony_ci			if ((xudc->gadget.speed == USB_SPEED_SUPER) ||
227962306a36Sopenharmony_ci			    (xudc->device_state == USB_STATE_DEFAULT))
228062306a36Sopenharmony_ci				return -EINVAL;
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci			val = xudc_readl(xudc, PORTPM);
228362306a36Sopenharmony_ci			if (set)
228462306a36Sopenharmony_ci				val |= PORTPM_RWE;
228562306a36Sopenharmony_ci			else
228662306a36Sopenharmony_ci				val &= ~PORTPM_RWE;
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci			xudc_writel(xudc, val, PORTPM);
228962306a36Sopenharmony_ci			break;
229062306a36Sopenharmony_ci		case USB_DEVICE_U1_ENABLE:
229162306a36Sopenharmony_ci		case USB_DEVICE_U2_ENABLE:
229262306a36Sopenharmony_ci			if ((xudc->device_state != USB_STATE_CONFIGURED) ||
229362306a36Sopenharmony_ci			    (xudc->gadget.speed != USB_SPEED_SUPER))
229462306a36Sopenharmony_ci				return -EINVAL;
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci			val = xudc_readl(xudc, PORTPM);
229762306a36Sopenharmony_ci			if ((feature == USB_DEVICE_U1_ENABLE) &&
229862306a36Sopenharmony_ci			     xudc->soc->u1_enable) {
229962306a36Sopenharmony_ci				if (set)
230062306a36Sopenharmony_ci					val |= PORTPM_U1E;
230162306a36Sopenharmony_ci				else
230262306a36Sopenharmony_ci					val &= ~PORTPM_U1E;
230362306a36Sopenharmony_ci			}
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci			if ((feature == USB_DEVICE_U2_ENABLE) &&
230662306a36Sopenharmony_ci			     xudc->soc->u2_enable) {
230762306a36Sopenharmony_ci				if (set)
230862306a36Sopenharmony_ci					val |= PORTPM_U2E;
230962306a36Sopenharmony_ci				else
231062306a36Sopenharmony_ci					val &= ~PORTPM_U2E;
231162306a36Sopenharmony_ci			}
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci			xudc_writel(xudc, val, PORTPM);
231462306a36Sopenharmony_ci			break;
231562306a36Sopenharmony_ci		case USB_DEVICE_TEST_MODE:
231662306a36Sopenharmony_ci			if (xudc->gadget.speed != USB_SPEED_HIGH)
231762306a36Sopenharmony_ci				return -EINVAL;
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci			if (!set)
232062306a36Sopenharmony_ci				return -EINVAL;
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci			xudc->test_mode_pattern = index >> 8;
232362306a36Sopenharmony_ci			break;
232462306a36Sopenharmony_ci		default:
232562306a36Sopenharmony_ci			return -EINVAL;
232662306a36Sopenharmony_ci		}
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_ci		break;
232962306a36Sopenharmony_ci	case USB_RECIP_INTERFACE:
233062306a36Sopenharmony_ci		if (xudc->device_state != USB_STATE_CONFIGURED)
233162306a36Sopenharmony_ci			return -EINVAL;
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_ci		switch (feature) {
233462306a36Sopenharmony_ci		case USB_INTRF_FUNC_SUSPEND:
233562306a36Sopenharmony_ci			if (set) {
233662306a36Sopenharmony_ci				val = xudc_readl(xudc, PORTPM);
233762306a36Sopenharmony_ci
233862306a36Sopenharmony_ci				if (index & USB_INTRF_FUNC_SUSPEND_RW)
233962306a36Sopenharmony_ci					val |= PORTPM_FRWE;
234062306a36Sopenharmony_ci				else
234162306a36Sopenharmony_ci					val &= ~PORTPM_FRWE;
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci				xudc_writel(xudc, val, PORTPM);
234462306a36Sopenharmony_ci			}
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci			return tegra_xudc_ep0_delegate_req(xudc, ctrl);
234762306a36Sopenharmony_ci		default:
234862306a36Sopenharmony_ci			return -EINVAL;
234962306a36Sopenharmony_ci		}
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci		break;
235262306a36Sopenharmony_ci	case USB_RECIP_ENDPOINT:
235362306a36Sopenharmony_ci		ep = (index & USB_ENDPOINT_NUMBER_MASK) * 2 +
235462306a36Sopenharmony_ci			((index & USB_DIR_IN) ? 1 : 0);
235562306a36Sopenharmony_ci
235662306a36Sopenharmony_ci		if ((xudc->device_state == USB_STATE_DEFAULT) ||
235762306a36Sopenharmony_ci		    ((xudc->device_state == USB_STATE_ADDRESS) &&
235862306a36Sopenharmony_ci		     (index != 0)))
235962306a36Sopenharmony_ci			return -EINVAL;
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci		ret = __tegra_xudc_ep_set_halt(&xudc->ep[ep], set);
236262306a36Sopenharmony_ci		if (ret < 0)
236362306a36Sopenharmony_ci			return ret;
236462306a36Sopenharmony_ci		break;
236562306a36Sopenharmony_ci	default:
236662306a36Sopenharmony_ci		return -EINVAL;
236762306a36Sopenharmony_ci	}
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci	return tegra_xudc_ep0_queue_status(xudc, set_feature_complete);
237062306a36Sopenharmony_ci}
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_cistatic int tegra_xudc_ep0_get_status(struct tegra_xudc *xudc,
237362306a36Sopenharmony_ci				     struct usb_ctrlrequest *ctrl)
237462306a36Sopenharmony_ci{
237562306a36Sopenharmony_ci	struct tegra_xudc_ep_context *ep_ctx;
237662306a36Sopenharmony_ci	u32 val, ep, index = le16_to_cpu(ctrl->wIndex);
237762306a36Sopenharmony_ci	u16 status = 0;
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci	if (!(ctrl->bRequestType & USB_DIR_IN))
238062306a36Sopenharmony_ci		return -EINVAL;
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_ci	if ((le16_to_cpu(ctrl->wValue) != 0) ||
238362306a36Sopenharmony_ci	    (le16_to_cpu(ctrl->wLength) != 2))
238462306a36Sopenharmony_ci		return -EINVAL;
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci	switch (ctrl->bRequestType & USB_RECIP_MASK) {
238762306a36Sopenharmony_ci	case USB_RECIP_DEVICE:
238862306a36Sopenharmony_ci		val = xudc_readl(xudc, PORTPM);
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci		if (xudc->selfpowered)
239162306a36Sopenharmony_ci			status |= BIT(USB_DEVICE_SELF_POWERED);
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci		if ((xudc->gadget.speed < USB_SPEED_SUPER) &&
239462306a36Sopenharmony_ci		    (val & PORTPM_RWE))
239562306a36Sopenharmony_ci			status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci		if (xudc->gadget.speed == USB_SPEED_SUPER) {
239862306a36Sopenharmony_ci			if (val & PORTPM_U1E)
239962306a36Sopenharmony_ci				status |= BIT(USB_DEV_STAT_U1_ENABLED);
240062306a36Sopenharmony_ci			if (val & PORTPM_U2E)
240162306a36Sopenharmony_ci				status |= BIT(USB_DEV_STAT_U2_ENABLED);
240262306a36Sopenharmony_ci		}
240362306a36Sopenharmony_ci		break;
240462306a36Sopenharmony_ci	case USB_RECIP_INTERFACE:
240562306a36Sopenharmony_ci		if (xudc->gadget.speed == USB_SPEED_SUPER) {
240662306a36Sopenharmony_ci			status |= USB_INTRF_STAT_FUNC_RW_CAP;
240762306a36Sopenharmony_ci			val = xudc_readl(xudc, PORTPM);
240862306a36Sopenharmony_ci			if (val & PORTPM_FRWE)
240962306a36Sopenharmony_ci				status |= USB_INTRF_STAT_FUNC_RW;
241062306a36Sopenharmony_ci		}
241162306a36Sopenharmony_ci		break;
241262306a36Sopenharmony_ci	case USB_RECIP_ENDPOINT:
241362306a36Sopenharmony_ci		ep = (index & USB_ENDPOINT_NUMBER_MASK) * 2 +
241462306a36Sopenharmony_ci			((index & USB_DIR_IN) ? 1 : 0);
241562306a36Sopenharmony_ci		ep_ctx = &xudc->ep_context[ep];
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci		if ((xudc->device_state != USB_STATE_CONFIGURED) &&
241862306a36Sopenharmony_ci		    ((xudc->device_state != USB_STATE_ADDRESS) || (ep != 0)))
241962306a36Sopenharmony_ci			return -EINVAL;
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci		if (ep_ctx_read_state(ep_ctx) == EP_STATE_DISABLED)
242262306a36Sopenharmony_ci			return -EINVAL;
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_ci		if (xudc_readl(xudc, EP_HALT) & BIT(ep))
242562306a36Sopenharmony_ci			status |= BIT(USB_ENDPOINT_HALT);
242662306a36Sopenharmony_ci		break;
242762306a36Sopenharmony_ci	default:
242862306a36Sopenharmony_ci		return -EINVAL;
242962306a36Sopenharmony_ci	}
243062306a36Sopenharmony_ci
243162306a36Sopenharmony_ci	xudc->status_buf = cpu_to_le16(status);
243262306a36Sopenharmony_ci	return tegra_xudc_ep0_queue_data(xudc, &xudc->status_buf,
243362306a36Sopenharmony_ci					 sizeof(xudc->status_buf),
243462306a36Sopenharmony_ci					 no_op_complete);
243562306a36Sopenharmony_ci}
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_cistatic void set_sel_complete(struct usb_ep *ep, struct usb_request *req)
243862306a36Sopenharmony_ci{
243962306a36Sopenharmony_ci	/* Nothing to do with SEL values */
244062306a36Sopenharmony_ci}
244162306a36Sopenharmony_ci
244262306a36Sopenharmony_cistatic int tegra_xudc_ep0_set_sel(struct tegra_xudc *xudc,
244362306a36Sopenharmony_ci				  struct usb_ctrlrequest *ctrl)
244462306a36Sopenharmony_ci{
244562306a36Sopenharmony_ci	if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE |
244662306a36Sopenharmony_ci				     USB_TYPE_STANDARD))
244762306a36Sopenharmony_ci		return -EINVAL;
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci	if (xudc->device_state == USB_STATE_DEFAULT)
245062306a36Sopenharmony_ci		return -EINVAL;
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci	if ((le16_to_cpu(ctrl->wIndex) != 0) ||
245362306a36Sopenharmony_ci	    (le16_to_cpu(ctrl->wValue) != 0) ||
245462306a36Sopenharmony_ci	    (le16_to_cpu(ctrl->wLength) != 6))
245562306a36Sopenharmony_ci		return -EINVAL;
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ci	return tegra_xudc_ep0_queue_data(xudc, &xudc->sel_timing,
245862306a36Sopenharmony_ci					 sizeof(xudc->sel_timing),
245962306a36Sopenharmony_ci					 set_sel_complete);
246062306a36Sopenharmony_ci}
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_cistatic void set_isoch_delay_complete(struct usb_ep *ep, struct usb_request *req)
246362306a36Sopenharmony_ci{
246462306a36Sopenharmony_ci	/* Nothing to do with isoch delay */
246562306a36Sopenharmony_ci}
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_cistatic int tegra_xudc_ep0_set_isoch_delay(struct tegra_xudc *xudc,
246862306a36Sopenharmony_ci					  struct usb_ctrlrequest *ctrl)
246962306a36Sopenharmony_ci{
247062306a36Sopenharmony_ci	u32 delay = le16_to_cpu(ctrl->wValue);
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci	if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE |
247362306a36Sopenharmony_ci				   USB_TYPE_STANDARD))
247462306a36Sopenharmony_ci		return -EINVAL;
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci	if ((delay > 65535) || (le16_to_cpu(ctrl->wIndex) != 0) ||
247762306a36Sopenharmony_ci	    (le16_to_cpu(ctrl->wLength) != 0))
247862306a36Sopenharmony_ci		return -EINVAL;
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci	xudc->isoch_delay = delay;
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	return tegra_xudc_ep0_queue_status(xudc, set_isoch_delay_complete);
248362306a36Sopenharmony_ci}
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_cistatic void set_address_complete(struct usb_ep *ep, struct usb_request *req)
248662306a36Sopenharmony_ci{
248762306a36Sopenharmony_ci	struct tegra_xudc *xudc = req->context;
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci	if ((xudc->device_state == USB_STATE_DEFAULT) &&
249062306a36Sopenharmony_ci	    (xudc->dev_addr != 0)) {
249162306a36Sopenharmony_ci		xudc->device_state = USB_STATE_ADDRESS;
249262306a36Sopenharmony_ci		usb_gadget_set_state(&xudc->gadget, xudc->device_state);
249362306a36Sopenharmony_ci	} else if ((xudc->device_state == USB_STATE_ADDRESS) &&
249462306a36Sopenharmony_ci		   (xudc->dev_addr == 0)) {
249562306a36Sopenharmony_ci		xudc->device_state = USB_STATE_DEFAULT;
249662306a36Sopenharmony_ci		usb_gadget_set_state(&xudc->gadget, xudc->device_state);
249762306a36Sopenharmony_ci	}
249862306a36Sopenharmony_ci}
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_cistatic int tegra_xudc_ep0_set_address(struct tegra_xudc *xudc,
250162306a36Sopenharmony_ci				      struct usb_ctrlrequest *ctrl)
250262306a36Sopenharmony_ci{
250362306a36Sopenharmony_ci	struct tegra_xudc_ep *ep0 = &xudc->ep[0];
250462306a36Sopenharmony_ci	u32 val, addr = le16_to_cpu(ctrl->wValue);
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE |
250762306a36Sopenharmony_ci				     USB_TYPE_STANDARD))
250862306a36Sopenharmony_ci		return -EINVAL;
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci	if ((addr > 127) || (le16_to_cpu(ctrl->wIndex) != 0) ||
251162306a36Sopenharmony_ci	    (le16_to_cpu(ctrl->wLength) != 0))
251262306a36Sopenharmony_ci		return -EINVAL;
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_ci	if (xudc->device_state == USB_STATE_CONFIGURED)
251562306a36Sopenharmony_ci		return -EINVAL;
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci	dev_dbg(xudc->dev, "set address: %u\n", addr);
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci	xudc->dev_addr = addr;
252062306a36Sopenharmony_ci	val = xudc_readl(xudc, CTRL);
252162306a36Sopenharmony_ci	val &= ~(CTRL_DEVADDR_MASK);
252262306a36Sopenharmony_ci	val |= CTRL_DEVADDR(addr);
252362306a36Sopenharmony_ci	xudc_writel(xudc, val, CTRL);
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_ci	ep_ctx_write_devaddr(ep0->context, addr);
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	return tegra_xudc_ep0_queue_status(xudc, set_address_complete);
252862306a36Sopenharmony_ci}
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_cistatic int tegra_xudc_ep0_standard_req(struct tegra_xudc *xudc,
253162306a36Sopenharmony_ci				      struct usb_ctrlrequest *ctrl)
253262306a36Sopenharmony_ci{
253362306a36Sopenharmony_ci	int ret;
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	switch (ctrl->bRequest) {
253662306a36Sopenharmony_ci	case USB_REQ_GET_STATUS:
253762306a36Sopenharmony_ci		dev_dbg(xudc->dev, "USB_REQ_GET_STATUS\n");
253862306a36Sopenharmony_ci		ret = tegra_xudc_ep0_get_status(xudc, ctrl);
253962306a36Sopenharmony_ci		break;
254062306a36Sopenharmony_ci	case USB_REQ_SET_ADDRESS:
254162306a36Sopenharmony_ci		dev_dbg(xudc->dev, "USB_REQ_SET_ADDRESS\n");
254262306a36Sopenharmony_ci		ret = tegra_xudc_ep0_set_address(xudc, ctrl);
254362306a36Sopenharmony_ci		break;
254462306a36Sopenharmony_ci	case USB_REQ_SET_SEL:
254562306a36Sopenharmony_ci		dev_dbg(xudc->dev, "USB_REQ_SET_SEL\n");
254662306a36Sopenharmony_ci		ret = tegra_xudc_ep0_set_sel(xudc, ctrl);
254762306a36Sopenharmony_ci		break;
254862306a36Sopenharmony_ci	case USB_REQ_SET_ISOCH_DELAY:
254962306a36Sopenharmony_ci		dev_dbg(xudc->dev, "USB_REQ_SET_ISOCH_DELAY\n");
255062306a36Sopenharmony_ci		ret = tegra_xudc_ep0_set_isoch_delay(xudc, ctrl);
255162306a36Sopenharmony_ci		break;
255262306a36Sopenharmony_ci	case USB_REQ_CLEAR_FEATURE:
255362306a36Sopenharmony_ci	case USB_REQ_SET_FEATURE:
255462306a36Sopenharmony_ci		dev_dbg(xudc->dev, "USB_REQ_CLEAR/SET_FEATURE\n");
255562306a36Sopenharmony_ci		ret = tegra_xudc_ep0_set_feature(xudc, ctrl);
255662306a36Sopenharmony_ci		break;
255762306a36Sopenharmony_ci	case USB_REQ_SET_CONFIGURATION:
255862306a36Sopenharmony_ci		dev_dbg(xudc->dev, "USB_REQ_SET_CONFIGURATION\n");
255962306a36Sopenharmony_ci		/*
256062306a36Sopenharmony_ci		 * In theory we need to clear RUN bit before status stage of
256162306a36Sopenharmony_ci		 * deconfig request sent, but this seems to be causing problems.
256262306a36Sopenharmony_ci		 * Clear RUN once all endpoints are disabled instead.
256362306a36Sopenharmony_ci		 */
256462306a36Sopenharmony_ci		fallthrough;
256562306a36Sopenharmony_ci	default:
256662306a36Sopenharmony_ci		ret = tegra_xudc_ep0_delegate_req(xudc, ctrl);
256762306a36Sopenharmony_ci		break;
256862306a36Sopenharmony_ci	}
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci	return ret;
257162306a36Sopenharmony_ci}
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_cistatic void tegra_xudc_handle_ep0_setup_packet(struct tegra_xudc *xudc,
257462306a36Sopenharmony_ci					       struct usb_ctrlrequest *ctrl,
257562306a36Sopenharmony_ci					       u16 seq_num)
257662306a36Sopenharmony_ci{
257762306a36Sopenharmony_ci	int ret;
257862306a36Sopenharmony_ci
257962306a36Sopenharmony_ci	xudc->setup_seq_num = seq_num;
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ci	/* Ensure EP0 is unhalted. */
258262306a36Sopenharmony_ci	ep_unhalt(xudc, 0);
258362306a36Sopenharmony_ci
258462306a36Sopenharmony_ci	/*
258562306a36Sopenharmony_ci	 * On Tegra210, setup packets with sequence numbers 0xfffe or 0xffff
258662306a36Sopenharmony_ci	 * are invalid.  Halt EP0 until we get a valid packet.
258762306a36Sopenharmony_ci	 */
258862306a36Sopenharmony_ci	if (xudc->soc->invalid_seq_num &&
258962306a36Sopenharmony_ci	    (seq_num == 0xfffe || seq_num == 0xffff)) {
259062306a36Sopenharmony_ci		dev_warn(xudc->dev, "invalid sequence number detected\n");
259162306a36Sopenharmony_ci		ep_halt(xudc, 0);
259262306a36Sopenharmony_ci		return;
259362306a36Sopenharmony_ci	}
259462306a36Sopenharmony_ci
259562306a36Sopenharmony_ci	if (ctrl->wLength)
259662306a36Sopenharmony_ci		xudc->setup_state = (ctrl->bRequestType & USB_DIR_IN) ?
259762306a36Sopenharmony_ci			DATA_STAGE_XFER :  DATA_STAGE_RECV;
259862306a36Sopenharmony_ci	else
259962306a36Sopenharmony_ci		xudc->setup_state = STATUS_STAGE_XFER;
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
260262306a36Sopenharmony_ci		ret = tegra_xudc_ep0_standard_req(xudc, ctrl);
260362306a36Sopenharmony_ci	else
260462306a36Sopenharmony_ci		ret = tegra_xudc_ep0_delegate_req(xudc, ctrl);
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_ci	if (ret < 0) {
260762306a36Sopenharmony_ci		dev_warn(xudc->dev, "setup request failed: %d\n", ret);
260862306a36Sopenharmony_ci		xudc->setup_state = WAIT_FOR_SETUP;
260962306a36Sopenharmony_ci		ep_halt(xudc, 0);
261062306a36Sopenharmony_ci	}
261162306a36Sopenharmony_ci}
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_cistatic void tegra_xudc_handle_ep0_event(struct tegra_xudc *xudc,
261462306a36Sopenharmony_ci					struct tegra_xudc_trb *event)
261562306a36Sopenharmony_ci{
261662306a36Sopenharmony_ci	struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *)event;
261762306a36Sopenharmony_ci	u16 seq_num = trb_read_seq_num(event);
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci	if (xudc->setup_state != WAIT_FOR_SETUP) {
262062306a36Sopenharmony_ci		/*
262162306a36Sopenharmony_ci		 * The controller is in the process of handling another
262262306a36Sopenharmony_ci		 * setup request.  Queue subsequent requests and handle
262362306a36Sopenharmony_ci		 * the last one once the controller reports a sequence
262462306a36Sopenharmony_ci		 * number error.
262562306a36Sopenharmony_ci		 */
262662306a36Sopenharmony_ci		memcpy(&xudc->setup_packet.ctrl_req, ctrl, sizeof(*ctrl));
262762306a36Sopenharmony_ci		xudc->setup_packet.seq_num = seq_num;
262862306a36Sopenharmony_ci		xudc->queued_setup_packet = true;
262962306a36Sopenharmony_ci	} else {
263062306a36Sopenharmony_ci		tegra_xudc_handle_ep0_setup_packet(xudc, ctrl, seq_num);
263162306a36Sopenharmony_ci	}
263262306a36Sopenharmony_ci}
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_cistatic struct tegra_xudc_request *
263562306a36Sopenharmony_citrb_to_request(struct tegra_xudc_ep *ep, struct tegra_xudc_trb *trb)
263662306a36Sopenharmony_ci{
263762306a36Sopenharmony_ci	struct tegra_xudc_request *req;
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci	list_for_each_entry(req, &ep->queue, list) {
264062306a36Sopenharmony_ci		if (!req->trbs_queued)
264162306a36Sopenharmony_ci			break;
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_ci		if (trb_in_request(ep, req, trb))
264462306a36Sopenharmony_ci			return req;
264562306a36Sopenharmony_ci	}
264662306a36Sopenharmony_ci
264762306a36Sopenharmony_ci	return NULL;
264862306a36Sopenharmony_ci}
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_cistatic void tegra_xudc_handle_transfer_completion(struct tegra_xudc *xudc,
265162306a36Sopenharmony_ci						  struct tegra_xudc_ep *ep,
265262306a36Sopenharmony_ci						  struct tegra_xudc_trb *event)
265362306a36Sopenharmony_ci{
265462306a36Sopenharmony_ci	struct tegra_xudc_request *req;
265562306a36Sopenharmony_ci	struct tegra_xudc_trb *trb;
265662306a36Sopenharmony_ci	bool short_packet;
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ci	short_packet = (trb_read_cmpl_code(event) ==
265962306a36Sopenharmony_ci			TRB_CMPL_CODE_SHORT_PACKET);
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_ci	trb = trb_phys_to_virt(ep, trb_read_data_ptr(event));
266262306a36Sopenharmony_ci	req = trb_to_request(ep, trb);
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_ci	/*
266562306a36Sopenharmony_ci	 * TDs are complete on short packet or when the completed TRB is the
266662306a36Sopenharmony_ci	 * last TRB in the TD (the CHAIN bit is unset).
266762306a36Sopenharmony_ci	 */
266862306a36Sopenharmony_ci	if (req && (short_packet || (!trb_read_chain(trb) &&
266962306a36Sopenharmony_ci		(req->trbs_needed == req->trbs_queued)))) {
267062306a36Sopenharmony_ci		struct tegra_xudc_trb *last = req->last_trb;
267162306a36Sopenharmony_ci		unsigned int residual;
267262306a36Sopenharmony_ci
267362306a36Sopenharmony_ci		residual = trb_read_transfer_len(event);
267462306a36Sopenharmony_ci		req->usb_req.actual = req->usb_req.length - residual;
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci		dev_dbg(xudc->dev, "bytes transferred %u / %u\n",
267762306a36Sopenharmony_ci			req->usb_req.actual, req->usb_req.length);
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci		tegra_xudc_req_done(ep, req, 0);
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci		if (ep->desc && usb_endpoint_xfer_control(ep->desc))
268262306a36Sopenharmony_ci			tegra_xudc_ep0_req_done(xudc);
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci		/*
268562306a36Sopenharmony_ci		 * Advance the dequeue pointer past the end of the current TD
268662306a36Sopenharmony_ci		 * on short packet completion.
268762306a36Sopenharmony_ci		 */
268862306a36Sopenharmony_ci		if (short_packet) {
268962306a36Sopenharmony_ci			ep->deq_ptr = (last - ep->transfer_ring) + 1;
269062306a36Sopenharmony_ci			if (ep->deq_ptr == XUDC_TRANSFER_RING_SIZE - 1)
269162306a36Sopenharmony_ci				ep->deq_ptr = 0;
269262306a36Sopenharmony_ci		}
269362306a36Sopenharmony_ci	} else if (!req) {
269462306a36Sopenharmony_ci		dev_warn(xudc->dev, "transfer event on dequeued request\n");
269562306a36Sopenharmony_ci	}
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci	if (ep->desc)
269862306a36Sopenharmony_ci		tegra_xudc_ep_kick_queue(ep);
269962306a36Sopenharmony_ci}
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_cistatic void tegra_xudc_handle_transfer_event(struct tegra_xudc *xudc,
270262306a36Sopenharmony_ci					     struct tegra_xudc_trb *event)
270362306a36Sopenharmony_ci{
270462306a36Sopenharmony_ci	unsigned int ep_index = trb_read_endpoint_id(event);
270562306a36Sopenharmony_ci	struct tegra_xudc_ep *ep = &xudc->ep[ep_index];
270662306a36Sopenharmony_ci	struct tegra_xudc_trb *trb;
270762306a36Sopenharmony_ci	u16 comp_code;
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci	if (ep_ctx_read_state(ep->context) == EP_STATE_DISABLED) {
271062306a36Sopenharmony_ci		dev_warn(xudc->dev, "transfer event on disabled EP %u\n",
271162306a36Sopenharmony_ci			 ep_index);
271262306a36Sopenharmony_ci		return;
271362306a36Sopenharmony_ci	}
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_ci	/* Update transfer ring dequeue pointer. */
271662306a36Sopenharmony_ci	trb = trb_phys_to_virt(ep, trb_read_data_ptr(event));
271762306a36Sopenharmony_ci	comp_code = trb_read_cmpl_code(event);
271862306a36Sopenharmony_ci	if (comp_code != TRB_CMPL_CODE_BABBLE_DETECTED_ERR) {
271962306a36Sopenharmony_ci		ep->deq_ptr = (trb - ep->transfer_ring) + 1;
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci		if (ep->deq_ptr == XUDC_TRANSFER_RING_SIZE - 1)
272262306a36Sopenharmony_ci			ep->deq_ptr = 0;
272362306a36Sopenharmony_ci		ep->ring_full = false;
272462306a36Sopenharmony_ci	}
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci	switch (comp_code) {
272762306a36Sopenharmony_ci	case TRB_CMPL_CODE_SUCCESS:
272862306a36Sopenharmony_ci	case TRB_CMPL_CODE_SHORT_PACKET:
272962306a36Sopenharmony_ci		tegra_xudc_handle_transfer_completion(xudc, ep, event);
273062306a36Sopenharmony_ci		break;
273162306a36Sopenharmony_ci	case TRB_CMPL_CODE_HOST_REJECTED:
273262306a36Sopenharmony_ci		dev_info(xudc->dev, "stream rejected on EP %u\n", ep_index);
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_ci		ep->stream_rejected = true;
273562306a36Sopenharmony_ci		break;
273662306a36Sopenharmony_ci	case TRB_CMPL_CODE_PRIME_PIPE_RECEIVED:
273762306a36Sopenharmony_ci		dev_info(xudc->dev, "prime pipe received on EP %u\n", ep_index);
273862306a36Sopenharmony_ci
273962306a36Sopenharmony_ci		if (ep->stream_rejected) {
274062306a36Sopenharmony_ci			ep->stream_rejected = false;
274162306a36Sopenharmony_ci			/*
274262306a36Sopenharmony_ci			 * An EP is stopped when a stream is rejected.  Wait
274362306a36Sopenharmony_ci			 * for the EP to report that it is stopped and then
274462306a36Sopenharmony_ci			 * un-stop it.
274562306a36Sopenharmony_ci			 */
274662306a36Sopenharmony_ci			ep_wait_for_stopped(xudc, ep_index);
274762306a36Sopenharmony_ci		}
274862306a36Sopenharmony_ci		tegra_xudc_ep_ring_doorbell(ep);
274962306a36Sopenharmony_ci		break;
275062306a36Sopenharmony_ci	case TRB_CMPL_CODE_BABBLE_DETECTED_ERR:
275162306a36Sopenharmony_ci		/*
275262306a36Sopenharmony_ci		 * Wait for the EP to be stopped so the controller stops
275362306a36Sopenharmony_ci		 * processing doorbells.
275462306a36Sopenharmony_ci		 */
275562306a36Sopenharmony_ci		ep_wait_for_stopped(xudc, ep_index);
275662306a36Sopenharmony_ci		ep->enq_ptr = ep->deq_ptr;
275762306a36Sopenharmony_ci		tegra_xudc_ep_nuke(ep, -EIO);
275862306a36Sopenharmony_ci		fallthrough;
275962306a36Sopenharmony_ci	case TRB_CMPL_CODE_STREAM_NUMP_ERROR:
276062306a36Sopenharmony_ci	case TRB_CMPL_CODE_CTRL_DIR_ERR:
276162306a36Sopenharmony_ci	case TRB_CMPL_CODE_INVALID_STREAM_TYPE_ERR:
276262306a36Sopenharmony_ci	case TRB_CMPL_CODE_RING_UNDERRUN:
276362306a36Sopenharmony_ci	case TRB_CMPL_CODE_RING_OVERRUN:
276462306a36Sopenharmony_ci	case TRB_CMPL_CODE_ISOCH_BUFFER_OVERRUN:
276562306a36Sopenharmony_ci	case TRB_CMPL_CODE_USB_TRANS_ERR:
276662306a36Sopenharmony_ci	case TRB_CMPL_CODE_TRB_ERR:
276762306a36Sopenharmony_ci		dev_err(xudc->dev, "completion error %#x on EP %u\n",
276862306a36Sopenharmony_ci			comp_code, ep_index);
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci		ep_halt(xudc, ep_index);
277162306a36Sopenharmony_ci		break;
277262306a36Sopenharmony_ci	case TRB_CMPL_CODE_CTRL_SEQNUM_ERR:
277362306a36Sopenharmony_ci		dev_info(xudc->dev, "sequence number error\n");
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci		/*
277662306a36Sopenharmony_ci		 * Kill any queued control request and skip to the last
277762306a36Sopenharmony_ci		 * setup packet we received.
277862306a36Sopenharmony_ci		 */
277962306a36Sopenharmony_ci		tegra_xudc_ep_nuke(ep, -EINVAL);
278062306a36Sopenharmony_ci		xudc->setup_state = WAIT_FOR_SETUP;
278162306a36Sopenharmony_ci		if (!xudc->queued_setup_packet)
278262306a36Sopenharmony_ci			break;
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_ci		tegra_xudc_handle_ep0_setup_packet(xudc,
278562306a36Sopenharmony_ci						   &xudc->setup_packet.ctrl_req,
278662306a36Sopenharmony_ci						   xudc->setup_packet.seq_num);
278762306a36Sopenharmony_ci		xudc->queued_setup_packet = false;
278862306a36Sopenharmony_ci		break;
278962306a36Sopenharmony_ci	case TRB_CMPL_CODE_STOPPED:
279062306a36Sopenharmony_ci		dev_dbg(xudc->dev, "stop completion code on EP %u\n",
279162306a36Sopenharmony_ci			ep_index);
279262306a36Sopenharmony_ci
279362306a36Sopenharmony_ci		/* Disconnected. */
279462306a36Sopenharmony_ci		tegra_xudc_ep_nuke(ep, -ECONNREFUSED);
279562306a36Sopenharmony_ci		break;
279662306a36Sopenharmony_ci	default:
279762306a36Sopenharmony_ci		dev_dbg(xudc->dev, "completion event %#x on EP %u\n",
279862306a36Sopenharmony_ci			comp_code, ep_index);
279962306a36Sopenharmony_ci		break;
280062306a36Sopenharmony_ci	}
280162306a36Sopenharmony_ci}
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_cistatic void tegra_xudc_reset(struct tegra_xudc *xudc)
280462306a36Sopenharmony_ci{
280562306a36Sopenharmony_ci	struct tegra_xudc_ep *ep0 = &xudc->ep[0];
280662306a36Sopenharmony_ci	dma_addr_t deq_ptr;
280762306a36Sopenharmony_ci	unsigned int i;
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_ci	xudc->setup_state = WAIT_FOR_SETUP;
281062306a36Sopenharmony_ci	xudc->device_state = USB_STATE_DEFAULT;
281162306a36Sopenharmony_ci	usb_gadget_set_state(&xudc->gadget, xudc->device_state);
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_ci	ep_unpause_all(xudc);
281462306a36Sopenharmony_ci
281562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(xudc->ep); i++)
281662306a36Sopenharmony_ci		tegra_xudc_ep_nuke(&xudc->ep[i], -ESHUTDOWN);
281762306a36Sopenharmony_ci
281862306a36Sopenharmony_ci	/*
281962306a36Sopenharmony_ci	 * Reset sequence number and dequeue pointer to flush the transfer
282062306a36Sopenharmony_ci	 * ring.
282162306a36Sopenharmony_ci	 */
282262306a36Sopenharmony_ci	ep0->deq_ptr = ep0->enq_ptr;
282362306a36Sopenharmony_ci	ep0->ring_full = false;
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci	xudc->setup_seq_num = 0;
282662306a36Sopenharmony_ci	xudc->queued_setup_packet = false;
282762306a36Sopenharmony_ci
282862306a36Sopenharmony_ci	ep_ctx_write_rsvd(ep0->context, 0);
282962306a36Sopenharmony_ci	ep_ctx_write_partial_td(ep0->context, 0);
283062306a36Sopenharmony_ci	ep_ctx_write_splitxstate(ep0->context, 0);
283162306a36Sopenharmony_ci	ep_ctx_write_seq_num(ep0->context, 0);
283262306a36Sopenharmony_ci
283362306a36Sopenharmony_ci	deq_ptr = trb_virt_to_phys(ep0, &ep0->transfer_ring[ep0->deq_ptr]);
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci	if (!dma_mapping_error(xudc->dev, deq_ptr)) {
283662306a36Sopenharmony_ci		ep_ctx_write_deq_ptr(ep0->context, deq_ptr);
283762306a36Sopenharmony_ci		ep_ctx_write_dcs(ep0->context, ep0->pcs);
283862306a36Sopenharmony_ci	}
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci	ep_unhalt_all(xudc);
284162306a36Sopenharmony_ci	ep_reload(xudc, 0);
284262306a36Sopenharmony_ci	ep_unpause(xudc, 0);
284362306a36Sopenharmony_ci}
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_cistatic void tegra_xudc_port_connect(struct tegra_xudc *xudc)
284662306a36Sopenharmony_ci{
284762306a36Sopenharmony_ci	struct tegra_xudc_ep *ep0 = &xudc->ep[0];
284862306a36Sopenharmony_ci	u16 maxpacket;
284962306a36Sopenharmony_ci	u32 val;
285062306a36Sopenharmony_ci
285162306a36Sopenharmony_ci	val = (xudc_readl(xudc, PORTSC) & PORTSC_PS_MASK) >> PORTSC_PS_SHIFT;
285262306a36Sopenharmony_ci	switch (val) {
285362306a36Sopenharmony_ci	case PORTSC_PS_LS:
285462306a36Sopenharmony_ci		xudc->gadget.speed = USB_SPEED_LOW;
285562306a36Sopenharmony_ci		break;
285662306a36Sopenharmony_ci	case PORTSC_PS_FS:
285762306a36Sopenharmony_ci		xudc->gadget.speed = USB_SPEED_FULL;
285862306a36Sopenharmony_ci		break;
285962306a36Sopenharmony_ci	case PORTSC_PS_HS:
286062306a36Sopenharmony_ci		xudc->gadget.speed = USB_SPEED_HIGH;
286162306a36Sopenharmony_ci		break;
286262306a36Sopenharmony_ci	case PORTSC_PS_SS:
286362306a36Sopenharmony_ci		xudc->gadget.speed = USB_SPEED_SUPER;
286462306a36Sopenharmony_ci		break;
286562306a36Sopenharmony_ci	default:
286662306a36Sopenharmony_ci		xudc->gadget.speed = USB_SPEED_UNKNOWN;
286762306a36Sopenharmony_ci		break;
286862306a36Sopenharmony_ci	}
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_ci	xudc->device_state = USB_STATE_DEFAULT;
287162306a36Sopenharmony_ci	usb_gadget_set_state(&xudc->gadget, xudc->device_state);
287262306a36Sopenharmony_ci
287362306a36Sopenharmony_ci	xudc->setup_state = WAIT_FOR_SETUP;
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_ci	if (xudc->gadget.speed == USB_SPEED_SUPER)
287662306a36Sopenharmony_ci		maxpacket = 512;
287762306a36Sopenharmony_ci	else
287862306a36Sopenharmony_ci		maxpacket = 64;
287962306a36Sopenharmony_ci
288062306a36Sopenharmony_ci	ep_ctx_write_max_packet_size(ep0->context, maxpacket);
288162306a36Sopenharmony_ci	tegra_xudc_ep0_desc.wMaxPacketSize = cpu_to_le16(maxpacket);
288262306a36Sopenharmony_ci	usb_ep_set_maxpacket_limit(&ep0->usb_ep, maxpacket);
288362306a36Sopenharmony_ci
288462306a36Sopenharmony_ci	if (!xudc->soc->u1_enable) {
288562306a36Sopenharmony_ci		val = xudc_readl(xudc, PORTPM);
288662306a36Sopenharmony_ci		val &= ~(PORTPM_U1TIMEOUT_MASK);
288762306a36Sopenharmony_ci		xudc_writel(xudc, val, PORTPM);
288862306a36Sopenharmony_ci	}
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_ci	if (!xudc->soc->u2_enable) {
289162306a36Sopenharmony_ci		val = xudc_readl(xudc, PORTPM);
289262306a36Sopenharmony_ci		val &= ~(PORTPM_U2TIMEOUT_MASK);
289362306a36Sopenharmony_ci		xudc_writel(xudc, val, PORTPM);
289462306a36Sopenharmony_ci	}
289562306a36Sopenharmony_ci
289662306a36Sopenharmony_ci	if (xudc->gadget.speed <= USB_SPEED_HIGH) {
289762306a36Sopenharmony_ci		val = xudc_readl(xudc, PORTPM);
289862306a36Sopenharmony_ci		val &= ~(PORTPM_L1S_MASK);
289962306a36Sopenharmony_ci		if (xudc->soc->lpm_enable)
290062306a36Sopenharmony_ci			val |= PORTPM_L1S(PORTPM_L1S_ACCEPT);
290162306a36Sopenharmony_ci		else
290262306a36Sopenharmony_ci			val |= PORTPM_L1S(PORTPM_L1S_NYET);
290362306a36Sopenharmony_ci		xudc_writel(xudc, val, PORTPM);
290462306a36Sopenharmony_ci	}
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci	val = xudc_readl(xudc, ST);
290762306a36Sopenharmony_ci	if (val & ST_RC)
290862306a36Sopenharmony_ci		xudc_writel(xudc, ST_RC, ST);
290962306a36Sopenharmony_ci}
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_cistatic void tegra_xudc_port_disconnect(struct tegra_xudc *xudc)
291262306a36Sopenharmony_ci{
291362306a36Sopenharmony_ci	tegra_xudc_reset(xudc);
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_ci	if (xudc->driver && xudc->driver->disconnect) {
291662306a36Sopenharmony_ci		spin_unlock(&xudc->lock);
291762306a36Sopenharmony_ci		xudc->driver->disconnect(&xudc->gadget);
291862306a36Sopenharmony_ci		spin_lock(&xudc->lock);
291962306a36Sopenharmony_ci	}
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci	xudc->device_state = USB_STATE_NOTATTACHED;
292262306a36Sopenharmony_ci	usb_gadget_set_state(&xudc->gadget, xudc->device_state);
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci	complete(&xudc->disconnect_complete);
292562306a36Sopenharmony_ci}
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_cistatic void tegra_xudc_port_reset(struct tegra_xudc *xudc)
292862306a36Sopenharmony_ci{
292962306a36Sopenharmony_ci	tegra_xudc_reset(xudc);
293062306a36Sopenharmony_ci
293162306a36Sopenharmony_ci	if (xudc->driver) {
293262306a36Sopenharmony_ci		spin_unlock(&xudc->lock);
293362306a36Sopenharmony_ci		usb_gadget_udc_reset(&xudc->gadget, xudc->driver);
293462306a36Sopenharmony_ci		spin_lock(&xudc->lock);
293562306a36Sopenharmony_ci	}
293662306a36Sopenharmony_ci
293762306a36Sopenharmony_ci	tegra_xudc_port_connect(xudc);
293862306a36Sopenharmony_ci}
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_cistatic void tegra_xudc_port_suspend(struct tegra_xudc *xudc)
294162306a36Sopenharmony_ci{
294262306a36Sopenharmony_ci	dev_dbg(xudc->dev, "port suspend\n");
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci	xudc->resume_state = xudc->device_state;
294562306a36Sopenharmony_ci	xudc->device_state = USB_STATE_SUSPENDED;
294662306a36Sopenharmony_ci	usb_gadget_set_state(&xudc->gadget, xudc->device_state);
294762306a36Sopenharmony_ci
294862306a36Sopenharmony_ci	if (xudc->driver->suspend) {
294962306a36Sopenharmony_ci		spin_unlock(&xudc->lock);
295062306a36Sopenharmony_ci		xudc->driver->suspend(&xudc->gadget);
295162306a36Sopenharmony_ci		spin_lock(&xudc->lock);
295262306a36Sopenharmony_ci	}
295362306a36Sopenharmony_ci}
295462306a36Sopenharmony_ci
295562306a36Sopenharmony_cistatic void tegra_xudc_port_resume(struct tegra_xudc *xudc)
295662306a36Sopenharmony_ci{
295762306a36Sopenharmony_ci	dev_dbg(xudc->dev, "port resume\n");
295862306a36Sopenharmony_ci
295962306a36Sopenharmony_ci	tegra_xudc_resume_device_state(xudc);
296062306a36Sopenharmony_ci
296162306a36Sopenharmony_ci	if (xudc->driver->resume) {
296262306a36Sopenharmony_ci		spin_unlock(&xudc->lock);
296362306a36Sopenharmony_ci		xudc->driver->resume(&xudc->gadget);
296462306a36Sopenharmony_ci		spin_lock(&xudc->lock);
296562306a36Sopenharmony_ci	}
296662306a36Sopenharmony_ci}
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_cistatic inline void clear_port_change(struct tegra_xudc *xudc, u32 flag)
296962306a36Sopenharmony_ci{
297062306a36Sopenharmony_ci	u32 val;
297162306a36Sopenharmony_ci
297262306a36Sopenharmony_ci	val = xudc_readl(xudc, PORTSC);
297362306a36Sopenharmony_ci	val &= ~PORTSC_CHANGE_MASK;
297462306a36Sopenharmony_ci	val |= flag;
297562306a36Sopenharmony_ci	xudc_writel(xudc, val, PORTSC);
297662306a36Sopenharmony_ci}
297762306a36Sopenharmony_ci
297862306a36Sopenharmony_cistatic void __tegra_xudc_handle_port_status(struct tegra_xudc *xudc)
297962306a36Sopenharmony_ci{
298062306a36Sopenharmony_ci	u32 portsc, porthalt;
298162306a36Sopenharmony_ci
298262306a36Sopenharmony_ci	porthalt = xudc_readl(xudc, PORTHALT);
298362306a36Sopenharmony_ci	if ((porthalt & PORTHALT_STCHG_REQ) &&
298462306a36Sopenharmony_ci	    (porthalt & PORTHALT_HALT_LTSSM)) {
298562306a36Sopenharmony_ci		dev_dbg(xudc->dev, "STCHG_REQ, PORTHALT = %#x\n", porthalt);
298662306a36Sopenharmony_ci		porthalt &= ~PORTHALT_HALT_LTSSM;
298762306a36Sopenharmony_ci		xudc_writel(xudc, porthalt, PORTHALT);
298862306a36Sopenharmony_ci	}
298962306a36Sopenharmony_ci
299062306a36Sopenharmony_ci	portsc = xudc_readl(xudc, PORTSC);
299162306a36Sopenharmony_ci	if ((portsc & PORTSC_PRC) && (portsc & PORTSC_PR)) {
299262306a36Sopenharmony_ci		dev_dbg(xudc->dev, "PRC, PR, PORTSC = %#x\n", portsc);
299362306a36Sopenharmony_ci		clear_port_change(xudc, PORTSC_PRC | PORTSC_PED);
299462306a36Sopenharmony_ci#define TOGGLE_VBUS_WAIT_MS 100
299562306a36Sopenharmony_ci		if (xudc->soc->port_reset_quirk) {
299662306a36Sopenharmony_ci			schedule_delayed_work(&xudc->port_reset_war_work,
299762306a36Sopenharmony_ci				msecs_to_jiffies(TOGGLE_VBUS_WAIT_MS));
299862306a36Sopenharmony_ci			xudc->wait_for_sec_prc = 1;
299962306a36Sopenharmony_ci		}
300062306a36Sopenharmony_ci	}
300162306a36Sopenharmony_ci
300262306a36Sopenharmony_ci	if ((portsc & PORTSC_PRC) && !(portsc & PORTSC_PR)) {
300362306a36Sopenharmony_ci		dev_dbg(xudc->dev, "PRC, Not PR, PORTSC = %#x\n", portsc);
300462306a36Sopenharmony_ci		clear_port_change(xudc, PORTSC_PRC | PORTSC_PED);
300562306a36Sopenharmony_ci		tegra_xudc_port_reset(xudc);
300662306a36Sopenharmony_ci		cancel_delayed_work(&xudc->port_reset_war_work);
300762306a36Sopenharmony_ci		xudc->wait_for_sec_prc = 0;
300862306a36Sopenharmony_ci	}
300962306a36Sopenharmony_ci
301062306a36Sopenharmony_ci	portsc = xudc_readl(xudc, PORTSC);
301162306a36Sopenharmony_ci	if (portsc & PORTSC_WRC) {
301262306a36Sopenharmony_ci		dev_dbg(xudc->dev, "WRC, PORTSC = %#x\n", portsc);
301362306a36Sopenharmony_ci		clear_port_change(xudc, PORTSC_WRC | PORTSC_PED);
301462306a36Sopenharmony_ci		if (!(xudc_readl(xudc, PORTSC) & PORTSC_WPR))
301562306a36Sopenharmony_ci			tegra_xudc_port_reset(xudc);
301662306a36Sopenharmony_ci	}
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_ci	portsc = xudc_readl(xudc, PORTSC);
301962306a36Sopenharmony_ci	if (portsc & PORTSC_CSC) {
302062306a36Sopenharmony_ci		dev_dbg(xudc->dev, "CSC, PORTSC = %#x\n", portsc);
302162306a36Sopenharmony_ci		clear_port_change(xudc, PORTSC_CSC);
302262306a36Sopenharmony_ci
302362306a36Sopenharmony_ci		if (portsc & PORTSC_CCS)
302462306a36Sopenharmony_ci			tegra_xudc_port_connect(xudc);
302562306a36Sopenharmony_ci		else
302662306a36Sopenharmony_ci			tegra_xudc_port_disconnect(xudc);
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci		if (xudc->wait_csc) {
302962306a36Sopenharmony_ci			cancel_delayed_work(&xudc->plc_reset_work);
303062306a36Sopenharmony_ci			xudc->wait_csc = false;
303162306a36Sopenharmony_ci		}
303262306a36Sopenharmony_ci	}
303362306a36Sopenharmony_ci
303462306a36Sopenharmony_ci	portsc = xudc_readl(xudc, PORTSC);
303562306a36Sopenharmony_ci	if (portsc & PORTSC_PLC) {
303662306a36Sopenharmony_ci		u32 pls = (portsc & PORTSC_PLS_MASK) >> PORTSC_PLS_SHIFT;
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_ci		dev_dbg(xudc->dev, "PLC, PORTSC = %#x\n", portsc);
303962306a36Sopenharmony_ci		clear_port_change(xudc, PORTSC_PLC);
304062306a36Sopenharmony_ci		switch (pls) {
304162306a36Sopenharmony_ci		case PORTSC_PLS_U3:
304262306a36Sopenharmony_ci			tegra_xudc_port_suspend(xudc);
304362306a36Sopenharmony_ci			break;
304462306a36Sopenharmony_ci		case PORTSC_PLS_U0:
304562306a36Sopenharmony_ci			if (xudc->gadget.speed < USB_SPEED_SUPER)
304662306a36Sopenharmony_ci				tegra_xudc_port_resume(xudc);
304762306a36Sopenharmony_ci			break;
304862306a36Sopenharmony_ci		case PORTSC_PLS_RESUME:
304962306a36Sopenharmony_ci			if (xudc->gadget.speed == USB_SPEED_SUPER)
305062306a36Sopenharmony_ci				tegra_xudc_port_resume(xudc);
305162306a36Sopenharmony_ci			break;
305262306a36Sopenharmony_ci		case PORTSC_PLS_INACTIVE:
305362306a36Sopenharmony_ci			schedule_delayed_work(&xudc->plc_reset_work,
305462306a36Sopenharmony_ci					msecs_to_jiffies(TOGGLE_VBUS_WAIT_MS));
305562306a36Sopenharmony_ci			xudc->wait_csc = true;
305662306a36Sopenharmony_ci			break;
305762306a36Sopenharmony_ci		default:
305862306a36Sopenharmony_ci			break;
305962306a36Sopenharmony_ci		}
306062306a36Sopenharmony_ci	}
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_ci	if (portsc & PORTSC_CEC) {
306362306a36Sopenharmony_ci		dev_warn(xudc->dev, "CEC, PORTSC = %#x\n", portsc);
306462306a36Sopenharmony_ci		clear_port_change(xudc, PORTSC_CEC);
306562306a36Sopenharmony_ci	}
306662306a36Sopenharmony_ci
306762306a36Sopenharmony_ci	dev_dbg(xudc->dev, "PORTSC = %#x\n", xudc_readl(xudc, PORTSC));
306862306a36Sopenharmony_ci}
306962306a36Sopenharmony_ci
307062306a36Sopenharmony_cistatic void tegra_xudc_handle_port_status(struct tegra_xudc *xudc)
307162306a36Sopenharmony_ci{
307262306a36Sopenharmony_ci	while ((xudc_readl(xudc, PORTSC) & PORTSC_CHANGE_MASK) ||
307362306a36Sopenharmony_ci	       (xudc_readl(xudc, PORTHALT) & PORTHALT_STCHG_REQ))
307462306a36Sopenharmony_ci		__tegra_xudc_handle_port_status(xudc);
307562306a36Sopenharmony_ci}
307662306a36Sopenharmony_ci
307762306a36Sopenharmony_cistatic void tegra_xudc_handle_event(struct tegra_xudc *xudc,
307862306a36Sopenharmony_ci				    struct tegra_xudc_trb *event)
307962306a36Sopenharmony_ci{
308062306a36Sopenharmony_ci	u32 type = trb_read_type(event);
308162306a36Sopenharmony_ci
308262306a36Sopenharmony_ci	dump_trb(xudc, "EVENT", event);
308362306a36Sopenharmony_ci
308462306a36Sopenharmony_ci	switch (type) {
308562306a36Sopenharmony_ci	case TRB_TYPE_PORT_STATUS_CHANGE_EVENT:
308662306a36Sopenharmony_ci		tegra_xudc_handle_port_status(xudc);
308762306a36Sopenharmony_ci		break;
308862306a36Sopenharmony_ci	case TRB_TYPE_TRANSFER_EVENT:
308962306a36Sopenharmony_ci		tegra_xudc_handle_transfer_event(xudc, event);
309062306a36Sopenharmony_ci		break;
309162306a36Sopenharmony_ci	case TRB_TYPE_SETUP_PACKET_EVENT:
309262306a36Sopenharmony_ci		tegra_xudc_handle_ep0_event(xudc, event);
309362306a36Sopenharmony_ci		break;
309462306a36Sopenharmony_ci	default:
309562306a36Sopenharmony_ci		dev_info(xudc->dev, "Unrecognized TRB type = %#x\n", type);
309662306a36Sopenharmony_ci		break;
309762306a36Sopenharmony_ci	}
309862306a36Sopenharmony_ci}
309962306a36Sopenharmony_ci
310062306a36Sopenharmony_cistatic void tegra_xudc_process_event_ring(struct tegra_xudc *xudc)
310162306a36Sopenharmony_ci{
310262306a36Sopenharmony_ci	struct tegra_xudc_trb *event;
310362306a36Sopenharmony_ci	dma_addr_t erdp;
310462306a36Sopenharmony_ci
310562306a36Sopenharmony_ci	while (true) {
310662306a36Sopenharmony_ci		event = xudc->event_ring[xudc->event_ring_index] +
310762306a36Sopenharmony_ci			xudc->event_ring_deq_ptr;
310862306a36Sopenharmony_ci
310962306a36Sopenharmony_ci		if (trb_read_cycle(event) != xudc->ccs)
311062306a36Sopenharmony_ci			break;
311162306a36Sopenharmony_ci
311262306a36Sopenharmony_ci		tegra_xudc_handle_event(xudc, event);
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_ci		xudc->event_ring_deq_ptr++;
311562306a36Sopenharmony_ci		if (xudc->event_ring_deq_ptr == XUDC_EVENT_RING_SIZE) {
311662306a36Sopenharmony_ci			xudc->event_ring_deq_ptr = 0;
311762306a36Sopenharmony_ci			xudc->event_ring_index++;
311862306a36Sopenharmony_ci		}
311962306a36Sopenharmony_ci
312062306a36Sopenharmony_ci		if (xudc->event_ring_index == XUDC_NR_EVENT_RINGS) {
312162306a36Sopenharmony_ci			xudc->event_ring_index = 0;
312262306a36Sopenharmony_ci			xudc->ccs = !xudc->ccs;
312362306a36Sopenharmony_ci		}
312462306a36Sopenharmony_ci	}
312562306a36Sopenharmony_ci
312662306a36Sopenharmony_ci	erdp = xudc->event_ring_phys[xudc->event_ring_index] +
312762306a36Sopenharmony_ci		xudc->event_ring_deq_ptr * sizeof(*event);
312862306a36Sopenharmony_ci
312962306a36Sopenharmony_ci	xudc_writel(xudc, upper_32_bits(erdp), ERDPHI);
313062306a36Sopenharmony_ci	xudc_writel(xudc, lower_32_bits(erdp) | ERDPLO_EHB, ERDPLO);
313162306a36Sopenharmony_ci}
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_cistatic irqreturn_t tegra_xudc_irq(int irq, void *data)
313462306a36Sopenharmony_ci{
313562306a36Sopenharmony_ci	struct tegra_xudc *xudc = data;
313662306a36Sopenharmony_ci	unsigned long flags;
313762306a36Sopenharmony_ci	u32 val;
313862306a36Sopenharmony_ci
313962306a36Sopenharmony_ci	val = xudc_readl(xudc, ST);
314062306a36Sopenharmony_ci	if (!(val & ST_IP))
314162306a36Sopenharmony_ci		return IRQ_NONE;
314262306a36Sopenharmony_ci	xudc_writel(xudc, ST_IP, ST);
314362306a36Sopenharmony_ci
314462306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
314562306a36Sopenharmony_ci	tegra_xudc_process_event_ring(xudc);
314662306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
314762306a36Sopenharmony_ci
314862306a36Sopenharmony_ci	return IRQ_HANDLED;
314962306a36Sopenharmony_ci}
315062306a36Sopenharmony_ci
315162306a36Sopenharmony_cistatic int tegra_xudc_alloc_ep(struct tegra_xudc *xudc, unsigned int index)
315262306a36Sopenharmony_ci{
315362306a36Sopenharmony_ci	struct tegra_xudc_ep *ep = &xudc->ep[index];
315462306a36Sopenharmony_ci
315562306a36Sopenharmony_ci	ep->xudc = xudc;
315662306a36Sopenharmony_ci	ep->index = index;
315762306a36Sopenharmony_ci	ep->context = &xudc->ep_context[index];
315862306a36Sopenharmony_ci	INIT_LIST_HEAD(&ep->queue);
315962306a36Sopenharmony_ci
316062306a36Sopenharmony_ci	/*
316162306a36Sopenharmony_ci	 * EP1 would be the input endpoint corresponding to EP0, but since
316262306a36Sopenharmony_ci	 * EP0 is bi-directional, EP1 is unused.
316362306a36Sopenharmony_ci	 */
316462306a36Sopenharmony_ci	if (index == 1)
316562306a36Sopenharmony_ci		return 0;
316662306a36Sopenharmony_ci
316762306a36Sopenharmony_ci	ep->transfer_ring = dma_pool_alloc(xudc->transfer_ring_pool,
316862306a36Sopenharmony_ci					   GFP_KERNEL,
316962306a36Sopenharmony_ci					   &ep->transfer_ring_phys);
317062306a36Sopenharmony_ci	if (!ep->transfer_ring)
317162306a36Sopenharmony_ci		return -ENOMEM;
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci	if (index) {
317462306a36Sopenharmony_ci		snprintf(ep->name, sizeof(ep->name), "ep%u%s", index / 2,
317562306a36Sopenharmony_ci			 (index % 2 == 0) ? "out" : "in");
317662306a36Sopenharmony_ci		ep->usb_ep.name = ep->name;
317762306a36Sopenharmony_ci		usb_ep_set_maxpacket_limit(&ep->usb_ep, 1024);
317862306a36Sopenharmony_ci		ep->usb_ep.max_streams = 16;
317962306a36Sopenharmony_ci		ep->usb_ep.ops = &tegra_xudc_ep_ops;
318062306a36Sopenharmony_ci		ep->usb_ep.caps.type_bulk = true;
318162306a36Sopenharmony_ci		ep->usb_ep.caps.type_int = true;
318262306a36Sopenharmony_ci		if (index & 1)
318362306a36Sopenharmony_ci			ep->usb_ep.caps.dir_in = true;
318462306a36Sopenharmony_ci		else
318562306a36Sopenharmony_ci			ep->usb_ep.caps.dir_out = true;
318662306a36Sopenharmony_ci		list_add_tail(&ep->usb_ep.ep_list, &xudc->gadget.ep_list);
318762306a36Sopenharmony_ci	} else {
318862306a36Sopenharmony_ci		strscpy(ep->name, "ep0", 3);
318962306a36Sopenharmony_ci		ep->usb_ep.name = ep->name;
319062306a36Sopenharmony_ci		usb_ep_set_maxpacket_limit(&ep->usb_ep, 512);
319162306a36Sopenharmony_ci		ep->usb_ep.ops = &tegra_xudc_ep0_ops;
319262306a36Sopenharmony_ci		ep->usb_ep.caps.type_control = true;
319362306a36Sopenharmony_ci		ep->usb_ep.caps.dir_in = true;
319462306a36Sopenharmony_ci		ep->usb_ep.caps.dir_out = true;
319562306a36Sopenharmony_ci	}
319662306a36Sopenharmony_ci
319762306a36Sopenharmony_ci	return 0;
319862306a36Sopenharmony_ci}
319962306a36Sopenharmony_ci
320062306a36Sopenharmony_cistatic void tegra_xudc_free_ep(struct tegra_xudc *xudc, unsigned int index)
320162306a36Sopenharmony_ci{
320262306a36Sopenharmony_ci	struct tegra_xudc_ep *ep = &xudc->ep[index];
320362306a36Sopenharmony_ci
320462306a36Sopenharmony_ci	/*
320562306a36Sopenharmony_ci	 * EP1 would be the input endpoint corresponding to EP0, but since
320662306a36Sopenharmony_ci	 * EP0 is bi-directional, EP1 is unused.
320762306a36Sopenharmony_ci	 */
320862306a36Sopenharmony_ci	if (index == 1)
320962306a36Sopenharmony_ci		return;
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_ci	dma_pool_free(xudc->transfer_ring_pool, ep->transfer_ring,
321262306a36Sopenharmony_ci		      ep->transfer_ring_phys);
321362306a36Sopenharmony_ci}
321462306a36Sopenharmony_ci
321562306a36Sopenharmony_cistatic int tegra_xudc_alloc_eps(struct tegra_xudc *xudc)
321662306a36Sopenharmony_ci{
321762306a36Sopenharmony_ci	struct usb_request *req;
321862306a36Sopenharmony_ci	unsigned int i;
321962306a36Sopenharmony_ci	int err;
322062306a36Sopenharmony_ci
322162306a36Sopenharmony_ci	xudc->ep_context =
322262306a36Sopenharmony_ci		dma_alloc_coherent(xudc->dev, XUDC_NR_EPS *
322362306a36Sopenharmony_ci				    sizeof(*xudc->ep_context),
322462306a36Sopenharmony_ci				    &xudc->ep_context_phys, GFP_KERNEL);
322562306a36Sopenharmony_ci	if (!xudc->ep_context)
322662306a36Sopenharmony_ci		return -ENOMEM;
322762306a36Sopenharmony_ci
322862306a36Sopenharmony_ci	xudc->transfer_ring_pool =
322962306a36Sopenharmony_ci		dmam_pool_create(dev_name(xudc->dev), xudc->dev,
323062306a36Sopenharmony_ci				 XUDC_TRANSFER_RING_SIZE *
323162306a36Sopenharmony_ci				 sizeof(struct tegra_xudc_trb),
323262306a36Sopenharmony_ci				 sizeof(struct tegra_xudc_trb), 0);
323362306a36Sopenharmony_ci	if (!xudc->transfer_ring_pool) {
323462306a36Sopenharmony_ci		err = -ENOMEM;
323562306a36Sopenharmony_ci		goto free_ep_context;
323662306a36Sopenharmony_ci	}
323762306a36Sopenharmony_ci
323862306a36Sopenharmony_ci	INIT_LIST_HEAD(&xudc->gadget.ep_list);
323962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) {
324062306a36Sopenharmony_ci		err = tegra_xudc_alloc_ep(xudc, i);
324162306a36Sopenharmony_ci		if (err < 0)
324262306a36Sopenharmony_ci			goto free_eps;
324362306a36Sopenharmony_ci	}
324462306a36Sopenharmony_ci
324562306a36Sopenharmony_ci	req = tegra_xudc_ep_alloc_request(&xudc->ep[0].usb_ep, GFP_KERNEL);
324662306a36Sopenharmony_ci	if (!req) {
324762306a36Sopenharmony_ci		err = -ENOMEM;
324862306a36Sopenharmony_ci		goto free_eps;
324962306a36Sopenharmony_ci	}
325062306a36Sopenharmony_ci	xudc->ep0_req = to_xudc_req(req);
325162306a36Sopenharmony_ci
325262306a36Sopenharmony_ci	return 0;
325362306a36Sopenharmony_ci
325462306a36Sopenharmony_cifree_eps:
325562306a36Sopenharmony_ci	for (; i > 0; i--)
325662306a36Sopenharmony_ci		tegra_xudc_free_ep(xudc, i - 1);
325762306a36Sopenharmony_cifree_ep_context:
325862306a36Sopenharmony_ci	dma_free_coherent(xudc->dev, XUDC_NR_EPS * sizeof(*xudc->ep_context),
325962306a36Sopenharmony_ci			  xudc->ep_context, xudc->ep_context_phys);
326062306a36Sopenharmony_ci	return err;
326162306a36Sopenharmony_ci}
326262306a36Sopenharmony_ci
326362306a36Sopenharmony_cistatic void tegra_xudc_init_eps(struct tegra_xudc *xudc)
326462306a36Sopenharmony_ci{
326562306a36Sopenharmony_ci	xudc_writel(xudc, lower_32_bits(xudc->ep_context_phys), ECPLO);
326662306a36Sopenharmony_ci	xudc_writel(xudc, upper_32_bits(xudc->ep_context_phys), ECPHI);
326762306a36Sopenharmony_ci}
326862306a36Sopenharmony_ci
326962306a36Sopenharmony_cistatic void tegra_xudc_free_eps(struct tegra_xudc *xudc)
327062306a36Sopenharmony_ci{
327162306a36Sopenharmony_ci	unsigned int i;
327262306a36Sopenharmony_ci
327362306a36Sopenharmony_ci	tegra_xudc_ep_free_request(&xudc->ep[0].usb_ep,
327462306a36Sopenharmony_ci				   &xudc->ep0_req->usb_req);
327562306a36Sopenharmony_ci
327662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(xudc->ep); i++)
327762306a36Sopenharmony_ci		tegra_xudc_free_ep(xudc, i);
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci	dma_free_coherent(xudc->dev, XUDC_NR_EPS * sizeof(*xudc->ep_context),
328062306a36Sopenharmony_ci			  xudc->ep_context, xudc->ep_context_phys);
328162306a36Sopenharmony_ci}
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_cistatic int tegra_xudc_alloc_event_ring(struct tegra_xudc *xudc)
328462306a36Sopenharmony_ci{
328562306a36Sopenharmony_ci	unsigned int i;
328662306a36Sopenharmony_ci
328762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) {
328862306a36Sopenharmony_ci		xudc->event_ring[i] =
328962306a36Sopenharmony_ci			dma_alloc_coherent(xudc->dev, XUDC_EVENT_RING_SIZE *
329062306a36Sopenharmony_ci					   sizeof(*xudc->event_ring[i]),
329162306a36Sopenharmony_ci					   &xudc->event_ring_phys[i],
329262306a36Sopenharmony_ci					   GFP_KERNEL);
329362306a36Sopenharmony_ci		if (!xudc->event_ring[i])
329462306a36Sopenharmony_ci			goto free_dma;
329562306a36Sopenharmony_ci	}
329662306a36Sopenharmony_ci
329762306a36Sopenharmony_ci	return 0;
329862306a36Sopenharmony_ci
329962306a36Sopenharmony_cifree_dma:
330062306a36Sopenharmony_ci	for (; i > 0; i--) {
330162306a36Sopenharmony_ci		dma_free_coherent(xudc->dev, XUDC_EVENT_RING_SIZE *
330262306a36Sopenharmony_ci				  sizeof(*xudc->event_ring[i - 1]),
330362306a36Sopenharmony_ci				  xudc->event_ring[i - 1],
330462306a36Sopenharmony_ci				  xudc->event_ring_phys[i - 1]);
330562306a36Sopenharmony_ci	}
330662306a36Sopenharmony_ci	return -ENOMEM;
330762306a36Sopenharmony_ci}
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_cistatic void tegra_xudc_init_event_ring(struct tegra_xudc *xudc)
331062306a36Sopenharmony_ci{
331162306a36Sopenharmony_ci	unsigned int i;
331262306a36Sopenharmony_ci	u32 val;
331362306a36Sopenharmony_ci
331462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) {
331562306a36Sopenharmony_ci		memset(xudc->event_ring[i], 0, XUDC_EVENT_RING_SIZE *
331662306a36Sopenharmony_ci		       sizeof(*xudc->event_ring[i]));
331762306a36Sopenharmony_ci
331862306a36Sopenharmony_ci		val = xudc_readl(xudc, ERSTSZ);
331962306a36Sopenharmony_ci		val &= ~(ERSTSZ_ERSTXSZ_MASK << ERSTSZ_ERSTXSZ_SHIFT(i));
332062306a36Sopenharmony_ci		val |= XUDC_EVENT_RING_SIZE << ERSTSZ_ERSTXSZ_SHIFT(i);
332162306a36Sopenharmony_ci		xudc_writel(xudc, val, ERSTSZ);
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci		xudc_writel(xudc, lower_32_bits(xudc->event_ring_phys[i]),
332462306a36Sopenharmony_ci			    ERSTXBALO(i));
332562306a36Sopenharmony_ci		xudc_writel(xudc, upper_32_bits(xudc->event_ring_phys[i]),
332662306a36Sopenharmony_ci			    ERSTXBAHI(i));
332762306a36Sopenharmony_ci	}
332862306a36Sopenharmony_ci
332962306a36Sopenharmony_ci	val = lower_32_bits(xudc->event_ring_phys[0]);
333062306a36Sopenharmony_ci	xudc_writel(xudc, val, ERDPLO);
333162306a36Sopenharmony_ci	val |= EREPLO_ECS;
333262306a36Sopenharmony_ci	xudc_writel(xudc, val, EREPLO);
333362306a36Sopenharmony_ci
333462306a36Sopenharmony_ci	val = upper_32_bits(xudc->event_ring_phys[0]);
333562306a36Sopenharmony_ci	xudc_writel(xudc, val, ERDPHI);
333662306a36Sopenharmony_ci	xudc_writel(xudc, val, EREPHI);
333762306a36Sopenharmony_ci
333862306a36Sopenharmony_ci	xudc->ccs = true;
333962306a36Sopenharmony_ci	xudc->event_ring_index = 0;
334062306a36Sopenharmony_ci	xudc->event_ring_deq_ptr = 0;
334162306a36Sopenharmony_ci}
334262306a36Sopenharmony_ci
334362306a36Sopenharmony_cistatic void tegra_xudc_free_event_ring(struct tegra_xudc *xudc)
334462306a36Sopenharmony_ci{
334562306a36Sopenharmony_ci	unsigned int i;
334662306a36Sopenharmony_ci
334762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) {
334862306a36Sopenharmony_ci		dma_free_coherent(xudc->dev, XUDC_EVENT_RING_SIZE *
334962306a36Sopenharmony_ci				  sizeof(*xudc->event_ring[i]),
335062306a36Sopenharmony_ci				  xudc->event_ring[i],
335162306a36Sopenharmony_ci				  xudc->event_ring_phys[i]);
335262306a36Sopenharmony_ci	}
335362306a36Sopenharmony_ci}
335462306a36Sopenharmony_ci
335562306a36Sopenharmony_cistatic void tegra_xudc_fpci_ipfs_init(struct tegra_xudc *xudc)
335662306a36Sopenharmony_ci{
335762306a36Sopenharmony_ci	u32 val;
335862306a36Sopenharmony_ci
335962306a36Sopenharmony_ci	if (xudc->soc->has_ipfs) {
336062306a36Sopenharmony_ci		val = ipfs_readl(xudc, XUSB_DEV_CONFIGURATION_0);
336162306a36Sopenharmony_ci		val |= XUSB_DEV_CONFIGURATION_0_EN_FPCI;
336262306a36Sopenharmony_ci		ipfs_writel(xudc, val, XUSB_DEV_CONFIGURATION_0);
336362306a36Sopenharmony_ci		usleep_range(10, 15);
336462306a36Sopenharmony_ci	}
336562306a36Sopenharmony_ci
336662306a36Sopenharmony_ci	/* Enable bus master */
336762306a36Sopenharmony_ci	val = XUSB_DEV_CFG_1_IO_SPACE_EN | XUSB_DEV_CFG_1_MEMORY_SPACE_EN |
336862306a36Sopenharmony_ci		XUSB_DEV_CFG_1_BUS_MASTER_EN;
336962306a36Sopenharmony_ci	fpci_writel(xudc, val, XUSB_DEV_CFG_1);
337062306a36Sopenharmony_ci
337162306a36Sopenharmony_ci	/* Program BAR0 space */
337262306a36Sopenharmony_ci	val = fpci_readl(xudc, XUSB_DEV_CFG_4);
337362306a36Sopenharmony_ci	val &= ~(XUSB_DEV_CFG_4_BASE_ADDR_MASK);
337462306a36Sopenharmony_ci	val |= xudc->phys_base & (XUSB_DEV_CFG_4_BASE_ADDR_MASK);
337562306a36Sopenharmony_ci
337662306a36Sopenharmony_ci	fpci_writel(xudc, val, XUSB_DEV_CFG_4);
337762306a36Sopenharmony_ci	fpci_writel(xudc, upper_32_bits(xudc->phys_base), XUSB_DEV_CFG_5);
337862306a36Sopenharmony_ci
337962306a36Sopenharmony_ci	usleep_range(100, 200);
338062306a36Sopenharmony_ci
338162306a36Sopenharmony_ci	if (xudc->soc->has_ipfs) {
338262306a36Sopenharmony_ci		/* Enable interrupt assertion */
338362306a36Sopenharmony_ci		val = ipfs_readl(xudc, XUSB_DEV_INTR_MASK_0);
338462306a36Sopenharmony_ci		val |= XUSB_DEV_INTR_MASK_0_IP_INT_MASK;
338562306a36Sopenharmony_ci		ipfs_writel(xudc, val, XUSB_DEV_INTR_MASK_0);
338662306a36Sopenharmony_ci	}
338762306a36Sopenharmony_ci}
338862306a36Sopenharmony_ci
338962306a36Sopenharmony_cistatic void tegra_xudc_device_params_init(struct tegra_xudc *xudc)
339062306a36Sopenharmony_ci{
339162306a36Sopenharmony_ci	u32 val, imod;
339262306a36Sopenharmony_ci
339362306a36Sopenharmony_ci	if (xudc->soc->has_ipfs) {
339462306a36Sopenharmony_ci		val = xudc_readl(xudc, BLCG);
339562306a36Sopenharmony_ci		val |= BLCG_ALL;
339662306a36Sopenharmony_ci		val &= ~(BLCG_DFPCI | BLCG_UFPCI | BLCG_FE |
339762306a36Sopenharmony_ci				BLCG_COREPLL_PWRDN);
339862306a36Sopenharmony_ci		val |= BLCG_IOPLL_0_PWRDN;
339962306a36Sopenharmony_ci		val |= BLCG_IOPLL_1_PWRDN;
340062306a36Sopenharmony_ci		val |= BLCG_IOPLL_2_PWRDN;
340162306a36Sopenharmony_ci
340262306a36Sopenharmony_ci		xudc_writel(xudc, val, BLCG);
340362306a36Sopenharmony_ci	}
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci	if (xudc->soc->port_speed_quirk)
340662306a36Sopenharmony_ci		tegra_xudc_limit_port_speed(xudc);
340762306a36Sopenharmony_ci
340862306a36Sopenharmony_ci	/* Set a reasonable U3 exit timer value. */
340962306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_PADCTL4);
341062306a36Sopenharmony_ci	val &= ~(SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK);
341162306a36Sopenharmony_ci	val |= SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3(0x5dc0);
341262306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_PADCTL4);
341362306a36Sopenharmony_ci
341462306a36Sopenharmony_ci	/* Default ping LFPS tBurst is too large. */
341562306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT0);
341662306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT0_PING_TBURST_MASK);
341762306a36Sopenharmony_ci	val |= SSPX_CORE_CNT0_PING_TBURST(0xa);
341862306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT0);
341962306a36Sopenharmony_ci
342062306a36Sopenharmony_ci	/* Default tPortConfiguration timeout is too small. */
342162306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT30);
342262306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT30_LMPITP_TIMER_MASK);
342362306a36Sopenharmony_ci	val |= SSPX_CORE_CNT30_LMPITP_TIMER(0x978);
342462306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT30);
342562306a36Sopenharmony_ci
342662306a36Sopenharmony_ci	if (xudc->soc->lpm_enable) {
342762306a36Sopenharmony_ci		/* Set L1 resume duration to 95 us. */
342862306a36Sopenharmony_ci		val = xudc_readl(xudc, HSFSPI_COUNT13);
342962306a36Sopenharmony_ci		val &= ~(HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK);
343062306a36Sopenharmony_ci		val |= HSFSPI_COUNT13_U2_RESUME_K_DURATION(0x2c88);
343162306a36Sopenharmony_ci		xudc_writel(xudc, val, HSFSPI_COUNT13);
343262306a36Sopenharmony_ci	}
343362306a36Sopenharmony_ci
343462306a36Sopenharmony_ci	/*
343562306a36Sopenharmony_ci	 * Compliance suite appears to be violating polling LFPS tBurst max
343662306a36Sopenharmony_ci	 * of 1.4us.  Send 1.45us instead.
343762306a36Sopenharmony_ci	 */
343862306a36Sopenharmony_ci	val = xudc_readl(xudc, SSPX_CORE_CNT32);
343962306a36Sopenharmony_ci	val &= ~(SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK);
344062306a36Sopenharmony_ci	val |= SSPX_CORE_CNT32_POLL_TBURST_MAX(0xb0);
344162306a36Sopenharmony_ci	xudc_writel(xudc, val, SSPX_CORE_CNT32);
344262306a36Sopenharmony_ci
344362306a36Sopenharmony_ci	/* Direct HS/FS port instance to RxDetect. */
344462306a36Sopenharmony_ci	val = xudc_readl(xudc, CFG_DEV_FE);
344562306a36Sopenharmony_ci	val &= ~(CFG_DEV_FE_PORTREGSEL_MASK);
344662306a36Sopenharmony_ci	val |= CFG_DEV_FE_PORTREGSEL(CFG_DEV_FE_PORTREGSEL_HSFS_PI);
344762306a36Sopenharmony_ci	xudc_writel(xudc, val, CFG_DEV_FE);
344862306a36Sopenharmony_ci
344962306a36Sopenharmony_ci	val = xudc_readl(xudc, PORTSC);
345062306a36Sopenharmony_ci	val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK);
345162306a36Sopenharmony_ci	val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_RXDETECT);
345262306a36Sopenharmony_ci	xudc_writel(xudc, val, PORTSC);
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci	/* Direct SS port instance to RxDetect. */
345562306a36Sopenharmony_ci	val = xudc_readl(xudc, CFG_DEV_FE);
345662306a36Sopenharmony_ci	val &= ~(CFG_DEV_FE_PORTREGSEL_MASK);
345762306a36Sopenharmony_ci	val |= CFG_DEV_FE_PORTREGSEL_SS_PI & CFG_DEV_FE_PORTREGSEL_MASK;
345862306a36Sopenharmony_ci	xudc_writel(xudc, val, CFG_DEV_FE);
345962306a36Sopenharmony_ci
346062306a36Sopenharmony_ci	val = xudc_readl(xudc, PORTSC);
346162306a36Sopenharmony_ci	val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK);
346262306a36Sopenharmony_ci	val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_RXDETECT);
346362306a36Sopenharmony_ci	xudc_writel(xudc, val, PORTSC);
346462306a36Sopenharmony_ci
346562306a36Sopenharmony_ci	/* Restore port instance. */
346662306a36Sopenharmony_ci	val = xudc_readl(xudc, CFG_DEV_FE);
346762306a36Sopenharmony_ci	val &= ~(CFG_DEV_FE_PORTREGSEL_MASK);
346862306a36Sopenharmony_ci	xudc_writel(xudc, val, CFG_DEV_FE);
346962306a36Sopenharmony_ci
347062306a36Sopenharmony_ci	/*
347162306a36Sopenharmony_ci	 * Enable INFINITE_SS_RETRY to prevent device from entering
347262306a36Sopenharmony_ci	 * Disabled.Error when attached to buggy SuperSpeed hubs.
347362306a36Sopenharmony_ci	 */
347462306a36Sopenharmony_ci	val = xudc_readl(xudc, CFG_DEV_FE);
347562306a36Sopenharmony_ci	val |= CFG_DEV_FE_INFINITE_SS_RETRY;
347662306a36Sopenharmony_ci	xudc_writel(xudc, val, CFG_DEV_FE);
347762306a36Sopenharmony_ci
347862306a36Sopenharmony_ci	/* Set interrupt moderation. */
347962306a36Sopenharmony_ci	imod = XUDC_INTERRUPT_MODERATION_US * 4;
348062306a36Sopenharmony_ci	val = xudc_readl(xudc, RT_IMOD);
348162306a36Sopenharmony_ci	val &= ~((RT_IMOD_IMODI_MASK) | (RT_IMOD_IMODC_MASK));
348262306a36Sopenharmony_ci	val |= (RT_IMOD_IMODI(imod) | RT_IMOD_IMODC(imod));
348362306a36Sopenharmony_ci	xudc_writel(xudc, val, RT_IMOD);
348462306a36Sopenharmony_ci
348562306a36Sopenharmony_ci	/* increase SSPI transaction timeout from 32us to 512us */
348662306a36Sopenharmony_ci	val = xudc_readl(xudc, CFG_DEV_SSPI_XFER);
348762306a36Sopenharmony_ci	val &= ~(CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK);
348862306a36Sopenharmony_ci	val |= CFG_DEV_SSPI_XFER_ACKTIMEOUT(0xf000);
348962306a36Sopenharmony_ci	xudc_writel(xudc, val, CFG_DEV_SSPI_XFER);
349062306a36Sopenharmony_ci}
349162306a36Sopenharmony_ci
349262306a36Sopenharmony_cistatic int tegra_xudc_phy_get(struct tegra_xudc *xudc)
349362306a36Sopenharmony_ci{
349462306a36Sopenharmony_ci	int err = 0, usb3_companion_port;
349562306a36Sopenharmony_ci	unsigned int i, j;
349662306a36Sopenharmony_ci
349762306a36Sopenharmony_ci	xudc->utmi_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
349862306a36Sopenharmony_ci					   sizeof(*xudc->utmi_phy), GFP_KERNEL);
349962306a36Sopenharmony_ci	if (!xudc->utmi_phy)
350062306a36Sopenharmony_ci		return -ENOMEM;
350162306a36Sopenharmony_ci
350262306a36Sopenharmony_ci	xudc->usb3_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
350362306a36Sopenharmony_ci					   sizeof(*xudc->usb3_phy), GFP_KERNEL);
350462306a36Sopenharmony_ci	if (!xudc->usb3_phy)
350562306a36Sopenharmony_ci		return -ENOMEM;
350662306a36Sopenharmony_ci
350762306a36Sopenharmony_ci	xudc->usbphy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
350862306a36Sopenharmony_ci					   sizeof(*xudc->usbphy), GFP_KERNEL);
350962306a36Sopenharmony_ci	if (!xudc->usbphy)
351062306a36Sopenharmony_ci		return -ENOMEM;
351162306a36Sopenharmony_ci
351262306a36Sopenharmony_ci	xudc->vbus_nb.notifier_call = tegra_xudc_vbus_notify;
351362306a36Sopenharmony_ci
351462306a36Sopenharmony_ci	for (i = 0; i < xudc->soc->num_phys; i++) {
351562306a36Sopenharmony_ci		char phy_name[] = "usb.-.";
351662306a36Sopenharmony_ci
351762306a36Sopenharmony_ci		/* Get USB2 phy */
351862306a36Sopenharmony_ci		snprintf(phy_name, sizeof(phy_name), "usb2-%d", i);
351962306a36Sopenharmony_ci		xudc->utmi_phy[i] = devm_phy_optional_get(xudc->dev, phy_name);
352062306a36Sopenharmony_ci		if (IS_ERR(xudc->utmi_phy[i])) {
352162306a36Sopenharmony_ci			err = PTR_ERR(xudc->utmi_phy[i]);
352262306a36Sopenharmony_ci			dev_err_probe(xudc->dev, err,
352362306a36Sopenharmony_ci				"failed to get PHY for phy-name usb2-%d\n", i);
352462306a36Sopenharmony_ci			goto clean_up;
352562306a36Sopenharmony_ci		} else if (xudc->utmi_phy[i]) {
352662306a36Sopenharmony_ci			/* Get usb-phy, if utmi phy is available */
352762306a36Sopenharmony_ci			xudc->usbphy[i] = devm_usb_get_phy_by_node(xudc->dev,
352862306a36Sopenharmony_ci						xudc->utmi_phy[i]->dev.of_node,
352962306a36Sopenharmony_ci						NULL);
353062306a36Sopenharmony_ci			if (IS_ERR(xudc->usbphy[i])) {
353162306a36Sopenharmony_ci				err = PTR_ERR(xudc->usbphy[i]);
353262306a36Sopenharmony_ci				dev_err_probe(xudc->dev, err,
353362306a36Sopenharmony_ci					      "failed to get usbphy-%d\n", i);
353462306a36Sopenharmony_ci				goto clean_up;
353562306a36Sopenharmony_ci			}
353662306a36Sopenharmony_ci		} else if (!xudc->utmi_phy[i]) {
353762306a36Sopenharmony_ci			/* if utmi phy is not available, ignore USB3 phy get */
353862306a36Sopenharmony_ci			continue;
353962306a36Sopenharmony_ci		}
354062306a36Sopenharmony_ci
354162306a36Sopenharmony_ci		/* Get USB3 phy */
354262306a36Sopenharmony_ci		usb3_companion_port = tegra_xusb_padctl_get_usb3_companion(xudc->padctl, i);
354362306a36Sopenharmony_ci		if (usb3_companion_port < 0)
354462306a36Sopenharmony_ci			continue;
354562306a36Sopenharmony_ci
354662306a36Sopenharmony_ci		for (j = 0; j < xudc->soc->num_phys; j++) {
354762306a36Sopenharmony_ci			snprintf(phy_name, sizeof(phy_name), "usb3-%d", j);
354862306a36Sopenharmony_ci			xudc->usb3_phy[i] = devm_phy_optional_get(xudc->dev, phy_name);
354962306a36Sopenharmony_ci			if (IS_ERR(xudc->usb3_phy[i])) {
355062306a36Sopenharmony_ci				err = PTR_ERR(xudc->usb3_phy[i]);
355162306a36Sopenharmony_ci				dev_err_probe(xudc->dev, err,
355262306a36Sopenharmony_ci					"failed to get PHY for phy-name usb3-%d\n", j);
355362306a36Sopenharmony_ci				goto clean_up;
355462306a36Sopenharmony_ci			} else if (xudc->usb3_phy[i]) {
355562306a36Sopenharmony_ci				int usb2_port =
355662306a36Sopenharmony_ci					tegra_xusb_padctl_get_port_number(xudc->utmi_phy[i]);
355762306a36Sopenharmony_ci				int usb3_port =
355862306a36Sopenharmony_ci					tegra_xusb_padctl_get_port_number(xudc->usb3_phy[i]);
355962306a36Sopenharmony_ci				if (usb3_port == usb3_companion_port) {
356062306a36Sopenharmony_ci					dev_dbg(xudc->dev, "USB2 port %d is paired with USB3 port %d for device mode port %d\n",
356162306a36Sopenharmony_ci					 usb2_port, usb3_port, i);
356262306a36Sopenharmony_ci					break;
356362306a36Sopenharmony_ci				}
356462306a36Sopenharmony_ci			}
356562306a36Sopenharmony_ci		}
356662306a36Sopenharmony_ci	}
356762306a36Sopenharmony_ci
356862306a36Sopenharmony_ci	return err;
356962306a36Sopenharmony_ci
357062306a36Sopenharmony_ciclean_up:
357162306a36Sopenharmony_ci	for (i = 0; i < xudc->soc->num_phys; i++) {
357262306a36Sopenharmony_ci		xudc->usb3_phy[i] = NULL;
357362306a36Sopenharmony_ci		xudc->utmi_phy[i] = NULL;
357462306a36Sopenharmony_ci		xudc->usbphy[i] = NULL;
357562306a36Sopenharmony_ci	}
357662306a36Sopenharmony_ci
357762306a36Sopenharmony_ci	return err;
357862306a36Sopenharmony_ci}
357962306a36Sopenharmony_ci
358062306a36Sopenharmony_cistatic void tegra_xudc_phy_exit(struct tegra_xudc *xudc)
358162306a36Sopenharmony_ci{
358262306a36Sopenharmony_ci	unsigned int i;
358362306a36Sopenharmony_ci
358462306a36Sopenharmony_ci	for (i = 0; i < xudc->soc->num_phys; i++) {
358562306a36Sopenharmony_ci		phy_exit(xudc->usb3_phy[i]);
358662306a36Sopenharmony_ci		phy_exit(xudc->utmi_phy[i]);
358762306a36Sopenharmony_ci	}
358862306a36Sopenharmony_ci}
358962306a36Sopenharmony_ci
359062306a36Sopenharmony_cistatic int tegra_xudc_phy_init(struct tegra_xudc *xudc)
359162306a36Sopenharmony_ci{
359262306a36Sopenharmony_ci	int err;
359362306a36Sopenharmony_ci	unsigned int i;
359462306a36Sopenharmony_ci
359562306a36Sopenharmony_ci	for (i = 0; i < xudc->soc->num_phys; i++) {
359662306a36Sopenharmony_ci		err = phy_init(xudc->utmi_phy[i]);
359762306a36Sopenharmony_ci		if (err < 0) {
359862306a36Sopenharmony_ci			dev_err(xudc->dev, "UTMI PHY #%u initialization failed: %d\n", i, err);
359962306a36Sopenharmony_ci			goto exit_phy;
360062306a36Sopenharmony_ci		}
360162306a36Sopenharmony_ci
360262306a36Sopenharmony_ci		err = phy_init(xudc->usb3_phy[i]);
360362306a36Sopenharmony_ci		if (err < 0) {
360462306a36Sopenharmony_ci			dev_err(xudc->dev, "USB3 PHY #%u initialization failed: %d\n", i, err);
360562306a36Sopenharmony_ci			goto exit_phy;
360662306a36Sopenharmony_ci		}
360762306a36Sopenharmony_ci	}
360862306a36Sopenharmony_ci	return 0;
360962306a36Sopenharmony_ci
361062306a36Sopenharmony_ciexit_phy:
361162306a36Sopenharmony_ci	tegra_xudc_phy_exit(xudc);
361262306a36Sopenharmony_ci	return err;
361362306a36Sopenharmony_ci}
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_cistatic const char * const tegra210_xudc_supply_names[] = {
361662306a36Sopenharmony_ci	"hvdd-usb",
361762306a36Sopenharmony_ci	"avddio-usb",
361862306a36Sopenharmony_ci};
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_cistatic const char * const tegra210_xudc_clock_names[] = {
362162306a36Sopenharmony_ci	"dev",
362262306a36Sopenharmony_ci	"ss",
362362306a36Sopenharmony_ci	"ss_src",
362462306a36Sopenharmony_ci	"hs_src",
362562306a36Sopenharmony_ci	"fs_src",
362662306a36Sopenharmony_ci};
362762306a36Sopenharmony_ci
362862306a36Sopenharmony_cistatic const char * const tegra186_xudc_clock_names[] = {
362962306a36Sopenharmony_ci	"dev",
363062306a36Sopenharmony_ci	"ss",
363162306a36Sopenharmony_ci	"ss_src",
363262306a36Sopenharmony_ci	"fs_src",
363362306a36Sopenharmony_ci};
363462306a36Sopenharmony_ci
363562306a36Sopenharmony_cistatic struct tegra_xudc_soc tegra210_xudc_soc_data = {
363662306a36Sopenharmony_ci	.supply_names = tegra210_xudc_supply_names,
363762306a36Sopenharmony_ci	.num_supplies = ARRAY_SIZE(tegra210_xudc_supply_names),
363862306a36Sopenharmony_ci	.clock_names = tegra210_xudc_clock_names,
363962306a36Sopenharmony_ci	.num_clks = ARRAY_SIZE(tegra210_xudc_clock_names),
364062306a36Sopenharmony_ci	.num_phys = 4,
364162306a36Sopenharmony_ci	.u1_enable = false,
364262306a36Sopenharmony_ci	.u2_enable = true,
364362306a36Sopenharmony_ci	.lpm_enable = false,
364462306a36Sopenharmony_ci	.invalid_seq_num = true,
364562306a36Sopenharmony_ci	.pls_quirk = true,
364662306a36Sopenharmony_ci	.port_reset_quirk = true,
364762306a36Sopenharmony_ci	.port_speed_quirk = false,
364862306a36Sopenharmony_ci	.has_ipfs = true,
364962306a36Sopenharmony_ci};
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_cistatic struct tegra_xudc_soc tegra186_xudc_soc_data = {
365262306a36Sopenharmony_ci	.clock_names = tegra186_xudc_clock_names,
365362306a36Sopenharmony_ci	.num_clks = ARRAY_SIZE(tegra186_xudc_clock_names),
365462306a36Sopenharmony_ci	.num_phys = 4,
365562306a36Sopenharmony_ci	.u1_enable = true,
365662306a36Sopenharmony_ci	.u2_enable = true,
365762306a36Sopenharmony_ci	.lpm_enable = false,
365862306a36Sopenharmony_ci	.invalid_seq_num = false,
365962306a36Sopenharmony_ci	.pls_quirk = false,
366062306a36Sopenharmony_ci	.port_reset_quirk = false,
366162306a36Sopenharmony_ci	.port_speed_quirk = false,
366262306a36Sopenharmony_ci	.has_ipfs = false,
366362306a36Sopenharmony_ci};
366462306a36Sopenharmony_ci
366562306a36Sopenharmony_cistatic struct tegra_xudc_soc tegra194_xudc_soc_data = {
366662306a36Sopenharmony_ci	.clock_names = tegra186_xudc_clock_names,
366762306a36Sopenharmony_ci	.num_clks = ARRAY_SIZE(tegra186_xudc_clock_names),
366862306a36Sopenharmony_ci	.num_phys = 4,
366962306a36Sopenharmony_ci	.u1_enable = true,
367062306a36Sopenharmony_ci	.u2_enable = true,
367162306a36Sopenharmony_ci	.lpm_enable = true,
367262306a36Sopenharmony_ci	.invalid_seq_num = false,
367362306a36Sopenharmony_ci	.pls_quirk = false,
367462306a36Sopenharmony_ci	.port_reset_quirk = false,
367562306a36Sopenharmony_ci	.port_speed_quirk = true,
367662306a36Sopenharmony_ci	.has_ipfs = false,
367762306a36Sopenharmony_ci};
367862306a36Sopenharmony_ci
367962306a36Sopenharmony_cistatic struct tegra_xudc_soc tegra234_xudc_soc_data = {
368062306a36Sopenharmony_ci	.clock_names = tegra186_xudc_clock_names,
368162306a36Sopenharmony_ci	.num_clks = ARRAY_SIZE(tegra186_xudc_clock_names),
368262306a36Sopenharmony_ci	.num_phys = 4,
368362306a36Sopenharmony_ci	.u1_enable = true,
368462306a36Sopenharmony_ci	.u2_enable = true,
368562306a36Sopenharmony_ci	.lpm_enable = true,
368662306a36Sopenharmony_ci	.invalid_seq_num = false,
368762306a36Sopenharmony_ci	.pls_quirk = false,
368862306a36Sopenharmony_ci	.port_reset_quirk = false,
368962306a36Sopenharmony_ci	.has_ipfs = false,
369062306a36Sopenharmony_ci};
369162306a36Sopenharmony_ci
369262306a36Sopenharmony_cistatic const struct of_device_id tegra_xudc_of_match[] = {
369362306a36Sopenharmony_ci	{
369462306a36Sopenharmony_ci		.compatible = "nvidia,tegra210-xudc",
369562306a36Sopenharmony_ci		.data = &tegra210_xudc_soc_data
369662306a36Sopenharmony_ci	},
369762306a36Sopenharmony_ci	{
369862306a36Sopenharmony_ci		.compatible = "nvidia,tegra186-xudc",
369962306a36Sopenharmony_ci		.data = &tegra186_xudc_soc_data
370062306a36Sopenharmony_ci	},
370162306a36Sopenharmony_ci	{
370262306a36Sopenharmony_ci		.compatible = "nvidia,tegra194-xudc",
370362306a36Sopenharmony_ci		.data = &tegra194_xudc_soc_data
370462306a36Sopenharmony_ci	},
370562306a36Sopenharmony_ci	{
370662306a36Sopenharmony_ci		.compatible = "nvidia,tegra234-xudc",
370762306a36Sopenharmony_ci		.data = &tegra234_xudc_soc_data
370862306a36Sopenharmony_ci	},
370962306a36Sopenharmony_ci	{ }
371062306a36Sopenharmony_ci};
371162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_xudc_of_match);
371262306a36Sopenharmony_ci
371362306a36Sopenharmony_cistatic void tegra_xudc_powerdomain_remove(struct tegra_xudc *xudc)
371462306a36Sopenharmony_ci{
371562306a36Sopenharmony_ci	if (xudc->genpd_dl_ss)
371662306a36Sopenharmony_ci		device_link_del(xudc->genpd_dl_ss);
371762306a36Sopenharmony_ci	if (xudc->genpd_dl_device)
371862306a36Sopenharmony_ci		device_link_del(xudc->genpd_dl_device);
371962306a36Sopenharmony_ci	if (xudc->genpd_dev_ss)
372062306a36Sopenharmony_ci		dev_pm_domain_detach(xudc->genpd_dev_ss, true);
372162306a36Sopenharmony_ci	if (xudc->genpd_dev_device)
372262306a36Sopenharmony_ci		dev_pm_domain_detach(xudc->genpd_dev_device, true);
372362306a36Sopenharmony_ci}
372462306a36Sopenharmony_ci
372562306a36Sopenharmony_cistatic int tegra_xudc_powerdomain_init(struct tegra_xudc *xudc)
372662306a36Sopenharmony_ci{
372762306a36Sopenharmony_ci	struct device *dev = xudc->dev;
372862306a36Sopenharmony_ci	int err;
372962306a36Sopenharmony_ci
373062306a36Sopenharmony_ci	xudc->genpd_dev_device = dev_pm_domain_attach_by_name(dev, "dev");
373162306a36Sopenharmony_ci	if (IS_ERR(xudc->genpd_dev_device)) {
373262306a36Sopenharmony_ci		err = PTR_ERR(xudc->genpd_dev_device);
373362306a36Sopenharmony_ci		dev_err(dev, "failed to get device power domain: %d\n", err);
373462306a36Sopenharmony_ci		return err;
373562306a36Sopenharmony_ci	}
373662306a36Sopenharmony_ci
373762306a36Sopenharmony_ci	xudc->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "ss");
373862306a36Sopenharmony_ci	if (IS_ERR(xudc->genpd_dev_ss)) {
373962306a36Sopenharmony_ci		err = PTR_ERR(xudc->genpd_dev_ss);
374062306a36Sopenharmony_ci		dev_err(dev, "failed to get SuperSpeed power domain: %d\n", err);
374162306a36Sopenharmony_ci		return err;
374262306a36Sopenharmony_ci	}
374362306a36Sopenharmony_ci
374462306a36Sopenharmony_ci	xudc->genpd_dl_device = device_link_add(dev, xudc->genpd_dev_device,
374562306a36Sopenharmony_ci						DL_FLAG_PM_RUNTIME |
374662306a36Sopenharmony_ci						DL_FLAG_STATELESS);
374762306a36Sopenharmony_ci	if (!xudc->genpd_dl_device) {
374862306a36Sopenharmony_ci		dev_err(dev, "failed to add USB device link\n");
374962306a36Sopenharmony_ci		return -ENODEV;
375062306a36Sopenharmony_ci	}
375162306a36Sopenharmony_ci
375262306a36Sopenharmony_ci	xudc->genpd_dl_ss = device_link_add(dev, xudc->genpd_dev_ss,
375362306a36Sopenharmony_ci					    DL_FLAG_PM_RUNTIME |
375462306a36Sopenharmony_ci					    DL_FLAG_STATELESS);
375562306a36Sopenharmony_ci	if (!xudc->genpd_dl_ss) {
375662306a36Sopenharmony_ci		dev_err(dev, "failed to add SuperSpeed device link\n");
375762306a36Sopenharmony_ci		return -ENODEV;
375862306a36Sopenharmony_ci	}
375962306a36Sopenharmony_ci
376062306a36Sopenharmony_ci	return 0;
376162306a36Sopenharmony_ci}
376262306a36Sopenharmony_ci
376362306a36Sopenharmony_cistatic int tegra_xudc_probe(struct platform_device *pdev)
376462306a36Sopenharmony_ci{
376562306a36Sopenharmony_ci	struct tegra_xudc *xudc;
376662306a36Sopenharmony_ci	struct resource *res;
376762306a36Sopenharmony_ci	unsigned int i;
376862306a36Sopenharmony_ci	int err;
376962306a36Sopenharmony_ci
377062306a36Sopenharmony_ci	xudc = devm_kzalloc(&pdev->dev, sizeof(*xudc), GFP_KERNEL);
377162306a36Sopenharmony_ci	if (!xudc)
377262306a36Sopenharmony_ci		return -ENOMEM;
377362306a36Sopenharmony_ci
377462306a36Sopenharmony_ci	xudc->dev = &pdev->dev;
377562306a36Sopenharmony_ci	platform_set_drvdata(pdev, xudc);
377662306a36Sopenharmony_ci
377762306a36Sopenharmony_ci	xudc->soc = of_device_get_match_data(&pdev->dev);
377862306a36Sopenharmony_ci	if (!xudc->soc)
377962306a36Sopenharmony_ci		return -ENODEV;
378062306a36Sopenharmony_ci
378162306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
378262306a36Sopenharmony_ci	xudc->base = devm_ioremap_resource(&pdev->dev, res);
378362306a36Sopenharmony_ci	if (IS_ERR(xudc->base))
378462306a36Sopenharmony_ci		return PTR_ERR(xudc->base);
378562306a36Sopenharmony_ci	xudc->phys_base = res->start;
378662306a36Sopenharmony_ci
378762306a36Sopenharmony_ci	xudc->fpci = devm_platform_ioremap_resource_byname(pdev, "fpci");
378862306a36Sopenharmony_ci	if (IS_ERR(xudc->fpci))
378962306a36Sopenharmony_ci		return PTR_ERR(xudc->fpci);
379062306a36Sopenharmony_ci
379162306a36Sopenharmony_ci	if (xudc->soc->has_ipfs) {
379262306a36Sopenharmony_ci		xudc->ipfs = devm_platform_ioremap_resource_byname(pdev, "ipfs");
379362306a36Sopenharmony_ci		if (IS_ERR(xudc->ipfs))
379462306a36Sopenharmony_ci			return PTR_ERR(xudc->ipfs);
379562306a36Sopenharmony_ci	}
379662306a36Sopenharmony_ci
379762306a36Sopenharmony_ci	xudc->irq = platform_get_irq(pdev, 0);
379862306a36Sopenharmony_ci	if (xudc->irq < 0)
379962306a36Sopenharmony_ci		return xudc->irq;
380062306a36Sopenharmony_ci
380162306a36Sopenharmony_ci	err = devm_request_irq(&pdev->dev, xudc->irq, tegra_xudc_irq, 0,
380262306a36Sopenharmony_ci			       dev_name(&pdev->dev), xudc);
380362306a36Sopenharmony_ci	if (err < 0) {
380462306a36Sopenharmony_ci		dev_err(xudc->dev, "failed to claim IRQ#%u: %d\n", xudc->irq,
380562306a36Sopenharmony_ci			err);
380662306a36Sopenharmony_ci		return err;
380762306a36Sopenharmony_ci	}
380862306a36Sopenharmony_ci
380962306a36Sopenharmony_ci	xudc->clks = devm_kcalloc(&pdev->dev, xudc->soc->num_clks, sizeof(*xudc->clks),
381062306a36Sopenharmony_ci				  GFP_KERNEL);
381162306a36Sopenharmony_ci	if (!xudc->clks)
381262306a36Sopenharmony_ci		return -ENOMEM;
381362306a36Sopenharmony_ci
381462306a36Sopenharmony_ci	for (i = 0; i < xudc->soc->num_clks; i++)
381562306a36Sopenharmony_ci		xudc->clks[i].id = xudc->soc->clock_names[i];
381662306a36Sopenharmony_ci
381762306a36Sopenharmony_ci	err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks, xudc->clks);
381862306a36Sopenharmony_ci	if (err) {
381962306a36Sopenharmony_ci		dev_err_probe(xudc->dev, err, "failed to request clocks\n");
382062306a36Sopenharmony_ci		return err;
382162306a36Sopenharmony_ci	}
382262306a36Sopenharmony_ci
382362306a36Sopenharmony_ci	xudc->supplies = devm_kcalloc(&pdev->dev, xudc->soc->num_supplies,
382462306a36Sopenharmony_ci				      sizeof(*xudc->supplies), GFP_KERNEL);
382562306a36Sopenharmony_ci	if (!xudc->supplies)
382662306a36Sopenharmony_ci		return -ENOMEM;
382762306a36Sopenharmony_ci
382862306a36Sopenharmony_ci	for (i = 0; i < xudc->soc->num_supplies; i++)
382962306a36Sopenharmony_ci		xudc->supplies[i].supply = xudc->soc->supply_names[i];
383062306a36Sopenharmony_ci
383162306a36Sopenharmony_ci	err = devm_regulator_bulk_get(&pdev->dev, xudc->soc->num_supplies,
383262306a36Sopenharmony_ci				      xudc->supplies);
383362306a36Sopenharmony_ci	if (err) {
383462306a36Sopenharmony_ci		dev_err_probe(xudc->dev, err, "failed to request regulators\n");
383562306a36Sopenharmony_ci		return err;
383662306a36Sopenharmony_ci	}
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_ci	xudc->padctl = tegra_xusb_padctl_get(&pdev->dev);
383962306a36Sopenharmony_ci	if (IS_ERR(xudc->padctl))
384062306a36Sopenharmony_ci		return PTR_ERR(xudc->padctl);
384162306a36Sopenharmony_ci
384262306a36Sopenharmony_ci	err = regulator_bulk_enable(xudc->soc->num_supplies, xudc->supplies);
384362306a36Sopenharmony_ci	if (err) {
384462306a36Sopenharmony_ci		dev_err(xudc->dev, "failed to enable regulators: %d\n", err);
384562306a36Sopenharmony_ci		goto put_padctl;
384662306a36Sopenharmony_ci	}
384762306a36Sopenharmony_ci
384862306a36Sopenharmony_ci	err = tegra_xudc_phy_get(xudc);
384962306a36Sopenharmony_ci	if (err)
385062306a36Sopenharmony_ci		goto disable_regulator;
385162306a36Sopenharmony_ci
385262306a36Sopenharmony_ci	err = tegra_xudc_powerdomain_init(xudc);
385362306a36Sopenharmony_ci	if (err)
385462306a36Sopenharmony_ci		goto put_powerdomains;
385562306a36Sopenharmony_ci
385662306a36Sopenharmony_ci	err = tegra_xudc_phy_init(xudc);
385762306a36Sopenharmony_ci	if (err)
385862306a36Sopenharmony_ci		goto put_powerdomains;
385962306a36Sopenharmony_ci
386062306a36Sopenharmony_ci	err = tegra_xudc_alloc_event_ring(xudc);
386162306a36Sopenharmony_ci	if (err)
386262306a36Sopenharmony_ci		goto disable_phy;
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_ci	err = tegra_xudc_alloc_eps(xudc);
386562306a36Sopenharmony_ci	if (err)
386662306a36Sopenharmony_ci		goto free_event_ring;
386762306a36Sopenharmony_ci
386862306a36Sopenharmony_ci	spin_lock_init(&xudc->lock);
386962306a36Sopenharmony_ci
387062306a36Sopenharmony_ci	init_completion(&xudc->disconnect_complete);
387162306a36Sopenharmony_ci
387262306a36Sopenharmony_ci	INIT_WORK(&xudc->usb_role_sw_work, tegra_xudc_usb_role_sw_work);
387362306a36Sopenharmony_ci
387462306a36Sopenharmony_ci	INIT_DELAYED_WORK(&xudc->plc_reset_work, tegra_xudc_plc_reset_work);
387562306a36Sopenharmony_ci
387662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&xudc->port_reset_war_work,
387762306a36Sopenharmony_ci				tegra_xudc_port_reset_war_work);
387862306a36Sopenharmony_ci
387962306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
388062306a36Sopenharmony_ci
388162306a36Sopenharmony_ci	xudc->gadget.ops = &tegra_xudc_gadget_ops;
388262306a36Sopenharmony_ci	xudc->gadget.ep0 = &xudc->ep[0].usb_ep;
388362306a36Sopenharmony_ci	xudc->gadget.name = "tegra-xudc";
388462306a36Sopenharmony_ci	xudc->gadget.max_speed = USB_SPEED_SUPER;
388562306a36Sopenharmony_ci
388662306a36Sopenharmony_ci	err = usb_add_gadget_udc(&pdev->dev, &xudc->gadget);
388762306a36Sopenharmony_ci	if (err) {
388862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to add USB gadget: %d\n", err);
388962306a36Sopenharmony_ci		goto free_eps;
389062306a36Sopenharmony_ci	}
389162306a36Sopenharmony_ci
389262306a36Sopenharmony_ci	for (i = 0; i < xudc->soc->num_phys; i++) {
389362306a36Sopenharmony_ci		if (!xudc->usbphy[i])
389462306a36Sopenharmony_ci			continue;
389562306a36Sopenharmony_ci
389662306a36Sopenharmony_ci		usb_register_notifier(xudc->usbphy[i], &xudc->vbus_nb);
389762306a36Sopenharmony_ci		tegra_xudc_update_data_role(xudc, xudc->usbphy[i]);
389862306a36Sopenharmony_ci	}
389962306a36Sopenharmony_ci
390062306a36Sopenharmony_ci	return 0;
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_cifree_eps:
390362306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
390462306a36Sopenharmony_ci	tegra_xudc_free_eps(xudc);
390562306a36Sopenharmony_cifree_event_ring:
390662306a36Sopenharmony_ci	tegra_xudc_free_event_ring(xudc);
390762306a36Sopenharmony_cidisable_phy:
390862306a36Sopenharmony_ci	tegra_xudc_phy_exit(xudc);
390962306a36Sopenharmony_ciput_powerdomains:
391062306a36Sopenharmony_ci	tegra_xudc_powerdomain_remove(xudc);
391162306a36Sopenharmony_cidisable_regulator:
391262306a36Sopenharmony_ci	regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies);
391362306a36Sopenharmony_ciput_padctl:
391462306a36Sopenharmony_ci	tegra_xusb_padctl_put(xudc->padctl);
391562306a36Sopenharmony_ci
391662306a36Sopenharmony_ci	return err;
391762306a36Sopenharmony_ci}
391862306a36Sopenharmony_ci
391962306a36Sopenharmony_cistatic void tegra_xudc_remove(struct platform_device *pdev)
392062306a36Sopenharmony_ci{
392162306a36Sopenharmony_ci	struct tegra_xudc *xudc = platform_get_drvdata(pdev);
392262306a36Sopenharmony_ci	unsigned int i;
392362306a36Sopenharmony_ci
392462306a36Sopenharmony_ci	pm_runtime_get_sync(xudc->dev);
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ci	cancel_delayed_work_sync(&xudc->plc_reset_work);
392762306a36Sopenharmony_ci	cancel_work_sync(&xudc->usb_role_sw_work);
392862306a36Sopenharmony_ci
392962306a36Sopenharmony_ci	usb_del_gadget_udc(&xudc->gadget);
393062306a36Sopenharmony_ci
393162306a36Sopenharmony_ci	tegra_xudc_free_eps(xudc);
393262306a36Sopenharmony_ci	tegra_xudc_free_event_ring(xudc);
393362306a36Sopenharmony_ci
393462306a36Sopenharmony_ci	tegra_xudc_powerdomain_remove(xudc);
393562306a36Sopenharmony_ci
393662306a36Sopenharmony_ci	regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies);
393762306a36Sopenharmony_ci
393862306a36Sopenharmony_ci	for (i = 0; i < xudc->soc->num_phys; i++) {
393962306a36Sopenharmony_ci		phy_power_off(xudc->utmi_phy[i]);
394062306a36Sopenharmony_ci		phy_power_off(xudc->usb3_phy[i]);
394162306a36Sopenharmony_ci	}
394262306a36Sopenharmony_ci
394362306a36Sopenharmony_ci	tegra_xudc_phy_exit(xudc);
394462306a36Sopenharmony_ci
394562306a36Sopenharmony_ci	pm_runtime_disable(xudc->dev);
394662306a36Sopenharmony_ci	pm_runtime_put(xudc->dev);
394762306a36Sopenharmony_ci
394862306a36Sopenharmony_ci	tegra_xusb_padctl_put(xudc->padctl);
394962306a36Sopenharmony_ci}
395062306a36Sopenharmony_ci
395162306a36Sopenharmony_cistatic int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc)
395262306a36Sopenharmony_ci{
395362306a36Sopenharmony_ci	unsigned long flags;
395462306a36Sopenharmony_ci
395562306a36Sopenharmony_ci	dev_dbg(xudc->dev, "entering ELPG\n");
395662306a36Sopenharmony_ci
395762306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
395862306a36Sopenharmony_ci
395962306a36Sopenharmony_ci	xudc->powergated = true;
396062306a36Sopenharmony_ci	xudc->saved_regs.ctrl = xudc_readl(xudc, CTRL);
396162306a36Sopenharmony_ci	xudc->saved_regs.portpm = xudc_readl(xudc, PORTPM);
396262306a36Sopenharmony_ci	xudc_writel(xudc, 0, CTRL);
396362306a36Sopenharmony_ci
396462306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
396562306a36Sopenharmony_ci
396662306a36Sopenharmony_ci	clk_bulk_disable_unprepare(xudc->soc->num_clks, xudc->clks);
396762306a36Sopenharmony_ci
396862306a36Sopenharmony_ci	regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies);
396962306a36Sopenharmony_ci
397062306a36Sopenharmony_ci	dev_dbg(xudc->dev, "entering ELPG done\n");
397162306a36Sopenharmony_ci	return 0;
397262306a36Sopenharmony_ci}
397362306a36Sopenharmony_ci
397462306a36Sopenharmony_cistatic int __maybe_unused tegra_xudc_unpowergate(struct tegra_xudc *xudc)
397562306a36Sopenharmony_ci{
397662306a36Sopenharmony_ci	unsigned long flags;
397762306a36Sopenharmony_ci	int err;
397862306a36Sopenharmony_ci
397962306a36Sopenharmony_ci	dev_dbg(xudc->dev, "exiting ELPG\n");
398062306a36Sopenharmony_ci
398162306a36Sopenharmony_ci	err = regulator_bulk_enable(xudc->soc->num_supplies,
398262306a36Sopenharmony_ci			xudc->supplies);
398362306a36Sopenharmony_ci	if (err < 0)
398462306a36Sopenharmony_ci		return err;
398562306a36Sopenharmony_ci
398662306a36Sopenharmony_ci	err = clk_bulk_prepare_enable(xudc->soc->num_clks, xudc->clks);
398762306a36Sopenharmony_ci	if (err < 0)
398862306a36Sopenharmony_ci		return err;
398962306a36Sopenharmony_ci
399062306a36Sopenharmony_ci	tegra_xudc_fpci_ipfs_init(xudc);
399162306a36Sopenharmony_ci
399262306a36Sopenharmony_ci	tegra_xudc_device_params_init(xudc);
399362306a36Sopenharmony_ci
399462306a36Sopenharmony_ci	tegra_xudc_init_event_ring(xudc);
399562306a36Sopenharmony_ci
399662306a36Sopenharmony_ci	tegra_xudc_init_eps(xudc);
399762306a36Sopenharmony_ci
399862306a36Sopenharmony_ci	xudc_writel(xudc, xudc->saved_regs.portpm, PORTPM);
399962306a36Sopenharmony_ci	xudc_writel(xudc, xudc->saved_regs.ctrl, CTRL);
400062306a36Sopenharmony_ci
400162306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
400262306a36Sopenharmony_ci	xudc->powergated = false;
400362306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
400462306a36Sopenharmony_ci
400562306a36Sopenharmony_ci	dev_dbg(xudc->dev, "exiting ELPG done\n");
400662306a36Sopenharmony_ci	return 0;
400762306a36Sopenharmony_ci}
400862306a36Sopenharmony_ci
400962306a36Sopenharmony_cistatic int __maybe_unused tegra_xudc_suspend(struct device *dev)
401062306a36Sopenharmony_ci{
401162306a36Sopenharmony_ci	struct tegra_xudc *xudc = dev_get_drvdata(dev);
401262306a36Sopenharmony_ci	unsigned long flags;
401362306a36Sopenharmony_ci
401462306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
401562306a36Sopenharmony_ci	xudc->suspended = true;
401662306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
401762306a36Sopenharmony_ci
401862306a36Sopenharmony_ci	flush_work(&xudc->usb_role_sw_work);
401962306a36Sopenharmony_ci
402062306a36Sopenharmony_ci	if (!pm_runtime_status_suspended(dev)) {
402162306a36Sopenharmony_ci		/* Forcibly disconnect before powergating. */
402262306a36Sopenharmony_ci		tegra_xudc_device_mode_off(xudc);
402362306a36Sopenharmony_ci		tegra_xudc_powergate(xudc);
402462306a36Sopenharmony_ci	}
402562306a36Sopenharmony_ci
402662306a36Sopenharmony_ci	pm_runtime_disable(dev);
402762306a36Sopenharmony_ci
402862306a36Sopenharmony_ci	return 0;
402962306a36Sopenharmony_ci}
403062306a36Sopenharmony_ci
403162306a36Sopenharmony_cistatic int __maybe_unused tegra_xudc_resume(struct device *dev)
403262306a36Sopenharmony_ci{
403362306a36Sopenharmony_ci	struct tegra_xudc *xudc = dev_get_drvdata(dev);
403462306a36Sopenharmony_ci	unsigned long flags;
403562306a36Sopenharmony_ci	int err;
403662306a36Sopenharmony_ci
403762306a36Sopenharmony_ci	err = tegra_xudc_unpowergate(xudc);
403862306a36Sopenharmony_ci	if (err < 0)
403962306a36Sopenharmony_ci		return err;
404062306a36Sopenharmony_ci
404162306a36Sopenharmony_ci	spin_lock_irqsave(&xudc->lock, flags);
404262306a36Sopenharmony_ci	xudc->suspended = false;
404362306a36Sopenharmony_ci	spin_unlock_irqrestore(&xudc->lock, flags);
404462306a36Sopenharmony_ci
404562306a36Sopenharmony_ci	schedule_work(&xudc->usb_role_sw_work);
404662306a36Sopenharmony_ci
404762306a36Sopenharmony_ci	pm_runtime_enable(dev);
404862306a36Sopenharmony_ci
404962306a36Sopenharmony_ci	return 0;
405062306a36Sopenharmony_ci}
405162306a36Sopenharmony_ci
405262306a36Sopenharmony_cistatic int __maybe_unused tegra_xudc_runtime_suspend(struct device *dev)
405362306a36Sopenharmony_ci{
405462306a36Sopenharmony_ci	struct tegra_xudc *xudc = dev_get_drvdata(dev);
405562306a36Sopenharmony_ci
405662306a36Sopenharmony_ci	return tegra_xudc_powergate(xudc);
405762306a36Sopenharmony_ci}
405862306a36Sopenharmony_ci
405962306a36Sopenharmony_cistatic int __maybe_unused tegra_xudc_runtime_resume(struct device *dev)
406062306a36Sopenharmony_ci{
406162306a36Sopenharmony_ci	struct tegra_xudc *xudc = dev_get_drvdata(dev);
406262306a36Sopenharmony_ci
406362306a36Sopenharmony_ci	return tegra_xudc_unpowergate(xudc);
406462306a36Sopenharmony_ci}
406562306a36Sopenharmony_ci
406662306a36Sopenharmony_cistatic const struct dev_pm_ops tegra_xudc_pm_ops = {
406762306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(tegra_xudc_suspend, tegra_xudc_resume)
406862306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(tegra_xudc_runtime_suspend,
406962306a36Sopenharmony_ci			   tegra_xudc_runtime_resume, NULL)
407062306a36Sopenharmony_ci};
407162306a36Sopenharmony_ci
407262306a36Sopenharmony_cistatic struct platform_driver tegra_xudc_driver = {
407362306a36Sopenharmony_ci	.probe = tegra_xudc_probe,
407462306a36Sopenharmony_ci	.remove_new = tegra_xudc_remove,
407562306a36Sopenharmony_ci	.driver = {
407662306a36Sopenharmony_ci		.name = "tegra-xudc",
407762306a36Sopenharmony_ci		.pm = &tegra_xudc_pm_ops,
407862306a36Sopenharmony_ci		.of_match_table = tegra_xudc_of_match,
407962306a36Sopenharmony_ci	},
408062306a36Sopenharmony_ci};
408162306a36Sopenharmony_cimodule_platform_driver(tegra_xudc_driver);
408262306a36Sopenharmony_ci
408362306a36Sopenharmony_ciMODULE_DESCRIPTION("NVIDIA Tegra XUSB Device Controller");
408462306a36Sopenharmony_ciMODULE_AUTHOR("Andrew Bresticker <abrestic@chromium.org>");
408562306a36Sopenharmony_ciMODULE_AUTHOR("Hui Fu <hfu@nvidia.com>");
408662306a36Sopenharmony_ciMODULE_AUTHOR("Nagarjuna Kristam <nkristam@nvidia.com>");
408762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
4088