18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2017, 2019, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/clk.h> 78c2ecf20Sopenharmony_ci#include <linux/delay.h> 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/nvmem-consumer.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/of_device.h> 168c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/regmap.h> 198c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 208c2ecf20Sopenharmony_ci#include <linux/reset.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <dt-bindings/phy/phy-qcom-qusb2.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define QUSB2PHY_PLL_TEST 0x04 268c2ecf20Sopenharmony_ci#define CLK_REF_SEL BIT(7) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define QUSB2PHY_PLL_TUNE 0x08 298c2ecf20Sopenharmony_ci#define QUSB2PHY_PLL_USER_CTL1 0x0c 308c2ecf20Sopenharmony_ci#define QUSB2PHY_PLL_USER_CTL2 0x10 318c2ecf20Sopenharmony_ci#define QUSB2PHY_PLL_AUTOPGM_CTL1 0x1c 328c2ecf20Sopenharmony_ci#define QUSB2PHY_PLL_PWR_CTRL 0x18 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* QUSB2PHY_PLL_STATUS register bits */ 358c2ecf20Sopenharmony_ci#define PLL_LOCKED BIT(5) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* QUSB2PHY_PLL_COMMON_STATUS_ONE register bits */ 388c2ecf20Sopenharmony_ci#define CORE_READY_STATUS BIT(0) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* QUSB2PHY_PORT_POWERDOWN register bits */ 418c2ecf20Sopenharmony_ci#define CLAMP_N_EN BIT(5) 428c2ecf20Sopenharmony_ci#define FREEZIO_N BIT(1) 438c2ecf20Sopenharmony_ci#define POWER_DOWN BIT(0) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* QUSB2PHY_PWR_CTRL1 register bits */ 468c2ecf20Sopenharmony_ci#define PWR_CTRL1_VREF_SUPPLY_TRIM BIT(5) 478c2ecf20Sopenharmony_ci#define PWR_CTRL1_CLAMP_N_EN BIT(1) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define QUSB2PHY_REFCLK_ENABLE BIT(0) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define PHY_CLK_SCHEME_SEL BIT(0) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* QUSB2PHY_INTR_CTRL register bits */ 548c2ecf20Sopenharmony_ci#define DMSE_INTR_HIGH_SEL BIT(4) 558c2ecf20Sopenharmony_ci#define DPSE_INTR_HIGH_SEL BIT(3) 568c2ecf20Sopenharmony_ci#define CHG_DET_INTR_EN BIT(2) 578c2ecf20Sopenharmony_ci#define DMSE_INTR_EN BIT(1) 588c2ecf20Sopenharmony_ci#define DPSE_INTR_EN BIT(0) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* QUSB2PHY_PLL_CORE_INPUT_OVERRIDE register bits */ 618c2ecf20Sopenharmony_ci#define CORE_PLL_EN_FROM_RESET BIT(4) 628c2ecf20Sopenharmony_ci#define CORE_RESET BIT(5) 638c2ecf20Sopenharmony_ci#define CORE_RESET_MUX BIT(6) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* QUSB2PHY_IMP_CTRL1 register bits */ 668c2ecf20Sopenharmony_ci#define IMP_RES_OFFSET_MASK GENMASK(5, 0) 678c2ecf20Sopenharmony_ci#define IMP_RES_OFFSET_SHIFT 0x0 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* QUSB2PHY_PLL_BIAS_CONTROL_2 register bits */ 708c2ecf20Sopenharmony_ci#define BIAS_CTRL2_RES_OFFSET_MASK GENMASK(5, 0) 718c2ecf20Sopenharmony_ci#define BIAS_CTRL2_RES_OFFSET_SHIFT 0x0 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* QUSB2PHY_CHG_CONTROL_2 register bits */ 748c2ecf20Sopenharmony_ci#define CHG_CTRL2_OFFSET_MASK GENMASK(5, 4) 758c2ecf20Sopenharmony_ci#define CHG_CTRL2_OFFSET_SHIFT 0x4 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* QUSB2PHY_PORT_TUNE1 register bits */ 788c2ecf20Sopenharmony_ci#define HSTX_TRIM_MASK GENMASK(7, 4) 798c2ecf20Sopenharmony_ci#define HSTX_TRIM_SHIFT 0x4 808c2ecf20Sopenharmony_ci#define PREEMPH_WIDTH_HALF_BIT BIT(2) 818c2ecf20Sopenharmony_ci#define PREEMPHASIS_EN_MASK GENMASK(1, 0) 828c2ecf20Sopenharmony_ci#define PREEMPHASIS_EN_SHIFT 0x0 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* QUSB2PHY_PORT_TUNE2 register bits */ 858c2ecf20Sopenharmony_ci#define HSDISC_TRIM_MASK GENMASK(1, 0) 868c2ecf20Sopenharmony_ci#define HSDISC_TRIM_SHIFT 0x0 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#define QUSB2PHY_PLL_ANALOG_CONTROLS_TWO 0x04 898c2ecf20Sopenharmony_ci#define QUSB2PHY_PLL_CLOCK_INVERTERS 0x18c 908c2ecf20Sopenharmony_ci#define QUSB2PHY_PLL_CMODE 0x2c 918c2ecf20Sopenharmony_ci#define QUSB2PHY_PLL_LOCK_DELAY 0x184 928c2ecf20Sopenharmony_ci#define QUSB2PHY_PLL_DIGITAL_TIMERS_TWO 0xb4 938c2ecf20Sopenharmony_ci#define QUSB2PHY_PLL_BIAS_CONTROL_1 0x194 948c2ecf20Sopenharmony_ci#define QUSB2PHY_PLL_BIAS_CONTROL_2 0x198 958c2ecf20Sopenharmony_ci#define QUSB2PHY_PWR_CTRL2 0x214 968c2ecf20Sopenharmony_ci#define QUSB2PHY_IMP_CTRL1 0x220 978c2ecf20Sopenharmony_ci#define QUSB2PHY_IMP_CTRL2 0x224 988c2ecf20Sopenharmony_ci#define QUSB2PHY_CHG_CTRL2 0x23c 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistruct qusb2_phy_init_tbl { 1018c2ecf20Sopenharmony_ci unsigned int offset; 1028c2ecf20Sopenharmony_ci unsigned int val; 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * register part of layout ? 1058c2ecf20Sopenharmony_ci * if yes, then offset gives index in the reg-layout 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci int in_layout; 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#define QUSB2_PHY_INIT_CFG(o, v) \ 1118c2ecf20Sopenharmony_ci { \ 1128c2ecf20Sopenharmony_ci .offset = o, \ 1138c2ecf20Sopenharmony_ci .val = v, \ 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#define QUSB2_PHY_INIT_CFG_L(o, v) \ 1178c2ecf20Sopenharmony_ci { \ 1188c2ecf20Sopenharmony_ci .offset = o, \ 1198c2ecf20Sopenharmony_ci .val = v, \ 1208c2ecf20Sopenharmony_ci .in_layout = 1, \ 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* set of registers with offsets different per-PHY */ 1248c2ecf20Sopenharmony_cienum qusb2phy_reg_layout { 1258c2ecf20Sopenharmony_ci QUSB2PHY_PLL_CORE_INPUT_OVERRIDE, 1268c2ecf20Sopenharmony_ci QUSB2PHY_PLL_STATUS, 1278c2ecf20Sopenharmony_ci QUSB2PHY_PORT_TUNE1, 1288c2ecf20Sopenharmony_ci QUSB2PHY_PORT_TUNE2, 1298c2ecf20Sopenharmony_ci QUSB2PHY_PORT_TUNE3, 1308c2ecf20Sopenharmony_ci QUSB2PHY_PORT_TUNE4, 1318c2ecf20Sopenharmony_ci QUSB2PHY_PORT_TUNE5, 1328c2ecf20Sopenharmony_ci QUSB2PHY_PORT_TEST1, 1338c2ecf20Sopenharmony_ci QUSB2PHY_PORT_TEST2, 1348c2ecf20Sopenharmony_ci QUSB2PHY_PORT_POWERDOWN, 1358c2ecf20Sopenharmony_ci QUSB2PHY_INTR_CTRL, 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic const unsigned int msm8996_regs_layout[] = { 1398c2ecf20Sopenharmony_ci [QUSB2PHY_PLL_STATUS] = 0x38, 1408c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TUNE1] = 0x80, 1418c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TUNE2] = 0x84, 1428c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TUNE3] = 0x88, 1438c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TUNE4] = 0x8c, 1448c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TUNE5] = 0x90, 1458c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TEST1] = 0xb8, 1468c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TEST2] = 0x9c, 1478c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_POWERDOWN] = 0xb4, 1488c2ecf20Sopenharmony_ci [QUSB2PHY_INTR_CTRL] = 0xbc, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic const struct qusb2_phy_init_tbl msm8996_init_tbl[] = { 1528c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xf8), 1538c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0xb3), 1548c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x83), 1558c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0xc0), 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30), 1588c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79), 1598c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21), 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14), 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f), 1648c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic const unsigned int msm8998_regs_layout[] = { 1688c2ecf20Sopenharmony_ci [QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8, 1698c2ecf20Sopenharmony_ci [QUSB2PHY_PLL_STATUS] = 0x1a0, 1708c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TUNE1] = 0x23c, 1718c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TUNE2] = 0x240, 1728c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TUNE3] = 0x244, 1738c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TUNE4] = 0x248, 1748c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TEST1] = 0x24c, 1758c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TEST2] = 0x250, 1768c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_POWERDOWN] = 0x210, 1778c2ecf20Sopenharmony_ci [QUSB2PHY_INTR_CTRL] = 0x22c, 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic const struct qusb2_phy_init_tbl msm8998_init_tbl[] = { 1818c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x13), 1828c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c), 1838c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CMODE, 0x80), 1848c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_LOCK_DELAY, 0x0a), 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xa5), 1878c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x09), 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19), 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic const unsigned int qusb2_v2_regs_layout[] = { 1938c2ecf20Sopenharmony_ci [QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8, 1948c2ecf20Sopenharmony_ci [QUSB2PHY_PLL_STATUS] = 0x1a0, 1958c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TUNE1] = 0x240, 1968c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TUNE2] = 0x244, 1978c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TUNE3] = 0x248, 1988c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TUNE4] = 0x24c, 1998c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TUNE5] = 0x250, 2008c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TEST1] = 0x254, 2018c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_TEST2] = 0x258, 2028c2ecf20Sopenharmony_ci [QUSB2PHY_PORT_POWERDOWN] = 0x210, 2038c2ecf20Sopenharmony_ci [QUSB2PHY_INTR_CTRL] = 0x230, 2048c2ecf20Sopenharmony_ci}; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic const struct qusb2_phy_init_tbl qusb2_v2_init_tbl[] = { 2078c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x03), 2088c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c), 2098c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CMODE, 0x80), 2108c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_LOCK_DELAY, 0x0a), 2118c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19), 2128c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_BIAS_CONTROL_1, 0x40), 2138c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_BIAS_CONTROL_2, 0x20), 2148c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_PWR_CTRL2, 0x21), 2158c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_IMP_CTRL1, 0x0), 2168c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_IMP_CTRL2, 0x58), 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0x30), 2198c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x29), 2208c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0xca), 2218c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0x04), 2228c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE5, 0x03), 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci QUSB2_PHY_INIT_CFG(QUSB2PHY_CHG_CTRL2, 0x0), 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistruct qusb2_phy_cfg { 2288c2ecf20Sopenharmony_ci const struct qusb2_phy_init_tbl *tbl; 2298c2ecf20Sopenharmony_ci /* number of entries in the table */ 2308c2ecf20Sopenharmony_ci unsigned int tbl_num; 2318c2ecf20Sopenharmony_ci /* offset to PHY_CLK_SCHEME register in TCSR map */ 2328c2ecf20Sopenharmony_ci unsigned int clk_scheme_offset; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* array of registers with different offsets */ 2358c2ecf20Sopenharmony_ci const unsigned int *regs; 2368c2ecf20Sopenharmony_ci unsigned int mask_core_ready; 2378c2ecf20Sopenharmony_ci unsigned int disable_ctrl; 2388c2ecf20Sopenharmony_ci unsigned int autoresume_en; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* true if PHY has PLL_TEST register to select clk_scheme */ 2418c2ecf20Sopenharmony_ci bool has_pll_test; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* true if TUNE1 register must be updated by fused value, else TUNE2 */ 2448c2ecf20Sopenharmony_ci bool update_tune1_with_efuse; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* true if PHY has PLL_CORE_INPUT_OVERRIDE register to reset PLL */ 2478c2ecf20Sopenharmony_ci bool has_pll_override; 2488c2ecf20Sopenharmony_ci}; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic const struct qusb2_phy_cfg msm8996_phy_cfg = { 2518c2ecf20Sopenharmony_ci .tbl = msm8996_init_tbl, 2528c2ecf20Sopenharmony_ci .tbl_num = ARRAY_SIZE(msm8996_init_tbl), 2538c2ecf20Sopenharmony_ci .regs = msm8996_regs_layout, 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci .has_pll_test = true, 2568c2ecf20Sopenharmony_ci .disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN), 2578c2ecf20Sopenharmony_ci .mask_core_ready = PLL_LOCKED, 2588c2ecf20Sopenharmony_ci .autoresume_en = BIT(3), 2598c2ecf20Sopenharmony_ci}; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic const struct qusb2_phy_cfg msm8998_phy_cfg = { 2628c2ecf20Sopenharmony_ci .tbl = msm8998_init_tbl, 2638c2ecf20Sopenharmony_ci .tbl_num = ARRAY_SIZE(msm8998_init_tbl), 2648c2ecf20Sopenharmony_ci .regs = msm8998_regs_layout, 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci .disable_ctrl = POWER_DOWN, 2678c2ecf20Sopenharmony_ci .mask_core_ready = CORE_READY_STATUS, 2688c2ecf20Sopenharmony_ci .has_pll_override = true, 2698c2ecf20Sopenharmony_ci .autoresume_en = BIT(0), 2708c2ecf20Sopenharmony_ci .update_tune1_with_efuse = true, 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic const struct qusb2_phy_cfg qusb2_v2_phy_cfg = { 2748c2ecf20Sopenharmony_ci .tbl = qusb2_v2_init_tbl, 2758c2ecf20Sopenharmony_ci .tbl_num = ARRAY_SIZE(qusb2_v2_init_tbl), 2768c2ecf20Sopenharmony_ci .regs = qusb2_v2_regs_layout, 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci .disable_ctrl = (PWR_CTRL1_VREF_SUPPLY_TRIM | PWR_CTRL1_CLAMP_N_EN | 2798c2ecf20Sopenharmony_ci POWER_DOWN), 2808c2ecf20Sopenharmony_ci .mask_core_ready = CORE_READY_STATUS, 2818c2ecf20Sopenharmony_ci .has_pll_override = true, 2828c2ecf20Sopenharmony_ci .autoresume_en = BIT(0), 2838c2ecf20Sopenharmony_ci .update_tune1_with_efuse = true, 2848c2ecf20Sopenharmony_ci}; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic const char * const qusb2_phy_vreg_names[] = { 2878c2ecf20Sopenharmony_ci "vdda-pll", "vdda-phy-dpdm", 2888c2ecf20Sopenharmony_ci}; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci#define QUSB2_NUM_VREGS ARRAY_SIZE(qusb2_phy_vreg_names) 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* struct override_param - structure holding qusb2 v2 phy overriding param 2938c2ecf20Sopenharmony_ci * set override true if the device tree property exists and read and assign 2948c2ecf20Sopenharmony_ci * to value 2958c2ecf20Sopenharmony_ci */ 2968c2ecf20Sopenharmony_cistruct override_param { 2978c2ecf20Sopenharmony_ci bool override; 2988c2ecf20Sopenharmony_ci u8 value; 2998c2ecf20Sopenharmony_ci}; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/*struct override_params - structure holding qusb2 v2 phy overriding params 3028c2ecf20Sopenharmony_ci * @imp_res_offset: rescode offset to be updated in IMP_CTRL1 register 3038c2ecf20Sopenharmony_ci * @hstx_trim: HSTX_TRIM to be updated in TUNE1 register 3048c2ecf20Sopenharmony_ci * @preemphasis: Amplitude Pre-Emphasis to be updated in TUNE1 register 3058c2ecf20Sopenharmony_ci * @preemphasis_width: half/full-width Pre-Emphasis updated via TUNE1 3068c2ecf20Sopenharmony_ci * @bias_ctrl: bias ctrl to be updated in BIAS_CONTROL_2 register 3078c2ecf20Sopenharmony_ci * @charge_ctrl: charge ctrl to be updated in CHG_CTRL2 register 3088c2ecf20Sopenharmony_ci * @hsdisc_trim: disconnect threshold to be updated in TUNE2 register 3098c2ecf20Sopenharmony_ci */ 3108c2ecf20Sopenharmony_cistruct override_params { 3118c2ecf20Sopenharmony_ci struct override_param imp_res_offset; 3128c2ecf20Sopenharmony_ci struct override_param hstx_trim; 3138c2ecf20Sopenharmony_ci struct override_param preemphasis; 3148c2ecf20Sopenharmony_ci struct override_param preemphasis_width; 3158c2ecf20Sopenharmony_ci struct override_param bias_ctrl; 3168c2ecf20Sopenharmony_ci struct override_param charge_ctrl; 3178c2ecf20Sopenharmony_ci struct override_param hsdisc_trim; 3188c2ecf20Sopenharmony_ci}; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/** 3218c2ecf20Sopenharmony_ci * struct qusb2_phy - structure holding qusb2 phy attributes 3228c2ecf20Sopenharmony_ci * 3238c2ecf20Sopenharmony_ci * @phy: generic phy 3248c2ecf20Sopenharmony_ci * @base: iomapped memory space for qubs2 phy 3258c2ecf20Sopenharmony_ci * 3268c2ecf20Sopenharmony_ci * @cfg_ahb_clk: AHB2PHY interface clock 3278c2ecf20Sopenharmony_ci * @ref_clk: phy reference clock 3288c2ecf20Sopenharmony_ci * @iface_clk: phy interface clock 3298c2ecf20Sopenharmony_ci * @phy_reset: phy reset control 3308c2ecf20Sopenharmony_ci * @vregs: regulator supplies bulk data 3318c2ecf20Sopenharmony_ci * 3328c2ecf20Sopenharmony_ci * @tcsr: TCSR syscon register map 3338c2ecf20Sopenharmony_ci * @cell: nvmem cell containing phy tuning value 3348c2ecf20Sopenharmony_ci * 3358c2ecf20Sopenharmony_ci * @overrides: pointer to structure for all overriding tuning params 3368c2ecf20Sopenharmony_ci * 3378c2ecf20Sopenharmony_ci * @cfg: phy config data 3388c2ecf20Sopenharmony_ci * @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme 3398c2ecf20Sopenharmony_ci * @phy_initialized: indicate if PHY has been initialized 3408c2ecf20Sopenharmony_ci * @mode: current PHY mode 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_cistruct qusb2_phy { 3438c2ecf20Sopenharmony_ci struct phy *phy; 3448c2ecf20Sopenharmony_ci void __iomem *base; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci struct clk *cfg_ahb_clk; 3478c2ecf20Sopenharmony_ci struct clk *ref_clk; 3488c2ecf20Sopenharmony_ci struct clk *iface_clk; 3498c2ecf20Sopenharmony_ci struct reset_control *phy_reset; 3508c2ecf20Sopenharmony_ci struct regulator_bulk_data vregs[QUSB2_NUM_VREGS]; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci struct regmap *tcsr; 3538c2ecf20Sopenharmony_ci struct nvmem_cell *cell; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci struct override_params overrides; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci const struct qusb2_phy_cfg *cfg; 3588c2ecf20Sopenharmony_ci bool has_se_clk_scheme; 3598c2ecf20Sopenharmony_ci bool phy_initialized; 3608c2ecf20Sopenharmony_ci enum phy_mode mode; 3618c2ecf20Sopenharmony_ci}; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic inline void qusb2_write_mask(void __iomem *base, u32 offset, 3648c2ecf20Sopenharmony_ci u32 val, u32 mask) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci u32 reg; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci reg = readl(base + offset); 3698c2ecf20Sopenharmony_ci reg &= ~mask; 3708c2ecf20Sopenharmony_ci reg |= val & mask; 3718c2ecf20Sopenharmony_ci writel(reg, base + offset); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* Ensure above write is completed */ 3748c2ecf20Sopenharmony_ci readl(base + offset); 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci u32 reg; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci reg = readl(base + offset); 3828c2ecf20Sopenharmony_ci reg |= val; 3838c2ecf20Sopenharmony_ci writel(reg, base + offset); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* Ensure above write is completed */ 3868c2ecf20Sopenharmony_ci readl(base + offset); 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic inline void qusb2_clrbits(void __iomem *base, u32 offset, u32 val) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci u32 reg; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci reg = readl(base + offset); 3948c2ecf20Sopenharmony_ci reg &= ~val; 3958c2ecf20Sopenharmony_ci writel(reg, base + offset); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* Ensure above write is completed */ 3988c2ecf20Sopenharmony_ci readl(base + offset); 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic inline 4028c2ecf20Sopenharmony_civoid qcom_qusb2_phy_configure(void __iomem *base, 4038c2ecf20Sopenharmony_ci const unsigned int *regs, 4048c2ecf20Sopenharmony_ci const struct qusb2_phy_init_tbl tbl[], int num) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci int i; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 4098c2ecf20Sopenharmony_ci if (tbl[i].in_layout) 4108c2ecf20Sopenharmony_ci writel(tbl[i].val, base + regs[tbl[i].offset]); 4118c2ecf20Sopenharmony_ci else 4128c2ecf20Sopenharmony_ci writel(tbl[i].val, base + tbl[i].offset); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci/* 4178c2ecf20Sopenharmony_ci * Update board specific PHY tuning override values if specified from 4188c2ecf20Sopenharmony_ci * device tree. 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_cistatic void qusb2_phy_override_phy_params(struct qusb2_phy *qphy) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci const struct qusb2_phy_cfg *cfg = qphy->cfg; 4238c2ecf20Sopenharmony_ci struct override_params *or = &qphy->overrides; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (or->imp_res_offset.override) 4268c2ecf20Sopenharmony_ci qusb2_write_mask(qphy->base, QUSB2PHY_IMP_CTRL1, 4278c2ecf20Sopenharmony_ci or->imp_res_offset.value << IMP_RES_OFFSET_SHIFT, 4288c2ecf20Sopenharmony_ci IMP_RES_OFFSET_MASK); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (or->bias_ctrl.override) 4318c2ecf20Sopenharmony_ci qusb2_write_mask(qphy->base, QUSB2PHY_PLL_BIAS_CONTROL_2, 4328c2ecf20Sopenharmony_ci or->bias_ctrl.value << BIAS_CTRL2_RES_OFFSET_SHIFT, 4338c2ecf20Sopenharmony_ci BIAS_CTRL2_RES_OFFSET_MASK); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (or->charge_ctrl.override) 4368c2ecf20Sopenharmony_ci qusb2_write_mask(qphy->base, QUSB2PHY_CHG_CTRL2, 4378c2ecf20Sopenharmony_ci or->charge_ctrl.value << CHG_CTRL2_OFFSET_SHIFT, 4388c2ecf20Sopenharmony_ci CHG_CTRL2_OFFSET_MASK); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (or->hstx_trim.override) 4418c2ecf20Sopenharmony_ci qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1], 4428c2ecf20Sopenharmony_ci or->hstx_trim.value << HSTX_TRIM_SHIFT, 4438c2ecf20Sopenharmony_ci HSTX_TRIM_MASK); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (or->preemphasis.override) 4468c2ecf20Sopenharmony_ci qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1], 4478c2ecf20Sopenharmony_ci or->preemphasis.value << PREEMPHASIS_EN_SHIFT, 4488c2ecf20Sopenharmony_ci PREEMPHASIS_EN_MASK); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (or->preemphasis_width.override) { 4518c2ecf20Sopenharmony_ci if (or->preemphasis_width.value == 4528c2ecf20Sopenharmony_ci QUSB2_V2_PREEMPHASIS_WIDTH_HALF_BIT) 4538c2ecf20Sopenharmony_ci qusb2_setbits(qphy->base, 4548c2ecf20Sopenharmony_ci cfg->regs[QUSB2PHY_PORT_TUNE1], 4558c2ecf20Sopenharmony_ci PREEMPH_WIDTH_HALF_BIT); 4568c2ecf20Sopenharmony_ci else 4578c2ecf20Sopenharmony_ci qusb2_clrbits(qphy->base, 4588c2ecf20Sopenharmony_ci cfg->regs[QUSB2PHY_PORT_TUNE1], 4598c2ecf20Sopenharmony_ci PREEMPH_WIDTH_HALF_BIT); 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (or->hsdisc_trim.override) 4638c2ecf20Sopenharmony_ci qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE2], 4648c2ecf20Sopenharmony_ci or->hsdisc_trim.value << HSDISC_TRIM_SHIFT, 4658c2ecf20Sopenharmony_ci HSDISC_TRIM_MASK); 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci/* 4698c2ecf20Sopenharmony_ci * Fetches HS Tx tuning value from nvmem and sets the 4708c2ecf20Sopenharmony_ci * QUSB2PHY_PORT_TUNE1/2 register. 4718c2ecf20Sopenharmony_ci * For error case, skip setting the value and use the default value. 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_cistatic void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct device *dev = &qphy->phy->dev; 4768c2ecf20Sopenharmony_ci const struct qusb2_phy_cfg *cfg = qphy->cfg; 4778c2ecf20Sopenharmony_ci u8 *val, hstx_trim; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* efuse register is optional */ 4808c2ecf20Sopenharmony_ci if (!qphy->cell) 4818c2ecf20Sopenharmony_ci return; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* 4848c2ecf20Sopenharmony_ci * Read efuse register having TUNE2/1 parameter's high nibble. 4858c2ecf20Sopenharmony_ci * If efuse register shows value as 0x0 (indicating value is not 4868c2ecf20Sopenharmony_ci * fused), or if we fail to find a valid efuse register setting, 4878c2ecf20Sopenharmony_ci * then use default value for high nibble that we have already 4888c2ecf20Sopenharmony_ci * set while configuring the phy. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ci val = nvmem_cell_read(qphy->cell, NULL); 4918c2ecf20Sopenharmony_ci if (IS_ERR(val)) { 4928c2ecf20Sopenharmony_ci dev_dbg(dev, "failed to read a valid hs-tx trim value\n"); 4938c2ecf20Sopenharmony_ci return; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci hstx_trim = val[0]; 4968c2ecf20Sopenharmony_ci kfree(val); 4978c2ecf20Sopenharmony_ci if (!hstx_trim) { 4988c2ecf20Sopenharmony_ci dev_dbg(dev, "failed to read a valid hs-tx trim value\n"); 4998c2ecf20Sopenharmony_ci return; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Fused TUNE1/2 value is the higher nibble only */ 5038c2ecf20Sopenharmony_ci if (cfg->update_tune1_with_efuse) 5048c2ecf20Sopenharmony_ci qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1], 5058c2ecf20Sopenharmony_ci hstx_trim << HSTX_TRIM_SHIFT, HSTX_TRIM_MASK); 5068c2ecf20Sopenharmony_ci else 5078c2ecf20Sopenharmony_ci qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE2], 5088c2ecf20Sopenharmony_ci hstx_trim << HSTX_TRIM_SHIFT, HSTX_TRIM_MASK); 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic int qusb2_phy_set_mode(struct phy *phy, 5128c2ecf20Sopenharmony_ci enum phy_mode mode, int submode) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct qusb2_phy *qphy = phy_get_drvdata(phy); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci qphy->mode = mode; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return 0; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic int __maybe_unused qusb2_phy_runtime_suspend(struct device *dev) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct qusb2_phy *qphy = dev_get_drvdata(dev); 5248c2ecf20Sopenharmony_ci const struct qusb2_phy_cfg *cfg = qphy->cfg; 5258c2ecf20Sopenharmony_ci u32 intr_mask; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci dev_vdbg(dev, "Suspending QUSB2 Phy, mode:%d\n", qphy->mode); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (!qphy->phy_initialized) { 5308c2ecf20Sopenharmony_ci dev_vdbg(dev, "PHY not initialized, bailing out\n"); 5318c2ecf20Sopenharmony_ci return 0; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* 5358c2ecf20Sopenharmony_ci * Enable DP/DM interrupts to detect line state changes based on current 5368c2ecf20Sopenharmony_ci * speed. In other words, enable the triggers _opposite_ of what the 5378c2ecf20Sopenharmony_ci * current D+/D- levels are e.g. if currently D+ high, D- low 5388c2ecf20Sopenharmony_ci * (HS 'J'/Suspend), configure the mask to trigger on D+ low OR D- high 5398c2ecf20Sopenharmony_ci */ 5408c2ecf20Sopenharmony_ci intr_mask = DPSE_INTR_EN | DMSE_INTR_EN; 5418c2ecf20Sopenharmony_ci switch (qphy->mode) { 5428c2ecf20Sopenharmony_ci case PHY_MODE_USB_HOST_HS: 5438c2ecf20Sopenharmony_ci case PHY_MODE_USB_HOST_FS: 5448c2ecf20Sopenharmony_ci case PHY_MODE_USB_DEVICE_HS: 5458c2ecf20Sopenharmony_ci case PHY_MODE_USB_DEVICE_FS: 5468c2ecf20Sopenharmony_ci intr_mask |= DMSE_INTR_HIGH_SEL; 5478c2ecf20Sopenharmony_ci break; 5488c2ecf20Sopenharmony_ci case PHY_MODE_USB_HOST_LS: 5498c2ecf20Sopenharmony_ci case PHY_MODE_USB_DEVICE_LS: 5508c2ecf20Sopenharmony_ci intr_mask |= DPSE_INTR_HIGH_SEL; 5518c2ecf20Sopenharmony_ci break; 5528c2ecf20Sopenharmony_ci default: 5538c2ecf20Sopenharmony_ci /* No device connected, enable both DP/DM high interrupt */ 5548c2ecf20Sopenharmony_ci intr_mask |= DMSE_INTR_HIGH_SEL; 5558c2ecf20Sopenharmony_ci intr_mask |= DPSE_INTR_HIGH_SEL; 5568c2ecf20Sopenharmony_ci break; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci writel(intr_mask, qphy->base + cfg->regs[QUSB2PHY_INTR_CTRL]); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* hold core PLL into reset */ 5628c2ecf20Sopenharmony_ci if (cfg->has_pll_override) { 5638c2ecf20Sopenharmony_ci qusb2_setbits(qphy->base, 5648c2ecf20Sopenharmony_ci cfg->regs[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE], 5658c2ecf20Sopenharmony_ci CORE_PLL_EN_FROM_RESET | CORE_RESET | 5668c2ecf20Sopenharmony_ci CORE_RESET_MUX); 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* enable phy auto-resume only if device is connected on bus */ 5708c2ecf20Sopenharmony_ci if (qphy->mode != PHY_MODE_INVALID) { 5718c2ecf20Sopenharmony_ci qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TEST1], 5728c2ecf20Sopenharmony_ci cfg->autoresume_en); 5738c2ecf20Sopenharmony_ci /* Autoresume bit has to be toggled in order to enable it */ 5748c2ecf20Sopenharmony_ci qusb2_clrbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TEST1], 5758c2ecf20Sopenharmony_ci cfg->autoresume_en); 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (!qphy->has_se_clk_scheme) 5798c2ecf20Sopenharmony_ci clk_disable_unprepare(qphy->ref_clk); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci clk_disable_unprepare(qphy->cfg_ahb_clk); 5828c2ecf20Sopenharmony_ci clk_disable_unprepare(qphy->iface_clk); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci return 0; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic int __maybe_unused qusb2_phy_runtime_resume(struct device *dev) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci struct qusb2_phy *qphy = dev_get_drvdata(dev); 5908c2ecf20Sopenharmony_ci const struct qusb2_phy_cfg *cfg = qphy->cfg; 5918c2ecf20Sopenharmony_ci int ret; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci dev_vdbg(dev, "Resuming QUSB2 phy, mode:%d\n", qphy->mode); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (!qphy->phy_initialized) { 5968c2ecf20Sopenharmony_ci dev_vdbg(dev, "PHY not initialized, bailing out\n"); 5978c2ecf20Sopenharmony_ci return 0; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci ret = clk_prepare_enable(qphy->iface_clk); 6018c2ecf20Sopenharmony_ci if (ret) { 6028c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable iface_clk, %d\n", ret); 6038c2ecf20Sopenharmony_ci return ret; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci ret = clk_prepare_enable(qphy->cfg_ahb_clk); 6078c2ecf20Sopenharmony_ci if (ret) { 6088c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable cfg ahb clock, %d\n", ret); 6098c2ecf20Sopenharmony_ci goto disable_iface_clk; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (!qphy->has_se_clk_scheme) { 6138c2ecf20Sopenharmony_ci ret = clk_prepare_enable(qphy->ref_clk); 6148c2ecf20Sopenharmony_ci if (ret) { 6158c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable ref clk, %d\n", ret); 6168c2ecf20Sopenharmony_ci goto disable_ahb_clk; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci writel(0x0, qphy->base + cfg->regs[QUSB2PHY_INTR_CTRL]); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci /* bring core PLL out of reset */ 6238c2ecf20Sopenharmony_ci if (cfg->has_pll_override) { 6248c2ecf20Sopenharmony_ci qusb2_clrbits(qphy->base, 6258c2ecf20Sopenharmony_ci cfg->regs[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE], 6268c2ecf20Sopenharmony_ci CORE_RESET | CORE_RESET_MUX); 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return 0; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cidisable_ahb_clk: 6328c2ecf20Sopenharmony_ci clk_disable_unprepare(qphy->cfg_ahb_clk); 6338c2ecf20Sopenharmony_cidisable_iface_clk: 6348c2ecf20Sopenharmony_ci clk_disable_unprepare(qphy->iface_clk); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci return ret; 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic int qusb2_phy_init(struct phy *phy) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct qusb2_phy *qphy = phy_get_drvdata(phy); 6428c2ecf20Sopenharmony_ci const struct qusb2_phy_cfg *cfg = qphy->cfg; 6438c2ecf20Sopenharmony_ci unsigned int val = 0; 6448c2ecf20Sopenharmony_ci unsigned int clk_scheme; 6458c2ecf20Sopenharmony_ci int ret; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci dev_vdbg(&phy->dev, "%s(): Initializing QUSB2 phy\n", __func__); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* turn on regulator supplies */ 6508c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(qphy->vregs), qphy->vregs); 6518c2ecf20Sopenharmony_ci if (ret) 6528c2ecf20Sopenharmony_ci return ret; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci ret = clk_prepare_enable(qphy->iface_clk); 6558c2ecf20Sopenharmony_ci if (ret) { 6568c2ecf20Sopenharmony_ci dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret); 6578c2ecf20Sopenharmony_ci goto poweroff_phy; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* enable ahb interface clock to program phy */ 6618c2ecf20Sopenharmony_ci ret = clk_prepare_enable(qphy->cfg_ahb_clk); 6628c2ecf20Sopenharmony_ci if (ret) { 6638c2ecf20Sopenharmony_ci dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret); 6648c2ecf20Sopenharmony_ci goto disable_iface_clk; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* Perform phy reset */ 6688c2ecf20Sopenharmony_ci ret = reset_control_assert(qphy->phy_reset); 6698c2ecf20Sopenharmony_ci if (ret) { 6708c2ecf20Sopenharmony_ci dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret); 6718c2ecf20Sopenharmony_ci goto disable_ahb_clk; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* 100 us delay to keep PHY in reset mode */ 6758c2ecf20Sopenharmony_ci usleep_range(100, 150); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci ret = reset_control_deassert(qphy->phy_reset); 6788c2ecf20Sopenharmony_ci if (ret) { 6798c2ecf20Sopenharmony_ci dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret); 6808c2ecf20Sopenharmony_ci goto disable_ahb_clk; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci /* Disable the PHY */ 6848c2ecf20Sopenharmony_ci qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_POWERDOWN], 6858c2ecf20Sopenharmony_ci qphy->cfg->disable_ctrl); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (cfg->has_pll_test) { 6888c2ecf20Sopenharmony_ci /* save reset value to override reference clock scheme later */ 6898c2ecf20Sopenharmony_ci val = readl(qphy->base + QUSB2PHY_PLL_TEST); 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci qcom_qusb2_phy_configure(qphy->base, cfg->regs, cfg->tbl, 6938c2ecf20Sopenharmony_ci cfg->tbl_num); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci /* Override board specific PHY tuning values */ 6968c2ecf20Sopenharmony_ci qusb2_phy_override_phy_params(qphy); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci /* Set efuse value for tuning the PHY */ 6998c2ecf20Sopenharmony_ci qusb2_phy_set_tune2_param(qphy); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* Enable the PHY */ 7028c2ecf20Sopenharmony_ci qusb2_clrbits(qphy->base, cfg->regs[QUSB2PHY_PORT_POWERDOWN], 7038c2ecf20Sopenharmony_ci POWER_DOWN); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* Required to get phy pll lock successfully */ 7068c2ecf20Sopenharmony_ci usleep_range(150, 160); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* Default is single-ended clock on msm8996 */ 7098c2ecf20Sopenharmony_ci qphy->has_se_clk_scheme = true; 7108c2ecf20Sopenharmony_ci /* 7118c2ecf20Sopenharmony_ci * read TCSR_PHY_CLK_SCHEME register to check if single-ended 7128c2ecf20Sopenharmony_ci * clock scheme is selected. If yes, then disable differential 7138c2ecf20Sopenharmony_ci * ref_clk and use single-ended clock, otherwise use differential 7148c2ecf20Sopenharmony_ci * ref_clk only. 7158c2ecf20Sopenharmony_ci */ 7168c2ecf20Sopenharmony_ci if (qphy->tcsr) { 7178c2ecf20Sopenharmony_ci ret = regmap_read(qphy->tcsr, qphy->cfg->clk_scheme_offset, 7188c2ecf20Sopenharmony_ci &clk_scheme); 7198c2ecf20Sopenharmony_ci if (ret) { 7208c2ecf20Sopenharmony_ci dev_err(&phy->dev, "failed to read clk scheme reg\n"); 7218c2ecf20Sopenharmony_ci goto assert_phy_reset; 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci /* is it a differential clock scheme ? */ 7258c2ecf20Sopenharmony_ci if (!(clk_scheme & PHY_CLK_SCHEME_SEL)) { 7268c2ecf20Sopenharmony_ci dev_vdbg(&phy->dev, "%s(): select differential clk\n", 7278c2ecf20Sopenharmony_ci __func__); 7288c2ecf20Sopenharmony_ci qphy->has_se_clk_scheme = false; 7298c2ecf20Sopenharmony_ci } else { 7308c2ecf20Sopenharmony_ci dev_vdbg(&phy->dev, "%s(): select single-ended clk\n", 7318c2ecf20Sopenharmony_ci __func__); 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (!qphy->has_se_clk_scheme) { 7368c2ecf20Sopenharmony_ci ret = clk_prepare_enable(qphy->ref_clk); 7378c2ecf20Sopenharmony_ci if (ret) { 7388c2ecf20Sopenharmony_ci dev_err(&phy->dev, "failed to enable ref clk, %d\n", 7398c2ecf20Sopenharmony_ci ret); 7408c2ecf20Sopenharmony_ci goto assert_phy_reset; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (cfg->has_pll_test) { 7458c2ecf20Sopenharmony_ci if (!qphy->has_se_clk_scheme) 7468c2ecf20Sopenharmony_ci val &= ~CLK_REF_SEL; 7478c2ecf20Sopenharmony_ci else 7488c2ecf20Sopenharmony_ci val |= CLK_REF_SEL; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci writel(val, qphy->base + QUSB2PHY_PLL_TEST); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* ensure above write is through */ 7538c2ecf20Sopenharmony_ci readl(qphy->base + QUSB2PHY_PLL_TEST); 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* Required to get phy pll lock successfully */ 7578c2ecf20Sopenharmony_ci usleep_range(100, 110); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci val = readb(qphy->base + cfg->regs[QUSB2PHY_PLL_STATUS]); 7608c2ecf20Sopenharmony_ci if (!(val & cfg->mask_core_ready)) { 7618c2ecf20Sopenharmony_ci dev_err(&phy->dev, 7628c2ecf20Sopenharmony_ci "QUSB2PHY pll lock failed: status reg = %x\n", val); 7638c2ecf20Sopenharmony_ci ret = -EBUSY; 7648c2ecf20Sopenharmony_ci goto disable_ref_clk; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci qphy->phy_initialized = true; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci return 0; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cidisable_ref_clk: 7718c2ecf20Sopenharmony_ci if (!qphy->has_se_clk_scheme) 7728c2ecf20Sopenharmony_ci clk_disable_unprepare(qphy->ref_clk); 7738c2ecf20Sopenharmony_ciassert_phy_reset: 7748c2ecf20Sopenharmony_ci reset_control_assert(qphy->phy_reset); 7758c2ecf20Sopenharmony_cidisable_ahb_clk: 7768c2ecf20Sopenharmony_ci clk_disable_unprepare(qphy->cfg_ahb_clk); 7778c2ecf20Sopenharmony_cidisable_iface_clk: 7788c2ecf20Sopenharmony_ci clk_disable_unprepare(qphy->iface_clk); 7798c2ecf20Sopenharmony_cipoweroff_phy: 7808c2ecf20Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci return ret; 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic int qusb2_phy_exit(struct phy *phy) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci struct qusb2_phy *qphy = phy_get_drvdata(phy); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci /* Disable the PHY */ 7908c2ecf20Sopenharmony_ci qusb2_setbits(qphy->base, qphy->cfg->regs[QUSB2PHY_PORT_POWERDOWN], 7918c2ecf20Sopenharmony_ci qphy->cfg->disable_ctrl); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci if (!qphy->has_se_clk_scheme) 7948c2ecf20Sopenharmony_ci clk_disable_unprepare(qphy->ref_clk); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci reset_control_assert(qphy->phy_reset); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci clk_disable_unprepare(qphy->cfg_ahb_clk); 7998c2ecf20Sopenharmony_ci clk_disable_unprepare(qphy->iface_clk); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci qphy->phy_initialized = false; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci return 0; 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_cistatic const struct phy_ops qusb2_phy_gen_ops = { 8098c2ecf20Sopenharmony_ci .init = qusb2_phy_init, 8108c2ecf20Sopenharmony_ci .exit = qusb2_phy_exit, 8118c2ecf20Sopenharmony_ci .set_mode = qusb2_phy_set_mode, 8128c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 8138c2ecf20Sopenharmony_ci}; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic const struct of_device_id qusb2_phy_of_match_table[] = { 8168c2ecf20Sopenharmony_ci { 8178c2ecf20Sopenharmony_ci .compatible = "qcom,ipq8074-qusb2-phy", 8188c2ecf20Sopenharmony_ci .data = &msm8996_phy_cfg, 8198c2ecf20Sopenharmony_ci }, { 8208c2ecf20Sopenharmony_ci .compatible = "qcom,msm8996-qusb2-phy", 8218c2ecf20Sopenharmony_ci .data = &msm8996_phy_cfg, 8228c2ecf20Sopenharmony_ci }, { 8238c2ecf20Sopenharmony_ci .compatible = "qcom,msm8998-qusb2-phy", 8248c2ecf20Sopenharmony_ci .data = &msm8998_phy_cfg, 8258c2ecf20Sopenharmony_ci }, { 8268c2ecf20Sopenharmony_ci /* 8278c2ecf20Sopenharmony_ci * Deprecated. Only here to support legacy device 8288c2ecf20Sopenharmony_ci * trees that didn't include "qcom,qusb2-v2-phy" 8298c2ecf20Sopenharmony_ci */ 8308c2ecf20Sopenharmony_ci .compatible = "qcom,sdm845-qusb2-phy", 8318c2ecf20Sopenharmony_ci .data = &qusb2_v2_phy_cfg, 8328c2ecf20Sopenharmony_ci }, { 8338c2ecf20Sopenharmony_ci .compatible = "qcom,qusb2-v2-phy", 8348c2ecf20Sopenharmony_ci .data = &qusb2_v2_phy_cfg, 8358c2ecf20Sopenharmony_ci }, 8368c2ecf20Sopenharmony_ci { }, 8378c2ecf20Sopenharmony_ci}; 8388c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic const struct dev_pm_ops qusb2_phy_pm_ops = { 8418c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(qusb2_phy_runtime_suspend, 8428c2ecf20Sopenharmony_ci qusb2_phy_runtime_resume, NULL) 8438c2ecf20Sopenharmony_ci}; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_cistatic int qusb2_phy_probe(struct platform_device *pdev) 8468c2ecf20Sopenharmony_ci{ 8478c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 8488c2ecf20Sopenharmony_ci struct qusb2_phy *qphy; 8498c2ecf20Sopenharmony_ci struct phy_provider *phy_provider; 8508c2ecf20Sopenharmony_ci struct phy *generic_phy; 8518c2ecf20Sopenharmony_ci struct resource *res; 8528c2ecf20Sopenharmony_ci int ret, i; 8538c2ecf20Sopenharmony_ci int num; 8548c2ecf20Sopenharmony_ci u32 value; 8558c2ecf20Sopenharmony_ci struct override_params *or; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); 8588c2ecf20Sopenharmony_ci if (!qphy) 8598c2ecf20Sopenharmony_ci return -ENOMEM; 8608c2ecf20Sopenharmony_ci or = &qphy->overrides; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 8638c2ecf20Sopenharmony_ci qphy->base = devm_ioremap_resource(dev, res); 8648c2ecf20Sopenharmony_ci if (IS_ERR(qphy->base)) 8658c2ecf20Sopenharmony_ci return PTR_ERR(qphy->base); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb"); 8688c2ecf20Sopenharmony_ci if (IS_ERR(qphy->cfg_ahb_clk)) { 8698c2ecf20Sopenharmony_ci ret = PTR_ERR(qphy->cfg_ahb_clk); 8708c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 8718c2ecf20Sopenharmony_ci dev_err(dev, "failed to get cfg ahb clk, %d\n", ret); 8728c2ecf20Sopenharmony_ci return ret; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci qphy->ref_clk = devm_clk_get(dev, "ref"); 8768c2ecf20Sopenharmony_ci if (IS_ERR(qphy->ref_clk)) { 8778c2ecf20Sopenharmony_ci ret = PTR_ERR(qphy->ref_clk); 8788c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 8798c2ecf20Sopenharmony_ci dev_err(dev, "failed to get ref clk, %d\n", ret); 8808c2ecf20Sopenharmony_ci return ret; 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci qphy->iface_clk = devm_clk_get_optional(dev, "iface"); 8848c2ecf20Sopenharmony_ci if (IS_ERR(qphy->iface_clk)) 8858c2ecf20Sopenharmony_ci return PTR_ERR(qphy->iface_clk); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci qphy->phy_reset = devm_reset_control_get_by_index(&pdev->dev, 0); 8888c2ecf20Sopenharmony_ci if (IS_ERR(qphy->phy_reset)) { 8898c2ecf20Sopenharmony_ci dev_err(dev, "failed to get phy core reset\n"); 8908c2ecf20Sopenharmony_ci return PTR_ERR(qphy->phy_reset); 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci num = ARRAY_SIZE(qphy->vregs); 8948c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) 8958c2ecf20Sopenharmony_ci qphy->vregs[i].supply = qusb2_phy_vreg_names[i]; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci ret = devm_regulator_bulk_get(dev, num, qphy->vregs); 8988c2ecf20Sopenharmony_ci if (ret) { 8998c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 9008c2ecf20Sopenharmony_ci dev_err(dev, "failed to get regulator supplies: %d\n", 9018c2ecf20Sopenharmony_ci ret); 9028c2ecf20Sopenharmony_ci return ret; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci /* Get the specific init parameters of QMP phy */ 9068c2ecf20Sopenharmony_ci qphy->cfg = of_device_get_match_data(dev); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci qphy->tcsr = syscon_regmap_lookup_by_phandle(dev->of_node, 9098c2ecf20Sopenharmony_ci "qcom,tcsr-syscon"); 9108c2ecf20Sopenharmony_ci if (IS_ERR(qphy->tcsr)) { 9118c2ecf20Sopenharmony_ci dev_dbg(dev, "failed to lookup TCSR regmap\n"); 9128c2ecf20Sopenharmony_ci qphy->tcsr = NULL; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci qphy->cell = devm_nvmem_cell_get(dev, NULL); 9168c2ecf20Sopenharmony_ci if (IS_ERR(qphy->cell)) { 9178c2ecf20Sopenharmony_ci if (PTR_ERR(qphy->cell) == -EPROBE_DEFER) 9188c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 9198c2ecf20Sopenharmony_ci qphy->cell = NULL; 9208c2ecf20Sopenharmony_ci dev_dbg(dev, "failed to lookup tune2 hstx trim value\n"); 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "qcom,imp-res-offset-value", 9248c2ecf20Sopenharmony_ci &value)) { 9258c2ecf20Sopenharmony_ci or->imp_res_offset.value = (u8)value; 9268c2ecf20Sopenharmony_ci or->imp_res_offset.override = true; 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "qcom,bias-ctrl-value", 9308c2ecf20Sopenharmony_ci &value)) { 9318c2ecf20Sopenharmony_ci or->bias_ctrl.value = (u8)value; 9328c2ecf20Sopenharmony_ci or->bias_ctrl.override = true; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "qcom,charge-ctrl-value", 9368c2ecf20Sopenharmony_ci &value)) { 9378c2ecf20Sopenharmony_ci or->charge_ctrl.value = (u8)value; 9388c2ecf20Sopenharmony_ci or->charge_ctrl.override = true; 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "qcom,hstx-trim-value", 9428c2ecf20Sopenharmony_ci &value)) { 9438c2ecf20Sopenharmony_ci or->hstx_trim.value = (u8)value; 9448c2ecf20Sopenharmony_ci or->hstx_trim.override = true; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "qcom,preemphasis-level", 9488c2ecf20Sopenharmony_ci &value)) { 9498c2ecf20Sopenharmony_ci or->preemphasis.value = (u8)value; 9508c2ecf20Sopenharmony_ci or->preemphasis.override = true; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "qcom,preemphasis-width", 9548c2ecf20Sopenharmony_ci &value)) { 9558c2ecf20Sopenharmony_ci or->preemphasis_width.value = (u8)value; 9568c2ecf20Sopenharmony_ci or->preemphasis_width.override = true; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "qcom,hsdisc-trim-value", 9608c2ecf20Sopenharmony_ci &value)) { 9618c2ecf20Sopenharmony_ci or->hsdisc_trim.value = (u8)value; 9628c2ecf20Sopenharmony_ci or->hsdisc_trim.override = true; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci pm_runtime_set_active(dev); 9668c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 9678c2ecf20Sopenharmony_ci /* 9688c2ecf20Sopenharmony_ci * Prevent runtime pm from being ON by default. Users can enable 9698c2ecf20Sopenharmony_ci * it using power/control in sysfs. 9708c2ecf20Sopenharmony_ci */ 9718c2ecf20Sopenharmony_ci pm_runtime_forbid(dev); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops); 9748c2ecf20Sopenharmony_ci if (IS_ERR(generic_phy)) { 9758c2ecf20Sopenharmony_ci ret = PTR_ERR(generic_phy); 9768c2ecf20Sopenharmony_ci dev_err(dev, "failed to create phy, %d\n", ret); 9778c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 9788c2ecf20Sopenharmony_ci return ret; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci qphy->phy = generic_phy; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci dev_set_drvdata(dev, qphy); 9838c2ecf20Sopenharmony_ci phy_set_drvdata(generic_phy, qphy); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 9868c2ecf20Sopenharmony_ci if (!IS_ERR(phy_provider)) 9878c2ecf20Sopenharmony_ci dev_info(dev, "Registered Qcom-QUSB2 phy\n"); 9888c2ecf20Sopenharmony_ci else 9898c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic struct platform_driver qusb2_phy_driver = { 9958c2ecf20Sopenharmony_ci .probe = qusb2_phy_probe, 9968c2ecf20Sopenharmony_ci .driver = { 9978c2ecf20Sopenharmony_ci .name = "qcom-qusb2-phy", 9988c2ecf20Sopenharmony_ci .pm = &qusb2_phy_pm_ops, 9998c2ecf20Sopenharmony_ci .of_match_table = qusb2_phy_of_match_table, 10008c2ecf20Sopenharmony_ci }, 10018c2ecf20Sopenharmony_ci}; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cimodule_platform_driver(qusb2_phy_driver); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>"); 10068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm QUSB2 PHY driver"); 10078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1008