On Wed, Mar 26, 2014 at 01:16:40PM +0800, Peter Chen wrote:
> On Wed, Mar 19, 2014 at 06:01:40PM +0800, Li Jun wrote:
> > From: Li Jun <b47...@freescale.com>
> > 
> > USB OTG interrupt handling and fsm transitions according to USB OTG
> > and EH 2.0.
> > 
> > Signed-off-by: Li Jun <b47...@freescale.com>
> > ---
> >  drivers/usb/chipidea/core.c    |   10 +-
> >  drivers/usb/chipidea/otg.c     |    9 +-
> >  drivers/usb/chipidea/otg_fsm.c |  235 
> > +++++++++++++++++++++++++++++++++++++++-
> >  drivers/usb/chipidea/otg_fsm.h |   18 +++
> >  drivers/usb/chipidea/udc.c     |    6 +
> >  5 files changed, 270 insertions(+), 8 deletions(-)
> > 
> > diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
> > index ff38cf3..c0eacf4 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[] = {
> > @@ -412,8 +413,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_otgsc(ci, ~0);
> > +           ret = ci_otg_fsm_irq(ci);
> > +           if (ret == IRQ_HANDLED)
> > +                   return ret;
> > +   }
> >  
> >     /*
> >      * Handle id change interrupt, it indicates device/host function
> > @@ -697,6 +702,9 @@ static int ci_hdrc_probe(struct platform_device *pdev)
> >             goto stop;
> >     }
> >  
> > +   if (ci->is_otg)
> > +           ci_hdrc_otg_fsm_start(ci);
> > +
> >     platform_set_drvdata(pdev, ci);
> >     ret = request_irq(ci->irq, ci_irq, IRQF_SHARED, ci->platdata->name,
> >                       ci);
> > diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
> > index 3082b52..5098982 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>
> > @@ -91,6 +91,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 1fcb2b0..569f208 100644
> > --- a/drivers/usb/chipidea/otg_fsm.c
> > +++ b/drivers/usb/chipidea/otg_fsm.c
> > @@ -92,6 +92,33 @@ static void ci_otg_del_timer(struct ci_hdrc *ci, enum 
> > ci_otg_fsm_timer_index t)
> >             hw_write_otgsc(ci, OTGSC_1MSIE, 0);
> >  }
> >  
> > +/*
> > + * Reduce timer count by 1, and find timeout conditions.
> > + * Called by otg 1ms timer interrupt
> > + */
> > +static inline int ci_otg_tick_timer(struct ci_hdrc *ci)
> > +{
> > +   struct ci_otg_fsm_timer *tmp_timer, *del_tmp;
> > +   struct list_head *active_timers = &ci->fsm_timer->active_timers;
> > +   int expired = 0;
> > +
> > +   list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) {
> > +           tmp_timer->count--;
> > +           /* check if timer expires */
> > +           if (!tmp_timer->count) {
> > +                   list_del(&tmp_timer->list);
> > +                   tmp_timer->function(ci, tmp_timer->data);
> > +                   expired = 1;
> > +           }
> > +   }
> > +
> > +   /* disable 1ms irq if there is no any timer active */
> > +   if ((expired == 1) && list_empty(active_timers))
> > +           hw_write_otgsc(ci, OTGSC_1MSIE, 0);
> > +
> > +   return expired;
> > +}
> > +
> >  /* The timeout callback function to set time out bit */
> >  static void set_tmout(void *ptr, unsigned long indicator)
> >  {
> > @@ -297,11 +324,6 @@ static void ci_otg_drv_vbus(struct otg_fsm *fsm, int 
> > on)
> >                     regulator_disable(ci->platdata->reg_vbus);
> >             /* Disable port power */
> >             hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, 0);
> > -
> > -           /* Clear exsiting DP irq */
> > -           hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
> > -           /* Enable data pulse irq */
> > -           hw_write_otgsc(ci, OTGSC_DPIE, OTGSC_DPIE);
> 
> Why the above changes are at this patch?
> 

Sorry, my mistake in patch split, I will correct this.

