18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Samsung Electronics Co., Ltd. 68c2ecf20Sopenharmony_ci * Authors: Kamil Debski <k.debski@samsung.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 128c2ecf20Sopenharmony_ci#include "phy-samsung-usb2.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* Exynos USB PHY registers */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* PHY power control */ 178c2ecf20Sopenharmony_ci#define S5PV210_UPHYPWR 0x0 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define S5PV210_UPHYPWR_PHY0_SUSPEND BIT(0) 208c2ecf20Sopenharmony_ci#define S5PV210_UPHYPWR_PHY0_PWR BIT(3) 218c2ecf20Sopenharmony_ci#define S5PV210_UPHYPWR_PHY0_OTG_PWR BIT(4) 228c2ecf20Sopenharmony_ci#define S5PV210_UPHYPWR_PHY0 ( \ 238c2ecf20Sopenharmony_ci S5PV210_UPHYPWR_PHY0_SUSPEND | \ 248c2ecf20Sopenharmony_ci S5PV210_UPHYPWR_PHY0_PWR | \ 258c2ecf20Sopenharmony_ci S5PV210_UPHYPWR_PHY0_OTG_PWR) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define S5PV210_UPHYPWR_PHY1_SUSPEND BIT(6) 288c2ecf20Sopenharmony_ci#define S5PV210_UPHYPWR_PHY1_PWR BIT(7) 298c2ecf20Sopenharmony_ci#define S5PV210_UPHYPWR_PHY1 ( \ 308c2ecf20Sopenharmony_ci S5PV210_UPHYPWR_PHY1_SUSPEND | \ 318c2ecf20Sopenharmony_ci S5PV210_UPHYPWR_PHY1_PWR) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* PHY clock control */ 348c2ecf20Sopenharmony_ci#define S5PV210_UPHYCLK 0x4 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define S5PV210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) 378c2ecf20Sopenharmony_ci#define S5PV210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) 388c2ecf20Sopenharmony_ci#define S5PV210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) 398c2ecf20Sopenharmony_ci#define S5PV210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define S5PV210_UPHYCLK_PHY0_ID_PULLUP BIT(2) 428c2ecf20Sopenharmony_ci#define S5PV210_UPHYCLK_PHY0_COMMON_ON BIT(4) 438c2ecf20Sopenharmony_ci#define S5PV210_UPHYCLK_PHY1_COMMON_ON BIT(7) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* PHY reset control */ 468c2ecf20Sopenharmony_ci#define S5PV210_UPHYRST 0x8 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define S5PV210_URSTCON_PHY0 BIT(0) 498c2ecf20Sopenharmony_ci#define S5PV210_URSTCON_OTG_HLINK BIT(1) 508c2ecf20Sopenharmony_ci#define S5PV210_URSTCON_OTG_PHYLINK BIT(2) 518c2ecf20Sopenharmony_ci#define S5PV210_URSTCON_PHY1_ALL BIT(3) 528c2ecf20Sopenharmony_ci#define S5PV210_URSTCON_HOST_LINK_ALL BIT(4) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Isolation, configured in the power management unit */ 558c2ecf20Sopenharmony_ci#define S5PV210_USB_ISOL_OFFSET 0x680c 568c2ecf20Sopenharmony_ci#define S5PV210_USB_ISOL_DEVICE BIT(0) 578c2ecf20Sopenharmony_ci#define S5PV210_USB_ISOL_HOST BIT(1) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cienum s5pv210_phy_id { 618c2ecf20Sopenharmony_ci S5PV210_DEVICE, 628c2ecf20Sopenharmony_ci S5PV210_HOST, 638c2ecf20Sopenharmony_ci S5PV210_NUM_PHYS, 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* 678c2ecf20Sopenharmony_ci * s5pv210_rate_to_clk() converts the supplied clock rate to the value that 688c2ecf20Sopenharmony_ci * can be written to the phy register. 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_cistatic int s5pv210_rate_to_clk(unsigned long rate, u32 *reg) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci switch (rate) { 738c2ecf20Sopenharmony_ci case 12 * MHZ: 748c2ecf20Sopenharmony_ci *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ; 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci case 24 * MHZ: 778c2ecf20Sopenharmony_ci *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ; 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci case 48 * MHZ: 808c2ecf20Sopenharmony_ci *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ; 818c2ecf20Sopenharmony_ci break; 828c2ecf20Sopenharmony_ci default: 838c2ecf20Sopenharmony_ci return -EINVAL; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct samsung_usb2_phy_driver *drv = inst->drv; 928c2ecf20Sopenharmony_ci u32 mask; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci switch (inst->cfg->id) { 958c2ecf20Sopenharmony_ci case S5PV210_DEVICE: 968c2ecf20Sopenharmony_ci mask = S5PV210_USB_ISOL_DEVICE; 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci case S5PV210_HOST: 998c2ecf20Sopenharmony_ci mask = S5PV210_USB_ISOL_HOST; 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci default: 1028c2ecf20Sopenharmony_ci return; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET, 1068c2ecf20Sopenharmony_ci mask, on ? 0 : mask); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct samsung_usb2_phy_driver *drv = inst->drv; 1128c2ecf20Sopenharmony_ci u32 rstbits = 0; 1138c2ecf20Sopenharmony_ci u32 phypwr = 0; 1148c2ecf20Sopenharmony_ci u32 rst; 1158c2ecf20Sopenharmony_ci u32 pwr; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci switch (inst->cfg->id) { 1188c2ecf20Sopenharmony_ci case S5PV210_DEVICE: 1198c2ecf20Sopenharmony_ci phypwr = S5PV210_UPHYPWR_PHY0; 1208c2ecf20Sopenharmony_ci rstbits = S5PV210_URSTCON_PHY0; 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci case S5PV210_HOST: 1238c2ecf20Sopenharmony_ci phypwr = S5PV210_UPHYPWR_PHY1; 1248c2ecf20Sopenharmony_ci rstbits = S5PV210_URSTCON_PHY1_ALL | 1258c2ecf20Sopenharmony_ci S5PV210_URSTCON_HOST_LINK_ALL; 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (on) { 1308c2ecf20Sopenharmony_ci writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); 1338c2ecf20Sopenharmony_ci pwr &= ~phypwr; 1348c2ecf20Sopenharmony_ci writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci rst = readl(drv->reg_phy + S5PV210_UPHYRST); 1378c2ecf20Sopenharmony_ci rst |= rstbits; 1388c2ecf20Sopenharmony_ci writel(rst, drv->reg_phy + S5PV210_UPHYRST); 1398c2ecf20Sopenharmony_ci udelay(10); 1408c2ecf20Sopenharmony_ci rst &= ~rstbits; 1418c2ecf20Sopenharmony_ci writel(rst, drv->reg_phy + S5PV210_UPHYRST); 1428c2ecf20Sopenharmony_ci /* The following delay is necessary for the reset sequence to be 1438c2ecf20Sopenharmony_ci * completed 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci udelay(80); 1468c2ecf20Sopenharmony_ci } else { 1478c2ecf20Sopenharmony_ci pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); 1488c2ecf20Sopenharmony_ci pwr |= phypwr; 1498c2ecf20Sopenharmony_ci writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int s5pv210_power_on(struct samsung_usb2_phy_instance *inst) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci s5pv210_isol(inst, 0); 1568c2ecf20Sopenharmony_ci s5pv210_phy_pwr(inst, 1); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int s5pv210_power_off(struct samsung_usb2_phy_instance *inst) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci s5pv210_phy_pwr(inst, 0); 1648c2ecf20Sopenharmony_ci s5pv210_isol(inst, 1); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = { 1708c2ecf20Sopenharmony_ci [S5PV210_DEVICE] = { 1718c2ecf20Sopenharmony_ci .label = "device", 1728c2ecf20Sopenharmony_ci .id = S5PV210_DEVICE, 1738c2ecf20Sopenharmony_ci .power_on = s5pv210_power_on, 1748c2ecf20Sopenharmony_ci .power_off = s5pv210_power_off, 1758c2ecf20Sopenharmony_ci }, 1768c2ecf20Sopenharmony_ci [S5PV210_HOST] = { 1778c2ecf20Sopenharmony_ci .label = "host", 1788c2ecf20Sopenharmony_ci .id = S5PV210_HOST, 1798c2ecf20Sopenharmony_ci .power_on = s5pv210_power_on, 1808c2ecf20Sopenharmony_ci .power_off = s5pv210_power_off, 1818c2ecf20Sopenharmony_ci }, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ciconst struct samsung_usb2_phy_config s5pv210_usb2_phy_config = { 1858c2ecf20Sopenharmony_ci .num_phys = ARRAY_SIZE(s5pv210_phys), 1868c2ecf20Sopenharmony_ci .phys = s5pv210_phys, 1878c2ecf20Sopenharmony_ci .rate_to_clk = s5pv210_rate_to_clk, 1888c2ecf20Sopenharmony_ci}; 189