xref: /linux/drivers/usb/dwc2/gadget.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
26fb914d7SGrigor Tovmasyan /*
347a1685fSDinh Nguyen  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
447a1685fSDinh Nguyen  *		http://www.samsung.com
547a1685fSDinh Nguyen  *
647a1685fSDinh Nguyen  * Copyright 2008 Openmoko, Inc.
747a1685fSDinh Nguyen  * Copyright 2008 Simtec Electronics
847a1685fSDinh Nguyen  *      Ben Dooks <ben@simtec.co.uk>
947a1685fSDinh Nguyen  *      http://armlinux.simtec.co.uk/
1047a1685fSDinh Nguyen  *
1147a1685fSDinh Nguyen  * S3C USB2.0 High-speed / OtG driver
1247a1685fSDinh Nguyen  */
1347a1685fSDinh Nguyen 
1447a1685fSDinh Nguyen #include <linux/kernel.h>
1547a1685fSDinh Nguyen #include <linux/module.h>
1647a1685fSDinh Nguyen #include <linux/spinlock.h>
1747a1685fSDinh Nguyen #include <linux/interrupt.h>
1847a1685fSDinh Nguyen #include <linux/platform_device.h>
1947a1685fSDinh Nguyen #include <linux/dma-mapping.h>
207ad8096eSMarek Szyprowski #include <linux/mutex.h>
2147a1685fSDinh Nguyen #include <linux/seq_file.h>
2247a1685fSDinh Nguyen #include <linux/delay.h>
2347a1685fSDinh Nguyen #include <linux/io.h>
2447a1685fSDinh Nguyen #include <linux/slab.h>
2547a1685fSDinh Nguyen 
2647a1685fSDinh Nguyen #include <linux/usb/ch9.h>
2747a1685fSDinh Nguyen #include <linux/usb/gadget.h>
2847a1685fSDinh Nguyen #include <linux/usb/phy.h>
29b4c53b4aSMinas Harutyunyan #include <linux/usb/composite.h>
30b4c53b4aSMinas Harutyunyan 
3147a1685fSDinh Nguyen 
32f7c0b143SDinh Nguyen #include "core.h"
33941fcce4SDinh Nguyen #include "hw.h"
3447a1685fSDinh Nguyen 
3547a1685fSDinh Nguyen /* conversion functions */
our_req(struct usb_request * req)361f91b4ccSFelipe Balbi static inline struct dwc2_hsotg_req *our_req(struct usb_request *req)
3747a1685fSDinh Nguyen {
381f91b4ccSFelipe Balbi 	return container_of(req, struct dwc2_hsotg_req, req);
3947a1685fSDinh Nguyen }
4047a1685fSDinh Nguyen 
our_ep(struct usb_ep * ep)411f91b4ccSFelipe Balbi static inline struct dwc2_hsotg_ep *our_ep(struct usb_ep *ep)
4247a1685fSDinh Nguyen {
431f91b4ccSFelipe Balbi 	return container_of(ep, struct dwc2_hsotg_ep, ep);
4447a1685fSDinh Nguyen }
4547a1685fSDinh Nguyen 
to_hsotg(struct usb_gadget * gadget)46941fcce4SDinh Nguyen static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget)
4747a1685fSDinh Nguyen {
48941fcce4SDinh Nguyen 	return container_of(gadget, struct dwc2_hsotg, gadget);
4947a1685fSDinh Nguyen }
5047a1685fSDinh Nguyen 
dwc2_set_bit(struct dwc2_hsotg * hsotg,u32 offset,u32 val)51f25c42b8SGevorg Sahakyan static inline void dwc2_set_bit(struct dwc2_hsotg *hsotg, u32 offset, u32 val)
5247a1685fSDinh Nguyen {
53f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, dwc2_readl(hsotg, offset) | val, offset);
5447a1685fSDinh Nguyen }
5547a1685fSDinh Nguyen 
dwc2_clear_bit(struct dwc2_hsotg * hsotg,u32 offset,u32 val)56f25c42b8SGevorg Sahakyan static inline void dwc2_clear_bit(struct dwc2_hsotg *hsotg, u32 offset, u32 val)
5747a1685fSDinh Nguyen {
58f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, dwc2_readl(hsotg, offset) & ~val, offset);
5947a1685fSDinh Nguyen }
6047a1685fSDinh Nguyen 
index_to_ep(struct dwc2_hsotg * hsotg,u32 ep_index,u32 dir_in)611f91b4ccSFelipe Balbi static inline struct dwc2_hsotg_ep *index_to_ep(struct dwc2_hsotg *hsotg,
62c6f5c050SMian Yousaf Kaukab 						u32 ep_index, u32 dir_in)
63c6f5c050SMian Yousaf Kaukab {
64c6f5c050SMian Yousaf Kaukab 	if (dir_in)
65c6f5c050SMian Yousaf Kaukab 		return hsotg->eps_in[ep_index];
66c6f5c050SMian Yousaf Kaukab 	else
67c6f5c050SMian Yousaf Kaukab 		return hsotg->eps_out[ep_index];
68c6f5c050SMian Yousaf Kaukab }
69c6f5c050SMian Yousaf Kaukab 
70997f4f81SMickael Maison /* forward declaration of functions */
711f91b4ccSFelipe Balbi static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg);
7247a1685fSDinh Nguyen 
7347a1685fSDinh Nguyen /**
7447a1685fSDinh Nguyen  * using_dma - return the DMA status of the driver.
7547a1685fSDinh Nguyen  * @hsotg: The driver state.
7647a1685fSDinh Nguyen  *
7747a1685fSDinh Nguyen  * Return true if we're using DMA.
7847a1685fSDinh Nguyen  *
7947a1685fSDinh Nguyen  * Currently, we have the DMA support code worked into everywhere
8047a1685fSDinh Nguyen  * that needs it, but the AMBA DMA implementation in the hardware can
8147a1685fSDinh Nguyen  * only DMA from 32bit aligned addresses. This means that gadgets such
8247a1685fSDinh Nguyen  * as the CDC Ethernet cannot work as they often pass packets which are
8347a1685fSDinh Nguyen  * not 32bit aligned.
8447a1685fSDinh Nguyen  *
8547a1685fSDinh Nguyen  * Unfortunately the choice to use DMA or not is global to the controller
8647a1685fSDinh Nguyen  * and seems to be only settable when the controller is being put through
8747a1685fSDinh Nguyen  * a core reset. This means we either need to fix the gadgets to take
8847a1685fSDinh Nguyen  * account of DMA alignment, or add bounce buffers (yuerk).
8947a1685fSDinh Nguyen  *
90edd74be8SGregory Herrero  * g_using_dma is set depending on dts flag.
9147a1685fSDinh Nguyen  */
using_dma(struct dwc2_hsotg * hsotg)92941fcce4SDinh Nguyen static inline bool using_dma(struct dwc2_hsotg *hsotg)
9347a1685fSDinh Nguyen {
9405ee799fSJohn Youn 	return hsotg->params.g_dma;
9547a1685fSDinh Nguyen }
9647a1685fSDinh Nguyen 
97dec4b556SVahram Aharonyan /*
98dec4b556SVahram Aharonyan  * using_desc_dma - return the descriptor DMA status of the driver.
99dec4b556SVahram Aharonyan  * @hsotg: The driver state.
100dec4b556SVahram Aharonyan  *
101dec4b556SVahram Aharonyan  * Return true if we're using descriptor DMA.
102dec4b556SVahram Aharonyan  */
using_desc_dma(struct dwc2_hsotg * hsotg)103dec4b556SVahram Aharonyan static inline bool using_desc_dma(struct dwc2_hsotg *hsotg)
104dec4b556SVahram Aharonyan {
105dec4b556SVahram Aharonyan 	return hsotg->params.g_dma_desc;
106dec4b556SVahram Aharonyan }
107dec4b556SVahram Aharonyan 
10847a1685fSDinh Nguyen /**
10992d1635dSVardan Mikayelyan  * dwc2_gadget_incr_frame_num - Increments the targeted frame number.
11092d1635dSVardan Mikayelyan  * @hs_ep: The endpoint
11192d1635dSVardan Mikayelyan  *
11292d1635dSVardan Mikayelyan  * This function will also check if the frame number overruns DSTS_SOFFN_LIMIT.
11392d1635dSVardan Mikayelyan  * If an overrun occurs it will wrap the value and set the frame_overrun flag.
11492d1635dSVardan Mikayelyan  */
dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep * hs_ep)11592d1635dSVardan Mikayelyan static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep)
11692d1635dSVardan Mikayelyan {
11791bb163eSMinas Harutyunyan 	struct dwc2_hsotg *hsotg = hs_ep->parent;
11891bb163eSMinas Harutyunyan 	u16 limit = DSTS_SOFFN_LIMIT;
11991bb163eSMinas Harutyunyan 
12091bb163eSMinas Harutyunyan 	if (hsotg->gadget.speed != USB_SPEED_HIGH)
12191bb163eSMinas Harutyunyan 		limit >>= 3;
12291bb163eSMinas Harutyunyan 
12392d1635dSVardan Mikayelyan 	hs_ep->target_frame += hs_ep->interval;
12491bb163eSMinas Harutyunyan 	if (hs_ep->target_frame > limit) {
125c1d5df69SGustavo A. R. Silva 		hs_ep->frame_overrun = true;
12691bb163eSMinas Harutyunyan 		hs_ep->target_frame &= limit;
12792d1635dSVardan Mikayelyan 	} else {
128c1d5df69SGustavo A. R. Silva 		hs_ep->frame_overrun = false;
12992d1635dSVardan Mikayelyan 	}
13092d1635dSVardan Mikayelyan }
13192d1635dSVardan Mikayelyan 
13292d1635dSVardan Mikayelyan /**
1339d630b9cSGrigor Tovmasyan  * dwc2_gadget_dec_frame_num_by_one - Decrements the targeted frame number
1349d630b9cSGrigor Tovmasyan  *                                    by one.
1359d630b9cSGrigor Tovmasyan  * @hs_ep: The endpoint.
1369d630b9cSGrigor Tovmasyan  *
1379d630b9cSGrigor Tovmasyan  * This function used in service interval based scheduling flow to calculate
1389d630b9cSGrigor Tovmasyan  * descriptor frame number filed value. For service interval mode frame
1399d630b9cSGrigor Tovmasyan  * number in descriptor should point to last (u)frame in the interval.
1409d630b9cSGrigor Tovmasyan  *
1419d630b9cSGrigor Tovmasyan  */
dwc2_gadget_dec_frame_num_by_one(struct dwc2_hsotg_ep * hs_ep)1429d630b9cSGrigor Tovmasyan static inline void dwc2_gadget_dec_frame_num_by_one(struct dwc2_hsotg_ep *hs_ep)
1439d630b9cSGrigor Tovmasyan {
14491bb163eSMinas Harutyunyan 	struct dwc2_hsotg *hsotg = hs_ep->parent;
14591bb163eSMinas Harutyunyan 	u16 limit = DSTS_SOFFN_LIMIT;
14691bb163eSMinas Harutyunyan 
14791bb163eSMinas Harutyunyan 	if (hsotg->gadget.speed != USB_SPEED_HIGH)
14891bb163eSMinas Harutyunyan 		limit >>= 3;
14991bb163eSMinas Harutyunyan 
1509d630b9cSGrigor Tovmasyan 	if (hs_ep->target_frame)
1519d630b9cSGrigor Tovmasyan 		hs_ep->target_frame -= 1;
1529d630b9cSGrigor Tovmasyan 	else
15391bb163eSMinas Harutyunyan 		hs_ep->target_frame = limit;
1549d630b9cSGrigor Tovmasyan }
1559d630b9cSGrigor Tovmasyan 
1569d630b9cSGrigor Tovmasyan /**
1571f91b4ccSFelipe Balbi  * dwc2_hsotg_en_gsint - enable one or more of the general interrupt
15847a1685fSDinh Nguyen  * @hsotg: The device state
15947a1685fSDinh Nguyen  * @ints: A bitmask of the interrupts to enable
16047a1685fSDinh Nguyen  */
dwc2_hsotg_en_gsint(struct dwc2_hsotg * hsotg,u32 ints)1611f91b4ccSFelipe Balbi static void dwc2_hsotg_en_gsint(struct dwc2_hsotg *hsotg, u32 ints)
16247a1685fSDinh Nguyen {
163f25c42b8SGevorg Sahakyan 	u32 gsintmsk = dwc2_readl(hsotg, GINTMSK);
16447a1685fSDinh Nguyen 	u32 new_gsintmsk;
16547a1685fSDinh Nguyen 
16647a1685fSDinh Nguyen 	new_gsintmsk = gsintmsk | ints;
16747a1685fSDinh Nguyen 
16847a1685fSDinh Nguyen 	if (new_gsintmsk != gsintmsk) {
16947a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "gsintmsk now 0x%08x\n", new_gsintmsk);
170f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, new_gsintmsk, GINTMSK);
17147a1685fSDinh Nguyen 	}
17247a1685fSDinh Nguyen }
17347a1685fSDinh Nguyen 
17447a1685fSDinh Nguyen /**
1751f91b4ccSFelipe Balbi  * dwc2_hsotg_disable_gsint - disable one or more of the general interrupt
17647a1685fSDinh Nguyen  * @hsotg: The device state
17747a1685fSDinh Nguyen  * @ints: A bitmask of the interrupts to enable
17847a1685fSDinh Nguyen  */
dwc2_hsotg_disable_gsint(struct dwc2_hsotg * hsotg,u32 ints)1791f91b4ccSFelipe Balbi static void dwc2_hsotg_disable_gsint(struct dwc2_hsotg *hsotg, u32 ints)
18047a1685fSDinh Nguyen {
181f25c42b8SGevorg Sahakyan 	u32 gsintmsk = dwc2_readl(hsotg, GINTMSK);
18247a1685fSDinh Nguyen 	u32 new_gsintmsk;
18347a1685fSDinh Nguyen 
18447a1685fSDinh Nguyen 	new_gsintmsk = gsintmsk & ~ints;
18547a1685fSDinh Nguyen 
18647a1685fSDinh Nguyen 	if (new_gsintmsk != gsintmsk)
187f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, new_gsintmsk, GINTMSK);
18847a1685fSDinh Nguyen }
18947a1685fSDinh Nguyen 
19047a1685fSDinh Nguyen /**
1911f91b4ccSFelipe Balbi  * dwc2_hsotg_ctrl_epint - enable/disable an endpoint irq
19247a1685fSDinh Nguyen  * @hsotg: The device state
19347a1685fSDinh Nguyen  * @ep: The endpoint index
19447a1685fSDinh Nguyen  * @dir_in: True if direction is in.
19547a1685fSDinh Nguyen  * @en: The enable value, true to enable
19647a1685fSDinh Nguyen  *
19747a1685fSDinh Nguyen  * Set or clear the mask for an individual endpoint's interrupt
19847a1685fSDinh Nguyen  * request.
19947a1685fSDinh Nguyen  */
dwc2_hsotg_ctrl_epint(struct dwc2_hsotg * hsotg,unsigned int ep,unsigned int dir_in,unsigned int en)2001f91b4ccSFelipe Balbi static void dwc2_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg,
20147a1685fSDinh Nguyen 				  unsigned int ep, unsigned int dir_in,
20247a1685fSDinh Nguyen 				 unsigned int en)
20347a1685fSDinh Nguyen {
20447a1685fSDinh Nguyen 	unsigned long flags;
20547a1685fSDinh Nguyen 	u32 bit = 1 << ep;
20647a1685fSDinh Nguyen 	u32 daint;
20747a1685fSDinh Nguyen 
20847a1685fSDinh Nguyen 	if (!dir_in)
20947a1685fSDinh Nguyen 		bit <<= 16;
21047a1685fSDinh Nguyen 
21147a1685fSDinh Nguyen 	local_irq_save(flags);
212f25c42b8SGevorg Sahakyan 	daint = dwc2_readl(hsotg, DAINTMSK);
21347a1685fSDinh Nguyen 	if (en)
21447a1685fSDinh Nguyen 		daint |= bit;
21547a1685fSDinh Nguyen 	else
21647a1685fSDinh Nguyen 		daint &= ~bit;
217f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, daint, DAINTMSK);
21847a1685fSDinh Nguyen 	local_irq_restore(flags);
21947a1685fSDinh Nguyen }
22047a1685fSDinh Nguyen 
22147a1685fSDinh Nguyen /**
222c138ecfaSSevak Arakelyan  * dwc2_hsotg_tx_fifo_count - return count of TX FIFOs in device mode
2236fb914d7SGrigor Tovmasyan  *
2246fb914d7SGrigor Tovmasyan  * @hsotg: Programming view of the DWC_otg controller
225c138ecfaSSevak Arakelyan  */
dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg * hsotg)226c138ecfaSSevak Arakelyan int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg)
227c138ecfaSSevak Arakelyan {
228c138ecfaSSevak Arakelyan 	if (hsotg->hw_params.en_multiple_tx_fifo)
229c138ecfaSSevak Arakelyan 		/* In dedicated FIFO mode we need count of IN EPs */
2309273083aSMinas Harutyunyan 		return hsotg->hw_params.num_dev_in_eps;
231c138ecfaSSevak Arakelyan 	else
232c138ecfaSSevak Arakelyan 		/* In shared FIFO mode we need count of Periodic IN EPs */
233c138ecfaSSevak Arakelyan 		return hsotg->hw_params.num_dev_perio_in_ep;
234c138ecfaSSevak Arakelyan }
235c138ecfaSSevak Arakelyan 
236c138ecfaSSevak Arakelyan /**
237c138ecfaSSevak Arakelyan  * dwc2_hsotg_tx_fifo_total_depth - return total FIFO depth available for
238c138ecfaSSevak Arakelyan  * device mode TX FIFOs
2396fb914d7SGrigor Tovmasyan  *
2406fb914d7SGrigor Tovmasyan  * @hsotg: Programming view of the DWC_otg controller
241c138ecfaSSevak Arakelyan  */
dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg * hsotg)242c138ecfaSSevak Arakelyan int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg)
243c138ecfaSSevak Arakelyan {
244c138ecfaSSevak Arakelyan 	int addr;
245c138ecfaSSevak Arakelyan 	int tx_addr_max;
246c138ecfaSSevak Arakelyan 	u32 np_tx_fifo_size;
247c138ecfaSSevak Arakelyan 
248c138ecfaSSevak Arakelyan 	np_tx_fifo_size = min_t(u32, hsotg->hw_params.dev_nperio_tx_fifo_size,
249c138ecfaSSevak Arakelyan 				hsotg->params.g_np_tx_fifo_size);
250c138ecfaSSevak Arakelyan 
251c138ecfaSSevak Arakelyan 	/* Get Endpoint Info Control block size in DWORDs. */
2529273083aSMinas Harutyunyan 	tx_addr_max = hsotg->hw_params.total_fifo_size;
253c138ecfaSSevak Arakelyan 
254c138ecfaSSevak Arakelyan 	addr = hsotg->params.g_rx_fifo_size + np_tx_fifo_size;
255c138ecfaSSevak Arakelyan 	if (tx_addr_max <= addr)
256c138ecfaSSevak Arakelyan 		return 0;
257c138ecfaSSevak Arakelyan 
258c138ecfaSSevak Arakelyan 	return tx_addr_max - addr;
259c138ecfaSSevak Arakelyan }
260c138ecfaSSevak Arakelyan 
261c138ecfaSSevak Arakelyan /**
262187c5298SGrigor Tovmasyan  * dwc2_gadget_wkup_alert_handler - Handler for WKUP_ALERT interrupt
263187c5298SGrigor Tovmasyan  *
264187c5298SGrigor Tovmasyan  * @hsotg: Programming view of the DWC_otg controller
265187c5298SGrigor Tovmasyan  *
266187c5298SGrigor Tovmasyan  */
dwc2_gadget_wkup_alert_handler(struct dwc2_hsotg * hsotg)267187c5298SGrigor Tovmasyan static void dwc2_gadget_wkup_alert_handler(struct dwc2_hsotg *hsotg)
268187c5298SGrigor Tovmasyan {
269187c5298SGrigor Tovmasyan 	u32 gintsts2;
270187c5298SGrigor Tovmasyan 	u32 gintmsk2;
271187c5298SGrigor Tovmasyan 
272187c5298SGrigor Tovmasyan 	gintsts2 = dwc2_readl(hsotg, GINTSTS2);
273187c5298SGrigor Tovmasyan 	gintmsk2 = dwc2_readl(hsotg, GINTMSK2);
2749607f3cdSLee Jones 	gintsts2 &= gintmsk2;
275187c5298SGrigor Tovmasyan 
276187c5298SGrigor Tovmasyan 	if (gintsts2 & GINTSTS2_WKUP_ALERT_INT) {
277187c5298SGrigor Tovmasyan 		dev_dbg(hsotg->dev, "%s: Wkup_Alert_Int\n", __func__);
27887b6d2c5SMinas Harutyunyan 		dwc2_set_bit(hsotg, GINTSTS2, GINTSTS2_WKUP_ALERT_INT);
279d64bc8eeSArtur Petrosyan 		dwc2_set_bit(hsotg, DCTL, DCTL_RMTWKUPSIG);
280187c5298SGrigor Tovmasyan 	}
281187c5298SGrigor Tovmasyan }
282187c5298SGrigor Tovmasyan 
283187c5298SGrigor Tovmasyan /**
284c138ecfaSSevak Arakelyan  * dwc2_hsotg_tx_fifo_average_depth - returns average depth of device mode
285c138ecfaSSevak Arakelyan  * TX FIFOs
2866fb914d7SGrigor Tovmasyan  *
2876fb914d7SGrigor Tovmasyan  * @hsotg: Programming view of the DWC_otg controller
288c138ecfaSSevak Arakelyan  */
dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg * hsotg)289c138ecfaSSevak Arakelyan int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg)
290c138ecfaSSevak Arakelyan {
291c138ecfaSSevak Arakelyan 	int tx_fifo_count;
292c138ecfaSSevak Arakelyan 	int tx_fifo_depth;
293c138ecfaSSevak Arakelyan 
294c138ecfaSSevak Arakelyan 	tx_fifo_depth = dwc2_hsotg_tx_fifo_total_depth(hsotg);
295c138ecfaSSevak Arakelyan 
296c138ecfaSSevak Arakelyan 	tx_fifo_count = dwc2_hsotg_tx_fifo_count(hsotg);
297c138ecfaSSevak Arakelyan 
298c138ecfaSSevak Arakelyan 	if (!tx_fifo_count)
299c138ecfaSSevak Arakelyan 		return tx_fifo_depth;
300c138ecfaSSevak Arakelyan 	else
301c138ecfaSSevak Arakelyan 		return tx_fifo_depth / tx_fifo_count;
302c138ecfaSSevak Arakelyan }
303c138ecfaSSevak Arakelyan 
304c138ecfaSSevak Arakelyan /**
3051f91b4ccSFelipe Balbi  * dwc2_hsotg_init_fifo - initialise non-periodic FIFOs
30647a1685fSDinh Nguyen  * @hsotg: The device instance.
30747a1685fSDinh Nguyen  */
dwc2_hsotg_init_fifo(struct dwc2_hsotg * hsotg)3081f91b4ccSFelipe Balbi static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
30947a1685fSDinh Nguyen {
3102317eacdSJohn Youn 	unsigned int ep;
31147a1685fSDinh Nguyen 	unsigned int addr;
31247a1685fSDinh Nguyen 	int timeout;
31379d6b8c5SSevak Arakelyan 
31447a1685fSDinh Nguyen 	u32 val;
31505ee799fSJohn Youn 	u32 *txfsz = hsotg->params.g_tx_fifo_size;
31647a1685fSDinh Nguyen 
3177fcbc95cSGregory Herrero 	/* Reset fifo map if not correctly cleared during previous session */
3187fcbc95cSGregory Herrero 	WARN_ON(hsotg->fifo_map);
3197fcbc95cSGregory Herrero 	hsotg->fifo_map = 0;
3207fcbc95cSGregory Herrero 
3210a176279SGregory Herrero 	/* set RX/NPTX FIFO sizes */
322f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, hsotg->params.g_rx_fifo_size, GRXFSIZ);
323f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, (hsotg->params.g_rx_fifo_size <<
324f25c42b8SGevorg Sahakyan 		    FIFOSIZE_STARTADDR_SHIFT) |
32505ee799fSJohn Youn 		    (hsotg->params.g_np_tx_fifo_size << FIFOSIZE_DEPTH_SHIFT),
326f25c42b8SGevorg Sahakyan 		    GNPTXFSIZ);
32747a1685fSDinh Nguyen 
32847a1685fSDinh Nguyen 	/*
32947a1685fSDinh Nguyen 	 * arange all the rest of the TX FIFOs, as some versions of this
33047a1685fSDinh Nguyen 	 * block have overlapping default addresses. This also ensures
33147a1685fSDinh Nguyen 	 * that if the settings have been changed, then they are set to
33247a1685fSDinh Nguyen 	 * known values.
33347a1685fSDinh Nguyen 	 */
33447a1685fSDinh Nguyen 
33547a1685fSDinh Nguyen 	/* start at the end of the GNPTXFSIZ, rounded up */
33605ee799fSJohn Youn 	addr = hsotg->params.g_rx_fifo_size + hsotg->params.g_np_tx_fifo_size;
33747a1685fSDinh Nguyen 
33847a1685fSDinh Nguyen 	/*
3390a176279SGregory Herrero 	 * Configure fifos sizes from provided configuration and assign
340b203d0a2SRobert Baldyga 	 * them to endpoints dynamically according to maxpacket size value of
341b203d0a2SRobert Baldyga 	 * given endpoint.
34247a1685fSDinh Nguyen 	 */
3432317eacdSJohn Youn 	for (ep = 1; ep < MAX_EPS_CHANNELS; ep++) {
34405ee799fSJohn Youn 		if (!txfsz[ep])
3453fa95385SJohn Youn 			continue;
3463fa95385SJohn Youn 		val = addr;
34705ee799fSJohn Youn 		val |= txfsz[ep] << FIFOSIZE_DEPTH_SHIFT;
34805ee799fSJohn Youn 		WARN_ONCE(addr + txfsz[ep] > hsotg->fifo_mem,
3493fa95385SJohn Youn 			  "insufficient fifo memory");
35005ee799fSJohn Youn 		addr += txfsz[ep];
35147a1685fSDinh Nguyen 
352f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, val, DPTXFSIZN(ep));
353f25c42b8SGevorg Sahakyan 		val = dwc2_readl(hsotg, DPTXFSIZN(ep));
35447a1685fSDinh Nguyen 	}
35547a1685fSDinh Nguyen 
356f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, hsotg->hw_params.total_fifo_size |
357f87c842fSSevak Arakelyan 		    addr << GDFIFOCFG_EPINFOBASE_SHIFT,
358f25c42b8SGevorg Sahakyan 		    GDFIFOCFG);
35947a1685fSDinh Nguyen 	/*
36047a1685fSDinh Nguyen 	 * according to p428 of the design guide, we need to ensure that
36147a1685fSDinh Nguyen 	 * all fifos are flushed before continuing
36247a1685fSDinh Nguyen 	 */
36347a1685fSDinh Nguyen 
364f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH |
365f25c42b8SGevorg Sahakyan 	       GRSTCTL_RXFFLSH, GRSTCTL);
36647a1685fSDinh Nguyen 
36747a1685fSDinh Nguyen 	/* wait until the fifos are both flushed */
36847a1685fSDinh Nguyen 	timeout = 100;
36947a1685fSDinh Nguyen 	while (1) {
370f25c42b8SGevorg Sahakyan 		val = dwc2_readl(hsotg, GRSTCTL);
37147a1685fSDinh Nguyen 
37247a1685fSDinh Nguyen 		if ((val & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH)) == 0)
37347a1685fSDinh Nguyen 			break;
37447a1685fSDinh Nguyen 
37547a1685fSDinh Nguyen 		if (--timeout == 0) {
37647a1685fSDinh Nguyen 			dev_err(hsotg->dev,
37747a1685fSDinh Nguyen 				"%s: timeout flushing fifos (GRSTCTL=%08x)\n",
37847a1685fSDinh Nguyen 				__func__, val);
37948b20bcbSGregory Herrero 			break;
38047a1685fSDinh Nguyen 		}
38147a1685fSDinh Nguyen 
38247a1685fSDinh Nguyen 		udelay(1);
38347a1685fSDinh Nguyen 	}
38447a1685fSDinh Nguyen 
38547a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "FIFOs reset, timeout at %d\n", timeout);
38647a1685fSDinh Nguyen }
38747a1685fSDinh Nguyen 
38847a1685fSDinh Nguyen /**
3896fb914d7SGrigor Tovmasyan  * dwc2_hsotg_ep_alloc_request - allocate USB rerequest structure
39047a1685fSDinh Nguyen  * @ep: USB endpoint to allocate request for.
39147a1685fSDinh Nguyen  * @flags: Allocation flags
39247a1685fSDinh Nguyen  *
39347a1685fSDinh Nguyen  * Allocate a new USB request structure appropriate for the specified endpoint
39447a1685fSDinh Nguyen  */
dwc2_hsotg_ep_alloc_request(struct usb_ep * ep,gfp_t flags)3951f91b4ccSFelipe Balbi static struct usb_request *dwc2_hsotg_ep_alloc_request(struct usb_ep *ep,
39647a1685fSDinh Nguyen 						       gfp_t flags)
39747a1685fSDinh Nguyen {
3981f91b4ccSFelipe Balbi 	struct dwc2_hsotg_req *req;
39947a1685fSDinh Nguyen 
400ec33efe2SJohn Youn 	req = kzalloc(sizeof(*req), flags);
40147a1685fSDinh Nguyen 	if (!req)
40247a1685fSDinh Nguyen 		return NULL;
40347a1685fSDinh Nguyen 
40447a1685fSDinh Nguyen 	INIT_LIST_HEAD(&req->queue);
40547a1685fSDinh Nguyen 
40647a1685fSDinh Nguyen 	return &req->req;
40747a1685fSDinh Nguyen }
40847a1685fSDinh Nguyen 
40947a1685fSDinh Nguyen /**
41047a1685fSDinh Nguyen  * is_ep_periodic - return true if the endpoint is in periodic mode.
41147a1685fSDinh Nguyen  * @hs_ep: The endpoint to query.
41247a1685fSDinh Nguyen  *
41347a1685fSDinh Nguyen  * Returns true if the endpoint is in periodic mode, meaning it is being
41447a1685fSDinh Nguyen  * used for an Interrupt or ISO transfer.
41547a1685fSDinh Nguyen  */
is_ep_periodic(struct dwc2_hsotg_ep * hs_ep)4161f91b4ccSFelipe Balbi static inline int is_ep_periodic(struct dwc2_hsotg_ep *hs_ep)
41747a1685fSDinh Nguyen {
41847a1685fSDinh Nguyen 	return hs_ep->periodic;
41947a1685fSDinh Nguyen }
42047a1685fSDinh Nguyen 
42147a1685fSDinh Nguyen /**
4221f91b4ccSFelipe Balbi  * dwc2_hsotg_unmap_dma - unmap the DMA memory being used for the request
42347a1685fSDinh Nguyen  * @hsotg: The device state.
42447a1685fSDinh Nguyen  * @hs_ep: The endpoint for the request
42547a1685fSDinh Nguyen  * @hs_req: The request being processed.
42647a1685fSDinh Nguyen  *
4271f91b4ccSFelipe Balbi  * This is the reverse of dwc2_hsotg_map_dma(), called for the completion
42847a1685fSDinh Nguyen  * of a request to ensure the buffer is ready for access by the caller.
42947a1685fSDinh Nguyen  */
dwc2_hsotg_unmap_dma(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep,struct dwc2_hsotg_req * hs_req)4301f91b4ccSFelipe Balbi static void dwc2_hsotg_unmap_dma(struct dwc2_hsotg *hsotg,
4311f91b4ccSFelipe Balbi 				 struct dwc2_hsotg_ep *hs_ep,
4321f91b4ccSFelipe Balbi 				struct dwc2_hsotg_req *hs_req)
43347a1685fSDinh Nguyen {
43447a1685fSDinh Nguyen 	struct usb_request *req = &hs_req->req;
4359da51974SJohn Youn 
43675a41ce4SPhil Elwell 	usb_gadget_unmap_request(&hsotg->gadget, req, hs_ep->map_dir);
43747a1685fSDinh Nguyen }
43847a1685fSDinh Nguyen 
4390f6b80c0SVahram Aharonyan /*
4400f6b80c0SVahram Aharonyan  * dwc2_gadget_alloc_ctrl_desc_chains - allocate DMA descriptor chains
4410f6b80c0SVahram Aharonyan  * for Control endpoint
4420f6b80c0SVahram Aharonyan  * @hsotg: The device state.
4430f6b80c0SVahram Aharonyan  *
4440f6b80c0SVahram Aharonyan  * This function will allocate 4 descriptor chains for EP 0: 2 for
4450f6b80c0SVahram Aharonyan  * Setup stage, per one for IN and OUT data/status transactions.
4460f6b80c0SVahram Aharonyan  */
dwc2_gadget_alloc_ctrl_desc_chains(struct dwc2_hsotg * hsotg)4470f6b80c0SVahram Aharonyan static int dwc2_gadget_alloc_ctrl_desc_chains(struct dwc2_hsotg *hsotg)
4480f6b80c0SVahram Aharonyan {
4490f6b80c0SVahram Aharonyan 	hsotg->setup_desc[0] =
4500f6b80c0SVahram Aharonyan 		dmam_alloc_coherent(hsotg->dev,
4510f6b80c0SVahram Aharonyan 				    sizeof(struct dwc2_dma_desc),
4520f6b80c0SVahram Aharonyan 				    &hsotg->setup_desc_dma[0],
4530f6b80c0SVahram Aharonyan 				    GFP_KERNEL);
4540f6b80c0SVahram Aharonyan 	if (!hsotg->setup_desc[0])
4550f6b80c0SVahram Aharonyan 		goto fail;
4560f6b80c0SVahram Aharonyan 
4570f6b80c0SVahram Aharonyan 	hsotg->setup_desc[1] =
4580f6b80c0SVahram Aharonyan 		dmam_alloc_coherent(hsotg->dev,
4590f6b80c0SVahram Aharonyan 				    sizeof(struct dwc2_dma_desc),
4600f6b80c0SVahram Aharonyan 				    &hsotg->setup_desc_dma[1],
4610f6b80c0SVahram Aharonyan 				    GFP_KERNEL);
4620f6b80c0SVahram Aharonyan 	if (!hsotg->setup_desc[1])
4630f6b80c0SVahram Aharonyan 		goto fail;
4640f6b80c0SVahram Aharonyan 
4650f6b80c0SVahram Aharonyan 	hsotg->ctrl_in_desc =
4660f6b80c0SVahram Aharonyan 		dmam_alloc_coherent(hsotg->dev,
4670f6b80c0SVahram Aharonyan 				    sizeof(struct dwc2_dma_desc),
4680f6b80c0SVahram Aharonyan 				    &hsotg->ctrl_in_desc_dma,
4690f6b80c0SVahram Aharonyan 				    GFP_KERNEL);
4700f6b80c0SVahram Aharonyan 	if (!hsotg->ctrl_in_desc)
4710f6b80c0SVahram Aharonyan 		goto fail;
4720f6b80c0SVahram Aharonyan 
4730f6b80c0SVahram Aharonyan 	hsotg->ctrl_out_desc =
4740f6b80c0SVahram Aharonyan 		dmam_alloc_coherent(hsotg->dev,
4750f6b80c0SVahram Aharonyan 				    sizeof(struct dwc2_dma_desc),
4760f6b80c0SVahram Aharonyan 				    &hsotg->ctrl_out_desc_dma,
4770f6b80c0SVahram Aharonyan 				    GFP_KERNEL);
4780f6b80c0SVahram Aharonyan 	if (!hsotg->ctrl_out_desc)
4790f6b80c0SVahram Aharonyan 		goto fail;
4800f6b80c0SVahram Aharonyan 
4810f6b80c0SVahram Aharonyan 	return 0;
4820f6b80c0SVahram Aharonyan 
4830f6b80c0SVahram Aharonyan fail:
4840f6b80c0SVahram Aharonyan 	return -ENOMEM;
4850f6b80c0SVahram Aharonyan }
4860f6b80c0SVahram Aharonyan 
48747a1685fSDinh Nguyen /**
4881f91b4ccSFelipe Balbi  * dwc2_hsotg_write_fifo - write packet Data to the TxFIFO
48947a1685fSDinh Nguyen  * @hsotg: The controller state.
49047a1685fSDinh Nguyen  * @hs_ep: The endpoint we're going to write for.
49147a1685fSDinh Nguyen  * @hs_req: The request to write data for.
49247a1685fSDinh Nguyen  *
49347a1685fSDinh Nguyen  * This is called when the TxFIFO has some space in it to hold a new
49447a1685fSDinh Nguyen  * transmission and we have something to give it. The actual setup of
49547a1685fSDinh Nguyen  * the data size is done elsewhere, so all we have to do is to actually
49647a1685fSDinh Nguyen  * write the data.
49747a1685fSDinh Nguyen  *
49847a1685fSDinh Nguyen  * The return value is zero if there is more space (or nothing was done)
49947a1685fSDinh Nguyen  * otherwise -ENOSPC is returned if the FIFO space was used up.
50047a1685fSDinh Nguyen  *
50147a1685fSDinh Nguyen  * This routine is only needed for PIO
50247a1685fSDinh Nguyen  */
dwc2_hsotg_write_fifo(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep,struct dwc2_hsotg_req * hs_req)5031f91b4ccSFelipe Balbi static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
5041f91b4ccSFelipe Balbi 				 struct dwc2_hsotg_ep *hs_ep,
5051f91b4ccSFelipe Balbi 				struct dwc2_hsotg_req *hs_req)
50647a1685fSDinh Nguyen {
50747a1685fSDinh Nguyen 	bool periodic = is_ep_periodic(hs_ep);
508f25c42b8SGevorg Sahakyan 	u32 gnptxsts = dwc2_readl(hsotg, GNPTXSTS);
50947a1685fSDinh Nguyen 	int buf_pos = hs_req->req.actual;
51047a1685fSDinh Nguyen 	int to_write = hs_ep->size_loaded;
51147a1685fSDinh Nguyen 	void *data;
51247a1685fSDinh Nguyen 	int can_write;
51347a1685fSDinh Nguyen 	int pkt_round;
51447a1685fSDinh Nguyen 	int max_transfer;
51547a1685fSDinh Nguyen 
51647a1685fSDinh Nguyen 	to_write -= (buf_pos - hs_ep->last_load);
51747a1685fSDinh Nguyen 
51847a1685fSDinh Nguyen 	/* if there's nothing to write, get out early */
51947a1685fSDinh Nguyen 	if (to_write == 0)
52047a1685fSDinh Nguyen 		return 0;
52147a1685fSDinh Nguyen 
52247a1685fSDinh Nguyen 	if (periodic && !hsotg->dedicated_fifos) {
523f25c42b8SGevorg Sahakyan 		u32 epsize = dwc2_readl(hsotg, DIEPTSIZ(hs_ep->index));
52447a1685fSDinh Nguyen 		int size_left;
52547a1685fSDinh Nguyen 		int size_done;
52647a1685fSDinh Nguyen 
52747a1685fSDinh Nguyen 		/*
52847a1685fSDinh Nguyen 		 * work out how much data was loaded so we can calculate
52947a1685fSDinh Nguyen 		 * how much data is left in the fifo.
53047a1685fSDinh Nguyen 		 */
53147a1685fSDinh Nguyen 
53247a1685fSDinh Nguyen 		size_left = DXEPTSIZ_XFERSIZE_GET(epsize);
53347a1685fSDinh Nguyen 
53447a1685fSDinh Nguyen 		/*
53547a1685fSDinh Nguyen 		 * if shared fifo, we cannot write anything until the
53647a1685fSDinh Nguyen 		 * previous data has been completely sent.
53747a1685fSDinh Nguyen 		 */
53847a1685fSDinh Nguyen 		if (hs_ep->fifo_load != 0) {
5391f91b4ccSFelipe Balbi 			dwc2_hsotg_en_gsint(hsotg, GINTSTS_PTXFEMP);
54047a1685fSDinh Nguyen 			return -ENOSPC;
54147a1685fSDinh Nguyen 		}
54247a1685fSDinh Nguyen 
54347a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s: left=%d, load=%d, fifo=%d, size %d\n",
54447a1685fSDinh Nguyen 			__func__, size_left,
54547a1685fSDinh Nguyen 			hs_ep->size_loaded, hs_ep->fifo_load, hs_ep->fifo_size);
54647a1685fSDinh Nguyen 
54747a1685fSDinh Nguyen 		/* how much of the data has moved */
54847a1685fSDinh Nguyen 		size_done = hs_ep->size_loaded - size_left;
54947a1685fSDinh Nguyen 
55047a1685fSDinh Nguyen 		/* how much data is left in the fifo */
55147a1685fSDinh Nguyen 		can_write = hs_ep->fifo_load - size_done;
55247a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s: => can_write1=%d\n",
55347a1685fSDinh Nguyen 			__func__, can_write);
55447a1685fSDinh Nguyen 
55547a1685fSDinh Nguyen 		can_write = hs_ep->fifo_size - can_write;
55647a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s: => can_write2=%d\n",
55747a1685fSDinh Nguyen 			__func__, can_write);
55847a1685fSDinh Nguyen 
55947a1685fSDinh Nguyen 		if (can_write <= 0) {
5601f91b4ccSFelipe Balbi 			dwc2_hsotg_en_gsint(hsotg, GINTSTS_PTXFEMP);
56147a1685fSDinh Nguyen 			return -ENOSPC;
56247a1685fSDinh Nguyen 		}
56347a1685fSDinh Nguyen 	} else if (hsotg->dedicated_fifos && hs_ep->index != 0) {
564f25c42b8SGevorg Sahakyan 		can_write = dwc2_readl(hsotg,
565ad674a15SRobert Baldyga 				       DTXFSTS(hs_ep->fifo_index));
56647a1685fSDinh Nguyen 
56747a1685fSDinh Nguyen 		can_write &= 0xffff;
56847a1685fSDinh Nguyen 		can_write *= 4;
56947a1685fSDinh Nguyen 	} else {
57047a1685fSDinh Nguyen 		if (GNPTXSTS_NP_TXQ_SPC_AVAIL_GET(gnptxsts) == 0) {
57147a1685fSDinh Nguyen 			dev_dbg(hsotg->dev,
57247a1685fSDinh Nguyen 				"%s: no queue slots available (0x%08x)\n",
57347a1685fSDinh Nguyen 				__func__, gnptxsts);
57447a1685fSDinh Nguyen 
5751f91b4ccSFelipe Balbi 			dwc2_hsotg_en_gsint(hsotg, GINTSTS_NPTXFEMP);
57647a1685fSDinh Nguyen 			return -ENOSPC;
57747a1685fSDinh Nguyen 		}
57847a1685fSDinh Nguyen 
57947a1685fSDinh Nguyen 		can_write = GNPTXSTS_NP_TXF_SPC_AVAIL_GET(gnptxsts);
58047a1685fSDinh Nguyen 		can_write *= 4;	/* fifo size is in 32bit quantities. */
58147a1685fSDinh Nguyen 	}
58247a1685fSDinh Nguyen 
58347a1685fSDinh Nguyen 	max_transfer = hs_ep->ep.maxpacket * hs_ep->mc;
58447a1685fSDinh Nguyen 
58547a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, max_transfer %d\n",
58647a1685fSDinh Nguyen 		__func__, gnptxsts, can_write, to_write, max_transfer);
58747a1685fSDinh Nguyen 
58847a1685fSDinh Nguyen 	/*
58947a1685fSDinh Nguyen 	 * limit to 512 bytes of data, it seems at least on the non-periodic
59047a1685fSDinh Nguyen 	 * FIFO, requests of >512 cause the endpoint to get stuck with a
59147a1685fSDinh Nguyen 	 * fragment of the end of the transfer in it.
59247a1685fSDinh Nguyen 	 */
59347a1685fSDinh Nguyen 	if (can_write > 512 && !periodic)
59447a1685fSDinh Nguyen 		can_write = 512;
59547a1685fSDinh Nguyen 
59647a1685fSDinh Nguyen 	/*
59747a1685fSDinh Nguyen 	 * limit the write to one max-packet size worth of data, but allow
59847a1685fSDinh Nguyen 	 * the transfer to return that it did not run out of fifo space
59947a1685fSDinh Nguyen 	 * doing it.
60047a1685fSDinh Nguyen 	 */
60147a1685fSDinh Nguyen 	if (to_write > max_transfer) {
60247a1685fSDinh Nguyen 		to_write = max_transfer;
60347a1685fSDinh Nguyen 
60447a1685fSDinh Nguyen 		/* it's needed only when we do not use dedicated fifos */
60547a1685fSDinh Nguyen 		if (!hsotg->dedicated_fifos)
6061f91b4ccSFelipe Balbi 			dwc2_hsotg_en_gsint(hsotg,
60747a1685fSDinh Nguyen 					    periodic ? GINTSTS_PTXFEMP :
60847a1685fSDinh Nguyen 					   GINTSTS_NPTXFEMP);
60947a1685fSDinh Nguyen 	}
61047a1685fSDinh Nguyen 
61147a1685fSDinh Nguyen 	/* see if we can write data */
61247a1685fSDinh Nguyen 
61347a1685fSDinh Nguyen 	if (to_write > can_write) {
61447a1685fSDinh Nguyen 		to_write = can_write;
61547a1685fSDinh Nguyen 		pkt_round = to_write % max_transfer;
61647a1685fSDinh Nguyen 
61747a1685fSDinh Nguyen 		/*
61847a1685fSDinh Nguyen 		 * Round the write down to an
61947a1685fSDinh Nguyen 		 * exact number of packets.
62047a1685fSDinh Nguyen 		 *
62147a1685fSDinh Nguyen 		 * Note, we do not currently check to see if we can ever
62247a1685fSDinh Nguyen 		 * write a full packet or not to the FIFO.
62347a1685fSDinh Nguyen 		 */
62447a1685fSDinh Nguyen 
62547a1685fSDinh Nguyen 		if (pkt_round)
62647a1685fSDinh Nguyen 			to_write -= pkt_round;
62747a1685fSDinh Nguyen 
62847a1685fSDinh Nguyen 		/*
62947a1685fSDinh Nguyen 		 * enable correct FIFO interrupt to alert us when there
63047a1685fSDinh Nguyen 		 * is more room left.
63147a1685fSDinh Nguyen 		 */
63247a1685fSDinh Nguyen 
63347a1685fSDinh Nguyen 		/* it's needed only when we do not use dedicated fifos */
63447a1685fSDinh Nguyen 		if (!hsotg->dedicated_fifos)
6351f91b4ccSFelipe Balbi 			dwc2_hsotg_en_gsint(hsotg,
63647a1685fSDinh Nguyen 					    periodic ? GINTSTS_PTXFEMP :
63747a1685fSDinh Nguyen 					   GINTSTS_NPTXFEMP);
63847a1685fSDinh Nguyen 	}
63947a1685fSDinh Nguyen 
64047a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "write %d/%d, can_write %d, done %d\n",
64147a1685fSDinh Nguyen 		to_write, hs_req->req.length, can_write, buf_pos);
64247a1685fSDinh Nguyen 
64347a1685fSDinh Nguyen 	if (to_write <= 0)
64447a1685fSDinh Nguyen 		return -ENOSPC;
64547a1685fSDinh Nguyen 
64647a1685fSDinh Nguyen 	hs_req->req.actual = buf_pos + to_write;
64747a1685fSDinh Nguyen 	hs_ep->total_data += to_write;
64847a1685fSDinh Nguyen 
64947a1685fSDinh Nguyen 	if (periodic)
65047a1685fSDinh Nguyen 		hs_ep->fifo_load += to_write;
65147a1685fSDinh Nguyen 
65247a1685fSDinh Nguyen 	to_write = DIV_ROUND_UP(to_write, 4);
65347a1685fSDinh Nguyen 	data = hs_req->req.buf + buf_pos;
65447a1685fSDinh Nguyen 
655342ccce1SGevorg Sahakyan 	dwc2_writel_rep(hsotg, EPFIFO(hs_ep->index), data, to_write);
65647a1685fSDinh Nguyen 
65747a1685fSDinh Nguyen 	return (to_write >= can_write) ? -ENOSPC : 0;
65847a1685fSDinh Nguyen }
65947a1685fSDinh Nguyen 
66047a1685fSDinh Nguyen /**
66147a1685fSDinh Nguyen  * get_ep_limit - get the maximum data legnth for this endpoint
66247a1685fSDinh Nguyen  * @hs_ep: The endpoint
66347a1685fSDinh Nguyen  *
66447a1685fSDinh Nguyen  * Return the maximum data that can be queued in one go on a given endpoint
66547a1685fSDinh Nguyen  * so that transfers that are too long can be split.
66647a1685fSDinh Nguyen  */
get_ep_limit(struct dwc2_hsotg_ep * hs_ep)6679da51974SJohn Youn static unsigned int get_ep_limit(struct dwc2_hsotg_ep *hs_ep)
66847a1685fSDinh Nguyen {
66947a1685fSDinh Nguyen 	int index = hs_ep->index;
6709da51974SJohn Youn 	unsigned int maxsize;
6719da51974SJohn Youn 	unsigned int maxpkt;
67247a1685fSDinh Nguyen 
67347a1685fSDinh Nguyen 	if (index != 0) {
67447a1685fSDinh Nguyen 		maxsize = DXEPTSIZ_XFERSIZE_LIMIT + 1;
67547a1685fSDinh Nguyen 		maxpkt = DXEPTSIZ_PKTCNT_LIMIT + 1;
67647a1685fSDinh Nguyen 	} else {
67747a1685fSDinh Nguyen 		maxsize = 64 + 64;
67847a1685fSDinh Nguyen 		if (hs_ep->dir_in)
67947a1685fSDinh Nguyen 			maxpkt = DIEPTSIZ0_PKTCNT_LIMIT + 1;
68047a1685fSDinh Nguyen 		else
68147a1685fSDinh Nguyen 			maxpkt = 2;
68247a1685fSDinh Nguyen 	}
68347a1685fSDinh Nguyen 
68447a1685fSDinh Nguyen 	/* we made the constant loading easier above by using +1 */
68547a1685fSDinh Nguyen 	maxpkt--;
68647a1685fSDinh Nguyen 	maxsize--;
68747a1685fSDinh Nguyen 
68847a1685fSDinh Nguyen 	/*
68947a1685fSDinh Nguyen 	 * constrain by packet count if maxpkts*pktsize is greater
69047a1685fSDinh Nguyen 	 * than the length register size.
69147a1685fSDinh Nguyen 	 */
69247a1685fSDinh Nguyen 
69347a1685fSDinh Nguyen 	if ((maxpkt * hs_ep->ep.maxpacket) < maxsize)
69447a1685fSDinh Nguyen 		maxsize = maxpkt * hs_ep->ep.maxpacket;
69547a1685fSDinh Nguyen 
69647a1685fSDinh Nguyen 	return maxsize;
69747a1685fSDinh Nguyen }
69847a1685fSDinh Nguyen 
69947a1685fSDinh Nguyen /**
700381fc8f8SVardan Mikayelyan  * dwc2_hsotg_read_frameno - read current frame number
701381fc8f8SVardan Mikayelyan  * @hsotg: The device instance
702381fc8f8SVardan Mikayelyan  *
703381fc8f8SVardan Mikayelyan  * Return the current frame number
704381fc8f8SVardan Mikayelyan  */
dwc2_hsotg_read_frameno(struct dwc2_hsotg * hsotg)705381fc8f8SVardan Mikayelyan static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
706381fc8f8SVardan Mikayelyan {
707381fc8f8SVardan Mikayelyan 	u32 dsts;
708381fc8f8SVardan Mikayelyan 
709f25c42b8SGevorg Sahakyan 	dsts = dwc2_readl(hsotg, DSTS);
710381fc8f8SVardan Mikayelyan 	dsts &= DSTS_SOFFN_MASK;
711381fc8f8SVardan Mikayelyan 	dsts >>= DSTS_SOFFN_SHIFT;
712381fc8f8SVardan Mikayelyan 
713381fc8f8SVardan Mikayelyan 	return dsts;
714381fc8f8SVardan Mikayelyan }
715381fc8f8SVardan Mikayelyan 
716381fc8f8SVardan Mikayelyan /**
717cf77b5fbSVahram Aharonyan  * dwc2_gadget_get_chain_limit - get the maximum data payload value of the
718cf77b5fbSVahram Aharonyan  * DMA descriptor chain prepared for specific endpoint
719cf77b5fbSVahram Aharonyan  * @hs_ep: The endpoint
720cf77b5fbSVahram Aharonyan  *
721cf77b5fbSVahram Aharonyan  * Return the maximum data that can be queued in one go on a given endpoint
722cf77b5fbSVahram Aharonyan  * depending on its descriptor chain capacity so that transfers that
723cf77b5fbSVahram Aharonyan  * are too long can be split.
724cf77b5fbSVahram Aharonyan  */
dwc2_gadget_get_chain_limit(struct dwc2_hsotg_ep * hs_ep)725cf77b5fbSVahram Aharonyan static unsigned int dwc2_gadget_get_chain_limit(struct dwc2_hsotg_ep *hs_ep)
726cf77b5fbSVahram Aharonyan {
727b2c586ebSMinas Harutyunyan 	const struct usb_endpoint_descriptor *ep_desc = hs_ep->ep.desc;
728cf77b5fbSVahram Aharonyan 	int is_isoc = hs_ep->isochronous;
729cf77b5fbSVahram Aharonyan 	unsigned int maxsize;
730b2c586ebSMinas Harutyunyan 	u32 mps = hs_ep->ep.maxpacket;
731b2c586ebSMinas Harutyunyan 	int dir_in = hs_ep->dir_in;
732cf77b5fbSVahram Aharonyan 
733cf77b5fbSVahram Aharonyan 	if (is_isoc)
73454f37f56SMinas Harutyunyan 		maxsize = (hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_LIMIT :
73554f37f56SMinas Harutyunyan 					   DEV_DMA_ISOC_RX_NBYTES_LIMIT) *
73654f37f56SMinas Harutyunyan 					   MAX_DMA_DESC_NUM_HS_ISOC;
737cf77b5fbSVahram Aharonyan 	else
73854f37f56SMinas Harutyunyan 		maxsize = DEV_DMA_NBYTES_LIMIT * MAX_DMA_DESC_NUM_GENERIC;
739cf77b5fbSVahram Aharonyan 
740b2c586ebSMinas Harutyunyan 	/* Interrupt OUT EP with mps not multiple of 4 */
741b2c586ebSMinas Harutyunyan 	if (hs_ep->index)
742b2c586ebSMinas Harutyunyan 		if (usb_endpoint_xfer_int(ep_desc) && !dir_in && (mps % 4))
743b2c586ebSMinas Harutyunyan 			maxsize = mps * MAX_DMA_DESC_NUM_GENERIC;
744b2c586ebSMinas Harutyunyan 
745cf77b5fbSVahram Aharonyan 	return maxsize;
746cf77b5fbSVahram Aharonyan }
747cf77b5fbSVahram Aharonyan 
748e02f9aa6SVahram Aharonyan /*
749e02f9aa6SVahram Aharonyan  * dwc2_gadget_get_desc_params - get DMA descriptor parameters.
750e02f9aa6SVahram Aharonyan  * @hs_ep: The endpoint
751e02f9aa6SVahram Aharonyan  * @mask: RX/TX bytes mask to be defined
752e02f9aa6SVahram Aharonyan  *
753e02f9aa6SVahram Aharonyan  * Returns maximum data payload for one descriptor after analyzing endpoint
754e02f9aa6SVahram Aharonyan  * characteristics.
755e02f9aa6SVahram Aharonyan  * DMA descriptor transfer bytes limit depends on EP type:
756e02f9aa6SVahram Aharonyan  * Control out - MPS,
757e02f9aa6SVahram Aharonyan  * Isochronous - descriptor rx/tx bytes bitfield limit,
758e02f9aa6SVahram Aharonyan  * Control In/Bulk/Interrupt - multiple of mps. This will allow to not
759e02f9aa6SVahram Aharonyan  * have concatenations from various descriptors within one packet.
760b2c586ebSMinas Harutyunyan  * Interrupt OUT - if mps not multiple of 4 then a single packet corresponds
761b2c586ebSMinas Harutyunyan  * to a single descriptor.
762e02f9aa6SVahram Aharonyan  *
763e02f9aa6SVahram Aharonyan  * Selects corresponding mask for RX/TX bytes as well.
764e02f9aa6SVahram Aharonyan  */
dwc2_gadget_get_desc_params(struct dwc2_hsotg_ep * hs_ep,u32 * mask)765e02f9aa6SVahram Aharonyan static u32 dwc2_gadget_get_desc_params(struct dwc2_hsotg_ep *hs_ep, u32 *mask)
766e02f9aa6SVahram Aharonyan {
767b2c586ebSMinas Harutyunyan 	const struct usb_endpoint_descriptor *ep_desc = hs_ep->ep.desc;
768e02f9aa6SVahram Aharonyan 	u32 mps = hs_ep->ep.maxpacket;
769e02f9aa6SVahram Aharonyan 	int dir_in = hs_ep->dir_in;
770e02f9aa6SVahram Aharonyan 	u32 desc_size = 0;
771e02f9aa6SVahram Aharonyan 
772e02f9aa6SVahram Aharonyan 	if (!hs_ep->index && !dir_in) {
773e02f9aa6SVahram Aharonyan 		desc_size = mps;
774e02f9aa6SVahram Aharonyan 		*mask = DEV_DMA_NBYTES_MASK;
775e02f9aa6SVahram Aharonyan 	} else if (hs_ep->isochronous) {
776e02f9aa6SVahram Aharonyan 		if (dir_in) {
777e02f9aa6SVahram Aharonyan 			desc_size = DEV_DMA_ISOC_TX_NBYTES_LIMIT;
778e02f9aa6SVahram Aharonyan 			*mask = DEV_DMA_ISOC_TX_NBYTES_MASK;
779e02f9aa6SVahram Aharonyan 		} else {
780e02f9aa6SVahram Aharonyan 			desc_size = DEV_DMA_ISOC_RX_NBYTES_LIMIT;
781e02f9aa6SVahram Aharonyan 			*mask = DEV_DMA_ISOC_RX_NBYTES_MASK;
782e02f9aa6SVahram Aharonyan 		}
783e02f9aa6SVahram Aharonyan 	} else {
784e02f9aa6SVahram Aharonyan 		desc_size = DEV_DMA_NBYTES_LIMIT;
785e02f9aa6SVahram Aharonyan 		*mask = DEV_DMA_NBYTES_MASK;
786e02f9aa6SVahram Aharonyan 
787e02f9aa6SVahram Aharonyan 		/* Round down desc_size to be mps multiple */
788e02f9aa6SVahram Aharonyan 		desc_size -= desc_size % mps;
789e02f9aa6SVahram Aharonyan 	}
790e02f9aa6SVahram Aharonyan 
791b2c586ebSMinas Harutyunyan 	/* Interrupt OUT EP with mps not multiple of 4 */
792b2c586ebSMinas Harutyunyan 	if (hs_ep->index)
793b2c586ebSMinas Harutyunyan 		if (usb_endpoint_xfer_int(ep_desc) && !dir_in && (mps % 4)) {
794b2c586ebSMinas Harutyunyan 			desc_size = mps;
795b2c586ebSMinas Harutyunyan 			*mask = DEV_DMA_NBYTES_MASK;
796b2c586ebSMinas Harutyunyan 		}
797b2c586ebSMinas Harutyunyan 
798e02f9aa6SVahram Aharonyan 	return desc_size;
799e02f9aa6SVahram Aharonyan }
800e02f9aa6SVahram Aharonyan 
dwc2_gadget_fill_nonisoc_xfer_ddma_one(struct dwc2_hsotg_ep * hs_ep,struct dwc2_dma_desc ** desc,dma_addr_t dma_buff,unsigned int len,bool true_last)80110209abeSAndrzej Pietrasiewicz static void dwc2_gadget_fill_nonisoc_xfer_ddma_one(struct dwc2_hsotg_ep *hs_ep,
80210209abeSAndrzej Pietrasiewicz 						 struct dwc2_dma_desc **desc,
803e02f9aa6SVahram Aharonyan 						 dma_addr_t dma_buff,
80410209abeSAndrzej Pietrasiewicz 						 unsigned int len,
80510209abeSAndrzej Pietrasiewicz 						 bool true_last)
806e02f9aa6SVahram Aharonyan {
807e02f9aa6SVahram Aharonyan 	int dir_in = hs_ep->dir_in;
808e02f9aa6SVahram Aharonyan 	u32 mps = hs_ep->ep.maxpacket;
809e02f9aa6SVahram Aharonyan 	u32 maxsize = 0;
810e02f9aa6SVahram Aharonyan 	u32 offset = 0;
811e02f9aa6SVahram Aharonyan 	u32 mask = 0;
812e02f9aa6SVahram Aharonyan 	int i;
813e02f9aa6SVahram Aharonyan 
814e02f9aa6SVahram Aharonyan 	maxsize = dwc2_gadget_get_desc_params(hs_ep, &mask);
815e02f9aa6SVahram Aharonyan 
816e02f9aa6SVahram Aharonyan 	hs_ep->desc_count = (len / maxsize) +
817e02f9aa6SVahram Aharonyan 				((len % maxsize) ? 1 : 0);
818e02f9aa6SVahram Aharonyan 	if (len == 0)
819e02f9aa6SVahram Aharonyan 		hs_ep->desc_count = 1;
820e02f9aa6SVahram Aharonyan 
821e02f9aa6SVahram Aharonyan 	for (i = 0; i < hs_ep->desc_count; ++i) {
82210209abeSAndrzej Pietrasiewicz 		(*desc)->status = 0;
82310209abeSAndrzej Pietrasiewicz 		(*desc)->status |= (DEV_DMA_BUFF_STS_HBUSY
824e02f9aa6SVahram Aharonyan 				 << DEV_DMA_BUFF_STS_SHIFT);
825e02f9aa6SVahram Aharonyan 
826e02f9aa6SVahram Aharonyan 		if (len > maxsize) {
827e02f9aa6SVahram Aharonyan 			if (!hs_ep->index && !dir_in)
82810209abeSAndrzej Pietrasiewicz 				(*desc)->status |= (DEV_DMA_L | DEV_DMA_IOC);
829e02f9aa6SVahram Aharonyan 
83010209abeSAndrzej Pietrasiewicz 			(*desc)->status |=
83110209abeSAndrzej Pietrasiewicz 				maxsize << DEV_DMA_NBYTES_SHIFT & mask;
83210209abeSAndrzej Pietrasiewicz 			(*desc)->buf = dma_buff + offset;
833e02f9aa6SVahram Aharonyan 
834e02f9aa6SVahram Aharonyan 			len -= maxsize;
835e02f9aa6SVahram Aharonyan 			offset += maxsize;
836e02f9aa6SVahram Aharonyan 		} else {
83710209abeSAndrzej Pietrasiewicz 			if (true_last)
83810209abeSAndrzej Pietrasiewicz 				(*desc)->status |= (DEV_DMA_L | DEV_DMA_IOC);
839e02f9aa6SVahram Aharonyan 
840e02f9aa6SVahram Aharonyan 			if (dir_in)
84110209abeSAndrzej Pietrasiewicz 				(*desc)->status |= (len % mps) ? DEV_DMA_SHORT :
84210209abeSAndrzej Pietrasiewicz 					((hs_ep->send_zlp && true_last) ?
84310209abeSAndrzej Pietrasiewicz 					DEV_DMA_SHORT : 0);
844e02f9aa6SVahram Aharonyan 
84510209abeSAndrzej Pietrasiewicz 			(*desc)->status |=
846e02f9aa6SVahram Aharonyan 				len << DEV_DMA_NBYTES_SHIFT & mask;
84710209abeSAndrzej Pietrasiewicz 			(*desc)->buf = dma_buff + offset;
848e02f9aa6SVahram Aharonyan 		}
849e02f9aa6SVahram Aharonyan 
85010209abeSAndrzej Pietrasiewicz 		(*desc)->status &= ~DEV_DMA_BUFF_STS_MASK;
85110209abeSAndrzej Pietrasiewicz 		(*desc)->status |= (DEV_DMA_BUFF_STS_HREADY
852e02f9aa6SVahram Aharonyan 				 << DEV_DMA_BUFF_STS_SHIFT);
85310209abeSAndrzej Pietrasiewicz 		(*desc)++;
854e02f9aa6SVahram Aharonyan 	}
855e02f9aa6SVahram Aharonyan }
856e02f9aa6SVahram Aharonyan 
857540ccba0SVahram Aharonyan /*
85810209abeSAndrzej Pietrasiewicz  * dwc2_gadget_config_nonisoc_xfer_ddma - prepare non ISOC DMA desc chain.
85910209abeSAndrzej Pietrasiewicz  * @hs_ep: The endpoint
86010209abeSAndrzej Pietrasiewicz  * @ureq: Request to transfer
86110209abeSAndrzej Pietrasiewicz  * @offset: offset in bytes
86210209abeSAndrzej Pietrasiewicz  * @len: Length of the transfer
86310209abeSAndrzej Pietrasiewicz  *
86410209abeSAndrzej Pietrasiewicz  * This function will iterate over descriptor chain and fill its entries
86510209abeSAndrzej Pietrasiewicz  * with corresponding information based on transfer data.
86610209abeSAndrzej Pietrasiewicz  */
dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep * hs_ep,dma_addr_t dma_buff,unsigned int len)86710209abeSAndrzej Pietrasiewicz static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep,
868066cfd07SAndrzej Pietrasiewicz 						 dma_addr_t dma_buff,
86910209abeSAndrzej Pietrasiewicz 						 unsigned int len)
87010209abeSAndrzej Pietrasiewicz {
871066cfd07SAndrzej Pietrasiewicz 	struct usb_request *ureq = NULL;
87210209abeSAndrzej Pietrasiewicz 	struct dwc2_dma_desc *desc = hs_ep->desc_list;
87310209abeSAndrzej Pietrasiewicz 	struct scatterlist *sg;
87410209abeSAndrzej Pietrasiewicz 	int i;
87510209abeSAndrzej Pietrasiewicz 	u8 desc_count = 0;
87610209abeSAndrzej Pietrasiewicz 
877066cfd07SAndrzej Pietrasiewicz 	if (hs_ep->req)
878066cfd07SAndrzej Pietrasiewicz 		ureq = &hs_ep->req->req;
879066cfd07SAndrzej Pietrasiewicz 
88010209abeSAndrzej Pietrasiewicz 	/* non-DMA sg buffer */
881066cfd07SAndrzej Pietrasiewicz 	if (!ureq || !ureq->num_sgs) {
88210209abeSAndrzej Pietrasiewicz 		dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc,
883066cfd07SAndrzej Pietrasiewicz 			dma_buff, len, true);
88410209abeSAndrzej Pietrasiewicz 		return;
88510209abeSAndrzej Pietrasiewicz 	}
88610209abeSAndrzej Pietrasiewicz 
88710209abeSAndrzej Pietrasiewicz 	/* DMA sg buffer */
888*1134289bSPeng Hongchi 	for_each_sg(ureq->sg, sg, ureq->num_mapped_sgs, i) {
88910209abeSAndrzej Pietrasiewicz 		dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc,
89010209abeSAndrzej Pietrasiewicz 			sg_dma_address(sg) + sg->offset, sg_dma_len(sg),
891*1134289bSPeng Hongchi 			(i == (ureq->num_mapped_sgs - 1)));
89210209abeSAndrzej Pietrasiewicz 		desc_count += hs_ep->desc_count;
89310209abeSAndrzej Pietrasiewicz 	}
89410209abeSAndrzej Pietrasiewicz 
89510209abeSAndrzej Pietrasiewicz 	hs_ep->desc_count = desc_count;
89610209abeSAndrzej Pietrasiewicz }
89710209abeSAndrzej Pietrasiewicz 
89810209abeSAndrzej Pietrasiewicz /*
899540ccba0SVahram Aharonyan  * dwc2_gadget_fill_isoc_desc - fills next isochronous descriptor in chain.
900540ccba0SVahram Aharonyan  * @hs_ep: The isochronous endpoint.
901540ccba0SVahram Aharonyan  * @dma_buff: usb requests dma buffer.
902540ccba0SVahram Aharonyan  * @len: usb request transfer length.
903540ccba0SVahram Aharonyan  *
904729cac69SMinas Harutyunyan  * Fills next free descriptor with the data of the arrived usb request,
905540ccba0SVahram Aharonyan  * frame info, sets Last and IOC bits increments next_desc. If filled
906540ccba0SVahram Aharonyan  * descriptor is not the first one, removes L bit from the previous descriptor
907540ccba0SVahram Aharonyan  * status.
908540ccba0SVahram Aharonyan  */
dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep * hs_ep,dma_addr_t dma_buff,unsigned int len)909540ccba0SVahram Aharonyan static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep,
910540ccba0SVahram Aharonyan 				      dma_addr_t dma_buff, unsigned int len)
911540ccba0SVahram Aharonyan {
912540ccba0SVahram Aharonyan 	struct dwc2_dma_desc *desc;
913540ccba0SVahram Aharonyan 	struct dwc2_hsotg *hsotg = hs_ep->parent;
914540ccba0SVahram Aharonyan 	u32 index;
915540ccba0SVahram Aharonyan 	u32 mask = 0;
9161d8e5c00SMinas Harutyunyan 	u8 pid = 0;
917540ccba0SVahram Aharonyan 
918768a0741SLee Jones 	dwc2_gadget_get_desc_params(hs_ep, &mask);
919540ccba0SVahram Aharonyan 
920729cac69SMinas Harutyunyan 	index = hs_ep->next_desc;
921540ccba0SVahram Aharonyan 	desc = &hs_ep->desc_list[index];
922540ccba0SVahram Aharonyan 
923729cac69SMinas Harutyunyan 	/* Check if descriptor chain full */
924729cac69SMinas Harutyunyan 	if ((desc->status >> DEV_DMA_BUFF_STS_SHIFT) ==
925729cac69SMinas Harutyunyan 	    DEV_DMA_BUFF_STS_HREADY) {
926729cac69SMinas Harutyunyan 		dev_dbg(hsotg->dev, "%s: desc chain full\n", __func__);
927729cac69SMinas Harutyunyan 		return 1;
928729cac69SMinas Harutyunyan 	}
929729cac69SMinas Harutyunyan 
930540ccba0SVahram Aharonyan 	/* Clear L bit of previous desc if more than one entries in the chain */
931540ccba0SVahram Aharonyan 	if (hs_ep->next_desc)
932540ccba0SVahram Aharonyan 		hs_ep->desc_list[index - 1].status &= ~DEV_DMA_L;
933540ccba0SVahram Aharonyan 
934540ccba0SVahram Aharonyan 	dev_dbg(hsotg->dev, "%s: Filling ep %d, dir %s isoc desc # %d\n",
935540ccba0SVahram Aharonyan 		__func__, hs_ep->index, hs_ep->dir_in ? "in" : "out", index);
936540ccba0SVahram Aharonyan 
937540ccba0SVahram Aharonyan 	desc->status = 0;
938540ccba0SVahram Aharonyan 	desc->status |= (DEV_DMA_BUFF_STS_HBUSY	<< DEV_DMA_BUFF_STS_SHIFT);
939540ccba0SVahram Aharonyan 
940540ccba0SVahram Aharonyan 	desc->buf = dma_buff;
941540ccba0SVahram Aharonyan 	desc->status |= (DEV_DMA_L | DEV_DMA_IOC |
942540ccba0SVahram Aharonyan 			 ((len << DEV_DMA_NBYTES_SHIFT) & mask));
943540ccba0SVahram Aharonyan 
944540ccba0SVahram Aharonyan 	if (hs_ep->dir_in) {
9451d8e5c00SMinas Harutyunyan 		if (len)
9461d8e5c00SMinas Harutyunyan 			pid = DIV_ROUND_UP(len, hs_ep->ep.maxpacket);
9471d8e5c00SMinas Harutyunyan 		else
9481d8e5c00SMinas Harutyunyan 			pid = 1;
9491d8e5c00SMinas Harutyunyan 		desc->status |= ((pid << DEV_DMA_ISOC_PID_SHIFT) &
950540ccba0SVahram Aharonyan 				 DEV_DMA_ISOC_PID_MASK) |
951540ccba0SVahram Aharonyan 				((len % hs_ep->ep.maxpacket) ?
952540ccba0SVahram Aharonyan 				 DEV_DMA_SHORT : 0) |
953540ccba0SVahram Aharonyan 				((hs_ep->target_frame <<
954540ccba0SVahram Aharonyan 				  DEV_DMA_ISOC_FRNUM_SHIFT) &
955540ccba0SVahram Aharonyan 				 DEV_DMA_ISOC_FRNUM_MASK);
956540ccba0SVahram Aharonyan 	}
957540ccba0SVahram Aharonyan 
958540ccba0SVahram Aharonyan 	desc->status &= ~DEV_DMA_BUFF_STS_MASK;
959540ccba0SVahram Aharonyan 	desc->status |= (DEV_DMA_BUFF_STS_HREADY << DEV_DMA_BUFF_STS_SHIFT);
960540ccba0SVahram Aharonyan 
961729cac69SMinas Harutyunyan 	/* Increment frame number by interval for IN */
962729cac69SMinas Harutyunyan 	if (hs_ep->dir_in)
963729cac69SMinas Harutyunyan 		dwc2_gadget_incr_frame_num(hs_ep);
964729cac69SMinas Harutyunyan 
965540ccba0SVahram Aharonyan 	/* Update index of last configured entry in the chain */
966540ccba0SVahram Aharonyan 	hs_ep->next_desc++;
96754f37f56SMinas Harutyunyan 	if (hs_ep->next_desc >= MAX_DMA_DESC_NUM_HS_ISOC)
968729cac69SMinas Harutyunyan 		hs_ep->next_desc = 0;
969540ccba0SVahram Aharonyan 
970540ccba0SVahram Aharonyan 	return 0;
971540ccba0SVahram Aharonyan }
972540ccba0SVahram Aharonyan 
973540ccba0SVahram Aharonyan /*
974540ccba0SVahram Aharonyan  * dwc2_gadget_start_isoc_ddma - start isochronous transfer in DDMA
975540ccba0SVahram Aharonyan  * @hs_ep: The isochronous endpoint.
976540ccba0SVahram Aharonyan  *
977729cac69SMinas Harutyunyan  * Prepare descriptor chain for isochronous endpoints. Afterwards
978540ccba0SVahram Aharonyan  * write DMA address to HW and enable the endpoint.
979540ccba0SVahram Aharonyan  */
dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep * hs_ep)980540ccba0SVahram Aharonyan static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep)
981540ccba0SVahram Aharonyan {
982540ccba0SVahram Aharonyan 	struct dwc2_hsotg *hsotg = hs_ep->parent;
983540ccba0SVahram Aharonyan 	struct dwc2_hsotg_req *hs_req, *treq;
984540ccba0SVahram Aharonyan 	int index = hs_ep->index;
985540ccba0SVahram Aharonyan 	int ret;
986729cac69SMinas Harutyunyan 	int i;
987540ccba0SVahram Aharonyan 	u32 dma_reg;
988540ccba0SVahram Aharonyan 	u32 depctl;
989540ccba0SVahram Aharonyan 	u32 ctrl;
990729cac69SMinas Harutyunyan 	struct dwc2_dma_desc *desc;
991540ccba0SVahram Aharonyan 
992540ccba0SVahram Aharonyan 	if (list_empty(&hs_ep->queue)) {
9931ffba905SMinas Harutyunyan 		hs_ep->target_frame = TARGET_FRAME_INITIAL;
994540ccba0SVahram Aharonyan 		dev_dbg(hsotg->dev, "%s: No requests in queue\n", __func__);
995540ccba0SVahram Aharonyan 		return;
996540ccba0SVahram Aharonyan 	}
997540ccba0SVahram Aharonyan 
998729cac69SMinas Harutyunyan 	/* Initialize descriptor chain by Host Busy status */
99954f37f56SMinas Harutyunyan 	for (i = 0; i < MAX_DMA_DESC_NUM_HS_ISOC; i++) {
1000729cac69SMinas Harutyunyan 		desc = &hs_ep->desc_list[i];
1001729cac69SMinas Harutyunyan 		desc->status = 0;
1002729cac69SMinas Harutyunyan 		desc->status |= (DEV_DMA_BUFF_STS_HBUSY
1003729cac69SMinas Harutyunyan 				    << DEV_DMA_BUFF_STS_SHIFT);
1004729cac69SMinas Harutyunyan 	}
1005729cac69SMinas Harutyunyan 
1006729cac69SMinas Harutyunyan 	hs_ep->next_desc = 0;
1007540ccba0SVahram Aharonyan 	list_for_each_entry_safe(hs_req, treq, &hs_ep->queue, queue) {
100810209abeSAndrzej Pietrasiewicz 		dma_addr_t dma_addr = hs_req->req.dma;
100910209abeSAndrzej Pietrasiewicz 
101010209abeSAndrzej Pietrasiewicz 		if (hs_req->req.num_sgs) {
101110209abeSAndrzej Pietrasiewicz 			WARN_ON(hs_req->req.num_sgs > 1);
101210209abeSAndrzej Pietrasiewicz 			dma_addr = sg_dma_address(hs_req->req.sg);
101310209abeSAndrzej Pietrasiewicz 		}
101410209abeSAndrzej Pietrasiewicz 		ret = dwc2_gadget_fill_isoc_desc(hs_ep, dma_addr,
1015540ccba0SVahram Aharonyan 						 hs_req->req.length);
1016729cac69SMinas Harutyunyan 		if (ret)
1017540ccba0SVahram Aharonyan 			break;
1018540ccba0SVahram Aharonyan 	}
1019540ccba0SVahram Aharonyan 
1020729cac69SMinas Harutyunyan 	hs_ep->compl_desc = 0;
1021540ccba0SVahram Aharonyan 	depctl = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index);
1022540ccba0SVahram Aharonyan 	dma_reg = hs_ep->dir_in ? DIEPDMA(index) : DOEPDMA(index);
1023540ccba0SVahram Aharonyan 
1024540ccba0SVahram Aharonyan 	/* write descriptor chain address to control register */
1025f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, hs_ep->desc_list_dma, dma_reg);
1026540ccba0SVahram Aharonyan 
1027f25c42b8SGevorg Sahakyan 	ctrl = dwc2_readl(hsotg, depctl);
1028540ccba0SVahram Aharonyan 	ctrl |= DXEPCTL_EPENA | DXEPCTL_CNAK;
1029f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, ctrl, depctl);
1030540ccba0SVahram Aharonyan }
1031540ccba0SVahram Aharonyan 
103291bb163eSMinas Harutyunyan static bool dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep *hs_ep);
103391bb163eSMinas Harutyunyan static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg,
103491bb163eSMinas Harutyunyan 					struct dwc2_hsotg_ep *hs_ep,
103591bb163eSMinas Harutyunyan 				       struct dwc2_hsotg_req *hs_req,
103691bb163eSMinas Harutyunyan 				       int result);
103791bb163eSMinas Harutyunyan 
1038cf77b5fbSVahram Aharonyan /**
10391f91b4ccSFelipe Balbi  * dwc2_hsotg_start_req - start a USB request from an endpoint's queue
104047a1685fSDinh Nguyen  * @hsotg: The controller state.
104147a1685fSDinh Nguyen  * @hs_ep: The endpoint to process a request for
104247a1685fSDinh Nguyen  * @hs_req: The request to start.
104347a1685fSDinh Nguyen  * @continuing: True if we are doing more for the current request.
104447a1685fSDinh Nguyen  *
104547a1685fSDinh Nguyen  * Start the given request running by setting the endpoint registers
104647a1685fSDinh Nguyen  * appropriately, and writing any data to the FIFOs.
104747a1685fSDinh Nguyen  */
dwc2_hsotg_start_req(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep,struct dwc2_hsotg_req * hs_req,bool continuing)10481f91b4ccSFelipe Balbi static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
10491f91b4ccSFelipe Balbi 				 struct dwc2_hsotg_ep *hs_ep,
10501f91b4ccSFelipe Balbi 				struct dwc2_hsotg_req *hs_req,
105147a1685fSDinh Nguyen 				bool continuing)
105247a1685fSDinh Nguyen {
105347a1685fSDinh Nguyen 	struct usb_request *ureq = &hs_req->req;
105447a1685fSDinh Nguyen 	int index = hs_ep->index;
105547a1685fSDinh Nguyen 	int dir_in = hs_ep->dir_in;
105647a1685fSDinh Nguyen 	u32 epctrl_reg;
105747a1685fSDinh Nguyen 	u32 epsize_reg;
105847a1685fSDinh Nguyen 	u32 epsize;
105947a1685fSDinh Nguyen 	u32 ctrl;
10609da51974SJohn Youn 	unsigned int length;
10619da51974SJohn Youn 	unsigned int packets;
10629da51974SJohn Youn 	unsigned int maxreq;
1063aa3e8bc8SVahram Aharonyan 	unsigned int dma_reg;
106447a1685fSDinh Nguyen 
106547a1685fSDinh Nguyen 	if (index != 0) {
106647a1685fSDinh Nguyen 		if (hs_ep->req && !continuing) {
106747a1685fSDinh Nguyen 			dev_err(hsotg->dev, "%s: active request\n", __func__);
106847a1685fSDinh Nguyen 			WARN_ON(1);
106947a1685fSDinh Nguyen 			return;
107047a1685fSDinh Nguyen 		} else if (hs_ep->req != hs_req && continuing) {
107147a1685fSDinh Nguyen 			dev_err(hsotg->dev,
107247a1685fSDinh Nguyen 				"%s: continue different req\n", __func__);
107347a1685fSDinh Nguyen 			WARN_ON(1);
107447a1685fSDinh Nguyen 			return;
107547a1685fSDinh Nguyen 		}
107647a1685fSDinh Nguyen 	}
107747a1685fSDinh Nguyen 
1078aa3e8bc8SVahram Aharonyan 	dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index);
107947a1685fSDinh Nguyen 	epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
108047a1685fSDinh Nguyen 	epsize_reg = dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index);
108147a1685fSDinh Nguyen 
108247a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x, ep %d, dir %s\n",
1083f25c42b8SGevorg Sahakyan 		__func__, dwc2_readl(hsotg, epctrl_reg), index,
108447a1685fSDinh Nguyen 		hs_ep->dir_in ? "in" : "out");
108547a1685fSDinh Nguyen 
108647a1685fSDinh Nguyen 	/* If endpoint is stalled, we will restart request later */
1087f25c42b8SGevorg Sahakyan 	ctrl = dwc2_readl(hsotg, epctrl_reg);
108847a1685fSDinh Nguyen 
1089b2d4c54eSMian Yousaf Kaukab 	if (index && ctrl & DXEPCTL_STALL) {
109047a1685fSDinh Nguyen 		dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index);
109147a1685fSDinh Nguyen 		return;
109247a1685fSDinh Nguyen 	}
109347a1685fSDinh Nguyen 
109447a1685fSDinh Nguyen 	length = ureq->length - ureq->actual;
109547a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "ureq->length:%d ureq->actual:%d\n",
109647a1685fSDinh Nguyen 		ureq->length, ureq->actual);
109747a1685fSDinh Nguyen 
1098cf77b5fbSVahram Aharonyan 	if (!using_desc_dma(hsotg))
109947a1685fSDinh Nguyen 		maxreq = get_ep_limit(hs_ep);
1100cf77b5fbSVahram Aharonyan 	else
1101cf77b5fbSVahram Aharonyan 		maxreq = dwc2_gadget_get_chain_limit(hs_ep);
1102cf77b5fbSVahram Aharonyan 
110347a1685fSDinh Nguyen 	if (length > maxreq) {
110447a1685fSDinh Nguyen 		int round = maxreq % hs_ep->ep.maxpacket;
110547a1685fSDinh Nguyen 
110647a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s: length %d, max-req %d, r %d\n",
110747a1685fSDinh Nguyen 			__func__, length, maxreq, round);
110847a1685fSDinh Nguyen 
110947a1685fSDinh Nguyen 		/* round down to multiple of packets */
111047a1685fSDinh Nguyen 		if (round)
111147a1685fSDinh Nguyen 			maxreq -= round;
111247a1685fSDinh Nguyen 
111347a1685fSDinh Nguyen 		length = maxreq;
111447a1685fSDinh Nguyen 	}
111547a1685fSDinh Nguyen 
111647a1685fSDinh Nguyen 	if (length)
111747a1685fSDinh Nguyen 		packets = DIV_ROUND_UP(length, hs_ep->ep.maxpacket);
111847a1685fSDinh Nguyen 	else
111947a1685fSDinh Nguyen 		packets = 1;	/* send one packet if length is zero. */
112047a1685fSDinh Nguyen 
112147a1685fSDinh Nguyen 	if (dir_in && index != 0)
112247a1685fSDinh Nguyen 		if (hs_ep->isochronous)
112347a1685fSDinh Nguyen 			epsize = DXEPTSIZ_MC(packets);
112447a1685fSDinh Nguyen 		else
112547a1685fSDinh Nguyen 			epsize = DXEPTSIZ_MC(1);
112647a1685fSDinh Nguyen 	else
112747a1685fSDinh Nguyen 		epsize = 0;
112847a1685fSDinh Nguyen 
112947a1685fSDinh Nguyen 	/*
1130f71b5e25SMian Yousaf Kaukab 	 * zero length packet should be programmed on its own and should not
1131f71b5e25SMian Yousaf Kaukab 	 * be counted in DIEPTSIZ.PktCnt with other packets.
113247a1685fSDinh Nguyen 	 */
1133f71b5e25SMian Yousaf Kaukab 	if (dir_in && ureq->zero && !continuing) {
1134f71b5e25SMian Yousaf Kaukab 		/* Test if zlp is actually required. */
1135f71b5e25SMian Yousaf Kaukab 		if ((ureq->length >= hs_ep->ep.maxpacket) &&
1136f71b5e25SMian Yousaf Kaukab 		    !(ureq->length % hs_ep->ep.maxpacket))
11378a20fa45SMian Yousaf Kaukab 			hs_ep->send_zlp = 1;
113847a1685fSDinh Nguyen 	}
113947a1685fSDinh Nguyen 
114047a1685fSDinh Nguyen 	epsize |= DXEPTSIZ_PKTCNT(packets);
114147a1685fSDinh Nguyen 	epsize |= DXEPTSIZ_XFERSIZE(length);
114247a1685fSDinh Nguyen 
114347a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: %d@%d/%d, 0x%08x => 0x%08x\n",
114447a1685fSDinh Nguyen 		__func__, packets, length, ureq->length, epsize, epsize_reg);
114547a1685fSDinh Nguyen 
114647a1685fSDinh Nguyen 	/* store the request as the current one we're doing */
114747a1685fSDinh Nguyen 	hs_ep->req = hs_req;
114847a1685fSDinh Nguyen 
1149aa3e8bc8SVahram Aharonyan 	if (using_desc_dma(hsotg)) {
1150aa3e8bc8SVahram Aharonyan 		u32 offset = 0;
1151aa3e8bc8SVahram Aharonyan 		u32 mps = hs_ep->ep.maxpacket;
1152aa3e8bc8SVahram Aharonyan 
1153aa3e8bc8SVahram Aharonyan 		/* Adjust length: EP0 - MPS, other OUT EPs - multiple of MPS */
1154aa3e8bc8SVahram Aharonyan 		if (!dir_in) {
1155aa3e8bc8SVahram Aharonyan 			if (!index)
1156aa3e8bc8SVahram Aharonyan 				length = mps;
1157aa3e8bc8SVahram Aharonyan 			else if (length % mps)
1158aa3e8bc8SVahram Aharonyan 				length += (mps - (length % mps));
1159aa3e8bc8SVahram Aharonyan 		}
1160aa3e8bc8SVahram Aharonyan 
1161b2c586ebSMinas Harutyunyan 		if (continuing)
1162aa3e8bc8SVahram Aharonyan 			offset = ureq->actual;
1163aa3e8bc8SVahram Aharonyan 
1164aa3e8bc8SVahram Aharonyan 		/* Fill DDMA chain entries */
1165066cfd07SAndrzej Pietrasiewicz 		dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, ureq->dma + offset,
1166aa3e8bc8SVahram Aharonyan 						     length);
1167aa3e8bc8SVahram Aharonyan 
1168aa3e8bc8SVahram Aharonyan 		/* write descriptor chain address to control register */
1169f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, hs_ep->desc_list_dma, dma_reg);
1170aa3e8bc8SVahram Aharonyan 
1171aa3e8bc8SVahram Aharonyan 		dev_dbg(hsotg->dev, "%s: %08x pad => 0x%08x\n",
1172aa3e8bc8SVahram Aharonyan 			__func__, (u32)hs_ep->desc_list_dma, dma_reg);
1173aa3e8bc8SVahram Aharonyan 	} else {
117447a1685fSDinh Nguyen 		/* write size / packets */
1175f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, epsize, epsize_reg);
117647a1685fSDinh Nguyen 
1177729e6574SRazmik Karapetyan 		if (using_dma(hsotg) && !continuing && (length != 0)) {
117847a1685fSDinh Nguyen 			/*
1179aa3e8bc8SVahram Aharonyan 			 * write DMA address to control register, buffer
1180aa3e8bc8SVahram Aharonyan 			 * already synced by dwc2_hsotg_ep_queue().
118147a1685fSDinh Nguyen 			 */
118247a1685fSDinh Nguyen 
1183f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, ureq->dma, dma_reg);
118447a1685fSDinh Nguyen 
11850cc4cf6fSFabio Estevam 			dev_dbg(hsotg->dev, "%s: %pad => 0x%08x\n",
118647a1685fSDinh Nguyen 				__func__, &ureq->dma, dma_reg);
118747a1685fSDinh Nguyen 		}
1188aa3e8bc8SVahram Aharonyan 	}
118947a1685fSDinh Nguyen 
119091bb163eSMinas Harutyunyan 	if (hs_ep->isochronous) {
119191bb163eSMinas Harutyunyan 		if (!dwc2_gadget_target_frame_elapsed(hs_ep)) {
119291bb163eSMinas Harutyunyan 			if (hs_ep->interval == 1) {
1193837e9f00SVardan Mikayelyan 				if (hs_ep->target_frame & 0x1)
1194837e9f00SVardan Mikayelyan 					ctrl |= DXEPCTL_SETODDFR;
1195837e9f00SVardan Mikayelyan 				else
1196837e9f00SVardan Mikayelyan 					ctrl |= DXEPCTL_SETEVENFR;
1197837e9f00SVardan Mikayelyan 			}
119891bb163eSMinas Harutyunyan 			ctrl |= DXEPCTL_CNAK;
119991bb163eSMinas Harutyunyan 		} else {
12007ad4a0b1SMinas Harutyunyan 			hs_req->req.frame_number = hs_ep->target_frame;
12017ad4a0b1SMinas Harutyunyan 			hs_req->req.actual = 0;
120291bb163eSMinas Harutyunyan 			dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, -ENODATA);
120391bb163eSMinas Harutyunyan 			return;
120491bb163eSMinas Harutyunyan 		}
120591bb163eSMinas Harutyunyan 	}
1206837e9f00SVardan Mikayelyan 
120747a1685fSDinh Nguyen 	ctrl |= DXEPCTL_EPENA;	/* ensure ep enabled */
120847a1685fSDinh Nguyen 
1209fe0b94abSMian Yousaf Kaukab 	dev_dbg(hsotg->dev, "ep0 state:%d\n", hsotg->ep0_state);
121047a1685fSDinh Nguyen 
121147a1685fSDinh Nguyen 	/* For Setup request do not clear NAK */
1212fe0b94abSMian Yousaf Kaukab 	if (!(index == 0 && hsotg->ep0_state == DWC2_EP0_SETUP))
121347a1685fSDinh Nguyen 		ctrl |= DXEPCTL_CNAK;	/* clear NAK set by core */
121447a1685fSDinh Nguyen 
121547a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
1216f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, ctrl, epctrl_reg);
121747a1685fSDinh Nguyen 
121847a1685fSDinh Nguyen 	/*
121947a1685fSDinh Nguyen 	 * set these, it seems that DMA support increments past the end
122047a1685fSDinh Nguyen 	 * of the packet buffer so we need to calculate the length from
122147a1685fSDinh Nguyen 	 * this information.
122247a1685fSDinh Nguyen 	 */
122347a1685fSDinh Nguyen 	hs_ep->size_loaded = length;
122447a1685fSDinh Nguyen 	hs_ep->last_load = ureq->actual;
122547a1685fSDinh Nguyen 
122647a1685fSDinh Nguyen 	if (dir_in && !using_dma(hsotg)) {
122747a1685fSDinh Nguyen 		/* set these anyway, we may need them for non-periodic in */
122847a1685fSDinh Nguyen 		hs_ep->fifo_load = 0;
122947a1685fSDinh Nguyen 
12301f91b4ccSFelipe Balbi 		dwc2_hsotg_write_fifo(hsotg, hs_ep, hs_req);
123147a1685fSDinh Nguyen 	}
123247a1685fSDinh Nguyen 
123347a1685fSDinh Nguyen 	/*
123447a1685fSDinh Nguyen 	 * Note, trying to clear the NAK here causes problems with transmit
123547a1685fSDinh Nguyen 	 * on the S3C6400 ending up with the TXFIFO becoming full.
123647a1685fSDinh Nguyen 	 */
123747a1685fSDinh Nguyen 
123847a1685fSDinh Nguyen 	/* check ep is enabled */
1239f25c42b8SGevorg Sahakyan 	if (!(dwc2_readl(hsotg, epctrl_reg) & DXEPCTL_EPENA))
12401a0ed863SMian Yousaf Kaukab 		dev_dbg(hsotg->dev,
124147a1685fSDinh Nguyen 			"ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n",
1242f25c42b8SGevorg Sahakyan 			 index, dwc2_readl(hsotg, epctrl_reg));
124347a1685fSDinh Nguyen 
124447a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: DXEPCTL=0x%08x\n",
1245f25c42b8SGevorg Sahakyan 		__func__, dwc2_readl(hsotg, epctrl_reg));
124647a1685fSDinh Nguyen 
124747a1685fSDinh Nguyen 	/* enable ep interrupts */
12481f91b4ccSFelipe Balbi 	dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 1);
124947a1685fSDinh Nguyen }
125047a1685fSDinh Nguyen 
125147a1685fSDinh Nguyen /**
12521f91b4ccSFelipe Balbi  * dwc2_hsotg_map_dma - map the DMA memory being used for the request
125347a1685fSDinh Nguyen  * @hsotg: The device state.
125447a1685fSDinh Nguyen  * @hs_ep: The endpoint the request is on.
125547a1685fSDinh Nguyen  * @req: The request being processed.
125647a1685fSDinh Nguyen  *
125747a1685fSDinh Nguyen  * We've been asked to queue a request, so ensure that the memory buffer
125847a1685fSDinh Nguyen  * is correctly setup for DMA. If we've been passed an extant DMA address
125947a1685fSDinh Nguyen  * then ensure the buffer has been synced to memory. If our buffer has no
126047a1685fSDinh Nguyen  * DMA memory, then we map the memory and mark our request to allow us to
126147a1685fSDinh Nguyen  * cleanup on completion.
126247a1685fSDinh Nguyen  */
dwc2_hsotg_map_dma(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep,struct usb_request * req)12631f91b4ccSFelipe Balbi static int dwc2_hsotg_map_dma(struct dwc2_hsotg *hsotg,
12641f91b4ccSFelipe Balbi 			      struct dwc2_hsotg_ep *hs_ep,
126547a1685fSDinh Nguyen 			     struct usb_request *req)
126647a1685fSDinh Nguyen {
126747a1685fSDinh Nguyen 	int ret;
126847a1685fSDinh Nguyen 
126975a41ce4SPhil Elwell 	hs_ep->map_dir = hs_ep->dir_in;
127047a1685fSDinh Nguyen 	ret = usb_gadget_map_request(&hsotg->gadget, req, hs_ep->dir_in);
127147a1685fSDinh Nguyen 	if (ret)
127247a1685fSDinh Nguyen 		goto dma_error;
127347a1685fSDinh Nguyen 
127447a1685fSDinh Nguyen 	return 0;
127547a1685fSDinh Nguyen 
127647a1685fSDinh Nguyen dma_error:
127747a1685fSDinh Nguyen 	dev_err(hsotg->dev, "%s: failed to map buffer %p, %d bytes\n",
127847a1685fSDinh Nguyen 		__func__, req->buf, req->length);
127947a1685fSDinh Nguyen 
128047a1685fSDinh Nguyen 	return -EIO;
128147a1685fSDinh Nguyen }
128247a1685fSDinh Nguyen 
dwc2_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep,struct dwc2_hsotg_req * hs_req)12831f91b4ccSFelipe Balbi static int dwc2_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg,
1284b98866c2SJohn Youn 						 struct dwc2_hsotg_ep *hs_ep,
1285b98866c2SJohn Youn 						 struct dwc2_hsotg_req *hs_req)
12867d24c1b5SMian Yousaf Kaukab {
12877d24c1b5SMian Yousaf Kaukab 	void *req_buf = hs_req->req.buf;
12887d24c1b5SMian Yousaf Kaukab 
12897d24c1b5SMian Yousaf Kaukab 	/* If dma is not being used or buffer is aligned */
12907d24c1b5SMian Yousaf Kaukab 	if (!using_dma(hsotg) || !((long)req_buf & 3))
12917d24c1b5SMian Yousaf Kaukab 		return 0;
12927d24c1b5SMian Yousaf Kaukab 
12937d24c1b5SMian Yousaf Kaukab 	WARN_ON(hs_req->saved_req_buf);
12947d24c1b5SMian Yousaf Kaukab 
12957d24c1b5SMian Yousaf Kaukab 	dev_dbg(hsotg->dev, "%s: %s: buf=%p length=%d\n", __func__,
12967d24c1b5SMian Yousaf Kaukab 		hs_ep->ep.name, req_buf, hs_req->req.length);
12977d24c1b5SMian Yousaf Kaukab 
12987d24c1b5SMian Yousaf Kaukab 	hs_req->req.buf = kmalloc(hs_req->req.length, GFP_ATOMIC);
12997d24c1b5SMian Yousaf Kaukab 	if (!hs_req->req.buf) {
13007d24c1b5SMian Yousaf Kaukab 		hs_req->req.buf = req_buf;
13017d24c1b5SMian Yousaf Kaukab 		dev_err(hsotg->dev,
13027d24c1b5SMian Yousaf Kaukab 			"%s: unable to allocate memory for bounce buffer\n",
13037d24c1b5SMian Yousaf Kaukab 			__func__);
13047d24c1b5SMian Yousaf Kaukab 		return -ENOMEM;
13057d24c1b5SMian Yousaf Kaukab 	}
13067d24c1b5SMian Yousaf Kaukab 
13077d24c1b5SMian Yousaf Kaukab 	/* Save actual buffer */
13087d24c1b5SMian Yousaf Kaukab 	hs_req->saved_req_buf = req_buf;
13097d24c1b5SMian Yousaf Kaukab 
13107d24c1b5SMian Yousaf Kaukab 	if (hs_ep->dir_in)
13117d24c1b5SMian Yousaf Kaukab 		memcpy(hs_req->req.buf, req_buf, hs_req->req.length);
13127d24c1b5SMian Yousaf Kaukab 	return 0;
13137d24c1b5SMian Yousaf Kaukab }
13147d24c1b5SMian Yousaf Kaukab 
1315b98866c2SJohn Youn static void
dwc2_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep,struct dwc2_hsotg_req * hs_req)1316b98866c2SJohn Youn dwc2_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg,
1317b98866c2SJohn Youn 					 struct dwc2_hsotg_ep *hs_ep,
1318b98866c2SJohn Youn 					 struct dwc2_hsotg_req *hs_req)
13197d24c1b5SMian Yousaf Kaukab {
13207d24c1b5SMian Yousaf Kaukab 	/* If dma is not being used or buffer was aligned */
13217d24c1b5SMian Yousaf Kaukab 	if (!using_dma(hsotg) || !hs_req->saved_req_buf)
13227d24c1b5SMian Yousaf Kaukab 		return;
13237d24c1b5SMian Yousaf Kaukab 
13247d24c1b5SMian Yousaf Kaukab 	dev_dbg(hsotg->dev, "%s: %s: status=%d actual-length=%d\n", __func__,
13257d24c1b5SMian Yousaf Kaukab 		hs_ep->ep.name, hs_req->req.status, hs_req->req.actual);
13267d24c1b5SMian Yousaf Kaukab 
13277d24c1b5SMian Yousaf Kaukab 	/* Copy data from bounce buffer on successful out transfer */
13287d24c1b5SMian Yousaf Kaukab 	if (!hs_ep->dir_in && !hs_req->req.status)
13297d24c1b5SMian Yousaf Kaukab 		memcpy(hs_req->saved_req_buf, hs_req->req.buf,
13307d24c1b5SMian Yousaf Kaukab 		       hs_req->req.actual);
13317d24c1b5SMian Yousaf Kaukab 
13327d24c1b5SMian Yousaf Kaukab 	/* Free bounce buffer */
13337d24c1b5SMian Yousaf Kaukab 	kfree(hs_req->req.buf);
13347d24c1b5SMian Yousaf Kaukab 
13357d24c1b5SMian Yousaf Kaukab 	hs_req->req.buf = hs_req->saved_req_buf;
13367d24c1b5SMian Yousaf Kaukab 	hs_req->saved_req_buf = NULL;
13377d24c1b5SMian Yousaf Kaukab }
13387d24c1b5SMian Yousaf Kaukab 
1339381fc8f8SVardan Mikayelyan /**
1340381fc8f8SVardan Mikayelyan  * dwc2_gadget_target_frame_elapsed - Checks target frame
1341381fc8f8SVardan Mikayelyan  * @hs_ep: The driver endpoint to check
1342381fc8f8SVardan Mikayelyan  *
1343381fc8f8SVardan Mikayelyan  * Returns 1 if targeted frame elapsed. If returned 1 then we need to drop
1344381fc8f8SVardan Mikayelyan  * corresponding transfer.
1345381fc8f8SVardan Mikayelyan  */
dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep * hs_ep)1346381fc8f8SVardan Mikayelyan static bool dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep *hs_ep)
1347381fc8f8SVardan Mikayelyan {
1348381fc8f8SVardan Mikayelyan 	struct dwc2_hsotg *hsotg = hs_ep->parent;
1349381fc8f8SVardan Mikayelyan 	u32 target_frame = hs_ep->target_frame;
1350c7c24e7aSArtur Petrosyan 	u32 current_frame = hsotg->frame_number;
1351381fc8f8SVardan Mikayelyan 	bool frame_overrun = hs_ep->frame_overrun;
135291bb163eSMinas Harutyunyan 	u16 limit = DSTS_SOFFN_LIMIT;
135391bb163eSMinas Harutyunyan 
135491bb163eSMinas Harutyunyan 	if (hsotg->gadget.speed != USB_SPEED_HIGH)
135591bb163eSMinas Harutyunyan 		limit >>= 3;
1356381fc8f8SVardan Mikayelyan 
1357381fc8f8SVardan Mikayelyan 	if (!frame_overrun && current_frame >= target_frame)
1358381fc8f8SVardan Mikayelyan 		return true;
1359381fc8f8SVardan Mikayelyan 
1360381fc8f8SVardan Mikayelyan 	if (frame_overrun && current_frame >= target_frame &&
136191bb163eSMinas Harutyunyan 	    ((current_frame - target_frame) < limit / 2))
1362381fc8f8SVardan Mikayelyan 		return true;
1363381fc8f8SVardan Mikayelyan 
1364381fc8f8SVardan Mikayelyan 	return false;
1365381fc8f8SVardan Mikayelyan }
1366381fc8f8SVardan Mikayelyan 
1367e02f9aa6SVahram Aharonyan /*
1368e02f9aa6SVahram Aharonyan  * dwc2_gadget_set_ep0_desc_chain - Set EP's desc chain pointers
1369e02f9aa6SVahram Aharonyan  * @hsotg: The driver state
1370e02f9aa6SVahram Aharonyan  * @hs_ep: the ep descriptor chain is for
1371e02f9aa6SVahram Aharonyan  *
1372e02f9aa6SVahram Aharonyan  * Called to update EP0 structure's pointers depend on stage of
1373e02f9aa6SVahram Aharonyan  * control transfer.
1374e02f9aa6SVahram Aharonyan  */
dwc2_gadget_set_ep0_desc_chain(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep)1375e02f9aa6SVahram Aharonyan static int dwc2_gadget_set_ep0_desc_chain(struct dwc2_hsotg *hsotg,
1376e02f9aa6SVahram Aharonyan 					  struct dwc2_hsotg_ep *hs_ep)
1377e02f9aa6SVahram Aharonyan {
1378e02f9aa6SVahram Aharonyan 	switch (hsotg->ep0_state) {
1379e02f9aa6SVahram Aharonyan 	case DWC2_EP0_SETUP:
1380e02f9aa6SVahram Aharonyan 	case DWC2_EP0_STATUS_OUT:
1381e02f9aa6SVahram Aharonyan 		hs_ep->desc_list = hsotg->setup_desc[0];
1382e02f9aa6SVahram Aharonyan 		hs_ep->desc_list_dma = hsotg->setup_desc_dma[0];
1383e02f9aa6SVahram Aharonyan 		break;
1384e02f9aa6SVahram Aharonyan 	case DWC2_EP0_DATA_IN:
1385e02f9aa6SVahram Aharonyan 	case DWC2_EP0_STATUS_IN:
1386e02f9aa6SVahram Aharonyan 		hs_ep->desc_list = hsotg->ctrl_in_desc;
1387e02f9aa6SVahram Aharonyan 		hs_ep->desc_list_dma = hsotg->ctrl_in_desc_dma;
1388e02f9aa6SVahram Aharonyan 		break;
1389e02f9aa6SVahram Aharonyan 	case DWC2_EP0_DATA_OUT:
1390e02f9aa6SVahram Aharonyan 		hs_ep->desc_list = hsotg->ctrl_out_desc;
1391e02f9aa6SVahram Aharonyan 		hs_ep->desc_list_dma = hsotg->ctrl_out_desc_dma;
1392e02f9aa6SVahram Aharonyan 		break;
1393e02f9aa6SVahram Aharonyan 	default:
1394e02f9aa6SVahram Aharonyan 		dev_err(hsotg->dev, "invalid EP 0 state in queue %d\n",
1395e02f9aa6SVahram Aharonyan 			hsotg->ep0_state);
1396e02f9aa6SVahram Aharonyan 		return -EINVAL;
1397e02f9aa6SVahram Aharonyan 	}
1398e02f9aa6SVahram Aharonyan 
1399e02f9aa6SVahram Aharonyan 	return 0;
1400e02f9aa6SVahram Aharonyan }
1401e02f9aa6SVahram Aharonyan 
dwc2_hsotg_ep_queue(struct usb_ep * ep,struct usb_request * req,gfp_t gfp_flags)14021f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
140347a1685fSDinh Nguyen 			       gfp_t gfp_flags)
140447a1685fSDinh Nguyen {
14051f91b4ccSFelipe Balbi 	struct dwc2_hsotg_req *hs_req = our_req(req);
14061f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
1407941fcce4SDinh Nguyen 	struct dwc2_hsotg *hs = hs_ep->parent;
140847a1685fSDinh Nguyen 	bool first;
14097d24c1b5SMian Yousaf Kaukab 	int ret;
1410729cac69SMinas Harutyunyan 	u32 maxsize = 0;
1411729cac69SMinas Harutyunyan 	u32 mask = 0;
1412729cac69SMinas Harutyunyan 
141347a1685fSDinh Nguyen 
141447a1685fSDinh Nguyen 	dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
141547a1685fSDinh Nguyen 		ep->name, req, req->length, req->buf, req->no_interrupt,
141647a1685fSDinh Nguyen 		req->zero, req->short_not_ok);
141747a1685fSDinh Nguyen 
14185d69a3b5SMinas Harutyunyan 	if (hs->lx_state == DWC2_L1) {
14195d69a3b5SMinas Harutyunyan 		dwc2_wakeup_from_lpm_l1(hs, true);
14205d69a3b5SMinas Harutyunyan 	}
14215d69a3b5SMinas Harutyunyan 
14227ababa92SGregory Herrero 	/* Prevent new request submission when controller is suspended */
142388b02f2cSGrigor Tovmasyan 	if (hs->lx_state != DWC2_L0) {
142488b02f2cSGrigor Tovmasyan 		dev_dbg(hs->dev, "%s: submit request only in active state\n",
14257ababa92SGregory Herrero 			__func__);
14267ababa92SGregory Herrero 		return -EAGAIN;
14277ababa92SGregory Herrero 	}
14287ababa92SGregory Herrero 
142947a1685fSDinh Nguyen 	/* initialise status of the request */
143047a1685fSDinh Nguyen 	INIT_LIST_HEAD(&hs_req->queue);
143147a1685fSDinh Nguyen 	req->actual = 0;
143247a1685fSDinh Nguyen 	req->status = -EINPROGRESS;
143347a1685fSDinh Nguyen 
1434860ef6cdSMinas Harutyunyan 	/* Don't queue ISOC request if length greater than mps*mc */
1435860ef6cdSMinas Harutyunyan 	if (hs_ep->isochronous &&
1436860ef6cdSMinas Harutyunyan 	    req->length > (hs_ep->mc * hs_ep->ep.maxpacket)) {
1437860ef6cdSMinas Harutyunyan 		dev_err(hs->dev, "req length > maxpacket*mc\n");
1438860ef6cdSMinas Harutyunyan 		return -EINVAL;
1439860ef6cdSMinas Harutyunyan 	}
1440860ef6cdSMinas Harutyunyan 
1441729cac69SMinas Harutyunyan 	/* In DDMA mode for ISOC's don't queue request if length greater
1442729cac69SMinas Harutyunyan 	 * than descriptor limits.
1443729cac69SMinas Harutyunyan 	 */
1444729cac69SMinas Harutyunyan 	if (using_desc_dma(hs) && hs_ep->isochronous) {
1445729cac69SMinas Harutyunyan 		maxsize = dwc2_gadget_get_desc_params(hs_ep, &mask);
1446729cac69SMinas Harutyunyan 		if (hs_ep->dir_in && req->length > maxsize) {
1447729cac69SMinas Harutyunyan 			dev_err(hs->dev, "wrong length %d (maxsize=%d)\n",
1448729cac69SMinas Harutyunyan 				req->length, maxsize);
1449729cac69SMinas Harutyunyan 			return -EINVAL;
1450729cac69SMinas Harutyunyan 		}
1451729cac69SMinas Harutyunyan 
1452729cac69SMinas Harutyunyan 		if (!hs_ep->dir_in && req->length > hs_ep->ep.maxpacket) {
1453729cac69SMinas Harutyunyan 			dev_err(hs->dev, "ISOC OUT: wrong length %d (mps=%d)\n",
1454729cac69SMinas Harutyunyan 				req->length, hs_ep->ep.maxpacket);
1455729cac69SMinas Harutyunyan 			return -EINVAL;
1456729cac69SMinas Harutyunyan 		}
1457729cac69SMinas Harutyunyan 	}
1458729cac69SMinas Harutyunyan 
14591f91b4ccSFelipe Balbi 	ret = dwc2_hsotg_handle_unaligned_buf_start(hs, hs_ep, hs_req);
14607d24c1b5SMian Yousaf Kaukab 	if (ret)
14617d24c1b5SMian Yousaf Kaukab 		return ret;
14627d24c1b5SMian Yousaf Kaukab 
146347a1685fSDinh Nguyen 	/* if we're using DMA, sync the buffers as necessary */
146447a1685fSDinh Nguyen 	if (using_dma(hs)) {
14651f91b4ccSFelipe Balbi 		ret = dwc2_hsotg_map_dma(hs, hs_ep, req);
146647a1685fSDinh Nguyen 		if (ret)
146747a1685fSDinh Nguyen 			return ret;
146847a1685fSDinh Nguyen 	}
1469e02f9aa6SVahram Aharonyan 	/* If using descriptor DMA configure EP0 descriptor chain pointers */
1470e02f9aa6SVahram Aharonyan 	if (using_desc_dma(hs) && !hs_ep->index) {
1471e02f9aa6SVahram Aharonyan 		ret = dwc2_gadget_set_ep0_desc_chain(hs, hs_ep);
1472e02f9aa6SVahram Aharonyan 		if (ret)
1473e02f9aa6SVahram Aharonyan 			return ret;
1474e02f9aa6SVahram Aharonyan 	}
147547a1685fSDinh Nguyen 
147647a1685fSDinh Nguyen 	first = list_empty(&hs_ep->queue);
147747a1685fSDinh Nguyen 	list_add_tail(&hs_req->queue, &hs_ep->queue);
147847a1685fSDinh Nguyen 
1479540ccba0SVahram Aharonyan 	/*
1480540ccba0SVahram Aharonyan 	 * Handle DDMA isochronous transfers separately - just add new entry
1481729cac69SMinas Harutyunyan 	 * to the descriptor chain.
1482540ccba0SVahram Aharonyan 	 * Transfer will be started once SW gets either one of NAK or
1483540ccba0SVahram Aharonyan 	 * OutTknEpDis interrupts.
1484540ccba0SVahram Aharonyan 	 */
1485729cac69SMinas Harutyunyan 	if (using_desc_dma(hs) && hs_ep->isochronous) {
1486729cac69SMinas Harutyunyan 		if (hs_ep->target_frame != TARGET_FRAME_INITIAL) {
148710209abeSAndrzej Pietrasiewicz 			dma_addr_t dma_addr = hs_req->req.dma;
148810209abeSAndrzej Pietrasiewicz 
148910209abeSAndrzej Pietrasiewicz 			if (hs_req->req.num_sgs) {
149010209abeSAndrzej Pietrasiewicz 				WARN_ON(hs_req->req.num_sgs > 1);
149110209abeSAndrzej Pietrasiewicz 				dma_addr = sg_dma_address(hs_req->req.sg);
149210209abeSAndrzej Pietrasiewicz 			}
149310209abeSAndrzej Pietrasiewicz 			dwc2_gadget_fill_isoc_desc(hs_ep, dma_addr,
1494540ccba0SVahram Aharonyan 						   hs_req->req.length);
1495729cac69SMinas Harutyunyan 		}
1496540ccba0SVahram Aharonyan 		return 0;
1497540ccba0SVahram Aharonyan 	}
1498540ccba0SVahram Aharonyan 
1499b4c53b4aSMinas Harutyunyan 	/* Change EP direction if status phase request is after data out */
1500b4c53b4aSMinas Harutyunyan 	if (!hs_ep->index && !req->length && !hs_ep->dir_in &&
1501b4c53b4aSMinas Harutyunyan 	    hs->ep0_state == DWC2_EP0_DATA_OUT)
1502b4c53b4aSMinas Harutyunyan 		hs_ep->dir_in = 1;
1503b4c53b4aSMinas Harutyunyan 
1504837e9f00SVardan Mikayelyan 	if (first) {
1505837e9f00SVardan Mikayelyan 		if (!hs_ep->isochronous) {
15061f91b4ccSFelipe Balbi 			dwc2_hsotg_start_req(hs, hs_ep, hs_req, false);
1507837e9f00SVardan Mikayelyan 			return 0;
1508837e9f00SVardan Mikayelyan 		}
150947a1685fSDinh Nguyen 
1510c7c24e7aSArtur Petrosyan 		/* Update current frame number value. */
1511c7c24e7aSArtur Petrosyan 		hs->frame_number = dwc2_hsotg_read_frameno(hs);
1512c7c24e7aSArtur Petrosyan 		while (dwc2_gadget_target_frame_elapsed(hs_ep)) {
1513837e9f00SVardan Mikayelyan 			dwc2_gadget_incr_frame_num(hs_ep);
1514c7c24e7aSArtur Petrosyan 			/* Update current frame number value once more as it
1515c7c24e7aSArtur Petrosyan 			 * changes here.
1516c7c24e7aSArtur Petrosyan 			 */
1517c7c24e7aSArtur Petrosyan 			hs->frame_number = dwc2_hsotg_read_frameno(hs);
1518c7c24e7aSArtur Petrosyan 		}
1519837e9f00SVardan Mikayelyan 
1520837e9f00SVardan Mikayelyan 		if (hs_ep->target_frame != TARGET_FRAME_INITIAL)
1521837e9f00SVardan Mikayelyan 			dwc2_hsotg_start_req(hs, hs_ep, hs_req, false);
1522837e9f00SVardan Mikayelyan 	}
152347a1685fSDinh Nguyen 	return 0;
152447a1685fSDinh Nguyen }
152547a1685fSDinh Nguyen 
dwc2_hsotg_ep_queue_lock(struct usb_ep * ep,struct usb_request * req,gfp_t gfp_flags)15261f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req,
152747a1685fSDinh Nguyen 				    gfp_t gfp_flags)
152847a1685fSDinh Nguyen {
15291f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
1530941fcce4SDinh Nguyen 	struct dwc2_hsotg *hs = hs_ep->parent;
15318879904bSJohan Hovold 	unsigned long flags;
15328879904bSJohan Hovold 	int ret;
153347a1685fSDinh Nguyen 
153447a1685fSDinh Nguyen 	spin_lock_irqsave(&hs->lock, flags);
15351f91b4ccSFelipe Balbi 	ret = dwc2_hsotg_ep_queue(ep, req, gfp_flags);
153647a1685fSDinh Nguyen 	spin_unlock_irqrestore(&hs->lock, flags);
153747a1685fSDinh Nguyen 
153847a1685fSDinh Nguyen 	return ret;
153947a1685fSDinh Nguyen }
154047a1685fSDinh Nguyen 
dwc2_hsotg_ep_free_request(struct usb_ep * ep,struct usb_request * req)15411f91b4ccSFelipe Balbi static void dwc2_hsotg_ep_free_request(struct usb_ep *ep,
154247a1685fSDinh Nguyen 				       struct usb_request *req)
154347a1685fSDinh Nguyen {
15441f91b4ccSFelipe Balbi 	struct dwc2_hsotg_req *hs_req = our_req(req);
154547a1685fSDinh Nguyen 
154647a1685fSDinh Nguyen 	kfree(hs_req);
154747a1685fSDinh Nguyen }
154847a1685fSDinh Nguyen 
154947a1685fSDinh Nguyen /**
15501f91b4ccSFelipe Balbi  * dwc2_hsotg_complete_oursetup - setup completion callback
155147a1685fSDinh Nguyen  * @ep: The endpoint the request was on.
155247a1685fSDinh Nguyen  * @req: The request completed.
155347a1685fSDinh Nguyen  *
155447a1685fSDinh Nguyen  * Called on completion of any requests the driver itself
155547a1685fSDinh Nguyen  * submitted that need cleaning up.
155647a1685fSDinh Nguyen  */
dwc2_hsotg_complete_oursetup(struct usb_ep * ep,struct usb_request * req)15571f91b4ccSFelipe Balbi static void dwc2_hsotg_complete_oursetup(struct usb_ep *ep,
155847a1685fSDinh Nguyen 					 struct usb_request *req)
155947a1685fSDinh Nguyen {
15601f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
1561941fcce4SDinh Nguyen 	struct dwc2_hsotg *hsotg = hs_ep->parent;
156247a1685fSDinh Nguyen 
156347a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: ep %p, req %p\n", __func__, ep, req);
156447a1685fSDinh Nguyen 
15651f91b4ccSFelipe Balbi 	dwc2_hsotg_ep_free_request(ep, req);
156647a1685fSDinh Nguyen }
156747a1685fSDinh Nguyen 
156847a1685fSDinh Nguyen /**
156947a1685fSDinh Nguyen  * ep_from_windex - convert control wIndex value to endpoint
157047a1685fSDinh Nguyen  * @hsotg: The driver state.
157147a1685fSDinh Nguyen  * @windex: The control request wIndex field (in host order).
157247a1685fSDinh Nguyen  *
157347a1685fSDinh Nguyen  * Convert the given wIndex into a pointer to an driver endpoint
157447a1685fSDinh Nguyen  * structure, or return NULL if it is not a valid endpoint.
157547a1685fSDinh Nguyen  */
ep_from_windex(struct dwc2_hsotg * hsotg,u32 windex)15761f91b4ccSFelipe Balbi static struct dwc2_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
157747a1685fSDinh Nguyen 					    u32 windex)
157847a1685fSDinh Nguyen {
157947a1685fSDinh Nguyen 	int dir = (windex & USB_DIR_IN) ? 1 : 0;
158047a1685fSDinh Nguyen 	int idx = windex & 0x7F;
158147a1685fSDinh Nguyen 
158247a1685fSDinh Nguyen 	if (windex >= 0x100)
158347a1685fSDinh Nguyen 		return NULL;
158447a1685fSDinh Nguyen 
158547a1685fSDinh Nguyen 	if (idx > hsotg->num_of_eps)
158647a1685fSDinh Nguyen 		return NULL;
158747a1685fSDinh Nguyen 
1588f670e9f9SHeiko Stuebner 	return index_to_ep(hsotg, idx, dir);
158947a1685fSDinh Nguyen }
159047a1685fSDinh Nguyen 
159147a1685fSDinh Nguyen /**
15921f91b4ccSFelipe Balbi  * dwc2_hsotg_set_test_mode - Enable usb Test Modes
15939e14d0a5SGregory Herrero  * @hsotg: The driver state.
15949e14d0a5SGregory Herrero  * @testmode: requested usb test mode
15959e14d0a5SGregory Herrero  * Enable usb Test Mode requested by the Host.
15969e14d0a5SGregory Herrero  */
dwc2_hsotg_set_test_mode(struct dwc2_hsotg * hsotg,int testmode)15971f91b4ccSFelipe Balbi int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode)
15989e14d0a5SGregory Herrero {
1599f25c42b8SGevorg Sahakyan 	int dctl = dwc2_readl(hsotg, DCTL);
16009e14d0a5SGregory Herrero 
16019e14d0a5SGregory Herrero 	dctl &= ~DCTL_TSTCTL_MASK;
16029e14d0a5SGregory Herrero 	switch (testmode) {
160362fb45d3SGreg Kroah-Hartman 	case USB_TEST_J:
160462fb45d3SGreg Kroah-Hartman 	case USB_TEST_K:
160562fb45d3SGreg Kroah-Hartman 	case USB_TEST_SE0_NAK:
160662fb45d3SGreg Kroah-Hartman 	case USB_TEST_PACKET:
160762fb45d3SGreg Kroah-Hartman 	case USB_TEST_FORCE_ENABLE:
16089e14d0a5SGregory Herrero 		dctl |= testmode << DCTL_TSTCTL_SHIFT;
16099e14d0a5SGregory Herrero 		break;
16109e14d0a5SGregory Herrero 	default:
16119e14d0a5SGregory Herrero 		return -EINVAL;
16129e14d0a5SGregory Herrero 	}
1613f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, dctl, DCTL);
16149e14d0a5SGregory Herrero 	return 0;
16159e14d0a5SGregory Herrero }
16169e14d0a5SGregory Herrero 
16179e14d0a5SGregory Herrero /**
16181f91b4ccSFelipe Balbi  * dwc2_hsotg_send_reply - send reply to control request
161947a1685fSDinh Nguyen  * @hsotg: The device state
162047a1685fSDinh Nguyen  * @ep: Endpoint 0
162147a1685fSDinh Nguyen  * @buff: Buffer for request
162247a1685fSDinh Nguyen  * @length: Length of reply.
162347a1685fSDinh Nguyen  *
162447a1685fSDinh Nguyen  * Create a request and queue it on the given endpoint. This is useful as
162547a1685fSDinh Nguyen  * an internal method of sending replies to certain control requests, etc.
162647a1685fSDinh Nguyen  */
dwc2_hsotg_send_reply(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * ep,void * buff,int length)16271f91b4ccSFelipe Balbi static int dwc2_hsotg_send_reply(struct dwc2_hsotg *hsotg,
16281f91b4ccSFelipe Balbi 				 struct dwc2_hsotg_ep *ep,
162947a1685fSDinh Nguyen 				void *buff,
163047a1685fSDinh Nguyen 				int length)
163147a1685fSDinh Nguyen {
163247a1685fSDinh Nguyen 	struct usb_request *req;
163347a1685fSDinh Nguyen 	int ret;
163447a1685fSDinh Nguyen 
163547a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: buff %p, len %d\n", __func__, buff, length);
163647a1685fSDinh Nguyen 
16371f91b4ccSFelipe Balbi 	req = dwc2_hsotg_ep_alloc_request(&ep->ep, GFP_ATOMIC);
163847a1685fSDinh Nguyen 	hsotg->ep0_reply = req;
163947a1685fSDinh Nguyen 	if (!req) {
164047a1685fSDinh Nguyen 		dev_warn(hsotg->dev, "%s: cannot alloc req\n", __func__);
164147a1685fSDinh Nguyen 		return -ENOMEM;
164247a1685fSDinh Nguyen 	}
164347a1685fSDinh Nguyen 
164447a1685fSDinh Nguyen 	req->buf = hsotg->ep0_buff;
164547a1685fSDinh Nguyen 	req->length = length;
1646f71b5e25SMian Yousaf Kaukab 	/*
1647f71b5e25SMian Yousaf Kaukab 	 * zero flag is for sending zlp in DATA IN stage. It has no impact on
1648f71b5e25SMian Yousaf Kaukab 	 * STATUS stage.
1649f71b5e25SMian Yousaf Kaukab 	 */
1650f71b5e25SMian Yousaf Kaukab 	req->zero = 0;
16511f91b4ccSFelipe Balbi 	req->complete = dwc2_hsotg_complete_oursetup;
165247a1685fSDinh Nguyen 
165347a1685fSDinh Nguyen 	if (length)
165447a1685fSDinh Nguyen 		memcpy(req->buf, buff, length);
165547a1685fSDinh Nguyen 
16561f91b4ccSFelipe Balbi 	ret = dwc2_hsotg_ep_queue(&ep->ep, req, GFP_ATOMIC);
165747a1685fSDinh Nguyen 	if (ret) {
165847a1685fSDinh Nguyen 		dev_warn(hsotg->dev, "%s: cannot queue req\n", __func__);
165947a1685fSDinh Nguyen 		return ret;
166047a1685fSDinh Nguyen 	}
166147a1685fSDinh Nguyen 
166247a1685fSDinh Nguyen 	return 0;
166347a1685fSDinh Nguyen }
166447a1685fSDinh Nguyen 
166547a1685fSDinh Nguyen /**
16661f91b4ccSFelipe Balbi  * dwc2_hsotg_process_req_status - process request GET_STATUS
166747a1685fSDinh Nguyen  * @hsotg: The device state
166847a1685fSDinh Nguyen  * @ctrl: USB control request
166947a1685fSDinh Nguyen  */
dwc2_hsotg_process_req_status(struct dwc2_hsotg * hsotg,struct usb_ctrlrequest * ctrl)16701f91b4ccSFelipe Balbi static int dwc2_hsotg_process_req_status(struct dwc2_hsotg *hsotg,
167147a1685fSDinh Nguyen 					 struct usb_ctrlrequest *ctrl)
167247a1685fSDinh Nguyen {
16731f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0];
16741f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *ep;
167547a1685fSDinh Nguyen 	__le16 reply;
16769a0d6f7cSMinas Harutyunyan 	u16 status;
167747a1685fSDinh Nguyen 	int ret;
167847a1685fSDinh Nguyen 
167947a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: USB_REQ_GET_STATUS\n", __func__);
168047a1685fSDinh Nguyen 
168147a1685fSDinh Nguyen 	if (!ep0->dir_in) {
168247a1685fSDinh Nguyen 		dev_warn(hsotg->dev, "%s: direction out?\n", __func__);
168347a1685fSDinh Nguyen 		return -EINVAL;
168447a1685fSDinh Nguyen 	}
168547a1685fSDinh Nguyen 
168647a1685fSDinh Nguyen 	switch (ctrl->bRequestType & USB_RECIP_MASK) {
168747a1685fSDinh Nguyen 	case USB_RECIP_DEVICE:
16881a0808cbSJohn Keeping 		status = hsotg->gadget.is_selfpowered <<
16891a0808cbSJohn Keeping 			 USB_DEVICE_SELF_POWERED;
16909a0d6f7cSMinas Harutyunyan 		status |= hsotg->remote_wakeup_allowed <<
16919a0d6f7cSMinas Harutyunyan 			  USB_DEVICE_REMOTE_WAKEUP;
16929a0d6f7cSMinas Harutyunyan 		reply = cpu_to_le16(status);
169347a1685fSDinh Nguyen 		break;
169447a1685fSDinh Nguyen 
169547a1685fSDinh Nguyen 	case USB_RECIP_INTERFACE:
169647a1685fSDinh Nguyen 		/* currently, the data result should be zero */
169747a1685fSDinh Nguyen 		reply = cpu_to_le16(0);
169847a1685fSDinh Nguyen 		break;
169947a1685fSDinh Nguyen 
170047a1685fSDinh Nguyen 	case USB_RECIP_ENDPOINT:
170147a1685fSDinh Nguyen 		ep = ep_from_windex(hsotg, le16_to_cpu(ctrl->wIndex));
170247a1685fSDinh Nguyen 		if (!ep)
170347a1685fSDinh Nguyen 			return -ENOENT;
170447a1685fSDinh Nguyen 
170547a1685fSDinh Nguyen 		reply = cpu_to_le16(ep->halted ? 1 : 0);
170647a1685fSDinh Nguyen 		break;
170747a1685fSDinh Nguyen 
170847a1685fSDinh Nguyen 	default:
170947a1685fSDinh Nguyen 		return 0;
171047a1685fSDinh Nguyen 	}
171147a1685fSDinh Nguyen 
171247a1685fSDinh Nguyen 	if (le16_to_cpu(ctrl->wLength) != 2)
171347a1685fSDinh Nguyen 		return -EINVAL;
171447a1685fSDinh Nguyen 
17151f91b4ccSFelipe Balbi 	ret = dwc2_hsotg_send_reply(hsotg, ep0, &reply, 2);
171647a1685fSDinh Nguyen 	if (ret) {
171747a1685fSDinh Nguyen 		dev_err(hsotg->dev, "%s: failed to send reply\n", __func__);
171847a1685fSDinh Nguyen 		return ret;
171947a1685fSDinh Nguyen 	}
172047a1685fSDinh Nguyen 
172147a1685fSDinh Nguyen 	return 1;
172247a1685fSDinh Nguyen }
172347a1685fSDinh Nguyen 
172451da43b5SVahram Aharonyan static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now);
172547a1685fSDinh Nguyen 
172647a1685fSDinh Nguyen /**
172747a1685fSDinh Nguyen  * get_ep_head - return the first request on the endpoint
172847a1685fSDinh Nguyen  * @hs_ep: The controller endpoint to get
172947a1685fSDinh Nguyen  *
173047a1685fSDinh Nguyen  * Get the first request on the endpoint.
173147a1685fSDinh Nguyen  */
get_ep_head(struct dwc2_hsotg_ep * hs_ep)17321f91b4ccSFelipe Balbi static struct dwc2_hsotg_req *get_ep_head(struct dwc2_hsotg_ep *hs_ep)
173347a1685fSDinh Nguyen {
1734ffc4b406SMasahiro Yamada 	return list_first_entry_or_null(&hs_ep->queue, struct dwc2_hsotg_req,
1735ffc4b406SMasahiro Yamada 					queue);
173647a1685fSDinh Nguyen }
173747a1685fSDinh Nguyen 
173847a1685fSDinh Nguyen /**
173941cc4cd2SVardan Mikayelyan  * dwc2_gadget_start_next_request - Starts next request from ep queue
174041cc4cd2SVardan Mikayelyan  * @hs_ep: Endpoint structure
174141cc4cd2SVardan Mikayelyan  *
174241cc4cd2SVardan Mikayelyan  * If queue is empty and EP is ISOC-OUT - unmasks OUTTKNEPDIS which is masked
174341cc4cd2SVardan Mikayelyan  * in its handler. Hence we need to unmask it here to be able to do
174441cc4cd2SVardan Mikayelyan  * resynchronization.
174541cc4cd2SVardan Mikayelyan  */
dwc2_gadget_start_next_request(struct dwc2_hsotg_ep * hs_ep)174641cc4cd2SVardan Mikayelyan static void dwc2_gadget_start_next_request(struct dwc2_hsotg_ep *hs_ep)
174741cc4cd2SVardan Mikayelyan {
174841cc4cd2SVardan Mikayelyan 	struct dwc2_hsotg *hsotg = hs_ep->parent;
174941cc4cd2SVardan Mikayelyan 	int dir_in = hs_ep->dir_in;
175041cc4cd2SVardan Mikayelyan 	struct dwc2_hsotg_req *hs_req;
175141cc4cd2SVardan Mikayelyan 
175241cc4cd2SVardan Mikayelyan 	if (!list_empty(&hs_ep->queue)) {
175341cc4cd2SVardan Mikayelyan 		hs_req = get_ep_head(hs_ep);
175441cc4cd2SVardan Mikayelyan 		dwc2_hsotg_start_req(hsotg, hs_ep, hs_req, false);
175541cc4cd2SVardan Mikayelyan 		return;
175641cc4cd2SVardan Mikayelyan 	}
175741cc4cd2SVardan Mikayelyan 	if (!hs_ep->isochronous)
175841cc4cd2SVardan Mikayelyan 		return;
175941cc4cd2SVardan Mikayelyan 
176041cc4cd2SVardan Mikayelyan 	if (dir_in) {
176141cc4cd2SVardan Mikayelyan 		dev_dbg(hsotg->dev, "%s: No more ISOC-IN requests\n",
176241cc4cd2SVardan Mikayelyan 			__func__);
176341cc4cd2SVardan Mikayelyan 	} else {
176441cc4cd2SVardan Mikayelyan 		dev_dbg(hsotg->dev, "%s: No more ISOC-OUT requests\n",
176541cc4cd2SVardan Mikayelyan 			__func__);
176641cc4cd2SVardan Mikayelyan 	}
176741cc4cd2SVardan Mikayelyan }
176841cc4cd2SVardan Mikayelyan 
176941cc4cd2SVardan Mikayelyan /**
17701f91b4ccSFelipe Balbi  * dwc2_hsotg_process_req_feature - process request {SET,CLEAR}_FEATURE
177147a1685fSDinh Nguyen  * @hsotg: The device state
177247a1685fSDinh Nguyen  * @ctrl: USB control request
177347a1685fSDinh Nguyen  */
dwc2_hsotg_process_req_feature(struct dwc2_hsotg * hsotg,struct usb_ctrlrequest * ctrl)17741f91b4ccSFelipe Balbi static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
177547a1685fSDinh Nguyen 					  struct usb_ctrlrequest *ctrl)
177647a1685fSDinh Nguyen {
17771f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0];
17781f91b4ccSFelipe Balbi 	struct dwc2_hsotg_req *hs_req;
177947a1685fSDinh Nguyen 	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
17801f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *ep;
178147a1685fSDinh Nguyen 	int ret;
178247a1685fSDinh Nguyen 	bool halted;
17839e14d0a5SGregory Herrero 	u32 recip;
17849e14d0a5SGregory Herrero 	u32 wValue;
17859e14d0a5SGregory Herrero 	u32 wIndex;
178647a1685fSDinh Nguyen 
178747a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: %s_FEATURE\n",
178847a1685fSDinh Nguyen 		__func__, set ? "SET" : "CLEAR");
178947a1685fSDinh Nguyen 
17909e14d0a5SGregory Herrero 	wValue = le16_to_cpu(ctrl->wValue);
17919e14d0a5SGregory Herrero 	wIndex = le16_to_cpu(ctrl->wIndex);
17929e14d0a5SGregory Herrero 	recip = ctrl->bRequestType & USB_RECIP_MASK;
17939e14d0a5SGregory Herrero 
17949e14d0a5SGregory Herrero 	switch (recip) {
17959e14d0a5SGregory Herrero 	case USB_RECIP_DEVICE:
17969e14d0a5SGregory Herrero 		switch (wValue) {
1797fa389a6dSVardan Mikayelyan 		case USB_DEVICE_REMOTE_WAKEUP:
17989a0d6f7cSMinas Harutyunyan 			if (set)
1799fa389a6dSVardan Mikayelyan 				hsotg->remote_wakeup_allowed = 1;
18009a0d6f7cSMinas Harutyunyan 			else
18019a0d6f7cSMinas Harutyunyan 				hsotg->remote_wakeup_allowed = 0;
1802fa389a6dSVardan Mikayelyan 			break;
1803fa389a6dSVardan Mikayelyan 
18049e14d0a5SGregory Herrero 		case USB_DEVICE_TEST_MODE:
18059e14d0a5SGregory Herrero 			if ((wIndex & 0xff) != 0)
18069e14d0a5SGregory Herrero 				return -EINVAL;
18079e14d0a5SGregory Herrero 			if (!set)
18089e14d0a5SGregory Herrero 				return -EINVAL;
18099e14d0a5SGregory Herrero 
18109e14d0a5SGregory Herrero 			hsotg->test_mode = wIndex >> 8;
18119a0d6f7cSMinas Harutyunyan 			break;
18129a0d6f7cSMinas Harutyunyan 		default:
18139a0d6f7cSMinas Harutyunyan 			return -ENOENT;
18149a0d6f7cSMinas Harutyunyan 		}
18159a0d6f7cSMinas Harutyunyan 
18161f91b4ccSFelipe Balbi 		ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0);
18179e14d0a5SGregory Herrero 		if (ret) {
18189e14d0a5SGregory Herrero 			dev_err(hsotg->dev,
18199e14d0a5SGregory Herrero 				"%s: failed to send reply\n", __func__);
18209e14d0a5SGregory Herrero 			return ret;
18219e14d0a5SGregory Herrero 		}
18229e14d0a5SGregory Herrero 		break;
18239e14d0a5SGregory Herrero 
18249e14d0a5SGregory Herrero 	case USB_RECIP_ENDPOINT:
18259e14d0a5SGregory Herrero 		ep = ep_from_windex(hsotg, wIndex);
182647a1685fSDinh Nguyen 		if (!ep) {
182747a1685fSDinh Nguyen 			dev_dbg(hsotg->dev, "%s: no endpoint for 0x%04x\n",
18289e14d0a5SGregory Herrero 				__func__, wIndex);
182947a1685fSDinh Nguyen 			return -ENOENT;
183047a1685fSDinh Nguyen 		}
183147a1685fSDinh Nguyen 
18329e14d0a5SGregory Herrero 		switch (wValue) {
183347a1685fSDinh Nguyen 		case USB_ENDPOINT_HALT:
183447a1685fSDinh Nguyen 			halted = ep->halted;
183547a1685fSDinh Nguyen 
1836b833ce15SMinas Harutyunyan 			if (!ep->wedged)
183751da43b5SVahram Aharonyan 				dwc2_hsotg_ep_sethalt(&ep->ep, set, true);
183847a1685fSDinh Nguyen 
18391f91b4ccSFelipe Balbi 			ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0);
184047a1685fSDinh Nguyen 			if (ret) {
184147a1685fSDinh Nguyen 				dev_err(hsotg->dev,
184247a1685fSDinh Nguyen 					"%s: failed to send reply\n", __func__);
184347a1685fSDinh Nguyen 				return ret;
184447a1685fSDinh Nguyen 			}
184547a1685fSDinh Nguyen 
184647a1685fSDinh Nguyen 			/*
184747a1685fSDinh Nguyen 			 * we have to complete all requests for ep if it was
184847a1685fSDinh Nguyen 			 * halted, and the halt was cleared by CLEAR_FEATURE
184947a1685fSDinh Nguyen 			 */
185047a1685fSDinh Nguyen 
185147a1685fSDinh Nguyen 			if (!set && halted) {
185247a1685fSDinh Nguyen 				/*
185347a1685fSDinh Nguyen 				 * If we have request in progress,
185447a1685fSDinh Nguyen 				 * then complete it
185547a1685fSDinh Nguyen 				 */
185647a1685fSDinh Nguyen 				if (ep->req) {
185747a1685fSDinh Nguyen 					hs_req = ep->req;
185847a1685fSDinh Nguyen 					ep->req = NULL;
185947a1685fSDinh Nguyen 					list_del_init(&hs_req->queue);
1860c00dd4a6SGregory Herrero 					if (hs_req->req.complete) {
1861c00dd4a6SGregory Herrero 						spin_unlock(&hsotg->lock);
1862c00dd4a6SGregory Herrero 						usb_gadget_giveback_request(
1863c00dd4a6SGregory Herrero 							&ep->ep, &hs_req->req);
1864c00dd4a6SGregory Herrero 						spin_lock(&hsotg->lock);
1865c00dd4a6SGregory Herrero 					}
186647a1685fSDinh Nguyen 				}
186747a1685fSDinh Nguyen 
186847a1685fSDinh Nguyen 				/* If we have pending request, then start it */
186934c0887fSJohn Youn 				if (!ep->req)
187041cc4cd2SVardan Mikayelyan 					dwc2_gadget_start_next_request(ep);
187147a1685fSDinh Nguyen 			}
187247a1685fSDinh Nguyen 
187347a1685fSDinh Nguyen 			break;
187447a1685fSDinh Nguyen 
187547a1685fSDinh Nguyen 		default:
187647a1685fSDinh Nguyen 			return -ENOENT;
187747a1685fSDinh Nguyen 		}
18789e14d0a5SGregory Herrero 		break;
18799e14d0a5SGregory Herrero 	default:
18809e14d0a5SGregory Herrero 		return -ENOENT;
18819e14d0a5SGregory Herrero 	}
188247a1685fSDinh Nguyen 	return 1;
188347a1685fSDinh Nguyen }
188447a1685fSDinh Nguyen 
18851f91b4ccSFelipe Balbi static void dwc2_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg);
188647a1685fSDinh Nguyen 
188747a1685fSDinh Nguyen /**
18881f91b4ccSFelipe Balbi  * dwc2_hsotg_stall_ep0 - stall ep0
188947a1685fSDinh Nguyen  * @hsotg: The device state
189047a1685fSDinh Nguyen  *
189147a1685fSDinh Nguyen  * Set stall for ep0 as response for setup request.
189247a1685fSDinh Nguyen  */
dwc2_hsotg_stall_ep0(struct dwc2_hsotg * hsotg)18931f91b4ccSFelipe Balbi static void dwc2_hsotg_stall_ep0(struct dwc2_hsotg *hsotg)
1894e9ebe7c3SJingoo Han {
18951f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0];
189647a1685fSDinh Nguyen 	u32 reg;
189747a1685fSDinh Nguyen 	u32 ctrl;
189847a1685fSDinh Nguyen 
189947a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in);
190047a1685fSDinh Nguyen 	reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0;
190147a1685fSDinh Nguyen 
190247a1685fSDinh Nguyen 	/*
190347a1685fSDinh Nguyen 	 * DxEPCTL_Stall will be cleared by EP once it has
190447a1685fSDinh Nguyen 	 * taken effect, so no need to clear later.
190547a1685fSDinh Nguyen 	 */
190647a1685fSDinh Nguyen 
1907f25c42b8SGevorg Sahakyan 	ctrl = dwc2_readl(hsotg, reg);
190847a1685fSDinh Nguyen 	ctrl |= DXEPCTL_STALL;
190947a1685fSDinh Nguyen 	ctrl |= DXEPCTL_CNAK;
1910f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, ctrl, reg);
191147a1685fSDinh Nguyen 
191247a1685fSDinh Nguyen 	dev_dbg(hsotg->dev,
191347a1685fSDinh Nguyen 		"written DXEPCTL=0x%08x to %08x (DXEPCTL=0x%08x)\n",
1914f25c42b8SGevorg Sahakyan 		ctrl, reg, dwc2_readl(hsotg, reg));
191547a1685fSDinh Nguyen 
191647a1685fSDinh Nguyen 	 /*
191747a1685fSDinh Nguyen 	  * complete won't be called, so we enqueue
191847a1685fSDinh Nguyen 	  * setup request here
191947a1685fSDinh Nguyen 	  */
19201f91b4ccSFelipe Balbi 	 dwc2_hsotg_enqueue_setup(hsotg);
192147a1685fSDinh Nguyen }
192247a1685fSDinh Nguyen 
192347a1685fSDinh Nguyen /**
19241f91b4ccSFelipe Balbi  * dwc2_hsotg_process_control - process a control request
192547a1685fSDinh Nguyen  * @hsotg: The device state
192647a1685fSDinh Nguyen  * @ctrl: The control request received
192747a1685fSDinh Nguyen  *
192847a1685fSDinh Nguyen  * The controller has received the SETUP phase of a control request, and
192947a1685fSDinh Nguyen  * needs to work out what to do next (and whether to pass it on to the
193047a1685fSDinh Nguyen  * gadget driver).
193147a1685fSDinh Nguyen  */
dwc2_hsotg_process_control(struct dwc2_hsotg * hsotg,struct usb_ctrlrequest * ctrl)19321f91b4ccSFelipe Balbi static void dwc2_hsotg_process_control(struct dwc2_hsotg *hsotg,
193347a1685fSDinh Nguyen 				       struct usb_ctrlrequest *ctrl)
193447a1685fSDinh Nguyen {
19351f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0];
193647a1685fSDinh Nguyen 	int ret = 0;
193747a1685fSDinh Nguyen 	u32 dcfg;
193847a1685fSDinh Nguyen 
1939e525e743SMian Yousaf Kaukab 	dev_dbg(hsotg->dev,
1940e525e743SMian Yousaf Kaukab 		"ctrl Type=%02x, Req=%02x, V=%04x, I=%04x, L=%04x\n",
1941e525e743SMian Yousaf Kaukab 		ctrl->bRequestType, ctrl->bRequest, ctrl->wValue,
1942e525e743SMian Yousaf Kaukab 		ctrl->wIndex, ctrl->wLength);
194347a1685fSDinh Nguyen 
1944fe0b94abSMian Yousaf Kaukab 	if (ctrl->wLength == 0) {
194547a1685fSDinh Nguyen 		ep0->dir_in = 1;
1946fe0b94abSMian Yousaf Kaukab 		hsotg->ep0_state = DWC2_EP0_STATUS_IN;
1947fe0b94abSMian Yousaf Kaukab 	} else if (ctrl->bRequestType & USB_DIR_IN) {
1948fe0b94abSMian Yousaf Kaukab 		ep0->dir_in = 1;
1949fe0b94abSMian Yousaf Kaukab 		hsotg->ep0_state = DWC2_EP0_DATA_IN;
1950fe0b94abSMian Yousaf Kaukab 	} else {
1951fe0b94abSMian Yousaf Kaukab 		ep0->dir_in = 0;
1952fe0b94abSMian Yousaf Kaukab 		hsotg->ep0_state = DWC2_EP0_DATA_OUT;
1953fe0b94abSMian Yousaf Kaukab 	}
195447a1685fSDinh Nguyen 
195547a1685fSDinh Nguyen 	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
195647a1685fSDinh Nguyen 		switch (ctrl->bRequest) {
195747a1685fSDinh Nguyen 		case USB_REQ_SET_ADDRESS:
19586d713c15SMian Yousaf Kaukab 			hsotg->connected = 1;
1959f25c42b8SGevorg Sahakyan 			dcfg = dwc2_readl(hsotg, DCFG);
196047a1685fSDinh Nguyen 			dcfg &= ~DCFG_DEVADDR_MASK;
1961d5dbd3f7SPaul Zimmerman 			dcfg |= (le16_to_cpu(ctrl->wValue) <<
1962d5dbd3f7SPaul Zimmerman 				 DCFG_DEVADDR_SHIFT) & DCFG_DEVADDR_MASK;
1963f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, dcfg, DCFG);
196447a1685fSDinh Nguyen 
196547a1685fSDinh Nguyen 			dev_info(hsotg->dev, "new address %d\n", ctrl->wValue);
196647a1685fSDinh Nguyen 
19671f91b4ccSFelipe Balbi 			ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0);
196847a1685fSDinh Nguyen 			return;
196947a1685fSDinh Nguyen 
197047a1685fSDinh Nguyen 		case USB_REQ_GET_STATUS:
19711f91b4ccSFelipe Balbi 			ret = dwc2_hsotg_process_req_status(hsotg, ctrl);
197247a1685fSDinh Nguyen 			break;
197347a1685fSDinh Nguyen 
197447a1685fSDinh Nguyen 		case USB_REQ_CLEAR_FEATURE:
197547a1685fSDinh Nguyen 		case USB_REQ_SET_FEATURE:
19761f91b4ccSFelipe Balbi 			ret = dwc2_hsotg_process_req_feature(hsotg, ctrl);
197747a1685fSDinh Nguyen 			break;
197847a1685fSDinh Nguyen 		}
197947a1685fSDinh Nguyen 	}
198047a1685fSDinh Nguyen 
198147a1685fSDinh Nguyen 	/* as a fallback, try delivering it to the driver to deal with */
198247a1685fSDinh Nguyen 
198347a1685fSDinh Nguyen 	if (ret == 0 && hsotg->driver) {
198447a1685fSDinh Nguyen 		spin_unlock(&hsotg->lock);
198547a1685fSDinh Nguyen 		ret = hsotg->driver->setup(&hsotg->gadget, ctrl);
198647a1685fSDinh Nguyen 		spin_lock(&hsotg->lock);
198747a1685fSDinh Nguyen 		if (ret < 0)
198847a1685fSDinh Nguyen 			dev_dbg(hsotg->dev, "driver->setup() ret %d\n", ret);
198947a1685fSDinh Nguyen 	}
199047a1685fSDinh Nguyen 
1991b4c53b4aSMinas Harutyunyan 	hsotg->delayed_status = false;
1992b4c53b4aSMinas Harutyunyan 	if (ret == USB_GADGET_DELAYED_STATUS)
1993b4c53b4aSMinas Harutyunyan 		hsotg->delayed_status = true;
1994b4c53b4aSMinas Harutyunyan 
199547a1685fSDinh Nguyen 	/*
199647a1685fSDinh Nguyen 	 * the request is either unhandlable, or is not formatted correctly
199747a1685fSDinh Nguyen 	 * so respond with a STALL for the status stage to indicate failure.
199847a1685fSDinh Nguyen 	 */
199947a1685fSDinh Nguyen 
200047a1685fSDinh Nguyen 	if (ret < 0)
20011f91b4ccSFelipe Balbi 		dwc2_hsotg_stall_ep0(hsotg);
200247a1685fSDinh Nguyen }
200347a1685fSDinh Nguyen 
200447a1685fSDinh Nguyen /**
20051f91b4ccSFelipe Balbi  * dwc2_hsotg_complete_setup - completion of a setup transfer
200647a1685fSDinh Nguyen  * @ep: The endpoint the request was on.
200747a1685fSDinh Nguyen  * @req: The request completed.
200847a1685fSDinh Nguyen  *
200947a1685fSDinh Nguyen  * Called on completion of any requests the driver itself submitted for
201047a1685fSDinh Nguyen  * EP0 setup packets
201147a1685fSDinh Nguyen  */
dwc2_hsotg_complete_setup(struct usb_ep * ep,struct usb_request * req)20121f91b4ccSFelipe Balbi static void dwc2_hsotg_complete_setup(struct usb_ep *ep,
201347a1685fSDinh Nguyen 				      struct usb_request *req)
201447a1685fSDinh Nguyen {
20151f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
2016941fcce4SDinh Nguyen 	struct dwc2_hsotg *hsotg = hs_ep->parent;
201747a1685fSDinh Nguyen 
201847a1685fSDinh Nguyen 	if (req->status < 0) {
201947a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s: failed %d\n", __func__, req->status);
202047a1685fSDinh Nguyen 		return;
202147a1685fSDinh Nguyen 	}
202247a1685fSDinh Nguyen 
202347a1685fSDinh Nguyen 	spin_lock(&hsotg->lock);
202447a1685fSDinh Nguyen 	if (req->actual == 0)
20251f91b4ccSFelipe Balbi 		dwc2_hsotg_enqueue_setup(hsotg);
202647a1685fSDinh Nguyen 	else
20271f91b4ccSFelipe Balbi 		dwc2_hsotg_process_control(hsotg, req->buf);
202847a1685fSDinh Nguyen 	spin_unlock(&hsotg->lock);
202947a1685fSDinh Nguyen }
203047a1685fSDinh Nguyen 
203147a1685fSDinh Nguyen /**
20321f91b4ccSFelipe Balbi  * dwc2_hsotg_enqueue_setup - start a request for EP0 packets
203347a1685fSDinh Nguyen  * @hsotg: The device state.
203447a1685fSDinh Nguyen  *
203547a1685fSDinh Nguyen  * Enqueue a request on EP0 if necessary to received any SETUP packets
203647a1685fSDinh Nguyen  * received from the host.
203747a1685fSDinh Nguyen  */
dwc2_hsotg_enqueue_setup(struct dwc2_hsotg * hsotg)20381f91b4ccSFelipe Balbi static void dwc2_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg)
203947a1685fSDinh Nguyen {
204047a1685fSDinh Nguyen 	struct usb_request *req = hsotg->ctrl_req;
20411f91b4ccSFelipe Balbi 	struct dwc2_hsotg_req *hs_req = our_req(req);
204247a1685fSDinh Nguyen 	int ret;
204347a1685fSDinh Nguyen 
204447a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: queueing setup request\n", __func__);
204547a1685fSDinh Nguyen 
204647a1685fSDinh Nguyen 	req->zero = 0;
204747a1685fSDinh Nguyen 	req->length = 8;
204847a1685fSDinh Nguyen 	req->buf = hsotg->ctrl_buff;
20491f91b4ccSFelipe Balbi 	req->complete = dwc2_hsotg_complete_setup;
205047a1685fSDinh Nguyen 
205147a1685fSDinh Nguyen 	if (!list_empty(&hs_req->queue)) {
205247a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s already queued???\n", __func__);
205347a1685fSDinh Nguyen 		return;
205447a1685fSDinh Nguyen 	}
205547a1685fSDinh Nguyen 
2056c6f5c050SMian Yousaf Kaukab 	hsotg->eps_out[0]->dir_in = 0;
20578a20fa45SMian Yousaf Kaukab 	hsotg->eps_out[0]->send_zlp = 0;
2058fe0b94abSMian Yousaf Kaukab 	hsotg->ep0_state = DWC2_EP0_SETUP;
205947a1685fSDinh Nguyen 
20601f91b4ccSFelipe Balbi 	ret = dwc2_hsotg_ep_queue(&hsotg->eps_out[0]->ep, req, GFP_ATOMIC);
206147a1685fSDinh Nguyen 	if (ret < 0) {
206247a1685fSDinh Nguyen 		dev_err(hsotg->dev, "%s: failed queue (%d)\n", __func__, ret);
206347a1685fSDinh Nguyen 		/*
206447a1685fSDinh Nguyen 		 * Don't think there's much we can do other than watch the
206547a1685fSDinh Nguyen 		 * driver fail.
206647a1685fSDinh Nguyen 		 */
206747a1685fSDinh Nguyen 	}
206847a1685fSDinh Nguyen }
206947a1685fSDinh Nguyen 
dwc2_hsotg_program_zlp(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep)20701f91b4ccSFelipe Balbi static void dwc2_hsotg_program_zlp(struct dwc2_hsotg *hsotg,
20711f91b4ccSFelipe Balbi 				   struct dwc2_hsotg_ep *hs_ep)
2072fe0b94abSMian Yousaf Kaukab {
2073fe0b94abSMian Yousaf Kaukab 	u32 ctrl;
2074fe0b94abSMian Yousaf Kaukab 	u8 index = hs_ep->index;
2075fe0b94abSMian Yousaf Kaukab 	u32 epctl_reg = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index);
2076fe0b94abSMian Yousaf Kaukab 	u32 epsiz_reg = hs_ep->dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index);
2077fe0b94abSMian Yousaf Kaukab 
2078ccb34a91SMian Yousaf Kaukab 	if (hs_ep->dir_in)
2079ccb34a91SMian Yousaf Kaukab 		dev_dbg(hsotg->dev, "Sending zero-length packet on ep%d\n",
2080ccb34a91SMian Yousaf Kaukab 			index);
2081ccb34a91SMian Yousaf Kaukab 	else
2082ccb34a91SMian Yousaf Kaukab 		dev_dbg(hsotg->dev, "Receiving zero-length packet on ep%d\n",
2083ccb34a91SMian Yousaf Kaukab 			index);
2084e02f9aa6SVahram Aharonyan 	if (using_desc_dma(hsotg)) {
2085066cfd07SAndrzej Pietrasiewicz 		/* Not specific buffer needed for ep0 ZLP */
2086066cfd07SAndrzej Pietrasiewicz 		dma_addr_t dma = hs_ep->desc_list_dma;
2087066cfd07SAndrzej Pietrasiewicz 
2088201ec568SMinas Harutyunyan 		if (!index)
2089e02f9aa6SVahram Aharonyan 			dwc2_gadget_set_ep0_desc_chain(hsotg, hs_ep);
2090201ec568SMinas Harutyunyan 
2091066cfd07SAndrzej Pietrasiewicz 		dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, dma, 0);
2092e02f9aa6SVahram Aharonyan 	} else {
2093f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
2094f25c42b8SGevorg Sahakyan 			    DXEPTSIZ_XFERSIZE(0),
2095fe0b94abSMian Yousaf Kaukab 			    epsiz_reg);
2096e02f9aa6SVahram Aharonyan 	}
2097fe0b94abSMian Yousaf Kaukab 
2098f25c42b8SGevorg Sahakyan 	ctrl = dwc2_readl(hsotg, epctl_reg);
2099fe0b94abSMian Yousaf Kaukab 	ctrl |= DXEPCTL_CNAK;  /* clear NAK set by core */
2100fe0b94abSMian Yousaf Kaukab 	ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */
2101fe0b94abSMian Yousaf Kaukab 	ctrl |= DXEPCTL_USBACTEP;
2102f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, ctrl, epctl_reg);
2103fe0b94abSMian Yousaf Kaukab }
2104fe0b94abSMian Yousaf Kaukab 
210547a1685fSDinh Nguyen /**
21061f91b4ccSFelipe Balbi  * dwc2_hsotg_complete_request - complete a request given to us
210747a1685fSDinh Nguyen  * @hsotg: The device state.
210847a1685fSDinh Nguyen  * @hs_ep: The endpoint the request was on.
210947a1685fSDinh Nguyen  * @hs_req: The request to complete.
211047a1685fSDinh Nguyen  * @result: The result code (0 => Ok, otherwise errno)
211147a1685fSDinh Nguyen  *
211247a1685fSDinh Nguyen  * The given request has finished, so call the necessary completion
211347a1685fSDinh Nguyen  * if it has one and then look to see if we can start a new request
211447a1685fSDinh Nguyen  * on the endpoint.
211547a1685fSDinh Nguyen  *
211647a1685fSDinh Nguyen  * Note, expects the ep to already be locked as appropriate.
211747a1685fSDinh Nguyen  */
dwc2_hsotg_complete_request(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep,struct dwc2_hsotg_req * hs_req,int result)21181f91b4ccSFelipe Balbi static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg,
21191f91b4ccSFelipe Balbi 					struct dwc2_hsotg_ep *hs_ep,
21201f91b4ccSFelipe Balbi 				       struct dwc2_hsotg_req *hs_req,
212147a1685fSDinh Nguyen 				       int result)
212247a1685fSDinh Nguyen {
212347a1685fSDinh Nguyen 	if (!hs_req) {
212447a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s: nothing to complete?\n", __func__);
212547a1685fSDinh Nguyen 		return;
212647a1685fSDinh Nguyen 	}
212747a1685fSDinh Nguyen 
212847a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "complete: ep %p %s, req %p, %d => %p\n",
212947a1685fSDinh Nguyen 		hs_ep, hs_ep->ep.name, hs_req, result, hs_req->req.complete);
213047a1685fSDinh Nguyen 
213147a1685fSDinh Nguyen 	/*
213247a1685fSDinh Nguyen 	 * only replace the status if we've not already set an error
213347a1685fSDinh Nguyen 	 * from a previous transaction
213447a1685fSDinh Nguyen 	 */
213547a1685fSDinh Nguyen 
213647a1685fSDinh Nguyen 	if (hs_req->req.status == -EINPROGRESS)
213747a1685fSDinh Nguyen 		hs_req->req.status = result;
213847a1685fSDinh Nguyen 
213944583fecSYunzhi Li 	if (using_dma(hsotg))
214044583fecSYunzhi Li 		dwc2_hsotg_unmap_dma(hsotg, hs_ep, hs_req);
214144583fecSYunzhi Li 
21421f91b4ccSFelipe Balbi 	dwc2_hsotg_handle_unaligned_buf_complete(hsotg, hs_ep, hs_req);
21437d24c1b5SMian Yousaf Kaukab 
214447a1685fSDinh Nguyen 	hs_ep->req = NULL;
214547a1685fSDinh Nguyen 	list_del_init(&hs_req->queue);
214647a1685fSDinh Nguyen 
214747a1685fSDinh Nguyen 	/*
214847a1685fSDinh Nguyen 	 * call the complete request with the locks off, just in case the
214947a1685fSDinh Nguyen 	 * request tries to queue more work for this endpoint.
215047a1685fSDinh Nguyen 	 */
215147a1685fSDinh Nguyen 
215247a1685fSDinh Nguyen 	if (hs_req->req.complete) {
215347a1685fSDinh Nguyen 		spin_unlock(&hsotg->lock);
2154304f7e5eSMichal Sojka 		usb_gadget_giveback_request(&hs_ep->ep, &hs_req->req);
215547a1685fSDinh Nguyen 		spin_lock(&hsotg->lock);
215647a1685fSDinh Nguyen 	}
215747a1685fSDinh Nguyen 
2158540ccba0SVahram Aharonyan 	/* In DDMA don't need to proceed to starting of next ISOC request */
2159540ccba0SVahram Aharonyan 	if (using_desc_dma(hsotg) && hs_ep->isochronous)
2160540ccba0SVahram Aharonyan 		return;
2161540ccba0SVahram Aharonyan 
216247a1685fSDinh Nguyen 	/*
216347a1685fSDinh Nguyen 	 * Look to see if there is anything else to do. Note, the completion
216447a1685fSDinh Nguyen 	 * of the previous request may have caused a new request to be started
216547a1685fSDinh Nguyen 	 * so be careful when doing this.
216647a1685fSDinh Nguyen 	 */
216747a1685fSDinh Nguyen 
216834c0887fSJohn Youn 	if (!hs_ep->req && result >= 0)
216941cc4cd2SVardan Mikayelyan 		dwc2_gadget_start_next_request(hs_ep);
217047a1685fSDinh Nguyen }
217147a1685fSDinh Nguyen 
2172540ccba0SVahram Aharonyan /*
2173540ccba0SVahram Aharonyan  * dwc2_gadget_complete_isoc_request_ddma - complete an isoc request in DDMA
2174540ccba0SVahram Aharonyan  * @hs_ep: The endpoint the request was on.
2175540ccba0SVahram Aharonyan  *
2176540ccba0SVahram Aharonyan  * Get first request from the ep queue, determine descriptor on which complete
2177729cac69SMinas Harutyunyan  * happened. SW discovers which descriptor currently in use by HW, adjusts
2178729cac69SMinas Harutyunyan  * dma_address and calculates index of completed descriptor based on the value
2179729cac69SMinas Harutyunyan  * of DEPDMA register. Update actual length of request, giveback to gadget.
2180540ccba0SVahram Aharonyan  */
dwc2_gadget_complete_isoc_request_ddma(struct dwc2_hsotg_ep * hs_ep)2181540ccba0SVahram Aharonyan static void dwc2_gadget_complete_isoc_request_ddma(struct dwc2_hsotg_ep *hs_ep)
2182540ccba0SVahram Aharonyan {
2183540ccba0SVahram Aharonyan 	struct dwc2_hsotg *hsotg = hs_ep->parent;
2184540ccba0SVahram Aharonyan 	struct dwc2_hsotg_req *hs_req;
2185540ccba0SVahram Aharonyan 	struct usb_request *ureq;
2186540ccba0SVahram Aharonyan 	u32 desc_sts;
2187540ccba0SVahram Aharonyan 	u32 mask;
2188540ccba0SVahram Aharonyan 
2189729cac69SMinas Harutyunyan 	desc_sts = hs_ep->desc_list[hs_ep->compl_desc].status;
2190729cac69SMinas Harutyunyan 
2191729cac69SMinas Harutyunyan 	/* Process only descriptors with buffer status set to DMA done */
2192729cac69SMinas Harutyunyan 	while ((desc_sts & DEV_DMA_BUFF_STS_MASK) >>
2193729cac69SMinas Harutyunyan 		DEV_DMA_BUFF_STS_SHIFT == DEV_DMA_BUFF_STS_DMADONE) {
2194729cac69SMinas Harutyunyan 
2195540ccba0SVahram Aharonyan 		hs_req = get_ep_head(hs_ep);
2196540ccba0SVahram Aharonyan 		if (!hs_req) {
2197540ccba0SVahram Aharonyan 			dev_warn(hsotg->dev, "%s: ISOC EP queue empty\n", __func__);
2198540ccba0SVahram Aharonyan 			return;
2199540ccba0SVahram Aharonyan 		}
2200540ccba0SVahram Aharonyan 		ureq = &hs_req->req;
2201540ccba0SVahram Aharonyan 
2202729cac69SMinas Harutyunyan 		/* Check completion status */
2203729cac69SMinas Harutyunyan 		if ((desc_sts & DEV_DMA_STS_MASK) >> DEV_DMA_STS_SHIFT ==
2204729cac69SMinas Harutyunyan 			DEV_DMA_STS_SUCC) {
2205540ccba0SVahram Aharonyan 			mask = hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_MASK :
2206540ccba0SVahram Aharonyan 				DEV_DMA_ISOC_RX_NBYTES_MASK;
2207729cac69SMinas Harutyunyan 			ureq->actual = ureq->length - ((desc_sts & mask) >>
2208729cac69SMinas Harutyunyan 				DEV_DMA_ISOC_NBYTES_SHIFT);
2209540ccba0SVahram Aharonyan 
2210729cac69SMinas Harutyunyan 			/* Adjust actual len for ISOC Out if len is
2211729cac69SMinas Harutyunyan 			 * not align of 4
2212729cac69SMinas Harutyunyan 			 */
221395d2b037SVahram Aharonyan 			if (!hs_ep->dir_in && ureq->length & 0x3)
221495d2b037SVahram Aharonyan 				ureq->actual += 4 - (ureq->length & 0x3);
2215c8006f67SMinas Harutyunyan 
2216c8006f67SMinas Harutyunyan 			/* Set actual frame number for completed transfers */
2217c8006f67SMinas Harutyunyan 			ureq->frame_number =
2218c8006f67SMinas Harutyunyan 				(desc_sts & DEV_DMA_ISOC_FRNUM_MASK) >>
2219c8006f67SMinas Harutyunyan 				DEV_DMA_ISOC_FRNUM_SHIFT;
2220729cac69SMinas Harutyunyan 		}
222195d2b037SVahram Aharonyan 
2222540ccba0SVahram Aharonyan 		dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
2223729cac69SMinas Harutyunyan 
2224729cac69SMinas Harutyunyan 		hs_ep->compl_desc++;
222554f37f56SMinas Harutyunyan 		if (hs_ep->compl_desc > (MAX_DMA_DESC_NUM_HS_ISOC - 1))
2226729cac69SMinas Harutyunyan 			hs_ep->compl_desc = 0;
2227729cac69SMinas Harutyunyan 		desc_sts = hs_ep->desc_list[hs_ep->compl_desc].status;
2228729cac69SMinas Harutyunyan 	}
2229540ccba0SVahram Aharonyan }
2230540ccba0SVahram Aharonyan 
2231540ccba0SVahram Aharonyan /*
2232729cac69SMinas Harutyunyan  * dwc2_gadget_handle_isoc_bna - handle BNA interrupt for ISOC.
2233729cac69SMinas Harutyunyan  * @hs_ep: The isochronous endpoint.
2234540ccba0SVahram Aharonyan  *
2235729cac69SMinas Harutyunyan  * If EP ISOC OUT then need to flush RX FIFO to remove source of BNA
2236729cac69SMinas Harutyunyan  * interrupt. Reset target frame and next_desc to allow to start
2237729cac69SMinas Harutyunyan  * ISOC's on NAK interrupt for IN direction or on OUTTKNEPDIS
2238729cac69SMinas Harutyunyan  * interrupt for OUT direction.
2239540ccba0SVahram Aharonyan  */
dwc2_gadget_handle_isoc_bna(struct dwc2_hsotg_ep * hs_ep)2240729cac69SMinas Harutyunyan static void dwc2_gadget_handle_isoc_bna(struct dwc2_hsotg_ep *hs_ep)
2241540ccba0SVahram Aharonyan {
2242540ccba0SVahram Aharonyan 	struct dwc2_hsotg *hsotg = hs_ep->parent;
2243540ccba0SVahram Aharonyan 
2244729cac69SMinas Harutyunyan 	if (!hs_ep->dir_in)
2245729cac69SMinas Harutyunyan 		dwc2_flush_rx_fifo(hsotg);
2246729cac69SMinas Harutyunyan 	dwc2_hsotg_complete_request(hsotg, hs_ep, get_ep_head(hs_ep), 0);
2247540ccba0SVahram Aharonyan 
2248729cac69SMinas Harutyunyan 	hs_ep->target_frame = TARGET_FRAME_INITIAL;
2249540ccba0SVahram Aharonyan 	hs_ep->next_desc = 0;
2250729cac69SMinas Harutyunyan 	hs_ep->compl_desc = 0;
2251540ccba0SVahram Aharonyan }
2252540ccba0SVahram Aharonyan 
225347a1685fSDinh Nguyen /**
22541f91b4ccSFelipe Balbi  * dwc2_hsotg_rx_data - receive data from the FIFO for an endpoint
225547a1685fSDinh Nguyen  * @hsotg: The device state.
225647a1685fSDinh Nguyen  * @ep_idx: The endpoint index for the data
225747a1685fSDinh Nguyen  * @size: The size of data in the fifo, in bytes
225847a1685fSDinh Nguyen  *
225947a1685fSDinh Nguyen  * The FIFO status shows there is data to read from the FIFO for a given
226047a1685fSDinh Nguyen  * endpoint, so sort out whether we need to read the data into a request
226147a1685fSDinh Nguyen  * that has been made for that endpoint.
226247a1685fSDinh Nguyen  */
dwc2_hsotg_rx_data(struct dwc2_hsotg * hsotg,int ep_idx,int size)22631f91b4ccSFelipe Balbi static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
226447a1685fSDinh Nguyen {
22651f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *hs_ep = hsotg->eps_out[ep_idx];
22661f91b4ccSFelipe Balbi 	struct dwc2_hsotg_req *hs_req = hs_ep->req;
226747a1685fSDinh Nguyen 	int to_read;
226847a1685fSDinh Nguyen 	int max_req;
226947a1685fSDinh Nguyen 	int read_ptr;
227047a1685fSDinh Nguyen 
227147a1685fSDinh Nguyen 	if (!hs_req) {
2272f25c42b8SGevorg Sahakyan 		u32 epctl = dwc2_readl(hsotg, DOEPCTL(ep_idx));
227347a1685fSDinh Nguyen 		int ptr;
227447a1685fSDinh Nguyen 
22756b448af4SRobert Baldyga 		dev_dbg(hsotg->dev,
227647a1685fSDinh Nguyen 			"%s: FIFO %d bytes on ep%d but no req (DXEPCTl=0x%08x)\n",
227747a1685fSDinh Nguyen 			 __func__, size, ep_idx, epctl);
227847a1685fSDinh Nguyen 
227947a1685fSDinh Nguyen 		/* dump the data from the FIFO, we've nothing we can do */
228047a1685fSDinh Nguyen 		for (ptr = 0; ptr < size; ptr += 4)
2281f25c42b8SGevorg Sahakyan 			(void)dwc2_readl(hsotg, EPFIFO(ep_idx));
228247a1685fSDinh Nguyen 
228347a1685fSDinh Nguyen 		return;
228447a1685fSDinh Nguyen 	}
228547a1685fSDinh Nguyen 
228647a1685fSDinh Nguyen 	to_read = size;
228747a1685fSDinh Nguyen 	read_ptr = hs_req->req.actual;
228847a1685fSDinh Nguyen 	max_req = hs_req->req.length - read_ptr;
228947a1685fSDinh Nguyen 
229047a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n",
229147a1685fSDinh Nguyen 		__func__, to_read, max_req, read_ptr, hs_req->req.length);
229247a1685fSDinh Nguyen 
229347a1685fSDinh Nguyen 	if (to_read > max_req) {
229447a1685fSDinh Nguyen 		/*
229547a1685fSDinh Nguyen 		 * more data appeared than we where willing
229647a1685fSDinh Nguyen 		 * to deal with in this request.
229747a1685fSDinh Nguyen 		 */
229847a1685fSDinh Nguyen 
229947a1685fSDinh Nguyen 		/* currently we don't deal this */
230047a1685fSDinh Nguyen 		WARN_ON_ONCE(1);
230147a1685fSDinh Nguyen 	}
230247a1685fSDinh Nguyen 
230347a1685fSDinh Nguyen 	hs_ep->total_data += to_read;
230447a1685fSDinh Nguyen 	hs_req->req.actual += to_read;
230547a1685fSDinh Nguyen 	to_read = DIV_ROUND_UP(to_read, 4);
230647a1685fSDinh Nguyen 
230747a1685fSDinh Nguyen 	/*
230847a1685fSDinh Nguyen 	 * note, we might over-write the buffer end by 3 bytes depending on
230947a1685fSDinh Nguyen 	 * alignment of the data.
231047a1685fSDinh Nguyen 	 */
2311342ccce1SGevorg Sahakyan 	dwc2_readl_rep(hsotg, EPFIFO(ep_idx),
2312f25c42b8SGevorg Sahakyan 		       hs_req->req.buf + read_ptr, to_read);
231347a1685fSDinh Nguyen }
231447a1685fSDinh Nguyen 
231547a1685fSDinh Nguyen /**
23161f91b4ccSFelipe Balbi  * dwc2_hsotg_ep0_zlp - send/receive zero-length packet on control endpoint
231747a1685fSDinh Nguyen  * @hsotg: The device instance
2318fe0b94abSMian Yousaf Kaukab  * @dir_in: If IN zlp
231947a1685fSDinh Nguyen  *
232047a1685fSDinh Nguyen  * Generate a zero-length IN packet request for terminating a SETUP
232147a1685fSDinh Nguyen  * transaction.
232247a1685fSDinh Nguyen  *
232347a1685fSDinh Nguyen  * Note, since we don't write any data to the TxFIFO, then it is
232447a1685fSDinh Nguyen  * currently believed that we do not need to wait for any space in
232547a1685fSDinh Nguyen  * the TxFIFO.
232647a1685fSDinh Nguyen  */
dwc2_hsotg_ep0_zlp(struct dwc2_hsotg * hsotg,bool dir_in)23271f91b4ccSFelipe Balbi static void dwc2_hsotg_ep0_zlp(struct dwc2_hsotg *hsotg, bool dir_in)
232847a1685fSDinh Nguyen {
2329c6f5c050SMian Yousaf Kaukab 	/* eps_out[0] is used in both directions */
2330fe0b94abSMian Yousaf Kaukab 	hsotg->eps_out[0]->dir_in = dir_in;
2331fe0b94abSMian Yousaf Kaukab 	hsotg->ep0_state = dir_in ? DWC2_EP0_STATUS_IN : DWC2_EP0_STATUS_OUT;
233247a1685fSDinh Nguyen 
23331f91b4ccSFelipe Balbi 	dwc2_hsotg_program_zlp(hsotg, hsotg->eps_out[0]);
233447a1685fSDinh Nguyen }
233547a1685fSDinh Nguyen 
2336aa3e8bc8SVahram Aharonyan /*
2337aa3e8bc8SVahram Aharonyan  * dwc2_gadget_get_xfersize_ddma - get transferred bytes amount from desc
2338aa3e8bc8SVahram Aharonyan  * @hs_ep - The endpoint on which transfer went
2339aa3e8bc8SVahram Aharonyan  *
2340aa3e8bc8SVahram Aharonyan  * Iterate over endpoints descriptor chain and get info on bytes remained
2341aa3e8bc8SVahram Aharonyan  * in DMA descriptors after transfer has completed. Used for non isoc EPs.
2342aa3e8bc8SVahram Aharonyan  */
dwc2_gadget_get_xfersize_ddma(struct dwc2_hsotg_ep * hs_ep)2343aa3e8bc8SVahram Aharonyan static unsigned int dwc2_gadget_get_xfersize_ddma(struct dwc2_hsotg_ep *hs_ep)
2344aa3e8bc8SVahram Aharonyan {
2345b2c586ebSMinas Harutyunyan 	const struct usb_endpoint_descriptor *ep_desc = hs_ep->ep.desc;
2346aa3e8bc8SVahram Aharonyan 	struct dwc2_hsotg *hsotg = hs_ep->parent;
2347aa3e8bc8SVahram Aharonyan 	unsigned int bytes_rem = 0;
2348b2c586ebSMinas Harutyunyan 	unsigned int bytes_rem_correction = 0;
2349aa3e8bc8SVahram Aharonyan 	struct dwc2_dma_desc *desc = hs_ep->desc_list;
2350aa3e8bc8SVahram Aharonyan 	int i;
2351aa3e8bc8SVahram Aharonyan 	u32 status;
2352b2c586ebSMinas Harutyunyan 	u32 mps = hs_ep->ep.maxpacket;
2353b2c586ebSMinas Harutyunyan 	int dir_in = hs_ep->dir_in;
2354aa3e8bc8SVahram Aharonyan 
2355aa3e8bc8SVahram Aharonyan 	if (!desc)
2356aa3e8bc8SVahram Aharonyan 		return -EINVAL;
2357aa3e8bc8SVahram Aharonyan 
2358b2c586ebSMinas Harutyunyan 	/* Interrupt OUT EP with mps not multiple of 4 */
2359b2c586ebSMinas Harutyunyan 	if (hs_ep->index)
2360b2c586ebSMinas Harutyunyan 		if (usb_endpoint_xfer_int(ep_desc) && !dir_in && (mps % 4))
2361b2c586ebSMinas Harutyunyan 			bytes_rem_correction = 4 - (mps % 4);
2362b2c586ebSMinas Harutyunyan 
2363aa3e8bc8SVahram Aharonyan 	for (i = 0; i < hs_ep->desc_count; ++i) {
2364aa3e8bc8SVahram Aharonyan 		status = desc->status;
2365aa3e8bc8SVahram Aharonyan 		bytes_rem += status & DEV_DMA_NBYTES_MASK;
2366b2c586ebSMinas Harutyunyan 		bytes_rem -= bytes_rem_correction;
2367aa3e8bc8SVahram Aharonyan 
2368aa3e8bc8SVahram Aharonyan 		if (status & DEV_DMA_STS_MASK)
2369aa3e8bc8SVahram Aharonyan 			dev_err(hsotg->dev, "descriptor %d closed with %x\n",
2370aa3e8bc8SVahram Aharonyan 				i, status & DEV_DMA_STS_MASK);
2371b2c586ebSMinas Harutyunyan 
2372b2c586ebSMinas Harutyunyan 		if (status & DEV_DMA_L)
2373b2c586ebSMinas Harutyunyan 			break;
2374b2c586ebSMinas Harutyunyan 
23755acb4b97SMinas Harutyunyan 		desc++;
2376aa3e8bc8SVahram Aharonyan 	}
2377aa3e8bc8SVahram Aharonyan 
2378aa3e8bc8SVahram Aharonyan 	return bytes_rem;
2379aa3e8bc8SVahram Aharonyan }
2380aa3e8bc8SVahram Aharonyan 
238147a1685fSDinh Nguyen /**
23821f91b4ccSFelipe Balbi  * dwc2_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO
238347a1685fSDinh Nguyen  * @hsotg: The device instance
238447a1685fSDinh Nguyen  * @epnum: The endpoint received from
238547a1685fSDinh Nguyen  *
238647a1685fSDinh Nguyen  * The RXFIFO has delivered an OutDone event, which means that the data
238747a1685fSDinh Nguyen  * transfer for an OUT endpoint has been completed, either by a short
238847a1685fSDinh Nguyen  * packet or by the finish of a transfer.
238947a1685fSDinh Nguyen  */
dwc2_hsotg_handle_outdone(struct dwc2_hsotg * hsotg,int epnum)23901f91b4ccSFelipe Balbi static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
239147a1685fSDinh Nguyen {
2392f25c42b8SGevorg Sahakyan 	u32 epsize = dwc2_readl(hsotg, DOEPTSIZ(epnum));
23931f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *hs_ep = hsotg->eps_out[epnum];
23941f91b4ccSFelipe Balbi 	struct dwc2_hsotg_req *hs_req = hs_ep->req;
239547a1685fSDinh Nguyen 	struct usb_request *req = &hs_req->req;
23969da51974SJohn Youn 	unsigned int size_left = DXEPTSIZ_XFERSIZE_GET(epsize);
239747a1685fSDinh Nguyen 	int result = 0;
239847a1685fSDinh Nguyen 
239947a1685fSDinh Nguyen 	if (!hs_req) {
240047a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s: no request active\n", __func__);
240147a1685fSDinh Nguyen 		return;
240247a1685fSDinh Nguyen 	}
240347a1685fSDinh Nguyen 
2404fe0b94abSMian Yousaf Kaukab 	if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_OUT) {
2405fe0b94abSMian Yousaf Kaukab 		dev_dbg(hsotg->dev, "zlp packet received\n");
24061f91b4ccSFelipe Balbi 		dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
24071f91b4ccSFelipe Balbi 		dwc2_hsotg_enqueue_setup(hsotg);
2408fe0b94abSMian Yousaf Kaukab 		return;
2409fe0b94abSMian Yousaf Kaukab 	}
2410fe0b94abSMian Yousaf Kaukab 
2411aa3e8bc8SVahram Aharonyan 	if (using_desc_dma(hsotg))
2412aa3e8bc8SVahram Aharonyan 		size_left = dwc2_gadget_get_xfersize_ddma(hs_ep);
2413aa3e8bc8SVahram Aharonyan 
241447a1685fSDinh Nguyen 	if (using_dma(hsotg)) {
24159da51974SJohn Youn 		unsigned int size_done;
241647a1685fSDinh Nguyen 
241747a1685fSDinh Nguyen 		/*
241847a1685fSDinh Nguyen 		 * Calculate the size of the transfer by checking how much
241947a1685fSDinh Nguyen 		 * is left in the endpoint size register and then working it
242047a1685fSDinh Nguyen 		 * out from the amount we loaded for the transfer.
242147a1685fSDinh Nguyen 		 *
242247a1685fSDinh Nguyen 		 * We need to do this as DMA pointers are always 32bit aligned
242347a1685fSDinh Nguyen 		 * so may overshoot/undershoot the transfer.
242447a1685fSDinh Nguyen 		 */
242547a1685fSDinh Nguyen 
242647a1685fSDinh Nguyen 		size_done = hs_ep->size_loaded - size_left;
242747a1685fSDinh Nguyen 		size_done += hs_ep->last_load;
242847a1685fSDinh Nguyen 
242947a1685fSDinh Nguyen 		req->actual = size_done;
243047a1685fSDinh Nguyen 	}
243147a1685fSDinh Nguyen 
243247a1685fSDinh Nguyen 	/* if there is more request to do, schedule new transfer */
243347a1685fSDinh Nguyen 	if (req->actual < req->length && size_left == 0) {
24341f91b4ccSFelipe Balbi 		dwc2_hsotg_start_req(hsotg, hs_ep, hs_req, true);
243547a1685fSDinh Nguyen 		return;
243647a1685fSDinh Nguyen 	}
243747a1685fSDinh Nguyen 
243847a1685fSDinh Nguyen 	if (req->actual < req->length && req->short_not_ok) {
243947a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s: got %d/%d (short not ok) => error\n",
244047a1685fSDinh Nguyen 			__func__, req->actual, req->length);
244147a1685fSDinh Nguyen 
244247a1685fSDinh Nguyen 		/*
244347a1685fSDinh Nguyen 		 * todo - what should we return here? there's no one else
244447a1685fSDinh Nguyen 		 * even bothering to check the status.
244547a1685fSDinh Nguyen 		 */
244647a1685fSDinh Nguyen 	}
244747a1685fSDinh Nguyen 
2448ef750c71SVahram Aharonyan 	/* DDMA IN status phase will start from StsPhseRcvd interrupt */
2449ef750c71SVahram Aharonyan 	if (!using_desc_dma(hsotg) && epnum == 0 &&
2450ef750c71SVahram Aharonyan 	    hsotg->ep0_state == DWC2_EP0_DATA_OUT) {
2451fe0b94abSMian Yousaf Kaukab 		/* Move to STATUS IN */
2452b4c53b4aSMinas Harutyunyan 		if (!hsotg->delayed_status)
24531f91b4ccSFelipe Balbi 			dwc2_hsotg_ep0_zlp(hsotg, true);
245447a1685fSDinh Nguyen 	}
245547a1685fSDinh Nguyen 
245691bb163eSMinas Harutyunyan 	/* Set actual frame number for completed transfers */
245791bb163eSMinas Harutyunyan 	if (!using_desc_dma(hsotg) && hs_ep->isochronous) {
245891bb163eSMinas Harutyunyan 		req->frame_number = hs_ep->target_frame;
2459837e9f00SVardan Mikayelyan 		dwc2_gadget_incr_frame_num(hs_ep);
2460ec1f9d9fSRoman Bacik 	}
2461ec1f9d9fSRoman Bacik 
24621f91b4ccSFelipe Balbi 	dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
246347a1685fSDinh Nguyen }
246447a1685fSDinh Nguyen 
246547a1685fSDinh Nguyen /**
24661f91b4ccSFelipe Balbi  * dwc2_hsotg_handle_rx - RX FIFO has data
246747a1685fSDinh Nguyen  * @hsotg: The device instance
246847a1685fSDinh Nguyen  *
246947a1685fSDinh Nguyen  * The IRQ handler has detected that the RX FIFO has some data in it
247047a1685fSDinh Nguyen  * that requires processing, so find out what is in there and do the
247147a1685fSDinh Nguyen  * appropriate read.
247247a1685fSDinh Nguyen  *
247347a1685fSDinh Nguyen  * The RXFIFO is a true FIFO, the packets coming out are still in packet
247447a1685fSDinh Nguyen  * chunks, so if you have x packets received on an endpoint you'll get x
247547a1685fSDinh Nguyen  * FIFO events delivered, each with a packet's worth of data in it.
247647a1685fSDinh Nguyen  *
247747a1685fSDinh Nguyen  * When using DMA, we should not be processing events from the RXFIFO
247847a1685fSDinh Nguyen  * as the actual data should be sent to the memory directly and we turn
247947a1685fSDinh Nguyen  * on the completion interrupts to get notifications of transfer completion.
248047a1685fSDinh Nguyen  */
dwc2_hsotg_handle_rx(struct dwc2_hsotg * hsotg)24811f91b4ccSFelipe Balbi static void dwc2_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
248247a1685fSDinh Nguyen {
2483f25c42b8SGevorg Sahakyan 	u32 grxstsr = dwc2_readl(hsotg, GRXSTSP);
248447a1685fSDinh Nguyen 	u32 epnum, status, size;
248547a1685fSDinh Nguyen 
248647a1685fSDinh Nguyen 	WARN_ON(using_dma(hsotg));
248747a1685fSDinh Nguyen 
248847a1685fSDinh Nguyen 	epnum = grxstsr & GRXSTS_EPNUM_MASK;
248947a1685fSDinh Nguyen 	status = grxstsr & GRXSTS_PKTSTS_MASK;
249047a1685fSDinh Nguyen 
249147a1685fSDinh Nguyen 	size = grxstsr & GRXSTS_BYTECNT_MASK;
249247a1685fSDinh Nguyen 	size >>= GRXSTS_BYTECNT_SHIFT;
249347a1685fSDinh Nguyen 
249447a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n",
249547a1685fSDinh Nguyen 		__func__, grxstsr, size, epnum);
249647a1685fSDinh Nguyen 
249747a1685fSDinh Nguyen 	switch ((status & GRXSTS_PKTSTS_MASK) >> GRXSTS_PKTSTS_SHIFT) {
249847a1685fSDinh Nguyen 	case GRXSTS_PKTSTS_GLOBALOUTNAK:
249947a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "GLOBALOUTNAK\n");
250047a1685fSDinh Nguyen 		break;
250147a1685fSDinh Nguyen 
250247a1685fSDinh Nguyen 	case GRXSTS_PKTSTS_OUTDONE:
250347a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "OutDone (Frame=0x%08x)\n",
25041f91b4ccSFelipe Balbi 			dwc2_hsotg_read_frameno(hsotg));
250547a1685fSDinh Nguyen 
250647a1685fSDinh Nguyen 		if (!using_dma(hsotg))
25071f91b4ccSFelipe Balbi 			dwc2_hsotg_handle_outdone(hsotg, epnum);
250847a1685fSDinh Nguyen 		break;
250947a1685fSDinh Nguyen 
251047a1685fSDinh Nguyen 	case GRXSTS_PKTSTS_SETUPDONE:
251147a1685fSDinh Nguyen 		dev_dbg(hsotg->dev,
251247a1685fSDinh Nguyen 			"SetupDone (Frame=0x%08x, DOPEPCTL=0x%08x)\n",
25131f91b4ccSFelipe Balbi 			dwc2_hsotg_read_frameno(hsotg),
2514f25c42b8SGevorg Sahakyan 			dwc2_readl(hsotg, DOEPCTL(0)));
2515fe0b94abSMian Yousaf Kaukab 		/*
25161f91b4ccSFelipe Balbi 		 * Call dwc2_hsotg_handle_outdone here if it was not called from
2517fe0b94abSMian Yousaf Kaukab 		 * GRXSTS_PKTSTS_OUTDONE. That is, if the core didn't
2518fe0b94abSMian Yousaf Kaukab 		 * generate GRXSTS_PKTSTS_OUTDONE for setup packet.
2519fe0b94abSMian Yousaf Kaukab 		 */
2520fe0b94abSMian Yousaf Kaukab 		if (hsotg->ep0_state == DWC2_EP0_SETUP)
25211f91b4ccSFelipe Balbi 			dwc2_hsotg_handle_outdone(hsotg, epnum);
252247a1685fSDinh Nguyen 		break;
252347a1685fSDinh Nguyen 
252447a1685fSDinh Nguyen 	case GRXSTS_PKTSTS_OUTRX:
25251f91b4ccSFelipe Balbi 		dwc2_hsotg_rx_data(hsotg, epnum, size);
252647a1685fSDinh Nguyen 		break;
252747a1685fSDinh Nguyen 
252847a1685fSDinh Nguyen 	case GRXSTS_PKTSTS_SETUPRX:
252947a1685fSDinh Nguyen 		dev_dbg(hsotg->dev,
253047a1685fSDinh Nguyen 			"SetupRX (Frame=0x%08x, DOPEPCTL=0x%08x)\n",
25311f91b4ccSFelipe Balbi 			dwc2_hsotg_read_frameno(hsotg),
2532f25c42b8SGevorg Sahakyan 			dwc2_readl(hsotg, DOEPCTL(0)));
253347a1685fSDinh Nguyen 
2534fe0b94abSMian Yousaf Kaukab 		WARN_ON(hsotg->ep0_state != DWC2_EP0_SETUP);
2535fe0b94abSMian Yousaf Kaukab 
25361f91b4ccSFelipe Balbi 		dwc2_hsotg_rx_data(hsotg, epnum, size);
253747a1685fSDinh Nguyen 		break;
253847a1685fSDinh Nguyen 
253947a1685fSDinh Nguyen 	default:
254047a1685fSDinh Nguyen 		dev_warn(hsotg->dev, "%s: unknown status %08x\n",
254147a1685fSDinh Nguyen 			 __func__, grxstsr);
254247a1685fSDinh Nguyen 
25431f91b4ccSFelipe Balbi 		dwc2_hsotg_dump(hsotg);
254447a1685fSDinh Nguyen 		break;
254547a1685fSDinh Nguyen 	}
254647a1685fSDinh Nguyen }
254747a1685fSDinh Nguyen 
254847a1685fSDinh Nguyen /**
25491f91b4ccSFelipe Balbi  * dwc2_hsotg_ep0_mps - turn max packet size into register setting
255047a1685fSDinh Nguyen  * @mps: The maximum packet size in bytes.
255147a1685fSDinh Nguyen  */
dwc2_hsotg_ep0_mps(unsigned int mps)25521f91b4ccSFelipe Balbi static u32 dwc2_hsotg_ep0_mps(unsigned int mps)
255347a1685fSDinh Nguyen {
255447a1685fSDinh Nguyen 	switch (mps) {
255547a1685fSDinh Nguyen 	case 64:
255647a1685fSDinh Nguyen 		return D0EPCTL_MPS_64;
255747a1685fSDinh Nguyen 	case 32:
255847a1685fSDinh Nguyen 		return D0EPCTL_MPS_32;
255947a1685fSDinh Nguyen 	case 16:
256047a1685fSDinh Nguyen 		return D0EPCTL_MPS_16;
256147a1685fSDinh Nguyen 	case 8:
256247a1685fSDinh Nguyen 		return D0EPCTL_MPS_8;
256347a1685fSDinh Nguyen 	}
256447a1685fSDinh Nguyen 
256547a1685fSDinh Nguyen 	/* bad max packet size, warn and return invalid result */
256647a1685fSDinh Nguyen 	WARN_ON(1);
256747a1685fSDinh Nguyen 	return (u32)-1;
256847a1685fSDinh Nguyen }
256947a1685fSDinh Nguyen 
257047a1685fSDinh Nguyen /**
25711f91b4ccSFelipe Balbi  * dwc2_hsotg_set_ep_maxpacket - set endpoint's max-packet field
257247a1685fSDinh Nguyen  * @hsotg: The driver state.
257347a1685fSDinh Nguyen  * @ep: The index number of the endpoint
257447a1685fSDinh Nguyen  * @mps: The maximum packet size in bytes
2575ee2c40deSVardan Mikayelyan  * @mc: The multicount value
25766fb914d7SGrigor Tovmasyan  * @dir_in: True if direction is in.
257747a1685fSDinh Nguyen  *
257847a1685fSDinh Nguyen  * Configure the maximum packet size for the given endpoint, updating
257947a1685fSDinh Nguyen  * the hardware control registers to reflect this.
258047a1685fSDinh Nguyen  */
dwc2_hsotg_set_ep_maxpacket(struct dwc2_hsotg * hsotg,unsigned int ep,unsigned int mps,unsigned int mc,unsigned int dir_in)25811f91b4ccSFelipe Balbi static void dwc2_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
2582ee2c40deSVardan Mikayelyan 					unsigned int ep, unsigned int mps,
2583ee2c40deSVardan Mikayelyan 					unsigned int mc, unsigned int dir_in)
258447a1685fSDinh Nguyen {
25851f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *hs_ep;
258647a1685fSDinh Nguyen 	u32 reg;
258747a1685fSDinh Nguyen 
2588c6f5c050SMian Yousaf Kaukab 	hs_ep = index_to_ep(hsotg, ep, dir_in);
2589c6f5c050SMian Yousaf Kaukab 	if (!hs_ep)
2590c6f5c050SMian Yousaf Kaukab 		return;
2591c6f5c050SMian Yousaf Kaukab 
259247a1685fSDinh Nguyen 	if (ep == 0) {
2593ee2c40deSVardan Mikayelyan 		u32 mps_bytes = mps;
2594ee2c40deSVardan Mikayelyan 
259547a1685fSDinh Nguyen 		/* EP0 is a special case */
2596ee2c40deSVardan Mikayelyan 		mps = dwc2_hsotg_ep0_mps(mps_bytes);
2597ee2c40deSVardan Mikayelyan 		if (mps > 3)
259847a1685fSDinh Nguyen 			goto bad_mps;
2599ee2c40deSVardan Mikayelyan 		hs_ep->ep.maxpacket = mps_bytes;
260047a1685fSDinh Nguyen 		hs_ep->mc = 1;
260147a1685fSDinh Nguyen 	} else {
2602ee2c40deSVardan Mikayelyan 		if (mps > 1024)
260347a1685fSDinh Nguyen 			goto bad_mps;
2604ee2c40deSVardan Mikayelyan 		hs_ep->mc = mc;
2605ee2c40deSVardan Mikayelyan 		if (mc > 3)
260647a1685fSDinh Nguyen 			goto bad_mps;
2607ee2c40deSVardan Mikayelyan 		hs_ep->ep.maxpacket = mps;
260847a1685fSDinh Nguyen 	}
260947a1685fSDinh Nguyen 
2610c6f5c050SMian Yousaf Kaukab 	if (dir_in) {
2611f25c42b8SGevorg Sahakyan 		reg = dwc2_readl(hsotg, DIEPCTL(ep));
261247a1685fSDinh Nguyen 		reg &= ~DXEPCTL_MPS_MASK;
2613ee2c40deSVardan Mikayelyan 		reg |= mps;
2614f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, reg, DIEPCTL(ep));
2615c6f5c050SMian Yousaf Kaukab 	} else {
2616f25c42b8SGevorg Sahakyan 		reg = dwc2_readl(hsotg, DOEPCTL(ep));
261747a1685fSDinh Nguyen 		reg &= ~DXEPCTL_MPS_MASK;
2618ee2c40deSVardan Mikayelyan 		reg |= mps;
2619f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, reg, DOEPCTL(ep));
262047a1685fSDinh Nguyen 	}
262147a1685fSDinh Nguyen 
262247a1685fSDinh Nguyen 	return;
262347a1685fSDinh Nguyen 
262447a1685fSDinh Nguyen bad_mps:
262547a1685fSDinh Nguyen 	dev_err(hsotg->dev, "ep%d: bad mps of %d\n", ep, mps);
262647a1685fSDinh Nguyen }
262747a1685fSDinh Nguyen 
262847a1685fSDinh Nguyen /**
26291f91b4ccSFelipe Balbi  * dwc2_hsotg_txfifo_flush - flush Tx FIFO
263047a1685fSDinh Nguyen  * @hsotg: The driver state
263147a1685fSDinh Nguyen  * @idx: The index for the endpoint (0..15)
263247a1685fSDinh Nguyen  */
dwc2_hsotg_txfifo_flush(struct dwc2_hsotg * hsotg,unsigned int idx)26331f91b4ccSFelipe Balbi static void dwc2_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx)
263447a1685fSDinh Nguyen {
2635f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, GRSTCTL_TXFNUM(idx) | GRSTCTL_TXFFLSH,
2636f25c42b8SGevorg Sahakyan 		    GRSTCTL);
263747a1685fSDinh Nguyen 
263847a1685fSDinh Nguyen 	/* wait until the fifo is flushed */
263979d6b8c5SSevak Arakelyan 	if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 100))
264079d6b8c5SSevak Arakelyan 		dev_warn(hsotg->dev, "%s: timeout flushing fifo GRSTCTL_TXFFLSH\n",
264179d6b8c5SSevak Arakelyan 			 __func__);
264247a1685fSDinh Nguyen }
264347a1685fSDinh Nguyen 
264447a1685fSDinh Nguyen /**
26451f91b4ccSFelipe Balbi  * dwc2_hsotg_trytx - check to see if anything needs transmitting
264647a1685fSDinh Nguyen  * @hsotg: The driver state
264747a1685fSDinh Nguyen  * @hs_ep: The driver endpoint to check.
264847a1685fSDinh Nguyen  *
264947a1685fSDinh Nguyen  * Check to see if there is a request that has data to send, and if so
265047a1685fSDinh Nguyen  * make an attempt to write data into the FIFO.
265147a1685fSDinh Nguyen  */
dwc2_hsotg_trytx(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep)26521f91b4ccSFelipe Balbi static int dwc2_hsotg_trytx(struct dwc2_hsotg *hsotg,
26531f91b4ccSFelipe Balbi 			    struct dwc2_hsotg_ep *hs_ep)
265447a1685fSDinh Nguyen {
26551f91b4ccSFelipe Balbi 	struct dwc2_hsotg_req *hs_req = hs_ep->req;
265647a1685fSDinh Nguyen 
265747a1685fSDinh Nguyen 	if (!hs_ep->dir_in || !hs_req) {
265847a1685fSDinh Nguyen 		/**
265947a1685fSDinh Nguyen 		 * if request is not enqueued, we disable interrupts
266047a1685fSDinh Nguyen 		 * for endpoints, excepting ep0
266147a1685fSDinh Nguyen 		 */
266247a1685fSDinh Nguyen 		if (hs_ep->index != 0)
26631f91b4ccSFelipe Balbi 			dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index,
266447a1685fSDinh Nguyen 					      hs_ep->dir_in, 0);
266547a1685fSDinh Nguyen 		return 0;
266647a1685fSDinh Nguyen 	}
266747a1685fSDinh Nguyen 
266847a1685fSDinh Nguyen 	if (hs_req->req.actual < hs_req->req.length) {
266947a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "trying to write more for ep%d\n",
267047a1685fSDinh Nguyen 			hs_ep->index);
26711f91b4ccSFelipe Balbi 		return dwc2_hsotg_write_fifo(hsotg, hs_ep, hs_req);
267247a1685fSDinh Nguyen 	}
267347a1685fSDinh Nguyen 
267447a1685fSDinh Nguyen 	return 0;
267547a1685fSDinh Nguyen }
267647a1685fSDinh Nguyen 
267747a1685fSDinh Nguyen /**
26781f91b4ccSFelipe Balbi  * dwc2_hsotg_complete_in - complete IN transfer
267947a1685fSDinh Nguyen  * @hsotg: The device state.
268047a1685fSDinh Nguyen  * @hs_ep: The endpoint that has just completed.
268147a1685fSDinh Nguyen  *
268247a1685fSDinh Nguyen  * An IN transfer has been completed, update the transfer's state and then
268347a1685fSDinh Nguyen  * call the relevant completion routines.
268447a1685fSDinh Nguyen  */
dwc2_hsotg_complete_in(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep)26851f91b4ccSFelipe Balbi static void dwc2_hsotg_complete_in(struct dwc2_hsotg *hsotg,
26861f91b4ccSFelipe Balbi 				   struct dwc2_hsotg_ep *hs_ep)
268747a1685fSDinh Nguyen {
26881f91b4ccSFelipe Balbi 	struct dwc2_hsotg_req *hs_req = hs_ep->req;
2689f25c42b8SGevorg Sahakyan 	u32 epsize = dwc2_readl(hsotg, DIEPTSIZ(hs_ep->index));
269047a1685fSDinh Nguyen 	int size_left, size_done;
269147a1685fSDinh Nguyen 
269247a1685fSDinh Nguyen 	if (!hs_req) {
269347a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "XferCompl but no req\n");
269447a1685fSDinh Nguyen 		return;
269547a1685fSDinh Nguyen 	}
269647a1685fSDinh Nguyen 
269747a1685fSDinh Nguyen 	/* Finish ZLP handling for IN EP0 transactions */
2698fe0b94abSMian Yousaf Kaukab 	if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_IN) {
2699fe0b94abSMian Yousaf Kaukab 		dev_dbg(hsotg->dev, "zlp packet sent\n");
2700c3b22fe2SRazmik Karapetyan 
2701c3b22fe2SRazmik Karapetyan 		/*
2702c3b22fe2SRazmik Karapetyan 		 * While send zlp for DWC2_EP0_STATUS_IN EP direction was
2703c3b22fe2SRazmik Karapetyan 		 * changed to IN. Change back to complete OUT transfer request
2704c3b22fe2SRazmik Karapetyan 		 */
2705c3b22fe2SRazmik Karapetyan 		hs_ep->dir_in = 0;
2706c3b22fe2SRazmik Karapetyan 
27071f91b4ccSFelipe Balbi 		dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
27089e14d0a5SGregory Herrero 		if (hsotg->test_mode) {
27099e14d0a5SGregory Herrero 			int ret;
27109e14d0a5SGregory Herrero 
27111f91b4ccSFelipe Balbi 			ret = dwc2_hsotg_set_test_mode(hsotg, hsotg->test_mode);
27129e14d0a5SGregory Herrero 			if (ret < 0) {
27139e14d0a5SGregory Herrero 				dev_dbg(hsotg->dev, "Invalid Test #%d\n",
27149e14d0a5SGregory Herrero 					hsotg->test_mode);
27151f91b4ccSFelipe Balbi 				dwc2_hsotg_stall_ep0(hsotg);
27169e14d0a5SGregory Herrero 				return;
27179e14d0a5SGregory Herrero 			}
27189e14d0a5SGregory Herrero 		}
27191f91b4ccSFelipe Balbi 		dwc2_hsotg_enqueue_setup(hsotg);
272047a1685fSDinh Nguyen 		return;
272147a1685fSDinh Nguyen 	}
272247a1685fSDinh Nguyen 
272347a1685fSDinh Nguyen 	/*
272447a1685fSDinh Nguyen 	 * Calculate the size of the transfer by checking how much is left
272547a1685fSDinh Nguyen 	 * in the endpoint size register and then working it out from
272647a1685fSDinh Nguyen 	 * the amount we loaded for the transfer.
272747a1685fSDinh Nguyen 	 *
272847a1685fSDinh Nguyen 	 * We do this even for DMA, as the transfer may have incremented
272947a1685fSDinh Nguyen 	 * past the end of the buffer (DMA transfers are always 32bit
273047a1685fSDinh Nguyen 	 * aligned).
273147a1685fSDinh Nguyen 	 */
2732aa3e8bc8SVahram Aharonyan 	if (using_desc_dma(hsotg)) {
2733aa3e8bc8SVahram Aharonyan 		size_left = dwc2_gadget_get_xfersize_ddma(hs_ep);
2734aa3e8bc8SVahram Aharonyan 		if (size_left < 0)
2735aa3e8bc8SVahram Aharonyan 			dev_err(hsotg->dev, "error parsing DDMA results %d\n",
2736aa3e8bc8SVahram Aharonyan 				size_left);
2737aa3e8bc8SVahram Aharonyan 	} else {
273847a1685fSDinh Nguyen 		size_left = DXEPTSIZ_XFERSIZE_GET(epsize);
2739aa3e8bc8SVahram Aharonyan 	}
274047a1685fSDinh Nguyen 
274147a1685fSDinh Nguyen 	size_done = hs_ep->size_loaded - size_left;
274247a1685fSDinh Nguyen 	size_done += hs_ep->last_load;
274347a1685fSDinh Nguyen 
274447a1685fSDinh Nguyen 	if (hs_req->req.actual != size_done)
274547a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s: adjusting size done %d => %d\n",
274647a1685fSDinh Nguyen 			__func__, hs_req->req.actual, size_done);
274747a1685fSDinh Nguyen 
274847a1685fSDinh Nguyen 	hs_req->req.actual = size_done;
274947a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "req->length:%d req->actual:%d req->zero:%d\n",
275047a1685fSDinh Nguyen 		hs_req->req.length, hs_req->req.actual, hs_req->req.zero);
275147a1685fSDinh Nguyen 
275247a1685fSDinh Nguyen 	if (!size_left && hs_req->req.actual < hs_req->req.length) {
275347a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__);
27541f91b4ccSFelipe Balbi 		dwc2_hsotg_start_req(hsotg, hs_ep, hs_req, true);
2755fe0b94abSMian Yousaf Kaukab 		return;
2756fe0b94abSMian Yousaf Kaukab 	}
2757fe0b94abSMian Yousaf Kaukab 
2758d53dc388SMinas Harutyunyan 	/* Zlp for all endpoints in non DDMA, for ep0 only in DATA IN stage */
27598a20fa45SMian Yousaf Kaukab 	if (hs_ep->send_zlp) {
27608a20fa45SMian Yousaf Kaukab 		hs_ep->send_zlp = 0;
2761d53dc388SMinas Harutyunyan 		if (!using_desc_dma(hsotg)) {
2762d53dc388SMinas Harutyunyan 			dwc2_hsotg_program_zlp(hsotg, hs_ep);
2763f71b5e25SMian Yousaf Kaukab 			/* transfer will be completed on next complete interrupt */
2764f71b5e25SMian Yousaf Kaukab 			return;
2765f71b5e25SMian Yousaf Kaukab 		}
2766d53dc388SMinas Harutyunyan 	}
2767f71b5e25SMian Yousaf Kaukab 
2768fe0b94abSMian Yousaf Kaukab 	if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_DATA_IN) {
2769fe0b94abSMian Yousaf Kaukab 		/* Move to STATUS OUT */
27701f91b4ccSFelipe Balbi 		dwc2_hsotg_ep0_zlp(hsotg, false);
2771fe0b94abSMian Yousaf Kaukab 		return;
2772fe0b94abSMian Yousaf Kaukab 	}
2773fe0b94abSMian Yousaf Kaukab 
277491bb163eSMinas Harutyunyan 	/* Set actual frame number for completed transfers */
277591bb163eSMinas Harutyunyan 	if (!using_desc_dma(hsotg) && hs_ep->isochronous) {
277691bb163eSMinas Harutyunyan 		hs_req->req.frame_number = hs_ep->target_frame;
277791bb163eSMinas Harutyunyan 		dwc2_gadget_incr_frame_num(hs_ep);
277891bb163eSMinas Harutyunyan 	}
277991bb163eSMinas Harutyunyan 
27801f91b4ccSFelipe Balbi 	dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
278147a1685fSDinh Nguyen }
278247a1685fSDinh Nguyen 
278347a1685fSDinh Nguyen /**
278432601588SVardan Mikayelyan  * dwc2_gadget_read_ep_interrupts - reads interrupts for given ep
278532601588SVardan Mikayelyan  * @hsotg: The device state.
278632601588SVardan Mikayelyan  * @idx: Index of ep.
278732601588SVardan Mikayelyan  * @dir_in: Endpoint direction 1-in 0-out.
278832601588SVardan Mikayelyan  *
278932601588SVardan Mikayelyan  * Reads for endpoint with given index and direction, by masking
279032601588SVardan Mikayelyan  * epint_reg with coresponding mask.
279132601588SVardan Mikayelyan  */
dwc2_gadget_read_ep_interrupts(struct dwc2_hsotg * hsotg,unsigned int idx,int dir_in)279232601588SVardan Mikayelyan static u32 dwc2_gadget_read_ep_interrupts(struct dwc2_hsotg *hsotg,
279332601588SVardan Mikayelyan 					  unsigned int idx, int dir_in)
279432601588SVardan Mikayelyan {
279532601588SVardan Mikayelyan 	u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK;
279632601588SVardan Mikayelyan 	u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx);
279732601588SVardan Mikayelyan 	u32 ints;
279832601588SVardan Mikayelyan 	u32 mask;
279932601588SVardan Mikayelyan 	u32 diepempmsk;
280032601588SVardan Mikayelyan 
2801f25c42b8SGevorg Sahakyan 	mask = dwc2_readl(hsotg, epmsk_reg);
2802f25c42b8SGevorg Sahakyan 	diepempmsk = dwc2_readl(hsotg, DIEPEMPMSK);
280332601588SVardan Mikayelyan 	mask |= ((diepempmsk >> idx) & 0x1) ? DIEPMSK_TXFIFOEMPTY : 0;
280432601588SVardan Mikayelyan 	mask |= DXEPINT_SETUP_RCVD;
280532601588SVardan Mikayelyan 
2806f25c42b8SGevorg Sahakyan 	ints = dwc2_readl(hsotg, epint_reg);
280732601588SVardan Mikayelyan 	ints &= mask;
280832601588SVardan Mikayelyan 	return ints;
280932601588SVardan Mikayelyan }
281032601588SVardan Mikayelyan 
281132601588SVardan Mikayelyan /**
2812bd9971f0SVardan Mikayelyan  * dwc2_gadget_handle_ep_disabled - handle DXEPINT_EPDISBLD
2813bd9971f0SVardan Mikayelyan  * @hs_ep: The endpoint on which interrupt is asserted.
2814bd9971f0SVardan Mikayelyan  *
2815bd9971f0SVardan Mikayelyan  * This interrupt indicates that the endpoint has been disabled per the
2816bd9971f0SVardan Mikayelyan  * application's request.
2817bd9971f0SVardan Mikayelyan  *
2818bd9971f0SVardan Mikayelyan  * For IN endpoints flushes txfifo, in case of BULK clears DCTL_CGNPINNAK,
2819bd9971f0SVardan Mikayelyan  * in case of ISOC completes current request.
2820bd9971f0SVardan Mikayelyan  *
2821bd9971f0SVardan Mikayelyan  * For ISOC-OUT endpoints completes expired requests. If there is remaining
2822bd9971f0SVardan Mikayelyan  * request starts it.
2823bd9971f0SVardan Mikayelyan  */
dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep * hs_ep)2824bd9971f0SVardan Mikayelyan static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep)
2825bd9971f0SVardan Mikayelyan {
2826bd9971f0SVardan Mikayelyan 	struct dwc2_hsotg *hsotg = hs_ep->parent;
2827bd9971f0SVardan Mikayelyan 	struct dwc2_hsotg_req *hs_req;
2828bd9971f0SVardan Mikayelyan 	unsigned char idx = hs_ep->index;
2829bd9971f0SVardan Mikayelyan 	int dir_in = hs_ep->dir_in;
2830bd9971f0SVardan Mikayelyan 	u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx);
2831f25c42b8SGevorg Sahakyan 	int dctl = dwc2_readl(hsotg, DCTL);
2832bd9971f0SVardan Mikayelyan 
2833bd9971f0SVardan Mikayelyan 	dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__);
2834bd9971f0SVardan Mikayelyan 
2835bd9971f0SVardan Mikayelyan 	if (dir_in) {
2836f25c42b8SGevorg Sahakyan 		int epctl = dwc2_readl(hsotg, epctl_reg);
2837bd9971f0SVardan Mikayelyan 
2838bd9971f0SVardan Mikayelyan 		dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index);
2839bd9971f0SVardan Mikayelyan 
2840bd9971f0SVardan Mikayelyan 		if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) {
2841f25c42b8SGevorg Sahakyan 			int dctl = dwc2_readl(hsotg, DCTL);
2842bd9971f0SVardan Mikayelyan 
2843bd9971f0SVardan Mikayelyan 			dctl |= DCTL_CGNPINNAK;
2844f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, dctl, DCTL);
2845bd9971f0SVardan Mikayelyan 		}
284691bb163eSMinas Harutyunyan 	} else {
2847bd9971f0SVardan Mikayelyan 
2848bd9971f0SVardan Mikayelyan 		if (dctl & DCTL_GOUTNAKSTS) {
2849bd9971f0SVardan Mikayelyan 			dctl |= DCTL_CGOUTNAK;
2850f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, dctl, DCTL);
2851bd9971f0SVardan Mikayelyan 		}
285291bb163eSMinas Harutyunyan 	}
2853bd9971f0SVardan Mikayelyan 
2854bd9971f0SVardan Mikayelyan 	if (!hs_ep->isochronous)
2855bd9971f0SVardan Mikayelyan 		return;
2856bd9971f0SVardan Mikayelyan 
2857bd9971f0SVardan Mikayelyan 	if (list_empty(&hs_ep->queue)) {
2858bd9971f0SVardan Mikayelyan 		dev_dbg(hsotg->dev, "%s: complete_ep 0x%p, ep->queue empty!\n",
2859bd9971f0SVardan Mikayelyan 			__func__, hs_ep);
2860bd9971f0SVardan Mikayelyan 		return;
2861bd9971f0SVardan Mikayelyan 	}
2862bd9971f0SVardan Mikayelyan 
2863bd9971f0SVardan Mikayelyan 	do {
2864bd9971f0SVardan Mikayelyan 		hs_req = get_ep_head(hs_ep);
28657ad4a0b1SMinas Harutyunyan 		if (hs_req) {
28667ad4a0b1SMinas Harutyunyan 			hs_req->req.frame_number = hs_ep->target_frame;
28677ad4a0b1SMinas Harutyunyan 			hs_req->req.actual = 0;
2868bd9971f0SVardan Mikayelyan 			dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req,
2869bd9971f0SVardan Mikayelyan 						    -ENODATA);
28707ad4a0b1SMinas Harutyunyan 		}
2871bd9971f0SVardan Mikayelyan 		dwc2_gadget_incr_frame_num(hs_ep);
2872c7c24e7aSArtur Petrosyan 		/* Update current frame number value. */
2873c7c24e7aSArtur Petrosyan 		hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg);
2874bd9971f0SVardan Mikayelyan 	} while (dwc2_gadget_target_frame_elapsed(hs_ep));
2875bd9971f0SVardan Mikayelyan }
2876bd9971f0SVardan Mikayelyan 
2877bd9971f0SVardan Mikayelyan /**
28785321922cSVardan Mikayelyan  * dwc2_gadget_handle_out_token_ep_disabled - handle DXEPINT_OUTTKNEPDIS
28796fb914d7SGrigor Tovmasyan  * @ep: The endpoint on which interrupt is asserted.
28805321922cSVardan Mikayelyan  *
28815321922cSVardan Mikayelyan  * This is starting point for ISOC-OUT transfer, synchronization done with
28825321922cSVardan Mikayelyan  * first out token received from host while corresponding EP is disabled.
28835321922cSVardan Mikayelyan  *
28845321922cSVardan Mikayelyan  * Device does not know initial frame in which out token will come. For this
28855321922cSVardan Mikayelyan  * HW generates OUTTKNEPDIS - out token is received while EP is disabled. Upon
28865321922cSVardan Mikayelyan  * getting this interrupt SW starts calculation for next transfer frame.
28875321922cSVardan Mikayelyan  */
dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep * ep)28885321922cSVardan Mikayelyan static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep)
28895321922cSVardan Mikayelyan {
28905321922cSVardan Mikayelyan 	struct dwc2_hsotg *hsotg = ep->parent;
289191bb163eSMinas Harutyunyan 	struct dwc2_hsotg_req *hs_req;
28925321922cSVardan Mikayelyan 	int dir_in = ep->dir_in;
28935321922cSVardan Mikayelyan 
28945321922cSVardan Mikayelyan 	if (dir_in || !ep->isochronous)
28955321922cSVardan Mikayelyan 		return;
28965321922cSVardan Mikayelyan 
2897540ccba0SVahram Aharonyan 	if (using_desc_dma(hsotg)) {
2898540ccba0SVahram Aharonyan 		if (ep->target_frame == TARGET_FRAME_INITIAL) {
2899540ccba0SVahram Aharonyan 			/* Start first ISO Out */
29004d4f1e79SMinas Harutyunyan 			ep->target_frame = hsotg->frame_number;
2901540ccba0SVahram Aharonyan 			dwc2_gadget_start_isoc_ddma(ep);
2902540ccba0SVahram Aharonyan 		}
2903540ccba0SVahram Aharonyan 		return;
2904540ccba0SVahram Aharonyan 	}
2905540ccba0SVahram Aharonyan 
290691bb163eSMinas Harutyunyan 	if (ep->target_frame == TARGET_FRAME_INITIAL) {
29075321922cSVardan Mikayelyan 		u32 ctrl;
29085321922cSVardan Mikayelyan 
29094d4f1e79SMinas Harutyunyan 		ep->target_frame = hsotg->frame_number;
291091bb163eSMinas Harutyunyan 		if (ep->interval > 1) {
2911f25c42b8SGevorg Sahakyan 			ctrl = dwc2_readl(hsotg, DOEPCTL(ep->index));
29125321922cSVardan Mikayelyan 			if (ep->target_frame & 0x1)
29135321922cSVardan Mikayelyan 				ctrl |= DXEPCTL_SETODDFR;
29145321922cSVardan Mikayelyan 			else
29155321922cSVardan Mikayelyan 				ctrl |= DXEPCTL_SETEVENFR;
29165321922cSVardan Mikayelyan 
2917f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, ctrl, DOEPCTL(ep->index));
29185321922cSVardan Mikayelyan 		}
29195321922cSVardan Mikayelyan 	}
29205321922cSVardan Mikayelyan 
292191bb163eSMinas Harutyunyan 	while (dwc2_gadget_target_frame_elapsed(ep)) {
292291bb163eSMinas Harutyunyan 		hs_req = get_ep_head(ep);
29237ad4a0b1SMinas Harutyunyan 		if (hs_req) {
29247ad4a0b1SMinas Harutyunyan 			hs_req->req.frame_number = ep->target_frame;
29257ad4a0b1SMinas Harutyunyan 			hs_req->req.actual = 0;
292691bb163eSMinas Harutyunyan 			dwc2_hsotg_complete_request(hsotg, ep, hs_req, -ENODATA);
29277ad4a0b1SMinas Harutyunyan 		}
292891bb163eSMinas Harutyunyan 
292991bb163eSMinas Harutyunyan 		dwc2_gadget_incr_frame_num(ep);
293091bb163eSMinas Harutyunyan 		/* Update current frame number value. */
293191bb163eSMinas Harutyunyan 		hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg);
293291bb163eSMinas Harutyunyan 	}
293391bb163eSMinas Harutyunyan 
293491bb163eSMinas Harutyunyan 	if (!ep->req)
293591bb163eSMinas Harutyunyan 		dwc2_gadget_start_next_request(ep);
293691bb163eSMinas Harutyunyan 
293791bb163eSMinas Harutyunyan }
293891bb163eSMinas Harutyunyan 
293991bb163eSMinas Harutyunyan static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
294091bb163eSMinas Harutyunyan 				   struct dwc2_hsotg_ep *hs_ep);
294191bb163eSMinas Harutyunyan 
29425321922cSVardan Mikayelyan /**
29435321922cSVardan Mikayelyan  * dwc2_gadget_handle_nak - handle NAK interrupt
29445321922cSVardan Mikayelyan  * @hs_ep: The endpoint on which interrupt is asserted.
29455321922cSVardan Mikayelyan  *
29465321922cSVardan Mikayelyan  * This is starting point for ISOC-IN transfer, synchronization done with
29475321922cSVardan Mikayelyan  * first IN token received from host while corresponding EP is disabled.
29485321922cSVardan Mikayelyan  *
29495321922cSVardan Mikayelyan  * Device does not know when first one token will arrive from host. On first
29505321922cSVardan Mikayelyan  * token arrival HW generates 2 interrupts: 'in token received while FIFO empty'
29515321922cSVardan Mikayelyan  * and 'NAK'. NAK interrupt for ISOC-IN means that token has arrived and ZLP was
29525321922cSVardan Mikayelyan  * sent in response to that as there was no data in FIFO. SW is basing on this
29535321922cSVardan Mikayelyan  * interrupt to obtain frame in which token has come and then based on the
29545321922cSVardan Mikayelyan  * interval calculates next frame for transfer.
29555321922cSVardan Mikayelyan  */
dwc2_gadget_handle_nak(struct dwc2_hsotg_ep * hs_ep)29565321922cSVardan Mikayelyan static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep)
29575321922cSVardan Mikayelyan {
29585321922cSVardan Mikayelyan 	struct dwc2_hsotg *hsotg = hs_ep->parent;
295991bb163eSMinas Harutyunyan 	struct dwc2_hsotg_req *hs_req;
29605321922cSVardan Mikayelyan 	int dir_in = hs_ep->dir_in;
296191bb163eSMinas Harutyunyan 	u32 ctrl;
29625321922cSVardan Mikayelyan 
29635321922cSVardan Mikayelyan 	if (!dir_in || !hs_ep->isochronous)
29645321922cSVardan Mikayelyan 		return;
29655321922cSVardan Mikayelyan 
29665321922cSVardan Mikayelyan 	if (hs_ep->target_frame == TARGET_FRAME_INITIAL) {
2967540ccba0SVahram Aharonyan 
2968540ccba0SVahram Aharonyan 		if (using_desc_dma(hsotg)) {
29694d4f1e79SMinas Harutyunyan 			hs_ep->target_frame = hsotg->frame_number;
2970729cac69SMinas Harutyunyan 			dwc2_gadget_incr_frame_num(hs_ep);
297148dac4e4SGrigor Tovmasyan 
297248dac4e4SGrigor Tovmasyan 			/* In service interval mode target_frame must
297348dac4e4SGrigor Tovmasyan 			 * be set to last (u)frame of the service interval.
297448dac4e4SGrigor Tovmasyan 			 */
297548dac4e4SGrigor Tovmasyan 			if (hsotg->params.service_interval) {
297648dac4e4SGrigor Tovmasyan 				/* Set target_frame to the first (u)frame of
297748dac4e4SGrigor Tovmasyan 				 * the service interval
297848dac4e4SGrigor Tovmasyan 				 */
297948dac4e4SGrigor Tovmasyan 				hs_ep->target_frame &= ~hs_ep->interval + 1;
298048dac4e4SGrigor Tovmasyan 
298148dac4e4SGrigor Tovmasyan 				/* Set target_frame to the last (u)frame of
298248dac4e4SGrigor Tovmasyan 				 * the service interval
298348dac4e4SGrigor Tovmasyan 				 */
298448dac4e4SGrigor Tovmasyan 				dwc2_gadget_incr_frame_num(hs_ep);
298548dac4e4SGrigor Tovmasyan 				dwc2_gadget_dec_frame_num_by_one(hs_ep);
298648dac4e4SGrigor Tovmasyan 			}
298748dac4e4SGrigor Tovmasyan 
2988540ccba0SVahram Aharonyan 			dwc2_gadget_start_isoc_ddma(hs_ep);
2989540ccba0SVahram Aharonyan 			return;
2990540ccba0SVahram Aharonyan 		}
2991540ccba0SVahram Aharonyan 
29924d4f1e79SMinas Harutyunyan 		hs_ep->target_frame = hsotg->frame_number;
29935321922cSVardan Mikayelyan 		if (hs_ep->interval > 1) {
2994f25c42b8SGevorg Sahakyan 			u32 ctrl = dwc2_readl(hsotg,
29955321922cSVardan Mikayelyan 					      DIEPCTL(hs_ep->index));
29965321922cSVardan Mikayelyan 			if (hs_ep->target_frame & 0x1)
29975321922cSVardan Mikayelyan 				ctrl |= DXEPCTL_SETODDFR;
29985321922cSVardan Mikayelyan 			else
29995321922cSVardan Mikayelyan 				ctrl |= DXEPCTL_SETEVENFR;
30005321922cSVardan Mikayelyan 
3001f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, ctrl, DIEPCTL(hs_ep->index));
30025321922cSVardan Mikayelyan 		}
30035321922cSVardan Mikayelyan 	}
30045321922cSVardan Mikayelyan 
300591bb163eSMinas Harutyunyan 	if (using_desc_dma(hsotg))
300691bb163eSMinas Harutyunyan 		return;
300791bb163eSMinas Harutyunyan 
300891bb163eSMinas Harutyunyan 	ctrl = dwc2_readl(hsotg, DIEPCTL(hs_ep->index));
300991bb163eSMinas Harutyunyan 	if (ctrl & DXEPCTL_EPENA)
301091bb163eSMinas Harutyunyan 		dwc2_hsotg_ep_stop_xfr(hsotg, hs_ep);
301191bb163eSMinas Harutyunyan 	else
301291bb163eSMinas Harutyunyan 		dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index);
301391bb163eSMinas Harutyunyan 
301491bb163eSMinas Harutyunyan 	while (dwc2_gadget_target_frame_elapsed(hs_ep)) {
301591bb163eSMinas Harutyunyan 		hs_req = get_ep_head(hs_ep);
30167ad4a0b1SMinas Harutyunyan 		if (hs_req) {
30177ad4a0b1SMinas Harutyunyan 			hs_req->req.frame_number = hs_ep->target_frame;
30187ad4a0b1SMinas Harutyunyan 			hs_req->req.actual = 0;
301991bb163eSMinas Harutyunyan 			dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, -ENODATA);
30207ad4a0b1SMinas Harutyunyan 		}
302191bb163eSMinas Harutyunyan 
30225321922cSVardan Mikayelyan 		dwc2_gadget_incr_frame_num(hs_ep);
302391bb163eSMinas Harutyunyan 		/* Update current frame number value. */
302491bb163eSMinas Harutyunyan 		hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg);
302591bb163eSMinas Harutyunyan 	}
302691bb163eSMinas Harutyunyan 
302791bb163eSMinas Harutyunyan 	if (!hs_ep->req)
302891bb163eSMinas Harutyunyan 		dwc2_gadget_start_next_request(hs_ep);
30295321922cSVardan Mikayelyan }
30305321922cSVardan Mikayelyan 
30315321922cSVardan Mikayelyan /**
30321f91b4ccSFelipe Balbi  * dwc2_hsotg_epint - handle an in/out endpoint interrupt
303347a1685fSDinh Nguyen  * @hsotg: The driver state
303447a1685fSDinh Nguyen  * @idx: The index for the endpoint (0..15)
303547a1685fSDinh Nguyen  * @dir_in: Set if this is an IN endpoint
303647a1685fSDinh Nguyen  *
303747a1685fSDinh Nguyen  * Process and clear any interrupt pending for an individual endpoint
303847a1685fSDinh Nguyen  */
dwc2_hsotg_epint(struct dwc2_hsotg * hsotg,unsigned int idx,int dir_in)30391f91b4ccSFelipe Balbi static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
304047a1685fSDinh Nguyen 			     int dir_in)
304147a1685fSDinh Nguyen {
30421f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *hs_ep = index_to_ep(hsotg, idx, dir_in);
304347a1685fSDinh Nguyen 	u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx);
304447a1685fSDinh Nguyen 	u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx);
304547a1685fSDinh Nguyen 	u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx);
304647a1685fSDinh Nguyen 	u32 ints;
304747a1685fSDinh Nguyen 
304832601588SVardan Mikayelyan 	ints = dwc2_gadget_read_ep_interrupts(hsotg, idx, dir_in);
304947a1685fSDinh Nguyen 
305047a1685fSDinh Nguyen 	/* Clear endpoint interrupts */
3051f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, ints, epint_reg);
305247a1685fSDinh Nguyen 
3053c6f5c050SMian Yousaf Kaukab 	if (!hs_ep) {
3054c6f5c050SMian Yousaf Kaukab 		dev_err(hsotg->dev, "%s:Interrupt for unconfigured ep%d(%s)\n",
3055c6f5c050SMian Yousaf Kaukab 			__func__, idx, dir_in ? "in" : "out");
3056c6f5c050SMian Yousaf Kaukab 		return;
3057c6f5c050SMian Yousaf Kaukab 	}
3058c6f5c050SMian Yousaf Kaukab 
305947a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: ep%d(%s) DxEPINT=0x%08x\n",
306047a1685fSDinh Nguyen 		__func__, idx, dir_in ? "in" : "out", ints);
306147a1685fSDinh Nguyen 
3062b787d755SMian Yousaf Kaukab 	/* Don't process XferCompl interrupt if it is a setup packet */
3063b787d755SMian Yousaf Kaukab 	if (idx == 0 && (ints & (DXEPINT_SETUP | DXEPINT_SETUP_RCVD)))
3064b787d755SMian Yousaf Kaukab 		ints &= ~DXEPINT_XFERCOMPL;
3065b787d755SMian Yousaf Kaukab 
3066f0afdb42SVahram Aharonyan 	/*
3067f0afdb42SVahram Aharonyan 	 * Don't process XferCompl interrupt in DDMA if EP0 is still in SETUP
3068f0afdb42SVahram Aharonyan 	 * stage and xfercomplete was generated without SETUP phase done
3069f0afdb42SVahram Aharonyan 	 * interrupt. SW should parse received setup packet only after host's
3070f0afdb42SVahram Aharonyan 	 * exit from setup phase of control transfer.
3071f0afdb42SVahram Aharonyan 	 */
3072f0afdb42SVahram Aharonyan 	if (using_desc_dma(hsotg) && idx == 0 && !hs_ep->dir_in &&
3073f0afdb42SVahram Aharonyan 	    hsotg->ep0_state == DWC2_EP0_SETUP && !(ints & DXEPINT_SETUP))
3074f0afdb42SVahram Aharonyan 		ints &= ~DXEPINT_XFERCOMPL;
3075f0afdb42SVahram Aharonyan 
3076837e9f00SVardan Mikayelyan 	if (ints & DXEPINT_XFERCOMPL) {
307747a1685fSDinh Nguyen 		dev_dbg(hsotg->dev,
307847a1685fSDinh Nguyen 			"%s: XferCompl: DxEPCTL=0x%08x, DXEPTSIZ=%08x\n",
3079f25c42b8SGevorg Sahakyan 			__func__, dwc2_readl(hsotg, epctl_reg),
3080f25c42b8SGevorg Sahakyan 			dwc2_readl(hsotg, epsiz_reg));
308147a1685fSDinh Nguyen 
3082540ccba0SVahram Aharonyan 		/* In DDMA handle isochronous requests separately */
3083540ccba0SVahram Aharonyan 		if (using_desc_dma(hsotg) && hs_ep->isochronous) {
3084540ccba0SVahram Aharonyan 			dwc2_gadget_complete_isoc_request_ddma(hs_ep);
3085540ccba0SVahram Aharonyan 		} else if (dir_in) {
308647a1685fSDinh Nguyen 			/*
3087540ccba0SVahram Aharonyan 			 * We get OutDone from the FIFO, so we only
3088540ccba0SVahram Aharonyan 			 * need to look at completing IN requests here
3089540ccba0SVahram Aharonyan 			 * if operating slave mode
309047a1685fSDinh Nguyen 			 */
309191bb163eSMinas Harutyunyan 			if (!hs_ep->isochronous || !(ints & DXEPINT_NAKINTRPT))
30921f91b4ccSFelipe Balbi 				dwc2_hsotg_complete_in(hsotg, hs_ep);
309347a1685fSDinh Nguyen 
309447a1685fSDinh Nguyen 			if (idx == 0 && !hs_ep->req)
30951f91b4ccSFelipe Balbi 				dwc2_hsotg_enqueue_setup(hsotg);
309647a1685fSDinh Nguyen 		} else if (using_dma(hsotg)) {
309747a1685fSDinh Nguyen 			/*
309847a1685fSDinh Nguyen 			 * We're using DMA, we need to fire an OutDone here
309947a1685fSDinh Nguyen 			 * as we ignore the RXFIFO.
310047a1685fSDinh Nguyen 			 */
310191bb163eSMinas Harutyunyan 			if (!hs_ep->isochronous || !(ints & DXEPINT_OUTTKNEPDIS))
31021f91b4ccSFelipe Balbi 				dwc2_hsotg_handle_outdone(hsotg, idx);
310347a1685fSDinh Nguyen 		}
310447a1685fSDinh Nguyen 	}
310547a1685fSDinh Nguyen 
3106bd9971f0SVardan Mikayelyan 	if (ints & DXEPINT_EPDISBLD)
3107bd9971f0SVardan Mikayelyan 		dwc2_gadget_handle_ep_disabled(hs_ep);
310847a1685fSDinh Nguyen 
31095321922cSVardan Mikayelyan 	if (ints & DXEPINT_OUTTKNEPDIS)
31105321922cSVardan Mikayelyan 		dwc2_gadget_handle_out_token_ep_disabled(hs_ep);
31115321922cSVardan Mikayelyan 
31125321922cSVardan Mikayelyan 	if (ints & DXEPINT_NAKINTRPT)
31135321922cSVardan Mikayelyan 		dwc2_gadget_handle_nak(hs_ep);
31145321922cSVardan Mikayelyan 
311547a1685fSDinh Nguyen 	if (ints & DXEPINT_AHBERR)
311647a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s: AHBErr\n", __func__);
311747a1685fSDinh Nguyen 
311847a1685fSDinh Nguyen 	if (ints & DXEPINT_SETUP) {  /* Setup or Timeout */
311947a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s: Setup/Timeout\n",  __func__);
312047a1685fSDinh Nguyen 
312147a1685fSDinh Nguyen 		if (using_dma(hsotg) && idx == 0) {
312247a1685fSDinh Nguyen 			/*
312347a1685fSDinh Nguyen 			 * this is the notification we've received a
312447a1685fSDinh Nguyen 			 * setup packet. In non-DMA mode we'd get this
312547a1685fSDinh Nguyen 			 * from the RXFIFO, instead we need to process
312647a1685fSDinh Nguyen 			 * the setup here.
312747a1685fSDinh Nguyen 			 */
312847a1685fSDinh Nguyen 
312947a1685fSDinh Nguyen 			if (dir_in)
313047a1685fSDinh Nguyen 				WARN_ON_ONCE(1);
313147a1685fSDinh Nguyen 			else
31321f91b4ccSFelipe Balbi 				dwc2_hsotg_handle_outdone(hsotg, 0);
313347a1685fSDinh Nguyen 		}
313447a1685fSDinh Nguyen 	}
313547a1685fSDinh Nguyen 
3136ef750c71SVahram Aharonyan 	if (ints & DXEPINT_STSPHSERCVD) {
31379d9a6b07SVahram Aharonyan 		dev_dbg(hsotg->dev, "%s: StsPhseRcvd\n", __func__);
31389d9a6b07SVahram Aharonyan 
31399e95a66cSMinas Harutyunyan 		/* Safety check EP0 state when STSPHSERCVD asserted */
31409e95a66cSMinas Harutyunyan 		if (hsotg->ep0_state == DWC2_EP0_DATA_OUT) {
3141ef750c71SVahram Aharonyan 			/* Move to STATUS IN for DDMA */
3142b4c53b4aSMinas Harutyunyan 			if (using_desc_dma(hsotg)) {
3143b4c53b4aSMinas Harutyunyan 				if (!hsotg->delayed_status)
3144ef750c71SVahram Aharonyan 					dwc2_hsotg_ep0_zlp(hsotg, true);
3145b4c53b4aSMinas Harutyunyan 				else
3146b4c53b4aSMinas Harutyunyan 				/* In case of 3 stage Control Write with delayed
3147b4c53b4aSMinas Harutyunyan 				 * status, when Status IN transfer started
3148b4c53b4aSMinas Harutyunyan 				 * before STSPHSERCVD asserted, NAKSTS bit not
3149b4c53b4aSMinas Harutyunyan 				 * cleared by CNAK in dwc2_hsotg_start_req()
3150b4c53b4aSMinas Harutyunyan 				 * function. Clear now NAKSTS to allow complete
3151b4c53b4aSMinas Harutyunyan 				 * transfer.
3152b4c53b4aSMinas Harutyunyan 				 */
3153b4c53b4aSMinas Harutyunyan 					dwc2_set_bit(hsotg, DIEPCTL(0),
3154b4c53b4aSMinas Harutyunyan 						     DXEPCTL_CNAK);
3155b4c53b4aSMinas Harutyunyan 			}
3156ef750c71SVahram Aharonyan 		}
3157ef750c71SVahram Aharonyan 
31589e95a66cSMinas Harutyunyan 	}
31599e95a66cSMinas Harutyunyan 
316047a1685fSDinh Nguyen 	if (ints & DXEPINT_BACK2BACKSETUP)
316147a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__);
316247a1685fSDinh Nguyen 
3163540ccba0SVahram Aharonyan 	if (ints & DXEPINT_BNAINTR) {
3164540ccba0SVahram Aharonyan 		dev_dbg(hsotg->dev, "%s: BNA interrupt\n", __func__);
3165540ccba0SVahram Aharonyan 		if (hs_ep->isochronous)
3166729cac69SMinas Harutyunyan 			dwc2_gadget_handle_isoc_bna(hs_ep);
3167540ccba0SVahram Aharonyan 	}
3168540ccba0SVahram Aharonyan 
316947a1685fSDinh Nguyen 	if (dir_in && !hs_ep->isochronous) {
317047a1685fSDinh Nguyen 		/* not sure if this is important, but we'll clear it anyway */
317126ddef5dSVardan Mikayelyan 		if (ints & DXEPINT_INTKNTXFEMP) {
317247a1685fSDinh Nguyen 			dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n",
317347a1685fSDinh Nguyen 				__func__, idx);
317447a1685fSDinh Nguyen 		}
317547a1685fSDinh Nguyen 
317647a1685fSDinh Nguyen 		/* this probably means something bad is happening */
317726ddef5dSVardan Mikayelyan 		if (ints & DXEPINT_INTKNEPMIS) {
317847a1685fSDinh Nguyen 			dev_warn(hsotg->dev, "%s: ep%d: INTknEP\n",
317947a1685fSDinh Nguyen 				 __func__, idx);
318047a1685fSDinh Nguyen 		}
318147a1685fSDinh Nguyen 
318247a1685fSDinh Nguyen 		/* FIFO has space or is empty (see GAHBCFG) */
318347a1685fSDinh Nguyen 		if (hsotg->dedicated_fifos &&
318426ddef5dSVardan Mikayelyan 		    ints & DXEPINT_TXFEMP) {
318547a1685fSDinh Nguyen 			dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n",
318647a1685fSDinh Nguyen 				__func__, idx);
318747a1685fSDinh Nguyen 			if (!using_dma(hsotg))
31881f91b4ccSFelipe Balbi 				dwc2_hsotg_trytx(hsotg, hs_ep);
318947a1685fSDinh Nguyen 		}
319047a1685fSDinh Nguyen 	}
319147a1685fSDinh Nguyen }
319247a1685fSDinh Nguyen 
319347a1685fSDinh Nguyen /**
31941f91b4ccSFelipe Balbi  * dwc2_hsotg_irq_enumdone - Handle EnumDone interrupt (enumeration done)
319547a1685fSDinh Nguyen  * @hsotg: The device state.
319647a1685fSDinh Nguyen  *
319747a1685fSDinh Nguyen  * Handle updating the device settings after the enumeration phase has
319847a1685fSDinh Nguyen  * been completed.
319947a1685fSDinh Nguyen  */
dwc2_hsotg_irq_enumdone(struct dwc2_hsotg * hsotg)32001f91b4ccSFelipe Balbi static void dwc2_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
320147a1685fSDinh Nguyen {
3202f25c42b8SGevorg Sahakyan 	u32 dsts = dwc2_readl(hsotg, DSTS);
32039b2667f1SJingoo Han 	int ep0_mps = 0, ep_mps = 8;
320447a1685fSDinh Nguyen 
320547a1685fSDinh Nguyen 	/*
320647a1685fSDinh Nguyen 	 * This should signal the finish of the enumeration phase
320747a1685fSDinh Nguyen 	 * of the USB handshaking, so we should now know what rate
320847a1685fSDinh Nguyen 	 * we connected at.
320947a1685fSDinh Nguyen 	 */
321047a1685fSDinh Nguyen 
321147a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "EnumDone (DSTS=0x%08x)\n", dsts);
321247a1685fSDinh Nguyen 
321347a1685fSDinh Nguyen 	/*
321447a1685fSDinh Nguyen 	 * note, since we're limited by the size of transfer on EP0, and
321547a1685fSDinh Nguyen 	 * it seems IN transfers must be a even number of packets we do
321647a1685fSDinh Nguyen 	 * not advertise a 64byte MPS on EP0.
321747a1685fSDinh Nguyen 	 */
321847a1685fSDinh Nguyen 
321947a1685fSDinh Nguyen 	/* catch both EnumSpd_FS and EnumSpd_FS48 */
32206d76c92cSMarek Vasut 	switch ((dsts & DSTS_ENUMSPD_MASK) >> DSTS_ENUMSPD_SHIFT) {
322147a1685fSDinh Nguyen 	case DSTS_ENUMSPD_FS:
322247a1685fSDinh Nguyen 	case DSTS_ENUMSPD_FS48:
322347a1685fSDinh Nguyen 		hsotg->gadget.speed = USB_SPEED_FULL;
322447a1685fSDinh Nguyen 		ep0_mps = EP0_MPS_LIMIT;
322547a1685fSDinh Nguyen 		ep_mps = 1023;
322647a1685fSDinh Nguyen 		break;
322747a1685fSDinh Nguyen 
322847a1685fSDinh Nguyen 	case DSTS_ENUMSPD_HS:
322947a1685fSDinh Nguyen 		hsotg->gadget.speed = USB_SPEED_HIGH;
323047a1685fSDinh Nguyen 		ep0_mps = EP0_MPS_LIMIT;
323147a1685fSDinh Nguyen 		ep_mps = 1024;
323247a1685fSDinh Nguyen 		break;
323347a1685fSDinh Nguyen 
323447a1685fSDinh Nguyen 	case DSTS_ENUMSPD_LS:
323547a1685fSDinh Nguyen 		hsotg->gadget.speed = USB_SPEED_LOW;
3236552d940fSVardan Mikayelyan 		ep0_mps = 8;
3237552d940fSVardan Mikayelyan 		ep_mps = 8;
323847a1685fSDinh Nguyen 		/*
323947a1685fSDinh Nguyen 		 * note, we don't actually support LS in this driver at the
324047a1685fSDinh Nguyen 		 * moment, and the documentation seems to imply that it isn't
324147a1685fSDinh Nguyen 		 * supported by the PHYs on some of the devices.
324247a1685fSDinh Nguyen 		 */
324347a1685fSDinh Nguyen 		break;
324447a1685fSDinh Nguyen 	}
324547a1685fSDinh Nguyen 	dev_info(hsotg->dev, "new device is %s\n",
324647a1685fSDinh Nguyen 		 usb_speed_string(hsotg->gadget.speed));
324747a1685fSDinh Nguyen 
324847a1685fSDinh Nguyen 	/*
324947a1685fSDinh Nguyen 	 * we should now know the maximum packet size for an
325047a1685fSDinh Nguyen 	 * endpoint, so set the endpoints to a default value.
325147a1685fSDinh Nguyen 	 */
325247a1685fSDinh Nguyen 
325347a1685fSDinh Nguyen 	if (ep0_mps) {
325447a1685fSDinh Nguyen 		int i;
3255c6f5c050SMian Yousaf Kaukab 		/* Initialize ep0 for both in and out directions */
3256ee2c40deSVardan Mikayelyan 		dwc2_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 0, 1);
3257ee2c40deSVardan Mikayelyan 		dwc2_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 0, 0);
3258c6f5c050SMian Yousaf Kaukab 		for (i = 1; i < hsotg->num_of_eps; i++) {
3259c6f5c050SMian Yousaf Kaukab 			if (hsotg->eps_in[i])
3260ee2c40deSVardan Mikayelyan 				dwc2_hsotg_set_ep_maxpacket(hsotg, i, ep_mps,
3261ee2c40deSVardan Mikayelyan 							    0, 1);
3262c6f5c050SMian Yousaf Kaukab 			if (hsotg->eps_out[i])
3263ee2c40deSVardan Mikayelyan 				dwc2_hsotg_set_ep_maxpacket(hsotg, i, ep_mps,
3264ee2c40deSVardan Mikayelyan 							    0, 0);
3265c6f5c050SMian Yousaf Kaukab 		}
326647a1685fSDinh Nguyen 	}
326747a1685fSDinh Nguyen 
326847a1685fSDinh Nguyen 	/* ensure after enumeration our EP0 is active */
326947a1685fSDinh Nguyen 
32701f91b4ccSFelipe Balbi 	dwc2_hsotg_enqueue_setup(hsotg);
327147a1685fSDinh Nguyen 
327247a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
3273f25c42b8SGevorg Sahakyan 		dwc2_readl(hsotg, DIEPCTL0),
3274f25c42b8SGevorg Sahakyan 		dwc2_readl(hsotg, DOEPCTL0));
327547a1685fSDinh Nguyen }
327647a1685fSDinh Nguyen 
327747a1685fSDinh Nguyen /**
327847a1685fSDinh Nguyen  * kill_all_requests - remove all requests from the endpoint's queue
327947a1685fSDinh Nguyen  * @hsotg: The device state.
328047a1685fSDinh Nguyen  * @ep: The endpoint the requests may be on.
328147a1685fSDinh Nguyen  * @result: The result code to use.
328247a1685fSDinh Nguyen  *
328347a1685fSDinh Nguyen  * Go through the requests on the given endpoint and mark them
328447a1685fSDinh Nguyen  * completed with the given result code.
328547a1685fSDinh Nguyen  */
kill_all_requests(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * ep,int result)3286941fcce4SDinh Nguyen static void kill_all_requests(struct dwc2_hsotg *hsotg,
32871f91b4ccSFelipe Balbi 			      struct dwc2_hsotg_ep *ep,
32886b448af4SRobert Baldyga 			      int result)
328947a1685fSDinh Nguyen {
32909da51974SJohn Youn 	unsigned int size;
329147a1685fSDinh Nguyen 
32926b448af4SRobert Baldyga 	ep->req = NULL;
329347a1685fSDinh Nguyen 
329437bea42fSJohn Keeping 	while (!list_empty(&ep->queue)) {
329537bea42fSJohn Keeping 		struct dwc2_hsotg_req *req = get_ep_head(ep);
329637bea42fSJohn Keeping 
329737bea42fSJohn Keeping 		dwc2_hsotg_complete_request(hsotg, ep, req, result);
329837bea42fSJohn Keeping 	}
32996b448af4SRobert Baldyga 
3300b203d0a2SRobert Baldyga 	if (!hsotg->dedicated_fifos)
3301b203d0a2SRobert Baldyga 		return;
3302f25c42b8SGevorg Sahakyan 	size = (dwc2_readl(hsotg, DTXFSTS(ep->fifo_index)) & 0xffff) * 4;
3303b203d0a2SRobert Baldyga 	if (size < ep->fifo_size)
33041f91b4ccSFelipe Balbi 		dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index);
330547a1685fSDinh Nguyen }
330647a1685fSDinh Nguyen 
330747a1685fSDinh Nguyen /**
33081f91b4ccSFelipe Balbi  * dwc2_hsotg_disconnect - disconnect service
330947a1685fSDinh Nguyen  * @hsotg: The device state.
331047a1685fSDinh Nguyen  *
331147a1685fSDinh Nguyen  * The device has been disconnected. Remove all current
331247a1685fSDinh Nguyen  * transactions and signal the gadget driver that this
331347a1685fSDinh Nguyen  * has happened.
331447a1685fSDinh Nguyen  */
dwc2_hsotg_disconnect(struct dwc2_hsotg * hsotg)33151f91b4ccSFelipe Balbi void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg)
331647a1685fSDinh Nguyen {
33179da51974SJohn Youn 	unsigned int ep;
331847a1685fSDinh Nguyen 
33194ace06e8SMarek Szyprowski 	if (!hsotg->connected)
33204ace06e8SMarek Szyprowski 		return;
33214ace06e8SMarek Szyprowski 
33224ace06e8SMarek Szyprowski 	hsotg->connected = 0;
33239e14d0a5SGregory Herrero 	hsotg->test_mode = 0;
3324c6f5c050SMian Yousaf Kaukab 
3325dccf1badSMinas Harutyunyan 	/* all endpoints should be shutdown */
3326c6f5c050SMian Yousaf Kaukab 	for (ep = 0; ep < hsotg->num_of_eps; ep++) {
3327c6f5c050SMian Yousaf Kaukab 		if (hsotg->eps_in[ep])
33284fe4f9feSMinas Harutyunyan 			kill_all_requests(hsotg, hsotg->eps_in[ep],
33294fe4f9feSMinas Harutyunyan 					  -ESHUTDOWN);
3330c6f5c050SMian Yousaf Kaukab 		if (hsotg->eps_out[ep])
33314fe4f9feSMinas Harutyunyan 			kill_all_requests(hsotg, hsotg->eps_out[ep],
33324fe4f9feSMinas Harutyunyan 					  -ESHUTDOWN);
3333c6f5c050SMian Yousaf Kaukab 	}
333447a1685fSDinh Nguyen 
333547a1685fSDinh Nguyen 	call_gadget(hsotg, disconnect);
3336065d3931SGregory Herrero 	hsotg->lx_state = DWC2_L3;
3337ce2b21a4SJohn Stultz 
3338ce2b21a4SJohn Stultz 	usb_gadget_set_state(&hsotg->gadget, USB_STATE_NOTATTACHED);
333947a1685fSDinh Nguyen }
334047a1685fSDinh Nguyen 
334147a1685fSDinh Nguyen /**
33421f91b4ccSFelipe Balbi  * dwc2_hsotg_irq_fifoempty - TX FIFO empty interrupt handler
334347a1685fSDinh Nguyen  * @hsotg: The device state:
334447a1685fSDinh Nguyen  * @periodic: True if this is a periodic FIFO interrupt
334547a1685fSDinh Nguyen  */
dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg * hsotg,bool periodic)33461f91b4ccSFelipe Balbi static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
334747a1685fSDinh Nguyen {
33481f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *ep;
334947a1685fSDinh Nguyen 	int epno, ret;
335047a1685fSDinh Nguyen 
335147a1685fSDinh Nguyen 	/* look through for any more data to transmit */
335247a1685fSDinh Nguyen 	for (epno = 0; epno < hsotg->num_of_eps; epno++) {
3353c6f5c050SMian Yousaf Kaukab 		ep = index_to_ep(hsotg, epno, 1);
3354c6f5c050SMian Yousaf Kaukab 
3355c6f5c050SMian Yousaf Kaukab 		if (!ep)
3356c6f5c050SMian Yousaf Kaukab 			continue;
335747a1685fSDinh Nguyen 
335847a1685fSDinh Nguyen 		if (!ep->dir_in)
335947a1685fSDinh Nguyen 			continue;
336047a1685fSDinh Nguyen 
336147a1685fSDinh Nguyen 		if ((periodic && !ep->periodic) ||
336247a1685fSDinh Nguyen 		    (!periodic && ep->periodic))
336347a1685fSDinh Nguyen 			continue;
336447a1685fSDinh Nguyen 
33651f91b4ccSFelipe Balbi 		ret = dwc2_hsotg_trytx(hsotg, ep);
336647a1685fSDinh Nguyen 		if (ret < 0)
336747a1685fSDinh Nguyen 			break;
336847a1685fSDinh Nguyen 	}
336947a1685fSDinh Nguyen }
337047a1685fSDinh Nguyen 
337147a1685fSDinh Nguyen /* IRQ flags which will trigger a retry around the IRQ loop */
337247a1685fSDinh Nguyen #define IRQ_RETRY_MASK (GINTSTS_NPTXFEMP | \
337347a1685fSDinh Nguyen 			GINTSTS_PTXFEMP |  \
337447a1685fSDinh Nguyen 			GINTSTS_RXFLVL)
337547a1685fSDinh Nguyen 
33764fe4f9feSMinas Harutyunyan static int dwc2_hsotg_ep_disable(struct usb_ep *ep);
337747a1685fSDinh Nguyen /**
337858aff959SLee Jones  * dwc2_hsotg_core_init_disconnected - issue softreset to the core
337947a1685fSDinh Nguyen  * @hsotg: The device state
33806fb914d7SGrigor Tovmasyan  * @is_usb_reset: Usb resetting flag
338147a1685fSDinh Nguyen  *
338247a1685fSDinh Nguyen  * Issue a soft reset to the core, and await the core finishing it.
338347a1685fSDinh Nguyen  */
dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg * hsotg,bool is_usb_reset)33841f91b4ccSFelipe Balbi void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
3385643cc4deSGregory Herrero 				       bool is_usb_reset)
338647a1685fSDinh Nguyen {
33871ee6903bSGregory Herrero 	u32 intmsk;
3388643cc4deSGregory Herrero 	u32 val;
3389ecd9a7adSPrzemek Rudy 	u32 usbcfg;
339079c3b5bbSVahram Aharonyan 	u32 dcfg = 0;
3391dccf1badSMinas Harutyunyan 	int ep;
3392643cc4deSGregory Herrero 
33935390d438SMian Yousaf Kaukab 	/* Kill any ep0 requests as controller will be reinitialized */
33945390d438SMian Yousaf Kaukab 	kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
33955390d438SMian Yousaf Kaukab 
3396dccf1badSMinas Harutyunyan 	if (!is_usb_reset) {
33976e6360b6SJohn Stultz 		if (dwc2_core_reset(hsotg, true))
339886de4895SGregory Herrero 			return;
3399dccf1badSMinas Harutyunyan 	} else {
3400dccf1badSMinas Harutyunyan 		/* all endpoints should be shutdown */
3401dccf1badSMinas Harutyunyan 		for (ep = 1; ep < hsotg->num_of_eps; ep++) {
3402dccf1badSMinas Harutyunyan 			if (hsotg->eps_in[ep])
3403dccf1badSMinas Harutyunyan 				dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
3404dccf1badSMinas Harutyunyan 			if (hsotg->eps_out[ep])
3405dccf1badSMinas Harutyunyan 				dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
3406dccf1badSMinas Harutyunyan 		}
3407dccf1badSMinas Harutyunyan 	}
340847a1685fSDinh Nguyen 
340947a1685fSDinh Nguyen 	/*
341047a1685fSDinh Nguyen 	 * we must now enable ep0 ready for host detection and then
341147a1685fSDinh Nguyen 	 * set configuration.
341247a1685fSDinh Nguyen 	 */
341347a1685fSDinh Nguyen 
3414ecd9a7adSPrzemek Rudy 	/* keep other bits untouched (so e.g. forced modes are not lost) */
3415f25c42b8SGevorg Sahakyan 	usbcfg = dwc2_readl(hsotg, GUSBCFG);
34161e868545SJules Maselbas 	usbcfg &= ~GUSBCFG_TOUTCAL_MASK;
3417707d80f0SJules Maselbas 	usbcfg |= GUSBCFG_TOUTCAL(7);
3418ecd9a7adSPrzemek Rudy 
34191e868545SJules Maselbas 	/* remove the HNP/SRP and set the PHY */
34201e868545SJules Maselbas 	usbcfg &= ~(GUSBCFG_SRPCAP | GUSBCFG_HNPCAP);
3421f25c42b8SGevorg Sahakyan         dwc2_writel(hsotg, usbcfg, GUSBCFG);
342247a1685fSDinh Nguyen 
34231e868545SJules Maselbas 	dwc2_phy_init(hsotg, true);
34241e868545SJules Maselbas 
34251f91b4ccSFelipe Balbi 	dwc2_hsotg_init_fifo(hsotg);
342647a1685fSDinh Nguyen 
34277fd22e5bSMinas Harutyunyan 	if (!is_usb_reset) {
3428f25c42b8SGevorg Sahakyan 		dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);
34297fd22e5bSMinas Harutyunyan 		if (hsotg->params.eusb2_disc)
34307fd22e5bSMinas Harutyunyan 			dwc2_set_bit(hsotg, GOTGCTL, GOTGCTL_EUSB2_DISC_SUPP);
34317fd22e5bSMinas Harutyunyan 	}
343247a1685fSDinh Nguyen 
343379c3b5bbSVahram Aharonyan 	dcfg |= DCFG_EPMISCNT(1);
343438e9002bSVardan Mikayelyan 
343538e9002bSVardan Mikayelyan 	switch (hsotg->params.speed) {
343638e9002bSVardan Mikayelyan 	case DWC2_SPEED_PARAM_LOW:
343738e9002bSVardan Mikayelyan 		dcfg |= DCFG_DEVSPD_LS;
343838e9002bSVardan Mikayelyan 		break;
343938e9002bSVardan Mikayelyan 	case DWC2_SPEED_PARAM_FULL:
344079c3b5bbSVahram Aharonyan 		if (hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS)
344179c3b5bbSVahram Aharonyan 			dcfg |= DCFG_DEVSPD_FS48;
344279c3b5bbSVahram Aharonyan 		else
344379c3b5bbSVahram Aharonyan 			dcfg |= DCFG_DEVSPD_FS;
344438e9002bSVardan Mikayelyan 		break;
344538e9002bSVardan Mikayelyan 	default:
344679c3b5bbSVahram Aharonyan 		dcfg |= DCFG_DEVSPD_HS;
344779c3b5bbSVahram Aharonyan 	}
344838e9002bSVardan Mikayelyan 
3449b43ebc96SGrigor Tovmasyan 	if (hsotg->params.ipg_isoc_en)
3450b43ebc96SGrigor Tovmasyan 		dcfg |= DCFG_IPG_ISOC_SUPPORDED;
3451b43ebc96SGrigor Tovmasyan 
3452f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, dcfg,  DCFG);
345347a1685fSDinh Nguyen 
345447a1685fSDinh Nguyen 	/* Clear any pending OTG interrupts */
3455f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, 0xffffffff, GOTGINT);
345647a1685fSDinh Nguyen 
345747a1685fSDinh Nguyen 	/* Clear any pending interrupts */
3458f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, 0xffffffff, GINTSTS);
34591ee6903bSGregory Herrero 	intmsk = GINTSTS_ERLYSUSP | GINTSTS_SESSREQINT |
346047a1685fSDinh Nguyen 		GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF |
34611ee6903bSGregory Herrero 		GINTSTS_USBRST | GINTSTS_RESETDET |
34621ee6903bSGregory Herrero 		GINTSTS_ENUMDONE | GINTSTS_OTGINT |
3463376f0401SSevak Arakelyan 		GINTSTS_USBSUSP | GINTSTS_WKUPINT |
3464376f0401SSevak Arakelyan 		GINTSTS_LPMTRANRCVD;
3465f4736701SVahram Aharonyan 
3466f4736701SVahram Aharonyan 	if (!using_desc_dma(hsotg))
3467f4736701SVahram Aharonyan 		intmsk |= GINTSTS_INCOMPL_SOIN | GINTSTS_INCOMPL_SOOUT;
34681ee6903bSGregory Herrero 
346995832c00SJohn Youn 	if (!hsotg->params.external_id_pin_ctl)
34701ee6903bSGregory Herrero 		intmsk |= GINTSTS_CONIDSTSCHNG;
34711ee6903bSGregory Herrero 
3472f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, intmsk, GINTMSK);
347347a1685fSDinh Nguyen 
3474a5c18f11SVahram Aharonyan 	if (using_dma(hsotg)) {
3475f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN |
3476d1ac8c80SRazmik Karapetyan 			    hsotg->params.ahbcfg,
3477f25c42b8SGevorg Sahakyan 			    GAHBCFG);
3478a5c18f11SVahram Aharonyan 
3479a5c18f11SVahram Aharonyan 		/* Set DDMA mode support in the core if needed */
3480a5c18f11SVahram Aharonyan 		if (using_desc_dma(hsotg))
3481f25c42b8SGevorg Sahakyan 			dwc2_set_bit(hsotg, DCFG, DCFG_DESCDMA_EN);
3482a5c18f11SVahram Aharonyan 
3483a5c18f11SVahram Aharonyan 	} else {
3484f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, ((hsotg->dedicated_fifos) ?
348595c8bc36SAntti Seppälä 						(GAHBCFG_NP_TXF_EMP_LVL |
348647a1685fSDinh Nguyen 						 GAHBCFG_P_TXF_EMP_LVL) : 0) |
3487f25c42b8SGevorg Sahakyan 			    GAHBCFG_GLBL_INTR_EN, GAHBCFG);
3488a5c18f11SVahram Aharonyan 	}
348947a1685fSDinh Nguyen 
349047a1685fSDinh Nguyen 	/*
349147a1685fSDinh Nguyen 	 * If INTknTXFEmpMsk is enabled, it's important to disable ep interrupts
349247a1685fSDinh Nguyen 	 * when we have no data to transfer. Otherwise we get being flooded by
349347a1685fSDinh Nguyen 	 * interrupts.
349447a1685fSDinh Nguyen 	 */
349547a1685fSDinh Nguyen 
3496f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, ((hsotg->dedicated_fifos && !using_dma(hsotg)) ?
34976ff2e832SMian Yousaf Kaukab 		DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK : 0) |
349847a1685fSDinh Nguyen 		DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK |
3499837e9f00SVardan Mikayelyan 		DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK,
3500f25c42b8SGevorg Sahakyan 		DIEPMSK);
350147a1685fSDinh Nguyen 
350247a1685fSDinh Nguyen 	/*
350347a1685fSDinh Nguyen 	 * don't need XferCompl, we get that from RXFIFO in slave mode. In
35049d9a6b07SVahram Aharonyan 	 * DMA mode we may need this and StsPhseRcvd.
350547a1685fSDinh Nguyen 	 */
3506f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, (using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK |
35079d9a6b07SVahram Aharonyan 		DOEPMSK_STSPHSERCVDMSK) : 0) |
350847a1685fSDinh Nguyen 		DOEPMSK_EPDISBLDMSK | DOEPMSK_AHBERRMSK |
35099d9a6b07SVahram Aharonyan 		DOEPMSK_SETUPMSK,
3510f25c42b8SGevorg Sahakyan 		DOEPMSK);
351147a1685fSDinh Nguyen 
3512ec01f0b2SVahram Aharonyan 	/* Enable BNA interrupt for DDMA */
351337981e00SMinas Harutyunyan 	if (using_desc_dma(hsotg)) {
3514f25c42b8SGevorg Sahakyan 		dwc2_set_bit(hsotg, DOEPMSK, DOEPMSK_BNAMSK);
3515f25c42b8SGevorg Sahakyan 		dwc2_set_bit(hsotg, DIEPMSK, DIEPMSK_BNAININTRMSK);
351637981e00SMinas Harutyunyan 	}
3517ec01f0b2SVahram Aharonyan 
3518ca531bc2SGrigor Tovmasyan 	/* Enable Service Interval mode if supported */
3519ca531bc2SGrigor Tovmasyan 	if (using_desc_dma(hsotg) && hsotg->params.service_interval)
3520ca531bc2SGrigor Tovmasyan 		dwc2_set_bit(hsotg, DCTL, DCTL_SERVICE_INTERVAL_SUPPORTED);
3521ca531bc2SGrigor Tovmasyan 
3522f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, 0, DAINTMSK);
352347a1685fSDinh Nguyen 
352447a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
3525f25c42b8SGevorg Sahakyan 		dwc2_readl(hsotg, DIEPCTL0),
3526f25c42b8SGevorg Sahakyan 		dwc2_readl(hsotg, DOEPCTL0));
352747a1685fSDinh Nguyen 
352847a1685fSDinh Nguyen 	/* enable in and out endpoint interrupts */
35291f91b4ccSFelipe Balbi 	dwc2_hsotg_en_gsint(hsotg, GINTSTS_OEPINT | GINTSTS_IEPINT);
353047a1685fSDinh Nguyen 
353147a1685fSDinh Nguyen 	/*
353247a1685fSDinh Nguyen 	 * Enable the RXFIFO when in slave mode, as this is how we collect
353347a1685fSDinh Nguyen 	 * the data. In DMA mode, we get events from the FIFO but also
353447a1685fSDinh Nguyen 	 * things we cannot process, so do not use it.
353547a1685fSDinh Nguyen 	 */
353647a1685fSDinh Nguyen 	if (!using_dma(hsotg))
35371f91b4ccSFelipe Balbi 		dwc2_hsotg_en_gsint(hsotg, GINTSTS_RXFLVL);
353847a1685fSDinh Nguyen 
353947a1685fSDinh Nguyen 	/* Enable interrupts for EP0 in and out */
35401f91b4ccSFelipe Balbi 	dwc2_hsotg_ctrl_epint(hsotg, 0, 0, 1);
35411f91b4ccSFelipe Balbi 	dwc2_hsotg_ctrl_epint(hsotg, 0, 1, 1);
354247a1685fSDinh Nguyen 
3543643cc4deSGregory Herrero 	if (!is_usb_reset) {
3544f25c42b8SGevorg Sahakyan 		dwc2_set_bit(hsotg, DCTL, DCTL_PWRONPRGDONE);
354547a1685fSDinh Nguyen 		udelay(10);  /* see openiboot */
3546f25c42b8SGevorg Sahakyan 		dwc2_clear_bit(hsotg, DCTL, DCTL_PWRONPRGDONE);
3547643cc4deSGregory Herrero 	}
354847a1685fSDinh Nguyen 
3549f25c42b8SGevorg Sahakyan 	dev_dbg(hsotg->dev, "DCTL=0x%08x\n", dwc2_readl(hsotg, DCTL));
355047a1685fSDinh Nguyen 
355147a1685fSDinh Nguyen 	/*
355247a1685fSDinh Nguyen 	 * DxEPCTL_USBActEp says RO in manual, but seems to be set by
355347a1685fSDinh Nguyen 	 * writing to the EPCTL register..
355447a1685fSDinh Nguyen 	 */
355547a1685fSDinh Nguyen 
355647a1685fSDinh Nguyen 	/* set to read 1 8byte packet */
3557f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
3558f25c42b8SGevorg Sahakyan 	       DXEPTSIZ_XFERSIZE(8), DOEPTSIZ0);
355947a1685fSDinh Nguyen 
3560f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
356147a1685fSDinh Nguyen 	       DXEPCTL_CNAK | DXEPCTL_EPENA |
356247a1685fSDinh Nguyen 	       DXEPCTL_USBACTEP,
3563f25c42b8SGevorg Sahakyan 	       DOEPCTL0);
356447a1685fSDinh Nguyen 
356547a1685fSDinh Nguyen 	/* enable, but don't activate EP0in */
3566f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
3567f25c42b8SGevorg Sahakyan 	       DXEPCTL_USBACTEP, DIEPCTL0);
356847a1685fSDinh Nguyen 
356947a1685fSDinh Nguyen 	/* clear global NAKs */
3570643cc4deSGregory Herrero 	val = DCTL_CGOUTNAK | DCTL_CGNPINNAK;
3571643cc4deSGregory Herrero 	if (!is_usb_reset)
3572643cc4deSGregory Herrero 		val |= DCTL_SFTDISCON;
3573f25c42b8SGevorg Sahakyan 	dwc2_set_bit(hsotg, DCTL, val);
357447a1685fSDinh Nguyen 
357521b03405SSevak Arakelyan 	/* configure the core to support LPM */
357621b03405SSevak Arakelyan 	dwc2_gadget_init_lpm(hsotg);
357721b03405SSevak Arakelyan 
357815d9dbf8SGrigor Tovmasyan 	/* program GREFCLK register if needed */
357915d9dbf8SGrigor Tovmasyan 	if (using_desc_dma(hsotg) && hsotg->params.service_interval)
358015d9dbf8SGrigor Tovmasyan 		dwc2_gadget_program_ref_clk(hsotg);
358115d9dbf8SGrigor Tovmasyan 
358247a1685fSDinh Nguyen 	/* must be at-least 3ms to allow bus to see disconnect */
358347a1685fSDinh Nguyen 	mdelay(3);
358447a1685fSDinh Nguyen 
3585065d3931SGregory Herrero 	hsotg->lx_state = DWC2_L0;
3586755d7395SVardan Mikayelyan 
3587755d7395SVardan Mikayelyan 	dwc2_hsotg_enqueue_setup(hsotg);
3588755d7395SVardan Mikayelyan 
3589755d7395SVardan Mikayelyan 	dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
3590f25c42b8SGevorg Sahakyan 		dwc2_readl(hsotg, DIEPCTL0),
3591f25c42b8SGevorg Sahakyan 		dwc2_readl(hsotg, DOEPCTL0));
3592ad38dc5dSMarek Szyprowski }
3593ac3c81f3SMarek Szyprowski 
dwc2_hsotg_core_disconnect(struct dwc2_hsotg * hsotg)359417f93402SAmelie Delaunay void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
3595ad38dc5dSMarek Szyprowski {
3596ad38dc5dSMarek Szyprowski 	/* set the soft-disconnect bit */
3597f25c42b8SGevorg Sahakyan 	dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);
3598ad38dc5dSMarek Szyprowski }
3599ad38dc5dSMarek Szyprowski 
dwc2_hsotg_core_connect(struct dwc2_hsotg * hsotg)36001f91b4ccSFelipe Balbi void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg)
3601ad38dc5dSMarek Szyprowski {
360247a1685fSDinh Nguyen 	/* remove the soft-disconnect and let's go */
3603db638c65SAmelie Delaunay 	if (!hsotg->role_sw || (dwc2_readl(hsotg, GOTGCTL) & GOTGCTL_BSESVLD))
3604f25c42b8SGevorg Sahakyan 		dwc2_clear_bit(hsotg, DCTL, DCTL_SFTDISCON);
360547a1685fSDinh Nguyen }
360647a1685fSDinh Nguyen 
360747a1685fSDinh Nguyen /**
3608381fc8f8SVardan Mikayelyan  * dwc2_gadget_handle_incomplete_isoc_in - handle incomplete ISO IN Interrupt.
3609381fc8f8SVardan Mikayelyan  * @hsotg: The device state:
3610381fc8f8SVardan Mikayelyan  *
3611381fc8f8SVardan Mikayelyan  * This interrupt indicates one of the following conditions occurred while
3612381fc8f8SVardan Mikayelyan  * transmitting an ISOC transaction.
3613381fc8f8SVardan Mikayelyan  * - Corrupted IN Token for ISOC EP.
3614381fc8f8SVardan Mikayelyan  * - Packet not complete in FIFO.
3615381fc8f8SVardan Mikayelyan  *
3616381fc8f8SVardan Mikayelyan  * The following actions will be taken:
3617381fc8f8SVardan Mikayelyan  * - Determine the EP
3618381fc8f8SVardan Mikayelyan  * - Disable EP; when 'Endpoint Disabled' interrupt is received Flush FIFO
3619381fc8f8SVardan Mikayelyan  */
dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg * hsotg)3620381fc8f8SVardan Mikayelyan static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg)
3621381fc8f8SVardan Mikayelyan {
3622381fc8f8SVardan Mikayelyan 	struct dwc2_hsotg_ep *hs_ep;
3623381fc8f8SVardan Mikayelyan 	u32 epctrl;
36241b4977c7SRazmik Karapetyan 	u32 daintmsk;
3625381fc8f8SVardan Mikayelyan 	u32 idx;
3626381fc8f8SVardan Mikayelyan 
3627381fc8f8SVardan Mikayelyan 	dev_dbg(hsotg->dev, "Incomplete isoc in interrupt received:\n");
3628381fc8f8SVardan Mikayelyan 
3629f25c42b8SGevorg Sahakyan 	daintmsk = dwc2_readl(hsotg, DAINTMSK);
36301b4977c7SRazmik Karapetyan 
3631d5d5f079SArtur Petrosyan 	for (idx = 1; idx < hsotg->num_of_eps; idx++) {
3632381fc8f8SVardan Mikayelyan 		hs_ep = hsotg->eps_in[idx];
36331b4977c7SRazmik Karapetyan 		/* Proceed only unmasked ISOC EPs */
363489066b36SJohn Keeping 		if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous)
36351b4977c7SRazmik Karapetyan 			continue;
36361b4977c7SRazmik Karapetyan 
3637f25c42b8SGevorg Sahakyan 		epctrl = dwc2_readl(hsotg, DIEPCTL(idx));
36381b4977c7SRazmik Karapetyan 		if ((epctrl & DXEPCTL_EPENA) &&
3639381fc8f8SVardan Mikayelyan 		    dwc2_gadget_target_frame_elapsed(hs_ep)) {
3640381fc8f8SVardan Mikayelyan 			epctrl |= DXEPCTL_SNAK;
3641381fc8f8SVardan Mikayelyan 			epctrl |= DXEPCTL_EPDIS;
3642f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, epctrl, DIEPCTL(idx));
3643381fc8f8SVardan Mikayelyan 		}
3644381fc8f8SVardan Mikayelyan 	}
3645381fc8f8SVardan Mikayelyan 
3646381fc8f8SVardan Mikayelyan 	/* Clear interrupt */
3647f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, GINTSTS_INCOMPL_SOIN, GINTSTS);
3648381fc8f8SVardan Mikayelyan }
3649381fc8f8SVardan Mikayelyan 
3650381fc8f8SVardan Mikayelyan /**
3651381fc8f8SVardan Mikayelyan  * dwc2_gadget_handle_incomplete_isoc_out - handle incomplete ISO OUT Interrupt
3652381fc8f8SVardan Mikayelyan  * @hsotg: The device state:
3653381fc8f8SVardan Mikayelyan  *
3654381fc8f8SVardan Mikayelyan  * This interrupt indicates one of the following conditions occurred while
3655381fc8f8SVardan Mikayelyan  * transmitting an ISOC transaction.
3656381fc8f8SVardan Mikayelyan  * - Corrupted OUT Token for ISOC EP.
3657381fc8f8SVardan Mikayelyan  * - Packet not complete in FIFO.
3658381fc8f8SVardan Mikayelyan  *
3659381fc8f8SVardan Mikayelyan  * The following actions will be taken:
3660381fc8f8SVardan Mikayelyan  * - Determine the EP
3661381fc8f8SVardan Mikayelyan  * - Set DCTL_SGOUTNAK and unmask GOUTNAKEFF if target frame elapsed.
3662381fc8f8SVardan Mikayelyan  */
dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg * hsotg)3663381fc8f8SVardan Mikayelyan static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg)
3664381fc8f8SVardan Mikayelyan {
3665381fc8f8SVardan Mikayelyan 	u32 gintsts;
3666381fc8f8SVardan Mikayelyan 	u32 gintmsk;
3667689efb26SRazmik Karapetyan 	u32 daintmsk;
3668381fc8f8SVardan Mikayelyan 	u32 epctrl;
3669381fc8f8SVardan Mikayelyan 	struct dwc2_hsotg_ep *hs_ep;
3670381fc8f8SVardan Mikayelyan 	int idx;
3671381fc8f8SVardan Mikayelyan 
3672381fc8f8SVardan Mikayelyan 	dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__);
3673381fc8f8SVardan Mikayelyan 
3674f25c42b8SGevorg Sahakyan 	daintmsk = dwc2_readl(hsotg, DAINTMSK);
3675689efb26SRazmik Karapetyan 	daintmsk >>= DAINT_OUTEP_SHIFT;
3676689efb26SRazmik Karapetyan 
3677d5d5f079SArtur Petrosyan 	for (idx = 1; idx < hsotg->num_of_eps; idx++) {
3678381fc8f8SVardan Mikayelyan 		hs_ep = hsotg->eps_out[idx];
3679689efb26SRazmik Karapetyan 		/* Proceed only unmasked ISOC EPs */
368089066b36SJohn Keeping 		if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous)
3681689efb26SRazmik Karapetyan 			continue;
3682689efb26SRazmik Karapetyan 
3683f25c42b8SGevorg Sahakyan 		epctrl = dwc2_readl(hsotg, DOEPCTL(idx));
3684689efb26SRazmik Karapetyan 		if ((epctrl & DXEPCTL_EPENA) &&
3685381fc8f8SVardan Mikayelyan 		    dwc2_gadget_target_frame_elapsed(hs_ep)) {
3686381fc8f8SVardan Mikayelyan 			/* Unmask GOUTNAKEFF interrupt */
3687f25c42b8SGevorg Sahakyan 			gintmsk = dwc2_readl(hsotg, GINTMSK);
3688381fc8f8SVardan Mikayelyan 			gintmsk |= GINTSTS_GOUTNAKEFF;
3689f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, gintmsk, GINTMSK);
3690381fc8f8SVardan Mikayelyan 
3691f25c42b8SGevorg Sahakyan 			gintsts = dwc2_readl(hsotg, GINTSTS);
3692689efb26SRazmik Karapetyan 			if (!(gintsts & GINTSTS_GOUTNAKEFF)) {
3693f25c42b8SGevorg Sahakyan 				dwc2_set_bit(hsotg, DCTL, DCTL_SGOUTNAK);
3694689efb26SRazmik Karapetyan 				break;
3695689efb26SRazmik Karapetyan 			}
3696381fc8f8SVardan Mikayelyan 		}
3697381fc8f8SVardan Mikayelyan 	}
3698381fc8f8SVardan Mikayelyan 
3699381fc8f8SVardan Mikayelyan 	/* Clear interrupt */
3700f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, GINTSTS_INCOMPL_SOOUT, GINTSTS);
3701381fc8f8SVardan Mikayelyan }
3702381fc8f8SVardan Mikayelyan 
3703381fc8f8SVardan Mikayelyan /**
37041f91b4ccSFelipe Balbi  * dwc2_hsotg_irq - handle device interrupt
370547a1685fSDinh Nguyen  * @irq: The IRQ number triggered
370647a1685fSDinh Nguyen  * @pw: The pw value when registered the handler.
370747a1685fSDinh Nguyen  */
dwc2_hsotg_irq(int irq,void * pw)37081f91b4ccSFelipe Balbi static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
370947a1685fSDinh Nguyen {
3710941fcce4SDinh Nguyen 	struct dwc2_hsotg *hsotg = pw;
371147a1685fSDinh Nguyen 	int retry_count = 8;
371247a1685fSDinh Nguyen 	u32 gintsts;
371347a1685fSDinh Nguyen 	u32 gintmsk;
371447a1685fSDinh Nguyen 
3715ee3de8d7SVardan Mikayelyan 	if (!dwc2_is_device_mode(hsotg))
3716ee3de8d7SVardan Mikayelyan 		return IRQ_NONE;
3717ee3de8d7SVardan Mikayelyan 
371847a1685fSDinh Nguyen 	spin_lock(&hsotg->lock);
371947a1685fSDinh Nguyen irq_retry:
3720f25c42b8SGevorg Sahakyan 	gintsts = dwc2_readl(hsotg, GINTSTS);
3721f25c42b8SGevorg Sahakyan 	gintmsk = dwc2_readl(hsotg, GINTMSK);
372247a1685fSDinh Nguyen 
372347a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: %08x %08x (%08x) retry %d\n",
372447a1685fSDinh Nguyen 		__func__, gintsts, gintsts & gintmsk, gintmsk, retry_count);
372547a1685fSDinh Nguyen 
372647a1685fSDinh Nguyen 	gintsts &= gintmsk;
372747a1685fSDinh Nguyen 
37288fc37b82SMian Yousaf Kaukab 	if (gintsts & GINTSTS_RESETDET) {
37298fc37b82SMian Yousaf Kaukab 		dev_dbg(hsotg->dev, "%s: USBRstDet\n", __func__);
37308fc37b82SMian Yousaf Kaukab 
3731f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, GINTSTS_RESETDET, GINTSTS);
37328fc37b82SMian Yousaf Kaukab 
37338fc37b82SMian Yousaf Kaukab 		/* This event must be used only if controller is suspended */
3734c9c394abSArtur Petrosyan 		if (hsotg->in_ppd && hsotg->lx_state == DWC2_L2)
3735c9c394abSArtur Petrosyan 			dwc2_exit_partial_power_down(hsotg, 0, true);
3736c9c394abSArtur Petrosyan 
373731f42da3SMinas Harutyunyan 		/* Exit gadget mode clock gating. */
373831f42da3SMinas Harutyunyan 		if (hsotg->params.power_down ==
373931f42da3SMinas Harutyunyan 		    DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended &&
374031f42da3SMinas Harutyunyan 		    !hsotg->params.no_clock_gating)
374131f42da3SMinas Harutyunyan 			dwc2_gadget_exit_clock_gating(hsotg, 0);
374231f42da3SMinas Harutyunyan 
37438fc37b82SMian Yousaf Kaukab 		hsotg->lx_state = DWC2_L0;
37448fc37b82SMian Yousaf Kaukab 	}
37458fc37b82SMian Yousaf Kaukab 
37468fc37b82SMian Yousaf Kaukab 	if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) {
3747f25c42b8SGevorg Sahakyan 		u32 usb_status = dwc2_readl(hsotg, GOTGCTL);
37488fc37b82SMian Yousaf Kaukab 		u32 connected = hsotg->connected;
37498fc37b82SMian Yousaf Kaukab 
37508fc37b82SMian Yousaf Kaukab 		dev_dbg(hsotg->dev, "%s: USBRst\n", __func__);
37518fc37b82SMian Yousaf Kaukab 		dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n",
3752f25c42b8SGevorg Sahakyan 			dwc2_readl(hsotg, GNPTXSTS));
37538fc37b82SMian Yousaf Kaukab 
3754f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, GINTSTS_USBRST, GINTSTS);
37558fc37b82SMian Yousaf Kaukab 
37568fc37b82SMian Yousaf Kaukab 		/* Report disconnection if it is not already done. */
37578fc37b82SMian Yousaf Kaukab 		dwc2_hsotg_disconnect(hsotg);
37588fc37b82SMian Yousaf Kaukab 
3759307bc11fSMinas Harutyunyan 		/* Reset device address to zero */
3760f25c42b8SGevorg Sahakyan 		dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK);
3761307bc11fSMinas Harutyunyan 
37628fc37b82SMian Yousaf Kaukab 		if (usb_status & GOTGCTL_BSESVLD && connected)
37638fc37b82SMian Yousaf Kaukab 			dwc2_hsotg_core_init_disconnected(hsotg, true);
37648fc37b82SMian Yousaf Kaukab 	}
37658fc37b82SMian Yousaf Kaukab 
376647a1685fSDinh Nguyen 	if (gintsts & GINTSTS_ENUMDONE) {
3767f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, GINTSTS_ENUMDONE, GINTSTS);
376847a1685fSDinh Nguyen 
37691f91b4ccSFelipe Balbi 		dwc2_hsotg_irq_enumdone(hsotg);
377047a1685fSDinh Nguyen 	}
377147a1685fSDinh Nguyen 
377247a1685fSDinh Nguyen 	if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {
3773f25c42b8SGevorg Sahakyan 		u32 daint = dwc2_readl(hsotg, DAINT);
3774f25c42b8SGevorg Sahakyan 		u32 daintmsk = dwc2_readl(hsotg, DAINTMSK);
377547a1685fSDinh Nguyen 		u32 daint_out, daint_in;
377647a1685fSDinh Nguyen 		int ep;
377747a1685fSDinh Nguyen 
377847a1685fSDinh Nguyen 		daint &= daintmsk;
377947a1685fSDinh Nguyen 		daint_out = daint >> DAINT_OUTEP_SHIFT;
378047a1685fSDinh Nguyen 		daint_in = daint & ~(daint_out << DAINT_OUTEP_SHIFT);
378147a1685fSDinh Nguyen 
378247a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint);
378347a1685fSDinh Nguyen 
3784cec87f1dSMian Yousaf Kaukab 		for (ep = 0; ep < hsotg->num_of_eps && daint_out;
3785cec87f1dSMian Yousaf Kaukab 						ep++, daint_out >>= 1) {
378647a1685fSDinh Nguyen 			if (daint_out & 1)
37871f91b4ccSFelipe Balbi 				dwc2_hsotg_epint(hsotg, ep, 0);
378847a1685fSDinh Nguyen 		}
378947a1685fSDinh Nguyen 
3790cec87f1dSMian Yousaf Kaukab 		for (ep = 0; ep < hsotg->num_of_eps  && daint_in;
3791cec87f1dSMian Yousaf Kaukab 						ep++, daint_in >>= 1) {
379247a1685fSDinh Nguyen 			if (daint_in & 1)
37931f91b4ccSFelipe Balbi 				dwc2_hsotg_epint(hsotg, ep, 1);
379447a1685fSDinh Nguyen 		}
379547a1685fSDinh Nguyen 	}
379647a1685fSDinh Nguyen 
379747a1685fSDinh Nguyen 	/* check both FIFOs */
379847a1685fSDinh Nguyen 
379947a1685fSDinh Nguyen 	if (gintsts & GINTSTS_NPTXFEMP) {
380047a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "NPTxFEmp\n");
380147a1685fSDinh Nguyen 
380247a1685fSDinh Nguyen 		/*
380347a1685fSDinh Nguyen 		 * Disable the interrupt to stop it happening again
380447a1685fSDinh Nguyen 		 * unless one of these endpoint routines decides that
380547a1685fSDinh Nguyen 		 * it needs re-enabling
380647a1685fSDinh Nguyen 		 */
380747a1685fSDinh Nguyen 
38081f91b4ccSFelipe Balbi 		dwc2_hsotg_disable_gsint(hsotg, GINTSTS_NPTXFEMP);
38091f91b4ccSFelipe Balbi 		dwc2_hsotg_irq_fifoempty(hsotg, false);
381047a1685fSDinh Nguyen 	}
381147a1685fSDinh Nguyen 
381247a1685fSDinh Nguyen 	if (gintsts & GINTSTS_PTXFEMP) {
381347a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "PTxFEmp\n");
381447a1685fSDinh Nguyen 
381547a1685fSDinh Nguyen 		/* See note in GINTSTS_NPTxFEmp */
381647a1685fSDinh Nguyen 
38171f91b4ccSFelipe Balbi 		dwc2_hsotg_disable_gsint(hsotg, GINTSTS_PTXFEMP);
38181f91b4ccSFelipe Balbi 		dwc2_hsotg_irq_fifoempty(hsotg, true);
381947a1685fSDinh Nguyen 	}
382047a1685fSDinh Nguyen 
382147a1685fSDinh Nguyen 	if (gintsts & GINTSTS_RXFLVL) {
382247a1685fSDinh Nguyen 		/*
382347a1685fSDinh Nguyen 		 * note, since GINTSTS_RxFLvl doubles as FIFO-not-empty,
38241f91b4ccSFelipe Balbi 		 * we need to retry dwc2_hsotg_handle_rx if this is still
382547a1685fSDinh Nguyen 		 * set.
382647a1685fSDinh Nguyen 		 */
382747a1685fSDinh Nguyen 
38281f91b4ccSFelipe Balbi 		dwc2_hsotg_handle_rx(hsotg);
382947a1685fSDinh Nguyen 	}
383047a1685fSDinh Nguyen 
383147a1685fSDinh Nguyen 	if (gintsts & GINTSTS_ERLYSUSP) {
383247a1685fSDinh Nguyen 		dev_dbg(hsotg->dev, "GINTSTS_ErlySusp\n");
3833f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, GINTSTS_ERLYSUSP, GINTSTS);
383447a1685fSDinh Nguyen 	}
383547a1685fSDinh Nguyen 
383647a1685fSDinh Nguyen 	/*
383747a1685fSDinh Nguyen 	 * these next two seem to crop-up occasionally causing the core
383847a1685fSDinh Nguyen 	 * to shutdown the USB transfer, so try clearing them and logging
383947a1685fSDinh Nguyen 	 * the occurrence.
384047a1685fSDinh Nguyen 	 */
384147a1685fSDinh Nguyen 
384247a1685fSDinh Nguyen 	if (gintsts & GINTSTS_GOUTNAKEFF) {
3843837e9f00SVardan Mikayelyan 		u8 idx;
3844837e9f00SVardan Mikayelyan 		u32 epctrl;
3845837e9f00SVardan Mikayelyan 		u32 gintmsk;
3846d8484552SRazmik Karapetyan 		u32 daintmsk;
3847837e9f00SVardan Mikayelyan 		struct dwc2_hsotg_ep *hs_ep;
384847a1685fSDinh Nguyen 
3849f25c42b8SGevorg Sahakyan 		daintmsk = dwc2_readl(hsotg, DAINTMSK);
3850d8484552SRazmik Karapetyan 		daintmsk >>= DAINT_OUTEP_SHIFT;
3851837e9f00SVardan Mikayelyan 		/* Mask this interrupt */
3852f25c42b8SGevorg Sahakyan 		gintmsk = dwc2_readl(hsotg, GINTMSK);
3853837e9f00SVardan Mikayelyan 		gintmsk &= ~GINTSTS_GOUTNAKEFF;
3854f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, gintmsk, GINTMSK);
385547a1685fSDinh Nguyen 
3856837e9f00SVardan Mikayelyan 		dev_dbg(hsotg->dev, "GOUTNakEff triggered\n");
3857d5d5f079SArtur Petrosyan 		for (idx = 1; idx < hsotg->num_of_eps; idx++) {
3858837e9f00SVardan Mikayelyan 			hs_ep = hsotg->eps_out[idx];
3859d8484552SRazmik Karapetyan 			/* Proceed only unmasked ISOC EPs */
38606070636cSMinas Harutyunyan 			if (BIT(idx) & ~daintmsk)
3861d8484552SRazmik Karapetyan 				continue;
3862d8484552SRazmik Karapetyan 
3863f25c42b8SGevorg Sahakyan 			epctrl = dwc2_readl(hsotg, DOEPCTL(idx));
3864837e9f00SVardan Mikayelyan 
38656070636cSMinas Harutyunyan 			//ISOC Ep's only
38666070636cSMinas Harutyunyan 			if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous) {
3867837e9f00SVardan Mikayelyan 				epctrl |= DXEPCTL_SNAK;
3868837e9f00SVardan Mikayelyan 				epctrl |= DXEPCTL_EPDIS;
3869f25c42b8SGevorg Sahakyan 				dwc2_writel(hsotg, epctrl, DOEPCTL(idx));
38706070636cSMinas Harutyunyan 				continue;
38716070636cSMinas Harutyunyan 			}
38726070636cSMinas Harutyunyan 
38736070636cSMinas Harutyunyan 			//Non-ISOC EP's
38746070636cSMinas Harutyunyan 			if (hs_ep->halted) {
38756070636cSMinas Harutyunyan 				if (!(epctrl & DXEPCTL_EPENA))
38766070636cSMinas Harutyunyan 					epctrl |= DXEPCTL_EPENA;
38776070636cSMinas Harutyunyan 				epctrl |= DXEPCTL_EPDIS;
38786070636cSMinas Harutyunyan 				epctrl |= DXEPCTL_STALL;
38796070636cSMinas Harutyunyan 				dwc2_writel(hsotg, epctrl, DOEPCTL(idx));
3880837e9f00SVardan Mikayelyan 			}
3881837e9f00SVardan Mikayelyan 		}
3882837e9f00SVardan Mikayelyan 
3883837e9f00SVardan Mikayelyan 		/* This interrupt bit is cleared in DXEPINT_EPDISBLD handler */
388447a1685fSDinh Nguyen 	}
388547a1685fSDinh Nguyen 
388647a1685fSDinh Nguyen 	if (gintsts & GINTSTS_GINNAKEFF) {
388747a1685fSDinh Nguyen 		dev_info(hsotg->dev, "GINNakEff triggered\n");
388847a1685fSDinh Nguyen 
3889f25c42b8SGevorg Sahakyan 		dwc2_set_bit(hsotg, DCTL, DCTL_CGNPINNAK);
389047a1685fSDinh Nguyen 
38911f91b4ccSFelipe Balbi 		dwc2_hsotg_dump(hsotg);
389247a1685fSDinh Nguyen 	}
389347a1685fSDinh Nguyen 
3894381fc8f8SVardan Mikayelyan 	if (gintsts & GINTSTS_INCOMPL_SOIN)
3895381fc8f8SVardan Mikayelyan 		dwc2_gadget_handle_incomplete_isoc_in(hsotg);
3896ec1f9d9fSRoman Bacik 
3897381fc8f8SVardan Mikayelyan 	if (gintsts & GINTSTS_INCOMPL_SOOUT)
3898381fc8f8SVardan Mikayelyan 		dwc2_gadget_handle_incomplete_isoc_out(hsotg);
3899ec1f9d9fSRoman Bacik 
390047a1685fSDinh Nguyen 	/*
390147a1685fSDinh Nguyen 	 * if we've had fifo events, we should try and go around the
390247a1685fSDinh Nguyen 	 * loop again to see if there's any point in returning yet.
390347a1685fSDinh Nguyen 	 */
390447a1685fSDinh Nguyen 
390547a1685fSDinh Nguyen 	if (gintsts & IRQ_RETRY_MASK && --retry_count > 0)
390647a1685fSDinh Nguyen 		goto irq_retry;
390747a1685fSDinh Nguyen 
3908187c5298SGrigor Tovmasyan 	/* Check WKUP_ALERT interrupt*/
3909187c5298SGrigor Tovmasyan 	if (hsotg->params.service_interval)
3910187c5298SGrigor Tovmasyan 		dwc2_gadget_wkup_alert_handler(hsotg);
3911187c5298SGrigor Tovmasyan 
391247a1685fSDinh Nguyen 	spin_unlock(&hsotg->lock);
391347a1685fSDinh Nguyen 
391447a1685fSDinh Nguyen 	return IRQ_HANDLED;
391547a1685fSDinh Nguyen }
391647a1685fSDinh Nguyen 
dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep)3917a4f82771SVahram Aharonyan static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
3918a4f82771SVahram Aharonyan 				   struct dwc2_hsotg_ep *hs_ep)
3919a4f82771SVahram Aharonyan {
3920a4f82771SVahram Aharonyan 	u32 epctrl_reg;
3921a4f82771SVahram Aharonyan 	u32 epint_reg;
3922a4f82771SVahram Aharonyan 
3923a4f82771SVahram Aharonyan 	epctrl_reg = hs_ep->dir_in ? DIEPCTL(hs_ep->index) :
3924a4f82771SVahram Aharonyan 		DOEPCTL(hs_ep->index);
3925a4f82771SVahram Aharonyan 	epint_reg = hs_ep->dir_in ? DIEPINT(hs_ep->index) :
3926a4f82771SVahram Aharonyan 		DOEPINT(hs_ep->index);
3927a4f82771SVahram Aharonyan 
3928a4f82771SVahram Aharonyan 	dev_dbg(hsotg->dev, "%s: stopping transfer on %s\n", __func__,
3929a4f82771SVahram Aharonyan 		hs_ep->name);
3930a4f82771SVahram Aharonyan 
3931a4f82771SVahram Aharonyan 	if (hs_ep->dir_in) {
3932a4f82771SVahram Aharonyan 		if (hsotg->dedicated_fifos || hs_ep->periodic) {
3933f25c42b8SGevorg Sahakyan 			dwc2_set_bit(hsotg, epctrl_reg, DXEPCTL_SNAK);
3934a4f82771SVahram Aharonyan 			/* Wait for Nak effect */
3935a4f82771SVahram Aharonyan 			if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg,
3936a4f82771SVahram Aharonyan 						    DXEPINT_INEPNAKEFF, 100))
3937a4f82771SVahram Aharonyan 				dev_warn(hsotg->dev,
3938a4f82771SVahram Aharonyan 					 "%s: timeout DIEPINT.NAKEFF\n",
3939a4f82771SVahram Aharonyan 					 __func__);
3940a4f82771SVahram Aharonyan 		} else {
3941f25c42b8SGevorg Sahakyan 			dwc2_set_bit(hsotg, DCTL, DCTL_SGNPINNAK);
3942a4f82771SVahram Aharonyan 			/* Wait for Nak effect */
3943a4f82771SVahram Aharonyan 			if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
3944a4f82771SVahram Aharonyan 						    GINTSTS_GINNAKEFF, 100))
3945a4f82771SVahram Aharonyan 				dev_warn(hsotg->dev,
3946a4f82771SVahram Aharonyan 					 "%s: timeout GINTSTS.GINNAKEFF\n",
3947a4f82771SVahram Aharonyan 					 __func__);
3948a4f82771SVahram Aharonyan 		}
3949a4f82771SVahram Aharonyan 	} else {
3950fecb3a17SMinas Harutyunyan 		/* Mask GINTSTS_GOUTNAKEFF interrupt */
3951fecb3a17SMinas Harutyunyan 		dwc2_hsotg_disable_gsint(hsotg, GINTSTS_GOUTNAKEFF);
3952fecb3a17SMinas Harutyunyan 
3953f25c42b8SGevorg Sahakyan 		if (!(dwc2_readl(hsotg, GINTSTS) & GINTSTS_GOUTNAKEFF))
3954f25c42b8SGevorg Sahakyan 			dwc2_set_bit(hsotg, DCTL, DCTL_SGOUTNAK);
3955a4f82771SVahram Aharonyan 
3956fecb3a17SMinas Harutyunyan 		if (!using_dma(hsotg)) {
3957fecb3a17SMinas Harutyunyan 			/* Wait for GINTSTS_RXFLVL interrupt */
3958fecb3a17SMinas Harutyunyan 			if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
3959fecb3a17SMinas Harutyunyan 						    GINTSTS_RXFLVL, 100)) {
3960fecb3a17SMinas Harutyunyan 				dev_warn(hsotg->dev, "%s: timeout GINTSTS.RXFLVL\n",
3961fecb3a17SMinas Harutyunyan 					 __func__);
3962fecb3a17SMinas Harutyunyan 			} else {
3963fecb3a17SMinas Harutyunyan 				/*
3964fecb3a17SMinas Harutyunyan 				 * Pop GLOBAL OUT NAK status packet from RxFIFO
3965fecb3a17SMinas Harutyunyan 				 * to assert GOUTNAKEFF interrupt
3966fecb3a17SMinas Harutyunyan 				 */
3967fecb3a17SMinas Harutyunyan 				dwc2_readl(hsotg, GRXSTSP);
3968fecb3a17SMinas Harutyunyan 			}
3969fecb3a17SMinas Harutyunyan 		}
3970fecb3a17SMinas Harutyunyan 
3971a4f82771SVahram Aharonyan 		/* Wait for global nak to take effect */
3972a4f82771SVahram Aharonyan 		if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
3973a4f82771SVahram Aharonyan 					    GINTSTS_GOUTNAKEFF, 100))
3974a4f82771SVahram Aharonyan 			dev_warn(hsotg->dev, "%s: timeout GINTSTS.GOUTNAKEFF\n",
3975a4f82771SVahram Aharonyan 				 __func__);
3976a4f82771SVahram Aharonyan 	}
3977a4f82771SVahram Aharonyan 
3978a4f82771SVahram Aharonyan 	/* Disable ep */
3979f25c42b8SGevorg Sahakyan 	dwc2_set_bit(hsotg, epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK);
3980a4f82771SVahram Aharonyan 
3981a4f82771SVahram Aharonyan 	/* Wait for ep to be disabled */
3982a4f82771SVahram Aharonyan 	if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_EPDISBLD, 100))
3983a4f82771SVahram Aharonyan 		dev_warn(hsotg->dev,
3984a4f82771SVahram Aharonyan 			 "%s: timeout DOEPCTL.EPDisable\n", __func__);
3985a4f82771SVahram Aharonyan 
3986a4f82771SVahram Aharonyan 	/* Clear EPDISBLD interrupt */
3987f25c42b8SGevorg Sahakyan 	dwc2_set_bit(hsotg, epint_reg, DXEPINT_EPDISBLD);
3988a4f82771SVahram Aharonyan 
3989a4f82771SVahram Aharonyan 	if (hs_ep->dir_in) {
3990a4f82771SVahram Aharonyan 		unsigned short fifo_index;
3991a4f82771SVahram Aharonyan 
3992a4f82771SVahram Aharonyan 		if (hsotg->dedicated_fifos || hs_ep->periodic)
3993a4f82771SVahram Aharonyan 			fifo_index = hs_ep->fifo_index;
3994a4f82771SVahram Aharonyan 		else
3995a4f82771SVahram Aharonyan 			fifo_index = 0;
3996a4f82771SVahram Aharonyan 
3997a4f82771SVahram Aharonyan 		/* Flush TX FIFO */
3998a4f82771SVahram Aharonyan 		dwc2_flush_tx_fifo(hsotg, fifo_index);
3999a4f82771SVahram Aharonyan 
4000a4f82771SVahram Aharonyan 		/* Clear Global In NP NAK in Shared FIFO for non periodic ep */
4001a4f82771SVahram Aharonyan 		if (!hsotg->dedicated_fifos && !hs_ep->periodic)
4002f25c42b8SGevorg Sahakyan 			dwc2_set_bit(hsotg, DCTL, DCTL_CGNPINNAK);
4003a4f82771SVahram Aharonyan 
4004a4f82771SVahram Aharonyan 	} else {
4005a4f82771SVahram Aharonyan 		/* Remove global NAKs */
4006f25c42b8SGevorg Sahakyan 		dwc2_set_bit(hsotg, DCTL, DCTL_CGOUTNAK);
4007a4f82771SVahram Aharonyan 	}
4008a4f82771SVahram Aharonyan }
4009a4f82771SVahram Aharonyan 
401047a1685fSDinh Nguyen /**
40111f91b4ccSFelipe Balbi  * dwc2_hsotg_ep_enable - enable the given endpoint
401247a1685fSDinh Nguyen  * @ep: The USB endpint to configure
401347a1685fSDinh Nguyen  * @desc: The USB endpoint descriptor to configure with.
401447a1685fSDinh Nguyen  *
401547a1685fSDinh Nguyen  * This is called from the USB gadget code's usb_ep_enable().
401647a1685fSDinh Nguyen  */
dwc2_hsotg_ep_enable(struct usb_ep * ep,const struct usb_endpoint_descriptor * desc)40171f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
401847a1685fSDinh Nguyen 				const struct usb_endpoint_descriptor *desc)
401947a1685fSDinh Nguyen {
40201f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
4021941fcce4SDinh Nguyen 	struct dwc2_hsotg *hsotg = hs_ep->parent;
402247a1685fSDinh Nguyen 	unsigned long flags;
4023ca4c55adSMian Yousaf Kaukab 	unsigned int index = hs_ep->index;
402447a1685fSDinh Nguyen 	u32 epctrl_reg;
402547a1685fSDinh Nguyen 	u32 epctrl;
402647a1685fSDinh Nguyen 	u32 mps;
4027ee2c40deSVardan Mikayelyan 	u32 mc;
4028837e9f00SVardan Mikayelyan 	u32 mask;
4029ca4c55adSMian Yousaf Kaukab 	unsigned int dir_in;
4030ca4c55adSMian Yousaf Kaukab 	unsigned int i, val, size;
403147a1685fSDinh Nguyen 	int ret = 0;
4032729cac69SMinas Harutyunyan 	unsigned char ep_type;
403354f37f56SMinas Harutyunyan 	int desc_num;
403447a1685fSDinh Nguyen 
403547a1685fSDinh Nguyen 	dev_dbg(hsotg->dev,
403647a1685fSDinh Nguyen 		"%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n",
403747a1685fSDinh Nguyen 		__func__, ep->name, desc->bEndpointAddress, desc->bmAttributes,
403847a1685fSDinh Nguyen 		desc->wMaxPacketSize, desc->bInterval);
403947a1685fSDinh Nguyen 
404047a1685fSDinh Nguyen 	/* not to be called for EP0 */
40418c3d6092SVahram Aharonyan 	if (index == 0) {
40428c3d6092SVahram Aharonyan 		dev_err(hsotg->dev, "%s: called for EP 0\n", __func__);
40438c3d6092SVahram Aharonyan 		return -EINVAL;
40448c3d6092SVahram Aharonyan 	}
404547a1685fSDinh Nguyen 
404647a1685fSDinh Nguyen 	dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
404747a1685fSDinh Nguyen 	if (dir_in != hs_ep->dir_in) {
404847a1685fSDinh Nguyen 		dev_err(hsotg->dev, "%s: direction mismatch!\n", __func__);
404947a1685fSDinh Nguyen 		return -EINVAL;
405047a1685fSDinh Nguyen 	}
405147a1685fSDinh Nguyen 
4052729cac69SMinas Harutyunyan 	ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
405347a1685fSDinh Nguyen 	mps = usb_endpoint_maxp(desc);
4054ee2c40deSVardan Mikayelyan 	mc = usb_endpoint_maxp_mult(desc);
405547a1685fSDinh Nguyen 
4056729cac69SMinas Harutyunyan 	/* ISOC IN in DDMA supported bInterval up to 10 */
4057729cac69SMinas Harutyunyan 	if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC &&
4058729cac69SMinas Harutyunyan 	    dir_in && desc->bInterval > 10) {
4059729cac69SMinas Harutyunyan 		dev_err(hsotg->dev,
4060729cac69SMinas Harutyunyan 			"%s: ISOC IN, DDMA: bInterval>10 not supported!\n", __func__);
4061729cac69SMinas Harutyunyan 		return -EINVAL;
4062729cac69SMinas Harutyunyan 	}
4063729cac69SMinas Harutyunyan 
4064729cac69SMinas Harutyunyan 	/* High bandwidth ISOC OUT in DDMA not supported */
4065729cac69SMinas Harutyunyan 	if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC &&
4066729cac69SMinas Harutyunyan 	    !dir_in && mc > 1) {
4067729cac69SMinas Harutyunyan 		dev_err(hsotg->dev,
4068729cac69SMinas Harutyunyan 			"%s: ISOC OUT, DDMA: HB not supported!\n", __func__);
4069729cac69SMinas Harutyunyan 		return -EINVAL;
4070729cac69SMinas Harutyunyan 	}
4071729cac69SMinas Harutyunyan 
40721f91b4ccSFelipe Balbi 	/* note, we handle this here instead of dwc2_hsotg_set_ep_maxpacket */
407347a1685fSDinh Nguyen 
407447a1685fSDinh Nguyen 	epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
4075f25c42b8SGevorg Sahakyan 	epctrl = dwc2_readl(hsotg, epctrl_reg);
407647a1685fSDinh Nguyen 
407747a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n",
407847a1685fSDinh Nguyen 		__func__, epctrl, epctrl_reg);
407947a1685fSDinh Nguyen 
408054f37f56SMinas Harutyunyan 	if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC)
408154f37f56SMinas Harutyunyan 		desc_num = MAX_DMA_DESC_NUM_HS_ISOC;
408254f37f56SMinas Harutyunyan 	else
408354f37f56SMinas Harutyunyan 		desc_num = MAX_DMA_DESC_NUM_GENERIC;
408454f37f56SMinas Harutyunyan 
40855f54c54bSVahram Aharonyan 	/* Allocate DMA descriptor chain for non-ctrl endpoints */
40869383e084SVardan Mikayelyan 	if (using_desc_dma(hsotg) && !hs_ep->desc_list) {
40879383e084SVardan Mikayelyan 		hs_ep->desc_list = dmam_alloc_coherent(hsotg->dev,
408854f37f56SMinas Harutyunyan 			desc_num * sizeof(struct dwc2_dma_desc),
408986e881e7SMarek Szyprowski 			&hs_ep->desc_list_dma, GFP_ATOMIC);
40905f54c54bSVahram Aharonyan 		if (!hs_ep->desc_list) {
40915f54c54bSVahram Aharonyan 			ret = -ENOMEM;
40925f54c54bSVahram Aharonyan 			goto error2;
40935f54c54bSVahram Aharonyan 		}
40945f54c54bSVahram Aharonyan 	}
40955f54c54bSVahram Aharonyan 
409647a1685fSDinh Nguyen 	spin_lock_irqsave(&hsotg->lock, flags);
409747a1685fSDinh Nguyen 
409847a1685fSDinh Nguyen 	epctrl &= ~(DXEPCTL_EPTYPE_MASK | DXEPCTL_MPS_MASK);
409947a1685fSDinh Nguyen 	epctrl |= DXEPCTL_MPS(mps);
410047a1685fSDinh Nguyen 
410147a1685fSDinh Nguyen 	/*
410247a1685fSDinh Nguyen 	 * mark the endpoint as active, otherwise the core may ignore
410347a1685fSDinh Nguyen 	 * transactions entirely for this endpoint
410447a1685fSDinh Nguyen 	 */
410547a1685fSDinh Nguyen 	epctrl |= DXEPCTL_USBACTEP;
410647a1685fSDinh Nguyen 
410747a1685fSDinh Nguyen 	/* update the endpoint state */
4108ee2c40deSVardan Mikayelyan 	dwc2_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps, mc, dir_in);
410947a1685fSDinh Nguyen 
411047a1685fSDinh Nguyen 	/* default, set to non-periodic */
411147a1685fSDinh Nguyen 	hs_ep->isochronous = 0;
411247a1685fSDinh Nguyen 	hs_ep->periodic = 0;
411347a1685fSDinh Nguyen 	hs_ep->halted = 0;
4114b833ce15SMinas Harutyunyan 	hs_ep->wedged = 0;
411547a1685fSDinh Nguyen 	hs_ep->interval = desc->bInterval;
411647a1685fSDinh Nguyen 
4117729cac69SMinas Harutyunyan 	switch (ep_type) {
411847a1685fSDinh Nguyen 	case USB_ENDPOINT_XFER_ISOC:
411947a1685fSDinh Nguyen 		epctrl |= DXEPCTL_EPTYPE_ISO;
412047a1685fSDinh Nguyen 		epctrl |= DXEPCTL_SETEVENFR;
412147a1685fSDinh Nguyen 		hs_ep->isochronous = 1;
4122142bd33fSVardan Mikayelyan 		hs_ep->interval = 1 << (desc->bInterval - 1);
4123837e9f00SVardan Mikayelyan 		hs_ep->target_frame = TARGET_FRAME_INITIAL;
4124ab7d2192SVahram Aharonyan 		hs_ep->next_desc = 0;
4125729cac69SMinas Harutyunyan 		hs_ep->compl_desc = 0;
4126837e9f00SVardan Mikayelyan 		if (dir_in) {
412747a1685fSDinh Nguyen 			hs_ep->periodic = 1;
4128f25c42b8SGevorg Sahakyan 			mask = dwc2_readl(hsotg, DIEPMSK);
4129837e9f00SVardan Mikayelyan 			mask |= DIEPMSK_NAKMSK;
4130f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, mask, DIEPMSK);
4131837e9f00SVardan Mikayelyan 		} else {
413291bb163eSMinas Harutyunyan 			epctrl |= DXEPCTL_SNAK;
4133f25c42b8SGevorg Sahakyan 			mask = dwc2_readl(hsotg, DOEPMSK);
4134837e9f00SVardan Mikayelyan 			mask |= DOEPMSK_OUTTKNEPDISMSK;
4135f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, mask, DOEPMSK);
4136837e9f00SVardan Mikayelyan 		}
413747a1685fSDinh Nguyen 		break;
413847a1685fSDinh Nguyen 
413947a1685fSDinh Nguyen 	case USB_ENDPOINT_XFER_BULK:
414047a1685fSDinh Nguyen 		epctrl |= DXEPCTL_EPTYPE_BULK;
414147a1685fSDinh Nguyen 		break;
414247a1685fSDinh Nguyen 
414347a1685fSDinh Nguyen 	case USB_ENDPOINT_XFER_INT:
4144b203d0a2SRobert Baldyga 		if (dir_in)
414547a1685fSDinh Nguyen 			hs_ep->periodic = 1;
414647a1685fSDinh Nguyen 
4147142bd33fSVardan Mikayelyan 		if (hsotg->gadget.speed == USB_SPEED_HIGH)
4148142bd33fSVardan Mikayelyan 			hs_ep->interval = 1 << (desc->bInterval - 1);
4149142bd33fSVardan Mikayelyan 
415047a1685fSDinh Nguyen 		epctrl |= DXEPCTL_EPTYPE_INTERRUPT;
415147a1685fSDinh Nguyen 		break;
415247a1685fSDinh Nguyen 
415347a1685fSDinh Nguyen 	case USB_ENDPOINT_XFER_CONTROL:
415447a1685fSDinh Nguyen 		epctrl |= DXEPCTL_EPTYPE_CONTROL;
415547a1685fSDinh Nguyen 		break;
415647a1685fSDinh Nguyen 	}
415747a1685fSDinh Nguyen 
415847a1685fSDinh Nguyen 	/*
415947a1685fSDinh Nguyen 	 * if the hardware has dedicated fifos, we must give each IN EP
416047a1685fSDinh Nguyen 	 * a unique tx-fifo even if it is non-periodic.
416147a1685fSDinh Nguyen 	 */
416221f3bb52SRobert Baldyga 	if (dir_in && hsotg->dedicated_fifos) {
4163644139f8SJohn Keeping 		unsigned fifo_count = dwc2_hsotg_tx_fifo_count(hsotg);
4164ca4c55adSMian Yousaf Kaukab 		u32 fifo_index = 0;
4165ca4c55adSMian Yousaf Kaukab 		u32 fifo_size = UINT_MAX;
41669da51974SJohn Youn 
4167b203d0a2SRobert Baldyga 		size = hs_ep->ep.maxpacket * hs_ep->mc;
4168644139f8SJohn Keeping 		for (i = 1; i <= fifo_count; ++i) {
4169b203d0a2SRobert Baldyga 			if (hsotg->fifo_map & (1 << i))
4170b203d0a2SRobert Baldyga 				continue;
4171f25c42b8SGevorg Sahakyan 			val = dwc2_readl(hsotg, DPTXFSIZN(i));
4172b203d0a2SRobert Baldyga 			val = (val >> FIFOSIZE_DEPTH_SHIFT) * 4;
4173b203d0a2SRobert Baldyga 			if (val < size)
4174b203d0a2SRobert Baldyga 				continue;
4175ca4c55adSMian Yousaf Kaukab 			/* Search for smallest acceptable fifo */
4176ca4c55adSMian Yousaf Kaukab 			if (val < fifo_size) {
4177ca4c55adSMian Yousaf Kaukab 				fifo_size = val;
4178ca4c55adSMian Yousaf Kaukab 				fifo_index = i;
4179b203d0a2SRobert Baldyga 			}
4180ca4c55adSMian Yousaf Kaukab 		}
4181ca4c55adSMian Yousaf Kaukab 		if (!fifo_index) {
41825f2196bdSMian Yousaf Kaukab 			dev_err(hsotg->dev,
41835f2196bdSMian Yousaf Kaukab 				"%s: No suitable fifo found\n", __func__);
4184b585a48bSSudip Mukherjee 			ret = -ENOMEM;
41855f54c54bSVahram Aharonyan 			goto error1;
4186b585a48bSSudip Mukherjee 		}
418797311c8fSMinas Harutyunyan 		epctrl &= ~(DXEPCTL_TXFNUM_LIMIT << DXEPCTL_TXFNUM_SHIFT);
4188ca4c55adSMian Yousaf Kaukab 		hsotg->fifo_map |= 1 << fifo_index;
4189ca4c55adSMian Yousaf Kaukab 		epctrl |= DXEPCTL_TXFNUM(fifo_index);
4190ca4c55adSMian Yousaf Kaukab 		hs_ep->fifo_index = fifo_index;
4191ca4c55adSMian Yousaf Kaukab 		hs_ep->fifo_size = fifo_size;
4192b203d0a2SRobert Baldyga 	}
419347a1685fSDinh Nguyen 
419447a1685fSDinh Nguyen 	/* for non control endpoints, set PID to D0 */
4195837e9f00SVardan Mikayelyan 	if (index && !hs_ep->isochronous)
419647a1685fSDinh Nguyen 		epctrl |= DXEPCTL_SETD0PID;
419747a1685fSDinh Nguyen 
41985295322aSArtur Petrosyan 	/* WA for Full speed ISOC IN in DDMA mode.
41995295322aSArtur Petrosyan 	 * By Clear NAK status of EP, core will send ZLP
42005295322aSArtur Petrosyan 	 * to IN token and assert NAK interrupt relying
42015295322aSArtur Petrosyan 	 * on TxFIFO status only
42025295322aSArtur Petrosyan 	 */
42035295322aSArtur Petrosyan 
42045295322aSArtur Petrosyan 	if (hsotg->gadget.speed == USB_SPEED_FULL &&
42055295322aSArtur Petrosyan 	    hs_ep->isochronous && dir_in) {
42065295322aSArtur Petrosyan 		/* The WA applies only to core versions from 2.72a
42075295322aSArtur Petrosyan 		 * to 4.00a (including both). Also for FS_IOT_1.00a
42085295322aSArtur Petrosyan 		 * and HS_IOT_1.00a.
42095295322aSArtur Petrosyan 		 */
4210f25c42b8SGevorg Sahakyan 		u32 gsnpsid = dwc2_readl(hsotg, GSNPSID);
42115295322aSArtur Petrosyan 
42125295322aSArtur Petrosyan 		if ((gsnpsid >= DWC2_CORE_REV_2_72a &&
42135295322aSArtur Petrosyan 		     gsnpsid <= DWC2_CORE_REV_4_00a) ||
42145295322aSArtur Petrosyan 		     gsnpsid == DWC2_FS_IOT_REV_1_00a ||
42155295322aSArtur Petrosyan 		     gsnpsid == DWC2_HS_IOT_REV_1_00a)
42165295322aSArtur Petrosyan 			epctrl |= DXEPCTL_CNAK;
42175295322aSArtur Petrosyan 	}
42185295322aSArtur Petrosyan 
421947a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: write DxEPCTL=0x%08x\n",
422047a1685fSDinh Nguyen 		__func__, epctrl);
422147a1685fSDinh Nguyen 
4222f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, epctrl, epctrl_reg);
422347a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x\n",
4224f25c42b8SGevorg Sahakyan 		__func__, dwc2_readl(hsotg, epctrl_reg));
422547a1685fSDinh Nguyen 
422647a1685fSDinh Nguyen 	/* enable the endpoint interrupt */
42271f91b4ccSFelipe Balbi 	dwc2_hsotg_ctrl_epint(hsotg, index, dir_in, 1);
422847a1685fSDinh Nguyen 
42295f54c54bSVahram Aharonyan error1:
423047a1685fSDinh Nguyen 	spin_unlock_irqrestore(&hsotg->lock, flags);
42315f54c54bSVahram Aharonyan 
42325f54c54bSVahram Aharonyan error2:
42335f54c54bSVahram Aharonyan 	if (ret && using_desc_dma(hsotg) && hs_ep->desc_list) {
423454f37f56SMinas Harutyunyan 		dmam_free_coherent(hsotg->dev, desc_num *
42355f54c54bSVahram Aharonyan 			sizeof(struct dwc2_dma_desc),
42365f54c54bSVahram Aharonyan 			hs_ep->desc_list, hs_ep->desc_list_dma);
42375f54c54bSVahram Aharonyan 		hs_ep->desc_list = NULL;
42385f54c54bSVahram Aharonyan 	}
42395f54c54bSVahram Aharonyan 
424047a1685fSDinh Nguyen 	return ret;
424147a1685fSDinh Nguyen }
424247a1685fSDinh Nguyen 
424347a1685fSDinh Nguyen /**
42441f91b4ccSFelipe Balbi  * dwc2_hsotg_ep_disable - disable given endpoint
424547a1685fSDinh Nguyen  * @ep: The endpoint to disable.
424647a1685fSDinh Nguyen  */
dwc2_hsotg_ep_disable(struct usb_ep * ep)42471f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
424847a1685fSDinh Nguyen {
42491f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
4250941fcce4SDinh Nguyen 	struct dwc2_hsotg *hsotg = hs_ep->parent;
425147a1685fSDinh Nguyen 	int dir_in = hs_ep->dir_in;
425247a1685fSDinh Nguyen 	int index = hs_ep->index;
425347a1685fSDinh Nguyen 	u32 epctrl_reg;
425447a1685fSDinh Nguyen 	u32 ctrl;
425547a1685fSDinh Nguyen 
42561e011293SMarek Szyprowski 	dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep);
425747a1685fSDinh Nguyen 
4258c6f5c050SMian Yousaf Kaukab 	if (ep == &hsotg->eps_out[0]->ep) {
425947a1685fSDinh Nguyen 		dev_err(hsotg->dev, "%s: called for ep0\n", __func__);
426047a1685fSDinh Nguyen 		return -EINVAL;
426147a1685fSDinh Nguyen 	}
426247a1685fSDinh Nguyen 
42639b481092SJohn Stultz 	if (hsotg->op_state != OTG_STATE_B_PERIPHERAL) {
42649b481092SJohn Stultz 		dev_err(hsotg->dev, "%s: called in host mode?\n", __func__);
42659b481092SJohn Stultz 		return -EINVAL;
42669b481092SJohn Stultz 	}
42679b481092SJohn Stultz 
426847a1685fSDinh Nguyen 	epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
426947a1685fSDinh Nguyen 
4270f25c42b8SGevorg Sahakyan 	ctrl = dwc2_readl(hsotg, epctrl_reg);
4271a4f82771SVahram Aharonyan 
4272a4f82771SVahram Aharonyan 	if (ctrl & DXEPCTL_EPENA)
4273a4f82771SVahram Aharonyan 		dwc2_hsotg_ep_stop_xfr(hsotg, hs_ep);
4274a4f82771SVahram Aharonyan 
427547a1685fSDinh Nguyen 	ctrl &= ~DXEPCTL_EPENA;
427647a1685fSDinh Nguyen 	ctrl &= ~DXEPCTL_USBACTEP;
427747a1685fSDinh Nguyen 	ctrl |= DXEPCTL_SNAK;
427847a1685fSDinh Nguyen 
427947a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
4280f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, ctrl, epctrl_reg);
428147a1685fSDinh Nguyen 
428247a1685fSDinh Nguyen 	/* disable endpoint interrupts */
42831f91b4ccSFelipe Balbi 	dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0);
428447a1685fSDinh Nguyen 
42851141ea01SMian Yousaf Kaukab 	/* terminate all requests with shutdown */
42861141ea01SMian Yousaf Kaukab 	kill_all_requests(hsotg, hs_ep, -ESHUTDOWN);
42871141ea01SMian Yousaf Kaukab 
42881c07b20eSRobert Baldyga 	hsotg->fifo_map &= ~(1 << hs_ep->fifo_index);
42891c07b20eSRobert Baldyga 	hs_ep->fifo_index = 0;
42901c07b20eSRobert Baldyga 	hs_ep->fifo_size = 0;
42911c07b20eSRobert Baldyga 
429247a1685fSDinh Nguyen 	return 0;
429347a1685fSDinh Nguyen }
429447a1685fSDinh Nguyen 
dwc2_hsotg_ep_disable_lock(struct usb_ep * ep)42954fe4f9feSMinas Harutyunyan static int dwc2_hsotg_ep_disable_lock(struct usb_ep *ep)
42964fe4f9feSMinas Harutyunyan {
42974fe4f9feSMinas Harutyunyan 	struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
42984fe4f9feSMinas Harutyunyan 	struct dwc2_hsotg *hsotg = hs_ep->parent;
42994fe4f9feSMinas Harutyunyan 	unsigned long flags;
43004fe4f9feSMinas Harutyunyan 	int ret;
43014fe4f9feSMinas Harutyunyan 
43024fe4f9feSMinas Harutyunyan 	spin_lock_irqsave(&hsotg->lock, flags);
43034fe4f9feSMinas Harutyunyan 	ret = dwc2_hsotg_ep_disable(ep);
43044fe4f9feSMinas Harutyunyan 	spin_unlock_irqrestore(&hsotg->lock, flags);
43054fe4f9feSMinas Harutyunyan 	return ret;
43064fe4f9feSMinas Harutyunyan }
43074fe4f9feSMinas Harutyunyan 
430847a1685fSDinh Nguyen /**
430947a1685fSDinh Nguyen  * on_list - check request is on the given endpoint
431047a1685fSDinh Nguyen  * @ep: The endpoint to check.
431147a1685fSDinh Nguyen  * @test: The request to test if it is on the endpoint.
431247a1685fSDinh Nguyen  */
on_list(struct dwc2_hsotg_ep * ep,struct dwc2_hsotg_req * test)43131f91b4ccSFelipe Balbi static bool on_list(struct dwc2_hsotg_ep *ep, struct dwc2_hsotg_req *test)
431447a1685fSDinh Nguyen {
43151f91b4ccSFelipe Balbi 	struct dwc2_hsotg_req *req, *treq;
431647a1685fSDinh Nguyen 
431747a1685fSDinh Nguyen 	list_for_each_entry_safe(req, treq, &ep->queue, queue) {
431847a1685fSDinh Nguyen 		if (req == test)
431947a1685fSDinh Nguyen 			return true;
432047a1685fSDinh Nguyen 	}
432147a1685fSDinh Nguyen 
432247a1685fSDinh Nguyen 	return false;
432347a1685fSDinh Nguyen }
432447a1685fSDinh Nguyen 
432547a1685fSDinh Nguyen /**
43261f91b4ccSFelipe Balbi  * dwc2_hsotg_ep_dequeue - dequeue given endpoint
432747a1685fSDinh Nguyen  * @ep: The endpoint to dequeue.
432847a1685fSDinh Nguyen  * @req: The request to be removed from a queue.
432947a1685fSDinh Nguyen  */
dwc2_hsotg_ep_dequeue(struct usb_ep * ep,struct usb_request * req)43301f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
433147a1685fSDinh Nguyen {
43321f91b4ccSFelipe Balbi 	struct dwc2_hsotg_req *hs_req = our_req(req);
43331f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
4334941fcce4SDinh Nguyen 	struct dwc2_hsotg *hs = hs_ep->parent;
433547a1685fSDinh Nguyen 	unsigned long flags;
433647a1685fSDinh Nguyen 
43371e011293SMarek Szyprowski 	dev_dbg(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
433847a1685fSDinh Nguyen 
433947a1685fSDinh Nguyen 	spin_lock_irqsave(&hs->lock, flags);
434047a1685fSDinh Nguyen 
434147a1685fSDinh Nguyen 	if (!on_list(hs_ep, hs_req)) {
434247a1685fSDinh Nguyen 		spin_unlock_irqrestore(&hs->lock, flags);
434347a1685fSDinh Nguyen 		return -EINVAL;
434447a1685fSDinh Nguyen 	}
434547a1685fSDinh Nguyen 
4346c524dd5fSMian Yousaf Kaukab 	/* Dequeue already started request */
4347c524dd5fSMian Yousaf Kaukab 	if (req == &hs_ep->req->req)
4348c524dd5fSMian Yousaf Kaukab 		dwc2_hsotg_ep_stop_xfr(hs, hs_ep);
4349c524dd5fSMian Yousaf Kaukab 
43501f91b4ccSFelipe Balbi 	dwc2_hsotg_complete_request(hs, hs_ep, hs_req, -ECONNRESET);
435147a1685fSDinh Nguyen 	spin_unlock_irqrestore(&hs->lock, flags);
435247a1685fSDinh Nguyen 
435347a1685fSDinh Nguyen 	return 0;
435447a1685fSDinh Nguyen }
435547a1685fSDinh Nguyen 
435647a1685fSDinh Nguyen /**
4357b833ce15SMinas Harutyunyan  * dwc2_gadget_ep_set_wedge - set wedge on a given endpoint
4358b833ce15SMinas Harutyunyan  * @ep: The endpoint to be wedged.
4359b833ce15SMinas Harutyunyan  *
4360b833ce15SMinas Harutyunyan  */
dwc2_gadget_ep_set_wedge(struct usb_ep * ep)4361b833ce15SMinas Harutyunyan static int dwc2_gadget_ep_set_wedge(struct usb_ep *ep)
4362b833ce15SMinas Harutyunyan {
4363b833ce15SMinas Harutyunyan 	struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
4364b833ce15SMinas Harutyunyan 	struct dwc2_hsotg *hs = hs_ep->parent;
4365b833ce15SMinas Harutyunyan 
4366b833ce15SMinas Harutyunyan 	unsigned long	flags;
4367b833ce15SMinas Harutyunyan 	int		ret;
4368b833ce15SMinas Harutyunyan 
4369b833ce15SMinas Harutyunyan 	spin_lock_irqsave(&hs->lock, flags);
4370b833ce15SMinas Harutyunyan 	hs_ep->wedged = 1;
4371b833ce15SMinas Harutyunyan 	ret = dwc2_hsotg_ep_sethalt(ep, 1, false);
4372b833ce15SMinas Harutyunyan 	spin_unlock_irqrestore(&hs->lock, flags);
4373b833ce15SMinas Harutyunyan 
4374b833ce15SMinas Harutyunyan 	return ret;
4375b833ce15SMinas Harutyunyan }
4376b833ce15SMinas Harutyunyan 
4377b833ce15SMinas Harutyunyan /**
43781f91b4ccSFelipe Balbi  * dwc2_hsotg_ep_sethalt - set halt on a given endpoint
437947a1685fSDinh Nguyen  * @ep: The endpoint to set halt.
438047a1685fSDinh Nguyen  * @value: Set or unset the halt.
438151da43b5SVahram Aharonyan  * @now: If true, stall the endpoint now. Otherwise return -EAGAIN if
438251da43b5SVahram Aharonyan  *       the endpoint is busy processing requests.
438351da43b5SVahram Aharonyan  *
438451da43b5SVahram Aharonyan  * We need to stall the endpoint immediately if request comes from set_feature
438551da43b5SVahram Aharonyan  * protocol command handler.
438647a1685fSDinh Nguyen  */
dwc2_hsotg_ep_sethalt(struct usb_ep * ep,int value,bool now)438751da43b5SVahram Aharonyan static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now)
438847a1685fSDinh Nguyen {
43891f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
4390941fcce4SDinh Nguyen 	struct dwc2_hsotg *hs = hs_ep->parent;
439147a1685fSDinh Nguyen 	int index = hs_ep->index;
439247a1685fSDinh Nguyen 	u32 epreg;
439347a1685fSDinh Nguyen 	u32 epctl;
439447a1685fSDinh Nguyen 	u32 xfertype;
439547a1685fSDinh Nguyen 
439647a1685fSDinh Nguyen 	dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value);
439747a1685fSDinh Nguyen 
439847a1685fSDinh Nguyen 	if (index == 0) {
439947a1685fSDinh Nguyen 		if (value)
44001f91b4ccSFelipe Balbi 			dwc2_hsotg_stall_ep0(hs);
440147a1685fSDinh Nguyen 		else
440247a1685fSDinh Nguyen 			dev_warn(hs->dev,
440347a1685fSDinh Nguyen 				 "%s: can't clear halt on ep0\n", __func__);
440447a1685fSDinh Nguyen 		return 0;
440547a1685fSDinh Nguyen 	}
440647a1685fSDinh Nguyen 
440715186f10SVahram Aharonyan 	if (hs_ep->isochronous) {
440815186f10SVahram Aharonyan 		dev_err(hs->dev, "%s is Isochronous Endpoint\n", ep->name);
440915186f10SVahram Aharonyan 		return -EINVAL;
441015186f10SVahram Aharonyan 	}
441115186f10SVahram Aharonyan 
441251da43b5SVahram Aharonyan 	if (!now && value && !list_empty(&hs_ep->queue)) {
441351da43b5SVahram Aharonyan 		dev_dbg(hs->dev, "%s request is pending, cannot halt\n",
441451da43b5SVahram Aharonyan 			ep->name);
441551da43b5SVahram Aharonyan 		return -EAGAIN;
441651da43b5SVahram Aharonyan 	}
441751da43b5SVahram Aharonyan 
4418c6f5c050SMian Yousaf Kaukab 	if (hs_ep->dir_in) {
441947a1685fSDinh Nguyen 		epreg = DIEPCTL(index);
4420f25c42b8SGevorg Sahakyan 		epctl = dwc2_readl(hs, epreg);
442147a1685fSDinh Nguyen 
442247a1685fSDinh Nguyen 		if (value) {
44235a350d53SFelipe Balbi 			epctl |= DXEPCTL_STALL | DXEPCTL_SNAK;
442447a1685fSDinh Nguyen 			if (epctl & DXEPCTL_EPENA)
442547a1685fSDinh Nguyen 				epctl |= DXEPCTL_EPDIS;
442647a1685fSDinh Nguyen 		} else {
442747a1685fSDinh Nguyen 			epctl &= ~DXEPCTL_STALL;
4428b833ce15SMinas Harutyunyan 			hs_ep->wedged = 0;
442947a1685fSDinh Nguyen 			xfertype = epctl & DXEPCTL_EPTYPE_MASK;
443047a1685fSDinh Nguyen 			if (xfertype == DXEPCTL_EPTYPE_BULK ||
443147a1685fSDinh Nguyen 			    xfertype == DXEPCTL_EPTYPE_INTERRUPT)
443247a1685fSDinh Nguyen 				epctl |= DXEPCTL_SETD0PID;
443347a1685fSDinh Nguyen 		}
4434f25c42b8SGevorg Sahakyan 		dwc2_writel(hs, epctl, epreg);
4435c6f5c050SMian Yousaf Kaukab 	} else {
443647a1685fSDinh Nguyen 		epreg = DOEPCTL(index);
4437f25c42b8SGevorg Sahakyan 		epctl = dwc2_readl(hs, epreg);
443847a1685fSDinh Nguyen 
443934c0887fSJohn Youn 		if (value) {
4440fecb3a17SMinas Harutyunyan 			/* Unmask GOUTNAKEFF interrupt */
4441fecb3a17SMinas Harutyunyan 			dwc2_hsotg_en_gsint(hs, GINTSTS_GOUTNAKEFF);
4442fecb3a17SMinas Harutyunyan 
44436070636cSMinas Harutyunyan 			if (!(dwc2_readl(hs, GINTSTS) & GINTSTS_GOUTNAKEFF))
44446070636cSMinas Harutyunyan 				dwc2_set_bit(hs, DCTL, DCTL_SGOUTNAK);
44456070636cSMinas Harutyunyan 			// STALL bit will be set in GOUTNAKEFF interrupt handler
444634c0887fSJohn Youn 		} else {
444747a1685fSDinh Nguyen 			epctl &= ~DXEPCTL_STALL;
4448b833ce15SMinas Harutyunyan 			hs_ep->wedged = 0;
444947a1685fSDinh Nguyen 			xfertype = epctl & DXEPCTL_EPTYPE_MASK;
445047a1685fSDinh Nguyen 			if (xfertype == DXEPCTL_EPTYPE_BULK ||
445147a1685fSDinh Nguyen 			    xfertype == DXEPCTL_EPTYPE_INTERRUPT)
445247a1685fSDinh Nguyen 				epctl |= DXEPCTL_SETD0PID;
4453f25c42b8SGevorg Sahakyan 			dwc2_writel(hs, epctl, epreg);
4454c6f5c050SMian Yousaf Kaukab 		}
44556070636cSMinas Harutyunyan 	}
445647a1685fSDinh Nguyen 
445747a1685fSDinh Nguyen 	hs_ep->halted = value;
445847a1685fSDinh Nguyen 	return 0;
445947a1685fSDinh Nguyen }
446047a1685fSDinh Nguyen 
446147a1685fSDinh Nguyen /**
44621f91b4ccSFelipe Balbi  * dwc2_hsotg_ep_sethalt_lock - set halt on a given endpoint with lock held
446347a1685fSDinh Nguyen  * @ep: The endpoint to set halt.
446447a1685fSDinh Nguyen  * @value: Set or unset the halt.
446547a1685fSDinh Nguyen  */
dwc2_hsotg_ep_sethalt_lock(struct usb_ep * ep,int value)44661f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value)
446747a1685fSDinh Nguyen {
44681f91b4ccSFelipe Balbi 	struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
4469941fcce4SDinh Nguyen 	struct dwc2_hsotg *hs = hs_ep->parent;
44708879904bSJohan Hovold 	unsigned long flags;
44718879904bSJohan Hovold 	int ret;
447247a1685fSDinh Nguyen 
447347a1685fSDinh Nguyen 	spin_lock_irqsave(&hs->lock, flags);
447451da43b5SVahram Aharonyan 	ret = dwc2_hsotg_ep_sethalt(ep, value, false);
447547a1685fSDinh Nguyen 	spin_unlock_irqrestore(&hs->lock, flags);
447647a1685fSDinh Nguyen 
447747a1685fSDinh Nguyen 	return ret;
447847a1685fSDinh Nguyen }
447947a1685fSDinh Nguyen 
4480ebce561aSBhumika Goyal static const struct usb_ep_ops dwc2_hsotg_ep_ops = {
44811f91b4ccSFelipe Balbi 	.enable		= dwc2_hsotg_ep_enable,
44824fe4f9feSMinas Harutyunyan 	.disable	= dwc2_hsotg_ep_disable_lock,
44831f91b4ccSFelipe Balbi 	.alloc_request	= dwc2_hsotg_ep_alloc_request,
44841f91b4ccSFelipe Balbi 	.free_request	= dwc2_hsotg_ep_free_request,
44851f91b4ccSFelipe Balbi 	.queue		= dwc2_hsotg_ep_queue_lock,
44861f91b4ccSFelipe Balbi 	.dequeue	= dwc2_hsotg_ep_dequeue,
44871f91b4ccSFelipe Balbi 	.set_halt	= dwc2_hsotg_ep_sethalt_lock,
4488b833ce15SMinas Harutyunyan 	.set_wedge	= dwc2_gadget_ep_set_wedge,
448947a1685fSDinh Nguyen 	/* note, don't believe we have any call for the fifo routines */
449047a1685fSDinh Nguyen };
449147a1685fSDinh Nguyen 
449247a1685fSDinh Nguyen /**
44939da51974SJohn Youn  * dwc2_hsotg_init - initialize the usb core
449447a1685fSDinh Nguyen  * @hsotg: The driver state
449547a1685fSDinh Nguyen  */
dwc2_hsotg_init(struct dwc2_hsotg * hsotg)44961f91b4ccSFelipe Balbi static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg)
449747a1685fSDinh Nguyen {
449847a1685fSDinh Nguyen 	/* unmask subset of endpoint interrupts */
449947a1685fSDinh Nguyen 
4500f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
450147a1685fSDinh Nguyen 		    DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK,
4502f25c42b8SGevorg Sahakyan 		    DIEPMSK);
450347a1685fSDinh Nguyen 
4504f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, DOEPMSK_SETUPMSK | DOEPMSK_AHBERRMSK |
450547a1685fSDinh Nguyen 		    DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK,
4506f25c42b8SGevorg Sahakyan 		    DOEPMSK);
450747a1685fSDinh Nguyen 
4508f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, 0, DAINTMSK);
450947a1685fSDinh Nguyen 
451047a1685fSDinh Nguyen 	/* Be in disconnected state until gadget is registered */
4511f25c42b8SGevorg Sahakyan 	dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);
451247a1685fSDinh Nguyen 
451347a1685fSDinh Nguyen 	/* setup fifos */
451447a1685fSDinh Nguyen 
451547a1685fSDinh Nguyen 	dev_dbg(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
4516f25c42b8SGevorg Sahakyan 		dwc2_readl(hsotg, GRXFSIZ),
4517f25c42b8SGevorg Sahakyan 		dwc2_readl(hsotg, GNPTXFSIZ));
451847a1685fSDinh Nguyen 
45191f91b4ccSFelipe Balbi 	dwc2_hsotg_init_fifo(hsotg);
452047a1685fSDinh Nguyen 
4521f5090044SGregory Herrero 	if (using_dma(hsotg))
4522f25c42b8SGevorg Sahakyan 		dwc2_set_bit(hsotg, GAHBCFG, GAHBCFG_DMA_EN);
452347a1685fSDinh Nguyen }
452447a1685fSDinh Nguyen 
452547a1685fSDinh Nguyen /**
45261f91b4ccSFelipe Balbi  * dwc2_hsotg_udc_start - prepare the udc for work
452747a1685fSDinh Nguyen  * @gadget: The usb gadget state
452847a1685fSDinh Nguyen  * @driver: The usb gadget driver
452947a1685fSDinh Nguyen  *
453047a1685fSDinh Nguyen  * Perform initialization to prepare udc device and driver
453147a1685fSDinh Nguyen  * to work.
453247a1685fSDinh Nguyen  */
dwc2_hsotg_udc_start(struct usb_gadget * gadget,struct usb_gadget_driver * driver)45331f91b4ccSFelipe Balbi static int dwc2_hsotg_udc_start(struct usb_gadget *gadget,
453447a1685fSDinh Nguyen 				struct usb_gadget_driver *driver)
453547a1685fSDinh Nguyen {
4536941fcce4SDinh Nguyen 	struct dwc2_hsotg *hsotg = to_hsotg(gadget);
45375b9451f8SMarek Szyprowski 	unsigned long flags;
453847a1685fSDinh Nguyen 	int ret;
453947a1685fSDinh Nguyen 
454047a1685fSDinh Nguyen 	if (!hsotg) {
454147a1685fSDinh Nguyen 		pr_err("%s: called with no device\n", __func__);
454247a1685fSDinh Nguyen 		return -ENODEV;
454347a1685fSDinh Nguyen 	}
454447a1685fSDinh Nguyen 
454547a1685fSDinh Nguyen 	if (!driver) {
454647a1685fSDinh Nguyen 		dev_err(hsotg->dev, "%s: no driver\n", __func__);
454747a1685fSDinh Nguyen 		return -EINVAL;
454847a1685fSDinh Nguyen 	}
454947a1685fSDinh Nguyen 
455047a1685fSDinh Nguyen 	if (driver->max_speed < USB_SPEED_FULL)
455147a1685fSDinh Nguyen 		dev_err(hsotg->dev, "%s: bad speed\n", __func__);
455247a1685fSDinh Nguyen 
455347a1685fSDinh Nguyen 	if (!driver->setup) {
455447a1685fSDinh Nguyen 		dev_err(hsotg->dev, "%s: missing entry points\n", __func__);
455547a1685fSDinh Nguyen 		return -EINVAL;
455647a1685fSDinh Nguyen 	}
455747a1685fSDinh Nguyen 
455847a1685fSDinh Nguyen 	WARN_ON(hsotg->driver);
455947a1685fSDinh Nguyen 
456047a1685fSDinh Nguyen 	hsotg->driver = driver;
456147a1685fSDinh Nguyen 	hsotg->gadget.dev.of_node = hsotg->dev->of_node;
456247a1685fSDinh Nguyen 	hsotg->gadget.speed = USB_SPEED_UNKNOWN;
456347a1685fSDinh Nguyen 
456450213832SFabrice Gasnier 	if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) {
456509a75e85SMarek Szyprowski 		ret = dwc2_lowlevel_hw_enable(hsotg);
456609a75e85SMarek Szyprowski 		if (ret)
456747a1685fSDinh Nguyen 			goto err;
456847a1685fSDinh Nguyen 	}
456947a1685fSDinh Nguyen 
4570f6c01592SGregory Herrero 	if (!IS_ERR_OR_NULL(hsotg->uphy))
4571f6c01592SGregory Herrero 		otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget);
4572c816c47fSMarek Szyprowski 
45735b9451f8SMarek Szyprowski 	spin_lock_irqsave(&hsotg->lock, flags);
4574d0f0ac56SJohn Youn 	if (dwc2_hw_is_device(hsotg)) {
45751f91b4ccSFelipe Balbi 		dwc2_hsotg_init(hsotg);
45761f91b4ccSFelipe Balbi 		dwc2_hsotg_core_init_disconnected(hsotg, false);
4577d0f0ac56SJohn Youn 	}
4578d0f0ac56SJohn Youn 
4579dc6e69e6SMarek Szyprowski 	hsotg->enabled = 0;
45805b9451f8SMarek Szyprowski 	spin_unlock_irqrestore(&hsotg->lock, flags);
45815b9451f8SMarek Szyprowski 
458210209abeSAndrzej Pietrasiewicz 	gadget->sg_supported = using_desc_dma(hsotg);
458347a1685fSDinh Nguyen 	dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name);
45845b9451f8SMarek Szyprowski 
458547a1685fSDinh Nguyen 	return 0;
458647a1685fSDinh Nguyen 
458747a1685fSDinh Nguyen err:
458847a1685fSDinh Nguyen 	hsotg->driver = NULL;
458947a1685fSDinh Nguyen 	return ret;
459047a1685fSDinh Nguyen }
459147a1685fSDinh Nguyen 
459247a1685fSDinh Nguyen /**
45931f91b4ccSFelipe Balbi  * dwc2_hsotg_udc_stop - stop the udc
459447a1685fSDinh Nguyen  * @gadget: The usb gadget state
459547a1685fSDinh Nguyen  *
459647a1685fSDinh Nguyen  * Stop udc hw block and stay tunned for future transmissions
459747a1685fSDinh Nguyen  */
dwc2_hsotg_udc_stop(struct usb_gadget * gadget)45981f91b4ccSFelipe Balbi static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget)
459947a1685fSDinh Nguyen {
4600941fcce4SDinh Nguyen 	struct dwc2_hsotg *hsotg = to_hsotg(gadget);
46018879904bSJohan Hovold 	unsigned long flags;
460247a1685fSDinh Nguyen 	int ep;
460347a1685fSDinh Nguyen 
460447a1685fSDinh Nguyen 	if (!hsotg)
460547a1685fSDinh Nguyen 		return -ENODEV;
460647a1685fSDinh Nguyen 
460747a1685fSDinh Nguyen 	/* all endpoints should be shutdown */
4608c6f5c050SMian Yousaf Kaukab 	for (ep = 1; ep < hsotg->num_of_eps; ep++) {
4609c6f5c050SMian Yousaf Kaukab 		if (hsotg->eps_in[ep])
46104fe4f9feSMinas Harutyunyan 			dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep);
4611c6f5c050SMian Yousaf Kaukab 		if (hsotg->eps_out[ep])
46124fe4f9feSMinas Harutyunyan 			dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep);
4613c6f5c050SMian Yousaf Kaukab 	}
461447a1685fSDinh Nguyen 
461547a1685fSDinh Nguyen 	spin_lock_irqsave(&hsotg->lock, flags);
461647a1685fSDinh Nguyen 
461747a1685fSDinh Nguyen 	hsotg->driver = NULL;
461847a1685fSDinh Nguyen 	hsotg->gadget.speed = USB_SPEED_UNKNOWN;
4619dc6e69e6SMarek Szyprowski 	hsotg->enabled = 0;
462047a1685fSDinh Nguyen 
462147a1685fSDinh Nguyen 	spin_unlock_irqrestore(&hsotg->lock, flags);
462247a1685fSDinh Nguyen 
4623f6c01592SGregory Herrero 	if (!IS_ERR_OR_NULL(hsotg->uphy))
4624f6c01592SGregory Herrero 		otg_set_peripheral(hsotg->uphy->otg, NULL);
4625c816c47fSMarek Szyprowski 
462650213832SFabrice Gasnier 	if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
462709a75e85SMarek Szyprowski 		dwc2_lowlevel_hw_disable(hsotg);
462847a1685fSDinh Nguyen 
462947a1685fSDinh Nguyen 	return 0;
463047a1685fSDinh Nguyen }
463147a1685fSDinh Nguyen 
463247a1685fSDinh Nguyen /**
46331f91b4ccSFelipe Balbi  * dwc2_hsotg_gadget_getframe - read the frame number
463447a1685fSDinh Nguyen  * @gadget: The usb gadget state
463547a1685fSDinh Nguyen  *
463647a1685fSDinh Nguyen  * Read the {micro} frame number
463747a1685fSDinh Nguyen  */
dwc2_hsotg_gadget_getframe(struct usb_gadget * gadget)46381f91b4ccSFelipe Balbi static int dwc2_hsotg_gadget_getframe(struct usb_gadget *gadget)
463947a1685fSDinh Nguyen {
46401f91b4ccSFelipe Balbi 	return dwc2_hsotg_read_frameno(to_hsotg(gadget));
464147a1685fSDinh Nguyen }
464247a1685fSDinh Nguyen 
464347a1685fSDinh Nguyen /**
46441a0808cbSJohn Keeping  * dwc2_hsotg_set_selfpowered - set if device is self/bus powered
46451a0808cbSJohn Keeping  * @gadget: The usb gadget state
46461a0808cbSJohn Keeping  * @is_selfpowered: Whether the device is self-powered
46471a0808cbSJohn Keeping  *
46481a0808cbSJohn Keeping  * Set if the device is self or bus powered.
46491a0808cbSJohn Keeping  */
dwc2_hsotg_set_selfpowered(struct usb_gadget * gadget,int is_selfpowered)46501a0808cbSJohn Keeping static int dwc2_hsotg_set_selfpowered(struct usb_gadget *gadget,
46511a0808cbSJohn Keeping 				      int is_selfpowered)
46521a0808cbSJohn Keeping {
46531a0808cbSJohn Keeping 	struct dwc2_hsotg *hsotg = to_hsotg(gadget);
46541a0808cbSJohn Keeping 	unsigned long flags;
46551a0808cbSJohn Keeping 
46561a0808cbSJohn Keeping 	spin_lock_irqsave(&hsotg->lock, flags);
46571a0808cbSJohn Keeping 	gadget->is_selfpowered = !!is_selfpowered;
46581a0808cbSJohn Keeping 	spin_unlock_irqrestore(&hsotg->lock, flags);
46591a0808cbSJohn Keeping 
46601a0808cbSJohn Keeping 	return 0;
46611a0808cbSJohn Keeping }
46621a0808cbSJohn Keeping 
46631a0808cbSJohn Keeping /**
46641f91b4ccSFelipe Balbi  * dwc2_hsotg_pullup - connect/disconnect the USB PHY
466547a1685fSDinh Nguyen  * @gadget: The usb gadget state
466647a1685fSDinh Nguyen  * @is_on: Current state of the USB PHY
466747a1685fSDinh Nguyen  *
466847a1685fSDinh Nguyen  * Connect/Disconnect the USB PHY pullup
466947a1685fSDinh Nguyen  */
dwc2_hsotg_pullup(struct usb_gadget * gadget,int is_on)46701f91b4ccSFelipe Balbi static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on)
467147a1685fSDinh Nguyen {
4672941fcce4SDinh Nguyen 	struct dwc2_hsotg *hsotg = to_hsotg(gadget);
46738879904bSJohan Hovold 	unsigned long flags;
467447a1685fSDinh Nguyen 
467577ba9119SGregory Herrero 	dev_dbg(hsotg->dev, "%s: is_on: %d op_state: %d\n", __func__, is_on,
467677ba9119SGregory Herrero 		hsotg->op_state);
467777ba9119SGregory Herrero 
467877ba9119SGregory Herrero 	/* Don't modify pullup state while in host mode */
467977ba9119SGregory Herrero 	if (hsotg->op_state != OTG_STATE_B_PERIPHERAL) {
468077ba9119SGregory Herrero 		hsotg->enabled = is_on;
468177ba9119SGregory Herrero 		return 0;
468277ba9119SGregory Herrero 	}
468347a1685fSDinh Nguyen 
468447a1685fSDinh Nguyen 	spin_lock_irqsave(&hsotg->lock, flags);
468547a1685fSDinh Nguyen 	if (is_on) {
4686dc6e69e6SMarek Szyprowski 		hsotg->enabled = 1;
46871f91b4ccSFelipe Balbi 		dwc2_hsotg_core_init_disconnected(hsotg, false);
468866e77a24SRazmik Karapetyan 		/* Enable ACG feature in device mode,if supported */
468966e77a24SRazmik Karapetyan 		dwc2_enable_acg(hsotg);
46901f91b4ccSFelipe Balbi 		dwc2_hsotg_core_connect(hsotg);
469147a1685fSDinh Nguyen 	} else {
46921f91b4ccSFelipe Balbi 		dwc2_hsotg_core_disconnect(hsotg);
46931f91b4ccSFelipe Balbi 		dwc2_hsotg_disconnect(hsotg);
4694dc6e69e6SMarek Szyprowski 		hsotg->enabled = 0;
469547a1685fSDinh Nguyen 	}
469647a1685fSDinh Nguyen 
469747a1685fSDinh Nguyen 	hsotg->gadget.speed = USB_SPEED_UNKNOWN;
469847a1685fSDinh Nguyen 	spin_unlock_irqrestore(&hsotg->lock, flags);
469947a1685fSDinh Nguyen 
470047a1685fSDinh Nguyen 	return 0;
470147a1685fSDinh Nguyen }
470247a1685fSDinh Nguyen 
dwc2_hsotg_vbus_session(struct usb_gadget * gadget,int is_active)47031f91b4ccSFelipe Balbi static int dwc2_hsotg_vbus_session(struct usb_gadget *gadget, int is_active)
470483d98223SGregory Herrero {
470583d98223SGregory Herrero 	struct dwc2_hsotg *hsotg = to_hsotg(gadget);
470683d98223SGregory Herrero 	unsigned long flags;
470783d98223SGregory Herrero 
470883d98223SGregory Herrero 	dev_dbg(hsotg->dev, "%s: is_active: %d\n", __func__, is_active);
470983d98223SGregory Herrero 	spin_lock_irqsave(&hsotg->lock, flags);
471083d98223SGregory Herrero 
471118b2b37cSGregory Herrero 	/*
4712c9c394abSArtur Petrosyan 	 * If controller is in partial power down state, it must exit from
4713c9c394abSArtur Petrosyan 	 * that state before being initialized / de-initialized
471418b2b37cSGregory Herrero 	 */
4715c9c394abSArtur Petrosyan 	if (hsotg->lx_state == DWC2_L2 && hsotg->in_ppd)
4716c9c394abSArtur Petrosyan 		/*
4717c9c394abSArtur Petrosyan 		 * No need to check the return value as
4718c9c394abSArtur Petrosyan 		 * registers are not being restored.
4719c9c394abSArtur Petrosyan 		 */
4720c9c394abSArtur Petrosyan 		dwc2_exit_partial_power_down(hsotg, 0, false);
4721065d3931SGregory Herrero 
472261f7223bSGregory Herrero 	if (is_active) {
472361f7223bSGregory Herrero 		hsotg->op_state = OTG_STATE_B_PERIPHERAL;
472461f7223bSGregory Herrero 
47251f91b4ccSFelipe Balbi 		dwc2_hsotg_core_init_disconnected(hsotg, false);
472666e77a24SRazmik Karapetyan 		if (hsotg->enabled) {
472766e77a24SRazmik Karapetyan 			/* Enable ACG feature in device mode,if supported */
472866e77a24SRazmik Karapetyan 			dwc2_enable_acg(hsotg);
47291f91b4ccSFelipe Balbi 			dwc2_hsotg_core_connect(hsotg);
473066e77a24SRazmik Karapetyan 		}
473183d98223SGregory Herrero 	} else {
47321f91b4ccSFelipe Balbi 		dwc2_hsotg_core_disconnect(hsotg);
47331f91b4ccSFelipe Balbi 		dwc2_hsotg_disconnect(hsotg);
473483d98223SGregory Herrero 	}
473583d98223SGregory Herrero 
473683d98223SGregory Herrero 	spin_unlock_irqrestore(&hsotg->lock, flags);
473783d98223SGregory Herrero 	return 0;
473883d98223SGregory Herrero }
473983d98223SGregory Herrero 
4740596d696aSGregory Herrero /**
47411f91b4ccSFelipe Balbi  * dwc2_hsotg_vbus_draw - report bMaxPower field
4742596d696aSGregory Herrero  * @gadget: The usb gadget state
4743596d696aSGregory Herrero  * @mA: Amount of current
4744596d696aSGregory Herrero  *
4745596d696aSGregory Herrero  * Report how much power the device may consume to the phy.
4746596d696aSGregory Herrero  */
dwc2_hsotg_vbus_draw(struct usb_gadget * gadget,unsigned int mA)47479da51974SJohn Youn static int dwc2_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
4748596d696aSGregory Herrero {
4749596d696aSGregory Herrero 	struct dwc2_hsotg *hsotg = to_hsotg(gadget);
4750596d696aSGregory Herrero 
4751596d696aSGregory Herrero 	if (IS_ERR_OR_NULL(hsotg->uphy))
4752596d696aSGregory Herrero 		return -ENOTSUPP;
4753596d696aSGregory Herrero 	return usb_phy_set_power(hsotg->uphy, mA);
4754596d696aSGregory Herrero }
4755596d696aSGregory Herrero 
dwc2_gadget_set_speed(struct usb_gadget * g,enum usb_device_speed speed)47565324bad6SArgishti Aleksanyan static void dwc2_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed speed)
47575324bad6SArgishti Aleksanyan {
47585324bad6SArgishti Aleksanyan 	struct dwc2_hsotg *hsotg = to_hsotg(g);
47595324bad6SArgishti Aleksanyan 	unsigned long		flags;
47605324bad6SArgishti Aleksanyan 
47615324bad6SArgishti Aleksanyan 	spin_lock_irqsave(&hsotg->lock, flags);
47625324bad6SArgishti Aleksanyan 	switch (speed) {
47635324bad6SArgishti Aleksanyan 	case USB_SPEED_HIGH:
47645324bad6SArgishti Aleksanyan 		hsotg->params.speed = DWC2_SPEED_PARAM_HIGH;
47655324bad6SArgishti Aleksanyan 		break;
47665324bad6SArgishti Aleksanyan 	case USB_SPEED_FULL:
47675324bad6SArgishti Aleksanyan 		hsotg->params.speed = DWC2_SPEED_PARAM_FULL;
47685324bad6SArgishti Aleksanyan 		break;
47695324bad6SArgishti Aleksanyan 	case USB_SPEED_LOW:
47705324bad6SArgishti Aleksanyan 		hsotg->params.speed = DWC2_SPEED_PARAM_LOW;
47715324bad6SArgishti Aleksanyan 		break;
47725324bad6SArgishti Aleksanyan 	default:
47735324bad6SArgishti Aleksanyan 		dev_err(hsotg->dev, "invalid speed (%d)\n", speed);
47745324bad6SArgishti Aleksanyan 	}
47755324bad6SArgishti Aleksanyan 	spin_unlock_irqrestore(&hsotg->lock, flags);
47765324bad6SArgishti Aleksanyan }
47775324bad6SArgishti Aleksanyan 
47781f91b4ccSFelipe Balbi static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {
47791f91b4ccSFelipe Balbi 	.get_frame	= dwc2_hsotg_gadget_getframe,
47801a0808cbSJohn Keeping 	.set_selfpowered	= dwc2_hsotg_set_selfpowered,
47811f91b4ccSFelipe Balbi 	.udc_start		= dwc2_hsotg_udc_start,
47821f91b4ccSFelipe Balbi 	.udc_stop		= dwc2_hsotg_udc_stop,
47831f91b4ccSFelipe Balbi 	.pullup                 = dwc2_hsotg_pullup,
47845324bad6SArgishti Aleksanyan 	.udc_set_speed		= dwc2_gadget_set_speed,
47851f91b4ccSFelipe Balbi 	.vbus_session		= dwc2_hsotg_vbus_session,
47861f91b4ccSFelipe Balbi 	.vbus_draw		= dwc2_hsotg_vbus_draw,
478747a1685fSDinh Nguyen };
478847a1685fSDinh Nguyen 
478947a1685fSDinh Nguyen /**
47901f91b4ccSFelipe Balbi  * dwc2_hsotg_initep - initialise a single endpoint
479147a1685fSDinh Nguyen  * @hsotg: The device state.
479247a1685fSDinh Nguyen  * @hs_ep: The endpoint to be initialised.
479347a1685fSDinh Nguyen  * @epnum: The endpoint number
47946fb914d7SGrigor Tovmasyan  * @dir_in: True if direction is in.
479547a1685fSDinh Nguyen  *
479647a1685fSDinh Nguyen  * Initialise the given endpoint (as part of the probe and device state
479747a1685fSDinh Nguyen  * creation) to give to the gadget driver. Setup the endpoint name, any
479847a1685fSDinh Nguyen  * direction information and other state that may be required.
479947a1685fSDinh Nguyen  */
dwc2_hsotg_initep(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep,int epnum,bool dir_in)48001f91b4ccSFelipe Balbi static void dwc2_hsotg_initep(struct dwc2_hsotg *hsotg,
48011f91b4ccSFelipe Balbi 			      struct dwc2_hsotg_ep *hs_ep,
4802c6f5c050SMian Yousaf Kaukab 				       int epnum,
4803c6f5c050SMian Yousaf Kaukab 				       bool dir_in)
480447a1685fSDinh Nguyen {
480547a1685fSDinh Nguyen 	char *dir;
480647a1685fSDinh Nguyen 
480747a1685fSDinh Nguyen 	if (epnum == 0)
480847a1685fSDinh Nguyen 		dir = "";
4809c6f5c050SMian Yousaf Kaukab 	else if (dir_in)
481047a1685fSDinh Nguyen 		dir = "in";
4811c6f5c050SMian Yousaf Kaukab 	else
4812c6f5c050SMian Yousaf Kaukab 		dir = "out";
481347a1685fSDinh Nguyen 
4814c6f5c050SMian Yousaf Kaukab 	hs_ep->dir_in = dir_in;
481547a1685fSDinh Nguyen 	hs_ep->index = epnum;
481647a1685fSDinh Nguyen 
481747a1685fSDinh Nguyen 	snprintf(hs_ep->name, sizeof(hs_ep->name), "ep%d%s", epnum, dir);
481847a1685fSDinh Nguyen 
481947a1685fSDinh Nguyen 	INIT_LIST_HEAD(&hs_ep->queue);
482047a1685fSDinh Nguyen 	INIT_LIST_HEAD(&hs_ep->ep.ep_list);
482147a1685fSDinh Nguyen 
482247a1685fSDinh Nguyen 	/* add to the list of endpoints known by the gadget driver */
482347a1685fSDinh Nguyen 	if (epnum)
482447a1685fSDinh Nguyen 		list_add_tail(&hs_ep->ep.ep_list, &hsotg->gadget.ep_list);
482547a1685fSDinh Nguyen 
482647a1685fSDinh Nguyen 	hs_ep->parent = hsotg;
482747a1685fSDinh Nguyen 	hs_ep->ep.name = hs_ep->name;
482838e9002bSVardan Mikayelyan 
482938e9002bSVardan Mikayelyan 	if (hsotg->params.speed == DWC2_SPEED_PARAM_LOW)
483038e9002bSVardan Mikayelyan 		usb_ep_set_maxpacket_limit(&hs_ep->ep, 8);
483138e9002bSVardan Mikayelyan 	else
483238e9002bSVardan Mikayelyan 		usb_ep_set_maxpacket_limit(&hs_ep->ep,
483338e9002bSVardan Mikayelyan 					   epnum ? 1024 : EP0_MPS_LIMIT);
48341f91b4ccSFelipe Balbi 	hs_ep->ep.ops = &dwc2_hsotg_ep_ops;
483547a1685fSDinh Nguyen 
48362954522fSRobert Baldyga 	if (epnum == 0) {
48372954522fSRobert Baldyga 		hs_ep->ep.caps.type_control = true;
48382954522fSRobert Baldyga 	} else {
483938e9002bSVardan Mikayelyan 		if (hsotg->params.speed != DWC2_SPEED_PARAM_LOW) {
48402954522fSRobert Baldyga 			hs_ep->ep.caps.type_iso = true;
48412954522fSRobert Baldyga 			hs_ep->ep.caps.type_bulk = true;
484238e9002bSVardan Mikayelyan 		}
48432954522fSRobert Baldyga 		hs_ep->ep.caps.type_int = true;
48442954522fSRobert Baldyga 	}
48452954522fSRobert Baldyga 
48462954522fSRobert Baldyga 	if (dir_in)
48472954522fSRobert Baldyga 		hs_ep->ep.caps.dir_in = true;
48482954522fSRobert Baldyga 	else
48492954522fSRobert Baldyga 		hs_ep->ep.caps.dir_out = true;
48502954522fSRobert Baldyga 
485147a1685fSDinh Nguyen 	/*
485247a1685fSDinh Nguyen 	 * if we're using dma, we need to set the next-endpoint pointer
485347a1685fSDinh Nguyen 	 * to be something valid.
485447a1685fSDinh Nguyen 	 */
485547a1685fSDinh Nguyen 
485647a1685fSDinh Nguyen 	if (using_dma(hsotg)) {
485747a1685fSDinh Nguyen 		u32 next = DXEPCTL_NEXTEP((epnum + 1) % 15);
48589da51974SJohn Youn 
4859c6f5c050SMian Yousaf Kaukab 		if (dir_in)
4860f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, next, DIEPCTL(epnum));
4861c6f5c050SMian Yousaf Kaukab 		else
4862f25c42b8SGevorg Sahakyan 			dwc2_writel(hsotg, next, DOEPCTL(epnum));
486347a1685fSDinh Nguyen 	}
486447a1685fSDinh Nguyen }
486547a1685fSDinh Nguyen 
486647a1685fSDinh Nguyen /**
48671f91b4ccSFelipe Balbi  * dwc2_hsotg_hw_cfg - read HW configuration registers
48686fb914d7SGrigor Tovmasyan  * @hsotg: Programming view of the DWC_otg controller
486947a1685fSDinh Nguyen  *
487047a1685fSDinh Nguyen  * Read the USB core HW configuration registers
487147a1685fSDinh Nguyen  */
dwc2_hsotg_hw_cfg(struct dwc2_hsotg * hsotg)48721f91b4ccSFelipe Balbi static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
487347a1685fSDinh Nguyen {
4874c6f5c050SMian Yousaf Kaukab 	u32 cfg;
4875c6f5c050SMian Yousaf Kaukab 	u32 ep_type;
4876c6f5c050SMian Yousaf Kaukab 	u32 i;
4877c6f5c050SMian Yousaf Kaukab 
487847a1685fSDinh Nguyen 	/* check hardware configuration */
487947a1685fSDinh Nguyen 
488043e90349SJohn Youn 	hsotg->num_of_eps = hsotg->hw_params.num_dev_ep;
488143e90349SJohn Youn 
4882c6f5c050SMian Yousaf Kaukab 	/* Add ep0 */
4883c6f5c050SMian Yousaf Kaukab 	hsotg->num_of_eps++;
488447a1685fSDinh Nguyen 
4885b98866c2SJohn Youn 	hsotg->eps_in[0] = devm_kzalloc(hsotg->dev,
4886b98866c2SJohn Youn 					sizeof(struct dwc2_hsotg_ep),
4887c6f5c050SMian Yousaf Kaukab 					GFP_KERNEL);
4888c6f5c050SMian Yousaf Kaukab 	if (!hsotg->eps_in[0])
4889c6f5c050SMian Yousaf Kaukab 		return -ENOMEM;
48901f91b4ccSFelipe Balbi 	/* Same dwc2_hsotg_ep is used in both directions for ep0 */
4891c6f5c050SMian Yousaf Kaukab 	hsotg->eps_out[0] = hsotg->eps_in[0];
489247a1685fSDinh Nguyen 
489343e90349SJohn Youn 	cfg = hsotg->hw_params.dev_ep_dirs;
4894251a17f5SRoshan Pius 	for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) {
4895c6f5c050SMian Yousaf Kaukab 		ep_type = cfg & 3;
4896c6f5c050SMian Yousaf Kaukab 		/* Direction in or both */
4897c6f5c050SMian Yousaf Kaukab 		if (!(ep_type & 2)) {
4898c6f5c050SMian Yousaf Kaukab 			hsotg->eps_in[i] = devm_kzalloc(hsotg->dev,
48991f91b4ccSFelipe Balbi 				sizeof(struct dwc2_hsotg_ep), GFP_KERNEL);
4900c6f5c050SMian Yousaf Kaukab 			if (!hsotg->eps_in[i])
4901c6f5c050SMian Yousaf Kaukab 				return -ENOMEM;
4902c6f5c050SMian Yousaf Kaukab 		}
4903c6f5c050SMian Yousaf Kaukab 		/* Direction out or both */
4904c6f5c050SMian Yousaf Kaukab 		if (!(ep_type & 1)) {
4905c6f5c050SMian Yousaf Kaukab 			hsotg->eps_out[i] = devm_kzalloc(hsotg->dev,
49061f91b4ccSFelipe Balbi 				sizeof(struct dwc2_hsotg_ep), GFP_KERNEL);
4907c6f5c050SMian Yousaf Kaukab 			if (!hsotg->eps_out[i])
4908c6f5c050SMian Yousaf Kaukab 				return -ENOMEM;
4909c6f5c050SMian Yousaf Kaukab 		}
4910c6f5c050SMian Yousaf Kaukab 	}
4911c6f5c050SMian Yousaf Kaukab 
491243e90349SJohn Youn 	hsotg->fifo_mem = hsotg->hw_params.total_fifo_size;
491343e90349SJohn Youn 	hsotg->dedicated_fifos = hsotg->hw_params.en_multiple_tx_fifo;
491447a1685fSDinh Nguyen 
4915cff9eb75SMarek Szyprowski 	dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n",
4916cff9eb75SMarek Szyprowski 		 hsotg->num_of_eps,
4917cff9eb75SMarek Szyprowski 		 hsotg->dedicated_fifos ? "dedicated" : "shared",
4918cff9eb75SMarek Szyprowski 		 hsotg->fifo_mem);
4919c6f5c050SMian Yousaf Kaukab 	return 0;
492047a1685fSDinh Nguyen }
492147a1685fSDinh Nguyen 
492247a1685fSDinh Nguyen /**
49231f91b4ccSFelipe Balbi  * dwc2_hsotg_dump - dump state of the udc
49246fb914d7SGrigor Tovmasyan  * @hsotg: Programming view of the DWC_otg controller
49256fb914d7SGrigor Tovmasyan  *
492647a1685fSDinh Nguyen  */
dwc2_hsotg_dump(struct dwc2_hsotg * hsotg)49271f91b4ccSFelipe Balbi static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg)
492847a1685fSDinh Nguyen {
492947a1685fSDinh Nguyen #ifdef DEBUG
493047a1685fSDinh Nguyen 	struct device *dev = hsotg->dev;
493147a1685fSDinh Nguyen 	u32 val;
493247a1685fSDinh Nguyen 	int idx;
493347a1685fSDinh Nguyen 
493447a1685fSDinh Nguyen 	dev_info(dev, "DCFG=0x%08x, DCTL=0x%08x, DIEPMSK=%08x\n",
4935f25c42b8SGevorg Sahakyan 		 dwc2_readl(hsotg, DCFG), dwc2_readl(hsotg, DCTL),
4936f25c42b8SGevorg Sahakyan 		 dwc2_readl(hsotg, DIEPMSK));
493747a1685fSDinh Nguyen 
4938f889f23dSMian Yousaf Kaukab 	dev_info(dev, "GAHBCFG=0x%08x, GHWCFG1=0x%08x\n",
4939f25c42b8SGevorg Sahakyan 		 dwc2_readl(hsotg, GAHBCFG), dwc2_readl(hsotg, GHWCFG1));
494047a1685fSDinh Nguyen 
494147a1685fSDinh Nguyen 	dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
4942f25c42b8SGevorg Sahakyan 		 dwc2_readl(hsotg, GRXFSIZ), dwc2_readl(hsotg, GNPTXFSIZ));
494347a1685fSDinh Nguyen 
494447a1685fSDinh Nguyen 	/* show periodic fifo settings */
494547a1685fSDinh Nguyen 
4946364f8e93SMian Yousaf Kaukab 	for (idx = 1; idx < hsotg->num_of_eps; idx++) {
4947f25c42b8SGevorg Sahakyan 		val = dwc2_readl(hsotg, DPTXFSIZN(idx));
494847a1685fSDinh Nguyen 		dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx,
494947a1685fSDinh Nguyen 			 val >> FIFOSIZE_DEPTH_SHIFT,
495047a1685fSDinh Nguyen 			 val & FIFOSIZE_STARTADDR_MASK);
495147a1685fSDinh Nguyen 	}
495247a1685fSDinh Nguyen 
4953364f8e93SMian Yousaf Kaukab 	for (idx = 0; idx < hsotg->num_of_eps; idx++) {
495447a1685fSDinh Nguyen 		dev_info(dev,
495547a1685fSDinh Nguyen 			 "ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx,
4956f25c42b8SGevorg Sahakyan 			 dwc2_readl(hsotg, DIEPCTL(idx)),
4957f25c42b8SGevorg Sahakyan 			 dwc2_readl(hsotg, DIEPTSIZ(idx)),
4958f25c42b8SGevorg Sahakyan 			 dwc2_readl(hsotg, DIEPDMA(idx)));
495947a1685fSDinh Nguyen 
4960f25c42b8SGevorg Sahakyan 		val = dwc2_readl(hsotg, DOEPCTL(idx));
496147a1685fSDinh Nguyen 		dev_info(dev,
496247a1685fSDinh Nguyen 			 "ep%d-out: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n",
4963f25c42b8SGevorg Sahakyan 			 idx, dwc2_readl(hsotg, DOEPCTL(idx)),
4964f25c42b8SGevorg Sahakyan 			 dwc2_readl(hsotg, DOEPTSIZ(idx)),
4965f25c42b8SGevorg Sahakyan 			 dwc2_readl(hsotg, DOEPDMA(idx)));
496647a1685fSDinh Nguyen 	}
496747a1685fSDinh Nguyen 
496847a1685fSDinh Nguyen 	dev_info(dev, "DVBUSDIS=0x%08x, DVBUSPULSE=%08x\n",
4969f25c42b8SGevorg Sahakyan 		 dwc2_readl(hsotg, DVBUSDIS), dwc2_readl(hsotg, DVBUSPULSE));
497047a1685fSDinh Nguyen #endif
497147a1685fSDinh Nguyen }
497247a1685fSDinh Nguyen 
497347a1685fSDinh Nguyen /**
4974117777b2SDinh Nguyen  * dwc2_gadget_init - init function for gadget
49756fb914d7SGrigor Tovmasyan  * @hsotg: Programming view of the DWC_otg controller
49766fb914d7SGrigor Tovmasyan  *
497747a1685fSDinh Nguyen  */
dwc2_gadget_init(struct dwc2_hsotg * hsotg)4978f3768997SVardan Mikayelyan int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
497947a1685fSDinh Nguyen {
4980117777b2SDinh Nguyen 	struct device *dev = hsotg->dev;
498147a1685fSDinh Nguyen 	int epnum;
498247a1685fSDinh Nguyen 	int ret;
498343e90349SJohn Youn 
49840a176279SGregory Herrero 	/* Dump fifo information */
49850a176279SGregory Herrero 	dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n",
498605ee799fSJohn Youn 		hsotg->params.g_np_tx_fifo_size);
498705ee799fSJohn Youn 	dev_dbg(dev, "RXFIFO size: %d\n", hsotg->params.g_rx_fifo_size);
498847a1685fSDinh Nguyen 
498992ef98a4SJohn Keeping 	switch (hsotg->params.speed) {
499092ef98a4SJohn Keeping 	case DWC2_SPEED_PARAM_LOW:
499192ef98a4SJohn Keeping 		hsotg->gadget.max_speed = USB_SPEED_LOW;
499292ef98a4SJohn Keeping 		break;
499392ef98a4SJohn Keeping 	case DWC2_SPEED_PARAM_FULL:
499492ef98a4SJohn Keeping 		hsotg->gadget.max_speed = USB_SPEED_FULL;
499592ef98a4SJohn Keeping 		break;
499692ef98a4SJohn Keeping 	default:
499747a1685fSDinh Nguyen 		hsotg->gadget.max_speed = USB_SPEED_HIGH;
499892ef98a4SJohn Keeping 		break;
499992ef98a4SJohn Keeping 	}
500092ef98a4SJohn Keeping 
50011f91b4ccSFelipe Balbi 	hsotg->gadget.ops = &dwc2_hsotg_gadget_ops;
500247a1685fSDinh Nguyen 	hsotg->gadget.name = dev_name(dev);
5003f5c8a6cbSFabrice Gasnier 	hsotg->gadget.otg_caps = &hsotg->params.otg_caps;
5004fa389a6dSVardan Mikayelyan 	hsotg->remote_wakeup_allowed = 0;
50057455f8b7SJohn Youn 
50067455f8b7SJohn Youn 	if (hsotg->params.lpm)
50077455f8b7SJohn Youn 		hsotg->gadget.lpm_capable = true;
50087455f8b7SJohn Youn 
5009097ee662SGregory Herrero 	if (hsotg->dr_mode == USB_DR_MODE_OTG)
5010097ee662SGregory Herrero 		hsotg->gadget.is_otg = 1;
5011ec4cc657SMian Yousaf Kaukab 	else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
5012ec4cc657SMian Yousaf Kaukab 		hsotg->op_state = OTG_STATE_B_PERIPHERAL;
501347a1685fSDinh Nguyen 
50141f91b4ccSFelipe Balbi 	ret = dwc2_hsotg_hw_cfg(hsotg);
5015c6f5c050SMian Yousaf Kaukab 	if (ret) {
5016c6f5c050SMian Yousaf Kaukab 		dev_err(hsotg->dev, "Hardware configuration failed: %d\n", ret);
501709a75e85SMarek Szyprowski 		return ret;
5018c6f5c050SMian Yousaf Kaukab 	}
5019c6f5c050SMian Yousaf Kaukab 
50203f95001dSMian Yousaf Kaukab 	hsotg->ctrl_buff = devm_kzalloc(hsotg->dev,
50213f95001dSMian Yousaf Kaukab 			DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
50228bae0f8cSWolfram Sang 	if (!hsotg->ctrl_buff)
502309a75e85SMarek Szyprowski 		return -ENOMEM;
50243f95001dSMian Yousaf Kaukab 
50253f95001dSMian Yousaf Kaukab 	hsotg->ep0_buff = devm_kzalloc(hsotg->dev,
50263f95001dSMian Yousaf Kaukab 			DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
50278bae0f8cSWolfram Sang 	if (!hsotg->ep0_buff)
502809a75e85SMarek Szyprowski 		return -ENOMEM;
50293f95001dSMian Yousaf Kaukab 
50300f6b80c0SVahram Aharonyan 	if (using_desc_dma(hsotg)) {
50310f6b80c0SVahram Aharonyan 		ret = dwc2_gadget_alloc_ctrl_desc_chains(hsotg);
50320f6b80c0SVahram Aharonyan 		if (ret < 0)
50330f6b80c0SVahram Aharonyan 			return ret;
50340f6b80c0SVahram Aharonyan 	}
50350f6b80c0SVahram Aharonyan 
5036f3768997SVardan Mikayelyan 	ret = devm_request_irq(hsotg->dev, hsotg->irq, dwc2_hsotg_irq,
5037f3768997SVardan Mikayelyan 			       IRQF_SHARED, dev_name(hsotg->dev), hsotg);
5038eb3c56c5SMarek Szyprowski 	if (ret < 0) {
5039db8178c3SDinh Nguyen 		dev_err(dev, "cannot claim IRQ for gadget\n");
504009a75e85SMarek Szyprowski 		return ret;
5041eb3c56c5SMarek Szyprowski 	}
5042eb3c56c5SMarek Szyprowski 
504347a1685fSDinh Nguyen 	/* hsotg->num_of_eps holds number of EPs other than ep0 */
504447a1685fSDinh Nguyen 
504547a1685fSDinh Nguyen 	if (hsotg->num_of_eps == 0) {
504647a1685fSDinh Nguyen 		dev_err(dev, "wrong number of EPs (zero)\n");
504709a75e85SMarek Szyprowski 		return -EINVAL;
504847a1685fSDinh Nguyen 	}
504947a1685fSDinh Nguyen 
505047a1685fSDinh Nguyen 	/* setup endpoint information */
505147a1685fSDinh Nguyen 
505247a1685fSDinh Nguyen 	INIT_LIST_HEAD(&hsotg->gadget.ep_list);
5053c6f5c050SMian Yousaf Kaukab 	hsotg->gadget.ep0 = &hsotg->eps_out[0]->ep;
505447a1685fSDinh Nguyen 
505547a1685fSDinh Nguyen 	/* allocate EP0 request */
505647a1685fSDinh Nguyen 
50571f91b4ccSFelipe Balbi 	hsotg->ctrl_req = dwc2_hsotg_ep_alloc_request(&hsotg->eps_out[0]->ep,
505847a1685fSDinh Nguyen 						     GFP_KERNEL);
505947a1685fSDinh Nguyen 	if (!hsotg->ctrl_req) {
506047a1685fSDinh Nguyen 		dev_err(dev, "failed to allocate ctrl req\n");
506109a75e85SMarek Szyprowski 		return -ENOMEM;
506247a1685fSDinh Nguyen 	}
506347a1685fSDinh Nguyen 
506447a1685fSDinh Nguyen 	/* initialise the endpoints now the core has been initialised */
5065c6f5c050SMian Yousaf Kaukab 	for (epnum = 0; epnum < hsotg->num_of_eps; epnum++) {
5066c6f5c050SMian Yousaf Kaukab 		if (hsotg->eps_in[epnum])
50671f91b4ccSFelipe Balbi 			dwc2_hsotg_initep(hsotg, hsotg->eps_in[epnum],
5068c6f5c050SMian Yousaf Kaukab 					  epnum, 1);
5069c6f5c050SMian Yousaf Kaukab 		if (hsotg->eps_out[epnum])
50701f91b4ccSFelipe Balbi 			dwc2_hsotg_initep(hsotg, hsotg->eps_out[epnum],
5071c6f5c050SMian Yousaf Kaukab 					  epnum, 0);
5072c6f5c050SMian Yousaf Kaukab 	}
507347a1685fSDinh Nguyen 
50741f91b4ccSFelipe Balbi 	dwc2_hsotg_dump(hsotg);
507547a1685fSDinh Nguyen 
507647a1685fSDinh Nguyen 	return 0;
507747a1685fSDinh Nguyen }
507847a1685fSDinh Nguyen 
507947a1685fSDinh Nguyen /**
50801f91b4ccSFelipe Balbi  * dwc2_hsotg_remove - remove function for hsotg driver
50816fb914d7SGrigor Tovmasyan  * @hsotg: Programming view of the DWC_otg controller
50826fb914d7SGrigor Tovmasyan  *
508347a1685fSDinh Nguyen  */
dwc2_hsotg_remove(struct dwc2_hsotg * hsotg)50841f91b4ccSFelipe Balbi int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg)
508547a1685fSDinh Nguyen {
508647a1685fSDinh Nguyen 	usb_del_gadget_udc(&hsotg->gadget);
50879bb073a0SGrigor Tovmasyan 	dwc2_hsotg_ep_free_request(&hsotg->eps_out[0]->ep, hsotg->ctrl_req);
508847a1685fSDinh Nguyen 
508947a1685fSDinh Nguyen 	return 0;
509047a1685fSDinh Nguyen }
509147a1685fSDinh Nguyen 
dwc2_hsotg_suspend(struct dwc2_hsotg * hsotg)50921f91b4ccSFelipe Balbi int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg)
509347a1685fSDinh Nguyen {
509447a1685fSDinh Nguyen 	unsigned long flags;
509547a1685fSDinh Nguyen 
50969e779778SGregory Herrero 	if (hsotg->lx_state != DWC2_L0)
509709a75e85SMarek Szyprowski 		return 0;
50989e779778SGregory Herrero 
5099dc6e69e6SMarek Szyprowski 	if (hsotg->driver) {
5100dc6e69e6SMarek Szyprowski 		int ep;
5101dc6e69e6SMarek Szyprowski 
510247a1685fSDinh Nguyen 		dev_info(hsotg->dev, "suspending usb gadget %s\n",
510347a1685fSDinh Nguyen 			 hsotg->driver->driver.name);
510447a1685fSDinh Nguyen 
510547a1685fSDinh Nguyen 		spin_lock_irqsave(&hsotg->lock, flags);
5106dc6e69e6SMarek Szyprowski 		if (hsotg->enabled)
51071f91b4ccSFelipe Balbi 			dwc2_hsotg_core_disconnect(hsotg);
51081f91b4ccSFelipe Balbi 		dwc2_hsotg_disconnect(hsotg);
510947a1685fSDinh Nguyen 		hsotg->gadget.speed = USB_SPEED_UNKNOWN;
511047a1685fSDinh Nguyen 		spin_unlock_irqrestore(&hsotg->lock, flags);
511147a1685fSDinh Nguyen 
5112ac55d163SAmelie Delaunay 		for (ep = 1; ep < hsotg->num_of_eps; ep++) {
5113c6f5c050SMian Yousaf Kaukab 			if (hsotg->eps_in[ep])
51144fe4f9feSMinas Harutyunyan 				dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep);
5115c6f5c050SMian Yousaf Kaukab 			if (hsotg->eps_out[ep])
51164fe4f9feSMinas Harutyunyan 				dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep);
5117c6f5c050SMian Yousaf Kaukab 		}
511847a1685fSDinh Nguyen 	}
511947a1685fSDinh Nguyen 
512009a75e85SMarek Szyprowski 	return 0;
512147a1685fSDinh Nguyen }
512247a1685fSDinh Nguyen 
dwc2_hsotg_resume(struct dwc2_hsotg * hsotg)51231f91b4ccSFelipe Balbi int dwc2_hsotg_resume(struct dwc2_hsotg *hsotg)
512447a1685fSDinh Nguyen {
512547a1685fSDinh Nguyen 	unsigned long flags;
512647a1685fSDinh Nguyen 
51279e779778SGregory Herrero 	if (hsotg->lx_state == DWC2_L2)
512809a75e85SMarek Szyprowski 		return 0;
51299e779778SGregory Herrero 
513047a1685fSDinh Nguyen 	if (hsotg->driver) {
513147a1685fSDinh Nguyen 		dev_info(hsotg->dev, "resuming usb gadget %s\n",
513247a1685fSDinh Nguyen 			 hsotg->driver->driver.name);
5133d00b4142SRobert Baldyga 
513447a1685fSDinh Nguyen 		spin_lock_irqsave(&hsotg->lock, flags);
51351f91b4ccSFelipe Balbi 		dwc2_hsotg_core_init_disconnected(hsotg, false);
513666e77a24SRazmik Karapetyan 		if (hsotg->enabled) {
513766e77a24SRazmik Karapetyan 			/* Enable ACG feature in device mode,if supported */
513866e77a24SRazmik Karapetyan 			dwc2_enable_acg(hsotg);
51391f91b4ccSFelipe Balbi 			dwc2_hsotg_core_connect(hsotg);
514066e77a24SRazmik Karapetyan 		}
514147a1685fSDinh Nguyen 		spin_unlock_irqrestore(&hsotg->lock, flags);
5142dc6e69e6SMarek Szyprowski 	}
514347a1685fSDinh Nguyen 
514409a75e85SMarek Szyprowski 	return 0;
514547a1685fSDinh Nguyen }
514658e52ff6SJohn Youn 
514758e52ff6SJohn Youn /**
514858e52ff6SJohn Youn  * dwc2_backup_device_registers() - Backup controller device registers.
514958e52ff6SJohn Youn  * When suspending usb bus, registers needs to be backuped
515058e52ff6SJohn Youn  * if controller power is disabled once suspended.
515158e52ff6SJohn Youn  *
515258e52ff6SJohn Youn  * @hsotg: Programming view of the DWC_otg controller
515358e52ff6SJohn Youn  */
dwc2_backup_device_registers(struct dwc2_hsotg * hsotg)515458e52ff6SJohn Youn int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
515558e52ff6SJohn Youn {
515658e52ff6SJohn Youn 	struct dwc2_dregs_backup *dr;
515758e52ff6SJohn Youn 	int i;
515858e52ff6SJohn Youn 
515958e52ff6SJohn Youn 	dev_dbg(hsotg->dev, "%s\n", __func__);
516058e52ff6SJohn Youn 
516158e52ff6SJohn Youn 	/* Backup dev regs */
516258e52ff6SJohn Youn 	dr = &hsotg->dr_backup;
516358e52ff6SJohn Youn 
5164f25c42b8SGevorg Sahakyan 	dr->dcfg = dwc2_readl(hsotg, DCFG);
5165f25c42b8SGevorg Sahakyan 	dr->dctl = dwc2_readl(hsotg, DCTL);
5166f25c42b8SGevorg Sahakyan 	dr->daintmsk = dwc2_readl(hsotg, DAINTMSK);
5167f25c42b8SGevorg Sahakyan 	dr->diepmsk = dwc2_readl(hsotg, DIEPMSK);
5168f25c42b8SGevorg Sahakyan 	dr->doepmsk = dwc2_readl(hsotg, DOEPMSK);
516958e52ff6SJohn Youn 
517058e52ff6SJohn Youn 	for (i = 0; i < hsotg->num_of_eps; i++) {
517158e52ff6SJohn Youn 		/* Backup IN EPs */
5172f25c42b8SGevorg Sahakyan 		dr->diepctl[i] = dwc2_readl(hsotg, DIEPCTL(i));
517358e52ff6SJohn Youn 
517458e52ff6SJohn Youn 		/* Ensure DATA PID is correctly configured */
517558e52ff6SJohn Youn 		if (dr->diepctl[i] & DXEPCTL_DPID)
517658e52ff6SJohn Youn 			dr->diepctl[i] |= DXEPCTL_SETD1PID;
517758e52ff6SJohn Youn 		else
517858e52ff6SJohn Youn 			dr->diepctl[i] |= DXEPCTL_SETD0PID;
517958e52ff6SJohn Youn 
5180f25c42b8SGevorg Sahakyan 		dr->dieptsiz[i] = dwc2_readl(hsotg, DIEPTSIZ(i));
5181f25c42b8SGevorg Sahakyan 		dr->diepdma[i] = dwc2_readl(hsotg, DIEPDMA(i));
518258e52ff6SJohn Youn 
518358e52ff6SJohn Youn 		/* Backup OUT EPs */
5184f25c42b8SGevorg Sahakyan 		dr->doepctl[i] = dwc2_readl(hsotg, DOEPCTL(i));
518558e52ff6SJohn Youn 
518658e52ff6SJohn Youn 		/* Ensure DATA PID is correctly configured */
518758e52ff6SJohn Youn 		if (dr->doepctl[i] & DXEPCTL_DPID)
518858e52ff6SJohn Youn 			dr->doepctl[i] |= DXEPCTL_SETD1PID;
518958e52ff6SJohn Youn 		else
519058e52ff6SJohn Youn 			dr->doepctl[i] |= DXEPCTL_SETD0PID;
519158e52ff6SJohn Youn 
5192f25c42b8SGevorg Sahakyan 		dr->doeptsiz[i] = dwc2_readl(hsotg, DOEPTSIZ(i));
5193f25c42b8SGevorg Sahakyan 		dr->doepdma[i] = dwc2_readl(hsotg, DOEPDMA(i));
5194f25c42b8SGevorg Sahakyan 		dr->dtxfsiz[i] = dwc2_readl(hsotg, DPTXFSIZN(i));
519558e52ff6SJohn Youn 	}
519658e52ff6SJohn Youn 	dr->valid = true;
519758e52ff6SJohn Youn 	return 0;
519858e52ff6SJohn Youn }
519958e52ff6SJohn Youn 
520058e52ff6SJohn Youn /**
520158e52ff6SJohn Youn  * dwc2_restore_device_registers() - Restore controller device registers.
520258e52ff6SJohn Youn  * When resuming usb bus, device registers needs to be restored
520358e52ff6SJohn Youn  * if controller power were disabled.
520458e52ff6SJohn Youn  *
520558e52ff6SJohn Youn  * @hsotg: Programming view of the DWC_otg controller
52069a5d2816SVardan Mikayelyan  * @remote_wakeup: Indicates whether resume is initiated by Device or Host.
52079a5d2816SVardan Mikayelyan  *
52089a5d2816SVardan Mikayelyan  * Return: 0 if successful, negative error code otherwise
520958e52ff6SJohn Youn  */
dwc2_restore_device_registers(struct dwc2_hsotg * hsotg,int remote_wakeup)52109a5d2816SVardan Mikayelyan int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
521158e52ff6SJohn Youn {
521258e52ff6SJohn Youn 	struct dwc2_dregs_backup *dr;
521358e52ff6SJohn Youn 	int i;
521458e52ff6SJohn Youn 
521558e52ff6SJohn Youn 	dev_dbg(hsotg->dev, "%s\n", __func__);
521658e52ff6SJohn Youn 
521758e52ff6SJohn Youn 	/* Restore dev regs */
521858e52ff6SJohn Youn 	dr = &hsotg->dr_backup;
521958e52ff6SJohn Youn 	if (!dr->valid) {
522058e52ff6SJohn Youn 		dev_err(hsotg->dev, "%s: no device registers to restore\n",
522158e52ff6SJohn Youn 			__func__);
522258e52ff6SJohn Youn 		return -EINVAL;
522358e52ff6SJohn Youn 	}
522458e52ff6SJohn Youn 	dr->valid = false;
522558e52ff6SJohn Youn 
52269a5d2816SVardan Mikayelyan 	if (!remote_wakeup)
5227f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, dr->dctl, DCTL);
52289a5d2816SVardan Mikayelyan 
5229f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, dr->daintmsk, DAINTMSK);
5230f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, dr->diepmsk, DIEPMSK);
5231f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, dr->doepmsk, DOEPMSK);
523258e52ff6SJohn Youn 
523358e52ff6SJohn Youn 	for (i = 0; i < hsotg->num_of_eps; i++) {
523458e52ff6SJohn Youn 		/* Restore IN EPs */
5235f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, dr->dieptsiz[i], DIEPTSIZ(i));
5236f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, dr->diepdma[i], DIEPDMA(i));
5237f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, dr->doeptsiz[i], DOEPTSIZ(i));
52389a5d2816SVardan Mikayelyan 		/** WA for enabled EPx's IN in DDMA mode. On entering to
52399a5d2816SVardan Mikayelyan 		 * hibernation wrong value read and saved from DIEPDMAx,
52409a5d2816SVardan Mikayelyan 		 * as result BNA interrupt asserted on hibernation exit
52419a5d2816SVardan Mikayelyan 		 * by restoring from saved area.
52429a5d2816SVardan Mikayelyan 		 */
5243c4bc515dSJohn Keeping 		if (using_desc_dma(hsotg) &&
52449a5d2816SVardan Mikayelyan 		    (dr->diepctl[i] & DXEPCTL_EPENA))
52459a5d2816SVardan Mikayelyan 			dr->diepdma[i] = hsotg->eps_in[i]->desc_list_dma;
5246f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, dr->dtxfsiz[i], DPTXFSIZN(i));
5247f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, dr->diepctl[i], DIEPCTL(i));
52489a5d2816SVardan Mikayelyan 		/* Restore OUT EPs */
5249f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, dr->doeptsiz[i], DOEPTSIZ(i));
52509a5d2816SVardan Mikayelyan 		/* WA for enabled EPx's OUT in DDMA mode. On entering to
52519a5d2816SVardan Mikayelyan 		 * hibernation wrong value read and saved from DOEPDMAx,
52529a5d2816SVardan Mikayelyan 		 * as result BNA interrupt asserted on hibernation exit
52539a5d2816SVardan Mikayelyan 		 * by restoring from saved area.
52549a5d2816SVardan Mikayelyan 		 */
5255c4bc515dSJohn Keeping 		if (using_desc_dma(hsotg) &&
52569a5d2816SVardan Mikayelyan 		    (dr->doepctl[i] & DXEPCTL_EPENA))
52579a5d2816SVardan Mikayelyan 			dr->doepdma[i] = hsotg->eps_out[i]->desc_list_dma;
5258f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, dr->doepdma[i], DOEPDMA(i));
5259f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, dr->doepctl[i], DOEPCTL(i));
526058e52ff6SJohn Youn 	}
526158e52ff6SJohn Youn 
526258e52ff6SJohn Youn 	return 0;
526358e52ff6SJohn Youn }
526421b03405SSevak Arakelyan 
526521b03405SSevak Arakelyan /**
526621b03405SSevak Arakelyan  * dwc2_gadget_init_lpm - Configure the core to support LPM in device mode
526721b03405SSevak Arakelyan  *
526821b03405SSevak Arakelyan  * @hsotg: Programming view of DWC_otg controller
526921b03405SSevak Arakelyan  *
527021b03405SSevak Arakelyan  */
dwc2_gadget_init_lpm(struct dwc2_hsotg * hsotg)527121b03405SSevak Arakelyan void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg)
527221b03405SSevak Arakelyan {
527321b03405SSevak Arakelyan 	u32 val;
527421b03405SSevak Arakelyan 
527521b03405SSevak Arakelyan 	if (!hsotg->params.lpm)
527621b03405SSevak Arakelyan 		return;
527721b03405SSevak Arakelyan 
527821b03405SSevak Arakelyan 	val = GLPMCFG_LPMCAP | GLPMCFG_APPL1RES;
527921b03405SSevak Arakelyan 	val |= hsotg->params.hird_threshold_en ? GLPMCFG_HIRD_THRES_EN : 0;
528021b03405SSevak Arakelyan 	val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0;
528121b03405SSevak Arakelyan 	val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT;
528221b03405SSevak Arakelyan 	val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0;
528346637565SMinas Harutyunyan 	val |= GLPMCFG_LPM_REJECT_CTRL_CONTROL;
52849aed8c08SArtur Petrosyan 	val |= GLPMCFG_LPM_ACCEPT_CTRL_ISOC;
5285f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, val, GLPMCFG);
5286f25c42b8SGevorg Sahakyan 	dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG));
52874abe4537SGrigor Tovmasyan 
52884abe4537SGrigor Tovmasyan 	/* Unmask WKUP_ALERT Interrupt */
52894abe4537SGrigor Tovmasyan 	if (hsotg->params.service_interval)
52904abe4537SGrigor Tovmasyan 		dwc2_set_bit(hsotg, GINTMSK2, GINTMSK2_WKUP_ALERT_INT_MSK);
529121b03405SSevak Arakelyan }
5292c5c403dcSVardan Mikayelyan 
5293c5c403dcSVardan Mikayelyan /**
529415d9dbf8SGrigor Tovmasyan  * dwc2_gadget_program_ref_clk - Program GREFCLK register in device mode
529515d9dbf8SGrigor Tovmasyan  *
529615d9dbf8SGrigor Tovmasyan  * @hsotg: Programming view of DWC_otg controller
529715d9dbf8SGrigor Tovmasyan  *
529815d9dbf8SGrigor Tovmasyan  */
dwc2_gadget_program_ref_clk(struct dwc2_hsotg * hsotg)529915d9dbf8SGrigor Tovmasyan void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg)
530015d9dbf8SGrigor Tovmasyan {
530115d9dbf8SGrigor Tovmasyan 	u32 val = 0;
530215d9dbf8SGrigor Tovmasyan 
530315d9dbf8SGrigor Tovmasyan 	val |= GREFCLK_REF_CLK_MODE;
530415d9dbf8SGrigor Tovmasyan 	val |= hsotg->params.ref_clk_per << GREFCLK_REFCLKPER_SHIFT;
530515d9dbf8SGrigor Tovmasyan 	val |= hsotg->params.sof_cnt_wkup_alert <<
530615d9dbf8SGrigor Tovmasyan 	       GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT;
530715d9dbf8SGrigor Tovmasyan 
530815d9dbf8SGrigor Tovmasyan 	dwc2_writel(hsotg, val, GREFCLK);
530915d9dbf8SGrigor Tovmasyan 	dev_dbg(hsotg->dev, "GREFCLK=0x%08x\n", dwc2_readl(hsotg, GREFCLK));
531015d9dbf8SGrigor Tovmasyan }
531115d9dbf8SGrigor Tovmasyan 
531215d9dbf8SGrigor Tovmasyan /**
5313c5c403dcSVardan Mikayelyan  * dwc2_gadget_enter_hibernation() - Put controller in Hibernation.
5314c5c403dcSVardan Mikayelyan  *
5315c5c403dcSVardan Mikayelyan  * @hsotg: Programming view of the DWC_otg controller
5316c5c403dcSVardan Mikayelyan  *
5317c5c403dcSVardan Mikayelyan  * Return non-zero if failed to enter to hibernation.
5318c5c403dcSVardan Mikayelyan  */
dwc2_gadget_enter_hibernation(struct dwc2_hsotg * hsotg)5319c5c403dcSVardan Mikayelyan int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
5320c5c403dcSVardan Mikayelyan {
5321c5c403dcSVardan Mikayelyan 	u32 gpwrdn;
53224483ef3cSMinas Harutyunyan 	u32 gusbcfg;
53234483ef3cSMinas Harutyunyan 	u32 pcgcctl;
5324c5c403dcSVardan Mikayelyan 	int ret = 0;
5325c5c403dcSVardan Mikayelyan 
5326c5c403dcSVardan Mikayelyan 	/* Change to L2(suspend) state */
5327c5c403dcSVardan Mikayelyan 	hsotg->lx_state = DWC2_L2;
5328c5c403dcSVardan Mikayelyan 	dev_dbg(hsotg->dev, "Start of hibernation completed\n");
5329c5c403dcSVardan Mikayelyan 	ret = dwc2_backup_global_registers(hsotg);
5330c5c403dcSVardan Mikayelyan 	if (ret) {
5331c5c403dcSVardan Mikayelyan 		dev_err(hsotg->dev, "%s: failed to backup global registers\n",
5332c5c403dcSVardan Mikayelyan 			__func__);
5333c5c403dcSVardan Mikayelyan 		return ret;
5334c5c403dcSVardan Mikayelyan 	}
5335c5c403dcSVardan Mikayelyan 	ret = dwc2_backup_device_registers(hsotg);
5336c5c403dcSVardan Mikayelyan 	if (ret) {
5337c5c403dcSVardan Mikayelyan 		dev_err(hsotg->dev, "%s: failed to backup device registers\n",
5338c5c403dcSVardan Mikayelyan 			__func__);
5339c5c403dcSVardan Mikayelyan 		return ret;
5340c5c403dcSVardan Mikayelyan 	}
5341c5c403dcSVardan Mikayelyan 
5342c5c403dcSVardan Mikayelyan 	gpwrdn = GPWRDN_PWRDNRSTN;
53434483ef3cSMinas Harutyunyan 	udelay(10);
53444483ef3cSMinas Harutyunyan 	gusbcfg = dwc2_readl(hsotg, GUSBCFG);
53454483ef3cSMinas Harutyunyan 	if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) {
53464483ef3cSMinas Harutyunyan 		/* ULPI interface */
53474483ef3cSMinas Harutyunyan 		gpwrdn |= GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
53484483ef3cSMinas Harutyunyan 	}
53494483ef3cSMinas Harutyunyan 	dwc2_writel(hsotg, gpwrdn, GPWRDN);
53504483ef3cSMinas Harutyunyan 	udelay(10);
53514483ef3cSMinas Harutyunyan 
53524483ef3cSMinas Harutyunyan 	/* Suspend the Phy Clock */
53534483ef3cSMinas Harutyunyan 	pcgcctl = dwc2_readl(hsotg, PCGCTL);
53544483ef3cSMinas Harutyunyan 	pcgcctl |= PCGCTL_STOPPCLK;
53554483ef3cSMinas Harutyunyan 	dwc2_writel(hsotg, pcgcctl, PCGCTL);
53564483ef3cSMinas Harutyunyan 	udelay(10);
53574483ef3cSMinas Harutyunyan 
53584483ef3cSMinas Harutyunyan 	gpwrdn = dwc2_readl(hsotg, GPWRDN);
5359c5c403dcSVardan Mikayelyan 	gpwrdn |= GPWRDN_PMUACTV;
5360f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, gpwrdn, GPWRDN);
5361c5c403dcSVardan Mikayelyan 	udelay(10);
5362c5c403dcSVardan Mikayelyan 
5363c5c403dcSVardan Mikayelyan 	/* Set flag to indicate that we are in hibernation */
5364c5c403dcSVardan Mikayelyan 	hsotg->hibernated = 1;
5365c5c403dcSVardan Mikayelyan 
5366c5c403dcSVardan Mikayelyan 	/* Enable interrupts from wake up logic */
5367f25c42b8SGevorg Sahakyan 	gpwrdn = dwc2_readl(hsotg, GPWRDN);
5368c5c403dcSVardan Mikayelyan 	gpwrdn |= GPWRDN_PMUINTSEL;
5369f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, gpwrdn, GPWRDN);
5370c5c403dcSVardan Mikayelyan 	udelay(10);
5371c5c403dcSVardan Mikayelyan 
5372c5c403dcSVardan Mikayelyan 	/* Unmask device mode interrupts in GPWRDN */
5373f25c42b8SGevorg Sahakyan 	gpwrdn = dwc2_readl(hsotg, GPWRDN);
5374c5c403dcSVardan Mikayelyan 	gpwrdn |= GPWRDN_RST_DET_MSK;
5375c5c403dcSVardan Mikayelyan 	gpwrdn |= GPWRDN_LNSTSCHG_MSK;
5376c5c403dcSVardan Mikayelyan 	gpwrdn |= GPWRDN_STS_CHGINT_MSK;
5377f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, gpwrdn, GPWRDN);
5378c5c403dcSVardan Mikayelyan 	udelay(10);
5379c5c403dcSVardan Mikayelyan 
5380c5c403dcSVardan Mikayelyan 	/* Enable Power Down Clamp */
5381f25c42b8SGevorg Sahakyan 	gpwrdn = dwc2_readl(hsotg, GPWRDN);
5382c5c403dcSVardan Mikayelyan 	gpwrdn |= GPWRDN_PWRDNCLMP;
5383f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, gpwrdn, GPWRDN);
5384c5c403dcSVardan Mikayelyan 	udelay(10);
5385c5c403dcSVardan Mikayelyan 
5386c5c403dcSVardan Mikayelyan 	/* Switch off VDD */
5387f25c42b8SGevorg Sahakyan 	gpwrdn = dwc2_readl(hsotg, GPWRDN);
5388c5c403dcSVardan Mikayelyan 	gpwrdn |= GPWRDN_PWRDNSWTCH;
5389f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, gpwrdn, GPWRDN);
5390c5c403dcSVardan Mikayelyan 	udelay(10);
5391c5c403dcSVardan Mikayelyan 
5392c5c403dcSVardan Mikayelyan 	/* Save gpwrdn register for further usage if stschng interrupt */
5393f25c42b8SGevorg Sahakyan 	hsotg->gr_backup.gpwrdn = dwc2_readl(hsotg, GPWRDN);
5394c5c403dcSVardan Mikayelyan 	dev_dbg(hsotg->dev, "Hibernation completed\n");
5395c5c403dcSVardan Mikayelyan 
5396c5c403dcSVardan Mikayelyan 	return ret;
5397c5c403dcSVardan Mikayelyan }
5398c5c403dcSVardan Mikayelyan 
5399c5c403dcSVardan Mikayelyan /**
5400c5c403dcSVardan Mikayelyan  * dwc2_gadget_exit_hibernation()
5401c5c403dcSVardan Mikayelyan  * This function is for exiting from Device mode hibernation by host initiated
5402c5c403dcSVardan Mikayelyan  * resume/reset and device initiated remote-wakeup.
5403c5c403dcSVardan Mikayelyan  *
5404c5c403dcSVardan Mikayelyan  * @hsotg: Programming view of the DWC_otg controller
5405c5c403dcSVardan Mikayelyan  * @rem_wakeup: indicates whether resume is initiated by Device or Host.
54066fb914d7SGrigor Tovmasyan  * @reset: indicates whether resume is initiated by Reset.
5407c5c403dcSVardan Mikayelyan  *
5408c5c403dcSVardan Mikayelyan  * Return non-zero if failed to exit from hibernation.
5409c5c403dcSVardan Mikayelyan  */
dwc2_gadget_exit_hibernation(struct dwc2_hsotg * hsotg,int rem_wakeup,int reset)5410c5c403dcSVardan Mikayelyan int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
5411c5c403dcSVardan Mikayelyan 				 int rem_wakeup, int reset)
5412c5c403dcSVardan Mikayelyan {
5413c5c403dcSVardan Mikayelyan 	u32 pcgcctl;
5414c5c403dcSVardan Mikayelyan 	u32 gpwrdn;
5415c5c403dcSVardan Mikayelyan 	u32 dctl;
5416c5c403dcSVardan Mikayelyan 	int ret = 0;
5417c5c403dcSVardan Mikayelyan 	struct dwc2_gregs_backup *gr;
5418c5c403dcSVardan Mikayelyan 	struct dwc2_dregs_backup *dr;
5419c5c403dcSVardan Mikayelyan 
5420c5c403dcSVardan Mikayelyan 	gr = &hsotg->gr_backup;
5421c5c403dcSVardan Mikayelyan 	dr = &hsotg->dr_backup;
5422c5c403dcSVardan Mikayelyan 
5423c5c403dcSVardan Mikayelyan 	if (!hsotg->hibernated) {
5424c5c403dcSVardan Mikayelyan 		dev_dbg(hsotg->dev, "Already exited from Hibernation\n");
5425c5c403dcSVardan Mikayelyan 		return 1;
5426c5c403dcSVardan Mikayelyan 	}
5427c5c403dcSVardan Mikayelyan 	dev_dbg(hsotg->dev,
5428c5c403dcSVardan Mikayelyan 		"%s: called with rem_wakeup = %d reset = %d\n",
5429c5c403dcSVardan Mikayelyan 		__func__, rem_wakeup, reset);
5430c5c403dcSVardan Mikayelyan 
5431c5c403dcSVardan Mikayelyan 	dwc2_hib_restore_common(hsotg, rem_wakeup, 0);
5432c5c403dcSVardan Mikayelyan 
5433c5c403dcSVardan Mikayelyan 	if (!reset) {
5434c5c403dcSVardan Mikayelyan 		/* Clear all pending interupts */
5435f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, 0xffffffff, GINTSTS);
5436c5c403dcSVardan Mikayelyan 	}
5437c5c403dcSVardan Mikayelyan 
5438c5c403dcSVardan Mikayelyan 	/* De-assert Restore */
5439f25c42b8SGevorg Sahakyan 	gpwrdn = dwc2_readl(hsotg, GPWRDN);
5440c5c403dcSVardan Mikayelyan 	gpwrdn &= ~GPWRDN_RESTORE;
5441f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, gpwrdn, GPWRDN);
5442c5c403dcSVardan Mikayelyan 	udelay(10);
5443c5c403dcSVardan Mikayelyan 
5444c5c403dcSVardan Mikayelyan 	if (!rem_wakeup) {
5445f25c42b8SGevorg Sahakyan 		pcgcctl = dwc2_readl(hsotg, PCGCTL);
5446c5c403dcSVardan Mikayelyan 		pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
5447f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, pcgcctl, PCGCTL);
5448c5c403dcSVardan Mikayelyan 	}
5449c5c403dcSVardan Mikayelyan 
5450c5c403dcSVardan Mikayelyan 	/* Restore GUSBCFG, DCFG and DCTL */
5451f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG);
5452f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, dr->dcfg, DCFG);
5453f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, dr->dctl, DCTL);
5454c5c403dcSVardan Mikayelyan 
5455b29b494bSArtur Petrosyan 	/* On USB Reset, reset device address to zero */
5456b29b494bSArtur Petrosyan 	if (reset)
5457b29b494bSArtur Petrosyan 		dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK);
5458b29b494bSArtur Petrosyan 
54594483ef3cSMinas Harutyunyan 	/* Reset ULPI latch */
54604483ef3cSMinas Harutyunyan 	gpwrdn = dwc2_readl(hsotg, GPWRDN);
54614483ef3cSMinas Harutyunyan 	gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY;
54624483ef3cSMinas Harutyunyan 	dwc2_writel(hsotg, gpwrdn, GPWRDN);
54634483ef3cSMinas Harutyunyan 
5464c5c403dcSVardan Mikayelyan 	/* De-assert Wakeup Logic */
5465f25c42b8SGevorg Sahakyan 	gpwrdn = dwc2_readl(hsotg, GPWRDN);
5466c5c403dcSVardan Mikayelyan 	gpwrdn &= ~GPWRDN_PMUACTV;
5467f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, gpwrdn, GPWRDN);
5468c5c403dcSVardan Mikayelyan 
5469c5c403dcSVardan Mikayelyan 	if (rem_wakeup) {
5470c5c403dcSVardan Mikayelyan 		udelay(10);
5471c5c403dcSVardan Mikayelyan 		/* Start Remote Wakeup Signaling */
5472f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, dr->dctl | DCTL_RMTWKUPSIG, DCTL);
5473c5c403dcSVardan Mikayelyan 	} else {
5474c5c403dcSVardan Mikayelyan 		udelay(50);
5475c5c403dcSVardan Mikayelyan 		/* Set Device programming done bit */
5476f25c42b8SGevorg Sahakyan 		dctl = dwc2_readl(hsotg, DCTL);
5477c5c403dcSVardan Mikayelyan 		dctl |= DCTL_PWRONPRGDONE;
5478f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, dctl, DCTL);
5479c5c403dcSVardan Mikayelyan 	}
5480c5c403dcSVardan Mikayelyan 	/* Wait for interrupts which must be cleared */
5481c5c403dcSVardan Mikayelyan 	mdelay(2);
5482c5c403dcSVardan Mikayelyan 	/* Clear all pending interupts */
5483f25c42b8SGevorg Sahakyan 	dwc2_writel(hsotg, 0xffffffff, GINTSTS);
5484c5c403dcSVardan Mikayelyan 
5485c5c403dcSVardan Mikayelyan 	/* Restore global registers */
5486c5c403dcSVardan Mikayelyan 	ret = dwc2_restore_global_registers(hsotg);
5487c5c403dcSVardan Mikayelyan 	if (ret) {
5488c5c403dcSVardan Mikayelyan 		dev_err(hsotg->dev, "%s: failed to restore registers\n",
5489c5c403dcSVardan Mikayelyan 			__func__);
5490c5c403dcSVardan Mikayelyan 		return ret;
5491c5c403dcSVardan Mikayelyan 	}
5492c5c403dcSVardan Mikayelyan 
5493c5c403dcSVardan Mikayelyan 	/* Restore device registers */
5494c5c403dcSVardan Mikayelyan 	ret = dwc2_restore_device_registers(hsotg, rem_wakeup);
5495c5c403dcSVardan Mikayelyan 	if (ret) {
5496c5c403dcSVardan Mikayelyan 		dev_err(hsotg->dev, "%s: failed to restore device registers\n",
5497c5c403dcSVardan Mikayelyan 			__func__);
5498c5c403dcSVardan Mikayelyan 		return ret;
5499c5c403dcSVardan Mikayelyan 	}
5500c5c403dcSVardan Mikayelyan 
5501c5c403dcSVardan Mikayelyan 	if (rem_wakeup) {
5502c5c403dcSVardan Mikayelyan 		mdelay(10);
5503f25c42b8SGevorg Sahakyan 		dctl = dwc2_readl(hsotg, DCTL);
5504c5c403dcSVardan Mikayelyan 		dctl &= ~DCTL_RMTWKUPSIG;
5505f25c42b8SGevorg Sahakyan 		dwc2_writel(hsotg, dctl, DCTL);
5506c5c403dcSVardan Mikayelyan 	}
5507c5c403dcSVardan Mikayelyan 
5508c5c403dcSVardan Mikayelyan 	hsotg->hibernated = 0;
5509c5c403dcSVardan Mikayelyan 	hsotg->lx_state = DWC2_L0;
5510c5c403dcSVardan Mikayelyan 	dev_dbg(hsotg->dev, "Hibernation recovery completes here\n");
5511c5c403dcSVardan Mikayelyan 
5512c5c403dcSVardan Mikayelyan 	return ret;
5513c5c403dcSVardan Mikayelyan }
5514be2b960eSArtur Petrosyan 
5515be2b960eSArtur Petrosyan /**
5516be2b960eSArtur Petrosyan  * dwc2_gadget_enter_partial_power_down() - Put controller in partial
5517be2b960eSArtur Petrosyan  * power down.
5518be2b960eSArtur Petrosyan  *
5519be2b960eSArtur Petrosyan  * @hsotg: Programming view of the DWC_otg controller
5520be2b960eSArtur Petrosyan  *
5521be2b960eSArtur Petrosyan  * Return: non-zero if failed to enter device partial power down.
5522be2b960eSArtur Petrosyan  *
5523be2b960eSArtur Petrosyan  * This function is for entering device mode partial power down.
5524be2b960eSArtur Petrosyan  */
dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg * hsotg)5525be2b960eSArtur Petrosyan int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg)
5526be2b960eSArtur Petrosyan {
5527be2b960eSArtur Petrosyan 	u32 pcgcctl;
5528be2b960eSArtur Petrosyan 	int ret = 0;
5529be2b960eSArtur Petrosyan 
5530be2b960eSArtur Petrosyan 	dev_dbg(hsotg->dev, "Entering device partial power down started.\n");
5531be2b960eSArtur Petrosyan 
5532be2b960eSArtur Petrosyan 	/* Backup all registers */
5533be2b960eSArtur Petrosyan 	ret = dwc2_backup_global_registers(hsotg);
5534be2b960eSArtur Petrosyan 	if (ret) {
5535be2b960eSArtur Petrosyan 		dev_err(hsotg->dev, "%s: failed to backup global registers\n",
5536be2b960eSArtur Petrosyan 			__func__);
5537be2b960eSArtur Petrosyan 		return ret;
5538be2b960eSArtur Petrosyan 	}
5539be2b960eSArtur Petrosyan 
5540be2b960eSArtur Petrosyan 	ret = dwc2_backup_device_registers(hsotg);
5541be2b960eSArtur Petrosyan 	if (ret) {
5542be2b960eSArtur Petrosyan 		dev_err(hsotg->dev, "%s: failed to backup device registers\n",
5543be2b960eSArtur Petrosyan 			__func__);
5544be2b960eSArtur Petrosyan 		return ret;
5545be2b960eSArtur Petrosyan 	}
5546be2b960eSArtur Petrosyan 
5547be2b960eSArtur Petrosyan 	/*
5548be2b960eSArtur Petrosyan 	 * Clear any pending interrupts since dwc2 will not be able to
5549be2b960eSArtur Petrosyan 	 * clear them after entering partial_power_down.
5550be2b960eSArtur Petrosyan 	 */
5551be2b960eSArtur Petrosyan 	dwc2_writel(hsotg, 0xffffffff, GINTSTS);
5552be2b960eSArtur Petrosyan 
5553be2b960eSArtur Petrosyan 	/* Put the controller in low power state */
5554be2b960eSArtur Petrosyan 	pcgcctl = dwc2_readl(hsotg, PCGCTL);
5555be2b960eSArtur Petrosyan 
5556be2b960eSArtur Petrosyan 	pcgcctl |= PCGCTL_PWRCLMP;
5557be2b960eSArtur Petrosyan 	dwc2_writel(hsotg, pcgcctl, PCGCTL);
5558be2b960eSArtur Petrosyan 	udelay(5);
5559be2b960eSArtur Petrosyan 
5560be2b960eSArtur Petrosyan 	pcgcctl |= PCGCTL_RSTPDWNMODULE;
5561be2b960eSArtur Petrosyan 	dwc2_writel(hsotg, pcgcctl, PCGCTL);
5562be2b960eSArtur Petrosyan 	udelay(5);
5563be2b960eSArtur Petrosyan 
5564be2b960eSArtur Petrosyan 	pcgcctl |= PCGCTL_STOPPCLK;
5565be2b960eSArtur Petrosyan 	dwc2_writel(hsotg, pcgcctl, PCGCTL);
5566be2b960eSArtur Petrosyan 
5567be2b960eSArtur Petrosyan 	/* Set in_ppd flag to 1 as here core enters suspend. */
5568be2b960eSArtur Petrosyan 	hsotg->in_ppd = 1;
5569be2b960eSArtur Petrosyan 	hsotg->lx_state = DWC2_L2;
5570be2b960eSArtur Petrosyan 
5571be2b960eSArtur Petrosyan 	dev_dbg(hsotg->dev, "Entering device partial power down completed.\n");
5572be2b960eSArtur Petrosyan 
5573be2b960eSArtur Petrosyan 	return ret;
5574be2b960eSArtur Petrosyan }
5575be2b960eSArtur Petrosyan 
5576be2b960eSArtur Petrosyan /*
5577be2b960eSArtur Petrosyan  * dwc2_gadget_exit_partial_power_down() - Exit controller from device partial
5578be2b960eSArtur Petrosyan  * power down.
5579be2b960eSArtur Petrosyan  *
5580be2b960eSArtur Petrosyan  * @hsotg: Programming view of the DWC_otg controller
5581be2b960eSArtur Petrosyan  * @restore: indicates whether need to restore the registers or not.
5582be2b960eSArtur Petrosyan  *
5583be2b960eSArtur Petrosyan  * Return: non-zero if failed to exit device partial power down.
5584be2b960eSArtur Petrosyan  *
5585be2b960eSArtur Petrosyan  * This function is for exiting from device mode partial power down.
5586be2b960eSArtur Petrosyan  */
dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg * hsotg,bool restore)5587be2b960eSArtur Petrosyan int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg,
5588be2b960eSArtur Petrosyan 					bool restore)
5589be2b960eSArtur Petrosyan {
5590be2b960eSArtur Petrosyan 	u32 pcgcctl;
5591be2b960eSArtur Petrosyan 	u32 dctl;
5592be2b960eSArtur Petrosyan 	struct dwc2_dregs_backup *dr;
5593be2b960eSArtur Petrosyan 	int ret = 0;
5594be2b960eSArtur Petrosyan 
5595be2b960eSArtur Petrosyan 	dr = &hsotg->dr_backup;
5596be2b960eSArtur Petrosyan 
5597be2b960eSArtur Petrosyan 	dev_dbg(hsotg->dev, "Exiting device partial Power Down started.\n");
5598be2b960eSArtur Petrosyan 
5599be2b960eSArtur Petrosyan 	pcgcctl = dwc2_readl(hsotg, PCGCTL);
5600be2b960eSArtur Petrosyan 	pcgcctl &= ~PCGCTL_STOPPCLK;
5601be2b960eSArtur Petrosyan 	dwc2_writel(hsotg, pcgcctl, PCGCTL);
5602be2b960eSArtur Petrosyan 
5603be2b960eSArtur Petrosyan 	pcgcctl = dwc2_readl(hsotg, PCGCTL);
5604be2b960eSArtur Petrosyan 	pcgcctl &= ~PCGCTL_PWRCLMP;
5605be2b960eSArtur Petrosyan 	dwc2_writel(hsotg, pcgcctl, PCGCTL);
5606be2b960eSArtur Petrosyan 
5607be2b960eSArtur Petrosyan 	pcgcctl = dwc2_readl(hsotg, PCGCTL);
5608be2b960eSArtur Petrosyan 	pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
5609be2b960eSArtur Petrosyan 	dwc2_writel(hsotg, pcgcctl, PCGCTL);
5610be2b960eSArtur Petrosyan 
5611be2b960eSArtur Petrosyan 	udelay(100);
5612be2b960eSArtur Petrosyan 	if (restore) {
5613be2b960eSArtur Petrosyan 		ret = dwc2_restore_global_registers(hsotg);
5614be2b960eSArtur Petrosyan 		if (ret) {
5615be2b960eSArtur Petrosyan 			dev_err(hsotg->dev, "%s: failed to restore registers\n",
5616be2b960eSArtur Petrosyan 				__func__);
5617be2b960eSArtur Petrosyan 			return ret;
5618be2b960eSArtur Petrosyan 		}
5619be2b960eSArtur Petrosyan 		/* Restore DCFG */
5620be2b960eSArtur Petrosyan 		dwc2_writel(hsotg, dr->dcfg, DCFG);
5621be2b960eSArtur Petrosyan 
5622be2b960eSArtur Petrosyan 		ret = dwc2_restore_device_registers(hsotg, 0);
5623be2b960eSArtur Petrosyan 		if (ret) {
5624be2b960eSArtur Petrosyan 			dev_err(hsotg->dev, "%s: failed to restore device registers\n",
5625be2b960eSArtur Petrosyan 				__func__);
5626be2b960eSArtur Petrosyan 			return ret;
5627be2b960eSArtur Petrosyan 		}
5628be2b960eSArtur Petrosyan 	}
5629be2b960eSArtur Petrosyan 
5630be2b960eSArtur Petrosyan 	/* Set the Power-On Programming done bit */
5631be2b960eSArtur Petrosyan 	dctl = dwc2_readl(hsotg, DCTL);
5632be2b960eSArtur Petrosyan 	dctl |= DCTL_PWRONPRGDONE;
5633be2b960eSArtur Petrosyan 	dwc2_writel(hsotg, dctl, DCTL);
5634be2b960eSArtur Petrosyan 
5635be2b960eSArtur Petrosyan 	/* Set in_ppd flag to 0 as here core exits from suspend. */
5636be2b960eSArtur Petrosyan 	hsotg->in_ppd = 0;
5637be2b960eSArtur Petrosyan 	hsotg->lx_state = DWC2_L0;
5638be2b960eSArtur Petrosyan 
5639be2b960eSArtur Petrosyan 	dev_dbg(hsotg->dev, "Exiting device partial Power Down completed.\n");
5640be2b960eSArtur Petrosyan 	return ret;
5641be2b960eSArtur Petrosyan }
5642012466fcSArtur Petrosyan 
5643012466fcSArtur Petrosyan /**
5644012466fcSArtur Petrosyan  * dwc2_gadget_enter_clock_gating() - Put controller in clock gating.
5645012466fcSArtur Petrosyan  *
5646012466fcSArtur Petrosyan  * @hsotg: Programming view of the DWC_otg controller
5647012466fcSArtur Petrosyan  *
5648012466fcSArtur Petrosyan  * Return: non-zero if failed to enter device partial power down.
5649012466fcSArtur Petrosyan  *
5650012466fcSArtur Petrosyan  * This function is for entering device mode clock gating.
5651012466fcSArtur Petrosyan  */
dwc2_gadget_enter_clock_gating(struct dwc2_hsotg * hsotg)5652012466fcSArtur Petrosyan void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg)
5653012466fcSArtur Petrosyan {
5654012466fcSArtur Petrosyan 	u32 pcgctl;
5655012466fcSArtur Petrosyan 
5656012466fcSArtur Petrosyan 	dev_dbg(hsotg->dev, "Entering device clock gating.\n");
5657012466fcSArtur Petrosyan 
5658012466fcSArtur Petrosyan 	/* Set the Phy Clock bit as suspend is received. */
5659012466fcSArtur Petrosyan 	pcgctl = dwc2_readl(hsotg, PCGCTL);
5660012466fcSArtur Petrosyan 	pcgctl |= PCGCTL_STOPPCLK;
5661012466fcSArtur Petrosyan 	dwc2_writel(hsotg, pcgctl, PCGCTL);
5662012466fcSArtur Petrosyan 	udelay(5);
5663012466fcSArtur Petrosyan 
5664012466fcSArtur Petrosyan 	/* Set the Gate hclk as suspend is received. */
5665012466fcSArtur Petrosyan 	pcgctl = dwc2_readl(hsotg, PCGCTL);
5666012466fcSArtur Petrosyan 	pcgctl |= PCGCTL_GATEHCLK;
5667012466fcSArtur Petrosyan 	dwc2_writel(hsotg, pcgctl, PCGCTL);
5668012466fcSArtur Petrosyan 	udelay(5);
5669012466fcSArtur Petrosyan 
5670012466fcSArtur Petrosyan 	hsotg->lx_state = DWC2_L2;
5671012466fcSArtur Petrosyan 	hsotg->bus_suspended = true;
5672012466fcSArtur Petrosyan }
5673012466fcSArtur Petrosyan 
5674012466fcSArtur Petrosyan /*
5675012466fcSArtur Petrosyan  * dwc2_gadget_exit_clock_gating() - Exit controller from device clock gating.
5676012466fcSArtur Petrosyan  *
5677012466fcSArtur Petrosyan  * @hsotg: Programming view of the DWC_otg controller
5678012466fcSArtur Petrosyan  * @rem_wakeup: indicates whether remote wake up is enabled.
5679012466fcSArtur Petrosyan  *
5680012466fcSArtur Petrosyan  * This function is for exiting from device mode clock gating.
5681012466fcSArtur Petrosyan  */
dwc2_gadget_exit_clock_gating(struct dwc2_hsotg * hsotg,int rem_wakeup)5682012466fcSArtur Petrosyan void dwc2_gadget_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup)
5683012466fcSArtur Petrosyan {
5684012466fcSArtur Petrosyan 	u32 pcgctl;
5685012466fcSArtur Petrosyan 	u32 dctl;
5686012466fcSArtur Petrosyan 
5687012466fcSArtur Petrosyan 	dev_dbg(hsotg->dev, "Exiting device clock gating.\n");
5688012466fcSArtur Petrosyan 
5689012466fcSArtur Petrosyan 	/* Clear the Gate hclk. */
5690012466fcSArtur Petrosyan 	pcgctl = dwc2_readl(hsotg, PCGCTL);
5691012466fcSArtur Petrosyan 	pcgctl &= ~PCGCTL_GATEHCLK;
5692012466fcSArtur Petrosyan 	dwc2_writel(hsotg, pcgctl, PCGCTL);
5693012466fcSArtur Petrosyan 	udelay(5);
5694012466fcSArtur Petrosyan 
5695012466fcSArtur Petrosyan 	/* Phy Clock bit. */
5696012466fcSArtur Petrosyan 	pcgctl = dwc2_readl(hsotg, PCGCTL);
5697012466fcSArtur Petrosyan 	pcgctl &= ~PCGCTL_STOPPCLK;
5698012466fcSArtur Petrosyan 	dwc2_writel(hsotg, pcgctl, PCGCTL);
5699012466fcSArtur Petrosyan 	udelay(5);
5700012466fcSArtur Petrosyan 
5701012466fcSArtur Petrosyan 	if (rem_wakeup) {
5702012466fcSArtur Petrosyan 		/* Set Remote Wakeup Signaling */
5703012466fcSArtur Petrosyan 		dctl = dwc2_readl(hsotg, DCTL);
5704012466fcSArtur Petrosyan 		dctl |= DCTL_RMTWKUPSIG;
5705012466fcSArtur Petrosyan 		dwc2_writel(hsotg, dctl, DCTL);
5706012466fcSArtur Petrosyan 	}
5707012466fcSArtur Petrosyan 
5708012466fcSArtur Petrosyan 	/* Change to L0 state */
5709012466fcSArtur Petrosyan 	call_gadget(hsotg, resume);
5710012466fcSArtur Petrosyan 	hsotg->lx_state = DWC2_L0;
5711012466fcSArtur Petrosyan 	hsotg->bus_suspended = false;
5712012466fcSArtur Petrosyan }
5713