Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3aa4a091 authored by Bar Weiner's avatar Bar Weiner
Browse files

usb: dwc3: add support for pwr_event_irq



Adds support for the pwr_event_irq in general, and for
the L1_OUT pwr event specifically. The support consists of:
1. Enabling the pwr_event_irq based on the DT.
2. Adding a threaded irq handler as well as a centralized function
   (dwc3_pwr_event_handler()) for handling the various power events.
3. Enabling the L1_OUT pwr event. This is done on an
   arch-by-arch basis. The trigger for enabling this is
   dbm_l1_lpm_interrupt().
4. Calling usb_gadget_wakeup() in order to initiate a wakeup sequence
   without sending a remote-wakeup indication to the host.
5. Adding a call from dwc3_msm_resume() to dwc3_pwr_event_handler().
   This is done in order to overcome a situation where
   dwc3_msm_suspend() would have disabled the clocks, thereby
   disabling the ability to read the PWR_STAT register before calling
   the handler function.

Change-Id: Ia548ec2e9ee4a7c6634c87ba24fe4cdbf1221000
Signed-off-by: default avatarDov Levenglick <dovl@codeaurora.org>
Signed-off-by: default avatarBar Weiner <bweiner@codeaurora.org>
parent ef37df78
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@ Optional properties :
    "hs_phy_irq" : Interrupt from HSPHY for asynchronous events in LPM.
	This is not used if wakeup events are received externally (e.g. PMIC)
    "pmic_id_irq" : Interrupt from PMIC for external ID pin notification.
    "pwr_event_irq" : Interrupt to controller for asynchronous events in LPM.
	Used for SS-USB power events. This is not used if wakeup events are
	received externally (e.g. PMIC)
- qcom,otg-capability: If present then depend on PMIC for VBUS notifications,
  otherwise depend on PHY.
- qcom,suspend_event_enable: If present then suspend interrupt event is enabled.
+117 −3
Original line number Diff line number Diff line
@@ -113,6 +113,8 @@ MODULE_PARM_DESC(usb_lpm_override, "Override no_suspend_resume with USB");
#define PWR_EVNT_IRQ_MASK_REG    (QSCRATCH_REG_OFFSET + 0x5C)
#define HS_PHY_CTRL_COMMON_REG	(QSCRATCH_REG_OFFSET + 0xEC)

#define PWR_EVNT_LPM_OUT_L1_MASK		BIT(13)

/* TZ SCM parameters */
#define DWC3_MSM_RESTORE_SCM_CFG_CMD 0x2
struct dwc3_msm_scm_cmd_buf {
@@ -210,6 +212,8 @@ struct dwc3_msm {

	unsigned int		irq_to_affin;
	struct notifier_block	dwc3_cpu_notifier;

	int  pwr_event_irq;
};

#define USB_HSPHY_3P3_VOL_MIN		3050000 /* uV */
@@ -226,6 +230,8 @@ struct dwc3_msm {

static struct usb_ext_notification *usb_ext;

static void dwc3_pwr_event_handler(struct dwc3_msm *mdwc);

/**
 *
 * Read register with debug info.
@@ -1153,6 +1159,12 @@ static void dwc3_block_reset_usb_work(struct work_struct *w)
		reg |= DWC3_DEVTEN_SUSPEND;

	dwc3_msm_write_reg(mdwc->base, DWC3_DEVTEN, reg);

	/* Add power event if the dbm indicates coming out of L1 by interrupt */
	if (mdwc->dbm && dbm_l1_lpm_interrupt(mdwc->dbm))
		dwc3_msm_write_reg_field(mdwc->base,
					PWR_EVNT_IRQ_MASK_REG,
					PWR_EVNT_LPM_OUT_L1_MASK, 1);
}

static void dwc3_chg_enable_secondary_det(struct dwc3_msm *mdwc)
@@ -1722,6 +1734,12 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)

	dev_info(mdwc->dev, "DWC3 exited from low power mode\n");

	/*
	 * Handle other power events that could not have been handled during
	 * Low Power Mode
	 */
	dwc3_pwr_event_handler(mdwc);

	return 0;
}

@@ -1746,7 +1764,6 @@ static void dwc3_wait_for_ext_chg_done(struct dwc3_msm *mdwc)
		else
			dev_dbg(mdwc->dev, "ext chg wait done\n");
	}

}

static void dwc3_resume_work(struct work_struct *w)
@@ -1788,6 +1805,41 @@ static void dwc3_resume_work(struct work_struct *w)
	}
}

static void dwc3_pwr_event_handler(struct dwc3_msm *mdwc)
{
	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
	u32 irq_stat, irq_clear = 0;

	irq_stat = dwc3_msm_read_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG);

	/* Handle exit from L1 events */
	if (irq_stat & PWR_EVNT_LPM_OUT_L1_MASK) {
		dev_dbg(mdwc->dev, "%s: handling PWR_EVNT_LPM_OUT_L1_MASK\n",
				__func__);
		if (usb_gadget_wakeup(&dwc->gadget))
			dev_err(mdwc->dev, "%s failed to take dwc out of L1\n",
					__func__);
		irq_stat &= ~PWR_EVNT_LPM_OUT_L1_MASK;
		irq_clear |= PWR_EVNT_LPM_OUT_L1_MASK;
	}

	/* Unhandled events */
	if (irq_stat)
		dev_dbg(mdwc->dev, "%s: unexpected PWR_EVNT, irq_stat=%X\n",
			__func__, irq_stat);

	dwc3_msm_write_reg(mdwc->base, PWR_EVNT_IRQ_STAT_REG, irq_clear);
}

