162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Cadence USBSS and USBSSP DRD Driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2018-2020 Cadence.
662306a36Sopenharmony_ci * Copyright (C) 2019 Texas Instruments
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Author: Pawel Laszczak <pawell@cadence.com>
962306a36Sopenharmony_ci *         Roger Quadros <rogerq@ti.com>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/iopoll.h>
1662306a36Sopenharmony_ci#include <linux/usb/otg.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "drd.h"
1962306a36Sopenharmony_ci#include "core.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/**
2262306a36Sopenharmony_ci * cdns_set_mode - change mode of OTG Core
2362306a36Sopenharmony_ci * @cdns: pointer to context structure
2462306a36Sopenharmony_ci * @mode: selected mode from cdns_role
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * Returns 0 on success otherwise negative errno
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_cistatic int cdns_set_mode(struct cdns *cdns, enum usb_dr_mode mode)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	void __iomem  *override_reg;
3162306a36Sopenharmony_ci	u32 reg;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	switch (mode) {
3462306a36Sopenharmony_ci	case USB_DR_MODE_PERIPHERAL:
3562306a36Sopenharmony_ci		break;
3662306a36Sopenharmony_ci	case USB_DR_MODE_HOST:
3762306a36Sopenharmony_ci		break;
3862306a36Sopenharmony_ci	case USB_DR_MODE_OTG:
3962306a36Sopenharmony_ci		dev_dbg(cdns->dev, "Set controller to OTG mode\n");
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci		if (cdns->version == CDNSP_CONTROLLER_V2)
4262306a36Sopenharmony_ci			override_reg = &cdns->otg_cdnsp_regs->override;
4362306a36Sopenharmony_ci		else if (cdns->version == CDNS3_CONTROLLER_V1)
4462306a36Sopenharmony_ci			override_reg = &cdns->otg_v1_regs->override;
4562306a36Sopenharmony_ci		else
4662306a36Sopenharmony_ci			override_reg = &cdns->otg_v0_regs->ctrl1;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci		reg = readl(override_reg);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci		if (cdns->version != CDNS3_CONTROLLER_V0)
5162306a36Sopenharmony_ci			reg |= OVERRIDE_IDPULLUP;
5262306a36Sopenharmony_ci		else
5362306a36Sopenharmony_ci			reg |= OVERRIDE_IDPULLUP_V0;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci		writel(reg, override_reg);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		if (cdns->version == CDNS3_CONTROLLER_V1) {
5862306a36Sopenharmony_ci			/*
5962306a36Sopenharmony_ci			 * Enable work around feature built into the
6062306a36Sopenharmony_ci			 * controller to address issue with RX Sensitivity
6162306a36Sopenharmony_ci			 * est (EL_17) for USB2 PHY. The issue only occures
6262306a36Sopenharmony_ci			 * for 0x0002450D controller version.
6362306a36Sopenharmony_ci			 */
6462306a36Sopenharmony_ci			if (cdns->phyrst_a_enable) {
6562306a36Sopenharmony_ci				reg = readl(&cdns->otg_v1_regs->phyrst_cfg);
6662306a36Sopenharmony_ci				reg |= PHYRST_CFG_PHYRST_A_ENABLE;
6762306a36Sopenharmony_ci				writel(reg, &cdns->otg_v1_regs->phyrst_cfg);
6862306a36Sopenharmony_ci			}
6962306a36Sopenharmony_ci		}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		/*
7262306a36Sopenharmony_ci		 * Hardware specification says: "ID_VALUE must be valid within
7362306a36Sopenharmony_ci		 * 50ms after idpullup is set to '1" so driver must wait
7462306a36Sopenharmony_ci		 * 50ms before reading this pin.
7562306a36Sopenharmony_ci		 */
7662306a36Sopenharmony_ci		usleep_range(50000, 60000);
7762306a36Sopenharmony_ci		break;
7862306a36Sopenharmony_ci	default:
7962306a36Sopenharmony_ci		dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
8062306a36Sopenharmony_ci		return -EINVAL;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciint cdns_get_id(struct cdns *cdns)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	int id;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
9162306a36Sopenharmony_ci	dev_dbg(cdns->dev, "OTG ID: %d", id);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return id;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ciint cdns_get_vbus(struct cdns *cdns)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	int vbus;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	vbus = !!(readl(&cdns->otg_regs->sts) & OTGSTS_VBUS_VALID);
10162306a36Sopenharmony_ci	dev_dbg(cdns->dev, "OTG VBUS: %d", vbus);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return vbus;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_civoid cdns_clear_vbus(struct cdns *cdns)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	u32 reg;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (cdns->version != CDNSP_CONTROLLER_V2)
11162306a36Sopenharmony_ci		return;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	reg = readl(&cdns->otg_cdnsp_regs->override);
11462306a36Sopenharmony_ci	reg |= OVERRIDE_SESS_VLD_SEL;
11562306a36Sopenharmony_ci	writel(reg, &cdns->otg_cdnsp_regs->override);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdns_clear_vbus);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_civoid cdns_set_vbus(struct cdns *cdns)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	u32 reg;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (cdns->version != CDNSP_CONTROLLER_V2)
12462306a36Sopenharmony_ci		return;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	reg = readl(&cdns->otg_cdnsp_regs->override);
12762306a36Sopenharmony_ci	reg &= ~OVERRIDE_SESS_VLD_SEL;
12862306a36Sopenharmony_ci	writel(reg, &cdns->otg_cdnsp_regs->override);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdns_set_vbus);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cibool cdns_is_host(struct cdns *cdns)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	if (cdns->dr_mode == USB_DR_MODE_HOST)
13562306a36Sopenharmony_ci		return true;
13662306a36Sopenharmony_ci	else if (cdns_get_id(cdns) == CDNS3_ID_HOST)
13762306a36Sopenharmony_ci		return true;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return false;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cibool cdns_is_device(struct cdns *cdns)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL)
14562306a36Sopenharmony_ci		return true;
14662306a36Sopenharmony_ci	else if (cdns->dr_mode == USB_DR_MODE_OTG)
14762306a36Sopenharmony_ci		if (cdns_get_id(cdns) == CDNS3_ID_PERIPHERAL)
14862306a36Sopenharmony_ci			return true;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return false;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/**
15462306a36Sopenharmony_ci * cdns_otg_disable_irq - Disable all OTG interrupts
15562306a36Sopenharmony_ci * @cdns: Pointer to controller context structure
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_cistatic void cdns_otg_disable_irq(struct cdns *cdns)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	if (cdns->version)
16062306a36Sopenharmony_ci		writel(0, &cdns->otg_irq_regs->ien);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/**
16462306a36Sopenharmony_ci * cdns_otg_enable_irq - enable id and sess_valid interrupts
16562306a36Sopenharmony_ci * @cdns: Pointer to controller context structure
16662306a36Sopenharmony_ci */
16762306a36Sopenharmony_cistatic void cdns_otg_enable_irq(struct cdns *cdns)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT |
17062306a36Sopenharmony_ci	       OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_irq_regs->ien);
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci/**
17462306a36Sopenharmony_ci * cdns_drd_host_on - start host.
17562306a36Sopenharmony_ci * @cdns: Pointer to controller context structure.
17662306a36Sopenharmony_ci *
17762306a36Sopenharmony_ci * Returns 0 on success otherwise negative errno.
17862306a36Sopenharmony_ci */
17962306a36Sopenharmony_ciint cdns_drd_host_on(struct cdns *cdns)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	u32 val, ready_bit;
18262306a36Sopenharmony_ci	int ret;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* Enable host mode. */
18562306a36Sopenharmony_ci	writel(OTGCMD_HOST_BUS_REQ | OTGCMD_OTG_DIS,
18662306a36Sopenharmony_ci	       &cdns->otg_regs->cmd);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (cdns->version == CDNSP_CONTROLLER_V2)
18962306a36Sopenharmony_ci		ready_bit = OTGSTS_CDNSP_XHCI_READY;
19062306a36Sopenharmony_ci	else
19162306a36Sopenharmony_ci		ready_bit = OTGSTS_CDNS3_XHCI_READY;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n");
19462306a36Sopenharmony_ci	ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
19562306a36Sopenharmony_ci					val & ready_bit, 1, 100000);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (ret)
19862306a36Sopenharmony_ci		dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_HOST);
20162306a36Sopenharmony_ci	phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST);
20262306a36Sopenharmony_ci	return ret;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci/**
20662306a36Sopenharmony_ci * cdns_drd_host_off - stop host.
20762306a36Sopenharmony_ci * @cdns: Pointer to controller context structure.
20862306a36Sopenharmony_ci */
20962306a36Sopenharmony_civoid cdns_drd_host_off(struct cdns *cdns)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	u32 val;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
21462306a36Sopenharmony_ci	       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
21562306a36Sopenharmony_ci	       &cdns->otg_regs->cmd);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* Waiting till H_IDLE state.*/
21862306a36Sopenharmony_ci	readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
21962306a36Sopenharmony_ci				  !(val & OTGSTATE_HOST_STATE_MASK),
22062306a36Sopenharmony_ci				  1, 2000000);
22162306a36Sopenharmony_ci	phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID);
22262306a36Sopenharmony_ci	phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci/**
22662306a36Sopenharmony_ci * cdns_drd_gadget_on - start gadget.
22762306a36Sopenharmony_ci * @cdns: Pointer to controller context structure.
22862306a36Sopenharmony_ci *
22962306a36Sopenharmony_ci * Returns 0 on success otherwise negative errno
23062306a36Sopenharmony_ci */
23162306a36Sopenharmony_ciint cdns_drd_gadget_on(struct cdns *cdns)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	u32 reg = OTGCMD_OTG_DIS;
23462306a36Sopenharmony_ci	u32 ready_bit;
23562306a36Sopenharmony_ci	int ret, val;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/* switch OTG core */
23862306a36Sopenharmony_ci	writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n");
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (cdns->version == CDNSP_CONTROLLER_V2)
24362306a36Sopenharmony_ci		ready_bit = OTGSTS_CDNSP_DEV_READY;
24462306a36Sopenharmony_ci	else
24562306a36Sopenharmony_ci		ready_bit = OTGSTS_CDNS3_DEV_READY;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
24862306a36Sopenharmony_ci					val & ready_bit, 1, 100000);
24962306a36Sopenharmony_ci	if (ret) {
25062306a36Sopenharmony_ci		dev_err(cdns->dev, "timeout waiting for dev_ready\n");
25162306a36Sopenharmony_ci		return ret;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_DEVICE);
25562306a36Sopenharmony_ci	phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE);
25662306a36Sopenharmony_ci	return 0;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdns_drd_gadget_on);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci/**
26162306a36Sopenharmony_ci * cdns_drd_gadget_off - stop gadget.
26262306a36Sopenharmony_ci * @cdns: Pointer to controller context structure.
26362306a36Sopenharmony_ci */
26462306a36Sopenharmony_civoid cdns_drd_gadget_off(struct cdns *cdns)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	u32 val;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/*
26962306a36Sopenharmony_ci	 * Driver should wait at least 10us after disabling Device
27062306a36Sopenharmony_ci	 * before turning-off Device (DEV_BUS_DROP).
27162306a36Sopenharmony_ci	 */
27262306a36Sopenharmony_ci	usleep_range(20, 30);
27362306a36Sopenharmony_ci	writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
27462306a36Sopenharmony_ci	       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
27562306a36Sopenharmony_ci	       &cdns->otg_regs->cmd);
27662306a36Sopenharmony_ci	/* Waiting till DEV_IDLE state.*/
27762306a36Sopenharmony_ci	readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
27862306a36Sopenharmony_ci				  !(val & OTGSTATE_DEV_STATE_MASK),
27962306a36Sopenharmony_ci				  1, 2000000);
28062306a36Sopenharmony_ci	phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID);
28162306a36Sopenharmony_ci	phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdns_drd_gadget_off);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/**
28662306a36Sopenharmony_ci * cdns_init_otg_mode - initialize drd controller
28762306a36Sopenharmony_ci * @cdns: Pointer to controller context structure
28862306a36Sopenharmony_ci *
28962306a36Sopenharmony_ci * Returns 0 on success otherwise negative errno
29062306a36Sopenharmony_ci */
29162306a36Sopenharmony_cistatic int cdns_init_otg_mode(struct cdns *cdns)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	int ret;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	cdns_otg_disable_irq(cdns);
29662306a36Sopenharmony_ci	/* clear all interrupts */
29762306a36Sopenharmony_ci	writel(~0, &cdns->otg_irq_regs->ivect);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	ret = cdns_set_mode(cdns, USB_DR_MODE_OTG);
30062306a36Sopenharmony_ci	if (ret)
30162306a36Sopenharmony_ci		return ret;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	cdns_otg_enable_irq(cdns);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	return 0;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci/**
30962306a36Sopenharmony_ci * cdns_drd_update_mode - initialize mode of operation
31062306a36Sopenharmony_ci * @cdns: Pointer to controller context structure
31162306a36Sopenharmony_ci *
31262306a36Sopenharmony_ci * Returns 0 on success otherwise negative errno
31362306a36Sopenharmony_ci */
31462306a36Sopenharmony_ciint cdns_drd_update_mode(struct cdns *cdns)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	int ret;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	switch (cdns->dr_mode) {
31962306a36Sopenharmony_ci	case USB_DR_MODE_PERIPHERAL:
32062306a36Sopenharmony_ci		ret = cdns_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
32162306a36Sopenharmony_ci		break;
32262306a36Sopenharmony_ci	case USB_DR_MODE_HOST:
32362306a36Sopenharmony_ci		ret = cdns_set_mode(cdns, USB_DR_MODE_HOST);
32462306a36Sopenharmony_ci		break;
32562306a36Sopenharmony_ci	case USB_DR_MODE_OTG:
32662306a36Sopenharmony_ci		ret = cdns_init_otg_mode(cdns);
32762306a36Sopenharmony_ci		break;
32862306a36Sopenharmony_ci	default:
32962306a36Sopenharmony_ci		dev_err(cdns->dev, "Unsupported mode of operation %d\n",
33062306a36Sopenharmony_ci			cdns->dr_mode);
33162306a36Sopenharmony_ci		return -EINVAL;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return ret;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic irqreturn_t cdns_drd_thread_irq(int irq, void *data)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct cdns *cdns = data;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	cdns_hw_role_switch(cdns);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return IRQ_HANDLED;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/**
34762306a36Sopenharmony_ci * cdns_drd_irq - interrupt handler for OTG events
34862306a36Sopenharmony_ci *
34962306a36Sopenharmony_ci * @irq: irq number for cdns core device
35062306a36Sopenharmony_ci * @data: structure of cdns
35162306a36Sopenharmony_ci *
35262306a36Sopenharmony_ci * Returns IRQ_HANDLED or IRQ_NONE
35362306a36Sopenharmony_ci */
35462306a36Sopenharmony_cistatic irqreturn_t cdns_drd_irq(int irq, void *data)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
35762306a36Sopenharmony_ci	struct cdns *cdns = data;
35862306a36Sopenharmony_ci	u32 reg;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (cdns->dr_mode != USB_DR_MODE_OTG)
36162306a36Sopenharmony_ci		return IRQ_NONE;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (cdns->in_lpm)
36462306a36Sopenharmony_ci		return ret;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	reg = readl(&cdns->otg_irq_regs->ivect);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (!reg)
36962306a36Sopenharmony_ci		return IRQ_NONE;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (reg & OTGIEN_ID_CHANGE_INT) {
37262306a36Sopenharmony_ci		dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
37362306a36Sopenharmony_ci			cdns_get_id(cdns));
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		ret = IRQ_WAKE_THREAD;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (reg & (OTGIEN_VBUSVALID_RISE_INT | OTGIEN_VBUSVALID_FALL_INT)) {
37962306a36Sopenharmony_ci		dev_dbg(cdns->dev, "OTG IRQ: new VBUS: %d\n",
38062306a36Sopenharmony_ci			cdns_get_vbus(cdns));
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		ret = IRQ_WAKE_THREAD;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	writel(~0, &cdns->otg_irq_regs->ivect);
38662306a36Sopenharmony_ci	return ret;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ciint cdns_drd_init(struct cdns *cdns)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	void __iomem *regs;
39262306a36Sopenharmony_ci	u32 state;
39362306a36Sopenharmony_ci	int ret;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
39662306a36Sopenharmony_ci	if (IS_ERR(regs))
39762306a36Sopenharmony_ci		return PTR_ERR(regs);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/* Detection of DRD version. Controller has been released
40062306a36Sopenharmony_ci	 * in three versions. All are very similar and are software compatible,
40162306a36Sopenharmony_ci	 * but they have same changes in register maps.
40262306a36Sopenharmony_ci	 * The first register in oldest version is command register and it's
40362306a36Sopenharmony_ci	 * read only. Driver should read 0 from it. On the other hand, in v1
40462306a36Sopenharmony_ci	 * and v2 the first register contains device ID number which is not
40562306a36Sopenharmony_ci	 * set to 0. Driver uses this fact to detect the proper version of
40662306a36Sopenharmony_ci	 * controller.
40762306a36Sopenharmony_ci	 */
40862306a36Sopenharmony_ci	cdns->otg_v0_regs = regs;
40962306a36Sopenharmony_ci	if (!readl(&cdns->otg_v0_regs->cmd)) {
41062306a36Sopenharmony_ci		cdns->version  = CDNS3_CONTROLLER_V0;
41162306a36Sopenharmony_ci		cdns->otg_v1_regs = NULL;
41262306a36Sopenharmony_ci		cdns->otg_cdnsp_regs = NULL;
41362306a36Sopenharmony_ci		cdns->otg_regs = regs;
41462306a36Sopenharmony_ci		cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem  *)
41562306a36Sopenharmony_ci				     &cdns->otg_v0_regs->ien;
41662306a36Sopenharmony_ci		writel(1, &cdns->otg_v0_regs->simulate);
41762306a36Sopenharmony_ci		dev_dbg(cdns->dev, "DRD version v0 (%08x)\n",
41862306a36Sopenharmony_ci			 readl(&cdns->otg_v0_regs->version));
41962306a36Sopenharmony_ci	} else {
42062306a36Sopenharmony_ci		cdns->otg_v0_regs = NULL;
42162306a36Sopenharmony_ci		cdns->otg_v1_regs = regs;
42262306a36Sopenharmony_ci		cdns->otg_cdnsp_regs = regs;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		cdns->otg_regs = (void __iomem *)&cdns->otg_v1_regs->cmd;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		state = readl(&cdns->otg_cdnsp_regs->did);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		if (OTG_CDNSP_CHECK_DID(state)) {
42962306a36Sopenharmony_ci			cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *)
43062306a36Sopenharmony_ci					      &cdns->otg_cdnsp_regs->ien;
43162306a36Sopenharmony_ci			cdns->version  = CDNSP_CONTROLLER_V2;
43262306a36Sopenharmony_ci		} else if (OTG_CDNS3_CHECK_DID(state)) {
43362306a36Sopenharmony_ci			cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *)
43462306a36Sopenharmony_ci					      &cdns->otg_v1_regs->ien;
43562306a36Sopenharmony_ci			writel(1, &cdns->otg_v1_regs->simulate);
43662306a36Sopenharmony_ci			cdns->version  = CDNS3_CONTROLLER_V1;
43762306a36Sopenharmony_ci		} else {
43862306a36Sopenharmony_ci			dev_err(cdns->dev, "not supporte DID=0x%08x\n", state);
43962306a36Sopenharmony_ci			return -EINVAL;
44062306a36Sopenharmony_ci		}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		dev_dbg(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
44362306a36Sopenharmony_ci			 readl(&cdns->otg_v1_regs->did),
44462306a36Sopenharmony_ci			 readl(&cdns->otg_v1_regs->rid));
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	/* Update dr_mode according to STRAP configuration. */
45062306a36Sopenharmony_ci	cdns->dr_mode = USB_DR_MODE_OTG;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if ((cdns->version == CDNSP_CONTROLLER_V2 &&
45362306a36Sopenharmony_ci	     state == OTGSTS_CDNSP_STRAP_HOST) ||
45462306a36Sopenharmony_ci	    (cdns->version != CDNSP_CONTROLLER_V2 &&
45562306a36Sopenharmony_ci	     state == OTGSTS_STRAP_HOST)) {
45662306a36Sopenharmony_ci		dev_dbg(cdns->dev, "Controller strapped to HOST\n");
45762306a36Sopenharmony_ci		cdns->dr_mode = USB_DR_MODE_HOST;
45862306a36Sopenharmony_ci	} else if ((cdns->version == CDNSP_CONTROLLER_V2 &&
45962306a36Sopenharmony_ci		    state == OTGSTS_CDNSP_STRAP_GADGET) ||
46062306a36Sopenharmony_ci		   (cdns->version != CDNSP_CONTROLLER_V2 &&
46162306a36Sopenharmony_ci		    state == OTGSTS_STRAP_GADGET)) {
46262306a36Sopenharmony_ci		dev_dbg(cdns->dev, "Controller strapped to PERIPHERAL\n");
46362306a36Sopenharmony_ci		cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	ret = devm_request_threaded_irq(cdns->dev, cdns->otg_irq,
46762306a36Sopenharmony_ci					cdns_drd_irq,
46862306a36Sopenharmony_ci					cdns_drd_thread_irq,
46962306a36Sopenharmony_ci					IRQF_SHARED,
47062306a36Sopenharmony_ci					dev_name(cdns->dev), cdns);
47162306a36Sopenharmony_ci	if (ret) {
47262306a36Sopenharmony_ci		dev_err(cdns->dev, "couldn't get otg_irq\n");
47362306a36Sopenharmony_ci		return ret;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	state = readl(&cdns->otg_regs->sts);
47762306a36Sopenharmony_ci	if (OTGSTS_OTG_NRDY(state)) {
47862306a36Sopenharmony_ci		dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
47962306a36Sopenharmony_ci		return -ENODEV;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return 0;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ciint cdns_drd_exit(struct cdns *cdns)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	cdns_otg_disable_irq(cdns);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	return 0;
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci/* Indicate the cdns3 core was power lost before */
49362306a36Sopenharmony_cibool cdns_power_is_lost(struct cdns *cdns)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	if (cdns->version == CDNS3_CONTROLLER_V0) {
49662306a36Sopenharmony_ci		if (!(readl(&cdns->otg_v0_regs->simulate) & BIT(0)))
49762306a36Sopenharmony_ci			return true;
49862306a36Sopenharmony_ci	} else {
49962306a36Sopenharmony_ci		if (!(readl(&cdns->otg_v1_regs->simulate) & BIT(0)))
50062306a36Sopenharmony_ci			return true;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci	return false;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdns_power_is_lost);
505