162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * c67x00-sched.c: Cypress C67X00 USB Host Controller Driver - TD scheduling 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2006-2008 Barco N.V. 662306a36Sopenharmony_ci * Derived from the Cypress cy7c67200/300 ezusb linux driver and 762306a36Sopenharmony_ci * based on multiple host controller drivers inside the linux kernel. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kthread.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "c67x00.h" 1462306a36Sopenharmony_ci#include "c67x00-hcd.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * These are the stages for a control urb, they are kept 1862306a36Sopenharmony_ci * in both urb->interval and td->privdata. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci#define SETUP_STAGE 0 2162306a36Sopenharmony_ci#define DATA_STAGE 1 2262306a36Sopenharmony_ci#define STATUS_STAGE 2 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * struct c67x00_ep_data: Host endpoint data structure 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistruct c67x00_ep_data { 3062306a36Sopenharmony_ci struct list_head queue; 3162306a36Sopenharmony_ci struct list_head node; 3262306a36Sopenharmony_ci struct usb_host_endpoint *hep; 3362306a36Sopenharmony_ci struct usb_device *dev; 3462306a36Sopenharmony_ci u16 next_frame; /* For int/isoc transactions */ 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * struct c67x00_td 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * Hardware parts are little endiannes, SW in CPU endianess. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_cistruct c67x00_td { 4362306a36Sopenharmony_ci /* HW specific part */ 4462306a36Sopenharmony_ci __le16 ly_base_addr; /* Bytes 0-1 */ 4562306a36Sopenharmony_ci __le16 port_length; /* Bytes 2-3 */ 4662306a36Sopenharmony_ci u8 pid_ep; /* Byte 4 */ 4762306a36Sopenharmony_ci u8 dev_addr; /* Byte 5 */ 4862306a36Sopenharmony_ci u8 ctrl_reg; /* Byte 6 */ 4962306a36Sopenharmony_ci u8 status; /* Byte 7 */ 5062306a36Sopenharmony_ci u8 retry_cnt; /* Byte 8 */ 5162306a36Sopenharmony_ci#define TT_OFFSET 2 5262306a36Sopenharmony_ci#define TT_CONTROL 0 5362306a36Sopenharmony_ci#define TT_ISOCHRONOUS 1 5462306a36Sopenharmony_ci#define TT_BULK 2 5562306a36Sopenharmony_ci#define TT_INTERRUPT 3 5662306a36Sopenharmony_ci u8 residue; /* Byte 9 */ 5762306a36Sopenharmony_ci __le16 next_td_addr; /* Bytes 10-11 */ 5862306a36Sopenharmony_ci /* SW part */ 5962306a36Sopenharmony_ci struct list_head td_list; 6062306a36Sopenharmony_ci u16 td_addr; 6162306a36Sopenharmony_ci void *data; 6262306a36Sopenharmony_ci struct urb *urb; 6362306a36Sopenharmony_ci unsigned long privdata; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* These are needed for handling the toggle bits: 6662306a36Sopenharmony_ci * an urb can be dequeued while a td is in progress 6762306a36Sopenharmony_ci * after checking the td, the toggle bit might need to 6862306a36Sopenharmony_ci * be fixed */ 6962306a36Sopenharmony_ci struct c67x00_ep_data *ep_data; 7062306a36Sopenharmony_ci unsigned int pipe; 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistruct c67x00_urb_priv { 7462306a36Sopenharmony_ci struct list_head hep_node; 7562306a36Sopenharmony_ci struct urb *urb; 7662306a36Sopenharmony_ci int port; 7762306a36Sopenharmony_ci int cnt; /* packet number for isoc */ 7862306a36Sopenharmony_ci int status; 7962306a36Sopenharmony_ci struct c67x00_ep_data *ep_data; 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define td_udev(td) ((td)->ep_data->dev) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define CY_TD_SIZE 12 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define TD_PIDEP_OFFSET 0x04 8762306a36Sopenharmony_ci#define TD_PIDEPMASK_PID 0xF0 8862306a36Sopenharmony_ci#define TD_PIDEPMASK_EP 0x0F 8962306a36Sopenharmony_ci#define TD_PORTLENMASK_DL 0x03FF 9062306a36Sopenharmony_ci#define TD_PORTLENMASK_PN 0xC000 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define TD_STATUS_OFFSET 0x07 9362306a36Sopenharmony_ci#define TD_STATUSMASK_ACK 0x01 9462306a36Sopenharmony_ci#define TD_STATUSMASK_ERR 0x02 9562306a36Sopenharmony_ci#define TD_STATUSMASK_TMOUT 0x04 9662306a36Sopenharmony_ci#define TD_STATUSMASK_SEQ 0x08 9762306a36Sopenharmony_ci#define TD_STATUSMASK_SETUP 0x10 9862306a36Sopenharmony_ci#define TD_STATUSMASK_OVF 0x20 9962306a36Sopenharmony_ci#define TD_STATUSMASK_NAK 0x40 10062306a36Sopenharmony_ci#define TD_STATUSMASK_STALL 0x80 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define TD_ERROR_MASK (TD_STATUSMASK_ERR | TD_STATUSMASK_TMOUT | \ 10362306a36Sopenharmony_ci TD_STATUSMASK_STALL) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define TD_RETRYCNT_OFFSET 0x08 10662306a36Sopenharmony_ci#define TD_RETRYCNTMASK_ACT_FLG 0x10 10762306a36Sopenharmony_ci#define TD_RETRYCNTMASK_TX_TYPE 0x0C 10862306a36Sopenharmony_ci#define TD_RETRYCNTMASK_RTY_CNT 0x03 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define TD_RESIDUE_OVERFLOW 0x80 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#define TD_PID_IN 0x90 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* Residue: signed 8bits, neg -> OVERFLOW, pos -> UNDERFLOW */ 11562306a36Sopenharmony_ci#define td_residue(td) ((__s8)(td->residue)) 11662306a36Sopenharmony_ci#define td_ly_base_addr(td) (__le16_to_cpu((td)->ly_base_addr)) 11762306a36Sopenharmony_ci#define td_port_length(td) (__le16_to_cpu((td)->port_length)) 11862306a36Sopenharmony_ci#define td_next_td_addr(td) (__le16_to_cpu((td)->next_td_addr)) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define td_active(td) ((td)->retry_cnt & TD_RETRYCNTMASK_ACT_FLG) 12162306a36Sopenharmony_ci#define td_length(td) (td_port_length(td) & TD_PORTLENMASK_DL) 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define td_sequence_ok(td) (!td->status || \ 12462306a36Sopenharmony_ci (!(td->status & TD_STATUSMASK_SEQ) == \ 12562306a36Sopenharmony_ci !(td->ctrl_reg & SEQ_SEL))) 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define td_acked(td) (!td->status || \ 12862306a36Sopenharmony_ci (td->status & TD_STATUSMASK_ACK)) 12962306a36Sopenharmony_ci#define td_actual_bytes(td) (td_length(td) - td_residue(td)) 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* 13462306a36Sopenharmony_ci * dbg_td - Dump the contents of the TD 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_cistatic void dbg_td(struct c67x00_hcd *c67x00, struct c67x00_td *td, char *msg) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct device *dev = c67x00_hcd_dev(c67x00); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci dev_dbg(dev, "### %s at 0x%04x\n", msg, td->td_addr); 14162306a36Sopenharmony_ci dev_dbg(dev, "urb: 0x%p\n", td->urb); 14262306a36Sopenharmony_ci dev_dbg(dev, "endpoint: %4d\n", usb_pipeendpoint(td->pipe)); 14362306a36Sopenharmony_ci dev_dbg(dev, "pipeout: %4d\n", usb_pipeout(td->pipe)); 14462306a36Sopenharmony_ci dev_dbg(dev, "ly_base_addr: 0x%04x\n", td_ly_base_addr(td)); 14562306a36Sopenharmony_ci dev_dbg(dev, "port_length: 0x%04x\n", td_port_length(td)); 14662306a36Sopenharmony_ci dev_dbg(dev, "pid_ep: 0x%02x\n", td->pid_ep); 14762306a36Sopenharmony_ci dev_dbg(dev, "dev_addr: 0x%02x\n", td->dev_addr); 14862306a36Sopenharmony_ci dev_dbg(dev, "ctrl_reg: 0x%02x\n", td->ctrl_reg); 14962306a36Sopenharmony_ci dev_dbg(dev, "status: 0x%02x\n", td->status); 15062306a36Sopenharmony_ci dev_dbg(dev, "retry_cnt: 0x%02x\n", td->retry_cnt); 15162306a36Sopenharmony_ci dev_dbg(dev, "residue: 0x%02x\n", td->residue); 15262306a36Sopenharmony_ci dev_dbg(dev, "next_td_addr: 0x%04x\n", td_next_td_addr(td)); 15362306a36Sopenharmony_ci dev_dbg(dev, "data: %*ph\n", td_length(td), td->data); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 15762306a36Sopenharmony_ci/* Helper functions */ 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic inline u16 c67x00_get_current_frame_number(struct c67x00_hcd *c67x00) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci return c67x00_ll_husb_get_frame(c67x00->sie) & HOST_FRAME_MASK; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* 16562306a36Sopenharmony_ci * frame_add 16662306a36Sopenharmony_ci * Software wraparound for framenumbers. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_cistatic inline u16 frame_add(u16 a, u16 b) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci return (a + b) & HOST_FRAME_MASK; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/* 17462306a36Sopenharmony_ci * frame_after - is frame a after frame b 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_cistatic inline int frame_after(u16 a, u16 b) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci return ((HOST_FRAME_MASK + a - b) & HOST_FRAME_MASK) < 17962306a36Sopenharmony_ci (HOST_FRAME_MASK / 2); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* 18362306a36Sopenharmony_ci * frame_after_eq - is frame a after or equal to frame b 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_cistatic inline int frame_after_eq(u16 a, u16 b) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci return ((HOST_FRAME_MASK + 1 + a - b) & HOST_FRAME_MASK) < 18862306a36Sopenharmony_ci (HOST_FRAME_MASK / 2); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* 19462306a36Sopenharmony_ci * c67x00_release_urb - remove link from all tds to this urb 19562306a36Sopenharmony_ci * Disconnects the urb from it's tds, so that it can be given back. 19662306a36Sopenharmony_ci * pre: urb->hcpriv != NULL 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_cistatic void c67x00_release_urb(struct c67x00_hcd *c67x00, struct urb *urb) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct c67x00_td *td; 20162306a36Sopenharmony_ci struct c67x00_urb_priv *urbp; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci BUG_ON(!urb); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci c67x00->urb_count--; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { 20862306a36Sopenharmony_ci c67x00->urb_iso_count--; 20962306a36Sopenharmony_ci if (c67x00->urb_iso_count == 0) 21062306a36Sopenharmony_ci c67x00->max_frame_bw = MAX_FRAME_BW_STD; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* TODO this might be not so efficient when we've got many urbs! 21462306a36Sopenharmony_ci * Alternatives: 21562306a36Sopenharmony_ci * * only clear when needed 21662306a36Sopenharmony_ci * * keep a list of tds with each urbp 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_ci list_for_each_entry(td, &c67x00->td_list, td_list) 21962306a36Sopenharmony_ci if (urb == td->urb) 22062306a36Sopenharmony_ci td->urb = NULL; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci urbp = urb->hcpriv; 22362306a36Sopenharmony_ci urb->hcpriv = NULL; 22462306a36Sopenharmony_ci list_del(&urbp->hep_node); 22562306a36Sopenharmony_ci kfree(urbp); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic struct c67x00_ep_data * 23162306a36Sopenharmony_cic67x00_ep_data_alloc(struct c67x00_hcd *c67x00, struct urb *urb) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct usb_host_endpoint *hep = urb->ep; 23462306a36Sopenharmony_ci struct c67x00_ep_data *ep_data; 23562306a36Sopenharmony_ci int type; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci c67x00->current_frame = c67x00_get_current_frame_number(c67x00); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Check if endpoint already has a c67x00_ep_data struct allocated */ 24062306a36Sopenharmony_ci if (hep->hcpriv) { 24162306a36Sopenharmony_ci ep_data = hep->hcpriv; 24262306a36Sopenharmony_ci if (frame_after(c67x00->current_frame, ep_data->next_frame)) 24362306a36Sopenharmony_ci ep_data->next_frame = 24462306a36Sopenharmony_ci frame_add(c67x00->current_frame, 1); 24562306a36Sopenharmony_ci return hep->hcpriv; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Allocate and initialize a new c67x00 endpoint data structure */ 24962306a36Sopenharmony_ci ep_data = kzalloc(sizeof(*ep_data), GFP_ATOMIC); 25062306a36Sopenharmony_ci if (!ep_data) 25162306a36Sopenharmony_ci return NULL; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci INIT_LIST_HEAD(&ep_data->queue); 25462306a36Sopenharmony_ci INIT_LIST_HEAD(&ep_data->node); 25562306a36Sopenharmony_ci ep_data->hep = hep; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* hold a reference to udev as long as this endpoint lives, 25862306a36Sopenharmony_ci * this is needed to possibly fix the data toggle */ 25962306a36Sopenharmony_ci ep_data->dev = usb_get_dev(urb->dev); 26062306a36Sopenharmony_ci hep->hcpriv = ep_data; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* For ISOC and INT endpoints, start ASAP: */ 26362306a36Sopenharmony_ci ep_data->next_frame = frame_add(c67x00->current_frame, 1); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* Add the endpoint data to one of the pipe lists; must be added 26662306a36Sopenharmony_ci in order of endpoint address */ 26762306a36Sopenharmony_ci type = usb_pipetype(urb->pipe); 26862306a36Sopenharmony_ci if (list_empty(&ep_data->node)) { 26962306a36Sopenharmony_ci list_add(&ep_data->node, &c67x00->list[type]); 27062306a36Sopenharmony_ci } else { 27162306a36Sopenharmony_ci struct c67x00_ep_data *prev; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci list_for_each_entry(prev, &c67x00->list[type], node) { 27462306a36Sopenharmony_ci if (prev->hep->desc.bEndpointAddress > 27562306a36Sopenharmony_ci hep->desc.bEndpointAddress) { 27662306a36Sopenharmony_ci list_add(&ep_data->node, prev->node.prev); 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return ep_data; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int c67x00_ep_data_free(struct usb_host_endpoint *hep) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct c67x00_ep_data *ep_data = hep->hcpriv; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (!ep_data) 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (!list_empty(&ep_data->queue)) 29362306a36Sopenharmony_ci return -EBUSY; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci usb_put_dev(ep_data->dev); 29662306a36Sopenharmony_ci list_del(&ep_data->queue); 29762306a36Sopenharmony_ci list_del(&ep_data->node); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci kfree(ep_data); 30062306a36Sopenharmony_ci hep->hcpriv = NULL; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_civoid c67x00_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd); 30862306a36Sopenharmony_ci unsigned long flags; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (!list_empty(&ep->urb_list)) 31162306a36Sopenharmony_ci dev_warn(c67x00_hcd_dev(c67x00), "error: urb list not empty\n"); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci spin_lock_irqsave(&c67x00->lock, flags); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* loop waiting for all transfers in the endpoint queue to complete */ 31662306a36Sopenharmony_ci while (c67x00_ep_data_free(ep)) { 31762306a36Sopenharmony_ci /* Drop the lock so we can sleep waiting for the hardware */ 31862306a36Sopenharmony_ci spin_unlock_irqrestore(&c67x00->lock, flags); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* it could happen that we reinitialize this completion, while 32162306a36Sopenharmony_ci * somebody was waiting for that completion. The timeout and 32262306a36Sopenharmony_ci * while loop handle such cases, but this might be improved */ 32362306a36Sopenharmony_ci reinit_completion(&c67x00->endpoint_disable); 32462306a36Sopenharmony_ci c67x00_sched_kick(c67x00); 32562306a36Sopenharmony_ci wait_for_completion_timeout(&c67x00->endpoint_disable, 1 * HZ); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci spin_lock_irqsave(&c67x00->lock, flags); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci spin_unlock_irqrestore(&c67x00->lock, flags); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic inline int get_root_port(struct usb_device *dev) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci while (dev->parent->parent) 33862306a36Sopenharmony_ci dev = dev->parent; 33962306a36Sopenharmony_ci return dev->portnum; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ciint c67x00_urb_enqueue(struct usb_hcd *hcd, 34362306a36Sopenharmony_ci struct urb *urb, gfp_t mem_flags) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci int ret; 34662306a36Sopenharmony_ci unsigned long flags; 34762306a36Sopenharmony_ci struct c67x00_urb_priv *urbp; 34862306a36Sopenharmony_ci struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd); 34962306a36Sopenharmony_ci int port = get_root_port(urb->dev)-1; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* Allocate and initialize urb private data */ 35262306a36Sopenharmony_ci urbp = kzalloc(sizeof(*urbp), mem_flags); 35362306a36Sopenharmony_ci if (!urbp) { 35462306a36Sopenharmony_ci ret = -ENOMEM; 35562306a36Sopenharmony_ci goto err_urbp; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci spin_lock_irqsave(&c67x00->lock, flags); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* Make sure host controller is running */ 36162306a36Sopenharmony_ci if (!HC_IS_RUNNING(hcd->state)) { 36262306a36Sopenharmony_ci ret = -ENODEV; 36362306a36Sopenharmony_ci goto err_not_linked; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci ret = usb_hcd_link_urb_to_ep(hcd, urb); 36762306a36Sopenharmony_ci if (ret) 36862306a36Sopenharmony_ci goto err_not_linked; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci INIT_LIST_HEAD(&urbp->hep_node); 37162306a36Sopenharmony_ci urbp->urb = urb; 37262306a36Sopenharmony_ci urbp->port = port; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci urbp->ep_data = c67x00_ep_data_alloc(c67x00, urb); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (!urbp->ep_data) { 37762306a36Sopenharmony_ci ret = -ENOMEM; 37862306a36Sopenharmony_ci goto err_epdata; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* TODO claim bandwidth with usb_claim_bandwidth? 38262306a36Sopenharmony_ci * also release it somewhere! */ 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci urb->hcpriv = urbp; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci urb->actual_length = 0; /* Nothing received/transmitted yet */ 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci switch (usb_pipetype(urb->pipe)) { 38962306a36Sopenharmony_ci case PIPE_CONTROL: 39062306a36Sopenharmony_ci urb->interval = SETUP_STAGE; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci case PIPE_INTERRUPT: 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci case PIPE_BULK: 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci case PIPE_ISOCHRONOUS: 39762306a36Sopenharmony_ci if (c67x00->urb_iso_count == 0) 39862306a36Sopenharmony_ci c67x00->max_frame_bw = MAX_FRAME_BW_ISO; 39962306a36Sopenharmony_ci c67x00->urb_iso_count++; 40062306a36Sopenharmony_ci /* Assume always URB_ISO_ASAP, FIXME */ 40162306a36Sopenharmony_ci if (list_empty(&urbp->ep_data->queue)) 40262306a36Sopenharmony_ci urb->start_frame = urbp->ep_data->next_frame; 40362306a36Sopenharmony_ci else { 40462306a36Sopenharmony_ci /* Go right after the last one */ 40562306a36Sopenharmony_ci struct urb *last_urb; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci last_urb = list_entry(urbp->ep_data->queue.prev, 40862306a36Sopenharmony_ci struct c67x00_urb_priv, 40962306a36Sopenharmony_ci hep_node)->urb; 41062306a36Sopenharmony_ci urb->start_frame = 41162306a36Sopenharmony_ci frame_add(last_urb->start_frame, 41262306a36Sopenharmony_ci last_urb->number_of_packets * 41362306a36Sopenharmony_ci last_urb->interval); 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci urbp->cnt = 0; 41662306a36Sopenharmony_ci break; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Add the URB to the endpoint queue */ 42062306a36Sopenharmony_ci list_add_tail(&urbp->hep_node, &urbp->ep_data->queue); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* If this is the only URB, kick start the controller */ 42362306a36Sopenharmony_ci if (!c67x00->urb_count++) 42462306a36Sopenharmony_ci c67x00_ll_hpi_enable_sofeop(c67x00->sie); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci c67x00_sched_kick(c67x00); 42762306a36Sopenharmony_ci spin_unlock_irqrestore(&c67x00->lock, flags); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cierr_epdata: 43262306a36Sopenharmony_ci usb_hcd_unlink_urb_from_ep(hcd, urb); 43362306a36Sopenharmony_cierr_not_linked: 43462306a36Sopenharmony_ci spin_unlock_irqrestore(&c67x00->lock, flags); 43562306a36Sopenharmony_ci kfree(urbp); 43662306a36Sopenharmony_cierr_urbp: 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return ret; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ciint c67x00_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd); 44462306a36Sopenharmony_ci unsigned long flags; 44562306a36Sopenharmony_ci int rc; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci spin_lock_irqsave(&c67x00->lock, flags); 44862306a36Sopenharmony_ci rc = usb_hcd_check_unlink_urb(hcd, urb, status); 44962306a36Sopenharmony_ci if (rc) 45062306a36Sopenharmony_ci goto done; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci c67x00_release_urb(c67x00, urb); 45362306a36Sopenharmony_ci usb_hcd_unlink_urb_from_ep(hcd, urb); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci spin_unlock(&c67x00->lock); 45662306a36Sopenharmony_ci usb_hcd_giveback_urb(hcd, urb, status); 45762306a36Sopenharmony_ci spin_lock(&c67x00->lock); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci spin_unlock_irqrestore(&c67x00->lock, flags); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci done: 46462306a36Sopenharmony_ci spin_unlock_irqrestore(&c67x00->lock, flags); 46562306a36Sopenharmony_ci return rc; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci/* 47162306a36Sopenharmony_ci * pre: c67x00 locked, urb unlocked 47262306a36Sopenharmony_ci */ 47362306a36Sopenharmony_cistatic void 47462306a36Sopenharmony_cic67x00_giveback_urb(struct c67x00_hcd *c67x00, struct urb *urb, int status) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct c67x00_urb_priv *urbp; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (!urb) 47962306a36Sopenharmony_ci return; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci urbp = urb->hcpriv; 48262306a36Sopenharmony_ci urbp->status = status; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci list_del_init(&urbp->hep_node); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci c67x00_release_urb(c67x00, urb); 48762306a36Sopenharmony_ci usb_hcd_unlink_urb_from_ep(c67x00_hcd_to_hcd(c67x00), urb); 48862306a36Sopenharmony_ci spin_unlock(&c67x00->lock); 48962306a36Sopenharmony_ci usb_hcd_giveback_urb(c67x00_hcd_to_hcd(c67x00), urb, status); 49062306a36Sopenharmony_ci spin_lock(&c67x00->lock); 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic int c67x00_claim_frame_bw(struct c67x00_hcd *c67x00, struct urb *urb, 49662306a36Sopenharmony_ci int len, int periodic) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct c67x00_urb_priv *urbp = urb->hcpriv; 49962306a36Sopenharmony_ci int bit_time; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* According to the C67x00 BIOS user manual, page 3-18,19, the 50262306a36Sopenharmony_ci * following calculations provide the full speed bit times for 50362306a36Sopenharmony_ci * a transaction. 50462306a36Sopenharmony_ci * 50562306a36Sopenharmony_ci * FS(in) = 112.5 + 9.36*BC + HOST_DELAY 50662306a36Sopenharmony_ci * FS(in,iso) = 90.5 + 9.36*BC + HOST_DELAY 50762306a36Sopenharmony_ci * FS(out) = 112.5 + 9.36*BC + HOST_DELAY 50862306a36Sopenharmony_ci * FS(out,iso) = 78.4 + 9.36*BC + HOST_DELAY 50962306a36Sopenharmony_ci * LS(in) = 802.4 + 75.78*BC + HOST_DELAY 51062306a36Sopenharmony_ci * LS(out) = 802.6 + 74.67*BC + HOST_DELAY 51162306a36Sopenharmony_ci * 51262306a36Sopenharmony_ci * HOST_DELAY == 106 for the c67200 and c67300. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* make calculations in 1/100 bit times to maintain resolution */ 51662306a36Sopenharmony_ci if (urbp->ep_data->dev->speed == USB_SPEED_LOW) { 51762306a36Sopenharmony_ci /* Low speed pipe */ 51862306a36Sopenharmony_ci if (usb_pipein(urb->pipe)) 51962306a36Sopenharmony_ci bit_time = 80240 + 7578*len; 52062306a36Sopenharmony_ci else 52162306a36Sopenharmony_ci bit_time = 80260 + 7467*len; 52262306a36Sopenharmony_ci } else { 52362306a36Sopenharmony_ci /* FS pipes */ 52462306a36Sopenharmony_ci if (usb_pipeisoc(urb->pipe)) 52562306a36Sopenharmony_ci bit_time = usb_pipein(urb->pipe) ? 9050 : 7840; 52662306a36Sopenharmony_ci else 52762306a36Sopenharmony_ci bit_time = 11250; 52862306a36Sopenharmony_ci bit_time += 936*len; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* Scale back down to integer bit times. Use a host delay of 106. 53262306a36Sopenharmony_ci * (this is the only place it is used) */ 53362306a36Sopenharmony_ci bit_time = ((bit_time+50) / 100) + 106; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (unlikely(bit_time + c67x00->bandwidth_allocated >= 53662306a36Sopenharmony_ci c67x00->max_frame_bw)) 53762306a36Sopenharmony_ci return -EMSGSIZE; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (unlikely(c67x00->next_td_addr + CY_TD_SIZE >= 54062306a36Sopenharmony_ci c67x00->td_base_addr + SIE_TD_SIZE)) 54162306a36Sopenharmony_ci return -EMSGSIZE; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (unlikely(c67x00->next_buf_addr + len >= 54462306a36Sopenharmony_ci c67x00->buf_base_addr + SIE_TD_BUF_SIZE)) 54562306a36Sopenharmony_ci return -EMSGSIZE; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (periodic) { 54862306a36Sopenharmony_ci if (unlikely(bit_time + c67x00->periodic_bw_allocated >= 54962306a36Sopenharmony_ci MAX_PERIODIC_BW(c67x00->max_frame_bw))) 55062306a36Sopenharmony_ci return -EMSGSIZE; 55162306a36Sopenharmony_ci c67x00->periodic_bw_allocated += bit_time; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci c67x00->bandwidth_allocated += bit_time; 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/* 56162306a36Sopenharmony_ci * td_addr and buf_addr must be word aligned 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_cistatic int c67x00_create_td(struct c67x00_hcd *c67x00, struct urb *urb, 56462306a36Sopenharmony_ci void *data, int len, int pid, int toggle, 56562306a36Sopenharmony_ci unsigned long privdata) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct c67x00_td *td; 56862306a36Sopenharmony_ci struct c67x00_urb_priv *urbp = urb->hcpriv; 56962306a36Sopenharmony_ci const __u8 active_flag = 1, retry_cnt = 3; 57062306a36Sopenharmony_ci __u8 cmd = 0; 57162306a36Sopenharmony_ci int tt = 0; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (c67x00_claim_frame_bw(c67x00, urb, len, usb_pipeisoc(urb->pipe) 57462306a36Sopenharmony_ci || usb_pipeint(urb->pipe))) 57562306a36Sopenharmony_ci return -EMSGSIZE; /* Not really an error, but expected */ 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci td = kzalloc(sizeof(*td), GFP_ATOMIC); 57862306a36Sopenharmony_ci if (!td) 57962306a36Sopenharmony_ci return -ENOMEM; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci td->pipe = urb->pipe; 58262306a36Sopenharmony_ci td->ep_data = urbp->ep_data; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if ((td_udev(td)->speed == USB_SPEED_LOW) && 58562306a36Sopenharmony_ci !(c67x00->low_speed_ports & (1 << urbp->port))) 58662306a36Sopenharmony_ci cmd |= PREAMBLE_EN; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci switch (usb_pipetype(td->pipe)) { 58962306a36Sopenharmony_ci case PIPE_ISOCHRONOUS: 59062306a36Sopenharmony_ci tt = TT_ISOCHRONOUS; 59162306a36Sopenharmony_ci cmd |= ISO_EN; 59262306a36Sopenharmony_ci break; 59362306a36Sopenharmony_ci case PIPE_CONTROL: 59462306a36Sopenharmony_ci tt = TT_CONTROL; 59562306a36Sopenharmony_ci break; 59662306a36Sopenharmony_ci case PIPE_BULK: 59762306a36Sopenharmony_ci tt = TT_BULK; 59862306a36Sopenharmony_ci break; 59962306a36Sopenharmony_ci case PIPE_INTERRUPT: 60062306a36Sopenharmony_ci tt = TT_INTERRUPT; 60162306a36Sopenharmony_ci break; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (toggle) 60562306a36Sopenharmony_ci cmd |= SEQ_SEL; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci cmd |= ARM_EN; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* SW part */ 61062306a36Sopenharmony_ci td->td_addr = c67x00->next_td_addr; 61162306a36Sopenharmony_ci c67x00->next_td_addr = c67x00->next_td_addr + CY_TD_SIZE; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* HW part */ 61462306a36Sopenharmony_ci td->ly_base_addr = __cpu_to_le16(c67x00->next_buf_addr); 61562306a36Sopenharmony_ci td->port_length = __cpu_to_le16((c67x00->sie->sie_num << 15) | 61662306a36Sopenharmony_ci (urbp->port << 14) | (len & 0x3FF)); 61762306a36Sopenharmony_ci td->pid_ep = ((pid & 0xF) << TD_PIDEP_OFFSET) | 61862306a36Sopenharmony_ci (usb_pipeendpoint(td->pipe) & 0xF); 61962306a36Sopenharmony_ci td->dev_addr = usb_pipedevice(td->pipe) & 0x7F; 62062306a36Sopenharmony_ci td->ctrl_reg = cmd; 62162306a36Sopenharmony_ci td->status = 0; 62262306a36Sopenharmony_ci td->retry_cnt = (tt << TT_OFFSET) | (active_flag << 4) | retry_cnt; 62362306a36Sopenharmony_ci td->residue = 0; 62462306a36Sopenharmony_ci td->next_td_addr = __cpu_to_le16(c67x00->next_td_addr); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* SW part */ 62762306a36Sopenharmony_ci td->data = data; 62862306a36Sopenharmony_ci td->urb = urb; 62962306a36Sopenharmony_ci td->privdata = privdata; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci c67x00->next_buf_addr += (len + 1) & ~0x01; /* properly align */ 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci list_add_tail(&td->td_list, &c67x00->td_list); 63462306a36Sopenharmony_ci return 0; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic inline void c67x00_release_td(struct c67x00_td *td) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci list_del_init(&td->td_list); 64062306a36Sopenharmony_ci kfree(td); 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic int c67x00_add_data_urb(struct c67x00_hcd *c67x00, struct urb *urb) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci int remaining; 64862306a36Sopenharmony_ci int toggle; 64962306a36Sopenharmony_ci int pid; 65062306a36Sopenharmony_ci int ret = 0; 65162306a36Sopenharmony_ci int maxps; 65262306a36Sopenharmony_ci int need_empty; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), 65562306a36Sopenharmony_ci usb_pipeout(urb->pipe)); 65662306a36Sopenharmony_ci remaining = urb->transfer_buffer_length - urb->actual_length; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci maxps = usb_maxpacket(urb->dev, urb->pipe); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci need_empty = (urb->transfer_flags & URB_ZERO_PACKET) && 66162306a36Sopenharmony_ci usb_pipeout(urb->pipe) && !(remaining % maxps); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci while (remaining || need_empty) { 66462306a36Sopenharmony_ci int len; 66562306a36Sopenharmony_ci char *td_buf; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci len = (remaining > maxps) ? maxps : remaining; 66862306a36Sopenharmony_ci if (!len) 66962306a36Sopenharmony_ci need_empty = 0; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci pid = usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN; 67262306a36Sopenharmony_ci td_buf = urb->transfer_buffer + urb->transfer_buffer_length - 67362306a36Sopenharmony_ci remaining; 67462306a36Sopenharmony_ci ret = c67x00_create_td(c67x00, urb, td_buf, len, pid, toggle, 67562306a36Sopenharmony_ci DATA_STAGE); 67662306a36Sopenharmony_ci if (ret) 67762306a36Sopenharmony_ci return ret; /* td wasn't created */ 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci toggle ^= 1; 68062306a36Sopenharmony_ci remaining -= len; 68162306a36Sopenharmony_ci if (usb_pipecontrol(urb->pipe)) 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return 0; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci/* 68962306a36Sopenharmony_ci * return 0 in case more bandwidth is available, else errorcode 69062306a36Sopenharmony_ci */ 69162306a36Sopenharmony_cistatic int c67x00_add_ctrl_urb(struct c67x00_hcd *c67x00, struct urb *urb) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci int ret; 69462306a36Sopenharmony_ci int pid; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci switch (urb->interval) { 69762306a36Sopenharmony_ci default: 69862306a36Sopenharmony_ci case SETUP_STAGE: 69962306a36Sopenharmony_ci ret = c67x00_create_td(c67x00, urb, urb->setup_packet, 70062306a36Sopenharmony_ci 8, USB_PID_SETUP, 0, SETUP_STAGE); 70162306a36Sopenharmony_ci if (ret) 70262306a36Sopenharmony_ci return ret; 70362306a36Sopenharmony_ci urb->interval = SETUP_STAGE; 70462306a36Sopenharmony_ci usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), 70562306a36Sopenharmony_ci usb_pipeout(urb->pipe), 1); 70662306a36Sopenharmony_ci break; 70762306a36Sopenharmony_ci case DATA_STAGE: 70862306a36Sopenharmony_ci if (urb->transfer_buffer_length) { 70962306a36Sopenharmony_ci ret = c67x00_add_data_urb(c67x00, urb); 71062306a36Sopenharmony_ci if (ret) 71162306a36Sopenharmony_ci return ret; 71262306a36Sopenharmony_ci break; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci fallthrough; 71562306a36Sopenharmony_ci case STATUS_STAGE: 71662306a36Sopenharmony_ci pid = !usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN; 71762306a36Sopenharmony_ci ret = c67x00_create_td(c67x00, urb, NULL, 0, pid, 1, 71862306a36Sopenharmony_ci STATUS_STAGE); 71962306a36Sopenharmony_ci if (ret) 72062306a36Sopenharmony_ci return ret; 72162306a36Sopenharmony_ci break; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci return 0; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci/* 72862306a36Sopenharmony_ci * return 0 in case more bandwidth is available, else errorcode 72962306a36Sopenharmony_ci */ 73062306a36Sopenharmony_cistatic int c67x00_add_int_urb(struct c67x00_hcd *c67x00, struct urb *urb) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci struct c67x00_urb_priv *urbp = urb->hcpriv; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (frame_after_eq(c67x00->current_frame, urbp->ep_data->next_frame)) { 73562306a36Sopenharmony_ci urbp->ep_data->next_frame = 73662306a36Sopenharmony_ci frame_add(urbp->ep_data->next_frame, urb->interval); 73762306a36Sopenharmony_ci return c67x00_add_data_urb(c67x00, urb); 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci return 0; 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic int c67x00_add_iso_urb(struct c67x00_hcd *c67x00, struct urb *urb) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci struct c67x00_urb_priv *urbp = urb->hcpriv; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (frame_after_eq(c67x00->current_frame, urbp->ep_data->next_frame)) { 74762306a36Sopenharmony_ci char *td_buf; 74862306a36Sopenharmony_ci int len, pid, ret; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci BUG_ON(urbp->cnt >= urb->number_of_packets); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci td_buf = urb->transfer_buffer + 75362306a36Sopenharmony_ci urb->iso_frame_desc[urbp->cnt].offset; 75462306a36Sopenharmony_ci len = urb->iso_frame_desc[urbp->cnt].length; 75562306a36Sopenharmony_ci pid = usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci ret = c67x00_create_td(c67x00, urb, td_buf, len, pid, 0, 75862306a36Sopenharmony_ci urbp->cnt); 75962306a36Sopenharmony_ci if (ret) { 76062306a36Sopenharmony_ci dev_dbg(c67x00_hcd_dev(c67x00), "create failed: %d\n", 76162306a36Sopenharmony_ci ret); 76262306a36Sopenharmony_ci urb->iso_frame_desc[urbp->cnt].actual_length = 0; 76362306a36Sopenharmony_ci urb->iso_frame_desc[urbp->cnt].status = ret; 76462306a36Sopenharmony_ci if (urbp->cnt + 1 == urb->number_of_packets) 76562306a36Sopenharmony_ci c67x00_giveback_urb(c67x00, urb, 0); 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci urbp->ep_data->next_frame = 76962306a36Sopenharmony_ci frame_add(urbp->ep_data->next_frame, urb->interval); 77062306a36Sopenharmony_ci urbp->cnt++; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci return 0; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic void c67x00_fill_from_list(struct c67x00_hcd *c67x00, int type, 77862306a36Sopenharmony_ci int (*add)(struct c67x00_hcd *, struct urb *)) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci struct c67x00_ep_data *ep_data; 78162306a36Sopenharmony_ci struct urb *urb; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* traverse every endpoint on the list */ 78462306a36Sopenharmony_ci list_for_each_entry(ep_data, &c67x00->list[type], node) { 78562306a36Sopenharmony_ci if (!list_empty(&ep_data->queue)) { 78662306a36Sopenharmony_ci /* and add the first urb */ 78762306a36Sopenharmony_ci /* isochronous transfer rely on this */ 78862306a36Sopenharmony_ci urb = list_entry(ep_data->queue.next, 78962306a36Sopenharmony_ci struct c67x00_urb_priv, 79062306a36Sopenharmony_ci hep_node)->urb; 79162306a36Sopenharmony_ci add(c67x00, urb); 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic void c67x00_fill_frame(struct c67x00_hcd *c67x00) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci struct c67x00_td *td, *ttd; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* Check if we can proceed */ 80162306a36Sopenharmony_ci if (!list_empty(&c67x00->td_list)) { 80262306a36Sopenharmony_ci dev_warn(c67x00_hcd_dev(c67x00), 80362306a36Sopenharmony_ci "TD list not empty! This should not happen!\n"); 80462306a36Sopenharmony_ci list_for_each_entry_safe(td, ttd, &c67x00->td_list, td_list) { 80562306a36Sopenharmony_ci dbg_td(c67x00, td, "Unprocessed td"); 80662306a36Sopenharmony_ci c67x00_release_td(td); 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* Reinitialize variables */ 81162306a36Sopenharmony_ci c67x00->bandwidth_allocated = 0; 81262306a36Sopenharmony_ci c67x00->periodic_bw_allocated = 0; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci c67x00->next_td_addr = c67x00->td_base_addr; 81562306a36Sopenharmony_ci c67x00->next_buf_addr = c67x00->buf_base_addr; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* Fill the list */ 81862306a36Sopenharmony_ci c67x00_fill_from_list(c67x00, PIPE_ISOCHRONOUS, c67x00_add_iso_urb); 81962306a36Sopenharmony_ci c67x00_fill_from_list(c67x00, PIPE_INTERRUPT, c67x00_add_int_urb); 82062306a36Sopenharmony_ci c67x00_fill_from_list(c67x00, PIPE_CONTROL, c67x00_add_ctrl_urb); 82162306a36Sopenharmony_ci c67x00_fill_from_list(c67x00, PIPE_BULK, c67x00_add_data_urb); 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci/* 82762306a36Sopenharmony_ci * Get TD from C67X00 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_cistatic inline void 83062306a36Sopenharmony_cic67x00_parse_td(struct c67x00_hcd *c67x00, struct c67x00_td *td) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci c67x00_ll_read_mem_le16(c67x00->sie->dev, 83362306a36Sopenharmony_ci td->td_addr, td, CY_TD_SIZE); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (usb_pipein(td->pipe) && td_actual_bytes(td)) 83662306a36Sopenharmony_ci c67x00_ll_read_mem_le16(c67x00->sie->dev, td_ly_base_addr(td), 83762306a36Sopenharmony_ci td->data, td_actual_bytes(td)); 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistatic int c67x00_td_to_error(struct c67x00_hcd *c67x00, struct c67x00_td *td) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci if (td->status & TD_STATUSMASK_ERR) { 84362306a36Sopenharmony_ci dbg_td(c67x00, td, "ERROR_FLAG"); 84462306a36Sopenharmony_ci return -EILSEQ; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci if (td->status & TD_STATUSMASK_STALL) { 84762306a36Sopenharmony_ci /* dbg_td(c67x00, td, "STALL"); */ 84862306a36Sopenharmony_ci return -EPIPE; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci if (td->status & TD_STATUSMASK_TMOUT) { 85162306a36Sopenharmony_ci dbg_td(c67x00, td, "TIMEOUT"); 85262306a36Sopenharmony_ci return -ETIMEDOUT; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci return 0; 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic inline int c67x00_end_of_data(struct c67x00_td *td) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci int maxps, need_empty, remaining; 86162306a36Sopenharmony_ci struct urb *urb = td->urb; 86262306a36Sopenharmony_ci int act_bytes; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci act_bytes = td_actual_bytes(td); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (unlikely(!act_bytes)) 86762306a36Sopenharmony_ci return 1; /* This was an empty packet */ 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci maxps = usb_maxpacket(td_udev(td), td->pipe); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (unlikely(act_bytes < maxps)) 87262306a36Sopenharmony_ci return 1; /* Smaller then full packet */ 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci remaining = urb->transfer_buffer_length - urb->actual_length; 87562306a36Sopenharmony_ci need_empty = (urb->transfer_flags & URB_ZERO_PACKET) && 87662306a36Sopenharmony_ci usb_pipeout(urb->pipe) && !(remaining % maxps); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (unlikely(!remaining && !need_empty)) 87962306a36Sopenharmony_ci return 1; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci return 0; 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci/* Remove all td's from the list which come 88762306a36Sopenharmony_ci * after last_td and are meant for the same pipe. 88862306a36Sopenharmony_ci * This is used when a short packet has occurred */ 88962306a36Sopenharmony_cistatic inline void c67x00_clear_pipe(struct c67x00_hcd *c67x00, 89062306a36Sopenharmony_ci struct c67x00_td *last_td) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci struct c67x00_td *td, *tmp; 89362306a36Sopenharmony_ci td = last_td; 89462306a36Sopenharmony_ci tmp = last_td; 89562306a36Sopenharmony_ci while (td->td_list.next != &c67x00->td_list) { 89662306a36Sopenharmony_ci td = list_entry(td->td_list.next, struct c67x00_td, td_list); 89762306a36Sopenharmony_ci if (td->pipe == last_td->pipe) { 89862306a36Sopenharmony_ci c67x00_release_td(td); 89962306a36Sopenharmony_ci td = tmp; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci tmp = td; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic void c67x00_handle_successful_td(struct c67x00_hcd *c67x00, 90862306a36Sopenharmony_ci struct c67x00_td *td) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci struct urb *urb = td->urb; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci if (!urb) 91362306a36Sopenharmony_ci return; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci urb->actual_length += td_actual_bytes(td); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci switch (usb_pipetype(td->pipe)) { 91862306a36Sopenharmony_ci /* isochronous tds are handled separately */ 91962306a36Sopenharmony_ci case PIPE_CONTROL: 92062306a36Sopenharmony_ci switch (td->privdata) { 92162306a36Sopenharmony_ci case SETUP_STAGE: 92262306a36Sopenharmony_ci urb->interval = 92362306a36Sopenharmony_ci urb->transfer_buffer_length ? 92462306a36Sopenharmony_ci DATA_STAGE : STATUS_STAGE; 92562306a36Sopenharmony_ci /* Don't count setup_packet with normal data: */ 92662306a36Sopenharmony_ci urb->actual_length = 0; 92762306a36Sopenharmony_ci break; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci case DATA_STAGE: 93062306a36Sopenharmony_ci if (c67x00_end_of_data(td)) { 93162306a36Sopenharmony_ci urb->interval = STATUS_STAGE; 93262306a36Sopenharmony_ci c67x00_clear_pipe(c67x00, td); 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci break; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci case STATUS_STAGE: 93762306a36Sopenharmony_ci urb->interval = 0; 93862306a36Sopenharmony_ci c67x00_giveback_urb(c67x00, urb, 0); 93962306a36Sopenharmony_ci break; 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci break; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci case PIPE_INTERRUPT: 94462306a36Sopenharmony_ci case PIPE_BULK: 94562306a36Sopenharmony_ci if (unlikely(c67x00_end_of_data(td))) { 94662306a36Sopenharmony_ci c67x00_clear_pipe(c67x00, td); 94762306a36Sopenharmony_ci c67x00_giveback_urb(c67x00, urb, 0); 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci break; 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic void c67x00_handle_isoc(struct c67x00_hcd *c67x00, struct c67x00_td *td) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci struct urb *urb = td->urb; 95662306a36Sopenharmony_ci int cnt; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (!urb) 95962306a36Sopenharmony_ci return; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci cnt = td->privdata; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci if (td->status & TD_ERROR_MASK) 96462306a36Sopenharmony_ci urb->error_count++; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci urb->iso_frame_desc[cnt].actual_length = td_actual_bytes(td); 96762306a36Sopenharmony_ci urb->iso_frame_desc[cnt].status = c67x00_td_to_error(c67x00, td); 96862306a36Sopenharmony_ci if (cnt + 1 == urb->number_of_packets) /* Last packet */ 96962306a36Sopenharmony_ci c67x00_giveback_urb(c67x00, urb, 0); 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci/* 97562306a36Sopenharmony_ci * c67x00_check_td_list - handle tds which have been processed by the c67x00 97662306a36Sopenharmony_ci * pre: current_td == 0 97762306a36Sopenharmony_ci */ 97862306a36Sopenharmony_cistatic inline void c67x00_check_td_list(struct c67x00_hcd *c67x00) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci struct c67x00_td *td, *tmp; 98162306a36Sopenharmony_ci struct urb *urb; 98262306a36Sopenharmony_ci int ack_ok; 98362306a36Sopenharmony_ci int clear_endpoint; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci list_for_each_entry_safe(td, tmp, &c67x00->td_list, td_list) { 98662306a36Sopenharmony_ci /* get the TD */ 98762306a36Sopenharmony_ci c67x00_parse_td(c67x00, td); 98862306a36Sopenharmony_ci urb = td->urb; /* urb can be NULL! */ 98962306a36Sopenharmony_ci ack_ok = 0; 99062306a36Sopenharmony_ci clear_endpoint = 1; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* Handle isochronous transfers separately */ 99362306a36Sopenharmony_ci if (usb_pipeisoc(td->pipe)) { 99462306a36Sopenharmony_ci clear_endpoint = 0; 99562306a36Sopenharmony_ci c67x00_handle_isoc(c67x00, td); 99662306a36Sopenharmony_ci goto cont; 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci /* When an error occurs, all td's for that pipe go into an 100062306a36Sopenharmony_ci * inactive state. This state matches successful transfers so 100162306a36Sopenharmony_ci * we must make sure not to service them. */ 100262306a36Sopenharmony_ci if (td->status & TD_ERROR_MASK) { 100362306a36Sopenharmony_ci c67x00_giveback_urb(c67x00, urb, 100462306a36Sopenharmony_ci c67x00_td_to_error(c67x00, td)); 100562306a36Sopenharmony_ci goto cont; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if ((td->status & TD_STATUSMASK_NAK) || !td_sequence_ok(td) || 100962306a36Sopenharmony_ci !td_acked(td)) 101062306a36Sopenharmony_ci goto cont; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci /* Sequence ok and acked, don't need to fix toggle */ 101362306a36Sopenharmony_ci ack_ok = 1; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (unlikely(td->status & TD_STATUSMASK_OVF)) { 101662306a36Sopenharmony_ci if (td_residue(td) & TD_RESIDUE_OVERFLOW) { 101762306a36Sopenharmony_ci /* Overflow */ 101862306a36Sopenharmony_ci c67x00_giveback_urb(c67x00, urb, -EOVERFLOW); 101962306a36Sopenharmony_ci goto cont; 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci clear_endpoint = 0; 102462306a36Sopenharmony_ci c67x00_handle_successful_td(c67x00, td); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cicont: 102762306a36Sopenharmony_ci if (clear_endpoint) 102862306a36Sopenharmony_ci c67x00_clear_pipe(c67x00, td); 102962306a36Sopenharmony_ci if (ack_ok) 103062306a36Sopenharmony_ci usb_settoggle(td_udev(td), usb_pipeendpoint(td->pipe), 103162306a36Sopenharmony_ci usb_pipeout(td->pipe), 103262306a36Sopenharmony_ci !(td->ctrl_reg & SEQ_SEL)); 103362306a36Sopenharmony_ci /* next in list could have been removed, due to clear_pipe! */ 103462306a36Sopenharmony_ci tmp = list_entry(td->td_list.next, typeof(*td), td_list); 103562306a36Sopenharmony_ci c67x00_release_td(td); 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cistatic inline int c67x00_all_tds_processed(struct c67x00_hcd *c67x00) 104262306a36Sopenharmony_ci{ 104362306a36Sopenharmony_ci /* If all tds are processed, we can check the previous frame (if 104462306a36Sopenharmony_ci * there was any) and start our next frame. 104562306a36Sopenharmony_ci */ 104662306a36Sopenharmony_ci return !c67x00_ll_husb_get_current_td(c67x00->sie); 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci/* 105062306a36Sopenharmony_ci * Send td to C67X00 105162306a36Sopenharmony_ci */ 105262306a36Sopenharmony_cistatic void c67x00_send_td(struct c67x00_hcd *c67x00, struct c67x00_td *td) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci int len = td_length(td); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci if (len && ((td->pid_ep & TD_PIDEPMASK_PID) != TD_PID_IN)) 105762306a36Sopenharmony_ci c67x00_ll_write_mem_le16(c67x00->sie->dev, td_ly_base_addr(td), 105862306a36Sopenharmony_ci td->data, len); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci c67x00_ll_write_mem_le16(c67x00->sie->dev, 106162306a36Sopenharmony_ci td->td_addr, td, CY_TD_SIZE); 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cistatic void c67x00_send_frame(struct c67x00_hcd *c67x00) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci struct c67x00_td *td; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci if (list_empty(&c67x00->td_list)) 106962306a36Sopenharmony_ci dev_warn(c67x00_hcd_dev(c67x00), 107062306a36Sopenharmony_ci "%s: td list should not be empty here!\n", 107162306a36Sopenharmony_ci __func__); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci list_for_each_entry(td, &c67x00->td_list, td_list) { 107462306a36Sopenharmony_ci if (td->td_list.next == &c67x00->td_list) 107562306a36Sopenharmony_ci td->next_td_addr = 0; /* Last td in list */ 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci c67x00_send_td(c67x00, td); 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci c67x00_ll_husb_set_current_td(c67x00->sie, c67x00->td_base_addr); 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci/* 108662306a36Sopenharmony_ci * c67x00_do_work - Schedulers state machine 108762306a36Sopenharmony_ci */ 108862306a36Sopenharmony_cistatic void c67x00_do_work(struct c67x00_hcd *c67x00) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci spin_lock(&c67x00->lock); 109162306a36Sopenharmony_ci /* Make sure all tds are processed */ 109262306a36Sopenharmony_ci if (!c67x00_all_tds_processed(c67x00)) 109362306a36Sopenharmony_ci goto out; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci c67x00_check_td_list(c67x00); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci /* no td's are being processed (current == 0) 109862306a36Sopenharmony_ci * and all have been "checked" */ 109962306a36Sopenharmony_ci complete(&c67x00->endpoint_disable); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci if (!list_empty(&c67x00->td_list)) 110262306a36Sopenharmony_ci goto out; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci c67x00->current_frame = c67x00_get_current_frame_number(c67x00); 110562306a36Sopenharmony_ci if (c67x00->current_frame == c67x00->last_frame) 110662306a36Sopenharmony_ci goto out; /* Don't send tds in same frame */ 110762306a36Sopenharmony_ci c67x00->last_frame = c67x00->current_frame; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci /* If no urbs are scheduled, our work is done */ 111062306a36Sopenharmony_ci if (!c67x00->urb_count) { 111162306a36Sopenharmony_ci c67x00_ll_hpi_disable_sofeop(c67x00->sie); 111262306a36Sopenharmony_ci goto out; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci c67x00_fill_frame(c67x00); 111662306a36Sopenharmony_ci if (!list_empty(&c67x00->td_list)) 111762306a36Sopenharmony_ci /* TD's have been added to the frame */ 111862306a36Sopenharmony_ci c67x00_send_frame(c67x00); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci out: 112162306a36Sopenharmony_ci spin_unlock(&c67x00->lock); 112262306a36Sopenharmony_ci} 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_cistatic void c67x00_sched_work(struct work_struct *work) 112762306a36Sopenharmony_ci{ 112862306a36Sopenharmony_ci struct c67x00_hcd *c67x00; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci c67x00 = container_of(work, struct c67x00_hcd, work); 113162306a36Sopenharmony_ci c67x00_do_work(c67x00); 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_civoid c67x00_sched_kick(struct c67x00_hcd *c67x00) 113562306a36Sopenharmony_ci{ 113662306a36Sopenharmony_ci queue_work(system_highpri_wq, &c67x00->work); 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ciint c67x00_sched_start_scheduler(struct c67x00_hcd *c67x00) 114062306a36Sopenharmony_ci{ 114162306a36Sopenharmony_ci INIT_WORK(&c67x00->work, c67x00_sched_work); 114262306a36Sopenharmony_ci return 0; 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_civoid c67x00_sched_stop_scheduler(struct c67x00_hcd *c67x00) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci cancel_work_sync(&c67x00->work); 114862306a36Sopenharmony_ci} 1149