18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Rockchip emmc PHY driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2016 ROCKCHIP, Inc. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/of_address.h> 158c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/regmap.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * The higher 16-bit of this register is used for write protection 218c2ecf20Sopenharmony_ci * only if BIT(x + 16) set to 1 the BIT(x) can be written. 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci#define HIWORD_UPDATE(val, mask, shift) \ 248c2ecf20Sopenharmony_ci ((val) << (shift) | (mask) << ((shift) + 16)) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Register definition */ 278c2ecf20Sopenharmony_ci#define GRF_EMMCPHY_CON0 0x0 288c2ecf20Sopenharmony_ci#define GRF_EMMCPHY_CON1 0x4 298c2ecf20Sopenharmony_ci#define GRF_EMMCPHY_CON2 0x8 308c2ecf20Sopenharmony_ci#define GRF_EMMCPHY_CON3 0xc 318c2ecf20Sopenharmony_ci#define GRF_EMMCPHY_CON4 0x10 328c2ecf20Sopenharmony_ci#define GRF_EMMCPHY_CON5 0x14 338c2ecf20Sopenharmony_ci#define GRF_EMMCPHY_CON6 0x18 348c2ecf20Sopenharmony_ci#define GRF_EMMCPHY_STATUS 0x20 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define PHYCTRL_PDB_MASK 0x1 378c2ecf20Sopenharmony_ci#define PHYCTRL_PDB_SHIFT 0x0 388c2ecf20Sopenharmony_ci#define PHYCTRL_PDB_PWR_ON 0x1 398c2ecf20Sopenharmony_ci#define PHYCTRL_PDB_PWR_OFF 0x0 408c2ecf20Sopenharmony_ci#define PHYCTRL_ENDLL_MASK 0x1 418c2ecf20Sopenharmony_ci#define PHYCTRL_ENDLL_SHIFT 0x1 428c2ecf20Sopenharmony_ci#define PHYCTRL_ENDLL_ENABLE 0x1 438c2ecf20Sopenharmony_ci#define PHYCTRL_ENDLL_DISABLE 0x0 448c2ecf20Sopenharmony_ci#define PHYCTRL_CALDONE_MASK 0x1 458c2ecf20Sopenharmony_ci#define PHYCTRL_CALDONE_SHIFT 0x6 468c2ecf20Sopenharmony_ci#define PHYCTRL_CALDONE_DONE 0x1 478c2ecf20Sopenharmony_ci#define PHYCTRL_CALDONE_GOING 0x0 488c2ecf20Sopenharmony_ci#define PHYCTRL_DLLRDY_MASK 0x1 498c2ecf20Sopenharmony_ci#define PHYCTRL_DLLRDY_SHIFT 0x5 508c2ecf20Sopenharmony_ci#define PHYCTRL_DLLRDY_DONE 0x1 518c2ecf20Sopenharmony_ci#define PHYCTRL_DLLRDY_GOING 0x0 528c2ecf20Sopenharmony_ci#define PHYCTRL_FREQSEL_200M 0x0 538c2ecf20Sopenharmony_ci#define PHYCTRL_FREQSEL_50M 0x1 548c2ecf20Sopenharmony_ci#define PHYCTRL_FREQSEL_100M 0x2 558c2ecf20Sopenharmony_ci#define PHYCTRL_FREQSEL_150M 0x3 568c2ecf20Sopenharmony_ci#define PHYCTRL_FREQSEL_MASK 0x3 578c2ecf20Sopenharmony_ci#define PHYCTRL_FREQSEL_SHIFT 0xc 588c2ecf20Sopenharmony_ci#define PHYCTRL_DR_MASK 0x7 598c2ecf20Sopenharmony_ci#define PHYCTRL_DR_SHIFT 0x4 608c2ecf20Sopenharmony_ci#define PHYCTRL_DR_50OHM 0x0 618c2ecf20Sopenharmony_ci#define PHYCTRL_DR_33OHM 0x1 628c2ecf20Sopenharmony_ci#define PHYCTRL_DR_66OHM 0x2 638c2ecf20Sopenharmony_ci#define PHYCTRL_DR_100OHM 0x3 648c2ecf20Sopenharmony_ci#define PHYCTRL_DR_40OHM 0x4 658c2ecf20Sopenharmony_ci#define PHYCTRL_OTAPDLYENA 0x1 668c2ecf20Sopenharmony_ci#define PHYCTRL_OTAPDLYENA_MASK 0x1 678c2ecf20Sopenharmony_ci#define PHYCTRL_OTAPDLYENA_SHIFT 0xb 688c2ecf20Sopenharmony_ci#define PHYCTRL_OTAPDLYSEL_MASK 0xf 698c2ecf20Sopenharmony_ci#define PHYCTRL_OTAPDLYSEL_SHIFT 0x7 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define PHYCTRL_IS_CALDONE(x) \ 728c2ecf20Sopenharmony_ci ((((x) >> PHYCTRL_CALDONE_SHIFT) & \ 738c2ecf20Sopenharmony_ci PHYCTRL_CALDONE_MASK) == PHYCTRL_CALDONE_DONE) 748c2ecf20Sopenharmony_ci#define PHYCTRL_IS_DLLRDY(x) \ 758c2ecf20Sopenharmony_ci ((((x) >> PHYCTRL_DLLRDY_SHIFT) & \ 768c2ecf20Sopenharmony_ci PHYCTRL_DLLRDY_MASK) == PHYCTRL_DLLRDY_DONE) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistruct rockchip_emmc_phy { 798c2ecf20Sopenharmony_ci unsigned int reg_offset; 808c2ecf20Sopenharmony_ci struct regmap *reg_base; 818c2ecf20Sopenharmony_ci struct clk *emmcclk; 828c2ecf20Sopenharmony_ci unsigned int drive_impedance; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int rockchip_emmc_phy_power(struct phy *phy, bool on_off) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); 888c2ecf20Sopenharmony_ci unsigned int caldone; 898c2ecf20Sopenharmony_ci unsigned int dllrdy; 908c2ecf20Sopenharmony_ci unsigned int freqsel = PHYCTRL_FREQSEL_200M; 918c2ecf20Sopenharmony_ci unsigned long rate; 928c2ecf20Sopenharmony_ci int ret; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* 958c2ecf20Sopenharmony_ci * Keep phyctrl_pdb and phyctrl_endll low to allow 968c2ecf20Sopenharmony_ci * initialization of CALIO state M/C DFFs 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci regmap_write(rk_phy->reg_base, 998c2ecf20Sopenharmony_ci rk_phy->reg_offset + GRF_EMMCPHY_CON6, 1008c2ecf20Sopenharmony_ci HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF, 1018c2ecf20Sopenharmony_ci PHYCTRL_PDB_MASK, 1028c2ecf20Sopenharmony_ci PHYCTRL_PDB_SHIFT)); 1038c2ecf20Sopenharmony_ci regmap_write(rk_phy->reg_base, 1048c2ecf20Sopenharmony_ci rk_phy->reg_offset + GRF_EMMCPHY_CON6, 1058c2ecf20Sopenharmony_ci HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE, 1068c2ecf20Sopenharmony_ci PHYCTRL_ENDLL_MASK, 1078c2ecf20Sopenharmony_ci PHYCTRL_ENDLL_SHIFT)); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Already finish power_off above */ 1108c2ecf20Sopenharmony_ci if (on_off == PHYCTRL_PDB_PWR_OFF) 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci rate = clk_get_rate(rk_phy->emmcclk); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (rate != 0) { 1168c2ecf20Sopenharmony_ci unsigned long ideal_rate; 1178c2ecf20Sopenharmony_ci unsigned long diff; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci switch (rate) { 1208c2ecf20Sopenharmony_ci case 1 ... 74999999: 1218c2ecf20Sopenharmony_ci ideal_rate = 50000000; 1228c2ecf20Sopenharmony_ci freqsel = PHYCTRL_FREQSEL_50M; 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci case 75000000 ... 124999999: 1258c2ecf20Sopenharmony_ci ideal_rate = 100000000; 1268c2ecf20Sopenharmony_ci freqsel = PHYCTRL_FREQSEL_100M; 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci case 125000000 ... 174999999: 1298c2ecf20Sopenharmony_ci ideal_rate = 150000000; 1308c2ecf20Sopenharmony_ci freqsel = PHYCTRL_FREQSEL_150M; 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci default: 1338c2ecf20Sopenharmony_ci ideal_rate = 200000000; 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci diff = (rate > ideal_rate) ? 1388c2ecf20Sopenharmony_ci rate - ideal_rate : ideal_rate - rate; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* 1418c2ecf20Sopenharmony_ci * In order for tuning delays to be accurate we need to be 1428c2ecf20Sopenharmony_ci * pretty spot on for the DLL range, so warn if we're too 1438c2ecf20Sopenharmony_ci * far off. Also warn if we're above the 200 MHz max. Don't 1448c2ecf20Sopenharmony_ci * warn for really slow rates since we won't be tuning then. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_ci if ((rate > 50000000 && diff > 15000000) || (rate > 200000000)) 1478c2ecf20Sopenharmony_ci dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* 1518c2ecf20Sopenharmony_ci * According to the user manual, calpad calibration 1528c2ecf20Sopenharmony_ci * cycle takes more than 2us without the minimal recommended 1538c2ecf20Sopenharmony_ci * value, so we may need a little margin here 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci udelay(3); 1568c2ecf20Sopenharmony_ci regmap_write(rk_phy->reg_base, 1578c2ecf20Sopenharmony_ci rk_phy->reg_offset + GRF_EMMCPHY_CON6, 1588c2ecf20Sopenharmony_ci HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON, 1598c2ecf20Sopenharmony_ci PHYCTRL_PDB_MASK, 1608c2ecf20Sopenharmony_ci PHYCTRL_PDB_SHIFT)); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* 1638c2ecf20Sopenharmony_ci * According to the user manual, it asks driver to wait 5us for 1648c2ecf20Sopenharmony_ci * calpad busy trimming. However it is documented that this value is 1658c2ecf20Sopenharmony_ci * PVT(A.K.A process,voltage and temperature) relevant, so some 1668c2ecf20Sopenharmony_ci * failure cases are found which indicates we should be more tolerant 1678c2ecf20Sopenharmony_ci * to calpad busy trimming. 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_ci ret = regmap_read_poll_timeout(rk_phy->reg_base, 1708c2ecf20Sopenharmony_ci rk_phy->reg_offset + GRF_EMMCPHY_STATUS, 1718c2ecf20Sopenharmony_ci caldone, PHYCTRL_IS_CALDONE(caldone), 1728c2ecf20Sopenharmony_ci 0, 50); 1738c2ecf20Sopenharmony_ci if (ret) { 1748c2ecf20Sopenharmony_ci pr_err("%s: caldone failed, ret=%d\n", __func__, ret); 1758c2ecf20Sopenharmony_ci return ret; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Set the frequency of the DLL operation */ 1798c2ecf20Sopenharmony_ci regmap_write(rk_phy->reg_base, 1808c2ecf20Sopenharmony_ci rk_phy->reg_offset + GRF_EMMCPHY_CON0, 1818c2ecf20Sopenharmony_ci HIWORD_UPDATE(freqsel, PHYCTRL_FREQSEL_MASK, 1828c2ecf20Sopenharmony_ci PHYCTRL_FREQSEL_SHIFT)); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Turn on the DLL */ 1858c2ecf20Sopenharmony_ci regmap_write(rk_phy->reg_base, 1868c2ecf20Sopenharmony_ci rk_phy->reg_offset + GRF_EMMCPHY_CON6, 1878c2ecf20Sopenharmony_ci HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE, 1888c2ecf20Sopenharmony_ci PHYCTRL_ENDLL_MASK, 1898c2ecf20Sopenharmony_ci PHYCTRL_ENDLL_SHIFT)); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* 1928c2ecf20Sopenharmony_ci * We turned on the DLL even though the rate was 0 because we the 1938c2ecf20Sopenharmony_ci * clock might be turned on later. ...but we can't wait for the DLL 1948c2ecf20Sopenharmony_ci * to lock when the rate is 0 because it will never lock with no 1958c2ecf20Sopenharmony_ci * input clock. 1968c2ecf20Sopenharmony_ci * 1978c2ecf20Sopenharmony_ci * Technically we should be checking the lock later when the clock 1988c2ecf20Sopenharmony_ci * is turned on, but for now we won't. 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ci if (rate == 0) 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * After enabling analog DLL circuits docs say that we need 10.2 us if 2058c2ecf20Sopenharmony_ci * our source clock is at 50 MHz and that lock time scales linearly 2068c2ecf20Sopenharmony_ci * with clock speed. If we are powering on the PHY and the card clock 2078c2ecf20Sopenharmony_ci * is super slow (like 100 kHZ) this could take as long as 5.1 ms as 2088c2ecf20Sopenharmony_ci * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms 2098c2ecf20Sopenharmony_ci * Hopefully we won't be running at 100 kHz, but we should still make 2108c2ecf20Sopenharmony_ci * sure we wait long enough. 2118c2ecf20Sopenharmony_ci * 2128c2ecf20Sopenharmony_ci * NOTE: There appear to be corner cases where the DLL seems to take 2138c2ecf20Sopenharmony_ci * extra long to lock for reasons that aren't understood. In some 2148c2ecf20Sopenharmony_ci * extreme cases we've seen it take up to over 10ms (!). We'll be 2158c2ecf20Sopenharmony_ci * generous and give it 50ms. 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_ci ret = regmap_read_poll_timeout(rk_phy->reg_base, 2188c2ecf20Sopenharmony_ci rk_phy->reg_offset + GRF_EMMCPHY_STATUS, 2198c2ecf20Sopenharmony_ci dllrdy, PHYCTRL_IS_DLLRDY(dllrdy), 2208c2ecf20Sopenharmony_ci 0, 50 * USEC_PER_MSEC); 2218c2ecf20Sopenharmony_ci if (ret) { 2228c2ecf20Sopenharmony_ci pr_err("%s: dllrdy failed. ret=%d\n", __func__, ret); 2238c2ecf20Sopenharmony_ci return ret; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int rockchip_emmc_phy_init(struct phy *phy) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); 2328c2ecf20Sopenharmony_ci int ret = 0; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* 2358c2ecf20Sopenharmony_ci * We purposely get the clock here and not in probe to avoid the 2368c2ecf20Sopenharmony_ci * circular dependency problem. We expect: 2378c2ecf20Sopenharmony_ci * - PHY driver to probe 2388c2ecf20Sopenharmony_ci * - SDHCI driver to start probe 2398c2ecf20Sopenharmony_ci * - SDHCI driver to register it's clock 2408c2ecf20Sopenharmony_ci * - SDHCI driver to get the PHY 2418c2ecf20Sopenharmony_ci * - SDHCI driver to init the PHY 2428c2ecf20Sopenharmony_ci * 2438c2ecf20Sopenharmony_ci * The clock is optional, using clk_get_optional() to get the clock 2448c2ecf20Sopenharmony_ci * and do error processing if the return value != NULL 2458c2ecf20Sopenharmony_ci * 2468c2ecf20Sopenharmony_ci * NOTE: we don't do anything special for EPROBE_DEFER here. Given the 2478c2ecf20Sopenharmony_ci * above expected use case, EPROBE_DEFER isn't sensible to expect, so 2488c2ecf20Sopenharmony_ci * it's just like any other error. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ci rk_phy->emmcclk = clk_get_optional(&phy->dev, "emmcclk"); 2518c2ecf20Sopenharmony_ci if (IS_ERR(rk_phy->emmcclk)) { 2528c2ecf20Sopenharmony_ci ret = PTR_ERR(rk_phy->emmcclk); 2538c2ecf20Sopenharmony_ci dev_err(&phy->dev, "Error getting emmcclk: %d\n", ret); 2548c2ecf20Sopenharmony_ci rk_phy->emmcclk = NULL; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return ret; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic int rockchip_emmc_phy_exit(struct phy *phy) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci clk_put(rk_phy->emmcclk); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int rockchip_emmc_phy_power_off(struct phy *phy) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci /* Power down emmc phy analog blocks */ 2728c2ecf20Sopenharmony_ci return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_OFF); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int rockchip_emmc_phy_power_on(struct phy *phy) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* Drive impedance: from DTS */ 2808c2ecf20Sopenharmony_ci regmap_write(rk_phy->reg_base, 2818c2ecf20Sopenharmony_ci rk_phy->reg_offset + GRF_EMMCPHY_CON6, 2828c2ecf20Sopenharmony_ci HIWORD_UPDATE(rk_phy->drive_impedance, 2838c2ecf20Sopenharmony_ci PHYCTRL_DR_MASK, 2848c2ecf20Sopenharmony_ci PHYCTRL_DR_SHIFT)); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Output tap delay: enable */ 2878c2ecf20Sopenharmony_ci regmap_write(rk_phy->reg_base, 2888c2ecf20Sopenharmony_ci rk_phy->reg_offset + GRF_EMMCPHY_CON0, 2898c2ecf20Sopenharmony_ci HIWORD_UPDATE(PHYCTRL_OTAPDLYENA, 2908c2ecf20Sopenharmony_ci PHYCTRL_OTAPDLYENA_MASK, 2918c2ecf20Sopenharmony_ci PHYCTRL_OTAPDLYENA_SHIFT)); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* Output tap delay */ 2948c2ecf20Sopenharmony_ci regmap_write(rk_phy->reg_base, 2958c2ecf20Sopenharmony_ci rk_phy->reg_offset + GRF_EMMCPHY_CON0, 2968c2ecf20Sopenharmony_ci HIWORD_UPDATE(4, 2978c2ecf20Sopenharmony_ci PHYCTRL_OTAPDLYSEL_MASK, 2988c2ecf20Sopenharmony_ci PHYCTRL_OTAPDLYSEL_SHIFT)); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* Power up emmc phy analog blocks */ 3018c2ecf20Sopenharmony_ci return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON); 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic const struct phy_ops ops = { 3058c2ecf20Sopenharmony_ci .init = rockchip_emmc_phy_init, 3068c2ecf20Sopenharmony_ci .exit = rockchip_emmc_phy_exit, 3078c2ecf20Sopenharmony_ci .power_on = rockchip_emmc_phy_power_on, 3088c2ecf20Sopenharmony_ci .power_off = rockchip_emmc_phy_power_off, 3098c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3108c2ecf20Sopenharmony_ci}; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic u32 convert_drive_impedance_ohm(struct platform_device *pdev, u32 dr_ohm) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci switch (dr_ohm) { 3158c2ecf20Sopenharmony_ci case 100: 3168c2ecf20Sopenharmony_ci return PHYCTRL_DR_100OHM; 3178c2ecf20Sopenharmony_ci case 66: 3188c2ecf20Sopenharmony_ci return PHYCTRL_DR_66OHM; 3198c2ecf20Sopenharmony_ci case 50: 3208c2ecf20Sopenharmony_ci return PHYCTRL_DR_50OHM; 3218c2ecf20Sopenharmony_ci case 40: 3228c2ecf20Sopenharmony_ci return PHYCTRL_DR_40OHM; 3238c2ecf20Sopenharmony_ci case 33: 3248c2ecf20Sopenharmony_ci return PHYCTRL_DR_33OHM; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Invalid value %u for drive-impedance-ohm.\n", 3288c2ecf20Sopenharmony_ci dr_ohm); 3298c2ecf20Sopenharmony_ci return PHYCTRL_DR_50OHM; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int rockchip_emmc_phy_probe(struct platform_device *pdev) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3358c2ecf20Sopenharmony_ci struct rockchip_emmc_phy *rk_phy; 3368c2ecf20Sopenharmony_ci struct phy *generic_phy; 3378c2ecf20Sopenharmony_ci struct phy_provider *phy_provider; 3388c2ecf20Sopenharmony_ci struct regmap *grf; 3398c2ecf20Sopenharmony_ci unsigned int reg_offset; 3408c2ecf20Sopenharmony_ci u32 val; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (!dev->parent || !dev->parent->of_node) 3438c2ecf20Sopenharmony_ci return -ENODEV; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci grf = syscon_node_to_regmap(dev->parent->of_node); 3468c2ecf20Sopenharmony_ci if (IS_ERR(grf)) { 3478c2ecf20Sopenharmony_ci dev_err(dev, "Missing rockchip,grf property\n"); 3488c2ecf20Sopenharmony_ci return PTR_ERR(grf); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL); 3528c2ecf20Sopenharmony_ci if (!rk_phy) 3538c2ecf20Sopenharmony_ci return -ENOMEM; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (of_property_read_u32(dev->of_node, "reg", ®_offset)) { 3568c2ecf20Sopenharmony_ci dev_err(dev, "missing reg property in node %pOFn\n", 3578c2ecf20Sopenharmony_ci dev->of_node); 3588c2ecf20Sopenharmony_ci return -EINVAL; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci rk_phy->reg_offset = reg_offset; 3628c2ecf20Sopenharmony_ci rk_phy->reg_base = grf; 3638c2ecf20Sopenharmony_ci rk_phy->drive_impedance = PHYCTRL_DR_50OHM; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "drive-impedance-ohm", &val)) 3668c2ecf20Sopenharmony_ci rk_phy->drive_impedance = convert_drive_impedance_ohm(pdev, val); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci generic_phy = devm_phy_create(dev, dev->of_node, &ops); 3698c2ecf20Sopenharmony_ci if (IS_ERR(generic_phy)) { 3708c2ecf20Sopenharmony_ci dev_err(dev, "failed to create PHY\n"); 3718c2ecf20Sopenharmony_ci return PTR_ERR(generic_phy); 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci phy_set_drvdata(generic_phy, rk_phy); 3758c2ecf20Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic const struct of_device_id rockchip_emmc_phy_dt_ids[] = { 3818c2ecf20Sopenharmony_ci { .compatible = "rockchip,rk3399-emmc-phy" }, 3828c2ecf20Sopenharmony_ci {} 3838c2ecf20Sopenharmony_ci}; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic struct platform_driver rockchip_emmc_driver = { 3888c2ecf20Sopenharmony_ci .probe = rockchip_emmc_phy_probe, 3898c2ecf20Sopenharmony_ci .driver = { 3908c2ecf20Sopenharmony_ci .name = "rockchip-emmc-phy", 3918c2ecf20Sopenharmony_ci .of_match_table = rockchip_emmc_phy_dt_ids, 3928c2ecf20Sopenharmony_ci }, 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cimodule_platform_driver(rockchip_emmc_driver); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ciMODULE_AUTHOR("Shawn Lin <shawn.lin@rock-chips.com>"); 3988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Rockchip EMMC PHY driver"); 3998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 400