18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Samsung Electronics Co., Ltd. 68c2ecf20Sopenharmony_ci * Author: 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 <linux/regmap.h> 138c2ecf20Sopenharmony_ci#include "phy-samsung-usb2.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* Exynos USB PHY registers */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* PHY power control */ 188c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR 0x0 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND BIT(0) 218c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_PHY0_PWR BIT(3) 228c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR BIT(4) 238c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP BIT(5) 248c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_PHY0 ( \ 258c2ecf20Sopenharmony_ci EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \ 268c2ecf20Sopenharmony_ci EXYNOS_4210_UPHYPWR_PHY0_PWR | \ 278c2ecf20Sopenharmony_ci EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \ 288c2ecf20Sopenharmony_ci EXYNOS_4210_UPHYPWR_PHY0_SLEEP) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND BIT(6) 318c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_PHY1_PWR BIT(7) 328c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP BIT(8) 338c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_PHY1 ( \ 348c2ecf20Sopenharmony_ci EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \ 358c2ecf20Sopenharmony_ci EXYNOS_4210_UPHYPWR_PHY1_PWR | \ 368c2ecf20Sopenharmony_ci EXYNOS_4210_UPHYPWR_PHY1_SLEEP) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND BIT(9) 398c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP BIT(10) 408c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_HSIC0 ( \ 418c2ecf20Sopenharmony_ci EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \ 428c2ecf20Sopenharmony_ci EXYNOS_4210_UPHYPWR_HSIC0_SLEEP) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND BIT(11) 458c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP BIT(12) 468c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYPWR_HSIC1 ( \ 478c2ecf20Sopenharmony_ci EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \ 488c2ecf20Sopenharmony_ci EXYNOS_4210_UPHYPWR_HSIC1_SLEEP) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* PHY clock control */ 518c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYCLK 0x4 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) 548c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET 0 558c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) 568c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) 578c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP BIT(2) 608c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON BIT(4) 618c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON BIT(7) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* PHY reset control */ 648c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHYRST 0x8 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define EXYNOS_4210_URSTCON_PHY0 BIT(0) 678c2ecf20Sopenharmony_ci#define EXYNOS_4210_URSTCON_OTG_HLINK BIT(1) 688c2ecf20Sopenharmony_ci#define EXYNOS_4210_URSTCON_OTG_PHYLINK BIT(2) 698c2ecf20Sopenharmony_ci#define EXYNOS_4210_URSTCON_PHY1_ALL BIT(3) 708c2ecf20Sopenharmony_ci#define EXYNOS_4210_URSTCON_PHY1_P0 BIT(4) 718c2ecf20Sopenharmony_ci#define EXYNOS_4210_URSTCON_PHY1_P1P2 BIT(5) 728c2ecf20Sopenharmony_ci#define EXYNOS_4210_URSTCON_HOST_LINK_ALL BIT(6) 738c2ecf20Sopenharmony_ci#define EXYNOS_4210_URSTCON_HOST_LINK_P0 BIT(7) 748c2ecf20Sopenharmony_ci#define EXYNOS_4210_URSTCON_HOST_LINK_P1 BIT(8) 758c2ecf20Sopenharmony_ci#define EXYNOS_4210_URSTCON_HOST_LINK_P2 BIT(9) 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* Isolation, configured in the power management unit */ 788c2ecf20Sopenharmony_ci#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET 0x704 798c2ecf20Sopenharmony_ci#define EXYNOS_4210_USB_ISOL_DEVICE BIT(0) 808c2ecf20Sopenharmony_ci#define EXYNOS_4210_USB_ISOL_HOST_OFFSET 0x708 818c2ecf20Sopenharmony_ci#define EXYNOS_4210_USB_ISOL_HOST BIT(0) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* USBYPHY1 Floating prevention */ 848c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHY1CON 0x34 858c2ecf20Sopenharmony_ci#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION 0x1 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* Mode switching SUB Device <-> Host */ 888c2ecf20Sopenharmony_ci#define EXYNOS_4210_MODE_SWITCH_OFFSET 0x21c 898c2ecf20Sopenharmony_ci#define EXYNOS_4210_MODE_SWITCH_MASK 1 908c2ecf20Sopenharmony_ci#define EXYNOS_4210_MODE_SWITCH_DEVICE 0 918c2ecf20Sopenharmony_ci#define EXYNOS_4210_MODE_SWITCH_HOST 1 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cienum exynos4210_phy_id { 948c2ecf20Sopenharmony_ci EXYNOS4210_DEVICE, 958c2ecf20Sopenharmony_ci EXYNOS4210_HOST, 968c2ecf20Sopenharmony_ci EXYNOS4210_HSIC0, 978c2ecf20Sopenharmony_ci EXYNOS4210_HSIC1, 988c2ecf20Sopenharmony_ci EXYNOS4210_NUM_PHYS, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* 1028c2ecf20Sopenharmony_ci * exynos4210_rate_to_clk() converts the supplied clock rate to the value that 1038c2ecf20Sopenharmony_ci * can be written to the phy register. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_cistatic int exynos4210_rate_to_clk(unsigned long rate, u32 *reg) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci switch (rate) { 1088c2ecf20Sopenharmony_ci case 12 * MHZ: 1098c2ecf20Sopenharmony_ci *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ; 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci case 24 * MHZ: 1128c2ecf20Sopenharmony_ci *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ; 1138c2ecf20Sopenharmony_ci break; 1148c2ecf20Sopenharmony_ci case 48 * MHZ: 1158c2ecf20Sopenharmony_ci *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ; 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci default: 1188c2ecf20Sopenharmony_ci return -EINVAL; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct samsung_usb2_phy_driver *drv = inst->drv; 1278c2ecf20Sopenharmony_ci u32 offset; 1288c2ecf20Sopenharmony_ci u32 mask; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci switch (inst->cfg->id) { 1318c2ecf20Sopenharmony_ci case EXYNOS4210_DEVICE: 1328c2ecf20Sopenharmony_ci offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET; 1338c2ecf20Sopenharmony_ci mask = EXYNOS_4210_USB_ISOL_DEVICE; 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci case EXYNOS4210_HOST: 1368c2ecf20Sopenharmony_ci offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET; 1378c2ecf20Sopenharmony_ci mask = EXYNOS_4210_USB_ISOL_HOST; 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci default: 1408c2ecf20Sopenharmony_ci return; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct samsung_usb2_phy_driver *drv = inst->drv; 1498c2ecf20Sopenharmony_ci u32 rstbits = 0; 1508c2ecf20Sopenharmony_ci u32 phypwr = 0; 1518c2ecf20Sopenharmony_ci u32 rst; 1528c2ecf20Sopenharmony_ci u32 pwr; 1538c2ecf20Sopenharmony_ci u32 clk; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci switch (inst->cfg->id) { 1568c2ecf20Sopenharmony_ci case EXYNOS4210_DEVICE: 1578c2ecf20Sopenharmony_ci phypwr = EXYNOS_4210_UPHYPWR_PHY0; 1588c2ecf20Sopenharmony_ci rstbits = EXYNOS_4210_URSTCON_PHY0; 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci case EXYNOS4210_HOST: 1618c2ecf20Sopenharmony_ci phypwr = EXYNOS_4210_UPHYPWR_PHY1; 1628c2ecf20Sopenharmony_ci rstbits = EXYNOS_4210_URSTCON_PHY1_ALL | 1638c2ecf20Sopenharmony_ci EXYNOS_4210_URSTCON_PHY1_P0 | 1648c2ecf20Sopenharmony_ci EXYNOS_4210_URSTCON_PHY1_P1P2 | 1658c2ecf20Sopenharmony_ci EXYNOS_4210_URSTCON_HOST_LINK_ALL | 1668c2ecf20Sopenharmony_ci EXYNOS_4210_URSTCON_HOST_LINK_P0; 1678c2ecf20Sopenharmony_ci writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON); 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci case EXYNOS4210_HSIC0: 1708c2ecf20Sopenharmony_ci phypwr = EXYNOS_4210_UPHYPWR_HSIC0; 1718c2ecf20Sopenharmony_ci rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | 1728c2ecf20Sopenharmony_ci EXYNOS_4210_URSTCON_HOST_LINK_P1; 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci case EXYNOS4210_HSIC1: 1758c2ecf20Sopenharmony_ci phypwr = EXYNOS_4210_UPHYPWR_HSIC1; 1768c2ecf20Sopenharmony_ci rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | 1778c2ecf20Sopenharmony_ci EXYNOS_4210_URSTCON_HOST_LINK_P2; 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (on) { 1828c2ecf20Sopenharmony_ci clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK); 1838c2ecf20Sopenharmony_ci clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK; 1848c2ecf20Sopenharmony_ci clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET; 1858c2ecf20Sopenharmony_ci writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR); 1888c2ecf20Sopenharmony_ci pwr &= ~phypwr; 1898c2ecf20Sopenharmony_ci writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST); 1928c2ecf20Sopenharmony_ci rst |= rstbits; 1938c2ecf20Sopenharmony_ci writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST); 1948c2ecf20Sopenharmony_ci udelay(10); 1958c2ecf20Sopenharmony_ci rst &= ~rstbits; 1968c2ecf20Sopenharmony_ci writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST); 1978c2ecf20Sopenharmony_ci /* The following delay is necessary for the reset sequence to be 1988c2ecf20Sopenharmony_ci * completed */ 1998c2ecf20Sopenharmony_ci udelay(80); 2008c2ecf20Sopenharmony_ci } else { 2018c2ecf20Sopenharmony_ci pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR); 2028c2ecf20Sopenharmony_ci pwr |= phypwr; 2038c2ecf20Sopenharmony_ci writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR); 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int exynos4210_power_on(struct samsung_usb2_phy_instance *inst) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci /* Order of initialisation is important - first power then isolation */ 2108c2ecf20Sopenharmony_ci exynos4210_phy_pwr(inst, 1); 2118c2ecf20Sopenharmony_ci exynos4210_isol(inst, 0); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int exynos4210_power_off(struct samsung_usb2_phy_instance *inst) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci exynos4210_isol(inst, 1); 2198c2ecf20Sopenharmony_ci exynos4210_phy_pwr(inst, 0); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic const struct samsung_usb2_common_phy exynos4210_phys[] = { 2268c2ecf20Sopenharmony_ci { 2278c2ecf20Sopenharmony_ci .label = "device", 2288c2ecf20Sopenharmony_ci .id = EXYNOS4210_DEVICE, 2298c2ecf20Sopenharmony_ci .power_on = exynos4210_power_on, 2308c2ecf20Sopenharmony_ci .power_off = exynos4210_power_off, 2318c2ecf20Sopenharmony_ci }, 2328c2ecf20Sopenharmony_ci { 2338c2ecf20Sopenharmony_ci .label = "host", 2348c2ecf20Sopenharmony_ci .id = EXYNOS4210_HOST, 2358c2ecf20Sopenharmony_ci .power_on = exynos4210_power_on, 2368c2ecf20Sopenharmony_ci .power_off = exynos4210_power_off, 2378c2ecf20Sopenharmony_ci }, 2388c2ecf20Sopenharmony_ci { 2398c2ecf20Sopenharmony_ci .label = "hsic0", 2408c2ecf20Sopenharmony_ci .id = EXYNOS4210_HSIC0, 2418c2ecf20Sopenharmony_ci .power_on = exynos4210_power_on, 2428c2ecf20Sopenharmony_ci .power_off = exynos4210_power_off, 2438c2ecf20Sopenharmony_ci }, 2448c2ecf20Sopenharmony_ci { 2458c2ecf20Sopenharmony_ci .label = "hsic1", 2468c2ecf20Sopenharmony_ci .id = EXYNOS4210_HSIC1, 2478c2ecf20Sopenharmony_ci .power_on = exynos4210_power_on, 2488c2ecf20Sopenharmony_ci .power_off = exynos4210_power_off, 2498c2ecf20Sopenharmony_ci }, 2508c2ecf20Sopenharmony_ci}; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ciconst struct samsung_usb2_phy_config exynos4210_usb2_phy_config = { 2538c2ecf20Sopenharmony_ci .has_mode_switch = 0, 2548c2ecf20Sopenharmony_ci .num_phys = EXYNOS4210_NUM_PHYS, 2558c2ecf20Sopenharmony_ci .phys = exynos4210_phys, 2568c2ecf20Sopenharmony_ci .rate_to_clk = exynos4210_rate_to_clk, 2578c2ecf20Sopenharmony_ci}; 258