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

Commit 6ac9a42b authored by Pavankumar Kondeti's avatar Pavankumar Kondeti
Browse files

usb: phy-msm-usb: Add support for the PHY based ID detection



The Femto PHY wrapper has an IRQ to generate an interrupt for ID line
state change. The USB PHY can generate this interrupt in retention mode
and also MPM pin is available for ID detection during deep sleep. The
PHY regulators can not be turned off, instead they are put into LPM.

Change-Id: I20f82989fcde3cfd4740f76bf0b3c72194cffc49
Signed-off-by: default avatarPavankumar Kondeti <pkondeti@codeaurora.org>
parent 15a5a1db
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ Optional properties :
- interrupt-names : Optional interrupt resource entries are:
    "async_irq" : Interrupt from HSPHY for asynchronous wakeup events in LPM.
    "pmic_id_irq" : Interrupt from PMIC for external ID pin notification.
    "phy_irq" : Interrupt from PHY. Used for ID detection.
- qcom,hsusb-otg-disable-reset: If present then core is RESET only during
	    init, otherwise core is RESET for every cable disconnect as well
- qcom,hsusb-otg-pnoc-errata-fix: If present then workaround for PNOC
+108 −5
Original line number Diff line number Diff line
@@ -702,6 +702,9 @@ static int msm_otg_reset(struct usb_phy *phy)
			ULPI_SET(ULPI_PWR_CLK_MNG_REG));
		/* Enable PMIC pull-up */
		pm8xxx_usb_id_pullup(1);
		if (motg->phy_irq)
			writeb_relaxed(USB_PHY_ID_MASK,
				USB2_PHY_USB_PHY_INTERRUPT_MASK1);
	}

	if (motg->caps & ALLOW_VDD_MIN_WITH_RETENTION_DISABLED)
@@ -1044,7 +1047,8 @@ static void msm_otg_enable_phy_hv_int(struct msm_otg *motg)
	bool dp_dm_hv_int = false;
	u32 val;

	if (motg->pdata->otg_control == OTG_PHY_CONTROL)
	if (motg->pdata->otg_control == OTG_PHY_CONTROL ||
				motg->phy_irq)
		bsv_id_hv_int = true;
	if (motg->host_bus_suspend || motg->device_bus_suspend)
		dp_dm_hv_int = true;
@@ -1076,6 +1080,8 @@ static void msm_otg_enable_phy_hv_int(struct msm_otg *motg)
	default:
		break;
	}
	pr_debug("%s: bsv_id_hv = %d dp_dm_hv_int = %d\n",
			__func__, bsv_id_hv_int, dp_dm_hv_int);
}

static void msm_otg_disable_phy_hv_int(struct msm_otg *motg)
@@ -1084,7 +1090,8 @@ static void msm_otg_disable_phy_hv_int(struct msm_otg *motg)
	bool dp_dm_hv_int = false;
	u32 val;

	if (motg->pdata->otg_control == OTG_PHY_CONTROL)
	if (motg->pdata->otg_control == OTG_PHY_CONTROL ||
				motg->phy_irq)
		bsv_id_hv_int = true;
	if (motg->host_bus_suspend || motg->device_bus_suspend)
		dp_dm_hv_int = true;
@@ -1117,6 +1124,8 @@ static void msm_otg_disable_phy_hv_int(struct msm_otg *motg)
	default:
		break;
	}
	pr_debug("%s: bsv_id_hv = %d dp_dm_hv_int = %d\n",
			__func__, bsv_id_hv_int, dp_dm_hv_int);
}

static void msm_otg_enter_phy_retention(struct msm_otg *motg)
@@ -1138,6 +1147,7 @@ static void msm_otg_enter_phy_retention(struct msm_otg *motg)
	default:
		break;
	}
	pr_debug("USB PHY is in retention\n");
}

static void msm_otg_exit_phy_retention(struct msm_otg *motg)
@@ -1160,6 +1170,25 @@ static void msm_otg_exit_phy_retention(struct msm_otg *motg)
	default:
		break;
	}
	pr_debug("USB PHY is exited from retention\n");
}

static void msm_id_status_w(struct work_struct *w);
static irqreturn_t msm_otg_phy_irq_handler(int irq, void *data)
{
	struct msm_otg *motg = data;

	if (atomic_read(&motg->in_lpm)) {
		pr_debug("PHY ID IRQ in LPM\n");
		motg->phy_irq_pending = true;
		if (!atomic_read(&motg->pm_suspended))
			pm_request_resume(motg->phy.dev);
	} else {
		pr_debug("PHY ID IRQ outside LPM\n");
		msm_id_status_w(&motg->id_status_work.work);
	}

	return IRQ_HANDLED;
}

#define PHY_SUSPEND_TIMEOUT_USEC	(500 * 1000)
@@ -1417,6 +1446,8 @@ phcd_retry:
		else
			enable_irq_wake(motg->irq);

		if (motg->phy_irq)
			enable_irq_wake(motg->phy_irq);
		if (motg->pdata->pmic_id_irq)
			enable_irq_wake(motg->pdata->pmic_id_irq);
		if (motg->ext_id_irq)
