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 */ 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 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 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 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 88810209abeSAndrzej Pietrasiewicz for_each_sg(ureq->sg, sg, ureq->num_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), 89110209abeSAndrzej Pietrasiewicz sg_is_last(sg)); 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 */ 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 */ 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 */ 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 */ 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 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 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 */ 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 */ 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 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 1418*5d69a3b5SMinas Harutyunyan if (hs->lx_state == DWC2_L1) { 1419*5d69a3b5SMinas Harutyunyan dwc2_wakeup_from_lpm_l1(hs, true); 1420*5d69a3b5SMinas Harutyunyan } 1421*5d69a3b5SMinas 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 */ 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 */ 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 */ 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 373431f42da3SMinas Harutyunyan /* Exit gadget mode clock gating. */ 373531f42da3SMinas Harutyunyan if (hsotg->params.power_down == 373631f42da3SMinas Harutyunyan DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended && 373731f42da3SMinas Harutyunyan !hsotg->params.no_clock_gating) 373831f42da3SMinas Harutyunyan dwc2_gadget_exit_clock_gating(hsotg, 0); 373931f42da3SMinas 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 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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; 461547a1685fSDinh Nguyen hsotg->gadget.speed = USB_SPEED_UNKNOWN; 4616dc6e69e6SMarek Szyprowski hsotg->enabled = 0; 461747a1685fSDinh Nguyen 461847a1685fSDinh Nguyen spin_unlock_irqrestore(&hsotg->lock, flags); 461947a1685fSDinh Nguyen 4620f6c01592SGregory Herrero if (!IS_ERR_OR_NULL(hsotg->uphy)) 4621f6c01592SGregory Herrero otg_set_peripheral(hsotg->uphy->otg, NULL); 4622c816c47fSMarek Szyprowski 462350213832SFabrice Gasnier if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) 462409a75e85SMarek Szyprowski dwc2_lowlevel_hw_disable(hsotg); 462547a1685fSDinh Nguyen 462647a1685fSDinh Nguyen return 0; 462747a1685fSDinh Nguyen } 462847a1685fSDinh Nguyen 462947a1685fSDinh Nguyen /** 46301f91b4ccSFelipe Balbi * dwc2_hsotg_gadget_getframe - read the frame number 463147a1685fSDinh Nguyen * @gadget: The usb gadget state 463247a1685fSDinh Nguyen * 463347a1685fSDinh Nguyen * Read the {micro} frame number 463447a1685fSDinh Nguyen */ 46351f91b4ccSFelipe Balbi static int dwc2_hsotg_gadget_getframe(struct usb_gadget *gadget) 463647a1685fSDinh Nguyen { 46371f91b4ccSFelipe Balbi return dwc2_hsotg_read_frameno(to_hsotg(gadget)); 463847a1685fSDinh Nguyen } 463947a1685fSDinh Nguyen 464047a1685fSDinh Nguyen /** 46411a0808cbSJohn Keeping * dwc2_hsotg_set_selfpowered - set if device is self/bus powered 46421a0808cbSJohn Keeping * @gadget: The usb gadget state 46431a0808cbSJohn Keeping * @is_selfpowered: Whether the device is self-powered 46441a0808cbSJohn Keeping * 46451a0808cbSJohn Keeping * Set if the device is self or bus powered. 46461a0808cbSJohn Keeping */ 46471a0808cbSJohn Keeping static int dwc2_hsotg_set_selfpowered(struct usb_gadget *gadget, 46481a0808cbSJohn Keeping int is_selfpowered) 46491a0808cbSJohn Keeping { 46501a0808cbSJohn Keeping struct dwc2_hsotg *hsotg = to_hsotg(gadget); 46511a0808cbSJohn Keeping unsigned long flags; 46521a0808cbSJohn Keeping 46531a0808cbSJohn Keeping spin_lock_irqsave(&hsotg->lock, flags); 46541a0808cbSJohn Keeping gadget->is_selfpowered = !!is_selfpowered; 46551a0808cbSJohn Keeping spin_unlock_irqrestore(&hsotg->lock, flags); 46561a0808cbSJohn Keeping 46571a0808cbSJohn Keeping return 0; 46581a0808cbSJohn Keeping } 46591a0808cbSJohn Keeping 46601a0808cbSJohn Keeping /** 46611f91b4ccSFelipe Balbi * dwc2_hsotg_pullup - connect/disconnect the USB PHY 466247a1685fSDinh Nguyen * @gadget: The usb gadget state 466347a1685fSDinh Nguyen * @is_on: Current state of the USB PHY 466447a1685fSDinh Nguyen * 466547a1685fSDinh Nguyen * Connect/Disconnect the USB PHY pullup 466647a1685fSDinh Nguyen */ 46671f91b4ccSFelipe Balbi static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on) 466847a1685fSDinh Nguyen { 4669941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = to_hsotg(gadget); 46708879904bSJohan Hovold unsigned long flags; 467147a1685fSDinh Nguyen 467277ba9119SGregory Herrero dev_dbg(hsotg->dev, "%s: is_on: %d op_state: %d\n", __func__, is_on, 467377ba9119SGregory Herrero hsotg->op_state); 467477ba9119SGregory Herrero 467577ba9119SGregory Herrero /* Don't modify pullup state while in host mode */ 467677ba9119SGregory Herrero if (hsotg->op_state != OTG_STATE_B_PERIPHERAL) { 467777ba9119SGregory Herrero hsotg->enabled = is_on; 467877ba9119SGregory Herrero return 0; 467977ba9119SGregory Herrero } 468047a1685fSDinh Nguyen 468147a1685fSDinh Nguyen spin_lock_irqsave(&hsotg->lock, flags); 468247a1685fSDinh Nguyen if (is_on) { 4683dc6e69e6SMarek Szyprowski hsotg->enabled = 1; 46841f91b4ccSFelipe Balbi dwc2_hsotg_core_init_disconnected(hsotg, false); 468566e77a24SRazmik Karapetyan /* Enable ACG feature in device mode,if supported */ 468666e77a24SRazmik Karapetyan dwc2_enable_acg(hsotg); 46871f91b4ccSFelipe Balbi dwc2_hsotg_core_connect(hsotg); 468847a1685fSDinh Nguyen } else { 46891f91b4ccSFelipe Balbi dwc2_hsotg_core_disconnect(hsotg); 46901f91b4ccSFelipe Balbi dwc2_hsotg_disconnect(hsotg); 4691dc6e69e6SMarek Szyprowski hsotg->enabled = 0; 469247a1685fSDinh Nguyen } 469347a1685fSDinh Nguyen 469447a1685fSDinh Nguyen hsotg->gadget.speed = USB_SPEED_UNKNOWN; 469547a1685fSDinh Nguyen spin_unlock_irqrestore(&hsotg->lock, flags); 469647a1685fSDinh Nguyen 469747a1685fSDinh Nguyen return 0; 469847a1685fSDinh Nguyen } 469947a1685fSDinh Nguyen 47001f91b4ccSFelipe Balbi static int dwc2_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) 470183d98223SGregory Herrero { 470283d98223SGregory Herrero struct dwc2_hsotg *hsotg = to_hsotg(gadget); 470383d98223SGregory Herrero unsigned long flags; 470483d98223SGregory Herrero 470583d98223SGregory Herrero dev_dbg(hsotg->dev, "%s: is_active: %d\n", __func__, is_active); 470683d98223SGregory Herrero spin_lock_irqsave(&hsotg->lock, flags); 470783d98223SGregory Herrero 470818b2b37cSGregory Herrero /* 4709c9c394abSArtur Petrosyan * If controller is in partial power down state, it must exit from 4710c9c394abSArtur Petrosyan * that state before being initialized / de-initialized 471118b2b37cSGregory Herrero */ 4712c9c394abSArtur Petrosyan if (hsotg->lx_state == DWC2_L2 && hsotg->in_ppd) 4713c9c394abSArtur Petrosyan /* 4714c9c394abSArtur Petrosyan * No need to check the return value as 4715c9c394abSArtur Petrosyan * registers are not being restored. 4716c9c394abSArtur Petrosyan */ 4717c9c394abSArtur Petrosyan dwc2_exit_partial_power_down(hsotg, 0, false); 4718065d3931SGregory Herrero 471961f7223bSGregory Herrero if (is_active) { 472061f7223bSGregory Herrero hsotg->op_state = OTG_STATE_B_PERIPHERAL; 472161f7223bSGregory Herrero 47221f91b4ccSFelipe Balbi dwc2_hsotg_core_init_disconnected(hsotg, false); 472366e77a24SRazmik Karapetyan if (hsotg->enabled) { 472466e77a24SRazmik Karapetyan /* Enable ACG feature in device mode,if supported */ 472566e77a24SRazmik Karapetyan dwc2_enable_acg(hsotg); 47261f91b4ccSFelipe Balbi dwc2_hsotg_core_connect(hsotg); 472766e77a24SRazmik Karapetyan } 472883d98223SGregory Herrero } else { 47291f91b4ccSFelipe Balbi dwc2_hsotg_core_disconnect(hsotg); 47301f91b4ccSFelipe Balbi dwc2_hsotg_disconnect(hsotg); 473183d98223SGregory Herrero } 473283d98223SGregory Herrero 473383d98223SGregory Herrero spin_unlock_irqrestore(&hsotg->lock, flags); 473483d98223SGregory Herrero return 0; 473583d98223SGregory Herrero } 473683d98223SGregory Herrero 4737596d696aSGregory Herrero /** 47381f91b4ccSFelipe Balbi * dwc2_hsotg_vbus_draw - report bMaxPower field 4739596d696aSGregory Herrero * @gadget: The usb gadget state 4740596d696aSGregory Herrero * @mA: Amount of current 4741596d696aSGregory Herrero * 4742596d696aSGregory Herrero * Report how much power the device may consume to the phy. 4743596d696aSGregory Herrero */ 47449da51974SJohn Youn static int dwc2_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned int mA) 4745596d696aSGregory Herrero { 4746596d696aSGregory Herrero struct dwc2_hsotg *hsotg = to_hsotg(gadget); 4747596d696aSGregory Herrero 4748596d696aSGregory Herrero if (IS_ERR_OR_NULL(hsotg->uphy)) 4749596d696aSGregory Herrero return -ENOTSUPP; 4750596d696aSGregory Herrero return usb_phy_set_power(hsotg->uphy, mA); 4751596d696aSGregory Herrero } 4752596d696aSGregory Herrero 47535324bad6SArgishti Aleksanyan static void dwc2_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed speed) 47545324bad6SArgishti Aleksanyan { 47555324bad6SArgishti Aleksanyan struct dwc2_hsotg *hsotg = to_hsotg(g); 47565324bad6SArgishti Aleksanyan unsigned long flags; 47575324bad6SArgishti Aleksanyan 47585324bad6SArgishti Aleksanyan spin_lock_irqsave(&hsotg->lock, flags); 47595324bad6SArgishti Aleksanyan switch (speed) { 47605324bad6SArgishti Aleksanyan case USB_SPEED_HIGH: 47615324bad6SArgishti Aleksanyan hsotg->params.speed = DWC2_SPEED_PARAM_HIGH; 47625324bad6SArgishti Aleksanyan break; 47635324bad6SArgishti Aleksanyan case USB_SPEED_FULL: 47645324bad6SArgishti Aleksanyan hsotg->params.speed = DWC2_SPEED_PARAM_FULL; 47655324bad6SArgishti Aleksanyan break; 47665324bad6SArgishti Aleksanyan case USB_SPEED_LOW: 47675324bad6SArgishti Aleksanyan hsotg->params.speed = DWC2_SPEED_PARAM_LOW; 47685324bad6SArgishti Aleksanyan break; 47695324bad6SArgishti Aleksanyan default: 47705324bad6SArgishti Aleksanyan dev_err(hsotg->dev, "invalid speed (%d)\n", speed); 47715324bad6SArgishti Aleksanyan } 47725324bad6SArgishti Aleksanyan spin_unlock_irqrestore(&hsotg->lock, flags); 47735324bad6SArgishti Aleksanyan } 47745324bad6SArgishti Aleksanyan 47751f91b4ccSFelipe Balbi static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = { 47761f91b4ccSFelipe Balbi .get_frame = dwc2_hsotg_gadget_getframe, 47771a0808cbSJohn Keeping .set_selfpowered = dwc2_hsotg_set_selfpowered, 47781f91b4ccSFelipe Balbi .udc_start = dwc2_hsotg_udc_start, 47791f91b4ccSFelipe Balbi .udc_stop = dwc2_hsotg_udc_stop, 47801f91b4ccSFelipe Balbi .pullup = dwc2_hsotg_pullup, 47815324bad6SArgishti Aleksanyan .udc_set_speed = dwc2_gadget_set_speed, 47821f91b4ccSFelipe Balbi .vbus_session = dwc2_hsotg_vbus_session, 47831f91b4ccSFelipe Balbi .vbus_draw = dwc2_hsotg_vbus_draw, 478447a1685fSDinh Nguyen }; 478547a1685fSDinh Nguyen 478647a1685fSDinh Nguyen /** 47871f91b4ccSFelipe Balbi * dwc2_hsotg_initep - initialise a single endpoint 478847a1685fSDinh Nguyen * @hsotg: The device state. 478947a1685fSDinh Nguyen * @hs_ep: The endpoint to be initialised. 479047a1685fSDinh Nguyen * @epnum: The endpoint number 47916fb914d7SGrigor Tovmasyan * @dir_in: True if direction is in. 479247a1685fSDinh Nguyen * 479347a1685fSDinh Nguyen * Initialise the given endpoint (as part of the probe and device state 479447a1685fSDinh Nguyen * creation) to give to the gadget driver. Setup the endpoint name, any 479547a1685fSDinh Nguyen * direction information and other state that may be required. 479647a1685fSDinh Nguyen */ 47971f91b4ccSFelipe Balbi static void dwc2_hsotg_initep(struct dwc2_hsotg *hsotg, 47981f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep, 4799c6f5c050SMian Yousaf Kaukab int epnum, 4800c6f5c050SMian Yousaf Kaukab bool dir_in) 480147a1685fSDinh Nguyen { 480247a1685fSDinh Nguyen char *dir; 480347a1685fSDinh Nguyen 480447a1685fSDinh Nguyen if (epnum == 0) 480547a1685fSDinh Nguyen dir = ""; 4806c6f5c050SMian Yousaf Kaukab else if (dir_in) 480747a1685fSDinh Nguyen dir = "in"; 4808c6f5c050SMian Yousaf Kaukab else 4809c6f5c050SMian Yousaf Kaukab dir = "out"; 481047a1685fSDinh Nguyen 4811c6f5c050SMian Yousaf Kaukab hs_ep->dir_in = dir_in; 481247a1685fSDinh Nguyen hs_ep->index = epnum; 481347a1685fSDinh Nguyen 481447a1685fSDinh Nguyen snprintf(hs_ep->name, sizeof(hs_ep->name), "ep%d%s", epnum, dir); 481547a1685fSDinh Nguyen 481647a1685fSDinh Nguyen INIT_LIST_HEAD(&hs_ep->queue); 481747a1685fSDinh Nguyen INIT_LIST_HEAD(&hs_ep->ep.ep_list); 481847a1685fSDinh Nguyen 481947a1685fSDinh Nguyen /* add to the list of endpoints known by the gadget driver */ 482047a1685fSDinh Nguyen if (epnum) 482147a1685fSDinh Nguyen list_add_tail(&hs_ep->ep.ep_list, &hsotg->gadget.ep_list); 482247a1685fSDinh Nguyen 482347a1685fSDinh Nguyen hs_ep->parent = hsotg; 482447a1685fSDinh Nguyen hs_ep->ep.name = hs_ep->name; 482538e9002bSVardan Mikayelyan 482638e9002bSVardan Mikayelyan if (hsotg->params.speed == DWC2_SPEED_PARAM_LOW) 482738e9002bSVardan Mikayelyan usb_ep_set_maxpacket_limit(&hs_ep->ep, 8); 482838e9002bSVardan Mikayelyan else 482938e9002bSVardan Mikayelyan usb_ep_set_maxpacket_limit(&hs_ep->ep, 483038e9002bSVardan Mikayelyan epnum ? 1024 : EP0_MPS_LIMIT); 48311f91b4ccSFelipe Balbi hs_ep->ep.ops = &dwc2_hsotg_ep_ops; 483247a1685fSDinh Nguyen 48332954522fSRobert Baldyga if (epnum == 0) { 48342954522fSRobert Baldyga hs_ep->ep.caps.type_control = true; 48352954522fSRobert Baldyga } else { 483638e9002bSVardan Mikayelyan if (hsotg->params.speed != DWC2_SPEED_PARAM_LOW) { 48372954522fSRobert Baldyga hs_ep->ep.caps.type_iso = true; 48382954522fSRobert Baldyga hs_ep->ep.caps.type_bulk = true; 483938e9002bSVardan Mikayelyan } 48402954522fSRobert Baldyga hs_ep->ep.caps.type_int = true; 48412954522fSRobert Baldyga } 48422954522fSRobert Baldyga 48432954522fSRobert Baldyga if (dir_in) 48442954522fSRobert Baldyga hs_ep->ep.caps.dir_in = true; 48452954522fSRobert Baldyga else 48462954522fSRobert Baldyga hs_ep->ep.caps.dir_out = true; 48472954522fSRobert Baldyga 484847a1685fSDinh Nguyen /* 484947a1685fSDinh Nguyen * if we're using dma, we need to set the next-endpoint pointer 485047a1685fSDinh Nguyen * to be something valid. 485147a1685fSDinh Nguyen */ 485247a1685fSDinh Nguyen 485347a1685fSDinh Nguyen if (using_dma(hsotg)) { 485447a1685fSDinh Nguyen u32 next = DXEPCTL_NEXTEP((epnum + 1) % 15); 48559da51974SJohn Youn 4856c6f5c050SMian Yousaf Kaukab if (dir_in) 4857f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, next, DIEPCTL(epnum)); 4858c6f5c050SMian Yousaf Kaukab else 4859f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, next, DOEPCTL(epnum)); 486047a1685fSDinh Nguyen } 486147a1685fSDinh Nguyen } 486247a1685fSDinh Nguyen 486347a1685fSDinh Nguyen /** 48641f91b4ccSFelipe Balbi * dwc2_hsotg_hw_cfg - read HW configuration registers 48656fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller 486647a1685fSDinh Nguyen * 486747a1685fSDinh Nguyen * Read the USB core HW configuration registers 486847a1685fSDinh Nguyen */ 48691f91b4ccSFelipe Balbi static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg) 487047a1685fSDinh Nguyen { 4871c6f5c050SMian Yousaf Kaukab u32 cfg; 4872c6f5c050SMian Yousaf Kaukab u32 ep_type; 4873c6f5c050SMian Yousaf Kaukab u32 i; 4874c6f5c050SMian Yousaf Kaukab 487547a1685fSDinh Nguyen /* check hardware configuration */ 487647a1685fSDinh Nguyen 487743e90349SJohn Youn hsotg->num_of_eps = hsotg->hw_params.num_dev_ep; 487843e90349SJohn Youn 4879c6f5c050SMian Yousaf Kaukab /* Add ep0 */ 4880c6f5c050SMian Yousaf Kaukab hsotg->num_of_eps++; 488147a1685fSDinh Nguyen 4882b98866c2SJohn Youn hsotg->eps_in[0] = devm_kzalloc(hsotg->dev, 4883b98866c2SJohn Youn sizeof(struct dwc2_hsotg_ep), 4884c6f5c050SMian Yousaf Kaukab GFP_KERNEL); 4885c6f5c050SMian Yousaf Kaukab if (!hsotg->eps_in[0]) 4886c6f5c050SMian Yousaf Kaukab return -ENOMEM; 48871f91b4ccSFelipe Balbi /* Same dwc2_hsotg_ep is used in both directions for ep0 */ 4888c6f5c050SMian Yousaf Kaukab hsotg->eps_out[0] = hsotg->eps_in[0]; 488947a1685fSDinh Nguyen 489043e90349SJohn Youn cfg = hsotg->hw_params.dev_ep_dirs; 4891251a17f5SRoshan Pius for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) { 4892c6f5c050SMian Yousaf Kaukab ep_type = cfg & 3; 4893c6f5c050SMian Yousaf Kaukab /* Direction in or both */ 4894c6f5c050SMian Yousaf Kaukab if (!(ep_type & 2)) { 4895c6f5c050SMian Yousaf Kaukab hsotg->eps_in[i] = devm_kzalloc(hsotg->dev, 48961f91b4ccSFelipe Balbi sizeof(struct dwc2_hsotg_ep), GFP_KERNEL); 4897c6f5c050SMian Yousaf Kaukab if (!hsotg->eps_in[i]) 4898c6f5c050SMian Yousaf Kaukab return -ENOMEM; 4899c6f5c050SMian Yousaf Kaukab } 4900c6f5c050SMian Yousaf Kaukab /* Direction out or both */ 4901c6f5c050SMian Yousaf Kaukab if (!(ep_type & 1)) { 4902c6f5c050SMian Yousaf Kaukab hsotg->eps_out[i] = devm_kzalloc(hsotg->dev, 49031f91b4ccSFelipe Balbi sizeof(struct dwc2_hsotg_ep), GFP_KERNEL); 4904c6f5c050SMian Yousaf Kaukab if (!hsotg->eps_out[i]) 4905c6f5c050SMian Yousaf Kaukab return -ENOMEM; 4906c6f5c050SMian Yousaf Kaukab } 4907c6f5c050SMian Yousaf Kaukab } 4908c6f5c050SMian Yousaf Kaukab 490943e90349SJohn Youn hsotg->fifo_mem = hsotg->hw_params.total_fifo_size; 491043e90349SJohn Youn hsotg->dedicated_fifos = hsotg->hw_params.en_multiple_tx_fifo; 491147a1685fSDinh Nguyen 4912cff9eb75SMarek Szyprowski dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n", 4913cff9eb75SMarek Szyprowski hsotg->num_of_eps, 4914cff9eb75SMarek Szyprowski hsotg->dedicated_fifos ? "dedicated" : "shared", 4915cff9eb75SMarek Szyprowski hsotg->fifo_mem); 4916c6f5c050SMian Yousaf Kaukab return 0; 491747a1685fSDinh Nguyen } 491847a1685fSDinh Nguyen 491947a1685fSDinh Nguyen /** 49201f91b4ccSFelipe Balbi * dwc2_hsotg_dump - dump state of the udc 49216fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller 49226fb914d7SGrigor Tovmasyan * 492347a1685fSDinh Nguyen */ 49241f91b4ccSFelipe Balbi static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg) 492547a1685fSDinh Nguyen { 492647a1685fSDinh Nguyen #ifdef DEBUG 492747a1685fSDinh Nguyen struct device *dev = hsotg->dev; 492847a1685fSDinh Nguyen u32 val; 492947a1685fSDinh Nguyen int idx; 493047a1685fSDinh Nguyen 493147a1685fSDinh Nguyen dev_info(dev, "DCFG=0x%08x, DCTL=0x%08x, DIEPMSK=%08x\n", 4932f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DCFG), dwc2_readl(hsotg, DCTL), 4933f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPMSK)); 493447a1685fSDinh Nguyen 4935f889f23dSMian Yousaf Kaukab dev_info(dev, "GAHBCFG=0x%08x, GHWCFG1=0x%08x\n", 4936f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, GAHBCFG), dwc2_readl(hsotg, GHWCFG1)); 493747a1685fSDinh Nguyen 493847a1685fSDinh Nguyen dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n", 4939f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, GRXFSIZ), dwc2_readl(hsotg, GNPTXFSIZ)); 494047a1685fSDinh Nguyen 494147a1685fSDinh Nguyen /* show periodic fifo settings */ 494247a1685fSDinh Nguyen 4943364f8e93SMian Yousaf Kaukab for (idx = 1; idx < hsotg->num_of_eps; idx++) { 4944f25c42b8SGevorg Sahakyan val = dwc2_readl(hsotg, DPTXFSIZN(idx)); 494547a1685fSDinh Nguyen dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx, 494647a1685fSDinh Nguyen val >> FIFOSIZE_DEPTH_SHIFT, 494747a1685fSDinh Nguyen val & FIFOSIZE_STARTADDR_MASK); 494847a1685fSDinh Nguyen } 494947a1685fSDinh Nguyen 4950364f8e93SMian Yousaf Kaukab for (idx = 0; idx < hsotg->num_of_eps; idx++) { 495147a1685fSDinh Nguyen dev_info(dev, 495247a1685fSDinh Nguyen "ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx, 4953f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPCTL(idx)), 4954f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPTSIZ(idx)), 4955f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPDMA(idx))); 495647a1685fSDinh Nguyen 4957f25c42b8SGevorg Sahakyan val = dwc2_readl(hsotg, DOEPCTL(idx)); 495847a1685fSDinh Nguyen dev_info(dev, 495947a1685fSDinh Nguyen "ep%d-out: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", 4960f25c42b8SGevorg Sahakyan idx, dwc2_readl(hsotg, DOEPCTL(idx)), 4961f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DOEPTSIZ(idx)), 4962f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DOEPDMA(idx))); 496347a1685fSDinh Nguyen } 496447a1685fSDinh Nguyen 496547a1685fSDinh Nguyen dev_info(dev, "DVBUSDIS=0x%08x, DVBUSPULSE=%08x\n", 4966f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DVBUSDIS), dwc2_readl(hsotg, DVBUSPULSE)); 496747a1685fSDinh Nguyen #endif 496847a1685fSDinh Nguyen } 496947a1685fSDinh Nguyen 497047a1685fSDinh Nguyen /** 4971117777b2SDinh Nguyen * dwc2_gadget_init - init function for gadget 49726fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller 49736fb914d7SGrigor Tovmasyan * 497447a1685fSDinh Nguyen */ 4975f3768997SVardan Mikayelyan int dwc2_gadget_init(struct dwc2_hsotg *hsotg) 497647a1685fSDinh Nguyen { 4977117777b2SDinh Nguyen struct device *dev = hsotg->dev; 497847a1685fSDinh Nguyen int epnum; 497947a1685fSDinh Nguyen int ret; 498043e90349SJohn Youn 49810a176279SGregory Herrero /* Dump fifo information */ 49820a176279SGregory Herrero dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n", 498305ee799fSJohn Youn hsotg->params.g_np_tx_fifo_size); 498405ee799fSJohn Youn dev_dbg(dev, "RXFIFO size: %d\n", hsotg->params.g_rx_fifo_size); 498547a1685fSDinh Nguyen 498692ef98a4SJohn Keeping switch (hsotg->params.speed) { 498792ef98a4SJohn Keeping case DWC2_SPEED_PARAM_LOW: 498892ef98a4SJohn Keeping hsotg->gadget.max_speed = USB_SPEED_LOW; 498992ef98a4SJohn Keeping break; 499092ef98a4SJohn Keeping case DWC2_SPEED_PARAM_FULL: 499192ef98a4SJohn Keeping hsotg->gadget.max_speed = USB_SPEED_FULL; 499292ef98a4SJohn Keeping break; 499392ef98a4SJohn Keeping default: 499447a1685fSDinh Nguyen hsotg->gadget.max_speed = USB_SPEED_HIGH; 499592ef98a4SJohn Keeping break; 499692ef98a4SJohn Keeping } 499792ef98a4SJohn Keeping 49981f91b4ccSFelipe Balbi hsotg->gadget.ops = &dwc2_hsotg_gadget_ops; 499947a1685fSDinh Nguyen hsotg->gadget.name = dev_name(dev); 5000f5c8a6cbSFabrice Gasnier hsotg->gadget.otg_caps = &hsotg->params.otg_caps; 5001fa389a6dSVardan Mikayelyan hsotg->remote_wakeup_allowed = 0; 50027455f8b7SJohn Youn 50037455f8b7SJohn Youn if (hsotg->params.lpm) 50047455f8b7SJohn Youn hsotg->gadget.lpm_capable = true; 50057455f8b7SJohn Youn 5006097ee662SGregory Herrero if (hsotg->dr_mode == USB_DR_MODE_OTG) 5007097ee662SGregory Herrero hsotg->gadget.is_otg = 1; 5008ec4cc657SMian Yousaf Kaukab else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) 5009ec4cc657SMian Yousaf Kaukab hsotg->op_state = OTG_STATE_B_PERIPHERAL; 501047a1685fSDinh Nguyen 50111f91b4ccSFelipe Balbi ret = dwc2_hsotg_hw_cfg(hsotg); 5012c6f5c050SMian Yousaf Kaukab if (ret) { 5013c6f5c050SMian Yousaf Kaukab dev_err(hsotg->dev, "Hardware configuration failed: %d\n", ret); 501409a75e85SMarek Szyprowski return ret; 5015c6f5c050SMian Yousaf Kaukab } 5016c6f5c050SMian Yousaf Kaukab 50173f95001dSMian Yousaf Kaukab hsotg->ctrl_buff = devm_kzalloc(hsotg->dev, 50183f95001dSMian Yousaf Kaukab DWC2_CTRL_BUFF_SIZE, GFP_KERNEL); 50198bae0f8cSWolfram Sang if (!hsotg->ctrl_buff) 502009a75e85SMarek Szyprowski return -ENOMEM; 50213f95001dSMian Yousaf Kaukab 50223f95001dSMian Yousaf Kaukab hsotg->ep0_buff = devm_kzalloc(hsotg->dev, 50233f95001dSMian Yousaf Kaukab DWC2_CTRL_BUFF_SIZE, GFP_KERNEL); 50248bae0f8cSWolfram Sang if (!hsotg->ep0_buff) 502509a75e85SMarek Szyprowski return -ENOMEM; 50263f95001dSMian Yousaf Kaukab 50270f6b80c0SVahram Aharonyan if (using_desc_dma(hsotg)) { 50280f6b80c0SVahram Aharonyan ret = dwc2_gadget_alloc_ctrl_desc_chains(hsotg); 50290f6b80c0SVahram Aharonyan if (ret < 0) 50300f6b80c0SVahram Aharonyan return ret; 50310f6b80c0SVahram Aharonyan } 50320f6b80c0SVahram Aharonyan 5033f3768997SVardan Mikayelyan ret = devm_request_irq(hsotg->dev, hsotg->irq, dwc2_hsotg_irq, 5034f3768997SVardan Mikayelyan IRQF_SHARED, dev_name(hsotg->dev), hsotg); 5035eb3c56c5SMarek Szyprowski if (ret < 0) { 5036db8178c3SDinh Nguyen dev_err(dev, "cannot claim IRQ for gadget\n"); 503709a75e85SMarek Szyprowski return ret; 5038eb3c56c5SMarek Szyprowski } 5039eb3c56c5SMarek Szyprowski 504047a1685fSDinh Nguyen /* hsotg->num_of_eps holds number of EPs other than ep0 */ 504147a1685fSDinh Nguyen 504247a1685fSDinh Nguyen if (hsotg->num_of_eps == 0) { 504347a1685fSDinh Nguyen dev_err(dev, "wrong number of EPs (zero)\n"); 504409a75e85SMarek Szyprowski return -EINVAL; 504547a1685fSDinh Nguyen } 504647a1685fSDinh Nguyen 504747a1685fSDinh Nguyen /* setup endpoint information */ 504847a1685fSDinh Nguyen 504947a1685fSDinh Nguyen INIT_LIST_HEAD(&hsotg->gadget.ep_list); 5050c6f5c050SMian Yousaf Kaukab hsotg->gadget.ep0 = &hsotg->eps_out[0]->ep; 505147a1685fSDinh Nguyen 505247a1685fSDinh Nguyen /* allocate EP0 request */ 505347a1685fSDinh Nguyen 50541f91b4ccSFelipe Balbi hsotg->ctrl_req = dwc2_hsotg_ep_alloc_request(&hsotg->eps_out[0]->ep, 505547a1685fSDinh Nguyen GFP_KERNEL); 505647a1685fSDinh Nguyen if (!hsotg->ctrl_req) { 505747a1685fSDinh Nguyen dev_err(dev, "failed to allocate ctrl req\n"); 505809a75e85SMarek Szyprowski return -ENOMEM; 505947a1685fSDinh Nguyen } 506047a1685fSDinh Nguyen 506147a1685fSDinh Nguyen /* initialise the endpoints now the core has been initialised */ 5062c6f5c050SMian Yousaf Kaukab for (epnum = 0; epnum < hsotg->num_of_eps; epnum++) { 5063c6f5c050SMian Yousaf Kaukab if (hsotg->eps_in[epnum]) 50641f91b4ccSFelipe Balbi dwc2_hsotg_initep(hsotg, hsotg->eps_in[epnum], 5065c6f5c050SMian Yousaf Kaukab epnum, 1); 5066c6f5c050SMian Yousaf Kaukab if (hsotg->eps_out[epnum]) 50671f91b4ccSFelipe Balbi dwc2_hsotg_initep(hsotg, hsotg->eps_out[epnum], 5068c6f5c050SMian Yousaf Kaukab epnum, 0); 5069c6f5c050SMian Yousaf Kaukab } 507047a1685fSDinh Nguyen 50711f91b4ccSFelipe Balbi dwc2_hsotg_dump(hsotg); 507247a1685fSDinh Nguyen 507347a1685fSDinh Nguyen return 0; 507447a1685fSDinh Nguyen } 507547a1685fSDinh Nguyen 507647a1685fSDinh Nguyen /** 50771f91b4ccSFelipe Balbi * dwc2_hsotg_remove - remove function for hsotg driver 50786fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller 50796fb914d7SGrigor Tovmasyan * 508047a1685fSDinh Nguyen */ 50811f91b4ccSFelipe Balbi int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg) 508247a1685fSDinh Nguyen { 508347a1685fSDinh Nguyen usb_del_gadget_udc(&hsotg->gadget); 50849bb073a0SGrigor Tovmasyan dwc2_hsotg_ep_free_request(&hsotg->eps_out[0]->ep, hsotg->ctrl_req); 508547a1685fSDinh Nguyen 508647a1685fSDinh Nguyen return 0; 508747a1685fSDinh Nguyen } 508847a1685fSDinh Nguyen 50891f91b4ccSFelipe Balbi int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg) 509047a1685fSDinh Nguyen { 509147a1685fSDinh Nguyen unsigned long flags; 509247a1685fSDinh Nguyen 50939e779778SGregory Herrero if (hsotg->lx_state != DWC2_L0) 509409a75e85SMarek Szyprowski return 0; 50959e779778SGregory Herrero 5096dc6e69e6SMarek Szyprowski if (hsotg->driver) { 5097dc6e69e6SMarek Szyprowski int ep; 5098dc6e69e6SMarek Szyprowski 509947a1685fSDinh Nguyen dev_info(hsotg->dev, "suspending usb gadget %s\n", 510047a1685fSDinh Nguyen hsotg->driver->driver.name); 510147a1685fSDinh Nguyen 510247a1685fSDinh Nguyen spin_lock_irqsave(&hsotg->lock, flags); 5103dc6e69e6SMarek Szyprowski if (hsotg->enabled) 51041f91b4ccSFelipe Balbi dwc2_hsotg_core_disconnect(hsotg); 51051f91b4ccSFelipe Balbi dwc2_hsotg_disconnect(hsotg); 510647a1685fSDinh Nguyen hsotg->gadget.speed = USB_SPEED_UNKNOWN; 510747a1685fSDinh Nguyen spin_unlock_irqrestore(&hsotg->lock, flags); 510847a1685fSDinh Nguyen 5109ac55d163SAmelie Delaunay for (ep = 1; ep < hsotg->num_of_eps; ep++) { 5110c6f5c050SMian Yousaf Kaukab if (hsotg->eps_in[ep]) 51114fe4f9feSMinas Harutyunyan dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep); 5112c6f5c050SMian Yousaf Kaukab if (hsotg->eps_out[ep]) 51134fe4f9feSMinas Harutyunyan dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep); 5114c6f5c050SMian Yousaf Kaukab } 511547a1685fSDinh Nguyen } 511647a1685fSDinh Nguyen 511709a75e85SMarek Szyprowski return 0; 511847a1685fSDinh Nguyen } 511947a1685fSDinh Nguyen 51201f91b4ccSFelipe Balbi int dwc2_hsotg_resume(struct dwc2_hsotg *hsotg) 512147a1685fSDinh Nguyen { 512247a1685fSDinh Nguyen unsigned long flags; 512347a1685fSDinh Nguyen 51249e779778SGregory Herrero if (hsotg->lx_state == DWC2_L2) 512509a75e85SMarek Szyprowski return 0; 51269e779778SGregory Herrero 512747a1685fSDinh Nguyen if (hsotg->driver) { 512847a1685fSDinh Nguyen dev_info(hsotg->dev, "resuming usb gadget %s\n", 512947a1685fSDinh Nguyen hsotg->driver->driver.name); 5130d00b4142SRobert Baldyga 513147a1685fSDinh Nguyen spin_lock_irqsave(&hsotg->lock, flags); 51321f91b4ccSFelipe Balbi dwc2_hsotg_core_init_disconnected(hsotg, false); 513366e77a24SRazmik Karapetyan if (hsotg->enabled) { 513466e77a24SRazmik Karapetyan /* Enable ACG feature in device mode,if supported */ 513566e77a24SRazmik Karapetyan dwc2_enable_acg(hsotg); 51361f91b4ccSFelipe Balbi dwc2_hsotg_core_connect(hsotg); 513766e77a24SRazmik Karapetyan } 513847a1685fSDinh Nguyen spin_unlock_irqrestore(&hsotg->lock, flags); 5139dc6e69e6SMarek Szyprowski } 514047a1685fSDinh Nguyen 514109a75e85SMarek Szyprowski return 0; 514247a1685fSDinh Nguyen } 514358e52ff6SJohn Youn 514458e52ff6SJohn Youn /** 514558e52ff6SJohn Youn * dwc2_backup_device_registers() - Backup controller device registers. 514658e52ff6SJohn Youn * When suspending usb bus, registers needs to be backuped 514758e52ff6SJohn Youn * if controller power is disabled once suspended. 514858e52ff6SJohn Youn * 514958e52ff6SJohn Youn * @hsotg: Programming view of the DWC_otg controller 515058e52ff6SJohn Youn */ 515158e52ff6SJohn Youn int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) 515258e52ff6SJohn Youn { 515358e52ff6SJohn Youn struct dwc2_dregs_backup *dr; 515458e52ff6SJohn Youn int i; 515558e52ff6SJohn Youn 515658e52ff6SJohn Youn dev_dbg(hsotg->dev, "%s\n", __func__); 515758e52ff6SJohn Youn 515858e52ff6SJohn Youn /* Backup dev regs */ 515958e52ff6SJohn Youn dr = &hsotg->dr_backup; 516058e52ff6SJohn Youn 5161f25c42b8SGevorg Sahakyan dr->dcfg = dwc2_readl(hsotg, DCFG); 5162f25c42b8SGevorg Sahakyan dr->dctl = dwc2_readl(hsotg, DCTL); 5163f25c42b8SGevorg Sahakyan dr->daintmsk = dwc2_readl(hsotg, DAINTMSK); 5164f25c42b8SGevorg Sahakyan dr->diepmsk = dwc2_readl(hsotg, DIEPMSK); 5165f25c42b8SGevorg Sahakyan dr->doepmsk = dwc2_readl(hsotg, DOEPMSK); 516658e52ff6SJohn Youn 516758e52ff6SJohn Youn for (i = 0; i < hsotg->num_of_eps; i++) { 516858e52ff6SJohn Youn /* Backup IN EPs */ 5169f25c42b8SGevorg Sahakyan dr->diepctl[i] = dwc2_readl(hsotg, DIEPCTL(i)); 517058e52ff6SJohn Youn 517158e52ff6SJohn Youn /* Ensure DATA PID is correctly configured */ 517258e52ff6SJohn Youn if (dr->diepctl[i] & DXEPCTL_DPID) 517358e52ff6SJohn Youn dr->diepctl[i] |= DXEPCTL_SETD1PID; 517458e52ff6SJohn Youn else 517558e52ff6SJohn Youn dr->diepctl[i] |= DXEPCTL_SETD0PID; 517658e52ff6SJohn Youn 5177f25c42b8SGevorg Sahakyan dr->dieptsiz[i] = dwc2_readl(hsotg, DIEPTSIZ(i)); 5178f25c42b8SGevorg Sahakyan dr->diepdma[i] = dwc2_readl(hsotg, DIEPDMA(i)); 517958e52ff6SJohn Youn 518058e52ff6SJohn Youn /* Backup OUT EPs */ 5181f25c42b8SGevorg Sahakyan dr->doepctl[i] = dwc2_readl(hsotg, DOEPCTL(i)); 518258e52ff6SJohn Youn 518358e52ff6SJohn Youn /* Ensure DATA PID is correctly configured */ 518458e52ff6SJohn Youn if (dr->doepctl[i] & DXEPCTL_DPID) 518558e52ff6SJohn Youn dr->doepctl[i] |= DXEPCTL_SETD1PID; 518658e52ff6SJohn Youn else 518758e52ff6SJohn Youn dr->doepctl[i] |= DXEPCTL_SETD0PID; 518858e52ff6SJohn Youn 5189f25c42b8SGevorg Sahakyan dr->doeptsiz[i] = dwc2_readl(hsotg, DOEPTSIZ(i)); 5190f25c42b8SGevorg Sahakyan dr->doepdma[i] = dwc2_readl(hsotg, DOEPDMA(i)); 5191f25c42b8SGevorg Sahakyan dr->dtxfsiz[i] = dwc2_readl(hsotg, DPTXFSIZN(i)); 519258e52ff6SJohn Youn } 519358e52ff6SJohn Youn dr->valid = true; 519458e52ff6SJohn Youn return 0; 519558e52ff6SJohn Youn } 519658e52ff6SJohn Youn 519758e52ff6SJohn Youn /** 519858e52ff6SJohn Youn * dwc2_restore_device_registers() - Restore controller device registers. 519958e52ff6SJohn Youn * When resuming usb bus, device registers needs to be restored 520058e52ff6SJohn Youn * if controller power were disabled. 520158e52ff6SJohn Youn * 520258e52ff6SJohn Youn * @hsotg: Programming view of the DWC_otg controller 52039a5d2816SVardan Mikayelyan * @remote_wakeup: Indicates whether resume is initiated by Device or Host. 52049a5d2816SVardan Mikayelyan * 52059a5d2816SVardan Mikayelyan * Return: 0 if successful, negative error code otherwise 520658e52ff6SJohn Youn */ 52079a5d2816SVardan Mikayelyan int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup) 520858e52ff6SJohn Youn { 520958e52ff6SJohn Youn struct dwc2_dregs_backup *dr; 521058e52ff6SJohn Youn int i; 521158e52ff6SJohn Youn 521258e52ff6SJohn Youn dev_dbg(hsotg->dev, "%s\n", __func__); 521358e52ff6SJohn Youn 521458e52ff6SJohn Youn /* Restore dev regs */ 521558e52ff6SJohn Youn dr = &hsotg->dr_backup; 521658e52ff6SJohn Youn if (!dr->valid) { 521758e52ff6SJohn Youn dev_err(hsotg->dev, "%s: no device registers to restore\n", 521858e52ff6SJohn Youn __func__); 521958e52ff6SJohn Youn return -EINVAL; 522058e52ff6SJohn Youn } 522158e52ff6SJohn Youn dr->valid = false; 522258e52ff6SJohn Youn 52239a5d2816SVardan Mikayelyan if (!remote_wakeup) 5224f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dctl, DCTL); 52259a5d2816SVardan Mikayelyan 5226f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->daintmsk, DAINTMSK); 5227f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->diepmsk, DIEPMSK); 5228f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doepmsk, DOEPMSK); 522958e52ff6SJohn Youn 523058e52ff6SJohn Youn for (i = 0; i < hsotg->num_of_eps; i++) { 523158e52ff6SJohn Youn /* Restore IN EPs */ 5232f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dieptsiz[i], DIEPTSIZ(i)); 5233f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->diepdma[i], DIEPDMA(i)); 5234f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doeptsiz[i], DOEPTSIZ(i)); 52359a5d2816SVardan Mikayelyan /** WA for enabled EPx's IN in DDMA mode. On entering to 52369a5d2816SVardan Mikayelyan * hibernation wrong value read and saved from DIEPDMAx, 52379a5d2816SVardan Mikayelyan * as result BNA interrupt asserted on hibernation exit 52389a5d2816SVardan Mikayelyan * by restoring from saved area. 52399a5d2816SVardan Mikayelyan */ 5240c4bc515dSJohn Keeping if (using_desc_dma(hsotg) && 52419a5d2816SVardan Mikayelyan (dr->diepctl[i] & DXEPCTL_EPENA)) 52429a5d2816SVardan Mikayelyan dr->diepdma[i] = hsotg->eps_in[i]->desc_list_dma; 5243f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dtxfsiz[i], DPTXFSIZN(i)); 5244f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->diepctl[i], DIEPCTL(i)); 52459a5d2816SVardan Mikayelyan /* Restore OUT EPs */ 5246f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doeptsiz[i], DOEPTSIZ(i)); 52479a5d2816SVardan Mikayelyan /* WA for enabled EPx's OUT in DDMA mode. On entering to 52489a5d2816SVardan Mikayelyan * hibernation wrong value read and saved from DOEPDMAx, 52499a5d2816SVardan Mikayelyan * as result BNA interrupt asserted on hibernation exit 52509a5d2816SVardan Mikayelyan * by restoring from saved area. 52519a5d2816SVardan Mikayelyan */ 5252c4bc515dSJohn Keeping if (using_desc_dma(hsotg) && 52539a5d2816SVardan Mikayelyan (dr->doepctl[i] & DXEPCTL_EPENA)) 52549a5d2816SVardan Mikayelyan dr->doepdma[i] = hsotg->eps_out[i]->desc_list_dma; 5255f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doepdma[i], DOEPDMA(i)); 5256f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doepctl[i], DOEPCTL(i)); 525758e52ff6SJohn Youn } 525858e52ff6SJohn Youn 525958e52ff6SJohn Youn return 0; 526058e52ff6SJohn Youn } 526121b03405SSevak Arakelyan 526221b03405SSevak Arakelyan /** 526321b03405SSevak Arakelyan * dwc2_gadget_init_lpm - Configure the core to support LPM in device mode 526421b03405SSevak Arakelyan * 526521b03405SSevak Arakelyan * @hsotg: Programming view of DWC_otg controller 526621b03405SSevak Arakelyan * 526721b03405SSevak Arakelyan */ 526821b03405SSevak Arakelyan void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) 526921b03405SSevak Arakelyan { 527021b03405SSevak Arakelyan u32 val; 527121b03405SSevak Arakelyan 527221b03405SSevak Arakelyan if (!hsotg->params.lpm) 527321b03405SSevak Arakelyan return; 527421b03405SSevak Arakelyan 527521b03405SSevak Arakelyan val = GLPMCFG_LPMCAP | GLPMCFG_APPL1RES; 527621b03405SSevak Arakelyan val |= hsotg->params.hird_threshold_en ? GLPMCFG_HIRD_THRES_EN : 0; 527721b03405SSevak Arakelyan val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0; 527821b03405SSevak Arakelyan val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT; 527921b03405SSevak Arakelyan val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0; 528046637565SMinas Harutyunyan val |= GLPMCFG_LPM_REJECT_CTRL_CONTROL; 52819aed8c08SArtur Petrosyan val |= GLPMCFG_LPM_ACCEPT_CTRL_ISOC; 5282f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, val, GLPMCFG); 5283f25c42b8SGevorg Sahakyan dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG)); 52844abe4537SGrigor Tovmasyan 52854abe4537SGrigor Tovmasyan /* Unmask WKUP_ALERT Interrupt */ 52864abe4537SGrigor Tovmasyan if (hsotg->params.service_interval) 52874abe4537SGrigor Tovmasyan dwc2_set_bit(hsotg, GINTMSK2, GINTMSK2_WKUP_ALERT_INT_MSK); 528821b03405SSevak Arakelyan } 5289c5c403dcSVardan Mikayelyan 5290c5c403dcSVardan Mikayelyan /** 529115d9dbf8SGrigor Tovmasyan * dwc2_gadget_program_ref_clk - Program GREFCLK register in device mode 529215d9dbf8SGrigor Tovmasyan * 529315d9dbf8SGrigor Tovmasyan * @hsotg: Programming view of DWC_otg controller 529415d9dbf8SGrigor Tovmasyan * 529515d9dbf8SGrigor Tovmasyan */ 529615d9dbf8SGrigor Tovmasyan void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) 529715d9dbf8SGrigor Tovmasyan { 529815d9dbf8SGrigor Tovmasyan u32 val = 0; 529915d9dbf8SGrigor Tovmasyan 530015d9dbf8SGrigor Tovmasyan val |= GREFCLK_REF_CLK_MODE; 530115d9dbf8SGrigor Tovmasyan val |= hsotg->params.ref_clk_per << GREFCLK_REFCLKPER_SHIFT; 530215d9dbf8SGrigor Tovmasyan val |= hsotg->params.sof_cnt_wkup_alert << 530315d9dbf8SGrigor Tovmasyan GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT; 530415d9dbf8SGrigor Tovmasyan 530515d9dbf8SGrigor Tovmasyan dwc2_writel(hsotg, val, GREFCLK); 530615d9dbf8SGrigor Tovmasyan dev_dbg(hsotg->dev, "GREFCLK=0x%08x\n", dwc2_readl(hsotg, GREFCLK)); 530715d9dbf8SGrigor Tovmasyan } 530815d9dbf8SGrigor Tovmasyan 530915d9dbf8SGrigor Tovmasyan /** 5310c5c403dcSVardan Mikayelyan * dwc2_gadget_enter_hibernation() - Put controller in Hibernation. 5311c5c403dcSVardan Mikayelyan * 5312c5c403dcSVardan Mikayelyan * @hsotg: Programming view of the DWC_otg controller 5313c5c403dcSVardan Mikayelyan * 5314c5c403dcSVardan Mikayelyan * Return non-zero if failed to enter to hibernation. 5315c5c403dcSVardan Mikayelyan */ 5316c5c403dcSVardan Mikayelyan int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) 5317c5c403dcSVardan Mikayelyan { 5318c5c403dcSVardan Mikayelyan u32 gpwrdn; 5319c5c403dcSVardan Mikayelyan int ret = 0; 5320c5c403dcSVardan Mikayelyan 5321c5c403dcSVardan Mikayelyan /* Change to L2(suspend) state */ 5322c5c403dcSVardan Mikayelyan hsotg->lx_state = DWC2_L2; 5323c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev, "Start of hibernation completed\n"); 5324c5c403dcSVardan Mikayelyan ret = dwc2_backup_global_registers(hsotg); 5325c5c403dcSVardan Mikayelyan if (ret) { 5326c5c403dcSVardan Mikayelyan dev_err(hsotg->dev, "%s: failed to backup global registers\n", 5327c5c403dcSVardan Mikayelyan __func__); 5328c5c403dcSVardan Mikayelyan return ret; 5329c5c403dcSVardan Mikayelyan } 5330c5c403dcSVardan Mikayelyan ret = dwc2_backup_device_registers(hsotg); 5331c5c403dcSVardan Mikayelyan if (ret) { 5332c5c403dcSVardan Mikayelyan dev_err(hsotg->dev, "%s: failed to backup device registers\n", 5333c5c403dcSVardan Mikayelyan __func__); 5334c5c403dcSVardan Mikayelyan return ret; 5335c5c403dcSVardan Mikayelyan } 5336c5c403dcSVardan Mikayelyan 5337c5c403dcSVardan Mikayelyan gpwrdn = GPWRDN_PWRDNRSTN; 5338c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_PMUACTV; 5339f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN); 5340c5c403dcSVardan Mikayelyan udelay(10); 5341c5c403dcSVardan Mikayelyan 5342c5c403dcSVardan Mikayelyan /* Set flag to indicate that we are in hibernation */ 5343c5c403dcSVardan Mikayelyan hsotg->hibernated = 1; 5344c5c403dcSVardan Mikayelyan 5345c5c403dcSVardan Mikayelyan /* Enable interrupts from wake up logic */ 5346f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN); 5347c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_PMUINTSEL; 5348f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN); 5349c5c403dcSVardan Mikayelyan udelay(10); 5350c5c403dcSVardan Mikayelyan 5351c5c403dcSVardan Mikayelyan /* Unmask device mode interrupts in GPWRDN */ 5352f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN); 5353c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_RST_DET_MSK; 5354c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_LNSTSCHG_MSK; 5355c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_STS_CHGINT_MSK; 5356f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN); 5357c5c403dcSVardan Mikayelyan udelay(10); 5358c5c403dcSVardan Mikayelyan 5359c5c403dcSVardan Mikayelyan /* Enable Power Down Clamp */ 5360f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN); 5361c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_PWRDNCLMP; 5362f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN); 5363c5c403dcSVardan Mikayelyan udelay(10); 5364c5c403dcSVardan Mikayelyan 5365c5c403dcSVardan Mikayelyan /* Switch off VDD */ 5366f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN); 5367c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_PWRDNSWTCH; 5368f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN); 5369c5c403dcSVardan Mikayelyan udelay(10); 5370c5c403dcSVardan Mikayelyan 5371c5c403dcSVardan Mikayelyan /* Save gpwrdn register for further usage if stschng interrupt */ 5372f25c42b8SGevorg Sahakyan hsotg->gr_backup.gpwrdn = dwc2_readl(hsotg, GPWRDN); 5373c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev, "Hibernation completed\n"); 5374c5c403dcSVardan Mikayelyan 5375c5c403dcSVardan Mikayelyan return ret; 5376c5c403dcSVardan Mikayelyan } 5377c5c403dcSVardan Mikayelyan 5378c5c403dcSVardan Mikayelyan /** 5379c5c403dcSVardan Mikayelyan * dwc2_gadget_exit_hibernation() 5380c5c403dcSVardan Mikayelyan * This function is for exiting from Device mode hibernation by host initiated 5381c5c403dcSVardan Mikayelyan * resume/reset and device initiated remote-wakeup. 5382c5c403dcSVardan Mikayelyan * 5383c5c403dcSVardan Mikayelyan * @hsotg: Programming view of the DWC_otg controller 5384c5c403dcSVardan Mikayelyan * @rem_wakeup: indicates whether resume is initiated by Device or Host. 53856fb914d7SGrigor Tovmasyan * @reset: indicates whether resume is initiated by Reset. 5386c5c403dcSVardan Mikayelyan * 5387c5c403dcSVardan Mikayelyan * Return non-zero if failed to exit from hibernation. 5388c5c403dcSVardan Mikayelyan */ 5389c5c403dcSVardan Mikayelyan int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, 5390c5c403dcSVardan Mikayelyan int rem_wakeup, int reset) 5391c5c403dcSVardan Mikayelyan { 5392c5c403dcSVardan Mikayelyan u32 pcgcctl; 5393c5c403dcSVardan Mikayelyan u32 gpwrdn; 5394c5c403dcSVardan Mikayelyan u32 dctl; 5395c5c403dcSVardan Mikayelyan int ret = 0; 5396c5c403dcSVardan Mikayelyan struct dwc2_gregs_backup *gr; 5397c5c403dcSVardan Mikayelyan struct dwc2_dregs_backup *dr; 5398c5c403dcSVardan Mikayelyan 5399c5c403dcSVardan Mikayelyan gr = &hsotg->gr_backup; 5400c5c403dcSVardan Mikayelyan dr = &hsotg->dr_backup; 5401c5c403dcSVardan Mikayelyan 5402c5c403dcSVardan Mikayelyan if (!hsotg->hibernated) { 5403c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev, "Already exited from Hibernation\n"); 5404c5c403dcSVardan Mikayelyan return 1; 5405c5c403dcSVardan Mikayelyan } 5406c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev, 5407c5c403dcSVardan Mikayelyan "%s: called with rem_wakeup = %d reset = %d\n", 5408c5c403dcSVardan Mikayelyan __func__, rem_wakeup, reset); 5409c5c403dcSVardan Mikayelyan 5410c5c403dcSVardan Mikayelyan dwc2_hib_restore_common(hsotg, rem_wakeup, 0); 5411c5c403dcSVardan Mikayelyan 5412c5c403dcSVardan Mikayelyan if (!reset) { 5413c5c403dcSVardan Mikayelyan /* Clear all pending interupts */ 5414f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, 0xffffffff, GINTSTS); 5415c5c403dcSVardan Mikayelyan } 5416c5c403dcSVardan Mikayelyan 5417c5c403dcSVardan Mikayelyan /* De-assert Restore */ 5418f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN); 5419c5c403dcSVardan Mikayelyan gpwrdn &= ~GPWRDN_RESTORE; 5420f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN); 5421c5c403dcSVardan Mikayelyan udelay(10); 5422c5c403dcSVardan Mikayelyan 5423c5c403dcSVardan Mikayelyan if (!rem_wakeup) { 5424f25c42b8SGevorg Sahakyan pcgcctl = dwc2_readl(hsotg, PCGCTL); 5425c5c403dcSVardan Mikayelyan pcgcctl &= ~PCGCTL_RSTPDWNMODULE; 5426f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, pcgcctl, PCGCTL); 5427c5c403dcSVardan Mikayelyan } 5428c5c403dcSVardan Mikayelyan 5429c5c403dcSVardan Mikayelyan /* Restore GUSBCFG, DCFG and DCTL */ 5430f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG); 5431f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dcfg, DCFG); 5432f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dctl, DCTL); 5433c5c403dcSVardan Mikayelyan 5434b29b494bSArtur Petrosyan /* On USB Reset, reset device address to zero */ 5435b29b494bSArtur Petrosyan if (reset) 5436b29b494bSArtur Petrosyan dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK); 5437b29b494bSArtur Petrosyan 5438c5c403dcSVardan Mikayelyan /* De-assert Wakeup Logic */ 5439f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN); 5440c5c403dcSVardan Mikayelyan gpwrdn &= ~GPWRDN_PMUACTV; 5441f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN); 5442c5c403dcSVardan Mikayelyan 5443c5c403dcSVardan Mikayelyan if (rem_wakeup) { 5444c5c403dcSVardan Mikayelyan udelay(10); 5445c5c403dcSVardan Mikayelyan /* Start Remote Wakeup Signaling */ 5446f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dctl | DCTL_RMTWKUPSIG, DCTL); 5447c5c403dcSVardan Mikayelyan } else { 5448c5c403dcSVardan Mikayelyan udelay(50); 5449c5c403dcSVardan Mikayelyan /* Set Device programming done bit */ 5450f25c42b8SGevorg Sahakyan dctl = dwc2_readl(hsotg, DCTL); 5451c5c403dcSVardan Mikayelyan dctl |= DCTL_PWRONPRGDONE; 5452f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dctl, DCTL); 5453c5c403dcSVardan Mikayelyan } 5454c5c403dcSVardan Mikayelyan /* Wait for interrupts which must be cleared */ 5455c5c403dcSVardan Mikayelyan mdelay(2); 5456c5c403dcSVardan Mikayelyan /* Clear all pending interupts */ 5457f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, 0xffffffff, GINTSTS); 5458c5c403dcSVardan Mikayelyan 5459c5c403dcSVardan Mikayelyan /* Restore global registers */ 5460c5c403dcSVardan Mikayelyan ret = dwc2_restore_global_registers(hsotg); 5461c5c403dcSVardan Mikayelyan if (ret) { 5462c5c403dcSVardan Mikayelyan dev_err(hsotg->dev, "%s: failed to restore registers\n", 5463c5c403dcSVardan Mikayelyan __func__); 5464c5c403dcSVardan Mikayelyan return ret; 5465c5c403dcSVardan Mikayelyan } 5466c5c403dcSVardan Mikayelyan 5467c5c403dcSVardan Mikayelyan /* Restore device registers */ 5468c5c403dcSVardan Mikayelyan ret = dwc2_restore_device_registers(hsotg, rem_wakeup); 5469c5c403dcSVardan Mikayelyan if (ret) { 5470c5c403dcSVardan Mikayelyan dev_err(hsotg->dev, "%s: failed to restore device registers\n", 5471c5c403dcSVardan Mikayelyan __func__); 5472c5c403dcSVardan Mikayelyan return ret; 5473c5c403dcSVardan Mikayelyan } 5474c5c403dcSVardan Mikayelyan 5475c5c403dcSVardan Mikayelyan if (rem_wakeup) { 5476c5c403dcSVardan Mikayelyan mdelay(10); 5477f25c42b8SGevorg Sahakyan dctl = dwc2_readl(hsotg, DCTL); 5478c5c403dcSVardan Mikayelyan dctl &= ~DCTL_RMTWKUPSIG; 5479f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dctl, DCTL); 5480c5c403dcSVardan Mikayelyan } 5481c5c403dcSVardan Mikayelyan 5482c5c403dcSVardan Mikayelyan hsotg->hibernated = 0; 5483c5c403dcSVardan Mikayelyan hsotg->lx_state = DWC2_L0; 5484c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev, "Hibernation recovery completes here\n"); 5485c5c403dcSVardan Mikayelyan 5486c5c403dcSVardan Mikayelyan return ret; 5487c5c403dcSVardan Mikayelyan } 5488be2b960eSArtur Petrosyan 5489be2b960eSArtur Petrosyan /** 5490be2b960eSArtur Petrosyan * dwc2_gadget_enter_partial_power_down() - Put controller in partial 5491be2b960eSArtur Petrosyan * power down. 5492be2b960eSArtur Petrosyan * 5493be2b960eSArtur Petrosyan * @hsotg: Programming view of the DWC_otg controller 5494be2b960eSArtur Petrosyan * 5495be2b960eSArtur Petrosyan * Return: non-zero if failed to enter device partial power down. 5496be2b960eSArtur Petrosyan * 5497be2b960eSArtur Petrosyan * This function is for entering device mode partial power down. 5498be2b960eSArtur Petrosyan */ 5499be2b960eSArtur Petrosyan int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg) 5500be2b960eSArtur Petrosyan { 5501be2b960eSArtur Petrosyan u32 pcgcctl; 5502be2b960eSArtur Petrosyan int ret = 0; 5503be2b960eSArtur Petrosyan 5504be2b960eSArtur Petrosyan dev_dbg(hsotg->dev, "Entering device partial power down started.\n"); 5505be2b960eSArtur Petrosyan 5506be2b960eSArtur Petrosyan /* Backup all registers */ 5507be2b960eSArtur Petrosyan ret = dwc2_backup_global_registers(hsotg); 5508be2b960eSArtur Petrosyan if (ret) { 5509be2b960eSArtur Petrosyan dev_err(hsotg->dev, "%s: failed to backup global registers\n", 5510be2b960eSArtur Petrosyan __func__); 5511be2b960eSArtur Petrosyan return ret; 5512be2b960eSArtur Petrosyan } 5513be2b960eSArtur Petrosyan 5514be2b960eSArtur Petrosyan ret = dwc2_backup_device_registers(hsotg); 5515be2b960eSArtur Petrosyan if (ret) { 5516be2b960eSArtur Petrosyan dev_err(hsotg->dev, "%s: failed to backup device registers\n", 5517be2b960eSArtur Petrosyan __func__); 5518be2b960eSArtur Petrosyan return ret; 5519be2b960eSArtur Petrosyan } 5520be2b960eSArtur Petrosyan 5521be2b960eSArtur Petrosyan /* 5522be2b960eSArtur Petrosyan * Clear any pending interrupts since dwc2 will not be able to 5523be2b960eSArtur Petrosyan * clear them after entering partial_power_down. 5524be2b960eSArtur Petrosyan */ 5525be2b960eSArtur Petrosyan dwc2_writel(hsotg, 0xffffffff, GINTSTS); 5526be2b960eSArtur Petrosyan 5527be2b960eSArtur Petrosyan /* Put the controller in low power state */ 5528be2b960eSArtur Petrosyan pcgcctl = dwc2_readl(hsotg, PCGCTL); 5529be2b960eSArtur Petrosyan 5530be2b960eSArtur Petrosyan pcgcctl |= PCGCTL_PWRCLMP; 5531be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL); 5532be2b960eSArtur Petrosyan udelay(5); 5533be2b960eSArtur Petrosyan 5534be2b960eSArtur Petrosyan pcgcctl |= PCGCTL_RSTPDWNMODULE; 5535be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL); 5536be2b960eSArtur Petrosyan udelay(5); 5537be2b960eSArtur Petrosyan 5538be2b960eSArtur Petrosyan pcgcctl |= PCGCTL_STOPPCLK; 5539be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL); 5540be2b960eSArtur Petrosyan 5541be2b960eSArtur Petrosyan /* Set in_ppd flag to 1 as here core enters suspend. */ 5542be2b960eSArtur Petrosyan hsotg->in_ppd = 1; 5543be2b960eSArtur Petrosyan hsotg->lx_state = DWC2_L2; 5544be2b960eSArtur Petrosyan 5545be2b960eSArtur Petrosyan dev_dbg(hsotg->dev, "Entering device partial power down completed.\n"); 5546be2b960eSArtur Petrosyan 5547be2b960eSArtur Petrosyan return ret; 5548be2b960eSArtur Petrosyan } 5549be2b960eSArtur Petrosyan 5550be2b960eSArtur Petrosyan /* 5551be2b960eSArtur Petrosyan * dwc2_gadget_exit_partial_power_down() - Exit controller from device partial 5552be2b960eSArtur Petrosyan * power down. 5553be2b960eSArtur Petrosyan * 5554be2b960eSArtur Petrosyan * @hsotg: Programming view of the DWC_otg controller 5555be2b960eSArtur Petrosyan * @restore: indicates whether need to restore the registers or not. 5556be2b960eSArtur Petrosyan * 5557be2b960eSArtur Petrosyan * Return: non-zero if failed to exit device partial power down. 5558be2b960eSArtur Petrosyan * 5559be2b960eSArtur Petrosyan * This function is for exiting from device mode partial power down. 5560be2b960eSArtur Petrosyan */ 5561be2b960eSArtur Petrosyan int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, 5562be2b960eSArtur Petrosyan bool restore) 5563be2b960eSArtur Petrosyan { 5564be2b960eSArtur Petrosyan u32 pcgcctl; 5565be2b960eSArtur Petrosyan u32 dctl; 5566be2b960eSArtur Petrosyan struct dwc2_dregs_backup *dr; 5567be2b960eSArtur Petrosyan int ret = 0; 5568be2b960eSArtur Petrosyan 5569be2b960eSArtur Petrosyan dr = &hsotg->dr_backup; 5570be2b960eSArtur Petrosyan 5571be2b960eSArtur Petrosyan dev_dbg(hsotg->dev, "Exiting device partial Power Down started.\n"); 5572be2b960eSArtur Petrosyan 5573be2b960eSArtur Petrosyan pcgcctl = dwc2_readl(hsotg, PCGCTL); 5574be2b960eSArtur Petrosyan pcgcctl &= ~PCGCTL_STOPPCLK; 5575be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL); 5576be2b960eSArtur Petrosyan 5577be2b960eSArtur Petrosyan pcgcctl = dwc2_readl(hsotg, PCGCTL); 5578be2b960eSArtur Petrosyan pcgcctl &= ~PCGCTL_PWRCLMP; 5579be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL); 5580be2b960eSArtur Petrosyan 5581be2b960eSArtur Petrosyan pcgcctl = dwc2_readl(hsotg, PCGCTL); 5582be2b960eSArtur Petrosyan pcgcctl &= ~PCGCTL_RSTPDWNMODULE; 5583be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL); 5584be2b960eSArtur Petrosyan 5585be2b960eSArtur Petrosyan udelay(100); 5586be2b960eSArtur Petrosyan if (restore) { 5587be2b960eSArtur Petrosyan ret = dwc2_restore_global_registers(hsotg); 5588be2b960eSArtur Petrosyan if (ret) { 5589be2b960eSArtur Petrosyan dev_err(hsotg->dev, "%s: failed to restore registers\n", 5590be2b960eSArtur Petrosyan __func__); 5591be2b960eSArtur Petrosyan return ret; 5592be2b960eSArtur Petrosyan } 5593be2b960eSArtur Petrosyan /* Restore DCFG */ 5594be2b960eSArtur Petrosyan dwc2_writel(hsotg, dr->dcfg, DCFG); 5595be2b960eSArtur Petrosyan 5596be2b960eSArtur Petrosyan ret = dwc2_restore_device_registers(hsotg, 0); 5597be2b960eSArtur Petrosyan if (ret) { 5598be2b960eSArtur Petrosyan dev_err(hsotg->dev, "%s: failed to restore device registers\n", 5599be2b960eSArtur Petrosyan __func__); 5600be2b960eSArtur Petrosyan return ret; 5601be2b960eSArtur Petrosyan } 5602be2b960eSArtur Petrosyan } 5603be2b960eSArtur Petrosyan 5604be2b960eSArtur Petrosyan /* Set the Power-On Programming done bit */ 5605be2b960eSArtur Petrosyan dctl = dwc2_readl(hsotg, DCTL); 5606be2b960eSArtur Petrosyan dctl |= DCTL_PWRONPRGDONE; 5607be2b960eSArtur Petrosyan dwc2_writel(hsotg, dctl, DCTL); 5608be2b960eSArtur Petrosyan 5609be2b960eSArtur Petrosyan /* Set in_ppd flag to 0 as here core exits from suspend. */ 5610be2b960eSArtur Petrosyan hsotg->in_ppd = 0; 5611be2b960eSArtur Petrosyan hsotg->lx_state = DWC2_L0; 5612be2b960eSArtur Petrosyan 5613be2b960eSArtur Petrosyan dev_dbg(hsotg->dev, "Exiting device partial Power Down completed.\n"); 5614be2b960eSArtur Petrosyan return ret; 5615be2b960eSArtur Petrosyan } 5616012466fcSArtur Petrosyan 5617012466fcSArtur Petrosyan /** 5618012466fcSArtur Petrosyan * dwc2_gadget_enter_clock_gating() - Put controller in clock gating. 5619012466fcSArtur Petrosyan * 5620012466fcSArtur Petrosyan * @hsotg: Programming view of the DWC_otg controller 5621012466fcSArtur Petrosyan * 5622012466fcSArtur Petrosyan * Return: non-zero if failed to enter device partial power down. 5623012466fcSArtur Petrosyan * 5624012466fcSArtur Petrosyan * This function is for entering device mode clock gating. 5625012466fcSArtur Petrosyan */ 5626012466fcSArtur Petrosyan void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg) 5627012466fcSArtur Petrosyan { 5628012466fcSArtur Petrosyan u32 pcgctl; 5629012466fcSArtur Petrosyan 5630012466fcSArtur Petrosyan dev_dbg(hsotg->dev, "Entering device clock gating.\n"); 5631012466fcSArtur Petrosyan 5632012466fcSArtur Petrosyan /* Set the Phy Clock bit as suspend is received. */ 5633012466fcSArtur Petrosyan pcgctl = dwc2_readl(hsotg, PCGCTL); 5634012466fcSArtur Petrosyan pcgctl |= PCGCTL_STOPPCLK; 5635012466fcSArtur Petrosyan dwc2_writel(hsotg, pcgctl, PCGCTL); 5636012466fcSArtur Petrosyan udelay(5); 5637012466fcSArtur Petrosyan 5638012466fcSArtur Petrosyan /* Set the Gate hclk as suspend is received. */ 5639012466fcSArtur Petrosyan pcgctl = dwc2_readl(hsotg, PCGCTL); 5640012466fcSArtur Petrosyan pcgctl |= PCGCTL_GATEHCLK; 5641012466fcSArtur Petrosyan dwc2_writel(hsotg, pcgctl, PCGCTL); 5642012466fcSArtur Petrosyan udelay(5); 5643012466fcSArtur Petrosyan 5644012466fcSArtur Petrosyan hsotg->lx_state = DWC2_L2; 5645012466fcSArtur Petrosyan hsotg->bus_suspended = true; 5646012466fcSArtur Petrosyan } 5647012466fcSArtur Petrosyan 5648012466fcSArtur Petrosyan /* 5649012466fcSArtur Petrosyan * dwc2_gadget_exit_clock_gating() - Exit controller from device clock gating. 5650012466fcSArtur Petrosyan * 5651012466fcSArtur Petrosyan * @hsotg: Programming view of the DWC_otg controller 5652012466fcSArtur Petrosyan * @rem_wakeup: indicates whether remote wake up is enabled. 5653012466fcSArtur Petrosyan * 5654012466fcSArtur Petrosyan * This function is for exiting from device mode clock gating. 5655012466fcSArtur Petrosyan */ 5656012466fcSArtur Petrosyan void dwc2_gadget_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup) 5657012466fcSArtur Petrosyan { 5658012466fcSArtur Petrosyan u32 pcgctl; 5659012466fcSArtur Petrosyan u32 dctl; 5660012466fcSArtur Petrosyan 5661012466fcSArtur Petrosyan dev_dbg(hsotg->dev, "Exiting device clock gating.\n"); 5662012466fcSArtur Petrosyan 5663012466fcSArtur Petrosyan /* Clear the Gate hclk. */ 5664012466fcSArtur Petrosyan pcgctl = dwc2_readl(hsotg, PCGCTL); 5665012466fcSArtur Petrosyan pcgctl &= ~PCGCTL_GATEHCLK; 5666012466fcSArtur Petrosyan dwc2_writel(hsotg, pcgctl, PCGCTL); 5667012466fcSArtur Petrosyan udelay(5); 5668012466fcSArtur Petrosyan 5669012466fcSArtur Petrosyan /* Phy Clock bit. */ 5670012466fcSArtur Petrosyan pcgctl = dwc2_readl(hsotg, PCGCTL); 5671012466fcSArtur Petrosyan pcgctl &= ~PCGCTL_STOPPCLK; 5672012466fcSArtur Petrosyan dwc2_writel(hsotg, pcgctl, PCGCTL); 5673012466fcSArtur Petrosyan udelay(5); 5674012466fcSArtur Petrosyan 5675012466fcSArtur Petrosyan if (rem_wakeup) { 5676012466fcSArtur Petrosyan /* Set Remote Wakeup Signaling */ 5677012466fcSArtur Petrosyan dctl = dwc2_readl(hsotg, DCTL); 5678012466fcSArtur Petrosyan dctl |= DCTL_RMTWKUPSIG; 5679012466fcSArtur Petrosyan dwc2_writel(hsotg, dctl, DCTL); 5680012466fcSArtur Petrosyan } 5681012466fcSArtur Petrosyan 5682012466fcSArtur Petrosyan /* Change to L0 state */ 5683012466fcSArtur Petrosyan call_gadget(hsotg, resume); 5684012466fcSArtur Petrosyan hsotg->lx_state = DWC2_L0; 5685012466fcSArtur Petrosyan hsotg->bus_suspended = false; 5686012466fcSArtur Petrosyan } 5687