> >     }
> >  }
> >  
> > @@ -397,6 +419,209 @@ 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)
> > +           return -ENODEV;
> > +
> > +   /*
> > +    * Don't do fsm transition for B device
> > +    * when there is no gadget class driver
> > +    */
> > +   if (ci->fsm.id && !(ci->driver) &&
> > +           ci->transceiver->state < OTG_STATE_A_IDLE)
> > +           return 0;
> > +
> > +   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;
> > +}
> > +
> > +/*
> > + * Update fsm variables in each state if catching expected interrupts,
> > + * called by otg fsm isr.
> > + */
> > +static void ci_otg_fsm_event(struct ci_hdrc *ci)
> > +{
> > +   u32 intr_sts, otg_bsess_vld, port_conn;
> > +   struct otg_fsm *fsm = &ci->fsm;
> > +
> > +   intr_sts = hw_read_intr_status(ci);
> > +   otg_bsess_vld = hw_read_otgsc(ci, 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;
> > +                   if (fsm->power_up)
> > +                           fsm->power_up = 0;
> > +                   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
> > +                    */
> > +                    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 - otg fsm related irq handling
> > + * and also update otg fsm variable by monitoring usb host and udc
> > + * state change interrupts.
> > + * @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->is_otg) || (ci->platdata->dr_mode != USB_DR_MODE_OTG))
> > +           return retval;
> > +
> > +   otgsc = hw_read_otgsc(ci, ~0);
> > +   otg_int_src = otgsc & OTGSC_INT_STATUS_BITS & (otgsc >> 8);
> > +   fsm->id = (otgsc & OTGSC_ID) ? 1 : 0;
> > +
> > +   if (otg_int_src) {
> > +           if (otg_int_src & OTGSC_1MSIS) {
> > +                   hw_write_otgsc(ci, OTGSC_1MSIS, OTGSC_1MSIS);
> > +                   retval = ci_otg_tick_timer(ci);
> > +                   return IRQ_HANDLED;
> > +           } else if (otg_int_src & OTGSC_DPIS) {
> > +                   hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
> > +                   fsm->a_srp_det = 1;
> > +                   fsm->a_bus_drop = 0;
> > +           } else if (otg_int_src & OTGSC_IDIS) {
> > +                   hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
> > +                   if (fsm->id == 0) {
> > +                           fsm->a_bus_req = 1;
> > +                           ci->id_event = true;
> > +                   }
> > +           } else if (otg_int_src & OTGSC_BSVIS) {
> > +                   hw_write_otgsc(ci, OTGSC_BSVIS, 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) {
> > +                   hw_write_otgsc(ci, OTGSC_AVVIS, 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);
> > +
> > +   return retval;
> > +}
> > +
> > +void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
> > +{
> > +   if (ci->platdata->dr_mode == USB_DR_MODE_OTG)
> > +           ci_otg_fsm_work(ci);
> > +}
> > +
> 
> We may need an inline function to indicates OTG support at runtime,
> do you think so? You may consider the API I suggested at patch 2/11.
> 

I will replace with your suggested check way.

> >  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 8e2d16c..8ce7356 100644
> > --- a/drivers/usb/chipidea/otg_fsm.h
> > +++ b/drivers/usb/chipidea/otg_fsm.h
> > @@ -85,6 +85,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
> >  
> > @@ -93,6 +96,21 @@ static 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 void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
> > +{
> > +
> > +}
> > +
> >  #endif
> >  
> >  #endif /* __DRIVERS_USB_CHIPIDEA_OTG_FSM_H */
> > diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
> > index 022dd18..a149f08 100644
> > --- a/drivers/usb/chipidea/udc.c
> > +++ b/drivers/usb/chipidea/udc.c
> > @@ -28,6 +28,7 @@
> >  #include "bits.h"
> >  #include "debug.h"
> >  #include "otg.h"
> > +#include "otg_fsm.h"
> >  
> >  /* control endpoint description */
> >  static const struct usb_endpoint_descriptor
> > @@ -1645,6 +1646,11 @@ static int ci_udc_start(struct usb_gadget *gadget,
> >  
> >     ci->driver = driver;
> >     pm_runtime_get_sync(&ci->gadget.dev);
> > +
> > +   /* Start otg fsm for B-device */
> > +   if (ci->fsm.id == 1)
> > +           ci_hdrc_otg_fsm_start(ci);
> > +
> >     if (ci->vbus_active) {
> >             spin_lock_irqsave(&ci->lock, flags);
> >             hw_device_reset(ci, USBMODE_CM_DC);
> > -- 
> > 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to