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 #include <linux/of_platform.h> 2647a1685fSDinh Nguyen 2747a1685fSDinh Nguyen #include <linux/usb/ch9.h> 2847a1685fSDinh Nguyen #include <linux/usb/gadget.h> 2947a1685fSDinh Nguyen #include <linux/usb/phy.h> 30b4c53b4aSMinas Harutyunyan #include <linux/usb/composite.h> 31b4c53b4aSMinas Harutyunyan 3247a1685fSDinh Nguyen 33f7c0b143SDinh Nguyen #include "core.h" 34941fcce4SDinh Nguyen #include "hw.h" 3547a1685fSDinh Nguyen 3647a1685fSDinh Nguyen /* conversion functions */ 371f91b4ccSFelipe Balbi static inline struct dwc2_hsotg_req *our_req(struct usb_request *req) 3847a1685fSDinh Nguyen { 391f91b4ccSFelipe Balbi return container_of(req, struct dwc2_hsotg_req, req); 4047a1685fSDinh Nguyen } 4147a1685fSDinh Nguyen 421f91b4ccSFelipe Balbi static inline struct dwc2_hsotg_ep *our_ep(struct usb_ep *ep) 4347a1685fSDinh Nguyen { 441f91b4ccSFelipe Balbi return container_of(ep, struct dwc2_hsotg_ep, ep); 4547a1685fSDinh Nguyen } 4647a1685fSDinh Nguyen 47941fcce4SDinh Nguyen static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget) 4847a1685fSDinh Nguyen { 49941fcce4SDinh Nguyen return container_of(gadget, struct dwc2_hsotg, gadget); 5047a1685fSDinh Nguyen } 5147a1685fSDinh Nguyen 52f25c42b8SGevorg Sahakyan static inline void dwc2_set_bit(struct dwc2_hsotg *hsotg, u32 offset, u32 val) 5347a1685fSDinh Nguyen { 54f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dwc2_readl(hsotg, offset) | val, offset); 5547a1685fSDinh Nguyen } 5647a1685fSDinh Nguyen 57f25c42b8SGevorg Sahakyan static inline void dwc2_clear_bit(struct dwc2_hsotg *hsotg, u32 offset, u32 val) 5847a1685fSDinh Nguyen { 59f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dwc2_readl(hsotg, offset) & ~val, offset); 6047a1685fSDinh Nguyen } 6147a1685fSDinh Nguyen 621f91b4ccSFelipe Balbi static inline struct dwc2_hsotg_ep *index_to_ep(struct dwc2_hsotg *hsotg, 63c6f5c050SMian Yousaf Kaukab u32 ep_index, u32 dir_in) 64c6f5c050SMian Yousaf Kaukab { 65c6f5c050SMian Yousaf Kaukab if (dir_in) 66c6f5c050SMian Yousaf Kaukab return hsotg->eps_in[ep_index]; 67c6f5c050SMian Yousaf Kaukab else 68c6f5c050SMian Yousaf Kaukab return hsotg->eps_out[ep_index]; 69c6f5c050SMian Yousaf Kaukab } 70c6f5c050SMian Yousaf Kaukab 71997f4f81SMickael Maison /* forward declaration of functions */ 721f91b4ccSFelipe Balbi static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg); 7347a1685fSDinh Nguyen 7447a1685fSDinh Nguyen /** 7547a1685fSDinh Nguyen * using_dma - return the DMA status of the driver. 7647a1685fSDinh Nguyen * @hsotg: The driver state. 7747a1685fSDinh Nguyen * 7847a1685fSDinh Nguyen * Return true if we're using DMA. 7947a1685fSDinh Nguyen * 8047a1685fSDinh Nguyen * Currently, we have the DMA support code worked into everywhere 8147a1685fSDinh Nguyen * that needs it, but the AMBA DMA implementation in the hardware can 8247a1685fSDinh Nguyen * only DMA from 32bit aligned addresses. This means that gadgets such 8347a1685fSDinh Nguyen * as the CDC Ethernet cannot work as they often pass packets which are 8447a1685fSDinh Nguyen * not 32bit aligned. 8547a1685fSDinh Nguyen * 8647a1685fSDinh Nguyen * Unfortunately the choice to use DMA or not is global to the controller 8747a1685fSDinh Nguyen * and seems to be only settable when the controller is being put through 8847a1685fSDinh Nguyen * a core reset. This means we either need to fix the gadgets to take 8947a1685fSDinh Nguyen * account of DMA alignment, or add bounce buffers (yuerk). 9047a1685fSDinh Nguyen * 91edd74be8SGregory Herrero * g_using_dma is set depending on dts flag. 9247a1685fSDinh Nguyen */ 93941fcce4SDinh Nguyen static inline bool using_dma(struct dwc2_hsotg *hsotg) 9447a1685fSDinh Nguyen { 9505ee799fSJohn Youn return hsotg->params.g_dma; 9647a1685fSDinh Nguyen } 9747a1685fSDinh Nguyen 98dec4b556SVahram Aharonyan /* 99dec4b556SVahram Aharonyan * using_desc_dma - return the descriptor DMA status of the driver. 100dec4b556SVahram Aharonyan * @hsotg: The driver state. 101dec4b556SVahram Aharonyan * 102dec4b556SVahram Aharonyan * Return true if we're using descriptor DMA. 103dec4b556SVahram Aharonyan */ 104dec4b556SVahram Aharonyan static inline bool using_desc_dma(struct dwc2_hsotg *hsotg) 105dec4b556SVahram Aharonyan { 106dec4b556SVahram Aharonyan return hsotg->params.g_dma_desc; 107dec4b556SVahram Aharonyan } 108dec4b556SVahram Aharonyan 10947a1685fSDinh Nguyen /** 11092d1635dSVardan Mikayelyan * dwc2_gadget_incr_frame_num - Increments the targeted frame number. 11192d1635dSVardan Mikayelyan * @hs_ep: The endpoint 11292d1635dSVardan Mikayelyan * 11392d1635dSVardan Mikayelyan * This function will also check if the frame number overruns DSTS_SOFFN_LIMIT. 11492d1635dSVardan Mikayelyan * If an overrun occurs it will wrap the value and set the frame_overrun flag. 11592d1635dSVardan Mikayelyan */ 11692d1635dSVardan Mikayelyan static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep) 11792d1635dSVardan Mikayelyan { 11892d1635dSVardan Mikayelyan hs_ep->target_frame += hs_ep->interval; 11992d1635dSVardan Mikayelyan if (hs_ep->target_frame > DSTS_SOFFN_LIMIT) { 120c1d5df69SGustavo A. R. Silva hs_ep->frame_overrun = true; 12192d1635dSVardan Mikayelyan hs_ep->target_frame &= DSTS_SOFFN_LIMIT; 12292d1635dSVardan Mikayelyan } else { 123c1d5df69SGustavo A. R. Silva hs_ep->frame_overrun = false; 12492d1635dSVardan Mikayelyan } 12592d1635dSVardan Mikayelyan } 12692d1635dSVardan Mikayelyan 12792d1635dSVardan Mikayelyan /** 1289d630b9cSGrigor Tovmasyan * dwc2_gadget_dec_frame_num_by_one - Decrements the targeted frame number 1299d630b9cSGrigor Tovmasyan * by one. 1309d630b9cSGrigor Tovmasyan * @hs_ep: The endpoint. 1319d630b9cSGrigor Tovmasyan * 1329d630b9cSGrigor Tovmasyan * This function used in service interval based scheduling flow to calculate 1339d630b9cSGrigor Tovmasyan * descriptor frame number filed value. For service interval mode frame 1349d630b9cSGrigor Tovmasyan * number in descriptor should point to last (u)frame in the interval. 1359d630b9cSGrigor Tovmasyan * 1369d630b9cSGrigor Tovmasyan */ 1379d630b9cSGrigor Tovmasyan static inline void dwc2_gadget_dec_frame_num_by_one(struct dwc2_hsotg_ep *hs_ep) 1389d630b9cSGrigor Tovmasyan { 1399d630b9cSGrigor Tovmasyan if (hs_ep->target_frame) 1409d630b9cSGrigor Tovmasyan hs_ep->target_frame -= 1; 1419d630b9cSGrigor Tovmasyan else 1429d630b9cSGrigor Tovmasyan hs_ep->target_frame = DSTS_SOFFN_LIMIT; 1439d630b9cSGrigor Tovmasyan } 1449d630b9cSGrigor Tovmasyan 1459d630b9cSGrigor Tovmasyan /** 1461f91b4ccSFelipe Balbi * dwc2_hsotg_en_gsint - enable one or more of the general interrupt 14747a1685fSDinh Nguyen * @hsotg: The device state 14847a1685fSDinh Nguyen * @ints: A bitmask of the interrupts to enable 14947a1685fSDinh Nguyen */ 1501f91b4ccSFelipe Balbi static void dwc2_hsotg_en_gsint(struct dwc2_hsotg *hsotg, u32 ints) 15147a1685fSDinh Nguyen { 152f25c42b8SGevorg Sahakyan u32 gsintmsk = dwc2_readl(hsotg, GINTMSK); 15347a1685fSDinh Nguyen u32 new_gsintmsk; 15447a1685fSDinh Nguyen 15547a1685fSDinh Nguyen new_gsintmsk = gsintmsk | ints; 15647a1685fSDinh Nguyen 15747a1685fSDinh Nguyen if (new_gsintmsk != gsintmsk) { 15847a1685fSDinh Nguyen dev_dbg(hsotg->dev, "gsintmsk now 0x%08x\n", new_gsintmsk); 159f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, new_gsintmsk, GINTMSK); 16047a1685fSDinh Nguyen } 16147a1685fSDinh Nguyen } 16247a1685fSDinh Nguyen 16347a1685fSDinh Nguyen /** 1641f91b4ccSFelipe Balbi * dwc2_hsotg_disable_gsint - disable one or more of the general interrupt 16547a1685fSDinh Nguyen * @hsotg: The device state 16647a1685fSDinh Nguyen * @ints: A bitmask of the interrupts to enable 16747a1685fSDinh Nguyen */ 1681f91b4ccSFelipe Balbi static void dwc2_hsotg_disable_gsint(struct dwc2_hsotg *hsotg, u32 ints) 16947a1685fSDinh Nguyen { 170f25c42b8SGevorg Sahakyan u32 gsintmsk = dwc2_readl(hsotg, GINTMSK); 17147a1685fSDinh Nguyen u32 new_gsintmsk; 17247a1685fSDinh Nguyen 17347a1685fSDinh Nguyen new_gsintmsk = gsintmsk & ~ints; 17447a1685fSDinh Nguyen 17547a1685fSDinh Nguyen if (new_gsintmsk != gsintmsk) 176f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, new_gsintmsk, GINTMSK); 17747a1685fSDinh Nguyen } 17847a1685fSDinh Nguyen 17947a1685fSDinh Nguyen /** 1801f91b4ccSFelipe Balbi * dwc2_hsotg_ctrl_epint - enable/disable an endpoint irq 18147a1685fSDinh Nguyen * @hsotg: The device state 18247a1685fSDinh Nguyen * @ep: The endpoint index 18347a1685fSDinh Nguyen * @dir_in: True if direction is in. 18447a1685fSDinh Nguyen * @en: The enable value, true to enable 18547a1685fSDinh Nguyen * 18647a1685fSDinh Nguyen * Set or clear the mask for an individual endpoint's interrupt 18747a1685fSDinh Nguyen * request. 18847a1685fSDinh Nguyen */ 1891f91b4ccSFelipe Balbi static void dwc2_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg, 19047a1685fSDinh Nguyen unsigned int ep, unsigned int dir_in, 19147a1685fSDinh Nguyen unsigned int en) 19247a1685fSDinh Nguyen { 19347a1685fSDinh Nguyen unsigned long flags; 19447a1685fSDinh Nguyen u32 bit = 1 << ep; 19547a1685fSDinh Nguyen u32 daint; 19647a1685fSDinh Nguyen 19747a1685fSDinh Nguyen if (!dir_in) 19847a1685fSDinh Nguyen bit <<= 16; 19947a1685fSDinh Nguyen 20047a1685fSDinh Nguyen local_irq_save(flags); 201f25c42b8SGevorg Sahakyan daint = dwc2_readl(hsotg, DAINTMSK); 20247a1685fSDinh Nguyen if (en) 20347a1685fSDinh Nguyen daint |= bit; 20447a1685fSDinh Nguyen else 20547a1685fSDinh Nguyen daint &= ~bit; 206f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, daint, DAINTMSK); 20747a1685fSDinh Nguyen local_irq_restore(flags); 20847a1685fSDinh Nguyen } 20947a1685fSDinh Nguyen 21047a1685fSDinh Nguyen /** 211c138ecfaSSevak Arakelyan * dwc2_hsotg_tx_fifo_count - return count of TX FIFOs in device mode 2126fb914d7SGrigor Tovmasyan * 2136fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller 214c138ecfaSSevak Arakelyan */ 215c138ecfaSSevak Arakelyan int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg) 216c138ecfaSSevak Arakelyan { 217c138ecfaSSevak Arakelyan if (hsotg->hw_params.en_multiple_tx_fifo) 218c138ecfaSSevak Arakelyan /* In dedicated FIFO mode we need count of IN EPs */ 2199273083aSMinas Harutyunyan return hsotg->hw_params.num_dev_in_eps; 220c138ecfaSSevak Arakelyan else 221c138ecfaSSevak Arakelyan /* In shared FIFO mode we need count of Periodic IN EPs */ 222c138ecfaSSevak Arakelyan return hsotg->hw_params.num_dev_perio_in_ep; 223c138ecfaSSevak Arakelyan } 224c138ecfaSSevak Arakelyan 225c138ecfaSSevak Arakelyan /** 226c138ecfaSSevak Arakelyan * dwc2_hsotg_tx_fifo_total_depth - return total FIFO depth available for 227c138ecfaSSevak Arakelyan * device mode TX FIFOs 2286fb914d7SGrigor Tovmasyan * 2296fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller 230c138ecfaSSevak Arakelyan */ 231c138ecfaSSevak Arakelyan int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) 232c138ecfaSSevak Arakelyan { 233c138ecfaSSevak Arakelyan int addr; 234c138ecfaSSevak Arakelyan int tx_addr_max; 235c138ecfaSSevak Arakelyan u32 np_tx_fifo_size; 236c138ecfaSSevak Arakelyan 237c138ecfaSSevak Arakelyan np_tx_fifo_size = min_t(u32, hsotg->hw_params.dev_nperio_tx_fifo_size, 238c138ecfaSSevak Arakelyan hsotg->params.g_np_tx_fifo_size); 239c138ecfaSSevak Arakelyan 240c138ecfaSSevak Arakelyan /* Get Endpoint Info Control block size in DWORDs. */ 2419273083aSMinas Harutyunyan tx_addr_max = hsotg->hw_params.total_fifo_size; 242c138ecfaSSevak Arakelyan 243c138ecfaSSevak Arakelyan addr = hsotg->params.g_rx_fifo_size + np_tx_fifo_size; 244c138ecfaSSevak Arakelyan if (tx_addr_max <= addr) 245c138ecfaSSevak Arakelyan return 0; 246c138ecfaSSevak Arakelyan 247c138ecfaSSevak Arakelyan return tx_addr_max - addr; 248c138ecfaSSevak Arakelyan } 249c138ecfaSSevak Arakelyan 250c138ecfaSSevak Arakelyan /** 251187c5298SGrigor Tovmasyan * dwc2_gadget_wkup_alert_handler - Handler for WKUP_ALERT interrupt 252187c5298SGrigor Tovmasyan * 253187c5298SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller 254187c5298SGrigor Tovmasyan * 255187c5298SGrigor Tovmasyan */ 256187c5298SGrigor Tovmasyan static void dwc2_gadget_wkup_alert_handler(struct dwc2_hsotg *hsotg) 257187c5298SGrigor Tovmasyan { 258187c5298SGrigor Tovmasyan u32 gintsts2; 259187c5298SGrigor Tovmasyan u32 gintmsk2; 260187c5298SGrigor Tovmasyan 261187c5298SGrigor Tovmasyan gintsts2 = dwc2_readl(hsotg, GINTSTS2); 262187c5298SGrigor Tovmasyan gintmsk2 = dwc2_readl(hsotg, GINTMSK2); 2639607f3cdSLee Jones gintsts2 &= gintmsk2; 264187c5298SGrigor Tovmasyan 265187c5298SGrigor Tovmasyan if (gintsts2 & GINTSTS2_WKUP_ALERT_INT) { 266187c5298SGrigor Tovmasyan dev_dbg(hsotg->dev, "%s: Wkup_Alert_Int\n", __func__); 26787b6d2c5SMinas Harutyunyan dwc2_set_bit(hsotg, GINTSTS2, GINTSTS2_WKUP_ALERT_INT); 268d64bc8eeSArtur Petrosyan dwc2_set_bit(hsotg, DCTL, DCTL_RMTWKUPSIG); 269187c5298SGrigor Tovmasyan } 270187c5298SGrigor Tovmasyan } 271187c5298SGrigor Tovmasyan 272187c5298SGrigor Tovmasyan /** 273c138ecfaSSevak Arakelyan * dwc2_hsotg_tx_fifo_average_depth - returns average depth of device mode 274c138ecfaSSevak Arakelyan * TX FIFOs 2756fb914d7SGrigor Tovmasyan * 2766fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller 277c138ecfaSSevak Arakelyan */ 278c138ecfaSSevak Arakelyan int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg) 279c138ecfaSSevak Arakelyan { 280c138ecfaSSevak Arakelyan int tx_fifo_count; 281c138ecfaSSevak Arakelyan int tx_fifo_depth; 282c138ecfaSSevak Arakelyan 283c138ecfaSSevak Arakelyan tx_fifo_depth = dwc2_hsotg_tx_fifo_total_depth(hsotg); 284c138ecfaSSevak Arakelyan 285c138ecfaSSevak Arakelyan tx_fifo_count = dwc2_hsotg_tx_fifo_count(hsotg); 286c138ecfaSSevak Arakelyan 287c138ecfaSSevak Arakelyan if (!tx_fifo_count) 288c138ecfaSSevak Arakelyan return tx_fifo_depth; 289c138ecfaSSevak Arakelyan else 290c138ecfaSSevak Arakelyan return tx_fifo_depth / tx_fifo_count; 291c138ecfaSSevak Arakelyan } 292c138ecfaSSevak Arakelyan 293c138ecfaSSevak Arakelyan /** 2941f91b4ccSFelipe Balbi * dwc2_hsotg_init_fifo - initialise non-periodic FIFOs 29547a1685fSDinh Nguyen * @hsotg: The device instance. 29647a1685fSDinh Nguyen */ 2971f91b4ccSFelipe Balbi static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg) 29847a1685fSDinh Nguyen { 2992317eacdSJohn Youn unsigned int ep; 30047a1685fSDinh Nguyen unsigned int addr; 30147a1685fSDinh Nguyen int timeout; 30279d6b8c5SSevak Arakelyan 30347a1685fSDinh Nguyen u32 val; 30405ee799fSJohn Youn u32 *txfsz = hsotg->params.g_tx_fifo_size; 30547a1685fSDinh Nguyen 3067fcbc95cSGregory Herrero /* Reset fifo map if not correctly cleared during previous session */ 3077fcbc95cSGregory Herrero WARN_ON(hsotg->fifo_map); 3087fcbc95cSGregory Herrero hsotg->fifo_map = 0; 3097fcbc95cSGregory Herrero 3100a176279SGregory Herrero /* set RX/NPTX FIFO sizes */ 311f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, hsotg->params.g_rx_fifo_size, GRXFSIZ); 312f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, (hsotg->params.g_rx_fifo_size << 313f25c42b8SGevorg Sahakyan FIFOSIZE_STARTADDR_SHIFT) | 31405ee799fSJohn Youn (hsotg->params.g_np_tx_fifo_size << FIFOSIZE_DEPTH_SHIFT), 315f25c42b8SGevorg Sahakyan GNPTXFSIZ); 31647a1685fSDinh Nguyen 31747a1685fSDinh Nguyen /* 31847a1685fSDinh Nguyen * arange all the rest of the TX FIFOs, as some versions of this 31947a1685fSDinh Nguyen * block have overlapping default addresses. This also ensures 32047a1685fSDinh Nguyen * that if the settings have been changed, then they are set to 32147a1685fSDinh Nguyen * known values. 32247a1685fSDinh Nguyen */ 32347a1685fSDinh Nguyen 32447a1685fSDinh Nguyen /* start at the end of the GNPTXFSIZ, rounded up */ 32505ee799fSJohn Youn addr = hsotg->params.g_rx_fifo_size + hsotg->params.g_np_tx_fifo_size; 32647a1685fSDinh Nguyen 32747a1685fSDinh Nguyen /* 3280a176279SGregory Herrero * Configure fifos sizes from provided configuration and assign 329b203d0a2SRobert Baldyga * them to endpoints dynamically according to maxpacket size value of 330b203d0a2SRobert Baldyga * given endpoint. 33147a1685fSDinh Nguyen */ 3322317eacdSJohn Youn for (ep = 1; ep < MAX_EPS_CHANNELS; ep++) { 33305ee799fSJohn Youn if (!txfsz[ep]) 3343fa95385SJohn Youn continue; 3353fa95385SJohn Youn val = addr; 33605ee799fSJohn Youn val |= txfsz[ep] << FIFOSIZE_DEPTH_SHIFT; 33705ee799fSJohn Youn WARN_ONCE(addr + txfsz[ep] > hsotg->fifo_mem, 3383fa95385SJohn Youn "insufficient fifo memory"); 33905ee799fSJohn Youn addr += txfsz[ep]; 34047a1685fSDinh Nguyen 341f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, val, DPTXFSIZN(ep)); 342f25c42b8SGevorg Sahakyan val = dwc2_readl(hsotg, DPTXFSIZN(ep)); 34347a1685fSDinh Nguyen } 34447a1685fSDinh Nguyen 345f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, hsotg->hw_params.total_fifo_size | 346f87c842fSSevak Arakelyan addr << GDFIFOCFG_EPINFOBASE_SHIFT, 347f25c42b8SGevorg Sahakyan GDFIFOCFG); 34847a1685fSDinh Nguyen /* 34947a1685fSDinh Nguyen * according to p428 of the design guide, we need to ensure that 35047a1685fSDinh Nguyen * all fifos are flushed before continuing 35147a1685fSDinh Nguyen */ 35247a1685fSDinh Nguyen 353f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH | 354f25c42b8SGevorg Sahakyan GRSTCTL_RXFFLSH, GRSTCTL); 35547a1685fSDinh Nguyen 35647a1685fSDinh Nguyen /* wait until the fifos are both flushed */ 35747a1685fSDinh Nguyen timeout = 100; 35847a1685fSDinh Nguyen while (1) { 359f25c42b8SGevorg Sahakyan val = dwc2_readl(hsotg, GRSTCTL); 36047a1685fSDinh Nguyen 36147a1685fSDinh Nguyen if ((val & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH)) == 0) 36247a1685fSDinh Nguyen break; 36347a1685fSDinh Nguyen 36447a1685fSDinh Nguyen if (--timeout == 0) { 36547a1685fSDinh Nguyen dev_err(hsotg->dev, 36647a1685fSDinh Nguyen "%s: timeout flushing fifos (GRSTCTL=%08x)\n", 36747a1685fSDinh Nguyen __func__, val); 36848b20bcbSGregory Herrero break; 36947a1685fSDinh Nguyen } 37047a1685fSDinh Nguyen 37147a1685fSDinh Nguyen udelay(1); 37247a1685fSDinh Nguyen } 37347a1685fSDinh Nguyen 37447a1685fSDinh Nguyen dev_dbg(hsotg->dev, "FIFOs reset, timeout at %d\n", timeout); 37547a1685fSDinh Nguyen } 37647a1685fSDinh Nguyen 37747a1685fSDinh Nguyen /** 3786fb914d7SGrigor Tovmasyan * dwc2_hsotg_ep_alloc_request - allocate USB rerequest structure 37947a1685fSDinh Nguyen * @ep: USB endpoint to allocate request for. 38047a1685fSDinh Nguyen * @flags: Allocation flags 38147a1685fSDinh Nguyen * 38247a1685fSDinh Nguyen * Allocate a new USB request structure appropriate for the specified endpoint 38347a1685fSDinh Nguyen */ 3841f91b4ccSFelipe Balbi static struct usb_request *dwc2_hsotg_ep_alloc_request(struct usb_ep *ep, 38547a1685fSDinh Nguyen gfp_t flags) 38647a1685fSDinh Nguyen { 3871f91b4ccSFelipe Balbi struct dwc2_hsotg_req *req; 38847a1685fSDinh Nguyen 389ec33efe2SJohn Youn req = kzalloc(sizeof(*req), flags); 39047a1685fSDinh Nguyen if (!req) 39147a1685fSDinh Nguyen return NULL; 39247a1685fSDinh Nguyen 39347a1685fSDinh Nguyen INIT_LIST_HEAD(&req->queue); 39447a1685fSDinh Nguyen 39547a1685fSDinh Nguyen return &req->req; 39647a1685fSDinh Nguyen } 39747a1685fSDinh Nguyen 39847a1685fSDinh Nguyen /** 39947a1685fSDinh Nguyen * is_ep_periodic - return true if the endpoint is in periodic mode. 40047a1685fSDinh Nguyen * @hs_ep: The endpoint to query. 40147a1685fSDinh Nguyen * 40247a1685fSDinh Nguyen * Returns true if the endpoint is in periodic mode, meaning it is being 40347a1685fSDinh Nguyen * used for an Interrupt or ISO transfer. 40447a1685fSDinh Nguyen */ 4051f91b4ccSFelipe Balbi static inline int is_ep_periodic(struct dwc2_hsotg_ep *hs_ep) 40647a1685fSDinh Nguyen { 40747a1685fSDinh Nguyen return hs_ep->periodic; 40847a1685fSDinh Nguyen } 40947a1685fSDinh Nguyen 41047a1685fSDinh Nguyen /** 4111f91b4ccSFelipe Balbi * dwc2_hsotg_unmap_dma - unmap the DMA memory being used for the request 41247a1685fSDinh Nguyen * @hsotg: The device state. 41347a1685fSDinh Nguyen * @hs_ep: The endpoint for the request 41447a1685fSDinh Nguyen * @hs_req: The request being processed. 41547a1685fSDinh Nguyen * 4161f91b4ccSFelipe Balbi * This is the reverse of dwc2_hsotg_map_dma(), called for the completion 41747a1685fSDinh Nguyen * of a request to ensure the buffer is ready for access by the caller. 41847a1685fSDinh Nguyen */ 4191f91b4ccSFelipe Balbi static void dwc2_hsotg_unmap_dma(struct dwc2_hsotg *hsotg, 4201f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep, 4211f91b4ccSFelipe Balbi struct dwc2_hsotg_req *hs_req) 42247a1685fSDinh Nguyen { 42347a1685fSDinh Nguyen struct usb_request *req = &hs_req->req; 4249da51974SJohn Youn 42575a41ce4SPhil Elwell usb_gadget_unmap_request(&hsotg->gadget, req, hs_ep->map_dir); 42647a1685fSDinh Nguyen } 42747a1685fSDinh Nguyen 4280f6b80c0SVahram Aharonyan /* 4290f6b80c0SVahram Aharonyan * dwc2_gadget_alloc_ctrl_desc_chains - allocate DMA descriptor chains 4300f6b80c0SVahram Aharonyan * for Control endpoint 4310f6b80c0SVahram Aharonyan * @hsotg: The device state. 4320f6b80c0SVahram Aharonyan * 4330f6b80c0SVahram Aharonyan * This function will allocate 4 descriptor chains for EP 0: 2 for 4340f6b80c0SVahram Aharonyan * Setup stage, per one for IN and OUT data/status transactions. 4350f6b80c0SVahram Aharonyan */ 4360f6b80c0SVahram Aharonyan static int dwc2_gadget_alloc_ctrl_desc_chains(struct dwc2_hsotg *hsotg) 4370f6b80c0SVahram Aharonyan { 4380f6b80c0SVahram Aharonyan hsotg->setup_desc[0] = 4390f6b80c0SVahram Aharonyan dmam_alloc_coherent(hsotg->dev, 4400f6b80c0SVahram Aharonyan sizeof(struct dwc2_dma_desc), 4410f6b80c0SVahram Aharonyan &hsotg->setup_desc_dma[0], 4420f6b80c0SVahram Aharonyan GFP_KERNEL); 4430f6b80c0SVahram Aharonyan if (!hsotg->setup_desc[0]) 4440f6b80c0SVahram Aharonyan goto fail; 4450f6b80c0SVahram Aharonyan 4460f6b80c0SVahram Aharonyan hsotg->setup_desc[1] = 4470f6b80c0SVahram Aharonyan dmam_alloc_coherent(hsotg->dev, 4480f6b80c0SVahram Aharonyan sizeof(struct dwc2_dma_desc), 4490f6b80c0SVahram Aharonyan &hsotg->setup_desc_dma[1], 4500f6b80c0SVahram Aharonyan GFP_KERNEL); 4510f6b80c0SVahram Aharonyan if (!hsotg->setup_desc[1]) 4520f6b80c0SVahram Aharonyan goto fail; 4530f6b80c0SVahram Aharonyan 4540f6b80c0SVahram Aharonyan hsotg->ctrl_in_desc = 4550f6b80c0SVahram Aharonyan dmam_alloc_coherent(hsotg->dev, 4560f6b80c0SVahram Aharonyan sizeof(struct dwc2_dma_desc), 4570f6b80c0SVahram Aharonyan &hsotg->ctrl_in_desc_dma, 4580f6b80c0SVahram Aharonyan GFP_KERNEL); 4590f6b80c0SVahram Aharonyan if (!hsotg->ctrl_in_desc) 4600f6b80c0SVahram Aharonyan goto fail; 4610f6b80c0SVahram Aharonyan 4620f6b80c0SVahram Aharonyan hsotg->ctrl_out_desc = 4630f6b80c0SVahram Aharonyan dmam_alloc_coherent(hsotg->dev, 4640f6b80c0SVahram Aharonyan sizeof(struct dwc2_dma_desc), 4650f6b80c0SVahram Aharonyan &hsotg->ctrl_out_desc_dma, 4660f6b80c0SVahram Aharonyan GFP_KERNEL); 4670f6b80c0SVahram Aharonyan if (!hsotg->ctrl_out_desc) 4680f6b80c0SVahram Aharonyan goto fail; 4690f6b80c0SVahram Aharonyan 4700f6b80c0SVahram Aharonyan return 0; 4710f6b80c0SVahram Aharonyan 4720f6b80c0SVahram Aharonyan fail: 4730f6b80c0SVahram Aharonyan return -ENOMEM; 4740f6b80c0SVahram Aharonyan } 4750f6b80c0SVahram Aharonyan 47647a1685fSDinh Nguyen /** 4771f91b4ccSFelipe Balbi * dwc2_hsotg_write_fifo - write packet Data to the TxFIFO 47847a1685fSDinh Nguyen * @hsotg: The controller state. 47947a1685fSDinh Nguyen * @hs_ep: The endpoint we're going to write for. 48047a1685fSDinh Nguyen * @hs_req: The request to write data for. 48147a1685fSDinh Nguyen * 48247a1685fSDinh Nguyen * This is called when the TxFIFO has some space in it to hold a new 48347a1685fSDinh Nguyen * transmission and we have something to give it. The actual setup of 48447a1685fSDinh Nguyen * the data size is done elsewhere, so all we have to do is to actually 48547a1685fSDinh Nguyen * write the data. 48647a1685fSDinh Nguyen * 48747a1685fSDinh Nguyen * The return value is zero if there is more space (or nothing was done) 48847a1685fSDinh Nguyen * otherwise -ENOSPC is returned if the FIFO space was used up. 48947a1685fSDinh Nguyen * 49047a1685fSDinh Nguyen * This routine is only needed for PIO 49147a1685fSDinh Nguyen */ 4921f91b4ccSFelipe Balbi static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg, 4931f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep, 4941f91b4ccSFelipe Balbi struct dwc2_hsotg_req *hs_req) 49547a1685fSDinh Nguyen { 49647a1685fSDinh Nguyen bool periodic = is_ep_periodic(hs_ep); 497f25c42b8SGevorg Sahakyan u32 gnptxsts = dwc2_readl(hsotg, GNPTXSTS); 49847a1685fSDinh Nguyen int buf_pos = hs_req->req.actual; 49947a1685fSDinh Nguyen int to_write = hs_ep->size_loaded; 50047a1685fSDinh Nguyen void *data; 50147a1685fSDinh Nguyen int can_write; 50247a1685fSDinh Nguyen int pkt_round; 50347a1685fSDinh Nguyen int max_transfer; 50447a1685fSDinh Nguyen 50547a1685fSDinh Nguyen to_write -= (buf_pos - hs_ep->last_load); 50647a1685fSDinh Nguyen 50747a1685fSDinh Nguyen /* if there's nothing to write, get out early */ 50847a1685fSDinh Nguyen if (to_write == 0) 50947a1685fSDinh Nguyen return 0; 51047a1685fSDinh Nguyen 51147a1685fSDinh Nguyen if (periodic && !hsotg->dedicated_fifos) { 512f25c42b8SGevorg Sahakyan u32 epsize = dwc2_readl(hsotg, DIEPTSIZ(hs_ep->index)); 51347a1685fSDinh Nguyen int size_left; 51447a1685fSDinh Nguyen int size_done; 51547a1685fSDinh Nguyen 51647a1685fSDinh Nguyen /* 51747a1685fSDinh Nguyen * work out how much data was loaded so we can calculate 51847a1685fSDinh Nguyen * how much data is left in the fifo. 51947a1685fSDinh Nguyen */ 52047a1685fSDinh Nguyen 52147a1685fSDinh Nguyen size_left = DXEPTSIZ_XFERSIZE_GET(epsize); 52247a1685fSDinh Nguyen 52347a1685fSDinh Nguyen /* 52447a1685fSDinh Nguyen * if shared fifo, we cannot write anything until the 52547a1685fSDinh Nguyen * previous data has been completely sent. 52647a1685fSDinh Nguyen */ 52747a1685fSDinh Nguyen if (hs_ep->fifo_load != 0) { 5281f91b4ccSFelipe Balbi dwc2_hsotg_en_gsint(hsotg, GINTSTS_PTXFEMP); 52947a1685fSDinh Nguyen return -ENOSPC; 53047a1685fSDinh Nguyen } 53147a1685fSDinh Nguyen 53247a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: left=%d, load=%d, fifo=%d, size %d\n", 53347a1685fSDinh Nguyen __func__, size_left, 53447a1685fSDinh Nguyen hs_ep->size_loaded, hs_ep->fifo_load, hs_ep->fifo_size); 53547a1685fSDinh Nguyen 53647a1685fSDinh Nguyen /* how much of the data has moved */ 53747a1685fSDinh Nguyen size_done = hs_ep->size_loaded - size_left; 53847a1685fSDinh Nguyen 53947a1685fSDinh Nguyen /* how much data is left in the fifo */ 54047a1685fSDinh Nguyen can_write = hs_ep->fifo_load - size_done; 54147a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: => can_write1=%d\n", 54247a1685fSDinh Nguyen __func__, can_write); 54347a1685fSDinh Nguyen 54447a1685fSDinh Nguyen can_write = hs_ep->fifo_size - can_write; 54547a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: => can_write2=%d\n", 54647a1685fSDinh Nguyen __func__, can_write); 54747a1685fSDinh Nguyen 54847a1685fSDinh Nguyen if (can_write <= 0) { 5491f91b4ccSFelipe Balbi dwc2_hsotg_en_gsint(hsotg, GINTSTS_PTXFEMP); 55047a1685fSDinh Nguyen return -ENOSPC; 55147a1685fSDinh Nguyen } 55247a1685fSDinh Nguyen } else if (hsotg->dedicated_fifos && hs_ep->index != 0) { 553f25c42b8SGevorg Sahakyan can_write = dwc2_readl(hsotg, 554ad674a15SRobert Baldyga DTXFSTS(hs_ep->fifo_index)); 55547a1685fSDinh Nguyen 55647a1685fSDinh Nguyen can_write &= 0xffff; 55747a1685fSDinh Nguyen can_write *= 4; 55847a1685fSDinh Nguyen } else { 55947a1685fSDinh Nguyen if (GNPTXSTS_NP_TXQ_SPC_AVAIL_GET(gnptxsts) == 0) { 56047a1685fSDinh Nguyen dev_dbg(hsotg->dev, 56147a1685fSDinh Nguyen "%s: no queue slots available (0x%08x)\n", 56247a1685fSDinh Nguyen __func__, gnptxsts); 56347a1685fSDinh Nguyen 5641f91b4ccSFelipe Balbi dwc2_hsotg_en_gsint(hsotg, GINTSTS_NPTXFEMP); 56547a1685fSDinh Nguyen return -ENOSPC; 56647a1685fSDinh Nguyen } 56747a1685fSDinh Nguyen 56847a1685fSDinh Nguyen can_write = GNPTXSTS_NP_TXF_SPC_AVAIL_GET(gnptxsts); 56947a1685fSDinh Nguyen can_write *= 4; /* fifo size is in 32bit quantities. */ 57047a1685fSDinh Nguyen } 57147a1685fSDinh Nguyen 57247a1685fSDinh Nguyen max_transfer = hs_ep->ep.maxpacket * hs_ep->mc; 57347a1685fSDinh Nguyen 57447a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, max_transfer %d\n", 57547a1685fSDinh Nguyen __func__, gnptxsts, can_write, to_write, max_transfer); 57647a1685fSDinh Nguyen 57747a1685fSDinh Nguyen /* 57847a1685fSDinh Nguyen * limit to 512 bytes of data, it seems at least on the non-periodic 57947a1685fSDinh Nguyen * FIFO, requests of >512 cause the endpoint to get stuck with a 58047a1685fSDinh Nguyen * fragment of the end of the transfer in it. 58147a1685fSDinh Nguyen */ 58247a1685fSDinh Nguyen if (can_write > 512 && !periodic) 58347a1685fSDinh Nguyen can_write = 512; 58447a1685fSDinh Nguyen 58547a1685fSDinh Nguyen /* 58647a1685fSDinh Nguyen * limit the write to one max-packet size worth of data, but allow 58747a1685fSDinh Nguyen * the transfer to return that it did not run out of fifo space 58847a1685fSDinh Nguyen * doing it. 58947a1685fSDinh Nguyen */ 59047a1685fSDinh Nguyen if (to_write > max_transfer) { 59147a1685fSDinh Nguyen to_write = max_transfer; 59247a1685fSDinh Nguyen 59347a1685fSDinh Nguyen /* it's needed only when we do not use dedicated fifos */ 59447a1685fSDinh Nguyen if (!hsotg->dedicated_fifos) 5951f91b4ccSFelipe Balbi dwc2_hsotg_en_gsint(hsotg, 59647a1685fSDinh Nguyen periodic ? GINTSTS_PTXFEMP : 59747a1685fSDinh Nguyen GINTSTS_NPTXFEMP); 59847a1685fSDinh Nguyen } 59947a1685fSDinh Nguyen 60047a1685fSDinh Nguyen /* see if we can write data */ 60147a1685fSDinh Nguyen 60247a1685fSDinh Nguyen if (to_write > can_write) { 60347a1685fSDinh Nguyen to_write = can_write; 60447a1685fSDinh Nguyen pkt_round = to_write % max_transfer; 60547a1685fSDinh Nguyen 60647a1685fSDinh Nguyen /* 60747a1685fSDinh Nguyen * Round the write down to an 60847a1685fSDinh Nguyen * exact number of packets. 60947a1685fSDinh Nguyen * 61047a1685fSDinh Nguyen * Note, we do not currently check to see if we can ever 61147a1685fSDinh Nguyen * write a full packet or not to the FIFO. 61247a1685fSDinh Nguyen */ 61347a1685fSDinh Nguyen 61447a1685fSDinh Nguyen if (pkt_round) 61547a1685fSDinh Nguyen to_write -= pkt_round; 61647a1685fSDinh Nguyen 61747a1685fSDinh Nguyen /* 61847a1685fSDinh Nguyen * enable correct FIFO interrupt to alert us when there 61947a1685fSDinh Nguyen * is more room left. 62047a1685fSDinh Nguyen */ 62147a1685fSDinh Nguyen 62247a1685fSDinh Nguyen /* it's needed only when we do not use dedicated fifos */ 62347a1685fSDinh Nguyen if (!hsotg->dedicated_fifos) 6241f91b4ccSFelipe Balbi dwc2_hsotg_en_gsint(hsotg, 62547a1685fSDinh Nguyen periodic ? GINTSTS_PTXFEMP : 62647a1685fSDinh Nguyen GINTSTS_NPTXFEMP); 62747a1685fSDinh Nguyen } 62847a1685fSDinh Nguyen 62947a1685fSDinh Nguyen dev_dbg(hsotg->dev, "write %d/%d, can_write %d, done %d\n", 63047a1685fSDinh Nguyen to_write, hs_req->req.length, can_write, buf_pos); 63147a1685fSDinh Nguyen 63247a1685fSDinh Nguyen if (to_write <= 0) 63347a1685fSDinh Nguyen return -ENOSPC; 63447a1685fSDinh Nguyen 63547a1685fSDinh Nguyen hs_req->req.actual = buf_pos + to_write; 63647a1685fSDinh Nguyen hs_ep->total_data += to_write; 63747a1685fSDinh Nguyen 63847a1685fSDinh Nguyen if (periodic) 63947a1685fSDinh Nguyen hs_ep->fifo_load += to_write; 64047a1685fSDinh Nguyen 64147a1685fSDinh Nguyen to_write = DIV_ROUND_UP(to_write, 4); 64247a1685fSDinh Nguyen data = hs_req->req.buf + buf_pos; 64347a1685fSDinh Nguyen 644342ccce1SGevorg Sahakyan dwc2_writel_rep(hsotg, EPFIFO(hs_ep->index), data, to_write); 64547a1685fSDinh Nguyen 64647a1685fSDinh Nguyen return (to_write >= can_write) ? -ENOSPC : 0; 64747a1685fSDinh Nguyen } 64847a1685fSDinh Nguyen 64947a1685fSDinh Nguyen /** 65047a1685fSDinh Nguyen * get_ep_limit - get the maximum data legnth for this endpoint 65147a1685fSDinh Nguyen * @hs_ep: The endpoint 65247a1685fSDinh Nguyen * 65347a1685fSDinh Nguyen * Return the maximum data that can be queued in one go on a given endpoint 65447a1685fSDinh Nguyen * so that transfers that are too long can be split. 65547a1685fSDinh Nguyen */ 6569da51974SJohn Youn static unsigned int get_ep_limit(struct dwc2_hsotg_ep *hs_ep) 65747a1685fSDinh Nguyen { 65847a1685fSDinh Nguyen int index = hs_ep->index; 6599da51974SJohn Youn unsigned int maxsize; 6609da51974SJohn Youn unsigned int maxpkt; 66147a1685fSDinh Nguyen 66247a1685fSDinh Nguyen if (index != 0) { 66347a1685fSDinh Nguyen maxsize = DXEPTSIZ_XFERSIZE_LIMIT + 1; 66447a1685fSDinh Nguyen maxpkt = DXEPTSIZ_PKTCNT_LIMIT + 1; 66547a1685fSDinh Nguyen } else { 66647a1685fSDinh Nguyen maxsize = 64 + 64; 66747a1685fSDinh Nguyen if (hs_ep->dir_in) 66847a1685fSDinh Nguyen maxpkt = DIEPTSIZ0_PKTCNT_LIMIT + 1; 66947a1685fSDinh Nguyen else 67047a1685fSDinh Nguyen maxpkt = 2; 67147a1685fSDinh Nguyen } 67247a1685fSDinh Nguyen 67347a1685fSDinh Nguyen /* we made the constant loading easier above by using +1 */ 67447a1685fSDinh Nguyen maxpkt--; 67547a1685fSDinh Nguyen maxsize--; 67647a1685fSDinh Nguyen 67747a1685fSDinh Nguyen /* 67847a1685fSDinh Nguyen * constrain by packet count if maxpkts*pktsize is greater 67947a1685fSDinh Nguyen * than the length register size. 68047a1685fSDinh Nguyen */ 68147a1685fSDinh Nguyen 68247a1685fSDinh Nguyen if ((maxpkt * hs_ep->ep.maxpacket) < maxsize) 68347a1685fSDinh Nguyen maxsize = maxpkt * hs_ep->ep.maxpacket; 68447a1685fSDinh Nguyen 68547a1685fSDinh Nguyen return maxsize; 68647a1685fSDinh Nguyen } 68747a1685fSDinh Nguyen 68847a1685fSDinh Nguyen /** 689381fc8f8SVardan Mikayelyan * dwc2_hsotg_read_frameno - read current frame number 690381fc8f8SVardan Mikayelyan * @hsotg: The device instance 691381fc8f8SVardan Mikayelyan * 692381fc8f8SVardan Mikayelyan * Return the current frame number 693381fc8f8SVardan Mikayelyan */ 694381fc8f8SVardan Mikayelyan static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg) 695381fc8f8SVardan Mikayelyan { 696381fc8f8SVardan Mikayelyan u32 dsts; 697381fc8f8SVardan Mikayelyan 698f25c42b8SGevorg Sahakyan dsts = dwc2_readl(hsotg, DSTS); 699381fc8f8SVardan Mikayelyan dsts &= DSTS_SOFFN_MASK; 700381fc8f8SVardan Mikayelyan dsts >>= DSTS_SOFFN_SHIFT; 701381fc8f8SVardan Mikayelyan 702381fc8f8SVardan Mikayelyan return dsts; 703381fc8f8SVardan Mikayelyan } 704381fc8f8SVardan Mikayelyan 705381fc8f8SVardan Mikayelyan /** 706cf77b5fbSVahram Aharonyan * dwc2_gadget_get_chain_limit - get the maximum data payload value of the 707cf77b5fbSVahram Aharonyan * DMA descriptor chain prepared for specific endpoint 708cf77b5fbSVahram Aharonyan * @hs_ep: The endpoint 709cf77b5fbSVahram Aharonyan * 710cf77b5fbSVahram Aharonyan * Return the maximum data that can be queued in one go on a given endpoint 711cf77b5fbSVahram Aharonyan * depending on its descriptor chain capacity so that transfers that 712cf77b5fbSVahram Aharonyan * are too long can be split. 713cf77b5fbSVahram Aharonyan */ 714cf77b5fbSVahram Aharonyan static unsigned int dwc2_gadget_get_chain_limit(struct dwc2_hsotg_ep *hs_ep) 715cf77b5fbSVahram Aharonyan { 716b2c586ebSMinas Harutyunyan const struct usb_endpoint_descriptor *ep_desc = hs_ep->ep.desc; 717cf77b5fbSVahram Aharonyan int is_isoc = hs_ep->isochronous; 718cf77b5fbSVahram Aharonyan unsigned int maxsize; 719b2c586ebSMinas Harutyunyan u32 mps = hs_ep->ep.maxpacket; 720b2c586ebSMinas Harutyunyan int dir_in = hs_ep->dir_in; 721cf77b5fbSVahram Aharonyan 722cf77b5fbSVahram Aharonyan if (is_isoc) 72354f37f56SMinas Harutyunyan maxsize = (hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_LIMIT : 72454f37f56SMinas Harutyunyan DEV_DMA_ISOC_RX_NBYTES_LIMIT) * 72554f37f56SMinas Harutyunyan MAX_DMA_DESC_NUM_HS_ISOC; 726cf77b5fbSVahram Aharonyan else 72754f37f56SMinas Harutyunyan maxsize = DEV_DMA_NBYTES_LIMIT * MAX_DMA_DESC_NUM_GENERIC; 728cf77b5fbSVahram Aharonyan 729b2c586ebSMinas Harutyunyan /* Interrupt OUT EP with mps not multiple of 4 */ 730b2c586ebSMinas Harutyunyan if (hs_ep->index) 731b2c586ebSMinas Harutyunyan if (usb_endpoint_xfer_int(ep_desc) && !dir_in && (mps % 4)) 732b2c586ebSMinas Harutyunyan maxsize = mps * MAX_DMA_DESC_NUM_GENERIC; 733b2c586ebSMinas Harutyunyan 734cf77b5fbSVahram Aharonyan return maxsize; 735cf77b5fbSVahram Aharonyan } 736cf77b5fbSVahram Aharonyan 737e02f9aa6SVahram Aharonyan /* 738e02f9aa6SVahram Aharonyan * dwc2_gadget_get_desc_params - get DMA descriptor parameters. 739e02f9aa6SVahram Aharonyan * @hs_ep: The endpoint 740e02f9aa6SVahram Aharonyan * @mask: RX/TX bytes mask to be defined 741e02f9aa6SVahram Aharonyan * 742e02f9aa6SVahram Aharonyan * Returns maximum data payload for one descriptor after analyzing endpoint 743e02f9aa6SVahram Aharonyan * characteristics. 744e02f9aa6SVahram Aharonyan * DMA descriptor transfer bytes limit depends on EP type: 745e02f9aa6SVahram Aharonyan * Control out - MPS, 746e02f9aa6SVahram Aharonyan * Isochronous - descriptor rx/tx bytes bitfield limit, 747e02f9aa6SVahram Aharonyan * Control In/Bulk/Interrupt - multiple of mps. This will allow to not 748e02f9aa6SVahram Aharonyan * have concatenations from various descriptors within one packet. 749b2c586ebSMinas Harutyunyan * Interrupt OUT - if mps not multiple of 4 then a single packet corresponds 750b2c586ebSMinas Harutyunyan * to a single descriptor. 751e02f9aa6SVahram Aharonyan * 752e02f9aa6SVahram Aharonyan * Selects corresponding mask for RX/TX bytes as well. 753e02f9aa6SVahram Aharonyan */ 754e02f9aa6SVahram Aharonyan static u32 dwc2_gadget_get_desc_params(struct dwc2_hsotg_ep *hs_ep, u32 *mask) 755e02f9aa6SVahram Aharonyan { 756b2c586ebSMinas Harutyunyan const struct usb_endpoint_descriptor *ep_desc = hs_ep->ep.desc; 757e02f9aa6SVahram Aharonyan u32 mps = hs_ep->ep.maxpacket; 758e02f9aa6SVahram Aharonyan int dir_in = hs_ep->dir_in; 759e02f9aa6SVahram Aharonyan u32 desc_size = 0; 760e02f9aa6SVahram Aharonyan 761e02f9aa6SVahram Aharonyan if (!hs_ep->index && !dir_in) { 762e02f9aa6SVahram Aharonyan desc_size = mps; 763e02f9aa6SVahram Aharonyan *mask = DEV_DMA_NBYTES_MASK; 764e02f9aa6SVahram Aharonyan } else if (hs_ep->isochronous) { 765e02f9aa6SVahram Aharonyan if (dir_in) { 766e02f9aa6SVahram Aharonyan desc_size = DEV_DMA_ISOC_TX_NBYTES_LIMIT; 767e02f9aa6SVahram Aharonyan *mask = DEV_DMA_ISOC_TX_NBYTES_MASK; 768e02f9aa6SVahram Aharonyan } else { 769e02f9aa6SVahram Aharonyan desc_size = DEV_DMA_ISOC_RX_NBYTES_LIMIT; 770e02f9aa6SVahram Aharonyan *mask = DEV_DMA_ISOC_RX_NBYTES_MASK; 771e02f9aa6SVahram Aharonyan } 772e02f9aa6SVahram Aharonyan } else { 773e02f9aa6SVahram Aharonyan desc_size = DEV_DMA_NBYTES_LIMIT; 774e02f9aa6SVahram Aharonyan *mask = DEV_DMA_NBYTES_MASK; 775e02f9aa6SVahram Aharonyan 776e02f9aa6SVahram Aharonyan /* Round down desc_size to be mps multiple */ 777e02f9aa6SVahram Aharonyan desc_size -= desc_size % mps; 778e02f9aa6SVahram Aharonyan } 779e02f9aa6SVahram Aharonyan 780b2c586ebSMinas Harutyunyan /* Interrupt OUT EP with mps not multiple of 4 */ 781b2c586ebSMinas Harutyunyan if (hs_ep->index) 782b2c586ebSMinas Harutyunyan if (usb_endpoint_xfer_int(ep_desc) && !dir_in && (mps % 4)) { 783b2c586ebSMinas Harutyunyan desc_size = mps; 784b2c586ebSMinas Harutyunyan *mask = DEV_DMA_NBYTES_MASK; 785b2c586ebSMinas Harutyunyan } 786b2c586ebSMinas Harutyunyan 787e02f9aa6SVahram Aharonyan return desc_size; 788e02f9aa6SVahram Aharonyan } 789e02f9aa6SVahram Aharonyan 79010209abeSAndrzej Pietrasiewicz static void dwc2_gadget_fill_nonisoc_xfer_ddma_one(struct dwc2_hsotg_ep *hs_ep, 79110209abeSAndrzej Pietrasiewicz struct dwc2_dma_desc **desc, 792e02f9aa6SVahram Aharonyan dma_addr_t dma_buff, 79310209abeSAndrzej Pietrasiewicz unsigned int len, 79410209abeSAndrzej Pietrasiewicz bool true_last) 795e02f9aa6SVahram Aharonyan { 796e02f9aa6SVahram Aharonyan int dir_in = hs_ep->dir_in; 797e02f9aa6SVahram Aharonyan u32 mps = hs_ep->ep.maxpacket; 798e02f9aa6SVahram Aharonyan u32 maxsize = 0; 799e02f9aa6SVahram Aharonyan u32 offset = 0; 800e02f9aa6SVahram Aharonyan u32 mask = 0; 801e02f9aa6SVahram Aharonyan int i; 802e02f9aa6SVahram Aharonyan 803e02f9aa6SVahram Aharonyan maxsize = dwc2_gadget_get_desc_params(hs_ep, &mask); 804e02f9aa6SVahram Aharonyan 805e02f9aa6SVahram Aharonyan hs_ep->desc_count = (len / maxsize) + 806e02f9aa6SVahram Aharonyan ((len % maxsize) ? 1 : 0); 807e02f9aa6SVahram Aharonyan if (len == 0) 808e02f9aa6SVahram Aharonyan hs_ep->desc_count = 1; 809e02f9aa6SVahram Aharonyan 810e02f9aa6SVahram Aharonyan for (i = 0; i < hs_ep->desc_count; ++i) { 81110209abeSAndrzej Pietrasiewicz (*desc)->status = 0; 81210209abeSAndrzej Pietrasiewicz (*desc)->status |= (DEV_DMA_BUFF_STS_HBUSY 813e02f9aa6SVahram Aharonyan << DEV_DMA_BUFF_STS_SHIFT); 814e02f9aa6SVahram Aharonyan 815e02f9aa6SVahram Aharonyan if (len > maxsize) { 816e02f9aa6SVahram Aharonyan if (!hs_ep->index && !dir_in) 81710209abeSAndrzej Pietrasiewicz (*desc)->status |= (DEV_DMA_L | DEV_DMA_IOC); 818e02f9aa6SVahram Aharonyan 81910209abeSAndrzej Pietrasiewicz (*desc)->status |= 82010209abeSAndrzej Pietrasiewicz maxsize << DEV_DMA_NBYTES_SHIFT & mask; 82110209abeSAndrzej Pietrasiewicz (*desc)->buf = dma_buff + offset; 822e02f9aa6SVahram Aharonyan 823e02f9aa6SVahram Aharonyan len -= maxsize; 824e02f9aa6SVahram Aharonyan offset += maxsize; 825e02f9aa6SVahram Aharonyan } else { 82610209abeSAndrzej Pietrasiewicz if (true_last) 82710209abeSAndrzej Pietrasiewicz (*desc)->status |= (DEV_DMA_L | DEV_DMA_IOC); 828e02f9aa6SVahram Aharonyan 829e02f9aa6SVahram Aharonyan if (dir_in) 83010209abeSAndrzej Pietrasiewicz (*desc)->status |= (len % mps) ? DEV_DMA_SHORT : 83110209abeSAndrzej Pietrasiewicz ((hs_ep->send_zlp && true_last) ? 83210209abeSAndrzej Pietrasiewicz DEV_DMA_SHORT : 0); 833e02f9aa6SVahram Aharonyan 83410209abeSAndrzej Pietrasiewicz (*desc)->status |= 835e02f9aa6SVahram Aharonyan len << DEV_DMA_NBYTES_SHIFT & mask; 83610209abeSAndrzej Pietrasiewicz (*desc)->buf = dma_buff + offset; 837e02f9aa6SVahram Aharonyan } 838e02f9aa6SVahram Aharonyan 83910209abeSAndrzej Pietrasiewicz (*desc)->status &= ~DEV_DMA_BUFF_STS_MASK; 84010209abeSAndrzej Pietrasiewicz (*desc)->status |= (DEV_DMA_BUFF_STS_HREADY 841e02f9aa6SVahram Aharonyan << DEV_DMA_BUFF_STS_SHIFT); 84210209abeSAndrzej Pietrasiewicz (*desc)++; 843e02f9aa6SVahram Aharonyan } 844e02f9aa6SVahram Aharonyan } 845e02f9aa6SVahram Aharonyan 846540ccba0SVahram Aharonyan /* 84710209abeSAndrzej Pietrasiewicz * dwc2_gadget_config_nonisoc_xfer_ddma - prepare non ISOC DMA desc chain. 84810209abeSAndrzej Pietrasiewicz * @hs_ep: The endpoint 84910209abeSAndrzej Pietrasiewicz * @ureq: Request to transfer 85010209abeSAndrzej Pietrasiewicz * @offset: offset in bytes 85110209abeSAndrzej Pietrasiewicz * @len: Length of the transfer 85210209abeSAndrzej Pietrasiewicz * 85310209abeSAndrzej Pietrasiewicz * This function will iterate over descriptor chain and fill its entries 85410209abeSAndrzej Pietrasiewicz * with corresponding information based on transfer data. 85510209abeSAndrzej Pietrasiewicz */ 85610209abeSAndrzej Pietrasiewicz static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep, 857066cfd07SAndrzej Pietrasiewicz dma_addr_t dma_buff, 85810209abeSAndrzej Pietrasiewicz unsigned int len) 85910209abeSAndrzej Pietrasiewicz { 860066cfd07SAndrzej Pietrasiewicz struct usb_request *ureq = NULL; 86110209abeSAndrzej Pietrasiewicz struct dwc2_dma_desc *desc = hs_ep->desc_list; 86210209abeSAndrzej Pietrasiewicz struct scatterlist *sg; 86310209abeSAndrzej Pietrasiewicz int i; 86410209abeSAndrzej Pietrasiewicz u8 desc_count = 0; 86510209abeSAndrzej Pietrasiewicz 866066cfd07SAndrzej Pietrasiewicz if (hs_ep->req) 867066cfd07SAndrzej Pietrasiewicz ureq = &hs_ep->req->req; 868066cfd07SAndrzej Pietrasiewicz 86910209abeSAndrzej Pietrasiewicz /* non-DMA sg buffer */ 870066cfd07SAndrzej Pietrasiewicz if (!ureq || !ureq->num_sgs) { 87110209abeSAndrzej Pietrasiewicz dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc, 872066cfd07SAndrzej Pietrasiewicz dma_buff, len, true); 87310209abeSAndrzej Pietrasiewicz return; 87410209abeSAndrzej Pietrasiewicz } 87510209abeSAndrzej Pietrasiewicz 87610209abeSAndrzej Pietrasiewicz /* DMA sg buffer */ 87710209abeSAndrzej Pietrasiewicz for_each_sg(ureq->sg, sg, ureq->num_sgs, i) { 87810209abeSAndrzej Pietrasiewicz dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc, 87910209abeSAndrzej Pietrasiewicz sg_dma_address(sg) + sg->offset, sg_dma_len(sg), 88010209abeSAndrzej Pietrasiewicz sg_is_last(sg)); 88110209abeSAndrzej Pietrasiewicz desc_count += hs_ep->desc_count; 88210209abeSAndrzej Pietrasiewicz } 88310209abeSAndrzej Pietrasiewicz 88410209abeSAndrzej Pietrasiewicz hs_ep->desc_count = desc_count; 88510209abeSAndrzej Pietrasiewicz } 88610209abeSAndrzej Pietrasiewicz 88710209abeSAndrzej Pietrasiewicz /* 888540ccba0SVahram Aharonyan * dwc2_gadget_fill_isoc_desc - fills next isochronous descriptor in chain. 889540ccba0SVahram Aharonyan * @hs_ep: The isochronous endpoint. 890540ccba0SVahram Aharonyan * @dma_buff: usb requests dma buffer. 891540ccba0SVahram Aharonyan * @len: usb request transfer length. 892540ccba0SVahram Aharonyan * 893729cac69SMinas Harutyunyan * Fills next free descriptor with the data of the arrived usb request, 894540ccba0SVahram Aharonyan * frame info, sets Last and IOC bits increments next_desc. If filled 895540ccba0SVahram Aharonyan * descriptor is not the first one, removes L bit from the previous descriptor 896540ccba0SVahram Aharonyan * status. 897540ccba0SVahram Aharonyan */ 898540ccba0SVahram Aharonyan static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep, 899540ccba0SVahram Aharonyan dma_addr_t dma_buff, unsigned int len) 900540ccba0SVahram Aharonyan { 901540ccba0SVahram Aharonyan struct dwc2_dma_desc *desc; 902540ccba0SVahram Aharonyan struct dwc2_hsotg *hsotg = hs_ep->parent; 903540ccba0SVahram Aharonyan u32 index; 904540ccba0SVahram Aharonyan u32 mask = 0; 9051d8e5c00SMinas Harutyunyan u8 pid = 0; 906540ccba0SVahram Aharonyan 907768a0741SLee Jones dwc2_gadget_get_desc_params(hs_ep, &mask); 908540ccba0SVahram Aharonyan 909729cac69SMinas Harutyunyan index = hs_ep->next_desc; 910540ccba0SVahram Aharonyan desc = &hs_ep->desc_list[index]; 911540ccba0SVahram Aharonyan 912729cac69SMinas Harutyunyan /* Check if descriptor chain full */ 913729cac69SMinas Harutyunyan if ((desc->status >> DEV_DMA_BUFF_STS_SHIFT) == 914729cac69SMinas Harutyunyan DEV_DMA_BUFF_STS_HREADY) { 915729cac69SMinas Harutyunyan dev_dbg(hsotg->dev, "%s: desc chain full\n", __func__); 916729cac69SMinas Harutyunyan return 1; 917729cac69SMinas Harutyunyan } 918729cac69SMinas Harutyunyan 919540ccba0SVahram Aharonyan /* Clear L bit of previous desc if more than one entries in the chain */ 920540ccba0SVahram Aharonyan if (hs_ep->next_desc) 921540ccba0SVahram Aharonyan hs_ep->desc_list[index - 1].status &= ~DEV_DMA_L; 922540ccba0SVahram Aharonyan 923540ccba0SVahram Aharonyan dev_dbg(hsotg->dev, "%s: Filling ep %d, dir %s isoc desc # %d\n", 924540ccba0SVahram Aharonyan __func__, hs_ep->index, hs_ep->dir_in ? "in" : "out", index); 925540ccba0SVahram Aharonyan 926540ccba0SVahram Aharonyan desc->status = 0; 927540ccba0SVahram Aharonyan desc->status |= (DEV_DMA_BUFF_STS_HBUSY << DEV_DMA_BUFF_STS_SHIFT); 928540ccba0SVahram Aharonyan 929540ccba0SVahram Aharonyan desc->buf = dma_buff; 930540ccba0SVahram Aharonyan desc->status |= (DEV_DMA_L | DEV_DMA_IOC | 931540ccba0SVahram Aharonyan ((len << DEV_DMA_NBYTES_SHIFT) & mask)); 932540ccba0SVahram Aharonyan 933540ccba0SVahram Aharonyan if (hs_ep->dir_in) { 9341d8e5c00SMinas Harutyunyan if (len) 9351d8e5c00SMinas Harutyunyan pid = DIV_ROUND_UP(len, hs_ep->ep.maxpacket); 9361d8e5c00SMinas Harutyunyan else 9371d8e5c00SMinas Harutyunyan pid = 1; 9381d8e5c00SMinas Harutyunyan desc->status |= ((pid << DEV_DMA_ISOC_PID_SHIFT) & 939540ccba0SVahram Aharonyan DEV_DMA_ISOC_PID_MASK) | 940540ccba0SVahram Aharonyan ((len % hs_ep->ep.maxpacket) ? 941540ccba0SVahram Aharonyan DEV_DMA_SHORT : 0) | 942540ccba0SVahram Aharonyan ((hs_ep->target_frame << 943540ccba0SVahram Aharonyan DEV_DMA_ISOC_FRNUM_SHIFT) & 944540ccba0SVahram Aharonyan DEV_DMA_ISOC_FRNUM_MASK); 945540ccba0SVahram Aharonyan } 946540ccba0SVahram Aharonyan 947540ccba0SVahram Aharonyan desc->status &= ~DEV_DMA_BUFF_STS_MASK; 948540ccba0SVahram Aharonyan desc->status |= (DEV_DMA_BUFF_STS_HREADY << DEV_DMA_BUFF_STS_SHIFT); 949540ccba0SVahram Aharonyan 950729cac69SMinas Harutyunyan /* Increment frame number by interval for IN */ 951729cac69SMinas Harutyunyan if (hs_ep->dir_in) 952729cac69SMinas Harutyunyan dwc2_gadget_incr_frame_num(hs_ep); 953729cac69SMinas Harutyunyan 954540ccba0SVahram Aharonyan /* Update index of last configured entry in the chain */ 955540ccba0SVahram Aharonyan hs_ep->next_desc++; 95654f37f56SMinas Harutyunyan if (hs_ep->next_desc >= MAX_DMA_DESC_NUM_HS_ISOC) 957729cac69SMinas Harutyunyan hs_ep->next_desc = 0; 958540ccba0SVahram Aharonyan 959540ccba0SVahram Aharonyan return 0; 960540ccba0SVahram Aharonyan } 961540ccba0SVahram Aharonyan 962540ccba0SVahram Aharonyan /* 963540ccba0SVahram Aharonyan * dwc2_gadget_start_isoc_ddma - start isochronous transfer in DDMA 964540ccba0SVahram Aharonyan * @hs_ep: The isochronous endpoint. 965540ccba0SVahram Aharonyan * 966729cac69SMinas Harutyunyan * Prepare descriptor chain for isochronous endpoints. Afterwards 967540ccba0SVahram Aharonyan * write DMA address to HW and enable the endpoint. 968540ccba0SVahram Aharonyan */ 969540ccba0SVahram Aharonyan static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep) 970540ccba0SVahram Aharonyan { 971540ccba0SVahram Aharonyan struct dwc2_hsotg *hsotg = hs_ep->parent; 972540ccba0SVahram Aharonyan struct dwc2_hsotg_req *hs_req, *treq; 973540ccba0SVahram Aharonyan int index = hs_ep->index; 974540ccba0SVahram Aharonyan int ret; 975729cac69SMinas Harutyunyan int i; 976540ccba0SVahram Aharonyan u32 dma_reg; 977540ccba0SVahram Aharonyan u32 depctl; 978540ccba0SVahram Aharonyan u32 ctrl; 979729cac69SMinas Harutyunyan struct dwc2_dma_desc *desc; 980540ccba0SVahram Aharonyan 981540ccba0SVahram Aharonyan if (list_empty(&hs_ep->queue)) { 9821ffba905SMinas Harutyunyan hs_ep->target_frame = TARGET_FRAME_INITIAL; 983540ccba0SVahram Aharonyan dev_dbg(hsotg->dev, "%s: No requests in queue\n", __func__); 984540ccba0SVahram Aharonyan return; 985540ccba0SVahram Aharonyan } 986540ccba0SVahram Aharonyan 987729cac69SMinas Harutyunyan /* Initialize descriptor chain by Host Busy status */ 98854f37f56SMinas Harutyunyan for (i = 0; i < MAX_DMA_DESC_NUM_HS_ISOC; i++) { 989729cac69SMinas Harutyunyan desc = &hs_ep->desc_list[i]; 990729cac69SMinas Harutyunyan desc->status = 0; 991729cac69SMinas Harutyunyan desc->status |= (DEV_DMA_BUFF_STS_HBUSY 992729cac69SMinas Harutyunyan << DEV_DMA_BUFF_STS_SHIFT); 993729cac69SMinas Harutyunyan } 994729cac69SMinas Harutyunyan 995729cac69SMinas Harutyunyan hs_ep->next_desc = 0; 996540ccba0SVahram Aharonyan list_for_each_entry_safe(hs_req, treq, &hs_ep->queue, queue) { 99710209abeSAndrzej Pietrasiewicz dma_addr_t dma_addr = hs_req->req.dma; 99810209abeSAndrzej Pietrasiewicz 99910209abeSAndrzej Pietrasiewicz if (hs_req->req.num_sgs) { 100010209abeSAndrzej Pietrasiewicz WARN_ON(hs_req->req.num_sgs > 1); 100110209abeSAndrzej Pietrasiewicz dma_addr = sg_dma_address(hs_req->req.sg); 100210209abeSAndrzej Pietrasiewicz } 100310209abeSAndrzej Pietrasiewicz ret = dwc2_gadget_fill_isoc_desc(hs_ep, dma_addr, 1004540ccba0SVahram Aharonyan hs_req->req.length); 1005729cac69SMinas Harutyunyan if (ret) 1006540ccba0SVahram Aharonyan break; 1007540ccba0SVahram Aharonyan } 1008540ccba0SVahram Aharonyan 1009729cac69SMinas Harutyunyan hs_ep->compl_desc = 0; 1010540ccba0SVahram Aharonyan depctl = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index); 1011540ccba0SVahram Aharonyan dma_reg = hs_ep->dir_in ? DIEPDMA(index) : DOEPDMA(index); 1012540ccba0SVahram Aharonyan 1013540ccba0SVahram Aharonyan /* write descriptor chain address to control register */ 1014f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, hs_ep->desc_list_dma, dma_reg); 1015540ccba0SVahram Aharonyan 1016f25c42b8SGevorg Sahakyan ctrl = dwc2_readl(hsotg, depctl); 1017540ccba0SVahram Aharonyan ctrl |= DXEPCTL_EPENA | DXEPCTL_CNAK; 1018f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ctrl, depctl); 1019540ccba0SVahram Aharonyan } 1020540ccba0SVahram Aharonyan 1021cf77b5fbSVahram Aharonyan /** 10221f91b4ccSFelipe Balbi * dwc2_hsotg_start_req - start a USB request from an endpoint's queue 102347a1685fSDinh Nguyen * @hsotg: The controller state. 102447a1685fSDinh Nguyen * @hs_ep: The endpoint to process a request for 102547a1685fSDinh Nguyen * @hs_req: The request to start. 102647a1685fSDinh Nguyen * @continuing: True if we are doing more for the current request. 102747a1685fSDinh Nguyen * 102847a1685fSDinh Nguyen * Start the given request running by setting the endpoint registers 102947a1685fSDinh Nguyen * appropriately, and writing any data to the FIFOs. 103047a1685fSDinh Nguyen */ 10311f91b4ccSFelipe Balbi static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, 10321f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep, 10331f91b4ccSFelipe Balbi struct dwc2_hsotg_req *hs_req, 103447a1685fSDinh Nguyen bool continuing) 103547a1685fSDinh Nguyen { 103647a1685fSDinh Nguyen struct usb_request *ureq = &hs_req->req; 103747a1685fSDinh Nguyen int index = hs_ep->index; 103847a1685fSDinh Nguyen int dir_in = hs_ep->dir_in; 103947a1685fSDinh Nguyen u32 epctrl_reg; 104047a1685fSDinh Nguyen u32 epsize_reg; 104147a1685fSDinh Nguyen u32 epsize; 104247a1685fSDinh Nguyen u32 ctrl; 10439da51974SJohn Youn unsigned int length; 10449da51974SJohn Youn unsigned int packets; 10459da51974SJohn Youn unsigned int maxreq; 1046aa3e8bc8SVahram Aharonyan unsigned int dma_reg; 104747a1685fSDinh Nguyen 104847a1685fSDinh Nguyen if (index != 0) { 104947a1685fSDinh Nguyen if (hs_ep->req && !continuing) { 105047a1685fSDinh Nguyen dev_err(hsotg->dev, "%s: active request\n", __func__); 105147a1685fSDinh Nguyen WARN_ON(1); 105247a1685fSDinh Nguyen return; 105347a1685fSDinh Nguyen } else if (hs_ep->req != hs_req && continuing) { 105447a1685fSDinh Nguyen dev_err(hsotg->dev, 105547a1685fSDinh Nguyen "%s: continue different req\n", __func__); 105647a1685fSDinh Nguyen WARN_ON(1); 105747a1685fSDinh Nguyen return; 105847a1685fSDinh Nguyen } 105947a1685fSDinh Nguyen } 106047a1685fSDinh Nguyen 1061aa3e8bc8SVahram Aharonyan dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index); 106247a1685fSDinh Nguyen epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); 106347a1685fSDinh Nguyen epsize_reg = dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index); 106447a1685fSDinh Nguyen 106547a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x, ep %d, dir %s\n", 1066f25c42b8SGevorg Sahakyan __func__, dwc2_readl(hsotg, epctrl_reg), index, 106747a1685fSDinh Nguyen hs_ep->dir_in ? "in" : "out"); 106847a1685fSDinh Nguyen 106947a1685fSDinh Nguyen /* If endpoint is stalled, we will restart request later */ 1070f25c42b8SGevorg Sahakyan ctrl = dwc2_readl(hsotg, epctrl_reg); 107147a1685fSDinh Nguyen 1072b2d4c54eSMian Yousaf Kaukab if (index && ctrl & DXEPCTL_STALL) { 107347a1685fSDinh Nguyen dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index); 107447a1685fSDinh Nguyen return; 107547a1685fSDinh Nguyen } 107647a1685fSDinh Nguyen 107747a1685fSDinh Nguyen length = ureq->length - ureq->actual; 107847a1685fSDinh Nguyen dev_dbg(hsotg->dev, "ureq->length:%d ureq->actual:%d\n", 107947a1685fSDinh Nguyen ureq->length, ureq->actual); 108047a1685fSDinh Nguyen 1081cf77b5fbSVahram Aharonyan if (!using_desc_dma(hsotg)) 108247a1685fSDinh Nguyen maxreq = get_ep_limit(hs_ep); 1083cf77b5fbSVahram Aharonyan else 1084cf77b5fbSVahram Aharonyan maxreq = dwc2_gadget_get_chain_limit(hs_ep); 1085cf77b5fbSVahram Aharonyan 108647a1685fSDinh Nguyen if (length > maxreq) { 108747a1685fSDinh Nguyen int round = maxreq % hs_ep->ep.maxpacket; 108847a1685fSDinh Nguyen 108947a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: length %d, max-req %d, r %d\n", 109047a1685fSDinh Nguyen __func__, length, maxreq, round); 109147a1685fSDinh Nguyen 109247a1685fSDinh Nguyen /* round down to multiple of packets */ 109347a1685fSDinh Nguyen if (round) 109447a1685fSDinh Nguyen maxreq -= round; 109547a1685fSDinh Nguyen 109647a1685fSDinh Nguyen length = maxreq; 109747a1685fSDinh Nguyen } 109847a1685fSDinh Nguyen 109947a1685fSDinh Nguyen if (length) 110047a1685fSDinh Nguyen packets = DIV_ROUND_UP(length, hs_ep->ep.maxpacket); 110147a1685fSDinh Nguyen else 110247a1685fSDinh Nguyen packets = 1; /* send one packet if length is zero. */ 110347a1685fSDinh Nguyen 110447a1685fSDinh Nguyen if (dir_in && index != 0) 110547a1685fSDinh Nguyen if (hs_ep->isochronous) 110647a1685fSDinh Nguyen epsize = DXEPTSIZ_MC(packets); 110747a1685fSDinh Nguyen else 110847a1685fSDinh Nguyen epsize = DXEPTSIZ_MC(1); 110947a1685fSDinh Nguyen else 111047a1685fSDinh Nguyen epsize = 0; 111147a1685fSDinh Nguyen 111247a1685fSDinh Nguyen /* 1113f71b5e25SMian Yousaf Kaukab * zero length packet should be programmed on its own and should not 1114f71b5e25SMian Yousaf Kaukab * be counted in DIEPTSIZ.PktCnt with other packets. 111547a1685fSDinh Nguyen */ 1116f71b5e25SMian Yousaf Kaukab if (dir_in && ureq->zero && !continuing) { 1117f71b5e25SMian Yousaf Kaukab /* Test if zlp is actually required. */ 1118f71b5e25SMian Yousaf Kaukab if ((ureq->length >= hs_ep->ep.maxpacket) && 1119f71b5e25SMian Yousaf Kaukab !(ureq->length % hs_ep->ep.maxpacket)) 11208a20fa45SMian Yousaf Kaukab hs_ep->send_zlp = 1; 112147a1685fSDinh Nguyen } 112247a1685fSDinh Nguyen 112347a1685fSDinh Nguyen epsize |= DXEPTSIZ_PKTCNT(packets); 112447a1685fSDinh Nguyen epsize |= DXEPTSIZ_XFERSIZE(length); 112547a1685fSDinh Nguyen 112647a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: %d@%d/%d, 0x%08x => 0x%08x\n", 112747a1685fSDinh Nguyen __func__, packets, length, ureq->length, epsize, epsize_reg); 112847a1685fSDinh Nguyen 112947a1685fSDinh Nguyen /* store the request as the current one we're doing */ 113047a1685fSDinh Nguyen hs_ep->req = hs_req; 113147a1685fSDinh Nguyen 1132aa3e8bc8SVahram Aharonyan if (using_desc_dma(hsotg)) { 1133aa3e8bc8SVahram Aharonyan u32 offset = 0; 1134aa3e8bc8SVahram Aharonyan u32 mps = hs_ep->ep.maxpacket; 1135aa3e8bc8SVahram Aharonyan 1136aa3e8bc8SVahram Aharonyan /* Adjust length: EP0 - MPS, other OUT EPs - multiple of MPS */ 1137aa3e8bc8SVahram Aharonyan if (!dir_in) { 1138aa3e8bc8SVahram Aharonyan if (!index) 1139aa3e8bc8SVahram Aharonyan length = mps; 1140aa3e8bc8SVahram Aharonyan else if (length % mps) 1141aa3e8bc8SVahram Aharonyan length += (mps - (length % mps)); 1142aa3e8bc8SVahram Aharonyan } 1143aa3e8bc8SVahram Aharonyan 1144b2c586ebSMinas Harutyunyan if (continuing) 1145aa3e8bc8SVahram Aharonyan offset = ureq->actual; 1146aa3e8bc8SVahram Aharonyan 1147aa3e8bc8SVahram Aharonyan /* Fill DDMA chain entries */ 1148066cfd07SAndrzej Pietrasiewicz dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, ureq->dma + offset, 1149aa3e8bc8SVahram Aharonyan length); 1150aa3e8bc8SVahram Aharonyan 1151aa3e8bc8SVahram Aharonyan /* write descriptor chain address to control register */ 1152f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, hs_ep->desc_list_dma, dma_reg); 1153aa3e8bc8SVahram Aharonyan 1154aa3e8bc8SVahram Aharonyan dev_dbg(hsotg->dev, "%s: %08x pad => 0x%08x\n", 1155aa3e8bc8SVahram Aharonyan __func__, (u32)hs_ep->desc_list_dma, dma_reg); 1156aa3e8bc8SVahram Aharonyan } else { 115747a1685fSDinh Nguyen /* write size / packets */ 1158f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, epsize, epsize_reg); 115947a1685fSDinh Nguyen 1160729e6574SRazmik Karapetyan if (using_dma(hsotg) && !continuing && (length != 0)) { 116147a1685fSDinh Nguyen /* 1162aa3e8bc8SVahram Aharonyan * write DMA address to control register, buffer 1163aa3e8bc8SVahram Aharonyan * already synced by dwc2_hsotg_ep_queue(). 116447a1685fSDinh Nguyen */ 116547a1685fSDinh Nguyen 1166f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ureq->dma, dma_reg); 116747a1685fSDinh Nguyen 11680cc4cf6fSFabio Estevam dev_dbg(hsotg->dev, "%s: %pad => 0x%08x\n", 116947a1685fSDinh Nguyen __func__, &ureq->dma, dma_reg); 117047a1685fSDinh Nguyen } 1171aa3e8bc8SVahram Aharonyan } 117247a1685fSDinh Nguyen 1173837e9f00SVardan Mikayelyan if (hs_ep->isochronous && hs_ep->interval == 1) { 1174837e9f00SVardan Mikayelyan hs_ep->target_frame = dwc2_hsotg_read_frameno(hsotg); 1175837e9f00SVardan Mikayelyan dwc2_gadget_incr_frame_num(hs_ep); 1176837e9f00SVardan Mikayelyan 1177837e9f00SVardan Mikayelyan if (hs_ep->target_frame & 0x1) 1178837e9f00SVardan Mikayelyan ctrl |= DXEPCTL_SETODDFR; 1179837e9f00SVardan Mikayelyan else 1180837e9f00SVardan Mikayelyan ctrl |= DXEPCTL_SETEVENFR; 1181837e9f00SVardan Mikayelyan } 1182837e9f00SVardan Mikayelyan 118347a1685fSDinh Nguyen ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ 118447a1685fSDinh Nguyen 1185fe0b94abSMian Yousaf Kaukab dev_dbg(hsotg->dev, "ep0 state:%d\n", hsotg->ep0_state); 118647a1685fSDinh Nguyen 118747a1685fSDinh Nguyen /* For Setup request do not clear NAK */ 1188fe0b94abSMian Yousaf Kaukab if (!(index == 0 && hsotg->ep0_state == DWC2_EP0_SETUP)) 118947a1685fSDinh Nguyen ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ 119047a1685fSDinh Nguyen 119147a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); 1192f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ctrl, epctrl_reg); 119347a1685fSDinh Nguyen 119447a1685fSDinh Nguyen /* 119547a1685fSDinh Nguyen * set these, it seems that DMA support increments past the end 119647a1685fSDinh Nguyen * of the packet buffer so we need to calculate the length from 119747a1685fSDinh Nguyen * this information. 119847a1685fSDinh Nguyen */ 119947a1685fSDinh Nguyen hs_ep->size_loaded = length; 120047a1685fSDinh Nguyen hs_ep->last_load = ureq->actual; 120147a1685fSDinh Nguyen 120247a1685fSDinh Nguyen if (dir_in && !using_dma(hsotg)) { 120347a1685fSDinh Nguyen /* set these anyway, we may need them for non-periodic in */ 120447a1685fSDinh Nguyen hs_ep->fifo_load = 0; 120547a1685fSDinh Nguyen 12061f91b4ccSFelipe Balbi dwc2_hsotg_write_fifo(hsotg, hs_ep, hs_req); 120747a1685fSDinh Nguyen } 120847a1685fSDinh Nguyen 120947a1685fSDinh Nguyen /* 121047a1685fSDinh Nguyen * Note, trying to clear the NAK here causes problems with transmit 121147a1685fSDinh Nguyen * on the S3C6400 ending up with the TXFIFO becoming full. 121247a1685fSDinh Nguyen */ 121347a1685fSDinh Nguyen 121447a1685fSDinh Nguyen /* check ep is enabled */ 1215f25c42b8SGevorg Sahakyan if (!(dwc2_readl(hsotg, epctrl_reg) & DXEPCTL_EPENA)) 12161a0ed863SMian Yousaf Kaukab dev_dbg(hsotg->dev, 121747a1685fSDinh Nguyen "ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n", 1218f25c42b8SGevorg Sahakyan index, dwc2_readl(hsotg, epctrl_reg)); 121947a1685fSDinh Nguyen 122047a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: DXEPCTL=0x%08x\n", 1221f25c42b8SGevorg Sahakyan __func__, dwc2_readl(hsotg, epctrl_reg)); 122247a1685fSDinh Nguyen 122347a1685fSDinh Nguyen /* enable ep interrupts */ 12241f91b4ccSFelipe Balbi dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 1); 122547a1685fSDinh Nguyen } 122647a1685fSDinh Nguyen 122747a1685fSDinh Nguyen /** 12281f91b4ccSFelipe Balbi * dwc2_hsotg_map_dma - map the DMA memory being used for the request 122947a1685fSDinh Nguyen * @hsotg: The device state. 123047a1685fSDinh Nguyen * @hs_ep: The endpoint the request is on. 123147a1685fSDinh Nguyen * @req: The request being processed. 123247a1685fSDinh Nguyen * 123347a1685fSDinh Nguyen * We've been asked to queue a request, so ensure that the memory buffer 123447a1685fSDinh Nguyen * is correctly setup for DMA. If we've been passed an extant DMA address 123547a1685fSDinh Nguyen * then ensure the buffer has been synced to memory. If our buffer has no 123647a1685fSDinh Nguyen * DMA memory, then we map the memory and mark our request to allow us to 123747a1685fSDinh Nguyen * cleanup on completion. 123847a1685fSDinh Nguyen */ 12391f91b4ccSFelipe Balbi static int dwc2_hsotg_map_dma(struct dwc2_hsotg *hsotg, 12401f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep, 124147a1685fSDinh Nguyen struct usb_request *req) 124247a1685fSDinh Nguyen { 124347a1685fSDinh Nguyen int ret; 124447a1685fSDinh Nguyen 124575a41ce4SPhil Elwell hs_ep->map_dir = hs_ep->dir_in; 124647a1685fSDinh Nguyen ret = usb_gadget_map_request(&hsotg->gadget, req, hs_ep->dir_in); 124747a1685fSDinh Nguyen if (ret) 124847a1685fSDinh Nguyen goto dma_error; 124947a1685fSDinh Nguyen 125047a1685fSDinh Nguyen return 0; 125147a1685fSDinh Nguyen 125247a1685fSDinh Nguyen dma_error: 125347a1685fSDinh Nguyen dev_err(hsotg->dev, "%s: failed to map buffer %p, %d bytes\n", 125447a1685fSDinh Nguyen __func__, req->buf, req->length); 125547a1685fSDinh Nguyen 125647a1685fSDinh Nguyen return -EIO; 125747a1685fSDinh Nguyen } 125847a1685fSDinh Nguyen 12591f91b4ccSFelipe Balbi static int dwc2_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg, 1260b98866c2SJohn Youn struct dwc2_hsotg_ep *hs_ep, 1261b98866c2SJohn Youn struct dwc2_hsotg_req *hs_req) 12627d24c1b5SMian Yousaf Kaukab { 12637d24c1b5SMian Yousaf Kaukab void *req_buf = hs_req->req.buf; 12647d24c1b5SMian Yousaf Kaukab 12657d24c1b5SMian Yousaf Kaukab /* If dma is not being used or buffer is aligned */ 12667d24c1b5SMian Yousaf Kaukab if (!using_dma(hsotg) || !((long)req_buf & 3)) 12677d24c1b5SMian Yousaf Kaukab return 0; 12687d24c1b5SMian Yousaf Kaukab 12697d24c1b5SMian Yousaf Kaukab WARN_ON(hs_req->saved_req_buf); 12707d24c1b5SMian Yousaf Kaukab 12717d24c1b5SMian Yousaf Kaukab dev_dbg(hsotg->dev, "%s: %s: buf=%p length=%d\n", __func__, 12727d24c1b5SMian Yousaf Kaukab hs_ep->ep.name, req_buf, hs_req->req.length); 12737d24c1b5SMian Yousaf Kaukab 12747d24c1b5SMian Yousaf Kaukab hs_req->req.buf = kmalloc(hs_req->req.length, GFP_ATOMIC); 12757d24c1b5SMian Yousaf Kaukab if (!hs_req->req.buf) { 12767d24c1b5SMian Yousaf Kaukab hs_req->req.buf = req_buf; 12777d24c1b5SMian Yousaf Kaukab dev_err(hsotg->dev, 12787d24c1b5SMian Yousaf Kaukab "%s: unable to allocate memory for bounce buffer\n", 12797d24c1b5SMian Yousaf Kaukab __func__); 12807d24c1b5SMian Yousaf Kaukab return -ENOMEM; 12817d24c1b5SMian Yousaf Kaukab } 12827d24c1b5SMian Yousaf Kaukab 12837d24c1b5SMian Yousaf Kaukab /* Save actual buffer */ 12847d24c1b5SMian Yousaf Kaukab hs_req->saved_req_buf = req_buf; 12857d24c1b5SMian Yousaf Kaukab 12867d24c1b5SMian Yousaf Kaukab if (hs_ep->dir_in) 12877d24c1b5SMian Yousaf Kaukab memcpy(hs_req->req.buf, req_buf, hs_req->req.length); 12887d24c1b5SMian Yousaf Kaukab return 0; 12897d24c1b5SMian Yousaf Kaukab } 12907d24c1b5SMian Yousaf Kaukab 1291b98866c2SJohn Youn static void 1292b98866c2SJohn Youn dwc2_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg, 1293b98866c2SJohn Youn struct dwc2_hsotg_ep *hs_ep, 1294b98866c2SJohn Youn struct dwc2_hsotg_req *hs_req) 12957d24c1b5SMian Yousaf Kaukab { 12967d24c1b5SMian Yousaf Kaukab /* If dma is not being used or buffer was aligned */ 12977d24c1b5SMian Yousaf Kaukab if (!using_dma(hsotg) || !hs_req->saved_req_buf) 12987d24c1b5SMian Yousaf Kaukab return; 12997d24c1b5SMian Yousaf Kaukab 13007d24c1b5SMian Yousaf Kaukab dev_dbg(hsotg->dev, "%s: %s: status=%d actual-length=%d\n", __func__, 13017d24c1b5SMian Yousaf Kaukab hs_ep->ep.name, hs_req->req.status, hs_req->req.actual); 13027d24c1b5SMian Yousaf Kaukab 13037d24c1b5SMian Yousaf Kaukab /* Copy data from bounce buffer on successful out transfer */ 13047d24c1b5SMian Yousaf Kaukab if (!hs_ep->dir_in && !hs_req->req.status) 13057d24c1b5SMian Yousaf Kaukab memcpy(hs_req->saved_req_buf, hs_req->req.buf, 13067d24c1b5SMian Yousaf Kaukab hs_req->req.actual); 13077d24c1b5SMian Yousaf Kaukab 13087d24c1b5SMian Yousaf Kaukab /* Free bounce buffer */ 13097d24c1b5SMian Yousaf Kaukab kfree(hs_req->req.buf); 13107d24c1b5SMian Yousaf Kaukab 13117d24c1b5SMian Yousaf Kaukab hs_req->req.buf = hs_req->saved_req_buf; 13127d24c1b5SMian Yousaf Kaukab hs_req->saved_req_buf = NULL; 13137d24c1b5SMian Yousaf Kaukab } 13147d24c1b5SMian Yousaf Kaukab 1315381fc8f8SVardan Mikayelyan /** 1316381fc8f8SVardan Mikayelyan * dwc2_gadget_target_frame_elapsed - Checks target frame 1317381fc8f8SVardan Mikayelyan * @hs_ep: The driver endpoint to check 1318381fc8f8SVardan Mikayelyan * 1319381fc8f8SVardan Mikayelyan * Returns 1 if targeted frame elapsed. If returned 1 then we need to drop 1320381fc8f8SVardan Mikayelyan * corresponding transfer. 1321381fc8f8SVardan Mikayelyan */ 1322381fc8f8SVardan Mikayelyan static bool dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep *hs_ep) 1323381fc8f8SVardan Mikayelyan { 1324381fc8f8SVardan Mikayelyan struct dwc2_hsotg *hsotg = hs_ep->parent; 1325381fc8f8SVardan Mikayelyan u32 target_frame = hs_ep->target_frame; 1326c7c24e7aSArtur Petrosyan u32 current_frame = hsotg->frame_number; 1327381fc8f8SVardan Mikayelyan bool frame_overrun = hs_ep->frame_overrun; 1328381fc8f8SVardan Mikayelyan 1329381fc8f8SVardan Mikayelyan if (!frame_overrun && current_frame >= target_frame) 1330381fc8f8SVardan Mikayelyan return true; 1331381fc8f8SVardan Mikayelyan 1332381fc8f8SVardan Mikayelyan if (frame_overrun && current_frame >= target_frame && 1333381fc8f8SVardan Mikayelyan ((current_frame - target_frame) < DSTS_SOFFN_LIMIT / 2)) 1334381fc8f8SVardan Mikayelyan return true; 1335381fc8f8SVardan Mikayelyan 1336381fc8f8SVardan Mikayelyan return false; 1337381fc8f8SVardan Mikayelyan } 1338381fc8f8SVardan Mikayelyan 1339e02f9aa6SVahram Aharonyan /* 1340e02f9aa6SVahram Aharonyan * dwc2_gadget_set_ep0_desc_chain - Set EP's desc chain pointers 1341e02f9aa6SVahram Aharonyan * @hsotg: The driver state 1342e02f9aa6SVahram Aharonyan * @hs_ep: the ep descriptor chain is for 1343e02f9aa6SVahram Aharonyan * 1344e02f9aa6SVahram Aharonyan * Called to update EP0 structure's pointers depend on stage of 1345e02f9aa6SVahram Aharonyan * control transfer. 1346e02f9aa6SVahram Aharonyan */ 1347e02f9aa6SVahram Aharonyan static int dwc2_gadget_set_ep0_desc_chain(struct dwc2_hsotg *hsotg, 1348e02f9aa6SVahram Aharonyan struct dwc2_hsotg_ep *hs_ep) 1349e02f9aa6SVahram Aharonyan { 1350e02f9aa6SVahram Aharonyan switch (hsotg->ep0_state) { 1351e02f9aa6SVahram Aharonyan case DWC2_EP0_SETUP: 1352e02f9aa6SVahram Aharonyan case DWC2_EP0_STATUS_OUT: 1353e02f9aa6SVahram Aharonyan hs_ep->desc_list = hsotg->setup_desc[0]; 1354e02f9aa6SVahram Aharonyan hs_ep->desc_list_dma = hsotg->setup_desc_dma[0]; 1355e02f9aa6SVahram Aharonyan break; 1356e02f9aa6SVahram Aharonyan case DWC2_EP0_DATA_IN: 1357e02f9aa6SVahram Aharonyan case DWC2_EP0_STATUS_IN: 1358e02f9aa6SVahram Aharonyan hs_ep->desc_list = hsotg->ctrl_in_desc; 1359e02f9aa6SVahram Aharonyan hs_ep->desc_list_dma = hsotg->ctrl_in_desc_dma; 1360e02f9aa6SVahram Aharonyan break; 1361e02f9aa6SVahram Aharonyan case DWC2_EP0_DATA_OUT: 1362e02f9aa6SVahram Aharonyan hs_ep->desc_list = hsotg->ctrl_out_desc; 1363e02f9aa6SVahram Aharonyan hs_ep->desc_list_dma = hsotg->ctrl_out_desc_dma; 1364e02f9aa6SVahram Aharonyan break; 1365e02f9aa6SVahram Aharonyan default: 1366e02f9aa6SVahram Aharonyan dev_err(hsotg->dev, "invalid EP 0 state in queue %d\n", 1367e02f9aa6SVahram Aharonyan hsotg->ep0_state); 1368e02f9aa6SVahram Aharonyan return -EINVAL; 1369e02f9aa6SVahram Aharonyan } 1370e02f9aa6SVahram Aharonyan 1371e02f9aa6SVahram Aharonyan return 0; 1372e02f9aa6SVahram Aharonyan } 1373e02f9aa6SVahram Aharonyan 13741f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, 137547a1685fSDinh Nguyen gfp_t gfp_flags) 137647a1685fSDinh Nguyen { 13771f91b4ccSFelipe Balbi struct dwc2_hsotg_req *hs_req = our_req(req); 13781f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = our_ep(ep); 1379941fcce4SDinh Nguyen struct dwc2_hsotg *hs = hs_ep->parent; 138047a1685fSDinh Nguyen bool first; 13817d24c1b5SMian Yousaf Kaukab int ret; 1382729cac69SMinas Harutyunyan u32 maxsize = 0; 1383729cac69SMinas Harutyunyan u32 mask = 0; 1384729cac69SMinas Harutyunyan 138547a1685fSDinh Nguyen 138647a1685fSDinh Nguyen dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n", 138747a1685fSDinh Nguyen ep->name, req, req->length, req->buf, req->no_interrupt, 138847a1685fSDinh Nguyen req->zero, req->short_not_ok); 138947a1685fSDinh Nguyen 13907ababa92SGregory Herrero /* Prevent new request submission when controller is suspended */ 139188b02f2cSGrigor Tovmasyan if (hs->lx_state != DWC2_L0) { 139288b02f2cSGrigor Tovmasyan dev_dbg(hs->dev, "%s: submit request only in active state\n", 13937ababa92SGregory Herrero __func__); 13947ababa92SGregory Herrero return -EAGAIN; 13957ababa92SGregory Herrero } 13967ababa92SGregory Herrero 139747a1685fSDinh Nguyen /* initialise status of the request */ 139847a1685fSDinh Nguyen INIT_LIST_HEAD(&hs_req->queue); 139947a1685fSDinh Nguyen req->actual = 0; 140047a1685fSDinh Nguyen req->status = -EINPROGRESS; 140147a1685fSDinh Nguyen 1402860ef6cdSMinas Harutyunyan /* Don't queue ISOC request if length greater than mps*mc */ 1403860ef6cdSMinas Harutyunyan if (hs_ep->isochronous && 1404860ef6cdSMinas Harutyunyan req->length > (hs_ep->mc * hs_ep->ep.maxpacket)) { 1405860ef6cdSMinas Harutyunyan dev_err(hs->dev, "req length > maxpacket*mc\n"); 1406860ef6cdSMinas Harutyunyan return -EINVAL; 1407860ef6cdSMinas Harutyunyan } 1408860ef6cdSMinas Harutyunyan 1409729cac69SMinas Harutyunyan /* In DDMA mode for ISOC's don't queue request if length greater 1410729cac69SMinas Harutyunyan * than descriptor limits. 1411729cac69SMinas Harutyunyan */ 1412729cac69SMinas Harutyunyan if (using_desc_dma(hs) && hs_ep->isochronous) { 1413729cac69SMinas Harutyunyan maxsize = dwc2_gadget_get_desc_params(hs_ep, &mask); 1414729cac69SMinas Harutyunyan if (hs_ep->dir_in && req->length > maxsize) { 1415729cac69SMinas Harutyunyan dev_err(hs->dev, "wrong length %d (maxsize=%d)\n", 1416729cac69SMinas Harutyunyan req->length, maxsize); 1417729cac69SMinas Harutyunyan return -EINVAL; 1418729cac69SMinas Harutyunyan } 1419729cac69SMinas Harutyunyan 1420729cac69SMinas Harutyunyan if (!hs_ep->dir_in && req->length > hs_ep->ep.maxpacket) { 1421729cac69SMinas Harutyunyan dev_err(hs->dev, "ISOC OUT: wrong length %d (mps=%d)\n", 1422729cac69SMinas Harutyunyan req->length, hs_ep->ep.maxpacket); 1423729cac69SMinas Harutyunyan return -EINVAL; 1424729cac69SMinas Harutyunyan } 1425729cac69SMinas Harutyunyan } 1426729cac69SMinas Harutyunyan 14271f91b4ccSFelipe Balbi ret = dwc2_hsotg_handle_unaligned_buf_start(hs, hs_ep, hs_req); 14287d24c1b5SMian Yousaf Kaukab if (ret) 14297d24c1b5SMian Yousaf Kaukab return ret; 14307d24c1b5SMian Yousaf Kaukab 143147a1685fSDinh Nguyen /* if we're using DMA, sync the buffers as necessary */ 143247a1685fSDinh Nguyen if (using_dma(hs)) { 14331f91b4ccSFelipe Balbi ret = dwc2_hsotg_map_dma(hs, hs_ep, req); 143447a1685fSDinh Nguyen if (ret) 143547a1685fSDinh Nguyen return ret; 143647a1685fSDinh Nguyen } 1437e02f9aa6SVahram Aharonyan /* If using descriptor DMA configure EP0 descriptor chain pointers */ 1438e02f9aa6SVahram Aharonyan if (using_desc_dma(hs) && !hs_ep->index) { 1439e02f9aa6SVahram Aharonyan ret = dwc2_gadget_set_ep0_desc_chain(hs, hs_ep); 1440e02f9aa6SVahram Aharonyan if (ret) 1441e02f9aa6SVahram Aharonyan return ret; 1442e02f9aa6SVahram Aharonyan } 144347a1685fSDinh Nguyen 144447a1685fSDinh Nguyen first = list_empty(&hs_ep->queue); 144547a1685fSDinh Nguyen list_add_tail(&hs_req->queue, &hs_ep->queue); 144647a1685fSDinh Nguyen 1447540ccba0SVahram Aharonyan /* 1448540ccba0SVahram Aharonyan * Handle DDMA isochronous transfers separately - just add new entry 1449729cac69SMinas Harutyunyan * to the descriptor chain. 1450540ccba0SVahram Aharonyan * Transfer will be started once SW gets either one of NAK or 1451540ccba0SVahram Aharonyan * OutTknEpDis interrupts. 1452540ccba0SVahram Aharonyan */ 1453729cac69SMinas Harutyunyan if (using_desc_dma(hs) && hs_ep->isochronous) { 1454729cac69SMinas Harutyunyan if (hs_ep->target_frame != TARGET_FRAME_INITIAL) { 145510209abeSAndrzej Pietrasiewicz dma_addr_t dma_addr = hs_req->req.dma; 145610209abeSAndrzej Pietrasiewicz 145710209abeSAndrzej Pietrasiewicz if (hs_req->req.num_sgs) { 145810209abeSAndrzej Pietrasiewicz WARN_ON(hs_req->req.num_sgs > 1); 145910209abeSAndrzej Pietrasiewicz dma_addr = sg_dma_address(hs_req->req.sg); 146010209abeSAndrzej Pietrasiewicz } 146110209abeSAndrzej Pietrasiewicz dwc2_gadget_fill_isoc_desc(hs_ep, dma_addr, 1462540ccba0SVahram Aharonyan hs_req->req.length); 1463729cac69SMinas Harutyunyan } 1464540ccba0SVahram Aharonyan return 0; 1465540ccba0SVahram Aharonyan } 1466540ccba0SVahram Aharonyan 1467b4c53b4aSMinas Harutyunyan /* Change EP direction if status phase request is after data out */ 1468b4c53b4aSMinas Harutyunyan if (!hs_ep->index && !req->length && !hs_ep->dir_in && 1469b4c53b4aSMinas Harutyunyan hs->ep0_state == DWC2_EP0_DATA_OUT) 1470b4c53b4aSMinas Harutyunyan hs_ep->dir_in = 1; 1471b4c53b4aSMinas Harutyunyan 1472837e9f00SVardan Mikayelyan if (first) { 1473837e9f00SVardan Mikayelyan if (!hs_ep->isochronous) { 14741f91b4ccSFelipe Balbi dwc2_hsotg_start_req(hs, hs_ep, hs_req, false); 1475837e9f00SVardan Mikayelyan return 0; 1476837e9f00SVardan Mikayelyan } 147747a1685fSDinh Nguyen 1478c7c24e7aSArtur Petrosyan /* Update current frame number value. */ 1479c7c24e7aSArtur Petrosyan hs->frame_number = dwc2_hsotg_read_frameno(hs); 1480c7c24e7aSArtur Petrosyan while (dwc2_gadget_target_frame_elapsed(hs_ep)) { 1481837e9f00SVardan Mikayelyan dwc2_gadget_incr_frame_num(hs_ep); 1482c7c24e7aSArtur Petrosyan /* Update current frame number value once more as it 1483c7c24e7aSArtur Petrosyan * changes here. 1484c7c24e7aSArtur Petrosyan */ 1485c7c24e7aSArtur Petrosyan hs->frame_number = dwc2_hsotg_read_frameno(hs); 1486c7c24e7aSArtur Petrosyan } 1487837e9f00SVardan Mikayelyan 1488837e9f00SVardan Mikayelyan if (hs_ep->target_frame != TARGET_FRAME_INITIAL) 1489837e9f00SVardan Mikayelyan dwc2_hsotg_start_req(hs, hs_ep, hs_req, false); 1490837e9f00SVardan Mikayelyan } 149147a1685fSDinh Nguyen return 0; 149247a1685fSDinh Nguyen } 149347a1685fSDinh Nguyen 14941f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req, 149547a1685fSDinh Nguyen gfp_t gfp_flags) 149647a1685fSDinh Nguyen { 14971f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = our_ep(ep); 1498941fcce4SDinh Nguyen struct dwc2_hsotg *hs = hs_ep->parent; 14998879904bSJohan Hovold unsigned long flags; 15008879904bSJohan Hovold int ret; 150147a1685fSDinh Nguyen 150247a1685fSDinh Nguyen spin_lock_irqsave(&hs->lock, flags); 15031f91b4ccSFelipe Balbi ret = dwc2_hsotg_ep_queue(ep, req, gfp_flags); 150447a1685fSDinh Nguyen spin_unlock_irqrestore(&hs->lock, flags); 150547a1685fSDinh Nguyen 150647a1685fSDinh Nguyen return ret; 150747a1685fSDinh Nguyen } 150847a1685fSDinh Nguyen 15091f91b4ccSFelipe Balbi static void dwc2_hsotg_ep_free_request(struct usb_ep *ep, 151047a1685fSDinh Nguyen struct usb_request *req) 151147a1685fSDinh Nguyen { 15121f91b4ccSFelipe Balbi struct dwc2_hsotg_req *hs_req = our_req(req); 151347a1685fSDinh Nguyen 151447a1685fSDinh Nguyen kfree(hs_req); 151547a1685fSDinh Nguyen } 151647a1685fSDinh Nguyen 151747a1685fSDinh Nguyen /** 15181f91b4ccSFelipe Balbi * dwc2_hsotg_complete_oursetup - setup completion callback 151947a1685fSDinh Nguyen * @ep: The endpoint the request was on. 152047a1685fSDinh Nguyen * @req: The request completed. 152147a1685fSDinh Nguyen * 152247a1685fSDinh Nguyen * Called on completion of any requests the driver itself 152347a1685fSDinh Nguyen * submitted that need cleaning up. 152447a1685fSDinh Nguyen */ 15251f91b4ccSFelipe Balbi static void dwc2_hsotg_complete_oursetup(struct usb_ep *ep, 152647a1685fSDinh Nguyen struct usb_request *req) 152747a1685fSDinh Nguyen { 15281f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = our_ep(ep); 1529941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = hs_ep->parent; 153047a1685fSDinh Nguyen 153147a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: ep %p, req %p\n", __func__, ep, req); 153247a1685fSDinh Nguyen 15331f91b4ccSFelipe Balbi dwc2_hsotg_ep_free_request(ep, req); 153447a1685fSDinh Nguyen } 153547a1685fSDinh Nguyen 153647a1685fSDinh Nguyen /** 153747a1685fSDinh Nguyen * ep_from_windex - convert control wIndex value to endpoint 153847a1685fSDinh Nguyen * @hsotg: The driver state. 153947a1685fSDinh Nguyen * @windex: The control request wIndex field (in host order). 154047a1685fSDinh Nguyen * 154147a1685fSDinh Nguyen * Convert the given wIndex into a pointer to an driver endpoint 154247a1685fSDinh Nguyen * structure, or return NULL if it is not a valid endpoint. 154347a1685fSDinh Nguyen */ 15441f91b4ccSFelipe Balbi static struct dwc2_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg, 154547a1685fSDinh Nguyen u32 windex) 154647a1685fSDinh Nguyen { 154747a1685fSDinh Nguyen int dir = (windex & USB_DIR_IN) ? 1 : 0; 154847a1685fSDinh Nguyen int idx = windex & 0x7F; 154947a1685fSDinh Nguyen 155047a1685fSDinh Nguyen if (windex >= 0x100) 155147a1685fSDinh Nguyen return NULL; 155247a1685fSDinh Nguyen 155347a1685fSDinh Nguyen if (idx > hsotg->num_of_eps) 155447a1685fSDinh Nguyen return NULL; 155547a1685fSDinh Nguyen 1556f670e9f9SHeiko Stuebner return index_to_ep(hsotg, idx, dir); 155747a1685fSDinh Nguyen } 155847a1685fSDinh Nguyen 155947a1685fSDinh Nguyen /** 15601f91b4ccSFelipe Balbi * dwc2_hsotg_set_test_mode - Enable usb Test Modes 15619e14d0a5SGregory Herrero * @hsotg: The driver state. 15629e14d0a5SGregory Herrero * @testmode: requested usb test mode 15639e14d0a5SGregory Herrero * Enable usb Test Mode requested by the Host. 15649e14d0a5SGregory Herrero */ 15651f91b4ccSFelipe Balbi int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) 15669e14d0a5SGregory Herrero { 1567f25c42b8SGevorg Sahakyan int dctl = dwc2_readl(hsotg, DCTL); 15689e14d0a5SGregory Herrero 15699e14d0a5SGregory Herrero dctl &= ~DCTL_TSTCTL_MASK; 15709e14d0a5SGregory Herrero switch (testmode) { 157162fb45d3SGreg Kroah-Hartman case USB_TEST_J: 157262fb45d3SGreg Kroah-Hartman case USB_TEST_K: 157362fb45d3SGreg Kroah-Hartman case USB_TEST_SE0_NAK: 157462fb45d3SGreg Kroah-Hartman case USB_TEST_PACKET: 157562fb45d3SGreg Kroah-Hartman case USB_TEST_FORCE_ENABLE: 15769e14d0a5SGregory Herrero dctl |= testmode << DCTL_TSTCTL_SHIFT; 15779e14d0a5SGregory Herrero break; 15789e14d0a5SGregory Herrero default: 15799e14d0a5SGregory Herrero return -EINVAL; 15809e14d0a5SGregory Herrero } 1581f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dctl, DCTL); 15829e14d0a5SGregory Herrero return 0; 15839e14d0a5SGregory Herrero } 15849e14d0a5SGregory Herrero 15859e14d0a5SGregory Herrero /** 15861f91b4ccSFelipe Balbi * dwc2_hsotg_send_reply - send reply to control request 158747a1685fSDinh Nguyen * @hsotg: The device state 158847a1685fSDinh Nguyen * @ep: Endpoint 0 158947a1685fSDinh Nguyen * @buff: Buffer for request 159047a1685fSDinh Nguyen * @length: Length of reply. 159147a1685fSDinh Nguyen * 159247a1685fSDinh Nguyen * Create a request and queue it on the given endpoint. This is useful as 159347a1685fSDinh Nguyen * an internal method of sending replies to certain control requests, etc. 159447a1685fSDinh Nguyen */ 15951f91b4ccSFelipe Balbi static int dwc2_hsotg_send_reply(struct dwc2_hsotg *hsotg, 15961f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *ep, 159747a1685fSDinh Nguyen void *buff, 159847a1685fSDinh Nguyen int length) 159947a1685fSDinh Nguyen { 160047a1685fSDinh Nguyen struct usb_request *req; 160147a1685fSDinh Nguyen int ret; 160247a1685fSDinh Nguyen 160347a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: buff %p, len %d\n", __func__, buff, length); 160447a1685fSDinh Nguyen 16051f91b4ccSFelipe Balbi req = dwc2_hsotg_ep_alloc_request(&ep->ep, GFP_ATOMIC); 160647a1685fSDinh Nguyen hsotg->ep0_reply = req; 160747a1685fSDinh Nguyen if (!req) { 160847a1685fSDinh Nguyen dev_warn(hsotg->dev, "%s: cannot alloc req\n", __func__); 160947a1685fSDinh Nguyen return -ENOMEM; 161047a1685fSDinh Nguyen } 161147a1685fSDinh Nguyen 161247a1685fSDinh Nguyen req->buf = hsotg->ep0_buff; 161347a1685fSDinh Nguyen req->length = length; 1614f71b5e25SMian Yousaf Kaukab /* 1615f71b5e25SMian Yousaf Kaukab * zero flag is for sending zlp in DATA IN stage. It has no impact on 1616f71b5e25SMian Yousaf Kaukab * STATUS stage. 1617f71b5e25SMian Yousaf Kaukab */ 1618f71b5e25SMian Yousaf Kaukab req->zero = 0; 16191f91b4ccSFelipe Balbi req->complete = dwc2_hsotg_complete_oursetup; 162047a1685fSDinh Nguyen 162147a1685fSDinh Nguyen if (length) 162247a1685fSDinh Nguyen memcpy(req->buf, buff, length); 162347a1685fSDinh Nguyen 16241f91b4ccSFelipe Balbi ret = dwc2_hsotg_ep_queue(&ep->ep, req, GFP_ATOMIC); 162547a1685fSDinh Nguyen if (ret) { 162647a1685fSDinh Nguyen dev_warn(hsotg->dev, "%s: cannot queue req\n", __func__); 162747a1685fSDinh Nguyen return ret; 162847a1685fSDinh Nguyen } 162947a1685fSDinh Nguyen 163047a1685fSDinh Nguyen return 0; 163147a1685fSDinh Nguyen } 163247a1685fSDinh Nguyen 163347a1685fSDinh Nguyen /** 16341f91b4ccSFelipe Balbi * dwc2_hsotg_process_req_status - process request GET_STATUS 163547a1685fSDinh Nguyen * @hsotg: The device state 163647a1685fSDinh Nguyen * @ctrl: USB control request 163747a1685fSDinh Nguyen */ 16381f91b4ccSFelipe Balbi static int dwc2_hsotg_process_req_status(struct dwc2_hsotg *hsotg, 163947a1685fSDinh Nguyen struct usb_ctrlrequest *ctrl) 164047a1685fSDinh Nguyen { 16411f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0]; 16421f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *ep; 164347a1685fSDinh Nguyen __le16 reply; 16449a0d6f7cSMinas Harutyunyan u16 status; 164547a1685fSDinh Nguyen int ret; 164647a1685fSDinh Nguyen 164747a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: USB_REQ_GET_STATUS\n", __func__); 164847a1685fSDinh Nguyen 164947a1685fSDinh Nguyen if (!ep0->dir_in) { 165047a1685fSDinh Nguyen dev_warn(hsotg->dev, "%s: direction out?\n", __func__); 165147a1685fSDinh Nguyen return -EINVAL; 165247a1685fSDinh Nguyen } 165347a1685fSDinh Nguyen 165447a1685fSDinh Nguyen switch (ctrl->bRequestType & USB_RECIP_MASK) { 165547a1685fSDinh Nguyen case USB_RECIP_DEVICE: 16561a0808cbSJohn Keeping status = hsotg->gadget.is_selfpowered << 16571a0808cbSJohn Keeping USB_DEVICE_SELF_POWERED; 16589a0d6f7cSMinas Harutyunyan status |= hsotg->remote_wakeup_allowed << 16599a0d6f7cSMinas Harutyunyan USB_DEVICE_REMOTE_WAKEUP; 16609a0d6f7cSMinas Harutyunyan reply = cpu_to_le16(status); 166147a1685fSDinh Nguyen break; 166247a1685fSDinh Nguyen 166347a1685fSDinh Nguyen case USB_RECIP_INTERFACE: 166447a1685fSDinh Nguyen /* currently, the data result should be zero */ 166547a1685fSDinh Nguyen reply = cpu_to_le16(0); 166647a1685fSDinh Nguyen break; 166747a1685fSDinh Nguyen 166847a1685fSDinh Nguyen case USB_RECIP_ENDPOINT: 166947a1685fSDinh Nguyen ep = ep_from_windex(hsotg, le16_to_cpu(ctrl->wIndex)); 167047a1685fSDinh Nguyen if (!ep) 167147a1685fSDinh Nguyen return -ENOENT; 167247a1685fSDinh Nguyen 167347a1685fSDinh Nguyen reply = cpu_to_le16(ep->halted ? 1 : 0); 167447a1685fSDinh Nguyen break; 167547a1685fSDinh Nguyen 167647a1685fSDinh Nguyen default: 167747a1685fSDinh Nguyen return 0; 167847a1685fSDinh Nguyen } 167947a1685fSDinh Nguyen 168047a1685fSDinh Nguyen if (le16_to_cpu(ctrl->wLength) != 2) 168147a1685fSDinh Nguyen return -EINVAL; 168247a1685fSDinh Nguyen 16831f91b4ccSFelipe Balbi ret = dwc2_hsotg_send_reply(hsotg, ep0, &reply, 2); 168447a1685fSDinh Nguyen if (ret) { 168547a1685fSDinh Nguyen dev_err(hsotg->dev, "%s: failed to send reply\n", __func__); 168647a1685fSDinh Nguyen return ret; 168747a1685fSDinh Nguyen } 168847a1685fSDinh Nguyen 168947a1685fSDinh Nguyen return 1; 169047a1685fSDinh Nguyen } 169147a1685fSDinh Nguyen 169251da43b5SVahram Aharonyan static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now); 169347a1685fSDinh Nguyen 169447a1685fSDinh Nguyen /** 169547a1685fSDinh Nguyen * get_ep_head - return the first request on the endpoint 169647a1685fSDinh Nguyen * @hs_ep: The controller endpoint to get 169747a1685fSDinh Nguyen * 169847a1685fSDinh Nguyen * Get the first request on the endpoint. 169947a1685fSDinh Nguyen */ 17001f91b4ccSFelipe Balbi static struct dwc2_hsotg_req *get_ep_head(struct dwc2_hsotg_ep *hs_ep) 170147a1685fSDinh Nguyen { 1702ffc4b406SMasahiro Yamada return list_first_entry_or_null(&hs_ep->queue, struct dwc2_hsotg_req, 1703ffc4b406SMasahiro Yamada queue); 170447a1685fSDinh Nguyen } 170547a1685fSDinh Nguyen 170647a1685fSDinh Nguyen /** 170741cc4cd2SVardan Mikayelyan * dwc2_gadget_start_next_request - Starts next request from ep queue 170841cc4cd2SVardan Mikayelyan * @hs_ep: Endpoint structure 170941cc4cd2SVardan Mikayelyan * 171041cc4cd2SVardan Mikayelyan * If queue is empty and EP is ISOC-OUT - unmasks OUTTKNEPDIS which is masked 171141cc4cd2SVardan Mikayelyan * in its handler. Hence we need to unmask it here to be able to do 171241cc4cd2SVardan Mikayelyan * resynchronization. 171341cc4cd2SVardan Mikayelyan */ 171441cc4cd2SVardan Mikayelyan static void dwc2_gadget_start_next_request(struct dwc2_hsotg_ep *hs_ep) 171541cc4cd2SVardan Mikayelyan { 171641cc4cd2SVardan Mikayelyan u32 mask; 171741cc4cd2SVardan Mikayelyan struct dwc2_hsotg *hsotg = hs_ep->parent; 171841cc4cd2SVardan Mikayelyan int dir_in = hs_ep->dir_in; 171941cc4cd2SVardan Mikayelyan struct dwc2_hsotg_req *hs_req; 172041cc4cd2SVardan Mikayelyan u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK; 172141cc4cd2SVardan Mikayelyan 172241cc4cd2SVardan Mikayelyan if (!list_empty(&hs_ep->queue)) { 172341cc4cd2SVardan Mikayelyan hs_req = get_ep_head(hs_ep); 172441cc4cd2SVardan Mikayelyan dwc2_hsotg_start_req(hsotg, hs_ep, hs_req, false); 172541cc4cd2SVardan Mikayelyan return; 172641cc4cd2SVardan Mikayelyan } 172741cc4cd2SVardan Mikayelyan if (!hs_ep->isochronous) 172841cc4cd2SVardan Mikayelyan return; 172941cc4cd2SVardan Mikayelyan 173041cc4cd2SVardan Mikayelyan if (dir_in) { 173141cc4cd2SVardan Mikayelyan dev_dbg(hsotg->dev, "%s: No more ISOC-IN requests\n", 173241cc4cd2SVardan Mikayelyan __func__); 173341cc4cd2SVardan Mikayelyan } else { 173441cc4cd2SVardan Mikayelyan dev_dbg(hsotg->dev, "%s: No more ISOC-OUT requests\n", 173541cc4cd2SVardan Mikayelyan __func__); 1736f25c42b8SGevorg Sahakyan mask = dwc2_readl(hsotg, epmsk_reg); 173741cc4cd2SVardan Mikayelyan mask |= DOEPMSK_OUTTKNEPDISMSK; 1738f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, mask, epmsk_reg); 173941cc4cd2SVardan Mikayelyan } 174041cc4cd2SVardan Mikayelyan } 174141cc4cd2SVardan Mikayelyan 174241cc4cd2SVardan Mikayelyan /** 17431f91b4ccSFelipe Balbi * dwc2_hsotg_process_req_feature - process request {SET,CLEAR}_FEATURE 174447a1685fSDinh Nguyen * @hsotg: The device state 174547a1685fSDinh Nguyen * @ctrl: USB control request 174647a1685fSDinh Nguyen */ 17471f91b4ccSFelipe Balbi static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, 174847a1685fSDinh Nguyen struct usb_ctrlrequest *ctrl) 174947a1685fSDinh Nguyen { 17501f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0]; 17511f91b4ccSFelipe Balbi struct dwc2_hsotg_req *hs_req; 175247a1685fSDinh Nguyen bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); 17531f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *ep; 175447a1685fSDinh Nguyen int ret; 175547a1685fSDinh Nguyen bool halted; 17569e14d0a5SGregory Herrero u32 recip; 17579e14d0a5SGregory Herrero u32 wValue; 17589e14d0a5SGregory Herrero u32 wIndex; 175947a1685fSDinh Nguyen 176047a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: %s_FEATURE\n", 176147a1685fSDinh Nguyen __func__, set ? "SET" : "CLEAR"); 176247a1685fSDinh Nguyen 17639e14d0a5SGregory Herrero wValue = le16_to_cpu(ctrl->wValue); 17649e14d0a5SGregory Herrero wIndex = le16_to_cpu(ctrl->wIndex); 17659e14d0a5SGregory Herrero recip = ctrl->bRequestType & USB_RECIP_MASK; 17669e14d0a5SGregory Herrero 17679e14d0a5SGregory Herrero switch (recip) { 17689e14d0a5SGregory Herrero case USB_RECIP_DEVICE: 17699e14d0a5SGregory Herrero switch (wValue) { 1770fa389a6dSVardan Mikayelyan case USB_DEVICE_REMOTE_WAKEUP: 17719a0d6f7cSMinas Harutyunyan if (set) 1772fa389a6dSVardan Mikayelyan hsotg->remote_wakeup_allowed = 1; 17739a0d6f7cSMinas Harutyunyan else 17749a0d6f7cSMinas Harutyunyan hsotg->remote_wakeup_allowed = 0; 1775fa389a6dSVardan Mikayelyan break; 1776fa389a6dSVardan Mikayelyan 17779e14d0a5SGregory Herrero case USB_DEVICE_TEST_MODE: 17789e14d0a5SGregory Herrero if ((wIndex & 0xff) != 0) 17799e14d0a5SGregory Herrero return -EINVAL; 17809e14d0a5SGregory Herrero if (!set) 17819e14d0a5SGregory Herrero return -EINVAL; 17829e14d0a5SGregory Herrero 17839e14d0a5SGregory Herrero hsotg->test_mode = wIndex >> 8; 17849a0d6f7cSMinas Harutyunyan break; 17859a0d6f7cSMinas Harutyunyan default: 17869a0d6f7cSMinas Harutyunyan return -ENOENT; 17879a0d6f7cSMinas Harutyunyan } 17889a0d6f7cSMinas Harutyunyan 17891f91b4ccSFelipe Balbi ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0); 17909e14d0a5SGregory Herrero if (ret) { 17919e14d0a5SGregory Herrero dev_err(hsotg->dev, 17929e14d0a5SGregory Herrero "%s: failed to send reply\n", __func__); 17939e14d0a5SGregory Herrero return ret; 17949e14d0a5SGregory Herrero } 17959e14d0a5SGregory Herrero break; 17969e14d0a5SGregory Herrero 17979e14d0a5SGregory Herrero case USB_RECIP_ENDPOINT: 17989e14d0a5SGregory Herrero ep = ep_from_windex(hsotg, wIndex); 179947a1685fSDinh Nguyen if (!ep) { 180047a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: no endpoint for 0x%04x\n", 18019e14d0a5SGregory Herrero __func__, wIndex); 180247a1685fSDinh Nguyen return -ENOENT; 180347a1685fSDinh Nguyen } 180447a1685fSDinh Nguyen 18059e14d0a5SGregory Herrero switch (wValue) { 180647a1685fSDinh Nguyen case USB_ENDPOINT_HALT: 180747a1685fSDinh Nguyen halted = ep->halted; 180847a1685fSDinh Nguyen 1809b833ce15SMinas Harutyunyan if (!ep->wedged) 181051da43b5SVahram Aharonyan dwc2_hsotg_ep_sethalt(&ep->ep, set, true); 181147a1685fSDinh Nguyen 18121f91b4ccSFelipe Balbi ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0); 181347a1685fSDinh Nguyen if (ret) { 181447a1685fSDinh Nguyen dev_err(hsotg->dev, 181547a1685fSDinh Nguyen "%s: failed to send reply\n", __func__); 181647a1685fSDinh Nguyen return ret; 181747a1685fSDinh Nguyen } 181847a1685fSDinh Nguyen 181947a1685fSDinh Nguyen /* 182047a1685fSDinh Nguyen * we have to complete all requests for ep if it was 182147a1685fSDinh Nguyen * halted, and the halt was cleared by CLEAR_FEATURE 182247a1685fSDinh Nguyen */ 182347a1685fSDinh Nguyen 182447a1685fSDinh Nguyen if (!set && halted) { 182547a1685fSDinh Nguyen /* 182647a1685fSDinh Nguyen * If we have request in progress, 182747a1685fSDinh Nguyen * then complete it 182847a1685fSDinh Nguyen */ 182947a1685fSDinh Nguyen if (ep->req) { 183047a1685fSDinh Nguyen hs_req = ep->req; 183147a1685fSDinh Nguyen ep->req = NULL; 183247a1685fSDinh Nguyen list_del_init(&hs_req->queue); 1833c00dd4a6SGregory Herrero if (hs_req->req.complete) { 1834c00dd4a6SGregory Herrero spin_unlock(&hsotg->lock); 1835c00dd4a6SGregory Herrero usb_gadget_giveback_request( 1836c00dd4a6SGregory Herrero &ep->ep, &hs_req->req); 1837c00dd4a6SGregory Herrero spin_lock(&hsotg->lock); 1838c00dd4a6SGregory Herrero } 183947a1685fSDinh Nguyen } 184047a1685fSDinh Nguyen 184147a1685fSDinh Nguyen /* If we have pending request, then start it */ 184234c0887fSJohn Youn if (!ep->req) 184341cc4cd2SVardan Mikayelyan dwc2_gadget_start_next_request(ep); 184447a1685fSDinh Nguyen } 184547a1685fSDinh Nguyen 184647a1685fSDinh Nguyen break; 184747a1685fSDinh Nguyen 184847a1685fSDinh Nguyen default: 184947a1685fSDinh Nguyen return -ENOENT; 185047a1685fSDinh Nguyen } 18519e14d0a5SGregory Herrero break; 18529e14d0a5SGregory Herrero default: 18539e14d0a5SGregory Herrero return -ENOENT; 18549e14d0a5SGregory Herrero } 185547a1685fSDinh Nguyen return 1; 185647a1685fSDinh Nguyen } 185747a1685fSDinh Nguyen 18581f91b4ccSFelipe Balbi static void dwc2_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg); 185947a1685fSDinh Nguyen 186047a1685fSDinh Nguyen /** 18611f91b4ccSFelipe Balbi * dwc2_hsotg_stall_ep0 - stall ep0 186247a1685fSDinh Nguyen * @hsotg: The device state 186347a1685fSDinh Nguyen * 186447a1685fSDinh Nguyen * Set stall for ep0 as response for setup request. 186547a1685fSDinh Nguyen */ 18661f91b4ccSFelipe Balbi static void dwc2_hsotg_stall_ep0(struct dwc2_hsotg *hsotg) 1867e9ebe7c3SJingoo Han { 18681f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0]; 186947a1685fSDinh Nguyen u32 reg; 187047a1685fSDinh Nguyen u32 ctrl; 187147a1685fSDinh Nguyen 187247a1685fSDinh Nguyen dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in); 187347a1685fSDinh Nguyen reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0; 187447a1685fSDinh Nguyen 187547a1685fSDinh Nguyen /* 187647a1685fSDinh Nguyen * DxEPCTL_Stall will be cleared by EP once it has 187747a1685fSDinh Nguyen * taken effect, so no need to clear later. 187847a1685fSDinh Nguyen */ 187947a1685fSDinh Nguyen 1880f25c42b8SGevorg Sahakyan ctrl = dwc2_readl(hsotg, reg); 188147a1685fSDinh Nguyen ctrl |= DXEPCTL_STALL; 188247a1685fSDinh Nguyen ctrl |= DXEPCTL_CNAK; 1883f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ctrl, reg); 188447a1685fSDinh Nguyen 188547a1685fSDinh Nguyen dev_dbg(hsotg->dev, 188647a1685fSDinh Nguyen "written DXEPCTL=0x%08x to %08x (DXEPCTL=0x%08x)\n", 1887f25c42b8SGevorg Sahakyan ctrl, reg, dwc2_readl(hsotg, reg)); 188847a1685fSDinh Nguyen 188947a1685fSDinh Nguyen /* 189047a1685fSDinh Nguyen * complete won't be called, so we enqueue 189147a1685fSDinh Nguyen * setup request here 189247a1685fSDinh Nguyen */ 18931f91b4ccSFelipe Balbi dwc2_hsotg_enqueue_setup(hsotg); 189447a1685fSDinh Nguyen } 189547a1685fSDinh Nguyen 189647a1685fSDinh Nguyen /** 18971f91b4ccSFelipe Balbi * dwc2_hsotg_process_control - process a control request 189847a1685fSDinh Nguyen * @hsotg: The device state 189947a1685fSDinh Nguyen * @ctrl: The control request received 190047a1685fSDinh Nguyen * 190147a1685fSDinh Nguyen * The controller has received the SETUP phase of a control request, and 190247a1685fSDinh Nguyen * needs to work out what to do next (and whether to pass it on to the 190347a1685fSDinh Nguyen * gadget driver). 190447a1685fSDinh Nguyen */ 19051f91b4ccSFelipe Balbi static void dwc2_hsotg_process_control(struct dwc2_hsotg *hsotg, 190647a1685fSDinh Nguyen struct usb_ctrlrequest *ctrl) 190747a1685fSDinh Nguyen { 19081f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0]; 190947a1685fSDinh Nguyen int ret = 0; 191047a1685fSDinh Nguyen u32 dcfg; 191147a1685fSDinh Nguyen 1912e525e743SMian Yousaf Kaukab dev_dbg(hsotg->dev, 1913e525e743SMian Yousaf Kaukab "ctrl Type=%02x, Req=%02x, V=%04x, I=%04x, L=%04x\n", 1914e525e743SMian Yousaf Kaukab ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, 1915e525e743SMian Yousaf Kaukab ctrl->wIndex, ctrl->wLength); 191647a1685fSDinh Nguyen 1917fe0b94abSMian Yousaf Kaukab if (ctrl->wLength == 0) { 191847a1685fSDinh Nguyen ep0->dir_in = 1; 1919fe0b94abSMian Yousaf Kaukab hsotg->ep0_state = DWC2_EP0_STATUS_IN; 1920fe0b94abSMian Yousaf Kaukab } else if (ctrl->bRequestType & USB_DIR_IN) { 1921fe0b94abSMian Yousaf Kaukab ep0->dir_in = 1; 1922fe0b94abSMian Yousaf Kaukab hsotg->ep0_state = DWC2_EP0_DATA_IN; 1923fe0b94abSMian Yousaf Kaukab } else { 1924fe0b94abSMian Yousaf Kaukab ep0->dir_in = 0; 1925fe0b94abSMian Yousaf Kaukab hsotg->ep0_state = DWC2_EP0_DATA_OUT; 1926fe0b94abSMian Yousaf Kaukab } 192747a1685fSDinh Nguyen 192847a1685fSDinh Nguyen if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { 192947a1685fSDinh Nguyen switch (ctrl->bRequest) { 193047a1685fSDinh Nguyen case USB_REQ_SET_ADDRESS: 19316d713c15SMian Yousaf Kaukab hsotg->connected = 1; 1932f25c42b8SGevorg Sahakyan dcfg = dwc2_readl(hsotg, DCFG); 193347a1685fSDinh Nguyen dcfg &= ~DCFG_DEVADDR_MASK; 1934d5dbd3f7SPaul Zimmerman dcfg |= (le16_to_cpu(ctrl->wValue) << 1935d5dbd3f7SPaul Zimmerman DCFG_DEVADDR_SHIFT) & DCFG_DEVADDR_MASK; 1936f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dcfg, DCFG); 193747a1685fSDinh Nguyen 193847a1685fSDinh Nguyen dev_info(hsotg->dev, "new address %d\n", ctrl->wValue); 193947a1685fSDinh Nguyen 19401f91b4ccSFelipe Balbi ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0); 194147a1685fSDinh Nguyen return; 194247a1685fSDinh Nguyen 194347a1685fSDinh Nguyen case USB_REQ_GET_STATUS: 19441f91b4ccSFelipe Balbi ret = dwc2_hsotg_process_req_status(hsotg, ctrl); 194547a1685fSDinh Nguyen break; 194647a1685fSDinh Nguyen 194747a1685fSDinh Nguyen case USB_REQ_CLEAR_FEATURE: 194847a1685fSDinh Nguyen case USB_REQ_SET_FEATURE: 19491f91b4ccSFelipe Balbi ret = dwc2_hsotg_process_req_feature(hsotg, ctrl); 195047a1685fSDinh Nguyen break; 195147a1685fSDinh Nguyen } 195247a1685fSDinh Nguyen } 195347a1685fSDinh Nguyen 195447a1685fSDinh Nguyen /* as a fallback, try delivering it to the driver to deal with */ 195547a1685fSDinh Nguyen 195647a1685fSDinh Nguyen if (ret == 0 && hsotg->driver) { 195747a1685fSDinh Nguyen spin_unlock(&hsotg->lock); 195847a1685fSDinh Nguyen ret = hsotg->driver->setup(&hsotg->gadget, ctrl); 195947a1685fSDinh Nguyen spin_lock(&hsotg->lock); 196047a1685fSDinh Nguyen if (ret < 0) 196147a1685fSDinh Nguyen dev_dbg(hsotg->dev, "driver->setup() ret %d\n", ret); 196247a1685fSDinh Nguyen } 196347a1685fSDinh Nguyen 1964b4c53b4aSMinas Harutyunyan hsotg->delayed_status = false; 1965b4c53b4aSMinas Harutyunyan if (ret == USB_GADGET_DELAYED_STATUS) 1966b4c53b4aSMinas Harutyunyan hsotg->delayed_status = true; 1967b4c53b4aSMinas Harutyunyan 196847a1685fSDinh Nguyen /* 196947a1685fSDinh Nguyen * the request is either unhandlable, or is not formatted correctly 197047a1685fSDinh Nguyen * so respond with a STALL for the status stage to indicate failure. 197147a1685fSDinh Nguyen */ 197247a1685fSDinh Nguyen 197347a1685fSDinh Nguyen if (ret < 0) 19741f91b4ccSFelipe Balbi dwc2_hsotg_stall_ep0(hsotg); 197547a1685fSDinh Nguyen } 197647a1685fSDinh Nguyen 197747a1685fSDinh Nguyen /** 19781f91b4ccSFelipe Balbi * dwc2_hsotg_complete_setup - completion of a setup transfer 197947a1685fSDinh Nguyen * @ep: The endpoint the request was on. 198047a1685fSDinh Nguyen * @req: The request completed. 198147a1685fSDinh Nguyen * 198247a1685fSDinh Nguyen * Called on completion of any requests the driver itself submitted for 198347a1685fSDinh Nguyen * EP0 setup packets 198447a1685fSDinh Nguyen */ 19851f91b4ccSFelipe Balbi static void dwc2_hsotg_complete_setup(struct usb_ep *ep, 198647a1685fSDinh Nguyen struct usb_request *req) 198747a1685fSDinh Nguyen { 19881f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = our_ep(ep); 1989941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = hs_ep->parent; 199047a1685fSDinh Nguyen 199147a1685fSDinh Nguyen if (req->status < 0) { 199247a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: failed %d\n", __func__, req->status); 199347a1685fSDinh Nguyen return; 199447a1685fSDinh Nguyen } 199547a1685fSDinh Nguyen 199647a1685fSDinh Nguyen spin_lock(&hsotg->lock); 199747a1685fSDinh Nguyen if (req->actual == 0) 19981f91b4ccSFelipe Balbi dwc2_hsotg_enqueue_setup(hsotg); 199947a1685fSDinh Nguyen else 20001f91b4ccSFelipe Balbi dwc2_hsotg_process_control(hsotg, req->buf); 200147a1685fSDinh Nguyen spin_unlock(&hsotg->lock); 200247a1685fSDinh Nguyen } 200347a1685fSDinh Nguyen 200447a1685fSDinh Nguyen /** 20051f91b4ccSFelipe Balbi * dwc2_hsotg_enqueue_setup - start a request for EP0 packets 200647a1685fSDinh Nguyen * @hsotg: The device state. 200747a1685fSDinh Nguyen * 200847a1685fSDinh Nguyen * Enqueue a request on EP0 if necessary to received any SETUP packets 200947a1685fSDinh Nguyen * received from the host. 201047a1685fSDinh Nguyen */ 20111f91b4ccSFelipe Balbi static void dwc2_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg) 201247a1685fSDinh Nguyen { 201347a1685fSDinh Nguyen struct usb_request *req = hsotg->ctrl_req; 20141f91b4ccSFelipe Balbi struct dwc2_hsotg_req *hs_req = our_req(req); 201547a1685fSDinh Nguyen int ret; 201647a1685fSDinh Nguyen 201747a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: queueing setup request\n", __func__); 201847a1685fSDinh Nguyen 201947a1685fSDinh Nguyen req->zero = 0; 202047a1685fSDinh Nguyen req->length = 8; 202147a1685fSDinh Nguyen req->buf = hsotg->ctrl_buff; 20221f91b4ccSFelipe Balbi req->complete = dwc2_hsotg_complete_setup; 202347a1685fSDinh Nguyen 202447a1685fSDinh Nguyen if (!list_empty(&hs_req->queue)) { 202547a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s already queued???\n", __func__); 202647a1685fSDinh Nguyen return; 202747a1685fSDinh Nguyen } 202847a1685fSDinh Nguyen 2029c6f5c050SMian Yousaf Kaukab hsotg->eps_out[0]->dir_in = 0; 20308a20fa45SMian Yousaf Kaukab hsotg->eps_out[0]->send_zlp = 0; 2031fe0b94abSMian Yousaf Kaukab hsotg->ep0_state = DWC2_EP0_SETUP; 203247a1685fSDinh Nguyen 20331f91b4ccSFelipe Balbi ret = dwc2_hsotg_ep_queue(&hsotg->eps_out[0]->ep, req, GFP_ATOMIC); 203447a1685fSDinh Nguyen if (ret < 0) { 203547a1685fSDinh Nguyen dev_err(hsotg->dev, "%s: failed queue (%d)\n", __func__, ret); 203647a1685fSDinh Nguyen /* 203747a1685fSDinh Nguyen * Don't think there's much we can do other than watch the 203847a1685fSDinh Nguyen * driver fail. 203947a1685fSDinh Nguyen */ 204047a1685fSDinh Nguyen } 204147a1685fSDinh Nguyen } 204247a1685fSDinh Nguyen 20431f91b4ccSFelipe Balbi static void dwc2_hsotg_program_zlp(struct dwc2_hsotg *hsotg, 20441f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep) 2045fe0b94abSMian Yousaf Kaukab { 2046fe0b94abSMian Yousaf Kaukab u32 ctrl; 2047fe0b94abSMian Yousaf Kaukab u8 index = hs_ep->index; 2048fe0b94abSMian Yousaf Kaukab u32 epctl_reg = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index); 2049fe0b94abSMian Yousaf Kaukab u32 epsiz_reg = hs_ep->dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index); 2050fe0b94abSMian Yousaf Kaukab 2051ccb34a91SMian Yousaf Kaukab if (hs_ep->dir_in) 2052ccb34a91SMian Yousaf Kaukab dev_dbg(hsotg->dev, "Sending zero-length packet on ep%d\n", 2053ccb34a91SMian Yousaf Kaukab index); 2054ccb34a91SMian Yousaf Kaukab else 2055ccb34a91SMian Yousaf Kaukab dev_dbg(hsotg->dev, "Receiving zero-length packet on ep%d\n", 2056ccb34a91SMian Yousaf Kaukab index); 2057e02f9aa6SVahram Aharonyan if (using_desc_dma(hsotg)) { 2058066cfd07SAndrzej Pietrasiewicz /* Not specific buffer needed for ep0 ZLP */ 2059066cfd07SAndrzej Pietrasiewicz dma_addr_t dma = hs_ep->desc_list_dma; 2060066cfd07SAndrzej Pietrasiewicz 2061201ec568SMinas Harutyunyan if (!index) 2062e02f9aa6SVahram Aharonyan dwc2_gadget_set_ep0_desc_chain(hsotg, hs_ep); 2063201ec568SMinas Harutyunyan 2064066cfd07SAndrzej Pietrasiewicz dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, dma, 0); 2065e02f9aa6SVahram Aharonyan } else { 2066f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | 2067f25c42b8SGevorg Sahakyan DXEPTSIZ_XFERSIZE(0), 2068fe0b94abSMian Yousaf Kaukab epsiz_reg); 2069e02f9aa6SVahram Aharonyan } 2070fe0b94abSMian Yousaf Kaukab 2071f25c42b8SGevorg Sahakyan ctrl = dwc2_readl(hsotg, epctl_reg); 2072fe0b94abSMian Yousaf Kaukab ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ 2073fe0b94abSMian Yousaf Kaukab ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ 2074fe0b94abSMian Yousaf Kaukab ctrl |= DXEPCTL_USBACTEP; 2075f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ctrl, epctl_reg); 2076fe0b94abSMian Yousaf Kaukab } 2077fe0b94abSMian Yousaf Kaukab 207847a1685fSDinh Nguyen /** 20791f91b4ccSFelipe Balbi * dwc2_hsotg_complete_request - complete a request given to us 208047a1685fSDinh Nguyen * @hsotg: The device state. 208147a1685fSDinh Nguyen * @hs_ep: The endpoint the request was on. 208247a1685fSDinh Nguyen * @hs_req: The request to complete. 208347a1685fSDinh Nguyen * @result: The result code (0 => Ok, otherwise errno) 208447a1685fSDinh Nguyen * 208547a1685fSDinh Nguyen * The given request has finished, so call the necessary completion 208647a1685fSDinh Nguyen * if it has one and then look to see if we can start a new request 208747a1685fSDinh Nguyen * on the endpoint. 208847a1685fSDinh Nguyen * 208947a1685fSDinh Nguyen * Note, expects the ep to already be locked as appropriate. 209047a1685fSDinh Nguyen */ 20911f91b4ccSFelipe Balbi static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg, 20921f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep, 20931f91b4ccSFelipe Balbi struct dwc2_hsotg_req *hs_req, 209447a1685fSDinh Nguyen int result) 209547a1685fSDinh Nguyen { 209647a1685fSDinh Nguyen if (!hs_req) { 209747a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: nothing to complete?\n", __func__); 209847a1685fSDinh Nguyen return; 209947a1685fSDinh Nguyen } 210047a1685fSDinh Nguyen 210147a1685fSDinh Nguyen dev_dbg(hsotg->dev, "complete: ep %p %s, req %p, %d => %p\n", 210247a1685fSDinh Nguyen hs_ep, hs_ep->ep.name, hs_req, result, hs_req->req.complete); 210347a1685fSDinh Nguyen 210447a1685fSDinh Nguyen /* 210547a1685fSDinh Nguyen * only replace the status if we've not already set an error 210647a1685fSDinh Nguyen * from a previous transaction 210747a1685fSDinh Nguyen */ 210847a1685fSDinh Nguyen 210947a1685fSDinh Nguyen if (hs_req->req.status == -EINPROGRESS) 211047a1685fSDinh Nguyen hs_req->req.status = result; 211147a1685fSDinh Nguyen 211244583fecSYunzhi Li if (using_dma(hsotg)) 211344583fecSYunzhi Li dwc2_hsotg_unmap_dma(hsotg, hs_ep, hs_req); 211444583fecSYunzhi Li 21151f91b4ccSFelipe Balbi dwc2_hsotg_handle_unaligned_buf_complete(hsotg, hs_ep, hs_req); 21167d24c1b5SMian Yousaf Kaukab 211747a1685fSDinh Nguyen hs_ep->req = NULL; 211847a1685fSDinh Nguyen list_del_init(&hs_req->queue); 211947a1685fSDinh Nguyen 212047a1685fSDinh Nguyen /* 212147a1685fSDinh Nguyen * call the complete request with the locks off, just in case the 212247a1685fSDinh Nguyen * request tries to queue more work for this endpoint. 212347a1685fSDinh Nguyen */ 212447a1685fSDinh Nguyen 212547a1685fSDinh Nguyen if (hs_req->req.complete) { 212647a1685fSDinh Nguyen spin_unlock(&hsotg->lock); 2127304f7e5eSMichal Sojka usb_gadget_giveback_request(&hs_ep->ep, &hs_req->req); 212847a1685fSDinh Nguyen spin_lock(&hsotg->lock); 212947a1685fSDinh Nguyen } 213047a1685fSDinh Nguyen 2131540ccba0SVahram Aharonyan /* In DDMA don't need to proceed to starting of next ISOC request */ 2132540ccba0SVahram Aharonyan if (using_desc_dma(hsotg) && hs_ep->isochronous) 2133540ccba0SVahram Aharonyan return; 2134540ccba0SVahram Aharonyan 213547a1685fSDinh Nguyen /* 213647a1685fSDinh Nguyen * Look to see if there is anything else to do. Note, the completion 213747a1685fSDinh Nguyen * of the previous request may have caused a new request to be started 213847a1685fSDinh Nguyen * so be careful when doing this. 213947a1685fSDinh Nguyen */ 214047a1685fSDinh Nguyen 214134c0887fSJohn Youn if (!hs_ep->req && result >= 0) 214241cc4cd2SVardan Mikayelyan dwc2_gadget_start_next_request(hs_ep); 214347a1685fSDinh Nguyen } 214447a1685fSDinh Nguyen 2145540ccba0SVahram Aharonyan /* 2146540ccba0SVahram Aharonyan * dwc2_gadget_complete_isoc_request_ddma - complete an isoc request in DDMA 2147540ccba0SVahram Aharonyan * @hs_ep: The endpoint the request was on. 2148540ccba0SVahram Aharonyan * 2149540ccba0SVahram Aharonyan * Get first request from the ep queue, determine descriptor on which complete 2150729cac69SMinas Harutyunyan * happened. SW discovers which descriptor currently in use by HW, adjusts 2151729cac69SMinas Harutyunyan * dma_address and calculates index of completed descriptor based on the value 2152729cac69SMinas Harutyunyan * of DEPDMA register. Update actual length of request, giveback to gadget. 2153540ccba0SVahram Aharonyan */ 2154540ccba0SVahram Aharonyan static void dwc2_gadget_complete_isoc_request_ddma(struct dwc2_hsotg_ep *hs_ep) 2155540ccba0SVahram Aharonyan { 2156540ccba0SVahram Aharonyan struct dwc2_hsotg *hsotg = hs_ep->parent; 2157540ccba0SVahram Aharonyan struct dwc2_hsotg_req *hs_req; 2158540ccba0SVahram Aharonyan struct usb_request *ureq; 2159540ccba0SVahram Aharonyan u32 desc_sts; 2160540ccba0SVahram Aharonyan u32 mask; 2161540ccba0SVahram Aharonyan 2162729cac69SMinas Harutyunyan desc_sts = hs_ep->desc_list[hs_ep->compl_desc].status; 2163729cac69SMinas Harutyunyan 2164729cac69SMinas Harutyunyan /* Process only descriptors with buffer status set to DMA done */ 2165729cac69SMinas Harutyunyan while ((desc_sts & DEV_DMA_BUFF_STS_MASK) >> 2166729cac69SMinas Harutyunyan DEV_DMA_BUFF_STS_SHIFT == DEV_DMA_BUFF_STS_DMADONE) { 2167729cac69SMinas Harutyunyan 2168540ccba0SVahram Aharonyan hs_req = get_ep_head(hs_ep); 2169540ccba0SVahram Aharonyan if (!hs_req) { 2170540ccba0SVahram Aharonyan dev_warn(hsotg->dev, "%s: ISOC EP queue empty\n", __func__); 2171540ccba0SVahram Aharonyan return; 2172540ccba0SVahram Aharonyan } 2173540ccba0SVahram Aharonyan ureq = &hs_req->req; 2174540ccba0SVahram Aharonyan 2175729cac69SMinas Harutyunyan /* Check completion status */ 2176729cac69SMinas Harutyunyan if ((desc_sts & DEV_DMA_STS_MASK) >> DEV_DMA_STS_SHIFT == 2177729cac69SMinas Harutyunyan DEV_DMA_STS_SUCC) { 2178540ccba0SVahram Aharonyan mask = hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_MASK : 2179540ccba0SVahram Aharonyan DEV_DMA_ISOC_RX_NBYTES_MASK; 2180729cac69SMinas Harutyunyan ureq->actual = ureq->length - ((desc_sts & mask) >> 2181729cac69SMinas Harutyunyan DEV_DMA_ISOC_NBYTES_SHIFT); 2182540ccba0SVahram Aharonyan 2183729cac69SMinas Harutyunyan /* Adjust actual len for ISOC Out if len is 2184729cac69SMinas Harutyunyan * not align of 4 2185729cac69SMinas Harutyunyan */ 218695d2b037SVahram Aharonyan if (!hs_ep->dir_in && ureq->length & 0x3) 218795d2b037SVahram Aharonyan ureq->actual += 4 - (ureq->length & 0x3); 2188c8006f67SMinas Harutyunyan 2189c8006f67SMinas Harutyunyan /* Set actual frame number for completed transfers */ 2190c8006f67SMinas Harutyunyan ureq->frame_number = 2191c8006f67SMinas Harutyunyan (desc_sts & DEV_DMA_ISOC_FRNUM_MASK) >> 2192c8006f67SMinas Harutyunyan DEV_DMA_ISOC_FRNUM_SHIFT; 2193729cac69SMinas Harutyunyan } 219495d2b037SVahram Aharonyan 2195540ccba0SVahram Aharonyan dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); 2196729cac69SMinas Harutyunyan 2197729cac69SMinas Harutyunyan hs_ep->compl_desc++; 219854f37f56SMinas Harutyunyan if (hs_ep->compl_desc > (MAX_DMA_DESC_NUM_HS_ISOC - 1)) 2199729cac69SMinas Harutyunyan hs_ep->compl_desc = 0; 2200729cac69SMinas Harutyunyan desc_sts = hs_ep->desc_list[hs_ep->compl_desc].status; 2201729cac69SMinas Harutyunyan } 2202540ccba0SVahram Aharonyan } 2203540ccba0SVahram Aharonyan 2204540ccba0SVahram Aharonyan /* 2205729cac69SMinas Harutyunyan * dwc2_gadget_handle_isoc_bna - handle BNA interrupt for ISOC. 2206729cac69SMinas Harutyunyan * @hs_ep: The isochronous endpoint. 2207540ccba0SVahram Aharonyan * 2208729cac69SMinas Harutyunyan * If EP ISOC OUT then need to flush RX FIFO to remove source of BNA 2209729cac69SMinas Harutyunyan * interrupt. Reset target frame and next_desc to allow to start 2210729cac69SMinas Harutyunyan * ISOC's on NAK interrupt for IN direction or on OUTTKNEPDIS 2211729cac69SMinas Harutyunyan * interrupt for OUT direction. 2212540ccba0SVahram Aharonyan */ 2213729cac69SMinas Harutyunyan static void dwc2_gadget_handle_isoc_bna(struct dwc2_hsotg_ep *hs_ep) 2214540ccba0SVahram Aharonyan { 2215540ccba0SVahram Aharonyan struct dwc2_hsotg *hsotg = hs_ep->parent; 2216540ccba0SVahram Aharonyan 2217729cac69SMinas Harutyunyan if (!hs_ep->dir_in) 2218729cac69SMinas Harutyunyan dwc2_flush_rx_fifo(hsotg); 2219729cac69SMinas Harutyunyan dwc2_hsotg_complete_request(hsotg, hs_ep, get_ep_head(hs_ep), 0); 2220540ccba0SVahram Aharonyan 2221729cac69SMinas Harutyunyan hs_ep->target_frame = TARGET_FRAME_INITIAL; 2222540ccba0SVahram Aharonyan hs_ep->next_desc = 0; 2223729cac69SMinas Harutyunyan hs_ep->compl_desc = 0; 2224540ccba0SVahram Aharonyan } 2225540ccba0SVahram Aharonyan 222647a1685fSDinh Nguyen /** 22271f91b4ccSFelipe Balbi * dwc2_hsotg_rx_data - receive data from the FIFO for an endpoint 222847a1685fSDinh Nguyen * @hsotg: The device state. 222947a1685fSDinh Nguyen * @ep_idx: The endpoint index for the data 223047a1685fSDinh Nguyen * @size: The size of data in the fifo, in bytes 223147a1685fSDinh Nguyen * 223247a1685fSDinh Nguyen * The FIFO status shows there is data to read from the FIFO for a given 223347a1685fSDinh Nguyen * endpoint, so sort out whether we need to read the data into a request 223447a1685fSDinh Nguyen * that has been made for that endpoint. 223547a1685fSDinh Nguyen */ 22361f91b4ccSFelipe Balbi static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) 223747a1685fSDinh Nguyen { 22381f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = hsotg->eps_out[ep_idx]; 22391f91b4ccSFelipe Balbi struct dwc2_hsotg_req *hs_req = hs_ep->req; 224047a1685fSDinh Nguyen int to_read; 224147a1685fSDinh Nguyen int max_req; 224247a1685fSDinh Nguyen int read_ptr; 224347a1685fSDinh Nguyen 224447a1685fSDinh Nguyen if (!hs_req) { 2245f25c42b8SGevorg Sahakyan u32 epctl = dwc2_readl(hsotg, DOEPCTL(ep_idx)); 224647a1685fSDinh Nguyen int ptr; 224747a1685fSDinh Nguyen 22486b448af4SRobert Baldyga dev_dbg(hsotg->dev, 224947a1685fSDinh Nguyen "%s: FIFO %d bytes on ep%d but no req (DXEPCTl=0x%08x)\n", 225047a1685fSDinh Nguyen __func__, size, ep_idx, epctl); 225147a1685fSDinh Nguyen 225247a1685fSDinh Nguyen /* dump the data from the FIFO, we've nothing we can do */ 225347a1685fSDinh Nguyen for (ptr = 0; ptr < size; ptr += 4) 2254f25c42b8SGevorg Sahakyan (void)dwc2_readl(hsotg, EPFIFO(ep_idx)); 225547a1685fSDinh Nguyen 225647a1685fSDinh Nguyen return; 225747a1685fSDinh Nguyen } 225847a1685fSDinh Nguyen 225947a1685fSDinh Nguyen to_read = size; 226047a1685fSDinh Nguyen read_ptr = hs_req->req.actual; 226147a1685fSDinh Nguyen max_req = hs_req->req.length - read_ptr; 226247a1685fSDinh Nguyen 226347a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n", 226447a1685fSDinh Nguyen __func__, to_read, max_req, read_ptr, hs_req->req.length); 226547a1685fSDinh Nguyen 226647a1685fSDinh Nguyen if (to_read > max_req) { 226747a1685fSDinh Nguyen /* 226847a1685fSDinh Nguyen * more data appeared than we where willing 226947a1685fSDinh Nguyen * to deal with in this request. 227047a1685fSDinh Nguyen */ 227147a1685fSDinh Nguyen 227247a1685fSDinh Nguyen /* currently we don't deal this */ 227347a1685fSDinh Nguyen WARN_ON_ONCE(1); 227447a1685fSDinh Nguyen } 227547a1685fSDinh Nguyen 227647a1685fSDinh Nguyen hs_ep->total_data += to_read; 227747a1685fSDinh Nguyen hs_req->req.actual += to_read; 227847a1685fSDinh Nguyen to_read = DIV_ROUND_UP(to_read, 4); 227947a1685fSDinh Nguyen 228047a1685fSDinh Nguyen /* 228147a1685fSDinh Nguyen * note, we might over-write the buffer end by 3 bytes depending on 228247a1685fSDinh Nguyen * alignment of the data. 228347a1685fSDinh Nguyen */ 2284342ccce1SGevorg Sahakyan dwc2_readl_rep(hsotg, EPFIFO(ep_idx), 2285f25c42b8SGevorg Sahakyan hs_req->req.buf + read_ptr, to_read); 228647a1685fSDinh Nguyen } 228747a1685fSDinh Nguyen 228847a1685fSDinh Nguyen /** 22891f91b4ccSFelipe Balbi * dwc2_hsotg_ep0_zlp - send/receive zero-length packet on control endpoint 229047a1685fSDinh Nguyen * @hsotg: The device instance 2291fe0b94abSMian Yousaf Kaukab * @dir_in: If IN zlp 229247a1685fSDinh Nguyen * 229347a1685fSDinh Nguyen * Generate a zero-length IN packet request for terminating a SETUP 229447a1685fSDinh Nguyen * transaction. 229547a1685fSDinh Nguyen * 229647a1685fSDinh Nguyen * Note, since we don't write any data to the TxFIFO, then it is 229747a1685fSDinh Nguyen * currently believed that we do not need to wait for any space in 229847a1685fSDinh Nguyen * the TxFIFO. 229947a1685fSDinh Nguyen */ 23001f91b4ccSFelipe Balbi static void dwc2_hsotg_ep0_zlp(struct dwc2_hsotg *hsotg, bool dir_in) 230147a1685fSDinh Nguyen { 2302c6f5c050SMian Yousaf Kaukab /* eps_out[0] is used in both directions */ 2303fe0b94abSMian Yousaf Kaukab hsotg->eps_out[0]->dir_in = dir_in; 2304fe0b94abSMian Yousaf Kaukab hsotg->ep0_state = dir_in ? DWC2_EP0_STATUS_IN : DWC2_EP0_STATUS_OUT; 230547a1685fSDinh Nguyen 23061f91b4ccSFelipe Balbi dwc2_hsotg_program_zlp(hsotg, hsotg->eps_out[0]); 230747a1685fSDinh Nguyen } 230847a1685fSDinh Nguyen 2309ec1f9d9fSRoman Bacik static void dwc2_hsotg_change_ep_iso_parity(struct dwc2_hsotg *hsotg, 2310ec1f9d9fSRoman Bacik u32 epctl_reg) 2311ec1f9d9fSRoman Bacik { 2312ec1f9d9fSRoman Bacik u32 ctrl; 2313ec1f9d9fSRoman Bacik 2314f25c42b8SGevorg Sahakyan ctrl = dwc2_readl(hsotg, epctl_reg); 2315ec1f9d9fSRoman Bacik if (ctrl & DXEPCTL_EOFRNUM) 2316ec1f9d9fSRoman Bacik ctrl |= DXEPCTL_SETEVENFR; 2317ec1f9d9fSRoman Bacik else 2318ec1f9d9fSRoman Bacik ctrl |= DXEPCTL_SETODDFR; 2319f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ctrl, epctl_reg); 2320ec1f9d9fSRoman Bacik } 2321ec1f9d9fSRoman Bacik 2322aa3e8bc8SVahram Aharonyan /* 2323aa3e8bc8SVahram Aharonyan * dwc2_gadget_get_xfersize_ddma - get transferred bytes amount from desc 2324aa3e8bc8SVahram Aharonyan * @hs_ep - The endpoint on which transfer went 2325aa3e8bc8SVahram Aharonyan * 2326aa3e8bc8SVahram Aharonyan * Iterate over endpoints descriptor chain and get info on bytes remained 2327aa3e8bc8SVahram Aharonyan * in DMA descriptors after transfer has completed. Used for non isoc EPs. 2328aa3e8bc8SVahram Aharonyan */ 2329aa3e8bc8SVahram Aharonyan static unsigned int dwc2_gadget_get_xfersize_ddma(struct dwc2_hsotg_ep *hs_ep) 2330aa3e8bc8SVahram Aharonyan { 2331b2c586ebSMinas Harutyunyan const struct usb_endpoint_descriptor *ep_desc = hs_ep->ep.desc; 2332aa3e8bc8SVahram Aharonyan struct dwc2_hsotg *hsotg = hs_ep->parent; 2333aa3e8bc8SVahram Aharonyan unsigned int bytes_rem = 0; 2334b2c586ebSMinas Harutyunyan unsigned int bytes_rem_correction = 0; 2335aa3e8bc8SVahram Aharonyan struct dwc2_dma_desc *desc = hs_ep->desc_list; 2336aa3e8bc8SVahram Aharonyan int i; 2337aa3e8bc8SVahram Aharonyan u32 status; 2338b2c586ebSMinas Harutyunyan u32 mps = hs_ep->ep.maxpacket; 2339b2c586ebSMinas Harutyunyan int dir_in = hs_ep->dir_in; 2340aa3e8bc8SVahram Aharonyan 2341aa3e8bc8SVahram Aharonyan if (!desc) 2342aa3e8bc8SVahram Aharonyan return -EINVAL; 2343aa3e8bc8SVahram Aharonyan 2344b2c586ebSMinas Harutyunyan /* Interrupt OUT EP with mps not multiple of 4 */ 2345b2c586ebSMinas Harutyunyan if (hs_ep->index) 2346b2c586ebSMinas Harutyunyan if (usb_endpoint_xfer_int(ep_desc) && !dir_in && (mps % 4)) 2347b2c586ebSMinas Harutyunyan bytes_rem_correction = 4 - (mps % 4); 2348b2c586ebSMinas Harutyunyan 2349aa3e8bc8SVahram Aharonyan for (i = 0; i < hs_ep->desc_count; ++i) { 2350aa3e8bc8SVahram Aharonyan status = desc->status; 2351aa3e8bc8SVahram Aharonyan bytes_rem += status & DEV_DMA_NBYTES_MASK; 2352b2c586ebSMinas Harutyunyan bytes_rem -= bytes_rem_correction; 2353aa3e8bc8SVahram Aharonyan 2354aa3e8bc8SVahram Aharonyan if (status & DEV_DMA_STS_MASK) 2355aa3e8bc8SVahram Aharonyan dev_err(hsotg->dev, "descriptor %d closed with %x\n", 2356aa3e8bc8SVahram Aharonyan i, status & DEV_DMA_STS_MASK); 2357b2c586ebSMinas Harutyunyan 2358b2c586ebSMinas Harutyunyan if (status & DEV_DMA_L) 2359b2c586ebSMinas Harutyunyan break; 2360b2c586ebSMinas Harutyunyan 23615acb4b97SMinas Harutyunyan desc++; 2362aa3e8bc8SVahram Aharonyan } 2363aa3e8bc8SVahram Aharonyan 2364aa3e8bc8SVahram Aharonyan return bytes_rem; 2365aa3e8bc8SVahram Aharonyan } 2366aa3e8bc8SVahram Aharonyan 236747a1685fSDinh Nguyen /** 23681f91b4ccSFelipe Balbi * dwc2_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO 236947a1685fSDinh Nguyen * @hsotg: The device instance 237047a1685fSDinh Nguyen * @epnum: The endpoint received from 237147a1685fSDinh Nguyen * 237247a1685fSDinh Nguyen * The RXFIFO has delivered an OutDone event, which means that the data 237347a1685fSDinh Nguyen * transfer for an OUT endpoint has been completed, either by a short 237447a1685fSDinh Nguyen * packet or by the finish of a transfer. 237547a1685fSDinh Nguyen */ 23761f91b4ccSFelipe Balbi static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) 237747a1685fSDinh Nguyen { 2378f25c42b8SGevorg Sahakyan u32 epsize = dwc2_readl(hsotg, DOEPTSIZ(epnum)); 23791f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = hsotg->eps_out[epnum]; 23801f91b4ccSFelipe Balbi struct dwc2_hsotg_req *hs_req = hs_ep->req; 238147a1685fSDinh Nguyen struct usb_request *req = &hs_req->req; 23829da51974SJohn Youn unsigned int size_left = DXEPTSIZ_XFERSIZE_GET(epsize); 238347a1685fSDinh Nguyen int result = 0; 238447a1685fSDinh Nguyen 238547a1685fSDinh Nguyen if (!hs_req) { 238647a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: no request active\n", __func__); 238747a1685fSDinh Nguyen return; 238847a1685fSDinh Nguyen } 238947a1685fSDinh Nguyen 2390fe0b94abSMian Yousaf Kaukab if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_OUT) { 2391fe0b94abSMian Yousaf Kaukab dev_dbg(hsotg->dev, "zlp packet received\n"); 23921f91b4ccSFelipe Balbi dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); 23931f91b4ccSFelipe Balbi dwc2_hsotg_enqueue_setup(hsotg); 2394fe0b94abSMian Yousaf Kaukab return; 2395fe0b94abSMian Yousaf Kaukab } 2396fe0b94abSMian Yousaf Kaukab 2397aa3e8bc8SVahram Aharonyan if (using_desc_dma(hsotg)) 2398aa3e8bc8SVahram Aharonyan size_left = dwc2_gadget_get_xfersize_ddma(hs_ep); 2399aa3e8bc8SVahram Aharonyan 240047a1685fSDinh Nguyen if (using_dma(hsotg)) { 24019da51974SJohn Youn unsigned int size_done; 240247a1685fSDinh Nguyen 240347a1685fSDinh Nguyen /* 240447a1685fSDinh Nguyen * Calculate the size of the transfer by checking how much 240547a1685fSDinh Nguyen * is left in the endpoint size register and then working it 240647a1685fSDinh Nguyen * out from the amount we loaded for the transfer. 240747a1685fSDinh Nguyen * 240847a1685fSDinh Nguyen * We need to do this as DMA pointers are always 32bit aligned 240947a1685fSDinh Nguyen * so may overshoot/undershoot the transfer. 241047a1685fSDinh Nguyen */ 241147a1685fSDinh Nguyen 241247a1685fSDinh Nguyen size_done = hs_ep->size_loaded - size_left; 241347a1685fSDinh Nguyen size_done += hs_ep->last_load; 241447a1685fSDinh Nguyen 241547a1685fSDinh Nguyen req->actual = size_done; 241647a1685fSDinh Nguyen } 241747a1685fSDinh Nguyen 241847a1685fSDinh Nguyen /* if there is more request to do, schedule new transfer */ 241947a1685fSDinh Nguyen if (req->actual < req->length && size_left == 0) { 24201f91b4ccSFelipe Balbi dwc2_hsotg_start_req(hsotg, hs_ep, hs_req, true); 242147a1685fSDinh Nguyen return; 242247a1685fSDinh Nguyen } 242347a1685fSDinh Nguyen 242447a1685fSDinh Nguyen if (req->actual < req->length && req->short_not_ok) { 242547a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: got %d/%d (short not ok) => error\n", 242647a1685fSDinh Nguyen __func__, req->actual, req->length); 242747a1685fSDinh Nguyen 242847a1685fSDinh Nguyen /* 242947a1685fSDinh Nguyen * todo - what should we return here? there's no one else 243047a1685fSDinh Nguyen * even bothering to check the status. 243147a1685fSDinh Nguyen */ 243247a1685fSDinh Nguyen } 243347a1685fSDinh Nguyen 2434ef750c71SVahram Aharonyan /* DDMA IN status phase will start from StsPhseRcvd interrupt */ 2435ef750c71SVahram Aharonyan if (!using_desc_dma(hsotg) && epnum == 0 && 2436ef750c71SVahram Aharonyan hsotg->ep0_state == DWC2_EP0_DATA_OUT) { 2437fe0b94abSMian Yousaf Kaukab /* Move to STATUS IN */ 2438b4c53b4aSMinas Harutyunyan if (!hsotg->delayed_status) 24391f91b4ccSFelipe Balbi dwc2_hsotg_ep0_zlp(hsotg, true); 244047a1685fSDinh Nguyen } 244147a1685fSDinh Nguyen 2442ec1f9d9fSRoman Bacik /* 2443ec1f9d9fSRoman Bacik * Slave mode OUT transfers do not go through XferComplete so 2444ec1f9d9fSRoman Bacik * adjust the ISOC parity here. 2445ec1f9d9fSRoman Bacik */ 2446ec1f9d9fSRoman Bacik if (!using_dma(hsotg)) { 2447ec1f9d9fSRoman Bacik if (hs_ep->isochronous && hs_ep->interval == 1) 2448ec1f9d9fSRoman Bacik dwc2_hsotg_change_ep_iso_parity(hsotg, DOEPCTL(epnum)); 2449837e9f00SVardan Mikayelyan else if (hs_ep->isochronous && hs_ep->interval > 1) 2450837e9f00SVardan Mikayelyan dwc2_gadget_incr_frame_num(hs_ep); 2451ec1f9d9fSRoman Bacik } 2452ec1f9d9fSRoman Bacik 24534faf3b36SMinas Harutyunyan /* Set actual frame number for completed transfers */ 24544faf3b36SMinas Harutyunyan if (!using_desc_dma(hsotg) && hs_ep->isochronous) 24554faf3b36SMinas Harutyunyan req->frame_number = hsotg->frame_number; 24564faf3b36SMinas Harutyunyan 24571f91b4ccSFelipe Balbi dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, result); 245847a1685fSDinh Nguyen } 245947a1685fSDinh Nguyen 246047a1685fSDinh Nguyen /** 24611f91b4ccSFelipe Balbi * dwc2_hsotg_handle_rx - RX FIFO has data 246247a1685fSDinh Nguyen * @hsotg: The device instance 246347a1685fSDinh Nguyen * 246447a1685fSDinh Nguyen * The IRQ handler has detected that the RX FIFO has some data in it 246547a1685fSDinh Nguyen * that requires processing, so find out what is in there and do the 246647a1685fSDinh Nguyen * appropriate read. 246747a1685fSDinh Nguyen * 246847a1685fSDinh Nguyen * The RXFIFO is a true FIFO, the packets coming out are still in packet 246947a1685fSDinh Nguyen * chunks, so if you have x packets received on an endpoint you'll get x 247047a1685fSDinh Nguyen * FIFO events delivered, each with a packet's worth of data in it. 247147a1685fSDinh Nguyen * 247247a1685fSDinh Nguyen * When using DMA, we should not be processing events from the RXFIFO 247347a1685fSDinh Nguyen * as the actual data should be sent to the memory directly and we turn 247447a1685fSDinh Nguyen * on the completion interrupts to get notifications of transfer completion. 247547a1685fSDinh Nguyen */ 24761f91b4ccSFelipe Balbi static void dwc2_hsotg_handle_rx(struct dwc2_hsotg *hsotg) 247747a1685fSDinh Nguyen { 2478f25c42b8SGevorg Sahakyan u32 grxstsr = dwc2_readl(hsotg, GRXSTSP); 247947a1685fSDinh Nguyen u32 epnum, status, size; 248047a1685fSDinh Nguyen 248147a1685fSDinh Nguyen WARN_ON(using_dma(hsotg)); 248247a1685fSDinh Nguyen 248347a1685fSDinh Nguyen epnum = grxstsr & GRXSTS_EPNUM_MASK; 248447a1685fSDinh Nguyen status = grxstsr & GRXSTS_PKTSTS_MASK; 248547a1685fSDinh Nguyen 248647a1685fSDinh Nguyen size = grxstsr & GRXSTS_BYTECNT_MASK; 248747a1685fSDinh Nguyen size >>= GRXSTS_BYTECNT_SHIFT; 248847a1685fSDinh Nguyen 248947a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n", 249047a1685fSDinh Nguyen __func__, grxstsr, size, epnum); 249147a1685fSDinh Nguyen 249247a1685fSDinh Nguyen switch ((status & GRXSTS_PKTSTS_MASK) >> GRXSTS_PKTSTS_SHIFT) { 249347a1685fSDinh Nguyen case GRXSTS_PKTSTS_GLOBALOUTNAK: 249447a1685fSDinh Nguyen dev_dbg(hsotg->dev, "GLOBALOUTNAK\n"); 249547a1685fSDinh Nguyen break; 249647a1685fSDinh Nguyen 249747a1685fSDinh Nguyen case GRXSTS_PKTSTS_OUTDONE: 249847a1685fSDinh Nguyen dev_dbg(hsotg->dev, "OutDone (Frame=0x%08x)\n", 24991f91b4ccSFelipe Balbi dwc2_hsotg_read_frameno(hsotg)); 250047a1685fSDinh Nguyen 250147a1685fSDinh Nguyen if (!using_dma(hsotg)) 25021f91b4ccSFelipe Balbi dwc2_hsotg_handle_outdone(hsotg, epnum); 250347a1685fSDinh Nguyen break; 250447a1685fSDinh Nguyen 250547a1685fSDinh Nguyen case GRXSTS_PKTSTS_SETUPDONE: 250647a1685fSDinh Nguyen dev_dbg(hsotg->dev, 250747a1685fSDinh Nguyen "SetupDone (Frame=0x%08x, DOPEPCTL=0x%08x)\n", 25081f91b4ccSFelipe Balbi dwc2_hsotg_read_frameno(hsotg), 2509f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DOEPCTL(0))); 2510fe0b94abSMian Yousaf Kaukab /* 25111f91b4ccSFelipe Balbi * Call dwc2_hsotg_handle_outdone here if it was not called from 2512fe0b94abSMian Yousaf Kaukab * GRXSTS_PKTSTS_OUTDONE. That is, if the core didn't 2513fe0b94abSMian Yousaf Kaukab * generate GRXSTS_PKTSTS_OUTDONE for setup packet. 2514fe0b94abSMian Yousaf Kaukab */ 2515fe0b94abSMian Yousaf Kaukab if (hsotg->ep0_state == DWC2_EP0_SETUP) 25161f91b4ccSFelipe Balbi dwc2_hsotg_handle_outdone(hsotg, epnum); 251747a1685fSDinh Nguyen break; 251847a1685fSDinh Nguyen 251947a1685fSDinh Nguyen case GRXSTS_PKTSTS_OUTRX: 25201f91b4ccSFelipe Balbi dwc2_hsotg_rx_data(hsotg, epnum, size); 252147a1685fSDinh Nguyen break; 252247a1685fSDinh Nguyen 252347a1685fSDinh Nguyen case GRXSTS_PKTSTS_SETUPRX: 252447a1685fSDinh Nguyen dev_dbg(hsotg->dev, 252547a1685fSDinh Nguyen "SetupRX (Frame=0x%08x, DOPEPCTL=0x%08x)\n", 25261f91b4ccSFelipe Balbi dwc2_hsotg_read_frameno(hsotg), 2527f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DOEPCTL(0))); 252847a1685fSDinh Nguyen 2529fe0b94abSMian Yousaf Kaukab WARN_ON(hsotg->ep0_state != DWC2_EP0_SETUP); 2530fe0b94abSMian Yousaf Kaukab 25311f91b4ccSFelipe Balbi dwc2_hsotg_rx_data(hsotg, epnum, size); 253247a1685fSDinh Nguyen break; 253347a1685fSDinh Nguyen 253447a1685fSDinh Nguyen default: 253547a1685fSDinh Nguyen dev_warn(hsotg->dev, "%s: unknown status %08x\n", 253647a1685fSDinh Nguyen __func__, grxstsr); 253747a1685fSDinh Nguyen 25381f91b4ccSFelipe Balbi dwc2_hsotg_dump(hsotg); 253947a1685fSDinh Nguyen break; 254047a1685fSDinh Nguyen } 254147a1685fSDinh Nguyen } 254247a1685fSDinh Nguyen 254347a1685fSDinh Nguyen /** 25441f91b4ccSFelipe Balbi * dwc2_hsotg_ep0_mps - turn max packet size into register setting 254547a1685fSDinh Nguyen * @mps: The maximum packet size in bytes. 254647a1685fSDinh Nguyen */ 25471f91b4ccSFelipe Balbi static u32 dwc2_hsotg_ep0_mps(unsigned int mps) 254847a1685fSDinh Nguyen { 254947a1685fSDinh Nguyen switch (mps) { 255047a1685fSDinh Nguyen case 64: 255147a1685fSDinh Nguyen return D0EPCTL_MPS_64; 255247a1685fSDinh Nguyen case 32: 255347a1685fSDinh Nguyen return D0EPCTL_MPS_32; 255447a1685fSDinh Nguyen case 16: 255547a1685fSDinh Nguyen return D0EPCTL_MPS_16; 255647a1685fSDinh Nguyen case 8: 255747a1685fSDinh Nguyen return D0EPCTL_MPS_8; 255847a1685fSDinh Nguyen } 255947a1685fSDinh Nguyen 256047a1685fSDinh Nguyen /* bad max packet size, warn and return invalid result */ 256147a1685fSDinh Nguyen WARN_ON(1); 256247a1685fSDinh Nguyen return (u32)-1; 256347a1685fSDinh Nguyen } 256447a1685fSDinh Nguyen 256547a1685fSDinh Nguyen /** 25661f91b4ccSFelipe Balbi * dwc2_hsotg_set_ep_maxpacket - set endpoint's max-packet field 256747a1685fSDinh Nguyen * @hsotg: The driver state. 256847a1685fSDinh Nguyen * @ep: The index number of the endpoint 256947a1685fSDinh Nguyen * @mps: The maximum packet size in bytes 2570ee2c40deSVardan Mikayelyan * @mc: The multicount value 25716fb914d7SGrigor Tovmasyan * @dir_in: True if direction is in. 257247a1685fSDinh Nguyen * 257347a1685fSDinh Nguyen * Configure the maximum packet size for the given endpoint, updating 257447a1685fSDinh Nguyen * the hardware control registers to reflect this. 257547a1685fSDinh Nguyen */ 25761f91b4ccSFelipe Balbi static void dwc2_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg, 2577ee2c40deSVardan Mikayelyan unsigned int ep, unsigned int mps, 2578ee2c40deSVardan Mikayelyan unsigned int mc, unsigned int dir_in) 257947a1685fSDinh Nguyen { 25801f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep; 258147a1685fSDinh Nguyen u32 reg; 258247a1685fSDinh Nguyen 2583c6f5c050SMian Yousaf Kaukab hs_ep = index_to_ep(hsotg, ep, dir_in); 2584c6f5c050SMian Yousaf Kaukab if (!hs_ep) 2585c6f5c050SMian Yousaf Kaukab return; 2586c6f5c050SMian Yousaf Kaukab 258747a1685fSDinh Nguyen if (ep == 0) { 2588ee2c40deSVardan Mikayelyan u32 mps_bytes = mps; 2589ee2c40deSVardan Mikayelyan 259047a1685fSDinh Nguyen /* EP0 is a special case */ 2591ee2c40deSVardan Mikayelyan mps = dwc2_hsotg_ep0_mps(mps_bytes); 2592ee2c40deSVardan Mikayelyan if (mps > 3) 259347a1685fSDinh Nguyen goto bad_mps; 2594ee2c40deSVardan Mikayelyan hs_ep->ep.maxpacket = mps_bytes; 259547a1685fSDinh Nguyen hs_ep->mc = 1; 259647a1685fSDinh Nguyen } else { 2597ee2c40deSVardan Mikayelyan if (mps > 1024) 259847a1685fSDinh Nguyen goto bad_mps; 2599ee2c40deSVardan Mikayelyan hs_ep->mc = mc; 2600ee2c40deSVardan Mikayelyan if (mc > 3) 260147a1685fSDinh Nguyen goto bad_mps; 2602ee2c40deSVardan Mikayelyan hs_ep->ep.maxpacket = mps; 260347a1685fSDinh Nguyen } 260447a1685fSDinh Nguyen 2605c6f5c050SMian Yousaf Kaukab if (dir_in) { 2606f25c42b8SGevorg Sahakyan reg = dwc2_readl(hsotg, DIEPCTL(ep)); 260747a1685fSDinh Nguyen reg &= ~DXEPCTL_MPS_MASK; 2608ee2c40deSVardan Mikayelyan reg |= mps; 2609f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, reg, DIEPCTL(ep)); 2610c6f5c050SMian Yousaf Kaukab } else { 2611f25c42b8SGevorg Sahakyan reg = dwc2_readl(hsotg, DOEPCTL(ep)); 261247a1685fSDinh Nguyen reg &= ~DXEPCTL_MPS_MASK; 2613ee2c40deSVardan Mikayelyan reg |= mps; 2614f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, reg, DOEPCTL(ep)); 261547a1685fSDinh Nguyen } 261647a1685fSDinh Nguyen 261747a1685fSDinh Nguyen return; 261847a1685fSDinh Nguyen 261947a1685fSDinh Nguyen bad_mps: 262047a1685fSDinh Nguyen dev_err(hsotg->dev, "ep%d: bad mps of %d\n", ep, mps); 262147a1685fSDinh Nguyen } 262247a1685fSDinh Nguyen 262347a1685fSDinh Nguyen /** 26241f91b4ccSFelipe Balbi * dwc2_hsotg_txfifo_flush - flush Tx FIFO 262547a1685fSDinh Nguyen * @hsotg: The driver state 262647a1685fSDinh Nguyen * @idx: The index for the endpoint (0..15) 262747a1685fSDinh Nguyen */ 26281f91b4ccSFelipe Balbi static void dwc2_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx) 262947a1685fSDinh Nguyen { 2630f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GRSTCTL_TXFNUM(idx) | GRSTCTL_TXFFLSH, 2631f25c42b8SGevorg Sahakyan GRSTCTL); 263247a1685fSDinh Nguyen 263347a1685fSDinh Nguyen /* wait until the fifo is flushed */ 263479d6b8c5SSevak Arakelyan if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 100)) 263579d6b8c5SSevak Arakelyan dev_warn(hsotg->dev, "%s: timeout flushing fifo GRSTCTL_TXFFLSH\n", 263679d6b8c5SSevak Arakelyan __func__); 263747a1685fSDinh Nguyen } 263847a1685fSDinh Nguyen 263947a1685fSDinh Nguyen /** 26401f91b4ccSFelipe Balbi * dwc2_hsotg_trytx - check to see if anything needs transmitting 264147a1685fSDinh Nguyen * @hsotg: The driver state 264247a1685fSDinh Nguyen * @hs_ep: The driver endpoint to check. 264347a1685fSDinh Nguyen * 264447a1685fSDinh Nguyen * Check to see if there is a request that has data to send, and if so 264547a1685fSDinh Nguyen * make an attempt to write data into the FIFO. 264647a1685fSDinh Nguyen */ 26471f91b4ccSFelipe Balbi static int dwc2_hsotg_trytx(struct dwc2_hsotg *hsotg, 26481f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep) 264947a1685fSDinh Nguyen { 26501f91b4ccSFelipe Balbi struct dwc2_hsotg_req *hs_req = hs_ep->req; 265147a1685fSDinh Nguyen 265247a1685fSDinh Nguyen if (!hs_ep->dir_in || !hs_req) { 265347a1685fSDinh Nguyen /** 265447a1685fSDinh Nguyen * if request is not enqueued, we disable interrupts 265547a1685fSDinh Nguyen * for endpoints, excepting ep0 265647a1685fSDinh Nguyen */ 265747a1685fSDinh Nguyen if (hs_ep->index != 0) 26581f91b4ccSFelipe Balbi dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index, 265947a1685fSDinh Nguyen hs_ep->dir_in, 0); 266047a1685fSDinh Nguyen return 0; 266147a1685fSDinh Nguyen } 266247a1685fSDinh Nguyen 266347a1685fSDinh Nguyen if (hs_req->req.actual < hs_req->req.length) { 266447a1685fSDinh Nguyen dev_dbg(hsotg->dev, "trying to write more for ep%d\n", 266547a1685fSDinh Nguyen hs_ep->index); 26661f91b4ccSFelipe Balbi return dwc2_hsotg_write_fifo(hsotg, hs_ep, hs_req); 266747a1685fSDinh Nguyen } 266847a1685fSDinh Nguyen 266947a1685fSDinh Nguyen return 0; 267047a1685fSDinh Nguyen } 267147a1685fSDinh Nguyen 267247a1685fSDinh Nguyen /** 26731f91b4ccSFelipe Balbi * dwc2_hsotg_complete_in - complete IN transfer 267447a1685fSDinh Nguyen * @hsotg: The device state. 267547a1685fSDinh Nguyen * @hs_ep: The endpoint that has just completed. 267647a1685fSDinh Nguyen * 267747a1685fSDinh Nguyen * An IN transfer has been completed, update the transfer's state and then 267847a1685fSDinh Nguyen * call the relevant completion routines. 267947a1685fSDinh Nguyen */ 26801f91b4ccSFelipe Balbi static void dwc2_hsotg_complete_in(struct dwc2_hsotg *hsotg, 26811f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep) 268247a1685fSDinh Nguyen { 26831f91b4ccSFelipe Balbi struct dwc2_hsotg_req *hs_req = hs_ep->req; 2684f25c42b8SGevorg Sahakyan u32 epsize = dwc2_readl(hsotg, DIEPTSIZ(hs_ep->index)); 268547a1685fSDinh Nguyen int size_left, size_done; 268647a1685fSDinh Nguyen 268747a1685fSDinh Nguyen if (!hs_req) { 268847a1685fSDinh Nguyen dev_dbg(hsotg->dev, "XferCompl but no req\n"); 268947a1685fSDinh Nguyen return; 269047a1685fSDinh Nguyen } 269147a1685fSDinh Nguyen 269247a1685fSDinh Nguyen /* Finish ZLP handling for IN EP0 transactions */ 2693fe0b94abSMian Yousaf Kaukab if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_IN) { 2694fe0b94abSMian Yousaf Kaukab dev_dbg(hsotg->dev, "zlp packet sent\n"); 2695c3b22fe2SRazmik Karapetyan 2696c3b22fe2SRazmik Karapetyan /* 2697c3b22fe2SRazmik Karapetyan * While send zlp for DWC2_EP0_STATUS_IN EP direction was 2698c3b22fe2SRazmik Karapetyan * changed to IN. Change back to complete OUT transfer request 2699c3b22fe2SRazmik Karapetyan */ 2700c3b22fe2SRazmik Karapetyan hs_ep->dir_in = 0; 2701c3b22fe2SRazmik Karapetyan 27021f91b4ccSFelipe Balbi dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); 27039e14d0a5SGregory Herrero if (hsotg->test_mode) { 27049e14d0a5SGregory Herrero int ret; 27059e14d0a5SGregory Herrero 27061f91b4ccSFelipe Balbi ret = dwc2_hsotg_set_test_mode(hsotg, hsotg->test_mode); 27079e14d0a5SGregory Herrero if (ret < 0) { 27089e14d0a5SGregory Herrero dev_dbg(hsotg->dev, "Invalid Test #%d\n", 27099e14d0a5SGregory Herrero hsotg->test_mode); 27101f91b4ccSFelipe Balbi dwc2_hsotg_stall_ep0(hsotg); 27119e14d0a5SGregory Herrero return; 27129e14d0a5SGregory Herrero } 27139e14d0a5SGregory Herrero } 27141f91b4ccSFelipe Balbi dwc2_hsotg_enqueue_setup(hsotg); 271547a1685fSDinh Nguyen return; 271647a1685fSDinh Nguyen } 271747a1685fSDinh Nguyen 271847a1685fSDinh Nguyen /* 271947a1685fSDinh Nguyen * Calculate the size of the transfer by checking how much is left 272047a1685fSDinh Nguyen * in the endpoint size register and then working it out from 272147a1685fSDinh Nguyen * the amount we loaded for the transfer. 272247a1685fSDinh Nguyen * 272347a1685fSDinh Nguyen * We do this even for DMA, as the transfer may have incremented 272447a1685fSDinh Nguyen * past the end of the buffer (DMA transfers are always 32bit 272547a1685fSDinh Nguyen * aligned). 272647a1685fSDinh Nguyen */ 2727aa3e8bc8SVahram Aharonyan if (using_desc_dma(hsotg)) { 2728aa3e8bc8SVahram Aharonyan size_left = dwc2_gadget_get_xfersize_ddma(hs_ep); 2729aa3e8bc8SVahram Aharonyan if (size_left < 0) 2730aa3e8bc8SVahram Aharonyan dev_err(hsotg->dev, "error parsing DDMA results %d\n", 2731aa3e8bc8SVahram Aharonyan size_left); 2732aa3e8bc8SVahram Aharonyan } else { 273347a1685fSDinh Nguyen size_left = DXEPTSIZ_XFERSIZE_GET(epsize); 2734aa3e8bc8SVahram Aharonyan } 273547a1685fSDinh Nguyen 273647a1685fSDinh Nguyen size_done = hs_ep->size_loaded - size_left; 273747a1685fSDinh Nguyen size_done += hs_ep->last_load; 273847a1685fSDinh Nguyen 273947a1685fSDinh Nguyen if (hs_req->req.actual != size_done) 274047a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: adjusting size done %d => %d\n", 274147a1685fSDinh Nguyen __func__, hs_req->req.actual, size_done); 274247a1685fSDinh Nguyen 274347a1685fSDinh Nguyen hs_req->req.actual = size_done; 274447a1685fSDinh Nguyen dev_dbg(hsotg->dev, "req->length:%d req->actual:%d req->zero:%d\n", 274547a1685fSDinh Nguyen hs_req->req.length, hs_req->req.actual, hs_req->req.zero); 274647a1685fSDinh Nguyen 274747a1685fSDinh Nguyen if (!size_left && hs_req->req.actual < hs_req->req.length) { 274847a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__); 27491f91b4ccSFelipe Balbi dwc2_hsotg_start_req(hsotg, hs_ep, hs_req, true); 2750fe0b94abSMian Yousaf Kaukab return; 2751fe0b94abSMian Yousaf Kaukab } 2752fe0b94abSMian Yousaf Kaukab 2753d53dc388SMinas Harutyunyan /* Zlp for all endpoints in non DDMA, for ep0 only in DATA IN stage */ 27548a20fa45SMian Yousaf Kaukab if (hs_ep->send_zlp) { 27558a20fa45SMian Yousaf Kaukab hs_ep->send_zlp = 0; 2756d53dc388SMinas Harutyunyan if (!using_desc_dma(hsotg)) { 2757d53dc388SMinas Harutyunyan dwc2_hsotg_program_zlp(hsotg, hs_ep); 2758f71b5e25SMian Yousaf Kaukab /* transfer will be completed on next complete interrupt */ 2759f71b5e25SMian Yousaf Kaukab return; 2760f71b5e25SMian Yousaf Kaukab } 2761d53dc388SMinas Harutyunyan } 2762f71b5e25SMian Yousaf Kaukab 2763fe0b94abSMian Yousaf Kaukab if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_DATA_IN) { 2764fe0b94abSMian Yousaf Kaukab /* Move to STATUS OUT */ 27651f91b4ccSFelipe Balbi dwc2_hsotg_ep0_zlp(hsotg, false); 2766fe0b94abSMian Yousaf Kaukab return; 2767fe0b94abSMian Yousaf Kaukab } 2768fe0b94abSMian Yousaf Kaukab 27691f91b4ccSFelipe Balbi dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); 277047a1685fSDinh Nguyen } 277147a1685fSDinh Nguyen 277247a1685fSDinh Nguyen /** 277332601588SVardan Mikayelyan * dwc2_gadget_read_ep_interrupts - reads interrupts for given ep 277432601588SVardan Mikayelyan * @hsotg: The device state. 277532601588SVardan Mikayelyan * @idx: Index of ep. 277632601588SVardan Mikayelyan * @dir_in: Endpoint direction 1-in 0-out. 277732601588SVardan Mikayelyan * 277832601588SVardan Mikayelyan * Reads for endpoint with given index and direction, by masking 277932601588SVardan Mikayelyan * epint_reg with coresponding mask. 278032601588SVardan Mikayelyan */ 278132601588SVardan Mikayelyan static u32 dwc2_gadget_read_ep_interrupts(struct dwc2_hsotg *hsotg, 278232601588SVardan Mikayelyan unsigned int idx, int dir_in) 278332601588SVardan Mikayelyan { 278432601588SVardan Mikayelyan u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK; 278532601588SVardan Mikayelyan u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx); 278632601588SVardan Mikayelyan u32 ints; 278732601588SVardan Mikayelyan u32 mask; 278832601588SVardan Mikayelyan u32 diepempmsk; 278932601588SVardan Mikayelyan 2790f25c42b8SGevorg Sahakyan mask = dwc2_readl(hsotg, epmsk_reg); 2791f25c42b8SGevorg Sahakyan diepempmsk = dwc2_readl(hsotg, DIEPEMPMSK); 279232601588SVardan Mikayelyan mask |= ((diepempmsk >> idx) & 0x1) ? DIEPMSK_TXFIFOEMPTY : 0; 279332601588SVardan Mikayelyan mask |= DXEPINT_SETUP_RCVD; 279432601588SVardan Mikayelyan 2795f25c42b8SGevorg Sahakyan ints = dwc2_readl(hsotg, epint_reg); 279632601588SVardan Mikayelyan ints &= mask; 279732601588SVardan Mikayelyan return ints; 279832601588SVardan Mikayelyan } 279932601588SVardan Mikayelyan 280032601588SVardan Mikayelyan /** 2801bd9971f0SVardan Mikayelyan * dwc2_gadget_handle_ep_disabled - handle DXEPINT_EPDISBLD 2802bd9971f0SVardan Mikayelyan * @hs_ep: The endpoint on which interrupt is asserted. 2803bd9971f0SVardan Mikayelyan * 2804bd9971f0SVardan Mikayelyan * This interrupt indicates that the endpoint has been disabled per the 2805bd9971f0SVardan Mikayelyan * application's request. 2806bd9971f0SVardan Mikayelyan * 2807bd9971f0SVardan Mikayelyan * For IN endpoints flushes txfifo, in case of BULK clears DCTL_CGNPINNAK, 2808bd9971f0SVardan Mikayelyan * in case of ISOC completes current request. 2809bd9971f0SVardan Mikayelyan * 2810bd9971f0SVardan Mikayelyan * For ISOC-OUT endpoints completes expired requests. If there is remaining 2811bd9971f0SVardan Mikayelyan * request starts it. 2812bd9971f0SVardan Mikayelyan */ 2813bd9971f0SVardan Mikayelyan static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep) 2814bd9971f0SVardan Mikayelyan { 2815bd9971f0SVardan Mikayelyan struct dwc2_hsotg *hsotg = hs_ep->parent; 2816bd9971f0SVardan Mikayelyan struct dwc2_hsotg_req *hs_req; 2817bd9971f0SVardan Mikayelyan unsigned char idx = hs_ep->index; 2818bd9971f0SVardan Mikayelyan int dir_in = hs_ep->dir_in; 2819bd9971f0SVardan Mikayelyan u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); 2820f25c42b8SGevorg Sahakyan int dctl = dwc2_readl(hsotg, DCTL); 2821bd9971f0SVardan Mikayelyan 2822bd9971f0SVardan Mikayelyan dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__); 2823bd9971f0SVardan Mikayelyan 2824bd9971f0SVardan Mikayelyan if (dir_in) { 2825f25c42b8SGevorg Sahakyan int epctl = dwc2_readl(hsotg, epctl_reg); 2826bd9971f0SVardan Mikayelyan 2827bd9971f0SVardan Mikayelyan dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index); 2828bd9971f0SVardan Mikayelyan 2829bd9971f0SVardan Mikayelyan if (hs_ep->isochronous) { 2830bd9971f0SVardan Mikayelyan dwc2_hsotg_complete_in(hsotg, hs_ep); 2831bd9971f0SVardan Mikayelyan return; 2832bd9971f0SVardan Mikayelyan } 2833bd9971f0SVardan Mikayelyan 2834bd9971f0SVardan Mikayelyan if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) { 2835f25c42b8SGevorg Sahakyan int dctl = dwc2_readl(hsotg, DCTL); 2836bd9971f0SVardan Mikayelyan 2837bd9971f0SVardan Mikayelyan dctl |= DCTL_CGNPINNAK; 2838f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dctl, DCTL); 2839bd9971f0SVardan Mikayelyan } 2840bd9971f0SVardan Mikayelyan return; 2841bd9971f0SVardan Mikayelyan } 2842bd9971f0SVardan Mikayelyan 2843bd9971f0SVardan Mikayelyan if (dctl & DCTL_GOUTNAKSTS) { 2844bd9971f0SVardan Mikayelyan dctl |= DCTL_CGOUTNAK; 2845f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dctl, DCTL); 2846bd9971f0SVardan Mikayelyan } 2847bd9971f0SVardan Mikayelyan 2848bd9971f0SVardan Mikayelyan if (!hs_ep->isochronous) 2849bd9971f0SVardan Mikayelyan return; 2850bd9971f0SVardan Mikayelyan 2851bd9971f0SVardan Mikayelyan if (list_empty(&hs_ep->queue)) { 2852bd9971f0SVardan Mikayelyan dev_dbg(hsotg->dev, "%s: complete_ep 0x%p, ep->queue empty!\n", 2853bd9971f0SVardan Mikayelyan __func__, hs_ep); 2854bd9971f0SVardan Mikayelyan return; 2855bd9971f0SVardan Mikayelyan } 2856bd9971f0SVardan Mikayelyan 2857bd9971f0SVardan Mikayelyan do { 2858bd9971f0SVardan Mikayelyan hs_req = get_ep_head(hs_ep); 2859bd9971f0SVardan Mikayelyan if (hs_req) 2860bd9971f0SVardan Mikayelyan dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 2861bd9971f0SVardan Mikayelyan -ENODATA); 2862bd9971f0SVardan Mikayelyan dwc2_gadget_incr_frame_num(hs_ep); 2863c7c24e7aSArtur Petrosyan /* Update current frame number value. */ 2864c7c24e7aSArtur Petrosyan hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg); 2865bd9971f0SVardan Mikayelyan } while (dwc2_gadget_target_frame_elapsed(hs_ep)); 2866bd9971f0SVardan Mikayelyan 2867bd9971f0SVardan Mikayelyan dwc2_gadget_start_next_request(hs_ep); 2868bd9971f0SVardan Mikayelyan } 2869bd9971f0SVardan Mikayelyan 2870bd9971f0SVardan Mikayelyan /** 28715321922cSVardan Mikayelyan * dwc2_gadget_handle_out_token_ep_disabled - handle DXEPINT_OUTTKNEPDIS 28726fb914d7SGrigor Tovmasyan * @ep: The endpoint on which interrupt is asserted. 28735321922cSVardan Mikayelyan * 28745321922cSVardan Mikayelyan * This is starting point for ISOC-OUT transfer, synchronization done with 28755321922cSVardan Mikayelyan * first out token received from host while corresponding EP is disabled. 28765321922cSVardan Mikayelyan * 28775321922cSVardan Mikayelyan * Device does not know initial frame in which out token will come. For this 28785321922cSVardan Mikayelyan * HW generates OUTTKNEPDIS - out token is received while EP is disabled. Upon 28795321922cSVardan Mikayelyan * getting this interrupt SW starts calculation for next transfer frame. 28805321922cSVardan Mikayelyan */ 28815321922cSVardan Mikayelyan static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep) 28825321922cSVardan Mikayelyan { 28835321922cSVardan Mikayelyan struct dwc2_hsotg *hsotg = ep->parent; 28845321922cSVardan Mikayelyan int dir_in = ep->dir_in; 28855321922cSVardan Mikayelyan u32 doepmsk; 28865321922cSVardan Mikayelyan 28875321922cSVardan Mikayelyan if (dir_in || !ep->isochronous) 28885321922cSVardan Mikayelyan return; 28895321922cSVardan Mikayelyan 2890540ccba0SVahram Aharonyan if (using_desc_dma(hsotg)) { 2891540ccba0SVahram Aharonyan if (ep->target_frame == TARGET_FRAME_INITIAL) { 2892540ccba0SVahram Aharonyan /* Start first ISO Out */ 28934d4f1e79SMinas Harutyunyan ep->target_frame = hsotg->frame_number; 2894540ccba0SVahram Aharonyan dwc2_gadget_start_isoc_ddma(ep); 2895540ccba0SVahram Aharonyan } 2896540ccba0SVahram Aharonyan return; 2897540ccba0SVahram Aharonyan } 2898540ccba0SVahram Aharonyan 28995321922cSVardan Mikayelyan if (ep->interval > 1 && 29005321922cSVardan Mikayelyan ep->target_frame == TARGET_FRAME_INITIAL) { 29015321922cSVardan Mikayelyan u32 ctrl; 29025321922cSVardan Mikayelyan 29034d4f1e79SMinas Harutyunyan ep->target_frame = hsotg->frame_number; 29045321922cSVardan Mikayelyan dwc2_gadget_incr_frame_num(ep); 29055321922cSVardan Mikayelyan 2906f25c42b8SGevorg Sahakyan ctrl = dwc2_readl(hsotg, DOEPCTL(ep->index)); 29075321922cSVardan Mikayelyan if (ep->target_frame & 0x1) 29085321922cSVardan Mikayelyan ctrl |= DXEPCTL_SETODDFR; 29095321922cSVardan Mikayelyan else 29105321922cSVardan Mikayelyan ctrl |= DXEPCTL_SETEVENFR; 29115321922cSVardan Mikayelyan 2912f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ctrl, DOEPCTL(ep->index)); 29135321922cSVardan Mikayelyan } 29145321922cSVardan Mikayelyan 29155321922cSVardan Mikayelyan dwc2_gadget_start_next_request(ep); 2916f25c42b8SGevorg Sahakyan doepmsk = dwc2_readl(hsotg, DOEPMSK); 29175321922cSVardan Mikayelyan doepmsk &= ~DOEPMSK_OUTTKNEPDISMSK; 2918f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, doepmsk, DOEPMSK); 29195321922cSVardan Mikayelyan } 29205321922cSVardan Mikayelyan 29215321922cSVardan Mikayelyan /** 29225321922cSVardan Mikayelyan * dwc2_gadget_handle_nak - handle NAK interrupt 29235321922cSVardan Mikayelyan * @hs_ep: The endpoint on which interrupt is asserted. 29245321922cSVardan Mikayelyan * 29255321922cSVardan Mikayelyan * This is starting point for ISOC-IN transfer, synchronization done with 29265321922cSVardan Mikayelyan * first IN token received from host while corresponding EP is disabled. 29275321922cSVardan Mikayelyan * 29285321922cSVardan Mikayelyan * Device does not know when first one token will arrive from host. On first 29295321922cSVardan Mikayelyan * token arrival HW generates 2 interrupts: 'in token received while FIFO empty' 29305321922cSVardan Mikayelyan * and 'NAK'. NAK interrupt for ISOC-IN means that token has arrived and ZLP was 29315321922cSVardan Mikayelyan * sent in response to that as there was no data in FIFO. SW is basing on this 29325321922cSVardan Mikayelyan * interrupt to obtain frame in which token has come and then based on the 29335321922cSVardan Mikayelyan * interval calculates next frame for transfer. 29345321922cSVardan Mikayelyan */ 29355321922cSVardan Mikayelyan static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep) 29365321922cSVardan Mikayelyan { 29375321922cSVardan Mikayelyan struct dwc2_hsotg *hsotg = hs_ep->parent; 29385321922cSVardan Mikayelyan int dir_in = hs_ep->dir_in; 29395321922cSVardan Mikayelyan 29405321922cSVardan Mikayelyan if (!dir_in || !hs_ep->isochronous) 29415321922cSVardan Mikayelyan return; 29425321922cSVardan Mikayelyan 29435321922cSVardan Mikayelyan if (hs_ep->target_frame == TARGET_FRAME_INITIAL) { 2944540ccba0SVahram Aharonyan 2945540ccba0SVahram Aharonyan if (using_desc_dma(hsotg)) { 29464d4f1e79SMinas Harutyunyan hs_ep->target_frame = hsotg->frame_number; 2947729cac69SMinas Harutyunyan dwc2_gadget_incr_frame_num(hs_ep); 294848dac4e4SGrigor Tovmasyan 294948dac4e4SGrigor Tovmasyan /* In service interval mode target_frame must 295048dac4e4SGrigor Tovmasyan * be set to last (u)frame of the service interval. 295148dac4e4SGrigor Tovmasyan */ 295248dac4e4SGrigor Tovmasyan if (hsotg->params.service_interval) { 295348dac4e4SGrigor Tovmasyan /* Set target_frame to the first (u)frame of 295448dac4e4SGrigor Tovmasyan * the service interval 295548dac4e4SGrigor Tovmasyan */ 295648dac4e4SGrigor Tovmasyan hs_ep->target_frame &= ~hs_ep->interval + 1; 295748dac4e4SGrigor Tovmasyan 295848dac4e4SGrigor Tovmasyan /* Set target_frame to the last (u)frame of 295948dac4e4SGrigor Tovmasyan * the service interval 296048dac4e4SGrigor Tovmasyan */ 296148dac4e4SGrigor Tovmasyan dwc2_gadget_incr_frame_num(hs_ep); 296248dac4e4SGrigor Tovmasyan dwc2_gadget_dec_frame_num_by_one(hs_ep); 296348dac4e4SGrigor Tovmasyan } 296448dac4e4SGrigor Tovmasyan 2965540ccba0SVahram Aharonyan dwc2_gadget_start_isoc_ddma(hs_ep); 2966540ccba0SVahram Aharonyan return; 2967540ccba0SVahram Aharonyan } 2968540ccba0SVahram Aharonyan 29694d4f1e79SMinas Harutyunyan hs_ep->target_frame = hsotg->frame_number; 29705321922cSVardan Mikayelyan if (hs_ep->interval > 1) { 2971f25c42b8SGevorg Sahakyan u32 ctrl = dwc2_readl(hsotg, 29725321922cSVardan Mikayelyan DIEPCTL(hs_ep->index)); 29735321922cSVardan Mikayelyan if (hs_ep->target_frame & 0x1) 29745321922cSVardan Mikayelyan ctrl |= DXEPCTL_SETODDFR; 29755321922cSVardan Mikayelyan else 29765321922cSVardan Mikayelyan ctrl |= DXEPCTL_SETEVENFR; 29775321922cSVardan Mikayelyan 2978f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ctrl, DIEPCTL(hs_ep->index)); 29795321922cSVardan Mikayelyan } 29805321922cSVardan Mikayelyan 29815321922cSVardan Mikayelyan dwc2_hsotg_complete_request(hsotg, hs_ep, 29825321922cSVardan Mikayelyan get_ep_head(hs_ep), 0); 29835321922cSVardan Mikayelyan } 29845321922cSVardan Mikayelyan 2985729cac69SMinas Harutyunyan if (!using_desc_dma(hsotg)) 29865321922cSVardan Mikayelyan dwc2_gadget_incr_frame_num(hs_ep); 29875321922cSVardan Mikayelyan } 29885321922cSVardan Mikayelyan 29895321922cSVardan Mikayelyan /** 29901f91b4ccSFelipe Balbi * dwc2_hsotg_epint - handle an in/out endpoint interrupt 299147a1685fSDinh Nguyen * @hsotg: The driver state 299247a1685fSDinh Nguyen * @idx: The index for the endpoint (0..15) 299347a1685fSDinh Nguyen * @dir_in: Set if this is an IN endpoint 299447a1685fSDinh Nguyen * 299547a1685fSDinh Nguyen * Process and clear any interrupt pending for an individual endpoint 299647a1685fSDinh Nguyen */ 29971f91b4ccSFelipe Balbi static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, 299847a1685fSDinh Nguyen int dir_in) 299947a1685fSDinh Nguyen { 30001f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = index_to_ep(hsotg, idx, dir_in); 300147a1685fSDinh Nguyen u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx); 300247a1685fSDinh Nguyen u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); 300347a1685fSDinh Nguyen u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx); 300447a1685fSDinh Nguyen u32 ints; 300547a1685fSDinh Nguyen 300632601588SVardan Mikayelyan ints = dwc2_gadget_read_ep_interrupts(hsotg, idx, dir_in); 300747a1685fSDinh Nguyen 300847a1685fSDinh Nguyen /* Clear endpoint interrupts */ 3009f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ints, epint_reg); 301047a1685fSDinh Nguyen 3011c6f5c050SMian Yousaf Kaukab if (!hs_ep) { 3012c6f5c050SMian Yousaf Kaukab dev_err(hsotg->dev, "%s:Interrupt for unconfigured ep%d(%s)\n", 3013c6f5c050SMian Yousaf Kaukab __func__, idx, dir_in ? "in" : "out"); 3014c6f5c050SMian Yousaf Kaukab return; 3015c6f5c050SMian Yousaf Kaukab } 3016c6f5c050SMian Yousaf Kaukab 301747a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: ep%d(%s) DxEPINT=0x%08x\n", 301847a1685fSDinh Nguyen __func__, idx, dir_in ? "in" : "out", ints); 301947a1685fSDinh Nguyen 3020b787d755SMian Yousaf Kaukab /* Don't process XferCompl interrupt if it is a setup packet */ 3021b787d755SMian Yousaf Kaukab if (idx == 0 && (ints & (DXEPINT_SETUP | DXEPINT_SETUP_RCVD))) 3022b787d755SMian Yousaf Kaukab ints &= ~DXEPINT_XFERCOMPL; 3023b787d755SMian Yousaf Kaukab 3024f0afdb42SVahram Aharonyan /* 3025f0afdb42SVahram Aharonyan * Don't process XferCompl interrupt in DDMA if EP0 is still in SETUP 3026f0afdb42SVahram Aharonyan * stage and xfercomplete was generated without SETUP phase done 3027f0afdb42SVahram Aharonyan * interrupt. SW should parse received setup packet only after host's 3028f0afdb42SVahram Aharonyan * exit from setup phase of control transfer. 3029f0afdb42SVahram Aharonyan */ 3030f0afdb42SVahram Aharonyan if (using_desc_dma(hsotg) && idx == 0 && !hs_ep->dir_in && 3031f0afdb42SVahram Aharonyan hsotg->ep0_state == DWC2_EP0_SETUP && !(ints & DXEPINT_SETUP)) 3032f0afdb42SVahram Aharonyan ints &= ~DXEPINT_XFERCOMPL; 3033f0afdb42SVahram Aharonyan 3034837e9f00SVardan Mikayelyan if (ints & DXEPINT_XFERCOMPL) { 303547a1685fSDinh Nguyen dev_dbg(hsotg->dev, 303647a1685fSDinh Nguyen "%s: XferCompl: DxEPCTL=0x%08x, DXEPTSIZ=%08x\n", 3037f25c42b8SGevorg Sahakyan __func__, dwc2_readl(hsotg, epctl_reg), 3038f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, epsiz_reg)); 303947a1685fSDinh Nguyen 3040540ccba0SVahram Aharonyan /* In DDMA handle isochronous requests separately */ 3041540ccba0SVahram Aharonyan if (using_desc_dma(hsotg) && hs_ep->isochronous) { 3042729cac69SMinas Harutyunyan /* XferCompl set along with BNA */ 3043729cac69SMinas Harutyunyan if (!(ints & DXEPINT_BNAINTR)) 3044540ccba0SVahram Aharonyan dwc2_gadget_complete_isoc_request_ddma(hs_ep); 3045540ccba0SVahram Aharonyan } else if (dir_in) { 304647a1685fSDinh Nguyen /* 3047540ccba0SVahram Aharonyan * We get OutDone from the FIFO, so we only 3048540ccba0SVahram Aharonyan * need to look at completing IN requests here 3049540ccba0SVahram Aharonyan * if operating slave mode 305047a1685fSDinh Nguyen */ 3051837e9f00SVardan Mikayelyan if (hs_ep->isochronous && hs_ep->interval > 1) 3052837e9f00SVardan Mikayelyan dwc2_gadget_incr_frame_num(hs_ep); 3053837e9f00SVardan Mikayelyan 30541f91b4ccSFelipe Balbi dwc2_hsotg_complete_in(hsotg, hs_ep); 3055837e9f00SVardan Mikayelyan if (ints & DXEPINT_NAKINTRPT) 3056837e9f00SVardan Mikayelyan ints &= ~DXEPINT_NAKINTRPT; 305747a1685fSDinh Nguyen 305847a1685fSDinh Nguyen if (idx == 0 && !hs_ep->req) 30591f91b4ccSFelipe Balbi dwc2_hsotg_enqueue_setup(hsotg); 306047a1685fSDinh Nguyen } else if (using_dma(hsotg)) { 306147a1685fSDinh Nguyen /* 306247a1685fSDinh Nguyen * We're using DMA, we need to fire an OutDone here 306347a1685fSDinh Nguyen * as we ignore the RXFIFO. 306447a1685fSDinh Nguyen */ 3065837e9f00SVardan Mikayelyan if (hs_ep->isochronous && hs_ep->interval > 1) 3066837e9f00SVardan Mikayelyan dwc2_gadget_incr_frame_num(hs_ep); 306747a1685fSDinh Nguyen 30681f91b4ccSFelipe Balbi dwc2_hsotg_handle_outdone(hsotg, idx); 306947a1685fSDinh Nguyen } 307047a1685fSDinh Nguyen } 307147a1685fSDinh Nguyen 3072bd9971f0SVardan Mikayelyan if (ints & DXEPINT_EPDISBLD) 3073bd9971f0SVardan Mikayelyan dwc2_gadget_handle_ep_disabled(hs_ep); 307447a1685fSDinh Nguyen 30755321922cSVardan Mikayelyan if (ints & DXEPINT_OUTTKNEPDIS) 30765321922cSVardan Mikayelyan dwc2_gadget_handle_out_token_ep_disabled(hs_ep); 30775321922cSVardan Mikayelyan 30785321922cSVardan Mikayelyan if (ints & DXEPINT_NAKINTRPT) 30795321922cSVardan Mikayelyan dwc2_gadget_handle_nak(hs_ep); 30805321922cSVardan Mikayelyan 308147a1685fSDinh Nguyen if (ints & DXEPINT_AHBERR) 308247a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: AHBErr\n", __func__); 308347a1685fSDinh Nguyen 308447a1685fSDinh Nguyen if (ints & DXEPINT_SETUP) { /* Setup or Timeout */ 308547a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: Setup/Timeout\n", __func__); 308647a1685fSDinh Nguyen 308747a1685fSDinh Nguyen if (using_dma(hsotg) && idx == 0) { 308847a1685fSDinh Nguyen /* 308947a1685fSDinh Nguyen * this is the notification we've received a 309047a1685fSDinh Nguyen * setup packet. In non-DMA mode we'd get this 309147a1685fSDinh Nguyen * from the RXFIFO, instead we need to process 309247a1685fSDinh Nguyen * the setup here. 309347a1685fSDinh Nguyen */ 309447a1685fSDinh Nguyen 309547a1685fSDinh Nguyen if (dir_in) 309647a1685fSDinh Nguyen WARN_ON_ONCE(1); 309747a1685fSDinh Nguyen else 30981f91b4ccSFelipe Balbi dwc2_hsotg_handle_outdone(hsotg, 0); 309947a1685fSDinh Nguyen } 310047a1685fSDinh Nguyen } 310147a1685fSDinh Nguyen 3102ef750c71SVahram Aharonyan if (ints & DXEPINT_STSPHSERCVD) { 31039d9a6b07SVahram Aharonyan dev_dbg(hsotg->dev, "%s: StsPhseRcvd\n", __func__); 31049d9a6b07SVahram Aharonyan 31059e95a66cSMinas Harutyunyan /* Safety check EP0 state when STSPHSERCVD asserted */ 31069e95a66cSMinas Harutyunyan if (hsotg->ep0_state == DWC2_EP0_DATA_OUT) { 3107ef750c71SVahram Aharonyan /* Move to STATUS IN for DDMA */ 3108b4c53b4aSMinas Harutyunyan if (using_desc_dma(hsotg)) { 3109b4c53b4aSMinas Harutyunyan if (!hsotg->delayed_status) 3110ef750c71SVahram Aharonyan dwc2_hsotg_ep0_zlp(hsotg, true); 3111b4c53b4aSMinas Harutyunyan else 3112b4c53b4aSMinas Harutyunyan /* In case of 3 stage Control Write with delayed 3113b4c53b4aSMinas Harutyunyan * status, when Status IN transfer started 3114b4c53b4aSMinas Harutyunyan * before STSPHSERCVD asserted, NAKSTS bit not 3115b4c53b4aSMinas Harutyunyan * cleared by CNAK in dwc2_hsotg_start_req() 3116b4c53b4aSMinas Harutyunyan * function. Clear now NAKSTS to allow complete 3117b4c53b4aSMinas Harutyunyan * transfer. 3118b4c53b4aSMinas Harutyunyan */ 3119b4c53b4aSMinas Harutyunyan dwc2_set_bit(hsotg, DIEPCTL(0), 3120b4c53b4aSMinas Harutyunyan DXEPCTL_CNAK); 3121b4c53b4aSMinas Harutyunyan } 3122ef750c71SVahram Aharonyan } 3123ef750c71SVahram Aharonyan 31249e95a66cSMinas Harutyunyan } 31259e95a66cSMinas Harutyunyan 312647a1685fSDinh Nguyen if (ints & DXEPINT_BACK2BACKSETUP) 312747a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__); 312847a1685fSDinh Nguyen 3129540ccba0SVahram Aharonyan if (ints & DXEPINT_BNAINTR) { 3130540ccba0SVahram Aharonyan dev_dbg(hsotg->dev, "%s: BNA interrupt\n", __func__); 3131540ccba0SVahram Aharonyan if (hs_ep->isochronous) 3132729cac69SMinas Harutyunyan dwc2_gadget_handle_isoc_bna(hs_ep); 3133540ccba0SVahram Aharonyan } 3134540ccba0SVahram Aharonyan 313547a1685fSDinh Nguyen if (dir_in && !hs_ep->isochronous) { 313647a1685fSDinh Nguyen /* not sure if this is important, but we'll clear it anyway */ 313726ddef5dSVardan Mikayelyan if (ints & DXEPINT_INTKNTXFEMP) { 313847a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n", 313947a1685fSDinh Nguyen __func__, idx); 314047a1685fSDinh Nguyen } 314147a1685fSDinh Nguyen 314247a1685fSDinh Nguyen /* this probably means something bad is happening */ 314326ddef5dSVardan Mikayelyan if (ints & DXEPINT_INTKNEPMIS) { 314447a1685fSDinh Nguyen dev_warn(hsotg->dev, "%s: ep%d: INTknEP\n", 314547a1685fSDinh Nguyen __func__, idx); 314647a1685fSDinh Nguyen } 314747a1685fSDinh Nguyen 314847a1685fSDinh Nguyen /* FIFO has space or is empty (see GAHBCFG) */ 314947a1685fSDinh Nguyen if (hsotg->dedicated_fifos && 315026ddef5dSVardan Mikayelyan ints & DXEPINT_TXFEMP) { 315147a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n", 315247a1685fSDinh Nguyen __func__, idx); 315347a1685fSDinh Nguyen if (!using_dma(hsotg)) 31541f91b4ccSFelipe Balbi dwc2_hsotg_trytx(hsotg, hs_ep); 315547a1685fSDinh Nguyen } 315647a1685fSDinh Nguyen } 315747a1685fSDinh Nguyen } 315847a1685fSDinh Nguyen 315947a1685fSDinh Nguyen /** 31601f91b4ccSFelipe Balbi * dwc2_hsotg_irq_enumdone - Handle EnumDone interrupt (enumeration done) 316147a1685fSDinh Nguyen * @hsotg: The device state. 316247a1685fSDinh Nguyen * 316347a1685fSDinh Nguyen * Handle updating the device settings after the enumeration phase has 316447a1685fSDinh Nguyen * been completed. 316547a1685fSDinh Nguyen */ 31661f91b4ccSFelipe Balbi static void dwc2_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg) 316747a1685fSDinh Nguyen { 3168f25c42b8SGevorg Sahakyan u32 dsts = dwc2_readl(hsotg, DSTS); 31699b2667f1SJingoo Han int ep0_mps = 0, ep_mps = 8; 317047a1685fSDinh Nguyen 317147a1685fSDinh Nguyen /* 317247a1685fSDinh Nguyen * This should signal the finish of the enumeration phase 317347a1685fSDinh Nguyen * of the USB handshaking, so we should now know what rate 317447a1685fSDinh Nguyen * we connected at. 317547a1685fSDinh Nguyen */ 317647a1685fSDinh Nguyen 317747a1685fSDinh Nguyen dev_dbg(hsotg->dev, "EnumDone (DSTS=0x%08x)\n", dsts); 317847a1685fSDinh Nguyen 317947a1685fSDinh Nguyen /* 318047a1685fSDinh Nguyen * note, since we're limited by the size of transfer on EP0, and 318147a1685fSDinh Nguyen * it seems IN transfers must be a even number of packets we do 318247a1685fSDinh Nguyen * not advertise a 64byte MPS on EP0. 318347a1685fSDinh Nguyen */ 318447a1685fSDinh Nguyen 318547a1685fSDinh Nguyen /* catch both EnumSpd_FS and EnumSpd_FS48 */ 31866d76c92cSMarek Vasut switch ((dsts & DSTS_ENUMSPD_MASK) >> DSTS_ENUMSPD_SHIFT) { 318747a1685fSDinh Nguyen case DSTS_ENUMSPD_FS: 318847a1685fSDinh Nguyen case DSTS_ENUMSPD_FS48: 318947a1685fSDinh Nguyen hsotg->gadget.speed = USB_SPEED_FULL; 319047a1685fSDinh Nguyen ep0_mps = EP0_MPS_LIMIT; 319147a1685fSDinh Nguyen ep_mps = 1023; 319247a1685fSDinh Nguyen break; 319347a1685fSDinh Nguyen 319447a1685fSDinh Nguyen case DSTS_ENUMSPD_HS: 319547a1685fSDinh Nguyen hsotg->gadget.speed = USB_SPEED_HIGH; 319647a1685fSDinh Nguyen ep0_mps = EP0_MPS_LIMIT; 319747a1685fSDinh Nguyen ep_mps = 1024; 319847a1685fSDinh Nguyen break; 319947a1685fSDinh Nguyen 320047a1685fSDinh Nguyen case DSTS_ENUMSPD_LS: 320147a1685fSDinh Nguyen hsotg->gadget.speed = USB_SPEED_LOW; 3202552d940fSVardan Mikayelyan ep0_mps = 8; 3203552d940fSVardan Mikayelyan ep_mps = 8; 320447a1685fSDinh Nguyen /* 320547a1685fSDinh Nguyen * note, we don't actually support LS in this driver at the 320647a1685fSDinh Nguyen * moment, and the documentation seems to imply that it isn't 320747a1685fSDinh Nguyen * supported by the PHYs on some of the devices. 320847a1685fSDinh Nguyen */ 320947a1685fSDinh Nguyen break; 321047a1685fSDinh Nguyen } 321147a1685fSDinh Nguyen dev_info(hsotg->dev, "new device is %s\n", 321247a1685fSDinh Nguyen usb_speed_string(hsotg->gadget.speed)); 321347a1685fSDinh Nguyen 321447a1685fSDinh Nguyen /* 321547a1685fSDinh Nguyen * we should now know the maximum packet size for an 321647a1685fSDinh Nguyen * endpoint, so set the endpoints to a default value. 321747a1685fSDinh Nguyen */ 321847a1685fSDinh Nguyen 321947a1685fSDinh Nguyen if (ep0_mps) { 322047a1685fSDinh Nguyen int i; 3221c6f5c050SMian Yousaf Kaukab /* Initialize ep0 for both in and out directions */ 3222ee2c40deSVardan Mikayelyan dwc2_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 0, 1); 3223ee2c40deSVardan Mikayelyan dwc2_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 0, 0); 3224c6f5c050SMian Yousaf Kaukab for (i = 1; i < hsotg->num_of_eps; i++) { 3225c6f5c050SMian Yousaf Kaukab if (hsotg->eps_in[i]) 3226ee2c40deSVardan Mikayelyan dwc2_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 3227ee2c40deSVardan Mikayelyan 0, 1); 3228c6f5c050SMian Yousaf Kaukab if (hsotg->eps_out[i]) 3229ee2c40deSVardan Mikayelyan dwc2_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 3230ee2c40deSVardan Mikayelyan 0, 0); 3231c6f5c050SMian Yousaf Kaukab } 323247a1685fSDinh Nguyen } 323347a1685fSDinh Nguyen 323447a1685fSDinh Nguyen /* ensure after enumeration our EP0 is active */ 323547a1685fSDinh Nguyen 32361f91b4ccSFelipe Balbi dwc2_hsotg_enqueue_setup(hsotg); 323747a1685fSDinh Nguyen 323847a1685fSDinh Nguyen dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", 3239f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPCTL0), 3240f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DOEPCTL0)); 324147a1685fSDinh Nguyen } 324247a1685fSDinh Nguyen 324347a1685fSDinh Nguyen /** 324447a1685fSDinh Nguyen * kill_all_requests - remove all requests from the endpoint's queue 324547a1685fSDinh Nguyen * @hsotg: The device state. 324647a1685fSDinh Nguyen * @ep: The endpoint the requests may be on. 324747a1685fSDinh Nguyen * @result: The result code to use. 324847a1685fSDinh Nguyen * 324947a1685fSDinh Nguyen * Go through the requests on the given endpoint and mark them 325047a1685fSDinh Nguyen * completed with the given result code. 325147a1685fSDinh Nguyen */ 3252941fcce4SDinh Nguyen static void kill_all_requests(struct dwc2_hsotg *hsotg, 32531f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *ep, 32546b448af4SRobert Baldyga int result) 325547a1685fSDinh Nguyen { 32569da51974SJohn Youn unsigned int size; 325747a1685fSDinh Nguyen 32586b448af4SRobert Baldyga ep->req = NULL; 325947a1685fSDinh Nguyen 326037bea42fSJohn Keeping while (!list_empty(&ep->queue)) { 326137bea42fSJohn Keeping struct dwc2_hsotg_req *req = get_ep_head(ep); 326237bea42fSJohn Keeping 326337bea42fSJohn Keeping dwc2_hsotg_complete_request(hsotg, ep, req, result); 326437bea42fSJohn Keeping } 32656b448af4SRobert Baldyga 3266b203d0a2SRobert Baldyga if (!hsotg->dedicated_fifos) 3267b203d0a2SRobert Baldyga return; 3268f25c42b8SGevorg Sahakyan size = (dwc2_readl(hsotg, DTXFSTS(ep->fifo_index)) & 0xffff) * 4; 3269b203d0a2SRobert Baldyga if (size < ep->fifo_size) 32701f91b4ccSFelipe Balbi dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index); 327147a1685fSDinh Nguyen } 327247a1685fSDinh Nguyen 327347a1685fSDinh Nguyen /** 32741f91b4ccSFelipe Balbi * dwc2_hsotg_disconnect - disconnect service 327547a1685fSDinh Nguyen * @hsotg: The device state. 327647a1685fSDinh Nguyen * 327747a1685fSDinh Nguyen * The device has been disconnected. Remove all current 327847a1685fSDinh Nguyen * transactions and signal the gadget driver that this 327947a1685fSDinh Nguyen * has happened. 328047a1685fSDinh Nguyen */ 32811f91b4ccSFelipe Balbi void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg) 328247a1685fSDinh Nguyen { 32839da51974SJohn Youn unsigned int ep; 328447a1685fSDinh Nguyen 32854ace06e8SMarek Szyprowski if (!hsotg->connected) 32864ace06e8SMarek Szyprowski return; 32874ace06e8SMarek Szyprowski 32884ace06e8SMarek Szyprowski hsotg->connected = 0; 32899e14d0a5SGregory Herrero hsotg->test_mode = 0; 3290c6f5c050SMian Yousaf Kaukab 3291dccf1badSMinas Harutyunyan /* all endpoints should be shutdown */ 3292c6f5c050SMian Yousaf Kaukab for (ep = 0; ep < hsotg->num_of_eps; ep++) { 3293c6f5c050SMian Yousaf Kaukab if (hsotg->eps_in[ep]) 32944fe4f9feSMinas Harutyunyan kill_all_requests(hsotg, hsotg->eps_in[ep], 32954fe4f9feSMinas Harutyunyan -ESHUTDOWN); 3296c6f5c050SMian Yousaf Kaukab if (hsotg->eps_out[ep]) 32974fe4f9feSMinas Harutyunyan kill_all_requests(hsotg, hsotg->eps_out[ep], 32984fe4f9feSMinas Harutyunyan -ESHUTDOWN); 3299c6f5c050SMian Yousaf Kaukab } 330047a1685fSDinh Nguyen 330147a1685fSDinh Nguyen call_gadget(hsotg, disconnect); 3302065d3931SGregory Herrero hsotg->lx_state = DWC2_L3; 3303ce2b21a4SJohn Stultz 3304ce2b21a4SJohn Stultz usb_gadget_set_state(&hsotg->gadget, USB_STATE_NOTATTACHED); 330547a1685fSDinh Nguyen } 330647a1685fSDinh Nguyen 330747a1685fSDinh Nguyen /** 33081f91b4ccSFelipe Balbi * dwc2_hsotg_irq_fifoempty - TX FIFO empty interrupt handler 330947a1685fSDinh Nguyen * @hsotg: The device state: 331047a1685fSDinh Nguyen * @periodic: True if this is a periodic FIFO interrupt 331147a1685fSDinh Nguyen */ 33121f91b4ccSFelipe Balbi static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic) 331347a1685fSDinh Nguyen { 33141f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *ep; 331547a1685fSDinh Nguyen int epno, ret; 331647a1685fSDinh Nguyen 331747a1685fSDinh Nguyen /* look through for any more data to transmit */ 331847a1685fSDinh Nguyen for (epno = 0; epno < hsotg->num_of_eps; epno++) { 3319c6f5c050SMian Yousaf Kaukab ep = index_to_ep(hsotg, epno, 1); 3320c6f5c050SMian Yousaf Kaukab 3321c6f5c050SMian Yousaf Kaukab if (!ep) 3322c6f5c050SMian Yousaf Kaukab continue; 332347a1685fSDinh Nguyen 332447a1685fSDinh Nguyen if (!ep->dir_in) 332547a1685fSDinh Nguyen continue; 332647a1685fSDinh Nguyen 332747a1685fSDinh Nguyen if ((periodic && !ep->periodic) || 332847a1685fSDinh Nguyen (!periodic && ep->periodic)) 332947a1685fSDinh Nguyen continue; 333047a1685fSDinh Nguyen 33311f91b4ccSFelipe Balbi ret = dwc2_hsotg_trytx(hsotg, ep); 333247a1685fSDinh Nguyen if (ret < 0) 333347a1685fSDinh Nguyen break; 333447a1685fSDinh Nguyen } 333547a1685fSDinh Nguyen } 333647a1685fSDinh Nguyen 333747a1685fSDinh Nguyen /* IRQ flags which will trigger a retry around the IRQ loop */ 333847a1685fSDinh Nguyen #define IRQ_RETRY_MASK (GINTSTS_NPTXFEMP | \ 333947a1685fSDinh Nguyen GINTSTS_PTXFEMP | \ 334047a1685fSDinh Nguyen GINTSTS_RXFLVL) 334147a1685fSDinh Nguyen 33424fe4f9feSMinas Harutyunyan static int dwc2_hsotg_ep_disable(struct usb_ep *ep); 334347a1685fSDinh Nguyen /** 334458aff959SLee Jones * dwc2_hsotg_core_init_disconnected - issue softreset to the core 334547a1685fSDinh Nguyen * @hsotg: The device state 33466fb914d7SGrigor Tovmasyan * @is_usb_reset: Usb resetting flag 334747a1685fSDinh Nguyen * 334847a1685fSDinh Nguyen * Issue a soft reset to the core, and await the core finishing it. 334947a1685fSDinh Nguyen */ 33501f91b4ccSFelipe Balbi void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, 3351643cc4deSGregory Herrero bool is_usb_reset) 335247a1685fSDinh Nguyen { 33531ee6903bSGregory Herrero u32 intmsk; 3354643cc4deSGregory Herrero u32 val; 3355ecd9a7adSPrzemek Rudy u32 usbcfg; 335679c3b5bbSVahram Aharonyan u32 dcfg = 0; 3357dccf1badSMinas Harutyunyan int ep; 3358643cc4deSGregory Herrero 33595390d438SMian Yousaf Kaukab /* Kill any ep0 requests as controller will be reinitialized */ 33605390d438SMian Yousaf Kaukab kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); 33615390d438SMian Yousaf Kaukab 3362dccf1badSMinas Harutyunyan if (!is_usb_reset) { 33636e6360b6SJohn Stultz if (dwc2_core_reset(hsotg, true)) 336486de4895SGregory Herrero return; 3365dccf1badSMinas Harutyunyan } else { 3366dccf1badSMinas Harutyunyan /* all endpoints should be shutdown */ 3367dccf1badSMinas Harutyunyan for (ep = 1; ep < hsotg->num_of_eps; ep++) { 3368dccf1badSMinas Harutyunyan if (hsotg->eps_in[ep]) 3369dccf1badSMinas Harutyunyan dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); 3370dccf1badSMinas Harutyunyan if (hsotg->eps_out[ep]) 3371dccf1badSMinas Harutyunyan dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); 3372dccf1badSMinas Harutyunyan } 3373dccf1badSMinas Harutyunyan } 337447a1685fSDinh Nguyen 337547a1685fSDinh Nguyen /* 337647a1685fSDinh Nguyen * we must now enable ep0 ready for host detection and then 337747a1685fSDinh Nguyen * set configuration. 337847a1685fSDinh Nguyen */ 337947a1685fSDinh Nguyen 3380ecd9a7adSPrzemek Rudy /* keep other bits untouched (so e.g. forced modes are not lost) */ 3381f25c42b8SGevorg Sahakyan usbcfg = dwc2_readl(hsotg, GUSBCFG); 33821e868545SJules Maselbas usbcfg &= ~GUSBCFG_TOUTCAL_MASK; 3383707d80f0SJules Maselbas usbcfg |= GUSBCFG_TOUTCAL(7); 3384ecd9a7adSPrzemek Rudy 33851e868545SJules Maselbas /* remove the HNP/SRP and set the PHY */ 33861e868545SJules Maselbas usbcfg &= ~(GUSBCFG_SRPCAP | GUSBCFG_HNPCAP); 3387f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, usbcfg, GUSBCFG); 338847a1685fSDinh Nguyen 33891e868545SJules Maselbas dwc2_phy_init(hsotg, true); 33901e868545SJules Maselbas 33911f91b4ccSFelipe Balbi dwc2_hsotg_init_fifo(hsotg); 339247a1685fSDinh Nguyen 3393643cc4deSGregory Herrero if (!is_usb_reset) 3394f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON); 339547a1685fSDinh Nguyen 339679c3b5bbSVahram Aharonyan dcfg |= DCFG_EPMISCNT(1); 339738e9002bSVardan Mikayelyan 339838e9002bSVardan Mikayelyan switch (hsotg->params.speed) { 339938e9002bSVardan Mikayelyan case DWC2_SPEED_PARAM_LOW: 340038e9002bSVardan Mikayelyan dcfg |= DCFG_DEVSPD_LS; 340138e9002bSVardan Mikayelyan break; 340238e9002bSVardan Mikayelyan case DWC2_SPEED_PARAM_FULL: 340379c3b5bbSVahram Aharonyan if (hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) 340479c3b5bbSVahram Aharonyan dcfg |= DCFG_DEVSPD_FS48; 340579c3b5bbSVahram Aharonyan else 340679c3b5bbSVahram Aharonyan dcfg |= DCFG_DEVSPD_FS; 340738e9002bSVardan Mikayelyan break; 340838e9002bSVardan Mikayelyan default: 340979c3b5bbSVahram Aharonyan dcfg |= DCFG_DEVSPD_HS; 341079c3b5bbSVahram Aharonyan } 341138e9002bSVardan Mikayelyan 3412b43ebc96SGrigor Tovmasyan if (hsotg->params.ipg_isoc_en) 3413b43ebc96SGrigor Tovmasyan dcfg |= DCFG_IPG_ISOC_SUPPORDED; 3414b43ebc96SGrigor Tovmasyan 3415f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dcfg, DCFG); 341647a1685fSDinh Nguyen 341747a1685fSDinh Nguyen /* Clear any pending OTG interrupts */ 3418f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, 0xffffffff, GOTGINT); 341947a1685fSDinh Nguyen 342047a1685fSDinh Nguyen /* Clear any pending interrupts */ 3421f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, 0xffffffff, GINTSTS); 34221ee6903bSGregory Herrero intmsk = GINTSTS_ERLYSUSP | GINTSTS_SESSREQINT | 342347a1685fSDinh Nguyen GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF | 34241ee6903bSGregory Herrero GINTSTS_USBRST | GINTSTS_RESETDET | 34251ee6903bSGregory Herrero GINTSTS_ENUMDONE | GINTSTS_OTGINT | 3426376f0401SSevak Arakelyan GINTSTS_USBSUSP | GINTSTS_WKUPINT | 3427376f0401SSevak Arakelyan GINTSTS_LPMTRANRCVD; 3428f4736701SVahram Aharonyan 3429f4736701SVahram Aharonyan if (!using_desc_dma(hsotg)) 3430f4736701SVahram Aharonyan intmsk |= GINTSTS_INCOMPL_SOIN | GINTSTS_INCOMPL_SOOUT; 34311ee6903bSGregory Herrero 343295832c00SJohn Youn if (!hsotg->params.external_id_pin_ctl) 34331ee6903bSGregory Herrero intmsk |= GINTSTS_CONIDSTSCHNG; 34341ee6903bSGregory Herrero 3435f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, intmsk, GINTMSK); 343647a1685fSDinh Nguyen 3437a5c18f11SVahram Aharonyan if (using_dma(hsotg)) { 3438f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN | 3439d1ac8c80SRazmik Karapetyan hsotg->params.ahbcfg, 3440f25c42b8SGevorg Sahakyan GAHBCFG); 3441a5c18f11SVahram Aharonyan 3442a5c18f11SVahram Aharonyan /* Set DDMA mode support in the core if needed */ 3443a5c18f11SVahram Aharonyan if (using_desc_dma(hsotg)) 3444f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCFG, DCFG_DESCDMA_EN); 3445a5c18f11SVahram Aharonyan 3446a5c18f11SVahram Aharonyan } else { 3447f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ((hsotg->dedicated_fifos) ? 344895c8bc36SAntti Seppälä (GAHBCFG_NP_TXF_EMP_LVL | 344947a1685fSDinh Nguyen GAHBCFG_P_TXF_EMP_LVL) : 0) | 3450f25c42b8SGevorg Sahakyan GAHBCFG_GLBL_INTR_EN, GAHBCFG); 3451a5c18f11SVahram Aharonyan } 345247a1685fSDinh Nguyen 345347a1685fSDinh Nguyen /* 345447a1685fSDinh Nguyen * If INTknTXFEmpMsk is enabled, it's important to disable ep interrupts 345547a1685fSDinh Nguyen * when we have no data to transfer. Otherwise we get being flooded by 345647a1685fSDinh Nguyen * interrupts. 345747a1685fSDinh Nguyen */ 345847a1685fSDinh Nguyen 3459f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ((hsotg->dedicated_fifos && !using_dma(hsotg)) ? 34606ff2e832SMian Yousaf Kaukab DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK : 0) | 346147a1685fSDinh Nguyen DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK | 3462837e9f00SVardan Mikayelyan DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK, 3463f25c42b8SGevorg Sahakyan DIEPMSK); 346447a1685fSDinh Nguyen 346547a1685fSDinh Nguyen /* 346647a1685fSDinh Nguyen * don't need XferCompl, we get that from RXFIFO in slave mode. In 34679d9a6b07SVahram Aharonyan * DMA mode we may need this and StsPhseRcvd. 346847a1685fSDinh Nguyen */ 3469f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, (using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK | 34709d9a6b07SVahram Aharonyan DOEPMSK_STSPHSERCVDMSK) : 0) | 347147a1685fSDinh Nguyen DOEPMSK_EPDISBLDMSK | DOEPMSK_AHBERRMSK | 34729d9a6b07SVahram Aharonyan DOEPMSK_SETUPMSK, 3473f25c42b8SGevorg Sahakyan DOEPMSK); 347447a1685fSDinh Nguyen 3475ec01f0b2SVahram Aharonyan /* Enable BNA interrupt for DDMA */ 347637981e00SMinas Harutyunyan if (using_desc_dma(hsotg)) { 3477f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DOEPMSK, DOEPMSK_BNAMSK); 3478f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DIEPMSK, DIEPMSK_BNAININTRMSK); 347937981e00SMinas Harutyunyan } 3480ec01f0b2SVahram Aharonyan 3481ca531bc2SGrigor Tovmasyan /* Enable Service Interval mode if supported */ 3482ca531bc2SGrigor Tovmasyan if (using_desc_dma(hsotg) && hsotg->params.service_interval) 3483ca531bc2SGrigor Tovmasyan dwc2_set_bit(hsotg, DCTL, DCTL_SERVICE_INTERVAL_SUPPORTED); 3484ca531bc2SGrigor Tovmasyan 3485f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, 0, DAINTMSK); 348647a1685fSDinh Nguyen 348747a1685fSDinh Nguyen dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", 3488f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPCTL0), 3489f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DOEPCTL0)); 349047a1685fSDinh Nguyen 349147a1685fSDinh Nguyen /* enable in and out endpoint interrupts */ 34921f91b4ccSFelipe Balbi dwc2_hsotg_en_gsint(hsotg, GINTSTS_OEPINT | GINTSTS_IEPINT); 349347a1685fSDinh Nguyen 349447a1685fSDinh Nguyen /* 349547a1685fSDinh Nguyen * Enable the RXFIFO when in slave mode, as this is how we collect 349647a1685fSDinh Nguyen * the data. In DMA mode, we get events from the FIFO but also 349747a1685fSDinh Nguyen * things we cannot process, so do not use it. 349847a1685fSDinh Nguyen */ 349947a1685fSDinh Nguyen if (!using_dma(hsotg)) 35001f91b4ccSFelipe Balbi dwc2_hsotg_en_gsint(hsotg, GINTSTS_RXFLVL); 350147a1685fSDinh Nguyen 350247a1685fSDinh Nguyen /* Enable interrupts for EP0 in and out */ 35031f91b4ccSFelipe Balbi dwc2_hsotg_ctrl_epint(hsotg, 0, 0, 1); 35041f91b4ccSFelipe Balbi dwc2_hsotg_ctrl_epint(hsotg, 0, 1, 1); 350547a1685fSDinh Nguyen 3506643cc4deSGregory Herrero if (!is_usb_reset) { 3507f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_PWRONPRGDONE); 350847a1685fSDinh Nguyen udelay(10); /* see openiboot */ 3509f25c42b8SGevorg Sahakyan dwc2_clear_bit(hsotg, DCTL, DCTL_PWRONPRGDONE); 3510643cc4deSGregory Herrero } 351147a1685fSDinh Nguyen 3512f25c42b8SGevorg Sahakyan dev_dbg(hsotg->dev, "DCTL=0x%08x\n", dwc2_readl(hsotg, DCTL)); 351347a1685fSDinh Nguyen 351447a1685fSDinh Nguyen /* 351547a1685fSDinh Nguyen * DxEPCTL_USBActEp says RO in manual, but seems to be set by 351647a1685fSDinh Nguyen * writing to the EPCTL register.. 351747a1685fSDinh Nguyen */ 351847a1685fSDinh Nguyen 351947a1685fSDinh Nguyen /* set to read 1 8byte packet */ 3520f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | 3521f25c42b8SGevorg Sahakyan DXEPTSIZ_XFERSIZE(8), DOEPTSIZ0); 352247a1685fSDinh Nguyen 3523f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) | 352447a1685fSDinh Nguyen DXEPCTL_CNAK | DXEPCTL_EPENA | 352547a1685fSDinh Nguyen DXEPCTL_USBACTEP, 3526f25c42b8SGevorg Sahakyan DOEPCTL0); 352747a1685fSDinh Nguyen 352847a1685fSDinh Nguyen /* enable, but don't activate EP0in */ 3529f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) | 3530f25c42b8SGevorg Sahakyan DXEPCTL_USBACTEP, DIEPCTL0); 353147a1685fSDinh Nguyen 353247a1685fSDinh Nguyen /* clear global NAKs */ 3533643cc4deSGregory Herrero val = DCTL_CGOUTNAK | DCTL_CGNPINNAK; 3534643cc4deSGregory Herrero if (!is_usb_reset) 3535643cc4deSGregory Herrero val |= DCTL_SFTDISCON; 3536f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, val); 353747a1685fSDinh Nguyen 353821b03405SSevak Arakelyan /* configure the core to support LPM */ 353921b03405SSevak Arakelyan dwc2_gadget_init_lpm(hsotg); 354021b03405SSevak Arakelyan 354115d9dbf8SGrigor Tovmasyan /* program GREFCLK register if needed */ 354215d9dbf8SGrigor Tovmasyan if (using_desc_dma(hsotg) && hsotg->params.service_interval) 354315d9dbf8SGrigor Tovmasyan dwc2_gadget_program_ref_clk(hsotg); 354415d9dbf8SGrigor Tovmasyan 354547a1685fSDinh Nguyen /* must be at-least 3ms to allow bus to see disconnect */ 354647a1685fSDinh Nguyen mdelay(3); 354747a1685fSDinh Nguyen 3548065d3931SGregory Herrero hsotg->lx_state = DWC2_L0; 3549755d7395SVardan Mikayelyan 3550755d7395SVardan Mikayelyan dwc2_hsotg_enqueue_setup(hsotg); 3551755d7395SVardan Mikayelyan 3552755d7395SVardan Mikayelyan dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", 3553f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPCTL0), 3554f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DOEPCTL0)); 3555ad38dc5dSMarek Szyprowski } 3556ac3c81f3SMarek Szyprowski 355717f93402SAmelie Delaunay void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) 3558ad38dc5dSMarek Szyprowski { 3559ad38dc5dSMarek Szyprowski /* set the soft-disconnect bit */ 3560f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON); 3561ad38dc5dSMarek Szyprowski } 3562ad38dc5dSMarek Szyprowski 35631f91b4ccSFelipe Balbi void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) 3564ad38dc5dSMarek Szyprowski { 356547a1685fSDinh Nguyen /* remove the soft-disconnect and let's go */ 3566f25c42b8SGevorg Sahakyan dwc2_clear_bit(hsotg, DCTL, DCTL_SFTDISCON); 356747a1685fSDinh Nguyen } 356847a1685fSDinh Nguyen 356947a1685fSDinh Nguyen /** 3570381fc8f8SVardan Mikayelyan * dwc2_gadget_handle_incomplete_isoc_in - handle incomplete ISO IN Interrupt. 3571381fc8f8SVardan Mikayelyan * @hsotg: The device state: 3572381fc8f8SVardan Mikayelyan * 3573381fc8f8SVardan Mikayelyan * This interrupt indicates one of the following conditions occurred while 3574381fc8f8SVardan Mikayelyan * transmitting an ISOC transaction. 3575381fc8f8SVardan Mikayelyan * - Corrupted IN Token for ISOC EP. 3576381fc8f8SVardan Mikayelyan * - Packet not complete in FIFO. 3577381fc8f8SVardan Mikayelyan * 3578381fc8f8SVardan Mikayelyan * The following actions will be taken: 3579381fc8f8SVardan Mikayelyan * - Determine the EP 3580381fc8f8SVardan Mikayelyan * - Disable EP; when 'Endpoint Disabled' interrupt is received Flush FIFO 3581381fc8f8SVardan Mikayelyan */ 3582381fc8f8SVardan Mikayelyan static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg) 3583381fc8f8SVardan Mikayelyan { 3584381fc8f8SVardan Mikayelyan struct dwc2_hsotg_ep *hs_ep; 3585381fc8f8SVardan Mikayelyan u32 epctrl; 35861b4977c7SRazmik Karapetyan u32 daintmsk; 3587381fc8f8SVardan Mikayelyan u32 idx; 3588381fc8f8SVardan Mikayelyan 3589381fc8f8SVardan Mikayelyan dev_dbg(hsotg->dev, "Incomplete isoc in interrupt received:\n"); 3590381fc8f8SVardan Mikayelyan 3591f25c42b8SGevorg Sahakyan daintmsk = dwc2_readl(hsotg, DAINTMSK); 35921b4977c7SRazmik Karapetyan 3593d5d5f079SArtur Petrosyan for (idx = 1; idx < hsotg->num_of_eps; idx++) { 3594381fc8f8SVardan Mikayelyan hs_ep = hsotg->eps_in[idx]; 35951b4977c7SRazmik Karapetyan /* Proceed only unmasked ISOC EPs */ 359689066b36SJohn Keeping if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous) 35971b4977c7SRazmik Karapetyan continue; 35981b4977c7SRazmik Karapetyan 3599f25c42b8SGevorg Sahakyan epctrl = dwc2_readl(hsotg, DIEPCTL(idx)); 36001b4977c7SRazmik Karapetyan if ((epctrl & DXEPCTL_EPENA) && 3601381fc8f8SVardan Mikayelyan dwc2_gadget_target_frame_elapsed(hs_ep)) { 3602381fc8f8SVardan Mikayelyan epctrl |= DXEPCTL_SNAK; 3603381fc8f8SVardan Mikayelyan epctrl |= DXEPCTL_EPDIS; 3604f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, epctrl, DIEPCTL(idx)); 3605381fc8f8SVardan Mikayelyan } 3606381fc8f8SVardan Mikayelyan } 3607381fc8f8SVardan Mikayelyan 3608381fc8f8SVardan Mikayelyan /* Clear interrupt */ 3609f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GINTSTS_INCOMPL_SOIN, GINTSTS); 3610381fc8f8SVardan Mikayelyan } 3611381fc8f8SVardan Mikayelyan 3612381fc8f8SVardan Mikayelyan /** 3613381fc8f8SVardan Mikayelyan * dwc2_gadget_handle_incomplete_isoc_out - handle incomplete ISO OUT Interrupt 3614381fc8f8SVardan Mikayelyan * @hsotg: The device state: 3615381fc8f8SVardan Mikayelyan * 3616381fc8f8SVardan Mikayelyan * This interrupt indicates one of the following conditions occurred while 3617381fc8f8SVardan Mikayelyan * transmitting an ISOC transaction. 3618381fc8f8SVardan Mikayelyan * - Corrupted OUT Token for ISOC EP. 3619381fc8f8SVardan Mikayelyan * - Packet not complete in FIFO. 3620381fc8f8SVardan Mikayelyan * 3621381fc8f8SVardan Mikayelyan * The following actions will be taken: 3622381fc8f8SVardan Mikayelyan * - Determine the EP 3623381fc8f8SVardan Mikayelyan * - Set DCTL_SGOUTNAK and unmask GOUTNAKEFF if target frame elapsed. 3624381fc8f8SVardan Mikayelyan */ 3625381fc8f8SVardan Mikayelyan static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg) 3626381fc8f8SVardan Mikayelyan { 3627381fc8f8SVardan Mikayelyan u32 gintsts; 3628381fc8f8SVardan Mikayelyan u32 gintmsk; 3629689efb26SRazmik Karapetyan u32 daintmsk; 3630381fc8f8SVardan Mikayelyan u32 epctrl; 3631381fc8f8SVardan Mikayelyan struct dwc2_hsotg_ep *hs_ep; 3632381fc8f8SVardan Mikayelyan int idx; 3633381fc8f8SVardan Mikayelyan 3634381fc8f8SVardan Mikayelyan dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__); 3635381fc8f8SVardan Mikayelyan 3636f25c42b8SGevorg Sahakyan daintmsk = dwc2_readl(hsotg, DAINTMSK); 3637689efb26SRazmik Karapetyan daintmsk >>= DAINT_OUTEP_SHIFT; 3638689efb26SRazmik Karapetyan 3639d5d5f079SArtur Petrosyan for (idx = 1; idx < hsotg->num_of_eps; idx++) { 3640381fc8f8SVardan Mikayelyan hs_ep = hsotg->eps_out[idx]; 3641689efb26SRazmik Karapetyan /* Proceed only unmasked ISOC EPs */ 364289066b36SJohn Keeping if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous) 3643689efb26SRazmik Karapetyan continue; 3644689efb26SRazmik Karapetyan 3645f25c42b8SGevorg Sahakyan epctrl = dwc2_readl(hsotg, DOEPCTL(idx)); 3646689efb26SRazmik Karapetyan if ((epctrl & DXEPCTL_EPENA) && 3647381fc8f8SVardan Mikayelyan dwc2_gadget_target_frame_elapsed(hs_ep)) { 3648381fc8f8SVardan Mikayelyan /* Unmask GOUTNAKEFF interrupt */ 3649f25c42b8SGevorg Sahakyan gintmsk = dwc2_readl(hsotg, GINTMSK); 3650381fc8f8SVardan Mikayelyan gintmsk |= GINTSTS_GOUTNAKEFF; 3651f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gintmsk, GINTMSK); 3652381fc8f8SVardan Mikayelyan 3653f25c42b8SGevorg Sahakyan gintsts = dwc2_readl(hsotg, GINTSTS); 3654689efb26SRazmik Karapetyan if (!(gintsts & GINTSTS_GOUTNAKEFF)) { 3655f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_SGOUTNAK); 3656689efb26SRazmik Karapetyan break; 3657689efb26SRazmik Karapetyan } 3658381fc8f8SVardan Mikayelyan } 3659381fc8f8SVardan Mikayelyan } 3660381fc8f8SVardan Mikayelyan 3661381fc8f8SVardan Mikayelyan /* Clear interrupt */ 3662f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GINTSTS_INCOMPL_SOOUT, GINTSTS); 3663381fc8f8SVardan Mikayelyan } 3664381fc8f8SVardan Mikayelyan 3665381fc8f8SVardan Mikayelyan /** 36661f91b4ccSFelipe Balbi * dwc2_hsotg_irq - handle device interrupt 366747a1685fSDinh Nguyen * @irq: The IRQ number triggered 366847a1685fSDinh Nguyen * @pw: The pw value when registered the handler. 366947a1685fSDinh Nguyen */ 36701f91b4ccSFelipe Balbi static irqreturn_t dwc2_hsotg_irq(int irq, void *pw) 367147a1685fSDinh Nguyen { 3672941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = pw; 367347a1685fSDinh Nguyen int retry_count = 8; 367447a1685fSDinh Nguyen u32 gintsts; 367547a1685fSDinh Nguyen u32 gintmsk; 367647a1685fSDinh Nguyen 3677ee3de8d7SVardan Mikayelyan if (!dwc2_is_device_mode(hsotg)) 3678ee3de8d7SVardan Mikayelyan return IRQ_NONE; 3679ee3de8d7SVardan Mikayelyan 368047a1685fSDinh Nguyen spin_lock(&hsotg->lock); 368147a1685fSDinh Nguyen irq_retry: 3682f25c42b8SGevorg Sahakyan gintsts = dwc2_readl(hsotg, GINTSTS); 3683f25c42b8SGevorg Sahakyan gintmsk = dwc2_readl(hsotg, GINTMSK); 368447a1685fSDinh Nguyen 368547a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: %08x %08x (%08x) retry %d\n", 368647a1685fSDinh Nguyen __func__, gintsts, gintsts & gintmsk, gintmsk, retry_count); 368747a1685fSDinh Nguyen 368847a1685fSDinh Nguyen gintsts &= gintmsk; 368947a1685fSDinh Nguyen 36908fc37b82SMian Yousaf Kaukab if (gintsts & GINTSTS_RESETDET) { 36918fc37b82SMian Yousaf Kaukab dev_dbg(hsotg->dev, "%s: USBRstDet\n", __func__); 36928fc37b82SMian Yousaf Kaukab 3693f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GINTSTS_RESETDET, GINTSTS); 36948fc37b82SMian Yousaf Kaukab 36958fc37b82SMian Yousaf Kaukab /* This event must be used only if controller is suspended */ 3696c9c394abSArtur Petrosyan if (hsotg->in_ppd && hsotg->lx_state == DWC2_L2) 3697c9c394abSArtur Petrosyan dwc2_exit_partial_power_down(hsotg, 0, true); 3698c9c394abSArtur Petrosyan 36998fc37b82SMian Yousaf Kaukab hsotg->lx_state = DWC2_L0; 37008fc37b82SMian Yousaf Kaukab } 37018fc37b82SMian Yousaf Kaukab 37028fc37b82SMian Yousaf Kaukab if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) { 3703f25c42b8SGevorg Sahakyan u32 usb_status = dwc2_readl(hsotg, GOTGCTL); 37048fc37b82SMian Yousaf Kaukab u32 connected = hsotg->connected; 37058fc37b82SMian Yousaf Kaukab 37068fc37b82SMian Yousaf Kaukab dev_dbg(hsotg->dev, "%s: USBRst\n", __func__); 37078fc37b82SMian Yousaf Kaukab dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n", 3708f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, GNPTXSTS)); 37098fc37b82SMian Yousaf Kaukab 3710f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GINTSTS_USBRST, GINTSTS); 37118fc37b82SMian Yousaf Kaukab 37128fc37b82SMian Yousaf Kaukab /* Report disconnection if it is not already done. */ 37138fc37b82SMian Yousaf Kaukab dwc2_hsotg_disconnect(hsotg); 37148fc37b82SMian Yousaf Kaukab 3715307bc11fSMinas Harutyunyan /* Reset device address to zero */ 3716f25c42b8SGevorg Sahakyan dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK); 3717307bc11fSMinas Harutyunyan 37188fc37b82SMian Yousaf Kaukab if (usb_status & GOTGCTL_BSESVLD && connected) 37198fc37b82SMian Yousaf Kaukab dwc2_hsotg_core_init_disconnected(hsotg, true); 37208fc37b82SMian Yousaf Kaukab } 37218fc37b82SMian Yousaf Kaukab 372247a1685fSDinh Nguyen if (gintsts & GINTSTS_ENUMDONE) { 3723f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GINTSTS_ENUMDONE, GINTSTS); 372447a1685fSDinh Nguyen 37251f91b4ccSFelipe Balbi dwc2_hsotg_irq_enumdone(hsotg); 372647a1685fSDinh Nguyen } 372747a1685fSDinh Nguyen 372847a1685fSDinh Nguyen if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) { 3729f25c42b8SGevorg Sahakyan u32 daint = dwc2_readl(hsotg, DAINT); 3730f25c42b8SGevorg Sahakyan u32 daintmsk = dwc2_readl(hsotg, DAINTMSK); 373147a1685fSDinh Nguyen u32 daint_out, daint_in; 373247a1685fSDinh Nguyen int ep; 373347a1685fSDinh Nguyen 373447a1685fSDinh Nguyen daint &= daintmsk; 373547a1685fSDinh Nguyen daint_out = daint >> DAINT_OUTEP_SHIFT; 373647a1685fSDinh Nguyen daint_in = daint & ~(daint_out << DAINT_OUTEP_SHIFT); 373747a1685fSDinh Nguyen 373847a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint); 373947a1685fSDinh Nguyen 3740cec87f1dSMian Yousaf Kaukab for (ep = 0; ep < hsotg->num_of_eps && daint_out; 3741cec87f1dSMian Yousaf Kaukab ep++, daint_out >>= 1) { 374247a1685fSDinh Nguyen if (daint_out & 1) 37431f91b4ccSFelipe Balbi dwc2_hsotg_epint(hsotg, ep, 0); 374447a1685fSDinh Nguyen } 374547a1685fSDinh Nguyen 3746cec87f1dSMian Yousaf Kaukab for (ep = 0; ep < hsotg->num_of_eps && daint_in; 3747cec87f1dSMian Yousaf Kaukab ep++, daint_in >>= 1) { 374847a1685fSDinh Nguyen if (daint_in & 1) 37491f91b4ccSFelipe Balbi dwc2_hsotg_epint(hsotg, ep, 1); 375047a1685fSDinh Nguyen } 375147a1685fSDinh Nguyen } 375247a1685fSDinh Nguyen 375347a1685fSDinh Nguyen /* check both FIFOs */ 375447a1685fSDinh Nguyen 375547a1685fSDinh Nguyen if (gintsts & GINTSTS_NPTXFEMP) { 375647a1685fSDinh Nguyen dev_dbg(hsotg->dev, "NPTxFEmp\n"); 375747a1685fSDinh Nguyen 375847a1685fSDinh Nguyen /* 375947a1685fSDinh Nguyen * Disable the interrupt to stop it happening again 376047a1685fSDinh Nguyen * unless one of these endpoint routines decides that 376147a1685fSDinh Nguyen * it needs re-enabling 376247a1685fSDinh Nguyen */ 376347a1685fSDinh Nguyen 37641f91b4ccSFelipe Balbi dwc2_hsotg_disable_gsint(hsotg, GINTSTS_NPTXFEMP); 37651f91b4ccSFelipe Balbi dwc2_hsotg_irq_fifoempty(hsotg, false); 376647a1685fSDinh Nguyen } 376747a1685fSDinh Nguyen 376847a1685fSDinh Nguyen if (gintsts & GINTSTS_PTXFEMP) { 376947a1685fSDinh Nguyen dev_dbg(hsotg->dev, "PTxFEmp\n"); 377047a1685fSDinh Nguyen 377147a1685fSDinh Nguyen /* See note in GINTSTS_NPTxFEmp */ 377247a1685fSDinh Nguyen 37731f91b4ccSFelipe Balbi dwc2_hsotg_disable_gsint(hsotg, GINTSTS_PTXFEMP); 37741f91b4ccSFelipe Balbi dwc2_hsotg_irq_fifoempty(hsotg, true); 377547a1685fSDinh Nguyen } 377647a1685fSDinh Nguyen 377747a1685fSDinh Nguyen if (gintsts & GINTSTS_RXFLVL) { 377847a1685fSDinh Nguyen /* 377947a1685fSDinh Nguyen * note, since GINTSTS_RxFLvl doubles as FIFO-not-empty, 37801f91b4ccSFelipe Balbi * we need to retry dwc2_hsotg_handle_rx if this is still 378147a1685fSDinh Nguyen * set. 378247a1685fSDinh Nguyen */ 378347a1685fSDinh Nguyen 37841f91b4ccSFelipe Balbi dwc2_hsotg_handle_rx(hsotg); 378547a1685fSDinh Nguyen } 378647a1685fSDinh Nguyen 378747a1685fSDinh Nguyen if (gintsts & GINTSTS_ERLYSUSP) { 378847a1685fSDinh Nguyen dev_dbg(hsotg->dev, "GINTSTS_ErlySusp\n"); 3789f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, GINTSTS_ERLYSUSP, GINTSTS); 379047a1685fSDinh Nguyen } 379147a1685fSDinh Nguyen 379247a1685fSDinh Nguyen /* 379347a1685fSDinh Nguyen * these next two seem to crop-up occasionally causing the core 379447a1685fSDinh Nguyen * to shutdown the USB transfer, so try clearing them and logging 379547a1685fSDinh Nguyen * the occurrence. 379647a1685fSDinh Nguyen */ 379747a1685fSDinh Nguyen 379847a1685fSDinh Nguyen if (gintsts & GINTSTS_GOUTNAKEFF) { 3799837e9f00SVardan Mikayelyan u8 idx; 3800837e9f00SVardan Mikayelyan u32 epctrl; 3801837e9f00SVardan Mikayelyan u32 gintmsk; 3802d8484552SRazmik Karapetyan u32 daintmsk; 3803837e9f00SVardan Mikayelyan struct dwc2_hsotg_ep *hs_ep; 380447a1685fSDinh Nguyen 3805f25c42b8SGevorg Sahakyan daintmsk = dwc2_readl(hsotg, DAINTMSK); 3806d8484552SRazmik Karapetyan daintmsk >>= DAINT_OUTEP_SHIFT; 3807837e9f00SVardan Mikayelyan /* Mask this interrupt */ 3808f25c42b8SGevorg Sahakyan gintmsk = dwc2_readl(hsotg, GINTMSK); 3809837e9f00SVardan Mikayelyan gintmsk &= ~GINTSTS_GOUTNAKEFF; 3810f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gintmsk, GINTMSK); 381147a1685fSDinh Nguyen 3812837e9f00SVardan Mikayelyan dev_dbg(hsotg->dev, "GOUTNakEff triggered\n"); 3813d5d5f079SArtur Petrosyan for (idx = 1; idx < hsotg->num_of_eps; idx++) { 3814837e9f00SVardan Mikayelyan hs_ep = hsotg->eps_out[idx]; 3815d8484552SRazmik Karapetyan /* Proceed only unmasked ISOC EPs */ 38166070636cSMinas Harutyunyan if (BIT(idx) & ~daintmsk) 3817d8484552SRazmik Karapetyan continue; 3818d8484552SRazmik Karapetyan 3819f25c42b8SGevorg Sahakyan epctrl = dwc2_readl(hsotg, DOEPCTL(idx)); 3820837e9f00SVardan Mikayelyan 38216070636cSMinas Harutyunyan //ISOC Ep's only 38226070636cSMinas Harutyunyan if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous) { 3823837e9f00SVardan Mikayelyan epctrl |= DXEPCTL_SNAK; 3824837e9f00SVardan Mikayelyan epctrl |= DXEPCTL_EPDIS; 3825f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, epctrl, DOEPCTL(idx)); 38266070636cSMinas Harutyunyan continue; 38276070636cSMinas Harutyunyan } 38286070636cSMinas Harutyunyan 38296070636cSMinas Harutyunyan //Non-ISOC EP's 38306070636cSMinas Harutyunyan if (hs_ep->halted) { 38316070636cSMinas Harutyunyan if (!(epctrl & DXEPCTL_EPENA)) 38326070636cSMinas Harutyunyan epctrl |= DXEPCTL_EPENA; 38336070636cSMinas Harutyunyan epctrl |= DXEPCTL_EPDIS; 38346070636cSMinas Harutyunyan epctrl |= DXEPCTL_STALL; 38356070636cSMinas Harutyunyan dwc2_writel(hsotg, epctrl, DOEPCTL(idx)); 3836837e9f00SVardan Mikayelyan } 3837837e9f00SVardan Mikayelyan } 3838837e9f00SVardan Mikayelyan 3839837e9f00SVardan Mikayelyan /* This interrupt bit is cleared in DXEPINT_EPDISBLD handler */ 384047a1685fSDinh Nguyen } 384147a1685fSDinh Nguyen 384247a1685fSDinh Nguyen if (gintsts & GINTSTS_GINNAKEFF) { 384347a1685fSDinh Nguyen dev_info(hsotg->dev, "GINNakEff triggered\n"); 384447a1685fSDinh Nguyen 3845f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_CGNPINNAK); 384647a1685fSDinh Nguyen 38471f91b4ccSFelipe Balbi dwc2_hsotg_dump(hsotg); 384847a1685fSDinh Nguyen } 384947a1685fSDinh Nguyen 3850381fc8f8SVardan Mikayelyan if (gintsts & GINTSTS_INCOMPL_SOIN) 3851381fc8f8SVardan Mikayelyan dwc2_gadget_handle_incomplete_isoc_in(hsotg); 3852ec1f9d9fSRoman Bacik 3853381fc8f8SVardan Mikayelyan if (gintsts & GINTSTS_INCOMPL_SOOUT) 3854381fc8f8SVardan Mikayelyan dwc2_gadget_handle_incomplete_isoc_out(hsotg); 3855ec1f9d9fSRoman Bacik 385647a1685fSDinh Nguyen /* 385747a1685fSDinh Nguyen * if we've had fifo events, we should try and go around the 385847a1685fSDinh Nguyen * loop again to see if there's any point in returning yet. 385947a1685fSDinh Nguyen */ 386047a1685fSDinh Nguyen 386147a1685fSDinh Nguyen if (gintsts & IRQ_RETRY_MASK && --retry_count > 0) 386247a1685fSDinh Nguyen goto irq_retry; 386347a1685fSDinh Nguyen 3864187c5298SGrigor Tovmasyan /* Check WKUP_ALERT interrupt*/ 3865187c5298SGrigor Tovmasyan if (hsotg->params.service_interval) 3866187c5298SGrigor Tovmasyan dwc2_gadget_wkup_alert_handler(hsotg); 3867187c5298SGrigor Tovmasyan 386847a1685fSDinh Nguyen spin_unlock(&hsotg->lock); 386947a1685fSDinh Nguyen 387047a1685fSDinh Nguyen return IRQ_HANDLED; 387147a1685fSDinh Nguyen } 387247a1685fSDinh Nguyen 3873a4f82771SVahram Aharonyan static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, 3874a4f82771SVahram Aharonyan struct dwc2_hsotg_ep *hs_ep) 3875a4f82771SVahram Aharonyan { 3876a4f82771SVahram Aharonyan u32 epctrl_reg; 3877a4f82771SVahram Aharonyan u32 epint_reg; 3878a4f82771SVahram Aharonyan 3879a4f82771SVahram Aharonyan epctrl_reg = hs_ep->dir_in ? DIEPCTL(hs_ep->index) : 3880a4f82771SVahram Aharonyan DOEPCTL(hs_ep->index); 3881a4f82771SVahram Aharonyan epint_reg = hs_ep->dir_in ? DIEPINT(hs_ep->index) : 3882a4f82771SVahram Aharonyan DOEPINT(hs_ep->index); 3883a4f82771SVahram Aharonyan 3884a4f82771SVahram Aharonyan dev_dbg(hsotg->dev, "%s: stopping transfer on %s\n", __func__, 3885a4f82771SVahram Aharonyan hs_ep->name); 3886a4f82771SVahram Aharonyan 3887a4f82771SVahram Aharonyan if (hs_ep->dir_in) { 3888a4f82771SVahram Aharonyan if (hsotg->dedicated_fifos || hs_ep->periodic) { 3889f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, epctrl_reg, DXEPCTL_SNAK); 3890a4f82771SVahram Aharonyan /* Wait for Nak effect */ 3891a4f82771SVahram Aharonyan if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, 3892a4f82771SVahram Aharonyan DXEPINT_INEPNAKEFF, 100)) 3893a4f82771SVahram Aharonyan dev_warn(hsotg->dev, 3894a4f82771SVahram Aharonyan "%s: timeout DIEPINT.NAKEFF\n", 3895a4f82771SVahram Aharonyan __func__); 3896a4f82771SVahram Aharonyan } else { 3897f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_SGNPINNAK); 3898a4f82771SVahram Aharonyan /* Wait for Nak effect */ 3899a4f82771SVahram Aharonyan if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, 3900a4f82771SVahram Aharonyan GINTSTS_GINNAKEFF, 100)) 3901a4f82771SVahram Aharonyan dev_warn(hsotg->dev, 3902a4f82771SVahram Aharonyan "%s: timeout GINTSTS.GINNAKEFF\n", 3903a4f82771SVahram Aharonyan __func__); 3904a4f82771SVahram Aharonyan } 3905a4f82771SVahram Aharonyan } else { 3906fecb3a17SMinas Harutyunyan /* Mask GINTSTS_GOUTNAKEFF interrupt */ 3907fecb3a17SMinas Harutyunyan dwc2_hsotg_disable_gsint(hsotg, GINTSTS_GOUTNAKEFF); 3908fecb3a17SMinas Harutyunyan 3909f25c42b8SGevorg Sahakyan if (!(dwc2_readl(hsotg, GINTSTS) & GINTSTS_GOUTNAKEFF)) 3910f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_SGOUTNAK); 3911a4f82771SVahram Aharonyan 3912fecb3a17SMinas Harutyunyan if (!using_dma(hsotg)) { 3913fecb3a17SMinas Harutyunyan /* Wait for GINTSTS_RXFLVL interrupt */ 3914fecb3a17SMinas Harutyunyan if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, 3915fecb3a17SMinas Harutyunyan GINTSTS_RXFLVL, 100)) { 3916fecb3a17SMinas Harutyunyan dev_warn(hsotg->dev, "%s: timeout GINTSTS.RXFLVL\n", 3917fecb3a17SMinas Harutyunyan __func__); 3918fecb3a17SMinas Harutyunyan } else { 3919fecb3a17SMinas Harutyunyan /* 3920fecb3a17SMinas Harutyunyan * Pop GLOBAL OUT NAK status packet from RxFIFO 3921fecb3a17SMinas Harutyunyan * to assert GOUTNAKEFF interrupt 3922fecb3a17SMinas Harutyunyan */ 3923fecb3a17SMinas Harutyunyan dwc2_readl(hsotg, GRXSTSP); 3924fecb3a17SMinas Harutyunyan } 3925fecb3a17SMinas Harutyunyan } 3926fecb3a17SMinas Harutyunyan 3927a4f82771SVahram Aharonyan /* Wait for global nak to take effect */ 3928a4f82771SVahram Aharonyan if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, 3929a4f82771SVahram Aharonyan GINTSTS_GOUTNAKEFF, 100)) 3930a4f82771SVahram Aharonyan dev_warn(hsotg->dev, "%s: timeout GINTSTS.GOUTNAKEFF\n", 3931a4f82771SVahram Aharonyan __func__); 3932a4f82771SVahram Aharonyan } 3933a4f82771SVahram Aharonyan 3934a4f82771SVahram Aharonyan /* Disable ep */ 3935f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK); 3936a4f82771SVahram Aharonyan 3937a4f82771SVahram Aharonyan /* Wait for ep to be disabled */ 3938a4f82771SVahram Aharonyan if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_EPDISBLD, 100)) 3939a4f82771SVahram Aharonyan dev_warn(hsotg->dev, 3940a4f82771SVahram Aharonyan "%s: timeout DOEPCTL.EPDisable\n", __func__); 3941a4f82771SVahram Aharonyan 3942a4f82771SVahram Aharonyan /* Clear EPDISBLD interrupt */ 3943f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, epint_reg, DXEPINT_EPDISBLD); 3944a4f82771SVahram Aharonyan 3945a4f82771SVahram Aharonyan if (hs_ep->dir_in) { 3946a4f82771SVahram Aharonyan unsigned short fifo_index; 3947a4f82771SVahram Aharonyan 3948a4f82771SVahram Aharonyan if (hsotg->dedicated_fifos || hs_ep->periodic) 3949a4f82771SVahram Aharonyan fifo_index = hs_ep->fifo_index; 3950a4f82771SVahram Aharonyan else 3951a4f82771SVahram Aharonyan fifo_index = 0; 3952a4f82771SVahram Aharonyan 3953a4f82771SVahram Aharonyan /* Flush TX FIFO */ 3954a4f82771SVahram Aharonyan dwc2_flush_tx_fifo(hsotg, fifo_index); 3955a4f82771SVahram Aharonyan 3956a4f82771SVahram Aharonyan /* Clear Global In NP NAK in Shared FIFO for non periodic ep */ 3957a4f82771SVahram Aharonyan if (!hsotg->dedicated_fifos && !hs_ep->periodic) 3958f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_CGNPINNAK); 3959a4f82771SVahram Aharonyan 3960a4f82771SVahram Aharonyan } else { 3961a4f82771SVahram Aharonyan /* Remove global NAKs */ 3962f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_CGOUTNAK); 3963a4f82771SVahram Aharonyan } 3964a4f82771SVahram Aharonyan } 3965a4f82771SVahram Aharonyan 396647a1685fSDinh Nguyen /** 39671f91b4ccSFelipe Balbi * dwc2_hsotg_ep_enable - enable the given endpoint 396847a1685fSDinh Nguyen * @ep: The USB endpint to configure 396947a1685fSDinh Nguyen * @desc: The USB endpoint descriptor to configure with. 397047a1685fSDinh Nguyen * 397147a1685fSDinh Nguyen * This is called from the USB gadget code's usb_ep_enable(). 397247a1685fSDinh Nguyen */ 39731f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_enable(struct usb_ep *ep, 397447a1685fSDinh Nguyen const struct usb_endpoint_descriptor *desc) 397547a1685fSDinh Nguyen { 39761f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = our_ep(ep); 3977941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = hs_ep->parent; 397847a1685fSDinh Nguyen unsigned long flags; 3979ca4c55adSMian Yousaf Kaukab unsigned int index = hs_ep->index; 398047a1685fSDinh Nguyen u32 epctrl_reg; 398147a1685fSDinh Nguyen u32 epctrl; 398247a1685fSDinh Nguyen u32 mps; 3983ee2c40deSVardan Mikayelyan u32 mc; 3984837e9f00SVardan Mikayelyan u32 mask; 3985ca4c55adSMian Yousaf Kaukab unsigned int dir_in; 3986ca4c55adSMian Yousaf Kaukab unsigned int i, val, size; 398747a1685fSDinh Nguyen int ret = 0; 3988729cac69SMinas Harutyunyan unsigned char ep_type; 398954f37f56SMinas Harutyunyan int desc_num; 399047a1685fSDinh Nguyen 399147a1685fSDinh Nguyen dev_dbg(hsotg->dev, 399247a1685fSDinh Nguyen "%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n", 399347a1685fSDinh Nguyen __func__, ep->name, desc->bEndpointAddress, desc->bmAttributes, 399447a1685fSDinh Nguyen desc->wMaxPacketSize, desc->bInterval); 399547a1685fSDinh Nguyen 399647a1685fSDinh Nguyen /* not to be called for EP0 */ 39978c3d6092SVahram Aharonyan if (index == 0) { 39988c3d6092SVahram Aharonyan dev_err(hsotg->dev, "%s: called for EP 0\n", __func__); 39998c3d6092SVahram Aharonyan return -EINVAL; 40008c3d6092SVahram Aharonyan } 400147a1685fSDinh Nguyen 400247a1685fSDinh Nguyen dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0; 400347a1685fSDinh Nguyen if (dir_in != hs_ep->dir_in) { 400447a1685fSDinh Nguyen dev_err(hsotg->dev, "%s: direction mismatch!\n", __func__); 400547a1685fSDinh Nguyen return -EINVAL; 400647a1685fSDinh Nguyen } 400747a1685fSDinh Nguyen 4008729cac69SMinas Harutyunyan ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; 400947a1685fSDinh Nguyen mps = usb_endpoint_maxp(desc); 4010ee2c40deSVardan Mikayelyan mc = usb_endpoint_maxp_mult(desc); 401147a1685fSDinh Nguyen 4012729cac69SMinas Harutyunyan /* ISOC IN in DDMA supported bInterval up to 10 */ 4013729cac69SMinas Harutyunyan if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC && 4014729cac69SMinas Harutyunyan dir_in && desc->bInterval > 10) { 4015729cac69SMinas Harutyunyan dev_err(hsotg->dev, 4016729cac69SMinas Harutyunyan "%s: ISOC IN, DDMA: bInterval>10 not supported!\n", __func__); 4017729cac69SMinas Harutyunyan return -EINVAL; 4018729cac69SMinas Harutyunyan } 4019729cac69SMinas Harutyunyan 4020729cac69SMinas Harutyunyan /* High bandwidth ISOC OUT in DDMA not supported */ 4021729cac69SMinas Harutyunyan if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC && 4022729cac69SMinas Harutyunyan !dir_in && mc > 1) { 4023729cac69SMinas Harutyunyan dev_err(hsotg->dev, 4024729cac69SMinas Harutyunyan "%s: ISOC OUT, DDMA: HB not supported!\n", __func__); 4025729cac69SMinas Harutyunyan return -EINVAL; 4026729cac69SMinas Harutyunyan } 4027729cac69SMinas Harutyunyan 40281f91b4ccSFelipe Balbi /* note, we handle this here instead of dwc2_hsotg_set_ep_maxpacket */ 402947a1685fSDinh Nguyen 403047a1685fSDinh Nguyen epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); 4031f25c42b8SGevorg Sahakyan epctrl = dwc2_readl(hsotg, epctrl_reg); 403247a1685fSDinh Nguyen 403347a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n", 403447a1685fSDinh Nguyen __func__, epctrl, epctrl_reg); 403547a1685fSDinh Nguyen 403654f37f56SMinas Harutyunyan if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC) 403754f37f56SMinas Harutyunyan desc_num = MAX_DMA_DESC_NUM_HS_ISOC; 403854f37f56SMinas Harutyunyan else 403954f37f56SMinas Harutyunyan desc_num = MAX_DMA_DESC_NUM_GENERIC; 404054f37f56SMinas Harutyunyan 40415f54c54bSVahram Aharonyan /* Allocate DMA descriptor chain for non-ctrl endpoints */ 40429383e084SVardan Mikayelyan if (using_desc_dma(hsotg) && !hs_ep->desc_list) { 40439383e084SVardan Mikayelyan hs_ep->desc_list = dmam_alloc_coherent(hsotg->dev, 404454f37f56SMinas Harutyunyan desc_num * sizeof(struct dwc2_dma_desc), 404586e881e7SMarek Szyprowski &hs_ep->desc_list_dma, GFP_ATOMIC); 40465f54c54bSVahram Aharonyan if (!hs_ep->desc_list) { 40475f54c54bSVahram Aharonyan ret = -ENOMEM; 40485f54c54bSVahram Aharonyan goto error2; 40495f54c54bSVahram Aharonyan } 40505f54c54bSVahram Aharonyan } 40515f54c54bSVahram Aharonyan 405247a1685fSDinh Nguyen spin_lock_irqsave(&hsotg->lock, flags); 405347a1685fSDinh Nguyen 405447a1685fSDinh Nguyen epctrl &= ~(DXEPCTL_EPTYPE_MASK | DXEPCTL_MPS_MASK); 405547a1685fSDinh Nguyen epctrl |= DXEPCTL_MPS(mps); 405647a1685fSDinh Nguyen 405747a1685fSDinh Nguyen /* 405847a1685fSDinh Nguyen * mark the endpoint as active, otherwise the core may ignore 405947a1685fSDinh Nguyen * transactions entirely for this endpoint 406047a1685fSDinh Nguyen */ 406147a1685fSDinh Nguyen epctrl |= DXEPCTL_USBACTEP; 406247a1685fSDinh Nguyen 406347a1685fSDinh Nguyen /* update the endpoint state */ 4064ee2c40deSVardan Mikayelyan dwc2_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps, mc, dir_in); 406547a1685fSDinh Nguyen 406647a1685fSDinh Nguyen /* default, set to non-periodic */ 406747a1685fSDinh Nguyen hs_ep->isochronous = 0; 406847a1685fSDinh Nguyen hs_ep->periodic = 0; 406947a1685fSDinh Nguyen hs_ep->halted = 0; 4070b833ce15SMinas Harutyunyan hs_ep->wedged = 0; 407147a1685fSDinh Nguyen hs_ep->interval = desc->bInterval; 407247a1685fSDinh Nguyen 4073729cac69SMinas Harutyunyan switch (ep_type) { 407447a1685fSDinh Nguyen case USB_ENDPOINT_XFER_ISOC: 407547a1685fSDinh Nguyen epctrl |= DXEPCTL_EPTYPE_ISO; 407647a1685fSDinh Nguyen epctrl |= DXEPCTL_SETEVENFR; 407747a1685fSDinh Nguyen hs_ep->isochronous = 1; 4078142bd33fSVardan Mikayelyan hs_ep->interval = 1 << (desc->bInterval - 1); 4079837e9f00SVardan Mikayelyan hs_ep->target_frame = TARGET_FRAME_INITIAL; 4080ab7d2192SVahram Aharonyan hs_ep->next_desc = 0; 4081729cac69SMinas Harutyunyan hs_ep->compl_desc = 0; 4082837e9f00SVardan Mikayelyan if (dir_in) { 408347a1685fSDinh Nguyen hs_ep->periodic = 1; 4084f25c42b8SGevorg Sahakyan mask = dwc2_readl(hsotg, DIEPMSK); 4085837e9f00SVardan Mikayelyan mask |= DIEPMSK_NAKMSK; 4086f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, mask, DIEPMSK); 4087837e9f00SVardan Mikayelyan } else { 4088f25c42b8SGevorg Sahakyan mask = dwc2_readl(hsotg, DOEPMSK); 4089837e9f00SVardan Mikayelyan mask |= DOEPMSK_OUTTKNEPDISMSK; 4090f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, mask, DOEPMSK); 4091837e9f00SVardan Mikayelyan } 409247a1685fSDinh Nguyen break; 409347a1685fSDinh Nguyen 409447a1685fSDinh Nguyen case USB_ENDPOINT_XFER_BULK: 409547a1685fSDinh Nguyen epctrl |= DXEPCTL_EPTYPE_BULK; 409647a1685fSDinh Nguyen break; 409747a1685fSDinh Nguyen 409847a1685fSDinh Nguyen case USB_ENDPOINT_XFER_INT: 4099b203d0a2SRobert Baldyga if (dir_in) 410047a1685fSDinh Nguyen hs_ep->periodic = 1; 410147a1685fSDinh Nguyen 4102142bd33fSVardan Mikayelyan if (hsotg->gadget.speed == USB_SPEED_HIGH) 4103142bd33fSVardan Mikayelyan hs_ep->interval = 1 << (desc->bInterval - 1); 4104142bd33fSVardan Mikayelyan 410547a1685fSDinh Nguyen epctrl |= DXEPCTL_EPTYPE_INTERRUPT; 410647a1685fSDinh Nguyen break; 410747a1685fSDinh Nguyen 410847a1685fSDinh Nguyen case USB_ENDPOINT_XFER_CONTROL: 410947a1685fSDinh Nguyen epctrl |= DXEPCTL_EPTYPE_CONTROL; 411047a1685fSDinh Nguyen break; 411147a1685fSDinh Nguyen } 411247a1685fSDinh Nguyen 411347a1685fSDinh Nguyen /* 411447a1685fSDinh Nguyen * if the hardware has dedicated fifos, we must give each IN EP 411547a1685fSDinh Nguyen * a unique tx-fifo even if it is non-periodic. 411647a1685fSDinh Nguyen */ 411721f3bb52SRobert Baldyga if (dir_in && hsotg->dedicated_fifos) { 4118644139f8SJohn Keeping unsigned fifo_count = dwc2_hsotg_tx_fifo_count(hsotg); 4119ca4c55adSMian Yousaf Kaukab u32 fifo_index = 0; 4120ca4c55adSMian Yousaf Kaukab u32 fifo_size = UINT_MAX; 41219da51974SJohn Youn 4122b203d0a2SRobert Baldyga size = hs_ep->ep.maxpacket * hs_ep->mc; 4123644139f8SJohn Keeping for (i = 1; i <= fifo_count; ++i) { 4124b203d0a2SRobert Baldyga if (hsotg->fifo_map & (1 << i)) 4125b203d0a2SRobert Baldyga continue; 4126f25c42b8SGevorg Sahakyan val = dwc2_readl(hsotg, DPTXFSIZN(i)); 4127b203d0a2SRobert Baldyga val = (val >> FIFOSIZE_DEPTH_SHIFT) * 4; 4128b203d0a2SRobert Baldyga if (val < size) 4129b203d0a2SRobert Baldyga continue; 4130ca4c55adSMian Yousaf Kaukab /* Search for smallest acceptable fifo */ 4131ca4c55adSMian Yousaf Kaukab if (val < fifo_size) { 4132ca4c55adSMian Yousaf Kaukab fifo_size = val; 4133ca4c55adSMian Yousaf Kaukab fifo_index = i; 4134b203d0a2SRobert Baldyga } 4135ca4c55adSMian Yousaf Kaukab } 4136ca4c55adSMian Yousaf Kaukab if (!fifo_index) { 41375f2196bdSMian Yousaf Kaukab dev_err(hsotg->dev, 41385f2196bdSMian Yousaf Kaukab "%s: No suitable fifo found\n", __func__); 4139b585a48bSSudip Mukherjee ret = -ENOMEM; 41405f54c54bSVahram Aharonyan goto error1; 4141b585a48bSSudip Mukherjee } 414297311c8fSMinas Harutyunyan epctrl &= ~(DXEPCTL_TXFNUM_LIMIT << DXEPCTL_TXFNUM_SHIFT); 4143ca4c55adSMian Yousaf Kaukab hsotg->fifo_map |= 1 << fifo_index; 4144ca4c55adSMian Yousaf Kaukab epctrl |= DXEPCTL_TXFNUM(fifo_index); 4145ca4c55adSMian Yousaf Kaukab hs_ep->fifo_index = fifo_index; 4146ca4c55adSMian Yousaf Kaukab hs_ep->fifo_size = fifo_size; 4147b203d0a2SRobert Baldyga } 414847a1685fSDinh Nguyen 414947a1685fSDinh Nguyen /* for non control endpoints, set PID to D0 */ 4150837e9f00SVardan Mikayelyan if (index && !hs_ep->isochronous) 415147a1685fSDinh Nguyen epctrl |= DXEPCTL_SETD0PID; 415247a1685fSDinh Nguyen 41535295322aSArtur Petrosyan /* WA for Full speed ISOC IN in DDMA mode. 41545295322aSArtur Petrosyan * By Clear NAK status of EP, core will send ZLP 41555295322aSArtur Petrosyan * to IN token and assert NAK interrupt relying 41565295322aSArtur Petrosyan * on TxFIFO status only 41575295322aSArtur Petrosyan */ 41585295322aSArtur Petrosyan 41595295322aSArtur Petrosyan if (hsotg->gadget.speed == USB_SPEED_FULL && 41605295322aSArtur Petrosyan hs_ep->isochronous && dir_in) { 41615295322aSArtur Petrosyan /* The WA applies only to core versions from 2.72a 41625295322aSArtur Petrosyan * to 4.00a (including both). Also for FS_IOT_1.00a 41635295322aSArtur Petrosyan * and HS_IOT_1.00a. 41645295322aSArtur Petrosyan */ 4165f25c42b8SGevorg Sahakyan u32 gsnpsid = dwc2_readl(hsotg, GSNPSID); 41665295322aSArtur Petrosyan 41675295322aSArtur Petrosyan if ((gsnpsid >= DWC2_CORE_REV_2_72a && 41685295322aSArtur Petrosyan gsnpsid <= DWC2_CORE_REV_4_00a) || 41695295322aSArtur Petrosyan gsnpsid == DWC2_FS_IOT_REV_1_00a || 41705295322aSArtur Petrosyan gsnpsid == DWC2_HS_IOT_REV_1_00a) 41715295322aSArtur Petrosyan epctrl |= DXEPCTL_CNAK; 41725295322aSArtur Petrosyan } 41735295322aSArtur Petrosyan 417447a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: write DxEPCTL=0x%08x\n", 417547a1685fSDinh Nguyen __func__, epctrl); 417647a1685fSDinh Nguyen 4177f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, epctrl, epctrl_reg); 417847a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x\n", 4179f25c42b8SGevorg Sahakyan __func__, dwc2_readl(hsotg, epctrl_reg)); 418047a1685fSDinh Nguyen 418147a1685fSDinh Nguyen /* enable the endpoint interrupt */ 41821f91b4ccSFelipe Balbi dwc2_hsotg_ctrl_epint(hsotg, index, dir_in, 1); 418347a1685fSDinh Nguyen 41845f54c54bSVahram Aharonyan error1: 418547a1685fSDinh Nguyen spin_unlock_irqrestore(&hsotg->lock, flags); 41865f54c54bSVahram Aharonyan 41875f54c54bSVahram Aharonyan error2: 41885f54c54bSVahram Aharonyan if (ret && using_desc_dma(hsotg) && hs_ep->desc_list) { 418954f37f56SMinas Harutyunyan dmam_free_coherent(hsotg->dev, desc_num * 41905f54c54bSVahram Aharonyan sizeof(struct dwc2_dma_desc), 41915f54c54bSVahram Aharonyan hs_ep->desc_list, hs_ep->desc_list_dma); 41925f54c54bSVahram Aharonyan hs_ep->desc_list = NULL; 41935f54c54bSVahram Aharonyan } 41945f54c54bSVahram Aharonyan 419547a1685fSDinh Nguyen return ret; 419647a1685fSDinh Nguyen } 419747a1685fSDinh Nguyen 419847a1685fSDinh Nguyen /** 41991f91b4ccSFelipe Balbi * dwc2_hsotg_ep_disable - disable given endpoint 420047a1685fSDinh Nguyen * @ep: The endpoint to disable. 420147a1685fSDinh Nguyen */ 42021f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_disable(struct usb_ep *ep) 420347a1685fSDinh Nguyen { 42041f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = our_ep(ep); 4205941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = hs_ep->parent; 420647a1685fSDinh Nguyen int dir_in = hs_ep->dir_in; 420747a1685fSDinh Nguyen int index = hs_ep->index; 420847a1685fSDinh Nguyen u32 epctrl_reg; 420947a1685fSDinh Nguyen u32 ctrl; 421047a1685fSDinh Nguyen 42111e011293SMarek Szyprowski dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep); 421247a1685fSDinh Nguyen 4213c6f5c050SMian Yousaf Kaukab if (ep == &hsotg->eps_out[0]->ep) { 421447a1685fSDinh Nguyen dev_err(hsotg->dev, "%s: called for ep0\n", __func__); 421547a1685fSDinh Nguyen return -EINVAL; 421647a1685fSDinh Nguyen } 421747a1685fSDinh Nguyen 42189b481092SJohn Stultz if (hsotg->op_state != OTG_STATE_B_PERIPHERAL) { 42199b481092SJohn Stultz dev_err(hsotg->dev, "%s: called in host mode?\n", __func__); 42209b481092SJohn Stultz return -EINVAL; 42219b481092SJohn Stultz } 42229b481092SJohn Stultz 422347a1685fSDinh Nguyen epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); 422447a1685fSDinh Nguyen 4225f25c42b8SGevorg Sahakyan ctrl = dwc2_readl(hsotg, epctrl_reg); 4226a4f82771SVahram Aharonyan 4227a4f82771SVahram Aharonyan if (ctrl & DXEPCTL_EPENA) 4228a4f82771SVahram Aharonyan dwc2_hsotg_ep_stop_xfr(hsotg, hs_ep); 4229a4f82771SVahram Aharonyan 423047a1685fSDinh Nguyen ctrl &= ~DXEPCTL_EPENA; 423147a1685fSDinh Nguyen ctrl &= ~DXEPCTL_USBACTEP; 423247a1685fSDinh Nguyen ctrl |= DXEPCTL_SNAK; 423347a1685fSDinh Nguyen 423447a1685fSDinh Nguyen dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); 4235f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, ctrl, epctrl_reg); 423647a1685fSDinh Nguyen 423747a1685fSDinh Nguyen /* disable endpoint interrupts */ 42381f91b4ccSFelipe Balbi dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0); 423947a1685fSDinh Nguyen 42401141ea01SMian Yousaf Kaukab /* terminate all requests with shutdown */ 42411141ea01SMian Yousaf Kaukab kill_all_requests(hsotg, hs_ep, -ESHUTDOWN); 42421141ea01SMian Yousaf Kaukab 42431c07b20eSRobert Baldyga hsotg->fifo_map &= ~(1 << hs_ep->fifo_index); 42441c07b20eSRobert Baldyga hs_ep->fifo_index = 0; 42451c07b20eSRobert Baldyga hs_ep->fifo_size = 0; 42461c07b20eSRobert Baldyga 424747a1685fSDinh Nguyen return 0; 424847a1685fSDinh Nguyen } 424947a1685fSDinh Nguyen 42504fe4f9feSMinas Harutyunyan static int dwc2_hsotg_ep_disable_lock(struct usb_ep *ep) 42514fe4f9feSMinas Harutyunyan { 42524fe4f9feSMinas Harutyunyan struct dwc2_hsotg_ep *hs_ep = our_ep(ep); 42534fe4f9feSMinas Harutyunyan struct dwc2_hsotg *hsotg = hs_ep->parent; 42544fe4f9feSMinas Harutyunyan unsigned long flags; 42554fe4f9feSMinas Harutyunyan int ret; 42564fe4f9feSMinas Harutyunyan 42574fe4f9feSMinas Harutyunyan spin_lock_irqsave(&hsotg->lock, flags); 42584fe4f9feSMinas Harutyunyan ret = dwc2_hsotg_ep_disable(ep); 42594fe4f9feSMinas Harutyunyan spin_unlock_irqrestore(&hsotg->lock, flags); 42604fe4f9feSMinas Harutyunyan return ret; 42614fe4f9feSMinas Harutyunyan } 42624fe4f9feSMinas Harutyunyan 426347a1685fSDinh Nguyen /** 426447a1685fSDinh Nguyen * on_list - check request is on the given endpoint 426547a1685fSDinh Nguyen * @ep: The endpoint to check. 426647a1685fSDinh Nguyen * @test: The request to test if it is on the endpoint. 426747a1685fSDinh Nguyen */ 42681f91b4ccSFelipe Balbi static bool on_list(struct dwc2_hsotg_ep *ep, struct dwc2_hsotg_req *test) 426947a1685fSDinh Nguyen { 42701f91b4ccSFelipe Balbi struct dwc2_hsotg_req *req, *treq; 427147a1685fSDinh Nguyen 427247a1685fSDinh Nguyen list_for_each_entry_safe(req, treq, &ep->queue, queue) { 427347a1685fSDinh Nguyen if (req == test) 427447a1685fSDinh Nguyen return true; 427547a1685fSDinh Nguyen } 427647a1685fSDinh Nguyen 427747a1685fSDinh Nguyen return false; 427847a1685fSDinh Nguyen } 427947a1685fSDinh Nguyen 428047a1685fSDinh Nguyen /** 42811f91b4ccSFelipe Balbi * dwc2_hsotg_ep_dequeue - dequeue given endpoint 428247a1685fSDinh Nguyen * @ep: The endpoint to dequeue. 428347a1685fSDinh Nguyen * @req: The request to be removed from a queue. 428447a1685fSDinh Nguyen */ 42851f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) 428647a1685fSDinh Nguyen { 42871f91b4ccSFelipe Balbi struct dwc2_hsotg_req *hs_req = our_req(req); 42881f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = our_ep(ep); 4289941fcce4SDinh Nguyen struct dwc2_hsotg *hs = hs_ep->parent; 429047a1685fSDinh Nguyen unsigned long flags; 429147a1685fSDinh Nguyen 42921e011293SMarek Szyprowski dev_dbg(hs->dev, "ep_dequeue(%p,%p)\n", ep, req); 429347a1685fSDinh Nguyen 429447a1685fSDinh Nguyen spin_lock_irqsave(&hs->lock, flags); 429547a1685fSDinh Nguyen 429647a1685fSDinh Nguyen if (!on_list(hs_ep, hs_req)) { 429747a1685fSDinh Nguyen spin_unlock_irqrestore(&hs->lock, flags); 429847a1685fSDinh Nguyen return -EINVAL; 429947a1685fSDinh Nguyen } 430047a1685fSDinh Nguyen 4301c524dd5fSMian Yousaf Kaukab /* Dequeue already started request */ 4302c524dd5fSMian Yousaf Kaukab if (req == &hs_ep->req->req) 4303c524dd5fSMian Yousaf Kaukab dwc2_hsotg_ep_stop_xfr(hs, hs_ep); 4304c524dd5fSMian Yousaf Kaukab 43051f91b4ccSFelipe Balbi dwc2_hsotg_complete_request(hs, hs_ep, hs_req, -ECONNRESET); 430647a1685fSDinh Nguyen spin_unlock_irqrestore(&hs->lock, flags); 430747a1685fSDinh Nguyen 430847a1685fSDinh Nguyen return 0; 430947a1685fSDinh Nguyen } 431047a1685fSDinh Nguyen 431147a1685fSDinh Nguyen /** 4312b833ce15SMinas Harutyunyan * dwc2_gadget_ep_set_wedge - set wedge on a given endpoint 4313b833ce15SMinas Harutyunyan * @ep: The endpoint to be wedged. 4314b833ce15SMinas Harutyunyan * 4315b833ce15SMinas Harutyunyan */ 4316b833ce15SMinas Harutyunyan static int dwc2_gadget_ep_set_wedge(struct usb_ep *ep) 4317b833ce15SMinas Harutyunyan { 4318b833ce15SMinas Harutyunyan struct dwc2_hsotg_ep *hs_ep = our_ep(ep); 4319b833ce15SMinas Harutyunyan struct dwc2_hsotg *hs = hs_ep->parent; 4320b833ce15SMinas Harutyunyan 4321b833ce15SMinas Harutyunyan unsigned long flags; 4322b833ce15SMinas Harutyunyan int ret; 4323b833ce15SMinas Harutyunyan 4324b833ce15SMinas Harutyunyan spin_lock_irqsave(&hs->lock, flags); 4325b833ce15SMinas Harutyunyan hs_ep->wedged = 1; 4326b833ce15SMinas Harutyunyan ret = dwc2_hsotg_ep_sethalt(ep, 1, false); 4327b833ce15SMinas Harutyunyan spin_unlock_irqrestore(&hs->lock, flags); 4328b833ce15SMinas Harutyunyan 4329b833ce15SMinas Harutyunyan return ret; 4330b833ce15SMinas Harutyunyan } 4331b833ce15SMinas Harutyunyan 4332b833ce15SMinas Harutyunyan /** 43331f91b4ccSFelipe Balbi * dwc2_hsotg_ep_sethalt - set halt on a given endpoint 433447a1685fSDinh Nguyen * @ep: The endpoint to set halt. 433547a1685fSDinh Nguyen * @value: Set or unset the halt. 433651da43b5SVahram Aharonyan * @now: If true, stall the endpoint now. Otherwise return -EAGAIN if 433751da43b5SVahram Aharonyan * the endpoint is busy processing requests. 433851da43b5SVahram Aharonyan * 433951da43b5SVahram Aharonyan * We need to stall the endpoint immediately if request comes from set_feature 434051da43b5SVahram Aharonyan * protocol command handler. 434147a1685fSDinh Nguyen */ 434251da43b5SVahram Aharonyan static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now) 434347a1685fSDinh Nguyen { 43441f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = our_ep(ep); 4345941fcce4SDinh Nguyen struct dwc2_hsotg *hs = hs_ep->parent; 434647a1685fSDinh Nguyen int index = hs_ep->index; 434747a1685fSDinh Nguyen u32 epreg; 434847a1685fSDinh Nguyen u32 epctl; 434947a1685fSDinh Nguyen u32 xfertype; 435047a1685fSDinh Nguyen 435147a1685fSDinh Nguyen dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value); 435247a1685fSDinh Nguyen 435347a1685fSDinh Nguyen if (index == 0) { 435447a1685fSDinh Nguyen if (value) 43551f91b4ccSFelipe Balbi dwc2_hsotg_stall_ep0(hs); 435647a1685fSDinh Nguyen else 435747a1685fSDinh Nguyen dev_warn(hs->dev, 435847a1685fSDinh Nguyen "%s: can't clear halt on ep0\n", __func__); 435947a1685fSDinh Nguyen return 0; 436047a1685fSDinh Nguyen } 436147a1685fSDinh Nguyen 436215186f10SVahram Aharonyan if (hs_ep->isochronous) { 436315186f10SVahram Aharonyan dev_err(hs->dev, "%s is Isochronous Endpoint\n", ep->name); 436415186f10SVahram Aharonyan return -EINVAL; 436515186f10SVahram Aharonyan } 436615186f10SVahram Aharonyan 436751da43b5SVahram Aharonyan if (!now && value && !list_empty(&hs_ep->queue)) { 436851da43b5SVahram Aharonyan dev_dbg(hs->dev, "%s request is pending, cannot halt\n", 436951da43b5SVahram Aharonyan ep->name); 437051da43b5SVahram Aharonyan return -EAGAIN; 437151da43b5SVahram Aharonyan } 437251da43b5SVahram Aharonyan 4373c6f5c050SMian Yousaf Kaukab if (hs_ep->dir_in) { 437447a1685fSDinh Nguyen epreg = DIEPCTL(index); 4375f25c42b8SGevorg Sahakyan epctl = dwc2_readl(hs, epreg); 437647a1685fSDinh Nguyen 437747a1685fSDinh Nguyen if (value) { 43785a350d53SFelipe Balbi epctl |= DXEPCTL_STALL | DXEPCTL_SNAK; 437947a1685fSDinh Nguyen if (epctl & DXEPCTL_EPENA) 438047a1685fSDinh Nguyen epctl |= DXEPCTL_EPDIS; 438147a1685fSDinh Nguyen } else { 438247a1685fSDinh Nguyen epctl &= ~DXEPCTL_STALL; 4383b833ce15SMinas Harutyunyan hs_ep->wedged = 0; 438447a1685fSDinh Nguyen xfertype = epctl & DXEPCTL_EPTYPE_MASK; 438547a1685fSDinh Nguyen if (xfertype == DXEPCTL_EPTYPE_BULK || 438647a1685fSDinh Nguyen xfertype == DXEPCTL_EPTYPE_INTERRUPT) 438747a1685fSDinh Nguyen epctl |= DXEPCTL_SETD0PID; 438847a1685fSDinh Nguyen } 4389f25c42b8SGevorg Sahakyan dwc2_writel(hs, epctl, epreg); 4390c6f5c050SMian Yousaf Kaukab } else { 439147a1685fSDinh Nguyen epreg = DOEPCTL(index); 4392f25c42b8SGevorg Sahakyan epctl = dwc2_readl(hs, epreg); 439347a1685fSDinh Nguyen 439434c0887fSJohn Youn if (value) { 4395fecb3a17SMinas Harutyunyan /* Unmask GOUTNAKEFF interrupt */ 4396fecb3a17SMinas Harutyunyan dwc2_hsotg_en_gsint(hs, GINTSTS_GOUTNAKEFF); 4397fecb3a17SMinas Harutyunyan 43986070636cSMinas Harutyunyan if (!(dwc2_readl(hs, GINTSTS) & GINTSTS_GOUTNAKEFF)) 43996070636cSMinas Harutyunyan dwc2_set_bit(hs, DCTL, DCTL_SGOUTNAK); 44006070636cSMinas Harutyunyan // STALL bit will be set in GOUTNAKEFF interrupt handler 440134c0887fSJohn Youn } else { 440247a1685fSDinh Nguyen epctl &= ~DXEPCTL_STALL; 4403b833ce15SMinas Harutyunyan hs_ep->wedged = 0; 440447a1685fSDinh Nguyen xfertype = epctl & DXEPCTL_EPTYPE_MASK; 440547a1685fSDinh Nguyen if (xfertype == DXEPCTL_EPTYPE_BULK || 440647a1685fSDinh Nguyen xfertype == DXEPCTL_EPTYPE_INTERRUPT) 440747a1685fSDinh Nguyen epctl |= DXEPCTL_SETD0PID; 4408f25c42b8SGevorg Sahakyan dwc2_writel(hs, epctl, epreg); 4409c6f5c050SMian Yousaf Kaukab } 44106070636cSMinas Harutyunyan } 441147a1685fSDinh Nguyen 441247a1685fSDinh Nguyen hs_ep->halted = value; 441347a1685fSDinh Nguyen return 0; 441447a1685fSDinh Nguyen } 441547a1685fSDinh Nguyen 441647a1685fSDinh Nguyen /** 44171f91b4ccSFelipe Balbi * dwc2_hsotg_ep_sethalt_lock - set halt on a given endpoint with lock held 441847a1685fSDinh Nguyen * @ep: The endpoint to set halt. 441947a1685fSDinh Nguyen * @value: Set or unset the halt. 442047a1685fSDinh Nguyen */ 44211f91b4ccSFelipe Balbi static int dwc2_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value) 442247a1685fSDinh Nguyen { 44231f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep = our_ep(ep); 4424941fcce4SDinh Nguyen struct dwc2_hsotg *hs = hs_ep->parent; 44258879904bSJohan Hovold unsigned long flags; 44268879904bSJohan Hovold int ret; 442747a1685fSDinh Nguyen 442847a1685fSDinh Nguyen spin_lock_irqsave(&hs->lock, flags); 442951da43b5SVahram Aharonyan ret = dwc2_hsotg_ep_sethalt(ep, value, false); 443047a1685fSDinh Nguyen spin_unlock_irqrestore(&hs->lock, flags); 443147a1685fSDinh Nguyen 443247a1685fSDinh Nguyen return ret; 443347a1685fSDinh Nguyen } 443447a1685fSDinh Nguyen 4435ebce561aSBhumika Goyal static const struct usb_ep_ops dwc2_hsotg_ep_ops = { 44361f91b4ccSFelipe Balbi .enable = dwc2_hsotg_ep_enable, 44374fe4f9feSMinas Harutyunyan .disable = dwc2_hsotg_ep_disable_lock, 44381f91b4ccSFelipe Balbi .alloc_request = dwc2_hsotg_ep_alloc_request, 44391f91b4ccSFelipe Balbi .free_request = dwc2_hsotg_ep_free_request, 44401f91b4ccSFelipe Balbi .queue = dwc2_hsotg_ep_queue_lock, 44411f91b4ccSFelipe Balbi .dequeue = dwc2_hsotg_ep_dequeue, 44421f91b4ccSFelipe Balbi .set_halt = dwc2_hsotg_ep_sethalt_lock, 4443b833ce15SMinas Harutyunyan .set_wedge = dwc2_gadget_ep_set_wedge, 444447a1685fSDinh Nguyen /* note, don't believe we have any call for the fifo routines */ 444547a1685fSDinh Nguyen }; 444647a1685fSDinh Nguyen 444747a1685fSDinh Nguyen /** 44489da51974SJohn Youn * dwc2_hsotg_init - initialize the usb core 444947a1685fSDinh Nguyen * @hsotg: The driver state 445047a1685fSDinh Nguyen */ 44511f91b4ccSFelipe Balbi static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg) 445247a1685fSDinh Nguyen { 445347a1685fSDinh Nguyen /* unmask subset of endpoint interrupts */ 445447a1685fSDinh Nguyen 4455f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK | 445647a1685fSDinh Nguyen DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK, 4457f25c42b8SGevorg Sahakyan DIEPMSK); 445847a1685fSDinh Nguyen 4459f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, DOEPMSK_SETUPMSK | DOEPMSK_AHBERRMSK | 446047a1685fSDinh Nguyen DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK, 4461f25c42b8SGevorg Sahakyan DOEPMSK); 446247a1685fSDinh Nguyen 4463f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, 0, DAINTMSK); 446447a1685fSDinh Nguyen 446547a1685fSDinh Nguyen /* Be in disconnected state until gadget is registered */ 4466f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON); 446747a1685fSDinh Nguyen 446847a1685fSDinh Nguyen /* setup fifos */ 446947a1685fSDinh Nguyen 447047a1685fSDinh Nguyen dev_dbg(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n", 4471f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, GRXFSIZ), 4472f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, GNPTXFSIZ)); 447347a1685fSDinh Nguyen 44741f91b4ccSFelipe Balbi dwc2_hsotg_init_fifo(hsotg); 447547a1685fSDinh Nguyen 4476f5090044SGregory Herrero if (using_dma(hsotg)) 4477f25c42b8SGevorg Sahakyan dwc2_set_bit(hsotg, GAHBCFG, GAHBCFG_DMA_EN); 447847a1685fSDinh Nguyen } 447947a1685fSDinh Nguyen 448047a1685fSDinh Nguyen /** 44811f91b4ccSFelipe Balbi * dwc2_hsotg_udc_start - prepare the udc for work 448247a1685fSDinh Nguyen * @gadget: The usb gadget state 448347a1685fSDinh Nguyen * @driver: The usb gadget driver 448447a1685fSDinh Nguyen * 448547a1685fSDinh Nguyen * Perform initialization to prepare udc device and driver 448647a1685fSDinh Nguyen * to work. 448747a1685fSDinh Nguyen */ 44881f91b4ccSFelipe Balbi static int dwc2_hsotg_udc_start(struct usb_gadget *gadget, 448947a1685fSDinh Nguyen struct usb_gadget_driver *driver) 449047a1685fSDinh Nguyen { 4491941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = to_hsotg(gadget); 44925b9451f8SMarek Szyprowski unsigned long flags; 449347a1685fSDinh Nguyen int ret; 449447a1685fSDinh Nguyen 449547a1685fSDinh Nguyen if (!hsotg) { 449647a1685fSDinh Nguyen pr_err("%s: called with no device\n", __func__); 449747a1685fSDinh Nguyen return -ENODEV; 449847a1685fSDinh Nguyen } 449947a1685fSDinh Nguyen 450047a1685fSDinh Nguyen if (!driver) { 450147a1685fSDinh Nguyen dev_err(hsotg->dev, "%s: no driver\n", __func__); 450247a1685fSDinh Nguyen return -EINVAL; 450347a1685fSDinh Nguyen } 450447a1685fSDinh Nguyen 450547a1685fSDinh Nguyen if (driver->max_speed < USB_SPEED_FULL) 450647a1685fSDinh Nguyen dev_err(hsotg->dev, "%s: bad speed\n", __func__); 450747a1685fSDinh Nguyen 450847a1685fSDinh Nguyen if (!driver->setup) { 450947a1685fSDinh Nguyen dev_err(hsotg->dev, "%s: missing entry points\n", __func__); 451047a1685fSDinh Nguyen return -EINVAL; 451147a1685fSDinh Nguyen } 451247a1685fSDinh Nguyen 451347a1685fSDinh Nguyen WARN_ON(hsotg->driver); 451447a1685fSDinh Nguyen 451547a1685fSDinh Nguyen driver->driver.bus = NULL; 451647a1685fSDinh Nguyen hsotg->driver = driver; 451747a1685fSDinh Nguyen hsotg->gadget.dev.of_node = hsotg->dev->of_node; 451847a1685fSDinh Nguyen hsotg->gadget.speed = USB_SPEED_UNKNOWN; 451947a1685fSDinh Nguyen 452009a75e85SMarek Szyprowski if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) { 452109a75e85SMarek Szyprowski ret = dwc2_lowlevel_hw_enable(hsotg); 452209a75e85SMarek Szyprowski if (ret) 452347a1685fSDinh Nguyen goto err; 452447a1685fSDinh Nguyen } 452547a1685fSDinh Nguyen 4526f6c01592SGregory Herrero if (!IS_ERR_OR_NULL(hsotg->uphy)) 4527f6c01592SGregory Herrero otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget); 4528c816c47fSMarek Szyprowski 45295b9451f8SMarek Szyprowski spin_lock_irqsave(&hsotg->lock, flags); 4530d0f0ac56SJohn Youn if (dwc2_hw_is_device(hsotg)) { 45311f91b4ccSFelipe Balbi dwc2_hsotg_init(hsotg); 45321f91b4ccSFelipe Balbi dwc2_hsotg_core_init_disconnected(hsotg, false); 4533d0f0ac56SJohn Youn } 4534d0f0ac56SJohn Youn 4535dc6e69e6SMarek Szyprowski hsotg->enabled = 0; 45365b9451f8SMarek Szyprowski spin_unlock_irqrestore(&hsotg->lock, flags); 45375b9451f8SMarek Szyprowski 453810209abeSAndrzej Pietrasiewicz gadget->sg_supported = using_desc_dma(hsotg); 453947a1685fSDinh Nguyen dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name); 45405b9451f8SMarek Szyprowski 454147a1685fSDinh Nguyen return 0; 454247a1685fSDinh Nguyen 454347a1685fSDinh Nguyen err: 454447a1685fSDinh Nguyen hsotg->driver = NULL; 454547a1685fSDinh Nguyen return ret; 454647a1685fSDinh Nguyen } 454747a1685fSDinh Nguyen 454847a1685fSDinh Nguyen /** 45491f91b4ccSFelipe Balbi * dwc2_hsotg_udc_stop - stop the udc 455047a1685fSDinh Nguyen * @gadget: The usb gadget state 455147a1685fSDinh Nguyen * 455247a1685fSDinh Nguyen * Stop udc hw block and stay tunned for future transmissions 455347a1685fSDinh Nguyen */ 45541f91b4ccSFelipe Balbi static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget) 455547a1685fSDinh Nguyen { 4556941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = to_hsotg(gadget); 45578879904bSJohan Hovold unsigned long flags; 455847a1685fSDinh Nguyen int ep; 455947a1685fSDinh Nguyen 456047a1685fSDinh Nguyen if (!hsotg) 456147a1685fSDinh Nguyen return -ENODEV; 456247a1685fSDinh Nguyen 456347a1685fSDinh Nguyen /* all endpoints should be shutdown */ 4564c6f5c050SMian Yousaf Kaukab for (ep = 1; ep < hsotg->num_of_eps; ep++) { 4565c6f5c050SMian Yousaf Kaukab if (hsotg->eps_in[ep]) 45664fe4f9feSMinas Harutyunyan dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep); 4567c6f5c050SMian Yousaf Kaukab if (hsotg->eps_out[ep]) 45684fe4f9feSMinas Harutyunyan dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep); 4569c6f5c050SMian Yousaf Kaukab } 457047a1685fSDinh Nguyen 457147a1685fSDinh Nguyen spin_lock_irqsave(&hsotg->lock, flags); 457247a1685fSDinh Nguyen 457347a1685fSDinh Nguyen hsotg->driver = NULL; 457447a1685fSDinh Nguyen hsotg->gadget.speed = USB_SPEED_UNKNOWN; 4575dc6e69e6SMarek Szyprowski hsotg->enabled = 0; 457647a1685fSDinh Nguyen 457747a1685fSDinh Nguyen spin_unlock_irqrestore(&hsotg->lock, flags); 457847a1685fSDinh Nguyen 4579f6c01592SGregory Herrero if (!IS_ERR_OR_NULL(hsotg->uphy)) 4580f6c01592SGregory Herrero otg_set_peripheral(hsotg->uphy->otg, NULL); 4581c816c47fSMarek Szyprowski 458209a75e85SMarek Szyprowski if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) 458309a75e85SMarek Szyprowski dwc2_lowlevel_hw_disable(hsotg); 458447a1685fSDinh Nguyen 458547a1685fSDinh Nguyen return 0; 458647a1685fSDinh Nguyen } 458747a1685fSDinh Nguyen 458847a1685fSDinh Nguyen /** 45891f91b4ccSFelipe Balbi * dwc2_hsotg_gadget_getframe - read the frame number 459047a1685fSDinh Nguyen * @gadget: The usb gadget state 459147a1685fSDinh Nguyen * 459247a1685fSDinh Nguyen * Read the {micro} frame number 459347a1685fSDinh Nguyen */ 45941f91b4ccSFelipe Balbi static int dwc2_hsotg_gadget_getframe(struct usb_gadget *gadget) 459547a1685fSDinh Nguyen { 45961f91b4ccSFelipe Balbi return dwc2_hsotg_read_frameno(to_hsotg(gadget)); 459747a1685fSDinh Nguyen } 459847a1685fSDinh Nguyen 459947a1685fSDinh Nguyen /** 46001a0808cbSJohn Keeping * dwc2_hsotg_set_selfpowered - set if device is self/bus powered 46011a0808cbSJohn Keeping * @gadget: The usb gadget state 46021a0808cbSJohn Keeping * @is_selfpowered: Whether the device is self-powered 46031a0808cbSJohn Keeping * 46041a0808cbSJohn Keeping * Set if the device is self or bus powered. 46051a0808cbSJohn Keeping */ 46061a0808cbSJohn Keeping static int dwc2_hsotg_set_selfpowered(struct usb_gadget *gadget, 46071a0808cbSJohn Keeping int is_selfpowered) 46081a0808cbSJohn Keeping { 46091a0808cbSJohn Keeping struct dwc2_hsotg *hsotg = to_hsotg(gadget); 46101a0808cbSJohn Keeping unsigned long flags; 46111a0808cbSJohn Keeping 46121a0808cbSJohn Keeping spin_lock_irqsave(&hsotg->lock, flags); 46131a0808cbSJohn Keeping gadget->is_selfpowered = !!is_selfpowered; 46141a0808cbSJohn Keeping spin_unlock_irqrestore(&hsotg->lock, flags); 46151a0808cbSJohn Keeping 46161a0808cbSJohn Keeping return 0; 46171a0808cbSJohn Keeping } 46181a0808cbSJohn Keeping 46191a0808cbSJohn Keeping /** 46201f91b4ccSFelipe Balbi * dwc2_hsotg_pullup - connect/disconnect the USB PHY 462147a1685fSDinh Nguyen * @gadget: The usb gadget state 462247a1685fSDinh Nguyen * @is_on: Current state of the USB PHY 462347a1685fSDinh Nguyen * 462447a1685fSDinh Nguyen * Connect/Disconnect the USB PHY pullup 462547a1685fSDinh Nguyen */ 46261f91b4ccSFelipe Balbi static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on) 462747a1685fSDinh Nguyen { 4628941fcce4SDinh Nguyen struct dwc2_hsotg *hsotg = to_hsotg(gadget); 46298879904bSJohan Hovold unsigned long flags; 463047a1685fSDinh Nguyen 463177ba9119SGregory Herrero dev_dbg(hsotg->dev, "%s: is_on: %d op_state: %d\n", __func__, is_on, 463277ba9119SGregory Herrero hsotg->op_state); 463377ba9119SGregory Herrero 463477ba9119SGregory Herrero /* Don't modify pullup state while in host mode */ 463577ba9119SGregory Herrero if (hsotg->op_state != OTG_STATE_B_PERIPHERAL) { 463677ba9119SGregory Herrero hsotg->enabled = is_on; 463777ba9119SGregory Herrero return 0; 463877ba9119SGregory Herrero } 463947a1685fSDinh Nguyen 464047a1685fSDinh Nguyen spin_lock_irqsave(&hsotg->lock, flags); 464147a1685fSDinh Nguyen if (is_on) { 4642dc6e69e6SMarek Szyprowski hsotg->enabled = 1; 46431f91b4ccSFelipe Balbi dwc2_hsotg_core_init_disconnected(hsotg, false); 464466e77a24SRazmik Karapetyan /* Enable ACG feature in device mode,if supported */ 464566e77a24SRazmik Karapetyan dwc2_enable_acg(hsotg); 46461f91b4ccSFelipe Balbi dwc2_hsotg_core_connect(hsotg); 464747a1685fSDinh Nguyen } else { 46481f91b4ccSFelipe Balbi dwc2_hsotg_core_disconnect(hsotg); 46491f91b4ccSFelipe Balbi dwc2_hsotg_disconnect(hsotg); 4650dc6e69e6SMarek Szyprowski hsotg->enabled = 0; 465147a1685fSDinh Nguyen } 465247a1685fSDinh Nguyen 465347a1685fSDinh Nguyen hsotg->gadget.speed = USB_SPEED_UNKNOWN; 465447a1685fSDinh Nguyen spin_unlock_irqrestore(&hsotg->lock, flags); 465547a1685fSDinh Nguyen 465647a1685fSDinh Nguyen return 0; 465747a1685fSDinh Nguyen } 465847a1685fSDinh Nguyen 46591f91b4ccSFelipe Balbi static int dwc2_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) 466083d98223SGregory Herrero { 466183d98223SGregory Herrero struct dwc2_hsotg *hsotg = to_hsotg(gadget); 466283d98223SGregory Herrero unsigned long flags; 466383d98223SGregory Herrero 466483d98223SGregory Herrero dev_dbg(hsotg->dev, "%s: is_active: %d\n", __func__, is_active); 466583d98223SGregory Herrero spin_lock_irqsave(&hsotg->lock, flags); 466683d98223SGregory Herrero 466718b2b37cSGregory Herrero /* 4668c9c394abSArtur Petrosyan * If controller is in partial power down state, it must exit from 4669c9c394abSArtur Petrosyan * that state before being initialized / de-initialized 467018b2b37cSGregory Herrero */ 4671c9c394abSArtur Petrosyan if (hsotg->lx_state == DWC2_L2 && hsotg->in_ppd) 4672c9c394abSArtur Petrosyan /* 4673c9c394abSArtur Petrosyan * No need to check the return value as 4674c9c394abSArtur Petrosyan * registers are not being restored. 4675c9c394abSArtur Petrosyan */ 4676c9c394abSArtur Petrosyan dwc2_exit_partial_power_down(hsotg, 0, false); 4677065d3931SGregory Herrero 467861f7223bSGregory Herrero if (is_active) { 467961f7223bSGregory Herrero hsotg->op_state = OTG_STATE_B_PERIPHERAL; 468061f7223bSGregory Herrero 46811f91b4ccSFelipe Balbi dwc2_hsotg_core_init_disconnected(hsotg, false); 468266e77a24SRazmik Karapetyan if (hsotg->enabled) { 468366e77a24SRazmik Karapetyan /* Enable ACG feature in device mode,if supported */ 468466e77a24SRazmik Karapetyan dwc2_enable_acg(hsotg); 46851f91b4ccSFelipe Balbi dwc2_hsotg_core_connect(hsotg); 468666e77a24SRazmik Karapetyan } 468783d98223SGregory Herrero } else { 46881f91b4ccSFelipe Balbi dwc2_hsotg_core_disconnect(hsotg); 46891f91b4ccSFelipe Balbi dwc2_hsotg_disconnect(hsotg); 469083d98223SGregory Herrero } 469183d98223SGregory Herrero 469283d98223SGregory Herrero spin_unlock_irqrestore(&hsotg->lock, flags); 469383d98223SGregory Herrero return 0; 469483d98223SGregory Herrero } 469583d98223SGregory Herrero 4696596d696aSGregory Herrero /** 46971f91b4ccSFelipe Balbi * dwc2_hsotg_vbus_draw - report bMaxPower field 4698596d696aSGregory Herrero * @gadget: The usb gadget state 4699596d696aSGregory Herrero * @mA: Amount of current 4700596d696aSGregory Herrero * 4701596d696aSGregory Herrero * Report how much power the device may consume to the phy. 4702596d696aSGregory Herrero */ 47039da51974SJohn Youn static int dwc2_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned int mA) 4704596d696aSGregory Herrero { 4705596d696aSGregory Herrero struct dwc2_hsotg *hsotg = to_hsotg(gadget); 4706596d696aSGregory Herrero 4707596d696aSGregory Herrero if (IS_ERR_OR_NULL(hsotg->uphy)) 4708596d696aSGregory Herrero return -ENOTSUPP; 4709596d696aSGregory Herrero return usb_phy_set_power(hsotg->uphy, mA); 4710596d696aSGregory Herrero } 4711596d696aSGregory Herrero 4712*5324bad6SArgishti Aleksanyan static void dwc2_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed speed) 4713*5324bad6SArgishti Aleksanyan { 4714*5324bad6SArgishti Aleksanyan struct dwc2_hsotg *hsotg = to_hsotg(g); 4715*5324bad6SArgishti Aleksanyan unsigned long flags; 4716*5324bad6SArgishti Aleksanyan 4717*5324bad6SArgishti Aleksanyan spin_lock_irqsave(&hsotg->lock, flags); 4718*5324bad6SArgishti Aleksanyan switch (speed) { 4719*5324bad6SArgishti Aleksanyan case USB_SPEED_HIGH: 4720*5324bad6SArgishti Aleksanyan hsotg->params.speed = DWC2_SPEED_PARAM_HIGH; 4721*5324bad6SArgishti Aleksanyan break; 4722*5324bad6SArgishti Aleksanyan case USB_SPEED_FULL: 4723*5324bad6SArgishti Aleksanyan hsotg->params.speed = DWC2_SPEED_PARAM_FULL; 4724*5324bad6SArgishti Aleksanyan break; 4725*5324bad6SArgishti Aleksanyan case USB_SPEED_LOW: 4726*5324bad6SArgishti Aleksanyan hsotg->params.speed = DWC2_SPEED_PARAM_LOW; 4727*5324bad6SArgishti Aleksanyan break; 4728*5324bad6SArgishti Aleksanyan default: 4729*5324bad6SArgishti Aleksanyan dev_err(hsotg->dev, "invalid speed (%d)\n", speed); 4730*5324bad6SArgishti Aleksanyan } 4731*5324bad6SArgishti Aleksanyan spin_unlock_irqrestore(&hsotg->lock, flags); 4732*5324bad6SArgishti Aleksanyan } 4733*5324bad6SArgishti Aleksanyan 47341f91b4ccSFelipe Balbi static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = { 47351f91b4ccSFelipe Balbi .get_frame = dwc2_hsotg_gadget_getframe, 47361a0808cbSJohn Keeping .set_selfpowered = dwc2_hsotg_set_selfpowered, 47371f91b4ccSFelipe Balbi .udc_start = dwc2_hsotg_udc_start, 47381f91b4ccSFelipe Balbi .udc_stop = dwc2_hsotg_udc_stop, 47391f91b4ccSFelipe Balbi .pullup = dwc2_hsotg_pullup, 4740*5324bad6SArgishti Aleksanyan .udc_set_speed = dwc2_gadget_set_speed, 47411f91b4ccSFelipe Balbi .vbus_session = dwc2_hsotg_vbus_session, 47421f91b4ccSFelipe Balbi .vbus_draw = dwc2_hsotg_vbus_draw, 474347a1685fSDinh Nguyen }; 474447a1685fSDinh Nguyen 474547a1685fSDinh Nguyen /** 47461f91b4ccSFelipe Balbi * dwc2_hsotg_initep - initialise a single endpoint 474747a1685fSDinh Nguyen * @hsotg: The device state. 474847a1685fSDinh Nguyen * @hs_ep: The endpoint to be initialised. 474947a1685fSDinh Nguyen * @epnum: The endpoint number 47506fb914d7SGrigor Tovmasyan * @dir_in: True if direction is in. 475147a1685fSDinh Nguyen * 475247a1685fSDinh Nguyen * Initialise the given endpoint (as part of the probe and device state 475347a1685fSDinh Nguyen * creation) to give to the gadget driver. Setup the endpoint name, any 475447a1685fSDinh Nguyen * direction information and other state that may be required. 475547a1685fSDinh Nguyen */ 47561f91b4ccSFelipe Balbi static void dwc2_hsotg_initep(struct dwc2_hsotg *hsotg, 47571f91b4ccSFelipe Balbi struct dwc2_hsotg_ep *hs_ep, 4758c6f5c050SMian Yousaf Kaukab int epnum, 4759c6f5c050SMian Yousaf Kaukab bool dir_in) 476047a1685fSDinh Nguyen { 476147a1685fSDinh Nguyen char *dir; 476247a1685fSDinh Nguyen 476347a1685fSDinh Nguyen if (epnum == 0) 476447a1685fSDinh Nguyen dir = ""; 4765c6f5c050SMian Yousaf Kaukab else if (dir_in) 476647a1685fSDinh Nguyen dir = "in"; 4767c6f5c050SMian Yousaf Kaukab else 4768c6f5c050SMian Yousaf Kaukab dir = "out"; 476947a1685fSDinh Nguyen 4770c6f5c050SMian Yousaf Kaukab hs_ep->dir_in = dir_in; 477147a1685fSDinh Nguyen hs_ep->index = epnum; 477247a1685fSDinh Nguyen 477347a1685fSDinh Nguyen snprintf(hs_ep->name, sizeof(hs_ep->name), "ep%d%s", epnum, dir); 477447a1685fSDinh Nguyen 477547a1685fSDinh Nguyen INIT_LIST_HEAD(&hs_ep->queue); 477647a1685fSDinh Nguyen INIT_LIST_HEAD(&hs_ep->ep.ep_list); 477747a1685fSDinh Nguyen 477847a1685fSDinh Nguyen /* add to the list of endpoints known by the gadget driver */ 477947a1685fSDinh Nguyen if (epnum) 478047a1685fSDinh Nguyen list_add_tail(&hs_ep->ep.ep_list, &hsotg->gadget.ep_list); 478147a1685fSDinh Nguyen 478247a1685fSDinh Nguyen hs_ep->parent = hsotg; 478347a1685fSDinh Nguyen hs_ep->ep.name = hs_ep->name; 478438e9002bSVardan Mikayelyan 478538e9002bSVardan Mikayelyan if (hsotg->params.speed == DWC2_SPEED_PARAM_LOW) 478638e9002bSVardan Mikayelyan usb_ep_set_maxpacket_limit(&hs_ep->ep, 8); 478738e9002bSVardan Mikayelyan else 478838e9002bSVardan Mikayelyan usb_ep_set_maxpacket_limit(&hs_ep->ep, 478938e9002bSVardan Mikayelyan epnum ? 1024 : EP0_MPS_LIMIT); 47901f91b4ccSFelipe Balbi hs_ep->ep.ops = &dwc2_hsotg_ep_ops; 479147a1685fSDinh Nguyen 47922954522fSRobert Baldyga if (epnum == 0) { 47932954522fSRobert Baldyga hs_ep->ep.caps.type_control = true; 47942954522fSRobert Baldyga } else { 479538e9002bSVardan Mikayelyan if (hsotg->params.speed != DWC2_SPEED_PARAM_LOW) { 47962954522fSRobert Baldyga hs_ep->ep.caps.type_iso = true; 47972954522fSRobert Baldyga hs_ep->ep.caps.type_bulk = true; 479838e9002bSVardan Mikayelyan } 47992954522fSRobert Baldyga hs_ep->ep.caps.type_int = true; 48002954522fSRobert Baldyga } 48012954522fSRobert Baldyga 48022954522fSRobert Baldyga if (dir_in) 48032954522fSRobert Baldyga hs_ep->ep.caps.dir_in = true; 48042954522fSRobert Baldyga else 48052954522fSRobert Baldyga hs_ep->ep.caps.dir_out = true; 48062954522fSRobert Baldyga 480747a1685fSDinh Nguyen /* 480847a1685fSDinh Nguyen * if we're using dma, we need to set the next-endpoint pointer 480947a1685fSDinh Nguyen * to be something valid. 481047a1685fSDinh Nguyen */ 481147a1685fSDinh Nguyen 481247a1685fSDinh Nguyen if (using_dma(hsotg)) { 481347a1685fSDinh Nguyen u32 next = DXEPCTL_NEXTEP((epnum + 1) % 15); 48149da51974SJohn Youn 4815c6f5c050SMian Yousaf Kaukab if (dir_in) 4816f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, next, DIEPCTL(epnum)); 4817c6f5c050SMian Yousaf Kaukab else 4818f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, next, DOEPCTL(epnum)); 481947a1685fSDinh Nguyen } 482047a1685fSDinh Nguyen } 482147a1685fSDinh Nguyen 482247a1685fSDinh Nguyen /** 48231f91b4ccSFelipe Balbi * dwc2_hsotg_hw_cfg - read HW configuration registers 48246fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller 482547a1685fSDinh Nguyen * 482647a1685fSDinh Nguyen * Read the USB core HW configuration registers 482747a1685fSDinh Nguyen */ 48281f91b4ccSFelipe Balbi static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg) 482947a1685fSDinh Nguyen { 4830c6f5c050SMian Yousaf Kaukab u32 cfg; 4831c6f5c050SMian Yousaf Kaukab u32 ep_type; 4832c6f5c050SMian Yousaf Kaukab u32 i; 4833c6f5c050SMian Yousaf Kaukab 483447a1685fSDinh Nguyen /* check hardware configuration */ 483547a1685fSDinh Nguyen 483643e90349SJohn Youn hsotg->num_of_eps = hsotg->hw_params.num_dev_ep; 483743e90349SJohn Youn 4838c6f5c050SMian Yousaf Kaukab /* Add ep0 */ 4839c6f5c050SMian Yousaf Kaukab hsotg->num_of_eps++; 484047a1685fSDinh Nguyen 4841b98866c2SJohn Youn hsotg->eps_in[0] = devm_kzalloc(hsotg->dev, 4842b98866c2SJohn Youn sizeof(struct dwc2_hsotg_ep), 4843c6f5c050SMian Yousaf Kaukab GFP_KERNEL); 4844c6f5c050SMian Yousaf Kaukab if (!hsotg->eps_in[0]) 4845c6f5c050SMian Yousaf Kaukab return -ENOMEM; 48461f91b4ccSFelipe Balbi /* Same dwc2_hsotg_ep is used in both directions for ep0 */ 4847c6f5c050SMian Yousaf Kaukab hsotg->eps_out[0] = hsotg->eps_in[0]; 484847a1685fSDinh Nguyen 484943e90349SJohn Youn cfg = hsotg->hw_params.dev_ep_dirs; 4850251a17f5SRoshan Pius for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) { 4851c6f5c050SMian Yousaf Kaukab ep_type = cfg & 3; 4852c6f5c050SMian Yousaf Kaukab /* Direction in or both */ 4853c6f5c050SMian Yousaf Kaukab if (!(ep_type & 2)) { 4854c6f5c050SMian Yousaf Kaukab hsotg->eps_in[i] = devm_kzalloc(hsotg->dev, 48551f91b4ccSFelipe Balbi sizeof(struct dwc2_hsotg_ep), GFP_KERNEL); 4856c6f5c050SMian Yousaf Kaukab if (!hsotg->eps_in[i]) 4857c6f5c050SMian Yousaf Kaukab return -ENOMEM; 4858c6f5c050SMian Yousaf Kaukab } 4859c6f5c050SMian Yousaf Kaukab /* Direction out or both */ 4860c6f5c050SMian Yousaf Kaukab if (!(ep_type & 1)) { 4861c6f5c050SMian Yousaf Kaukab hsotg->eps_out[i] = devm_kzalloc(hsotg->dev, 48621f91b4ccSFelipe Balbi sizeof(struct dwc2_hsotg_ep), GFP_KERNEL); 4863c6f5c050SMian Yousaf Kaukab if (!hsotg->eps_out[i]) 4864c6f5c050SMian Yousaf Kaukab return -ENOMEM; 4865c6f5c050SMian Yousaf Kaukab } 4866c6f5c050SMian Yousaf Kaukab } 4867c6f5c050SMian Yousaf Kaukab 486843e90349SJohn Youn hsotg->fifo_mem = hsotg->hw_params.total_fifo_size; 486943e90349SJohn Youn hsotg->dedicated_fifos = hsotg->hw_params.en_multiple_tx_fifo; 487047a1685fSDinh Nguyen 4871cff9eb75SMarek Szyprowski dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n", 4872cff9eb75SMarek Szyprowski hsotg->num_of_eps, 4873cff9eb75SMarek Szyprowski hsotg->dedicated_fifos ? "dedicated" : "shared", 4874cff9eb75SMarek Szyprowski hsotg->fifo_mem); 4875c6f5c050SMian Yousaf Kaukab return 0; 487647a1685fSDinh Nguyen } 487747a1685fSDinh Nguyen 487847a1685fSDinh Nguyen /** 48791f91b4ccSFelipe Balbi * dwc2_hsotg_dump - dump state of the udc 48806fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller 48816fb914d7SGrigor Tovmasyan * 488247a1685fSDinh Nguyen */ 48831f91b4ccSFelipe Balbi static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg) 488447a1685fSDinh Nguyen { 488547a1685fSDinh Nguyen #ifdef DEBUG 488647a1685fSDinh Nguyen struct device *dev = hsotg->dev; 488747a1685fSDinh Nguyen u32 val; 488847a1685fSDinh Nguyen int idx; 488947a1685fSDinh Nguyen 489047a1685fSDinh Nguyen dev_info(dev, "DCFG=0x%08x, DCTL=0x%08x, DIEPMSK=%08x\n", 4891f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DCFG), dwc2_readl(hsotg, DCTL), 4892f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPMSK)); 489347a1685fSDinh Nguyen 4894f889f23dSMian Yousaf Kaukab dev_info(dev, "GAHBCFG=0x%08x, GHWCFG1=0x%08x\n", 4895f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, GAHBCFG), dwc2_readl(hsotg, GHWCFG1)); 489647a1685fSDinh Nguyen 489747a1685fSDinh Nguyen dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n", 4898f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, GRXFSIZ), dwc2_readl(hsotg, GNPTXFSIZ)); 489947a1685fSDinh Nguyen 490047a1685fSDinh Nguyen /* show periodic fifo settings */ 490147a1685fSDinh Nguyen 4902364f8e93SMian Yousaf Kaukab for (idx = 1; idx < hsotg->num_of_eps; idx++) { 4903f25c42b8SGevorg Sahakyan val = dwc2_readl(hsotg, DPTXFSIZN(idx)); 490447a1685fSDinh Nguyen dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx, 490547a1685fSDinh Nguyen val >> FIFOSIZE_DEPTH_SHIFT, 490647a1685fSDinh Nguyen val & FIFOSIZE_STARTADDR_MASK); 490747a1685fSDinh Nguyen } 490847a1685fSDinh Nguyen 4909364f8e93SMian Yousaf Kaukab for (idx = 0; idx < hsotg->num_of_eps; idx++) { 491047a1685fSDinh Nguyen dev_info(dev, 491147a1685fSDinh Nguyen "ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx, 4912f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPCTL(idx)), 4913f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPTSIZ(idx)), 4914f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DIEPDMA(idx))); 491547a1685fSDinh Nguyen 4916f25c42b8SGevorg Sahakyan val = dwc2_readl(hsotg, DOEPCTL(idx)); 491747a1685fSDinh Nguyen dev_info(dev, 491847a1685fSDinh Nguyen "ep%d-out: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", 4919f25c42b8SGevorg Sahakyan idx, dwc2_readl(hsotg, DOEPCTL(idx)), 4920f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DOEPTSIZ(idx)), 4921f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DOEPDMA(idx))); 492247a1685fSDinh Nguyen } 492347a1685fSDinh Nguyen 492447a1685fSDinh Nguyen dev_info(dev, "DVBUSDIS=0x%08x, DVBUSPULSE=%08x\n", 4925f25c42b8SGevorg Sahakyan dwc2_readl(hsotg, DVBUSDIS), dwc2_readl(hsotg, DVBUSPULSE)); 492647a1685fSDinh Nguyen #endif 492747a1685fSDinh Nguyen } 492847a1685fSDinh Nguyen 492947a1685fSDinh Nguyen /** 4930117777b2SDinh Nguyen * dwc2_gadget_init - init function for gadget 49316fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller 49326fb914d7SGrigor Tovmasyan * 493347a1685fSDinh Nguyen */ 4934f3768997SVardan Mikayelyan int dwc2_gadget_init(struct dwc2_hsotg *hsotg) 493547a1685fSDinh Nguyen { 4936117777b2SDinh Nguyen struct device *dev = hsotg->dev; 493747a1685fSDinh Nguyen int epnum; 493847a1685fSDinh Nguyen int ret; 493943e90349SJohn Youn 49400a176279SGregory Herrero /* Dump fifo information */ 49410a176279SGregory Herrero dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n", 494205ee799fSJohn Youn hsotg->params.g_np_tx_fifo_size); 494305ee799fSJohn Youn dev_dbg(dev, "RXFIFO size: %d\n", hsotg->params.g_rx_fifo_size); 494447a1685fSDinh Nguyen 494547a1685fSDinh Nguyen hsotg->gadget.max_speed = USB_SPEED_HIGH; 49461f91b4ccSFelipe Balbi hsotg->gadget.ops = &dwc2_hsotg_gadget_ops; 494747a1685fSDinh Nguyen hsotg->gadget.name = dev_name(dev); 4948fa389a6dSVardan Mikayelyan hsotg->remote_wakeup_allowed = 0; 49497455f8b7SJohn Youn 49507455f8b7SJohn Youn if (hsotg->params.lpm) 49517455f8b7SJohn Youn hsotg->gadget.lpm_capable = true; 49527455f8b7SJohn Youn 4953097ee662SGregory Herrero if (hsotg->dr_mode == USB_DR_MODE_OTG) 4954097ee662SGregory Herrero hsotg->gadget.is_otg = 1; 4955ec4cc657SMian Yousaf Kaukab else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) 4956ec4cc657SMian Yousaf Kaukab hsotg->op_state = OTG_STATE_B_PERIPHERAL; 495747a1685fSDinh Nguyen 49581f91b4ccSFelipe Balbi ret = dwc2_hsotg_hw_cfg(hsotg); 4959c6f5c050SMian Yousaf Kaukab if (ret) { 4960c6f5c050SMian Yousaf Kaukab dev_err(hsotg->dev, "Hardware configuration failed: %d\n", ret); 496109a75e85SMarek Szyprowski return ret; 4962c6f5c050SMian Yousaf Kaukab } 4963c6f5c050SMian Yousaf Kaukab 49643f95001dSMian Yousaf Kaukab hsotg->ctrl_buff = devm_kzalloc(hsotg->dev, 49653f95001dSMian Yousaf Kaukab DWC2_CTRL_BUFF_SIZE, GFP_KERNEL); 49668bae0f8cSWolfram Sang if (!hsotg->ctrl_buff) 496709a75e85SMarek Szyprowski return -ENOMEM; 49683f95001dSMian Yousaf Kaukab 49693f95001dSMian Yousaf Kaukab hsotg->ep0_buff = devm_kzalloc(hsotg->dev, 49703f95001dSMian Yousaf Kaukab DWC2_CTRL_BUFF_SIZE, GFP_KERNEL); 49718bae0f8cSWolfram Sang if (!hsotg->ep0_buff) 497209a75e85SMarek Szyprowski return -ENOMEM; 49733f95001dSMian Yousaf Kaukab 49740f6b80c0SVahram Aharonyan if (using_desc_dma(hsotg)) { 49750f6b80c0SVahram Aharonyan ret = dwc2_gadget_alloc_ctrl_desc_chains(hsotg); 49760f6b80c0SVahram Aharonyan if (ret < 0) 49770f6b80c0SVahram Aharonyan return ret; 49780f6b80c0SVahram Aharonyan } 49790f6b80c0SVahram Aharonyan 4980f3768997SVardan Mikayelyan ret = devm_request_irq(hsotg->dev, hsotg->irq, dwc2_hsotg_irq, 4981f3768997SVardan Mikayelyan IRQF_SHARED, dev_name(hsotg->dev), hsotg); 4982eb3c56c5SMarek Szyprowski if (ret < 0) { 4983db8178c3SDinh Nguyen dev_err(dev, "cannot claim IRQ for gadget\n"); 498409a75e85SMarek Szyprowski return ret; 4985eb3c56c5SMarek Szyprowski } 4986eb3c56c5SMarek Szyprowski 498747a1685fSDinh Nguyen /* hsotg->num_of_eps holds number of EPs other than ep0 */ 498847a1685fSDinh Nguyen 498947a1685fSDinh Nguyen if (hsotg->num_of_eps == 0) { 499047a1685fSDinh Nguyen dev_err(dev, "wrong number of EPs (zero)\n"); 499109a75e85SMarek Szyprowski return -EINVAL; 499247a1685fSDinh Nguyen } 499347a1685fSDinh Nguyen 499447a1685fSDinh Nguyen /* setup endpoint information */ 499547a1685fSDinh Nguyen 499647a1685fSDinh Nguyen INIT_LIST_HEAD(&hsotg->gadget.ep_list); 4997c6f5c050SMian Yousaf Kaukab hsotg->gadget.ep0 = &hsotg->eps_out[0]->ep; 499847a1685fSDinh Nguyen 499947a1685fSDinh Nguyen /* allocate EP0 request */ 500047a1685fSDinh Nguyen 50011f91b4ccSFelipe Balbi hsotg->ctrl_req = dwc2_hsotg_ep_alloc_request(&hsotg->eps_out[0]->ep, 500247a1685fSDinh Nguyen GFP_KERNEL); 500347a1685fSDinh Nguyen if (!hsotg->ctrl_req) { 500447a1685fSDinh Nguyen dev_err(dev, "failed to allocate ctrl req\n"); 500509a75e85SMarek Szyprowski return -ENOMEM; 500647a1685fSDinh Nguyen } 500747a1685fSDinh Nguyen 500847a1685fSDinh Nguyen /* initialise the endpoints now the core has been initialised */ 5009c6f5c050SMian Yousaf Kaukab for (epnum = 0; epnum < hsotg->num_of_eps; epnum++) { 5010c6f5c050SMian Yousaf Kaukab if (hsotg->eps_in[epnum]) 50111f91b4ccSFelipe Balbi dwc2_hsotg_initep(hsotg, hsotg->eps_in[epnum], 5012c6f5c050SMian Yousaf Kaukab epnum, 1); 5013c6f5c050SMian Yousaf Kaukab if (hsotg->eps_out[epnum]) 50141f91b4ccSFelipe Balbi dwc2_hsotg_initep(hsotg, hsotg->eps_out[epnum], 5015c6f5c050SMian Yousaf Kaukab epnum, 0); 5016c6f5c050SMian Yousaf Kaukab } 501747a1685fSDinh Nguyen 50181f91b4ccSFelipe Balbi dwc2_hsotg_dump(hsotg); 501947a1685fSDinh Nguyen 502047a1685fSDinh Nguyen return 0; 502147a1685fSDinh Nguyen } 502247a1685fSDinh Nguyen 502347a1685fSDinh Nguyen /** 50241f91b4ccSFelipe Balbi * dwc2_hsotg_remove - remove function for hsotg driver 50256fb914d7SGrigor Tovmasyan * @hsotg: Programming view of the DWC_otg controller 50266fb914d7SGrigor Tovmasyan * 502747a1685fSDinh Nguyen */ 50281f91b4ccSFelipe Balbi int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg) 502947a1685fSDinh Nguyen { 503047a1685fSDinh Nguyen usb_del_gadget_udc(&hsotg->gadget); 50319bb073a0SGrigor Tovmasyan dwc2_hsotg_ep_free_request(&hsotg->eps_out[0]->ep, hsotg->ctrl_req); 503247a1685fSDinh Nguyen 503347a1685fSDinh Nguyen return 0; 503447a1685fSDinh Nguyen } 503547a1685fSDinh Nguyen 50361f91b4ccSFelipe Balbi int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg) 503747a1685fSDinh Nguyen { 503847a1685fSDinh Nguyen unsigned long flags; 503947a1685fSDinh Nguyen 50409e779778SGregory Herrero if (hsotg->lx_state != DWC2_L0) 504109a75e85SMarek Szyprowski return 0; 50429e779778SGregory Herrero 5043dc6e69e6SMarek Szyprowski if (hsotg->driver) { 5044dc6e69e6SMarek Szyprowski int ep; 5045dc6e69e6SMarek Szyprowski 504647a1685fSDinh Nguyen dev_info(hsotg->dev, "suspending usb gadget %s\n", 504747a1685fSDinh Nguyen hsotg->driver->driver.name); 504847a1685fSDinh Nguyen 504947a1685fSDinh Nguyen spin_lock_irqsave(&hsotg->lock, flags); 5050dc6e69e6SMarek Szyprowski if (hsotg->enabled) 50511f91b4ccSFelipe Balbi dwc2_hsotg_core_disconnect(hsotg); 50521f91b4ccSFelipe Balbi dwc2_hsotg_disconnect(hsotg); 505347a1685fSDinh Nguyen hsotg->gadget.speed = USB_SPEED_UNKNOWN; 505447a1685fSDinh Nguyen spin_unlock_irqrestore(&hsotg->lock, flags); 505547a1685fSDinh Nguyen 5056c6f5c050SMian Yousaf Kaukab for (ep = 0; ep < hsotg->num_of_eps; ep++) { 5057c6f5c050SMian Yousaf Kaukab if (hsotg->eps_in[ep]) 50584fe4f9feSMinas Harutyunyan dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep); 5059c6f5c050SMian Yousaf Kaukab if (hsotg->eps_out[ep]) 50604fe4f9feSMinas Harutyunyan dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep); 5061c6f5c050SMian Yousaf Kaukab } 506247a1685fSDinh Nguyen } 506347a1685fSDinh Nguyen 506409a75e85SMarek Szyprowski return 0; 506547a1685fSDinh Nguyen } 506647a1685fSDinh Nguyen 50671f91b4ccSFelipe Balbi int dwc2_hsotg_resume(struct dwc2_hsotg *hsotg) 506847a1685fSDinh Nguyen { 506947a1685fSDinh Nguyen unsigned long flags; 507047a1685fSDinh Nguyen 50719e779778SGregory Herrero if (hsotg->lx_state == DWC2_L2) 507209a75e85SMarek Szyprowski return 0; 50739e779778SGregory Herrero 507447a1685fSDinh Nguyen if (hsotg->driver) { 507547a1685fSDinh Nguyen dev_info(hsotg->dev, "resuming usb gadget %s\n", 507647a1685fSDinh Nguyen hsotg->driver->driver.name); 5077d00b4142SRobert Baldyga 507847a1685fSDinh Nguyen spin_lock_irqsave(&hsotg->lock, flags); 50791f91b4ccSFelipe Balbi dwc2_hsotg_core_init_disconnected(hsotg, false); 508066e77a24SRazmik Karapetyan if (hsotg->enabled) { 508166e77a24SRazmik Karapetyan /* Enable ACG feature in device mode,if supported */ 508266e77a24SRazmik Karapetyan dwc2_enable_acg(hsotg); 50831f91b4ccSFelipe Balbi dwc2_hsotg_core_connect(hsotg); 508466e77a24SRazmik Karapetyan } 508547a1685fSDinh Nguyen spin_unlock_irqrestore(&hsotg->lock, flags); 5086dc6e69e6SMarek Szyprowski } 508747a1685fSDinh Nguyen 508809a75e85SMarek Szyprowski return 0; 508947a1685fSDinh Nguyen } 509058e52ff6SJohn Youn 509158e52ff6SJohn Youn /** 509258e52ff6SJohn Youn * dwc2_backup_device_registers() - Backup controller device registers. 509358e52ff6SJohn Youn * When suspending usb bus, registers needs to be backuped 509458e52ff6SJohn Youn * if controller power is disabled once suspended. 509558e52ff6SJohn Youn * 509658e52ff6SJohn Youn * @hsotg: Programming view of the DWC_otg controller 509758e52ff6SJohn Youn */ 509858e52ff6SJohn Youn int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) 509958e52ff6SJohn Youn { 510058e52ff6SJohn Youn struct dwc2_dregs_backup *dr; 510158e52ff6SJohn Youn int i; 510258e52ff6SJohn Youn 510358e52ff6SJohn Youn dev_dbg(hsotg->dev, "%s\n", __func__); 510458e52ff6SJohn Youn 510558e52ff6SJohn Youn /* Backup dev regs */ 510658e52ff6SJohn Youn dr = &hsotg->dr_backup; 510758e52ff6SJohn Youn 5108f25c42b8SGevorg Sahakyan dr->dcfg = dwc2_readl(hsotg, DCFG); 5109f25c42b8SGevorg Sahakyan dr->dctl = dwc2_readl(hsotg, DCTL); 5110f25c42b8SGevorg Sahakyan dr->daintmsk = dwc2_readl(hsotg, DAINTMSK); 5111f25c42b8SGevorg Sahakyan dr->diepmsk = dwc2_readl(hsotg, DIEPMSK); 5112f25c42b8SGevorg Sahakyan dr->doepmsk = dwc2_readl(hsotg, DOEPMSK); 511358e52ff6SJohn Youn 511458e52ff6SJohn Youn for (i = 0; i < hsotg->num_of_eps; i++) { 511558e52ff6SJohn Youn /* Backup IN EPs */ 5116f25c42b8SGevorg Sahakyan dr->diepctl[i] = dwc2_readl(hsotg, DIEPCTL(i)); 511758e52ff6SJohn Youn 511858e52ff6SJohn Youn /* Ensure DATA PID is correctly configured */ 511958e52ff6SJohn Youn if (dr->diepctl[i] & DXEPCTL_DPID) 512058e52ff6SJohn Youn dr->diepctl[i] |= DXEPCTL_SETD1PID; 512158e52ff6SJohn Youn else 512258e52ff6SJohn Youn dr->diepctl[i] |= DXEPCTL_SETD0PID; 512358e52ff6SJohn Youn 5124f25c42b8SGevorg Sahakyan dr->dieptsiz[i] = dwc2_readl(hsotg, DIEPTSIZ(i)); 5125f25c42b8SGevorg Sahakyan dr->diepdma[i] = dwc2_readl(hsotg, DIEPDMA(i)); 512658e52ff6SJohn Youn 512758e52ff6SJohn Youn /* Backup OUT EPs */ 5128f25c42b8SGevorg Sahakyan dr->doepctl[i] = dwc2_readl(hsotg, DOEPCTL(i)); 512958e52ff6SJohn Youn 513058e52ff6SJohn Youn /* Ensure DATA PID is correctly configured */ 513158e52ff6SJohn Youn if (dr->doepctl[i] & DXEPCTL_DPID) 513258e52ff6SJohn Youn dr->doepctl[i] |= DXEPCTL_SETD1PID; 513358e52ff6SJohn Youn else 513458e52ff6SJohn Youn dr->doepctl[i] |= DXEPCTL_SETD0PID; 513558e52ff6SJohn Youn 5136f25c42b8SGevorg Sahakyan dr->doeptsiz[i] = dwc2_readl(hsotg, DOEPTSIZ(i)); 5137f25c42b8SGevorg Sahakyan dr->doepdma[i] = dwc2_readl(hsotg, DOEPDMA(i)); 5138f25c42b8SGevorg Sahakyan dr->dtxfsiz[i] = dwc2_readl(hsotg, DPTXFSIZN(i)); 513958e52ff6SJohn Youn } 514058e52ff6SJohn Youn dr->valid = true; 514158e52ff6SJohn Youn return 0; 514258e52ff6SJohn Youn } 514358e52ff6SJohn Youn 514458e52ff6SJohn Youn /** 514558e52ff6SJohn Youn * dwc2_restore_device_registers() - Restore controller device registers. 514658e52ff6SJohn Youn * When resuming usb bus, device registers needs to be restored 514758e52ff6SJohn Youn * if controller power were disabled. 514858e52ff6SJohn Youn * 514958e52ff6SJohn Youn * @hsotg: Programming view of the DWC_otg controller 51509a5d2816SVardan Mikayelyan * @remote_wakeup: Indicates whether resume is initiated by Device or Host. 51519a5d2816SVardan Mikayelyan * 51529a5d2816SVardan Mikayelyan * Return: 0 if successful, negative error code otherwise 515358e52ff6SJohn Youn */ 51549a5d2816SVardan Mikayelyan int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup) 515558e52ff6SJohn Youn { 515658e52ff6SJohn Youn struct dwc2_dregs_backup *dr; 515758e52ff6SJohn Youn int i; 515858e52ff6SJohn Youn 515958e52ff6SJohn Youn dev_dbg(hsotg->dev, "%s\n", __func__); 516058e52ff6SJohn Youn 516158e52ff6SJohn Youn /* Restore dev regs */ 516258e52ff6SJohn Youn dr = &hsotg->dr_backup; 516358e52ff6SJohn Youn if (!dr->valid) { 516458e52ff6SJohn Youn dev_err(hsotg->dev, "%s: no device registers to restore\n", 516558e52ff6SJohn Youn __func__); 516658e52ff6SJohn Youn return -EINVAL; 516758e52ff6SJohn Youn } 516858e52ff6SJohn Youn dr->valid = false; 516958e52ff6SJohn Youn 51709a5d2816SVardan Mikayelyan if (!remote_wakeup) 5171f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dctl, DCTL); 51729a5d2816SVardan Mikayelyan 5173f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->daintmsk, DAINTMSK); 5174f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->diepmsk, DIEPMSK); 5175f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doepmsk, DOEPMSK); 517658e52ff6SJohn Youn 517758e52ff6SJohn Youn for (i = 0; i < hsotg->num_of_eps; i++) { 517858e52ff6SJohn Youn /* Restore IN EPs */ 5179f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dieptsiz[i], DIEPTSIZ(i)); 5180f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->diepdma[i], DIEPDMA(i)); 5181f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doeptsiz[i], DOEPTSIZ(i)); 51829a5d2816SVardan Mikayelyan /** WA for enabled EPx's IN in DDMA mode. On entering to 51839a5d2816SVardan Mikayelyan * hibernation wrong value read and saved from DIEPDMAx, 51849a5d2816SVardan Mikayelyan * as result BNA interrupt asserted on hibernation exit 51859a5d2816SVardan Mikayelyan * by restoring from saved area. 51869a5d2816SVardan Mikayelyan */ 51879a5d2816SVardan Mikayelyan if (hsotg->params.g_dma_desc && 51889a5d2816SVardan Mikayelyan (dr->diepctl[i] & DXEPCTL_EPENA)) 51899a5d2816SVardan Mikayelyan dr->diepdma[i] = hsotg->eps_in[i]->desc_list_dma; 5190f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dtxfsiz[i], DPTXFSIZN(i)); 5191f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->diepctl[i], DIEPCTL(i)); 51929a5d2816SVardan Mikayelyan /* Restore OUT EPs */ 5193f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doeptsiz[i], DOEPTSIZ(i)); 51949a5d2816SVardan Mikayelyan /* WA for enabled EPx's OUT in DDMA mode. On entering to 51959a5d2816SVardan Mikayelyan * hibernation wrong value read and saved from DOEPDMAx, 51969a5d2816SVardan Mikayelyan * as result BNA interrupt asserted on hibernation exit 51979a5d2816SVardan Mikayelyan * by restoring from saved area. 51989a5d2816SVardan Mikayelyan */ 51999a5d2816SVardan Mikayelyan if (hsotg->params.g_dma_desc && 52009a5d2816SVardan Mikayelyan (dr->doepctl[i] & DXEPCTL_EPENA)) 52019a5d2816SVardan Mikayelyan dr->doepdma[i] = hsotg->eps_out[i]->desc_list_dma; 5202f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doepdma[i], DOEPDMA(i)); 5203f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->doepctl[i], DOEPCTL(i)); 520458e52ff6SJohn Youn } 520558e52ff6SJohn Youn 520658e52ff6SJohn Youn return 0; 520758e52ff6SJohn Youn } 520821b03405SSevak Arakelyan 520921b03405SSevak Arakelyan /** 521021b03405SSevak Arakelyan * dwc2_gadget_init_lpm - Configure the core to support LPM in device mode 521121b03405SSevak Arakelyan * 521221b03405SSevak Arakelyan * @hsotg: Programming view of DWC_otg controller 521321b03405SSevak Arakelyan * 521421b03405SSevak Arakelyan */ 521521b03405SSevak Arakelyan void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) 521621b03405SSevak Arakelyan { 521721b03405SSevak Arakelyan u32 val; 521821b03405SSevak Arakelyan 521921b03405SSevak Arakelyan if (!hsotg->params.lpm) 522021b03405SSevak Arakelyan return; 522121b03405SSevak Arakelyan 522221b03405SSevak Arakelyan val = GLPMCFG_LPMCAP | GLPMCFG_APPL1RES; 522321b03405SSevak Arakelyan val |= hsotg->params.hird_threshold_en ? GLPMCFG_HIRD_THRES_EN : 0; 522421b03405SSevak Arakelyan val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0; 522521b03405SSevak Arakelyan val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT; 522621b03405SSevak Arakelyan val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0; 522746637565SMinas Harutyunyan val |= GLPMCFG_LPM_REJECT_CTRL_CONTROL; 52289aed8c08SArtur Petrosyan val |= GLPMCFG_LPM_ACCEPT_CTRL_ISOC; 5229f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, val, GLPMCFG); 5230f25c42b8SGevorg Sahakyan dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG)); 52314abe4537SGrigor Tovmasyan 52324abe4537SGrigor Tovmasyan /* Unmask WKUP_ALERT Interrupt */ 52334abe4537SGrigor Tovmasyan if (hsotg->params.service_interval) 52344abe4537SGrigor Tovmasyan dwc2_set_bit(hsotg, GINTMSK2, GINTMSK2_WKUP_ALERT_INT_MSK); 523521b03405SSevak Arakelyan } 5236c5c403dcSVardan Mikayelyan 5237c5c403dcSVardan Mikayelyan /** 523815d9dbf8SGrigor Tovmasyan * dwc2_gadget_program_ref_clk - Program GREFCLK register in device mode 523915d9dbf8SGrigor Tovmasyan * 524015d9dbf8SGrigor Tovmasyan * @hsotg: Programming view of DWC_otg controller 524115d9dbf8SGrigor Tovmasyan * 524215d9dbf8SGrigor Tovmasyan */ 524315d9dbf8SGrigor Tovmasyan void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) 524415d9dbf8SGrigor Tovmasyan { 524515d9dbf8SGrigor Tovmasyan u32 val = 0; 524615d9dbf8SGrigor Tovmasyan 524715d9dbf8SGrigor Tovmasyan val |= GREFCLK_REF_CLK_MODE; 524815d9dbf8SGrigor Tovmasyan val |= hsotg->params.ref_clk_per << GREFCLK_REFCLKPER_SHIFT; 524915d9dbf8SGrigor Tovmasyan val |= hsotg->params.sof_cnt_wkup_alert << 525015d9dbf8SGrigor Tovmasyan GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT; 525115d9dbf8SGrigor Tovmasyan 525215d9dbf8SGrigor Tovmasyan dwc2_writel(hsotg, val, GREFCLK); 525315d9dbf8SGrigor Tovmasyan dev_dbg(hsotg->dev, "GREFCLK=0x%08x\n", dwc2_readl(hsotg, GREFCLK)); 525415d9dbf8SGrigor Tovmasyan } 525515d9dbf8SGrigor Tovmasyan 525615d9dbf8SGrigor Tovmasyan /** 5257c5c403dcSVardan Mikayelyan * dwc2_gadget_enter_hibernation() - Put controller in Hibernation. 5258c5c403dcSVardan Mikayelyan * 5259c5c403dcSVardan Mikayelyan * @hsotg: Programming view of the DWC_otg controller 5260c5c403dcSVardan Mikayelyan * 5261c5c403dcSVardan Mikayelyan * Return non-zero if failed to enter to hibernation. 5262c5c403dcSVardan Mikayelyan */ 5263c5c403dcSVardan Mikayelyan int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) 5264c5c403dcSVardan Mikayelyan { 5265c5c403dcSVardan Mikayelyan u32 gpwrdn; 5266c5c403dcSVardan Mikayelyan int ret = 0; 5267c5c403dcSVardan Mikayelyan 5268c5c403dcSVardan Mikayelyan /* Change to L2(suspend) state */ 5269c5c403dcSVardan Mikayelyan hsotg->lx_state = DWC2_L2; 5270c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev, "Start of hibernation completed\n"); 5271c5c403dcSVardan Mikayelyan ret = dwc2_backup_global_registers(hsotg); 5272c5c403dcSVardan Mikayelyan if (ret) { 5273c5c403dcSVardan Mikayelyan dev_err(hsotg->dev, "%s: failed to backup global registers\n", 5274c5c403dcSVardan Mikayelyan __func__); 5275c5c403dcSVardan Mikayelyan return ret; 5276c5c403dcSVardan Mikayelyan } 5277c5c403dcSVardan Mikayelyan ret = dwc2_backup_device_registers(hsotg); 5278c5c403dcSVardan Mikayelyan if (ret) { 5279c5c403dcSVardan Mikayelyan dev_err(hsotg->dev, "%s: failed to backup device registers\n", 5280c5c403dcSVardan Mikayelyan __func__); 5281c5c403dcSVardan Mikayelyan return ret; 5282c5c403dcSVardan Mikayelyan } 5283c5c403dcSVardan Mikayelyan 5284c5c403dcSVardan Mikayelyan gpwrdn = GPWRDN_PWRDNRSTN; 5285c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_PMUACTV; 5286f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN); 5287c5c403dcSVardan Mikayelyan udelay(10); 5288c5c403dcSVardan Mikayelyan 5289c5c403dcSVardan Mikayelyan /* Set flag to indicate that we are in hibernation */ 5290c5c403dcSVardan Mikayelyan hsotg->hibernated = 1; 5291c5c403dcSVardan Mikayelyan 5292c5c403dcSVardan Mikayelyan /* Enable interrupts from wake up logic */ 5293f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN); 5294c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_PMUINTSEL; 5295f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN); 5296c5c403dcSVardan Mikayelyan udelay(10); 5297c5c403dcSVardan Mikayelyan 5298c5c403dcSVardan Mikayelyan /* Unmask device mode interrupts in GPWRDN */ 5299f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN); 5300c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_RST_DET_MSK; 5301c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_LNSTSCHG_MSK; 5302c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_STS_CHGINT_MSK; 5303f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN); 5304c5c403dcSVardan Mikayelyan udelay(10); 5305c5c403dcSVardan Mikayelyan 5306c5c403dcSVardan Mikayelyan /* Enable Power Down Clamp */ 5307f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN); 5308c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_PWRDNCLMP; 5309f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN); 5310c5c403dcSVardan Mikayelyan udelay(10); 5311c5c403dcSVardan Mikayelyan 5312c5c403dcSVardan Mikayelyan /* Switch off VDD */ 5313f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN); 5314c5c403dcSVardan Mikayelyan gpwrdn |= GPWRDN_PWRDNSWTCH; 5315f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN); 5316c5c403dcSVardan Mikayelyan udelay(10); 5317c5c403dcSVardan Mikayelyan 5318c5c403dcSVardan Mikayelyan /* Save gpwrdn register for further usage if stschng interrupt */ 5319f25c42b8SGevorg Sahakyan hsotg->gr_backup.gpwrdn = dwc2_readl(hsotg, GPWRDN); 5320c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev, "Hibernation completed\n"); 5321c5c403dcSVardan Mikayelyan 5322c5c403dcSVardan Mikayelyan return ret; 5323c5c403dcSVardan Mikayelyan } 5324c5c403dcSVardan Mikayelyan 5325c5c403dcSVardan Mikayelyan /** 5326c5c403dcSVardan Mikayelyan * dwc2_gadget_exit_hibernation() 5327c5c403dcSVardan Mikayelyan * This function is for exiting from Device mode hibernation by host initiated 5328c5c403dcSVardan Mikayelyan * resume/reset and device initiated remote-wakeup. 5329c5c403dcSVardan Mikayelyan * 5330c5c403dcSVardan Mikayelyan * @hsotg: Programming view of the DWC_otg controller 5331c5c403dcSVardan Mikayelyan * @rem_wakeup: indicates whether resume is initiated by Device or Host. 53326fb914d7SGrigor Tovmasyan * @reset: indicates whether resume is initiated by Reset. 5333c5c403dcSVardan Mikayelyan * 5334c5c403dcSVardan Mikayelyan * Return non-zero if failed to exit from hibernation. 5335c5c403dcSVardan Mikayelyan */ 5336c5c403dcSVardan Mikayelyan int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, 5337c5c403dcSVardan Mikayelyan int rem_wakeup, int reset) 5338c5c403dcSVardan Mikayelyan { 5339c5c403dcSVardan Mikayelyan u32 pcgcctl; 5340c5c403dcSVardan Mikayelyan u32 gpwrdn; 5341c5c403dcSVardan Mikayelyan u32 dctl; 5342c5c403dcSVardan Mikayelyan int ret = 0; 5343c5c403dcSVardan Mikayelyan struct dwc2_gregs_backup *gr; 5344c5c403dcSVardan Mikayelyan struct dwc2_dregs_backup *dr; 5345c5c403dcSVardan Mikayelyan 5346c5c403dcSVardan Mikayelyan gr = &hsotg->gr_backup; 5347c5c403dcSVardan Mikayelyan dr = &hsotg->dr_backup; 5348c5c403dcSVardan Mikayelyan 5349c5c403dcSVardan Mikayelyan if (!hsotg->hibernated) { 5350c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev, "Already exited from Hibernation\n"); 5351c5c403dcSVardan Mikayelyan return 1; 5352c5c403dcSVardan Mikayelyan } 5353c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev, 5354c5c403dcSVardan Mikayelyan "%s: called with rem_wakeup = %d reset = %d\n", 5355c5c403dcSVardan Mikayelyan __func__, rem_wakeup, reset); 5356c5c403dcSVardan Mikayelyan 5357c5c403dcSVardan Mikayelyan dwc2_hib_restore_common(hsotg, rem_wakeup, 0); 5358c5c403dcSVardan Mikayelyan 5359c5c403dcSVardan Mikayelyan if (!reset) { 5360c5c403dcSVardan Mikayelyan /* Clear all pending interupts */ 5361f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, 0xffffffff, GINTSTS); 5362c5c403dcSVardan Mikayelyan } 5363c5c403dcSVardan Mikayelyan 5364c5c403dcSVardan Mikayelyan /* De-assert Restore */ 5365f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN); 5366c5c403dcSVardan Mikayelyan gpwrdn &= ~GPWRDN_RESTORE; 5367f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN); 5368c5c403dcSVardan Mikayelyan udelay(10); 5369c5c403dcSVardan Mikayelyan 5370c5c403dcSVardan Mikayelyan if (!rem_wakeup) { 5371f25c42b8SGevorg Sahakyan pcgcctl = dwc2_readl(hsotg, PCGCTL); 5372c5c403dcSVardan Mikayelyan pcgcctl &= ~PCGCTL_RSTPDWNMODULE; 5373f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, pcgcctl, PCGCTL); 5374c5c403dcSVardan Mikayelyan } 5375c5c403dcSVardan Mikayelyan 5376c5c403dcSVardan Mikayelyan /* Restore GUSBCFG, DCFG and DCTL */ 5377f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG); 5378f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dcfg, DCFG); 5379f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dctl, DCTL); 5380c5c403dcSVardan Mikayelyan 5381b29b494bSArtur Petrosyan /* On USB Reset, reset device address to zero */ 5382b29b494bSArtur Petrosyan if (reset) 5383b29b494bSArtur Petrosyan dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK); 5384b29b494bSArtur Petrosyan 5385c5c403dcSVardan Mikayelyan /* De-assert Wakeup Logic */ 5386f25c42b8SGevorg Sahakyan gpwrdn = dwc2_readl(hsotg, GPWRDN); 5387c5c403dcSVardan Mikayelyan gpwrdn &= ~GPWRDN_PMUACTV; 5388f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, gpwrdn, GPWRDN); 5389c5c403dcSVardan Mikayelyan 5390c5c403dcSVardan Mikayelyan if (rem_wakeup) { 5391c5c403dcSVardan Mikayelyan udelay(10); 5392c5c403dcSVardan Mikayelyan /* Start Remote Wakeup Signaling */ 5393f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dr->dctl | DCTL_RMTWKUPSIG, DCTL); 5394c5c403dcSVardan Mikayelyan } else { 5395c5c403dcSVardan Mikayelyan udelay(50); 5396c5c403dcSVardan Mikayelyan /* Set Device programming done bit */ 5397f25c42b8SGevorg Sahakyan dctl = dwc2_readl(hsotg, DCTL); 5398c5c403dcSVardan Mikayelyan dctl |= DCTL_PWRONPRGDONE; 5399f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dctl, DCTL); 5400c5c403dcSVardan Mikayelyan } 5401c5c403dcSVardan Mikayelyan /* Wait for interrupts which must be cleared */ 5402c5c403dcSVardan Mikayelyan mdelay(2); 5403c5c403dcSVardan Mikayelyan /* Clear all pending interupts */ 5404f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, 0xffffffff, GINTSTS); 5405c5c403dcSVardan Mikayelyan 5406c5c403dcSVardan Mikayelyan /* Restore global registers */ 5407c5c403dcSVardan Mikayelyan ret = dwc2_restore_global_registers(hsotg); 5408c5c403dcSVardan Mikayelyan if (ret) { 5409c5c403dcSVardan Mikayelyan dev_err(hsotg->dev, "%s: failed to restore registers\n", 5410c5c403dcSVardan Mikayelyan __func__); 5411c5c403dcSVardan Mikayelyan return ret; 5412c5c403dcSVardan Mikayelyan } 5413c5c403dcSVardan Mikayelyan 5414c5c403dcSVardan Mikayelyan /* Restore device registers */ 5415c5c403dcSVardan Mikayelyan ret = dwc2_restore_device_registers(hsotg, rem_wakeup); 5416c5c403dcSVardan Mikayelyan if (ret) { 5417c5c403dcSVardan Mikayelyan dev_err(hsotg->dev, "%s: failed to restore device registers\n", 5418c5c403dcSVardan Mikayelyan __func__); 5419c5c403dcSVardan Mikayelyan return ret; 5420c5c403dcSVardan Mikayelyan } 5421c5c403dcSVardan Mikayelyan 5422c5c403dcSVardan Mikayelyan if (rem_wakeup) { 5423c5c403dcSVardan Mikayelyan mdelay(10); 5424f25c42b8SGevorg Sahakyan dctl = dwc2_readl(hsotg, DCTL); 5425c5c403dcSVardan Mikayelyan dctl &= ~DCTL_RMTWKUPSIG; 5426f25c42b8SGevorg Sahakyan dwc2_writel(hsotg, dctl, DCTL); 5427c5c403dcSVardan Mikayelyan } 5428c5c403dcSVardan Mikayelyan 5429c5c403dcSVardan Mikayelyan hsotg->hibernated = 0; 5430c5c403dcSVardan Mikayelyan hsotg->lx_state = DWC2_L0; 5431c5c403dcSVardan Mikayelyan dev_dbg(hsotg->dev, "Hibernation recovery completes here\n"); 5432c5c403dcSVardan Mikayelyan 5433c5c403dcSVardan Mikayelyan return ret; 5434c5c403dcSVardan Mikayelyan } 5435be2b960eSArtur Petrosyan 5436be2b960eSArtur Petrosyan /** 5437be2b960eSArtur Petrosyan * dwc2_gadget_enter_partial_power_down() - Put controller in partial 5438be2b960eSArtur Petrosyan * power down. 5439be2b960eSArtur Petrosyan * 5440be2b960eSArtur Petrosyan * @hsotg: Programming view of the DWC_otg controller 5441be2b960eSArtur Petrosyan * 5442be2b960eSArtur Petrosyan * Return: non-zero if failed to enter device partial power down. 5443be2b960eSArtur Petrosyan * 5444be2b960eSArtur Petrosyan * This function is for entering device mode partial power down. 5445be2b960eSArtur Petrosyan */ 5446be2b960eSArtur Petrosyan int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg) 5447be2b960eSArtur Petrosyan { 5448be2b960eSArtur Petrosyan u32 pcgcctl; 5449be2b960eSArtur Petrosyan int ret = 0; 5450be2b960eSArtur Petrosyan 5451be2b960eSArtur Petrosyan dev_dbg(hsotg->dev, "Entering device partial power down started.\n"); 5452be2b960eSArtur Petrosyan 5453be2b960eSArtur Petrosyan /* Backup all registers */ 5454be2b960eSArtur Petrosyan ret = dwc2_backup_global_registers(hsotg); 5455be2b960eSArtur Petrosyan if (ret) { 5456be2b960eSArtur Petrosyan dev_err(hsotg->dev, "%s: failed to backup global registers\n", 5457be2b960eSArtur Petrosyan __func__); 5458be2b960eSArtur Petrosyan return ret; 5459be2b960eSArtur Petrosyan } 5460be2b960eSArtur Petrosyan 5461be2b960eSArtur Petrosyan ret = dwc2_backup_device_registers(hsotg); 5462be2b960eSArtur Petrosyan if (ret) { 5463be2b960eSArtur Petrosyan dev_err(hsotg->dev, "%s: failed to backup device registers\n", 5464be2b960eSArtur Petrosyan __func__); 5465be2b960eSArtur Petrosyan return ret; 5466be2b960eSArtur Petrosyan } 5467be2b960eSArtur Petrosyan 5468be2b960eSArtur Petrosyan /* 5469be2b960eSArtur Petrosyan * Clear any pending interrupts since dwc2 will not be able to 5470be2b960eSArtur Petrosyan * clear them after entering partial_power_down. 5471be2b960eSArtur Petrosyan */ 5472be2b960eSArtur Petrosyan dwc2_writel(hsotg, 0xffffffff, GINTSTS); 5473be2b960eSArtur Petrosyan 5474be2b960eSArtur Petrosyan /* Put the controller in low power state */ 5475be2b960eSArtur Petrosyan pcgcctl = dwc2_readl(hsotg, PCGCTL); 5476be2b960eSArtur Petrosyan 5477be2b960eSArtur Petrosyan pcgcctl |= PCGCTL_PWRCLMP; 5478be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL); 5479be2b960eSArtur Petrosyan udelay(5); 5480be2b960eSArtur Petrosyan 5481be2b960eSArtur Petrosyan pcgcctl |= PCGCTL_RSTPDWNMODULE; 5482be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL); 5483be2b960eSArtur Petrosyan udelay(5); 5484be2b960eSArtur Petrosyan 5485be2b960eSArtur Petrosyan pcgcctl |= PCGCTL_STOPPCLK; 5486be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL); 5487be2b960eSArtur Petrosyan 5488be2b960eSArtur Petrosyan /* Set in_ppd flag to 1 as here core enters suspend. */ 5489be2b960eSArtur Petrosyan hsotg->in_ppd = 1; 5490be2b960eSArtur Petrosyan hsotg->lx_state = DWC2_L2; 5491be2b960eSArtur Petrosyan 5492be2b960eSArtur Petrosyan dev_dbg(hsotg->dev, "Entering device partial power down completed.\n"); 5493be2b960eSArtur Petrosyan 5494be2b960eSArtur Petrosyan return ret; 5495be2b960eSArtur Petrosyan } 5496be2b960eSArtur Petrosyan 5497be2b960eSArtur Petrosyan /* 5498be2b960eSArtur Petrosyan * dwc2_gadget_exit_partial_power_down() - Exit controller from device partial 5499be2b960eSArtur Petrosyan * power down. 5500be2b960eSArtur Petrosyan * 5501be2b960eSArtur Petrosyan * @hsotg: Programming view of the DWC_otg controller 5502be2b960eSArtur Petrosyan * @restore: indicates whether need to restore the registers or not. 5503be2b960eSArtur Petrosyan * 5504be2b960eSArtur Petrosyan * Return: non-zero if failed to exit device partial power down. 5505be2b960eSArtur Petrosyan * 5506be2b960eSArtur Petrosyan * This function is for exiting from device mode partial power down. 5507be2b960eSArtur Petrosyan */ 5508be2b960eSArtur Petrosyan int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, 5509be2b960eSArtur Petrosyan bool restore) 5510be2b960eSArtur Petrosyan { 5511be2b960eSArtur Petrosyan u32 pcgcctl; 5512be2b960eSArtur Petrosyan u32 dctl; 5513be2b960eSArtur Petrosyan struct dwc2_dregs_backup *dr; 5514be2b960eSArtur Petrosyan int ret = 0; 5515be2b960eSArtur Petrosyan 5516be2b960eSArtur Petrosyan dr = &hsotg->dr_backup; 5517be2b960eSArtur Petrosyan 5518be2b960eSArtur Petrosyan dev_dbg(hsotg->dev, "Exiting device partial Power Down started.\n"); 5519be2b960eSArtur Petrosyan 5520be2b960eSArtur Petrosyan pcgcctl = dwc2_readl(hsotg, PCGCTL); 5521be2b960eSArtur Petrosyan pcgcctl &= ~PCGCTL_STOPPCLK; 5522be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL); 5523be2b960eSArtur Petrosyan 5524be2b960eSArtur Petrosyan pcgcctl = dwc2_readl(hsotg, PCGCTL); 5525be2b960eSArtur Petrosyan pcgcctl &= ~PCGCTL_PWRCLMP; 5526be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL); 5527be2b960eSArtur Petrosyan 5528be2b960eSArtur Petrosyan pcgcctl = dwc2_readl(hsotg, PCGCTL); 5529be2b960eSArtur Petrosyan pcgcctl &= ~PCGCTL_RSTPDWNMODULE; 5530be2b960eSArtur Petrosyan dwc2_writel(hsotg, pcgcctl, PCGCTL); 5531be2b960eSArtur Petrosyan 5532be2b960eSArtur Petrosyan udelay(100); 5533be2b960eSArtur Petrosyan if (restore) { 5534be2b960eSArtur Petrosyan ret = dwc2_restore_global_registers(hsotg); 5535be2b960eSArtur Petrosyan if (ret) { 5536be2b960eSArtur Petrosyan dev_err(hsotg->dev, "%s: failed to restore registers\n", 5537be2b960eSArtur Petrosyan __func__); 5538be2b960eSArtur Petrosyan return ret; 5539be2b960eSArtur Petrosyan } 5540be2b960eSArtur Petrosyan /* Restore DCFG */ 5541be2b960eSArtur Petrosyan dwc2_writel(hsotg, dr->dcfg, DCFG); 5542be2b960eSArtur Petrosyan 5543be2b960eSArtur Petrosyan ret = dwc2_restore_device_registers(hsotg, 0); 5544be2b960eSArtur Petrosyan if (ret) { 5545be2b960eSArtur Petrosyan dev_err(hsotg->dev, "%s: failed to restore device registers\n", 5546be2b960eSArtur Petrosyan __func__); 5547be2b960eSArtur Petrosyan return ret; 5548be2b960eSArtur Petrosyan } 5549be2b960eSArtur Petrosyan } 5550be2b960eSArtur Petrosyan 5551be2b960eSArtur Petrosyan /* Set the Power-On Programming done bit */ 5552be2b960eSArtur Petrosyan dctl = dwc2_readl(hsotg, DCTL); 5553be2b960eSArtur Petrosyan dctl |= DCTL_PWRONPRGDONE; 5554be2b960eSArtur Petrosyan dwc2_writel(hsotg, dctl, DCTL); 5555be2b960eSArtur Petrosyan 5556be2b960eSArtur Petrosyan /* Set in_ppd flag to 0 as here core exits from suspend. */ 5557be2b960eSArtur Petrosyan hsotg->in_ppd = 0; 5558be2b960eSArtur Petrosyan hsotg->lx_state = DWC2_L0; 5559be2b960eSArtur Petrosyan 5560be2b960eSArtur Petrosyan dev_dbg(hsotg->dev, "Exiting device partial Power Down completed.\n"); 5561be2b960eSArtur Petrosyan return ret; 5562be2b960eSArtur Petrosyan } 5563012466fcSArtur Petrosyan 5564012466fcSArtur Petrosyan /** 5565012466fcSArtur Petrosyan * dwc2_gadget_enter_clock_gating() - Put controller in clock gating. 5566012466fcSArtur Petrosyan * 5567012466fcSArtur Petrosyan * @hsotg: Programming view of the DWC_otg controller 5568012466fcSArtur Petrosyan * 5569012466fcSArtur Petrosyan * Return: non-zero if failed to enter device partial power down. 5570012466fcSArtur Petrosyan * 5571012466fcSArtur Petrosyan * This function is for entering device mode clock gating. 5572012466fcSArtur Petrosyan */ 5573012466fcSArtur Petrosyan void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg) 5574012466fcSArtur Petrosyan { 5575012466fcSArtur Petrosyan u32 pcgctl; 5576012466fcSArtur Petrosyan 5577012466fcSArtur Petrosyan dev_dbg(hsotg->dev, "Entering device clock gating.\n"); 5578012466fcSArtur Petrosyan 5579012466fcSArtur Petrosyan /* Set the Phy Clock bit as suspend is received. */ 5580012466fcSArtur Petrosyan pcgctl = dwc2_readl(hsotg, PCGCTL); 5581012466fcSArtur Petrosyan pcgctl |= PCGCTL_STOPPCLK; 5582012466fcSArtur Petrosyan dwc2_writel(hsotg, pcgctl, PCGCTL); 5583012466fcSArtur Petrosyan udelay(5); 5584012466fcSArtur Petrosyan 5585012466fcSArtur Petrosyan /* Set the Gate hclk as suspend is received. */ 5586012466fcSArtur Petrosyan pcgctl = dwc2_readl(hsotg, PCGCTL); 5587012466fcSArtur Petrosyan pcgctl |= PCGCTL_GATEHCLK; 5588012466fcSArtur Petrosyan dwc2_writel(hsotg, pcgctl, PCGCTL); 5589012466fcSArtur Petrosyan udelay(5); 5590012466fcSArtur Petrosyan 5591012466fcSArtur Petrosyan hsotg->lx_state = DWC2_L2; 5592012466fcSArtur Petrosyan hsotg->bus_suspended = true; 5593012466fcSArtur Petrosyan } 5594012466fcSArtur Petrosyan 5595012466fcSArtur Petrosyan /* 5596012466fcSArtur Petrosyan * dwc2_gadget_exit_clock_gating() - Exit controller from device clock gating. 5597012466fcSArtur Petrosyan * 5598012466fcSArtur Petrosyan * @hsotg: Programming view of the DWC_otg controller 5599012466fcSArtur Petrosyan * @rem_wakeup: indicates whether remote wake up is enabled. 5600012466fcSArtur Petrosyan * 5601012466fcSArtur Petrosyan * This function is for exiting from device mode clock gating. 5602012466fcSArtur Petrosyan */ 5603012466fcSArtur Petrosyan void dwc2_gadget_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup) 5604012466fcSArtur Petrosyan { 5605012466fcSArtur Petrosyan u32 pcgctl; 5606012466fcSArtur Petrosyan u32 dctl; 5607012466fcSArtur Petrosyan 5608012466fcSArtur Petrosyan dev_dbg(hsotg->dev, "Exiting device clock gating.\n"); 5609012466fcSArtur Petrosyan 5610012466fcSArtur Petrosyan /* Clear the Gate hclk. */ 5611012466fcSArtur Petrosyan pcgctl = dwc2_readl(hsotg, PCGCTL); 5612012466fcSArtur Petrosyan pcgctl &= ~PCGCTL_GATEHCLK; 5613012466fcSArtur Petrosyan dwc2_writel(hsotg, pcgctl, PCGCTL); 5614012466fcSArtur Petrosyan udelay(5); 5615012466fcSArtur Petrosyan 5616012466fcSArtur Petrosyan /* Phy Clock bit. */ 5617012466fcSArtur Petrosyan pcgctl = dwc2_readl(hsotg, PCGCTL); 5618012466fcSArtur Petrosyan pcgctl &= ~PCGCTL_STOPPCLK; 5619012466fcSArtur Petrosyan dwc2_writel(hsotg, pcgctl, PCGCTL); 5620012466fcSArtur Petrosyan udelay(5); 5621012466fcSArtur Petrosyan 5622012466fcSArtur Petrosyan if (rem_wakeup) { 5623012466fcSArtur Petrosyan /* Set Remote Wakeup Signaling */ 5624012466fcSArtur Petrosyan dctl = dwc2_readl(hsotg, DCTL); 5625012466fcSArtur Petrosyan dctl |= DCTL_RMTWKUPSIG; 5626012466fcSArtur Petrosyan dwc2_writel(hsotg, dctl, DCTL); 5627012466fcSArtur Petrosyan } 5628012466fcSArtur Petrosyan 5629012466fcSArtur Petrosyan /* Change to L0 state */ 5630012466fcSArtur Petrosyan call_gadget(hsotg, resume); 5631012466fcSArtur Petrosyan hsotg->lx_state = DWC2_L0; 5632012466fcSArtur Petrosyan hsotg->bus_suspended = false; 5633012466fcSArtur Petrosyan } 5634