2016-10-19 10:28:23 +08:00
/*
* mtu3_gadget_ep0 . c - MediaTek USB3 DRD peripheral driver ep0 handling
*
* Copyright ( c ) 2016 MediaTek Inc .
*
* Author : Chunfeng . Yun < chunfeng . yun @ mediatek . com >
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* 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 .
*
*/
# include "mtu3.h"
/* ep0 is always mtu3->in_eps[0] */
# define next_ep0_request(mtu) next_request((mtu)->ep0)
/* for high speed test mode; see USB 2.0 spec 7.1.20 */
static const u8 mtu3_test_packet [ 53 ] = {
/* implicit SYNC then DATA0 to start */
/* JKJKJKJK x9 */
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
/* JJKKJJKK x8 */
0xaa , 0xaa , 0xaa , 0xaa , 0xaa , 0xaa , 0xaa , 0xaa ,
/* JJJJKKKK x8 */
0xee , 0xee , 0xee , 0xee , 0xee , 0xee , 0xee , 0xee ,
/* JJJJJJJKKKKKKK x8 */
0xfe , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
/* JJJJJJJK x8 */
0x7f , 0xbf , 0xdf , 0xef , 0xf7 , 0xfb , 0xfd ,
/* JKKKKKKK x10, JK */
0xfc , 0x7e , 0xbf , 0xdf , 0xef , 0xf7 , 0xfb , 0xfd , 0x7e ,
/* implicit CRC16 then EOP to end */
} ;
static char * decode_ep0_state ( struct mtu3 * mtu )
{
switch ( mtu - > ep0_state ) {
case MU3D_EP0_STATE_SETUP :
return " SETUP " ;
case MU3D_EP0_STATE_TX :
return " IN " ;
case MU3D_EP0_STATE_RX :
return " OUT " ;
case MU3D_EP0_STATE_TX_END :
return " TX-END " ;
case MU3D_EP0_STATE_STALL :
return " STALL " ;
default :
return " ?? " ;
}
}
static void ep0_req_giveback ( struct mtu3 * mtu , struct usb_request * req )
{
mtu3_req_complete ( mtu - > ep0 , req , 0 ) ;
}
static int
forward_to_driver ( struct mtu3 * mtu , const struct usb_ctrlrequest * setup )
__releases ( mtu - > lock )
__acquires ( mtu - > lock )
{
int ret ;
if ( ! mtu - > gadget_driver )
return - EOPNOTSUPP ;
spin_unlock ( & mtu - > lock ) ;
ret = mtu - > gadget_driver - > setup ( & mtu - > g , setup ) ;
spin_lock ( & mtu - > lock ) ;
dev_dbg ( mtu - > dev , " %s ret %d \n " , __func__ , ret ) ;
return ret ;
}
static void ep0_write_fifo ( struct mtu3_ep * mep , const u8 * src , u16 len )
{
void __iomem * fifo = mep - > mtu - > mac_base + U3D_FIFO0 ;
u16 index = 0 ;
dev_dbg ( mep - > mtu - > dev , " %s: ep%din, len=%d, buf=%p \n " ,
__func__ , mep - > epnum , len , src ) ;
if ( len > = 4 ) {
iowrite32_rep ( fifo , src , len > > 2 ) ;
index = len & ~ 0x03 ;
}
if ( len & 0x02 ) {
writew ( * ( u16 * ) & src [ index ] , fifo ) ;
index + = 2 ;
}
if ( len & 0x01 )
writeb ( src [ index ] , fifo ) ;
}
static void ep0_read_fifo ( struct mtu3_ep * mep , u8 * dst , u16 len )
{
void __iomem * fifo = mep - > mtu - > mac_base + U3D_FIFO0 ;
u32 value ;
u16 index = 0 ;
dev_dbg ( mep - > mtu - > dev , " %s: ep%dout len=%d buf=%p \n " ,
__func__ , mep - > epnum , len , dst ) ;
if ( len > = 4 ) {
ioread32_rep ( fifo , dst , len > > 2 ) ;
index = len & ~ 0x03 ;
}
if ( len & 0x3 ) {
value = readl ( fifo ) ;
memcpy ( & dst [ index ] , & value , len & 0x3 ) ;
}
}
static void ep0_load_test_packet ( struct mtu3 * mtu )
{
/*
* because the length of test packet is less than max packet of HS ep0 ,
* write it into fifo directly .
*/
ep0_write_fifo ( mtu - > ep0 , mtu3_test_packet , sizeof ( mtu3_test_packet ) ) ;
}
/*
* A . send STALL for setup transfer without data stage :
* set SENDSTALL and SETUPPKTRDY at the same time ;
* B . send STALL for other cases :
* set SENDSTALL only .
*/
static void ep0_stall_set ( struct mtu3_ep * mep0 , bool set , u32 pktrdy )
{
struct mtu3 * mtu = mep0 - > mtu ;
void __iomem * mbase = mtu - > mac_base ;
u32 csr ;
/* EP0_SENTSTALL is W1C */
csr = mtu3_readl ( mbase , U3D_EP0CSR ) & EP0_W1C_BITS ;
if ( set )
csr | = EP0_SENDSTALL | pktrdy ;
else
csr = ( csr & ~ EP0_SENDSTALL ) | EP0_SENTSTALL ;
mtu3_writel ( mtu - > mac_base , U3D_EP0CSR , csr ) ;
mtu - > ep0_state = MU3D_EP0_STATE_SETUP ;
dev_dbg ( mtu - > dev , " ep0: %s STALL, ep0_state: %s \n " ,
set ? " SEND " : " CLEAR " , decode_ep0_state ( mtu ) ) ;
}
static int ep0_queue ( struct mtu3_ep * mep0 , struct mtu3_request * mreq ) ;
static void ep0_dummy_complete ( struct usb_ep * ep , struct usb_request * req )
{ }
2016-10-19 10:28:24 +08:00
static void ep0_set_sel_complete ( struct usb_ep * ep , struct usb_request * req )
{
struct mtu3_request * mreq ;
struct mtu3 * mtu ;
struct usb_set_sel_req sel ;
memcpy ( & sel , req - > buf , sizeof ( sel ) ) ;
mreq = to_mtu3_request ( req ) ;
mtu = mreq - > mtu ;
dev_dbg ( mtu - > dev , " u1sel:%d, u1pel:%d, u2sel:%d, u2pel:%d \n " ,
sel . u1_sel , sel . u1_pel , sel . u2_sel , sel . u2_pel ) ;
}
/* queue data stage to handle 6 byte SET_SEL request */
static int ep0_set_sel ( struct mtu3 * mtu , struct usb_ctrlrequest * setup )
{
int ret ;
u16 length = le16_to_cpu ( setup - > wLength ) ;
if ( unlikely ( length ! = 6 ) ) {
dev_err ( mtu - > dev , " %s wrong wLength:%d \n " ,
__func__ , length ) ;
return - EINVAL ;
}
mtu - > ep0_req . mep = mtu - > ep0 ;
mtu - > ep0_req . request . length = 6 ;
mtu - > ep0_req . request . buf = mtu - > setup_buf ;
mtu - > ep0_req . request . complete = ep0_set_sel_complete ;
ret = ep0_queue ( mtu - > ep0 , & mtu - > ep0_req ) ;
return ret < 0 ? ret : 1 ;
}
2016-10-19 10:28:23 +08:00
static int
ep0_get_status ( struct mtu3 * mtu , const struct usb_ctrlrequest * setup )
{
struct mtu3_ep * mep = NULL ;
int handled = 1 ;
u8 result [ 2 ] = { 0 , 0 } ;
u8 epnum = 0 ;
int is_in ;
switch ( setup - > bRequestType & USB_RECIP_MASK ) {
case USB_RECIP_DEVICE :
result [ 0 ] = mtu - > is_self_powered < < USB_DEVICE_SELF_POWERED ;
result [ 0 ] | = mtu - > may_wakeup < < USB_DEVICE_REMOTE_WAKEUP ;
2016-10-19 10:28:24 +08:00
/* superspeed only */
if ( mtu - > g . speed = = USB_SPEED_SUPER ) {
result [ 0 ] | = mtu - > u1_enable < < USB_DEV_STAT_U1_ENABLED ;
result [ 0 ] | = mtu - > u2_enable < < USB_DEV_STAT_U2_ENABLED ;
}
dev_dbg ( mtu - > dev , " %s result=%x, U1=%x, U2=%x \n " , __func__ ,
result [ 0 ] , mtu - > u1_enable , mtu - > u2_enable ) ;
2016-10-19 10:28:23 +08:00
break ;
case USB_RECIP_INTERFACE :
break ;
case USB_RECIP_ENDPOINT :
epnum = ( u8 ) le16_to_cpu ( setup - > wIndex ) ;
is_in = epnum & USB_DIR_IN ;
epnum & = USB_ENDPOINT_NUMBER_MASK ;
if ( epnum > = mtu - > num_eps ) {
handled = - EINVAL ;
break ;
}
if ( ! epnum )
break ;
mep = ( is_in ? mtu - > in_eps : mtu - > out_eps ) + epnum ;
if ( ! mep - > desc ) {
handled = - EINVAL ;
break ;
}
if ( mep - > flags & MTU3_EP_STALL )
result [ 0 ] | = 1 < < USB_ENDPOINT_HALT ;
break ;
default :
/* class, vendor, etc ... delegate */
handled = 0 ;
break ;
}
if ( handled > 0 ) {
int ret ;
/* prepare a data stage for GET_STATUS */
dev_dbg ( mtu - > dev , " get_status=%x \n " , * ( u16 * ) result ) ;
memcpy ( mtu - > setup_buf , result , sizeof ( result ) ) ;
mtu - > ep0_req . mep = mtu - > ep0 ;
mtu - > ep0_req . request . length = 2 ;
mtu - > ep0_req . request . buf = & mtu - > setup_buf ;
mtu - > ep0_req . request . complete = ep0_dummy_complete ;
ret = ep0_queue ( mtu - > ep0 , & mtu - > ep0_req ) ;
if ( ret < 0 )
handled = ret ;
}
return handled ;
}
static int handle_test_mode ( struct mtu3 * mtu , struct usb_ctrlrequest * setup )
{
void __iomem * mbase = mtu - > mac_base ;
int handled = 1 ;
switch ( le16_to_cpu ( setup - > wIndex ) > > 8 ) {
case TEST_J :
dev_dbg ( mtu - > dev , " TEST_J \n " ) ;
mtu - > test_mode_nr = TEST_J_MODE ;
break ;
case TEST_K :
dev_dbg ( mtu - > dev , " TEST_K \n " ) ;
mtu - > test_mode_nr = TEST_K_MODE ;
break ;
case TEST_SE0_NAK :
dev_dbg ( mtu - > dev , " TEST_SE0_NAK \n " ) ;
mtu - > test_mode_nr = TEST_SE0_NAK_MODE ;
break ;
case TEST_PACKET :
dev_dbg ( mtu - > dev , " TEST_PACKET \n " ) ;
mtu - > test_mode_nr = TEST_PACKET_MODE ;
break ;
default :
handled = - EINVAL ;
goto out ;
}
mtu - > test_mode = true ;
/* no TX completion interrupt, and need restart platform after test */
if ( mtu - > test_mode_nr = = TEST_PACKET_MODE )
ep0_load_test_packet ( mtu ) ;
mtu3_writel ( mbase , U3D_USB2_TEST_MODE , mtu - > test_mode_nr ) ;
mtu - > ep0_state = MU3D_EP0_STATE_SETUP ;
out :
return handled ;
}
static int ep0_handle_feature_dev ( struct mtu3 * mtu ,
struct usb_ctrlrequest * setup , bool set )
{
2016-10-19 10:28:24 +08:00
void __iomem * mbase = mtu - > mac_base ;
2016-10-19 10:28:23 +08:00
int handled = - EINVAL ;
2016-10-19 10:28:24 +08:00
u32 lpc ;
2016-10-19 10:28:23 +08:00
switch ( le16_to_cpu ( setup - > wValue ) ) {
case USB_DEVICE_REMOTE_WAKEUP :
mtu - > may_wakeup = ! ! set ;
handled = 1 ;
break ;
case USB_DEVICE_TEST_MODE :
if ( ! set | | ( mtu - > g . speed ! = USB_SPEED_HIGH ) | |
( le16_to_cpu ( setup - > wIndex ) & 0xff ) )
break ;
handled = handle_test_mode ( mtu , setup ) ;
break ;
2016-10-19 10:28:24 +08:00
case USB_DEVICE_U1_ENABLE :
if ( mtu - > g . speed ! = USB_SPEED_SUPER | |
mtu - > g . state ! = USB_STATE_CONFIGURED )
break ;
lpc = mtu3_readl ( mbase , U3D_LINK_POWER_CONTROL ) ;
if ( set )
lpc | = SW_U1_ACCEPT_ENABLE ;
else
lpc & = ~ SW_U1_ACCEPT_ENABLE ;
mtu3_writel ( mbase , U3D_LINK_POWER_CONTROL , lpc ) ;
mtu - > u1_enable = ! ! set ;
handled = 1 ;
break ;
case USB_DEVICE_U2_ENABLE :
if ( mtu - > g . speed ! = USB_SPEED_SUPER | |
mtu - > g . state ! = USB_STATE_CONFIGURED )
break ;
lpc = mtu3_readl ( mbase , U3D_LINK_POWER_CONTROL ) ;
if ( set )
lpc | = SW_U2_ACCEPT_ENABLE ;
else
lpc & = ~ SW_U2_ACCEPT_ENABLE ;
mtu3_writel ( mbase , U3D_LINK_POWER_CONTROL , lpc ) ;
mtu - > u2_enable = ! ! set ;
handled = 1 ;
break ;
2016-10-19 10:28:23 +08:00
default :
handled = - EINVAL ;
break ;
}
return handled ;
}
static int ep0_handle_feature ( struct mtu3 * mtu ,
struct usb_ctrlrequest * setup , bool set )
{
struct mtu3_ep * mep ;
int handled = - EINVAL ;
int is_in ;
u16 value ;
u16 index ;
u8 epnum ;
value = le16_to_cpu ( setup - > wValue ) ;
index = le16_to_cpu ( setup - > wIndex ) ;
switch ( setup - > bRequestType & USB_RECIP_MASK ) {
case USB_RECIP_DEVICE :
handled = ep0_handle_feature_dev ( mtu , setup , set ) ;
break ;
2016-10-19 10:28:24 +08:00
case USB_RECIP_INTERFACE :
/* superspeed only */
if ( ( value = = USB_INTRF_FUNC_SUSPEND )
& & ( mtu - > g . speed = = USB_SPEED_SUPER ) ) {
/*
* forward the request because function drivers
* should handle it
*/
handled = 0 ;
}
break ;
2016-10-19 10:28:23 +08:00
case USB_RECIP_ENDPOINT :
epnum = index & USB_ENDPOINT_NUMBER_MASK ;
if ( epnum = = 0 | | epnum > = mtu - > num_eps | |
value ! = USB_ENDPOINT_HALT )
break ;
is_in = index & USB_DIR_IN ;
mep = ( is_in ? mtu - > in_eps : mtu - > out_eps ) + epnum ;
if ( ! mep - > desc )
break ;
handled = 1 ;
/* ignore request if endpoint is wedged */
if ( mep - > wedged )
break ;
mtu3_ep_stall_set ( mep , set ) ;
break ;
default :
/* class, vendor, etc ... delegate */
handled = 0 ;
break ;
}
return handled ;
}
/*
* handle all control requests can be handled
* returns :
* negative errno - error happened
* zero - need delegate SETUP to gadget driver
* positive - already handled
*/
static int handle_standard_request ( struct mtu3 * mtu ,
struct usb_ctrlrequest * setup )
{
void __iomem * mbase = mtu - > mac_base ;
enum usb_device_state state = mtu - > g . state ;
int handled = - EINVAL ;
u32 dev_conf ;
u16 value ;
value = le16_to_cpu ( setup - > wValue ) ;
/* the gadget driver handles everything except what we must handle */
switch ( setup - > bRequest ) {
case USB_REQ_SET_ADDRESS :
/* change it after the status stage */
mtu - > address = ( u8 ) ( value & 0x7f ) ;
dev_dbg ( mtu - > dev , " set address to 0x%x \n " , mtu - > address ) ;
dev_conf = mtu3_readl ( mbase , U3D_DEVICE_CONF ) ;
dev_conf & = ~ DEV_ADDR_MSK ;
dev_conf | = DEV_ADDR ( mtu - > address ) ;
mtu3_writel ( mbase , U3D_DEVICE_CONF , dev_conf ) ;
if ( mtu - > address )
usb_gadget_set_state ( & mtu - > g , USB_STATE_ADDRESS ) ;
else
usb_gadget_set_state ( & mtu - > g , USB_STATE_DEFAULT ) ;
handled = 1 ;
break ;
case USB_REQ_SET_CONFIGURATION :
if ( state = = USB_STATE_ADDRESS ) {
usb_gadget_set_state ( & mtu - > g ,
USB_STATE_CONFIGURED ) ;
} else if ( state = = USB_STATE_CONFIGURED ) {
/*
* USB2 spec sec 9.4 .7 , if wValue is 0 then dev
* is moved to addressed state
*/
if ( ! value )
usb_gadget_set_state ( & mtu - > g ,
USB_STATE_ADDRESS ) ;
}
handled = 0 ;
break ;
case USB_REQ_CLEAR_FEATURE :
handled = ep0_handle_feature ( mtu , setup , 0 ) ;
break ;
case USB_REQ_SET_FEATURE :
handled = ep0_handle_feature ( mtu , setup , 1 ) ;
break ;
case USB_REQ_GET_STATUS :
handled = ep0_get_status ( mtu , setup ) ;
break ;
2016-10-19 10:28:24 +08:00
case USB_REQ_SET_SEL :
handled = ep0_set_sel ( mtu , setup ) ;
break ;
2016-10-19 10:28:23 +08:00
case USB_REQ_SET_ISOCH_DELAY :
handled = 1 ;
break ;
default :
/* delegate SET_CONFIGURATION, etc */
handled = 0 ;
}
return handled ;
}
/* receive an data packet (OUT) */
static void ep0_rx_state ( struct mtu3 * mtu )
{
struct mtu3_request * mreq ;
struct usb_request * req ;
void __iomem * mbase = mtu - > mac_base ;
u32 maxp ;
u32 csr ;
u16 count = 0 ;
dev_dbg ( mtu - > dev , " %s \n " , __func__ ) ;
csr = mtu3_readl ( mbase , U3D_EP0CSR ) & EP0_W1C_BITS ;
mreq = next_ep0_request ( mtu ) ;
req = & mreq - > request ;
/* read packet and ack; or stall because of gadget driver bug */
if ( req ) {
void * buf = req - > buf + req - > actual ;
unsigned int len = req - > length - req - > actual ;
/* read the buffer */
count = mtu3_readl ( mbase , U3D_RXCOUNT0 ) ;
if ( count > len ) {
req - > status = - EOVERFLOW ;
count = len ;
}
ep0_read_fifo ( mtu - > ep0 , buf , count ) ;
req - > actual + = count ;
csr | = EP0_RXPKTRDY ;
maxp = mtu - > g . ep0 - > maxpacket ;
if ( count < maxp | | req - > actual = = req - > length ) {
mtu - > ep0_state = MU3D_EP0_STATE_SETUP ;
dev_dbg ( mtu - > dev , " ep0 state: %s \n " ,
decode_ep0_state ( mtu ) ) ;
csr | = EP0_DATAEND ;
} else {
req = NULL ;
}
} else {
csr | = EP0_RXPKTRDY | EP0_SENDSTALL ;
dev_dbg ( mtu - > dev , " %s: SENDSTALL \n " , __func__ ) ;
}
mtu3_writel ( mbase , U3D_EP0CSR , csr ) ;
/* give back the request if have received all data */
if ( req )
ep0_req_giveback ( mtu , req ) ;
}
/* transmitting to the host (IN) */
static void ep0_tx_state ( struct mtu3 * mtu )
{
struct mtu3_request * mreq = next_ep0_request ( mtu ) ;
struct usb_request * req ;
u32 csr ;
u8 * src ;
u8 count ;
u32 maxp ;
dev_dbg ( mtu - > dev , " %s \n " , __func__ ) ;
if ( ! mreq )
return ;
maxp = mtu - > g . ep0 - > maxpacket ;
req = & mreq - > request ;
/* load the data */
src = ( u8 * ) req - > buf + req - > actual ;
count = min ( maxp , req - > length - req - > actual ) ;
if ( count )
ep0_write_fifo ( mtu - > ep0 , src , count ) ;
dev_dbg ( mtu - > dev , " %s act=%d, len=%d, cnt=%d, maxp=%d zero=%d \n " ,
__func__ , req - > actual , req - > length , count , maxp , req - > zero ) ;
req - > actual + = count ;
if ( ( count < maxp )
| | ( ( req - > actual = = req - > length ) & & ! req - > zero ) )
mtu - > ep0_state = MU3D_EP0_STATE_TX_END ;
/* send it out, triggering a "txpktrdy cleared" irq */
csr = mtu3_readl ( mtu - > mac_base , U3D_EP0CSR ) & EP0_W1C_BITS ;
mtu3_writel ( mtu - > mac_base , U3D_EP0CSR , csr | EP0_TXPKTRDY ) ;
dev_dbg ( mtu - > dev , " %s ep0csr=0x%x \n " , __func__ ,
mtu3_readl ( mtu - > mac_base , U3D_EP0CSR ) ) ;
}
static void ep0_read_setup ( struct mtu3 * mtu , struct usb_ctrlrequest * setup )
{
struct mtu3_request * mreq ;
u32 count ;
u32 csr ;
csr = mtu3_readl ( mtu - > mac_base , U3D_EP0CSR ) & EP0_W1C_BITS ;
count = mtu3_readl ( mtu - > mac_base , U3D_RXCOUNT0 ) ;
ep0_read_fifo ( mtu - > ep0 , ( u8 * ) setup , count ) ;
dev_dbg ( mtu - > dev , " SETUP req%02x.%02x v%04x i%04x l%04x \n " ,
setup - > bRequestType , setup - > bRequest ,
le16_to_cpu ( setup - > wValue ) , le16_to_cpu ( setup - > wIndex ) ,
le16_to_cpu ( setup - > wLength ) ) ;
/* clean up any leftover transfers */
mreq = next_ep0_request ( mtu ) ;
if ( mreq )
ep0_req_giveback ( mtu , & mreq - > request ) ;
if ( le16_to_cpu ( setup - > wLength ) = = 0 ) {
; /* no data stage, nothing to do */
} else if ( setup - > bRequestType & USB_DIR_IN ) {
mtu3_writel ( mtu - > mac_base , U3D_EP0CSR ,
csr | EP0_SETUPPKTRDY | EP0_DPHTX ) ;
mtu - > ep0_state = MU3D_EP0_STATE_TX ;
} else {
mtu3_writel ( mtu - > mac_base , U3D_EP0CSR ,
( csr | EP0_SETUPPKTRDY ) & ( ~ EP0_DPHTX ) ) ;
mtu - > ep0_state = MU3D_EP0_STATE_RX ;
}
}
static int ep0_handle_setup ( struct mtu3 * mtu )
__releases ( mtu - > lock )
__acquires ( mtu - > lock )
{
struct usb_ctrlrequest setup ;
struct mtu3_request * mreq ;
void __iomem * mbase = mtu - > mac_base ;
int handled = 0 ;
ep0_read_setup ( mtu , & setup ) ;
if ( ( setup . bRequestType & USB_TYPE_MASK ) = = USB_TYPE_STANDARD )
handled = handle_standard_request ( mtu , & setup ) ;
dev_dbg ( mtu - > dev , " handled %d, ep0_state: %s \n " ,
handled , decode_ep0_state ( mtu ) ) ;
if ( handled < 0 )
goto stall ;
else if ( handled > 0 )
goto finish ;
handled = forward_to_driver ( mtu , & setup ) ;
if ( handled < 0 ) {
stall :
dev_dbg ( mtu - > dev , " %s stall (%d) \n " , __func__ , handled ) ;
ep0_stall_set ( mtu - > ep0 , true ,
le16_to_cpu ( setup . wLength ) ? 0 : EP0_SETUPPKTRDY ) ;
return 0 ;
}
finish :
if ( mtu - > test_mode ) {
; /* nothing to do */
} else if ( le16_to_cpu ( setup . wLength ) = = 0 ) { /* no data stage */
mtu3_writel ( mbase , U3D_EP0CSR ,
( mtu3_readl ( mbase , U3D_EP0CSR ) & EP0_W1C_BITS )
| EP0_SETUPPKTRDY | EP0_DATAEND ) ;
/* complete zlp request directly */
mreq = next_ep0_request ( mtu ) ;
if ( mreq & & ! mreq - > request . length )
ep0_req_giveback ( mtu , & mreq - > request ) ;
}
return 0 ;
}
irqreturn_t mtu3_ep0_isr ( struct mtu3 * mtu )
{
void __iomem * mbase = mtu - > mac_base ;
struct mtu3_request * mreq ;
u32 int_status ;
irqreturn_t ret = IRQ_NONE ;
u32 csr ;
u32 len ;
int_status = mtu3_readl ( mbase , U3D_EPISR ) ;
int_status & = mtu3_readl ( mbase , U3D_EPIER ) ;
mtu3_writel ( mbase , U3D_EPISR , int_status ) ; /* W1C */
/* only handle ep0's */
if ( ! ( int_status & EP0ISR ) )
return IRQ_NONE ;
csr = mtu3_readl ( mbase , U3D_EP0CSR ) ;
dev_dbg ( mtu - > dev , " %s csr=0x%x \n " , __func__ , csr ) ;
/* we sent a stall.. need to clear it now.. */
if ( csr & EP0_SENTSTALL ) {
ep0_stall_set ( mtu - > ep0 , false , 0 ) ;
csr = mtu3_readl ( mbase , U3D_EP0CSR ) ;
ret = IRQ_HANDLED ;
}
dev_dbg ( mtu - > dev , " ep0_state: %s \n " , decode_ep0_state ( mtu ) ) ;
switch ( mtu - > ep0_state ) {
case MU3D_EP0_STATE_TX :
/* irq on clearing txpktrdy */
if ( ( csr & EP0_FIFOFULL ) = = 0 ) {
ep0_tx_state ( mtu ) ;
ret = IRQ_HANDLED ;
}
break ;
case MU3D_EP0_STATE_RX :
/* irq on set rxpktrdy */
if ( csr & EP0_RXPKTRDY ) {
ep0_rx_state ( mtu ) ;
ret = IRQ_HANDLED ;
}
break ;
case MU3D_EP0_STATE_TX_END :
mtu3_writel ( mbase , U3D_EP0CSR ,
( csr & EP0_W1C_BITS ) | EP0_DATAEND ) ;
mreq = next_ep0_request ( mtu ) ;
if ( mreq )
ep0_req_giveback ( mtu , & mreq - > request ) ;
mtu - > ep0_state = MU3D_EP0_STATE_SETUP ;
ret = IRQ_HANDLED ;
dev_dbg ( mtu - > dev , " ep0_state: %s \n " , decode_ep0_state ( mtu ) ) ;
break ;
case MU3D_EP0_STATE_SETUP :
if ( ! ( csr & EP0_SETUPPKTRDY ) )
break ;
len = mtu3_readl ( mbase , U3D_RXCOUNT0 ) ;
if ( len ! = 8 ) {
dev_err ( mtu - > dev , " SETUP packet len %d != 8 ? \n " , len ) ;
break ;
}
ep0_handle_setup ( mtu ) ;
ret = IRQ_HANDLED ;
break ;
default :
/* can't happen */
ep0_stall_set ( mtu - > ep0 , true , 0 ) ;
WARN_ON ( 1 ) ;
break ;
}
return ret ;
}
static int mtu3_ep0_enable ( struct usb_ep * ep ,
const struct usb_endpoint_descriptor * desc )
{
/* always enabled */
return - EINVAL ;
}
static int mtu3_ep0_disable ( struct usb_ep * ep )
{
/* always enabled */
return - EINVAL ;
}
static int ep0_queue ( struct mtu3_ep * mep , struct mtu3_request * mreq )
{
struct mtu3 * mtu = mep - > mtu ;
mreq - > mtu = mtu ;
mreq - > request . actual = 0 ;
mreq - > request . status = - EINPROGRESS ;
dev_dbg ( mtu - > dev , " %s %s (ep0_state: %s), len#%d \n " , __func__ ,
mep - > name , decode_ep0_state ( mtu ) , mreq - > request . length ) ;
if ( ! list_empty ( & mep - > req_list ) )
return - EBUSY ;
switch ( mtu - > ep0_state ) {
case MU3D_EP0_STATE_SETUP :
case MU3D_EP0_STATE_RX : /* control-OUT data */
case MU3D_EP0_STATE_TX : /* control-IN data */
break ;
default :
dev_err ( mtu - > dev , " %s, error in ep0 state %s \n " , __func__ ,
decode_ep0_state ( mtu ) ) ;
return - EINVAL ;
}
list_add_tail ( & mreq - > list , & mep - > req_list ) ;
/* sequence #1, IN ... start writing the data */
if ( mtu - > ep0_state = = MU3D_EP0_STATE_TX )
ep0_tx_state ( mtu ) ;
return 0 ;
}
static int mtu3_ep0_queue ( struct usb_ep * ep ,
struct usb_request * req , gfp_t gfp )
{
struct mtu3_ep * mep ;
struct mtu3_request * mreq ;
struct mtu3 * mtu ;
unsigned long flags ;
int ret = 0 ;
if ( ! ep | | ! req )
return - EINVAL ;
mep = to_mtu3_ep ( ep ) ;
mtu = mep - > mtu ;
mreq = to_mtu3_request ( req ) ;
spin_lock_irqsave ( & mtu - > lock , flags ) ;
ret = ep0_queue ( mep , mreq ) ;
spin_unlock_irqrestore ( & mtu - > lock , flags ) ;
return ret ;
}
static int mtu3_ep0_dequeue ( struct usb_ep * ep , struct usb_request * req )
{
/* we just won't support this */
return - EINVAL ;
}
static int mtu3_ep0_halt ( struct usb_ep * ep , int value )
{
struct mtu3_ep * mep ;
struct mtu3 * mtu ;
unsigned long flags ;
int ret = 0 ;
if ( ! ep | | ! value )
return - EINVAL ;
mep = to_mtu3_ep ( ep ) ;
mtu = mep - > mtu ;
dev_dbg ( mtu - > dev , " %s \n " , __func__ ) ;
spin_lock_irqsave ( & mtu - > lock , flags ) ;
if ( ! list_empty ( & mep - > req_list ) ) {
ret = - EBUSY ;
goto cleanup ;
}
switch ( mtu - > ep0_state ) {
/*
* stalls are usually issued after parsing SETUP packet , either
* directly in irq context from setup ( ) or else later .
*/
case MU3D_EP0_STATE_TX :
case MU3D_EP0_STATE_TX_END :
case MU3D_EP0_STATE_RX :
case MU3D_EP0_STATE_SETUP :
ep0_stall_set ( mtu - > ep0 , true , 0 ) ;
break ;
default :
dev_dbg ( mtu - > dev , " ep0 can't halt in state %s \n " ,
decode_ep0_state ( mtu ) ) ;
ret = - EINVAL ;
}
cleanup :
spin_unlock_irqrestore ( & mtu - > lock , flags ) ;
return ret ;
}
const struct usb_ep_ops mtu3_ep0_ops = {
. enable = mtu3_ep0_enable ,
. disable = mtu3_ep0_disable ,
. alloc_request = mtu3_alloc_request ,
. free_request = mtu3_free_request ,
. queue = mtu3_ep0_queue ,
. dequeue = mtu3_ep0_dequeue ,
. set_halt = mtu3_ep0_halt ,
} ;