2020-12-07 11:32:24 +01:00
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence CDNSP DRD Driver .
*
* Copyright ( C ) 2020 Cadence .
*
* Author : Pawel Laszczak < pawell @ cadence . com >
*
*/
# include <linux/usb/composite.h>
# include <linux/usb/gadget.h>
# include <linux/list.h>
# include "cdnsp-gadget.h"
2020-12-07 11:32:25 +01:00
# include "cdnsp-trace.h"
2020-12-07 11:32:24 +01:00
static void cdnsp_ep0_stall ( struct cdnsp_device * pdev )
{
struct cdnsp_request * preq ;
struct cdnsp_ep * pep ;
pep = & pdev - > eps [ 0 ] ;
preq = next_request ( & pep - > pending_list ) ;
if ( pdev - > three_stage_setup ) {
cdnsp_halt_endpoint ( pdev , pep , true ) ;
if ( preq )
cdnsp_gadget_giveback ( pep , preq , - ECONNRESET ) ;
} else {
pep - > ep_state | = EP0_HALTED_STATUS ;
if ( preq )
list_del ( & preq - > list ) ;
cdnsp_status_stage ( pdev ) ;
}
}
static int cdnsp_ep0_delegate_req ( struct cdnsp_device * pdev ,
struct usb_ctrlrequest * ctrl )
{
int ret ;
spin_unlock ( & pdev - > lock ) ;
ret = pdev - > gadget_driver - > setup ( & pdev - > gadget , ctrl ) ;
spin_lock ( & pdev - > lock ) ;
return ret ;
}
static int cdnsp_ep0_set_config ( struct cdnsp_device * pdev ,
struct usb_ctrlrequest * ctrl )
{
enum usb_device_state state = pdev - > gadget . state ;
u32 cfg ;
int ret ;
cfg = le16_to_cpu ( ctrl - > wValue ) ;
switch ( state ) {
case USB_STATE_ADDRESS :
2020-12-07 11:32:25 +01:00
trace_cdnsp_ep0_set_config ( " from Address state " ) ;
2020-12-07 11:32:24 +01:00
break ;
case USB_STATE_CONFIGURED :
2020-12-07 11:32:25 +01:00
trace_cdnsp_ep0_set_config ( " from Configured state " ) ;
2020-12-07 11:32:24 +01:00
break ;
default :
dev_err ( pdev - > dev , " Set Configuration - bad device state \n " ) ;
return - EINVAL ;
}
ret = cdnsp_ep0_delegate_req ( pdev , ctrl ) ;
if ( ret )
return ret ;
if ( ! cfg )
usb_gadget_set_state ( & pdev - > gadget , USB_STATE_ADDRESS ) ;
return 0 ;
}
static int cdnsp_ep0_set_address ( struct cdnsp_device * pdev ,
struct usb_ctrlrequest * ctrl )
{
enum usb_device_state state = pdev - > gadget . state ;
struct cdnsp_slot_ctx * slot_ctx ;
unsigned int slot_state ;
int ret ;
u32 addr ;
addr = le16_to_cpu ( ctrl - > wValue ) ;
if ( addr > 127 ) {
dev_err ( pdev - > dev , " Invalid device address %d \n " , addr ) ;
return - EINVAL ;
}
slot_ctx = cdnsp_get_slot_ctx ( & pdev - > out_ctx ) ;
if ( state = = USB_STATE_CONFIGURED ) {
dev_err ( pdev - > dev , " Can't Set Address from Configured State \n " ) ;
return - EINVAL ;
}
pdev - > device_address = le16_to_cpu ( ctrl - > wValue ) ;
slot_ctx = cdnsp_get_slot_ctx ( & pdev - > out_ctx ) ;
slot_state = GET_SLOT_STATE ( le32_to_cpu ( slot_ctx - > dev_state ) ) ;
if ( slot_state = = SLOT_STATE_ADDRESSED )
cdnsp_reset_device ( pdev ) ;
/*set device address*/
ret = cdnsp_setup_device ( pdev , SETUP_CONTEXT_ADDRESS ) ;
if ( ret )
return ret ;
if ( addr )
usb_gadget_set_state ( & pdev - > gadget , USB_STATE_ADDRESS ) ;
else
usb_gadget_set_state ( & pdev - > gadget , USB_STATE_DEFAULT ) ;
return 0 ;
}
int cdnsp_status_stage ( struct cdnsp_device * pdev )
{
pdev - > ep0_stage = CDNSP_STATUS_STAGE ;
pdev - > ep0_preq . request . length = 0 ;
return cdnsp_ep_enqueue ( pdev - > ep0_preq . pep , & pdev - > ep0_preq ) ;
}
2020-12-14 13:03:44 +01:00
static int cdnsp_w_index_to_ep_index ( u16 wIndex )
2020-12-07 11:32:24 +01:00
{
if ( ! ( wIndex & USB_ENDPOINT_NUMBER_MASK ) )
return 0 ;
return ( ( wIndex & USB_ENDPOINT_NUMBER_MASK ) * 2 ) +
( wIndex & USB_ENDPOINT_DIR_MASK ? 1 : 0 ) - 1 ;
}
static int cdnsp_ep0_handle_status ( struct cdnsp_device * pdev ,
struct usb_ctrlrequest * ctrl )
{
struct cdnsp_ep * pep ;
__le16 * response ;
int ep_sts = 0 ;
u16 status = 0 ;
u32 recipient ;
recipient = ctrl - > bRequestType & USB_RECIP_MASK ;
switch ( recipient ) {
case USB_RECIP_DEVICE :
status = pdev - > gadget . is_selfpowered ;
status | = pdev - > may_wakeup < < USB_DEVICE_REMOTE_WAKEUP ;
if ( pdev - > gadget . speed > = USB_SPEED_SUPER ) {
status | = pdev - > u1_allowed < < USB_DEV_STAT_U1_ENABLED ;
status | = pdev - > u2_allowed < < USB_DEV_STAT_U2_ENABLED ;
}
break ;
case USB_RECIP_INTERFACE :
/*
* Function Remote Wake Capable D0
* Function Remote Wakeup D1
*/
return cdnsp_ep0_delegate_req ( pdev , ctrl ) ;
case USB_RECIP_ENDPOINT :
2020-12-14 13:03:44 +01:00
ep_sts = cdnsp_w_index_to_ep_index ( le16_to_cpu ( ctrl - > wIndex ) ) ;
pep = & pdev - > eps [ ep_sts ] ;
2020-12-07 11:32:24 +01:00
ep_sts = GET_EP_CTX_STATE ( pep - > out_ctx ) ;
/* check if endpoint is stalled */
if ( ep_sts = = EP_STATE_HALTED )
status = BIT ( USB_ENDPOINT_HALT ) ;
break ;
default :
return - EINVAL ;
}
response = ( __le16 * ) pdev - > setup_buf ;
* response = cpu_to_le16 ( status ) ;
pdev - > ep0_preq . request . length = sizeof ( * response ) ;
pdev - > ep0_preq . request . buf = pdev - > setup_buf ;
return cdnsp_ep_enqueue ( pdev - > ep0_preq . pep , & pdev - > ep0_preq ) ;
}
static void cdnsp_enter_test_mode ( struct cdnsp_device * pdev )
{
u32 temp ;
temp = readl ( & pdev - > active_port - > regs - > portpmsc ) & ~ GENMASK ( 31 , 28 ) ;
temp | = PORT_TEST_MODE ( pdev - > test_mode ) ;
writel ( temp , & pdev - > active_port - > regs - > portpmsc ) ;
}
static int cdnsp_ep0_handle_feature_device ( struct cdnsp_device * pdev ,
struct usb_ctrlrequest * ctrl ,
int set )
{
enum usb_device_state state ;
enum usb_device_speed speed ;
u16 tmode ;
state = pdev - > gadget . state ;
speed = pdev - > gadget . speed ;
switch ( le16_to_cpu ( ctrl - > wValue ) ) {
case USB_DEVICE_REMOTE_WAKEUP :
pdev - > may_wakeup = ! ! set ;
2020-12-07 11:32:25 +01:00
trace_cdnsp_may_wakeup ( set ) ;
2020-12-07 11:32:24 +01:00
break ;
case USB_DEVICE_U1_ENABLE :
if ( state ! = USB_STATE_CONFIGURED | | speed < USB_SPEED_SUPER )
return - EINVAL ;
pdev - > u1_allowed = ! ! set ;
2020-12-07 11:32:25 +01:00
trace_cdnsp_u1 ( set ) ;
2020-12-07 11:32:24 +01:00
break ;
case USB_DEVICE_U2_ENABLE :
if ( state ! = USB_STATE_CONFIGURED | | speed < USB_SPEED_SUPER )
return - EINVAL ;
pdev - > u2_allowed = ! ! set ;
2020-12-07 11:32:25 +01:00
trace_cdnsp_u2 ( set ) ;
2020-12-07 11:32:24 +01:00
break ;
case USB_DEVICE_LTM_ENABLE :
return - EINVAL ;
case USB_DEVICE_TEST_MODE :
if ( state ! = USB_STATE_CONFIGURED | | speed > USB_SPEED_HIGH )
return - EINVAL ;
tmode = le16_to_cpu ( ctrl - > wIndex ) ;
if ( ! set | | ( tmode & 0xff ) ! = 0 )
return - EINVAL ;
tmode = tmode > > 8 ;
if ( tmode > USB_TEST_FORCE_ENABLE | | tmode < USB_TEST_J )
return - EINVAL ;
pdev - > test_mode = tmode ;
/*
* Test mode must be set before Status Stage but controller
* will start testing sequence after Status Stage .
*/
cdnsp_enter_test_mode ( pdev ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int cdnsp_ep0_handle_feature_intf ( struct cdnsp_device * pdev ,
struct usb_ctrlrequest * ctrl ,
int set )
{
u16 wValue , wIndex ;
int ret ;
wValue = le16_to_cpu ( ctrl - > wValue ) ;
wIndex = le16_to_cpu ( ctrl - > wIndex ) ;
switch ( wValue ) {
case USB_INTRF_FUNC_SUSPEND :
ret = cdnsp_ep0_delegate_req ( pdev , ctrl ) ;
if ( ret )
return ret ;
/*
* Remote wakeup is enabled when any function within a device
* is enabled for function remote wakeup .
*/
if ( wIndex & USB_INTRF_FUNC_SUSPEND_RW )
pdev - > may_wakeup + + ;
else
if ( pdev - > may_wakeup > 0 )
pdev - > may_wakeup - - ;
return 0 ;
default :
return - EINVAL ;
}
return 0 ;
}
static int cdnsp_ep0_handle_feature_endpoint ( struct cdnsp_device * pdev ,
struct usb_ctrlrequest * ctrl ,
int set )
{
struct cdnsp_ep * pep ;
2020-12-14 13:03:44 +01:00
u16 wValue ;
2020-12-07 11:32:24 +01:00
wValue = le16_to_cpu ( ctrl - > wValue ) ;
2020-12-14 13:03:44 +01:00
pep = & pdev - > eps [ cdnsp_w_index_to_ep_index ( le16_to_cpu ( ctrl - > wIndex ) ) ] ;
2020-12-07 11:32:24 +01:00
switch ( wValue ) {
case USB_ENDPOINT_HALT :
if ( ! set & & ( pep - > ep_state & EP_WEDGE ) ) {
/* Resets Sequence Number */
cdnsp_halt_endpoint ( pdev , pep , 0 ) ;
cdnsp_halt_endpoint ( pdev , pep , 1 ) ;
break ;
}
return cdnsp_halt_endpoint ( pdev , pep , set ) ;
default :
dev_warn ( pdev - > dev , " WARN Incorrect wValue %04x \n " , wValue ) ;
return - EINVAL ;
}
return 0 ;
}
static int cdnsp_ep0_handle_feature ( struct cdnsp_device * pdev ,
struct usb_ctrlrequest * ctrl ,
int set )
{
switch ( ctrl - > bRequestType & USB_RECIP_MASK ) {
case USB_RECIP_DEVICE :
return cdnsp_ep0_handle_feature_device ( pdev , ctrl , set ) ;
case USB_RECIP_INTERFACE :
return cdnsp_ep0_handle_feature_intf ( pdev , ctrl , set ) ;
case USB_RECIP_ENDPOINT :
return cdnsp_ep0_handle_feature_endpoint ( pdev , ctrl , set ) ;
default :
return - EINVAL ;
}
}
static int cdnsp_ep0_set_sel ( struct cdnsp_device * pdev ,
struct usb_ctrlrequest * ctrl )
{
enum usb_device_state state = pdev - > gadget . state ;
u16 wLength ;
if ( state = = USB_STATE_DEFAULT )
return - EINVAL ;
wLength = le16_to_cpu ( ctrl - > wLength ) ;
if ( wLength ! = 6 ) {
dev_err ( pdev - > dev , " Set SEL should be 6 bytes, got %d \n " ,
wLength ) ;
return - EINVAL ;
}
/*
* To handle Set SEL we need to receive 6 bytes from Host . So let ' s
* queue a usb_request for 6 bytes .
*/
pdev - > ep0_preq . request . length = 6 ;
pdev - > ep0_preq . request . buf = pdev - > setup_buf ;
return cdnsp_ep_enqueue ( pdev - > ep0_preq . pep , & pdev - > ep0_preq ) ;
}
static int cdnsp_ep0_set_isoch_delay ( struct cdnsp_device * pdev ,
struct usb_ctrlrequest * ctrl )
{
if ( le16_to_cpu ( ctrl - > wIndex ) | | le16_to_cpu ( ctrl - > wLength ) )
return - EINVAL ;
pdev - > gadget . isoch_delay = le16_to_cpu ( ctrl - > wValue ) ;
return 0 ;
}
static int cdnsp_ep0_std_request ( struct cdnsp_device * pdev ,
struct usb_ctrlrequest * ctrl )
{
int ret ;
switch ( ctrl - > bRequest ) {
case USB_REQ_GET_STATUS :
ret = cdnsp_ep0_handle_status ( pdev , ctrl ) ;
break ;
case USB_REQ_CLEAR_FEATURE :
ret = cdnsp_ep0_handle_feature ( pdev , ctrl , 0 ) ;
break ;
case USB_REQ_SET_FEATURE :
ret = cdnsp_ep0_handle_feature ( pdev , ctrl , 1 ) ;
break ;
case USB_REQ_SET_ADDRESS :
ret = cdnsp_ep0_set_address ( pdev , ctrl ) ;
break ;
case USB_REQ_SET_CONFIGURATION :
ret = cdnsp_ep0_set_config ( pdev , ctrl ) ;
break ;
case USB_REQ_SET_SEL :
ret = cdnsp_ep0_set_sel ( pdev , ctrl ) ;
break ;
case USB_REQ_SET_ISOCH_DELAY :
ret = cdnsp_ep0_set_isoch_delay ( pdev , ctrl ) ;
break ;
case USB_REQ_SET_INTERFACE :
/*
* Add request into pending list to block sending status stage
* by libcomposite .
*/
list_add_tail ( & pdev - > ep0_preq . list ,
& pdev - > ep0_preq . pep - > pending_list ) ;
ret = cdnsp_ep0_delegate_req ( pdev , ctrl ) ;
if ( ret = = - EBUSY )
ret = 0 ;
list_del ( & pdev - > ep0_preq . list ) ;
break ;
default :
ret = cdnsp_ep0_delegate_req ( pdev , ctrl ) ;
break ;
}
return ret ;
}
void cdnsp_setup_analyze ( struct cdnsp_device * pdev )
{
struct usb_ctrlrequest * ctrl = & pdev - > setup ;
int ret = 0 ;
2020-12-14 13:03:44 +01:00
u16 len ;
2020-12-07 11:32:24 +01:00
2020-12-07 11:32:25 +01:00
trace_cdnsp_ctrl_req ( ctrl ) ;
2020-12-07 11:32:24 +01:00
if ( ! pdev - > gadget_driver )
goto out ;
if ( pdev - > gadget . state = = USB_STATE_NOTATTACHED ) {
dev_err ( pdev - > dev , " ERR: Setup detected in unattached state \n " ) ;
ret = - EINVAL ;
goto out ;
}
/* Restore the ep0 to Stopped/Running state. */
2020-12-07 11:32:25 +01:00
if ( pdev - > eps [ 0 ] . ep_state & EP_HALTED ) {
trace_cdnsp_ep0_halted ( " Restore to normal state " ) ;
2020-12-07 11:32:24 +01:00
cdnsp_halt_endpoint ( pdev , & pdev - > eps [ 0 ] , 0 ) ;
2020-12-07 11:32:25 +01:00
}
2020-12-07 11:32:24 +01:00
/*
* Finishing previous SETUP transfer by removing request from
* list and informing upper layer
*/
if ( ! list_empty ( & pdev - > eps [ 0 ] . pending_list ) ) {
struct cdnsp_request * req ;
2020-12-07 11:32:25 +01:00
trace_cdnsp_ep0_request ( " Remove previous " ) ;
2020-12-07 11:32:24 +01:00
req = next_request ( & pdev - > eps [ 0 ] . pending_list ) ;
cdnsp_ep_dequeue ( & pdev - > eps [ 0 ] , req ) ;
}
len = le16_to_cpu ( ctrl - > wLength ) ;
if ( ! len ) {
pdev - > three_stage_setup = false ;
pdev - > ep0_expect_in = false ;
} else {
pdev - > three_stage_setup = true ;
pdev - > ep0_expect_in = ! ! ( ctrl - > bRequestType & USB_DIR_IN ) ;
}
if ( ( ctrl - > bRequestType & USB_TYPE_MASK ) = = USB_TYPE_STANDARD )
ret = cdnsp_ep0_std_request ( pdev , ctrl ) ;
else
ret = cdnsp_ep0_delegate_req ( pdev , ctrl ) ;
if ( ! len )
pdev - > ep0_stage = CDNSP_STATUS_STAGE ;
2020-12-07 11:32:25 +01:00
if ( ret = = USB_GADGET_DELAYED_STATUS ) {
trace_cdnsp_ep0_status_stage ( " delayed " ) ;
2020-12-07 11:32:24 +01:00
return ;
2020-12-07 11:32:25 +01:00
}
2020-12-07 11:32:24 +01:00
out :
if ( ret < 0 )
cdnsp_ep0_stall ( pdev ) ;
else if ( pdev - > ep0_stage = = CDNSP_STATUS_STAGE )
cdnsp_status_stage ( pdev ) ;
}