162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Allwinner sun4i USB phy driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014-2015 Hans de Goede <hdegoede@redhat.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Based on code from
862306a36Sopenharmony_ci * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Modelled after: Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver
1162306a36Sopenharmony_ci * Copyright (C) 2013 Samsung Electronics Co., Ltd.
1262306a36Sopenharmony_ci * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/clk.h>
1662306a36Sopenharmony_ci#include <linux/delay.h>
1762306a36Sopenharmony_ci#include <linux/err.h>
1862306a36Sopenharmony_ci#include <linux/extcon-provider.h>
1962306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2062306a36Sopenharmony_ci#include <linux/io.h>
2162306a36Sopenharmony_ci#include <linux/interrupt.h>
2262306a36Sopenharmony_ci#include <linux/kernel.h>
2362306a36Sopenharmony_ci#include <linux/module.h>
2462306a36Sopenharmony_ci#include <linux/mutex.h>
2562306a36Sopenharmony_ci#include <linux/of.h>
2662306a36Sopenharmony_ci#include <linux/of_gpio.h>
2762306a36Sopenharmony_ci#include <linux/phy/phy.h>
2862306a36Sopenharmony_ci#include <linux/phy/phy-sun4i-usb.h>
2962306a36Sopenharmony_ci#include <linux/platform_device.h>
3062306a36Sopenharmony_ci#include <linux/power_supply.h>
3162306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
3262306a36Sopenharmony_ci#include <linux/reset.h>
3362306a36Sopenharmony_ci#include <linux/spinlock.h>
3462306a36Sopenharmony_ci#include <linux/usb/of.h>
3562306a36Sopenharmony_ci#include <linux/workqueue.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define REG_ISCR			0x00
3862306a36Sopenharmony_ci#define REG_PHYCTL_A10			0x04
3962306a36Sopenharmony_ci#define REG_PHYBIST			0x08
4062306a36Sopenharmony_ci#define REG_PHYTUNE			0x0c
4162306a36Sopenharmony_ci#define REG_PHYCTL_A33			0x10
4262306a36Sopenharmony_ci#define REG_PHY_OTGCTL			0x20
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define REG_HCI_PHY_CTL			0x10
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define PHYCTL_DATA			BIT(7)
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define OTGCTL_ROUTE_MUSB		BIT(0)
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define SUNXI_AHB_ICHR8_EN		BIT(10)
5162306a36Sopenharmony_ci#define SUNXI_AHB_INCR4_BURST_EN	BIT(9)
5262306a36Sopenharmony_ci#define SUNXI_AHB_INCRX_ALIGN_EN	BIT(8)
5362306a36Sopenharmony_ci#define SUNXI_ULPI_BYPASS_EN		BIT(0)
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* ISCR, Interface Status and Control bits */
5662306a36Sopenharmony_ci#define ISCR_ID_PULLUP_EN		(1 << 17)
5762306a36Sopenharmony_ci#define ISCR_DPDM_PULLUP_EN	(1 << 16)
5862306a36Sopenharmony_ci/* sunxi has the phy id/vbus pins not connected, so we use the force bits */
5962306a36Sopenharmony_ci#define ISCR_FORCE_ID_MASK	(3 << 14)
6062306a36Sopenharmony_ci#define ISCR_FORCE_ID_LOW		(2 << 14)
6162306a36Sopenharmony_ci#define ISCR_FORCE_ID_HIGH	(3 << 14)
6262306a36Sopenharmony_ci#define ISCR_FORCE_VBUS_MASK	(3 << 12)
6362306a36Sopenharmony_ci#define ISCR_FORCE_VBUS_LOW	(2 << 12)
6462306a36Sopenharmony_ci#define ISCR_FORCE_VBUS_HIGH	(3 << 12)
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* Common Control Bits for Both PHYs */
6762306a36Sopenharmony_ci#define PHY_PLL_BW			0x03
6862306a36Sopenharmony_ci#define PHY_RES45_CAL_EN		0x0c
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* Private Control Bits for Each PHY */
7162306a36Sopenharmony_ci#define PHY_TX_AMPLITUDE_TUNE		0x20
7262306a36Sopenharmony_ci#define PHY_TX_SLEWRATE_TUNE		0x22
7362306a36Sopenharmony_ci#define PHY_VBUSVALID_TH_SEL		0x25
7462306a36Sopenharmony_ci#define PHY_PULLUP_RES_SEL		0x27
7562306a36Sopenharmony_ci#define PHY_OTG_FUNC_EN			0x28
7662306a36Sopenharmony_ci#define PHY_VBUS_DET_EN			0x29
7762306a36Sopenharmony_ci#define PHY_DISCON_TH_SEL		0x2a
7862306a36Sopenharmony_ci#define PHY_SQUELCH_DETECT		0x3c
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/* A83T specific control bits for PHY0 */
8162306a36Sopenharmony_ci#define PHY_CTL_VBUSVLDEXT		BIT(5)
8262306a36Sopenharmony_ci#define PHY_CTL_SIDDQ			BIT(3)
8362306a36Sopenharmony_ci#define PHY_CTL_H3_SIDDQ		BIT(1)
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* A83T specific control bits for PHY2 HSIC */
8662306a36Sopenharmony_ci#define SUNXI_EHCI_HS_FORCE		BIT(20)
8762306a36Sopenharmony_ci#define SUNXI_HSIC_CONNECT_DET		BIT(17)
8862306a36Sopenharmony_ci#define SUNXI_HSIC_CONNECT_INT		BIT(16)
8962306a36Sopenharmony_ci#define SUNXI_HSIC			BIT(1)
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci#define MAX_PHYS			4
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/*
9462306a36Sopenharmony_ci * Note do not raise the debounce time, we must report Vusb high within 100ms
9562306a36Sopenharmony_ci * otherwise we get Vbus errors
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_ci#define DEBOUNCE_TIME			msecs_to_jiffies(50)
9862306a36Sopenharmony_ci#define POLL_TIME			msecs_to_jiffies(250)
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistruct sun4i_usb_phy_cfg {
10162306a36Sopenharmony_ci	int num_phys;
10262306a36Sopenharmony_ci	int hsic_index;
10362306a36Sopenharmony_ci	u32 disc_thresh;
10462306a36Sopenharmony_ci	u32 hci_phy_ctl_clear;
10562306a36Sopenharmony_ci	u8 phyctl_offset;
10662306a36Sopenharmony_ci	bool dedicated_clocks;
10762306a36Sopenharmony_ci	bool phy0_dual_route;
10862306a36Sopenharmony_ci	bool needs_phy2_siddq;
10962306a36Sopenharmony_ci	bool siddq_in_base;
11062306a36Sopenharmony_ci	bool poll_vbusen;
11162306a36Sopenharmony_ci	int missing_phys;
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistruct sun4i_usb_phy_data {
11562306a36Sopenharmony_ci	void __iomem *base;
11662306a36Sopenharmony_ci	const struct sun4i_usb_phy_cfg *cfg;
11762306a36Sopenharmony_ci	enum usb_dr_mode dr_mode;
11862306a36Sopenharmony_ci	spinlock_t reg_lock; /* guard access to phyctl reg */
11962306a36Sopenharmony_ci	struct sun4i_usb_phy {
12062306a36Sopenharmony_ci		struct phy *phy;
12162306a36Sopenharmony_ci		void __iomem *pmu;
12262306a36Sopenharmony_ci		struct regulator *vbus;
12362306a36Sopenharmony_ci		struct reset_control *reset;
12462306a36Sopenharmony_ci		struct clk *clk;
12562306a36Sopenharmony_ci		struct clk *clk2;
12662306a36Sopenharmony_ci		bool regulator_on;
12762306a36Sopenharmony_ci		int index;
12862306a36Sopenharmony_ci	} phys[MAX_PHYS];
12962306a36Sopenharmony_ci	/* phy0 / otg related variables */
13062306a36Sopenharmony_ci	struct extcon_dev *extcon;
13162306a36Sopenharmony_ci	bool phy0_init;
13262306a36Sopenharmony_ci	struct gpio_desc *id_det_gpio;
13362306a36Sopenharmony_ci	struct gpio_desc *vbus_det_gpio;
13462306a36Sopenharmony_ci	struct power_supply *vbus_power_supply;
13562306a36Sopenharmony_ci	struct notifier_block vbus_power_nb;
13662306a36Sopenharmony_ci	bool vbus_power_nb_registered;
13762306a36Sopenharmony_ci	bool force_session_end;
13862306a36Sopenharmony_ci	int id_det_irq;
13962306a36Sopenharmony_ci	int vbus_det_irq;
14062306a36Sopenharmony_ci	int id_det;
14162306a36Sopenharmony_ci	int vbus_det;
14262306a36Sopenharmony_ci	struct delayed_work detect;
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci#define to_sun4i_usb_phy_data(phy) \
14662306a36Sopenharmony_ci	container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
15162306a36Sopenharmony_ci	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
15262306a36Sopenharmony_ci	u32 iscr;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	iscr = readl(data->base + REG_ISCR);
15562306a36Sopenharmony_ci	iscr &= ~clr;
15662306a36Sopenharmony_ci	iscr |= set;
15762306a36Sopenharmony_ci	writel(iscr, data->base + REG_ISCR);
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	if (val)
16362306a36Sopenharmony_ci		val = ISCR_FORCE_ID_HIGH;
16462306a36Sopenharmony_ci	else
16562306a36Sopenharmony_ci		val = ISCR_FORCE_ID_LOW;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	if (val)
17362306a36Sopenharmony_ci		val = ISCR_FORCE_VBUS_HIGH;
17462306a36Sopenharmony_ci	else
17562306a36Sopenharmony_ci		val = ISCR_FORCE_VBUS_LOW;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
18162306a36Sopenharmony_ci				int len)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
18462306a36Sopenharmony_ci	u32 temp, usbc_bit = BIT(phy->index * 2);
18562306a36Sopenharmony_ci	void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset;
18662306a36Sopenharmony_ci	unsigned long flags;
18762306a36Sopenharmony_ci	int i;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	spin_lock_irqsave(&phy_data->reg_lock, flags);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (phy_data->cfg->phyctl_offset == REG_PHYCTL_A33) {
19262306a36Sopenharmony_ci		/* SoCs newer than A33 need us to set phyctl to 0 explicitly */
19362306a36Sopenharmony_ci		writel(0, phyctl);
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
19762306a36Sopenharmony_ci		temp = readl(phyctl);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		/* clear the address portion */
20062306a36Sopenharmony_ci		temp &= ~(0xff << 8);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		/* set the address */
20362306a36Sopenharmony_ci		temp |= ((addr + i) << 8);
20462306a36Sopenharmony_ci		writel(temp, phyctl);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		/* set the data bit and clear usbc bit*/
20762306a36Sopenharmony_ci		temp = readb(phyctl);
20862306a36Sopenharmony_ci		if (data & 0x1)
20962306a36Sopenharmony_ci			temp |= PHYCTL_DATA;
21062306a36Sopenharmony_ci		else
21162306a36Sopenharmony_ci			temp &= ~PHYCTL_DATA;
21262306a36Sopenharmony_ci		temp &= ~usbc_bit;
21362306a36Sopenharmony_ci		writeb(temp, phyctl);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		/* pulse usbc_bit */
21662306a36Sopenharmony_ci		temp = readb(phyctl);
21762306a36Sopenharmony_ci		temp |= usbc_bit;
21862306a36Sopenharmony_ci		writeb(temp, phyctl);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		temp = readb(phyctl);
22162306a36Sopenharmony_ci		temp &= ~usbc_bit;
22262306a36Sopenharmony_ci		writeb(temp, phyctl);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		data >>= 1;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	spin_unlock_irqrestore(&phy_data->reg_lock, flags);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
23362306a36Sopenharmony_ci	u32 bits, reg_value;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (!phy->pmu)
23662306a36Sopenharmony_ci		return;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
23962306a36Sopenharmony_ci		SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* A83T USB2 is HSIC */
24262306a36Sopenharmony_ci	if (phy_data->cfg->hsic_index &&
24362306a36Sopenharmony_ci	    phy->index == phy_data->cfg->hsic_index)
24462306a36Sopenharmony_ci		bits |= SUNXI_EHCI_HS_FORCE | SUNXI_HSIC_CONNECT_INT |
24562306a36Sopenharmony_ci			SUNXI_HSIC;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	reg_value = readl(phy->pmu);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (enable)
25062306a36Sopenharmony_ci		reg_value |= bits;
25162306a36Sopenharmony_ci	else
25262306a36Sopenharmony_ci		reg_value &= ~bits;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	writel(reg_value, phy->pmu);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic int sun4i_usb_phy_init(struct phy *_phy)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
26062306a36Sopenharmony_ci	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
26162306a36Sopenharmony_ci	int ret;
26262306a36Sopenharmony_ci	u32 val;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	ret = clk_prepare_enable(phy->clk);
26562306a36Sopenharmony_ci	if (ret)
26662306a36Sopenharmony_ci		return ret;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	ret = clk_prepare_enable(phy->clk2);
26962306a36Sopenharmony_ci	if (ret) {
27062306a36Sopenharmony_ci		clk_disable_unprepare(phy->clk);
27162306a36Sopenharmony_ci		return ret;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	ret = reset_control_deassert(phy->reset);
27562306a36Sopenharmony_ci	if (ret) {
27662306a36Sopenharmony_ci		clk_disable_unprepare(phy->clk2);
27762306a36Sopenharmony_ci		clk_disable_unprepare(phy->clk);
27862306a36Sopenharmony_ci		return ret;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* Some PHYs on some SoCs need the help of PHY2 to work. */
28262306a36Sopenharmony_ci	if (data->cfg->needs_phy2_siddq && phy->index != 2) {
28362306a36Sopenharmony_ci		struct sun4i_usb_phy *phy2 = &data->phys[2];
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		ret = clk_prepare_enable(phy2->clk);
28662306a36Sopenharmony_ci		if (ret) {
28762306a36Sopenharmony_ci			reset_control_assert(phy->reset);
28862306a36Sopenharmony_ci			clk_disable_unprepare(phy->clk2);
28962306a36Sopenharmony_ci			clk_disable_unprepare(phy->clk);
29062306a36Sopenharmony_ci			return ret;
29162306a36Sopenharmony_ci		}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		ret = reset_control_deassert(phy2->reset);
29462306a36Sopenharmony_ci		if (ret) {
29562306a36Sopenharmony_ci			clk_disable_unprepare(phy2->clk);
29662306a36Sopenharmony_ci			reset_control_assert(phy->reset);
29762306a36Sopenharmony_ci			clk_disable_unprepare(phy->clk2);
29862306a36Sopenharmony_ci			clk_disable_unprepare(phy->clk);
29962306a36Sopenharmony_ci			return ret;
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		/*
30362306a36Sopenharmony_ci		 * This extra clock is just needed to access the
30462306a36Sopenharmony_ci		 * REG_HCI_PHY_CTL PMU register for PHY2.
30562306a36Sopenharmony_ci		 */
30662306a36Sopenharmony_ci		ret = clk_prepare_enable(phy2->clk2);
30762306a36Sopenharmony_ci		if (ret) {
30862306a36Sopenharmony_ci			reset_control_assert(phy2->reset);
30962306a36Sopenharmony_ci			clk_disable_unprepare(phy2->clk);
31062306a36Sopenharmony_ci			reset_control_assert(phy->reset);
31162306a36Sopenharmony_ci			clk_disable_unprepare(phy->clk2);
31262306a36Sopenharmony_ci			clk_disable_unprepare(phy->clk);
31362306a36Sopenharmony_ci			return ret;
31462306a36Sopenharmony_ci		}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		if (phy2->pmu && data->cfg->hci_phy_ctl_clear) {
31762306a36Sopenharmony_ci			val = readl(phy2->pmu + REG_HCI_PHY_CTL);
31862306a36Sopenharmony_ci			val &= ~data->cfg->hci_phy_ctl_clear;
31962306a36Sopenharmony_ci			writel(val, phy2->pmu + REG_HCI_PHY_CTL);
32062306a36Sopenharmony_ci		}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		clk_disable_unprepare(phy->clk2);
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (phy->pmu && data->cfg->hci_phy_ctl_clear) {
32662306a36Sopenharmony_ci		val = readl(phy->pmu + REG_HCI_PHY_CTL);
32762306a36Sopenharmony_ci		val &= ~data->cfg->hci_phy_ctl_clear;
32862306a36Sopenharmony_ci		writel(val, phy->pmu + REG_HCI_PHY_CTL);
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (data->cfg->siddq_in_base) {
33262306a36Sopenharmony_ci		if (phy->index == 0) {
33362306a36Sopenharmony_ci			val = readl(data->base + data->cfg->phyctl_offset);
33462306a36Sopenharmony_ci			val |= PHY_CTL_VBUSVLDEXT;
33562306a36Sopenharmony_ci			val &= ~PHY_CTL_SIDDQ;
33662306a36Sopenharmony_ci			writel(val, data->base + data->cfg->phyctl_offset);
33762306a36Sopenharmony_ci		}
33862306a36Sopenharmony_ci	} else {
33962306a36Sopenharmony_ci		/* Enable USB 45 Ohm resistor calibration */
34062306a36Sopenharmony_ci		if (phy->index == 0)
34162306a36Sopenharmony_ci			sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		/* Adjust PHY's magnitude and rate */
34462306a36Sopenharmony_ci		sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		/* Disconnect threshold adjustment */
34762306a36Sopenharmony_ci		sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL,
34862306a36Sopenharmony_ci				    data->cfg->disc_thresh, 2);
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	sun4i_usb_phy_passby(phy, 1);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (phy->index == 0) {
35462306a36Sopenharmony_ci		data->phy0_init = true;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		/* Enable pull-ups */
35762306a36Sopenharmony_ci		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN);
35862306a36Sopenharmony_ci		sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		/* Force ISCR and cable state updates */
36162306a36Sopenharmony_ci		data->id_det = -1;
36262306a36Sopenharmony_ci		data->vbus_det = -1;
36362306a36Sopenharmony_ci		queue_delayed_work(system_wq, &data->detect, 0);
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return 0;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int sun4i_usb_phy_exit(struct phy *_phy)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
37262306a36Sopenharmony_ci	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (phy->index == 0) {
37562306a36Sopenharmony_ci		if (data->cfg->siddq_in_base) {
37662306a36Sopenharmony_ci			void __iomem *phyctl = data->base +
37762306a36Sopenharmony_ci				data->cfg->phyctl_offset;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci			writel(readl(phyctl) | PHY_CTL_SIDDQ, phyctl);
38062306a36Sopenharmony_ci		}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		/* Disable pull-ups */
38362306a36Sopenharmony_ci		sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0);
38462306a36Sopenharmony_ci		sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0);
38562306a36Sopenharmony_ci		data->phy0_init = false;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (data->cfg->needs_phy2_siddq && phy->index != 2) {
38962306a36Sopenharmony_ci		struct sun4i_usb_phy *phy2 = &data->phys[2];
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci		clk_disable_unprepare(phy2->clk);
39262306a36Sopenharmony_ci		reset_control_assert(phy2->reset);
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	sun4i_usb_phy_passby(phy, 0);
39662306a36Sopenharmony_ci	reset_control_assert(phy->reset);
39762306a36Sopenharmony_ci	clk_disable_unprepare(phy->clk2);
39862306a36Sopenharmony_ci	clk_disable_unprepare(phy->clk);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	return 0;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic int sun4i_usb_phy0_get_id_det(struct sun4i_usb_phy_data *data)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	switch (data->dr_mode) {
40662306a36Sopenharmony_ci	case USB_DR_MODE_OTG:
40762306a36Sopenharmony_ci		if (data->id_det_gpio)
40862306a36Sopenharmony_ci			return gpiod_get_value_cansleep(data->id_det_gpio);
40962306a36Sopenharmony_ci		else
41062306a36Sopenharmony_ci			return 1; /* Fallback to peripheral mode */
41162306a36Sopenharmony_ci	case USB_DR_MODE_HOST:
41262306a36Sopenharmony_ci		return 0;
41362306a36Sopenharmony_ci	case USB_DR_MODE_PERIPHERAL:
41462306a36Sopenharmony_ci	default:
41562306a36Sopenharmony_ci		return 1;
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	if (data->vbus_det_gpio)
42262306a36Sopenharmony_ci		return gpiod_get_value_cansleep(data->vbus_det_gpio);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	if (data->vbus_power_supply) {
42562306a36Sopenharmony_ci		union power_supply_propval val;
42662306a36Sopenharmony_ci		int r;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		r = power_supply_get_property(data->vbus_power_supply,
42962306a36Sopenharmony_ci					      POWER_SUPPLY_PROP_PRESENT, &val);
43062306a36Sopenharmony_ci		if (r == 0)
43162306a36Sopenharmony_ci			return val.intval;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	/* Fallback: report vbus as high */
43562306a36Sopenharmony_ci	return 1;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	return data->vbus_det_gpio || data->vbus_power_supply;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	if ((data->id_det_gpio && data->id_det_irq <= 0) ||
44662306a36Sopenharmony_ci	    (data->vbus_det_gpio && data->vbus_det_irq <= 0))
44762306a36Sopenharmony_ci		return true;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	/*
45062306a36Sopenharmony_ci	 * The A31/A23/A33 companion pmics (AXP221/AXP223) do not
45162306a36Sopenharmony_ci	 * generate vbus change interrupts when the board is driving
45262306a36Sopenharmony_ci	 * vbus using the N_VBUSEN pin on the pmic, so we must poll
45362306a36Sopenharmony_ci	 * when using the pmic for vbus-det _and_ we're driving vbus.
45462306a36Sopenharmony_ci	 */
45562306a36Sopenharmony_ci	if (data->cfg->poll_vbusen && data->vbus_power_supply &&
45662306a36Sopenharmony_ci	    data->phys[0].regulator_on)
45762306a36Sopenharmony_ci		return true;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	return false;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic int sun4i_usb_phy_power_on(struct phy *_phy)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
46562306a36Sopenharmony_ci	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
46662306a36Sopenharmony_ci	int ret;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	if (!phy->vbus || phy->regulator_on)
46962306a36Sopenharmony_ci		return 0;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* For phy0 only turn on Vbus if we don't have an ext. Vbus */
47262306a36Sopenharmony_ci	if (phy->index == 0 && sun4i_usb_phy0_have_vbus_det(data) &&
47362306a36Sopenharmony_ci				data->vbus_det) {
47462306a36Sopenharmony_ci		dev_warn(&_phy->dev, "External vbus detected, not enabling our own vbus\n");
47562306a36Sopenharmony_ci		return 0;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	ret = regulator_enable(phy->vbus);
47962306a36Sopenharmony_ci	if (ret)
48062306a36Sopenharmony_ci		return ret;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	phy->regulator_on = true;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	/* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */
48562306a36Sopenharmony_ci	if (phy->index == 0 && sun4i_usb_phy0_poll(data))
48662306a36Sopenharmony_ci		mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	return 0;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic int sun4i_usb_phy_power_off(struct phy *_phy)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
49462306a36Sopenharmony_ci	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (!phy->vbus || !phy->regulator_on)
49762306a36Sopenharmony_ci		return 0;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	regulator_disable(phy->vbus);
50062306a36Sopenharmony_ci	phy->regulator_on = false;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/*
50362306a36Sopenharmony_ci	 * phy0 vbus typically slowly discharges, sometimes this causes the
50462306a36Sopenharmony_ci	 * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan.
50562306a36Sopenharmony_ci	 */
50662306a36Sopenharmony_ci	if (phy->index == 0 && !sun4i_usb_phy0_poll(data))
50762306a36Sopenharmony_ci		mod_delayed_work(system_wq, &data->detect, POLL_TIME);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	return 0;
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic int sun4i_usb_phy_set_mode(struct phy *_phy,
51362306a36Sopenharmony_ci				  enum phy_mode mode, int submode)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
51662306a36Sopenharmony_ci	struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
51762306a36Sopenharmony_ci	int new_mode;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	if (phy->index != 0) {
52062306a36Sopenharmony_ci		if (mode == PHY_MODE_USB_HOST)
52162306a36Sopenharmony_ci			return 0;
52262306a36Sopenharmony_ci		return -EINVAL;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	switch (mode) {
52662306a36Sopenharmony_ci	case PHY_MODE_USB_HOST:
52762306a36Sopenharmony_ci		new_mode = USB_DR_MODE_HOST;
52862306a36Sopenharmony_ci		break;
52962306a36Sopenharmony_ci	case PHY_MODE_USB_DEVICE:
53062306a36Sopenharmony_ci		new_mode = USB_DR_MODE_PERIPHERAL;
53162306a36Sopenharmony_ci		break;
53262306a36Sopenharmony_ci	case PHY_MODE_USB_OTG:
53362306a36Sopenharmony_ci		new_mode = USB_DR_MODE_OTG;
53462306a36Sopenharmony_ci		break;
53562306a36Sopenharmony_ci	default:
53662306a36Sopenharmony_ci		return -EINVAL;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (new_mode != data->dr_mode) {
54062306a36Sopenharmony_ci		dev_info(&_phy->dev, "Changing dr_mode to %d\n", new_mode);
54162306a36Sopenharmony_ci		data->dr_mode = new_mode;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	data->id_det = -1; /* Force reprocessing of id */
54562306a36Sopenharmony_ci	data->force_session_end = true;
54662306a36Sopenharmony_ci	queue_delayed_work(system_wq, &data->detect, 0);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	return 0;
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_civoid sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2);
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sun4i_usb_phy_set_squelch_detect);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic const struct phy_ops sun4i_usb_phy_ops = {
56062306a36Sopenharmony_ci	.init		= sun4i_usb_phy_init,
56162306a36Sopenharmony_ci	.exit		= sun4i_usb_phy_exit,
56262306a36Sopenharmony_ci	.power_on	= sun4i_usb_phy_power_on,
56362306a36Sopenharmony_ci	.power_off	= sun4i_usb_phy_power_off,
56462306a36Sopenharmony_ci	.set_mode	= sun4i_usb_phy_set_mode,
56562306a36Sopenharmony_ci	.owner		= THIS_MODULE,
56662306a36Sopenharmony_ci};
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic void sun4i_usb_phy0_reroute(struct sun4i_usb_phy_data *data, int id_det)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	u32 regval;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	regval = readl(data->base + REG_PHY_OTGCTL);
57362306a36Sopenharmony_ci	if (id_det == 0) {
57462306a36Sopenharmony_ci		/* Host mode. Route phy0 to EHCI/OHCI */
57562306a36Sopenharmony_ci		regval &= ~OTGCTL_ROUTE_MUSB;
57662306a36Sopenharmony_ci	} else {
57762306a36Sopenharmony_ci		/* Peripheral mode. Route phy0 to MUSB */
57862306a36Sopenharmony_ci		regval |= OTGCTL_ROUTE_MUSB;
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci	writel(regval, data->base + REG_PHY_OTGCTL);
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct sun4i_usb_phy_data *data =
58662306a36Sopenharmony_ci		container_of(work, struct sun4i_usb_phy_data, detect.work);
58762306a36Sopenharmony_ci	struct phy *phy0 = data->phys[0].phy;
58862306a36Sopenharmony_ci	struct sun4i_usb_phy *phy;
58962306a36Sopenharmony_ci	bool force_session_end, id_notify = false, vbus_notify = false;
59062306a36Sopenharmony_ci	int id_det, vbus_det;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	if (!phy0)
59362306a36Sopenharmony_ci		return;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	phy = phy_get_drvdata(phy0);
59662306a36Sopenharmony_ci	id_det = sun4i_usb_phy0_get_id_det(data);
59762306a36Sopenharmony_ci	vbus_det = sun4i_usb_phy0_get_vbus_det(data);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	mutex_lock(&phy0->mutex);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if (!data->phy0_init) {
60262306a36Sopenharmony_ci		mutex_unlock(&phy0->mutex);
60362306a36Sopenharmony_ci		return;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	force_session_end = data->force_session_end;
60762306a36Sopenharmony_ci	data->force_session_end = false;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	if (id_det != data->id_det) {
61062306a36Sopenharmony_ci		/* id-change, force session end if we've no vbus detection */
61162306a36Sopenharmony_ci		if (data->dr_mode == USB_DR_MODE_OTG &&
61262306a36Sopenharmony_ci		    !sun4i_usb_phy0_have_vbus_det(data))
61362306a36Sopenharmony_ci			force_session_end = true;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		/* When entering host mode (id = 0) force end the session now */
61662306a36Sopenharmony_ci		if (force_session_end && id_det == 0) {
61762306a36Sopenharmony_ci			sun4i_usb_phy0_set_vbus_detect(phy0, 0);
61862306a36Sopenharmony_ci			msleep(200);
61962306a36Sopenharmony_ci			sun4i_usb_phy0_set_vbus_detect(phy0, 1);
62062306a36Sopenharmony_ci		}
62162306a36Sopenharmony_ci		sun4i_usb_phy0_set_id_detect(phy0, id_det);
62262306a36Sopenharmony_ci		data->id_det = id_det;
62362306a36Sopenharmony_ci		id_notify = true;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	if (vbus_det != data->vbus_det) {
62762306a36Sopenharmony_ci		sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
62862306a36Sopenharmony_ci		data->vbus_det = vbus_det;
62962306a36Sopenharmony_ci		vbus_notify = true;
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	mutex_unlock(&phy0->mutex);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	if (id_notify) {
63562306a36Sopenharmony_ci		extcon_set_state_sync(data->extcon, EXTCON_USB_HOST,
63662306a36Sopenharmony_ci					!id_det);
63762306a36Sopenharmony_ci		/* When leaving host mode force end the session here */
63862306a36Sopenharmony_ci		if (force_session_end && id_det == 1) {
63962306a36Sopenharmony_ci			mutex_lock(&phy0->mutex);
64062306a36Sopenharmony_ci			sun4i_usb_phy0_set_vbus_detect(phy0, 0);
64162306a36Sopenharmony_ci			msleep(1000);
64262306a36Sopenharmony_ci			sun4i_usb_phy0_set_vbus_detect(phy0, 1);
64362306a36Sopenharmony_ci			mutex_unlock(&phy0->mutex);
64462306a36Sopenharmony_ci		}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci		/* Enable PHY0 passby for host mode only. */
64762306a36Sopenharmony_ci		sun4i_usb_phy_passby(phy, !id_det);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci		/* Re-route PHY0 if necessary */
65062306a36Sopenharmony_ci		if (data->cfg->phy0_dual_route)
65162306a36Sopenharmony_ci			sun4i_usb_phy0_reroute(data, id_det);
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (vbus_notify)
65562306a36Sopenharmony_ci		extcon_set_state_sync(data->extcon, EXTCON_USB, vbus_det);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (sun4i_usb_phy0_poll(data))
65862306a36Sopenharmony_ci		queue_delayed_work(system_wq, &data->detect, POLL_TIME);
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	struct sun4i_usb_phy_data *data = dev_id;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	/* vbus or id changed, let the pins settle and then scan them */
66662306a36Sopenharmony_ci	mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	return IRQ_HANDLED;
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic int sun4i_usb_phy0_vbus_notify(struct notifier_block *nb,
67262306a36Sopenharmony_ci				      unsigned long val, void *v)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	struct sun4i_usb_phy_data *data =
67562306a36Sopenharmony_ci		container_of(nb, struct sun4i_usb_phy_data, vbus_power_nb);
67662306a36Sopenharmony_ci	struct power_supply *psy = v;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	/* Properties on the vbus_power_supply changed, scan vbus_det */
67962306a36Sopenharmony_ci	if (val == PSY_EVENT_PROP_CHANGED && psy == data->vbus_power_supply)
68062306a36Sopenharmony_ci		mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	return NOTIFY_OK;
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_cistatic struct phy *sun4i_usb_phy_xlate(struct device *dev,
68662306a36Sopenharmony_ci					struct of_phandle_args *args)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	if (args->args[0] >= data->cfg->num_phys)
69162306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (data->cfg->missing_phys & BIT(args->args[0]))
69462306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	return data->phys[args->args[0]].phy;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic void sun4i_usb_phy_remove(struct platform_device *pdev)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
70262306a36Sopenharmony_ci	struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (data->vbus_power_nb_registered)
70562306a36Sopenharmony_ci		power_supply_unreg_notifier(&data->vbus_power_nb);
70662306a36Sopenharmony_ci	if (data->id_det_irq > 0)
70762306a36Sopenharmony_ci		devm_free_irq(dev, data->id_det_irq, data);
70862306a36Sopenharmony_ci	if (data->vbus_det_irq > 0)
70962306a36Sopenharmony_ci		devm_free_irq(dev, data->vbus_det_irq, data);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	cancel_delayed_work_sync(&data->detect);
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic const unsigned int sun4i_usb_phy0_cable[] = {
71562306a36Sopenharmony_ci	EXTCON_USB,
71662306a36Sopenharmony_ci	EXTCON_USB_HOST,
71762306a36Sopenharmony_ci	EXTCON_NONE,
71862306a36Sopenharmony_ci};
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic int sun4i_usb_phy_probe(struct platform_device *pdev)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	struct sun4i_usb_phy_data *data;
72362306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
72462306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
72562306a36Sopenharmony_ci	struct phy_provider *phy_provider;
72662306a36Sopenharmony_ci	int i, ret;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
72962306a36Sopenharmony_ci	if (!data)
73062306a36Sopenharmony_ci		return -ENOMEM;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	spin_lock_init(&data->reg_lock);
73362306a36Sopenharmony_ci	INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
73462306a36Sopenharmony_ci	dev_set_drvdata(dev, data);
73562306a36Sopenharmony_ci	data->cfg = of_device_get_match_data(dev);
73662306a36Sopenharmony_ci	if (!data->cfg)
73762306a36Sopenharmony_ci		return -EINVAL;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	data->base = devm_platform_ioremap_resource_byname(pdev, "phy_ctrl");
74062306a36Sopenharmony_ci	if (IS_ERR(data->base))
74162306a36Sopenharmony_ci		return PTR_ERR(data->base);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	data->id_det_gpio = devm_gpiod_get_optional(dev, "usb0_id_det",
74462306a36Sopenharmony_ci						    GPIOD_IN);
74562306a36Sopenharmony_ci	if (IS_ERR(data->id_det_gpio)) {
74662306a36Sopenharmony_ci		dev_err(dev, "Couldn't request ID GPIO\n");
74762306a36Sopenharmony_ci		return PTR_ERR(data->id_det_gpio);
74862306a36Sopenharmony_ci	}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	data->vbus_det_gpio = devm_gpiod_get_optional(dev, "usb0_vbus_det",
75162306a36Sopenharmony_ci						      GPIOD_IN);
75262306a36Sopenharmony_ci	if (IS_ERR(data->vbus_det_gpio)) {
75362306a36Sopenharmony_ci		dev_err(dev, "Couldn't request VBUS detect GPIO\n");
75462306a36Sopenharmony_ci		return PTR_ERR(data->vbus_det_gpio);
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	if (of_property_present(np, "usb0_vbus_power-supply")) {
75862306a36Sopenharmony_ci		data->vbus_power_supply = devm_power_supply_get_by_phandle(dev,
75962306a36Sopenharmony_ci						     "usb0_vbus_power-supply");
76062306a36Sopenharmony_ci		if (IS_ERR(data->vbus_power_supply)) {
76162306a36Sopenharmony_ci			dev_err(dev, "Couldn't get the VBUS power supply\n");
76262306a36Sopenharmony_ci			return PTR_ERR(data->vbus_power_supply);
76362306a36Sopenharmony_ci		}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci		if (!data->vbus_power_supply)
76662306a36Sopenharmony_ci			return -EPROBE_DEFER;
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	data->dr_mode = of_usb_get_dr_mode_by_phy(np, 0);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	data->extcon = devm_extcon_dev_allocate(dev, sun4i_usb_phy0_cable);
77262306a36Sopenharmony_ci	if (IS_ERR(data->extcon)) {
77362306a36Sopenharmony_ci		dev_err(dev, "Couldn't allocate our extcon device\n");
77462306a36Sopenharmony_ci		return PTR_ERR(data->extcon);
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	ret = devm_extcon_dev_register(dev, data->extcon);
77862306a36Sopenharmony_ci	if (ret) {
77962306a36Sopenharmony_ci		dev_err(dev, "failed to register extcon: %d\n", ret);
78062306a36Sopenharmony_ci		return ret;
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	for (i = 0; i < data->cfg->num_phys; i++) {
78462306a36Sopenharmony_ci		struct sun4i_usb_phy *phy = data->phys + i;
78562306a36Sopenharmony_ci		char name[16];
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci		if (data->cfg->missing_phys & BIT(i))
78862306a36Sopenharmony_ci			continue;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci		snprintf(name, sizeof(name), "usb%d_vbus", i);
79162306a36Sopenharmony_ci		phy->vbus = devm_regulator_get_optional(dev, name);
79262306a36Sopenharmony_ci		if (IS_ERR(phy->vbus)) {
79362306a36Sopenharmony_ci			if (PTR_ERR(phy->vbus) == -EPROBE_DEFER) {
79462306a36Sopenharmony_ci				dev_err(dev,
79562306a36Sopenharmony_ci					"Couldn't get regulator %s... Deferring probe\n",
79662306a36Sopenharmony_ci					name);
79762306a36Sopenharmony_ci				return -EPROBE_DEFER;
79862306a36Sopenharmony_ci			}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci			phy->vbus = NULL;
80162306a36Sopenharmony_ci		}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci		if (data->cfg->dedicated_clocks)
80462306a36Sopenharmony_ci			snprintf(name, sizeof(name), "usb%d_phy", i);
80562306a36Sopenharmony_ci		else
80662306a36Sopenharmony_ci			strscpy(name, "usb_phy", sizeof(name));
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci		phy->clk = devm_clk_get(dev, name);
80962306a36Sopenharmony_ci		if (IS_ERR(phy->clk)) {
81062306a36Sopenharmony_ci			dev_err(dev, "failed to get clock %s\n", name);
81162306a36Sopenharmony_ci			return PTR_ERR(phy->clk);
81262306a36Sopenharmony_ci		}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci		/* The first PHY is always tied to OTG, and never HSIC */
81562306a36Sopenharmony_ci		if (data->cfg->hsic_index && i == data->cfg->hsic_index) {
81662306a36Sopenharmony_ci			/* HSIC needs secondary clock */
81762306a36Sopenharmony_ci			snprintf(name, sizeof(name), "usb%d_hsic_12M", i);
81862306a36Sopenharmony_ci			phy->clk2 = devm_clk_get(dev, name);
81962306a36Sopenharmony_ci			if (IS_ERR(phy->clk2)) {
82062306a36Sopenharmony_ci				dev_err(dev, "failed to get clock %s\n", name);
82162306a36Sopenharmony_ci				return PTR_ERR(phy->clk2);
82262306a36Sopenharmony_ci			}
82362306a36Sopenharmony_ci		} else {
82462306a36Sopenharmony_ci			snprintf(name, sizeof(name), "pmu%d_clk", i);
82562306a36Sopenharmony_ci			phy->clk2 = devm_clk_get_optional(dev, name);
82662306a36Sopenharmony_ci			if (IS_ERR(phy->clk2)) {
82762306a36Sopenharmony_ci				dev_err(dev, "failed to get clock %s\n", name);
82862306a36Sopenharmony_ci				return PTR_ERR(phy->clk2);
82962306a36Sopenharmony_ci			}
83062306a36Sopenharmony_ci		}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci		snprintf(name, sizeof(name), "usb%d_reset", i);
83362306a36Sopenharmony_ci		phy->reset = devm_reset_control_get(dev, name);
83462306a36Sopenharmony_ci		if (IS_ERR(phy->reset)) {
83562306a36Sopenharmony_ci			dev_err(dev, "failed to get reset %s\n", name);
83662306a36Sopenharmony_ci			return PTR_ERR(phy->reset);
83762306a36Sopenharmony_ci		}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci		if (i || data->cfg->phy0_dual_route) { /* No pmu for musb */
84062306a36Sopenharmony_ci			snprintf(name, sizeof(name), "pmu%d", i);
84162306a36Sopenharmony_ci			phy->pmu = devm_platform_ioremap_resource_byname(pdev, name);
84262306a36Sopenharmony_ci			if (IS_ERR(phy->pmu))
84362306a36Sopenharmony_ci				return PTR_ERR(phy->pmu);
84462306a36Sopenharmony_ci		}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci		phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops);
84762306a36Sopenharmony_ci		if (IS_ERR(phy->phy)) {
84862306a36Sopenharmony_ci			dev_err(dev, "failed to create PHY %d\n", i);
84962306a36Sopenharmony_ci			return PTR_ERR(phy->phy);
85062306a36Sopenharmony_ci		}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci		phy->index = i;
85362306a36Sopenharmony_ci		phy_set_drvdata(phy->phy, &data->phys[i]);
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
85762306a36Sopenharmony_ci	if (data->id_det_irq > 0) {
85862306a36Sopenharmony_ci		ret = devm_request_irq(dev, data->id_det_irq,
85962306a36Sopenharmony_ci				sun4i_usb_phy0_id_vbus_det_irq,
86062306a36Sopenharmony_ci				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
86162306a36Sopenharmony_ci				"usb0-id-det", data);
86262306a36Sopenharmony_ci		if (ret) {
86362306a36Sopenharmony_ci			dev_err(dev, "Err requesting id-det-irq: %d\n", ret);
86462306a36Sopenharmony_ci			return ret;
86562306a36Sopenharmony_ci		}
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
86962306a36Sopenharmony_ci	if (data->vbus_det_irq > 0) {
87062306a36Sopenharmony_ci		ret = devm_request_irq(dev, data->vbus_det_irq,
87162306a36Sopenharmony_ci				sun4i_usb_phy0_id_vbus_det_irq,
87262306a36Sopenharmony_ci				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
87362306a36Sopenharmony_ci				"usb0-vbus-det", data);
87462306a36Sopenharmony_ci		if (ret) {
87562306a36Sopenharmony_ci			dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret);
87662306a36Sopenharmony_ci			data->vbus_det_irq = -1;
87762306a36Sopenharmony_ci			sun4i_usb_phy_remove(pdev); /* Stop detect work */
87862306a36Sopenharmony_ci			return ret;
87962306a36Sopenharmony_ci		}
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	if (data->vbus_power_supply) {
88362306a36Sopenharmony_ci		data->vbus_power_nb.notifier_call = sun4i_usb_phy0_vbus_notify;
88462306a36Sopenharmony_ci		data->vbus_power_nb.priority = 0;
88562306a36Sopenharmony_ci		ret = power_supply_reg_notifier(&data->vbus_power_nb);
88662306a36Sopenharmony_ci		if (ret) {
88762306a36Sopenharmony_ci			sun4i_usb_phy_remove(pdev); /* Stop detect work */
88862306a36Sopenharmony_ci			return ret;
88962306a36Sopenharmony_ci		}
89062306a36Sopenharmony_ci		data->vbus_power_nb_registered = true;
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
89462306a36Sopenharmony_ci	if (IS_ERR(phy_provider)) {
89562306a36Sopenharmony_ci		sun4i_usb_phy_remove(pdev); /* Stop detect work */
89662306a36Sopenharmony_ci		return PTR_ERR(phy_provider);
89762306a36Sopenharmony_ci	}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	dev_dbg(dev, "successfully loaded\n");
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	return 0;
90262306a36Sopenharmony_ci}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg suniv_f1c100s_cfg = {
90562306a36Sopenharmony_ci	.num_phys = 1,
90662306a36Sopenharmony_ci	.disc_thresh = 3,
90762306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A10,
90862306a36Sopenharmony_ci	.dedicated_clocks = true,
90962306a36Sopenharmony_ci};
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun4i_a10_cfg = {
91262306a36Sopenharmony_ci	.num_phys = 3,
91362306a36Sopenharmony_ci	.disc_thresh = 3,
91462306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A10,
91562306a36Sopenharmony_ci	.dedicated_clocks = false,
91662306a36Sopenharmony_ci};
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
91962306a36Sopenharmony_ci	.num_phys = 2,
92062306a36Sopenharmony_ci	.disc_thresh = 2,
92162306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A10,
92262306a36Sopenharmony_ci	.dedicated_clocks = false,
92362306a36Sopenharmony_ci};
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
92662306a36Sopenharmony_ci	.num_phys = 3,
92762306a36Sopenharmony_ci	.disc_thresh = 3,
92862306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A10,
92962306a36Sopenharmony_ci	.dedicated_clocks = true,
93062306a36Sopenharmony_ci	.poll_vbusen = true,
93162306a36Sopenharmony_ci};
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
93462306a36Sopenharmony_ci	.num_phys = 3,
93562306a36Sopenharmony_ci	.disc_thresh = 2,
93662306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A10,
93762306a36Sopenharmony_ci	.dedicated_clocks = false,
93862306a36Sopenharmony_ci};
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
94162306a36Sopenharmony_ci	.num_phys = 2,
94262306a36Sopenharmony_ci	.disc_thresh = 3,
94362306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A10,
94462306a36Sopenharmony_ci	.dedicated_clocks = true,
94562306a36Sopenharmony_ci	.poll_vbusen = true,
94662306a36Sopenharmony_ci};
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
94962306a36Sopenharmony_ci	.num_phys = 2,
95062306a36Sopenharmony_ci	.disc_thresh = 3,
95162306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A33,
95262306a36Sopenharmony_ci	.dedicated_clocks = true,
95362306a36Sopenharmony_ci	.poll_vbusen = true,
95462306a36Sopenharmony_ci};
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = {
95762306a36Sopenharmony_ci	.num_phys = 3,
95862306a36Sopenharmony_ci	.hsic_index = 2,
95962306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A33,
96062306a36Sopenharmony_ci	.dedicated_clocks = true,
96162306a36Sopenharmony_ci	.siddq_in_base = true,
96262306a36Sopenharmony_ci};
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
96562306a36Sopenharmony_ci	.num_phys = 4,
96662306a36Sopenharmony_ci	.disc_thresh = 3,
96762306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A33,
96862306a36Sopenharmony_ci	.dedicated_clocks = true,
96962306a36Sopenharmony_ci	.hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
97062306a36Sopenharmony_ci	.phy0_dual_route = true,
97162306a36Sopenharmony_ci};
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun8i_r40_cfg = {
97462306a36Sopenharmony_ci	.num_phys = 3,
97562306a36Sopenharmony_ci	.disc_thresh = 3,
97662306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A33,
97762306a36Sopenharmony_ci	.dedicated_clocks = true,
97862306a36Sopenharmony_ci	.hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
97962306a36Sopenharmony_ci	.phy0_dual_route = true,
98062306a36Sopenharmony_ci};
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = {
98362306a36Sopenharmony_ci	.num_phys = 1,
98462306a36Sopenharmony_ci	.disc_thresh = 3,
98562306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A33,
98662306a36Sopenharmony_ci	.dedicated_clocks = true,
98762306a36Sopenharmony_ci	.hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
98862306a36Sopenharmony_ci	.phy0_dual_route = true,
98962306a36Sopenharmony_ci};
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun20i_d1_cfg = {
99262306a36Sopenharmony_ci	.num_phys = 2,
99362306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A33,
99462306a36Sopenharmony_ci	.dedicated_clocks = true,
99562306a36Sopenharmony_ci	.hci_phy_ctl_clear = PHY_CTL_SIDDQ,
99662306a36Sopenharmony_ci	.phy0_dual_route = true,
99762306a36Sopenharmony_ci	.siddq_in_base = true,
99862306a36Sopenharmony_ci};
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
100162306a36Sopenharmony_ci	.num_phys = 2,
100262306a36Sopenharmony_ci	.disc_thresh = 3,
100362306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A33,
100462306a36Sopenharmony_ci	.dedicated_clocks = true,
100562306a36Sopenharmony_ci	.hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ,
100662306a36Sopenharmony_ci	.phy0_dual_route = true,
100762306a36Sopenharmony_ci};
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun50i_h6_cfg = {
101062306a36Sopenharmony_ci	.num_phys = 4,
101162306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A33,
101262306a36Sopenharmony_ci	.dedicated_clocks = true,
101362306a36Sopenharmony_ci	.phy0_dual_route = true,
101462306a36Sopenharmony_ci	.missing_phys = BIT(1) | BIT(2),
101562306a36Sopenharmony_ci	.siddq_in_base = true,
101662306a36Sopenharmony_ci};
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_cistatic const struct sun4i_usb_phy_cfg sun50i_h616_cfg = {
101962306a36Sopenharmony_ci	.num_phys = 4,
102062306a36Sopenharmony_ci	.disc_thresh = 3,
102162306a36Sopenharmony_ci	.phyctl_offset = REG_PHYCTL_A33,
102262306a36Sopenharmony_ci	.dedicated_clocks = true,
102362306a36Sopenharmony_ci	.phy0_dual_route = true,
102462306a36Sopenharmony_ci	.hci_phy_ctl_clear = PHY_CTL_SIDDQ,
102562306a36Sopenharmony_ci	.needs_phy2_siddq = true,
102662306a36Sopenharmony_ci	.siddq_in_base = true,
102762306a36Sopenharmony_ci};
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_cistatic const struct of_device_id sun4i_usb_phy_of_match[] = {
103062306a36Sopenharmony_ci	{ .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg },
103162306a36Sopenharmony_ci	{ .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg },
103262306a36Sopenharmony_ci	{ .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg },
103362306a36Sopenharmony_ci	{ .compatible = "allwinner,sun7i-a20-usb-phy", .data = &sun7i_a20_cfg },
103462306a36Sopenharmony_ci	{ .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg },
103562306a36Sopenharmony_ci	{ .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg },
103662306a36Sopenharmony_ci	{ .compatible = "allwinner,sun8i-a83t-usb-phy", .data = &sun8i_a83t_cfg },
103762306a36Sopenharmony_ci	{ .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
103862306a36Sopenharmony_ci	{ .compatible = "allwinner,sun8i-r40-usb-phy", .data = &sun8i_r40_cfg },
103962306a36Sopenharmony_ci	{ .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg },
104062306a36Sopenharmony_ci	{ .compatible = "allwinner,sun20i-d1-usb-phy", .data = &sun20i_d1_cfg },
104162306a36Sopenharmony_ci	{ .compatible = "allwinner,sun50i-a64-usb-phy",
104262306a36Sopenharmony_ci	  .data = &sun50i_a64_cfg},
104362306a36Sopenharmony_ci	{ .compatible = "allwinner,sun50i-h6-usb-phy", .data = &sun50i_h6_cfg },
104462306a36Sopenharmony_ci	{ .compatible = "allwinner,sun50i-h616-usb-phy", .data = &sun50i_h616_cfg },
104562306a36Sopenharmony_ci	{ .compatible = "allwinner,suniv-f1c100s-usb-phy",
104662306a36Sopenharmony_ci	  .data = &suniv_f1c100s_cfg },
104762306a36Sopenharmony_ci	{ },
104862306a36Sopenharmony_ci};
104962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cistatic struct platform_driver sun4i_usb_phy_driver = {
105262306a36Sopenharmony_ci	.probe	= sun4i_usb_phy_probe,
105362306a36Sopenharmony_ci	.remove_new = sun4i_usb_phy_remove,
105462306a36Sopenharmony_ci	.driver = {
105562306a36Sopenharmony_ci		.of_match_table	= sun4i_usb_phy_of_match,
105662306a36Sopenharmony_ci		.name  = "sun4i-usb-phy",
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci};
105962306a36Sopenharmony_cimodule_platform_driver(sun4i_usb_phy_driver);
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ciMODULE_DESCRIPTION("Allwinner sun4i USB phy driver");
106262306a36Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
106362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1064