/*
* c67x00-sched.c: Cypress C67X00 USB Host Controller Driver - TD scheduling
*
* Copyright (C) 2006-2008 Barco N.V.
* Derived from the Cypress cy7c67200/300 ezusb linux driver and
* based on multiple host controller drivers inside the linux kernel.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA.
*/
#include <linux/kthread.h>
#include "c67x00.h"
#include "c67x00-hcd.h"
/*
* These are the stages for a control urb, they are kept
* in both urb->interval and td->privdata.
*/
#define SETUP_STAGE 0
#define DATA_STAGE 1
#define STATUS_STAGE 2
/* -------------------------------------------------------------------------- */
/**
* struct c67x00_ep_data: Host endpoint data structure
*/
struct c67x00_ep_data {
struct list_head queue;
struct list_head node;
struct usb_host_endpoint *hep;
struct usb_device *dev;
u16 next_frame; /* For int/isoc transactions */
};
/**
* struct c67x00_td
*
* Hardware parts are little endiannes, SW in CPU endianess.
*/
struct c67x00_td {
/* HW specific part */
__le16 ly_base_addr; /* Bytes 0-1 */
__le16 port_length; /* Bytes 2-3 */
u8 pid_ep; /* Byte 4 */
u8 dev_addr; /* Byte 5 */
u8 ctrl_reg; /* Byte 6 */
u8 status; /* Byte 7 */
u8 retry_cnt; /* Byte 8 */
#define TT_OFFSET 2
#define TT_CONTROL 0
#define TT_ISOCHRONOUS 1
#define TT_BULK 2
#define TT_INTERRUPT 3
u8 residue; /* Byte 9 */
__le16 next_td_addr; /* Bytes 10-11 */
/* SW part */
struct list_head td_list;
u16 td_addr;
void *data;
struct urb *urb;
unsigned long privdata;
/* These are needed for handling the toggle bits:
* an urb can be dequeued while a td is in progress
* after checking the td, the toggle bit might need to
* be fixed */
struct c67x00_ep_data *ep_data;
unsigned int pipe;
};
struct c67x00_urb_priv {
struct list_head hep_node;
struct urb *urb;
int port;
int cnt; /* packet number for isoc */
int status;
struct c67x00_ep_data *ep_data;
};
#define td_udev(td) ((td)->ep_data->dev)
#define CY_TD_SIZE 12
#define TD_PIDEP_OFFSET 0x04
#define TD_PIDEPMASK_PID 0xF0
#define TD_PIDEPMASK_EP 0x0F
#define TD_PORTLENMASK_DL 0x02FF
#define TD_PORTLENMASK_PN 0xC000
#define TD_STATUS_OFFSET 0x07
#define TD_STATUSMASK_ACK 0x01
#define TD_STATUSMASK_ERR 0x02
#define TD_STATUSMASK_TMOUT 0x04
#define TD_STATUSMASK_SEQ 0x08
#define TD_STATUSMASK_SETUP 0x10
#define TD_STATUSMASK_OVF 0x20
#define TD_STATUSMASK_NAK 0x40
#define TD_STATUSMASK_STALL 0x80
#define TD_ERROR_MASK (TD_STATUSMASK_ERR | TD_STATUSMASK_TMOUT | \
TD_STATUSMASK_STALL)
#define TD_RETRYCNT_OFFSET 0x08
#define TD_RETRYCNTMASK_ACT_FLG 0x10
#define TD_RETRYCNTMASK_TX_TYPE 0x0C
#define TD_RETRYCNTMASK_RTY_CNT 0x03
#define TD_RESIDUE_OVERFLOW 0x80
#define TD_PID_IN 0x90
/* Residue: signed 8bits, neg -> OVERFLOW, pos -> UNDERFLOW */
#define td_residue(td) ((__s8)(td->residue))
#define td_ly_base_addr(td) (__le16_to_cpu((td)->ly_base_addr))
#define td_port_length(td) (__le16_to_cpu((td)->port_length))
#define td_next_td_addr(td) (__le16_to_cpu((td)->next_td_addr))
#define td_active(td) ((td)->retry_cnt & TD_RETRYCNTMASK_ACT_FLG)
#define td_length(td) (td_port_length(td) & TD_PORTLENMASK_DL)
#define td_sequence_ok(td) (!td->status || \
(!(td->status & TD_STATUSMASK_SEQ) == \
!(td->ctrl_reg & SEQ_SEL)))
#define td_acked(td) (!td->status || \
(td->status & TD_STATUSMASK_ACK))
#define td_actual_bytes(td) (td_length(td) - td_residue(td))
/* -------------------------------------------------------------------------- */
#ifdef DEBUG
/**
* dbg_td - Dump the contents of the TD
*/
static void dbg_td(struct c67x00_hcd *c67x00, struct c67x00_td *td, char *msg)
{
struct device *dev = c67x00_hcd_dev(c67x00);
dev_dbg(dev, "### %s at 0x%04x\n", msg, td->td_addr);
dev_dbg(dev, "urb: 0x%p\n", td->urb);
dev_dbg(dev, "endpoint: %4d\n", usb_pipeendpoint(td->pipe));
dev_dbg(dev, "pipeout: %4d\n", usb_pipeout(td->pipe));
dev_dbg(dev, "ly_base_addr: 0x%04x\n", td_ly_base_addr(td));
dev_dbg(dev, "port_length: 0x%04x\n", td_port_length(td));
dev_dbg(dev, "pid_ep: 0x%02x\n", td->pid_ep);
dev_dbg(dev, "dev_addr: 0x%02x\n", td->dev_addr);
dev_dbg(dev, "ctrl_reg: 0x%02x\n", td->ctrl_reg);
dev_dbg(dev, "status: 0x%02x\n", td