@@ -1604,6 +1635,8 @@ skip_phy_resume:
		else
			disable_irq_wake(motg->irq);

		if (motg->phy_irq)
			disable_irq_wake(motg->phy_irq);
		if (motg->pdata->pmic_id_irq)
			disable_irq_wake(motg->pdata->pmic_id_irq);
		if (motg->ext_id_irq)
@@ -1637,6 +1670,11 @@ skip_phy_resume:
	if (motg->async_irq)
		disable_irq(motg->async_irq);

	if (motg->phy_irq_pending) {
		motg->phy_irq_pending = false;
		msm_id_status_w(&motg->id_status_work.work);
	}

	if (motg->host_bus_suspend)
		usb_hcd_resume_root_hub(hcd);

@@ -2158,6 +2196,31 @@ static bool msm_otg_read_pmic_id_state(struct msm_otg *motg)
	return !!id;
}

static bool msm_otg_read_phy_id_state(struct msm_otg *motg)
{
	u8 val;

	/*
	 * clear the pending/outstanding interrupts and
	 * read the ID status from the SRC_STATUS register.
	 */
	writeb_relaxed(USB_PHY_ID_MASK, USB2_PHY_USB_PHY_INTERRUPT_CLEAR1);

	writeb_relaxed(0x1, USB2_PHY_USB_PHY_IRQ_CMD);
	/*
	 * Databook says 200 usec delay is required for
	 * clearing the interrupts.
	 */
	udelay(200);
	writeb_relaxed(0x0, USB2_PHY_USB_PHY_IRQ_CMD);

	val = readb_relaxed(USB2_PHY_USB_PHY_INTERRUPT_SRC_STATUS);
	if (val & USB_PHY_IDDIG_1_0)
		return false; /* ID is grounded */
	else
		return true;
}

static int msm_otg_mhl_register_callback(struct msm_otg *motg,
						void (*callback)(int on))
{
@@ -2868,6 +2931,11 @@ static void msm_otg_init_sm(struct msm_otg *motg)
					set_bit(ID, &motg->inputs);
				else
					clear_bit(ID, &motg->inputs);
			} else if (motg->phy_irq) {
				if (msm_otg_read_phy_id_state(motg))
					set_bit(ID, &motg->inputs);
				else
					clear_bit(ID, &motg->inputs);
			}
			/*
			 * VBUS initial state is reported after PMIC
@@ -3804,6 +3872,8 @@ static void msm_id_status_w(struct work_struct *w)
		id_state = msm_otg_read_pmic_id_state(motg);
	else if (motg->ext_id_irq)
		id_state = gpio_get_value(motg->pdata->usb_id_gpio);
	else if (motg->phy_irq)
		id_state = msm_otg_read_phy_id_state(motg);

	if (id_state) {
		if (!test_and_set_bit(ID, &motg->inputs)) {
@@ -5223,12 +5293,38 @@ static int msm_otg_probe(struct platform_device *pdev)
		goto destroy_wlock;
	}

	motg->phy_irq = platform_get_irq_byname(pdev, "phy_irq");
	if (motg->phy_irq < 0) {
		dev_dbg(&pdev->dev, "phy_irq is not present\n");
		motg->phy_irq = 0;
	} else {

		/* clear all interrupts before enabling the IRQ */
		writeb_relaxed(0xFF, USB2_PHY_USB_PHY_INTERRUPT_CLEAR0);
		writeb_relaxed(0xFF, USB2_PHY_USB_PHY_INTERRUPT_CLEAR1);

		writeb_relaxed(0x1, USB2_PHY_USB_PHY_IRQ_CMD);
		/*
		 * Databook says 200 usec delay is required for
		 * clearing the interrupts.
		 */
		udelay(200);
		writeb_relaxed(0x0, USB2_PHY_USB_PHY_IRQ_CMD);

		ret = request_irq(motg->phy_irq, msm_otg_phy_irq_handler,
				IRQF_TRIGGER_RISING, "msm_otg_phy_irq", motg);
		if (ret < 0) {
			dev_err(&pdev->dev, "phy_irq request fail %d\n", ret);
			goto free_irq;
		}
	}

	if (motg->async_irq) {
		ret = request_irq(motg->async_irq, msm_otg_irq,
					IRQF_TRIGGER_RISING, "msm_otg", motg);
		if (ret) {
			dev_err(&pdev->dev, "request irq failed (ASYNC INT)\n");
			goto free_irq;
			goto free_phy_irq;
		}
		disable_irq(motg->async_irq);
	}
