Commit ae57e97a authored by Li Jun's avatar Li Jun Committed by Felipe Balbi

usb: common: otg-fsm: add HNP polling support

Adds HNP polling timer when transits to host state, the OTG status
request will be sent to peripheral after timeout, if host request flag
is set, it will switch to peripheral state, otherwise it will repeat HNP
polling every 1.5s and maintain the current session.
Acked-by: default avatarPeter Chen <peter.chen@nxp.com>
Signed-off-by: default avatarLi Jun <jun.li@nxp.com>
Signed-off-by: default avatarFelipe Balbi <balbi@kernel.org>
parent 346dbc69
...@@ -78,6 +78,8 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) ...@@ -78,6 +78,8 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
fsm->b_srp_done = 0; fsm->b_srp_done = 0;
break; break;
case OTG_STATE_B_PERIPHERAL: case OTG_STATE_B_PERIPHERAL:
if (fsm->otg->gadget)
fsm->otg->gadget->host_request_flag = 0;
break; break;
case OTG_STATE_B_WAIT_ACON: case OTG_STATE_B_WAIT_ACON:
otg_del_timer(fsm, B_ASE0_BRST); otg_del_timer(fsm, B_ASE0_BRST);
...@@ -107,6 +109,8 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) ...@@ -107,6 +109,8 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
case OTG_STATE_A_PERIPHERAL: case OTG_STATE_A_PERIPHERAL:
otg_del_timer(fsm, A_BIDL_ADIS); otg_del_timer(fsm, A_BIDL_ADIS);
fsm->a_bidl_adis_tmout = 0; fsm->a_bidl_adis_tmout = 0;
if (fsm->otg->gadget)
fsm->otg->gadget->host_request_flag = 0;
break; break;
case OTG_STATE_A_WAIT_VFALL: case OTG_STATE_A_WAIT_VFALL:
otg_del_timer(fsm, A_WAIT_VFALL); otg_del_timer(fsm, A_WAIT_VFALL);
...@@ -120,6 +124,87 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) ...@@ -120,6 +124,87 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
} }
} }
static void otg_hnp_polling_work(struct work_struct *work)
{
struct otg_fsm *fsm = container_of(to_delayed_work(work),
struct otg_fsm, hnp_polling_work);
struct usb_device *udev;
enum usb_otg_state state = fsm->otg->state;
u8 flag;
int retval;
if (state != OTG_STATE_A_HOST && state != OTG_STATE_B_HOST)
return;
udev = usb_hub_find_child(fsm->otg->host->root_hub, 1);
if (!udev) {
dev_err(fsm->otg->host->controller,
"no usb dev connected, can't start HNP polling\n");
return;
}
*fsm->host_req_flag = 0;
/* Get host request flag from connected USB device */
retval = usb_control_msg(udev,
usb_rcvctrlpipe(udev, 0),
USB_REQ_GET_STATUS,
USB_DIR_IN | USB_RECIP_DEVICE,
0,
OTG_STS_SELECTOR,
fsm->host_req_flag,
1,
USB_CTRL_GET_TIMEOUT);
if (retval != 1) {
dev_err(&udev->dev, "Get one byte OTG status failed\n");
return;
}
flag = *fsm->host_req_flag;
if (flag == 0) {
/* Continue HNP polling */
schedule_delayed_work(&fsm->hnp_polling_work,
msecs_to_jiffies(T_HOST_REQ_POLL));
return;
} else if (flag != HOST_REQUEST_FLAG) {
dev_err(&udev->dev, "host request flag %d is invalid\n", flag);
return;
}
/* Host request flag is set */
if (state == OTG_STATE_A_HOST) {
/* Set b_hnp_enable */
if (!fsm->otg->host->b_hnp_enable) {
retval = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE, 0,
USB_DEVICE_B_HNP_ENABLE,
0, NULL, 0,
USB_CTRL_SET_TIMEOUT);
if (retval >= 0)
fsm->otg->host->b_hnp_enable = 1;
}
fsm->a_bus_req = 0;
} else if (state == OTG_STATE_B_HOST) {
fsm->b_bus_req = 0;
}
otg_statemachine(fsm);
}
static void otg_start_hnp_polling(struct otg_fsm *fsm)
{
/*
* The memory of host_req_flag should be allocated by
* controller driver, otherwise, hnp polling is not started.
*/
if (!fsm->host_req_flag)
return;
INIT_DELAYED_WORK(&fsm->hnp_polling_work, otg_hnp_polling_work);
schedule_delayed_work(&fsm->hnp_polling_work,
msecs_to_jiffies(T_HOST_REQ_POLL));
}
/* Called when entering a state */ /* Called when entering a state */
static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
{ {
...@@ -169,6 +254,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) ...@@ -169,6 +254,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
otg_set_protocol(fsm, PROTO_HOST); otg_set_protocol(fsm, PROTO_HOST);
usb_bus_start_enum(fsm->otg->host, usb_bus_start_enum(fsm->otg->host,
fsm->otg->host->otg_port); fsm->otg->host->otg_port);
otg_start_hnp_polling(fsm);
break; break;
case OTG_STATE_A_IDLE: case OTG_STATE_A_IDLE:
otg_drv_vbus(fsm, 0); otg_drv_vbus(fsm, 0);
...@@ -203,6 +289,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) ...@@ -203,6 +289,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
*/ */
if (!fsm->a_bus_req || fsm->a_suspend_req_inf) if (!fsm->a_bus_req || fsm->a_suspend_req_inf)
otg_add_timer(fsm, A_WAIT_ENUM); otg_add_timer(fsm, A_WAIT_ENUM);
otg_start_hnp_polling(fsm);
break; break;
case OTG_STATE_A_SUSPEND: case OTG_STATE_A_SUSPEND:
otg_drv_vbus(fsm, 1); otg_drv_vbus(fsm, 1);
......
...@@ -40,6 +40,18 @@ ...@@ -40,6 +40,18 @@
#define PROTO_HOST (1) #define PROTO_HOST (1)
#define PROTO_GADGET (2) #define PROTO_GADGET (2)
#define OTG_STS_SELECTOR 0xF000 /* OTG status selector, according to
* OTG and EH 2.0 Chapter 6.2.3
* Table:6-4
*/
#define HOST_REQUEST_FLAG 1 /* Host request flag, according to
* OTG and EH 2.0 Charpter 6.2.3
* Table:6-5
*/
#define T_HOST_REQ_POLL (1500) /* 1500ms, HNP polling interval */
enum otg_fsm_timer { enum otg_fsm_timer {
/* Standard OTG timers */ /* Standard OTG timers */
A_WAIT_VRISE, A_WAIT_VRISE,
...@@ -119,6 +131,8 @@ struct otg_fsm { ...@@ -119,6 +131,8 @@ struct otg_fsm {
/* Current usb protocol used: 0:undefine; 1:host; 2:client */ /* Current usb protocol used: 0:undefine; 1:host; 2:client */
int protocol; int protocol;
struct mutex lock; struct mutex lock;
u8 *host_req_flag;
struct delayed_work hnp_polling_work;
}; };
struct otg_fsm_ops { struct otg_fsm_ops {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment