Re: [PATCH v2 7/9] usb: chipidea: OTG HNP and SRP fsm implementation.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Feb 10, 2014 at 10:32:24AM +0800, Peter Chen wrote:
> On Mon, Jan 20, 2014 at 09:56:18AM +0800, Li Jun wrote:
> > USB OTG interrupt handling and fsm transition according to USB OTG
> > spec 2.0, update otg timer timeout handlers.
> 
> USB OTG and EH 2.0 
>

changed.

> > 
> > Signed-off-by: Li Jun <b47624@xxxxxxxxxxxxx>
> > ---
> >  drivers/usb/chipidea/bits.h    |    2 +
> >  drivers/usb/chipidea/core.c    |   10 ++-
> >  drivers/usb/chipidea/otg.c     |    9 ++-
> >  drivers/usb/chipidea/otg_fsm.c |  193 ++++++++++++++++++++++++++++++++++++++++
> >  drivers/usb/chipidea/otg_fsm.h |   18 ++++
> >  5 files changed, 229 insertions(+), 3 deletions(-)
> > 
> > diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h
> > index 88136d1..5a74f9d 100644
> > --- a/drivers/usb/chipidea/bits.h
> > +++ b/drivers/usb/chipidea/bits.h
> > @@ -33,6 +33,8 @@
> >  #define USBCMD_ATDTW          BIT(14)
> >  
> >  /* USBSTS & USBINTR */
> > +#define USBSTS_PCI            BIT(2)
> > +#define USBSTS_SLI            BIT(8)
> >  #define USBi_UI               BIT(0)
> >  #define USBi_UEI              BIT(1)
> >  #define USBi_PCI              BIT(2)
> > diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
> > index 33f22bc..6865451 100644
> > --- a/drivers/usb/chipidea/core.c
> > +++ b/drivers/usb/chipidea/core.c
> > @@ -73,6 +73,7 @@
> >  #include "host.h"
> >  #include "debug.h"
> >  #include "otg.h"
> > +#include "otg_fsm.h"
> >  
> >  /* Controller register map */
> >  static const u8 ci_regs_nolpm[] = {
> > @@ -351,8 +352,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
> > @@ -656,6 +661,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 5416fe3..e713b76 100644
> > --- a/drivers/usb/chipidea/otg_fsm.c
> > +++ b/drivers/usb/chipidea/otg_fsm.c
> > @@ -433,6 +433,199 @@ 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)
> > +{
> > +	if ((ci == NULL) || (fsm == NULL))
> > +		return;
> > +
> > +	switch (ci->transceiver->state) {
> > +	case OTG_STATE_A_WAIT_BCON:
> > +		if (hw_read(ci, OP_PORTSC, PORTSC_CCS)) {
> > +			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 (hw_read(ci, OP_OTGSC, OTGSC_BSV) &&
> > +			hw_read(ci, OP_USBSTS, USBSTS_PCI) &&
> > +			hw_read(ci, OP_PORTSC, PORTSC_CCS)) {
> > +			fsm->b_sess_vld = 1;
> > +			disable_irq_nosync(ci->irq);
> > +			queue_work(ci->wq, &ci->work);
> > +		}
> > +		break;
> > +	case OTG_STATE_B_PERIPHERAL:
> > +		if (hw_read(ci, OP_USBSTS, USBSTS_SLI) &&
> > +			hw_read(ci, OP_PORTSC, PORTSC_CCS) &&
> > +			hw_read(ci, OP_OTGSC, OTGSC_BSV)) {
> > +			fsm->a_bus_suspend = 1;
> > +			disable_irq_nosync(ci->irq);
> > +			queue_work(ci->wq, &ci->work);
> > +		} else if (hw_read(ci, OP_USBSTS, USBSTS_PCI)) {
> > +			if (fsm->a_bus_suspend == 1)
> > +				fsm->a_bus_suspend = 0;
> > +		}
> > +		break;
> > +	case OTG_STATE_B_HOST:
> > +		if (hw_read(ci, OP_USBSTS, USBSTS_PCI) &&
> > +			!(hw_read(ci, OP_PORTSC, PORTSC_CCS))) {
> > +			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 (hw_read(ci, OP_USBSTS, USBSTS_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 (hw_read(ci, OP_USBSTS, USBi_URI))
> > +			ci_otg_del_timer(ci, A_BIDL_ADIS);
> > +
> > +		if (hw_read(ci, OP_USBSTS, USBSTS_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 (hw_read(ci, OP_USBSTS, USBSTS_PCI) &&
> > +			!(hw_read(ci, OP_PORTSC, PORTSC_CCS))) {
> > +			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 (hw_read(ci, OP_USBSTS, USBSTS_PCI) &&
> > +			!(hw_read(ci, OP_PORTSC, PORTSC_CCS))) {
> > +			fsm->b_conn = 0;
> > +			disable_irq_nosync(ci->irq);
> > +			queue_work(ci->wq, &ci->work);
> > +		}
> > +		break;
> > +	case OTG_STATE_B_WAIT_ACON:
> > +		if (hw_read(ci, OP_USBSTS, USBSTS_PCI) &&
> > +			hw_read(ci, OP_PORTSC, PORTSC_CCS)) {
> > +			fsm->a_conn = 1;
> > +			disable_irq_nosync(ci->irq);
> > +			queue_work(ci->wq, &ci->work);
> > +		}
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +}
> 
> - Here, we read OP_USBSTS, but never clear them, please explain
> when they are needed and when to clear them.

OTG driver need catch these normal interrupts to prepare state transitions,
Here fall through and udc or host driver will clear and handle them.

> - Can we use USBi_PCI to instead of USBSTS_PCI?

changed.

> - Have a look hw_read_intr_status and  hw_read_intr_enable at udc.c,
> if otg driver also needs similar operation, try to move them
> to common file.

changed.

> 
> Peter
> 
> 
> > +
> > +/**
> > + * 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;
> > +
> > +	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.8
> > 
> > 
> 
> -- 
> 
> Best Regards,
> Peter Chen
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux