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 */
8884f3cb0f9SPeng 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),
8914f3cb0f9SPeng 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
14183e417f31SMinas Harutyunyan if (hs->lx_state == DWC2_L1) {
14193e417f31SMinas Harutyunyan dwc2_wakeup_from_lpm_l1(hs, true);
14203e417f31SMinas Harutyunyan }
14213e417f31SMinas 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
3427643cc4deSGregory Herrero if (!is_usb_reset)
3428f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);
342947a1685fSDinh Nguyen
343079c3b5bbSVahram Aharonyan dcfg |= DCFG_EPMISCNT(1);
343138e9002bSVardan Mikayelyan
343238e9002bSVardan Mikayelyan switch (hsotg->params.speed) {
343338e9002bSVardan Mikayelyan case DWC2_SPEED_PARAM_LOW:
343438e9002bSVardan Mikayelyan dcfg |= DCFG_DEVSPD_LS;
343538e9002bSVardan Mikayelyan break;
343638e9002bSVardan Mikayelyan case DWC2_SPEED_PARAM_FULL:
343779c3b5bbSVahram Aharonyan if (hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS)
343879c3b5bbSVahram Aharonyan dcfg |= DCFG_DEVSPD_FS48;
343979c3b5bbSVahram Aharonyan else
344079c3b5bbSVahram Aharonyan dcfg |= DCFG_DEVSPD_FS;
344138e9002bSVardan Mikayelyan break;
344238e9002bSVardan Mikayelyan default:
344379c3b5bbSVahram Aharonyan dcfg |= DCFG_DEVSPD_HS;
344479c3b5bbSVahram Aharonyan }
344538e9002bSVardan Mikayelyan
3446b43ebc96SGrigor Tovmasyan if (hsotg->params.ipg_isoc_en)
3447b43ebc96SGrigor Tovmasyan dcfg |= DCFG_IPG_ISOC_SUPPORDED;
3448b43ebc96SGrigor Tovmasyan
3449f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dcfg, DCFG);
345047a1685fSDinh Nguyen
345147a1685fSDinh Nguyen /* Clear any pending OTG interrupts */
3452f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, 0xffffffff, GOTGINT);
345347a1685fSDinh Nguyen
345447a1685fSDinh Nguyen /* Clear any pending interrupts */
3455f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, 0xffffffff, GINTSTS);
34561ee6903bSGregory Herrero intmsk = GINTSTS_ERLYSUSP | GINTSTS_SESSREQINT |
345747a1685fSDinh Nguyen GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF |
34581ee6903bSGregory Herrero GINTSTS_USBRST | GINTSTS_RESETDET |
34591ee6903bSGregory Herrero GINTSTS_ENUMDONE | GINTSTS_OTGINT |
3460376f0401SSevak Arakelyan GINTSTS_USBSUSP | GINTSTS_WKUPINT |
3461376f0401SSevak Arakelyan GINTSTS_LPMTRANRCVD;
3462f4736701SVahram Aharonyan
3463f4736701SVahram Aharonyan if (!using_desc_dma(hsotg))
3464f4736701SVahram Aharonyan intmsk |= GINTSTS_INCOMPL_SOIN | GINTSTS_INCOMPL_SOOUT;
34651ee6903bSGregory Herrero
346695832c00SJohn Youn if (!hsotg->params.external_id_pin_ctl)
34671ee6903bSGregory Herrero intmsk |= GINTSTS_CONIDSTSCHNG;
34681ee6903bSGregory Herrero
3469f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, intmsk, GINTMSK);
347047a1685fSDinh Nguyen
3471a5c18f11SVahram Aharonyan if (using_dma(hsotg)) {
3472f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN |
3473d1ac8c80SRazmik Karapetyan hsotg->params.ahbcfg,
3474f25c42b8SGevorg Sahakyan GAHBCFG);
3475a5c18f11SVahram Aharonyan
3476a5c18f11SVahram Aharonyan /* Set DDMA mode support in the core if needed */
3477a5c18f11SVahram Aharonyan if (using_desc_dma(hsotg))
3478f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCFG, DCFG_DESCDMA_EN);
3479a5c18f11SVahram Aharonyan
3480a5c18f11SVahram Aharonyan } else {
3481f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ((hsotg->dedicated_fifos) ?
348295c8bc36SAntti Seppälä (GAHBCFG_NP_TXF_EMP_LVL |
348347a1685fSDinh Nguyen GAHBCFG_P_TXF_EMP_LVL) : 0) |
3484f25c42b8SGevorg Sahakyan GAHBCFG_GLBL_INTR_EN, GAHBCFG);
3485a5c18f11SVahram Aharonyan }
348647a1685fSDinh Nguyen
348747a1685fSDinh Nguyen /*
348847a1685fSDinh Nguyen * If INTknTXFEmpMsk is enabled, it's important to disable ep interrupts
348947a1685fSDinh Nguyen * when we have no data to transfer. Otherwise we get being flooded by
349047a1685fSDinh Nguyen * interrupts.
349147a1685fSDinh Nguyen */
349247a1685fSDinh Nguyen
3493f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ((hsotg->dedicated_fifos && !using_dma(hsotg)) ?
34946ff2e832SMian Yousaf Kaukab DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK : 0) |
349547a1685fSDinh Nguyen DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK |
3496837e9f00SVardan Mikayelyan DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK,
3497f25c42b8SGevorg Sahakyan DIEPMSK);
349847a1685fSDinh Nguyen
349947a1685fSDinh Nguyen /*
350047a1685fSDinh Nguyen * don't need XferCompl, we get that from RXFIFO in slave mode. In
35019d9a6b07SVahram Aharonyan * DMA mode we may need this and StsPhseRcvd.
350247a1685fSDinh Nguyen */
3503f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, (using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK |
35049d9a6b07SVahram Aharonyan DOEPMSK_STSPHSERCVDMSK) : 0) |
350547a1685fSDinh Nguyen DOEPMSK_EPDISBLDMSK | DOEPMSK_AHBERRMSK |
35069d9a6b07SVahram Aharonyan DOEPMSK_SETUPMSK,
3507f25c42b8SGevorg Sahakyan DOEPMSK);
350847a1685fSDinh Nguyen
3509ec01f0b2SVahram Aharonyan /* Enable BNA interrupt for DDMA */
351037981e00SMinas Harutyunyan if (using_desc_dma(hsotg)) {
3511f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DOEPMSK, DOEPMSK_BNAMSK);
3512f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DIEPMSK, DIEPMSK_BNAININTRMSK);
351337981e00SMinas Harutyunyan }
3514ec01f0b2SVahram Aharonyan
3515ca531bc2SGrigor Tovmasyan /* Enable Service Interval mode if supported */
3516ca531bc2SGrigor Tovmasyan if (using_desc_dma(hsotg) && hsotg->params.service_interval)
3517ca531bc2SGrigor Tovmasyan dwc2_set_bit(hsotg, DCTL, DCTL_SERVICE_INTERVAL_SUPPORTED);
3518ca531bc2SGrigor Tovmasyan
3519f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, 0, DAINTMSK);
352047a1685fSDinh Nguyen
352147a1685fSDinh Nguyen dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
3522f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPCTL0),
3523f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DOEPCTL0));
352447a1685fSDinh Nguyen
352547a1685fSDinh Nguyen /* enable in and out endpoint interrupts */
35261f91b4ccSFelipe Balbi dwc2_hsotg_en_gsint(hsotg, GINTSTS_OEPINT | GINTSTS_IEPINT);
352747a1685fSDinh Nguyen
352847a1685fSDinh Nguyen /*
352947a1685fSDinh Nguyen * Enable the RXFIFO when in slave mode, as this is how we collect
353047a1685fSDinh Nguyen * the data. In DMA mode, we get events from the FIFO but also
353147a1685fSDinh Nguyen * things we cannot process, so do not use it.
353247a1685fSDinh Nguyen */
353347a1685fSDinh Nguyen if (!using_dma(hsotg))
35341f91b4ccSFelipe Balbi dwc2_hsotg_en_gsint(hsotg, GINTSTS_RXFLVL);
353547a1685fSDinh Nguyen
353647a1685fSDinh Nguyen /* Enable interrupts for EP0 in and out */
35371f91b4ccSFelipe Balbi dwc2_hsotg_ctrl_epint(hsotg, 0, 0, 1);
35381f91b4ccSFelipe Balbi dwc2_hsotg_ctrl_epint(hsotg, 0, 1, 1);
353947a1685fSDinh Nguyen
3540643cc4deSGregory Herrero if (!is_usb_reset) {
3541f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_PWRONPRGDONE);
354247a1685fSDinh Nguyen udelay(10); /* see openiboot */
3543f25c42b8SGevorg Sahakyan dwc2_clear_bit(hsotg, DCTL, DCTL_PWRONPRGDONE);
3544643cc4deSGregory Herrero }
354547a1685fSDinh Nguyen
3546f25c42b8SGevorg Sahakyan dev_dbg(hsotg->dev, "DCTL=0x%08x\n", dwc2_readl(hsotg, DCTL));
354747a1685fSDinh Nguyen
354847a1685fSDinh Nguyen /*
354947a1685fSDinh Nguyen * DxEPCTL_USBActEp says RO in manual, but seems to be set by
355047a1685fSDinh Nguyen * writing to the EPCTL register..
355147a1685fSDinh Nguyen */
355247a1685fSDinh Nguyen
355347a1685fSDinh Nguyen /* set to read 1 8byte packet */
3554f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
3555f25c42b8SGevorg Sahakyan DXEPTSIZ_XFERSIZE(8), DOEPTSIZ0);
355647a1685fSDinh Nguyen
3557f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
355847a1685fSDinh Nguyen DXEPCTL_CNAK | DXEPCTL_EPENA |
355947a1685fSDinh Nguyen DXEPCTL_USBACTEP,
3560f25c42b8SGevorg Sahakyan DOEPCTL0);
356147a1685fSDinh Nguyen
356247a1685fSDinh Nguyen /* enable, but don't activate EP0in */
3563f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
3564f25c42b8SGevorg Sahakyan DXEPCTL_USBACTEP, DIEPCTL0);
356547a1685fSDinh Nguyen
356647a1685fSDinh Nguyen /* clear global NAKs */
3567643cc4deSGregory Herrero val = DCTL_CGOUTNAK | DCTL_CGNPINNAK;
3568643cc4deSGregory Herrero if (!is_usb_reset)
3569643cc4deSGregory Herrero val |= DCTL_SFTDISCON;
3570f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, val);
357147a1685fSDinh Nguyen
357221b03405SSevak Arakelyan /* configure the core to support LPM */
357321b03405SSevak Arakelyan dwc2_gadget_init_lpm(hsotg);
357421b03405SSevak Arakelyan
357515d9dbf8SGrigor Tovmasyan /* program GREFCLK register if needed */
357615d9dbf8SGrigor Tovmasyan if (using_desc_dma(hsotg) && hsotg->params.service_interval)
357715d9dbf8SGrigor Tovmasyan dwc2_gadget_program_ref_clk(hsotg);
357815d9dbf8SGrigor Tovmasyan
357947a1685fSDinh Nguyen /* must be at-least 3ms to allow bus to see disconnect */
358047a1685fSDinh Nguyen mdelay(3);
358147a1685fSDinh Nguyen
3582065d3931SGregory Herrero hsotg->lx_state = DWC2_L0;
3583755d7395SVardan Mikayelyan
3584755d7395SVardan Mikayelyan dwc2_hsotg_enqueue_setup(hsotg);
3585755d7395SVardan Mikayelyan
3586755d7395SVardan Mikayelyan dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n",
3587f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPCTL0),
3588f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DOEPCTL0));
3589ad38dc5dSMarek Szyprowski }
3590ac3c81f3SMarek Szyprowski
dwc2_hsotg_core_disconnect(struct dwc2_hsotg * hsotg)359117f93402SAmelie Delaunay void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
3592ad38dc5dSMarek Szyprowski {
3593ad38dc5dSMarek Szyprowski /* set the soft-disconnect bit */
3594f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);
3595ad38dc5dSMarek Szyprowski }
3596ad38dc5dSMarek Szyprowski
dwc2_hsotg_core_connect(struct dwc2_hsotg * hsotg)35971f91b4ccSFelipe Balbi void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg)
3598ad38dc5dSMarek Szyprowski {
359947a1685fSDinh Nguyen /* remove the soft-disconnect and let's go */
3600db638c65SAmelie Delaunay if (!hsotg->role_sw || (dwc2_readl(hsotg, GOTGCTL) & GOTGCTL_BSESVLD))
3601f25c42b8SGevorg Sahakyan dwc2_clear_bit(hsotg, DCTL, DCTL_SFTDISCON);
360247a1685fSDinh Nguyen }
360347a1685fSDinh Nguyen
360447a1685fSDinh Nguyen /**
3605381fc8f8SVardan Mikayelyan * dwc2_gadget_handle_incomplete_isoc_in - handle incomplete ISO IN Interrupt.
3606381fc8f8SVardan Mikayelyan * @hsotg: The device state:
3607381fc8f8SVardan Mikayelyan *
3608381fc8f8SVardan Mikayelyan * This interrupt indicates one of the following conditions occurred while
3609381fc8f8SVardan Mikayelyan * transmitting an ISOC transaction.
3610381fc8f8SVardan Mikayelyan * - Corrupted IN Token for ISOC EP.
3611381fc8f8SVardan Mikayelyan * - Packet not complete in FIFO.
3612381fc8f8SVardan Mikayelyan *
3613381fc8f8SVardan Mikayelyan * The following actions will be taken:
3614381fc8f8SVardan Mikayelyan * - Determine the EP
3615381fc8f8SVardan Mikayelyan * - Disable EP; when 'Endpoint Disabled' interrupt is received Flush FIFO
3616381fc8f8SVardan Mikayelyan */
dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg * hsotg)3617381fc8f8SVardan Mikayelyan static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg)
3618381fc8f8SVardan Mikayelyan {
3619381fc8f8SVardan Mikayelyan struct dwc2_hsotg_ep *hs_ep;
3620381fc8f8SVardan Mikayelyan u32 epctrl;
36211b4977c7SRazmik Karapetyan u32 daintmsk;
3622381fc8f8SVardan Mikayelyan u32 idx;
3623381fc8f8SVardan Mikayelyan
3624381fc8f8SVardan Mikayelyan dev_dbg(hsotg->dev, "Incomplete isoc in interrupt received:\n");
3625381fc8f8SVardan Mikayelyan
3626f25c42b8SGevorg Sahakyan daintmsk = dwc2_readl(hsotg, DAINTMSK);
36271b4977c7SRazmik Karapetyan
3628d5d5f079SArtur Petrosyan for (idx = 1; idx < hsotg->num_of_eps; idx++) {
3629381fc8f8SVardan Mikayelyan hs_ep = hsotg->eps_in[idx];
36301b4977c7SRazmik Karapetyan /* Proceed only unmasked ISOC EPs */
363189066b36SJohn Keeping if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous)
36321b4977c7SRazmik Karapetyan continue;
36331b4977c7SRazmik Karapetyan
3634f25c42b8SGevorg Sahakyan epctrl = dwc2_readl(hsotg, DIEPCTL(idx));
36351b4977c7SRazmik Karapetyan if ((epctrl & DXEPCTL_EPENA) &&
3636381fc8f8SVardan Mikayelyan dwc2_gadget_target_frame_elapsed(hs_ep)) {
3637381fc8f8SVardan Mikayelyan epctrl |= DXEPCTL_SNAK;
3638381fc8f8SVardan Mikayelyan epctrl |= DXEPCTL_EPDIS;
3639f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, epctrl, DIEPCTL(idx));
3640381fc8f8SVardan Mikayelyan }
3641381fc8f8SVardan Mikayelyan }
3642381fc8f8SVardan Mikayelyan
3643381fc8f8SVardan Mikayelyan /* Clear interrupt */
3644f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GINTSTS_INCOMPL_SOIN, GINTSTS);
3645381fc8f8SVardan Mikayelyan }
3646381fc8f8SVardan Mikayelyan
3647381fc8f8SVardan Mikayelyan /**
3648381fc8f8SVardan Mikayelyan * dwc2_gadget_handle_incomplete_isoc_out - handle incomplete ISO OUT Interrupt
3649381fc8f8SVardan Mikayelyan * @hsotg: The device state:
3650381fc8f8SVardan Mikayelyan *
3651381fc8f8SVardan Mikayelyan * This interrupt indicates one of the following conditions occurred while
3652381fc8f8SVardan Mikayelyan * transmitting an ISOC transaction.
3653381fc8f8SVardan Mikayelyan * - Corrupted OUT Token for ISOC EP.
3654381fc8f8SVardan Mikayelyan * - Packet not complete in FIFO.
3655381fc8f8SVardan Mikayelyan *
3656381fc8f8SVardan Mikayelyan * The following actions will be taken:
3657381fc8f8SVardan Mikayelyan * - Determine the EP
3658381fc8f8SVardan Mikayelyan * - Set DCTL_SGOUTNAK and unmask GOUTNAKEFF if target frame elapsed.
3659381fc8f8SVardan Mikayelyan */
dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg * hsotg)3660381fc8f8SVardan Mikayelyan static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg)
3661381fc8f8SVardan Mikayelyan {
3662381fc8f8SVardan Mikayelyan u32 gintsts;
3663381fc8f8SVardan Mikayelyan u32 gintmsk;
3664689efb26SRazmik Karapetyan u32 daintmsk;
3665381fc8f8SVardan Mikayelyan u32 epctrl;
3666381fc8f8SVardan Mikayelyan struct dwc2_hsotg_ep *hs_ep;
3667381fc8f8SVardan Mikayelyan int idx;
3668381fc8f8SVardan Mikayelyan
3669381fc8f8SVardan Mikayelyan dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__);
3670381fc8f8SVardan Mikayelyan
3671f25c42b8SGevorg Sahakyan daintmsk = dwc2_readl(hsotg, DAINTMSK);
3672689efb26SRazmik Karapetyan daintmsk >>= DAINT_OUTEP_SHIFT;
3673689efb26SRazmik Karapetyan
3674d5d5f079SArtur Petrosyan for (idx = 1; idx < hsotg->num_of_eps; idx++) {
3675381fc8f8SVardan Mikayelyan hs_ep = hsotg->eps_out[idx];
3676689efb26SRazmik Karapetyan /* Proceed only unmasked ISOC EPs */
367789066b36SJohn Keeping if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous)
3678689efb26SRazmik Karapetyan continue;
3679689efb26SRazmik Karapetyan
3680f25c42b8SGevorg Sahakyan epctrl = dwc2_readl(hsotg, DOEPCTL(idx));
3681689efb26SRazmik Karapetyan if ((epctrl & DXEPCTL_EPENA) &&
3682381fc8f8SVardan Mikayelyan dwc2_gadget_target_frame_elapsed(hs_ep)) {
3683381fc8f8SVardan Mikayelyan /* Unmask GOUTNAKEFF interrupt */
3684f25c42b8SGevorg Sahakyan gintmsk = dwc2_readl(hsotg, GINTMSK);
3685381fc8f8SVardan Mikayelyan gintmsk |= GINTSTS_GOUTNAKEFF;
3686f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gintmsk, GINTMSK);
3687381fc8f8SVardan Mikayelyan
3688f25c42b8SGevorg Sahakyan gintsts = dwc2_readl(hsotg, GINTSTS);
3689689efb26SRazmik Karapetyan if (!(gintsts & GINTSTS_GOUTNAKEFF)) {
3690f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_SGOUTNAK);
3691689efb26SRazmik Karapetyan break;
3692689efb26SRazmik Karapetyan }
3693381fc8f8SVardan Mikayelyan }
3694381fc8f8SVardan Mikayelyan }
3695381fc8f8SVardan Mikayelyan
3696381fc8f8SVardan Mikayelyan /* Clear interrupt */
3697f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GINTSTS_INCOMPL_SOOUT, GINTSTS);
3698381fc8f8SVardan Mikayelyan }
3699381fc8f8SVardan Mikayelyan
3700381fc8f8SVardan Mikayelyan /**
37011f91b4ccSFelipe Balbi * dwc2_hsotg_irq - handle device interrupt
370247a1685fSDinh Nguyen * @irq: The IRQ number triggered
370347a1685fSDinh Nguyen * @pw: The pw value when registered the handler.
370447a1685fSDinh Nguyen */
dwc2_hsotg_irq(int irq,void * pw)37051f91b4ccSFelipe Balbi static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
370647a1685fSDinh Nguyen {
3707941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = pw;
370847a1685fSDinh Nguyen int retry_count = 8;
370947a1685fSDinh Nguyen u32 gintsts;
371047a1685fSDinh Nguyen u32 gintmsk;
371147a1685fSDinh Nguyen
3712ee3de8d7SVardan Mikayelyan if (!dwc2_is_device_mode(hsotg))
3713ee3de8d7SVardan Mikayelyan return IRQ_NONE;
3714ee3de8d7SVardan Mikayelyan
371547a1685fSDinh Nguyen spin_lock(&hsotg->lock);
371647a1685fSDinh Nguyen irq_retry:
3717f25c42b8SGevorg Sahakyan gintsts = dwc2_readl(hsotg, GINTSTS);
3718f25c42b8SGevorg Sahakyan gintmsk = dwc2_readl(hsotg, GINTMSK);
371947a1685fSDinh Nguyen
372047a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: %08x %08x (%08x) retry %d\n",
372147a1685fSDinh Nguyen __func__, gintsts, gintsts & gintmsk, gintmsk, retry_count);
372247a1685fSDinh Nguyen
372347a1685fSDinh Nguyen gintsts &= gintmsk;
372447a1685fSDinh Nguyen
37258fc37b82SMian Yousaf Kaukab if (gintsts & GINTSTS_RESETDET) {
37268fc37b82SMian Yousaf Kaukab dev_dbg(hsotg->dev, "%s: USBRstDet\n", __func__);
37278fc37b82SMian Yousaf Kaukab
3728f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GINTSTS_RESETDET, GINTSTS);
37298fc37b82SMian Yousaf Kaukab
37308fc37b82SMian Yousaf Kaukab /* This event must be used only if controller is suspended */
3731c9c394abSArtur Petrosyan if (hsotg->in_ppd && hsotg->lx_state == DWC2_L2)
3732c9c394abSArtur Petrosyan dwc2_exit_partial_power_down(hsotg, 0, true);
3733c9c394abSArtur Petrosyan
3734f047361fSMinas Harutyunyan /* Exit gadget mode clock gating. */
3735f047361fSMinas Harutyunyan if (hsotg->params.power_down ==
3736f047361fSMinas Harutyunyan DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended &&
3737f047361fSMinas Harutyunyan !hsotg->params.no_clock_gating)
3738f047361fSMinas Harutyunyan dwc2_gadget_exit_clock_gating(hsotg, 0);
3739f047361fSMinas Harutyunyan
37408fc37b82SMian Yousaf Kaukab hsotg->lx_state = DWC2_L0;
37418fc37b82SMian Yousaf Kaukab }
37428fc37b82SMian Yousaf Kaukab
37438fc37b82SMian Yousaf Kaukab if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) {
3744f25c42b8SGevorg Sahakyan u32 usb_status = dwc2_readl(hsotg, GOTGCTL);
37458fc37b82SMian Yousaf Kaukab u32 connected = hsotg->connected;
37468fc37b82SMian Yousaf Kaukab
37478fc37b82SMian Yousaf Kaukab dev_dbg(hsotg->dev, "%s: USBRst\n", __func__);
37488fc37b82SMian Yousaf Kaukab dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n",
3749f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, GNPTXSTS));
37508fc37b82SMian Yousaf Kaukab
3751f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GINTSTS_USBRST, GINTSTS);
37528fc37b82SMian Yousaf Kaukab
37538fc37b82SMian Yousaf Kaukab /* Report disconnection if it is not already done. */
37548fc37b82SMian Yousaf Kaukab dwc2_hsotg_disconnect(hsotg);
37558fc37b82SMian Yousaf Kaukab
3756307bc11fSMinas Harutyunyan /* Reset device address to zero */
3757f25c42b8SGevorg Sahakyan dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK);
3758307bc11fSMinas Harutyunyan
37598fc37b82SMian Yousaf Kaukab if (usb_status & GOTGCTL_BSESVLD && connected)
37608fc37b82SMian Yousaf Kaukab dwc2_hsotg_core_init_disconnected(hsotg, true);
37618fc37b82SMian Yousaf Kaukab }
37628fc37b82SMian Yousaf Kaukab
376347a1685fSDinh Nguyen if (gintsts & GINTSTS_ENUMDONE) {
3764f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GINTSTS_ENUMDONE, GINTSTS);
376547a1685fSDinh Nguyen
37661f91b4ccSFelipe Balbi dwc2_hsotg_irq_enumdone(hsotg);
376747a1685fSDinh Nguyen }
376847a1685fSDinh Nguyen
376947a1685fSDinh Nguyen if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {
3770f25c42b8SGevorg Sahakyan u32 daint = dwc2_readl(hsotg, DAINT);
3771f25c42b8SGevorg Sahakyan u32 daintmsk = dwc2_readl(hsotg, DAINTMSK);
377247a1685fSDinh Nguyen u32 daint_out, daint_in;
377347a1685fSDinh Nguyen int ep;
377447a1685fSDinh Nguyen
377547a1685fSDinh Nguyen daint &= daintmsk;
377647a1685fSDinh Nguyen daint_out = daint >> DAINT_OUTEP_SHIFT;
377747a1685fSDinh Nguyen daint_in = daint & ~(daint_out << DAINT_OUTEP_SHIFT);
377847a1685fSDinh Nguyen
377947a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint);
378047a1685fSDinh Nguyen
3781cec87f1dSMian Yousaf Kaukab for (ep = 0; ep < hsotg->num_of_eps && daint_out;
3782cec87f1dSMian Yousaf Kaukab ep++, daint_out >>= 1) {
378347a1685fSDinh Nguyen if (daint_out & 1)
37841f91b4ccSFelipe Balbi dwc2_hsotg_epint(hsotg, ep, 0);
378547a1685fSDinh Nguyen }
378647a1685fSDinh Nguyen
3787cec87f1dSMian Yousaf Kaukab for (ep = 0; ep < hsotg->num_of_eps && daint_in;
3788cec87f1dSMian Yousaf Kaukab ep++, daint_in >>= 1) {
378947a1685fSDinh Nguyen if (daint_in & 1)
37901f91b4ccSFelipe Balbi dwc2_hsotg_epint(hsotg, ep, 1);
379147a1685fSDinh Nguyen }
379247a1685fSDinh Nguyen }
379347a1685fSDinh Nguyen
379447a1685fSDinh Nguyen /* check both FIFOs */
379547a1685fSDinh Nguyen
379647a1685fSDinh Nguyen if (gintsts & GINTSTS_NPTXFEMP) {
379747a1685fSDinh Nguyen dev_dbg(hsotg->dev, "NPTxFEmp\n");
379847a1685fSDinh Nguyen
379947a1685fSDinh Nguyen /*
380047a1685fSDinh Nguyen * Disable the interrupt to stop it happening again
380147a1685fSDinh Nguyen * unless one of these endpoint routines decides that
380247a1685fSDinh Nguyen * it needs re-enabling
380347a1685fSDinh Nguyen */
380447a1685fSDinh Nguyen
38051f91b4ccSFelipe Balbi dwc2_hsotg_disable_gsint(hsotg, GINTSTS_NPTXFEMP);
38061f91b4ccSFelipe Balbi dwc2_hsotg_irq_fifoempty(hsotg, false);
380747a1685fSDinh Nguyen }
380847a1685fSDinh Nguyen
380947a1685fSDinh Nguyen if (gintsts & GINTSTS_PTXFEMP) {
381047a1685fSDinh Nguyen dev_dbg(hsotg->dev, "PTxFEmp\n");
381147a1685fSDinh Nguyen
381247a1685fSDinh Nguyen /* See note in GINTSTS_NPTxFEmp */
381347a1685fSDinh Nguyen
38141f91b4ccSFelipe Balbi dwc2_hsotg_disable_gsint(hsotg, GINTSTS_PTXFEMP);
38151f91b4ccSFelipe Balbi dwc2_hsotg_irq_fifoempty(hsotg, true);
381647a1685fSDinh Nguyen }
381747a1685fSDinh Nguyen
381847a1685fSDinh Nguyen if (gintsts & GINTSTS_RXFLVL) {
381947a1685fSDinh Nguyen /*
382047a1685fSDinh Nguyen * note, since GINTSTS_RxFLvl doubles as FIFO-not-empty,
38211f91b4ccSFelipe Balbi * we need to retry dwc2_hsotg_handle_rx if this is still
382247a1685fSDinh Nguyen * set.
382347a1685fSDinh Nguyen */
382447a1685fSDinh Nguyen
38251f91b4ccSFelipe Balbi dwc2_hsotg_handle_rx(hsotg);
382647a1685fSDinh Nguyen }
382747a1685fSDinh Nguyen
382847a1685fSDinh Nguyen if (gintsts & GINTSTS_ERLYSUSP) {
382947a1685fSDinh Nguyen dev_dbg(hsotg->dev, "GINTSTS_ErlySusp\n");
3830f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GINTSTS_ERLYSUSP, GINTSTS);
383147a1685fSDinh Nguyen }
383247a1685fSDinh Nguyen
383347a1685fSDinh Nguyen /*
383447a1685fSDinh Nguyen * these next two seem to crop-up occasionally causing the core
383547a1685fSDinh Nguyen * to shutdown the USB transfer, so try clearing them and logging
383647a1685fSDinh Nguyen * the occurrence.
383747a1685fSDinh Nguyen */
383847a1685fSDinh Nguyen
383947a1685fSDinh Nguyen if (gintsts & GINTSTS_GOUTNAKEFF) {
3840837e9f00SVardan Mikayelyan u8 idx;
3841837e9f00SVardan Mikayelyan u32 epctrl;
3842837e9f00SVardan Mikayelyan u32 gintmsk;
3843d8484552SRazmik Karapetyan u32 daintmsk;
3844837e9f00SVardan Mikayelyan struct dwc2_hsotg_ep *hs_ep;
384547a1685fSDinh Nguyen
3846f25c42b8SGevorg Sahakyan daintmsk = dwc2_readl(hsotg, DAINTMSK);
3847d8484552SRazmik Karapetyan daintmsk >>= DAINT_OUTEP_SHIFT;
3848837e9f00SVardan Mikayelyan /* Mask this interrupt */
3849f25c42b8SGevorg Sahakyan gintmsk = dwc2_readl(hsotg, GINTMSK);
3850837e9f00SVardan Mikayelyan gintmsk &= ~GINTSTS_GOUTNAKEFF;
3851f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gintmsk, GINTMSK);
385247a1685fSDinh Nguyen
3853837e9f00SVardan Mikayelyan dev_dbg(hsotg->dev, "GOUTNakEff triggered\n");
3854d5d5f079SArtur Petrosyan for (idx = 1; idx < hsotg->num_of_eps; idx++) {
3855837e9f00SVardan Mikayelyan hs_ep = hsotg->eps_out[idx];
3856d8484552SRazmik Karapetyan /* Proceed only unmasked ISOC EPs */
38576070636cSMinas Harutyunyan if (BIT(idx) & ~daintmsk)
3858d8484552SRazmik Karapetyan continue;
3859d8484552SRazmik Karapetyan
3860f25c42b8SGevorg Sahakyan epctrl = dwc2_readl(hsotg, DOEPCTL(idx));
3861837e9f00SVardan Mikayelyan
38626070636cSMinas Harutyunyan //ISOC Ep's only
38636070636cSMinas Harutyunyan if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous) {
3864837e9f00SVardan Mikayelyan epctrl |= DXEPCTL_SNAK;
3865837e9f00SVardan Mikayelyan epctrl |= DXEPCTL_EPDIS;
3866f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, epctrl, DOEPCTL(idx));
38676070636cSMinas Harutyunyan continue;
38686070636cSMinas Harutyunyan }
38696070636cSMinas Harutyunyan
38706070636cSMinas Harutyunyan //Non-ISOC EP's
38716070636cSMinas Harutyunyan if (hs_ep->halted) {
38726070636cSMinas Harutyunyan if (!(epctrl & DXEPCTL_EPENA))
38736070636cSMinas Harutyunyan epctrl |= DXEPCTL_EPENA;
38746070636cSMinas Harutyunyan epctrl |= DXEPCTL_EPDIS;
38756070636cSMinas Harutyunyan epctrl |= DXEPCTL_STALL;
38766070636cSMinas Harutyunyan dwc2_writel(hsotg, epctrl, DOEPCTL(idx));
3877837e9f00SVardan Mikayelyan }
3878837e9f00SVardan Mikayelyan }
3879837e9f00SVardan Mikayelyan
3880837e9f00SVardan Mikayelyan /* This interrupt bit is cleared in DXEPINT_EPDISBLD handler */
388147a1685fSDinh Nguyen }
388247a1685fSDinh Nguyen
388347a1685fSDinh Nguyen if (gintsts & GINTSTS_GINNAKEFF) {
388447a1685fSDinh Nguyen dev_info(hsotg->dev, "GINNakEff triggered\n");
388547a1685fSDinh Nguyen
3886f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_CGNPINNAK);
388747a1685fSDinh Nguyen
38881f91b4ccSFelipe Balbi dwc2_hsotg_dump(hsotg);
388947a1685fSDinh Nguyen }
389047a1685fSDinh Nguyen
3891381fc8f8SVardan Mikayelyan if (gintsts & GINTSTS_INCOMPL_SOIN)
3892381fc8f8SVardan Mikayelyan dwc2_gadget_handle_incomplete_isoc_in(hsotg);
3893ec1f9d9fSRoman Bacik
3894381fc8f8SVardan Mikayelyan if (gintsts & GINTSTS_INCOMPL_SOOUT)
3895381fc8f8SVardan Mikayelyan dwc2_gadget_handle_incomplete_isoc_out(hsotg);
3896ec1f9d9fSRoman Bacik
389747a1685fSDinh Nguyen /*
389847a1685fSDinh Nguyen * if we've had fifo events, we should try and go around the
389947a1685fSDinh Nguyen * loop again to see if there's any point in returning yet.
390047a1685fSDinh Nguyen */
390147a1685fSDinh Nguyen
390247a1685fSDinh Nguyen if (gintsts & IRQ_RETRY_MASK && --retry_count > 0)
390347a1685fSDinh Nguyen goto irq_retry;
390447a1685fSDinh Nguyen
3905187c5298SGrigor Tovmasyan /* Check WKUP_ALERT interrupt*/
3906187c5298SGrigor Tovmasyan if (hsotg->params.service_interval)
3907187c5298SGrigor Tovmasyan dwc2_gadget_wkup_alert_handler(hsotg);
3908187c5298SGrigor Tovmasyan
390947a1685fSDinh Nguyen spin_unlock(&hsotg->lock);
391047a1685fSDinh Nguyen
391147a1685fSDinh Nguyen return IRQ_HANDLED;
391247a1685fSDinh Nguyen }
391347a1685fSDinh Nguyen
dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep)3914a4f82771SVahram Aharonyan static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
3915a4f82771SVahram Aharonyan struct dwc2_hsotg_ep *hs_ep)
3916a4f82771SVahram Aharonyan {
3917a4f82771SVahram Aharonyan u32 epctrl_reg;
3918a4f82771SVahram Aharonyan u32 epint_reg;
3919a4f82771SVahram Aharonyan
3920a4f82771SVahram Aharonyan epctrl_reg = hs_ep->dir_in ? DIEPCTL(hs_ep->index) :
3921a4f82771SVahram Aharonyan DOEPCTL(hs_ep->index);
3922a4f82771SVahram Aharonyan epint_reg = hs_ep->dir_in ? DIEPINT(hs_ep->index) :
3923a4f82771SVahram Aharonyan DOEPINT(hs_ep->index);
3924a4f82771SVahram Aharonyan
3925a4f82771SVahram Aharonyan dev_dbg(hsotg->dev, "%s: stopping transfer on %s\n", __func__,
3926a4f82771SVahram Aharonyan hs_ep->name);
3927a4f82771SVahram Aharonyan
3928a4f82771SVahram Aharonyan if (hs_ep->dir_in) {
3929a4f82771SVahram Aharonyan if (hsotg->dedicated_fifos || hs_ep->periodic) {
3930f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, epctrl_reg, DXEPCTL_SNAK);
3931a4f82771SVahram Aharonyan /* Wait for Nak effect */
3932a4f82771SVahram Aharonyan if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg,
3933a4f82771SVahram Aharonyan DXEPINT_INEPNAKEFF, 100))
3934a4f82771SVahram Aharonyan dev_warn(hsotg->dev,
3935a4f82771SVahram Aharonyan "%s: timeout DIEPINT.NAKEFF\n",
3936a4f82771SVahram Aharonyan __func__);
3937a4f82771SVahram Aharonyan } else {
3938f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_SGNPINNAK);
3939a4f82771SVahram Aharonyan /* Wait for Nak effect */
3940a4f82771SVahram Aharonyan if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
3941a4f82771SVahram Aharonyan GINTSTS_GINNAKEFF, 100))
3942a4f82771SVahram Aharonyan dev_warn(hsotg->dev,
3943a4f82771SVahram Aharonyan "%s: timeout GINTSTS.GINNAKEFF\n",
3944a4f82771SVahram Aharonyan __func__);
3945a4f82771SVahram Aharonyan }
3946a4f82771SVahram Aharonyan } else {
3947fecb3a17SMinas Harutyunyan /* Mask GINTSTS_GOUTNAKEFF interrupt */
3948fecb3a17SMinas Harutyunyan dwc2_hsotg_disable_gsint(hsotg, GINTSTS_GOUTNAKEFF);
3949fecb3a17SMinas Harutyunyan
3950f25c42b8SGevorg Sahakyan if (!(dwc2_readl(hsotg, GINTSTS) & GINTSTS_GOUTNAKEFF))
3951f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_SGOUTNAK);
3952a4f82771SVahram Aharonyan
3953fecb3a17SMinas Harutyunyan if (!using_dma(hsotg)) {
3954fecb3a17SMinas Harutyunyan /* Wait for GINTSTS_RXFLVL interrupt */
3955fecb3a17SMinas Harutyunyan if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
3956fecb3a17SMinas Harutyunyan GINTSTS_RXFLVL, 100)) {
3957fecb3a17SMinas Harutyunyan dev_warn(hsotg->dev, "%s: timeout GINTSTS.RXFLVL\n",
3958fecb3a17SMinas Harutyunyan __func__);
3959fecb3a17SMinas Harutyunyan } else {
3960fecb3a17SMinas Harutyunyan /*
3961fecb3a17SMinas Harutyunyan * Pop GLOBAL OUT NAK status packet from RxFIFO
3962fecb3a17SMinas Harutyunyan * to assert GOUTNAKEFF interrupt
3963fecb3a17SMinas Harutyunyan */
3964fecb3a17SMinas Harutyunyan dwc2_readl(hsotg, GRXSTSP);
3965fecb3a17SMinas Harutyunyan }
3966fecb3a17SMinas Harutyunyan }
3967fecb3a17SMinas Harutyunyan
3968a4f82771SVahram Aharonyan /* Wait for global nak to take effect */
3969a4f82771SVahram Aharonyan if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
3970a4f82771SVahram Aharonyan GINTSTS_GOUTNAKEFF, 100))
3971a4f82771SVahram Aharonyan dev_warn(hsotg->dev, "%s: timeout GINTSTS.GOUTNAKEFF\n",
3972a4f82771SVahram Aharonyan __func__);
3973a4f82771SVahram Aharonyan }
3974a4f82771SVahram Aharonyan
3975a4f82771SVahram Aharonyan /* Disable ep */
3976f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK);
3977a4f82771SVahram Aharonyan
3978a4f82771SVahram Aharonyan /* Wait for ep to be disabled */
3979a4f82771SVahram Aharonyan if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_EPDISBLD, 100))
3980a4f82771SVahram Aharonyan dev_warn(hsotg->dev,
3981a4f82771SVahram Aharonyan "%s: timeout DOEPCTL.EPDisable\n", __func__);
3982a4f82771SVahram Aharonyan
3983a4f82771SVahram Aharonyan /* Clear EPDISBLD interrupt */
3984f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, epint_reg, DXEPINT_EPDISBLD);
3985a4f82771SVahram Aharonyan
3986a4f82771SVahram Aharonyan if (hs_ep->dir_in) {
3987a4f82771SVahram Aharonyan unsigned short fifo_index;
3988a4f82771SVahram Aharonyan
3989a4f82771SVahram Aharonyan if (hsotg->dedicated_fifos || hs_ep->periodic)
3990a4f82771SVahram Aharonyan fifo_index = hs_ep->fifo_index;
3991a4f82771SVahram Aharonyan else
3992a4f82771SVahram Aharonyan fifo_index = 0;
3993a4f82771SVahram Aharonyan
3994a4f82771SVahram Aharonyan /* Flush TX FIFO */
3995a4f82771SVahram Aharonyan dwc2_flush_tx_fifo(hsotg, fifo_index);
3996a4f82771SVahram Aharonyan
3997a4f82771SVahram Aharonyan /* Clear Global In NP NAK in Shared FIFO for non periodic ep */
3998a4f82771SVahram Aharonyan if (!hsotg->dedicated_fifos && !hs_ep->periodic)
3999f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_CGNPINNAK);
4000a4f82771SVahram Aharonyan
4001a4f82771SVahram Aharonyan } else {
4002a4f82771SVahram Aharonyan /* Remove global NAKs */
4003f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_CGOUTNAK);
4004a4f82771SVahram Aharonyan }
4005a4f82771SVahram Aharonyan }
4006a4f82771SVahram Aharonyan
400747a1685fSDinh Nguyen /**
40081f91b4ccSFelipe Balbi * dwc2_hsotg_ep_enable - enable the given endpoint
400947a1685fSDinh Nguyen * @ep: The USB endpint to configure
401047a1685fSDinh Nguyen * @desc: The USB endpoint descriptor to configure with.
401147a1685fSDinh Nguyen *
401247a1685fSDinh Nguyen * This is called from the USB gadget code's usb_ep_enable().
401347a1685fSDinh Nguyen */
dwc2_hsotg_ep_enable(struct usb_ep * ep,const struct usb_endpoint_descriptor * desc)40141f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
401547a1685fSDinh Nguyen const struct usb_endpoint_descriptor *desc)
401647a1685fSDinh Nguyen {
40171f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
4018941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = hs_ep->parent;
401947a1685fSDinh Nguyen unsigned long flags;
4020ca4c55adSMian Yousaf Kaukab unsigned int index = hs_ep->index;
402147a1685fSDinh Nguyen u32 epctrl_reg;
402247a1685fSDinh Nguyen u32 epctrl;
402347a1685fSDinh Nguyen u32 mps;
4024ee2c40deSVardan Mikayelyan u32 mc;
4025837e9f00SVardan Mikayelyan u32 mask;
4026ca4c55adSMian Yousaf Kaukab unsigned int dir_in;
4027ca4c55adSMian Yousaf Kaukab unsigned int i, val, size;
402847a1685fSDinh Nguyen int ret = 0;
4029729cac69SMinas Harutyunyan unsigned char ep_type;
403054f37f56SMinas Harutyunyan int desc_num;
403147a1685fSDinh Nguyen
403247a1685fSDinh Nguyen dev_dbg(hsotg->dev,
403347a1685fSDinh Nguyen "%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n",
403447a1685fSDinh Nguyen __func__, ep->name, desc->bEndpointAddress, desc->bmAttributes,
403547a1685fSDinh Nguyen desc->wMaxPacketSize, desc->bInterval);
403647a1685fSDinh Nguyen
403747a1685fSDinh Nguyen /* not to be called for EP0 */
40388c3d6092SVahram Aharonyan if (index == 0) {
40398c3d6092SVahram Aharonyan dev_err(hsotg->dev, "%s: called for EP 0\n", __func__);
40408c3d6092SVahram Aharonyan return -EINVAL;
40418c3d6092SVahram Aharonyan }
404247a1685fSDinh Nguyen
404347a1685fSDinh Nguyen dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
404447a1685fSDinh Nguyen if (dir_in != hs_ep->dir_in) {
404547a1685fSDinh Nguyen dev_err(hsotg->dev, "%s: direction mismatch!\n", __func__);
404647a1685fSDinh Nguyen return -EINVAL;
404747a1685fSDinh Nguyen }
404847a1685fSDinh Nguyen
4049729cac69SMinas Harutyunyan ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
405047a1685fSDinh Nguyen mps = usb_endpoint_maxp(desc);
4051ee2c40deSVardan Mikayelyan mc = usb_endpoint_maxp_mult(desc);
405247a1685fSDinh Nguyen
4053729cac69SMinas Harutyunyan /* ISOC IN in DDMA supported bInterval up to 10 */
4054729cac69SMinas Harutyunyan if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC &&
4055729cac69SMinas Harutyunyan dir_in && desc->bInterval > 10) {
4056729cac69SMinas Harutyunyan dev_err(hsotg->dev,
4057729cac69SMinas Harutyunyan "%s: ISOC IN, DDMA: bInterval>10 not supported!\n", __func__);
4058729cac69SMinas Harutyunyan return -EINVAL;
4059729cac69SMinas Harutyunyan }
4060729cac69SMinas Harutyunyan
4061729cac69SMinas Harutyunyan /* High bandwidth ISOC OUT in DDMA not supported */
4062729cac69SMinas Harutyunyan if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC &&
4063729cac69SMinas Harutyunyan !dir_in && mc > 1) {
4064729cac69SMinas Harutyunyan dev_err(hsotg->dev,
4065729cac69SMinas Harutyunyan "%s: ISOC OUT, DDMA: HB not supported!\n", __func__);
4066729cac69SMinas Harutyunyan return -EINVAL;
4067729cac69SMinas Harutyunyan }
4068729cac69SMinas Harutyunyan
40691f91b4ccSFelipe Balbi /* note, we handle this here instead of dwc2_hsotg_set_ep_maxpacket */
407047a1685fSDinh Nguyen
407147a1685fSDinh Nguyen epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
4072f25c42b8SGevorg Sahakyan epctrl = dwc2_readl(hsotg, epctrl_reg);
407347a1685fSDinh Nguyen
407447a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n",
407547a1685fSDinh Nguyen __func__, epctrl, epctrl_reg);
407647a1685fSDinh Nguyen
407754f37f56SMinas Harutyunyan if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC)
407854f37f56SMinas Harutyunyan desc_num = MAX_DMA_DESC_NUM_HS_ISOC;
407954f37f56SMinas Harutyunyan else
408054f37f56SMinas Harutyunyan desc_num = MAX_DMA_DESC_NUM_GENERIC;
408154f37f56SMinas Harutyunyan
40825f54c54bSVahram Aharonyan /* Allocate DMA descriptor chain for non-ctrl endpoints */
40839383e084SVardan Mikayelyan if (using_desc_dma(hsotg) && !hs_ep->desc_list) {
40849383e084SVardan Mikayelyan hs_ep->desc_list = dmam_alloc_coherent(hsotg->dev,
408554f37f56SMinas Harutyunyan desc_num * sizeof(struct dwc2_dma_desc),
408686e881e7SMarek Szyprowski &hs_ep->desc_list_dma, GFP_ATOMIC);
40875f54c54bSVahram Aharonyan if (!hs_ep->desc_list) {
40885f54c54bSVahram Aharonyan ret = -ENOMEM;
40895f54c54bSVahram Aharonyan goto error2;
40905f54c54bSVahram Aharonyan }
40915f54c54bSVahram Aharonyan }
40925f54c54bSVahram Aharonyan
409347a1685fSDinh Nguyen spin_lock_irqsave(&hsotg->lock, flags);
409447a1685fSDinh Nguyen
409547a1685fSDinh Nguyen epctrl &= ~(DXEPCTL_EPTYPE_MASK | DXEPCTL_MPS_MASK);
409647a1685fSDinh Nguyen epctrl |= DXEPCTL_MPS(mps);
409747a1685fSDinh Nguyen
409847a1685fSDinh Nguyen /*
409947a1685fSDinh Nguyen * mark the endpoint as active, otherwise the core may ignore
410047a1685fSDinh Nguyen * transactions entirely for this endpoint
410147a1685fSDinh Nguyen */
410247a1685fSDinh Nguyen epctrl |= DXEPCTL_USBACTEP;
410347a1685fSDinh Nguyen
410447a1685fSDinh Nguyen /* update the endpoint state */
4105ee2c40deSVardan Mikayelyan dwc2_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps, mc, dir_in);
410647a1685fSDinh Nguyen
410747a1685fSDinh Nguyen /* default, set to non-periodic */
410847a1685fSDinh Nguyen hs_ep->isochronous = 0;
410947a1685fSDinh Nguyen hs_ep->periodic = 0;
411047a1685fSDinh Nguyen hs_ep->halted = 0;
4111b833ce15SMinas Harutyunyan hs_ep->wedged = 0;
411247a1685fSDinh Nguyen hs_ep->interval = desc->bInterval;
411347a1685fSDinh Nguyen
4114729cac69SMinas Harutyunyan switch (ep_type) {
411547a1685fSDinh Nguyen case USB_ENDPOINT_XFER_ISOC:
411647a1685fSDinh Nguyen epctrl |= DXEPCTL_EPTYPE_ISO;
411747a1685fSDinh Nguyen epctrl |= DXEPCTL_SETEVENFR;
411847a1685fSDinh Nguyen hs_ep->isochronous = 1;
4119142bd33fSVardan Mikayelyan hs_ep->interval = 1 << (desc->bInterval - 1);
4120837e9f00SVardan Mikayelyan hs_ep->target_frame = TARGET_FRAME_INITIAL;
4121ab7d2192SVahram Aharonyan hs_ep->next_desc = 0;
4122729cac69SMinas Harutyunyan hs_ep->compl_desc = 0;
4123837e9f00SVardan Mikayelyan if (dir_in) {
412447a1685fSDinh Nguyen hs_ep->periodic = 1;
4125f25c42b8SGevorg Sahakyan mask = dwc2_readl(hsotg, DIEPMSK);
4126837e9f00SVardan Mikayelyan mask |= DIEPMSK_NAKMSK;
4127f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, mask, DIEPMSK);
4128837e9f00SVardan Mikayelyan } else {
412991bb163eSMinas Harutyunyan epctrl |= DXEPCTL_SNAK;
4130f25c42b8SGevorg Sahakyan mask = dwc2_readl(hsotg, DOEPMSK);
4131837e9f00SVardan Mikayelyan mask |= DOEPMSK_OUTTKNEPDISMSK;
4132f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, mask, DOEPMSK);
4133837e9f00SVardan Mikayelyan }
413447a1685fSDinh Nguyen break;
413547a1685fSDinh Nguyen
413647a1685fSDinh Nguyen case USB_ENDPOINT_XFER_BULK:
413747a1685fSDinh Nguyen epctrl |= DXEPCTL_EPTYPE_BULK;
413847a1685fSDinh Nguyen break;
413947a1685fSDinh Nguyen
414047a1685fSDinh Nguyen case USB_ENDPOINT_XFER_INT:
4141b203d0a2SRobert Baldyga if (dir_in)
414247a1685fSDinh Nguyen hs_ep->periodic = 1;
414347a1685fSDinh Nguyen
4144142bd33fSVardan Mikayelyan if (hsotg->gadget.speed == USB_SPEED_HIGH)
4145142bd33fSVardan Mikayelyan hs_ep->interval = 1 << (desc->bInterval - 1);
4146142bd33fSVardan Mikayelyan
414747a1685fSDinh Nguyen epctrl |= DXEPCTL_EPTYPE_INTERRUPT;
414847a1685fSDinh Nguyen break;
414947a1685fSDinh Nguyen
415047a1685fSDinh Nguyen case USB_ENDPOINT_XFER_CONTROL:
415147a1685fSDinh Nguyen epctrl |= DXEPCTL_EPTYPE_CONTROL;
415247a1685fSDinh Nguyen break;
415347a1685fSDinh Nguyen }
415447a1685fSDinh Nguyen
415547a1685fSDinh Nguyen /*
415647a1685fSDinh Nguyen * if the hardware has dedicated fifos, we must give each IN EP
415747a1685fSDinh Nguyen * a unique tx-fifo even if it is non-periodic.
415847a1685fSDinh Nguyen */
415921f3bb52SRobert Baldyga if (dir_in && hsotg->dedicated_fifos) {
4160644139f8SJohn Keeping unsigned fifo_count = dwc2_hsotg_tx_fifo_count(hsotg);
4161ca4c55adSMian Yousaf Kaukab u32 fifo_index = 0;
4162ca4c55adSMian Yousaf Kaukab u32 fifo_size = UINT_MAX;
41639da51974SJohn Youn
4164b203d0a2SRobert Baldyga size = hs_ep->ep.maxpacket * hs_ep->mc;
4165644139f8SJohn Keeping for (i = 1; i <= fifo_count; ++i) {
4166b203d0a2SRobert Baldyga if (hsotg->fifo_map & (1 << i))
4167b203d0a2SRobert Baldyga continue;
4168f25c42b8SGevorg Sahakyan val = dwc2_readl(hsotg, DPTXFSIZN(i));
4169b203d0a2SRobert Baldyga val = (val >> FIFOSIZE_DEPTH_SHIFT) * 4;
4170b203d0a2SRobert Baldyga if (val < size)
4171b203d0a2SRobert Baldyga continue;
4172ca4c55adSMian Yousaf Kaukab /* Search for smallest acceptable fifo */
4173ca4c55adSMian Yousaf Kaukab if (val < fifo_size) {
4174ca4c55adSMian Yousaf Kaukab fifo_size = val;
4175ca4c55adSMian Yousaf Kaukab fifo_index = i;
4176b203d0a2SRobert Baldyga }
4177ca4c55adSMian Yousaf Kaukab }
4178ca4c55adSMian Yousaf Kaukab if (!fifo_index) {
41795f2196bdSMian Yousaf Kaukab dev_err(hsotg->dev,
41805f2196bdSMian Yousaf Kaukab "%s: No suitable fifo found\n", __func__);
4181b585a48bSSudip Mukherjee ret = -ENOMEM;
41825f54c54bSVahram Aharonyan goto error1;
4183b585a48bSSudip Mukherjee }
418497311c8fSMinas Harutyunyan epctrl &= ~(DXEPCTL_TXFNUM_LIMIT << DXEPCTL_TXFNUM_SHIFT);
4185ca4c55adSMian Yousaf Kaukab hsotg->fifo_map |= 1 << fifo_index;
4186ca4c55adSMian Yousaf Kaukab epctrl |= DXEPCTL_TXFNUM(fifo_index);
4187ca4c55adSMian Yousaf Kaukab hs_ep->fifo_index = fifo_index;
4188ca4c55adSMian Yousaf Kaukab hs_ep->fifo_size = fifo_size;
4189b203d0a2SRobert Baldyga }
419047a1685fSDinh Nguyen
419147a1685fSDinh Nguyen /* for non control endpoints, set PID to D0 */
4192837e9f00SVardan Mikayelyan if (index && !hs_ep->isochronous)
419347a1685fSDinh Nguyen epctrl |= DXEPCTL_SETD0PID;
419447a1685fSDinh Nguyen
41955295322aSArtur Petrosyan /* WA for Full speed ISOC IN in DDMA mode.
41965295322aSArtur Petrosyan * By Clear NAK status of EP, core will send ZLP
41975295322aSArtur Petrosyan * to IN token and assert NAK interrupt relying
41985295322aSArtur Petrosyan * on TxFIFO status only
41995295322aSArtur Petrosyan */
42005295322aSArtur Petrosyan
42015295322aSArtur Petrosyan if (hsotg->gadget.speed == USB_SPEED_FULL &&
42025295322aSArtur Petrosyan hs_ep->isochronous && dir_in) {
42035295322aSArtur Petrosyan /* The WA applies only to core versions from 2.72a
42045295322aSArtur Petrosyan * to 4.00a (including both). Also for FS_IOT_1.00a
42055295322aSArtur Petrosyan * and HS_IOT_1.00a.
42065295322aSArtur Petrosyan */
4207f25c42b8SGevorg Sahakyan u32 gsnpsid = dwc2_readl(hsotg, GSNPSID);
42085295322aSArtur Petrosyan
42095295322aSArtur Petrosyan if ((gsnpsid >= DWC2_CORE_REV_2_72a &&
42105295322aSArtur Petrosyan gsnpsid <= DWC2_CORE_REV_4_00a) ||
42115295322aSArtur Petrosyan gsnpsid == DWC2_FS_IOT_REV_1_00a ||
42125295322aSArtur Petrosyan gsnpsid == DWC2_HS_IOT_REV_1_00a)
42135295322aSArtur Petrosyan epctrl |= DXEPCTL_CNAK;
42145295322aSArtur Petrosyan }
42155295322aSArtur Petrosyan
421647a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: write DxEPCTL=0x%08x\n",
421747a1685fSDinh Nguyen __func__, epctrl);
421847a1685fSDinh Nguyen
4219f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, epctrl, epctrl_reg);
422047a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x\n",
4221f25c42b8SGevorg Sahakyan __func__, dwc2_readl(hsotg, epctrl_reg));
422247a1685fSDinh Nguyen
422347a1685fSDinh Nguyen /* enable the endpoint interrupt */
42241f91b4ccSFelipe Balbi dwc2_hsotg_ctrl_epint(hsotg, index, dir_in, 1);
422547a1685fSDinh Nguyen
42265f54c54bSVahram Aharonyan error1:
422747a1685fSDinh Nguyen spin_unlock_irqrestore(&hsotg->lock, flags);
42285f54c54bSVahram Aharonyan
42295f54c54bSVahram Aharonyan error2:
42305f54c54bSVahram Aharonyan if (ret && using_desc_dma(hsotg) && hs_ep->desc_list) {
423154f37f56SMinas Harutyunyan dmam_free_coherent(hsotg->dev, desc_num *
42325f54c54bSVahram Aharonyan sizeof(struct dwc2_dma_desc),
42335f54c54bSVahram Aharonyan hs_ep->desc_list, hs_ep->desc_list_dma);
42345f54c54bSVahram Aharonyan hs_ep->desc_list = NULL;
42355f54c54bSVahram Aharonyan }
42365f54c54bSVahram Aharonyan
423747a1685fSDinh Nguyen return ret;
423847a1685fSDinh Nguyen }
423947a1685fSDinh Nguyen
424047a1685fSDinh Nguyen /**
42411f91b4ccSFelipe Balbi * dwc2_hsotg_ep_disable - disable given endpoint
424247a1685fSDinh Nguyen * @ep: The endpoint to disable.
424347a1685fSDinh Nguyen */
dwc2_hsotg_ep_disable(struct usb_ep * ep)42441f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
424547a1685fSDinh Nguyen {
42461f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
4247941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = hs_ep->parent;
424847a1685fSDinh Nguyen int dir_in = hs_ep->dir_in;
424947a1685fSDinh Nguyen int index = hs_ep->index;
425047a1685fSDinh Nguyen u32 epctrl_reg;
425147a1685fSDinh Nguyen u32 ctrl;
425247a1685fSDinh Nguyen
42531e011293SMarek Szyprowski dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep);
425447a1685fSDinh Nguyen
4255c6f5c050SMian Yousaf Kaukab if (ep == &hsotg->eps_out[0]->ep) {
425647a1685fSDinh Nguyen dev_err(hsotg->dev, "%s: called for ep0\n", __func__);
425747a1685fSDinh Nguyen return -EINVAL;
425847a1685fSDinh Nguyen }
425947a1685fSDinh Nguyen
42609b481092SJohn Stultz if (hsotg->op_state != OTG_STATE_B_PERIPHERAL) {
42619b481092SJohn Stultz dev_err(hsotg->dev, "%s: called in host mode?\n", __func__);
42629b481092SJohn Stultz return -EINVAL;
42639b481092SJohn Stultz }
42649b481092SJohn Stultz
426547a1685fSDinh Nguyen epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
426647a1685fSDinh Nguyen
4267f25c42b8SGevorg Sahakyan ctrl = dwc2_readl(hsotg, epctrl_reg);
4268a4f82771SVahram Aharonyan
4269a4f82771SVahram Aharonyan if (ctrl & DXEPCTL_EPENA)
4270a4f82771SVahram Aharonyan dwc2_hsotg_ep_stop_xfr(hsotg, hs_ep);
4271a4f82771SVahram Aharonyan
427247a1685fSDinh Nguyen ctrl &= ~DXEPCTL_EPENA;
427347a1685fSDinh Nguyen ctrl &= ~DXEPCTL_USBACTEP;
427447a1685fSDinh Nguyen ctrl |= DXEPCTL_SNAK;
427547a1685fSDinh Nguyen
427647a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
4277f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ctrl, epctrl_reg);
427847a1685fSDinh Nguyen
427947a1685fSDinh Nguyen /* disable endpoint interrupts */
42801f91b4ccSFelipe Balbi dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0);
428147a1685fSDinh Nguyen
42821141ea01SMian Yousaf Kaukab /* terminate all requests with shutdown */
42831141ea01SMian Yousaf Kaukab kill_all_requests(hsotg, hs_ep, -ESHUTDOWN);
42841141ea01SMian Yousaf Kaukab
42851c07b20eSRobert Baldyga hsotg->fifo_map &= ~(1 << hs_ep->fifo_index);
42861c07b20eSRobert Baldyga hs_ep->fifo_index = 0;
42871c07b20eSRobert Baldyga hs_ep->fifo_size = 0;
42881c07b20eSRobert Baldyga
428947a1685fSDinh Nguyen return 0;
429047a1685fSDinh Nguyen }
429147a1685fSDinh Nguyen
dwc2_hsotg_ep_disable_lock(struct usb_ep * ep)42924fe4f9feSMinas Harutyunyan static int dwc2_hsotg_ep_disable_lock(struct usb_ep *ep)
42934fe4f9feSMinas Harutyunyan {
42944fe4f9feSMinas Harutyunyan struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
42954fe4f9feSMinas Harutyunyan struct dwc2_hsotg *hsotg = hs_ep->parent;
42964fe4f9feSMinas Harutyunyan unsigned long flags;
42974fe4f9feSMinas Harutyunyan int ret;
42984fe4f9feSMinas Harutyunyan
42994fe4f9feSMinas Harutyunyan spin_lock_irqsave(&hsotg->lock, flags);
43004fe4f9feSMinas Harutyunyan ret = dwc2_hsotg_ep_disable(ep);
43014fe4f9feSMinas Harutyunyan spin_unlock_irqrestore(&hsotg->lock, flags);
43024fe4f9feSMinas Harutyunyan return ret;
43034fe4f9feSMinas Harutyunyan }
43044fe4f9feSMinas Harutyunyan
430547a1685fSDinh Nguyen /**
430647a1685fSDinh Nguyen * on_list - check request is on the given endpoint
430747a1685fSDinh Nguyen * @ep: The endpoint to check.
430847a1685fSDinh Nguyen * @test: The request to test if it is on the endpoint.
430947a1685fSDinh Nguyen */
on_list(struct dwc2_hsotg_ep * ep,struct dwc2_hsotg_req * test)43101f91b4ccSFelipe Balbi static bool on_list(struct dwc2_hsotg_ep *ep, struct dwc2_hsotg_req *test)
431147a1685fSDinh Nguyen {
43121f91b4ccSFelipe Balbi struct dwc2_hsotg_req *req, *treq;
431347a1685fSDinh Nguyen
431447a1685fSDinh Nguyen list_for_each_entry_safe(req, treq, &ep->queue, queue) {
431547a1685fSDinh Nguyen if (req == test)
431647a1685fSDinh Nguyen return true;
431747a1685fSDinh Nguyen }
431847a1685fSDinh Nguyen
431947a1685fSDinh Nguyen return false;
432047a1685fSDinh Nguyen }
432147a1685fSDinh Nguyen
432247a1685fSDinh Nguyen /**
43231f91b4ccSFelipe Balbi * dwc2_hsotg_ep_dequeue - dequeue given endpoint
432447a1685fSDinh Nguyen * @ep: The endpoint to dequeue.
432547a1685fSDinh Nguyen * @req: The request to be removed from a queue.
432647a1685fSDinh Nguyen */
dwc2_hsotg_ep_dequeue(struct usb_ep * ep,struct usb_request * req)43271f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
432847a1685fSDinh Nguyen {
43291f91b4ccSFelipe Balbi struct dwc2_hsotg_req *hs_req = our_req(req);
43301f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
4331941fcce4SDinh Nguyen struct dwc2_hsotg *hs = hs_ep->parent;
433247a1685fSDinh Nguyen unsigned long flags;
433347a1685fSDinh Nguyen
43341e011293SMarek Szyprowski dev_dbg(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
433547a1685fSDinh Nguyen
433647a1685fSDinh Nguyen spin_lock_irqsave(&hs->lock, flags);
433747a1685fSDinh Nguyen
433847a1685fSDinh Nguyen if (!on_list(hs_ep, hs_req)) {
433947a1685fSDinh Nguyen spin_unlock_irqrestore(&hs->lock, flags);
434047a1685fSDinh Nguyen return -EINVAL;
434147a1685fSDinh Nguyen }
434247a1685fSDinh Nguyen
4343c524dd5fSMian Yousaf Kaukab /* Dequeue already started request */
4344c524dd5fSMian Yousaf Kaukab if (req == &hs_ep->req->req)
4345c524dd5fSMian Yousaf Kaukab dwc2_hsotg_ep_stop_xfr(hs, hs_ep);
4346c524dd5fSMian Yousaf Kaukab
43471f91b4ccSFelipe Balbi dwc2_hsotg_complete_request(hs, hs_ep, hs_req, -ECONNRESET);
434847a1685fSDinh Nguyen spin_unlock_irqrestore(&hs->lock, flags);
434947a1685fSDinh Nguyen
435047a1685fSDinh Nguyen return 0;
435147a1685fSDinh Nguyen }
435247a1685fSDinh Nguyen
435347a1685fSDinh Nguyen /**
4354b833ce15SMinas Harutyunyan * dwc2_gadget_ep_set_wedge - set wedge on a given endpoint
4355b833ce15SMinas Harutyunyan * @ep: The endpoint to be wedged.
4356b833ce15SMinas Harutyunyan *
4357b833ce15SMinas Harutyunyan */
dwc2_gadget_ep_set_wedge(struct usb_ep * ep)4358b833ce15SMinas Harutyunyan static int dwc2_gadget_ep_set_wedge(struct usb_ep *ep)
4359b833ce15SMinas Harutyunyan {
4360b833ce15SMinas Harutyunyan struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
4361b833ce15SMinas Harutyunyan struct dwc2_hsotg *hs = hs_ep->parent;
4362b833ce15SMinas Harutyunyan
4363b833ce15SMinas Harutyunyan unsigned long flags;
4364b833ce15SMinas Harutyunyan int ret;
4365b833ce15SMinas Harutyunyan
4366b833ce15SMinas Harutyunyan spin_lock_irqsave(&hs->lock, flags);
4367b833ce15SMinas Harutyunyan hs_ep->wedged = 1;
4368b833ce15SMinas Harutyunyan ret = dwc2_hsotg_ep_sethalt(ep, 1, false);
4369b833ce15SMinas Harutyunyan spin_unlock_irqrestore(&hs->lock, flags);
4370b833ce15SMinas Harutyunyan
4371b833ce15SMinas Harutyunyan return ret;
4372b833ce15SMinas Harutyunyan }
4373b833ce15SMinas Harutyunyan
4374b833ce15SMinas Harutyunyan /**
43751f91b4ccSFelipe Balbi * dwc2_hsotg_ep_sethalt - set halt on a given endpoint
437647a1685fSDinh Nguyen * @ep: The endpoint to set halt.
437747a1685fSDinh Nguyen * @value: Set or unset the halt.
437851da43b5SVahram Aharonyan * @now: If true, stall the endpoint now. Otherwise return -EAGAIN if
437951da43b5SVahram Aharonyan * the endpoint is busy processing requests.
438051da43b5SVahram Aharonyan *
438151da43b5SVahram Aharonyan * We need to stall the endpoint immediately if request comes from set_feature
438251da43b5SVahram Aharonyan * protocol command handler.
438347a1685fSDinh Nguyen */
dwc2_hsotg_ep_sethalt(struct usb_ep * ep,int value,bool now)438451da43b5SVahram Aharonyan static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now)
438547a1685fSDinh Nguyen {
43861f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
4387941fcce4SDinh Nguyen struct dwc2_hsotg *hs = hs_ep->parent;
438847a1685fSDinh Nguyen int index = hs_ep->index;
438947a1685fSDinh Nguyen u32 epreg;
439047a1685fSDinh Nguyen u32 epctl;
439147a1685fSDinh Nguyen u32 xfertype;
439247a1685fSDinh Nguyen
439347a1685fSDinh Nguyen dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value);
439447a1685fSDinh Nguyen
439547a1685fSDinh Nguyen if (index == 0) {
439647a1685fSDinh Nguyen if (value)
43971f91b4ccSFelipe Balbi dwc2_hsotg_stall_ep0(hs);
439847a1685fSDinh Nguyen else
439947a1685fSDinh Nguyen dev_warn(hs->dev,
440047a1685fSDinh Nguyen "%s: can't clear halt on ep0\n", __func__);
440147a1685fSDinh Nguyen return 0;
440247a1685fSDinh Nguyen }
440347a1685fSDinh Nguyen
440415186f10SVahram Aharonyan if (hs_ep->isochronous) {
440515186f10SVahram Aharonyan dev_err(hs->dev, "%s is Isochronous Endpoint\n", ep->name);
440615186f10SVahram Aharonyan return -EINVAL;
440715186f10SVahram Aharonyan }
440815186f10SVahram Aharonyan
440951da43b5SVahram Aharonyan if (!now && value && !list_empty(&hs_ep->queue)) {
441051da43b5SVahram Aharonyan dev_dbg(hs->dev, "%s request is pending, cannot halt\n",
441151da43b5SVahram Aharonyan ep->name);
441251da43b5SVahram Aharonyan return -EAGAIN;
441351da43b5SVahram Aharonyan }
441451da43b5SVahram Aharonyan
4415c6f5c050SMian Yousaf Kaukab if (hs_ep->dir_in) {
441647a1685fSDinh Nguyen epreg = DIEPCTL(index);
4417f25c42b8SGevorg Sahakyan epctl = dwc2_readl(hs, epreg);
441847a1685fSDinh Nguyen
441947a1685fSDinh Nguyen if (value) {
44205a350d53SFelipe Balbi epctl |= DXEPCTL_STALL | DXEPCTL_SNAK;
442147a1685fSDinh Nguyen if (epctl & DXEPCTL_EPENA)
442247a1685fSDinh Nguyen epctl |= DXEPCTL_EPDIS;
442347a1685fSDinh Nguyen } else {
442447a1685fSDinh Nguyen epctl &= ~DXEPCTL_STALL;
4425b833ce15SMinas Harutyunyan hs_ep->wedged = 0;
442647a1685fSDinh Nguyen xfertype = epctl & DXEPCTL_EPTYPE_MASK;
442747a1685fSDinh Nguyen if (xfertype == DXEPCTL_EPTYPE_BULK ||
442847a1685fSDinh Nguyen xfertype == DXEPCTL_EPTYPE_INTERRUPT)
442947a1685fSDinh Nguyen epctl |= DXEPCTL_SETD0PID;
443047a1685fSDinh Nguyen }
4431f25c42b8SGevorg Sahakyan dwc2_writel(hs, epctl, epreg);
4432c6f5c050SMian Yousaf Kaukab } else {
443347a1685fSDinh Nguyen epreg = DOEPCTL(index);
4434f25c42b8SGevorg Sahakyan epctl = dwc2_readl(hs, epreg);
443547a1685fSDinh Nguyen
443634c0887fSJohn Youn if (value) {
4437fecb3a17SMinas Harutyunyan /* Unmask GOUTNAKEFF interrupt */
4438fecb3a17SMinas Harutyunyan dwc2_hsotg_en_gsint(hs, GINTSTS_GOUTNAKEFF);
4439fecb3a17SMinas Harutyunyan
44406070636cSMinas Harutyunyan if (!(dwc2_readl(hs, GINTSTS) & GINTSTS_GOUTNAKEFF))
44416070636cSMinas Harutyunyan dwc2_set_bit(hs, DCTL, DCTL_SGOUTNAK);
44426070636cSMinas Harutyunyan // STALL bit will be set in GOUTNAKEFF interrupt handler
444334c0887fSJohn Youn } else {
444447a1685fSDinh Nguyen epctl &= ~DXEPCTL_STALL;
4445b833ce15SMinas Harutyunyan hs_ep->wedged = 0;
444647a1685fSDinh Nguyen xfertype = epctl & DXEPCTL_EPTYPE_MASK;
444747a1685fSDinh Nguyen if (xfertype == DXEPCTL_EPTYPE_BULK ||
444847a1685fSDinh Nguyen xfertype == DXEPCTL_EPTYPE_INTERRUPT)
444947a1685fSDinh Nguyen epctl |= DXEPCTL_SETD0PID;
4450f25c42b8SGevorg Sahakyan dwc2_writel(hs, epctl, epreg);
4451c6f5c050SMian Yousaf Kaukab }
44526070636cSMinas Harutyunyan }
445347a1685fSDinh Nguyen
445447a1685fSDinh Nguyen hs_ep->halted = value;
445547a1685fSDinh Nguyen return 0;
445647a1685fSDinh Nguyen }
445747a1685fSDinh Nguyen
445847a1685fSDinh Nguyen /**
44591f91b4ccSFelipe Balbi * dwc2_hsotg_ep_sethalt_lock - set halt on a given endpoint with lock held
446047a1685fSDinh Nguyen * @ep: The endpoint to set halt.
446147a1685fSDinh Nguyen * @value: Set or unset the halt.
446247a1685fSDinh Nguyen */
dwc2_hsotg_ep_sethalt_lock(struct usb_ep * ep,int value)44631f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value)
446447a1685fSDinh Nguyen {
44651f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
4466941fcce4SDinh Nguyen struct dwc2_hsotg *hs = hs_ep->parent;
44678879904bSJohan Hovold unsigned long flags;
44688879904bSJohan Hovold int ret;
446947a1685fSDinh Nguyen
447047a1685fSDinh Nguyen spin_lock_irqsave(&hs->lock, flags);
447151da43b5SVahram Aharonyan ret = dwc2_hsotg_ep_sethalt(ep, value, false);
447247a1685fSDinh Nguyen spin_unlock_irqrestore(&hs->lock, flags);
447347a1685fSDinh Nguyen
447447a1685fSDinh Nguyen return ret;
447547a1685fSDinh Nguyen }
447647a1685fSDinh Nguyen
4477ebce561aSBhumika Goyal static const struct usb_ep_ops dwc2_hsotg_ep_ops = {
44781f91b4ccSFelipe Balbi .enable = dwc2_hsotg_ep_enable,
44794fe4f9feSMinas Harutyunyan .disable = dwc2_hsotg_ep_disable_lock,
44801f91b4ccSFelipe Balbi .alloc_request = dwc2_hsotg_ep_alloc_request,
44811f91b4ccSFelipe Balbi .free_request = dwc2_hsotg_ep_free_request,
44821f91b4ccSFelipe Balbi .queue = dwc2_hsotg_ep_queue_lock,
44831f91b4ccSFelipe Balbi .dequeue = dwc2_hsotg_ep_dequeue,
44841f91b4ccSFelipe Balbi .set_halt = dwc2_hsotg_ep_sethalt_lock,
4485b833ce15SMinas Harutyunyan .set_wedge = dwc2_gadget_ep_set_wedge,
448647a1685fSDinh Nguyen /* note, don't believe we have any call for the fifo routines */
448747a1685fSDinh Nguyen };
448847a1685fSDinh Nguyen
448947a1685fSDinh Nguyen /**
44909da51974SJohn Youn * dwc2_hsotg_init - initialize the usb core
449147a1685fSDinh Nguyen * @hsotg: The driver state
449247a1685fSDinh Nguyen */
dwc2_hsotg_init(struct dwc2_hsotg * hsotg)44931f91b4ccSFelipe Balbi static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg)
449447a1685fSDinh Nguyen {
449547a1685fSDinh Nguyen /* unmask subset of endpoint interrupts */
449647a1685fSDinh Nguyen
4497f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
449847a1685fSDinh Nguyen DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK,
4499f25c42b8SGevorg Sahakyan DIEPMSK);
450047a1685fSDinh Nguyen
4501f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, DOEPMSK_SETUPMSK | DOEPMSK_AHBERRMSK |
450247a1685fSDinh Nguyen DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK,
4503f25c42b8SGevorg Sahakyan DOEPMSK);
450447a1685fSDinh Nguyen
4505f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, 0, DAINTMSK);
450647a1685fSDinh Nguyen
450747a1685fSDinh Nguyen /* Be in disconnected state until gadget is registered */
4508f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);
450947a1685fSDinh Nguyen
451047a1685fSDinh Nguyen /* setup fifos */
451147a1685fSDinh Nguyen
451247a1685fSDinh Nguyen dev_dbg(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
4513f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, GRXFSIZ),
4514f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, GNPTXFSIZ));
451547a1685fSDinh Nguyen
45161f91b4ccSFelipe Balbi dwc2_hsotg_init_fifo(hsotg);
451747a1685fSDinh Nguyen
4518f5090044SGregory Herrero if (using_dma(hsotg))
4519f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, GAHBCFG, GAHBCFG_DMA_EN);
452047a1685fSDinh Nguyen }
452147a1685fSDinh Nguyen
452247a1685fSDinh Nguyen /**
45231f91b4ccSFelipe Balbi * dwc2_hsotg_udc_start - prepare the udc for work
452447a1685fSDinh Nguyen * @gadget: The usb gadget state
452547a1685fSDinh Nguyen * @driver: The usb gadget driver
452647a1685fSDinh Nguyen *
452747a1685fSDinh Nguyen * Perform initialization to prepare udc device and driver
452847a1685fSDinh Nguyen * to work.
452947a1685fSDinh Nguyen */
dwc2_hsotg_udc_start(struct usb_gadget * gadget,struct usb_gadget_driver * driver)45301f91b4ccSFelipe Balbi static int dwc2_hsotg_udc_start(struct usb_gadget *gadget,
453147a1685fSDinh Nguyen struct usb_gadget_driver *driver)
453247a1685fSDinh Nguyen {
4533941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = to_hsotg(gadget);
45345b9451f8SMarek Szyprowski unsigned long flags;
453547a1685fSDinh Nguyen int ret;
453647a1685fSDinh Nguyen
453747a1685fSDinh Nguyen if (!hsotg) {
453847a1685fSDinh Nguyen pr_err("%s: called with no device\n", __func__);
453947a1685fSDinh Nguyen return -ENODEV;
454047a1685fSDinh Nguyen }
454147a1685fSDinh Nguyen
454247a1685fSDinh Nguyen if (!driver) {
454347a1685fSDinh Nguyen dev_err(hsotg->dev, "%s: no driver\n", __func__);
454447a1685fSDinh Nguyen return -EINVAL;
454547a1685fSDinh Nguyen }
454647a1685fSDinh Nguyen
454747a1685fSDinh Nguyen if (driver->max_speed < USB_SPEED_FULL)
454847a1685fSDinh Nguyen dev_err(hsotg->dev, "%s: bad speed\n", __func__);
454947a1685fSDinh Nguyen
455047a1685fSDinh Nguyen if (!driver->setup) {
455147a1685fSDinh Nguyen dev_err(hsotg->dev, "%s: missing entry points\n", __func__);
455247a1685fSDinh Nguyen return -EINVAL;
455347a1685fSDinh Nguyen }
455447a1685fSDinh Nguyen
455547a1685fSDinh Nguyen WARN_ON(hsotg->driver);
455647a1685fSDinh Nguyen
455747a1685fSDinh Nguyen hsotg->driver = driver;
455847a1685fSDinh Nguyen hsotg->gadget.dev.of_node = hsotg->dev->of_node;
455947a1685fSDinh Nguyen hsotg->gadget.speed = USB_SPEED_UNKNOWN;
456047a1685fSDinh Nguyen
456150213832SFabrice Gasnier if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) {
456209a75e85SMarek Szyprowski ret = dwc2_lowlevel_hw_enable(hsotg);
456309a75e85SMarek Szyprowski if (ret)
456447a1685fSDinh Nguyen goto err;
456547a1685fSDinh Nguyen }
456647a1685fSDinh Nguyen
4567f6c01592SGregory Herrero if (!IS_ERR_OR_NULL(hsotg->uphy))
4568f6c01592SGregory Herrero otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget);
4569c816c47fSMarek Szyprowski
45705b9451f8SMarek Szyprowski spin_lock_irqsave(&hsotg->lock, flags);
4571d0f0ac56SJohn Youn if (dwc2_hw_is_device(hsotg)) {
45721f91b4ccSFelipe Balbi dwc2_hsotg_init(hsotg);
45731f91b4ccSFelipe Balbi dwc2_hsotg_core_init_disconnected(hsotg, false);
4574d0f0ac56SJohn Youn }
4575d0f0ac56SJohn Youn
4576dc6e69e6SMarek Szyprowski hsotg->enabled = 0;
45775b9451f8SMarek Szyprowski spin_unlock_irqrestore(&hsotg->lock, flags);
45785b9451f8SMarek Szyprowski
457910209abeSAndrzej Pietrasiewicz gadget->sg_supported = using_desc_dma(hsotg);
458047a1685fSDinh Nguyen dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name);
45815b9451f8SMarek Szyprowski
458247a1685fSDinh Nguyen return 0;
458347a1685fSDinh Nguyen
458447a1685fSDinh Nguyen err:
458547a1685fSDinh Nguyen hsotg->driver = NULL;
458647a1685fSDinh Nguyen return ret;
458747a1685fSDinh Nguyen }
458847a1685fSDinh Nguyen
458947a1685fSDinh Nguyen /**
45901f91b4ccSFelipe Balbi * dwc2_hsotg_udc_stop - stop the udc
459147a1685fSDinh Nguyen * @gadget: The usb gadget state
459247a1685fSDinh Nguyen *
459347a1685fSDinh Nguyen * Stop udc hw block and stay tunned for future transmissions
459447a1685fSDinh Nguyen */
dwc2_hsotg_udc_stop(struct usb_gadget * gadget)45951f91b4ccSFelipe Balbi static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget)
459647a1685fSDinh Nguyen {
4597941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = to_hsotg(gadget);
45988879904bSJohan Hovold unsigned long flags;
459947a1685fSDinh Nguyen int ep;
460047a1685fSDinh Nguyen
460147a1685fSDinh Nguyen if (!hsotg)
460247a1685fSDinh Nguyen return -ENODEV;
460347a1685fSDinh Nguyen
460447a1685fSDinh Nguyen /* all endpoints should be shutdown */
4605c6f5c050SMian Yousaf Kaukab for (ep = 1; ep < hsotg->num_of_eps; ep++) {
4606c6f5c050SMian Yousaf Kaukab if (hsotg->eps_in[ep])
46074fe4f9feSMinas Harutyunyan dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep);
4608c6f5c050SMian Yousaf Kaukab if (hsotg->eps_out[ep])
46094fe4f9feSMinas Harutyunyan dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep);
4610c6f5c050SMian Yousaf Kaukab }
461147a1685fSDinh Nguyen
461247a1685fSDinh Nguyen spin_lock_irqsave(&hsotg->lock, flags);
461347a1685fSDinh Nguyen
461447a1685fSDinh Nguyen hsotg->driver = NULL;
4615*da1b4506SFabrice Gasnier hsotg->gadget.dev.of_node = NULL;
461647a1685fSDinh Nguyen hsotg->gadget.speed = USB_SPEED_UNKNOWN;
4617dc6e69e6SMarek Szyprowski hsotg->enabled = 0;
461847a1685fSDinh Nguyen
461947a1685fSDinh Nguyen spin_unlock_irqrestore(&hsotg->lock, flags);
462047a1685fSDinh Nguyen
4621f6c01592SGregory Herrero if (!IS_ERR_OR_NULL(hsotg->uphy))
4622f6c01592SGregory Herrero otg_set_peripheral(hsotg->uphy->otg, NULL);
4623c816c47fSMarek Szyprowski
462450213832SFabrice Gasnier if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
462509a75e85SMarek Szyprowski dwc2_lowlevel_hw_disable(hsotg);
462647a1685fSDinh Nguyen
462747a1685fSDinh Nguyen return 0;
462847a1685fSDinh Nguyen }
462947a1685fSDinh Nguyen
463047a1685fSDinh Nguyen /**
46311f91b4ccSFelipe Balbi * dwc2_hsotg_gadget_getframe - read the frame number
463247a1685fSDinh Nguyen * @gadget: The usb gadget state
463347a1685fSDinh Nguyen *
463447a1685fSDinh Nguyen * Read the {micro} frame number
463547a1685fSDinh Nguyen */
dwc2_hsotg_gadget_getframe(struct usb_gadget * gadget)46361f91b4ccSFelipe Balbi static int dwc2_hsotg_gadget_getframe(struct usb_gadget *gadget)
463747a1685fSDinh Nguyen {
46381f91b4ccSFelipe Balbi return dwc2_hsotg_read_frameno(to_hsotg(gadget));
463947a1685fSDinh Nguyen }
464047a1685fSDinh Nguyen
464147a1685fSDinh Nguyen /**
46421a0808cbSJohn Keeping * dwc2_hsotg_set_selfpowered - set if device is self/bus powered
46431a0808cbSJohn Keeping * @gadget: The usb gadget state
46441a0808cbSJohn Keeping * @is_selfpowered: Whether the device is self-powered
46451a0808cbSJohn Keeping *
46461a0808cbSJohn Keeping * Set if the device is self or bus powered.
46471a0808cbSJohn Keeping */
dwc2_hsotg_set_selfpowered(struct usb_gadget * gadget,int is_selfpowered)46481a0808cbSJohn Keeping static int dwc2_hsotg_set_selfpowered(struct usb_gadget *gadget,
46491a0808cbSJohn Keeping int is_selfpowered)
46501a0808cbSJohn Keeping {
46511a0808cbSJohn Keeping struct dwc2_hsotg *hsotg = to_hsotg(gadget);
46521a0808cbSJohn Keeping unsigned long flags;
46531a0808cbSJohn Keeping
46541a0808cbSJohn Keeping spin_lock_irqsave(&hsotg->lock, flags);
46551a0808cbSJohn Keeping gadget->is_selfpowered = !!is_selfpowered;
46561a0808cbSJohn Keeping spin_unlock_irqrestore(&hsotg->lock, flags);
46571a0808cbSJohn Keeping
46581a0808cbSJohn Keeping return 0;
46591a0808cbSJohn Keeping }
46601a0808cbSJohn Keeping
46611a0808cbSJohn Keeping /**
46621f91b4ccSFelipe Balbi * dwc2_hsotg_pullup - connect/disconnect the USB PHY
466347a1685fSDinh Nguyen * @gadget: The usb gadget state
466447a1685fSDinh Nguyen * @is_on: Current state of the USB PHY
466547a1685fSDinh Nguyen *
466647a1685fSDinh Nguyen * Connect/Disconnect the USB PHY pullup
466747a1685fSDinh Nguyen */
dwc2_hsotg_pullup(struct usb_gadget * gadget,int is_on)46681f91b4ccSFelipe Balbi static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on)
466947a1685fSDinh Nguyen {
4670941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = to_hsotg(gadget);
46718879904bSJohan Hovold unsigned long flags;
467247a1685fSDinh Nguyen
467377ba9119SGregory Herrero dev_dbg(hsotg->dev, "%s: is_on: %d op_state: %d\n", __func__, is_on,
467477ba9119SGregory Herrero hsotg->op_state);
467577ba9119SGregory Herrero
467677ba9119SGregory Herrero /* Don't modify pullup state while in host mode */
467777ba9119SGregory Herrero if (hsotg->op_state != OTG_STATE_B_PERIPHERAL) {
467877ba9119SGregory Herrero hsotg->enabled = is_on;
467977ba9119SGregory Herrero return 0;
468077ba9119SGregory Herrero }
468147a1685fSDinh Nguyen
468247a1685fSDinh Nguyen spin_lock_irqsave(&hsotg->lock, flags);
468347a1685fSDinh Nguyen if (is_on) {
4684dc6e69e6SMarek Szyprowski hsotg->enabled = 1;
46851f91b4ccSFelipe Balbi dwc2_hsotg_core_init_disconnected(hsotg, false);
468666e77a24SRazmik Karapetyan /* Enable ACG feature in device mode,if supported */
468766e77a24SRazmik Karapetyan dwc2_enable_acg(hsotg);
46881f91b4ccSFelipe Balbi dwc2_hsotg_core_connect(hsotg);
468947a1685fSDinh Nguyen } else {
46901f91b4ccSFelipe Balbi dwc2_hsotg_core_disconnect(hsotg);
46911f91b4ccSFelipe Balbi dwc2_hsotg_disconnect(hsotg);
4692dc6e69e6SMarek Szyprowski hsotg->enabled = 0;
469347a1685fSDinh Nguyen }
469447a1685fSDinh Nguyen
469547a1685fSDinh Nguyen hsotg->gadget.speed = USB_SPEED_UNKNOWN;
469647a1685fSDinh Nguyen spin_unlock_irqrestore(&hsotg->lock, flags);
469747a1685fSDinh Nguyen
469847a1685fSDinh Nguyen return 0;
469947a1685fSDinh Nguyen }
470047a1685fSDinh Nguyen
dwc2_hsotg_vbus_session(struct usb_gadget * gadget,int is_active)47011f91b4ccSFelipe Balbi static int dwc2_hsotg_vbus_session(struct usb_gadget *gadget, int is_active)
470283d98223SGregory Herrero {
470383d98223SGregory Herrero struct dwc2_hsotg *hsotg = to_hsotg(gadget);
470483d98223SGregory Herrero unsigned long flags;
470583d98223SGregory Herrero
470683d98223SGregory Herrero dev_dbg(hsotg->dev, "%s: is_active: %d\n", __func__, is_active);
470783d98223SGregory Herrero spin_lock_irqsave(&hsotg->lock, flags);
470883d98223SGregory Herrero
470918b2b37cSGregory Herrero /*
4710c9c394abSArtur Petrosyan * If controller is in partial power down state, it must exit from
4711c9c394abSArtur Petrosyan * that state before being initialized / de-initialized
471218b2b37cSGregory Herrero */
4713c9c394abSArtur Petrosyan if (hsotg->lx_state == DWC2_L2 && hsotg->in_ppd)
4714c9c394abSArtur Petrosyan /*
4715c9c394abSArtur Petrosyan * No need to check the return value as
4716c9c394abSArtur Petrosyan * registers are not being restored.
4717c9c394abSArtur Petrosyan */
4718c9c394abSArtur Petrosyan dwc2_exit_partial_power_down(hsotg, 0, false);
4719065d3931SGregory Herrero
472061f7223bSGregory Herrero if (is_active) {
472161f7223bSGregory Herrero hsotg->op_state = OTG_STATE_B_PERIPHERAL;
472261f7223bSGregory Herrero
47231f91b4ccSFelipe Balbi dwc2_hsotg_core_init_disconnected(hsotg, false);
472466e77a24SRazmik Karapetyan if (hsotg->enabled) {
472566e77a24SRazmik Karapetyan /* Enable ACG feature in device mode,if supported */
472666e77a24SRazmik Karapetyan dwc2_enable_acg(hsotg);
47271f91b4ccSFelipe Balbi dwc2_hsotg_core_connect(hsotg);
472866e77a24SRazmik Karapetyan }
472983d98223SGregory Herrero } else {
47301f91b4ccSFelipe Balbi dwc2_hsotg_core_disconnect(hsotg);
47311f91b4ccSFelipe Balbi dwc2_hsotg_disconnect(hsotg);
473283d98223SGregory Herrero }
473383d98223SGregory Herrero
473483d98223SGregory Herrero spin_unlock_irqrestore(&hsotg->lock, flags);
473583d98223SGregory Herrero return 0;
473683d98223SGregory Herrero }
473783d98223SGregory Herrero
4738596d696aSGregory Herrero /**
47391f91b4ccSFelipe Balbi * dwc2_hsotg_vbus_draw - report bMaxPower field
4740596d696aSGregory Herrero * @gadget: The usb gadget state
4741596d696aSGregory Herrero * @mA: Amount of current
4742596d696aSGregory Herrero *
4743596d696aSGregory Herrero * Report how much power the device may consume to the phy.
4744596d696aSGregory Herrero */
dwc2_hsotg_vbus_draw(struct usb_gadget * gadget,unsigned int mA)47459da51974SJohn Youn static int dwc2_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
4746596d696aSGregory Herrero {
4747596d696aSGregory Herrero struct dwc2_hsotg *hsotg = to_hsotg(gadget);
4748596d696aSGregory Herrero
4749596d696aSGregory Herrero if (IS_ERR_OR_NULL(hsotg->uphy))
4750596d696aSGregory Herrero return -ENOTSUPP;
4751596d696aSGregory Herrero return usb_phy_set_power(hsotg->uphy, mA);
4752596d696aSGregory Herrero }
4753596d696aSGregory Herrero
dwc2_gadget_set_speed(struct usb_gadget * g,enum usb_device_speed speed)47545324bad6SArgishti Aleksanyan static void dwc2_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed speed)
47555324bad6SArgishti Aleksanyan {
47565324bad6SArgishti Aleksanyan struct dwc2_hsotg *hsotg = to_hsotg(g);
47575324bad6SArgishti Aleksanyan unsigned long flags;
47585324bad6SArgishti Aleksanyan
47595324bad6SArgishti Aleksanyan spin_lock_irqsave(&hsotg->lock, flags);
47605324bad6SArgishti Aleksanyan switch (speed) {
47615324bad6SArgishti Aleksanyan case USB_SPEED_HIGH:
47625324bad6SArgishti Aleksanyan hsotg->params.speed = DWC2_SPEED_PARAM_HIGH;
47635324bad6SArgishti Aleksanyan break;
47645324bad6SArgishti Aleksanyan case USB_SPEED_FULL:
47655324bad6SArgishti Aleksanyan hsotg->params.speed = DWC2_SPEED_PARAM_FULL;
47665324bad6SArgishti Aleksanyan break;
47675324bad6SArgishti Aleksanyan case USB_SPEED_LOW:
47685324bad6SArgishti Aleksanyan hsotg->params.speed = DWC2_SPEED_PARAM_LOW;
47695324bad6SArgishti Aleksanyan break;
47705324bad6SArgishti Aleksanyan default:
47715324bad6SArgishti Aleksanyan dev_err(hsotg->dev, "invalid speed (%d)\n", speed);
47725324bad6SArgishti Aleksanyan }
47735324bad6SArgishti Aleksanyan spin_unlock_irqrestore(&hsotg->lock, flags);
47745324bad6SArgishti Aleksanyan }
47755324bad6SArgishti Aleksanyan
47761f91b4ccSFelipe Balbi static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {
47771f91b4ccSFelipe Balbi .get_frame = dwc2_hsotg_gadget_getframe,
47781a0808cbSJohn Keeping .set_selfpowered = dwc2_hsotg_set_selfpowered,
47791f91b4ccSFelipe Balbi .udc_start = dwc2_hsotg_udc_start,
47801f91b4ccSFelipe Balbi .udc_stop = dwc2_hsotg_udc_stop,
47811f91b4ccSFelipe Balbi .pullup = dwc2_hsotg_pullup,
47825324bad6SArgishti Aleksanyan .udc_set_speed = dwc2_gadget_set_speed,
47831f91b4ccSFelipe Balbi .vbus_session = dwc2_hsotg_vbus_session,
47841f91b4ccSFelipe Balbi .vbus_draw = dwc2_hsotg_vbus_draw,
478547a1685fSDinh Nguyen };
478647a1685fSDinh Nguyen
478747a1685fSDinh Nguyen /**
47881f91b4ccSFelipe Balbi * dwc2_hsotg_initep - initialise a single endpoint
478947a1685fSDinh Nguyen * @hsotg: The device state.
479047a1685fSDinh Nguyen * @hs_ep: The endpoint to be initialised.
479147a1685fSDinh Nguyen * @epnum: The endpoint number
47926fb914d7SGrigor Tovmasyan * @dir_in: True if direction is in.
479347a1685fSDinh Nguyen *
479447a1685fSDinh Nguyen * Initialise the given endpoint (as part of the probe and device state
479547a1685fSDinh Nguyen * creation) to give to the gadget driver. Setup the endpoint name, any
479647a1685fSDinh Nguyen * direction information and other state that may be required.
479747a1685fSDinh Nguyen */
dwc2_hsotg_initep(struct dwc2_hsotg * hsotg,struct dwc2_hsotg_ep * hs_ep,int epnum,bool dir_in)47981f91b4ccSFelipe Balbi static void dwc2_hsotg_initep(struct dwc2_hsotg *hsotg,
47991f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep,
4800c6f5c050SMian Yousaf Kaukab int epnum,
4801c6f5c050SMian Yousaf Kaukab bool dir_in)
480247a1685fSDinh Nguyen {
480347a1685fSDinh Nguyen char *dir;
480447a1685fSDinh Nguyen
480547a1685fSDinh Nguyen if (epnum == 0)
480647a1685fSDinh Nguyen dir = "";
4807c6f5c050SMian Yousaf Kaukab else if (dir_in)
480847a1685fSDinh Nguyen dir = "in";
4809c6f5c050SMian Yousaf Kaukab else
4810c6f5c050SMian Yousaf Kaukab dir = "out";
481147a1685fSDinh Nguyen
4812c6f5c050SMian Yousaf Kaukab hs_ep->dir_in = dir_in;
481347a1685fSDinh Nguyen hs_ep->index = epnum;
481447a1685fSDinh Nguyen
481547a1685fSDinh Nguyen snprintf(hs_ep->name, sizeof(hs_ep->name), "ep%d%s", epnum, dir);
481647a1685fSDinh Nguyen
481747a1685fSDinh Nguyen INIT_LIST_HEAD(&hs_ep->queue);
481847a1685fSDinh Nguyen INIT_LIST_HEAD(&hs_ep->ep.ep_list);
481947a1685fSDinh Nguyen
482047a1685fSDinh Nguyen /* add to the list of endpoints known by the gadget driver */
482147a1685fSDinh Nguyen if (epnum)
482247a1685fSDinh Nguyen list_add_tail(&hs_ep->ep.ep_list, &hsotg->gadget.ep_list);
482347a1685fSDinh Nguyen
482447a1685fSDinh Nguyen hs_ep->parent = hsotg;
482547a1685fSDinh Nguyen hs_ep->ep.name = hs_ep->name;
482638e9002bSVardan Mikayelyan
482738e9002bSVardan Mikayelyan if (hsotg->params.speed == DWC2_SPEED_PARAM_LOW)
482838e9002bSVardan Mikayelyan usb_ep_set_maxpacket_limit(&hs_ep->ep, 8);
482938e9002bSVardan Mikayelyan else
483038e9002bSVardan Mikayelyan usb_ep_set_maxpacket_limit(&hs_ep->ep,
483138e9002bSVardan Mikayelyan epnum ? 1024 : EP0_MPS_LIMIT);
48321f91b4ccSFelipe Balbi hs_ep->ep.ops = &dwc2_hsotg_ep_ops;
483347a1685fSDinh Nguyen
48342954522fSRobert Baldyga if (epnum == 0) {
48352954522fSRobert Baldyga hs_ep->ep.caps.type_control = true;
48362954522fSRobert Baldyga } else {
483738e9002bSVardan Mikayelyan if (hsotg->params.speed != DWC2_SPEED_PARAM_LOW) {
48382954522fSRobert Baldyga hs_ep->ep.caps.type_iso = true;
48392954522fSRobert Baldyga hs_ep->ep.caps.type_bulk = true;
484038e9002bSVardan Mikayelyan }
48412954522fSRobert Baldyga hs_ep->ep.caps.type_int = true;
48422954522fSRobert Baldyga }
48432954522fSRobert Baldyga
48442954522fSRobert Baldyga if (dir_in)
48452954522fSRobert Baldyga hs_ep->ep.caps.dir_in = true;
48462954522fSRobert Baldyga else
48472954522fSRobert Baldyga hs_ep->ep.caps.dir_out = true;
48482954522fSRobert Baldyga
484947a1685fSDinh Nguyen /*
485047a1685fSDinh Nguyen * if we're using dma, we need to set the next-endpoint pointer
485147a1685fSDinh Nguyen * to be something valid.
485247a1685fSDinh Nguyen */
485347a1685fSDinh Nguyen
485447a1685fSDinh Nguyen if (using_dma(hsotg)) {
485547a1685fSDinh Nguyen u32 next = DXEPCTL_NEXTEP((epnum + 1) % 15);
48569da51974SJohn Youn
4857c6f5c050SMian Yousaf Kaukab if (dir_in)
4858f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, next, DIEPCTL(epnum));
4859c6f5c050SMian Yousaf Kaukab else
4860f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, next, DOEPCTL(epnum));
486147a1685fSDinh Nguyen }
486247a1685fSDinh Nguyen }
486347a1685fSDinh Nguyen
486447a1685fSDinh Nguyen /**
48651f91b4ccSFelipe Balbi * dwc2_hsotg_hw_cfg - read HW configuration registers
48666fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller
486747a1685fSDinh Nguyen *
486847a1685fSDinh Nguyen * Read the USB core HW configuration registers
486947a1685fSDinh Nguyen */
dwc2_hsotg_hw_cfg(struct dwc2_hsotg * hsotg)48701f91b4ccSFelipe Balbi static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
487147a1685fSDinh Nguyen {
4872c6f5c050SMian Yousaf Kaukab u32 cfg;
4873c6f5c050SMian Yousaf Kaukab u32 ep_type;
4874c6f5c050SMian Yousaf Kaukab u32 i;
4875c6f5c050SMian Yousaf Kaukab
487647a1685fSDinh Nguyen /* check hardware configuration */
487747a1685fSDinh Nguyen
487843e90349SJohn Youn hsotg->num_of_eps = hsotg->hw_params.num_dev_ep;
487943e90349SJohn Youn
4880c6f5c050SMian Yousaf Kaukab /* Add ep0 */
4881c6f5c050SMian Yousaf Kaukab hsotg->num_of_eps++;
488247a1685fSDinh Nguyen
4883b98866c2SJohn Youn hsotg->eps_in[0] = devm_kzalloc(hsotg->dev,
4884b98866c2SJohn Youn sizeof(struct dwc2_hsotg_ep),
4885c6f5c050SMian Yousaf Kaukab GFP_KERNEL);
4886c6f5c050SMian Yousaf Kaukab if (!hsotg->eps_in[0])
4887c6f5c050SMian Yousaf Kaukab return -ENOMEM;
48881f91b4ccSFelipe Balbi /* Same dwc2_hsotg_ep is used in both directions for ep0 */
4889c6f5c050SMian Yousaf Kaukab hsotg->eps_out[0] = hsotg->eps_in[0];
489047a1685fSDinh Nguyen
489143e90349SJohn Youn cfg = hsotg->hw_params.dev_ep_dirs;
4892251a17f5SRoshan Pius for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) {
4893c6f5c050SMian Yousaf Kaukab ep_type = cfg & 3;
4894c6f5c050SMian Yousaf Kaukab /* Direction in or both */
4895c6f5c050SMian Yousaf Kaukab if (!(ep_type & 2)) {
4896c6f5c050SMian Yousaf Kaukab hsotg->eps_in[i] = devm_kzalloc(hsotg->dev,
48971f91b4ccSFelipe Balbi sizeof(struct dwc2_hsotg_ep), GFP_KERNEL);
4898c6f5c050SMian Yousaf Kaukab if (!hsotg->eps_in[i])
4899c6f5c050SMian Yousaf Kaukab return -ENOMEM;
4900c6f5c050SMian Yousaf Kaukab }
4901c6f5c050SMian Yousaf Kaukab /* Direction out or both */
4902c6f5c050SMian Yousaf Kaukab if (!(ep_type & 1)) {
4903c6f5c050SMian Yousaf Kaukab hsotg->eps_out[i] = devm_kzalloc(hsotg->dev,
49041f91b4ccSFelipe Balbi sizeof(struct dwc2_hsotg_ep), GFP_KERNEL);
4905c6f5c050SMian Yousaf Kaukab if (!hsotg->eps_out[i])
4906c6f5c050SMian Yousaf Kaukab return -ENOMEM;
4907c6f5c050SMian Yousaf Kaukab }
4908c6f5c050SMian Yousaf Kaukab }
4909c6f5c050SMian Yousaf Kaukab
491043e90349SJohn Youn hsotg->fifo_mem = hsotg->hw_params.total_fifo_size;
491143e90349SJohn Youn hsotg->dedicated_fifos = hsotg->hw_params.en_multiple_tx_fifo;
491247a1685fSDinh Nguyen
4913cff9eb75SMarek Szyprowski dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n",
4914cff9eb75SMarek Szyprowski hsotg->num_of_eps,
4915cff9eb75SMarek Szyprowski hsotg->dedicated_fifos ? "dedicated" : "shared",
4916cff9eb75SMarek Szyprowski hsotg->fifo_mem);
4917c6f5c050SMian Yousaf Kaukab return 0;
491847a1685fSDinh Nguyen }
491947a1685fSDinh Nguyen
492047a1685fSDinh Nguyen /**
49211f91b4ccSFelipe Balbi * dwc2_hsotg_dump - dump state of the udc
49226fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller
49236fb914d7SGrigor Tovmasyan *
492447a1685fSDinh Nguyen */
dwc2_hsotg_dump(struct dwc2_hsotg * hsotg)49251f91b4ccSFelipe Balbi static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg)
492647a1685fSDinh Nguyen {
492747a1685fSDinh Nguyen #ifdef DEBUG
492847a1685fSDinh Nguyen struct device *dev = hsotg->dev;
492947a1685fSDinh Nguyen u32 val;
493047a1685fSDinh Nguyen int idx;
493147a1685fSDinh Nguyen
493247a1685fSDinh Nguyen dev_info(dev, "DCFG=0x%08x, DCTL=0x%08x, DIEPMSK=%08x\n",
4933f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DCFG), dwc2_readl(hsotg, DCTL),
4934f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPMSK));
493547a1685fSDinh Nguyen
4936f889f23dSMian Yousaf Kaukab dev_info(dev, "GAHBCFG=0x%08x, GHWCFG1=0x%08x\n",
4937f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, GAHBCFG), dwc2_readl(hsotg, GHWCFG1));
493847a1685fSDinh Nguyen
493947a1685fSDinh Nguyen dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
4940f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, GRXFSIZ), dwc2_readl(hsotg, GNPTXFSIZ));
494147a1685fSDinh Nguyen
494247a1685fSDinh Nguyen /* show periodic fifo settings */
494347a1685fSDinh Nguyen
4944364f8e93SMian Yousaf Kaukab for (idx = 1; idx < hsotg->num_of_eps; idx++) {
4945f25c42b8SGevorg Sahakyan val = dwc2_readl(hsotg, DPTXFSIZN(idx));
494647a1685fSDinh Nguyen dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx,
494747a1685fSDinh Nguyen val >> FIFOSIZE_DEPTH_SHIFT,
494847a1685fSDinh Nguyen val & FIFOSIZE_STARTADDR_MASK);
494947a1685fSDinh Nguyen }
495047a1685fSDinh Nguyen
4951364f8e93SMian Yousaf Kaukab for (idx = 0; idx < hsotg->num_of_eps; idx++) {
495247a1685fSDinh Nguyen dev_info(dev,
495347a1685fSDinh Nguyen "ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx,
4954f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPCTL(idx)),
4955f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPTSIZ(idx)),
4956f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPDMA(idx)));
495747a1685fSDinh Nguyen
4958f25c42b8SGevorg Sahakyan val = dwc2_readl(hsotg, DOEPCTL(idx));
495947a1685fSDinh Nguyen dev_info(dev,
496047a1685fSDinh Nguyen "ep%d-out: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n",
4961f25c42b8SGevorg Sahakyan idx, dwc2_readl(hsotg, DOEPCTL(idx)),
4962f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DOEPTSIZ(idx)),
4963f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DOEPDMA(idx)));
496447a1685fSDinh Nguyen }
496547a1685fSDinh Nguyen
496647a1685fSDinh Nguyen dev_info(dev, "DVBUSDIS=0x%08x, DVBUSPULSE=%08x\n",
4967f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DVBUSDIS), dwc2_readl(hsotg, DVBUSPULSE));
496847a1685fSDinh Nguyen #endif
496947a1685fSDinh Nguyen }
497047a1685fSDinh Nguyen
497147a1685fSDinh Nguyen /**
4972117777b2SDinh Nguyen * dwc2_gadget_init - init function for gadget
49736fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller
49746fb914d7SGrigor Tovmasyan *
497547a1685fSDinh Nguyen */
dwc2_gadget_init(struct dwc2_hsotg * hsotg)4976f3768997SVardan Mikayelyan int dwc2_gadget_init(struct dwc2_hsotg *hsotg)
497747a1685fSDinh Nguyen {
4978117777b2SDinh Nguyen struct device *dev = hsotg->dev;
497947a1685fSDinh Nguyen int epnum;
498047a1685fSDinh Nguyen int ret;
498143e90349SJohn Youn
49820a176279SGregory Herrero /* Dump fifo information */
49830a176279SGregory Herrero dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n",
498405ee799fSJohn Youn hsotg->params.g_np_tx_fifo_size);
498505ee799fSJohn Youn dev_dbg(dev, "RXFIFO size: %d\n", hsotg->params.g_rx_fifo_size);
498647a1685fSDinh Nguyen
498792ef98a4SJohn Keeping switch (hsotg->params.speed) {
498892ef98a4SJohn Keeping case DWC2_SPEED_PARAM_LOW:
498992ef98a4SJohn Keeping hsotg->gadget.max_speed = USB_SPEED_LOW;
499092ef98a4SJohn Keeping break;
499192ef98a4SJohn Keeping case DWC2_SPEED_PARAM_FULL:
499292ef98a4SJohn Keeping hsotg->gadget.max_speed = USB_SPEED_FULL;
499392ef98a4SJohn Keeping break;
499492ef98a4SJohn Keeping default:
499547a1685fSDinh Nguyen hsotg->gadget.max_speed = USB_SPEED_HIGH;
499692ef98a4SJohn Keeping break;
499792ef98a4SJohn Keeping }
499892ef98a4SJohn Keeping
49991f91b4ccSFelipe Balbi hsotg->gadget.ops = &dwc2_hsotg_gadget_ops;
500047a1685fSDinh Nguyen hsotg->gadget.name = dev_name(dev);
5001f5c8a6cbSFabrice Gasnier hsotg->gadget.otg_caps = &hsotg->params.otg_caps;
5002fa389a6dSVardan Mikayelyan hsotg->remote_wakeup_allowed = 0;
50037455f8b7SJohn Youn
50047455f8b7SJohn Youn if (hsotg->params.lpm)
50057455f8b7SJohn Youn hsotg->gadget.lpm_capable = true;
50067455f8b7SJohn Youn
5007097ee662SGregory Herrero if (hsotg->dr_mode == USB_DR_MODE_OTG)
5008097ee662SGregory Herrero hsotg->gadget.is_otg = 1;
5009ec4cc657SMian Yousaf Kaukab else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
5010ec4cc657SMian Yousaf Kaukab hsotg->op_state = OTG_STATE_B_PERIPHERAL;
501147a1685fSDinh Nguyen
50121f91b4ccSFelipe Balbi ret = dwc2_hsotg_hw_cfg(hsotg);
5013c6f5c050SMian Yousaf Kaukab if (ret) {
5014c6f5c050SMian Yousaf Kaukab dev_err(hsotg->dev, "Hardware configuration failed: %d\n", ret);
501509a75e85SMarek Szyprowski return ret;
5016c6f5c050SMian Yousaf Kaukab }
5017c6f5c050SMian Yousaf Kaukab
50183f95001dSMian Yousaf Kaukab hsotg->ctrl_buff = devm_kzalloc(hsotg->dev,
50193f95001dSMian Yousaf Kaukab DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
50208bae0f8cSWolfram Sang if (!hsotg->ctrl_buff)
502109a75e85SMarek Szyprowski return -ENOMEM;
50223f95001dSMian Yousaf Kaukab
50233f95001dSMian Yousaf Kaukab hsotg->ep0_buff = devm_kzalloc(hsotg->dev,
50243f95001dSMian Yousaf Kaukab DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
50258bae0f8cSWolfram Sang if (!hsotg->ep0_buff)
502609a75e85SMarek Szyprowski return -ENOMEM;
50273f95001dSMian Yousaf Kaukab
50280f6b80c0SVahram Aharonyan if (using_desc_dma(hsotg)) {
50290f6b80c0SVahram Aharonyan ret = dwc2_gadget_alloc_ctrl_desc_chains(hsotg);
50300f6b80c0SVahram Aharonyan if (ret < 0)
50310f6b80c0SVahram Aharonyan return ret;
50320f6b80c0SVahram Aharonyan }
50330f6b80c0SVahram Aharonyan
5034f3768997SVardan Mikayelyan ret = devm_request_irq(hsotg->dev, hsotg->irq, dwc2_hsotg_irq,
5035f3768997SVardan Mikayelyan IRQF_SHARED, dev_name(hsotg->dev), hsotg);
5036eb3c56c5SMarek Szyprowski if (ret < 0) {
5037db8178c3SDinh Nguyen dev_err(dev, "cannot claim IRQ for gadget\n");
503809a75e85SMarek Szyprowski return ret;
5039eb3c56c5SMarek Szyprowski }
5040eb3c56c5SMarek Szyprowski
504147a1685fSDinh Nguyen /* hsotg->num_of_eps holds number of EPs other than ep0 */
504247a1685fSDinh Nguyen
504347a1685fSDinh Nguyen if (hsotg->num_of_eps == 0) {
504447a1685fSDinh Nguyen dev_err(dev, "wrong number of EPs (zero)\n");
504509a75e85SMarek Szyprowski return -EINVAL;
504647a1685fSDinh Nguyen }
504747a1685fSDinh Nguyen
504847a1685fSDinh Nguyen /* setup endpoint information */
504947a1685fSDinh Nguyen
505047a1685fSDinh Nguyen INIT_LIST_HEAD(&hsotg->gadget.ep_list);
5051c6f5c050SMian Yousaf Kaukab hsotg->gadget.ep0 = &hsotg->eps_out[0]->ep;
505247a1685fSDinh Nguyen
505347a1685fSDinh Nguyen /* allocate EP0 request */
505447a1685fSDinh Nguyen
50551f91b4ccSFelipe Balbi hsotg->ctrl_req = dwc2_hsotg_ep_alloc_request(&hsotg->eps_out[0]->ep,
505647a1685fSDinh Nguyen GFP_KERNEL);
505747a1685fSDinh Nguyen if (!hsotg->ctrl_req) {
505847a1685fSDinh Nguyen dev_err(dev, "failed to allocate ctrl req\n");
505909a75e85SMarek Szyprowski return -ENOMEM;
506047a1685fSDinh Nguyen }
506147a1685fSDinh Nguyen
506247a1685fSDinh Nguyen /* initialise the endpoints now the core has been initialised */
5063c6f5c050SMian Yousaf Kaukab for (epnum = 0; epnum < hsotg->num_of_eps; epnum++) {
5064c6f5c050SMian Yousaf Kaukab if (hsotg->eps_in[epnum])
50651f91b4ccSFelipe Balbi dwc2_hsotg_initep(hsotg, hsotg->eps_in[epnum],
5066c6f5c050SMian Yousaf Kaukab epnum, 1);
5067c6f5c050SMian Yousaf Kaukab if (hsotg->eps_out[epnum])
50681f91b4ccSFelipe Balbi dwc2_hsotg_initep(hsotg, hsotg->eps_out[epnum],
5069c6f5c050SMian Yousaf Kaukab epnum, 0);
5070c6f5c050SMian Yousaf Kaukab }
507147a1685fSDinh Nguyen
50721f91b4ccSFelipe Balbi dwc2_hsotg_dump(hsotg);
507347a1685fSDinh Nguyen
507447a1685fSDinh Nguyen return 0;
507547a1685fSDinh Nguyen }
507647a1685fSDinh Nguyen
507747a1685fSDinh Nguyen /**
50781f91b4ccSFelipe Balbi * dwc2_hsotg_remove - remove function for hsotg driver
50796fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller
50806fb914d7SGrigor Tovmasyan *
508147a1685fSDinh Nguyen */
dwc2_hsotg_remove(struct dwc2_hsotg * hsotg)50821f91b4ccSFelipe Balbi int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg)
508347a1685fSDinh Nguyen {
508447a1685fSDinh Nguyen usb_del_gadget_udc(&hsotg->gadget);
50859bb073a0SGrigor Tovmasyan dwc2_hsotg_ep_free_request(&hsotg->eps_out[0]->ep, hsotg->ctrl_req);
508647a1685fSDinh Nguyen
508747a1685fSDinh Nguyen return 0;
508847a1685fSDinh Nguyen }
508947a1685fSDinh Nguyen
dwc2_hsotg_suspend(struct dwc2_hsotg * hsotg)50901f91b4ccSFelipe Balbi int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg)
509147a1685fSDinh Nguyen {
509247a1685fSDinh Nguyen unsigned long flags;
509347a1685fSDinh Nguyen
50949e779778SGregory Herrero if (hsotg->lx_state != DWC2_L0)
509509a75e85SMarek Szyprowski return 0;
50969e779778SGregory Herrero
5097dc6e69e6SMarek Szyprowski if (hsotg->driver) {
5098dc6e69e6SMarek Szyprowski int ep;
5099dc6e69e6SMarek Szyprowski
510047a1685fSDinh Nguyen dev_info(hsotg->dev, "suspending usb gadget %s\n",
510147a1685fSDinh Nguyen hsotg->driver->driver.name);
510247a1685fSDinh Nguyen
510347a1685fSDinh Nguyen spin_lock_irqsave(&hsotg->lock, flags);
5104dc6e69e6SMarek Szyprowski if (hsotg->enabled)
51051f91b4ccSFelipe Balbi dwc2_hsotg_core_disconnect(hsotg);
51061f91b4ccSFelipe Balbi dwc2_hsotg_disconnect(hsotg);
510747a1685fSDinh Nguyen hsotg->gadget.speed = USB_SPEED_UNKNOWN;
510847a1685fSDinh Nguyen spin_unlock_irqrestore(&hsotg->lock, flags);
510947a1685fSDinh Nguyen
5110ac55d163SAmelie Delaunay for (ep = 1; ep < hsotg->num_of_eps; ep++) {
5111c6f5c050SMian Yousaf Kaukab if (hsotg->eps_in[ep])
51124fe4f9feSMinas Harutyunyan dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep);
5113c6f5c050SMian Yousaf Kaukab if (hsotg->eps_out[ep])
51144fe4f9feSMinas Harutyunyan dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep);
5115c6f5c050SMian Yousaf Kaukab }
511647a1685fSDinh Nguyen }
511747a1685fSDinh Nguyen
511809a75e85SMarek Szyprowski return 0;
511947a1685fSDinh Nguyen }
512047a1685fSDinh Nguyen
dwc2_hsotg_resume(struct dwc2_hsotg * hsotg)51211f91b4ccSFelipe Balbi int dwc2_hsotg_resume(struct dwc2_hsotg *hsotg)
512247a1685fSDinh Nguyen {
512347a1685fSDinh Nguyen unsigned long flags;
512447a1685fSDinh Nguyen
51259e779778SGregory Herrero if (hsotg->lx_state == DWC2_L2)
512609a75e85SMarek Szyprowski return 0;
51279e779778SGregory Herrero
512847a1685fSDinh Nguyen if (hsotg->driver) {
512947a1685fSDinh Nguyen dev_info(hsotg->dev, "resuming usb gadget %s\n",
513047a1685fSDinh Nguyen hsotg->driver->driver.name);
5131d00b4142SRobert Baldyga
513247a1685fSDinh Nguyen spin_lock_irqsave(&hsotg->lock, flags);
51331f91b4ccSFelipe Balbi dwc2_hsotg_core_init_disconnected(hsotg, false);
513466e77a24SRazmik Karapetyan if (hsotg->enabled) {
513566e77a24SRazmik Karapetyan /* Enable ACG feature in device mode,if supported */
513666e77a24SRazmik Karapetyan dwc2_enable_acg(hsotg);
51371f91b4ccSFelipe Balbi dwc2_hsotg_core_connect(hsotg);
513866e77a24SRazmik Karapetyan }
513947a1685fSDinh Nguyen spin_unlock_irqrestore(&hsotg->lock, flags);
5140dc6e69e6SMarek Szyprowski }
514147a1685fSDinh Nguyen
514209a75e85SMarek Szyprowski return 0;
514347a1685fSDinh Nguyen }
514458e52ff6SJohn Youn
514558e52ff6SJohn Youn /**
514658e52ff6SJohn Youn * dwc2_backup_device_registers() - Backup controller device registers.
514758e52ff6SJohn Youn * When suspending usb bus, registers needs to be backuped
514858e52ff6SJohn Youn * if controller power is disabled once suspended.
514958e52ff6SJohn Youn *
515058e52ff6SJohn Youn * @hsotg: Programming view of the DWC_otg controller
515158e52ff6SJohn Youn */
dwc2_backup_device_registers(struct dwc2_hsotg * hsotg)515258e52ff6SJohn Youn int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
515358e52ff6SJohn Youn {
515458e52ff6SJohn Youn struct dwc2_dregs_backup *dr;
515558e52ff6SJohn Youn int i;
515658e52ff6SJohn Youn
515758e52ff6SJohn Youn dev_dbg(hsotg->dev, "%s\n", __func__);
515858e52ff6SJohn Youn
515958e52ff6SJohn Youn /* Backup dev regs */
516058e52ff6SJohn Youn dr = &hsotg->dr_backup;
516158e52ff6SJohn Youn
5162f25c42b8SGevorg Sahakyan dr->dcfg = dwc2_readl(hsotg, DCFG);
5163f25c42b8SGevorg Sahakyan dr->dctl = dwc2_readl(hsotg, DCTL);
5164f25c42b8SGevorg Sahakyan dr->daintmsk = dwc2_readl(hsotg, DAINTMSK);
5165f25c42b8SGevorg Sahakyan dr->diepmsk = dwc2_readl(hsotg, DIEPMSK);
5166f25c42b8SGevorg Sahakyan dr->doepmsk = dwc2_readl(hsotg, DOEPMSK);
516758e52ff6SJohn Youn
516858e52ff6SJohn Youn for (i = 0; i < hsotg->num_of_eps; i++) {
516958e52ff6SJohn Youn /* Backup IN EPs */
5170f25c42b8SGevorg Sahakyan dr->diepctl[i] = dwc2_readl(hsotg, DIEPCTL(i));
517158e52ff6SJohn Youn
517258e52ff6SJohn Youn /* Ensure DATA PID is correctly configured */
517358e52ff6SJohn Youn if (dr->diepctl[i] & DXEPCTL_DPID)
517458e52ff6SJohn Youn dr->diepctl[i] |= DXEPCTL_SETD1PID;
517558e52ff6SJohn Youn else
517658e52ff6SJohn Youn dr->diepctl[i] |= DXEPCTL_SETD0PID;
517758e52ff6SJohn Youn
5178f25c42b8SGevorg Sahakyan dr->dieptsiz[i] = dwc2_readl(hsotg, DIEPTSIZ(i));
5179f25c42b8SGevorg Sahakyan dr->diepdma[i] = dwc2_readl(hsotg, DIEPDMA(i));
518058e52ff6SJohn Youn
518158e52ff6SJohn Youn /* Backup OUT EPs */
5182f25c42b8SGevorg Sahakyan dr->doepctl[i] = dwc2_readl(hsotg, DOEPCTL(i));
518358e52ff6SJohn Youn
518458e52ff6SJohn Youn /* Ensure DATA PID is correctly configured */
518558e52ff6SJohn Youn if (dr->doepctl[i] & DXEPCTL_DPID)
518658e52ff6SJohn Youn dr->doepctl[i] |= DXEPCTL_SETD1PID;
518758e52ff6SJohn Youn else
518858e52ff6SJohn Youn dr->doepctl[i] |= DXEPCTL_SETD0PID;
518958e52ff6SJohn Youn
5190f25c42b8SGevorg Sahakyan dr->doeptsiz[i] = dwc2_readl(hsotg, DOEPTSIZ(i));
5191f25c42b8SGevorg Sahakyan dr->doepdma[i] = dwc2_readl(hsotg, DOEPDMA(i));
5192f25c42b8SGevorg Sahakyan dr->dtxfsiz[i] = dwc2_readl(hsotg, DPTXFSIZN(i));
519358e52ff6SJohn Youn }
519458e52ff6SJohn Youn dr->valid = true;
519558e52ff6SJohn Youn return 0;
519658e52ff6SJohn Youn }
519758e52ff6SJohn Youn
519858e52ff6SJohn Youn /**
519958e52ff6SJohn Youn * dwc2_restore_device_registers() - Restore controller device registers.
520058e52ff6SJohn Youn * When resuming usb bus, device registers needs to be restored
520158e52ff6SJohn Youn * if controller power were disabled.
520258e52ff6SJohn Youn *
520358e52ff6SJohn Youn * @hsotg: Programming view of the DWC_otg controller
52049a5d2816SVardan Mikayelyan * @remote_wakeup: Indicates whether resume is initiated by Device or Host.
52059a5d2816SVardan Mikayelyan *
52069a5d2816SVardan Mikayelyan * Return: 0 if successful, negative error code otherwise
520758e52ff6SJohn Youn */
dwc2_restore_device_registers(struct dwc2_hsotg * hsotg,int remote_wakeup)52089a5d2816SVardan Mikayelyan int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
520958e52ff6SJohn Youn {
521058e52ff6SJohn Youn struct dwc2_dregs_backup *dr;
521158e52ff6SJohn Youn int i;
521258e52ff6SJohn Youn
521358e52ff6SJohn Youn dev_dbg(hsotg->dev, "%s\n", __func__);
521458e52ff6SJohn Youn
521558e52ff6SJohn Youn /* Restore dev regs */
521658e52ff6SJohn Youn dr = &hsotg->dr_backup;
521758e52ff6SJohn Youn if (!dr->valid) {
521858e52ff6SJohn Youn dev_err(hsotg->dev, "%s: no device registers to restore\n",
521958e52ff6SJohn Youn __func__);
522058e52ff6SJohn Youn return -EINVAL;
522158e52ff6SJohn Youn }
522258e52ff6SJohn Youn dr->valid = false;
522358e52ff6SJohn Youn
52249a5d2816SVardan Mikayelyan if (!remote_wakeup)
5225f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dctl, DCTL);
52269a5d2816SVardan Mikayelyan
5227f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->daintmsk, DAINTMSK);
5228f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->diepmsk, DIEPMSK);
5229f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doepmsk, DOEPMSK);
523058e52ff6SJohn Youn
523158e52ff6SJohn Youn for (i = 0; i < hsotg->num_of_eps; i++) {
523258e52ff6SJohn Youn /* Restore IN EPs */
5233f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dieptsiz[i], DIEPTSIZ(i));
5234f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->diepdma[i], DIEPDMA(i));
5235f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doeptsiz[i], DOEPTSIZ(i));
52369a5d2816SVardan Mikayelyan /** WA for enabled EPx's IN in DDMA mode. On entering to
52379a5d2816SVardan Mikayelyan * hibernation wrong value read and saved from DIEPDMAx,
52389a5d2816SVardan Mikayelyan * as result BNA interrupt asserted on hibernation exit
52399a5d2816SVardan Mikayelyan * by restoring from saved area.
52409a5d2816SVardan Mikayelyan */
5241c4bc515dSJohn Keeping if (using_desc_dma(hsotg) &&
52429a5d2816SVardan Mikayelyan (dr->diepctl[i] & DXEPCTL_EPENA))
52439a5d2816SVardan Mikayelyan dr->diepdma[i] = hsotg->eps_in[i]->desc_list_dma;
5244f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dtxfsiz[i], DPTXFSIZN(i));
5245f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->diepctl[i], DIEPCTL(i));
52469a5d2816SVardan Mikayelyan /* Restore OUT EPs */
5247f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doeptsiz[i], DOEPTSIZ(i));
52489a5d2816SVardan Mikayelyan /* WA for enabled EPx's OUT in DDMA mode. On entering to
52499a5d2816SVardan Mikayelyan * hibernation wrong value read and saved from DOEPDMAx,
52509a5d2816SVardan Mikayelyan * as result BNA interrupt asserted on hibernation exit
52519a5d2816SVardan Mikayelyan * by restoring from saved area.
52529a5d2816SVardan Mikayelyan */
5253c4bc515dSJohn Keeping if (using_desc_dma(hsotg) &&
52549a5d2816SVardan Mikayelyan (dr->doepctl[i] & DXEPCTL_EPENA))
52559a5d2816SVardan Mikayelyan dr->doepdma[i] = hsotg->eps_out[i]->desc_list_dma;
5256f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doepdma[i], DOEPDMA(i));
5257f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doepctl[i], DOEPCTL(i));
525858e52ff6SJohn Youn }
525958e52ff6SJohn Youn
526058e52ff6SJohn Youn return 0;
526158e52ff6SJohn Youn }
526221b03405SSevak Arakelyan
526321b03405SSevak Arakelyan /**
526421b03405SSevak Arakelyan * dwc2_gadget_init_lpm - Configure the core to support LPM in device mode
526521b03405SSevak Arakelyan *
526621b03405SSevak Arakelyan * @hsotg: Programming view of DWC_otg controller
526721b03405SSevak Arakelyan *
526821b03405SSevak Arakelyan */
dwc2_gadget_init_lpm(struct dwc2_hsotg * hsotg)526921b03405SSevak Arakelyan void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg)
527021b03405SSevak Arakelyan {
527121b03405SSevak Arakelyan u32 val;
527221b03405SSevak Arakelyan
527321b03405SSevak Arakelyan if (!hsotg->params.lpm)
527421b03405SSevak Arakelyan return;
527521b03405SSevak Arakelyan
527621b03405SSevak Arakelyan val = GLPMCFG_LPMCAP | GLPMCFG_APPL1RES;
527721b03405SSevak Arakelyan val |= hsotg->params.hird_threshold_en ? GLPMCFG_HIRD_THRES_EN : 0;
527821b03405SSevak Arakelyan val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0;
527921b03405SSevak Arakelyan val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT;
528021b03405SSevak Arakelyan val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0;
528146637565SMinas Harutyunyan val |= GLPMCFG_LPM_REJECT_CTRL_CONTROL;
52829aed8c08SArtur Petrosyan val |= GLPMCFG_LPM_ACCEPT_CTRL_ISOC;
5283f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, val, GLPMCFG);
5284f25c42b8SGevorg Sahakyan dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG));
52854abe4537SGrigor Tovmasyan
52864abe4537SGrigor Tovmasyan /* Unmask WKUP_ALERT Interrupt */
52874abe4537SGrigor Tovmasyan if (hsotg->params.service_interval)
52884abe4537SGrigor Tovmasyan dwc2_set_bit(hsotg, GINTMSK2, GINTMSK2_WKUP_ALERT_INT_MSK);
528921b03405SSevak Arakelyan }
5290c5c403dcSVardan Mikayelyan
5291c5c403dcSVardan Mikayelyan /**
529215d9dbf8SGrigor Tovmasyan * dwc2_gadget_program_ref_clk - Program GREFCLK register in device mode
529315d9dbf8SGrigor Tovmasyan *
529415d9dbf8SGrigor Tovmasyan * @hsotg: Programming view of DWC_otg controller
529515d9dbf8SGrigor Tovmasyan *
529615d9dbf8SGrigor Tovmasyan */
dwc2_gadget_program_ref_clk(struct dwc2_hsotg * hsotg)529715d9dbf8SGrigor Tovmasyan void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg)
529815d9dbf8SGrigor Tovmasyan {
529915d9dbf8SGrigor Tovmasyan u32 val = 0;
530015d9dbf8SGrigor Tovmasyan
530115d9dbf8SGrigor Tovmasyan val |= GREFCLK_REF_CLK_MODE;
530215d9dbf8SGrigor Tovmasyan val |= hsotg->params.ref_clk_per << GREFCLK_REFCLKPER_SHIFT;
530315d9dbf8SGrigor Tovmasyan val |= hsotg->params.sof_cnt_wkup_alert <<
530415d9dbf8SGrigor Tovmasyan GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT;
530515d9dbf8SGrigor Tovmasyan
530615d9dbf8SGrigor Tovmasyan dwc2_writel(hsotg, val, GREFCLK);
530715d9dbf8SGrigor Tovmasyan dev_dbg(hsotg->dev, "GREFCLK=0x%08x\n", dwc2_readl(hsotg, GREFCLK));
530815d9dbf8SGrigor Tovmasyan }
530915d9dbf8SGrigor Tovmasyan
531015d9dbf8SGrigor Tovmasyan /**
5311c5c403dcSVardan Mikayelyan * dwc2_gadget_enter_hibernation() - Put controller in Hibernation.
5312c5c403dcSVardan Mikayelyan *
5313c5c403dcSVardan Mikayelyan * @hsotg: Programming view of the DWC_otg controller
5314c5c403dcSVardan Mikayelyan *
5315c5c403dcSVardan Mikayelyan * Return non-zero if failed to enter to hibernation.
5316c5c403dcSVardan Mikayelyan */
dwc2_gadget_enter_hibernation(struct dwc2_hsotg * hsotg)5317c5c403dcSVardan Mikayelyan int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
5318c5c403dcSVardan Mikayelyan {
5319c5c403dcSVardan Mikayelyan u32 gpwrdn;
5320c5c403dcSVardan Mikayelyan int ret = 0;
5321c5c403dcSVardan Mikayelyan
5322c5c403dcSVardan Mikayelyan /* Change to L2(suspend) state */
5323c5c403dcSVardan Mikayelyan hsotg->lx_state = DWC2_L2;
5324c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev, "Start of hibernation completed\n");
5325c5c403dcSVardan Mikayelyan ret = dwc2_backup_global_registers(hsotg);
5326c5c403dcSVardan Mikayelyan if (ret) {
5327c5c403dcSVardan Mikayelyan dev_err(hsotg->dev, "%s: failed to backup global registers\n",
5328c5c403dcSVardan Mikayelyan __func__);
5329c5c403dcSVardan Mikayelyan return ret;
5330c5c403dcSVardan Mikayelyan }
5331c5c403dcSVardan Mikayelyan ret = dwc2_backup_device_registers(hsotg);
5332c5c403dcSVardan Mikayelyan if (ret) {
5333c5c403dcSVardan Mikayelyan dev_err(hsotg->dev, "%s: failed to backup device registers\n",
5334c5c403dcSVardan Mikayelyan __func__);
5335c5c403dcSVardan Mikayelyan return ret;
5336c5c403dcSVardan Mikayelyan }
5337c5c403dcSVardan Mikayelyan
5338c5c403dcSVardan Mikayelyan gpwrdn = GPWRDN_PWRDNRSTN;
5339c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_PMUACTV;
5340f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN);
5341c5c403dcSVardan Mikayelyan udelay(10);
5342c5c403dcSVardan Mikayelyan
5343c5c403dcSVardan Mikayelyan /* Set flag to indicate that we are in hibernation */
5344c5c403dcSVardan Mikayelyan hsotg->hibernated = 1;
5345c5c403dcSVardan Mikayelyan
5346c5c403dcSVardan Mikayelyan /* Enable interrupts from wake up logic */
5347f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN);
5348c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_PMUINTSEL;
5349f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN);
5350c5c403dcSVardan Mikayelyan udelay(10);
5351c5c403dcSVardan Mikayelyan
5352c5c403dcSVardan Mikayelyan /* Unmask device mode interrupts in GPWRDN */
5353f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN);
5354c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_RST_DET_MSK;
5355c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_LNSTSCHG_MSK;
5356c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_STS_CHGINT_MSK;
5357f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN);
5358c5c403dcSVardan Mikayelyan udelay(10);
5359c5c403dcSVardan Mikayelyan
5360c5c403dcSVardan Mikayelyan /* Enable Power Down Clamp */
5361f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN);
5362c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_PWRDNCLMP;
5363f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN);
5364c5c403dcSVardan Mikayelyan udelay(10);
5365c5c403dcSVardan Mikayelyan
5366c5c403dcSVardan Mikayelyan /* Switch off VDD */
5367f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN);
5368c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_PWRDNSWTCH;
5369f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN);
5370c5c403dcSVardan Mikayelyan udelay(10);
5371c5c403dcSVardan Mikayelyan
5372c5c403dcSVardan Mikayelyan /* Save gpwrdn register for further usage if stschng interrupt */
5373f25c42b8SGevorg Sahakyan hsotg->gr_backup.gpwrdn = dwc2_readl(hsotg, GPWRDN);
5374c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev, "Hibernation completed\n");
5375c5c403dcSVardan Mikayelyan
5376c5c403dcSVardan Mikayelyan return ret;
5377c5c403dcSVardan Mikayelyan }
5378c5c403dcSVardan Mikayelyan
5379c5c403dcSVardan Mikayelyan /**
5380c5c403dcSVardan Mikayelyan * dwc2_gadget_exit_hibernation()
5381c5c403dcSVardan Mikayelyan * This function is for exiting from Device mode hibernation by host initiated
5382c5c403dcSVardan Mikayelyan * resume/reset and device initiated remote-wakeup.
5383c5c403dcSVardan Mikayelyan *
5384c5c403dcSVardan Mikayelyan * @hsotg: Programming view of the DWC_otg controller
5385c5c403dcSVardan Mikayelyan * @rem_wakeup: indicates whether resume is initiated by Device or Host.
53866fb914d7SGrigor Tovmasyan * @reset: indicates whether resume is initiated by Reset.
5387c5c403dcSVardan Mikayelyan *
5388c5c403dcSVardan Mikayelyan * Return non-zero if failed to exit from hibernation.
5389c5c403dcSVardan Mikayelyan */
dwc2_gadget_exit_hibernation(struct dwc2_hsotg * hsotg,int rem_wakeup,int reset)5390c5c403dcSVardan Mikayelyan int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
5391c5c403dcSVardan Mikayelyan int rem_wakeup, int reset)
5392c5c403dcSVardan Mikayelyan {
5393c5c403dcSVardan Mikayelyan u32 pcgcctl;
5394c5c403dcSVardan Mikayelyan u32 gpwrdn;
5395c5c403dcSVardan Mikayelyan u32 dctl;
5396c5c403dcSVardan Mikayelyan int ret = 0;
5397c5c403dcSVardan Mikayelyan struct dwc2_gregs_backup *gr;
5398c5c403dcSVardan Mikayelyan struct dwc2_dregs_backup *dr;
5399c5c403dcSVardan Mikayelyan
5400c5c403dcSVardan Mikayelyan gr = &hsotg->gr_backup;
5401c5c403dcSVardan Mikayelyan dr = &hsotg->dr_backup;
5402c5c403dcSVardan Mikayelyan
5403c5c403dcSVardan Mikayelyan if (!hsotg->hibernated) {
5404c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev, "Already exited from Hibernation\n");
5405c5c403dcSVardan Mikayelyan return 1;
5406c5c403dcSVardan Mikayelyan }
5407c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev,
5408c5c403dcSVardan Mikayelyan "%s: called with rem_wakeup = %d reset = %d\n",
5409c5c403dcSVardan Mikayelyan __func__, rem_wakeup, reset);
5410c5c403dcSVardan Mikayelyan
5411c5c403dcSVardan Mikayelyan dwc2_hib_restore_common(hsotg, rem_wakeup, 0);
5412c5c403dcSVardan Mikayelyan
5413c5c403dcSVardan Mikayelyan if (!reset) {
5414c5c403dcSVardan Mikayelyan /* Clear all pending interupts */
5415f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, 0xffffffff, GINTSTS);
5416c5c403dcSVardan Mikayelyan }
5417c5c403dcSVardan Mikayelyan
5418c5c403dcSVardan Mikayelyan /* De-assert Restore */
5419f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN);
5420c5c403dcSVardan Mikayelyan gpwrdn &= ~GPWRDN_RESTORE;
5421f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN);
5422c5c403dcSVardan Mikayelyan udelay(10);
5423c5c403dcSVardan Mikayelyan
5424c5c403dcSVardan Mikayelyan if (!rem_wakeup) {
5425f25c42b8SGevorg Sahakyan pcgcctl = dwc2_readl(hsotg, PCGCTL);
5426c5c403dcSVardan Mikayelyan pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
5427f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, pcgcctl, PCGCTL);
5428c5c403dcSVardan Mikayelyan }
5429c5c403dcSVardan Mikayelyan
5430c5c403dcSVardan Mikayelyan /* Restore GUSBCFG, DCFG and DCTL */
5431f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG);
5432f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dcfg, DCFG);
5433f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dctl, DCTL);
5434c5c403dcSVardan Mikayelyan
5435b29b494bSArtur Petrosyan /* On USB Reset, reset device address to zero */
5436b29b494bSArtur Petrosyan if (reset)
5437b29b494bSArtur Petrosyan dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK);
5438b29b494bSArtur Petrosyan
5439c5c403dcSVardan Mikayelyan /* De-assert Wakeup Logic */
5440f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN);
5441c5c403dcSVardan Mikayelyan gpwrdn &= ~GPWRDN_PMUACTV;
5442f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN);
5443c5c403dcSVardan Mikayelyan
5444c5c403dcSVardan Mikayelyan if (rem_wakeup) {
5445c5c403dcSVardan Mikayelyan udelay(10);
5446c5c403dcSVardan Mikayelyan /* Start Remote Wakeup Signaling */
5447f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dctl | DCTL_RMTWKUPSIG, DCTL);
5448c5c403dcSVardan Mikayelyan } else {
5449c5c403dcSVardan Mikayelyan udelay(50);
5450c5c403dcSVardan Mikayelyan /* Set Device programming done bit */
5451f25c42b8SGevorg Sahakyan dctl = dwc2_readl(hsotg, DCTL);
5452c5c403dcSVardan Mikayelyan dctl |= DCTL_PWRONPRGDONE;
5453f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dctl, DCTL);
5454c5c403dcSVardan Mikayelyan }
5455c5c403dcSVardan Mikayelyan /* Wait for interrupts which must be cleared */
5456c5c403dcSVardan Mikayelyan mdelay(2);
5457c5c403dcSVardan Mikayelyan /* Clear all pending interupts */
5458f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, 0xffffffff, GINTSTS);
5459c5c403dcSVardan Mikayelyan
5460c5c403dcSVardan Mikayelyan /* Restore global registers */
5461c5c403dcSVardan Mikayelyan ret = dwc2_restore_global_registers(hsotg);
5462c5c403dcSVardan Mikayelyan if (ret) {
5463c5c403dcSVardan Mikayelyan dev_err(hsotg->dev, "%s: failed to restore registers\n",
5464c5c403dcSVardan Mikayelyan __func__);
5465c5c403dcSVardan Mikayelyan return ret;
5466c5c403dcSVardan Mikayelyan }
5467c5c403dcSVardan Mikayelyan
5468c5c403dcSVardan Mikayelyan /* Restore device registers */
5469c5c403dcSVardan Mikayelyan ret = dwc2_restore_device_registers(hsotg, rem_wakeup);
5470c5c403dcSVardan Mikayelyan if (ret) {
5471c5c403dcSVardan Mikayelyan dev_err(hsotg->dev, "%s: failed to restore device registers\n",
5472c5c403dcSVardan Mikayelyan __func__);
5473c5c403dcSVardan Mikayelyan return ret;
5474c5c403dcSVardan Mikayelyan }
5475c5c403dcSVardan Mikayelyan
5476c5c403dcSVardan Mikayelyan if (rem_wakeup) {
5477c5c403dcSVardan Mikayelyan mdelay(10);
5478f25c42b8SGevorg Sahakyan dctl = dwc2_readl(hsotg, DCTL);
5479c5c403dcSVardan Mikayelyan dctl &= ~DCTL_RMTWKUPSIG;
5480f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dctl, DCTL);
5481c5c403dcSVardan Mikayelyan }
5482c5c403dcSVardan Mikayelyan
5483c5c403dcSVardan Mikayelyan hsotg->hibernated = 0;
5484c5c403dcSVardan Mikayelyan hsotg->lx_state = DWC2_L0;
5485c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev, "Hibernation recovery completes here\n");
5486c5c403dcSVardan Mikayelyan
5487c5c403dcSVardan Mikayelyan return ret;
5488c5c403dcSVardan Mikayelyan }
5489be2b960eSArtur Petrosyan
5490be2b960eSArtur Petrosyan /**
5491be2b960eSArtur Petrosyan * dwc2_gadget_enter_partial_power_down() - Put controller in partial
5492be2b960eSArtur Petrosyan * power down.
5493be2b960eSArtur Petrosyan *
5494be2b960eSArtur Petrosyan * @hsotg: Programming view of the DWC_otg controller
5495be2b960eSArtur Petrosyan *
5496be2b960eSArtur Petrosyan * Return: non-zero if failed to enter device partial power down.
5497be2b960eSArtur Petrosyan *
5498be2b960eSArtur Petrosyan * This function is for entering device mode partial power down.
5499be2b960eSArtur Petrosyan */
dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg * hsotg)5500be2b960eSArtur Petrosyan int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg)
5501be2b960eSArtur Petrosyan {
5502be2b960eSArtur Petrosyan u32 pcgcctl;
5503be2b960eSArtur Petrosyan int ret = 0;
5504be2b960eSArtur Petrosyan
5505be2b960eSArtur Petrosyan dev_dbg(hsotg->dev, "Entering device partial power down started.\n");
5506be2b960eSArtur Petrosyan
5507be2b960eSArtur Petrosyan /* Backup all registers */
5508be2b960eSArtur Petrosyan ret = dwc2_backup_global_registers(hsotg);
5509be2b960eSArtur Petrosyan if (ret) {
5510be2b960eSArtur Petrosyan dev_err(hsotg->dev, "%s: failed to backup global registers\n",
5511be2b960eSArtur Petrosyan __func__);
5512be2b960eSArtur Petrosyan return ret;
5513be2b960eSArtur Petrosyan }
5514be2b960eSArtur Petrosyan
5515be2b960eSArtur Petrosyan ret = dwc2_backup_device_registers(hsotg);
5516be2b960eSArtur Petrosyan if (ret) {
5517be2b960eSArtur Petrosyan dev_err(hsotg->dev, "%s: failed to backup device registers\n",
5518be2b960eSArtur Petrosyan __func__);
5519be2b960eSArtur Petrosyan return ret;
5520be2b960eSArtur Petrosyan }
5521be2b960eSArtur Petrosyan
5522be2b960eSArtur Petrosyan /*
5523be2b960eSArtur Petrosyan * Clear any pending interrupts since dwc2 will not be able to
5524be2b960eSArtur Petrosyan * clear them after entering partial_power_down.
5525be2b960eSArtur Petrosyan */
5526be2b960eSArtur Petrosyan dwc2_writel(hsotg, 0xffffffff, GINTSTS);
5527be2b960eSArtur Petrosyan
5528be2b960eSArtur Petrosyan /* Put the controller in low power state */
5529be2b960eSArtur Petrosyan pcgcctl = dwc2_readl(hsotg, PCGCTL);
5530be2b960eSArtur Petrosyan
5531be2b960eSArtur Petrosyan pcgcctl |= PCGCTL_PWRCLMP;
5532be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL);
5533be2b960eSArtur Petrosyan udelay(5);
5534be2b960eSArtur Petrosyan
5535be2b960eSArtur Petrosyan pcgcctl |= PCGCTL_RSTPDWNMODULE;
5536be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL);
5537be2b960eSArtur Petrosyan udelay(5);
5538be2b960eSArtur Petrosyan
5539be2b960eSArtur Petrosyan pcgcctl |= PCGCTL_STOPPCLK;
5540be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL);
5541be2b960eSArtur Petrosyan
5542be2b960eSArtur Petrosyan /* Set in_ppd flag to 1 as here core enters suspend. */
5543be2b960eSArtur Petrosyan hsotg->in_ppd = 1;
5544be2b960eSArtur Petrosyan hsotg->lx_state = DWC2_L2;
5545be2b960eSArtur Petrosyan
5546be2b960eSArtur Petrosyan dev_dbg(hsotg->dev, "Entering device partial power down completed.\n");
5547be2b960eSArtur Petrosyan
5548be2b960eSArtur Petrosyan return ret;
5549be2b960eSArtur Petrosyan }
5550be2b960eSArtur Petrosyan
5551be2b960eSArtur Petrosyan /*
5552be2b960eSArtur Petrosyan * dwc2_gadget_exit_partial_power_down() - Exit controller from device partial
5553be2b960eSArtur Petrosyan * power down.
5554be2b960eSArtur Petrosyan *
5555be2b960eSArtur Petrosyan * @hsotg: Programming view of the DWC_otg controller
5556be2b960eSArtur Petrosyan * @restore: indicates whether need to restore the registers or not.
5557be2b960eSArtur Petrosyan *
5558be2b960eSArtur Petrosyan * Return: non-zero if failed to exit device partial power down.
5559be2b960eSArtur Petrosyan *
5560be2b960eSArtur Petrosyan * This function is for exiting from device mode partial power down.
5561be2b960eSArtur Petrosyan */
dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg * hsotg,bool restore)5562be2b960eSArtur Petrosyan int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg,
5563be2b960eSArtur Petrosyan bool restore)
5564be2b960eSArtur Petrosyan {
5565be2b960eSArtur Petrosyan u32 pcgcctl;
5566be2b960eSArtur Petrosyan u32 dctl;
5567be2b960eSArtur Petrosyan struct dwc2_dregs_backup *dr;
5568be2b960eSArtur Petrosyan int ret = 0;
5569be2b960eSArtur Petrosyan
5570be2b960eSArtur Petrosyan dr = &hsotg->dr_backup;
5571be2b960eSArtur Petrosyan
5572be2b960eSArtur Petrosyan dev_dbg(hsotg->dev, "Exiting device partial Power Down started.\n");
5573be2b960eSArtur Petrosyan
5574be2b960eSArtur Petrosyan pcgcctl = dwc2_readl(hsotg, PCGCTL);
5575be2b960eSArtur Petrosyan pcgcctl &= ~PCGCTL_STOPPCLK;
5576be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL);
5577be2b960eSArtur Petrosyan
5578be2b960eSArtur Petrosyan pcgcctl = dwc2_readl(hsotg, PCGCTL);
5579be2b960eSArtur Petrosyan pcgcctl &= ~PCGCTL_PWRCLMP;
5580be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL);
5581be2b960eSArtur Petrosyan
5582be2b960eSArtur Petrosyan pcgcctl = dwc2_readl(hsotg, PCGCTL);
5583be2b960eSArtur Petrosyan pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
5584be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL);
5585be2b960eSArtur Petrosyan
5586be2b960eSArtur Petrosyan udelay(100);
5587be2b960eSArtur Petrosyan if (restore) {
5588be2b960eSArtur Petrosyan ret = dwc2_restore_global_registers(hsotg);
5589be2b960eSArtur Petrosyan if (ret) {
5590be2b960eSArtur Petrosyan dev_err(hsotg->dev, "%s: failed to restore registers\n",
5591be2b960eSArtur Petrosyan __func__);
5592be2b960eSArtur Petrosyan return ret;
5593be2b960eSArtur Petrosyan }
5594be2b960eSArtur Petrosyan /* Restore DCFG */
5595be2b960eSArtur Petrosyan dwc2_writel(hsotg, dr->dcfg, DCFG);
5596be2b960eSArtur Petrosyan
5597be2b960eSArtur Petrosyan ret = dwc2_restore_device_registers(hsotg, 0);
5598be2b960eSArtur Petrosyan if (ret) {
5599be2b960eSArtur Petrosyan dev_err(hsotg->dev, "%s: failed to restore device registers\n",
5600be2b960eSArtur Petrosyan __func__);
5601be2b960eSArtur Petrosyan return ret;
5602be2b960eSArtur Petrosyan }
5603be2b960eSArtur Petrosyan }
5604be2b960eSArtur Petrosyan
5605be2b960eSArtur Petrosyan /* Set the Power-On Programming done bit */
5606be2b960eSArtur Petrosyan dctl = dwc2_readl(hsotg, DCTL);
5607be2b960eSArtur Petrosyan dctl |= DCTL_PWRONPRGDONE;
5608be2b960eSArtur Petrosyan dwc2_writel(hsotg, dctl, DCTL);
5609be2b960eSArtur Petrosyan
5610be2b960eSArtur Petrosyan /* Set in_ppd flag to 0 as here core exits from suspend. */
5611be2b960eSArtur Petrosyan hsotg->in_ppd = 0;
5612be2b960eSArtur Petrosyan hsotg->lx_state = DWC2_L0;
5613be2b960eSArtur Petrosyan
5614be2b960eSArtur Petrosyan dev_dbg(hsotg->dev, "Exiting device partial Power Down completed.\n");
5615be2b960eSArtur Petrosyan return ret;
5616be2b960eSArtur Petrosyan }
5617012466fcSArtur Petrosyan
5618012466fcSArtur Petrosyan /**
5619012466fcSArtur Petrosyan * dwc2_gadget_enter_clock_gating() - Put controller in clock gating.
5620012466fcSArtur Petrosyan *
5621012466fcSArtur Petrosyan * @hsotg: Programming view of the DWC_otg controller
5622012466fcSArtur Petrosyan *
5623012466fcSArtur Petrosyan * Return: non-zero if failed to enter device partial power down.
5624012466fcSArtur Petrosyan *
5625012466fcSArtur Petrosyan * This function is for entering device mode clock gating.
5626012466fcSArtur Petrosyan */
dwc2_gadget_enter_clock_gating(struct dwc2_hsotg * hsotg)5627012466fcSArtur Petrosyan void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg)
5628012466fcSArtur Petrosyan {
5629012466fcSArtur Petrosyan u32 pcgctl;
5630012466fcSArtur Petrosyan
5631012466fcSArtur Petrosyan dev_dbg(hsotg->dev, "Entering device clock gating.\n");
5632012466fcSArtur Petrosyan
5633012466fcSArtur Petrosyan /* Set the Phy Clock bit as suspend is received. */
5634012466fcSArtur Petrosyan pcgctl = dwc2_readl(hsotg, PCGCTL);
5635012466fcSArtur Petrosyan pcgctl |= PCGCTL_STOPPCLK;
5636012466fcSArtur Petrosyan dwc2_writel(hsotg, pcgctl, PCGCTL);
5637012466fcSArtur Petrosyan udelay(5);
5638012466fcSArtur Petrosyan
5639012466fcSArtur Petrosyan /* Set the Gate hclk as suspend is received. */
5640012466fcSArtur Petrosyan pcgctl = dwc2_readl(hsotg, PCGCTL);
5641012466fcSArtur Petrosyan pcgctl |= PCGCTL_GATEHCLK;
5642012466fcSArtur Petrosyan dwc2_writel(hsotg, pcgctl, PCGCTL);
5643012466fcSArtur Petrosyan udelay(5);
5644012466fcSArtur Petrosyan
5645012466fcSArtur Petrosyan hsotg->lx_state = DWC2_L2;
5646012466fcSArtur Petrosyan hsotg->bus_suspended = true;
5647012466fcSArtur Petrosyan }
5648012466fcSArtur Petrosyan
5649012466fcSArtur Petrosyan /*
5650012466fcSArtur Petrosyan * dwc2_gadget_exit_clock_gating() - Exit controller from device clock gating.
5651012466fcSArtur Petrosyan *
5652012466fcSArtur Petrosyan * @hsotg: Programming view of the DWC_otg controller
5653012466fcSArtur Petrosyan * @rem_wakeup: indicates whether remote wake up is enabled.
5654012466fcSArtur Petrosyan *
5655012466fcSArtur Petrosyan * This function is for exiting from device mode clock gating.
5656012466fcSArtur Petrosyan */
dwc2_gadget_exit_clock_gating(struct dwc2_hsotg * hsotg,int rem_wakeup)5657012466fcSArtur Petrosyan void dwc2_gadget_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup)
5658012466fcSArtur Petrosyan {
5659012466fcSArtur Petrosyan u32 pcgctl;
5660012466fcSArtur Petrosyan u32 dctl;
5661012466fcSArtur Petrosyan
5662012466fcSArtur Petrosyan dev_dbg(hsotg->dev, "Exiting device clock gating.\n");
5663012466fcSArtur Petrosyan
5664012466fcSArtur Petrosyan /* Clear the Gate hclk. */
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 /* Phy Clock bit. */
5671012466fcSArtur Petrosyan pcgctl = dwc2_readl(hsotg, PCGCTL);
5672012466fcSArtur Petrosyan pcgctl &= ~PCGCTL_STOPPCLK;
5673012466fcSArtur Petrosyan dwc2_writel(hsotg, pcgctl, PCGCTL);
5674012466fcSArtur Petrosyan udelay(5);
5675012466fcSArtur Petrosyan
5676012466fcSArtur Petrosyan if (rem_wakeup) {
5677012466fcSArtur Petrosyan /* Set Remote Wakeup Signaling */
5678012466fcSArtur Petrosyan dctl = dwc2_readl(hsotg, DCTL);
5679012466fcSArtur Petrosyan dctl |= DCTL_RMTWKUPSIG;
5680012466fcSArtur Petrosyan dwc2_writel(hsotg, dctl, DCTL);
5681012466fcSArtur Petrosyan }
5682012466fcSArtur Petrosyan
5683012466fcSArtur Petrosyan /* Change to L0 state */
5684012466fcSArtur Petrosyan call_gadget(hsotg, resume);
5685012466fcSArtur Petrosyan hsotg->lx_state = DWC2_L0;
5686012466fcSArtur Petrosyan hsotg->bus_suspended = false;
5687012466fcSArtur Petrosyan }
5688