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