162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * core.c - ChipIdea USB IP core family device controller
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
662306a36Sopenharmony_ci * Copyright (C) 2020 NXP
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Author: David Lopo
962306a36Sopenharmony_ci *	   Peter Chen <peter.chen@nxp.com>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Main Features:
1262306a36Sopenharmony_ci * - Four transfers are supported, usbtest is passed
1362306a36Sopenharmony_ci * - USB Certification for gadget: CH9 and Mass Storage are passed
1462306a36Sopenharmony_ci * - Low power mode
1562306a36Sopenharmony_ci * - USB wakeup
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci#include <linux/delay.h>
1862306a36Sopenharmony_ci#include <linux/device.h>
1962306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2062306a36Sopenharmony_ci#include <linux/extcon.h>
2162306a36Sopenharmony_ci#include <linux/phy/phy.h>
2262306a36Sopenharmony_ci#include <linux/platform_device.h>
2362306a36Sopenharmony_ci#include <linux/module.h>
2462306a36Sopenharmony_ci#include <linux/idr.h>
2562306a36Sopenharmony_ci#include <linux/interrupt.h>
2662306a36Sopenharmony_ci#include <linux/io.h>
2762306a36Sopenharmony_ci#include <linux/kernel.h>
2862306a36Sopenharmony_ci#include <linux/slab.h>
2962306a36Sopenharmony_ci#include <linux/pm_runtime.h>
3062306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h>
3162306a36Sopenharmony_ci#include <linux/usb/ch9.h>
3262306a36Sopenharmony_ci#include <linux/usb/gadget.h>
3362306a36Sopenharmony_ci#include <linux/usb/otg.h>
3462306a36Sopenharmony_ci#include <linux/usb/chipidea.h>
3562306a36Sopenharmony_ci#include <linux/usb/of.h>
3662306a36Sopenharmony_ci#include <linux/of.h>
3762306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
3862306a36Sopenharmony_ci#include <linux/usb/ehci_def.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include "ci.h"
4162306a36Sopenharmony_ci#include "udc.h"
4262306a36Sopenharmony_ci#include "bits.h"
4362306a36Sopenharmony_ci#include "host.h"
4462306a36Sopenharmony_ci#include "otg.h"
4562306a36Sopenharmony_ci#include "otg_fsm.h"
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Controller register map */
4862306a36Sopenharmony_cistatic const u8 ci_regs_nolpm[] = {
4962306a36Sopenharmony_ci	[CAP_CAPLENGTH]		= 0x00U,
5062306a36Sopenharmony_ci	[CAP_HCCPARAMS]		= 0x08U,
5162306a36Sopenharmony_ci	[CAP_DCCPARAMS]		= 0x24U,
5262306a36Sopenharmony_ci	[CAP_TESTMODE]		= 0x38U,
5362306a36Sopenharmony_ci	[OP_USBCMD]		= 0x00U,
5462306a36Sopenharmony_ci	[OP_USBSTS]		= 0x04U,
5562306a36Sopenharmony_ci	[OP_USBINTR]		= 0x08U,
5662306a36Sopenharmony_ci	[OP_FRINDEX]		= 0x0CU,
5762306a36Sopenharmony_ci	[OP_DEVICEADDR]		= 0x14U,
5862306a36Sopenharmony_ci	[OP_ENDPTLISTADDR]	= 0x18U,
5962306a36Sopenharmony_ci	[OP_TTCTRL]		= 0x1CU,
6062306a36Sopenharmony_ci	[OP_BURSTSIZE]		= 0x20U,
6162306a36Sopenharmony_ci	[OP_ULPI_VIEWPORT]	= 0x30U,
6262306a36Sopenharmony_ci	[OP_PORTSC]		= 0x44U,
6362306a36Sopenharmony_ci	[OP_DEVLC]		= 0x84U,
6462306a36Sopenharmony_ci	[OP_OTGSC]		= 0x64U,
6562306a36Sopenharmony_ci	[OP_USBMODE]		= 0x68U,
6662306a36Sopenharmony_ci	[OP_ENDPTSETUPSTAT]	= 0x6CU,
6762306a36Sopenharmony_ci	[OP_ENDPTPRIME]		= 0x70U,
6862306a36Sopenharmony_ci	[OP_ENDPTFLUSH]		= 0x74U,
6962306a36Sopenharmony_ci	[OP_ENDPTSTAT]		= 0x78U,
7062306a36Sopenharmony_ci	[OP_ENDPTCOMPLETE]	= 0x7CU,
7162306a36Sopenharmony_ci	[OP_ENDPTCTRL]		= 0x80U,
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic const u8 ci_regs_lpm[] = {
7562306a36Sopenharmony_ci	[CAP_CAPLENGTH]		= 0x00U,
7662306a36Sopenharmony_ci	[CAP_HCCPARAMS]		= 0x08U,
7762306a36Sopenharmony_ci	[CAP_DCCPARAMS]		= 0x24U,
7862306a36Sopenharmony_ci	[CAP_TESTMODE]		= 0xFCU,
7962306a36Sopenharmony_ci	[OP_USBCMD]		= 0x00U,
8062306a36Sopenharmony_ci	[OP_USBSTS]		= 0x04U,
8162306a36Sopenharmony_ci	[OP_USBINTR]		= 0x08U,
8262306a36Sopenharmony_ci	[OP_FRINDEX]		= 0x0CU,
8362306a36Sopenharmony_ci	[OP_DEVICEADDR]		= 0x14U,
8462306a36Sopenharmony_ci	[OP_ENDPTLISTADDR]	= 0x18U,
8562306a36Sopenharmony_ci	[OP_TTCTRL]		= 0x1CU,
8662306a36Sopenharmony_ci	[OP_BURSTSIZE]		= 0x20U,
8762306a36Sopenharmony_ci	[OP_ULPI_VIEWPORT]	= 0x30U,
8862306a36Sopenharmony_ci	[OP_PORTSC]		= 0x44U,
8962306a36Sopenharmony_ci	[OP_DEVLC]		= 0x84U,
9062306a36Sopenharmony_ci	[OP_OTGSC]		= 0xC4U,
9162306a36Sopenharmony_ci	[OP_USBMODE]		= 0xC8U,
9262306a36Sopenharmony_ci	[OP_ENDPTSETUPSTAT]	= 0xD8U,
9362306a36Sopenharmony_ci	[OP_ENDPTPRIME]		= 0xDCU,
9462306a36Sopenharmony_ci	[OP_ENDPTFLUSH]		= 0xE0U,
9562306a36Sopenharmony_ci	[OP_ENDPTSTAT]		= 0xE4U,
9662306a36Sopenharmony_ci	[OP_ENDPTCOMPLETE]	= 0xE8U,
9762306a36Sopenharmony_ci	[OP_ENDPTCTRL]		= 0xECU,
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic void hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	int i;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	for (i = 0; i < OP_ENDPTCTRL; i++)
10562306a36Sopenharmony_ci		ci->hw_bank.regmap[i] =
10662306a36Sopenharmony_ci			(i <= CAP_LAST ? ci->hw_bank.cap : ci->hw_bank.op) +
10762306a36Sopenharmony_ci			(is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	for (; i <= OP_LAST; i++)
11062306a36Sopenharmony_ci		ci->hw_bank.regmap[i] = ci->hw_bank.op +
11162306a36Sopenharmony_ci			4 * (i - OP_ENDPTCTRL) +
11262306a36Sopenharmony_ci			(is_lpm
11362306a36Sopenharmony_ci			 ? ci_regs_lpm[OP_ENDPTCTRL]
11462306a36Sopenharmony_ci			 : ci_regs_nolpm[OP_ENDPTCTRL]);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic enum ci_revision ci_get_revision(struct ci_hdrc *ci)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	int ver = hw_read_id_reg(ci, ID_ID, VERSION) >> __ffs(VERSION);
12162306a36Sopenharmony_ci	enum ci_revision rev = CI_REVISION_UNKNOWN;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (ver == 0x2) {
12462306a36Sopenharmony_ci		rev = hw_read_id_reg(ci, ID_ID, REVISION)
12562306a36Sopenharmony_ci			>> __ffs(REVISION);
12662306a36Sopenharmony_ci		rev += CI_REVISION_20;
12762306a36Sopenharmony_ci	} else if (ver == 0x0) {
12862306a36Sopenharmony_ci		rev = CI_REVISION_1X;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return rev;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/**
13562306a36Sopenharmony_ci * hw_read_intr_enable: returns interrupt enable register
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci * @ci: the controller
13862306a36Sopenharmony_ci *
13962306a36Sopenharmony_ci * This function returns register data
14062306a36Sopenharmony_ci */
14162306a36Sopenharmony_ciu32 hw_read_intr_enable(struct ci_hdrc *ci)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	return hw_read(ci, OP_USBINTR, ~0);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci/**
14762306a36Sopenharmony_ci * hw_read_intr_status: returns interrupt status register
14862306a36Sopenharmony_ci *
14962306a36Sopenharmony_ci * @ci: the controller
15062306a36Sopenharmony_ci *
15162306a36Sopenharmony_ci * This function returns register data
15262306a36Sopenharmony_ci */
15362306a36Sopenharmony_ciu32 hw_read_intr_status(struct ci_hdrc *ci)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	return hw_read(ci, OP_USBSTS, ~0);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/**
15962306a36Sopenharmony_ci * hw_port_test_set: writes port test mode (execute without interruption)
16062306a36Sopenharmony_ci * @ci: the controller
16162306a36Sopenharmony_ci * @mode: new value
16262306a36Sopenharmony_ci *
16362306a36Sopenharmony_ci * This function returns an error code
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_ciint hw_port_test_set(struct ci_hdrc *ci, u8 mode)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	const u8 TEST_MODE_MAX = 7;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (mode > TEST_MODE_MAX)
17062306a36Sopenharmony_ci		return -EINVAL;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << __ffs(PORTSC_PTC));
17362306a36Sopenharmony_ci	return 0;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci/**
17762306a36Sopenharmony_ci * hw_port_test_get: reads port test mode value
17862306a36Sopenharmony_ci *
17962306a36Sopenharmony_ci * @ci: the controller
18062306a36Sopenharmony_ci *
18162306a36Sopenharmony_ci * This function returns port test mode value
18262306a36Sopenharmony_ci */
18362306a36Sopenharmony_ciu8 hw_port_test_get(struct ci_hdrc *ci)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic void hw_wait_phy_stable(void)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	/*
19162306a36Sopenharmony_ci	 * The phy needs some delay to output the stable status from low
19262306a36Sopenharmony_ci	 * power mode. And for OTGSC, the status inputs are debounced
19362306a36Sopenharmony_ci	 * using a 1 ms time constant, so, delay 2ms for controller to get
19462306a36Sopenharmony_ci	 * the stable status, like vbus and id when the phy leaves low power.
19562306a36Sopenharmony_ci	 */
19662306a36Sopenharmony_ci	usleep_range(2000, 2500);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci/* The PHY enters/leaves low power mode */
20062306a36Sopenharmony_cistatic void ci_hdrc_enter_lpm_common(struct ci_hdrc *ci, bool enable)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC;
20362306a36Sopenharmony_ci	bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm)));
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (enable && !lpm)
20662306a36Sopenharmony_ci		hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
20762306a36Sopenharmony_ci				PORTSC_PHCD(ci->hw_bank.lpm));
20862306a36Sopenharmony_ci	else if (!enable && lpm)
20962306a36Sopenharmony_ci		hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
21062306a36Sopenharmony_ci				0);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	return ci->platdata->enter_lpm(ci, enable);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	u32 reg;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* bank is a module variable */
22362306a36Sopenharmony_ci	ci->hw_bank.abs = base;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	ci->hw_bank.cap = ci->hw_bank.abs;
22662306a36Sopenharmony_ci	ci->hw_bank.cap += ci->platdata->capoffset;
22762306a36Sopenharmony_ci	ci->hw_bank.op = ci->hw_bank.cap + (ioread32(ci->hw_bank.cap) & 0xff);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	hw_alloc_regmap(ci, false);
23062306a36Sopenharmony_ci	reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >>
23162306a36Sopenharmony_ci		__ffs(HCCPARAMS_LEN);
23262306a36Sopenharmony_ci	ci->hw_bank.lpm  = reg;
23362306a36Sopenharmony_ci	if (reg)
23462306a36Sopenharmony_ci		hw_alloc_regmap(ci, !!reg);
23562306a36Sopenharmony_ci	ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs;
23662306a36Sopenharmony_ci	ci->hw_bank.size += OP_LAST;
23762306a36Sopenharmony_ci	ci->hw_bank.size /= sizeof(u32);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	reg = hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DEN) >>
24062306a36Sopenharmony_ci		__ffs(DCCPARAMS_DEN);
24162306a36Sopenharmony_ci	ci->hw_ep_max = reg * 2;   /* cache hw ENDPT_MAX */
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (ci->hw_ep_max > ENDPT_MAX)
24462306a36Sopenharmony_ci		return -ENODEV;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	ci_hdrc_enter_lpm(ci, false);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* Disable all interrupts bits */
24962306a36Sopenharmony_ci	hw_write(ci, OP_USBINTR, 0xffffffff, 0);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/* Clear all interrupts status bits*/
25262306a36Sopenharmony_ci	hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	ci->rev = ci_get_revision(ci);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	dev_dbg(ci->dev,
25762306a36Sopenharmony_ci		"revision: %d, lpm: %d; cap: %px op: %px\n",
25862306a36Sopenharmony_ci		ci->rev, ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* setup lock mode ? */
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	/* ENDPTSETUPSTAT is '0' by default */
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/* HCSPARAMS.bf.ppc SHOULD BE zero for device */
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	return 0;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_civoid hw_phymode_configure(struct ci_hdrc *ci)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	u32 portsc, lpm, sts = 0;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	switch (ci->platdata->phy_mode) {
27462306a36Sopenharmony_ci	case USBPHY_INTERFACE_MODE_UTMI:
27562306a36Sopenharmony_ci		portsc = PORTSC_PTS(PTS_UTMI);
27662306a36Sopenharmony_ci		lpm = DEVLC_PTS(PTS_UTMI);
27762306a36Sopenharmony_ci		break;
27862306a36Sopenharmony_ci	case USBPHY_INTERFACE_MODE_UTMIW:
27962306a36Sopenharmony_ci		portsc = PORTSC_PTS(PTS_UTMI) | PORTSC_PTW;
28062306a36Sopenharmony_ci		lpm = DEVLC_PTS(PTS_UTMI) | DEVLC_PTW;
28162306a36Sopenharmony_ci		break;
28262306a36Sopenharmony_ci	case USBPHY_INTERFACE_MODE_ULPI:
28362306a36Sopenharmony_ci		portsc = PORTSC_PTS(PTS_ULPI);
28462306a36Sopenharmony_ci		lpm = DEVLC_PTS(PTS_ULPI);
28562306a36Sopenharmony_ci		break;
28662306a36Sopenharmony_ci	case USBPHY_INTERFACE_MODE_SERIAL:
28762306a36Sopenharmony_ci		portsc = PORTSC_PTS(PTS_SERIAL);
28862306a36Sopenharmony_ci		lpm = DEVLC_PTS(PTS_SERIAL);
28962306a36Sopenharmony_ci		sts = 1;
29062306a36Sopenharmony_ci		break;
29162306a36Sopenharmony_ci	case USBPHY_INTERFACE_MODE_HSIC:
29262306a36Sopenharmony_ci		portsc = PORTSC_PTS(PTS_HSIC);
29362306a36Sopenharmony_ci		lpm = DEVLC_PTS(PTS_HSIC);
29462306a36Sopenharmony_ci		break;
29562306a36Sopenharmony_ci	default:
29662306a36Sopenharmony_ci		return;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	if (ci->hw_bank.lpm) {
30062306a36Sopenharmony_ci		hw_write(ci, OP_DEVLC, DEVLC_PTS(7) | DEVLC_PTW, lpm);
30162306a36Sopenharmony_ci		if (sts)
30262306a36Sopenharmony_ci			hw_write(ci, OP_DEVLC, DEVLC_STS, DEVLC_STS);
30362306a36Sopenharmony_ci	} else {
30462306a36Sopenharmony_ci		hw_write(ci, OP_PORTSC, PORTSC_PTS(7) | PORTSC_PTW, portsc);
30562306a36Sopenharmony_ci		if (sts)
30662306a36Sopenharmony_ci			hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS);
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hw_phymode_configure);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci/**
31262306a36Sopenharmony_ci * _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy
31362306a36Sopenharmony_ci * interfaces
31462306a36Sopenharmony_ci * @ci: the controller
31562306a36Sopenharmony_ci *
31662306a36Sopenharmony_ci * This function returns an error code if the phy failed to init
31762306a36Sopenharmony_ci */
31862306a36Sopenharmony_cistatic int _ci_usb_phy_init(struct ci_hdrc *ci)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	int ret;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (ci->phy) {
32362306a36Sopenharmony_ci		ret = phy_init(ci->phy);
32462306a36Sopenharmony_ci		if (ret)
32562306a36Sopenharmony_ci			return ret;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci		ret = phy_power_on(ci->phy);
32862306a36Sopenharmony_ci		if (ret) {
32962306a36Sopenharmony_ci			phy_exit(ci->phy);
33062306a36Sopenharmony_ci			return ret;
33162306a36Sopenharmony_ci		}
33262306a36Sopenharmony_ci	} else {
33362306a36Sopenharmony_ci		ret = usb_phy_init(ci->usb_phy);
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	return ret;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci/**
34062306a36Sopenharmony_ci * ci_usb_phy_exit: deinitialize phy taking in account both phy and usb_phy
34162306a36Sopenharmony_ci * interfaces
34262306a36Sopenharmony_ci * @ci: the controller
34362306a36Sopenharmony_ci */
34462306a36Sopenharmony_cistatic void ci_usb_phy_exit(struct ci_hdrc *ci)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL)
34762306a36Sopenharmony_ci		return;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	if (ci->phy) {
35062306a36Sopenharmony_ci		phy_power_off(ci->phy);
35162306a36Sopenharmony_ci		phy_exit(ci->phy);
35262306a36Sopenharmony_ci	} else {
35362306a36Sopenharmony_ci		usb_phy_shutdown(ci->usb_phy);
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci/**
35862306a36Sopenharmony_ci * ci_usb_phy_init: initialize phy according to different phy type
35962306a36Sopenharmony_ci * @ci: the controller
36062306a36Sopenharmony_ci *
36162306a36Sopenharmony_ci * This function returns an error code if usb_phy_init has failed
36262306a36Sopenharmony_ci */
36362306a36Sopenharmony_cistatic int ci_usb_phy_init(struct ci_hdrc *ci)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	int ret;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL)
36862306a36Sopenharmony_ci		return 0;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	switch (ci->platdata->phy_mode) {
37162306a36Sopenharmony_ci	case USBPHY_INTERFACE_MODE_UTMI:
37262306a36Sopenharmony_ci	case USBPHY_INTERFACE_MODE_UTMIW:
37362306a36Sopenharmony_ci	case USBPHY_INTERFACE_MODE_HSIC:
37462306a36Sopenharmony_ci		ret = _ci_usb_phy_init(ci);
37562306a36Sopenharmony_ci		if (!ret)
37662306a36Sopenharmony_ci			hw_wait_phy_stable();
37762306a36Sopenharmony_ci		else
37862306a36Sopenharmony_ci			return ret;
37962306a36Sopenharmony_ci		hw_phymode_configure(ci);
38062306a36Sopenharmony_ci		break;
38162306a36Sopenharmony_ci	case USBPHY_INTERFACE_MODE_ULPI:
38262306a36Sopenharmony_ci	case USBPHY_INTERFACE_MODE_SERIAL:
38362306a36Sopenharmony_ci		hw_phymode_configure(ci);
38462306a36Sopenharmony_ci		ret = _ci_usb_phy_init(ci);
38562306a36Sopenharmony_ci		if (ret)
38662306a36Sopenharmony_ci			return ret;
38762306a36Sopenharmony_ci		break;
38862306a36Sopenharmony_ci	default:
38962306a36Sopenharmony_ci		ret = _ci_usb_phy_init(ci);
39062306a36Sopenharmony_ci		if (!ret)
39162306a36Sopenharmony_ci			hw_wait_phy_stable();
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return ret;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci/**
39962306a36Sopenharmony_ci * ci_platform_configure: do controller configure
40062306a36Sopenharmony_ci * @ci: the controller
40162306a36Sopenharmony_ci *
40262306a36Sopenharmony_ci */
40362306a36Sopenharmony_civoid ci_platform_configure(struct ci_hdrc *ci)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	bool is_device_mode, is_host_mode;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	is_device_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_DC;
40862306a36Sopenharmony_ci	is_host_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_HC;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (is_device_mode) {
41162306a36Sopenharmony_ci		phy_set_mode(ci->phy, PHY_MODE_USB_DEVICE);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		if (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING)
41462306a36Sopenharmony_ci			hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS,
41562306a36Sopenharmony_ci				 USBMODE_CI_SDIS);
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (is_host_mode) {
41962306a36Sopenharmony_ci		phy_set_mode(ci->phy, PHY_MODE_USB_HOST);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		if (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING)
42262306a36Sopenharmony_ci			hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS,
42362306a36Sopenharmony_ci				 USBMODE_CI_SDIS);
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) {
42762306a36Sopenharmony_ci		if (ci->hw_bank.lpm)
42862306a36Sopenharmony_ci			hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC);
42962306a36Sopenharmony_ci		else
43062306a36Sopenharmony_ci			hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC);
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (ci->platdata->flags & CI_HDRC_SET_NON_ZERO_TTHA)
43462306a36Sopenharmony_ci		hw_write(ci, OP_TTCTRL, TTCTRL_TTHA_MASK, TTCTRL_TTHA);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	hw_write(ci, OP_USBCMD, 0xff0000, ci->platdata->itc_setting << 16);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (ci->platdata->flags & CI_HDRC_OVERRIDE_AHB_BURST)
43962306a36Sopenharmony_ci		hw_write_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK,
44062306a36Sopenharmony_ci			ci->platdata->ahb_burst_config);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	/* override burst size, take effect only when ahb_burst_config is 0 */
44362306a36Sopenharmony_ci	if (!hw_read_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK)) {
44462306a36Sopenharmony_ci		if (ci->platdata->flags & CI_HDRC_OVERRIDE_TX_BURST)
44562306a36Sopenharmony_ci			hw_write(ci, OP_BURSTSIZE, TX_BURST_MASK,
44662306a36Sopenharmony_ci			ci->platdata->tx_burst_size << __ffs(TX_BURST_MASK));
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		if (ci->platdata->flags & CI_HDRC_OVERRIDE_RX_BURST)
44962306a36Sopenharmony_ci			hw_write(ci, OP_BURSTSIZE, RX_BURST_MASK,
45062306a36Sopenharmony_ci				ci->platdata->rx_burst_size);
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci/**
45562306a36Sopenharmony_ci * hw_controller_reset: do controller reset
45662306a36Sopenharmony_ci * @ci: the controller
45762306a36Sopenharmony_ci  *
45862306a36Sopenharmony_ci * This function returns an error code
45962306a36Sopenharmony_ci */
46062306a36Sopenharmony_cistatic int hw_controller_reset(struct ci_hdrc *ci)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	int count = 0;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST);
46562306a36Sopenharmony_ci	while (hw_read(ci, OP_USBCMD, USBCMD_RST)) {
46662306a36Sopenharmony_ci		udelay(10);
46762306a36Sopenharmony_ci		if (count++ > 1000)
46862306a36Sopenharmony_ci			return -ETIMEDOUT;
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	return 0;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci/**
47562306a36Sopenharmony_ci * hw_device_reset: resets chip (execute without interruption)
47662306a36Sopenharmony_ci * @ci: the controller
47762306a36Sopenharmony_ci *
47862306a36Sopenharmony_ci * This function returns an error code
47962306a36Sopenharmony_ci */
48062306a36Sopenharmony_ciint hw_device_reset(struct ci_hdrc *ci)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	int ret;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	/* should flush & stop before reset */
48562306a36Sopenharmony_ci	hw_write(ci, OP_ENDPTFLUSH, ~0, ~0);
48662306a36Sopenharmony_ci	hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	ret = hw_controller_reset(ci);
48962306a36Sopenharmony_ci	if (ret) {
49062306a36Sopenharmony_ci		dev_err(ci->dev, "error resetting controller, ret=%d\n", ret);
49162306a36Sopenharmony_ci		return ret;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (ci->platdata->notify_event) {
49562306a36Sopenharmony_ci		ret = ci->platdata->notify_event(ci,
49662306a36Sopenharmony_ci			CI_HDRC_CONTROLLER_RESET_EVENT);
49762306a36Sopenharmony_ci		if (ret)
49862306a36Sopenharmony_ci			return ret;
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	/* USBMODE should be configured step by step */
50262306a36Sopenharmony_ci	hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
50362306a36Sopenharmony_ci	hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DC);
50462306a36Sopenharmony_ci	/* HW >= 2.3 */
50562306a36Sopenharmony_ci	hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) {
50862306a36Sopenharmony_ci		dev_err(ci->dev, "cannot enter in %s device mode\n",
50962306a36Sopenharmony_ci			ci_role(ci)->name);
51062306a36Sopenharmony_ci		dev_err(ci->dev, "lpm = %i\n", ci->hw_bank.lpm);
51162306a36Sopenharmony_ci		return -ENODEV;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	ci_platform_configure(ci);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	return 0;
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic irqreturn_t ci_irq_handler(int irq, void *data)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	struct ci_hdrc *ci = data;
52262306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
52362306a36Sopenharmony_ci	u32 otgsc = 0;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (ci->in_lpm) {
52662306a36Sopenharmony_ci		/*
52762306a36Sopenharmony_ci		 * If we already have a wakeup irq pending there,
52862306a36Sopenharmony_ci		 * let's just return to wait resume finished firstly.
52962306a36Sopenharmony_ci		 */
53062306a36Sopenharmony_ci		if (ci->wakeup_int)
53162306a36Sopenharmony_ci			return IRQ_HANDLED;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci		disable_irq_nosync(irq);
53462306a36Sopenharmony_ci		ci->wakeup_int = true;
53562306a36Sopenharmony_ci		pm_runtime_get(ci->dev);
53662306a36Sopenharmony_ci		return IRQ_HANDLED;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (ci->is_otg) {
54062306a36Sopenharmony_ci		otgsc = hw_read_otgsc(ci, ~0);
54162306a36Sopenharmony_ci		if (ci_otg_is_fsm_mode(ci)) {
54262306a36Sopenharmony_ci			ret = ci_otg_fsm_irq(ci);
54362306a36Sopenharmony_ci			if (ret == IRQ_HANDLED)
54462306a36Sopenharmony_ci				return ret;
54562306a36Sopenharmony_ci		}
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/*
54962306a36Sopenharmony_ci	 * Handle id change interrupt, it indicates device/host function
55062306a36Sopenharmony_ci	 * switch.
55162306a36Sopenharmony_ci	 */
55262306a36Sopenharmony_ci	if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
55362306a36Sopenharmony_ci		ci->id_event = true;
55462306a36Sopenharmony_ci		/* Clear ID change irq status */
55562306a36Sopenharmony_ci		hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
55662306a36Sopenharmony_ci		ci_otg_queue_work(ci);
55762306a36Sopenharmony_ci		return IRQ_HANDLED;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/*
56162306a36Sopenharmony_ci	 * Handle vbus change interrupt, it indicates device connection
56262306a36Sopenharmony_ci	 * and disconnection events.
56362306a36Sopenharmony_ci	 */
56462306a36Sopenharmony_ci	if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
56562306a36Sopenharmony_ci		ci->b_sess_valid_event = true;
56662306a36Sopenharmony_ci		/* Clear BSV irq */
56762306a36Sopenharmony_ci		hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
56862306a36Sopenharmony_ci		ci_otg_queue_work(ci);
56962306a36Sopenharmony_ci		return IRQ_HANDLED;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* Handle device/host interrupt */
57362306a36Sopenharmony_ci	if (ci->role != CI_ROLE_END)
57462306a36Sopenharmony_ci		ret = ci_role(ci)->irq(ci);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	return ret;
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_cistatic void ci_irq(struct ci_hdrc *ci)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	unsigned long flags;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	local_irq_save(flags);
58462306a36Sopenharmony_ci	ci_irq_handler(ci->irq, ci);
58562306a36Sopenharmony_ci	local_irq_restore(flags);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
58962306a36Sopenharmony_ci			     void *ptr)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct ci_hdrc_cable *cbl = container_of(nb, struct ci_hdrc_cable, nb);
59262306a36Sopenharmony_ci	struct ci_hdrc *ci = cbl->ci;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	cbl->connected = event;
59562306a36Sopenharmony_ci	cbl->changed = true;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	ci_irq(ci);
59862306a36Sopenharmony_ci	return NOTIFY_DONE;
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic enum usb_role ci_usb_role_switch_get(struct usb_role_switch *sw)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw);
60462306a36Sopenharmony_ci	enum usb_role role;
60562306a36Sopenharmony_ci	unsigned long flags;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	spin_lock_irqsave(&ci->lock, flags);
60862306a36Sopenharmony_ci	role = ci_role_to_usb_role(ci);
60962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ci->lock, flags);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	return role;
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistatic int ci_usb_role_switch_set(struct usb_role_switch *sw,
61562306a36Sopenharmony_ci				  enum usb_role role)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw);
61862306a36Sopenharmony_ci	struct ci_hdrc_cable *cable;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (role == USB_ROLE_HOST) {
62162306a36Sopenharmony_ci		cable = &ci->platdata->id_extcon;
62262306a36Sopenharmony_ci		cable->changed = true;
62362306a36Sopenharmony_ci		cable->connected = true;
62462306a36Sopenharmony_ci		cable = &ci->platdata->vbus_extcon;
62562306a36Sopenharmony_ci		cable->changed = true;
62662306a36Sopenharmony_ci		cable->connected = false;
62762306a36Sopenharmony_ci	} else if (role == USB_ROLE_DEVICE) {
62862306a36Sopenharmony_ci		cable = &ci->platdata->id_extcon;
62962306a36Sopenharmony_ci		cable->changed = true;
63062306a36Sopenharmony_ci		cable->connected = false;
63162306a36Sopenharmony_ci		cable = &ci->platdata->vbus_extcon;
63262306a36Sopenharmony_ci		cable->changed = true;
63362306a36Sopenharmony_ci		cable->connected = true;
63462306a36Sopenharmony_ci	} else {
63562306a36Sopenharmony_ci		cable = &ci->platdata->id_extcon;
63662306a36Sopenharmony_ci		cable->changed = true;
63762306a36Sopenharmony_ci		cable->connected = false;
63862306a36Sopenharmony_ci		cable = &ci->platdata->vbus_extcon;
63962306a36Sopenharmony_ci		cable->changed = true;
64062306a36Sopenharmony_ci		cable->connected = false;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	ci_irq(ci);
64462306a36Sopenharmony_ci	return 0;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic enum ci_role ci_get_role(struct ci_hdrc *ci)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	enum ci_role role;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
65262306a36Sopenharmony_ci		if (ci->is_otg) {
65362306a36Sopenharmony_ci			role = ci_otg_role(ci);
65462306a36Sopenharmony_ci			hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE);
65562306a36Sopenharmony_ci		} else {
65662306a36Sopenharmony_ci			/*
65762306a36Sopenharmony_ci			 * If the controller is not OTG capable, but support
65862306a36Sopenharmony_ci			 * role switch, the defalt role is gadget, and the
65962306a36Sopenharmony_ci			 * user can switch it through debugfs.
66062306a36Sopenharmony_ci			 */
66162306a36Sopenharmony_ci			role = CI_ROLE_GADGET;
66262306a36Sopenharmony_ci		}
66362306a36Sopenharmony_ci	} else {
66462306a36Sopenharmony_ci		role = ci->roles[CI_ROLE_HOST] ? CI_ROLE_HOST
66562306a36Sopenharmony_ci					: CI_ROLE_GADGET;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	return role;
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic struct usb_role_switch_desc ci_role_switch = {
67262306a36Sopenharmony_ci	.set = ci_usb_role_switch_set,
67362306a36Sopenharmony_ci	.get = ci_usb_role_switch_get,
67462306a36Sopenharmony_ci	.allow_userspace_control = true,
67562306a36Sopenharmony_ci};
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_cistatic int ci_get_platdata(struct device *dev,
67862306a36Sopenharmony_ci		struct ci_hdrc_platform_data *platdata)
67962306a36Sopenharmony_ci{
68062306a36Sopenharmony_ci	struct extcon_dev *ext_vbus, *ext_id;
68162306a36Sopenharmony_ci	struct ci_hdrc_cable *cable;
68262306a36Sopenharmony_ci	int ret;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if (!platdata->phy_mode)
68562306a36Sopenharmony_ci		platdata->phy_mode = of_usb_get_phy_mode(dev->of_node);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (!platdata->dr_mode)
68862306a36Sopenharmony_ci		platdata->dr_mode = usb_get_dr_mode(dev);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	if (platdata->dr_mode == USB_DR_MODE_UNKNOWN)
69162306a36Sopenharmony_ci		platdata->dr_mode = USB_DR_MODE_OTG;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) {
69462306a36Sopenharmony_ci		/* Get the vbus regulator */
69562306a36Sopenharmony_ci		platdata->reg_vbus = devm_regulator_get_optional(dev, "vbus");
69662306a36Sopenharmony_ci		if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) {
69762306a36Sopenharmony_ci			return -EPROBE_DEFER;
69862306a36Sopenharmony_ci		} else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) {
69962306a36Sopenharmony_ci			/* no vbus regulator is needed */
70062306a36Sopenharmony_ci			platdata->reg_vbus = NULL;
70162306a36Sopenharmony_ci		} else if (IS_ERR(platdata->reg_vbus)) {
70262306a36Sopenharmony_ci			dev_err(dev, "Getting regulator error: %ld\n",
70362306a36Sopenharmony_ci				PTR_ERR(platdata->reg_vbus));
70462306a36Sopenharmony_ci			return PTR_ERR(platdata->reg_vbus);
70562306a36Sopenharmony_ci		}
70662306a36Sopenharmony_ci		/* Get TPL support */
70762306a36Sopenharmony_ci		if (!platdata->tpl_support)
70862306a36Sopenharmony_ci			platdata->tpl_support =
70962306a36Sopenharmony_ci				of_usb_host_tpl_support(dev->of_node);
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	if (platdata->dr_mode == USB_DR_MODE_OTG) {
71362306a36Sopenharmony_ci		/* We can support HNP and SRP of OTG 2.0 */
71462306a36Sopenharmony_ci		platdata->ci_otg_caps.otg_rev = 0x0200;
71562306a36Sopenharmony_ci		platdata->ci_otg_caps.hnp_support = true;
71662306a36Sopenharmony_ci		platdata->ci_otg_caps.srp_support = true;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci		/* Update otg capabilities by DT properties */
71962306a36Sopenharmony_ci		ret = of_usb_update_otg_caps(dev->of_node,
72062306a36Sopenharmony_ci					&platdata->ci_otg_caps);
72162306a36Sopenharmony_ci		if (ret)
72262306a36Sopenharmony_ci			return ret;
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (usb_get_maximum_speed(dev) == USB_SPEED_FULL)
72662306a36Sopenharmony_ci		platdata->flags |= CI_HDRC_FORCE_FULLSPEED;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	of_property_read_u32(dev->of_node, "phy-clkgate-delay-us",
72962306a36Sopenharmony_ci				     &platdata->phy_clkgate_delay_us);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	platdata->itc_setting = 1;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	of_property_read_u32(dev->of_node, "itc-setting",
73462306a36Sopenharmony_ci					&platdata->itc_setting);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	ret = of_property_read_u32(dev->of_node, "ahb-burst-config",
73762306a36Sopenharmony_ci				&platdata->ahb_burst_config);
73862306a36Sopenharmony_ci	if (!ret) {
73962306a36Sopenharmony_ci		platdata->flags |= CI_HDRC_OVERRIDE_AHB_BURST;
74062306a36Sopenharmony_ci	} else if (ret != -EINVAL) {
74162306a36Sopenharmony_ci		dev_err(dev, "failed to get ahb-burst-config\n");
74262306a36Sopenharmony_ci		return ret;
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	ret = of_property_read_u32(dev->of_node, "tx-burst-size-dword",
74662306a36Sopenharmony_ci				&platdata->tx_burst_size);
74762306a36Sopenharmony_ci	if (!ret) {
74862306a36Sopenharmony_ci		platdata->flags |= CI_HDRC_OVERRIDE_TX_BURST;
74962306a36Sopenharmony_ci	} else if (ret != -EINVAL) {
75062306a36Sopenharmony_ci		dev_err(dev, "failed to get tx-burst-size-dword\n");
75162306a36Sopenharmony_ci		return ret;
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	ret = of_property_read_u32(dev->of_node, "rx-burst-size-dword",
75562306a36Sopenharmony_ci				&platdata->rx_burst_size);
75662306a36Sopenharmony_ci	if (!ret) {
75762306a36Sopenharmony_ci		platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST;
75862306a36Sopenharmony_ci	} else if (ret != -EINVAL) {
75962306a36Sopenharmony_ci		dev_err(dev, "failed to get rx-burst-size-dword\n");
76062306a36Sopenharmony_ci		return ret;
76162306a36Sopenharmony_ci	}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	if (of_property_read_bool(dev->of_node, "non-zero-ttctrl-ttha"))
76462306a36Sopenharmony_ci		platdata->flags |= CI_HDRC_SET_NON_ZERO_TTHA;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	ext_id = ERR_PTR(-ENODEV);
76762306a36Sopenharmony_ci	ext_vbus = ERR_PTR(-ENODEV);
76862306a36Sopenharmony_ci	if (of_property_read_bool(dev->of_node, "extcon")) {
76962306a36Sopenharmony_ci		/* Each one of them is not mandatory */
77062306a36Sopenharmony_ci		ext_vbus = extcon_get_edev_by_phandle(dev, 0);
77162306a36Sopenharmony_ci		if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV)
77262306a36Sopenharmony_ci			return PTR_ERR(ext_vbus);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci		ext_id = extcon_get_edev_by_phandle(dev, 1);
77562306a36Sopenharmony_ci		if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV)
77662306a36Sopenharmony_ci			return PTR_ERR(ext_id);
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	cable = &platdata->vbus_extcon;
78062306a36Sopenharmony_ci	cable->nb.notifier_call = ci_cable_notifier;
78162306a36Sopenharmony_ci	cable->edev = ext_vbus;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	if (!IS_ERR(ext_vbus)) {
78462306a36Sopenharmony_ci		ret = extcon_get_state(cable->edev, EXTCON_USB);
78562306a36Sopenharmony_ci		if (ret)
78662306a36Sopenharmony_ci			cable->connected = true;
78762306a36Sopenharmony_ci		else
78862306a36Sopenharmony_ci			cable->connected = false;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	cable = &platdata->id_extcon;
79262306a36Sopenharmony_ci	cable->nb.notifier_call = ci_cable_notifier;
79362306a36Sopenharmony_ci	cable->edev = ext_id;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if (!IS_ERR(ext_id)) {
79662306a36Sopenharmony_ci		ret = extcon_get_state(cable->edev, EXTCON_USB_HOST);
79762306a36Sopenharmony_ci		if (ret)
79862306a36Sopenharmony_ci			cable->connected = true;
79962306a36Sopenharmony_ci		else
80062306a36Sopenharmony_ci			cable->connected = false;
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	if (device_property_read_bool(dev, "usb-role-switch"))
80462306a36Sopenharmony_ci		ci_role_switch.fwnode = dev->fwnode;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	platdata->pctl = devm_pinctrl_get(dev);
80762306a36Sopenharmony_ci	if (!IS_ERR(platdata->pctl)) {
80862306a36Sopenharmony_ci		struct pinctrl_state *p;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci		p = pinctrl_lookup_state(platdata->pctl, "default");
81162306a36Sopenharmony_ci		if (!IS_ERR(p))
81262306a36Sopenharmony_ci			platdata->pins_default = p;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci		p = pinctrl_lookup_state(platdata->pctl, "host");
81562306a36Sopenharmony_ci		if (!IS_ERR(p))
81662306a36Sopenharmony_ci			platdata->pins_host = p;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci		p = pinctrl_lookup_state(platdata->pctl, "device");
81962306a36Sopenharmony_ci		if (!IS_ERR(p))
82062306a36Sopenharmony_ci			platdata->pins_device = p;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	if (!platdata->enter_lpm)
82462306a36Sopenharmony_ci		platdata->enter_lpm = ci_hdrc_enter_lpm_common;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	return 0;
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic int ci_extcon_register(struct ci_hdrc *ci)
83062306a36Sopenharmony_ci{
83162306a36Sopenharmony_ci	struct ci_hdrc_cable *id, *vbus;
83262306a36Sopenharmony_ci	int ret;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	id = &ci->platdata->id_extcon;
83562306a36Sopenharmony_ci	id->ci = ci;
83662306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(id->edev)) {
83762306a36Sopenharmony_ci		ret = devm_extcon_register_notifier(ci->dev, id->edev,
83862306a36Sopenharmony_ci						EXTCON_USB_HOST, &id->nb);
83962306a36Sopenharmony_ci		if (ret < 0) {
84062306a36Sopenharmony_ci			dev_err(ci->dev, "register ID failed\n");
84162306a36Sopenharmony_ci			return ret;
84262306a36Sopenharmony_ci		}
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	vbus = &ci->platdata->vbus_extcon;
84662306a36Sopenharmony_ci	vbus->ci = ci;
84762306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(vbus->edev)) {
84862306a36Sopenharmony_ci		ret = devm_extcon_register_notifier(ci->dev, vbus->edev,
84962306a36Sopenharmony_ci						EXTCON_USB, &vbus->nb);
85062306a36Sopenharmony_ci		if (ret < 0) {
85162306a36Sopenharmony_ci			dev_err(ci->dev, "register VBUS failed\n");
85262306a36Sopenharmony_ci			return ret;
85362306a36Sopenharmony_ci		}
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	return 0;
85762306a36Sopenharmony_ci}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_cistatic void ci_power_lost_work(struct work_struct *work)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	struct ci_hdrc *ci = container_of(work, struct ci_hdrc, power_lost_work);
86262306a36Sopenharmony_ci	enum ci_role role;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	disable_irq_nosync(ci->irq);
86562306a36Sopenharmony_ci	pm_runtime_get_sync(ci->dev);
86662306a36Sopenharmony_ci	if (!ci_otg_is_fsm_mode(ci)) {
86762306a36Sopenharmony_ci		role = ci_get_role(ci);
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci		if (ci->role != role) {
87062306a36Sopenharmony_ci			ci_handle_id_switch(ci);
87162306a36Sopenharmony_ci		} else if (role == CI_ROLE_GADGET) {
87262306a36Sopenharmony_ci			if (ci->is_otg && hw_read_otgsc(ci, OTGSC_BSV))
87362306a36Sopenharmony_ci				usb_gadget_vbus_connect(&ci->gadget);
87462306a36Sopenharmony_ci		}
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci	pm_runtime_put_sync(ci->dev);
87762306a36Sopenharmony_ci	enable_irq(ci->irq);
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic DEFINE_IDA(ci_ida);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cistruct platform_device *ci_hdrc_add_device(struct device *dev,
88362306a36Sopenharmony_ci			struct resource *res, int nres,
88462306a36Sopenharmony_ci			struct ci_hdrc_platform_data *platdata)
88562306a36Sopenharmony_ci{
88662306a36Sopenharmony_ci	struct platform_device *pdev;
88762306a36Sopenharmony_ci	int id, ret;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	ret = ci_get_platdata(dev, platdata);
89062306a36Sopenharmony_ci	if (ret)
89162306a36Sopenharmony_ci		return ERR_PTR(ret);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL);
89462306a36Sopenharmony_ci	if (id < 0)
89562306a36Sopenharmony_ci		return ERR_PTR(id);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	pdev = platform_device_alloc("ci_hdrc", id);
89862306a36Sopenharmony_ci	if (!pdev) {
89962306a36Sopenharmony_ci		ret = -ENOMEM;
90062306a36Sopenharmony_ci		goto put_id;
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	pdev->dev.parent = dev;
90462306a36Sopenharmony_ci	device_set_of_node_from_dev(&pdev->dev, dev);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	ret = platform_device_add_resources(pdev, res, nres);
90762306a36Sopenharmony_ci	if (ret)
90862306a36Sopenharmony_ci		goto err;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	ret = platform_device_add_data(pdev, platdata, sizeof(*platdata));
91162306a36Sopenharmony_ci	if (ret)
91262306a36Sopenharmony_ci		goto err;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	ret = platform_device_add(pdev);
91562306a36Sopenharmony_ci	if (ret)
91662306a36Sopenharmony_ci		goto err;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	return pdev;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cierr:
92162306a36Sopenharmony_ci	platform_device_put(pdev);
92262306a36Sopenharmony_ciput_id:
92362306a36Sopenharmony_ci	ida_simple_remove(&ci_ida, id);
92462306a36Sopenharmony_ci	return ERR_PTR(ret);
92562306a36Sopenharmony_ci}
92662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ci_hdrc_add_device);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_civoid ci_hdrc_remove_device(struct platform_device *pdev)
92962306a36Sopenharmony_ci{
93062306a36Sopenharmony_ci	int id = pdev->id;
93162306a36Sopenharmony_ci	platform_device_unregister(pdev);
93262306a36Sopenharmony_ci	ida_simple_remove(&ci_ida, id);
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ci_hdrc_remove_device);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci/**
93762306a36Sopenharmony_ci * ci_hdrc_query_available_role: get runtime available operation mode
93862306a36Sopenharmony_ci *
93962306a36Sopenharmony_ci * The glue layer can get current operation mode (host/peripheral/otg)
94062306a36Sopenharmony_ci * This function should be called after ci core device has created.
94162306a36Sopenharmony_ci *
94262306a36Sopenharmony_ci * @pdev: the platform device of ci core.
94362306a36Sopenharmony_ci *
94462306a36Sopenharmony_ci * Return runtime usb_dr_mode.
94562306a36Sopenharmony_ci */
94662306a36Sopenharmony_cienum usb_dr_mode ci_hdrc_query_available_role(struct platform_device *pdev)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	struct ci_hdrc *ci = platform_get_drvdata(pdev);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	if (!ci)
95162306a36Sopenharmony_ci		return USB_DR_MODE_UNKNOWN;
95262306a36Sopenharmony_ci	if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET])
95362306a36Sopenharmony_ci		return USB_DR_MODE_OTG;
95462306a36Sopenharmony_ci	else if (ci->roles[CI_ROLE_HOST])
95562306a36Sopenharmony_ci		return USB_DR_MODE_HOST;
95662306a36Sopenharmony_ci	else if (ci->roles[CI_ROLE_GADGET])
95762306a36Sopenharmony_ci		return USB_DR_MODE_PERIPHERAL;
95862306a36Sopenharmony_ci	else
95962306a36Sopenharmony_ci		return USB_DR_MODE_UNKNOWN;
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ci_hdrc_query_available_role);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_cistatic inline void ci_role_destroy(struct ci_hdrc *ci)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	ci_hdrc_gadget_destroy(ci);
96662306a36Sopenharmony_ci	ci_hdrc_host_destroy(ci);
96762306a36Sopenharmony_ci	if (ci->is_otg && ci->roles[CI_ROLE_GADGET])
96862306a36Sopenharmony_ci		ci_hdrc_otg_destroy(ci);
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_cistatic void ci_get_otg_capable(struct ci_hdrc *ci)
97262306a36Sopenharmony_ci{
97362306a36Sopenharmony_ci	if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG)
97462306a36Sopenharmony_ci		ci->is_otg = false;
97562306a36Sopenharmony_ci	else
97662306a36Sopenharmony_ci		ci->is_otg = (hw_read(ci, CAP_DCCPARAMS,
97762306a36Sopenharmony_ci				DCCPARAMS_DC | DCCPARAMS_HC)
97862306a36Sopenharmony_ci					== (DCCPARAMS_DC | DCCPARAMS_HC));
97962306a36Sopenharmony_ci	if (ci->is_otg) {
98062306a36Sopenharmony_ci		dev_dbg(ci->dev, "It is OTG capable controller\n");
98162306a36Sopenharmony_ci		/* Disable and clear all OTG irq */
98262306a36Sopenharmony_ci		hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
98362306a36Sopenharmony_ci							OTGSC_INT_STATUS_BITS);
98462306a36Sopenharmony_ci	}
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_cistatic ssize_t role_show(struct device *dev, struct device_attribute *attr,
98862306a36Sopenharmony_ci			  char *buf)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	struct ci_hdrc *ci = dev_get_drvdata(dev);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	if (ci->role != CI_ROLE_END)
99362306a36Sopenharmony_ci		return sprintf(buf, "%s\n", ci_role(ci)->name);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	return 0;
99662306a36Sopenharmony_ci}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_cistatic ssize_t role_store(struct device *dev,
99962306a36Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t n)
100062306a36Sopenharmony_ci{
100162306a36Sopenharmony_ci	struct ci_hdrc *ci = dev_get_drvdata(dev);
100262306a36Sopenharmony_ci	enum ci_role role;
100362306a36Sopenharmony_ci	int ret;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	if (!(ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET])) {
100662306a36Sopenharmony_ci		dev_warn(dev, "Current configuration is not dual-role, quit\n");
100762306a36Sopenharmony_ci		return -EPERM;
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++)
101162306a36Sopenharmony_ci		if (!strncmp(buf, ci->roles[role]->name,
101262306a36Sopenharmony_ci			     strlen(ci->roles[role]->name)))
101362306a36Sopenharmony_ci			break;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (role == CI_ROLE_END)
101662306a36Sopenharmony_ci		return -EINVAL;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	mutex_lock(&ci->mutex);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	if (role == ci->role) {
102162306a36Sopenharmony_ci		mutex_unlock(&ci->mutex);
102262306a36Sopenharmony_ci		return n;
102362306a36Sopenharmony_ci	}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	pm_runtime_get_sync(dev);
102662306a36Sopenharmony_ci	disable_irq(ci->irq);
102762306a36Sopenharmony_ci	ci_role_stop(ci);
102862306a36Sopenharmony_ci	ret = ci_role_start(ci, role);
102962306a36Sopenharmony_ci	if (!ret && ci->role == CI_ROLE_GADGET)
103062306a36Sopenharmony_ci		ci_handle_vbus_change(ci);
103162306a36Sopenharmony_ci	enable_irq(ci->irq);
103262306a36Sopenharmony_ci	pm_runtime_put_sync(dev);
103362306a36Sopenharmony_ci	mutex_unlock(&ci->mutex);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	return (ret == 0) ? n : ret;
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_cistatic DEVICE_ATTR_RW(role);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_cistatic struct attribute *ci_attrs[] = {
104062306a36Sopenharmony_ci	&dev_attr_role.attr,
104162306a36Sopenharmony_ci	NULL,
104262306a36Sopenharmony_ci};
104362306a36Sopenharmony_ciATTRIBUTE_GROUPS(ci);
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cistatic int ci_hdrc_probe(struct platform_device *pdev)
104662306a36Sopenharmony_ci{
104762306a36Sopenharmony_ci	struct device	*dev = &pdev->dev;
104862306a36Sopenharmony_ci	struct ci_hdrc	*ci;
104962306a36Sopenharmony_ci	struct resource	*res;
105062306a36Sopenharmony_ci	void __iomem	*base;
105162306a36Sopenharmony_ci	int		ret;
105262306a36Sopenharmony_ci	enum usb_dr_mode dr_mode;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	if (!dev_get_platdata(dev)) {
105562306a36Sopenharmony_ci		dev_err(dev, "platform data missing\n");
105662306a36Sopenharmony_ci		return -ENODEV;
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
106062306a36Sopenharmony_ci	if (IS_ERR(base))
106162306a36Sopenharmony_ci		return PTR_ERR(base);
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL);
106462306a36Sopenharmony_ci	if (!ci)
106562306a36Sopenharmony_ci		return -ENOMEM;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	spin_lock_init(&ci->lock);
106862306a36Sopenharmony_ci	mutex_init(&ci->mutex);
106962306a36Sopenharmony_ci	INIT_WORK(&ci->power_lost_work, ci_power_lost_work);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	ci->dev = dev;
107262306a36Sopenharmony_ci	ci->platdata = dev_get_platdata(dev);
107362306a36Sopenharmony_ci	ci->imx28_write_fix = !!(ci->platdata->flags &
107462306a36Sopenharmony_ci		CI_HDRC_IMX28_WRITE_FIX);
107562306a36Sopenharmony_ci	ci->supports_runtime_pm = !!(ci->platdata->flags &
107662306a36Sopenharmony_ci		CI_HDRC_SUPPORTS_RUNTIME_PM);
107762306a36Sopenharmony_ci	ci->has_portsc_pec_bug = !!(ci->platdata->flags &
107862306a36Sopenharmony_ci		CI_HDRC_HAS_PORTSC_PEC_MISSED);
107962306a36Sopenharmony_ci	platform_set_drvdata(pdev, ci);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	ret = hw_device_init(ci, base);
108262306a36Sopenharmony_ci	if (ret < 0) {
108362306a36Sopenharmony_ci		dev_err(dev, "can't initialize hardware\n");
108462306a36Sopenharmony_ci		return -ENODEV;
108562306a36Sopenharmony_ci	}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	ret = ci_ulpi_init(ci);
108862306a36Sopenharmony_ci	if (ret)
108962306a36Sopenharmony_ci		return ret;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	if (ci->platdata->phy) {
109262306a36Sopenharmony_ci		ci->phy = ci->platdata->phy;
109362306a36Sopenharmony_ci	} else if (ci->platdata->usb_phy) {
109462306a36Sopenharmony_ci		ci->usb_phy = ci->platdata->usb_phy;
109562306a36Sopenharmony_ci	} else {
109662306a36Sopenharmony_ci		/* Look for a generic PHY first */
109762306a36Sopenharmony_ci		ci->phy = devm_phy_get(dev->parent, "usb-phy");
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci		if (PTR_ERR(ci->phy) == -EPROBE_DEFER) {
110062306a36Sopenharmony_ci			ret = -EPROBE_DEFER;
110162306a36Sopenharmony_ci			goto ulpi_exit;
110262306a36Sopenharmony_ci		} else if (IS_ERR(ci->phy)) {
110362306a36Sopenharmony_ci			ci->phy = NULL;
110462306a36Sopenharmony_ci		}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci		/* Look for a legacy USB PHY from device-tree next */
110762306a36Sopenharmony_ci		if (!ci->phy) {
110862306a36Sopenharmony_ci			ci->usb_phy = devm_usb_get_phy_by_phandle(dev->parent,
110962306a36Sopenharmony_ci								  "phys", 0);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci			if (PTR_ERR(ci->usb_phy) == -EPROBE_DEFER) {
111262306a36Sopenharmony_ci				ret = -EPROBE_DEFER;
111362306a36Sopenharmony_ci				goto ulpi_exit;
111462306a36Sopenharmony_ci			} else if (IS_ERR(ci->usb_phy)) {
111562306a36Sopenharmony_ci				ci->usb_phy = NULL;
111662306a36Sopenharmony_ci			}
111762306a36Sopenharmony_ci		}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci		/* Look for any registered legacy USB PHY as last resort */
112062306a36Sopenharmony_ci		if (!ci->phy && !ci->usb_phy) {
112162306a36Sopenharmony_ci			ci->usb_phy = devm_usb_get_phy(dev->parent,
112262306a36Sopenharmony_ci						       USB_PHY_TYPE_USB2);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci			if (PTR_ERR(ci->usb_phy) == -EPROBE_DEFER) {
112562306a36Sopenharmony_ci				ret = -EPROBE_DEFER;
112662306a36Sopenharmony_ci				goto ulpi_exit;
112762306a36Sopenharmony_ci			} else if (IS_ERR(ci->usb_phy)) {
112862306a36Sopenharmony_ci				ci->usb_phy = NULL;
112962306a36Sopenharmony_ci			}
113062306a36Sopenharmony_ci		}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci		/* No USB PHY was found in the end */
113362306a36Sopenharmony_ci		if (!ci->phy && !ci->usb_phy) {
113462306a36Sopenharmony_ci			ret = -ENXIO;
113562306a36Sopenharmony_ci			goto ulpi_exit;
113662306a36Sopenharmony_ci		}
113762306a36Sopenharmony_ci	}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	ret = ci_usb_phy_init(ci);
114062306a36Sopenharmony_ci	if (ret) {
114162306a36Sopenharmony_ci		dev_err(dev, "unable to init phy: %d\n", ret);
114262306a36Sopenharmony_ci		goto ulpi_exit;
114362306a36Sopenharmony_ci	}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	ci->hw_bank.phys = res->start;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	ci->irq = platform_get_irq(pdev, 0);
114862306a36Sopenharmony_ci	if (ci->irq < 0) {
114962306a36Sopenharmony_ci		ret = ci->irq;
115062306a36Sopenharmony_ci		goto deinit_phy;
115162306a36Sopenharmony_ci	}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	ci_get_otg_capable(ci);
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	dr_mode = ci->platdata->dr_mode;
115662306a36Sopenharmony_ci	/* initialize role(s) before the interrupt is requested */
115762306a36Sopenharmony_ci	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
115862306a36Sopenharmony_ci		ret = ci_hdrc_host_init(ci);
115962306a36Sopenharmony_ci		if (ret) {
116062306a36Sopenharmony_ci			if (ret == -ENXIO)
116162306a36Sopenharmony_ci				dev_info(dev, "doesn't support host\n");
116262306a36Sopenharmony_ci			else
116362306a36Sopenharmony_ci				goto deinit_phy;
116462306a36Sopenharmony_ci		}
116562306a36Sopenharmony_ci	}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
116862306a36Sopenharmony_ci		ret = ci_hdrc_gadget_init(ci);
116962306a36Sopenharmony_ci		if (ret) {
117062306a36Sopenharmony_ci			if (ret == -ENXIO)
117162306a36Sopenharmony_ci				dev_info(dev, "doesn't support gadget\n");
117262306a36Sopenharmony_ci			else
117362306a36Sopenharmony_ci				goto deinit_host;
117462306a36Sopenharmony_ci		}
117562306a36Sopenharmony_ci	}
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) {
117862306a36Sopenharmony_ci		dev_err(dev, "no supported roles\n");
117962306a36Sopenharmony_ci		ret = -ENODEV;
118062306a36Sopenharmony_ci		goto deinit_gadget;
118162306a36Sopenharmony_ci	}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) {
118462306a36Sopenharmony_ci		ret = ci_hdrc_otg_init(ci);
118562306a36Sopenharmony_ci		if (ret) {
118662306a36Sopenharmony_ci			dev_err(dev, "init otg fails, ret = %d\n", ret);
118762306a36Sopenharmony_ci			goto deinit_gadget;
118862306a36Sopenharmony_ci		}
118962306a36Sopenharmony_ci	}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	if (ci_role_switch.fwnode) {
119262306a36Sopenharmony_ci		ci_role_switch.driver_data = ci;
119362306a36Sopenharmony_ci		ci->role_switch = usb_role_switch_register(dev,
119462306a36Sopenharmony_ci					&ci_role_switch);
119562306a36Sopenharmony_ci		if (IS_ERR(ci->role_switch)) {
119662306a36Sopenharmony_ci			ret = PTR_ERR(ci->role_switch);
119762306a36Sopenharmony_ci			goto deinit_otg;
119862306a36Sopenharmony_ci		}
119962306a36Sopenharmony_ci	}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	ci->role = ci_get_role(ci);
120262306a36Sopenharmony_ci	if (!ci_otg_is_fsm_mode(ci)) {
120362306a36Sopenharmony_ci		/* only update vbus status for peripheral */
120462306a36Sopenharmony_ci		if (ci->role == CI_ROLE_GADGET) {
120562306a36Sopenharmony_ci			/* Pull down DP for possible charger detection */
120662306a36Sopenharmony_ci			hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
120762306a36Sopenharmony_ci			ci_handle_vbus_change(ci);
120862306a36Sopenharmony_ci		}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci		ret = ci_role_start(ci, ci->role);
121162306a36Sopenharmony_ci		if (ret) {
121262306a36Sopenharmony_ci			dev_err(dev, "can't start %s role\n",
121362306a36Sopenharmony_ci						ci_role(ci)->name);
121462306a36Sopenharmony_ci			goto stop;
121562306a36Sopenharmony_ci		}
121662306a36Sopenharmony_ci	}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	ret = devm_request_irq(dev, ci->irq, ci_irq_handler, IRQF_SHARED,
121962306a36Sopenharmony_ci			ci->platdata->name, ci);
122062306a36Sopenharmony_ci	if (ret)
122162306a36Sopenharmony_ci		goto stop;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	ret = ci_extcon_register(ci);
122462306a36Sopenharmony_ci	if (ret)
122562306a36Sopenharmony_ci		goto stop;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	if (ci->supports_runtime_pm) {
122862306a36Sopenharmony_ci		pm_runtime_set_active(&pdev->dev);
122962306a36Sopenharmony_ci		pm_runtime_enable(&pdev->dev);
123062306a36Sopenharmony_ci		pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
123162306a36Sopenharmony_ci		pm_runtime_mark_last_busy(ci->dev);
123262306a36Sopenharmony_ci		pm_runtime_use_autosuspend(&pdev->dev);
123362306a36Sopenharmony_ci	}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	if (ci_otg_is_fsm_mode(ci))
123662306a36Sopenharmony_ci		ci_hdrc_otg_fsm_start(ci);
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	device_set_wakeup_capable(&pdev->dev, true);
123962306a36Sopenharmony_ci	dbg_create_files(ci);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	return 0;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_cistop:
124462306a36Sopenharmony_ci	if (ci->role_switch)
124562306a36Sopenharmony_ci		usb_role_switch_unregister(ci->role_switch);
124662306a36Sopenharmony_cideinit_otg:
124762306a36Sopenharmony_ci	if (ci->is_otg && ci->roles[CI_ROLE_GADGET])
124862306a36Sopenharmony_ci		ci_hdrc_otg_destroy(ci);
124962306a36Sopenharmony_cideinit_gadget:
125062306a36Sopenharmony_ci	ci_hdrc_gadget_destroy(ci);
125162306a36Sopenharmony_cideinit_host:
125262306a36Sopenharmony_ci	ci_hdrc_host_destroy(ci);
125362306a36Sopenharmony_cideinit_phy:
125462306a36Sopenharmony_ci	ci_usb_phy_exit(ci);
125562306a36Sopenharmony_ciulpi_exit:
125662306a36Sopenharmony_ci	ci_ulpi_exit(ci);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	return ret;
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_cistatic void ci_hdrc_remove(struct platform_device *pdev)
126262306a36Sopenharmony_ci{
126362306a36Sopenharmony_ci	struct ci_hdrc *ci = platform_get_drvdata(pdev);
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	if (ci->role_switch)
126662306a36Sopenharmony_ci		usb_role_switch_unregister(ci->role_switch);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	if (ci->supports_runtime_pm) {
126962306a36Sopenharmony_ci		pm_runtime_get_sync(&pdev->dev);
127062306a36Sopenharmony_ci		pm_runtime_disable(&pdev->dev);
127162306a36Sopenharmony_ci		pm_runtime_put_noidle(&pdev->dev);
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	dbg_remove_files(ci);
127562306a36Sopenharmony_ci	ci_role_destroy(ci);
127662306a36Sopenharmony_ci	ci_hdrc_enter_lpm(ci, true);
127762306a36Sopenharmony_ci	ci_usb_phy_exit(ci);
127862306a36Sopenharmony_ci	ci_ulpi_exit(ci);
127962306a36Sopenharmony_ci}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci#ifdef CONFIG_PM
128262306a36Sopenharmony_ci/* Prepare wakeup by SRP before suspend */
128362306a36Sopenharmony_cistatic void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci)
128462306a36Sopenharmony_ci{
128562306a36Sopenharmony_ci	if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
128662306a36Sopenharmony_ci				!hw_read_otgsc(ci, OTGSC_ID)) {
128762306a36Sopenharmony_ci		hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP,
128862306a36Sopenharmony_ci								PORTSC_PP);
128962306a36Sopenharmony_ci		hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_WKCN,
129062306a36Sopenharmony_ci								PORTSC_WKCN);
129162306a36Sopenharmony_ci	}
129262306a36Sopenharmony_ci}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci/* Handle SRP when wakeup by data pulse */
129562306a36Sopenharmony_cistatic void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci)
129662306a36Sopenharmony_ci{
129762306a36Sopenharmony_ci	if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
129862306a36Sopenharmony_ci		(ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) {
129962306a36Sopenharmony_ci		if (!hw_read_otgsc(ci, OTGSC_ID)) {
130062306a36Sopenharmony_ci			ci->fsm.a_srp_det = 1;
130162306a36Sopenharmony_ci			ci->fsm.a_bus_drop = 0;
130262306a36Sopenharmony_ci		} else {
130362306a36Sopenharmony_ci			ci->fsm.id = 1;
130462306a36Sopenharmony_ci		}
130562306a36Sopenharmony_ci		ci_otg_queue_work(ci);
130662306a36Sopenharmony_ci	}
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic void ci_controller_suspend(struct ci_hdrc *ci)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	disable_irq(ci->irq);
131262306a36Sopenharmony_ci	ci_hdrc_enter_lpm(ci, true);
131362306a36Sopenharmony_ci	if (ci->platdata->phy_clkgate_delay_us)
131462306a36Sopenharmony_ci		usleep_range(ci->platdata->phy_clkgate_delay_us,
131562306a36Sopenharmony_ci			     ci->platdata->phy_clkgate_delay_us + 50);
131662306a36Sopenharmony_ci	usb_phy_set_suspend(ci->usb_phy, 1);
131762306a36Sopenharmony_ci	ci->in_lpm = true;
131862306a36Sopenharmony_ci	enable_irq(ci->irq);
131962306a36Sopenharmony_ci}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci/*
132262306a36Sopenharmony_ci * Handle the wakeup interrupt triggered by extcon connector
132362306a36Sopenharmony_ci * We need to call ci_irq again for extcon since the first
132462306a36Sopenharmony_ci * interrupt (wakeup int) only let the controller be out of
132562306a36Sopenharmony_ci * low power mode, but not handle any interrupts.
132662306a36Sopenharmony_ci */
132762306a36Sopenharmony_cistatic void ci_extcon_wakeup_int(struct ci_hdrc *ci)
132862306a36Sopenharmony_ci{
132962306a36Sopenharmony_ci	struct ci_hdrc_cable *cable_id, *cable_vbus;
133062306a36Sopenharmony_ci	u32 otgsc = hw_read_otgsc(ci, ~0);
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	cable_id = &ci->platdata->id_extcon;
133362306a36Sopenharmony_ci	cable_vbus = &ci->platdata->vbus_extcon;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	if ((!IS_ERR(cable_id->edev) || ci->role_switch)
133662306a36Sopenharmony_ci		&& ci->is_otg &&
133762306a36Sopenharmony_ci		(otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS))
133862306a36Sopenharmony_ci		ci_irq(ci);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	if ((!IS_ERR(cable_vbus->edev) || ci->role_switch)
134162306a36Sopenharmony_ci		&& ci->is_otg &&
134262306a36Sopenharmony_ci		(otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS))
134362306a36Sopenharmony_ci		ci_irq(ci);
134462306a36Sopenharmony_ci}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_cistatic int ci_controller_resume(struct device *dev)
134762306a36Sopenharmony_ci{
134862306a36Sopenharmony_ci	struct ci_hdrc *ci = dev_get_drvdata(dev);
134962306a36Sopenharmony_ci	int ret;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	dev_dbg(dev, "at %s\n", __func__);
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	if (!ci->in_lpm) {
135462306a36Sopenharmony_ci		WARN_ON(1);
135562306a36Sopenharmony_ci		return 0;
135662306a36Sopenharmony_ci	}
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	ci_hdrc_enter_lpm(ci, false);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	ret = ci_ulpi_resume(ci);
136162306a36Sopenharmony_ci	if (ret)
136262306a36Sopenharmony_ci		return ret;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	if (ci->usb_phy) {
136562306a36Sopenharmony_ci		usb_phy_set_suspend(ci->usb_phy, 0);
136662306a36Sopenharmony_ci		usb_phy_set_wakeup(ci->usb_phy, false);
136762306a36Sopenharmony_ci		hw_wait_phy_stable();
136862306a36Sopenharmony_ci	}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	ci->in_lpm = false;
137162306a36Sopenharmony_ci	if (ci->wakeup_int) {
137262306a36Sopenharmony_ci		ci->wakeup_int = false;
137362306a36Sopenharmony_ci		pm_runtime_mark_last_busy(ci->dev);
137462306a36Sopenharmony_ci		pm_runtime_put_autosuspend(ci->dev);
137562306a36Sopenharmony_ci		enable_irq(ci->irq);
137662306a36Sopenharmony_ci		if (ci_otg_is_fsm_mode(ci))
137762306a36Sopenharmony_ci			ci_otg_fsm_wakeup_by_srp(ci);
137862306a36Sopenharmony_ci		ci_extcon_wakeup_int(ci);
137962306a36Sopenharmony_ci	}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	return 0;
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
138562306a36Sopenharmony_cistatic int ci_suspend(struct device *dev)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	struct ci_hdrc *ci = dev_get_drvdata(dev);
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	if (ci->wq)
139062306a36Sopenharmony_ci		flush_workqueue(ci->wq);
139162306a36Sopenharmony_ci	/*
139262306a36Sopenharmony_ci	 * Controller needs to be active during suspend, otherwise the core
139362306a36Sopenharmony_ci	 * may run resume when the parent is at suspend if other driver's
139462306a36Sopenharmony_ci	 * suspend fails, it occurs before parent's suspend has not started,
139562306a36Sopenharmony_ci	 * but the core suspend has finished.
139662306a36Sopenharmony_ci	 */
139762306a36Sopenharmony_ci	if (ci->in_lpm)
139862306a36Sopenharmony_ci		pm_runtime_resume(dev);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	if (ci->in_lpm) {
140162306a36Sopenharmony_ci		WARN_ON(1);
140262306a36Sopenharmony_ci		return 0;
140362306a36Sopenharmony_ci	}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	/* Extra routine per role before system suspend */
140662306a36Sopenharmony_ci	if (ci->role != CI_ROLE_END && ci_role(ci)->suspend)
140762306a36Sopenharmony_ci		ci_role(ci)->suspend(ci);
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	if (device_may_wakeup(dev)) {
141062306a36Sopenharmony_ci		if (ci_otg_is_fsm_mode(ci))
141162306a36Sopenharmony_ci			ci_otg_fsm_suspend_for_srp(ci);
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci		usb_phy_set_wakeup(ci->usb_phy, true);
141462306a36Sopenharmony_ci		enable_irq_wake(ci->irq);
141562306a36Sopenharmony_ci	}
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	ci_controller_suspend(ci);
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	return 0;
142062306a36Sopenharmony_ci}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_cistatic int ci_resume(struct device *dev)
142362306a36Sopenharmony_ci{
142462306a36Sopenharmony_ci	struct ci_hdrc *ci = dev_get_drvdata(dev);
142562306a36Sopenharmony_ci	bool power_lost;
142662306a36Sopenharmony_ci	int ret;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	/* Since ASYNCLISTADDR (host mode) and ENDPTLISTADDR (device
142962306a36Sopenharmony_ci	 * mode) share the same register address. We can check if
143062306a36Sopenharmony_ci	 * controller resume from power lost based on this address
143162306a36Sopenharmony_ci	 * due to this register will be reset after power lost.
143262306a36Sopenharmony_ci	 */
143362306a36Sopenharmony_ci	power_lost = !hw_read(ci, OP_ENDPTLISTADDR, ~0);
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	if (device_may_wakeup(dev))
143662306a36Sopenharmony_ci		disable_irq_wake(ci->irq);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	ret = ci_controller_resume(dev);
143962306a36Sopenharmony_ci	if (ret)
144062306a36Sopenharmony_ci		return ret;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	if (power_lost) {
144362306a36Sopenharmony_ci		/* shutdown and re-init for phy */
144462306a36Sopenharmony_ci		ci_usb_phy_exit(ci);
144562306a36Sopenharmony_ci		ci_usb_phy_init(ci);
144662306a36Sopenharmony_ci	}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	/* Extra routine per role after system resume */
144962306a36Sopenharmony_ci	if (ci->role != CI_ROLE_END && ci_role(ci)->resume)
145062306a36Sopenharmony_ci		ci_role(ci)->resume(ci, power_lost);
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	if (power_lost)
145362306a36Sopenharmony_ci		queue_work(system_freezable_wq, &ci->power_lost_work);
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	if (ci->supports_runtime_pm) {
145662306a36Sopenharmony_ci		pm_runtime_disable(dev);
145762306a36Sopenharmony_ci		pm_runtime_set_active(dev);
145862306a36Sopenharmony_ci		pm_runtime_enable(dev);
145962306a36Sopenharmony_ci	}
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	return ret;
146262306a36Sopenharmony_ci}
146362306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_cistatic int ci_runtime_suspend(struct device *dev)
146662306a36Sopenharmony_ci{
146762306a36Sopenharmony_ci	struct ci_hdrc *ci = dev_get_drvdata(dev);
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	dev_dbg(dev, "at %s\n", __func__);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	if (ci->in_lpm) {
147262306a36Sopenharmony_ci		WARN_ON(1);
147362306a36Sopenharmony_ci		return 0;
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	if (ci_otg_is_fsm_mode(ci))
147762306a36Sopenharmony_ci		ci_otg_fsm_suspend_for_srp(ci);
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	usb_phy_set_wakeup(ci->usb_phy, true);
148062306a36Sopenharmony_ci	ci_controller_suspend(ci);
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	return 0;
148362306a36Sopenharmony_ci}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_cistatic int ci_runtime_resume(struct device *dev)
148662306a36Sopenharmony_ci{
148762306a36Sopenharmony_ci	return ci_controller_resume(dev);
148862306a36Sopenharmony_ci}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci#endif /* CONFIG_PM */
149162306a36Sopenharmony_cistatic const struct dev_pm_ops ci_pm_ops = {
149262306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume)
149362306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(ci_runtime_suspend, ci_runtime_resume, NULL)
149462306a36Sopenharmony_ci};
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_cistatic struct platform_driver ci_hdrc_driver = {
149762306a36Sopenharmony_ci	.probe	= ci_hdrc_probe,
149862306a36Sopenharmony_ci	.remove_new = ci_hdrc_remove,
149962306a36Sopenharmony_ci	.driver	= {
150062306a36Sopenharmony_ci		.name	= "ci_hdrc",
150162306a36Sopenharmony_ci		.pm	= &ci_pm_ops,
150262306a36Sopenharmony_ci		.dev_groups = ci_groups,
150362306a36Sopenharmony_ci	},
150462306a36Sopenharmony_ci};
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_cistatic int __init ci_hdrc_platform_register(void)
150762306a36Sopenharmony_ci{
150862306a36Sopenharmony_ci	ci_hdrc_host_driver_init();
150962306a36Sopenharmony_ci	return platform_driver_register(&ci_hdrc_driver);
151062306a36Sopenharmony_ci}
151162306a36Sopenharmony_cimodule_init(ci_hdrc_platform_register);
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cistatic void __exit ci_hdrc_platform_unregister(void)
151462306a36Sopenharmony_ci{
151562306a36Sopenharmony_ci	platform_driver_unregister(&ci_hdrc_driver);
151662306a36Sopenharmony_ci}
151762306a36Sopenharmony_cimodule_exit(ci_hdrc_platform_unregister);
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ciMODULE_ALIAS("platform:ci_hdrc");
152062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
152162306a36Sopenharmony_ciMODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>");
152262306a36Sopenharmony_ciMODULE_DESCRIPTION("ChipIdea HDRC Driver");
1523