Re: [PATCH 07/10] usb: chipidea: OTG HNP and SRP fsm implementation.

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

 



On Wed, Jan 08, 2014 at 05:06:22PM +0800, Li Jun wrote:
> USB OTG interrupt handling and fsm transition according to USB OTG
> spec 2.0, update otg timer time out handlers.

timeout

> 
> Signed-off-by: Li Jun <b47624@xxxxxxxxxxxxx>
> ---
>  drivers/usb/chipidea/bits.h    |    3 +
>  drivers/usb/chipidea/core.c    |   10 ++-
>  drivers/usb/chipidea/otg.c     |    6 +
>  drivers/usb/chipidea/otg_fsm.c |  264 ++++++++++++++++++++++++++++++++++++++--
>  drivers/usb/chipidea/otg_fsm.h |   18 +++
>  5 files changed, 292 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h
> index 4347414..9a1c4c0 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)
> @@ -81,6 +83,7 @@
>  #define OTGSC_VC	      BIT(1)
>  #define OTGSC_IDPU	      BIT(5)
>  #define OTGSC_HADP	      BIT(6)
> +#define OTGSC_HABA            BIT(7)
>  #define OTGSC_ID	      BIT(8)
>  #define OTGSC_AVV	      BIT(9)
>  #define OTGSC_ASV	      BIT(10)
> diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
> index 9a5ef20..2f29791 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 uintptr_t ci_regs_nolpm[] = {
> @@ -356,8 +357,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
> @@ -659,6 +664,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 2e13f2f..5914c92 100644
> --- a/drivers/usb/chipidea/otg.c
> +++ b/drivers/usb/chipidea/otg.c
> @@ -22,6 +22,7 @@
>  #include "ci.h"
>  #include "bits.h"
>  #include "otg.h"
> +#include "otg_fsm.h"
>  
>  /**
>   * ci_otg_role - pick role based on ID pin state
> @@ -76,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 86bed68..cfdfebd 100644
> --- a/drivers/usb/chipidea/otg_fsm.c
> +++ b/drivers/usb/chipidea/otg_fsm.c
> @@ -192,6 +192,60 @@ void set_tmout(void *ptr, unsigned long indicator)
>  	*(int *)indicator = 1;
>  }
>  
> +void set_tmout_and_fsm(void *ptr, unsigned long indicator)
> +{
> +	struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
> +
> +	set_tmout(ci, indicator);
> +
> +	/* trans from a_wait_bcon to a_wait_vfall */
> +	disable_irq_nosync(ci->irq);
> +	queue_work(ci->wq, &ci->work);
> +}
> +
> +void b_ssend_srp_tmout_handler(void *ptr, unsigned long indicator)
> +{
> +	struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
> +	set_tmout(ci, indicator);
> +
> +	/* only vbus fall below B_sess_vld in b_idle state */
> +	if (ci->transceiver->state == OTG_STATE_B_IDLE) {
> +		disable_irq_nosync(ci->irq);
> +		queue_work(ci->wq, &ci->work);
> +	}
> +}
> +
> +void b_sess_vld_tmout_handler(void *ptr, unsigned long indicator)
> +{
> +	struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
> +
> +	/* Check if A detached */
> +	if (!(hw_read(ci, OP_OTGSC, OTGSC_BSV))) {
> +		ci->fsm->b_sess_vld = 0;
> +		ci_otg_add_timer(ci, b_ssend_srp_tmr);
> +		disable_irq_nosync(ci->irq);
> +		queue_work(ci->wq, &ci->work);
> +	}
> +}
> +
> +void b_data_pulse_end(void *ptr, unsigned long indicator)
> +{
> +	struct ci_hdrc *ci = (struct ci_hdrc *)ptr;
> +
> +	ci->fsm->b_srp_done = 1;
> +	ci->fsm->b_bus_req = 0;
> +	if (ci->fsm->power_up)
> +		ci->fsm->power_up = 0;
> +#ifdef HA_DATA_PULSE
> +	hw_write(ci, OP_OTGSC, OTGSC_INT_STATUS_BITS | OTGSC_HABA, 0);
> +
> +	disable_irq_nosync(ci->irq);
> +	queue_work(ci->wq, &ci->work);
> +#else
> +	ci_otg_loc_conn(ci->fsm, 0);
> +#endif
> +}
> +
>  /* Initialize timers */
>  int ci_otg_init_timers(struct otg_fsm *fsm)
>  {
> @@ -201,22 +255,22 @@ int ci_otg_init_timers(struct otg_fsm *fsm)
>  	if (a_wait_vrise_tmr == NULL)
>  		return -ENOMEM;
>  
> -	a_wait_vfall_tmr = otg_timer_initializer(&set_tmout,
> +	a_wait_vfall_tmr = otg_timer_initializer(&set_tmout_and_fsm,
>  			TA_WAIT_VFALL, (unsigned long)&fsm->a_wait_vfall_tmout);
>  	if (a_wait_vfall_tmr == NULL)
>  		return -ENOMEM;
>  
> -	a_wait_bcon_tmr = otg_timer_initializer(&set_tmout,
> +	a_wait_bcon_tmr = otg_timer_initializer(&set_tmout_and_fsm,
>  		TA_WAIT_BCON, (unsigned long)&fsm->a_wait_bcon_tmout);
>  	if (a_wait_bcon_tmr == NULL)
>  		return -ENOMEM;
>  
> -	a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout,
> +	a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout_and_fsm,
>  		TA_AIDL_BDIS, (unsigned long)&fsm->a_aidl_bdis_tmout);
>  	if (a_aidl_bdis_tmr == NULL)
>  		return -ENOMEM;
>  
> -	a_bidl_adis_tmr = otg_timer_initializer(&set_tmout,
> +	a_bidl_adis_tmr = otg_timer_initializer(&set_tmout_and_fsm,
>  		TA_BIDL_ADIS, (unsigned long)&fsm->a_bidl_adis_tmout);
>  	if (a_bidl_adis_tmr == NULL)
>  		return -ENOMEM;
> @@ -226,12 +280,12 @@ int ci_otg_init_timers(struct otg_fsm *fsm)
>  	if (b_ase0_brst_tmr == NULL)
>  		return -ENOMEM;
>  
> -	b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP,
> +	b_se0_srp_tmr = otg_timer_initializer(&set_tmout_and_fsm, TB_SE0_SRP,
>  				(unsigned long)&fsm->b_se0_srp);
>  	if (b_se0_srp_tmr == NULL)
>  		return -ENOMEM;
>  
> -	b_ssend_srp_tmr = otg_timer_initializer(&set_tmout,
> +	b_ssend_srp_tmr = otg_timer_initializer(&b_ssend_srp_tmout_handler,
>  			TB_SSEND_SRP, (unsigned long)&fsm->b_ssend_srp);
>  	if (b_ssend_srp_tmr == NULL)
>  		return -ENOMEM;
> @@ -241,11 +295,13 @@ int ci_otg_init_timers(struct otg_fsm *fsm)
>  	if (b_srp_fail_tmr == NULL)
>  		return -ENOMEM;
>  
> -	b_data_pulse_tmr = otg_timer_initializer(&set_tmout, TB_DATA_PLS, 0);
> +	b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end,
> +							TB_DATA_PLS, 0);
>  	if (b_data_pulse_tmr == NULL)
>  		return -ENOMEM;
>  
> -	b_sess_vld_tmr = otg_timer_initializer(&set_tmout, TB_SESS_VLD, 0);
> +	b_sess_vld_tmr = otg_timer_initializer(&b_sess_vld_tmout_handler,
> +							TB_SESS_VLD, 0);

If you have several changes for calling otg_timer_initializer, try to see
if they can be squashed (together).

Peter

>  	if (b_sess_vld_tmr == NULL)
>  		return -ENOMEM;
>  
> @@ -434,6 +490,198 @@ 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_tmr);
> +		}
> +		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_tmr);
> +		}
> +
> +		if (hw_read(ci, OP_USBSTS, USBi_URI))
> +			ci_otg_del_timer(ci, a_bidl_adis_tmr);
> +
> +		if (hw_read(ci, OP_USBSTS, USBSTS_PCI)) {
> +			if (fsm->b_bus_suspend == 1) {
> +				ci_otg_del_timer(ci, a_bidl_adis_tmr);
> +				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;
> +	}
> +}
> +/**
> + * 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_tmr);
> +				ci_otg_del_timer(ci, b_srp_fail_tmr);
> +				fsm->b_ssend_srp = 0;
> +			} else {
> +				fsm->b_sess_vld = 0;
> +				if (fsm->id)
> +					ci_otg_add_timer(ci, b_ssend_srp_tmr);
> +			}
> +		} 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 6e8226a..994e4c9 100644
> --- a/drivers/usb/chipidea/otg_fsm.h
> +++ b/drivers/usb/chipidea/otg_fsm.h
> @@ -87,6 +87,9 @@ struct ci_otg_fsm_timer {
>  #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
>  
> @@ -95,6 +98,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