18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * BCM6328 USBH PHY Controller Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
68c2ecf20Sopenharmony_ci * Copyright (C) 2015 Simon Arlott
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Derived from bcm963xx_4.12L.06B_consumer/kernel/linux/arch/mips/bcm963xx/setup.c:
98c2ecf20Sopenharmony_ci * Copyright (C) 2002 Broadcom Corporation
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Derived from OpenWrt patches:
128c2ecf20Sopenharmony_ci * Copyright (C) 2013 Jonas Gorski <jonas.gorski@gmail.com>
138c2ecf20Sopenharmony_ci * Copyright (C) 2013 Florian Fainelli <f.fainelli@gmail.com>
148c2ecf20Sopenharmony_ci * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/clk.h>
188c2ecf20Sopenharmony_ci#include <linux/io.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/phy/phy.h>
218c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
228c2ecf20Sopenharmony_ci#include <linux/reset.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* USBH control register offsets */
258c2ecf20Sopenharmony_cienum usbh_regs {
268c2ecf20Sopenharmony_ci	USBH_BRT_CONTROL1 = 0,
278c2ecf20Sopenharmony_ci	USBH_BRT_CONTROL2,
288c2ecf20Sopenharmony_ci	USBH_BRT_STATUS1,
298c2ecf20Sopenharmony_ci	USBH_BRT_STATUS2,
308c2ecf20Sopenharmony_ci	USBH_UTMI_CONTROL1,
318c2ecf20Sopenharmony_ci#define   USBH_UC1_DEV_MODE_SEL		BIT(0)
328c2ecf20Sopenharmony_ci	USBH_TEST_PORT_CONTROL,
338c2ecf20Sopenharmony_ci	USBH_PLL_CONTROL1,
348c2ecf20Sopenharmony_ci#define   USBH_PLLC_REFCLKSEL_SHIFT	0
358c2ecf20Sopenharmony_ci#define   USBH_PLLC_REFCLKSEL_MASK	(0x3 << USBH_PLLC_REFCLKSEL_SHIFT)
368c2ecf20Sopenharmony_ci#define   USBH_PLLC_CLKSEL_SHIFT	2
378c2ecf20Sopenharmony_ci#define   USBH_PLLC_CLKSEL_MASK		(0x3 << USBH_PLLC_CLKSEL_MASK)
388c2ecf20Sopenharmony_ci#define   USBH_PLLC_XTAL_PWRDWNB	BIT(4)
398c2ecf20Sopenharmony_ci#define   USBH_PLLC_PLL_PWRDWNB		BIT(5)
408c2ecf20Sopenharmony_ci#define   USBH_PLLC_PLL_CALEN		BIT(6)
418c2ecf20Sopenharmony_ci#define   USBH_PLLC_PHYPLL_BYP		BIT(7)
428c2ecf20Sopenharmony_ci#define   USBH_PLLC_PLL_RESET		BIT(8)
438c2ecf20Sopenharmony_ci#define   USBH_PLLC_PLL_IDDQ_PWRDN	BIT(9)
448c2ecf20Sopenharmony_ci#define   USBH_PLLC_PLL_PWRDN_DELAY	BIT(10)
458c2ecf20Sopenharmony_ci#define   USBH_6318_PLLC_PLL_SUSPEND_EN	BIT(27)
468c2ecf20Sopenharmony_ci#define   USBH_6318_PLLC_PHYPLL_BYP	BIT(29)
478c2ecf20Sopenharmony_ci#define   USBH_6318_PLLC_PLL_RESET	BIT(30)
488c2ecf20Sopenharmony_ci#define   USBH_6318_PLLC_PLL_IDDQ_PWRDN	BIT(31)
498c2ecf20Sopenharmony_ci	USBH_SWAP_CONTROL,
508c2ecf20Sopenharmony_ci#define   USBH_SC_OHCI_DATA_SWAP	BIT(0)
518c2ecf20Sopenharmony_ci#define   USBH_SC_OHCI_ENDIAN_SWAP	BIT(1)
528c2ecf20Sopenharmony_ci#define   USBH_SC_OHCI_LOGICAL_ADDR_EN	BIT(2)
538c2ecf20Sopenharmony_ci#define   USBH_SC_EHCI_DATA_SWAP	BIT(3)
548c2ecf20Sopenharmony_ci#define   USBH_SC_EHCI_ENDIAN_SWAP	BIT(4)
558c2ecf20Sopenharmony_ci#define   USBH_SC_EHCI_LOGICAL_ADDR_EN	BIT(5)
568c2ecf20Sopenharmony_ci#define   USBH_SC_USB_DEVICE_SEL	BIT(6)
578c2ecf20Sopenharmony_ci	USBH_GENERIC_CONTROL,
588c2ecf20Sopenharmony_ci#define   USBH_GC_PLL_SUSPEND_EN	BIT(1)
598c2ecf20Sopenharmony_ci	USBH_FRAME_ADJUST_VALUE,
608c2ecf20Sopenharmony_ci	USBH_SETUP,
618c2ecf20Sopenharmony_ci#define   USBH_S_IOC			BIT(4)
628c2ecf20Sopenharmony_ci#define   USBH_S_IPP			BIT(5)
638c2ecf20Sopenharmony_ci	USBH_MDIO,
648c2ecf20Sopenharmony_ci	USBH_MDIO32,
658c2ecf20Sopenharmony_ci	USBH_USB_SIM_CONTROL,
668c2ecf20Sopenharmony_ci#define   USBH_USC_LADDR_SEL		BIT(5)
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	__USBH_ENUM_SIZE
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistruct bcm63xx_usbh_phy_variant {
728c2ecf20Sopenharmony_ci	/* Registers */
738c2ecf20Sopenharmony_ci	long regs[__USBH_ENUM_SIZE];
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/* PLLC bits to set/clear for power on */
768c2ecf20Sopenharmony_ci	u32 power_pllc_clr;
778c2ecf20Sopenharmony_ci	u32 power_pllc_set;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/* Setup bits to set/clear for power on */
808c2ecf20Sopenharmony_ci	u32 setup_clr;
818c2ecf20Sopenharmony_ci	u32 setup_set;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/* Swap Control bits to set */
848c2ecf20Sopenharmony_ci	u32 swapctl_dev_set;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	/* Test Port Control value to set if non-zero */
878c2ecf20Sopenharmony_ci	u32 tpc_val;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/* USB Sim Control bits to set */
908c2ecf20Sopenharmony_ci	u32 usc_set;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* UTMI Control 1 bits to set */
938c2ecf20Sopenharmony_ci	u32 utmictl1_dev_set;
948c2ecf20Sopenharmony_ci};
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistruct bcm63xx_usbh_phy {
978c2ecf20Sopenharmony_ci	void __iomem *base;
988c2ecf20Sopenharmony_ci	struct clk *usbh_clk;
998c2ecf20Sopenharmony_ci	struct clk *usb_ref_clk;
1008c2ecf20Sopenharmony_ci	struct reset_control *reset;
1018c2ecf20Sopenharmony_ci	const struct bcm63xx_usbh_phy_variant *variant;
1028c2ecf20Sopenharmony_ci	bool device_mode;
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic const struct bcm63xx_usbh_phy_variant usbh_bcm6318 = {
1068c2ecf20Sopenharmony_ci	.regs = {
1078c2ecf20Sopenharmony_ci		[USBH_BRT_CONTROL1] = -1,
1088c2ecf20Sopenharmony_ci		[USBH_BRT_CONTROL2] = -1,
1098c2ecf20Sopenharmony_ci		[USBH_BRT_STATUS1] = -1,
1108c2ecf20Sopenharmony_ci		[USBH_BRT_STATUS2] = -1,
1118c2ecf20Sopenharmony_ci		[USBH_UTMI_CONTROL1] = 0x2c,
1128c2ecf20Sopenharmony_ci		[USBH_TEST_PORT_CONTROL] = 0x1c,
1138c2ecf20Sopenharmony_ci		[USBH_PLL_CONTROL1] = 0x04,
1148c2ecf20Sopenharmony_ci		[USBH_SWAP_CONTROL] = 0x0c,
1158c2ecf20Sopenharmony_ci		[USBH_GENERIC_CONTROL] = -1,
1168c2ecf20Sopenharmony_ci		[USBH_FRAME_ADJUST_VALUE] = 0x08,
1178c2ecf20Sopenharmony_ci		[USBH_SETUP] = 0x00,
1188c2ecf20Sopenharmony_ci		[USBH_MDIO] = 0x14,
1198c2ecf20Sopenharmony_ci		[USBH_MDIO32] = 0x18,
1208c2ecf20Sopenharmony_ci		[USBH_USB_SIM_CONTROL] = 0x20,
1218c2ecf20Sopenharmony_ci	},
1228c2ecf20Sopenharmony_ci	.power_pllc_clr = USBH_6318_PLLC_PLL_IDDQ_PWRDN,
1238c2ecf20Sopenharmony_ci	.power_pllc_set = USBH_6318_PLLC_PLL_SUSPEND_EN,
1248c2ecf20Sopenharmony_ci	.setup_set = USBH_S_IOC,
1258c2ecf20Sopenharmony_ci	.swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
1268c2ecf20Sopenharmony_ci	.usc_set = USBH_USC_LADDR_SEL,
1278c2ecf20Sopenharmony_ci	.utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
1288c2ecf20Sopenharmony_ci};
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic const struct bcm63xx_usbh_phy_variant usbh_bcm6328 = {
1318c2ecf20Sopenharmony_ci	.regs = {
1328c2ecf20Sopenharmony_ci		[USBH_BRT_CONTROL1] = 0x00,
1338c2ecf20Sopenharmony_ci		[USBH_BRT_CONTROL2] = 0x04,
1348c2ecf20Sopenharmony_ci		[USBH_BRT_STATUS1] = 0x08,
1358c2ecf20Sopenharmony_ci		[USBH_BRT_STATUS2] = 0x0c,
1368c2ecf20Sopenharmony_ci		[USBH_UTMI_CONTROL1] = 0x10,
1378c2ecf20Sopenharmony_ci		[USBH_TEST_PORT_CONTROL] = 0x14,
1388c2ecf20Sopenharmony_ci		[USBH_PLL_CONTROL1] = 0x18,
1398c2ecf20Sopenharmony_ci		[USBH_SWAP_CONTROL] = 0x1c,
1408c2ecf20Sopenharmony_ci		[USBH_GENERIC_CONTROL] = 0x20,
1418c2ecf20Sopenharmony_ci		[USBH_FRAME_ADJUST_VALUE] = 0x24,
1428c2ecf20Sopenharmony_ci		[USBH_SETUP] = 0x28,
1438c2ecf20Sopenharmony_ci		[USBH_MDIO] = 0x2c,
1448c2ecf20Sopenharmony_ci		[USBH_MDIO32] = 0x30,
1458c2ecf20Sopenharmony_ci		[USBH_USB_SIM_CONTROL] = 0x34,
1468c2ecf20Sopenharmony_ci	},
1478c2ecf20Sopenharmony_ci	.setup_set = USBH_S_IOC,
1488c2ecf20Sopenharmony_ci	.swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
1498c2ecf20Sopenharmony_ci	.utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic const struct bcm63xx_usbh_phy_variant usbh_bcm6358 = {
1538c2ecf20Sopenharmony_ci	.regs = {
1548c2ecf20Sopenharmony_ci		[USBH_BRT_CONTROL1] = -1,
1558c2ecf20Sopenharmony_ci		[USBH_BRT_CONTROL2] = -1,
1568c2ecf20Sopenharmony_ci		[USBH_BRT_STATUS1] = -1,
1578c2ecf20Sopenharmony_ci		[USBH_BRT_STATUS2] = -1,
1588c2ecf20Sopenharmony_ci		[USBH_UTMI_CONTROL1] = -1,
1598c2ecf20Sopenharmony_ci		[USBH_TEST_PORT_CONTROL] = 0x24,
1608c2ecf20Sopenharmony_ci		[USBH_PLL_CONTROL1] = -1,
1618c2ecf20Sopenharmony_ci		[USBH_SWAP_CONTROL] = 0x00,
1628c2ecf20Sopenharmony_ci		[USBH_GENERIC_CONTROL] = -1,
1638c2ecf20Sopenharmony_ci		[USBH_FRAME_ADJUST_VALUE] = -1,
1648c2ecf20Sopenharmony_ci		[USBH_SETUP] = -1,
1658c2ecf20Sopenharmony_ci		[USBH_MDIO] = -1,
1668c2ecf20Sopenharmony_ci		[USBH_MDIO32] = -1,
1678c2ecf20Sopenharmony_ci		[USBH_USB_SIM_CONTROL] = -1,
1688c2ecf20Sopenharmony_ci	},
1698c2ecf20Sopenharmony_ci	/*
1708c2ecf20Sopenharmony_ci	 * The magic value comes for the original vendor BSP
1718c2ecf20Sopenharmony_ci	 * and is needed for USB to work. Datasheet does not
1728c2ecf20Sopenharmony_ci	 * help, so the magic value is used as-is.
1738c2ecf20Sopenharmony_ci	 */
1748c2ecf20Sopenharmony_ci	.tpc_val = 0x1c0020,
1758c2ecf20Sopenharmony_ci};
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic const struct bcm63xx_usbh_phy_variant usbh_bcm6368 = {
1788c2ecf20Sopenharmony_ci	.regs = {
1798c2ecf20Sopenharmony_ci		[USBH_BRT_CONTROL1] = 0x00,
1808c2ecf20Sopenharmony_ci		[USBH_BRT_CONTROL2] = 0x04,
1818c2ecf20Sopenharmony_ci		[USBH_BRT_STATUS1] = 0x08,
1828c2ecf20Sopenharmony_ci		[USBH_BRT_STATUS2] = 0x0c,
1838c2ecf20Sopenharmony_ci		[USBH_UTMI_CONTROL1] = 0x10,
1848c2ecf20Sopenharmony_ci		[USBH_TEST_PORT_CONTROL] = 0x14,
1858c2ecf20Sopenharmony_ci		[USBH_PLL_CONTROL1] = 0x18,
1868c2ecf20Sopenharmony_ci		[USBH_SWAP_CONTROL] = 0x1c,
1878c2ecf20Sopenharmony_ci		[USBH_GENERIC_CONTROL] = -1,
1888c2ecf20Sopenharmony_ci		[USBH_FRAME_ADJUST_VALUE] = 0x24,
1898c2ecf20Sopenharmony_ci		[USBH_SETUP] = 0x28,
1908c2ecf20Sopenharmony_ci		[USBH_MDIO] = 0x2c,
1918c2ecf20Sopenharmony_ci		[USBH_MDIO32] = 0x30,
1928c2ecf20Sopenharmony_ci		[USBH_USB_SIM_CONTROL] = 0x34,
1938c2ecf20Sopenharmony_ci	},
1948c2ecf20Sopenharmony_ci	.power_pllc_clr = USBH_PLLC_PLL_IDDQ_PWRDN | USBH_PLLC_PLL_PWRDN_DELAY,
1958c2ecf20Sopenharmony_ci	.setup_set = USBH_S_IOC,
1968c2ecf20Sopenharmony_ci	.swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
1978c2ecf20Sopenharmony_ci	.utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
1988c2ecf20Sopenharmony_ci};
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic const struct bcm63xx_usbh_phy_variant usbh_bcm63268 = {
2018c2ecf20Sopenharmony_ci	.regs = {
2028c2ecf20Sopenharmony_ci		[USBH_BRT_CONTROL1] = 0x00,
2038c2ecf20Sopenharmony_ci		[USBH_BRT_CONTROL2] = 0x04,
2048c2ecf20Sopenharmony_ci		[USBH_BRT_STATUS1] = 0x08,
2058c2ecf20Sopenharmony_ci		[USBH_BRT_STATUS2] = 0x0c,
2068c2ecf20Sopenharmony_ci		[USBH_UTMI_CONTROL1] = 0x10,
2078c2ecf20Sopenharmony_ci		[USBH_TEST_PORT_CONTROL] = 0x14,
2088c2ecf20Sopenharmony_ci		[USBH_PLL_CONTROL1] = 0x18,
2098c2ecf20Sopenharmony_ci		[USBH_SWAP_CONTROL] = 0x1c,
2108c2ecf20Sopenharmony_ci		[USBH_GENERIC_CONTROL] = 0x20,
2118c2ecf20Sopenharmony_ci		[USBH_FRAME_ADJUST_VALUE] = 0x24,
2128c2ecf20Sopenharmony_ci		[USBH_SETUP] = 0x28,
2138c2ecf20Sopenharmony_ci		[USBH_MDIO] = 0x2c,
2148c2ecf20Sopenharmony_ci		[USBH_MDIO32] = 0x30,
2158c2ecf20Sopenharmony_ci		[USBH_USB_SIM_CONTROL] = 0x34,
2168c2ecf20Sopenharmony_ci	},
2178c2ecf20Sopenharmony_ci	.power_pllc_clr = USBH_PLLC_PLL_IDDQ_PWRDN | USBH_PLLC_PLL_PWRDN_DELAY,
2188c2ecf20Sopenharmony_ci	.setup_clr = USBH_S_IPP,
2198c2ecf20Sopenharmony_ci	.setup_set = USBH_S_IOC,
2208c2ecf20Sopenharmony_ci	.swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
2218c2ecf20Sopenharmony_ci	.utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
2228c2ecf20Sopenharmony_ci};
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic inline bool usbh_has_reg(struct bcm63xx_usbh_phy *usbh, int reg)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	return (usbh->variant->regs[reg] >= 0);
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic inline u32 usbh_readl(struct bcm63xx_usbh_phy *usbh, int reg)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	return __raw_readl(usbh->base + usbh->variant->regs[reg]);
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic inline void usbh_writel(struct bcm63xx_usbh_phy *usbh, int reg,
2358c2ecf20Sopenharmony_ci			       u32 value)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	__raw_writel(value, usbh->base + usbh->variant->regs[reg]);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic int bcm63xx_usbh_phy_init(struct phy *phy)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
2438c2ecf20Sopenharmony_ci	int ret;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(usbh->usbh_clk);
2468c2ecf20Sopenharmony_ci	if (ret) {
2478c2ecf20Sopenharmony_ci		dev_err(&phy->dev, "unable to enable usbh clock: %d\n", ret);
2488c2ecf20Sopenharmony_ci		return ret;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(usbh->usb_ref_clk);
2528c2ecf20Sopenharmony_ci	if (ret) {
2538c2ecf20Sopenharmony_ci		dev_err(&phy->dev, "unable to enable usb_ref clock: %d\n", ret);
2548c2ecf20Sopenharmony_ci		clk_disable_unprepare(usbh->usbh_clk);
2558c2ecf20Sopenharmony_ci		return ret;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	ret = reset_control_reset(usbh->reset);
2598c2ecf20Sopenharmony_ci	if (ret) {
2608c2ecf20Sopenharmony_ci		dev_err(&phy->dev, "unable to reset device: %d\n", ret);
2618c2ecf20Sopenharmony_ci		clk_disable_unprepare(usbh->usb_ref_clk);
2628c2ecf20Sopenharmony_ci		clk_disable_unprepare(usbh->usbh_clk);
2638c2ecf20Sopenharmony_ci		return ret;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	/* Configure to work in native CPU endian */
2678c2ecf20Sopenharmony_ci	if (usbh_has_reg(usbh, USBH_SWAP_CONTROL)) {
2688c2ecf20Sopenharmony_ci		u32 val = usbh_readl(usbh, USBH_SWAP_CONTROL);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci		val |= USBH_SC_EHCI_DATA_SWAP;
2718c2ecf20Sopenharmony_ci		val &= ~USBH_SC_EHCI_ENDIAN_SWAP;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		val |= USBH_SC_OHCI_DATA_SWAP;
2748c2ecf20Sopenharmony_ci		val &= ~USBH_SC_OHCI_ENDIAN_SWAP;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		if (usbh->device_mode && usbh->variant->swapctl_dev_set)
2778c2ecf20Sopenharmony_ci			val |= usbh->variant->swapctl_dev_set;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		usbh_writel(usbh, USBH_SWAP_CONTROL, val);
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (usbh_has_reg(usbh, USBH_SETUP)) {
2838c2ecf20Sopenharmony_ci		u32 val = usbh_readl(usbh, USBH_SETUP);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		val |= usbh->variant->setup_set;
2868c2ecf20Sopenharmony_ci		val &= ~usbh->variant->setup_clr;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		usbh_writel(usbh, USBH_SETUP, val);
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (usbh_has_reg(usbh, USBH_USB_SIM_CONTROL)) {
2928c2ecf20Sopenharmony_ci		u32 val = usbh_readl(usbh, USBH_USB_SIM_CONTROL);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		val |= usbh->variant->usc_set;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		usbh_writel(usbh, USBH_USB_SIM_CONTROL, val);
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (usbh->variant->tpc_val &&
3008c2ecf20Sopenharmony_ci	    usbh_has_reg(usbh, USBH_TEST_PORT_CONTROL))
3018c2ecf20Sopenharmony_ci		usbh_writel(usbh, USBH_TEST_PORT_CONTROL,
3028c2ecf20Sopenharmony_ci			    usbh->variant->tpc_val);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (usbh->device_mode &&
3058c2ecf20Sopenharmony_ci	    usbh_has_reg(usbh, USBH_UTMI_CONTROL1) &&
3068c2ecf20Sopenharmony_ci	    usbh->variant->utmictl1_dev_set) {
3078c2ecf20Sopenharmony_ci		u32 val = usbh_readl(usbh, USBH_UTMI_CONTROL1);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci		val |= usbh->variant->utmictl1_dev_set;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		usbh_writel(usbh, USBH_UTMI_CONTROL1, val);
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	return 0;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic int bcm63xx_usbh_phy_power_on(struct phy *phy)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (usbh_has_reg(usbh, USBH_PLL_CONTROL1)) {
3228c2ecf20Sopenharmony_ci		u32 val = usbh_readl(usbh, USBH_PLL_CONTROL1);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		val |= usbh->variant->power_pllc_set;
3258c2ecf20Sopenharmony_ci		val &= ~usbh->variant->power_pllc_clr;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		usbh_writel(usbh, USBH_PLL_CONTROL1, val);
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	return 0;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic int bcm63xx_usbh_phy_power_off(struct phy *phy)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (usbh_has_reg(usbh, USBH_PLL_CONTROL1)) {
3388c2ecf20Sopenharmony_ci		u32 val = usbh_readl(usbh, USBH_PLL_CONTROL1);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci		val &= ~usbh->variant->power_pllc_set;
3418c2ecf20Sopenharmony_ci		val |= usbh->variant->power_pllc_clr;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		usbh_writel(usbh, USBH_PLL_CONTROL1, val);
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	return 0;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic int bcm63xx_usbh_phy_exit(struct phy *phy)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	clk_disable_unprepare(usbh->usbh_clk);
3548c2ecf20Sopenharmony_ci	clk_disable_unprepare(usbh->usb_ref_clk);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	return 0;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic const struct phy_ops bcm63xx_usbh_phy_ops = {
3608c2ecf20Sopenharmony_ci	.exit = bcm63xx_usbh_phy_exit,
3618c2ecf20Sopenharmony_ci	.init = bcm63xx_usbh_phy_init,
3628c2ecf20Sopenharmony_ci	.power_off = bcm63xx_usbh_phy_power_off,
3638c2ecf20Sopenharmony_ci	.power_on = bcm63xx_usbh_phy_power_on,
3648c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
3658c2ecf20Sopenharmony_ci};
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic struct phy *bcm63xx_usbh_phy_xlate(struct device *dev,
3688c2ecf20Sopenharmony_ci					  struct of_phandle_args *args)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct bcm63xx_usbh_phy *usbh = dev_get_drvdata(dev);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	usbh->device_mode = !!args->args[0];
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	return of_phy_simple_xlate(dev, args);
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic int __init bcm63xx_usbh_phy_probe(struct platform_device *pdev)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
3808c2ecf20Sopenharmony_ci	struct bcm63xx_usbh_phy	*usbh;
3818c2ecf20Sopenharmony_ci	const struct bcm63xx_usbh_phy_variant *variant;
3828c2ecf20Sopenharmony_ci	struct phy *phy;
3838c2ecf20Sopenharmony_ci	struct phy_provider *phy_provider;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	usbh = devm_kzalloc(dev, sizeof(*usbh), GFP_KERNEL);
3868c2ecf20Sopenharmony_ci	if (!usbh)
3878c2ecf20Sopenharmony_ci		return -ENOMEM;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	variant = device_get_match_data(dev);
3908c2ecf20Sopenharmony_ci	if (!variant)
3918c2ecf20Sopenharmony_ci		return -EINVAL;
3928c2ecf20Sopenharmony_ci	usbh->variant = variant;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	usbh->base = devm_platform_ioremap_resource(pdev, 0);
3958c2ecf20Sopenharmony_ci	if (IS_ERR(usbh->base))
3968c2ecf20Sopenharmony_ci		return PTR_ERR(usbh->base);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	usbh->reset = devm_reset_control_get_exclusive(dev, NULL);
3998c2ecf20Sopenharmony_ci	if (IS_ERR(usbh->reset)) {
4008c2ecf20Sopenharmony_ci		if (PTR_ERR(usbh->reset) != -EPROBE_DEFER)
4018c2ecf20Sopenharmony_ci			dev_err(dev, "failed to get reset\n");
4028c2ecf20Sopenharmony_ci		return PTR_ERR(usbh->reset);
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	usbh->usbh_clk = devm_clk_get_optional(dev, "usbh");
4068c2ecf20Sopenharmony_ci	if (IS_ERR(usbh->usbh_clk))
4078c2ecf20Sopenharmony_ci		return PTR_ERR(usbh->usbh_clk);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	usbh->usb_ref_clk = devm_clk_get_optional(dev, "usb_ref");
4108c2ecf20Sopenharmony_ci	if (IS_ERR(usbh->usb_ref_clk))
4118c2ecf20Sopenharmony_ci		return PTR_ERR(usbh->usb_ref_clk);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	phy = devm_phy_create(dev, NULL, &bcm63xx_usbh_phy_ops);
4148c2ecf20Sopenharmony_ci	if (IS_ERR(phy)) {
4158c2ecf20Sopenharmony_ci		dev_err(dev, "failed to create PHY\n");
4168c2ecf20Sopenharmony_ci		return PTR_ERR(phy);
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, usbh);
4208c2ecf20Sopenharmony_ci	phy_set_drvdata(phy, usbh);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	phy_provider = devm_of_phy_provider_register(dev,
4238c2ecf20Sopenharmony_ci						     bcm63xx_usbh_phy_xlate);
4248c2ecf20Sopenharmony_ci	if (IS_ERR(phy_provider)) {
4258c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register PHY provider\n");
4268c2ecf20Sopenharmony_ci		return PTR_ERR(phy_provider);
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	dev_dbg(dev, "Registered BCM63xx USB PHY driver\n");
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	return 0;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic const struct of_device_id bcm63xx_usbh_phy_ids[] __initconst = {
4358c2ecf20Sopenharmony_ci	{ .compatible = "brcm,bcm6318-usbh-phy", .data = &usbh_bcm6318 },
4368c2ecf20Sopenharmony_ci	{ .compatible = "brcm,bcm6328-usbh-phy", .data = &usbh_bcm6328 },
4378c2ecf20Sopenharmony_ci	{ .compatible = "brcm,bcm6358-usbh-phy", .data = &usbh_bcm6358 },
4388c2ecf20Sopenharmony_ci	{ .compatible = "brcm,bcm6362-usbh-phy", .data = &usbh_bcm6368 },
4398c2ecf20Sopenharmony_ci	{ .compatible = "brcm,bcm6368-usbh-phy", .data = &usbh_bcm6368 },
4408c2ecf20Sopenharmony_ci	{ .compatible = "brcm,bcm63268-usbh-phy", .data = &usbh_bcm63268 },
4418c2ecf20Sopenharmony_ci	{ /* sentinel */ }
4428c2ecf20Sopenharmony_ci};
4438c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, bcm63xx_usbh_phy_ids);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic struct platform_driver bcm63xx_usbh_phy_driver __refdata = {
4468c2ecf20Sopenharmony_ci	.driver	= {
4478c2ecf20Sopenharmony_ci		.name = "bcm63xx-usbh-phy",
4488c2ecf20Sopenharmony_ci		.of_match_table = bcm63xx_usbh_phy_ids,
4498c2ecf20Sopenharmony_ci	},
4508c2ecf20Sopenharmony_ci	.probe	= bcm63xx_usbh_phy_probe,
4518c2ecf20Sopenharmony_ci};
4528c2ecf20Sopenharmony_cimodule_platform_driver(bcm63xx_usbh_phy_driver);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("BCM63xx USBH PHY driver");
4558c2ecf20Sopenharmony_ciMODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
4568c2ecf20Sopenharmony_ciMODULE_AUTHOR("Simon Arlott");
4578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
458