On Thu, Mar 06, 2014 at 12:36:45PM +0800, Peter Chen wrote:
> On Thu, Feb 27, 2014 at 07:38:27AM +0800, Li Jun wrote:
> > USB OTG interrupt handling and fsm transition according to USB OTG
> > and EH 2.0, update otg timer timeout handlers.
> >
> > Signed-off-by: Li Jun <[email protected]>
> > ---
> > drivers/usb/chipidea/core.c | 10 ++-
> > drivers/usb/chipidea/otg.c | 9 +-
> > drivers/usb/chipidea/otg_fsm.c | 191
> > ++++++++++++++++++++++++++++++++++++++++
> > drivers/usb/chipidea/otg_fsm.h | 18 ++++
> > 4 files changed, 225 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
> > index b2e01a2..cbc46c8 100644
> > --- a/drivers/usb/chipidea/core.c
> > +++ b/drivers/usb/chipidea/core.c
> > @@ -74,6 +74,7 @@
> > #include "host.h"
> > #include "debug.h"
> > #include "otg.h"
> > +#include "otg_fsm.h"
> >
> > /* Controller register map */
> > static const u8 ci_regs_nolpm[] = {
> > @@ -379,8 +380,12 @@ static irqreturn_t ci_irq(int irq, void *data)
> > irqreturn_t ret = IRQ_NONE;
> > u32 otgsc = 0;
> >
> > - if (ci->is_otg)
> > + if (ci->is_otg) {
> > otgsc = hw_read(ci, OP_OTGSC, ~0);
> > + ret = ci_otg_fsm_irq(ci);
> > + if (ret == IRQ_HANDLED)
> > + return ret;
> > + }
> >
> > /*
> > * Handle id change interrupt, it indicates device/host function
> > @@ -668,6 +673,9 @@ static int ci_hdrc_probe(struct platform_device *pdev)
> > if (ret)
> > goto stop;
> >
> > + if (ci->is_otg)
> > + ci_hdrc_otg_fsm_start(ci);
> > +
> > ret = dbg_create_files(ci);
> > if (!ret)
> > return 0;
> > diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
> > index cbf0167..4fb33a2 100644
> > --- a/drivers/usb/chipidea/otg.c
> > +++ b/drivers/usb/chipidea/otg.c
> > @@ -11,8 +11,8 @@
> > */
> >
> > /*
> > - * This file mainly handles otgsc register, it may include OTG operation
> > - * in the future.
> > + * This file mainly handles otgsc register, OTG fsm operations for HNP and
> > SRP
> > + * are also included.
> > */
> >
> > #include <linux/usb/otg.h>
> > @@ -77,6 +77,11 @@ static void ci_otg_work(struct work_struct *work)
> > {
> > struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work);
> >
> > + if (!ci_otg_fsm_work(ci)) {
> > + enable_irq(ci->irq);
> > + return;
> > + }
> > +
> > if (ci->id_event) {
> > ci->id_event = false;
> > ci_handle_id_switch(ci);
> > diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
> > index f9e536b..0601058 100644
> > --- a/drivers/usb/chipidea/otg_fsm.c
> > +++ b/drivers/usb/chipidea/otg_fsm.c
> > @@ -427,6 +427,197 @@ static struct otg_fsm_ops ci_otg_ops = {
> > .start_gadget = ci_otg_start_gadget,
> > };
> >
> > +int ci_otg_fsm_work(struct ci_hdrc *ci)
> > +{
> > + if (!ci->transceiver->otg || !ci->fsm)
> > + return -ENODEV;
> > +
> > + if (otg_statemachine(ci->fsm)) {
> > + if (ci->transceiver->state == OTG_STATE_A_IDLE) {
> > + if (ci->fsm->id)
> > + /* A idle to B idle */
> > + otg_statemachine(ci->fsm);
> > + else if ((ci->id_event) || (ci->fsm->power_up)) {
> > + ci->id_event = false;
> > + /* A idle to A wait vrise */
> > + otg_statemachine(ci->fsm);
> > + ci->fsm->power_up = false;
> > + }
> > + }
> > + }
> > + return 0;
> > +}
> > +
> > +static void ci_otg_fsm_event(struct ci_hdrc *ci, struct otg_fsm *fsm)
> > +{
> > + u32 intr_sts, otg_bsess_vld, port_conn;
> > +
> > + if ((ci == NULL) || (fsm == NULL))
> > + return;
> > +
> > + intr_sts = hw_read_intr_status(ci);
> > + otg_bsess_vld = hw_read(ci, OP_OTGSC, OTGSC_BSV);
> > + port_conn = hw_read(ci, OP_PORTSC, PORTSC_CCS);
> > +
> > + switch (ci->transceiver->state) {
> > + case OTG_STATE_A_WAIT_BCON:
> > + if (port_conn) {
> > + fsm->b_conn = 1;
> > + fsm->a_bus_req = 1;
> > + disable_irq_nosync(ci->irq);
> > + queue_work(ci->wq, &ci->work);
> > + }
> > + break;
> > + case OTG_STATE_B_IDLE:
> > + if (otg_bsess_vld && (intr_sts & USBi_PCI) && port_conn) {
> > + fsm->b_sess_vld = 1;
> > + disable_irq_nosync(ci->irq);
> > + queue_work(ci->wq, &ci->work);
> > + }
> > + break;
> > + case OTG_STATE_B_PERIPHERAL:
> > + if ((intr_sts & USBi_SLI) && port_conn && otg_bsess_vld) {
> > + fsm->a_bus_suspend = 1;
> > + disable_irq_nosync(ci->irq);
> > + queue_work(ci->wq, &ci->work);
> > + } else if (intr_sts & USBi_PCI) {
> > + if (fsm->a_bus_suspend == 1)
> > + fsm->a_bus_suspend = 0;
> > + }
> > + break;
> > + case OTG_STATE_B_HOST:
> > + if ((intr_sts & USBi_PCI) && !port_conn) {
> > + fsm->a_conn = 0;
> > + fsm->b_bus_req = 0;
> > + disable_irq_nosync(ci->irq);
> > + queue_work(ci->wq, &ci->work);
> > + ci_otg_add_timer(ci, B_SESS_VLD);
> > + }
> > + break;
> > + case OTG_STATE_A_PERIPHERAL:
> > + if (intr_sts & USBi_SLI) {
> > + fsm->b_bus_suspend = 1;
> > + /* Init a timer to know how long this suspend
> > + * will contine, if time out, indicates B no longer
> > + * wants to be host role */
>
> Change comment style please.
>
I will change.
> > + ci_otg_add_timer(ci, A_BIDL_ADIS);
> > + }
> > +
> > + if (intr_sts & USBi_URI)
> > + ci_otg_del_timer(ci, A_BIDL_ADIS);
> > +
> > + if (intr_sts & USBi_PCI) {
> > + if (fsm->b_bus_suspend == 1) {
> > + ci_otg_del_timer(ci, A_BIDL_ADIS);
> > + fsm->b_bus_suspend = 0;
> > + }
> > + }
> > + break;
> > + case OTG_STATE_A_SUSPEND:
> > + if ((intr_sts & USBi_PCI) && !port_conn) {
> > + fsm->b_conn = 0;
> > +
> > + /* if gadget driver is binded */
> > + if (ci->driver) {
> > + /* A device to be peripheral mode */
> > + ci->gadget.is_a_peripheral = 1;
> > + }
> > + disable_irq_nosync(ci->irq);
> > + queue_work(ci->wq, &ci->work);
> > + }
> > + break;
> > + case OTG_STATE_A_HOST:
> > + if ((intr_sts & USBi_PCI) && !port_conn) {
> > + fsm->b_conn = 0;
> > + disable_irq_nosync(ci->irq);
> > + queue_work(ci->wq, &ci->work);
> > + }
> > + break;
> > + case OTG_STATE_B_WAIT_ACON:
> > + if ((intr_sts & USBi_PCI) && port_conn) {
> > + fsm->a_conn = 1;
> > + disable_irq_nosync(ci->irq);
> > + queue_work(ci->wq, &ci->work);
> > + }
> > + break;
> > + default:
> > + break;
> > + }
> > +}
> > +
> > +/*
> > + * ci_otg_irq - perform otg fsm related irq handling
> > + * @ci: ci_hdrc
> > + */
> > +irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
> > +{
> > + irqreturn_t retval = IRQ_NONE;
> > + u32 otgsc, otg_int_src = 0;
> > + struct otg_fsm *fsm = ci->fsm;
> > +
> > + if ((ci == NULL) || !(ci->is_otg) ||
> > + (ci->platdata->dr_mode != USB_DR_MODE_OTG))
> > + return retval;
>
> If ci is NULL, the code struct otg_fsm *fsm = ci->fsm will cause
> null pointer oops, so the this assumption is not necessary.
>
> Peter
>
I will change, thanks!
> > +
> > + otgsc = hw_read(ci, OP_OTGSC, ~0);
> > + otg_int_src = otgsc & OTGSC_INT_STATUS_BITS & (otgsc >> 8);
> > + fsm->id = (otgsc & OTGSC_ID) ? 1 : 0;
> > +
> > + /* process OTG interrupts: ID,A vbus vld,B sess Vld,1ms */
> > + if (otg_int_src) {
> > + if (otg_int_src & OTGSC_1MSIS) {
> > + ci_clear_otg_interrupt(ci, OTGSC_1MSIS);
> > + retval = ci_otg_tick_timer(ci);
> > + return IRQ_HANDLED;
> > + } else if (otg_int_src & OTGSC_DPIS) {
> > + ci_clear_otg_interrupt(ci, OTGSC_DPIS);
> > + fsm->a_srp_det = 1;
> > + fsm->a_bus_drop = 0;
> > + } else if (otg_int_src & OTGSC_IDIS) {
> > + ci_clear_otg_interrupt(ci, OTGSC_IDIS);
> > + if (fsm->id == 0) {
> > + fsm->a_bus_req = 1;
> > + ci->id_event = true;
> > + }
> > + } else if (otg_int_src & OTGSC_BSVIS) {
> > + ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
> > + if (otgsc & OTGSC_BSV) {
> > + fsm->b_sess_vld = 1;
> > + ci_otg_del_timer(ci, B_SSEND_SRP);
> > + ci_otg_del_timer(ci, B_SRP_FAIL);
> > + fsm->b_ssend_srp = 0;
> > + } else {
> > + fsm->b_sess_vld = 0;
> > + if (fsm->id)
> > + ci_otg_add_timer(ci, B_SSEND_SRP);
> > + }
> > + } else if (otg_int_src & OTGSC_AVVIS) {
> > + ci_clear_otg_interrupt(ci, OTGSC_AVVIS);
> > + if (otgsc & OTGSC_AVV) {
> > + fsm->a_vbus_vld = 1;
> > + } else {
> > + fsm->a_vbus_vld = 0;
> > + fsm->b_conn = 0;
> > + }
> > + }
> > + disable_irq_nosync(ci->irq);
> > + queue_work(ci->wq, &ci->work);
> > + return IRQ_HANDLED;
> > + }
> > +
> > + ci_otg_fsm_event(ci, fsm);
> > +
> > + return retval;
> > +}
> > +
> > +void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
> > +{
> > + if (ci->platdata->dr_mode == USB_DR_MODE_OTG) {
> > + disable_irq_nosync(ci->irq);
> > + queue_work(ci->wq, &ci->work);
> > + }
> > +}
> > +
> > int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
> > {
> > int retval = 0;
> > diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h
> > index 77b604b..2177a5e 100644
> > --- a/drivers/usb/chipidea/otg_fsm.h
> > +++ b/drivers/usb/chipidea/otg_fsm.h
> > @@ -104,6 +104,9 @@ struct ci_otg_fsm_timer_list {
> > #ifdef CONFIG_USB_OTG_FSM
> >
> > int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci);
> > +int ci_otg_fsm_work(struct ci_hdrc *ci);
> > +irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci);
> > +void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci);
> >
> > #else
> >
> > @@ -112,6 +115,21 @@ static inline int ci_hdrc_otg_fsm_init(struct ci_hdrc
> > *ci)
> > return 0;
> > }
> >
> > +static inline int ci_otg_fsm_work(struct ci_hdrc *ci)
> > +{
> > + return -ENXIO;
> > +}
> > +
> > +static inline irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
> > +{
> > + return IRQ_NONE;
> > +}
> > +
> > +static inline void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
> > +{
> > +
> > +}
> > +
> > #endif
> >
> > #endif /* __DRIVERS_USB_CHIPIDEA_OTG_FSM_H */
> > --
> > 1.7.9.5
> >
> >
>
> --
>
> Best Regards,
> Peter Chen
>
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html