18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * c67x00-sched.c: Cypress C67X00 USB Host Controller Driver - TD scheduling 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2006-2008 Barco N.V. 68c2ecf20Sopenharmony_ci * Derived from the Cypress cy7c67200/300 ezusb linux driver and 78c2ecf20Sopenharmony_ci * based on multiple host controller drivers inside the linux kernel. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kthread.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "c67x00.h" 148c2ecf20Sopenharmony_ci#include "c67x00-hcd.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* 178c2ecf20Sopenharmony_ci * These are the stages for a control urb, they are kept 188c2ecf20Sopenharmony_ci * in both urb->interval and td->privdata. 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci#define SETUP_STAGE 0 218c2ecf20Sopenharmony_ci#define DATA_STAGE 1 228c2ecf20Sopenharmony_ci#define STATUS_STAGE 2 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * struct c67x00_ep_data: Host endpoint data structure 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_cistruct c67x00_ep_data { 308c2ecf20Sopenharmony_ci struct list_head queue; 318c2ecf20Sopenharmony_ci struct list_head node; 328c2ecf20Sopenharmony_ci struct usb_host_endpoint *hep; 338c2ecf20Sopenharmony_ci struct usb_device *dev; 348c2ecf20Sopenharmony_ci u16 next_frame; /* For int/isoc transactions */ 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * struct c67x00_td 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * Hardware parts are little endiannes, SW in CPU endianess. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_cistruct c67x00_td { 438c2ecf20Sopenharmony_ci /* HW specific part */ 448c2ecf20Sopenharmony_ci __le16 ly_base_addr; /* Bytes 0-1 */ 458c2ecf20Sopenharmony_ci __le16 port_length; /* Bytes 2-3 */ 468c2ecf20Sopenharmony_ci u8 pid_ep; /* Byte 4 */ 478c2ecf20Sopenharmony_ci u8 dev_addr; /* Byte 5 */ 488c2ecf20Sopenharmony_ci u8 ctrl_reg; /* Byte 6 */ 498c2ecf20Sopenharmony_ci u8 status; /* Byte 7 */ 508c2ecf20Sopenharmony_ci u8 retry_cnt; /* Byte 8 */ 518c2ecf20Sopenharmony_ci#define TT_OFFSET 2 528c2ecf20Sopenharmony_ci#define TT_CONTROL 0 538c2ecf20Sopenharmony_ci#define TT_ISOCHRONOUS 1 548c2ecf20Sopenharmony_ci#define TT_BULK 2 558c2ecf20Sopenharmony_ci#define TT_INTERRUPT 3 568c2ecf20Sopenharmony_ci u8 residue; /* Byte 9 */ 578c2ecf20Sopenharmony_ci __le16 next_td_addr; /* Bytes 10-11 */ 588c2ecf20Sopenharmony_ci /* SW part */ 598c2ecf20Sopenharmony_ci struct list_head td_list; 608c2ecf20Sopenharmony_ci u16 td_addr; 618c2ecf20Sopenharmony_ci void *data; 628c2ecf20Sopenharmony_ci struct urb *urb; 638c2ecf20Sopenharmony_ci unsigned long privdata; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* These are needed for handling the toggle bits: 668c2ecf20Sopenharmony_ci * an urb can be dequeued while a td is in progress 678c2ecf20Sopenharmony_ci * after checking the td, the toggle bit might need to 688c2ecf20Sopenharmony_ci * be fixed */ 698c2ecf20Sopenharmony_ci struct c67x00_ep_data *ep_data; 708c2ecf20Sopenharmony_ci unsigned int pipe; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistruct c67x00_urb_priv { 748c2ecf20Sopenharmony_ci struct list_head hep_node; 758c2ecf20Sopenharmony_ci struct urb *urb; 768c2ecf20Sopenharmony_ci int port; 778c2ecf20Sopenharmony_ci int cnt; /* packet number for isoc */ 788c2ecf20Sopenharmony_ci int status; 798c2ecf20Sopenharmony_ci struct c67x00_ep_data *ep_data; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define td_udev(td) ((td)->ep_data->dev) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define CY_TD_SIZE 12 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define TD_PIDEP_OFFSET 0x04 878c2ecf20Sopenharmony_ci#define TD_PIDEPMASK_PID 0xF0 888c2ecf20Sopenharmony_ci#define TD_PIDEPMASK_EP 0x0F 898c2ecf20Sopenharmony_ci#define TD_PORTLENMASK_DL 0x03FF 908c2ecf20Sopenharmony_ci#define TD_PORTLENMASK_PN 0xC000 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define TD_STATUS_OFFSET 0x07 938c2ecf20Sopenharmony_ci#define TD_STATUSMASK_ACK 0x01 948c2ecf20Sopenharmony_ci#define TD_STATUSMASK_ERR 0x02 958c2ecf20Sopenharmony_ci#define TD_STATUSMASK_TMOUT 0x04 968c2ecf20Sopenharmony_ci#define TD_STATUSMASK_SEQ 0x08 978c2ecf20Sopenharmony_ci#define TD_STATUSMASK_SETUP 0x10 988c2ecf20Sopenharmony_ci#define TD_STATUSMASK_OVF 0x20 998c2ecf20Sopenharmony_ci#define TD_STATUSMASK_NAK 0x40 1008c2ecf20Sopenharmony_ci#define TD_STATUSMASK_STALL 0x80 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#define TD_ERROR_MASK (TD_STATUSMASK_ERR | TD_STATUSMASK_TMOUT | \ 1038c2ecf20Sopenharmony_ci TD_STATUSMASK_STALL) 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#define TD_RETRYCNT_OFFSET 0x08 1068c2ecf20Sopenharmony_ci#define TD_RETRYCNTMASK_ACT_FLG 0x10 1078c2ecf20Sopenharmony_ci#define TD_RETRYCNTMASK_TX_TYPE 0x0C 1088c2ecf20Sopenharmony_ci#define TD_RETRYCNTMASK_RTY_CNT 0x03 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#define TD_RESIDUE_OVERFLOW 0x80 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#define TD_PID_IN 0x90 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* Residue: signed 8bits, neg -> OVERFLOW, pos -> UNDERFLOW */ 1158c2ecf20Sopenharmony_ci#define td_residue(td) ((__s8)(td->residue)) 1168c2ecf20Sopenharmony_ci#define td_ly_base_addr(td) (__le16_to_cpu((td)->ly_base_addr)) 1178c2ecf20Sopenharmony_ci#define td_port_length(td) (__le16_to_cpu((td)->port_length)) 1188c2ecf20Sopenharmony_ci#define td_next_td_addr(td) (__le16_to_cpu((td)->next_td_addr)) 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define td_active(td) ((td)->retry_cnt & TD_RETRYCNTMASK_ACT_FLG) 1218c2ecf20Sopenharmony_ci#define td_length(td) (td_port_length(td) & TD_PORTLENMASK_DL) 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define td_sequence_ok(td) (!td->status || \ 1248c2ecf20Sopenharmony_ci (!(td->status & TD_STATUSMASK_SEQ) == \ 1258c2ecf20Sopenharmony_ci !(td->ctrl_reg & SEQ_SEL))) 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci#define td_acked(td) (!td->status || \ 1288c2ecf20Sopenharmony_ci (td->status & TD_STATUSMASK_ACK)) 1298c2ecf20Sopenharmony_ci#define td_actual_bytes(td) (td_length(td) - td_residue(td)) 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* 1348c2ecf20Sopenharmony_ci * dbg_td - Dump the contents of the TD 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_cistatic void dbg_td(struct c67x00_hcd *c67x00, struct c67x00_td *td, char *msg) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct device *dev = c67x00_hcd_dev(c67x00); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci dev_dbg(dev, "### %s at 0x%04x\n", msg, td->td_addr); 1418c2ecf20Sopenharmony_ci dev_dbg(dev, "urb: 0x%p\n", td->urb); 1428c2ecf20Sopenharmony_ci dev_dbg(dev, "endpoint: %4d\n", usb_pipeendpoint(td->pipe)); 1438c2ecf20Sopenharmony_ci dev_dbg(dev, "pipeout: %4d\n", usb_pipeout(td->pipe)); 1448c2ecf20Sopenharmony_ci dev_dbg(dev, "ly_base_addr: 0x%04x\n", td_ly_base_addr(td)); 1458c2ecf20Sopenharmony_ci dev_dbg(dev, "port_length: 0x%04x\n", td_port_length(td)); 1468c2ecf20Sopenharmony_ci dev_dbg(dev, "pid_ep: 0x%02x\n", td->pid_ep); 1478c2ecf20Sopenharmony_ci dev_dbg(dev, "dev_addr: 0x%02x\n", td->dev_addr); 1488c2ecf20Sopenharmony_ci dev_dbg(dev, "ctrl_reg: 0x%02x\n", td->ctrl_reg); 1498c2ecf20Sopenharmony_ci dev_dbg(dev, "status: 0x%02x\n", td->status); 1508c2ecf20Sopenharmony_ci dev_dbg(dev, "retry_cnt: 0x%02x\n", td->retry_cnt); 1518c2ecf20Sopenharmony_ci dev_dbg(dev, "residue: 0x%02x\n", td->residue); 1528c2ecf20Sopenharmony_ci dev_dbg(dev, "next_td_addr: 0x%04x\n", td_next_td_addr(td)); 1538c2ecf20Sopenharmony_ci dev_dbg(dev, "data: %*ph\n", td_length(td), td->data); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 1578c2ecf20Sopenharmony_ci/* Helper functions */ 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic inline u16 c67x00_get_current_frame_number(struct c67x00_hcd *c67x00) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci return c67x00_ll_husb_get_frame(c67x00->sie) & HOST_FRAME_MASK; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/* 1658c2ecf20Sopenharmony_ci * frame_add 1668c2ecf20Sopenharmony_ci * Software wraparound for framenumbers. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_cistatic inline u16 frame_add(u16 a, u16 b) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci return (a + b) & HOST_FRAME_MASK; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* 1748c2ecf20Sopenharmony_ci * frame_after - is frame a after frame b 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_cistatic inline int frame_after(u16 a, u16 b) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci return ((HOST_FRAME_MASK + a - b) & HOST_FRAME_MASK) < 1798c2ecf20Sopenharmony_ci (HOST_FRAME_MASK / 2); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/* 1838c2ecf20Sopenharmony_ci * frame_after_eq - is frame a after or equal to frame b 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistatic inline int frame_after_eq(u16 a, u16 b) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci return ((HOST_FRAME_MASK + 1 + a - b) & HOST_FRAME_MASK) < 1888c2ecf20Sopenharmony_ci (HOST_FRAME_MASK / 2); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/* 1948c2ecf20Sopenharmony_ci * c67x00_release_urb - remove link from all tds to this urb 1958c2ecf20Sopenharmony_ci * Disconnects the urb from it's tds, so that it can be given back. 1968c2ecf20Sopenharmony_ci * pre: urb->hcpriv != NULL 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_cistatic void c67x00_release_urb(struct c67x00_hcd *c67x00, struct urb *urb) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct c67x00_td *td; 2018c2ecf20Sopenharmony_ci struct c67x00_urb_priv *urbp; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci BUG_ON(!urb); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci c67x00->urb_count--; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { 2088c2ecf20Sopenharmony_ci c67x00->urb_iso_count--; 2098c2ecf20Sopenharmony_ci if (c67x00->urb_iso_count == 0) 2108c2ecf20Sopenharmony_ci c67x00->max_frame_bw = MAX_FRAME_BW_STD; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* TODO this might be not so efficient when we've got many urbs! 2148c2ecf20Sopenharmony_ci * Alternatives: 2158c2ecf20Sopenharmony_ci * * only clear when needed 2168c2ecf20Sopenharmony_ci * * keep a list of tds with each urbp 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci list_for_each_entry(td, &c67x00->td_list, td_list) 2198c2ecf20Sopenharmony_ci if (urb == td->urb) 2208c2ecf20Sopenharmony_ci td->urb = NULL; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci urbp = urb->hcpriv; 2238c2ecf20Sopenharmony_ci urb->hcpriv = NULL; 2248c2ecf20Sopenharmony_ci list_del(&urbp->hep_node); 2258c2ecf20Sopenharmony_ci kfree(urbp); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic struct c67x00_ep_data * 2318c2ecf20Sopenharmony_cic67x00_ep_data_alloc(struct c67x00_hcd *c67x00, struct urb *urb) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct usb_host_endpoint *hep = urb->ep; 2348c2ecf20Sopenharmony_ci struct c67x00_ep_data *ep_data; 2358c2ecf20Sopenharmony_ci int type; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci c67x00->current_frame = c67x00_get_current_frame_number(c67x00); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* Check if endpoint already has a c67x00_ep_data struct allocated */ 2408c2ecf20Sopenharmony_ci if (hep->hcpriv) { 2418c2ecf20Sopenharmony_ci ep_data = hep->hcpriv; 2428c2ecf20Sopenharmony_ci if (frame_after(c67x00->current_frame, ep_data->next_frame)) 2438c2ecf20Sopenharmony_ci ep_data->next_frame = 2448c2ecf20Sopenharmony_ci frame_add(c67x00->current_frame, 1); 2458c2ecf20Sopenharmony_ci return hep->hcpriv; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Allocate and initialize a new c67x00 endpoint data structure */ 2498c2ecf20Sopenharmony_ci ep_data = kzalloc(sizeof(*ep_data), GFP_ATOMIC); 2508c2ecf20Sopenharmony_ci if (!ep_data) 2518c2ecf20Sopenharmony_ci return NULL; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ep_data->queue); 2548c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ep_data->node); 2558c2ecf20Sopenharmony_ci ep_data->hep = hep; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* hold a reference to udev as long as this endpoint lives, 2588c2ecf20Sopenharmony_ci * this is needed to possibly fix the data toggle */ 2598c2ecf20Sopenharmony_ci ep_data->dev = usb_get_dev(urb->dev); 2608c2ecf20Sopenharmony_ci hep->hcpriv = ep_data; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* For ISOC and INT endpoints, start ASAP: */ 2638c2ecf20Sopenharmony_ci ep_data->next_frame = frame_add(c67x00->current_frame, 1); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* Add the endpoint data to one of the pipe lists; must be added 2668c2ecf20Sopenharmony_ci in order of endpoint address */ 2678c2ecf20Sopenharmony_ci type = usb_pipetype(urb->pipe); 2688c2ecf20Sopenharmony_ci if (list_empty(&ep_data->node)) { 2698c2ecf20Sopenharmony_ci list_add(&ep_data->node, &c67x00->list[type]); 2708c2ecf20Sopenharmony_ci } else { 2718c2ecf20Sopenharmony_ci struct c67x00_ep_data *prev; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci list_for_each_entry(prev, &c67x00->list[type], node) { 2748c2ecf20Sopenharmony_ci if (prev->hep->desc.bEndpointAddress > 2758c2ecf20Sopenharmony_ci hep->desc.bEndpointAddress) { 2768c2ecf20Sopenharmony_ci list_add(&ep_data->node, prev->node.prev); 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return ep_data; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int c67x00_ep_data_free(struct usb_host_endpoint *hep) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct c67x00_ep_data *ep_data = hep->hcpriv; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (!ep_data) 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (!list_empty(&ep_data->queue)) 2938c2ecf20Sopenharmony_ci return -EBUSY; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci usb_put_dev(ep_data->dev); 2968c2ecf20Sopenharmony_ci list_del(&ep_data->queue); 2978c2ecf20Sopenharmony_ci list_del(&ep_data->node); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci kfree(ep_data); 3008c2ecf20Sopenharmony_ci hep->hcpriv = NULL; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return 0; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_civoid c67x00_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd); 3088c2ecf20Sopenharmony_ci unsigned long flags; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (!list_empty(&ep->urb_list)) 3118c2ecf20Sopenharmony_ci dev_warn(c67x00_hcd_dev(c67x00), "error: urb list not empty\n"); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci spin_lock_irqsave(&c67x00->lock, flags); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* loop waiting for all transfers in the endpoint queue to complete */ 3168c2ecf20Sopenharmony_ci while (c67x00_ep_data_free(ep)) { 3178c2ecf20Sopenharmony_ci /* Drop the lock so we can sleep waiting for the hardware */ 3188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&c67x00->lock, flags); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* it could happen that we reinitialize this completion, while 3218c2ecf20Sopenharmony_ci * somebody was waiting for that completion. The timeout and 3228c2ecf20Sopenharmony_ci * while loop handle such cases, but this might be improved */ 3238c2ecf20Sopenharmony_ci reinit_completion(&c67x00->endpoint_disable); 3248c2ecf20Sopenharmony_ci c67x00_sched_kick(c67x00); 3258c2ecf20Sopenharmony_ci wait_for_completion_timeout(&c67x00->endpoint_disable, 1 * HZ); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci spin_lock_irqsave(&c67x00->lock, flags); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&c67x00->lock, flags); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic inline int get_root_port(struct usb_device *dev) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci while (dev->parent->parent) 3388c2ecf20Sopenharmony_ci dev = dev->parent; 3398c2ecf20Sopenharmony_ci return dev->portnum; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ciint c67x00_urb_enqueue(struct usb_hcd *hcd, 3438c2ecf20Sopenharmony_ci struct urb *urb, gfp_t mem_flags) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci int ret; 3468c2ecf20Sopenharmony_ci unsigned long flags; 3478c2ecf20Sopenharmony_ci struct c67x00_urb_priv *urbp; 3488c2ecf20Sopenharmony_ci struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd); 3498c2ecf20Sopenharmony_ci int port = get_root_port(urb->dev)-1; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* Allocate and initialize urb private data */ 3528c2ecf20Sopenharmony_ci urbp = kzalloc(sizeof(*urbp), mem_flags); 3538c2ecf20Sopenharmony_ci if (!urbp) { 3548c2ecf20Sopenharmony_ci ret = -ENOMEM; 3558c2ecf20Sopenharmony_ci goto err_urbp; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci spin_lock_irqsave(&c67x00->lock, flags); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* Make sure host controller is running */ 3618c2ecf20Sopenharmony_ci if (!HC_IS_RUNNING(hcd->state)) { 3628c2ecf20Sopenharmony_ci ret = -ENODEV; 3638c2ecf20Sopenharmony_ci goto err_not_linked; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ret = usb_hcd_link_urb_to_ep(hcd, urb); 3678c2ecf20Sopenharmony_ci if (ret) 3688c2ecf20Sopenharmony_ci goto err_not_linked; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&urbp->hep_node); 3718c2ecf20Sopenharmony_ci urbp->urb = urb; 3728c2ecf20Sopenharmony_ci urbp->port = port; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci urbp->ep_data = c67x00_ep_data_alloc(c67x00, urb); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (!urbp->ep_data) { 3778c2ecf20Sopenharmony_ci ret = -ENOMEM; 3788c2ecf20Sopenharmony_ci goto err_epdata; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* TODO claim bandwidth with usb_claim_bandwidth? 3828c2ecf20Sopenharmony_ci * also release it somewhere! */ 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci urb->hcpriv = urbp; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci urb->actual_length = 0; /* Nothing received/transmitted yet */ 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci switch (usb_pipetype(urb->pipe)) { 3898c2ecf20Sopenharmony_ci case PIPE_CONTROL: 3908c2ecf20Sopenharmony_ci urb->interval = SETUP_STAGE; 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci case PIPE_INTERRUPT: 3938c2ecf20Sopenharmony_ci break; 3948c2ecf20Sopenharmony_ci case PIPE_BULK: 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci case PIPE_ISOCHRONOUS: 3978c2ecf20Sopenharmony_ci if (c67x00->urb_iso_count == 0) 3988c2ecf20Sopenharmony_ci c67x00->max_frame_bw = MAX_FRAME_BW_ISO; 3998c2ecf20Sopenharmony_ci c67x00->urb_iso_count++; 4008c2ecf20Sopenharmony_ci /* Assume always URB_ISO_ASAP, FIXME */ 4018c2ecf20Sopenharmony_ci if (list_empty(&urbp->ep_data->queue)) 4028c2ecf20Sopenharmony_ci urb->start_frame = urbp->ep_data->next_frame; 4038c2ecf20Sopenharmony_ci else { 4048c2ecf20Sopenharmony_ci /* Go right after the last one */ 4058c2ecf20Sopenharmony_ci struct urb *last_urb; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci last_urb = list_entry(urbp->ep_data->queue.prev, 4088c2ecf20Sopenharmony_ci struct c67x00_urb_priv, 4098c2ecf20Sopenharmony_ci hep_node)->urb; 4108c2ecf20Sopenharmony_ci urb->start_frame = 4118c2ecf20Sopenharmony_ci frame_add(last_urb->start_frame, 4128c2ecf20Sopenharmony_ci last_urb->number_of_packets * 4138c2ecf20Sopenharmony_ci last_urb->interval); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci urbp->cnt = 0; 4168c2ecf20Sopenharmony_ci break; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* Add the URB to the endpoint queue */ 4208c2ecf20Sopenharmony_ci list_add_tail(&urbp->hep_node, &urbp->ep_data->queue); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* If this is the only URB, kick start the controller */ 4238c2ecf20Sopenharmony_ci if (!c67x00->urb_count++) 4248c2ecf20Sopenharmony_ci c67x00_ll_hpi_enable_sofeop(c67x00->sie); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci c67x00_sched_kick(c67x00); 4278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&c67x00->lock, flags); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cierr_epdata: 4328c2ecf20Sopenharmony_ci usb_hcd_unlink_urb_from_ep(hcd, urb); 4338c2ecf20Sopenharmony_cierr_not_linked: 4348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&c67x00->lock, flags); 4358c2ecf20Sopenharmony_ci kfree(urbp); 4368c2ecf20Sopenharmony_cierr_urbp: 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return ret; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ciint c67x00_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd); 4448c2ecf20Sopenharmony_ci unsigned long flags; 4458c2ecf20Sopenharmony_ci int rc; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci spin_lock_irqsave(&c67x00->lock, flags); 4488c2ecf20Sopenharmony_ci rc = usb_hcd_check_unlink_urb(hcd, urb, status); 4498c2ecf20Sopenharmony_ci if (rc) 4508c2ecf20Sopenharmony_ci goto done; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci c67x00_release_urb(c67x00, urb); 4538c2ecf20Sopenharmony_ci usb_hcd_unlink_urb_from_ep(hcd, urb); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci spin_unlock(&c67x00->lock); 4568c2ecf20Sopenharmony_ci usb_hcd_giveback_urb(hcd, urb, status); 4578c2ecf20Sopenharmony_ci spin_lock(&c67x00->lock); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&c67x00->lock, flags); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return 0; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci done: 4648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&c67x00->lock, flags); 4658c2ecf20Sopenharmony_ci return rc; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci/* 4718c2ecf20Sopenharmony_ci * pre: c67x00 locked, urb unlocked 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_cistatic void 4748c2ecf20Sopenharmony_cic67x00_giveback_urb(struct c67x00_hcd *c67x00, struct urb *urb, int status) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct c67x00_urb_priv *urbp; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (!urb) 4798c2ecf20Sopenharmony_ci return; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci urbp = urb->hcpriv; 4828c2ecf20Sopenharmony_ci urbp->status = status; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci list_del_init(&urbp->hep_node); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci c67x00_release_urb(c67x00, urb); 4878c2ecf20Sopenharmony_ci usb_hcd_unlink_urb_from_ep(c67x00_hcd_to_hcd(c67x00), urb); 4888c2ecf20Sopenharmony_ci spin_unlock(&c67x00->lock); 4898c2ecf20Sopenharmony_ci usb_hcd_giveback_urb(c67x00_hcd_to_hcd(c67x00), urb, status); 4908c2ecf20Sopenharmony_ci spin_lock(&c67x00->lock); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic int c67x00_claim_frame_bw(struct c67x00_hcd *c67x00, struct urb *urb, 4968c2ecf20Sopenharmony_ci int len, int periodic) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct c67x00_urb_priv *urbp = urb->hcpriv; 4998c2ecf20Sopenharmony_ci int bit_time; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* According to the C67x00 BIOS user manual, page 3-18,19, the 5028c2ecf20Sopenharmony_ci * following calculations provide the full speed bit times for 5038c2ecf20Sopenharmony_ci * a transaction. 5048c2ecf20Sopenharmony_ci * 5058c2ecf20Sopenharmony_ci * FS(in) = 112.5 + 9.36*BC + HOST_DELAY 5068c2ecf20Sopenharmony_ci * FS(in,iso) = 90.5 + 9.36*BC + HOST_DELAY 5078c2ecf20Sopenharmony_ci * FS(out) = 112.5 + 9.36*BC + HOST_DELAY 5088c2ecf20Sopenharmony_ci * FS(out,iso) = 78.4 + 9.36*BC + HOST_DELAY 5098c2ecf20Sopenharmony_ci * LS(in) = 802.4 + 75.78*BC + HOST_DELAY 5108c2ecf20Sopenharmony_ci * LS(out) = 802.6 + 74.67*BC + HOST_DELAY 5118c2ecf20Sopenharmony_ci * 5128c2ecf20Sopenharmony_ci * HOST_DELAY == 106 for the c67200 and c67300. 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* make calculations in 1/100 bit times to maintain resolution */ 5168c2ecf20Sopenharmony_ci if (urbp->ep_data->dev->speed == USB_SPEED_LOW) { 5178c2ecf20Sopenharmony_ci /* Low speed pipe */ 5188c2ecf20Sopenharmony_ci if (usb_pipein(urb->pipe)) 5198c2ecf20Sopenharmony_ci bit_time = 80240 + 7578*len; 5208c2ecf20Sopenharmony_ci else 5218c2ecf20Sopenharmony_ci bit_time = 80260 + 7467*len; 5228c2ecf20Sopenharmony_ci } else { 5238c2ecf20Sopenharmony_ci /* FS pipes */ 5248c2ecf20Sopenharmony_ci if (usb_pipeisoc(urb->pipe)) 5258c2ecf20Sopenharmony_ci bit_time = usb_pipein(urb->pipe) ? 9050 : 7840; 5268c2ecf20Sopenharmony_ci else 5278c2ecf20Sopenharmony_ci bit_time = 11250; 5288c2ecf20Sopenharmony_ci bit_time += 936*len; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* Scale back down to integer bit times. Use a host delay of 106. 5328c2ecf20Sopenharmony_ci * (this is the only place it is used) */ 5338c2ecf20Sopenharmony_ci bit_time = ((bit_time+50) / 100) + 106; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (unlikely(bit_time + c67x00->bandwidth_allocated >= 5368c2ecf20Sopenharmony_ci c67x00->max_frame_bw)) 5378c2ecf20Sopenharmony_ci return -EMSGSIZE; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (unlikely(c67x00->next_td_addr + CY_TD_SIZE >= 5408c2ecf20Sopenharmony_ci c67x00->td_base_addr + SIE_TD_SIZE)) 5418c2ecf20Sopenharmony_ci return -EMSGSIZE; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (unlikely(c67x00->next_buf_addr + len >= 5448c2ecf20Sopenharmony_ci c67x00->buf_base_addr + SIE_TD_BUF_SIZE)) 5458c2ecf20Sopenharmony_ci return -EMSGSIZE; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (periodic) { 5488c2ecf20Sopenharmony_ci if (unlikely(bit_time + c67x00->periodic_bw_allocated >= 5498c2ecf20Sopenharmony_ci MAX_PERIODIC_BW(c67x00->max_frame_bw))) 5508c2ecf20Sopenharmony_ci return -EMSGSIZE; 5518c2ecf20Sopenharmony_ci c67x00->periodic_bw_allocated += bit_time; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci c67x00->bandwidth_allocated += bit_time; 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci/* 5618c2ecf20Sopenharmony_ci * td_addr and buf_addr must be word aligned 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_cistatic int c67x00_create_td(struct c67x00_hcd *c67x00, struct urb *urb, 5648c2ecf20Sopenharmony_ci void *data, int len, int pid, int toggle, 5658c2ecf20Sopenharmony_ci unsigned long privdata) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct c67x00_td *td; 5688c2ecf20Sopenharmony_ci struct c67x00_urb_priv *urbp = urb->hcpriv; 5698c2ecf20Sopenharmony_ci const __u8 active_flag = 1, retry_cnt = 3; 5708c2ecf20Sopenharmony_ci __u8 cmd = 0; 5718c2ecf20Sopenharmony_ci int tt = 0; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (c67x00_claim_frame_bw(c67x00, urb, len, usb_pipeisoc(urb->pipe) 5748c2ecf20Sopenharmony_ci || usb_pipeint(urb->pipe))) 5758c2ecf20Sopenharmony_ci return -EMSGSIZE; /* Not really an error, but expected */ 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci td = kzalloc(sizeof(*td), GFP_ATOMIC); 5788c2ecf20Sopenharmony_ci if (!td) 5798c2ecf20Sopenharmony_ci return -ENOMEM; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci td->pipe = urb->pipe; 5828c2ecf20Sopenharmony_ci td->ep_data = urbp->ep_data; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if ((td_udev(td)->speed == USB_SPEED_LOW) && 5858c2ecf20Sopenharmony_ci !(c67x00->low_speed_ports & (1 << urbp->port))) 5868c2ecf20Sopenharmony_ci cmd |= PREAMBLE_EN; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci switch (usb_pipetype(td->pipe)) { 5898c2ecf20Sopenharmony_ci case PIPE_ISOCHRONOUS: 5908c2ecf20Sopenharmony_ci tt = TT_ISOCHRONOUS; 5918c2ecf20Sopenharmony_ci cmd |= ISO_EN; 5928c2ecf20Sopenharmony_ci break; 5938c2ecf20Sopenharmony_ci case PIPE_CONTROL: 5948c2ecf20Sopenharmony_ci tt = TT_CONTROL; 5958c2ecf20Sopenharmony_ci break; 5968c2ecf20Sopenharmony_ci case PIPE_BULK: 5978c2ecf20Sopenharmony_ci tt = TT_BULK; 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci case PIPE_INTERRUPT: 6008c2ecf20Sopenharmony_ci tt = TT_INTERRUPT; 6018c2ecf20Sopenharmony_ci break; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (toggle) 6058c2ecf20Sopenharmony_ci cmd |= SEQ_SEL; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci cmd |= ARM_EN; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* SW part */ 6108c2ecf20Sopenharmony_ci td->td_addr = c67x00->next_td_addr; 6118c2ecf20Sopenharmony_ci c67x00->next_td_addr = c67x00->next_td_addr + CY_TD_SIZE; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* HW part */ 6148c2ecf20Sopenharmony_ci td->ly_base_addr = __cpu_to_le16(c67x00->next_buf_addr); 6158c2ecf20Sopenharmony_ci td->port_length = __cpu_to_le16((c67x00->sie->sie_num << 15) | 6168c2ecf20Sopenharmony_ci (urbp->port << 14) | (len & 0x3FF)); 6178c2ecf20Sopenharmony_ci td->pid_ep = ((pid & 0xF) << TD_PIDEP_OFFSET) | 6188c2ecf20Sopenharmony_ci (usb_pipeendpoint(td->pipe) & 0xF); 6198c2ecf20Sopenharmony_ci td->dev_addr = usb_pipedevice(td->pipe) & 0x7F; 6208c2ecf20Sopenharmony_ci td->ctrl_reg = cmd; 6218c2ecf20Sopenharmony_ci td->status = 0; 6228c2ecf20Sopenharmony_ci td->retry_cnt = (tt << TT_OFFSET) | (active_flag << 4) | retry_cnt; 6238c2ecf20Sopenharmony_ci td->residue = 0; 6248c2ecf20Sopenharmony_ci td->next_td_addr = __cpu_to_le16(c67x00->next_td_addr); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* SW part */ 6278c2ecf20Sopenharmony_ci td->data = data; 6288c2ecf20Sopenharmony_ci td->urb = urb; 6298c2ecf20Sopenharmony_ci td->privdata = privdata; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci c67x00->next_buf_addr += (len + 1) & ~0x01; /* properly align */ 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci list_add_tail(&td->td_list, &c67x00->td_list); 6348c2ecf20Sopenharmony_ci return 0; 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic inline void c67x00_release_td(struct c67x00_td *td) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci list_del_init(&td->td_list); 6408c2ecf20Sopenharmony_ci kfree(td); 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic int c67x00_add_data_urb(struct c67x00_hcd *c67x00, struct urb *urb) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci int remaining; 6488c2ecf20Sopenharmony_ci int toggle; 6498c2ecf20Sopenharmony_ci int pid; 6508c2ecf20Sopenharmony_ci int ret = 0; 6518c2ecf20Sopenharmony_ci int maxps; 6528c2ecf20Sopenharmony_ci int need_empty; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), 6558c2ecf20Sopenharmony_ci usb_pipeout(urb->pipe)); 6568c2ecf20Sopenharmony_ci remaining = urb->transfer_buffer_length - urb->actual_length; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci maxps = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci need_empty = (urb->transfer_flags & URB_ZERO_PACKET) && 6618c2ecf20Sopenharmony_ci usb_pipeout(urb->pipe) && !(remaining % maxps); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci while (remaining || need_empty) { 6648c2ecf20Sopenharmony_ci int len; 6658c2ecf20Sopenharmony_ci char *td_buf; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci len = (remaining > maxps) ? maxps : remaining; 6688c2ecf20Sopenharmony_ci if (!len) 6698c2ecf20Sopenharmony_ci need_empty = 0; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci pid = usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN; 6728c2ecf20Sopenharmony_ci td_buf = urb->transfer_buffer + urb->transfer_buffer_length - 6738c2ecf20Sopenharmony_ci remaining; 6748c2ecf20Sopenharmony_ci ret = c67x00_create_td(c67x00, urb, td_buf, len, pid, toggle, 6758c2ecf20Sopenharmony_ci DATA_STAGE); 6768c2ecf20Sopenharmony_ci if (ret) 6778c2ecf20Sopenharmony_ci return ret; /* td wasn't created */ 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci toggle ^= 1; 6808c2ecf20Sopenharmony_ci remaining -= len; 6818c2ecf20Sopenharmony_ci if (usb_pipecontrol(urb->pipe)) 6828c2ecf20Sopenharmony_ci break; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci return 0; 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci/* 6898c2ecf20Sopenharmony_ci * return 0 in case more bandwidth is available, else errorcode 6908c2ecf20Sopenharmony_ci */ 6918c2ecf20Sopenharmony_cistatic int c67x00_add_ctrl_urb(struct c67x00_hcd *c67x00, struct urb *urb) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci int ret; 6948c2ecf20Sopenharmony_ci int pid; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci switch (urb->interval) { 6978c2ecf20Sopenharmony_ci default: 6988c2ecf20Sopenharmony_ci case SETUP_STAGE: 6998c2ecf20Sopenharmony_ci ret = c67x00_create_td(c67x00, urb, urb->setup_packet, 7008c2ecf20Sopenharmony_ci 8, USB_PID_SETUP, 0, SETUP_STAGE); 7018c2ecf20Sopenharmony_ci if (ret) 7028c2ecf20Sopenharmony_ci return ret; 7038c2ecf20Sopenharmony_ci urb->interval = SETUP_STAGE; 7048c2ecf20Sopenharmony_ci usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), 7058c2ecf20Sopenharmony_ci usb_pipeout(urb->pipe), 1); 7068c2ecf20Sopenharmony_ci break; 7078c2ecf20Sopenharmony_ci case DATA_STAGE: 7088c2ecf20Sopenharmony_ci if (urb->transfer_buffer_length) { 7098c2ecf20Sopenharmony_ci ret = c67x00_add_data_urb(c67x00, urb); 7108c2ecf20Sopenharmony_ci if (ret) 7118c2ecf20Sopenharmony_ci return ret; 7128c2ecf20Sopenharmony_ci break; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci fallthrough; 7158c2ecf20Sopenharmony_ci case STATUS_STAGE: 7168c2ecf20Sopenharmony_ci pid = !usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN; 7178c2ecf20Sopenharmony_ci ret = c67x00_create_td(c67x00, urb, NULL, 0, pid, 1, 7188c2ecf20Sopenharmony_ci STATUS_STAGE); 7198c2ecf20Sopenharmony_ci if (ret) 7208c2ecf20Sopenharmony_ci return ret; 7218c2ecf20Sopenharmony_ci break; 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci return 0; 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci/* 7288c2ecf20Sopenharmony_ci * return 0 in case more bandwidth is available, else errorcode 7298c2ecf20Sopenharmony_ci */ 7308c2ecf20Sopenharmony_cistatic int c67x00_add_int_urb(struct c67x00_hcd *c67x00, struct urb *urb) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci struct c67x00_urb_priv *urbp = urb->hcpriv; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (frame_after_eq(c67x00->current_frame, urbp->ep_data->next_frame)) { 7358c2ecf20Sopenharmony_ci urbp->ep_data->next_frame = 7368c2ecf20Sopenharmony_ci frame_add(urbp->ep_data->next_frame, urb->interval); 7378c2ecf20Sopenharmony_ci return c67x00_add_data_urb(c67x00, urb); 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci return 0; 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cistatic int c67x00_add_iso_urb(struct c67x00_hcd *c67x00, struct urb *urb) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci struct c67x00_urb_priv *urbp = urb->hcpriv; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci if (frame_after_eq(c67x00->current_frame, urbp->ep_data->next_frame)) { 7478c2ecf20Sopenharmony_ci char *td_buf; 7488c2ecf20Sopenharmony_ci int len, pid, ret; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci BUG_ON(urbp->cnt >= urb->number_of_packets); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci td_buf = urb->transfer_buffer + 7538c2ecf20Sopenharmony_ci urb->iso_frame_desc[urbp->cnt].offset; 7548c2ecf20Sopenharmony_ci len = urb->iso_frame_desc[urbp->cnt].length; 7558c2ecf20Sopenharmony_ci pid = usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci ret = c67x00_create_td(c67x00, urb, td_buf, len, pid, 0, 7588c2ecf20Sopenharmony_ci urbp->cnt); 7598c2ecf20Sopenharmony_ci if (ret) { 7608c2ecf20Sopenharmony_ci dev_dbg(c67x00_hcd_dev(c67x00), "create failed: %d\n", 7618c2ecf20Sopenharmony_ci ret); 7628c2ecf20Sopenharmony_ci urb->iso_frame_desc[urbp->cnt].actual_length = 0; 7638c2ecf20Sopenharmony_ci urb->iso_frame_desc[urbp->cnt].status = ret; 7648c2ecf20Sopenharmony_ci if (urbp->cnt + 1 == urb->number_of_packets) 7658c2ecf20Sopenharmony_ci c67x00_giveback_urb(c67x00, urb, 0); 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci urbp->ep_data->next_frame = 7698c2ecf20Sopenharmony_ci frame_add(urbp->ep_data->next_frame, urb->interval); 7708c2ecf20Sopenharmony_ci urbp->cnt++; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci return 0; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_cistatic void c67x00_fill_from_list(struct c67x00_hcd *c67x00, int type, 7788c2ecf20Sopenharmony_ci int (*add)(struct c67x00_hcd *, struct urb *)) 7798c2ecf20Sopenharmony_ci{ 7808c2ecf20Sopenharmony_ci struct c67x00_ep_data *ep_data; 7818c2ecf20Sopenharmony_ci struct urb *urb; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci /* traverse every endpoint on the list */ 7848c2ecf20Sopenharmony_ci list_for_each_entry(ep_data, &c67x00->list[type], node) { 7858c2ecf20Sopenharmony_ci if (!list_empty(&ep_data->queue)) { 7868c2ecf20Sopenharmony_ci /* and add the first urb */ 7878c2ecf20Sopenharmony_ci /* isochronous transfer rely on this */ 7888c2ecf20Sopenharmony_ci urb = list_entry(ep_data->queue.next, 7898c2ecf20Sopenharmony_ci struct c67x00_urb_priv, 7908c2ecf20Sopenharmony_ci hep_node)->urb; 7918c2ecf20Sopenharmony_ci add(c67x00, urb); 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci} 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_cistatic void c67x00_fill_frame(struct c67x00_hcd *c67x00) 7978c2ecf20Sopenharmony_ci{ 7988c2ecf20Sopenharmony_ci struct c67x00_td *td, *ttd; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* Check if we can proceed */ 8018c2ecf20Sopenharmony_ci if (!list_empty(&c67x00->td_list)) { 8028c2ecf20Sopenharmony_ci dev_warn(c67x00_hcd_dev(c67x00), 8038c2ecf20Sopenharmony_ci "TD list not empty! This should not happen!\n"); 8048c2ecf20Sopenharmony_ci list_for_each_entry_safe(td, ttd, &c67x00->td_list, td_list) { 8058c2ecf20Sopenharmony_ci dbg_td(c67x00, td, "Unprocessed td"); 8068c2ecf20Sopenharmony_ci c67x00_release_td(td); 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci /* Reinitialize variables */ 8118c2ecf20Sopenharmony_ci c67x00->bandwidth_allocated = 0; 8128c2ecf20Sopenharmony_ci c67x00->periodic_bw_allocated = 0; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci c67x00->next_td_addr = c67x00->td_base_addr; 8158c2ecf20Sopenharmony_ci c67x00->next_buf_addr = c67x00->buf_base_addr; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci /* Fill the list */ 8188c2ecf20Sopenharmony_ci c67x00_fill_from_list(c67x00, PIPE_ISOCHRONOUS, c67x00_add_iso_urb); 8198c2ecf20Sopenharmony_ci c67x00_fill_from_list(c67x00, PIPE_INTERRUPT, c67x00_add_int_urb); 8208c2ecf20Sopenharmony_ci c67x00_fill_from_list(c67x00, PIPE_CONTROL, c67x00_add_ctrl_urb); 8218c2ecf20Sopenharmony_ci c67x00_fill_from_list(c67x00, PIPE_BULK, c67x00_add_data_urb); 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci/* 8278c2ecf20Sopenharmony_ci * Get TD from C67X00 8288c2ecf20Sopenharmony_ci */ 8298c2ecf20Sopenharmony_cistatic inline void 8308c2ecf20Sopenharmony_cic67x00_parse_td(struct c67x00_hcd *c67x00, struct c67x00_td *td) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci c67x00_ll_read_mem_le16(c67x00->sie->dev, 8338c2ecf20Sopenharmony_ci td->td_addr, td, CY_TD_SIZE); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (usb_pipein(td->pipe) && td_actual_bytes(td)) 8368c2ecf20Sopenharmony_ci c67x00_ll_read_mem_le16(c67x00->sie->dev, td_ly_base_addr(td), 8378c2ecf20Sopenharmony_ci td->data, td_actual_bytes(td)); 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic int c67x00_td_to_error(struct c67x00_hcd *c67x00, struct c67x00_td *td) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci if (td->status & TD_STATUSMASK_ERR) { 8438c2ecf20Sopenharmony_ci dbg_td(c67x00, td, "ERROR_FLAG"); 8448c2ecf20Sopenharmony_ci return -EILSEQ; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci if (td->status & TD_STATUSMASK_STALL) { 8478c2ecf20Sopenharmony_ci /* dbg_td(c67x00, td, "STALL"); */ 8488c2ecf20Sopenharmony_ci return -EPIPE; 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci if (td->status & TD_STATUSMASK_TMOUT) { 8518c2ecf20Sopenharmony_ci dbg_td(c67x00, td, "TIMEOUT"); 8528c2ecf20Sopenharmony_ci return -ETIMEDOUT; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci return 0; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cistatic inline int c67x00_end_of_data(struct c67x00_td *td) 8598c2ecf20Sopenharmony_ci{ 8608c2ecf20Sopenharmony_ci int maxps, need_empty, remaining; 8618c2ecf20Sopenharmony_ci struct urb *urb = td->urb; 8628c2ecf20Sopenharmony_ci int act_bytes; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci act_bytes = td_actual_bytes(td); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci if (unlikely(!act_bytes)) 8678c2ecf20Sopenharmony_ci return 1; /* This was an empty packet */ 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci maxps = usb_maxpacket(td_udev(td), td->pipe, usb_pipeout(td->pipe)); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci if (unlikely(act_bytes < maxps)) 8728c2ecf20Sopenharmony_ci return 1; /* Smaller then full packet */ 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci remaining = urb->transfer_buffer_length - urb->actual_length; 8758c2ecf20Sopenharmony_ci need_empty = (urb->transfer_flags & URB_ZERO_PACKET) && 8768c2ecf20Sopenharmony_ci usb_pipeout(urb->pipe) && !(remaining % maxps); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci if (unlikely(!remaining && !need_empty)) 8798c2ecf20Sopenharmony_ci return 1; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci return 0; 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci/* Remove all td's from the list which come 8878c2ecf20Sopenharmony_ci * after last_td and are meant for the same pipe. 8888c2ecf20Sopenharmony_ci * This is used when a short packet has occurred */ 8898c2ecf20Sopenharmony_cistatic inline void c67x00_clear_pipe(struct c67x00_hcd *c67x00, 8908c2ecf20Sopenharmony_ci struct c67x00_td *last_td) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci struct c67x00_td *td, *tmp; 8938c2ecf20Sopenharmony_ci td = last_td; 8948c2ecf20Sopenharmony_ci tmp = last_td; 8958c2ecf20Sopenharmony_ci while (td->td_list.next != &c67x00->td_list) { 8968c2ecf20Sopenharmony_ci td = list_entry(td->td_list.next, struct c67x00_td, td_list); 8978c2ecf20Sopenharmony_ci if (td->pipe == last_td->pipe) { 8988c2ecf20Sopenharmony_ci c67x00_release_td(td); 8998c2ecf20Sopenharmony_ci td = tmp; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci tmp = td; 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_cistatic void c67x00_handle_successful_td(struct c67x00_hcd *c67x00, 9088c2ecf20Sopenharmony_ci struct c67x00_td *td) 9098c2ecf20Sopenharmony_ci{ 9108c2ecf20Sopenharmony_ci struct urb *urb = td->urb; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (!urb) 9138c2ecf20Sopenharmony_ci return; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci urb->actual_length += td_actual_bytes(td); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci switch (usb_pipetype(td->pipe)) { 9188c2ecf20Sopenharmony_ci /* isochronous tds are handled separately */ 9198c2ecf20Sopenharmony_ci case PIPE_CONTROL: 9208c2ecf20Sopenharmony_ci switch (td->privdata) { 9218c2ecf20Sopenharmony_ci case SETUP_STAGE: 9228c2ecf20Sopenharmony_ci urb->interval = 9238c2ecf20Sopenharmony_ci urb->transfer_buffer_length ? 9248c2ecf20Sopenharmony_ci DATA_STAGE : STATUS_STAGE; 9258c2ecf20Sopenharmony_ci /* Don't count setup_packet with normal data: */ 9268c2ecf20Sopenharmony_ci urb->actual_length = 0; 9278c2ecf20Sopenharmony_ci break; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci case DATA_STAGE: 9308c2ecf20Sopenharmony_ci if (c67x00_end_of_data(td)) { 9318c2ecf20Sopenharmony_ci urb->interval = STATUS_STAGE; 9328c2ecf20Sopenharmony_ci c67x00_clear_pipe(c67x00, td); 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci break; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci case STATUS_STAGE: 9378c2ecf20Sopenharmony_ci urb->interval = 0; 9388c2ecf20Sopenharmony_ci c67x00_giveback_urb(c67x00, urb, 0); 9398c2ecf20Sopenharmony_ci break; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci break; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci case PIPE_INTERRUPT: 9448c2ecf20Sopenharmony_ci case PIPE_BULK: 9458c2ecf20Sopenharmony_ci if (unlikely(c67x00_end_of_data(td))) { 9468c2ecf20Sopenharmony_ci c67x00_clear_pipe(c67x00, td); 9478c2ecf20Sopenharmony_ci c67x00_giveback_urb(c67x00, urb, 0); 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci break; 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci} 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_cistatic void c67x00_handle_isoc(struct c67x00_hcd *c67x00, struct c67x00_td *td) 9548c2ecf20Sopenharmony_ci{ 9558c2ecf20Sopenharmony_ci struct urb *urb = td->urb; 9568c2ecf20Sopenharmony_ci int cnt; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (!urb) 9598c2ecf20Sopenharmony_ci return; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci cnt = td->privdata; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci if (td->status & TD_ERROR_MASK) 9648c2ecf20Sopenharmony_ci urb->error_count++; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci urb->iso_frame_desc[cnt].actual_length = td_actual_bytes(td); 9678c2ecf20Sopenharmony_ci urb->iso_frame_desc[cnt].status = c67x00_td_to_error(c67x00, td); 9688c2ecf20Sopenharmony_ci if (cnt + 1 == urb->number_of_packets) /* Last packet */ 9698c2ecf20Sopenharmony_ci c67x00_giveback_urb(c67x00, urb, 0); 9708c2ecf20Sopenharmony_ci} 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci/* 9758c2ecf20Sopenharmony_ci * c67x00_check_td_list - handle tds which have been processed by the c67x00 9768c2ecf20Sopenharmony_ci * pre: current_td == 0 9778c2ecf20Sopenharmony_ci */ 9788c2ecf20Sopenharmony_cistatic inline void c67x00_check_td_list(struct c67x00_hcd *c67x00) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci struct c67x00_td *td, *tmp; 9818c2ecf20Sopenharmony_ci struct urb *urb; 9828c2ecf20Sopenharmony_ci int ack_ok; 9838c2ecf20Sopenharmony_ci int clear_endpoint; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci list_for_each_entry_safe(td, tmp, &c67x00->td_list, td_list) { 9868c2ecf20Sopenharmony_ci /* get the TD */ 9878c2ecf20Sopenharmony_ci c67x00_parse_td(c67x00, td); 9888c2ecf20Sopenharmony_ci urb = td->urb; /* urb can be NULL! */ 9898c2ecf20Sopenharmony_ci ack_ok = 0; 9908c2ecf20Sopenharmony_ci clear_endpoint = 1; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* Handle isochronous transfers separately */ 9938c2ecf20Sopenharmony_ci if (usb_pipeisoc(td->pipe)) { 9948c2ecf20Sopenharmony_ci clear_endpoint = 0; 9958c2ecf20Sopenharmony_ci c67x00_handle_isoc(c67x00, td); 9968c2ecf20Sopenharmony_ci goto cont; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci /* When an error occurs, all td's for that pipe go into an 10008c2ecf20Sopenharmony_ci * inactive state. This state matches successful transfers so 10018c2ecf20Sopenharmony_ci * we must make sure not to service them. */ 10028c2ecf20Sopenharmony_ci if (td->status & TD_ERROR_MASK) { 10038c2ecf20Sopenharmony_ci c67x00_giveback_urb(c67x00, urb, 10048c2ecf20Sopenharmony_ci c67x00_td_to_error(c67x00, td)); 10058c2ecf20Sopenharmony_ci goto cont; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci if ((td->status & TD_STATUSMASK_NAK) || !td_sequence_ok(td) || 10098c2ecf20Sopenharmony_ci !td_acked(td)) 10108c2ecf20Sopenharmony_ci goto cont; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci /* Sequence ok and acked, don't need to fix toggle */ 10138c2ecf20Sopenharmony_ci ack_ok = 1; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci if (unlikely(td->status & TD_STATUSMASK_OVF)) { 10168c2ecf20Sopenharmony_ci if (td_residue(td) & TD_RESIDUE_OVERFLOW) { 10178c2ecf20Sopenharmony_ci /* Overflow */ 10188c2ecf20Sopenharmony_ci c67x00_giveback_urb(c67x00, urb, -EOVERFLOW); 10198c2ecf20Sopenharmony_ci goto cont; 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci clear_endpoint = 0; 10248c2ecf20Sopenharmony_ci c67x00_handle_successful_td(c67x00, td); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cicont: 10278c2ecf20Sopenharmony_ci if (clear_endpoint) 10288c2ecf20Sopenharmony_ci c67x00_clear_pipe(c67x00, td); 10298c2ecf20Sopenharmony_ci if (ack_ok) 10308c2ecf20Sopenharmony_ci usb_settoggle(td_udev(td), usb_pipeendpoint(td->pipe), 10318c2ecf20Sopenharmony_ci usb_pipeout(td->pipe), 10328c2ecf20Sopenharmony_ci !(td->ctrl_reg & SEQ_SEL)); 10338c2ecf20Sopenharmony_ci /* next in list could have been removed, due to clear_pipe! */ 10348c2ecf20Sopenharmony_ci tmp = list_entry(td->td_list.next, typeof(*td), td_list); 10358c2ecf20Sopenharmony_ci c67x00_release_td(td); 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistatic inline int c67x00_all_tds_processed(struct c67x00_hcd *c67x00) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci /* If all tds are processed, we can check the previous frame (if 10448c2ecf20Sopenharmony_ci * there was any) and start our next frame. 10458c2ecf20Sopenharmony_ci */ 10468c2ecf20Sopenharmony_ci return !c67x00_ll_husb_get_current_td(c67x00->sie); 10478c2ecf20Sopenharmony_ci} 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci/* 10508c2ecf20Sopenharmony_ci * Send td to C67X00 10518c2ecf20Sopenharmony_ci */ 10528c2ecf20Sopenharmony_cistatic void c67x00_send_td(struct c67x00_hcd *c67x00, struct c67x00_td *td) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci int len = td_length(td); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci if (len && ((td->pid_ep & TD_PIDEPMASK_PID) != TD_PID_IN)) 10578c2ecf20Sopenharmony_ci c67x00_ll_write_mem_le16(c67x00->sie->dev, td_ly_base_addr(td), 10588c2ecf20Sopenharmony_ci td->data, len); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci c67x00_ll_write_mem_le16(c67x00->sie->dev, 10618c2ecf20Sopenharmony_ci td->td_addr, td, CY_TD_SIZE); 10628c2ecf20Sopenharmony_ci} 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_cistatic void c67x00_send_frame(struct c67x00_hcd *c67x00) 10658c2ecf20Sopenharmony_ci{ 10668c2ecf20Sopenharmony_ci struct c67x00_td *td; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci if (list_empty(&c67x00->td_list)) 10698c2ecf20Sopenharmony_ci dev_warn(c67x00_hcd_dev(c67x00), 10708c2ecf20Sopenharmony_ci "%s: td list should not be empty here!\n", 10718c2ecf20Sopenharmony_ci __func__); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci list_for_each_entry(td, &c67x00->td_list, td_list) { 10748c2ecf20Sopenharmony_ci if (td->td_list.next == &c67x00->td_list) 10758c2ecf20Sopenharmony_ci td->next_td_addr = 0; /* Last td in list */ 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci c67x00_send_td(c67x00, td); 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci c67x00_ll_husb_set_current_td(c67x00->sie, c67x00->td_base_addr); 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci/* 10868c2ecf20Sopenharmony_ci * c67x00_do_work - Schedulers state machine 10878c2ecf20Sopenharmony_ci */ 10888c2ecf20Sopenharmony_cistatic void c67x00_do_work(struct c67x00_hcd *c67x00) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci spin_lock(&c67x00->lock); 10918c2ecf20Sopenharmony_ci /* Make sure all tds are processed */ 10928c2ecf20Sopenharmony_ci if (!c67x00_all_tds_processed(c67x00)) 10938c2ecf20Sopenharmony_ci goto out; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci c67x00_check_td_list(c67x00); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci /* no td's are being processed (current == 0) 10988c2ecf20Sopenharmony_ci * and all have been "checked" */ 10998c2ecf20Sopenharmony_ci complete(&c67x00->endpoint_disable); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci if (!list_empty(&c67x00->td_list)) 11028c2ecf20Sopenharmony_ci goto out; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci c67x00->current_frame = c67x00_get_current_frame_number(c67x00); 11058c2ecf20Sopenharmony_ci if (c67x00->current_frame == c67x00->last_frame) 11068c2ecf20Sopenharmony_ci goto out; /* Don't send tds in same frame */ 11078c2ecf20Sopenharmony_ci c67x00->last_frame = c67x00->current_frame; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci /* If no urbs are scheduled, our work is done */ 11108c2ecf20Sopenharmony_ci if (!c67x00->urb_count) { 11118c2ecf20Sopenharmony_ci c67x00_ll_hpi_disable_sofeop(c67x00->sie); 11128c2ecf20Sopenharmony_ci goto out; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci c67x00_fill_frame(c67x00); 11168c2ecf20Sopenharmony_ci if (!list_empty(&c67x00->td_list)) 11178c2ecf20Sopenharmony_ci /* TD's have been added to the frame */ 11188c2ecf20Sopenharmony_ci c67x00_send_frame(c67x00); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci out: 11218c2ecf20Sopenharmony_ci spin_unlock(&c67x00->lock); 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_cistatic void c67x00_sched_tasklet(struct tasklet_struct *t) 11278c2ecf20Sopenharmony_ci{ 11288c2ecf20Sopenharmony_ci struct c67x00_hcd *c67x00 = from_tasklet(c67x00, t, tasklet); 11298c2ecf20Sopenharmony_ci c67x00_do_work(c67x00); 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_civoid c67x00_sched_kick(struct c67x00_hcd *c67x00) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci tasklet_hi_schedule(&c67x00->tasklet); 11358c2ecf20Sopenharmony_ci} 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ciint c67x00_sched_start_scheduler(struct c67x00_hcd *c67x00) 11388c2ecf20Sopenharmony_ci{ 11398c2ecf20Sopenharmony_ci tasklet_setup(&c67x00->tasklet, c67x00_sched_tasklet); 11408c2ecf20Sopenharmony_ci return 0; 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_civoid c67x00_sched_stop_scheduler(struct c67x00_hcd *c67x00) 11448c2ecf20Sopenharmony_ci{ 11458c2ecf20Sopenharmony_ci tasklet_kill(&c67x00->tasklet); 11468c2ecf20Sopenharmony_ci} 1147