@@ -5265,7 +5361,8 @@ static int msm_otg_probe(struct platform_device *pdev)
	}

	if (motg->pdata->mode == USB_OTG &&
		motg->pdata->otg_control == OTG_PMIC_CONTROL) {
		motg->pdata->otg_control == OTG_PMIC_CONTROL &&
		!motg->phy_irq) {

		if (gpio_is_valid(motg->pdata->usb_id_gpio)) {
			/* usb_id_gpio request */
@@ -5316,7 +5413,7 @@ static int msm_otg_probe(struct platform_device *pdev)
			 motg->pdata->pmic_id_irq || motg->ext_id_irq))
		motg->caps = ALLOW_PHY_POWER_COLLAPSE | ALLOW_PHY_RETENTION;

	if (motg->pdata->otg_control == OTG_PHY_CONTROL)
	if (motg->pdata->otg_control == OTG_PHY_CONTROL || motg->phy_irq)
		motg->caps = ALLOW_PHY_RETENTION | ALLOW_PHY_REGULATORS_LPM;

	if (motg->pdata->mpm_dpshv_int || motg->pdata->mpm_dmshv_int)
@@ -5404,6 +5501,9 @@ remove_phy:
free_async_irq:
	if (motg->async_irq)
		free_irq(motg->async_irq, motg);
free_phy_irq:
	if (motg->phy_irq)
		free_irq(motg->phy_irq, motg);
free_irq:
	free_irq(motg->irq, motg);
destroy_wlock:
@@ -5495,6 +5595,8 @@ static int msm_otg_remove(struct platform_device *pdev)
	wake_lock_destroy(&motg->wlock);

	msm_hsusb_mhl_switch_enable(motg, 0);
	if (motg->phy_irq)
		free_irq(motg->phy_irq, motg);
	if (motg->pdata->pmic_id_irq)
		free_irq(motg->pdata->pmic_id_irq, motg);
	usb_remove_phy(phy);
@@ -5645,6 +5747,7 @@ static int msm_otg_pm_resume(struct device *dev)
	motg->pm_done = 0;

	if (motg->async_int || motg->sm_work_pending ||
			motg->phy_irq_pending ||
			!pm_runtime_suspended(dev)) {
		pm_runtime_get_noresume(dev);
		ret = msm_otg_resume(motg);
+5 −0
Original line number Diff line number Diff line
@@ -356,6 +356,8 @@ struct msm_otg_platform_data {
 * @pdata: otg device platform data.
 * @irq: IRQ number assigned for HSUSB controller.
 * @async_irq: IRQ number used by some controllers during low power state
 * @phy_irq: IRQ number assigned for PHY to notify events like id and line
		state changes.
 * @pclk: clock struct of iface_clk.
 * @core_clk: clock struct of core_bus_clk.
 * @sleep_clk: clock struct of sleep_clk for USB PHY.
@@ -405,12 +407,14 @@ struct msm_otg_platform_data {
	     the charger detection starts. When USB is disconnected and in lpm
	     pm_done is set to true.
 * @ext_id_irq: IRQ for ID interrupt.
 * @phy_irq_pending: Gets set when PHY IRQ arrives in LPM.
 */
struct msm_otg {
	struct usb_phy phy;
	struct msm_otg_platform_data *pdata;
	int irq;
	int async_irq;
	int phy_irq;
	struct clk *xo_clk;
	struct clk *pclk;
	struct clk *core_clk;
@@ -541,6 +545,7 @@ struct msm_otg {
	bool pm_done;
	struct qpnp_vadc_chip	*vadc_dev;
	int ext_id_irq;
	bool phy_irq_pending;
};

struct ci13xxx_platform_data {
+18 −0
Original line number Diff line number Diff line
@@ -135,4 +135,22 @@
#define USB_PHY_CSR_PHY_CTRL3 (MSM_USB_PHY_CSR_BASE + 0x094)
#define CLAMP_MPM_DPSE_DMSE_EN_N BIT(2)

#define USB2_PHY_USB_PHY_IRQ_CMD (MSM_USB_PHY_CSR_BASE + 0x0D0)
#define USB2_PHY_USB_PHY_INTERRUPT_SRC_STATUS (MSM_USB_PHY_CSR_BASE + 0x05C)

#define USB2_PHY_USB_PHY_INTERRUPT_STATUS0 (MSM_USB_PHY_CSR_BASE + 0x03C)
#define USB2_PHY_USB_PHY_INTERRUPT_STATUS1 (MSM_USB_PHY_CSR_BASE + 0x040)

#define USB2_PHY_USB_PHY_INTERRUPT_CLEAR0 (MSM_USB_PHY_CSR_BASE + 0x0DC)
#define USB2_PHY_USB_PHY_INTERRUPT_CLEAR1 (MSM_USB_PHY_CSR_BASE + 0x0E0)

#define USB2_PHY_USB_PHY_INTERRUPT_MASK0 (MSM_USB_PHY_CSR_BASE + 0x0D4)
#define USB2_PHY_USB_PHY_INTERRUPT_MASK1 (MSM_USB_PHY_CSR_BASE + 0x0D8)

#define USB_PHY_IDDIG_1_0 BIT(7)

#define USB_PHY_IDDIG_RISE_MASK BIT(0)
#define USB_PHY_IDDIG_FALL_MASK BIT(1)
#define USB_PHY_ID_MASK (USB_PHY_IDDIG_RISE_MASK | USB_PHY_IDDIG_FALL_MASK)

#endif /* __LINUX_USB_GADGET_MSM72K_UDC_H__ */