Re: [PATCH v4 5/9] usb: dwc3: core: make dual-role work with OTG irq

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

 



Hi,

On Wed, Sep 02, 2015 at 05:24:20PM +0300, Roger Quadros wrote:
> If the ID pin event is not available over extcon
> then we rely on the OTG controller to provide us ID and VBUS
> information.
> 
> We still don't support any OTG features but just
> dual-role operation.
> 
> Signed-off-by: Roger Quadros <rogerq@xxxxxx>
> ---
>  drivers/usb/dwc3/core.c | 217 ++++++++++++++++++++++++++++++++++++++++++++----
>  drivers/usb/dwc3/core.h |   3 +
>  2 files changed, 205 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> index 38b31df..632ee53 100644
> --- a/drivers/usb/dwc3/core.c
> +++ b/drivers/usb/dwc3/core.c
> @@ -704,6 +704,63 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
>  	return 0;
>  }
>  
> +/* Get OTG events and sync it to OTG fsm */
> +static void dwc3_otg_fsm_sync(struct dwc3 *dwc)
> +{
> +	u32 reg;
> +	int id, vbus;
> +
> +	reg = dwc3_readl(dwc->regs, DWC3_OSTS);
> +	dev_dbg(dwc->dev, "otgstatus 0x%x\n", reg);
> +
> +	id = !!(reg & DWC3_OSTS_CONIDSTS);
> +	vbus = !!(reg & DWC3_OSTS_BSESVLD);
> +
> +	if (id != dwc->fsm->id || vbus != dwc->fsm->b_sess_vld) {
> +		dev_dbg(dwc->dev, "id %d vbus %d\n", id, vbus);
> +		dwc->fsm->id = id;
> +		dwc->fsm->b_sess_vld = vbus;
> +		usb_otg_sync_inputs(dwc->fsm);
> +	}

this guy shouldn't try to filter events here. That's what the FSM should
be doing.

> +}
> +
> +static irqreturn_t dwc3_otg_thread_irq(int irq, void *_dwc)
> +{
> +	struct dwc3 *dwc = _dwc;
> +	unsigned long flags;
> +	irqreturn_t ret = IRQ_NONE;

this IRQ will be disabled pretty quickly. You *always* return IRQ_NONE

> +	spin_lock_irqsave(&dwc->lock, flags);

if you cache current OSTS in dwc3, you can use that instead and change
this to a spin_lock() instead of disabling IRQs here. This device's IRQs
are already masked anyway.

> +	dwc3_otg_fsm_sync(dwc);
> +	/* unmask interrupts */
> +	dwc3_writel(dwc->regs, DWC3_OEVTEN, dwc->oevten);
> +	spin_unlock_irqrestore(&dwc->lock, flags);
> +
> +	return ret;
> +}
> +
> +static irqreturn_t dwc3_otg_irq(int irq, void *_dwc)
> +{
> +	struct dwc3 *dwc = _dwc;
> +	irqreturn_t ret = IRQ_NONE;
> +	u32 reg;
> +
> +	spin_lock(&dwc->lock);

this seems unnecessary, we're already in hardirq with IRQs disabled.
What sort of race could we have ? (in fact, this also needs change in
dwc3/gadget.c).

> +
> +	reg = dwc3_readl(dwc->regs, DWC3_OEVT);
> +	if (reg) {
> +		dwc3_writel(dwc->regs, DWC3_OEVT, reg);
> +		/* mask interrupts till processed */
> +		dwc->oevten = dwc3_readl(dwc->regs, DWC3_OEVTEN);
> +		dwc3_writel(dwc->regs, DWC3_OEVTEN, 0);
> +		ret = IRQ_WAKE_THREAD;
> +	}
> +
> +	spin_unlock(&dwc->lock);
> +
> +	return ret;
> +}
> +
>  /* --------------------- Dual-Role management ------------------------------- */
>  
>  static void dwc3_drd_fsm_sync(struct dwc3 *dwc)
> @@ -728,15 +785,44 @@ static int dwc3_drd_start_host(struct otg_fsm *fsm, int on)
>  {
>  	struct device *dev = usb_otg_fsm_to_dev(fsm);
>  	struct dwc3 *dwc = dev_get_drvdata(dev);
> +	u32 reg;
>  
>  	dev_dbg(dwc->dev, "%s: %d\n", __func__, on);
> +	if (dwc->edev) {
> +		if (on) {
> +			dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
> +			/* start the HCD */
> +			usb_otg_start_host(fsm, true);
> +		} else {
> +			/* stop the HCD */
> +			usb_otg_start_host(fsm, false);
> +		}

		if (on)
			dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
		usb_otg_start_host(fsm, on);

> +
> +		return 0;
> +	}
> +
> +	/* switch OTG core */
>  	if (on) {
> -		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
> +		/* OCTL.PeriMode = 0 */
> +		reg = dwc3_readl(dwc->regs, DWC3_OCTL);
> +		reg &= ~DWC3_OCTL_PERIMODE;
> +		dwc3_writel(dwc->regs, DWC3_OCTL, reg);
> +		/* unconditionally turn on VBUS */
> +		reg |= DWC3_OCTL_PRTPWRCTL;
> +		dwc3_writel(dwc->regs, DWC3_OCTL, reg);
>  		/* start the HCD */
>  		usb_otg_start_host(fsm, true);
>  	} else {
>  		/* stop the HCD */
>  		usb_otg_start_host(fsm, false);
> +		/* turn off VBUS */
> +		reg = dwc3_readl(dwc->regs, DWC3_OCTL);
> +		reg &= ~DWC3_OCTL_PRTPWRCTL;
> +		dwc3_writel(dwc->regs, DWC3_OCTL, reg);
> +		/* OCTL.PeriMode = 1 */
> +		reg = dwc3_readl(dwc->regs, DWC3_OCTL);
> +		reg |= DWC3_OCTL_PERIMODE;
> +		dwc3_writel(dwc->regs, DWC3_OCTL, reg);
>  	}

it looks like you're not really following the fluxchart from SNPS
documentation, see Figure 11-4 on section 11.1.4.5

> @@ -746,15 +832,48 @@ static int dwc3_drd_start_gadget(struct otg_fsm *fsm, int on)
>  {
>  	struct device *dev = usb_otg_fsm_to_dev(fsm);
>  	struct dwc3 *dwc = dev_get_drvdata(dev);
> +	u32 reg;
>  
>  	dev_dbg(dwc->dev, "%s: %d\n", __func__, on);
> -	if (on) {
> -		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
> +	if (on)
>  		dwc3_event_buffers_setup(dwc);
>  
> +	if (dwc->edev) {
> +		if (on) {
> +			dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
> +			usb_otg_start_gadget(fsm, true);
> +		} else {
> +			usb_otg_start_gadget(fsm, false);
> +		}
> +
> +		return 0;
> +	}
> +
> +	/* switch OTG core */
> +	if (on) {
> +		/* OCTL.PeriMode = 1 */
> +		reg = dwc3_readl(dwc->regs, DWC3_OCTL);
> +		reg |= DWC3_OCTL_PERIMODE;
> +		dwc3_writel(dwc->regs, DWC3_OCTL, reg);
> +		/* GUSB2PHYCFG0.SusPHY = 1 */
> +		if (!dwc->dis_u2_susphy_quirk) {
> +			reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
> +			reg |= DWC3_GUSB2PHYCFG_SUSPHY;
> +			dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
> +		}
>  		/* start the UDC */
>  		usb_otg_start_gadget(fsm, true);
>  	} else {
> +		/* GUSB2PHYCFG0.SusPHY=0 */
> +		if (!dwc->dis_u2_susphy_quirk) {
> +			reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
> +			reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
> +			dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
> +		}
> +		/* OCTL.PeriMode = 1 */
> +		reg = dwc3_readl(dwc->regs, DWC3_OCTL);
> +		reg |= DWC3_OCTL_PERIMODE;
> +		dwc3_writel(dwc->regs, DWC3_OCTL, reg);
>  		/* stop the UDC */
>  		usb_otg_start_gadget(fsm, false);
>  	}
> @@ -777,10 +896,30 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
>  	return NOTIFY_DONE;
>  }
>  
> +static int dwc3_drd_register(struct dwc3 *dwc)
> +{
> +	int ret;
> +
> +	/* register parent as DRD device with OTG core */
> +	dwc->fsm = usb_otg_register(dwc->dev, &dwc->otg_config);
> +	if (IS_ERR(dwc->fsm)) {
> +		ret = PTR_ERR(dwc->fsm);
> +		if (ret == -ENOTSUPP)
> +			dev_err(dwc->dev, "CONFIG_USB_OTG needed for dual-role\n");
> +		else
> +			dev_err(dwc->dev, "Failed to register with OTG core\n");
> +
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
>  static int dwc3_drd_init(struct dwc3 *dwc)
>  {
>  	int ret, id, vbus;
>  	struct usb_otg_caps *otgcaps = &dwc->otg_config.otg_caps;
> +	u32 reg;
>  
>  	otgcaps->otg_rev = 0;
>  	otgcaps->hnp_support = false;
> @@ -788,9 +927,10 @@ static int dwc3_drd_init(struct dwc3 *dwc)
>  	otgcaps->adp_support = false;
>  	dwc->otg_config.fsm_ops = &dwc3_drd_ops;
>  
> +	/* If extcon device is not present we rely on OTG core for ID event */
>  	if (!dwc->edev) {
> -		dev_err(dwc->dev, "No extcon device found for OTG mode\n");
> -		return -ENODEV;
> +		dev_dbg(dwc->dev, "No extcon device found for OTG mode\n");
> +		goto try_otg_core;
>  	}
>  
>  	dwc->otg_nb.notifier_call = dwc3_drd_notifier;
> @@ -818,17 +958,9 @@ static int dwc3_drd_init(struct dwc3 *dwc)
>  		goto fail;
>  	}
>  
> -	/* register as DRD device with OTG core */
> -	dwc->fsm = usb_otg_register(dwc->dev, &dwc->otg_config);
> -	if (IS_ERR(dwc->fsm)) {
> -		ret = PTR_ERR(dwc->fsm);
> -		if (ret == -ENOTSUPP)
> -			dev_err(dwc->dev, "CONFIG_USB_OTG needed for dual-role\n");
> -		else
> -			dev_err(dwc->dev, "Failed to register with OTG core\n");
> -
> +	ret = dwc3_drd_register(dwc);
> +	if (ret)
>  		goto fail;
> -	}
>  
>  	dwc3_drd_fsm_sync(dwc);
>  
> @@ -839,6 +971,61 @@ extcon_fail:
>  	extcon_unregister_notifier(dwc->edev, EXTCON_USB, &dwc->otg_nb);
>  
>  	return ret;
> +
> +try_otg_core:
> +	ret = dwc3_drd_register(dwc);
> +	if (ret)
> +		return ret;
> +
> +	/* disable all irqs */
> +	dwc3_writel(dwc->regs, DWC3_OEVTEN, 0);
> +	/* clear all events */
> +	dwc3_writel(dwc->regs, DWC3_OEVT, ~0);
> +
> +	ret = request_threaded_irq(dwc->otg_irq, dwc3_otg_irq,
> +				   dwc3_otg_thread_irq,
> +				   IRQF_SHARED, "dwc3-otg", dwc);
> +	if (ret) {
> +		dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
> +			dwc->otg_irq, ret);
> +		ret = -ENODEV;
> +		goto error;
> +	}
> +
> +	/* we need to set OTG to get events from OTG core */
> +	dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
> +	/* GUSB2PHYCFG0.SusPHY=0 */
> +	if (!dwc->dis_u2_susphy_quirk) {
> +		reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
> +		reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
> +		dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
> +	}
> +
> +	/* Initialize OTG registers */
> +	/*
> +	 * Prevent host/device reset from resetting OTG core.
> +	 * If we don't do this then xhci_reset (USBCMD.HCRST) will reset
> +	 * the signal outputs sent to the PHY, the OTG FSM logic of the
> +	 * core and also the resets to the VBUS filters inside the core.
> +	 */
> +	reg = DWC3_OCFG_SFTRSTMASK;
> +	dwc3_writel(dwc->regs, DWC3_OCFG, reg);
> +	/* Enable ID event interrupt */
> +	dwc3_writel(dwc->regs, DWC3_OEVTEN, DWC3_OEVTEN_CONIDSTSCHNGEN |
> +			DWC3_OEVTEN_BDEVVBUSCHNGE |
> +			DWC3_OEVTEN_BDEVSESSVLDDETEN);
> +	/* OCTL.PeriMode = 1 */
> +	dwc3_writel(dwc->regs, DWC3_OCTL, DWC3_OCTL_PERIMODE);
> +
> +	dwc3_otg_fsm_sync(dwc);
> +	usb_otg_sync_inputs(dwc->fsm);
> +
> +	return 0;
> +
> +error:
> +	usb_otg_unregister(dwc->dev);
> +
> +	return ret;
>  }
>  
>  static void dwc3_drd_exit(struct dwc3 *dwc)
> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
> index bd32cb2..129ef37 100644
> --- a/drivers/usb/dwc3/core.h
> +++ b/drivers/usb/dwc3/core.h
> @@ -736,6 +736,7 @@ struct dwc3_scratchpad_array {
>   * @gadget_driver: pointer to the gadget driver
>   * @regs: base address for our registers
>   * @regs_size: address space size
> + * @oevten: otg interrupt enable mask copy
>   * @nr_scratch: number of scratch buffers
>   * @num_event_buffers: calculated number of event buffers
>   * @u1u2: only used on revisions <1.83a for workaround
> @@ -858,6 +859,8 @@ struct dwc3 {
>  	u32			dcfg;
>  	u32			gctl;
>  
> +	u32			oevten;
> +
>  	u32			nr_scratch;
>  	u32			num_event_buffers;
>  	u32			u1u2;
> -- 
> 2.1.4
> 

-- 
balbi

Attachment: signature.asc
Description: Digital signature


[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux