2008-04-24 02:37:04 +04:00
/*
* Driver for the NXP ISP1760 chip
*
* However , the code might contain some bugs . What doesn ' t work for sure is :
* - ISO
* - OTG
e The interrupt line is configured as active low , level .
*
* ( c ) 2007 Sebastian Siewior < bigeasy @ linutronix . de >
*
2011-04-26 23:48:30 +04:00
* ( c ) 2011 Arvid Brodin < arvid . brodin @ enea . com >
*
2008-04-24 02:37:04 +04:00
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/list.h>
# include <linux/usb.h>
2010-04-25 01:21:52 +04:00
# include <linux/usb/hcd.h>
2008-04-24 02:37:04 +04:00
# include <linux/debugfs.h>
# include <linux/uaccess.h>
# include <linux/io.h>
2010-02-02 18:31:02 +03:00
# include <linux/mm.h>
2011-08-21 10:29:26 +04:00
# include <linux/timer.h>
2008-04-24 02:37:04 +04:00
# include <asm/unaligned.h>
2010-02-02 18:31:02 +03:00
# include <asm/cacheflush.h>
2011-10-19 16:18:41 +04:00
# include <linux/gpio.h>
2008-04-24 02:37:04 +04:00
# include "isp1760-hcd.h"
static struct kmem_cache * qtd_cachep ;
static struct kmem_cache * qh_cachep ;
2011-04-26 23:48:30 +04:00
static struct kmem_cache * urb_listitem_cachep ;
2008-04-24 02:37:04 +04:00
struct isp1760_hcd {
u32 hcs_params ;
spinlock_t lock ;
2011-04-26 23:48:30 +04:00
struct slotinfo atl_slots [ 32 ] ;
2011-05-20 02:17:45 +04:00
int atl_done_map ;
2011-04-26 23:48:30 +04:00
struct slotinfo int_slots [ 32 ] ;
2011-05-20 02:17:45 +04:00
int int_done_map ;
2008-04-24 02:37:04 +04:00
struct memory_chunk memory_pool [ BLOCKS ] ;
2011-04-26 23:48:30 +04:00
struct list_head controlqhs , bulkqhs , interruptqhs ;
2008-04-24 02:37:04 +04:00
/* periodic schedule support */
# define DEFAULT_I_TDPS 1024
unsigned periodic_size ;
unsigned i_thresh ;
unsigned long reset_done ;
unsigned long next_statechange ;
2008-06-17 20:11:38 +04:00
unsigned int devflags ;
2011-10-19 16:18:41 +04:00
int rst_gpio ;
2008-04-24 02:37:04 +04:00
} ;
static inline struct isp1760_hcd * hcd_to_priv ( struct usb_hcd * hcd )
{
return ( struct isp1760_hcd * ) ( hcd - > hcd_priv ) ;
}
/* Section 2.2 Host Controller Capability Registers */
# define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
# define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
# define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
# define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
# define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
# define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
# define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
/* Section 2.3 Host Controller Operational Registers */
# define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
# define CMD_RESET (1<<1) /* reset HC not bus */
# define CMD_RUN (1<<0) /* start/stop HC */
# define STS_PCD (1<<2) /* port change detect */
# define FLAG_CF (1<<0) /* true: we'll support "high speed" */
# define PORT_OWNER (1<<13) /* true: companion hc owns this port */
# define PORT_POWER (1<<12) /* true: has power (see PPC) */
# define PORT_USB11(x) (((x) & (3 << 10)) == (1 << 10)) /* USB 1.1 device */
# define PORT_RESET (1<<8) /* reset port */
# define PORT_SUSPEND (1<<7) /* suspend port */
# define PORT_RESUME (1<<6) /* resume it */
# define PORT_PE (1<<2) /* port enable */
# define PORT_CSC (1<<1) /* connect status change */
# define PORT_CONNECT (1<<0) /* device connected */
# define PORT_RWC_BITS (PORT_CSC)
struct isp1760_qtd {
u8 packet_type ;
void * data_buffer ;
2011-02-27 00:04:40 +03:00
u32 payload_addr ;
2008-04-24 02:37:04 +04:00
/* the rest is HCD-private */
struct list_head qtd_list ;
struct urb * urb ;
size_t length ;
2011-04-26 23:48:30 +04:00
size_t actual_length ;
/* QTD_ENQUEUED: waiting for transfer (inactive) */
/* QTD_PAYLOAD_ALLOC: chip mem has been allocated for payload */
/* QTD_XFER_STARTED: valid ptd has been written to isp176x - only
interrupt handler may touch this qtd ! */
/* QTD_XFER_COMPLETE: payload has been transferred successfully */
/* QTD_RETIRE: transfer error/abort qtd */
# define QTD_ENQUEUED 0
# define QTD_PAYLOAD_ALLOC 1
# define QTD_XFER_STARTED 2
# define QTD_XFER_COMPLETE 3
# define QTD_RETIRE 4
2008-04-24 02:37:04 +04:00
u32 status ;
} ;
2011-04-26 23:48:30 +04:00
/* Queue head, one for each active endpoint */
2008-04-24 02:37:04 +04:00
struct isp1760_qh {
2011-04-26 23:48:30 +04:00
struct list_head qh_list ;
2008-04-24 02:37:04 +04:00
struct list_head qtd_list ;
u32 toggle ;
u32 ping ;
2011-04-26 23:48:30 +04:00
int slot ;
2011-08-23 03:25:30 +04:00
int tt_buffer_dirty ; /* See USB2.0 spec section 11.17.5 */
2011-04-26 23:48:30 +04:00
} ;
struct urb_listitem {
struct list_head urb_list ;
struct urb * urb ;
2008-04-24 02:37:04 +04:00
} ;
2011-02-27 00:02:57 +03:00
/*
* Access functions for isp176x registers ( addresses 0. .0 x03FF ) .
*/
static u32 reg_read32 ( void __iomem * base , u32 reg )
2008-04-24 02:37:04 +04:00
{
2011-02-27 00:02:57 +03:00
return readl ( base + reg ) ;
2008-04-24 02:37:04 +04:00
}
2011-02-27 00:02:57 +03:00
static void reg_write32 ( void __iomem * base , u32 reg , u32 val )
2008-04-24 02:37:04 +04:00
{
2011-02-27 00:02:57 +03:00
writel ( val , base + reg ) ;
2008-04-24 02:37:04 +04:00
}
/*
2011-02-27 00:02:57 +03:00
* Access functions for isp176x memory ( offset > = 0x0400 ) .
*
* bank_reads8 ( ) reads memory locations prefetched by an earlier write to
* HC_MEMORY_REG ( see isp176x datasheet ) . Unless you want to do fancy multi -
* bank optimizations , you should use the more generic mem_reads8 ( ) below .
*
* For access to ptd memory , use the specialized ptd_read ( ) and ptd_write ( )
* below .
*
* These functions copy via MMIO data to / from the device . memcpy_ { to | from } io ( )
2008-04-24 02:37:04 +04:00
* doesn ' t quite work because some people have to enforce 32 - bit access
*/
2011-02-27 00:02:57 +03:00
static void bank_reads8 ( void __iomem * src_base , u32 src_offset , u32 bank_addr ,
__u32 * dst , u32 bytes )
2008-04-24 02:37:04 +04:00
{
2011-02-27 00:02:57 +03:00
__u32 __iomem * src ;
2008-04-24 02:37:04 +04:00
u32 val ;
2011-02-27 00:02:57 +03:00
__u8 * src_byteptr ;
__u8 * dst_byteptr ;
2008-04-24 02:37:04 +04:00
2011-02-27 00:02:57 +03:00
src = src_base + ( bank_addr | src_offset ) ;
2008-04-24 02:37:04 +04:00
2011-02-27 00:02:57 +03:00
if ( src_offset < PAYLOAD_OFFSET ) {
while ( bytes > = 4 ) {
* dst = le32_to_cpu ( __raw_readl ( src ) ) ;
bytes - = 4 ;
src + + ;
dst + + ;
}
} else {
while ( bytes > = 4 ) {
* dst = __raw_readl ( src ) ;
bytes - = 4 ;
src + + ;
dst + + ;
}
2008-04-24 02:37:04 +04:00
}
2011-02-27 00:02:57 +03:00
if ( ! bytes )
2008-04-24 02:37:04 +04:00
return ;
/* in case we have 3, 2 or 1 by left. The dst buffer may not be fully
* allocated .
*/
2011-02-27 00:02:57 +03:00
if ( src_offset < PAYLOAD_OFFSET )
val = le32_to_cpu ( __raw_readl ( src ) ) ;
else
val = __raw_readl ( src ) ;
dst_byteptr = ( void * ) dst ;
src_byteptr = ( void * ) & val ;
while ( bytes > 0 ) {
* dst_byteptr = * src_byteptr ;
dst_byteptr + + ;
src_byteptr + + ;
bytes - - ;
2008-04-24 02:37:04 +04:00
}
}
2011-02-27 00:02:57 +03:00
static void mem_reads8 ( void __iomem * src_base , u32 src_offset , void * dst ,
u32 bytes )
2008-04-24 02:37:04 +04:00
{
2011-02-27 00:02:57 +03:00
reg_write32 ( src_base , HC_MEMORY_REG , src_offset + ISP_BANK ( 0 ) ) ;
ndelay ( 90 ) ;
bank_reads8 ( src_base , src_offset , ISP_BANK ( 0 ) , dst , bytes ) ;
}
static void mem_writes8 ( void __iomem * dst_base , u32 dst_offset ,
__u32 const * src , u32 bytes )
{
__u32 __iomem * dst ;
dst = dst_base + dst_offset ;
if ( dst_offset < PAYLOAD_OFFSET ) {
while ( bytes > = 4 ) {
__raw_writel ( cpu_to_le32 ( * src ) , dst ) ;
bytes - = 4 ;
src + + ;
dst + + ;
}
} else {
while ( bytes > = 4 ) {
__raw_writel ( * src , dst ) ;
bytes - = 4 ;
src + + ;
dst + + ;
}
2008-04-24 02:37:04 +04:00
}
2011-02-27 00:02:57 +03:00
if ( ! bytes )
2008-04-24 02:37:04 +04:00
return ;
2011-02-27 00:02:57 +03:00
/* in case we have 3, 2 or 1 bytes left. The buffer is allocated and the
* extra bytes should not be read by the HW .
2008-04-24 02:37:04 +04:00
*/
2011-02-27 00:02:57 +03:00
if ( dst_offset < PAYLOAD_OFFSET )
__raw_writel ( cpu_to_le32 ( * src ) , dst ) ;
else
__raw_writel ( * src , dst ) ;
2008-04-24 02:37:04 +04:00
}
2011-02-27 00:02:57 +03:00
/*
* Read and write ptds . ' ptd_offset ' should be one of ISO_PTD_OFFSET ,
* INT_PTD_OFFSET , and ATL_PTD_OFFSET . ' slot ' should be less than 32.
*/
static void ptd_read ( void __iomem * base , u32 ptd_offset , u32 slot ,
struct ptd * ptd )
{
reg_write32 ( base , HC_MEMORY_REG ,
ISP_BANK ( 0 ) + ptd_offset + slot * sizeof ( * ptd ) ) ;
ndelay ( 90 ) ;
bank_reads8 ( base , ptd_offset + slot * sizeof ( * ptd ) , ISP_BANK ( 0 ) ,
( void * ) ptd , sizeof ( * ptd ) ) ;
}
static void ptd_write ( void __iomem * base , u32 ptd_offset , u32 slot ,
struct ptd * ptd )
{
mem_writes8 ( base , ptd_offset + slot * sizeof ( * ptd ) + sizeof ( ptd - > dw0 ) ,
& ptd - > dw1 , 7 * sizeof ( ptd - > dw1 ) ) ;
/* Make sure dw0 gets written last (after other dw's and after payload)
since it contains the enable bit */
wmb ( ) ;
mem_writes8 ( base , ptd_offset + slot * sizeof ( * ptd ) , & ptd - > dw0 ,
sizeof ( ptd - > dw0 ) ) ;
}
2008-04-24 02:37:04 +04:00
/* memory management of the 60kb on the chip from 0x1000 to 0xffff */
static void init_memory ( struct isp1760_hcd * priv )
{
2011-02-27 00:04:40 +03:00
int i , curr ;
u32 payload_addr ;
2008-04-24 02:37:04 +04:00
2011-02-27 00:04:40 +03:00
payload_addr = PAYLOAD_OFFSET ;
2008-04-24 02:37:04 +04:00
for ( i = 0 ; i < BLOCK_1_NUM ; i + + ) {
2011-02-27 00:04:40 +03:00
priv - > memory_pool [ i ] . start = payload_addr ;
2008-04-24 02:37:04 +04:00
priv - > memory_pool [ i ] . size = BLOCK_1_SIZE ;
priv - > memory_pool [ i ] . free = 1 ;
2011-02-27 00:04:40 +03:00
payload_addr + = priv - > memory_pool [ i ] . size ;
2008-04-24 02:37:04 +04:00
}
2011-02-27 00:04:40 +03:00
curr = i ;
for ( i = 0 ; i < BLOCK_2_NUM ; i + + ) {
priv - > memory_pool [ curr + i ] . start = payload_addr ;
priv - > memory_pool [ curr + i ] . size = BLOCK_2_SIZE ;
priv - > memory_pool [ curr + i ] . free = 1 ;
payload_addr + = priv - > memory_pool [ curr + i ] . size ;
2008-04-24 02:37:04 +04:00
}
2011-02-27 00:04:40 +03:00
curr = i ;
for ( i = 0 ; i < BLOCK_3_NUM ; i + + ) {
priv - > memory_pool [ curr + i ] . start = payload_addr ;
priv - > memory_pool [ curr + i ] . size = BLOCK_3_SIZE ;
priv - > memory_pool [ curr + i ] . free = 1 ;
payload_addr + = priv - > memory_pool [ curr + i ] . size ;
2008-04-24 02:37:04 +04:00
}
2011-04-26 23:47:37 +04:00
WARN_ON ( payload_addr - priv - > memory_pool [ 0 ] . start > PAYLOAD_AREA_SIZE ) ;
2008-04-24 02:37:04 +04:00
}
2011-02-27 00:06:37 +03:00
static void alloc_mem ( struct usb_hcd * hcd , struct isp1760_qtd * qtd )
2008-04-24 02:37:04 +04:00
{
2011-02-27 00:06:37 +03:00
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
2008-04-24 02:37:04 +04:00
int i ;
2011-04-26 23:47:37 +04:00
WARN_ON ( qtd - > payload_addr ) ;
2011-02-27 00:04:40 +03:00
if ( ! qtd - > length )
return ;
2008-04-24 02:37:04 +04:00
for ( i = 0 ; i < BLOCKS ; i + + ) {
2011-02-27 00:04:40 +03:00
if ( priv - > memory_pool [ i ] . size > = qtd - > length & &
2008-04-24 02:37:04 +04:00
priv - > memory_pool [ i ] . free ) {
priv - > memory_pool [ i ] . free = 0 ;
2011-02-27 00:04:40 +03:00
qtd - > payload_addr = priv - > memory_pool [ i ] . start ;
return ;
2008-04-24 02:37:04 +04:00
}
}
}
2011-02-27 00:06:37 +03:00
static void free_mem ( struct usb_hcd * hcd , struct isp1760_qtd * qtd )
2008-04-24 02:37:04 +04:00
{
2011-02-27 00:06:37 +03:00
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
2008-04-24 02:37:04 +04:00
int i ;
2011-02-27 00:04:40 +03:00
if ( ! qtd - > payload_addr )
2008-04-24 02:37:04 +04:00
return ;
for ( i = 0 ; i < BLOCKS ; i + + ) {
2011-02-27 00:04:40 +03:00
if ( priv - > memory_pool [ i ] . start = = qtd - > payload_addr ) {
2011-04-26 23:47:37 +04:00
WARN_ON ( priv - > memory_pool [ i ] . free ) ;
2008-04-24 02:37:04 +04:00
priv - > memory_pool [ i ] . free = 1 ;
2011-02-27 00:04:40 +03:00
qtd - > payload_addr = 0 ;
return ;
2008-04-24 02:37:04 +04:00
}
}
2011-02-27 00:06:37 +03:00
dev_err ( hcd - > self . controller , " %s: Invalid pointer: %08x \n " ,
__func__ , qtd - > payload_addr ) ;
2011-04-26 23:48:30 +04:00
WARN_ON ( 1 ) ;
qtd - > payload_addr = 0 ;
2008-04-24 02:37:04 +04:00
}
2011-02-27 00:02:57 +03:00
static int handshake ( struct usb_hcd * hcd , u32 reg ,
2008-04-24 02:37:04 +04:00
u32 mask , u32 done , int usec )
{
u32 result ;
do {
2011-02-27 00:02:57 +03:00
result = reg_read32 ( hcd - > regs , reg ) ;
2008-04-24 02:37:04 +04:00
if ( result = = ~ 0 )
return - ENODEV ;
result & = mask ;
if ( result = = done )
return 0 ;
udelay ( 1 ) ;
usec - - ;
} while ( usec > 0 ) ;
return - ETIMEDOUT ;
}
/* reset a non-running (STS_HALT == 1) controller */
2011-02-27 00:06:37 +03:00
static int ehci_reset ( struct usb_hcd * hcd )
2008-04-24 02:37:04 +04:00
{
int retval ;
2011-02-27 00:06:37 +03:00
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
2011-02-27 00:02:57 +03:00
u32 command = reg_read32 ( hcd - > regs , HC_USBCMD ) ;
2008-04-24 02:37:04 +04:00
command | = CMD_RESET ;
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_USBCMD , command ) ;
2008-04-24 02:37:04 +04:00
hcd - > state = HC_STATE_HALT ;
priv - > next_statechange = jiffies ;
2011-02-27 00:02:57 +03:00
retval = handshake ( hcd , HC_USBCMD ,
2008-04-24 02:37:04 +04:00
CMD_RESET , 0 , 250 * 1000 ) ;
return retval ;
}
2011-04-26 23:48:30 +04:00
static struct isp1760_qh * qh_alloc ( gfp_t flags )
2008-04-24 02:37:04 +04:00
{
struct isp1760_qh * qh ;
qh = kmem_cache_zalloc ( qh_cachep , flags ) ;
if ( ! qh )
2011-04-26 23:48:30 +04:00
return NULL ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
INIT_LIST_HEAD ( & qh - > qh_list ) ;
2008-04-24 02:37:04 +04:00
INIT_LIST_HEAD ( & qh - > qtd_list ) ;
2011-04-26 23:48:30 +04:00
qh - > slot = - 1 ;
2008-04-24 02:37:04 +04:00
return qh ;
}
2011-04-26 23:48:30 +04:00
static void qh_free ( struct isp1760_qh * qh )
{
WARN_ON ( ! list_empty ( & qh - > qtd_list ) ) ;
WARN_ON ( qh - > slot > - 1 ) ;
kmem_cache_free ( qh_cachep , qh ) ;
}
2008-04-24 02:37:04 +04:00
/* one-time init, only for memory state */
static int priv_init ( struct usb_hcd * hcd )
{
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
u32 hcc_params ;
spin_lock_init ( & priv - > lock ) ;
2011-04-26 23:48:30 +04:00
INIT_LIST_HEAD ( & priv - > interruptqhs ) ;
INIT_LIST_HEAD ( & priv - > controlqhs ) ;
INIT_LIST_HEAD ( & priv - > bulkqhs ) ;
2008-04-24 02:37:04 +04:00
/*
* hw default : 1 K periodic list heads , one per frame .
* periodic_size can shrink by USBCMD update if hcc_params allows .
*/
priv - > periodic_size = DEFAULT_I_TDPS ;
/* controllers may cache some of the periodic schedule ... */
2011-02-27 00:02:57 +03:00
hcc_params = reg_read32 ( hcd - > regs , HC_HCCPARAMS ) ;
2008-04-24 02:37:04 +04:00
/* full frame cache */
if ( HCC_ISOC_CACHE ( hcc_params ) )
priv - > i_thresh = 8 ;
else /* N microframes cached */
priv - > i_thresh = 2 + HCC_ISOC_THRES ( hcc_params ) ;
return 0 ;
}
static int isp1760_hc_setup ( struct usb_hcd * hcd )
{
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
int result ;
2008-06-17 20:11:38 +04:00
u32 scratch , hwmode ;
2011-10-19 16:18:41 +04:00
/* low-level chip reset */
if ( gpio_is_valid ( priv - > rst_gpio ) ) {
unsigned int rst_lvl ;
rst_lvl = ( priv - > devflags &
ISP1760_FLAG_RESET_ACTIVE_HIGH ) ? 1 : 0 ;
gpio_set_value ( priv - > rst_gpio , rst_lvl ) ;
mdelay ( 50 ) ;
gpio_set_value ( priv - > rst_gpio , ! rst_lvl ) ;
}
2008-06-17 20:11:38 +04:00
/* Setup HW Mode Control: This assumes a level active-low interrupt */
hwmode = HW_DATA_BUS_32BIT ;
if ( priv - > devflags & ISP1760_FLAG_BUS_WIDTH_16 )
hwmode & = ~ HW_DATA_BUS_32BIT ;
if ( priv - > devflags & ISP1760_FLAG_ANALOG_OC )
hwmode | = HW_ANA_DIGI_OC ;
if ( priv - > devflags & ISP1760_FLAG_DACK_POL_HIGH )
hwmode | = HW_DACK_POL_HIGH ;
if ( priv - > devflags & ISP1760_FLAG_DREQ_POL_HIGH )
hwmode | = HW_DREQ_POL_HIGH ;
2009-07-16 07:22:54 +04:00
if ( priv - > devflags & ISP1760_FLAG_INTR_POL_HIGH )
hwmode | = HW_INTR_HIGH_ACT ;
if ( priv - > devflags & ISP1760_FLAG_INTR_EDGE_TRIG )
hwmode | = HW_INTR_EDGE_TRIG ;
2008-06-17 20:11:38 +04:00
/*
* We have to set this first in case we ' re in 16 - bit mode .
* Write it twice to ensure correct upper bits if switching
* to 16 - bit mode .
*/
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_HW_MODE_CTRL , hwmode ) ;
reg_write32 ( hcd - > regs , HC_HW_MODE_CTRL , hwmode ) ;
2008-04-24 02:37:04 +04:00
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_SCRATCH_REG , 0xdeadbabe ) ;
2008-06-17 20:11:38 +04:00
/* Change bus pattern */
2011-02-27 00:02:57 +03:00
scratch = reg_read32 ( hcd - > regs , HC_CHIP_ID_REG ) ;
scratch = reg_read32 ( hcd - > regs , HC_SCRATCH_REG ) ;
2008-04-24 02:37:04 +04:00
if ( scratch ! = 0xdeadbabe ) {
2011-02-27 00:06:37 +03:00
dev_err ( hcd - > self . controller , " Scratch test failed. \n " ) ;
2008-04-24 02:37:04 +04:00
return - ENODEV ;
}
/* pre reset */
2011-04-26 23:48:30 +04:00
reg_write32 ( hcd - > regs , HC_BUFFER_STATUS_REG , 0 ) ;
reg_write32 ( hcd - > regs , HC_ATL_PTD_SKIPMAP_REG , NO_TRANSFER_ACTIVE ) ;
reg_write32 ( hcd - > regs , HC_INT_PTD_SKIPMAP_REG , NO_TRANSFER_ACTIVE ) ;
reg_write32 ( hcd - > regs , HC_ISO_PTD_SKIPMAP_REG , NO_TRANSFER_ACTIVE ) ;
2008-04-24 02:37:04 +04:00
/* reset */
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_RESET_REG , SW_RESET_RESET_ALL ) ;
2008-04-24 02:37:04 +04:00
mdelay ( 100 ) ;
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_RESET_REG , SW_RESET_RESET_HC ) ;
2008-04-24 02:37:04 +04:00
mdelay ( 100 ) ;
2011-02-27 00:06:37 +03:00
result = ehci_reset ( hcd ) ;
2008-04-24 02:37:04 +04:00
if ( result )
return result ;
/* Step 11 passed */
2011-02-27 00:06:37 +03:00
dev_info ( hcd - > self . controller , " bus width: %d, oc: %s \n " ,
2008-06-17 20:11:38 +04:00
( priv - > devflags & ISP1760_FLAG_BUS_WIDTH_16 ) ?
16 : 32 , ( priv - > devflags & ISP1760_FLAG_ANALOG_OC ) ?
" analog " : " digital " ) ;
2008-04-24 02:37:04 +04:00
/* ATL reset */
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_HW_MODE_CTRL , hwmode | ALL_ATX_RESET ) ;
2008-04-24 02:37:04 +04:00
mdelay ( 10 ) ;
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_HW_MODE_CTRL , hwmode ) ;
2008-04-24 02:37:04 +04:00
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_INTERRUPT_ENABLE , INTERRUPT_ENABLE_MASK ) ;
2008-06-17 20:11:38 +04:00
/*
* PORT 1 Control register of the ISP1760 is the OTG control
2008-12-18 12:31:40 +03:00
* register on ISP1761 . Since there is no OTG or device controller
* support in this driver , we use port 1 as a " normal " USB host port on
* both chips .
2008-06-17 20:11:38 +04:00
*/
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_PORT1_CTRL , PORT1_POWER | PORT1_INIT2 ) ;
2008-12-18 12:31:40 +03:00
mdelay ( 10 ) ;
2008-04-24 02:37:04 +04:00
2011-02-27 00:02:57 +03:00
priv - > hcs_params = reg_read32 ( hcd - > regs , HC_HCSPARAMS ) ;
2008-04-24 02:37:04 +04:00
return priv_init ( hcd ) ;
}
static u32 base_to_chip ( u32 base )
{
return ( ( base - 0x400 ) > > 3 ) ;
}
2011-02-27 00:08:47 +03:00
static int last_qtd_of_urb ( struct isp1760_qtd * qtd , struct isp1760_qh * qh )
{
struct urb * urb ;
if ( list_is_last ( & qtd - > qtd_list , & qh - > qtd_list ) )
return 1 ;
urb = qtd - > urb ;
qtd = list_entry ( qtd - > qtd_list . next , typeof ( * qtd ) , qtd_list ) ;
return ( qtd - > urb ! = urb ) ;
}
2011-04-26 23:48:30 +04:00
/* magic numbers that can affect system performance */
# define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
# define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */
# define EHCI_TUNE_RL_TT 0
# define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
# define EHCI_TUNE_MULT_TT 1
# define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
static void create_ptd_atl ( struct isp1760_qh * qh ,
2011-02-27 00:04:40 +03:00
struct isp1760_qtd * qtd , struct ptd * ptd )
2008-04-24 02:37:04 +04:00
{
u32 maxpacket ;
u32 multi ;
u32 rl = RL_COUNTER ;
u32 nak = NAK_COUNTER ;
2011-02-27 00:02:57 +03:00
memset ( ptd , 0 , sizeof ( * ptd ) ) ;
2008-04-24 02:37:04 +04:00
/* according to 3.6.2, max packet len can not be > 0x400 */
2011-02-27 00:04:40 +03:00
maxpacket = usb_maxpacket ( qtd - > urb - > dev , qtd - > urb - > pipe ,
usb_pipeout ( qtd - > urb - > pipe ) ) ;
2008-04-24 02:37:04 +04:00
multi = 1 + ( ( maxpacket > > 11 ) & 0x3 ) ;
maxpacket & = 0x7ff ;
/* DW0 */
2011-04-26 23:48:30 +04:00
ptd - > dw0 = DW0_VALID_BIT ;
ptd - > dw0 | = TO_DW0_LENGTH ( qtd - > length ) ;
ptd - > dw0 | = TO_DW0_MAXPACKET ( maxpacket ) ;
ptd - > dw0 | = TO_DW0_ENDPOINT ( usb_pipeendpoint ( qtd - > urb - > pipe ) ) ;
2008-04-24 02:37:04 +04:00
/* DW1 */
2011-02-27 00:04:40 +03:00
ptd - > dw1 = usb_pipeendpoint ( qtd - > urb - > pipe ) > > 1 ;
2011-04-26 23:48:30 +04:00
ptd - > dw1 | = TO_DW1_DEVICE_ADDR ( usb_pipedevice ( qtd - > urb - > pipe ) ) ;
ptd - > dw1 | = TO_DW1_PID_TOKEN ( qtd - > packet_type ) ;
2008-04-24 02:37:04 +04:00
2011-02-27 00:04:40 +03:00
if ( usb_pipebulk ( qtd - > urb - > pipe ) )
2011-04-26 23:48:30 +04:00
ptd - > dw1 | = DW1_TRANS_BULK ;
2011-02-27 00:04:40 +03:00
else if ( usb_pipeint ( qtd - > urb - > pipe ) )
2011-04-26 23:48:30 +04:00
ptd - > dw1 | = DW1_TRANS_INT ;
2008-04-24 02:37:04 +04:00
2011-02-27 00:04:40 +03:00
if ( qtd - > urb - > dev - > speed ! = USB_SPEED_HIGH ) {
2008-04-24 02:37:04 +04:00
/* split transaction */
2011-04-26 23:48:30 +04:00
ptd - > dw1 | = DW1_TRANS_SPLIT ;
2011-02-27 00:04:40 +03:00
if ( qtd - > urb - > dev - > speed = = USB_SPEED_LOW )
2011-04-26 23:48:30 +04:00
ptd - > dw1 | = DW1_SE_USB_LOSPEED ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
ptd - > dw1 | = TO_DW1_PORT_NUM ( qtd - > urb - > dev - > ttport ) ;
ptd - > dw1 | = TO_DW1_HUB_NUM ( qtd - > urb - > dev - > tt - > hub - > devnum ) ;
2008-04-24 02:37:04 +04:00
/* SE bit for Split INT transfers */
2011-02-27 00:04:40 +03:00
if ( usb_pipeint ( qtd - > urb - > pipe ) & &
( qtd - > urb - > dev - > speed = = USB_SPEED_LOW ) )
2011-02-27 00:02:57 +03:00
ptd - > dw1 | = 2 < < 16 ;
2008-04-24 02:37:04 +04:00
rl = 0 ;
nak = 0 ;
} else {
2011-04-26 23:48:30 +04:00
ptd - > dw0 | = TO_DW0_MULTI ( multi ) ;
2011-02-27 00:04:40 +03:00
if ( usb_pipecontrol ( qtd - > urb - > pipe ) | |
usb_pipebulk ( qtd - > urb - > pipe ) )
2011-04-26 23:48:30 +04:00
ptd - > dw3 | = TO_DW3_PING ( qh - > ping ) ;
2008-04-24 02:37:04 +04:00
}
/* DW2 */
2011-02-27 00:02:57 +03:00
ptd - > dw2 = 0 ;
2011-04-26 23:48:30 +04:00
ptd - > dw2 | = TO_DW2_DATA_START_ADDR ( base_to_chip ( qtd - > payload_addr ) ) ;
ptd - > dw2 | = TO_DW2_RL ( rl ) ;
2008-04-24 02:37:04 +04:00
/* DW3 */
2011-04-26 23:48:30 +04:00
ptd - > dw3 | = TO_DW3_NAKCOUNT ( nak ) ;
ptd - > dw3 | = TO_DW3_DATA_TOGGLE ( qh - > toggle ) ;
2011-02-27 00:08:47 +03:00
if ( usb_pipecontrol ( qtd - > urb - > pipe ) ) {
if ( qtd - > data_buffer = = qtd - > urb - > setup_packet )
2011-04-26 23:48:30 +04:00
ptd - > dw3 & = ~ TO_DW3_DATA_TOGGLE ( 1 ) ;
2011-02-27 00:08:47 +03:00
else if ( last_qtd_of_urb ( qtd , qh ) )
2011-04-26 23:48:30 +04:00
ptd - > dw3 | = TO_DW3_DATA_TOGGLE ( 1 ) ;
2011-02-27 00:08:47 +03:00
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
ptd - > dw3 | = DW3_ACTIVE_BIT ;
2008-04-24 02:37:04 +04:00
/* Cerr */
2011-04-26 23:48:30 +04:00
ptd - > dw3 | = TO_DW3_CERR ( ERR_COUNTER ) ;
2008-04-24 02:37:04 +04:00
}
2011-02-27 00:06:37 +03:00
static void transform_add_int ( struct isp1760_qh * qh ,
2011-02-27 00:04:40 +03:00
struct isp1760_qtd * qtd , struct ptd * ptd )
2008-04-24 02:37:04 +04:00
{
2011-02-27 00:07:35 +03:00
u32 usof ;
2008-04-24 02:37:04 +04:00
u32 period ;
2011-02-27 00:07:35 +03:00
/*
* Most of this is guessing . ISP1761 datasheet is quite unclear , and
* the algorithm from the original Philips driver code , which was
* pretty much used in this driver before as well , is quite horrendous
* and , i believe , incorrect . The code below follows the datasheet and
* USB2 .0 spec as far as I can tell , and plug / unplug seems to be much
* more reliable this way ( fingers crossed . . . ) .
*/
2008-04-24 02:37:04 +04:00
2011-02-27 00:07:35 +03:00
if ( qtd - > urb - > dev - > speed = = USB_SPEED_HIGH ) {
/* urb->interval is in units of microframes (1/8 ms) */
period = qtd - > urb - > interval > > 3 ;
if ( qtd - > urb - > interval > 4 )
usof = 0x01 ; /* One bit set =>
interval 1 ms * uFrame - match */
else if ( qtd - > urb - > interval > 2 )
usof = 0x22 ; /* Two bits set => interval 1/2 ms */
else if ( qtd - > urb - > interval > 1 )
usof = 0x55 ; /* Four bits set => interval 1/4 ms */
2008-04-24 02:37:04 +04:00
else
2011-02-27 00:07:35 +03:00
usof = 0xff ; /* All bits set => interval 1/8 ms */
2008-04-24 02:37:04 +04:00
} else {
2011-02-27 00:07:35 +03:00
/* urb->interval is in units of frames (1 ms) */
period = qtd - > urb - > interval ;
usof = 0x0f ; /* Execute Start Split on any of the
four first uFrames */
/*
* First 8 bits in dw5 is uSCS and " specifies which uSOF the
* complete split needs to be sent . Valid only for IN . " Also,
* " All bits can be set to one for every transfer. " ( p 82 ,
* ISP1761 data sheet . ) 0x1c is from Philips driver . Where did
* that number come from ? 0xff seems to work fine . . .
*/
/* ptd->dw5 = 0x1c; */
ptd - > dw5 = 0xff ; /* Execute Complete Split on any uFrame */
2008-04-24 02:37:04 +04:00
}
2011-02-27 00:07:35 +03:00
period = period > > 1 ; /* Ensure equal or shorter period than requested */
period & = 0xf8 ; /* Mask off too large values and lowest unused 3 bits */
2011-02-27 00:02:57 +03:00
ptd - > dw2 | = period ;
ptd - > dw4 = usof ;
2008-04-24 02:37:04 +04:00
}
2011-04-26 23:48:30 +04:00
static void create_ptd_int ( struct isp1760_qh * qh ,
2011-02-27 00:04:40 +03:00
struct isp1760_qtd * qtd , struct ptd * ptd )
2008-04-24 02:37:04 +04:00
{
2011-04-26 23:48:30 +04:00
create_ptd_atl ( qh , qtd , ptd ) ;
2011-02-27 00:06:37 +03:00
transform_add_int ( qh , qtd , ptd ) ;
2008-04-24 02:37:04 +04:00
}
2011-02-27 00:06:37 +03:00
static void isp1760_urb_done ( struct usb_hcd * hcd , struct urb * urb )
2008-04-24 02:37:04 +04:00
__releases ( priv - > lock )
__acquires ( priv - > lock )
{
2011-02-27 00:06:37 +03:00
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
2008-04-24 02:37:04 +04:00
if ( ! urb - > unlinked ) {
2011-02-27 00:06:37 +03:00
if ( urb - > status = = - EINPROGRESS )
urb - > status = 0 ;
2008-04-24 02:37:04 +04:00
}
2010-02-02 18:31:02 +03:00
if ( usb_pipein ( urb - > pipe ) & & usb_pipetype ( urb - > pipe ) ! = PIPE_CONTROL ) {
void * ptr ;
for ( ptr = urb - > transfer_buffer ;
ptr < urb - > transfer_buffer + urb - > transfer_buffer_length ;
ptr + = PAGE_SIZE )
flush_dcache_page ( virt_to_page ( ptr ) ) ;
}
2008-04-24 02:37:04 +04:00
/* complete() can reenter this HCD */
2011-02-27 00:06:37 +03:00
usb_hcd_unlink_urb_from_ep ( hcd , urb ) ;
2008-04-24 02:37:04 +04:00
spin_unlock ( & priv - > lock ) ;
2011-02-27 00:06:37 +03:00
usb_hcd_giveback_urb ( hcd , urb , urb - > status ) ;
2008-04-24 02:37:04 +04:00
spin_lock ( & priv - > lock ) ;
}
2011-04-26 23:47:37 +04:00
static struct isp1760_qtd * qtd_alloc ( gfp_t flags , struct urb * urb ,
u8 packet_type )
2008-04-24 02:37:04 +04:00
{
2011-04-26 23:47:37 +04:00
struct isp1760_qtd * qtd ;
qtd = kmem_cache_zalloc ( qtd_cachep , flags ) ;
if ( ! qtd )
return NULL ;
INIT_LIST_HEAD ( & qtd - > qtd_list ) ;
qtd - > urb = urb ;
qtd - > packet_type = packet_type ;
2011-04-26 23:48:30 +04:00
qtd - > status = QTD_ENQUEUED ;
qtd - > actual_length = 0 ;
2011-04-26 23:47:37 +04:00
return qtd ;
}
static void qtd_free ( struct isp1760_qtd * qtd )
{
WARN_ON ( qtd - > payload_addr ) ;
2008-04-24 02:37:04 +04:00
kmem_cache_free ( qtd_cachep , qtd ) ;
}
2011-04-26 23:48:30 +04:00
static void start_bus_transfer ( struct usb_hcd * hcd , u32 ptd_offset , int slot ,
struct slotinfo * slots , struct isp1760_qtd * qtd ,
struct isp1760_qh * qh , struct ptd * ptd )
2008-04-24 02:37:04 +04:00
{
2011-04-26 23:48:30 +04:00
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
2011-05-20 02:17:45 +04:00
int skip_map ;
2011-04-26 23:48:30 +04:00
WARN_ON ( ( slot < 0 ) | | ( slot > 31 ) ) ;
WARN_ON ( qtd - > length & & ! qtd - > payload_addr ) ;
WARN_ON ( slots [ slot ] . qtd ) ;
WARN_ON ( slots [ slot ] . qh ) ;
WARN_ON ( qtd - > status ! = QTD_PAYLOAD_ALLOC ) ;
2011-05-20 02:17:45 +04:00
/* Make sure done map has not triggered from some unlinked transfer */
if ( ptd_offset = = ATL_PTD_OFFSET ) {
priv - > atl_done_map | = reg_read32 ( hcd - > regs ,
HC_ATL_PTD_DONEMAP_REG ) ;
2011-08-21 10:29:28 +04:00
priv - > atl_done_map & = ~ ( 1 < < slot ) ;
} else {
priv - > int_done_map | = reg_read32 ( hcd - > regs ,
HC_INT_PTD_DONEMAP_REG ) ;
priv - > int_done_map & = ~ ( 1 < < slot ) ;
}
2011-05-20 02:17:45 +04:00
2011-08-21 10:29:28 +04:00
qh - > slot = slot ;
qtd - > status = QTD_XFER_STARTED ;
slots [ slot ] . timestamp = jiffies ;
slots [ slot ] . qtd = qtd ;
slots [ slot ] . qh = qh ;
ptd_write ( hcd - > regs , ptd_offset , slot , ptd ) ;
if ( ptd_offset = = ATL_PTD_OFFSET ) {
2011-05-20 02:17:45 +04:00
skip_map = reg_read32 ( hcd - > regs , HC_ATL_PTD_SKIPMAP_REG ) ;
skip_map & = ~ ( 1 < < qh - > slot ) ;
reg_write32 ( hcd - > regs , HC_ATL_PTD_SKIPMAP_REG , skip_map ) ;
} else {
skip_map = reg_read32 ( hcd - > regs , HC_INT_PTD_SKIPMAP_REG ) ;
skip_map & = ~ ( 1 < < qh - > slot ) ;
reg_write32 ( hcd - > regs , HC_INT_PTD_SKIPMAP_REG , skip_map ) ;
}
2008-04-24 02:37:04 +04:00
}
2011-04-26 23:48:30 +04:00
static int is_short_bulk ( struct isp1760_qtd * qtd )
2008-04-24 02:37:04 +04:00
{
2011-04-26 23:48:30 +04:00
return ( usb_pipebulk ( qtd - > urb - > pipe ) & &
( qtd - > actual_length < qtd - > length ) ) ;
2008-04-24 02:37:04 +04:00
}
2011-04-26 23:48:30 +04:00
static void collect_qtds ( struct usb_hcd * hcd , struct isp1760_qh * qh ,
struct list_head * urb_list )
2008-04-24 02:37:04 +04:00
{
2011-04-26 23:48:30 +04:00
int last_qtd ;
struct isp1760_qtd * qtd , * qtd_next ;
struct urb_listitem * urb_listitem ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
list_for_each_entry_safe ( qtd , qtd_next , & qh - > qtd_list , qtd_list ) {
if ( qtd - > status < QTD_XFER_COMPLETE )
break ;
2008-04-24 02:37:04 +04:00
2011-08-21 10:29:27 +04:00
last_qtd = last_qtd_of_urb ( qtd , qh ) ;
2011-04-26 23:48:30 +04:00
if ( ( ! last_qtd ) & & ( qtd - > status = = QTD_RETIRE ) )
qtd_next - > status = QTD_RETIRE ;
if ( qtd - > status = = QTD_XFER_COMPLETE ) {
if ( qtd - > actual_length ) {
switch ( qtd - > packet_type ) {
case IN_PID :
mem_reads8 ( hcd - > regs , qtd - > payload_addr ,
qtd - > data_buffer ,
qtd - > actual_length ) ;
/* Fall through (?) */
case OUT_PID :
qtd - > urb - > actual_length + =
qtd - > actual_length ;
/* Fall through ... */
case SETUP_PID :
break ;
}
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
if ( is_short_bulk ( qtd ) ) {
if ( qtd - > urb - > transfer_flags & URB_SHORT_NOT_OK )
qtd - > urb - > status = - EREMOTEIO ;
if ( ! last_qtd )
qtd_next - > status = QTD_RETIRE ;
}
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
if ( qtd - > payload_addr )
free_mem ( hcd , qtd ) ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
if ( last_qtd ) {
if ( ( qtd - > status = = QTD_RETIRE ) & &
( qtd - > urb - > status = = - EINPROGRESS ) )
qtd - > urb - > status = - EPIPE ;
/* Defer calling of urb_done() since it releases lock */
urb_listitem = kmem_cache_zalloc ( urb_listitem_cachep ,
GFP_ATOMIC ) ;
if ( unlikely ( ! urb_listitem ) )
2011-08-21 10:29:27 +04:00
break ; /* Try again on next call */
2011-04-26 23:48:30 +04:00
urb_listitem - > urb = qtd - > urb ;
list_add_tail ( & urb_listitem - > urb_list , urb_list ) ;
}
2011-04-26 23:47:12 +04:00
2011-04-26 23:48:30 +04:00
list_del ( & qtd - > qtd_list ) ;
qtd_free ( qtd ) ;
}
}
2008-07-17 22:09:30 +04:00
2011-04-26 23:48:30 +04:00
# define ENQUEUE_DEPTH 2
static void enqueue_qtds ( struct usb_hcd * hcd , struct isp1760_qh * qh )
{
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
int ptd_offset ;
struct slotinfo * slots ;
int curr_slot , free_slot ;
int n ;
struct ptd ptd ;
struct isp1760_qtd * qtd ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
if ( unlikely ( list_empty ( & qh - > qtd_list ) ) ) {
WARN_ON ( 1 ) ;
return ;
}
2008-04-24 02:37:04 +04:00
2011-08-23 03:25:30 +04:00
/* Make sure this endpoint's TT buffer is clean before queueing ptds */
if ( qh - > tt_buffer_dirty )
return ;
2011-04-26 23:48:30 +04:00
if ( usb_pipeint ( list_entry ( qh - > qtd_list . next , struct isp1760_qtd ,
qtd_list ) - > urb - > pipe ) ) {
ptd_offset = INT_PTD_OFFSET ;
slots = priv - > int_slots ;
} else {
ptd_offset = ATL_PTD_OFFSET ;
slots = priv - > atl_slots ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
free_slot = - 1 ;
for ( curr_slot = 0 ; curr_slot < 32 ; curr_slot + + ) {
if ( ( free_slot = = - 1 ) & & ( slots [ curr_slot ] . qtd = = NULL ) )
free_slot = curr_slot ;
if ( slots [ curr_slot ] . qh = = qh )
break ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
n = 0 ;
list_for_each_entry ( qtd , & qh - > qtd_list , qtd_list ) {
if ( qtd - > status = = QTD_ENQUEUED ) {
WARN_ON ( qtd - > payload_addr ) ;
alloc_mem ( hcd , qtd ) ;
if ( ( qtd - > length ) & & ( ! qtd - > payload_addr ) )
break ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
if ( ( qtd - > length ) & &
( ( qtd - > packet_type = = SETUP_PID ) | |
( qtd - > packet_type = = OUT_PID ) ) ) {
mem_writes8 ( hcd - > regs , qtd - > payload_addr ,
qtd - > data_buffer , qtd - > length ) ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
qtd - > status = QTD_PAYLOAD_ALLOC ;
2008-04-24 02:37:04 +04:00
}
2011-04-26 23:48:30 +04:00
if ( qtd - > status = = QTD_PAYLOAD_ALLOC ) {
/*
if ( ( curr_slot > 31 ) & & ( free_slot = = - 1 ) )
dev_dbg ( hcd - > self . controller , " %s: No slot "
" available for transfer \n " , __func__ ) ;
*/
/* Start xfer for this endpoint if not already done */
if ( ( curr_slot > 31 ) & & ( free_slot > - 1 ) ) {
if ( usb_pipeint ( qtd - > urb - > pipe ) )
create_ptd_int ( qh , qtd , & ptd ) ;
else
create_ptd_atl ( qh , qtd , & ptd ) ;
start_bus_transfer ( hcd , ptd_offset , free_slot ,
slots , qtd , qh , & ptd ) ;
curr_slot = free_slot ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
n + + ;
if ( n > = ENQUEUE_DEPTH )
break ;
}
}
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
void schedule_ptds ( struct usb_hcd * hcd )
{
struct isp1760_hcd * priv ;
struct isp1760_qh * qh , * qh_next ;
struct list_head * ep_queue ;
struct usb_host_endpoint * ep ;
LIST_HEAD ( urb_list ) ;
struct urb_listitem * urb_listitem , * urb_listitem_next ;
if ( ! hcd ) {
WARN_ON ( 1 ) ;
return ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
priv = hcd_to_priv ( hcd ) ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
/*
* check finished / retired xfers , transfer payloads , call urb_done ( )
*/
ep_queue = & priv - > interruptqhs ;
while ( ep_queue ) {
list_for_each_entry_safe ( qh , qh_next , ep_queue , qh_list ) {
ep = list_entry ( qh - > qtd_list . next , struct isp1760_qtd ,
qtd_list ) - > urb - > ep ;
collect_qtds ( hcd , qh , & urb_list ) ;
if ( list_empty ( & qh - > qtd_list ) ) {
list_del ( & qh - > qh_list ) ;
if ( ep - > hcpriv = = NULL ) {
/* Endpoint has been disabled, so we
can free the associated queue head . */
qh_free ( qh ) ;
}
2008-04-24 02:37:04 +04:00
}
}
2011-04-26 23:48:30 +04:00
if ( ep_queue = = & priv - > interruptqhs )
ep_queue = & priv - > controlqhs ;
else if ( ep_queue = = & priv - > controlqhs )
ep_queue = & priv - > bulkqhs ;
else
ep_queue = NULL ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
list_for_each_entry_safe ( urb_listitem , urb_listitem_next , & urb_list ,
urb_list ) {
isp1760_urb_done ( hcd , urb_listitem - > urb ) ;
kmem_cache_free ( urb_listitem_cachep , urb_listitem ) ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
/*
* Schedule packets for transfer .
*
* According to USB2 .0 specification :
*
* 1 st prio : interrupt xfers , up to 80 % of bandwidth
* 2 nd prio : control xfers
* 3 rd prio : bulk xfers
*
* . . . but let ' s use a simpler scheme here ( mostly because ISP1761 doc
* is very unclear on how to prioritize traffic ) :
*
* 1 ) Enqueue any queued control transfers , as long as payload chip mem
* and PTD ATL slots are available .
* 2 ) Enqueue any queued INT transfers , as long as payload chip mem
* and PTD INT slots are available .
* 3 ) Enqueue any queued bulk transfers , as long as payload chip mem
* and PTD ATL slots are available .
*
* Use double buffering ( ENQUEUE_DEPTH = = 2 ) as a compromise between
* conservation of chip mem and performance .
*
* I ' m sure this scheme could be improved upon !
*/
ep_queue = & priv - > controlqhs ;
while ( ep_queue ) {
list_for_each_entry_safe ( qh , qh_next , ep_queue , qh_list )
enqueue_qtds ( hcd , qh ) ;
if ( ep_queue = = & priv - > controlqhs )
ep_queue = & priv - > interruptqhs ;
else if ( ep_queue = = & priv - > interruptqhs )
ep_queue = & priv - > bulkqhs ;
else
ep_queue = NULL ;
}
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
# define PTD_STATE_QTD_DONE 1
# define PTD_STATE_QTD_RELOAD 2
# define PTD_STATE_URB_RETIRE 3
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
static int check_int_transfer ( struct usb_hcd * hcd , struct ptd * ptd ,
struct urb * urb )
{
__dw dw4 ;
int i ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
dw4 = ptd - > dw4 ;
dw4 > > = 8 ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
/* FIXME: ISP1761 datasheet does not say what to do with these. Do we
need to handle these errors ? Is it done in hardware ? */
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
if ( ptd - > dw3 & DW3_HALT_BIT ) {
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
urb - > status = - EPROTO ; /* Default unknown error */
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
for ( i = 0 ; i < 8 ; i + + ) {
switch ( dw4 & 0x7 ) {
case INT_UNDERRUN :
dev_dbg ( hcd - > self . controller , " %s: underrun "
" during uFrame %d \n " ,
__func__ , i ) ;
urb - > status = - ECOMM ; /* Could not write data */
break ;
case INT_EXACT :
dev_dbg ( hcd - > self . controller , " %s: transaction "
" error during uFrame %d \n " ,
__func__ , i ) ;
urb - > status = - EPROTO ; /* timeout, bad CRC, PID
error etc . */
break ;
case INT_BABBLE :
dev_dbg ( hcd - > self . controller , " %s: babble "
" error during uFrame %d \n " ,
__func__ , i ) ;
urb - > status = - EOVERFLOW ;
break ;
}
dw4 > > = 3 ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
return PTD_STATE_URB_RETIRE ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
return PTD_STATE_QTD_DONE ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
static int check_atl_transfer ( struct usb_hcd * hcd , struct ptd * ptd ,
struct urb * urb )
{
WARN_ON ( ! ptd ) ;
if ( ptd - > dw3 & DW3_HALT_BIT ) {
if ( ptd - > dw3 & DW3_BABBLE_BIT )
urb - > status = - EOVERFLOW ;
else if ( FROM_DW3_CERR ( ptd - > dw3 ) )
urb - > status = - EPIPE ; /* Stall */
else if ( ptd - > dw3 & DW3_ERROR_BIT )
urb - > status = - EPROTO ; /* XactErr */
else
urb - > status = - EPROTO ; /* Unknown */
/*
dev_dbg ( hcd - > self . controller , " %s: ptd error: \n "
" dw0: %08x dw1: %08x dw2: %08x dw3: %08x \n "
" dw4: %08x dw5: %08x dw6: %08x dw7: %08x \n " ,
__func__ ,
ptd - > dw0 , ptd - > dw1 , ptd - > dw2 , ptd - > dw3 ,
ptd - > dw4 , ptd - > dw5 , ptd - > dw6 , ptd - > dw7 ) ;
*/
return PTD_STATE_URB_RETIRE ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
if ( ( ptd - > dw3 & DW3_ERROR_BIT ) & & ( ptd - > dw3 & DW3_ACTIVE_BIT ) ) {
/* Transfer Error, *but* active and no HALT -> reload */
dev_dbg ( hcd - > self . controller , " PID error; reloading ptd \n " ) ;
return PTD_STATE_QTD_RELOAD ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
if ( ! FROM_DW3_NAKCOUNT ( ptd - > dw3 ) & & ( ptd - > dw3 & DW3_ACTIVE_BIT ) ) {
/*
* NAKs are handled in HW by the chip . Usually if the
* device is not able to send data fast enough .
* This happens mostly on slower hardware .
*/
return PTD_STATE_QTD_RELOAD ;
2008-04-24 02:37:04 +04:00
}
2011-04-26 23:48:30 +04:00
return PTD_STATE_QTD_DONE ;
2008-04-24 02:37:04 +04:00
}
2011-08-21 10:29:26 +04:00
static void handle_done_ptds ( struct usb_hcd * hcd )
2008-04-24 02:37:04 +04:00
{
2011-02-27 00:02:57 +03:00
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
2008-04-24 02:37:04 +04:00
struct ptd ptd ;
struct isp1760_qh * qh ;
2011-04-26 23:48:30 +04:00
int slot ;
int state ;
struct slotinfo * slots ;
u32 ptd_offset ;
struct isp1760_qtd * qtd ;
int modified ;
2011-08-21 10:29:26 +04:00
int skip_map ;
2011-04-26 23:48:30 +04:00
2011-08-21 10:29:26 +04:00
skip_map = reg_read32 ( hcd - > regs , HC_INT_PTD_SKIPMAP_REG ) ;
priv - > int_done_map & = ~ skip_map ;
skip_map = reg_read32 ( hcd - > regs , HC_ATL_PTD_SKIPMAP_REG ) ;
priv - > atl_done_map & = ~ skip_map ;
2011-04-26 23:48:30 +04:00
2011-08-21 10:29:26 +04:00
modified = priv - > int_done_map | | priv - > atl_done_map ;
2011-05-20 02:17:45 +04:00
while ( priv - > int_done_map | | priv - > atl_done_map ) {
if ( priv - > int_done_map ) {
2011-04-26 23:48:30 +04:00
/* INT ptd */
2011-05-20 02:17:45 +04:00
slot = __ffs ( priv - > int_done_map ) ;
priv - > int_done_map & = ~ ( 1 < < slot ) ;
2011-04-26 23:48:30 +04:00
slots = priv - > int_slots ;
2011-05-20 02:17:45 +04:00
/* This should not trigger, and could be removed if
noone have any problems with it triggering : */
if ( ! slots [ slot ] . qh ) {
WARN_ON ( 1 ) ;
2011-04-26 23:48:30 +04:00
continue ;
2011-05-20 02:17:45 +04:00
}
2011-04-26 23:48:30 +04:00
ptd_offset = INT_PTD_OFFSET ;
ptd_read ( hcd - > regs , INT_PTD_OFFSET , slot , & ptd ) ;
state = check_int_transfer ( hcd , & ptd ,
slots [ slot ] . qtd - > urb ) ;
2008-04-24 02:37:04 +04:00
} else {
2011-04-26 23:48:30 +04:00
/* ATL ptd */
2011-05-20 02:17:45 +04:00
slot = __ffs ( priv - > atl_done_map ) ;
priv - > atl_done_map & = ~ ( 1 < < slot ) ;
2011-04-26 23:48:30 +04:00
slots = priv - > atl_slots ;
2011-05-20 02:17:45 +04:00
/* This should not trigger, and could be removed if
noone have any problems with it triggering : */
if ( ! slots [ slot ] . qh ) {
WARN_ON ( 1 ) ;
2011-04-26 23:48:30 +04:00
continue ;
2011-05-20 02:17:45 +04:00
}
2011-04-26 23:48:30 +04:00
ptd_offset = ATL_PTD_OFFSET ;
ptd_read ( hcd - > regs , ATL_PTD_OFFSET , slot , & ptd ) ;
state = check_atl_transfer ( hcd , & ptd ,
slots [ slot ] . qtd - > urb ) ;
2008-04-24 02:37:04 +04:00
}
2011-04-26 23:48:30 +04:00
qtd = slots [ slot ] . qtd ;
slots [ slot ] . qtd = NULL ;
qh = slots [ slot ] . qh ;
slots [ slot ] . qh = NULL ;
qh - > slot = - 1 ;
WARN_ON ( qtd - > status ! = QTD_XFER_STARTED ) ;
switch ( state ) {
case PTD_STATE_QTD_DONE :
if ( ( usb_pipeint ( qtd - > urb - > pipe ) ) & &
( qtd - > urb - > dev - > speed ! = USB_SPEED_HIGH ) )
qtd - > actual_length =
FROM_DW3_SCS_NRBYTESTRANSFERRED ( ptd . dw3 ) ;
else
qtd - > actual_length =
FROM_DW3_NRBYTESTRANSFERRED ( ptd . dw3 ) ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
qtd - > status = QTD_XFER_COMPLETE ;
if ( list_is_last ( & qtd - > qtd_list , & qh - > qtd_list ) | |
is_short_bulk ( qtd ) )
qtd = NULL ;
else
qtd = list_entry ( qtd - > qtd_list . next ,
typeof ( * qtd ) , qtd_list ) ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
qh - > toggle = FROM_DW3_DATA_TOGGLE ( ptd . dw3 ) ;
qh - > ping = FROM_DW3_PING ( ptd . dw3 ) ;
break ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
case PTD_STATE_QTD_RELOAD : /* QTD_RETRY, for atls only */
qtd - > status = QTD_PAYLOAD_ALLOC ;
ptd . dw0 | = DW0_VALID_BIT ;
/* RL counter = ERR counter */
ptd . dw3 & = ~ TO_DW3_NAKCOUNT ( 0xf ) ;
ptd . dw3 | = TO_DW3_NAKCOUNT ( FROM_DW2_RL ( ptd . dw2 ) ) ;
ptd . dw3 & = ~ TO_DW3_CERR ( 3 ) ;
ptd . dw3 | = TO_DW3_CERR ( ERR_COUNTER ) ;
qh - > toggle = FROM_DW3_DATA_TOGGLE ( ptd . dw3 ) ;
qh - > ping = FROM_DW3_PING ( ptd . dw3 ) ;
break ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
case PTD_STATE_URB_RETIRE :
qtd - > status = QTD_RETIRE ;
2011-08-23 03:25:30 +04:00
if ( ( qtd - > urb - > dev - > speed ! = USB_SPEED_HIGH ) & &
( qtd - > urb - > status ! = - EPIPE ) & &
( qtd - > urb - > status ! = - EREMOTEIO ) ) {
qh - > tt_buffer_dirty = 1 ;
if ( usb_hub_clear_tt_buffer ( qtd - > urb ) )
/* Clear failed; let's hope things work
anyway */
qh - > tt_buffer_dirty = 0 ;
}
2011-04-26 23:48:30 +04:00
qtd = NULL ;
qh - > toggle = 0 ;
qh - > ping = 0 ;
break ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
default :
WARN_ON ( 1 ) ;
continue ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
if ( qtd & & ( qtd - > status = = QTD_PAYLOAD_ALLOC ) ) {
if ( slots = = priv - > int_slots ) {
if ( state = = PTD_STATE_QTD_RELOAD )
dev_err ( hcd - > self . controller ,
" %s: PTD_STATE_QTD_RELOAD on "
" interrupt packet \n " , __func__ ) ;
if ( state ! = PTD_STATE_QTD_RELOAD )
create_ptd_int ( qh , qtd , & ptd ) ;
} else {
if ( state ! = PTD_STATE_QTD_RELOAD )
create_ptd_atl ( qh , qtd , & ptd ) ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
start_bus_transfer ( hcd , ptd_offset , slot , slots , qtd ,
qh , & ptd ) ;
}
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
if ( modified )
schedule_ptds ( hcd ) ;
2011-08-21 10:29:26 +04:00
}
2008-04-24 02:37:04 +04:00
2011-08-21 10:29:26 +04:00
static irqreturn_t isp1760_irq ( struct usb_hcd * hcd )
{
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
u32 imask ;
irqreturn_t irqret = IRQ_NONE ;
2008-04-24 02:37:04 +04:00
2011-08-21 10:29:26 +04:00
spin_lock ( & priv - > lock ) ;
if ( ! ( hcd - > state & HC_STATE_RUNNING ) )
goto leave ;
imask = reg_read32 ( hcd - > regs , HC_INTERRUPT_REG ) ;
if ( unlikely ( ! imask ) )
goto leave ;
reg_write32 ( hcd - > regs , HC_INTERRUPT_REG , imask ) ; /* Clear */
priv - > int_done_map | = reg_read32 ( hcd - > regs , HC_INT_PTD_DONEMAP_REG ) ;
priv - > atl_done_map | = reg_read32 ( hcd - > regs , HC_ATL_PTD_DONEMAP_REG ) ;
handle_done_ptds ( hcd ) ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
irqret = IRQ_HANDLED ;
leave :
spin_unlock ( & priv - > lock ) ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
return irqret ;
2008-04-24 02:37:04 +04:00
}
2011-08-21 10:29:26 +04:00
/*
* Workaround for problem described in chip errata 2 :
*
* Sometimes interrupts are not generated when ATL ( not INT ? ) completion occurs .
* One solution suggested in the errata is to use SOF interrupts _instead_of_
* ATL done interrupts ( the " instead of " might be important since it seems
* enabling ATL interrupts also causes the chip to sometimes - rarely - " forget "
* to set the PTD ' s done bit in addition to not generating an interrupt ! ) .
*
* So if we use SOF + ATL interrupts , we sometimes get stale PTDs since their
* done bit is not being set . This is bad - it blocks the endpoint until reboot .
*
* If we use SOF interrupts only , we get latency between ptd completion and the
* actual handling . This is very noticeable in testusb runs which takes several
* minutes longer without ATL interrupts .
*
* A better solution is to run the code below every SLOT_CHECK_PERIOD ms . If it
* finds active ATL slots which are older than SLOT_TIMEOUT ms , it checks the
* slot ' s ACTIVE and VALID bits . If these are not set , the ptd is considered
* completed and its done map bit is set .
*
* The values of SLOT_TIMEOUT and SLOT_CHECK_PERIOD have been arbitrarily chosen
* not to cause too much lag when this HW bug occurs , while still hopefully
* ensuring that the check does not falsely trigger .
*/
2011-08-21 10:29:28 +04:00
# define SLOT_TIMEOUT 300
2011-08-21 10:29:26 +04:00
# define SLOT_CHECK_PERIOD 200
static struct timer_list errata2_timer ;
void errata2_function ( unsigned long data )
{
struct usb_hcd * hcd = ( struct usb_hcd * ) data ;
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
int slot ;
struct ptd ptd ;
unsigned long spinflags ;
spin_lock_irqsave ( & priv - > lock , spinflags ) ;
for ( slot = 0 ; slot < 32 ; slot + + )
2011-08-21 10:29:28 +04:00
if ( priv - > atl_slots [ slot ] . qh & & time_after ( jiffies ,
priv - > atl_slots [ slot ] . timestamp +
SLOT_TIMEOUT * HZ / 1000 ) ) {
2011-08-21 10:29:26 +04:00
ptd_read ( hcd - > regs , ATL_PTD_OFFSET , slot , & ptd ) ;
if ( ! FROM_DW0_VALID ( ptd . dw0 ) & &
! FROM_DW3_ACTIVE ( ptd . dw3 ) )
priv - > atl_done_map | = 1 < < slot ;
}
2011-08-21 10:29:28 +04:00
if ( priv - > atl_done_map )
handle_done_ptds ( hcd ) ;
2011-08-21 10:29:26 +04:00
spin_unlock_irqrestore ( & priv - > lock , spinflags ) ;
errata2_timer . expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000 ;
add_timer ( & errata2_timer ) ;
}
2011-08-21 10:29:25 +04:00
static int isp1760_run ( struct usb_hcd * hcd )
{
int retval ;
u32 temp ;
u32 command ;
u32 chipid ;
hcd - > uses_new_polling = 1 ;
hcd - > state = HC_STATE_RUNNING ;
/* Set PTD interrupt AND & OR maps */
reg_write32 ( hcd - > regs , HC_ATL_IRQ_MASK_AND_REG , 0 ) ;
reg_write32 ( hcd - > regs , HC_ATL_IRQ_MASK_OR_REG , 0xffffffff ) ;
reg_write32 ( hcd - > regs , HC_INT_IRQ_MASK_AND_REG , 0 ) ;
reg_write32 ( hcd - > regs , HC_INT_IRQ_MASK_OR_REG , 0xffffffff ) ;
reg_write32 ( hcd - > regs , HC_ISO_IRQ_MASK_AND_REG , 0 ) ;
reg_write32 ( hcd - > regs , HC_ISO_IRQ_MASK_OR_REG , 0xffffffff ) ;
/* step 23 passed */
temp = reg_read32 ( hcd - > regs , HC_HW_MODE_CTRL ) ;
reg_write32 ( hcd - > regs , HC_HW_MODE_CTRL , temp | HW_GLOBAL_INTR_EN ) ;
command = reg_read32 ( hcd - > regs , HC_USBCMD ) ;
command & = ~ ( CMD_LRESET | CMD_RESET ) ;
command | = CMD_RUN ;
reg_write32 ( hcd - > regs , HC_USBCMD , command ) ;
retval = handshake ( hcd , HC_USBCMD , CMD_RUN , CMD_RUN , 250 * 1000 ) ;
if ( retval )
return retval ;
/*
* XXX
* Spec says to write FLAG_CF as last config action , priv code grabs
* the semaphore while doing so .
*/
down_write ( & ehci_cf_port_reset_rwsem ) ;
reg_write32 ( hcd - > regs , HC_CONFIGFLAG , FLAG_CF ) ;
retval = handshake ( hcd , HC_CONFIGFLAG , FLAG_CF , FLAG_CF , 250 * 1000 ) ;
up_write ( & ehci_cf_port_reset_rwsem ) ;
if ( retval )
return retval ;
2011-08-21 10:29:26 +04:00
init_timer ( & errata2_timer ) ;
errata2_timer . function = errata2_function ;
errata2_timer . data = ( unsigned long ) hcd ;
errata2_timer . expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000 ;
add_timer ( & errata2_timer ) ;
2011-08-21 10:29:25 +04:00
chipid = reg_read32 ( hcd - > regs , HC_CHIP_ID_REG ) ;
dev_info ( hcd - > self . controller , " USB ISP %04x HW rev. %d started \n " ,
chipid & 0xffff , chipid > > 16 ) ;
/* PTD Register Init Part 2, Step 28 */
/* Setup registers controlling PTD checking */
reg_write32 ( hcd - > regs , HC_ATL_PTD_LASTPTD_REG , 0x80000000 ) ;
reg_write32 ( hcd - > regs , HC_INT_PTD_LASTPTD_REG , 0x80000000 ) ;
reg_write32 ( hcd - > regs , HC_ISO_PTD_LASTPTD_REG , 0x00000001 ) ;
reg_write32 ( hcd - > regs , HC_ATL_PTD_SKIPMAP_REG , 0xffffffff ) ;
reg_write32 ( hcd - > regs , HC_INT_PTD_SKIPMAP_REG , 0xffffffff ) ;
reg_write32 ( hcd - > regs , HC_ISO_PTD_SKIPMAP_REG , 0xffffffff ) ;
reg_write32 ( hcd - > regs , HC_BUFFER_STATUS_REG ,
ATL_BUF_FILL | INT_BUF_FILL ) ;
/* GRR this is run-once init(), being done every time the HC starts.
* So long as they ' re part of class devices , we can ' t do it init ( )
* since the class device isn ' t created that early .
*/
return 0 ;
}
2011-04-26 23:47:37 +04:00
static int qtd_fill ( struct isp1760_qtd * qtd , void * databuffer , size_t len )
2008-04-24 02:37:04 +04:00
{
2011-04-26 23:47:37 +04:00
qtd - > data_buffer = databuffer ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:47:37 +04:00
if ( len > MAX_PAYLOAD_SIZE )
len = MAX_PAYLOAD_SIZE ;
qtd - > length = len ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:47:37 +04:00
return qtd - > length ;
2008-04-24 02:37:04 +04:00
}
2011-04-26 23:47:37 +04:00
static void qtd_list_free ( struct list_head * qtd_list )
2008-04-24 02:37:04 +04:00
{
2011-04-26 23:47:37 +04:00
struct isp1760_qtd * qtd , * qtd_next ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:47:37 +04:00
list_for_each_entry_safe ( qtd , qtd_next , qtd_list , qtd_list ) {
2008-04-24 02:37:04 +04:00
list_del ( & qtd - > qtd_list ) ;
2011-04-26 23:47:37 +04:00
qtd_free ( qtd ) ;
2008-04-24 02:37:04 +04:00
}
}
/*
2011-04-26 23:47:37 +04:00
* Packetize urb - > transfer_buffer into list of packets of size wMaxPacketSize .
* Also calculate the PID type ( SETUP / IN / OUT ) for each packet .
2008-04-24 02:37:04 +04:00
*/
2011-02-27 00:06:37 +03:00
# define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
2011-04-26 23:47:37 +04:00
static void packetize_urb ( struct usb_hcd * hcd ,
2008-04-24 02:37:04 +04:00
struct urb * urb , struct list_head * head , gfp_t flags )
{
2011-02-27 00:03:49 +03:00
struct isp1760_qtd * qtd ;
2008-04-24 02:37:04 +04:00
void * buf ;
2011-04-26 23:47:37 +04:00
int len , maxpacketsize ;
u8 packet_type ;
2008-04-24 02:37:04 +04:00
/*
* URBs map to sequences of QTDs : one logical transaction
*/
2011-04-26 23:47:37 +04:00
if ( ! urb - > transfer_buffer & & urb - > transfer_buffer_length ) {
/* XXX This looks like usb storage / SCSI bug */
dev_err ( hcd - > self . controller ,
" buf is null, dma is %08lx len is %d \n " ,
( long unsigned ) urb - > transfer_dma ,
urb - > transfer_buffer_length ) ;
WARN_ON ( 1 ) ;
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:47:37 +04:00
if ( usb_pipein ( urb - > pipe ) )
packet_type = IN_PID ;
else
packet_type = OUT_PID ;
2008-04-24 02:37:04 +04:00
if ( usb_pipecontrol ( urb - > pipe ) ) {
2011-04-26 23:47:37 +04:00
qtd = qtd_alloc ( flags , urb , SETUP_PID ) ;
2008-04-24 02:37:04 +04:00
if ( ! qtd )
goto cleanup ;
2011-04-26 23:47:37 +04:00
qtd_fill ( qtd , urb - > setup_packet , sizeof ( struct usb_ctrlrequest ) ) ;
2008-04-24 02:37:04 +04:00
list_add_tail ( & qtd - > qtd_list , head ) ;
/* for zero length DATA stages, STATUS is always IN */
2011-04-26 23:47:37 +04:00
if ( urb - > transfer_buffer_length = = 0 )
packet_type = IN_PID ;
2008-04-24 02:37:04 +04:00
}
2011-04-26 23:47:37 +04:00
maxpacketsize = max_packet ( usb_maxpacket ( urb - > dev , urb - > pipe ,
usb_pipeout ( urb - > pipe ) ) ) ;
2008-04-24 02:37:04 +04:00
/*
* buffer gets wrapped in one or more qtds ;
* last one may be " short " ( including zero len )
* and may serve as a control status ack
*/
2011-04-26 23:47:37 +04:00
buf = urb - > transfer_buffer ;
len = urb - > transfer_buffer_length ;
2008-04-24 02:37:04 +04:00
for ( ; ; ) {
int this_qtd_len ;
2011-04-26 23:47:37 +04:00
qtd = qtd_alloc ( flags , urb , packet_type ) ;
if ( ! qtd )
goto cleanup ;
this_qtd_len = qtd_fill ( qtd , buf , len ) ;
list_add_tail ( & qtd - > qtd_list , head ) ;
2008-04-24 02:37:04 +04:00
len - = this_qtd_len ;
buf + = this_qtd_len ;
if ( len < = 0 )
break ;
}
/*
* control requests may need a terminating data " status " ack ;
* bulk ones may need a terminating short packet ( zero length ) .
*/
if ( urb - > transfer_buffer_length ! = 0 ) {
int one_more = 0 ;
if ( usb_pipecontrol ( urb - > pipe ) ) {
one_more = 1 ;
2011-04-26 23:47:37 +04:00
if ( packet_type = = IN_PID )
packet_type = OUT_PID ;
else
packet_type = IN_PID ;
2008-04-24 02:37:04 +04:00
} else if ( usb_pipebulk ( urb - > pipe )
& & ( urb - > transfer_flags & URB_ZERO_PACKET )
2011-04-26 23:47:37 +04:00
& & ! ( urb - > transfer_buffer_length %
maxpacketsize ) ) {
2008-04-24 02:37:04 +04:00
one_more = 1 ;
}
if ( one_more ) {
2011-04-26 23:47:37 +04:00
qtd = qtd_alloc ( flags , urb , packet_type ) ;
2008-04-24 02:37:04 +04:00
if ( ! qtd )
goto cleanup ;
/* never any data in such packets */
2011-04-26 23:47:37 +04:00
qtd_fill ( qtd , NULL , 0 ) ;
list_add_tail ( & qtd - > qtd_list , head ) ;
2008-04-24 02:37:04 +04:00
}
}
2011-04-26 23:47:37 +04:00
return ;
2008-04-24 02:37:04 +04:00
cleanup :
2011-04-26 23:47:37 +04:00
qtd_list_free ( head ) ;
}
2008-04-24 02:37:04 +04:00
static int isp1760_urb_enqueue ( struct usb_hcd * hcd , struct urb * urb ,
gfp_t mem_flags )
{
2011-04-26 23:48:30 +04:00
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
struct list_head * ep_queue ;
struct isp1760_qh * qh , * qhit ;
unsigned long spinflags ;
LIST_HEAD ( new_qtds ) ;
int retval ;
int qh_in_queue ;
2008-04-24 02:37:04 +04:00
switch ( usb_pipetype ( urb - > pipe ) ) {
case PIPE_CONTROL :
2011-04-26 23:48:30 +04:00
ep_queue = & priv - > controlqhs ;
break ;
2008-04-24 02:37:04 +04:00
case PIPE_BULK :
2011-04-26 23:48:30 +04:00
ep_queue = & priv - > bulkqhs ;
2008-04-24 02:37:04 +04:00
break ;
case PIPE_INTERRUPT :
2011-04-26 23:48:30 +04:00
if ( urb - > interval < 0 )
return - EINVAL ;
/* FIXME: Check bandwidth */
ep_queue = & priv - > interruptqhs ;
2008-04-24 02:37:04 +04:00
break ;
case PIPE_ISOCHRONOUS :
2011-04-26 23:48:30 +04:00
dev_err ( hcd - > self . controller , " %s: isochronous USB packets "
" not yet supported \n " ,
__func__ ) ;
return - EPIPE ;
2008-04-24 02:37:04 +04:00
default :
2011-04-26 23:48:30 +04:00
dev_err ( hcd - > self . controller , " %s: unknown pipe type \n " ,
__func__ ) ;
2008-04-24 02:37:04 +04:00
return - EPIPE ;
}
2011-04-26 23:48:30 +04:00
if ( usb_pipein ( urb - > pipe ) )
urb - > actual_length = 0 ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
packetize_urb ( hcd , urb , & new_qtds , mem_flags ) ;
if ( list_empty ( & new_qtds ) )
return - ENOMEM ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
retval = 0 ;
spin_lock_irqsave ( & priv - > lock , spinflags ) ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
if ( ! test_bit ( HCD_FLAG_HW_ACCESSIBLE , & hcd - > flags ) ) {
retval = - ESHUTDOWN ;
goto out ;
2008-04-24 02:37:04 +04:00
}
2011-04-26 23:48:30 +04:00
retval = usb_hcd_link_urb_to_ep ( hcd , urb ) ;
if ( retval )
goto out ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
qh = urb - > ep - > hcpriv ;
if ( qh ) {
qh_in_queue = 0 ;
list_for_each_entry ( qhit , ep_queue , qh_list ) {
if ( qhit = = qh ) {
qh_in_queue = 1 ;
2009-05-08 12:27:08 +04:00
break ;
2011-04-26 23:48:30 +04:00
}
}
if ( ! qh_in_queue )
list_add_tail ( & qh - > qh_list , ep_queue ) ;
} else {
qh = qh_alloc ( GFP_ATOMIC ) ;
if ( ! qh ) {
retval = - ENOMEM ;
2011-08-21 10:29:27 +04:00
usb_hcd_unlink_urb_from_ep ( hcd , urb ) ;
2011-04-26 23:48:30 +04:00
goto out ;
2008-04-24 02:37:04 +04:00
}
2011-04-26 23:48:30 +04:00
list_add_tail ( & qh - > qh_list , ep_queue ) ;
urb - > ep - > hcpriv = qh ;
2008-04-24 02:37:04 +04:00
}
2011-04-26 23:48:30 +04:00
list_splice_tail ( & new_qtds , & qh - > qtd_list ) ;
schedule_ptds ( hcd ) ;
out :
spin_unlock_irqrestore ( & priv - > lock , spinflags ) ;
return retval ;
2008-04-24 02:37:04 +04:00
}
2011-05-20 02:17:45 +04:00
static void kill_transfer ( struct usb_hcd * hcd , struct urb * urb ,
struct isp1760_qh * qh )
{
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
int skip_map ;
WARN_ON ( qh - > slot = = - 1 ) ;
/* We need to forcefully reclaim the slot since some transfers never
return , e . g . interrupt transfers and NAKed bulk transfers . */
2011-06-17 20:45:37 +04:00
if ( usb_pipecontrol ( urb - > pipe ) | | usb_pipebulk ( urb - > pipe ) ) {
2011-05-20 02:17:45 +04:00
skip_map = reg_read32 ( hcd - > regs , HC_ATL_PTD_SKIPMAP_REG ) ;
skip_map | = ( 1 < < qh - > slot ) ;
reg_write32 ( hcd - > regs , HC_ATL_PTD_SKIPMAP_REG , skip_map ) ;
priv - > atl_slots [ qh - > slot ] . qh = NULL ;
priv - > atl_slots [ qh - > slot ] . qtd = NULL ;
} else {
skip_map = reg_read32 ( hcd - > regs , HC_INT_PTD_SKIPMAP_REG ) ;
skip_map | = ( 1 < < qh - > slot ) ;
reg_write32 ( hcd - > regs , HC_INT_PTD_SKIPMAP_REG , skip_map ) ;
priv - > int_slots [ qh - > slot ] . qh = NULL ;
priv - > int_slots [ qh - > slot ] . qtd = NULL ;
}
qh - > slot = - 1 ;
}
2011-08-23 03:25:30 +04:00
/*
* Retire the qtds beginning at ' qtd ' and belonging all to the same urb , killing
* any active transfer belonging to the urb in the process .
*/
static void dequeue_urb_from_qtd ( struct usb_hcd * hcd , struct isp1760_qh * qh ,
struct isp1760_qtd * qtd )
{
struct urb * urb ;
int urb_was_running ;
urb = qtd - > urb ;
urb_was_running = 0 ;
list_for_each_entry_from ( qtd , & qh - > qtd_list , qtd_list ) {
if ( qtd - > urb ! = urb )
break ;
if ( qtd - > status > = QTD_XFER_STARTED )
urb_was_running = 1 ;
if ( last_qtd_of_urb ( qtd , qh ) & &
( qtd - > status > = QTD_XFER_COMPLETE ) )
urb_was_running = 0 ;
if ( qtd - > status = = QTD_XFER_STARTED )
kill_transfer ( hcd , urb , qh ) ;
qtd - > status = QTD_RETIRE ;
}
if ( ( urb - > dev - > speed ! = USB_SPEED_HIGH ) & & urb_was_running ) {
qh - > tt_buffer_dirty = 1 ;
if ( usb_hub_clear_tt_buffer ( urb ) )
/* Clear failed; let's hope things work anyway */
qh - > tt_buffer_dirty = 0 ;
}
}
2011-04-26 23:48:30 +04:00
static int isp1760_urb_dequeue ( struct usb_hcd * hcd , struct urb * urb ,
int status )
2008-04-24 02:37:04 +04:00
{
2011-02-27 00:06:37 +03:00
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
2011-05-20 02:17:45 +04:00
unsigned long spinflags ;
2011-04-26 23:48:30 +04:00
struct isp1760_qh * qh ;
struct isp1760_qtd * qtd ;
int retval = 0 ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
spin_lock_irqsave ( & priv - > lock , spinflags ) ;
2011-07-20 05:13:46 +04:00
retval = usb_hcd_check_unlink_urb ( hcd , urb , status ) ;
if ( retval )
goto out ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
qh = urb - > ep - > hcpriv ;
if ( ! qh ) {
retval = - EINVAL ;
goto out ;
}
2008-04-24 02:37:04 +04:00
2011-05-20 02:17:45 +04:00
list_for_each_entry ( qtd , & qh - > qtd_list , qtd_list )
if ( qtd - > urb = = urb ) {
2011-08-23 03:25:30 +04:00
dequeue_urb_from_qtd ( hcd , qh , qtd ) ;
break ;
2011-05-20 02:17:45 +04:00
}
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
urb - > status = status ;
schedule_ptds ( hcd ) ;
2008-04-24 02:37:04 +04:00
2011-04-26 23:48:30 +04:00
out :
spin_unlock_irqrestore ( & priv - > lock , spinflags ) ;
return retval ;
2008-04-24 02:37:04 +04:00
}
2011-05-20 02:17:34 +04:00
static void isp1760_endpoint_disable ( struct usb_hcd * hcd ,
struct usb_host_endpoint * ep )
{
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
2011-05-20 02:17:45 +04:00
unsigned long spinflags ;
2011-05-20 02:17:34 +04:00
struct isp1760_qh * qh ;
struct isp1760_qtd * qtd ;
spin_lock_irqsave ( & priv - > lock , spinflags ) ;
2011-05-20 02:17:45 +04:00
2011-05-20 02:17:34 +04:00
qh = ep - > hcpriv ;
if ( ! qh )
goto out ;
2011-08-23 03:25:30 +04:00
list_for_each_entry ( qtd , & qh - > qtd_list , qtd_list )
if ( qtd - > status ! = QTD_RETIRE ) {
dequeue_urb_from_qtd ( hcd , qh , qtd ) ;
qtd - > urb - > status = - ECONNRESET ;
}
2011-05-20 02:17:45 +04:00
2011-05-20 02:17:34 +04:00
ep - > hcpriv = NULL ;
/* Cannot free qh here since it will be parsed by schedule_ptds() */
2011-05-20 02:17:45 +04:00
schedule_ptds ( hcd ) ;
2011-05-20 02:17:34 +04:00
out :
spin_unlock_irqrestore ( & priv - > lock , spinflags ) ;
}
2008-04-24 02:37:04 +04:00
static int isp1760_hub_status_data ( struct usb_hcd * hcd , char * buf )
{
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
u32 temp , status = 0 ;
u32 mask ;
int retval = 1 ;
unsigned long flags ;
/* if !USB_SUSPEND, root hub timers won't get shut down ... */
if ( ! HC_IS_RUNNING ( hcd - > state ) )
return 0 ;
/* init status to no-changes */
buf [ 0 ] = 0 ;
mask = PORT_CSC ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
2011-02-27 00:02:57 +03:00
temp = reg_read32 ( hcd - > regs , HC_PORTSC1 ) ;
2008-04-24 02:37:04 +04:00
if ( temp & PORT_OWNER ) {
if ( temp & PORT_CSC ) {
temp & = ~ PORT_CSC ;
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_PORTSC1 , temp ) ;
2008-04-24 02:37:04 +04:00
goto done ;
}
}
/*
* Return status information even for ports with OWNER set .
* Otherwise khubd wouldn ' t see the disconnect event when a
* high - speed device is switched over to the companion
* controller by the user .
*/
if ( ( temp & mask ) ! = 0
| | ( ( temp & PORT_RESUME ) ! = 0
& & time_after_eq ( jiffies ,
priv - > reset_done ) ) ) {
buf [ 0 ] | = 1 < < ( 0 + 1 ) ;
status = STS_PCD ;
}
/* FIXME autosuspend idle root hubs */
done :
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return status ? retval : 0 ;
}
static void isp1760_hub_descriptor ( struct isp1760_hcd * priv ,
struct usb_hub_descriptor * desc )
{
int ports = HCS_N_PORTS ( priv - > hcs_params ) ;
u16 temp ;
desc - > bDescriptorType = 0x29 ;
/* priv 1.0, 2.3.9 says 20ms max */
desc - > bPwrOn2PwrGood = 10 ;
desc - > bHubContrCurrent = 0 ;
desc - > bNbrPorts = ports ;
temp = 1 + ( ports / 8 ) ;
desc - > bDescLength = 7 + 2 * temp ;
2010-12-01 02:55:51 +03:00
/* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
2001-09-17 11:00:00 +04:00
memset ( & desc - > u . hs . DeviceRemovable [ 0 ] , 0 , temp ) ;
memset ( & desc - > u . hs . DeviceRemovable [ temp ] , 0xff , temp ) ;
2008-04-24 02:37:04 +04:00
/* per-port overcurrent reporting */
temp = 0x0008 ;
if ( HCS_PPC ( priv - > hcs_params ) )
/* per-port power control */
temp | = 0x0001 ;
else
/* no power switching */
temp | = 0x0002 ;
desc - > wHubCharacteristics = cpu_to_le16 ( temp ) ;
}
# define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
2011-02-27 00:02:57 +03:00
static int check_reset_complete ( struct usb_hcd * hcd , int index ,
int port_status )
2008-04-24 02:37:04 +04:00
{
if ( ! ( port_status & PORT_CONNECT ) )
return port_status ;
/* if reset finished and it's still not enabled -- handoff */
if ( ! ( port_status & PORT_PE ) ) {
2011-04-26 23:48:30 +04:00
dev_info ( hcd - > self . controller ,
2011-02-27 00:06:37 +03:00
" port %d full speed --> companion \n " ,
index + 1 ) ;
2008-04-24 02:37:04 +04:00
port_status | = PORT_OWNER ;
port_status & = ~ PORT_RWC_BITS ;
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_PORTSC1 , port_status ) ;
2008-04-24 02:37:04 +04:00
} else
2011-04-26 23:48:30 +04:00
dev_info ( hcd - > self . controller , " port %d high speed \n " ,
2011-02-27 00:06:37 +03:00
index + 1 ) ;
2008-04-24 02:37:04 +04:00
return port_status ;
}
static int isp1760_hub_control ( struct usb_hcd * hcd , u16 typeReq ,
u16 wValue , u16 wIndex , char * buf , u16 wLength )
{
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
int ports = HCS_N_PORTS ( priv - > hcs_params ) ;
u32 temp , status ;
unsigned long flags ;
int retval = 0 ;
unsigned selector ;
/*
* FIXME : support SetPortFeatures USB_PORT_FEAT_INDICATOR .
* HCS_INDICATOR may say we can change LEDs to off / amber / green .
* ( track current state ourselves ) . . . blink for diagnostics ,
* power , " this is the one " , etc . EHCI spec supports this .
*/
spin_lock_irqsave ( & priv - > lock , flags ) ;
switch ( typeReq ) {
case ClearHubFeature :
switch ( wValue ) {
case C_HUB_LOCAL_POWER :
case C_HUB_OVER_CURRENT :
/* no hub-wide feature/status flags */
break ;
default :
goto error ;
}
break ;
case ClearPortFeature :
if ( ! wIndex | | wIndex > ports )
goto error ;
wIndex - - ;
2011-02-27 00:02:57 +03:00
temp = reg_read32 ( hcd - > regs , HC_PORTSC1 ) ;
2008-04-24 02:37:04 +04:00
/*
* Even if OWNER is set , so the port is owned by the
* companion controller , khubd needs to be able to clear
* the port - change status bits ( especially
2010-03-05 01:05:08 +03:00
* USB_PORT_STAT_C_CONNECTION ) .
2008-04-24 02:37:04 +04:00
*/
switch ( wValue ) {
case USB_PORT_FEAT_ENABLE :
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_PORTSC1 , temp & ~ PORT_PE ) ;
2008-04-24 02:37:04 +04:00
break ;
case USB_PORT_FEAT_C_ENABLE :
/* XXX error? */
break ;
case USB_PORT_FEAT_SUSPEND :
if ( temp & PORT_RESET )
goto error ;
if ( temp & PORT_SUSPEND ) {
if ( ( temp & PORT_PE ) = = 0 )
goto error ;
/* resume signaling for 20 msec */
temp & = ~ ( PORT_RWC_BITS ) ;
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_PORTSC1 ,
temp | PORT_RESUME ) ;
2008-04-24 02:37:04 +04:00
priv - > reset_done = jiffies +
msecs_to_jiffies ( 20 ) ;
}
break ;
case USB_PORT_FEAT_C_SUSPEND :
/* we auto-clear this feature */
break ;
case USB_PORT_FEAT_POWER :
if ( HCS_PPC ( priv - > hcs_params ) )
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_PORTSC1 ,
temp & ~ PORT_POWER ) ;
2008-04-24 02:37:04 +04:00
break ;
case USB_PORT_FEAT_C_CONNECTION :
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_PORTSC1 , temp | PORT_CSC ) ;
2008-04-24 02:37:04 +04:00
break ;
case USB_PORT_FEAT_C_OVER_CURRENT :
/* XXX error ?*/
break ;
case USB_PORT_FEAT_C_RESET :
/* GetPortStatus clears reset */
break ;
default :
goto error ;
}
2011-02-27 00:02:57 +03:00
reg_read32 ( hcd - > regs , HC_USBCMD ) ;
2008-04-24 02:37:04 +04:00
break ;
case GetHubDescriptor :
isp1760_hub_descriptor ( priv , ( struct usb_hub_descriptor * )
buf ) ;
break ;
case GetHubStatus :
/* no hub-wide feature/status flags */
memset ( buf , 0 , 4 ) ;
break ;
case GetPortStatus :
if ( ! wIndex | | wIndex > ports )
goto error ;
wIndex - - ;
status = 0 ;
2011-02-27 00:02:57 +03:00
temp = reg_read32 ( hcd - > regs , HC_PORTSC1 ) ;
2008-04-24 02:37:04 +04:00
/* wPortChange bits */
if ( temp & PORT_CSC )
2010-03-05 01:05:08 +03:00
status | = USB_PORT_STAT_C_CONNECTION < < 16 ;
2008-04-24 02:37:04 +04:00
/* whoever resumes must GetPortStatus to complete it!! */
if ( temp & PORT_RESUME ) {
2011-02-27 00:06:37 +03:00
dev_err ( hcd - > self . controller , " Port resume should be skipped. \n " ) ;
2008-04-24 02:37:04 +04:00
/* Remote Wakeup received? */
if ( ! priv - > reset_done ) {
/* resume signaling for 20 msec */
priv - > reset_done = jiffies
+ msecs_to_jiffies ( 20 ) ;
/* check the port again */
2011-02-27 00:06:37 +03:00
mod_timer ( & hcd - > rh_timer , priv - > reset_done ) ;
2008-04-24 02:37:04 +04:00
}
/* resume completed? */
else if ( time_after_eq ( jiffies ,
priv - > reset_done ) ) {
2010-03-05 01:05:08 +03:00
status | = USB_PORT_STAT_C_SUSPEND < < 16 ;
2008-04-24 02:37:04 +04:00
priv - > reset_done = 0 ;
/* stop resume signaling */
2011-02-27 00:02:57 +03:00
temp = reg_read32 ( hcd - > regs , HC_PORTSC1 ) ;
reg_write32 ( hcd - > regs , HC_PORTSC1 ,
temp & ~ ( PORT_RWC_BITS | PORT_RESUME ) ) ;
retval = handshake ( hcd , HC_PORTSC1 ,
2008-04-24 02:37:04 +04:00
PORT_RESUME , 0 , 2000 /* 2msec */ ) ;
if ( retval ! = 0 ) {
2011-02-27 00:06:37 +03:00
dev_err ( hcd - > self . controller ,
2008-04-24 02:37:04 +04:00
" port %d resume error %d \n " ,
wIndex + 1 , retval ) ;
goto error ;
}
temp & = ~ ( PORT_SUSPEND | PORT_RESUME | ( 3 < < 10 ) ) ;
}
}
/* whoever resets must GetPortStatus to complete it!! */
if ( ( temp & PORT_RESET )
& & time_after_eq ( jiffies ,
priv - > reset_done ) ) {
2010-03-05 01:05:08 +03:00
status | = USB_PORT_STAT_C_RESET < < 16 ;
2008-04-24 02:37:04 +04:00
priv - > reset_done = 0 ;
/* force reset to complete */
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_PORTSC1 , temp & ~ PORT_RESET ) ;
2008-04-24 02:37:04 +04:00
/* REVISIT: some hardware needs 550+ usec to clear
* this bit ; seems too long to spin routinely . . .
*/
2011-02-27 00:02:57 +03:00
retval = handshake ( hcd , HC_PORTSC1 ,
2008-04-24 02:37:04 +04:00
PORT_RESET , 0 , 750 ) ;
if ( retval ! = 0 ) {
2011-02-27 00:06:37 +03:00
dev_err ( hcd - > self . controller , " port %d reset error %d \n " ,
2008-04-24 02:37:04 +04:00
wIndex + 1 , retval ) ;
goto error ;
}
/* see what we found out */
2011-02-27 00:02:57 +03:00
temp = check_reset_complete ( hcd , wIndex ,
reg_read32 ( hcd - > regs , HC_PORTSC1 ) ) ;
2008-04-24 02:37:04 +04:00
}
/*
* Even if OWNER is set , there ' s no harm letting khubd
* see the wPortStatus values ( they should all be 0 except
* for PORT_POWER anyway ) .
*/
if ( temp & PORT_OWNER )
2011-02-27 00:06:37 +03:00
dev_err ( hcd - > self . controller , " PORT_OWNER is set \n " ) ;
2008-04-24 02:37:04 +04:00
if ( temp & PORT_CONNECT ) {
2010-03-05 01:05:08 +03:00
status | = USB_PORT_STAT_CONNECTION ;
2008-04-24 02:37:04 +04:00
/* status may be from integrated TT */
2011-02-27 00:06:37 +03:00
status | = USB_PORT_STAT_HIGH_SPEED ;
2008-04-24 02:37:04 +04:00
}
if ( temp & PORT_PE )
2010-03-05 01:05:08 +03:00
status | = USB_PORT_STAT_ENABLE ;
2008-04-24 02:37:04 +04:00
if ( temp & ( PORT_SUSPEND | PORT_RESUME ) )
2010-03-05 01:05:08 +03:00
status | = USB_PORT_STAT_SUSPEND ;
2008-04-24 02:37:04 +04:00
if ( temp & PORT_RESET )
2010-03-05 01:05:08 +03:00
status | = USB_PORT_STAT_RESET ;
2008-04-24 02:37:04 +04:00
if ( temp & PORT_POWER )
2010-03-05 01:05:08 +03:00
status | = USB_PORT_STAT_POWER ;
2008-04-24 02:37:04 +04:00
put_unaligned ( cpu_to_le32 ( status ) , ( __le32 * ) buf ) ;
break ;
case SetHubFeature :
switch ( wValue ) {
case C_HUB_LOCAL_POWER :
case C_HUB_OVER_CURRENT :
/* no hub-wide feature/status flags */
break ;
default :
goto error ;
}
break ;
case SetPortFeature :
selector = wIndex > > 8 ;
wIndex & = 0xff ;
if ( ! wIndex | | wIndex > ports )
goto error ;
wIndex - - ;
2011-02-27 00:02:57 +03:00
temp = reg_read32 ( hcd - > regs , HC_PORTSC1 ) ;
2008-04-24 02:37:04 +04:00
if ( temp & PORT_OWNER )
break ;
/* temp &= ~PORT_RWC_BITS; */
switch ( wValue ) {
case USB_PORT_FEAT_ENABLE :
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_PORTSC1 , temp | PORT_PE ) ;
2008-04-24 02:37:04 +04:00
break ;
case USB_PORT_FEAT_SUSPEND :
if ( ( temp & PORT_PE ) = = 0
| | ( temp & PORT_RESET ) ! = 0 )
goto error ;
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_PORTSC1 , temp | PORT_SUSPEND ) ;
2008-04-24 02:37:04 +04:00
break ;
case USB_PORT_FEAT_POWER :
if ( HCS_PPC ( priv - > hcs_params ) )
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_PORTSC1 ,
temp | PORT_POWER ) ;
2008-04-24 02:37:04 +04:00
break ;
case USB_PORT_FEAT_RESET :
if ( temp & PORT_RESUME )
goto error ;
/* line status bits may report this as low speed,
* which can be fine if this root hub has a
* transaction translator built in .
*/
if ( ( temp & ( PORT_PE | PORT_CONNECT ) ) = = PORT_CONNECT
& & PORT_USB11 ( temp ) ) {
temp | = PORT_OWNER ;
} else {
temp | = PORT_RESET ;
temp & = ~ PORT_PE ;
/*
* caller must wait , then call GetPortStatus
* usb 2.0 spec says 50 ms resets on root
*/
priv - > reset_done = jiffies +
msecs_to_jiffies ( 50 ) ;
}
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_PORTSC1 , temp ) ;
2008-04-24 02:37:04 +04:00
break ;
default :
goto error ;
}
2011-02-27 00:02:57 +03:00
reg_read32 ( hcd - > regs , HC_USBCMD ) ;
2008-04-24 02:37:04 +04:00
break ;
default :
error :
/* "stall" on error */
retval = - EPIPE ;
}
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return retval ;
}
static int isp1760_get_frame ( struct usb_hcd * hcd )
{
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
u32 fr ;
2011-02-27 00:02:57 +03:00
fr = reg_read32 ( hcd - > regs , HC_FRINDEX ) ;
2008-04-24 02:37:04 +04:00
return ( fr > > 3 ) % priv - > periodic_size ;
}
static void isp1760_stop ( struct usb_hcd * hcd )
{
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
2008-06-17 20:11:38 +04:00
u32 temp ;
2008-04-24 02:37:04 +04:00
2011-08-21 10:29:26 +04:00
del_timer ( & errata2_timer ) ;
2008-04-24 02:37:04 +04:00
isp1760_hub_control ( hcd , ClearPortFeature , USB_PORT_FEAT_POWER , 1 ,
NULL , 0 ) ;
mdelay ( 20 ) ;
spin_lock_irq ( & priv - > lock ) ;
2011-02-27 00:06:37 +03:00
ehci_reset ( hcd ) ;
2008-04-24 02:37:04 +04:00
/* Disable IRQ */
2011-02-27 00:02:57 +03:00
temp = reg_read32 ( hcd - > regs , HC_HW_MODE_CTRL ) ;
reg_write32 ( hcd - > regs , HC_HW_MODE_CTRL , temp & = ~ HW_GLOBAL_INTR_EN ) ;
2008-04-24 02:37:04 +04:00
spin_unlock_irq ( & priv - > lock ) ;
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_CONFIGFLAG , 0 ) ;
2008-04-24 02:37:04 +04:00
}
static void isp1760_shutdown ( struct usb_hcd * hcd )
{
2008-06-17 20:11:38 +04:00
u32 command , temp ;
2008-04-24 02:37:04 +04:00
isp1760_stop ( hcd ) ;
2011-02-27 00:02:57 +03:00
temp = reg_read32 ( hcd - > regs , HC_HW_MODE_CTRL ) ;
reg_write32 ( hcd - > regs , HC_HW_MODE_CTRL , temp & = ~ HW_GLOBAL_INTR_EN ) ;
2008-04-24 02:37:04 +04:00
2011-02-27 00:02:57 +03:00
command = reg_read32 ( hcd - > regs , HC_USBCMD ) ;
2008-04-24 02:37:04 +04:00
command & = ~ CMD_RUN ;
2011-02-27 00:02:57 +03:00
reg_write32 ( hcd - > regs , HC_USBCMD , command ) ;
2008-04-24 02:37:04 +04:00
}
2011-08-23 03:25:30 +04:00
static void isp1760_clear_tt_buffer_complete ( struct usb_hcd * hcd ,
struct usb_host_endpoint * ep )
{
struct isp1760_hcd * priv = hcd_to_priv ( hcd ) ;
struct isp1760_qh * qh = ep - > hcpriv ;
unsigned long spinflags ;
if ( ! qh )
return ;
spin_lock_irqsave ( & priv - > lock , spinflags ) ;
qh - > tt_buffer_dirty = 0 ;
schedule_ptds ( hcd ) ;
spin_unlock_irqrestore ( & priv - > lock , spinflags ) ;
}
2008-04-24 02:37:04 +04:00
static const struct hc_driver isp1760_hc_driver = {
. description = " isp1760-hcd " ,
. product_desc = " NXP ISP1760 USB Host Controller " ,
. hcd_priv_size = sizeof ( struct isp1760_hcd ) ,
. irq = isp1760_irq ,
. flags = HCD_MEMORY | HCD_USB2 ,
. reset = isp1760_hc_setup ,
. start = isp1760_run ,
. stop = isp1760_stop ,
. shutdown = isp1760_shutdown ,
. urb_enqueue = isp1760_urb_enqueue ,
. urb_dequeue = isp1760_urb_dequeue ,
. endpoint_disable = isp1760_endpoint_disable ,
. get_frame_number = isp1760_get_frame ,
. hub_status_data = isp1760_hub_status_data ,
. hub_control = isp1760_hub_control ,
2011-08-23 03:25:30 +04:00
. clear_tt_buffer_complete = isp1760_clear_tt_buffer_complete ,
2008-04-24 02:37:04 +04:00
} ;
int __init init_kmem_once ( void )
{
2011-04-26 23:48:30 +04:00
urb_listitem_cachep = kmem_cache_create ( " isp1760 urb_listitem " ,
sizeof ( struct urb_listitem ) , 0 , SLAB_TEMPORARY |
SLAB_MEM_SPREAD , NULL ) ;
if ( ! urb_listitem_cachep )
return - ENOMEM ;
2008-04-24 02:37:04 +04:00
qtd_cachep = kmem_cache_create ( " isp1760_qtd " ,
sizeof ( struct isp1760_qtd ) , 0 , SLAB_TEMPORARY |
SLAB_MEM_SPREAD , NULL ) ;
if ( ! qtd_cachep )
return - ENOMEM ;
qh_cachep = kmem_cache_create ( " isp1760_qh " , sizeof ( struct isp1760_qh ) ,
0 , SLAB_TEMPORARY | SLAB_MEM_SPREAD , NULL ) ;
if ( ! qh_cachep ) {
kmem_cache_destroy ( qtd_cachep ) ;
return - ENOMEM ;
}
return 0 ;
}
void deinit_kmem_cache ( void )
{
kmem_cache_destroy ( qtd_cachep ) ;
kmem_cache_destroy ( qh_cachep ) ;
2011-04-26 23:48:30 +04:00
kmem_cache_destroy ( urb_listitem_cachep ) ;
2008-04-24 02:37:04 +04:00
}
2009-02-10 19:55:45 +03:00
struct usb_hcd * isp1760_register ( phys_addr_t res_start , resource_size_t res_len ,
int irq , unsigned long irqflags ,
2011-10-19 16:18:41 +04:00
int rst_gpio ,
2009-02-10 19:55:45 +03:00
struct device * dev , const char * busname ,
unsigned int devflags )
2008-04-24 02:37:04 +04:00
{
struct usb_hcd * hcd ;
struct isp1760_hcd * priv ;
int ret ;
if ( usb_disabled ( ) )
return ERR_PTR ( - ENODEV ) ;
/* prevent usb-core allocating DMA pages */
dev - > dma_mask = NULL ;
2008-05-02 08:02:41 +04:00
hcd = usb_create_hcd ( & isp1760_hc_driver , dev , dev_name ( dev ) ) ;
2008-04-24 02:37:04 +04:00
if ( ! hcd )
return ERR_PTR ( - ENOMEM ) ;
priv = hcd_to_priv ( hcd ) ;
2008-06-17 20:11:38 +04:00
priv - > devflags = devflags ;
2011-10-19 16:18:41 +04:00
priv - > rst_gpio = rst_gpio ;
2008-04-24 02:37:04 +04:00
init_memory ( priv ) ;
hcd - > regs = ioremap ( res_start , res_len ) ;
if ( ! hcd - > regs ) {
ret = - EIO ;
goto err_put ;
}
hcd - > irq = irq ;
hcd - > rsrc_start = res_start ;
hcd - > rsrc_len = res_len ;
2008-05-22 01:28:20 +04:00
ret = usb_add_hcd ( hcd , irq , irqflags ) ;
if ( ret )
goto err_unmap ;
2008-04-24 02:37:04 +04:00
return hcd ;
err_unmap :
iounmap ( hcd - > regs ) ;
err_put :
usb_put_hcd ( hcd ) ;
return ERR_PTR ( ret ) ;
}
MODULE_DESCRIPTION ( " Driver for the ISP1760 USB-controller from NXP " ) ;
MODULE_AUTHOR ( " Sebastian Siewior <bigeasy@linuxtronix.de> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;