static irqreturn_t msm_dwc3_pwr_irq_thread(int irq, void *_mdwc)
{
	struct dwc3_msm *mdwc = _mdwc;

	dev_dbg(mdwc->dev, "%s\n", __func__);
	dwc3_pwr_event_handler(mdwc);
	return IRQ_HANDLED;
}

static u32 debug_id = true, debug_bsv, debug_connect;

static int dwc3_connect_show(struct seq_file *s, void *unused)
@@ -1891,6 +1943,30 @@ static irqreturn_t msm_dwc3_irq(int irq, void *data)
	return IRQ_HANDLED;
}

static irqreturn_t msm_dwc3_pwr_irq(int irq, void *data)
{
	struct dwc3_msm *mdwc = data;

	dev_dbg(mdwc->dev, "%s received\n", __func__);
	/*
	 * When in Low Power Mode, can't read PWR_EVNT_IRQ_STAT_REG to acertain
	 * which interrupts have been triggered, as the clocks are disabled.
	 * After re-enabling the clocks, dwc3_msm_resume will call
	 * dwc3_pwr_event_handler to handle all other power events
	 */
	if (atomic_read(&mdwc->in_lpm)) {
		/* bail out if system resume in process, else initiate RESUME */
		if (atomic_read(&mdwc->pm_suspended))
			mdwc->resume_pending = true;
		else
			pm_runtime_get(mdwc->dev);

		return IRQ_HANDLED;
	}

	return IRQ_WAKE_THREAD;
}

static int
get_prop_usbin_voltage_now(struct dwc3_msm *mdwc)
{
@@ -2687,6 +2763,27 @@ static int dwc3_msm_probe(struct platform_device *pdev)
		}
	}

	/*
	 * Some platforms have a special interrupt line for indicating resume
	 * while in low power mode, when clocks are disabled.
	 */
	mdwc->pwr_event_irq = platform_get_irq_byname(pdev, "pwr_event_irq");
	if (mdwc->pwr_event_irq < 0) {
		dev_dbg(&pdev->dev, "pget_irq for pwr_event_irq failed\n");
		mdwc->pwr_event_irq = 0;
	} else {
		ret = devm_request_threaded_irq(&pdev->dev, mdwc->pwr_event_irq,
					msm_dwc3_pwr_irq,
					msm_dwc3_pwr_irq_thread,
					IRQF_TRIGGER_RISING,
					"msm_dwc3", mdwc);
		if (ret) {
			dev_err(&pdev->dev, "irqreq pwr_event_irq failed: %d\n",
					ret);
			goto disable_ref_clk;
		}
	}

	if (mdwc->ext_xceiv.otg_capability) {
		mdwc->pmic_id_irq =
			platform_get_irq_byname(pdev, "pmic_id_irq");
@@ -2766,6 +2863,21 @@ static int dwc3_msm_probe(struct platform_device *pdev)
			ret = -EPROBE_DEFER;
			goto disable_ref_clk;
		}
		/*
		 * Add power event if the dbm indicates coming out of L1
		 * by interrupt
		 */
		if (dbm_l1_lpm_interrupt(mdwc->dbm)) {
			if (!mdwc->pwr_event_irq) {
				dev_err(&pdev->dev,
					"need pwr_event_irq exiting L1\n");
				ret = -EINVAL;
				goto disable_ref_clk;
			}
			dwc3_msm_write_reg_field(mdwc->base,
					PWR_EVNT_IRQ_MASK_REG,
					PWR_EVNT_LPM_OUT_L1_MASK, 1);
		}
	}

	ret = of_property_read_u32(node, "qcom,restore-sec-cfg-for-scm-dev-id",
@@ -3028,14 +3140,16 @@ static int dwc3_msm_remove(struct platform_device *pdev)
		power_supply_unregister(&mdwc->usb_psy);
	if (mdwc->hs_phy)
		mdwc->hs_phy->flags &= ~PHY_HOST_MODE;

	platform_device_put(mdwc->dwc3);
	device_for_each_child(&pdev->dev, NULL, dwc3_msm_remove_children);

	if (!IS_ERR_OR_NULL(mdwc->vbus_otg))
		regulator_disable(mdwc->vbus_otg);

	if (mdwc->hs_phy_irq)
		disable_irq_wake(mdwc->hs_phy_irq);
	if (mdwc->pwr_event_irq)
		disable_irq(mdwc->pwr_event_irq);

	clk_disable_unprepare(mdwc->utmi_clk);
	clk_disable_unprepare(mdwc->core_clk);
+9 −1
Original line number Diff line number Diff line
@@ -1515,6 +1515,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)

	unsigned long		timeout;
	unsigned long		flags;
	bool			link_recover_only = false;

	u32			reg;

@@ -1538,6 +1539,12 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
	case DWC3_LINK_STATE_RX_DET:	/* in HS, means Early Suspend */
	case DWC3_LINK_STATE_U3:	/* in HS, means SUSPEND */
		break;
	case DWC3_LINK_STATE_U1:
		if (dwc->gadget.speed != USB_SPEED_SUPER) {
			link_recover_only = true;
			break;
		}
		/* Intentional fallthrough */
	default:
		dev_dbg(dwc->dev, "can't wakeup from link state %d\n",
				link_state);
@@ -1584,6 +1591,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
	 * the device is back at U0 state, it is required that
	 * the resume sequence is initiated by SW.
	 */
	if (!link_recover_only)
		dwc3_gadget_wakeup_interrupt(dwc);
out:
	spin_unlock_irqrestore(&dwc->lock, flags);