18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * phy-zynqmp.c - PHY driver for Xilinx ZynqMP GT. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2018-2020 Xilinx Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Anurag Kumar Vulisha <anuragku@xilinx.com> 88c2ecf20Sopenharmony_ci * Author: Subbaraya Sundeep <sundeep.lkml@gmail.com> 98c2ecf20Sopenharmony_ci * Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This driver is tested for USB, SATA and Display Port currently. 128c2ecf20Sopenharmony_ci * Other controllers PCIe and SGMII should also work but that is 138c2ecf20Sopenharmony_ci * experimental as of now. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/clk.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/of.h> 228c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <dt-bindings/phy/phy.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * Lane Registers 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* TX De-emphasis parameters */ 338c2ecf20Sopenharmony_ci#define L0_TX_ANA_TM_18 0x0048 348c2ecf20Sopenharmony_ci#define L0_TX_ANA_TM_118 0x01d8 358c2ecf20Sopenharmony_ci#define L0_TX_ANA_TM_118_FORCE_17_0 BIT(0) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* DN Resistor calibration code parameters */ 388c2ecf20Sopenharmony_ci#define L0_TXPMA_ST_3 0x0b0c 398c2ecf20Sopenharmony_ci#define L0_DN_CALIB_CODE 0x3f 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* PMA control parameters */ 428c2ecf20Sopenharmony_ci#define L0_TXPMD_TM_45 0x0cb4 438c2ecf20Sopenharmony_ci#define L0_TXPMD_TM_48 0x0cc0 448c2ecf20Sopenharmony_ci#define L0_TXPMD_TM_45_OVER_DP_MAIN BIT(0) 458c2ecf20Sopenharmony_ci#define L0_TXPMD_TM_45_ENABLE_DP_MAIN BIT(1) 468c2ecf20Sopenharmony_ci#define L0_TXPMD_TM_45_OVER_DP_POST1 BIT(2) 478c2ecf20Sopenharmony_ci#define L0_TXPMD_TM_45_ENABLE_DP_POST1 BIT(3) 488c2ecf20Sopenharmony_ci#define L0_TXPMD_TM_45_OVER_DP_POST2 BIT(4) 498c2ecf20Sopenharmony_ci#define L0_TXPMD_TM_45_ENABLE_DP_POST2 BIT(5) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* PCS control parameters */ 528c2ecf20Sopenharmony_ci#define L0_TM_DIG_6 0x106c 538c2ecf20Sopenharmony_ci#define L0_TM_DIS_DESCRAMBLE_DECODER 0x0f 548c2ecf20Sopenharmony_ci#define L0_TX_DIG_61 0x00f4 558c2ecf20Sopenharmony_ci#define L0_TM_DISABLE_SCRAMBLE_ENCODER 0x0f 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* PLL Test Mode register parameters */ 588c2ecf20Sopenharmony_ci#define L0_TM_PLL_DIG_37 0x2094 598c2ecf20Sopenharmony_ci#define L0_TM_COARSE_CODE_LIMIT 0x10 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* PLL SSC step size offsets */ 628c2ecf20Sopenharmony_ci#define L0_PLL_SS_STEPS_0_LSB 0x2368 638c2ecf20Sopenharmony_ci#define L0_PLL_SS_STEPS_1_MSB 0x236c 648c2ecf20Sopenharmony_ci#define L0_PLL_SS_STEP_SIZE_0_LSB 0x2370 658c2ecf20Sopenharmony_ci#define L0_PLL_SS_STEP_SIZE_1 0x2374 668c2ecf20Sopenharmony_ci#define L0_PLL_SS_STEP_SIZE_2 0x2378 678c2ecf20Sopenharmony_ci#define L0_PLL_SS_STEP_SIZE_3_MSB 0x237c 688c2ecf20Sopenharmony_ci#define L0_PLL_STATUS_READ_1 0x23e4 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* SSC step size parameters */ 718c2ecf20Sopenharmony_ci#define STEP_SIZE_0_MASK 0xff 728c2ecf20Sopenharmony_ci#define STEP_SIZE_1_MASK 0xff 738c2ecf20Sopenharmony_ci#define STEP_SIZE_2_MASK 0xff 748c2ecf20Sopenharmony_ci#define STEP_SIZE_3_MASK 0x3 758c2ecf20Sopenharmony_ci#define STEP_SIZE_SHIFT 8 768c2ecf20Sopenharmony_ci#define FORCE_STEP_SIZE 0x10 778c2ecf20Sopenharmony_ci#define FORCE_STEPS 0x20 788c2ecf20Sopenharmony_ci#define STEPS_0_MASK 0xff 798c2ecf20Sopenharmony_ci#define STEPS_1_MASK 0x07 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* Reference clock selection parameters */ 828c2ecf20Sopenharmony_ci#define L0_Ln_REF_CLK_SEL(n) (0x2860 + (n) * 4) 838c2ecf20Sopenharmony_ci#define L0_REF_CLK_SEL_MASK 0x8f 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* Calibration digital logic parameters */ 868c2ecf20Sopenharmony_ci#define L3_TM_CALIB_DIG19 0xec4c 878c2ecf20Sopenharmony_ci#define L3_CALIB_DONE_STATUS 0xef14 888c2ecf20Sopenharmony_ci#define L3_TM_CALIB_DIG18 0xec48 898c2ecf20Sopenharmony_ci#define L3_TM_CALIB_DIG19_NSW 0x07 908c2ecf20Sopenharmony_ci#define L3_TM_CALIB_DIG18_NSW 0xe0 918c2ecf20Sopenharmony_ci#define L3_TM_OVERRIDE_NSW_CODE 0x20 928c2ecf20Sopenharmony_ci#define L3_CALIB_DONE 0x02 938c2ecf20Sopenharmony_ci#define L3_NSW_SHIFT 5 948c2ecf20Sopenharmony_ci#define L3_NSW_PIPE_SHIFT 4 958c2ecf20Sopenharmony_ci#define L3_NSW_CALIB_SHIFT 3 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define PHY_REG_OFFSET 0x4000 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * Global Registers 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* Refclk selection parameters */ 1048c2ecf20Sopenharmony_ci#define PLL_REF_SEL(n) (0x10000 + (n) * 4) 1058c2ecf20Sopenharmony_ci#define PLL_FREQ_MASK 0x1f 1068c2ecf20Sopenharmony_ci#define PLL_STATUS_LOCKED 0x10 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* Inter Connect Matrix parameters */ 1098c2ecf20Sopenharmony_ci#define ICM_CFG0 0x10010 1108c2ecf20Sopenharmony_ci#define ICM_CFG1 0x10014 1118c2ecf20Sopenharmony_ci#define ICM_CFG0_L0_MASK 0x07 1128c2ecf20Sopenharmony_ci#define ICM_CFG0_L1_MASK 0x70 1138c2ecf20Sopenharmony_ci#define ICM_CFG1_L2_MASK 0x07 1148c2ecf20Sopenharmony_ci#define ICM_CFG2_L3_MASK 0x70 1158c2ecf20Sopenharmony_ci#define ICM_CFG_SHIFT 4 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* Inter Connect Matrix allowed protocols */ 1188c2ecf20Sopenharmony_ci#define ICM_PROTOCOL_PD 0x0 1198c2ecf20Sopenharmony_ci#define ICM_PROTOCOL_PCIE 0x1 1208c2ecf20Sopenharmony_ci#define ICM_PROTOCOL_SATA 0x2 1218c2ecf20Sopenharmony_ci#define ICM_PROTOCOL_USB 0x3 1228c2ecf20Sopenharmony_ci#define ICM_PROTOCOL_DP 0x4 1238c2ecf20Sopenharmony_ci#define ICM_PROTOCOL_SGMII 0x5 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* Test Mode common reset control parameters */ 1268c2ecf20Sopenharmony_ci#define TM_CMN_RST 0x10018 1278c2ecf20Sopenharmony_ci#define TM_CMN_RST_EN 0x1 1288c2ecf20Sopenharmony_ci#define TM_CMN_RST_SET 0x2 1298c2ecf20Sopenharmony_ci#define TM_CMN_RST_MASK 0x3 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* Bus width parameters */ 1328c2ecf20Sopenharmony_ci#define TX_PROT_BUS_WIDTH 0x10040 1338c2ecf20Sopenharmony_ci#define RX_PROT_BUS_WIDTH 0x10044 1348c2ecf20Sopenharmony_ci#define PROT_BUS_WIDTH_10 0x0 1358c2ecf20Sopenharmony_ci#define PROT_BUS_WIDTH_20 0x1 1368c2ecf20Sopenharmony_ci#define PROT_BUS_WIDTH_40 0x2 1378c2ecf20Sopenharmony_ci#define PROT_BUS_WIDTH_SHIFT(n) ((n) * 2) 1388c2ecf20Sopenharmony_ci#define PROT_BUS_WIDTH_MASK(n) GENMASK((n) * 2 + 1, (n) * 2) 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* Number of GT lanes */ 1418c2ecf20Sopenharmony_ci#define NUM_LANES 4 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* SIOU SATA control register */ 1448c2ecf20Sopenharmony_ci#define SATA_CONTROL_OFFSET 0x0100 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* Total number of controllers */ 1478c2ecf20Sopenharmony_ci#define CONTROLLERS_PER_LANE 5 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* Protocol Type parameters */ 1508c2ecf20Sopenharmony_ci#define XPSGTR_TYPE_USB0 0 /* USB controller 0 */ 1518c2ecf20Sopenharmony_ci#define XPSGTR_TYPE_USB1 1 /* USB controller 1 */ 1528c2ecf20Sopenharmony_ci#define XPSGTR_TYPE_SATA_0 2 /* SATA controller lane 0 */ 1538c2ecf20Sopenharmony_ci#define XPSGTR_TYPE_SATA_1 3 /* SATA controller lane 1 */ 1548c2ecf20Sopenharmony_ci#define XPSGTR_TYPE_PCIE_0 4 /* PCIe controller lane 0 */ 1558c2ecf20Sopenharmony_ci#define XPSGTR_TYPE_PCIE_1 5 /* PCIe controller lane 1 */ 1568c2ecf20Sopenharmony_ci#define XPSGTR_TYPE_PCIE_2 6 /* PCIe controller lane 2 */ 1578c2ecf20Sopenharmony_ci#define XPSGTR_TYPE_PCIE_3 7 /* PCIe controller lane 3 */ 1588c2ecf20Sopenharmony_ci#define XPSGTR_TYPE_DP_0 8 /* Display Port controller lane 0 */ 1598c2ecf20Sopenharmony_ci#define XPSGTR_TYPE_DP_1 9 /* Display Port controller lane 1 */ 1608c2ecf20Sopenharmony_ci#define XPSGTR_TYPE_SGMII0 10 /* Ethernet SGMII controller 0 */ 1618c2ecf20Sopenharmony_ci#define XPSGTR_TYPE_SGMII1 11 /* Ethernet SGMII controller 1 */ 1628c2ecf20Sopenharmony_ci#define XPSGTR_TYPE_SGMII2 12 /* Ethernet SGMII controller 2 */ 1638c2ecf20Sopenharmony_ci#define XPSGTR_TYPE_SGMII3 13 /* Ethernet SGMII controller 3 */ 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* Timeout values */ 1668c2ecf20Sopenharmony_ci#define TIMEOUT_US 1000 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistruct xpsgtr_dev; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/** 1718c2ecf20Sopenharmony_ci * struct xpsgtr_ssc - structure to hold SSC settings for a lane 1728c2ecf20Sopenharmony_ci * @refclk_rate: PLL reference clock frequency 1738c2ecf20Sopenharmony_ci * @pll_ref_clk: value to be written to register for corresponding ref clk rate 1748c2ecf20Sopenharmony_ci * @steps: number of steps of SSC (Spread Spectrum Clock) 1758c2ecf20Sopenharmony_ci * @step_size: step size of each step 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_cistruct xpsgtr_ssc { 1788c2ecf20Sopenharmony_ci u32 refclk_rate; 1798c2ecf20Sopenharmony_ci u8 pll_ref_clk; 1808c2ecf20Sopenharmony_ci u32 steps; 1818c2ecf20Sopenharmony_ci u32 step_size; 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/** 1858c2ecf20Sopenharmony_ci * struct xpsgtr_phy - representation of a lane 1868c2ecf20Sopenharmony_ci * @phy: pointer to the kernel PHY device 1878c2ecf20Sopenharmony_ci * @type: controller which uses this lane 1888c2ecf20Sopenharmony_ci * @lane: lane number 1898c2ecf20Sopenharmony_ci * @protocol: protocol in which the lane operates 1908c2ecf20Sopenharmony_ci * @skip_phy_init: skip phy_init() if true 1918c2ecf20Sopenharmony_ci * @dev: pointer to the xpsgtr_dev instance 1928c2ecf20Sopenharmony_ci * @refclk: reference clock index 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_cistruct xpsgtr_phy { 1958c2ecf20Sopenharmony_ci struct phy *phy; 1968c2ecf20Sopenharmony_ci u8 type; 1978c2ecf20Sopenharmony_ci u8 lane; 1988c2ecf20Sopenharmony_ci u8 protocol; 1998c2ecf20Sopenharmony_ci bool skip_phy_init; 2008c2ecf20Sopenharmony_ci struct xpsgtr_dev *dev; 2018c2ecf20Sopenharmony_ci unsigned int refclk; 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/** 2058c2ecf20Sopenharmony_ci * struct xpsgtr_dev - representation of a ZynMP GT device 2068c2ecf20Sopenharmony_ci * @dev: pointer to device 2078c2ecf20Sopenharmony_ci * @serdes: serdes base address 2088c2ecf20Sopenharmony_ci * @siou: siou base address 2098c2ecf20Sopenharmony_ci * @gtr_mutex: mutex for locking 2108c2ecf20Sopenharmony_ci * @phys: PHY lanes 2118c2ecf20Sopenharmony_ci * @refclk_sscs: spread spectrum settings for the reference clocks 2128c2ecf20Sopenharmony_ci * @tx_term_fix: fix for GT issue 2138c2ecf20Sopenharmony_ci * @saved_icm_cfg0: stored value of ICM CFG0 register 2148c2ecf20Sopenharmony_ci * @saved_icm_cfg1: stored value of ICM CFG1 register 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_cistruct xpsgtr_dev { 2178c2ecf20Sopenharmony_ci struct device *dev; 2188c2ecf20Sopenharmony_ci void __iomem *serdes; 2198c2ecf20Sopenharmony_ci void __iomem *siou; 2208c2ecf20Sopenharmony_ci struct mutex gtr_mutex; /* mutex for locking */ 2218c2ecf20Sopenharmony_ci struct xpsgtr_phy phys[NUM_LANES]; 2228c2ecf20Sopenharmony_ci const struct xpsgtr_ssc *refclk_sscs[NUM_LANES]; 2238c2ecf20Sopenharmony_ci bool tx_term_fix; 2248c2ecf20Sopenharmony_ci unsigned int saved_icm_cfg0; 2258c2ecf20Sopenharmony_ci unsigned int saved_icm_cfg1; 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* 2298c2ecf20Sopenharmony_ci * Configuration Data 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* lookup table to hold all settings needed for a ref clock frequency */ 2338c2ecf20Sopenharmony_cistatic const struct xpsgtr_ssc ssc_lookup[] = { 2348c2ecf20Sopenharmony_ci { 19200000, 0x05, 608, 264020 }, 2358c2ecf20Sopenharmony_ci { 20000000, 0x06, 634, 243454 }, 2368c2ecf20Sopenharmony_ci { 24000000, 0x07, 760, 168973 }, 2378c2ecf20Sopenharmony_ci { 26000000, 0x08, 824, 143860 }, 2388c2ecf20Sopenharmony_ci { 27000000, 0x09, 856, 86551 }, 2398c2ecf20Sopenharmony_ci { 38400000, 0x0a, 1218, 65896 }, 2408c2ecf20Sopenharmony_ci { 40000000, 0x0b, 634, 243454 }, 2418c2ecf20Sopenharmony_ci { 52000000, 0x0c, 824, 143860 }, 2428c2ecf20Sopenharmony_ci { 100000000, 0x0d, 1058, 87533 }, 2438c2ecf20Sopenharmony_ci { 108000000, 0x0e, 856, 86551 }, 2448c2ecf20Sopenharmony_ci { 125000000, 0x0f, 992, 119497 }, 2458c2ecf20Sopenharmony_ci { 135000000, 0x10, 1070, 55393 }, 2468c2ecf20Sopenharmony_ci { 150000000, 0x11, 792, 187091 } 2478c2ecf20Sopenharmony_ci}; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* 2508c2ecf20Sopenharmony_ci * I/O Accessors 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic inline u32 xpsgtr_read(struct xpsgtr_dev *gtr_dev, u32 reg) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci return readl(gtr_dev->serdes + reg); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic inline void xpsgtr_write(struct xpsgtr_dev *gtr_dev, u32 reg, u32 value) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci writel(value, gtr_dev->serdes + reg); 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic inline void xpsgtr_clr_set(struct xpsgtr_dev *gtr_dev, u32 reg, 2648c2ecf20Sopenharmony_ci u32 clr, u32 set) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci u32 value = xpsgtr_read(gtr_dev, reg); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci value &= ~clr; 2698c2ecf20Sopenharmony_ci value |= set; 2708c2ecf20Sopenharmony_ci xpsgtr_write(gtr_dev, reg, value); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic inline u32 xpsgtr_read_phy(struct xpsgtr_phy *gtr_phy, u32 reg) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci void __iomem *addr = gtr_phy->dev->serdes 2768c2ecf20Sopenharmony_ci + gtr_phy->lane * PHY_REG_OFFSET + reg; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return readl(addr); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic inline void xpsgtr_write_phy(struct xpsgtr_phy *gtr_phy, 2828c2ecf20Sopenharmony_ci u32 reg, u32 value) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci void __iomem *addr = gtr_phy->dev->serdes 2858c2ecf20Sopenharmony_ci + gtr_phy->lane * PHY_REG_OFFSET + reg; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci writel(value, addr); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic inline void xpsgtr_clr_set_phy(struct xpsgtr_phy *gtr_phy, 2918c2ecf20Sopenharmony_ci u32 reg, u32 clr, u32 set) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci void __iomem *addr = gtr_phy->dev->serdes 2948c2ecf20Sopenharmony_ci + gtr_phy->lane * PHY_REG_OFFSET + reg; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci writel((readl(addr) & ~clr) | set, addr); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* 3008c2ecf20Sopenharmony_ci * Hardware Configuration 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci/* Wait for the PLL to lock (with a timeout). */ 3048c2ecf20Sopenharmony_cistatic int xpsgtr_wait_pll_lock(struct phy *phy) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); 3078c2ecf20Sopenharmony_ci struct xpsgtr_dev *gtr_dev = gtr_phy->dev; 3088c2ecf20Sopenharmony_ci unsigned int timeout = TIMEOUT_US; 3098c2ecf20Sopenharmony_ci int ret; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci dev_dbg(gtr_dev->dev, "Waiting for PLL lock\n"); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci while (1) { 3148c2ecf20Sopenharmony_ci u32 reg = xpsgtr_read_phy(gtr_phy, L0_PLL_STATUS_READ_1); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if ((reg & PLL_STATUS_LOCKED) == PLL_STATUS_LOCKED) { 3178c2ecf20Sopenharmony_ci ret = 0; 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (--timeout == 0) { 3228c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci udelay(1); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (ret == -ETIMEDOUT) 3308c2ecf20Sopenharmony_ci dev_err(gtr_dev->dev, 3318c2ecf20Sopenharmony_ci "lane %u (type %u, protocol %u): PLL lock timeout\n", 3328c2ecf20Sopenharmony_ci gtr_phy->lane, gtr_phy->type, gtr_phy->protocol); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci return ret; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/* Configure PLL and spread-sprectrum clock. */ 3388c2ecf20Sopenharmony_cistatic void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci const struct xpsgtr_ssc *ssc; 3418c2ecf20Sopenharmony_ci u32 step_size; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk]; 3448c2ecf20Sopenharmony_ci step_size = ssc->step_size; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane), 3478c2ecf20Sopenharmony_ci PLL_FREQ_MASK, ssc->pll_ref_clk); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Enable lane clock sharing, if required */ 3508c2ecf20Sopenharmony_ci if (gtr_phy->refclk != gtr_phy->lane) { 3518c2ecf20Sopenharmony_ci /* Lane3 Ref Clock Selection Register */ 3528c2ecf20Sopenharmony_ci xpsgtr_clr_set(gtr_phy->dev, L0_Ln_REF_CLK_SEL(gtr_phy->lane), 3538c2ecf20Sopenharmony_ci L0_REF_CLK_SEL_MASK, 1 << gtr_phy->refclk); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* SSC step size [7:0] */ 3578c2ecf20Sopenharmony_ci xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_0_LSB, 3588c2ecf20Sopenharmony_ci STEP_SIZE_0_MASK, step_size & STEP_SIZE_0_MASK); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* SSC step size [15:8] */ 3618c2ecf20Sopenharmony_ci step_size >>= STEP_SIZE_SHIFT; 3628c2ecf20Sopenharmony_ci xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_1, 3638c2ecf20Sopenharmony_ci STEP_SIZE_1_MASK, step_size & STEP_SIZE_1_MASK); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* SSC step size [23:16] */ 3668c2ecf20Sopenharmony_ci step_size >>= STEP_SIZE_SHIFT; 3678c2ecf20Sopenharmony_ci xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_2, 3688c2ecf20Sopenharmony_ci STEP_SIZE_2_MASK, step_size & STEP_SIZE_2_MASK); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* SSC steps [7:0] */ 3718c2ecf20Sopenharmony_ci xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEPS_0_LSB, 3728c2ecf20Sopenharmony_ci STEPS_0_MASK, ssc->steps & STEPS_0_MASK); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* SSC steps [10:8] */ 3758c2ecf20Sopenharmony_ci xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEPS_1_MSB, 3768c2ecf20Sopenharmony_ci STEPS_1_MASK, 3778c2ecf20Sopenharmony_ci (ssc->steps >> STEP_SIZE_SHIFT) & STEPS_1_MASK); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* SSC step size [24:25] */ 3808c2ecf20Sopenharmony_ci step_size >>= STEP_SIZE_SHIFT; 3818c2ecf20Sopenharmony_ci xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB, 3828c2ecf20Sopenharmony_ci STEP_SIZE_3_MASK, (step_size & STEP_SIZE_3_MASK) | 3838c2ecf20Sopenharmony_ci FORCE_STEP_SIZE | FORCE_STEPS); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* Configure the lane protocol. */ 3878c2ecf20Sopenharmony_cistatic void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct xpsgtr_dev *gtr_dev = gtr_phy->dev; 3908c2ecf20Sopenharmony_ci u8 protocol = gtr_phy->protocol; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci switch (gtr_phy->lane) { 3938c2ecf20Sopenharmony_ci case 0: 3948c2ecf20Sopenharmony_ci xpsgtr_clr_set(gtr_dev, ICM_CFG0, ICM_CFG0_L0_MASK, protocol); 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci case 1: 3978c2ecf20Sopenharmony_ci xpsgtr_clr_set(gtr_dev, ICM_CFG0, ICM_CFG0_L1_MASK, 3988c2ecf20Sopenharmony_ci protocol << ICM_CFG_SHIFT); 3998c2ecf20Sopenharmony_ci break; 4008c2ecf20Sopenharmony_ci case 2: 4018c2ecf20Sopenharmony_ci xpsgtr_clr_set(gtr_dev, ICM_CFG1, ICM_CFG0_L0_MASK, protocol); 4028c2ecf20Sopenharmony_ci break; 4038c2ecf20Sopenharmony_ci case 3: 4048c2ecf20Sopenharmony_ci xpsgtr_clr_set(gtr_dev, ICM_CFG1, ICM_CFG0_L1_MASK, 4058c2ecf20Sopenharmony_ci protocol << ICM_CFG_SHIFT); 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci default: 4088c2ecf20Sopenharmony_ci /* We already checked 0 <= lane <= 3 */ 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci/* Bypass (de)scrambler and 8b/10b decoder and encoder. */ 4148c2ecf20Sopenharmony_cistatic void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci xpsgtr_write_phy(gtr_phy, L0_TM_DIG_6, L0_TM_DIS_DESCRAMBLE_DECODER); 4178c2ecf20Sopenharmony_ci xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61, L0_TM_DISABLE_SCRAMBLE_ENCODER); 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci/* DP-specific initialization. */ 4218c2ecf20Sopenharmony_cistatic void xpsgtr_phy_init_dp(struct xpsgtr_phy *gtr_phy) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci xpsgtr_write_phy(gtr_phy, L0_TXPMD_TM_45, 4248c2ecf20Sopenharmony_ci L0_TXPMD_TM_45_OVER_DP_MAIN | 4258c2ecf20Sopenharmony_ci L0_TXPMD_TM_45_ENABLE_DP_MAIN | 4268c2ecf20Sopenharmony_ci L0_TXPMD_TM_45_OVER_DP_POST1 | 4278c2ecf20Sopenharmony_ci L0_TXPMD_TM_45_OVER_DP_POST2 | 4288c2ecf20Sopenharmony_ci L0_TXPMD_TM_45_ENABLE_DP_POST2); 4298c2ecf20Sopenharmony_ci xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_118, 4308c2ecf20Sopenharmony_ci L0_TX_ANA_TM_118_FORCE_17_0); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci/* SATA-specific initialization. */ 4348c2ecf20Sopenharmony_cistatic void xpsgtr_phy_init_sata(struct xpsgtr_phy *gtr_phy) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct xpsgtr_dev *gtr_dev = gtr_phy->dev; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci xpsgtr_bypass_scrambler_8b10b(gtr_phy); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET); 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci/* SGMII-specific initialization. */ 4448c2ecf20Sopenharmony_cistatic void xpsgtr_phy_init_sgmii(struct xpsgtr_phy *gtr_phy) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct xpsgtr_dev *gtr_dev = gtr_phy->dev; 4478c2ecf20Sopenharmony_ci u32 mask = PROT_BUS_WIDTH_MASK(gtr_phy->lane); 4488c2ecf20Sopenharmony_ci u32 val = PROT_BUS_WIDTH_10 << PROT_BUS_WIDTH_SHIFT(gtr_phy->lane); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* Set SGMII protocol TX and RX bus width to 10 bits. */ 4518c2ecf20Sopenharmony_ci xpsgtr_clr_set(gtr_dev, TX_PROT_BUS_WIDTH, mask, val); 4528c2ecf20Sopenharmony_ci xpsgtr_clr_set(gtr_dev, RX_PROT_BUS_WIDTH, mask, val); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci xpsgtr_bypass_scrambler_8b10b(gtr_phy); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci/* Configure TX de-emphasis and margining for DP. */ 4588c2ecf20Sopenharmony_cistatic void xpsgtr_phy_configure_dp(struct xpsgtr_phy *gtr_phy, unsigned int pre, 4598c2ecf20Sopenharmony_ci unsigned int voltage) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci static const u8 voltage_swing[4][4] = { 4628c2ecf20Sopenharmony_ci { 0x2a, 0x27, 0x24, 0x20 }, 4638c2ecf20Sopenharmony_ci { 0x27, 0x23, 0x20, 0xff }, 4648c2ecf20Sopenharmony_ci { 0x24, 0x20, 0xff, 0xff }, 4658c2ecf20Sopenharmony_ci { 0xff, 0xff, 0xff, 0xff } 4668c2ecf20Sopenharmony_ci }; 4678c2ecf20Sopenharmony_ci static const u8 pre_emphasis[4][4] = { 4688c2ecf20Sopenharmony_ci { 0x02, 0x02, 0x02, 0x02 }, 4698c2ecf20Sopenharmony_ci { 0x01, 0x01, 0x01, 0xff }, 4708c2ecf20Sopenharmony_ci { 0x00, 0x00, 0xff, 0xff }, 4718c2ecf20Sopenharmony_ci { 0xff, 0xff, 0xff, 0xff } 4728c2ecf20Sopenharmony_ci }; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci xpsgtr_write_phy(gtr_phy, L0_TXPMD_TM_48, voltage_swing[pre][voltage]); 4758c2ecf20Sopenharmony_ci xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_18, pre_emphasis[pre][voltage]); 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/* 4798c2ecf20Sopenharmony_ci * PHY Operations 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic bool xpsgtr_phy_init_required(struct xpsgtr_phy *gtr_phy) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci /* 4858c2ecf20Sopenharmony_ci * As USB may save the snapshot of the states during hibernation, doing 4868c2ecf20Sopenharmony_ci * phy_init() will put the USB controller into reset, resulting in the 4878c2ecf20Sopenharmony_ci * losing of the saved snapshot. So try to avoid phy_init() for USB 4888c2ecf20Sopenharmony_ci * except when gtr_phy->skip_phy_init is false (this happens when FPD is 4898c2ecf20Sopenharmony_ci * shutdown during suspend or when gt lane is changed from current one) 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_ci if (gtr_phy->protocol == ICM_PROTOCOL_USB && gtr_phy->skip_phy_init) 4928c2ecf20Sopenharmony_ci return false; 4938c2ecf20Sopenharmony_ci else 4948c2ecf20Sopenharmony_ci return true; 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci/* 4988c2ecf20Sopenharmony_ci * There is a functional issue in the GT. The TX termination resistance can be 4998c2ecf20Sopenharmony_ci * out of spec due to a issue in the calibration logic. This is the workaround 5008c2ecf20Sopenharmony_ci * to fix it, required for XCZU9EG silicon. 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_cistatic int xpsgtr_phy_tx_term_fix(struct xpsgtr_phy *gtr_phy) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct xpsgtr_dev *gtr_dev = gtr_phy->dev; 5058c2ecf20Sopenharmony_ci u32 timeout = TIMEOUT_US; 5068c2ecf20Sopenharmony_ci u32 nsw; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* Enabling Test Mode control for CMN Rest */ 5098c2ecf20Sopenharmony_ci xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* Set Test Mode reset */ 5128c2ecf20Sopenharmony_ci xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_EN); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG18, 0x00); 5158c2ecf20Sopenharmony_ci xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG19, L3_TM_OVERRIDE_NSW_CODE); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* 5188c2ecf20Sopenharmony_ci * As a part of work around sequence for PMOS calibration fix, 5198c2ecf20Sopenharmony_ci * we need to configure any lane ICM_CFG to valid protocol. This 5208c2ecf20Sopenharmony_ci * will deassert the CMN_Resetn signal. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci xpsgtr_lane_set_protocol(gtr_phy); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* Clear Test Mode reset */ 5258c2ecf20Sopenharmony_ci xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci dev_dbg(gtr_dev->dev, "calibrating...\n"); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci do { 5308c2ecf20Sopenharmony_ci u32 reg = xpsgtr_read(gtr_dev, L3_CALIB_DONE_STATUS); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if ((reg & L3_CALIB_DONE) == L3_CALIB_DONE) 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (!--timeout) { 5368c2ecf20Sopenharmony_ci dev_err(gtr_dev->dev, "calibration time out\n"); 5378c2ecf20Sopenharmony_ci return -ETIMEDOUT; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci udelay(1); 5418c2ecf20Sopenharmony_ci } while (timeout > 0); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci dev_dbg(gtr_dev->dev, "calibration done\n"); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* Reading NMOS Register Code */ 5468c2ecf20Sopenharmony_ci nsw = xpsgtr_read(gtr_dev, L0_TXPMA_ST_3) & L0_DN_CALIB_CODE; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* Set Test Mode reset */ 5498c2ecf20Sopenharmony_ci xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_EN); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* Writing NMOS register values back [5:3] */ 5528c2ecf20Sopenharmony_ci xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG19, nsw >> L3_NSW_CALIB_SHIFT); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* Writing NMOS register value [2:0] */ 5558c2ecf20Sopenharmony_ci xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG18, 5568c2ecf20Sopenharmony_ci ((nsw & L3_TM_CALIB_DIG19_NSW) << L3_NSW_SHIFT) | 5578c2ecf20Sopenharmony_ci (1 << L3_NSW_PIPE_SHIFT)); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* Clear Test Mode reset */ 5608c2ecf20Sopenharmony_ci xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci return 0; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic int xpsgtr_phy_init(struct phy *phy) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); 5688c2ecf20Sopenharmony_ci struct xpsgtr_dev *gtr_dev = gtr_phy->dev; 5698c2ecf20Sopenharmony_ci int ret = 0; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci mutex_lock(>r_dev->gtr_mutex); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* Skip initialization if not required. */ 5748c2ecf20Sopenharmony_ci if (!xpsgtr_phy_init_required(gtr_phy)) 5758c2ecf20Sopenharmony_ci goto out; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (gtr_dev->tx_term_fix) { 5788c2ecf20Sopenharmony_ci ret = xpsgtr_phy_tx_term_fix(gtr_phy); 5798c2ecf20Sopenharmony_ci if (ret < 0) 5808c2ecf20Sopenharmony_ci goto out; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci gtr_dev->tx_term_fix = false; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* Enable coarse code saturation limiting logic. */ 5868c2ecf20Sopenharmony_ci xpsgtr_write_phy(gtr_phy, L0_TM_PLL_DIG_37, L0_TM_COARSE_CODE_LIMIT); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* 5898c2ecf20Sopenharmony_ci * Configure the PLL, the lane protocol, and perform protocol-specific 5908c2ecf20Sopenharmony_ci * initialization. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_ci xpsgtr_configure_pll(gtr_phy); 5938c2ecf20Sopenharmony_ci xpsgtr_lane_set_protocol(gtr_phy); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci switch (gtr_phy->protocol) { 5968c2ecf20Sopenharmony_ci case ICM_PROTOCOL_DP: 5978c2ecf20Sopenharmony_ci xpsgtr_phy_init_dp(gtr_phy); 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci case ICM_PROTOCOL_SATA: 6018c2ecf20Sopenharmony_ci xpsgtr_phy_init_sata(gtr_phy); 6028c2ecf20Sopenharmony_ci break; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci case ICM_PROTOCOL_SGMII: 6058c2ecf20Sopenharmony_ci xpsgtr_phy_init_sgmii(gtr_phy); 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ciout: 6108c2ecf20Sopenharmony_ci mutex_unlock(>r_dev->gtr_mutex); 6118c2ecf20Sopenharmony_ci return ret; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic int xpsgtr_phy_exit(struct phy *phy) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci gtr_phy->skip_phy_init = false; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci return 0; 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic int xpsgtr_phy_power_on(struct phy *phy) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); 6268c2ecf20Sopenharmony_ci int ret = 0; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci /* 6298c2ecf20Sopenharmony_ci * Wait for the PLL to lock. For DP, only wait on DP0 to avoid 6308c2ecf20Sopenharmony_ci * cumulating waits for both lanes. The user is expected to initialize 6318c2ecf20Sopenharmony_ci * lane 0 last. 6328c2ecf20Sopenharmony_ci */ 6338c2ecf20Sopenharmony_ci if (gtr_phy->protocol != ICM_PROTOCOL_DP || 6348c2ecf20Sopenharmony_ci gtr_phy->type == XPSGTR_TYPE_DP_0) 6358c2ecf20Sopenharmony_ci ret = xpsgtr_wait_pll_lock(phy); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci return ret; 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic int xpsgtr_phy_configure(struct phy *phy, union phy_configure_opts *opts) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (gtr_phy->protocol != ICM_PROTOCOL_DP) 6458c2ecf20Sopenharmony_ci return 0; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci xpsgtr_phy_configure_dp(gtr_phy, opts->dp.pre[0], opts->dp.voltage[0]); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci return 0; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic const struct phy_ops xpsgtr_phyops = { 6538c2ecf20Sopenharmony_ci .init = xpsgtr_phy_init, 6548c2ecf20Sopenharmony_ci .exit = xpsgtr_phy_exit, 6558c2ecf20Sopenharmony_ci .power_on = xpsgtr_phy_power_on, 6568c2ecf20Sopenharmony_ci .configure = xpsgtr_phy_configure, 6578c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6588c2ecf20Sopenharmony_ci}; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci/* 6618c2ecf20Sopenharmony_ci * OF Xlate Support 6628c2ecf20Sopenharmony_ci */ 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci/* Set the lane type and protocol based on the PHY type and instance number. */ 6658c2ecf20Sopenharmony_cistatic int xpsgtr_set_lane_type(struct xpsgtr_phy *gtr_phy, u8 phy_type, 6668c2ecf20Sopenharmony_ci unsigned int phy_instance) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci unsigned int num_phy_types; 6698c2ecf20Sopenharmony_ci const int *phy_types; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci switch (phy_type) { 6728c2ecf20Sopenharmony_ci case PHY_TYPE_SATA: { 6738c2ecf20Sopenharmony_ci static const int types[] = { 6748c2ecf20Sopenharmony_ci XPSGTR_TYPE_SATA_0, 6758c2ecf20Sopenharmony_ci XPSGTR_TYPE_SATA_1, 6768c2ecf20Sopenharmony_ci }; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci phy_types = types; 6798c2ecf20Sopenharmony_ci num_phy_types = ARRAY_SIZE(types); 6808c2ecf20Sopenharmony_ci gtr_phy->protocol = ICM_PROTOCOL_SATA; 6818c2ecf20Sopenharmony_ci break; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci case PHY_TYPE_USB3: { 6848c2ecf20Sopenharmony_ci static const int types[] = { 6858c2ecf20Sopenharmony_ci XPSGTR_TYPE_USB0, 6868c2ecf20Sopenharmony_ci XPSGTR_TYPE_USB1, 6878c2ecf20Sopenharmony_ci }; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci phy_types = types; 6908c2ecf20Sopenharmony_ci num_phy_types = ARRAY_SIZE(types); 6918c2ecf20Sopenharmony_ci gtr_phy->protocol = ICM_PROTOCOL_USB; 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci case PHY_TYPE_DP: { 6958c2ecf20Sopenharmony_ci static const int types[] = { 6968c2ecf20Sopenharmony_ci XPSGTR_TYPE_DP_0, 6978c2ecf20Sopenharmony_ci XPSGTR_TYPE_DP_1, 6988c2ecf20Sopenharmony_ci }; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci phy_types = types; 7018c2ecf20Sopenharmony_ci num_phy_types = ARRAY_SIZE(types); 7028c2ecf20Sopenharmony_ci gtr_phy->protocol = ICM_PROTOCOL_DP; 7038c2ecf20Sopenharmony_ci break; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci case PHY_TYPE_PCIE: { 7068c2ecf20Sopenharmony_ci static const int types[] = { 7078c2ecf20Sopenharmony_ci XPSGTR_TYPE_PCIE_0, 7088c2ecf20Sopenharmony_ci XPSGTR_TYPE_PCIE_1, 7098c2ecf20Sopenharmony_ci XPSGTR_TYPE_PCIE_2, 7108c2ecf20Sopenharmony_ci XPSGTR_TYPE_PCIE_3, 7118c2ecf20Sopenharmony_ci }; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci phy_types = types; 7148c2ecf20Sopenharmony_ci num_phy_types = ARRAY_SIZE(types); 7158c2ecf20Sopenharmony_ci gtr_phy->protocol = ICM_PROTOCOL_PCIE; 7168c2ecf20Sopenharmony_ci break; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci case PHY_TYPE_SGMII: { 7198c2ecf20Sopenharmony_ci static const int types[] = { 7208c2ecf20Sopenharmony_ci XPSGTR_TYPE_SGMII0, 7218c2ecf20Sopenharmony_ci XPSGTR_TYPE_SGMII1, 7228c2ecf20Sopenharmony_ci XPSGTR_TYPE_SGMII2, 7238c2ecf20Sopenharmony_ci XPSGTR_TYPE_SGMII3, 7248c2ecf20Sopenharmony_ci }; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci phy_types = types; 7278c2ecf20Sopenharmony_ci num_phy_types = ARRAY_SIZE(types); 7288c2ecf20Sopenharmony_ci gtr_phy->protocol = ICM_PROTOCOL_SGMII; 7298c2ecf20Sopenharmony_ci break; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci default: 7328c2ecf20Sopenharmony_ci return -EINVAL; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (phy_instance >= num_phy_types) 7368c2ecf20Sopenharmony_ci return -EINVAL; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci gtr_phy->type = phy_types[phy_instance]; 7398c2ecf20Sopenharmony_ci return 0; 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci/* 7438c2ecf20Sopenharmony_ci * Valid combinations of controllers and lanes (Interconnect Matrix). 7448c2ecf20Sopenharmony_ci */ 7458c2ecf20Sopenharmony_cistatic const unsigned int icm_matrix[NUM_LANES][CONTROLLERS_PER_LANE] = { 7468c2ecf20Sopenharmony_ci { XPSGTR_TYPE_PCIE_0, XPSGTR_TYPE_SATA_0, XPSGTR_TYPE_USB0, 7478c2ecf20Sopenharmony_ci XPSGTR_TYPE_DP_1, XPSGTR_TYPE_SGMII0 }, 7488c2ecf20Sopenharmony_ci { XPSGTR_TYPE_PCIE_1, XPSGTR_TYPE_SATA_1, XPSGTR_TYPE_USB0, 7498c2ecf20Sopenharmony_ci XPSGTR_TYPE_DP_0, XPSGTR_TYPE_SGMII1 }, 7508c2ecf20Sopenharmony_ci { XPSGTR_TYPE_PCIE_2, XPSGTR_TYPE_SATA_0, XPSGTR_TYPE_USB0, 7518c2ecf20Sopenharmony_ci XPSGTR_TYPE_DP_1, XPSGTR_TYPE_SGMII2 }, 7528c2ecf20Sopenharmony_ci { XPSGTR_TYPE_PCIE_3, XPSGTR_TYPE_SATA_1, XPSGTR_TYPE_USB1, 7538c2ecf20Sopenharmony_ci XPSGTR_TYPE_DP_0, XPSGTR_TYPE_SGMII3 } 7548c2ecf20Sopenharmony_ci}; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci/* Translate OF phandle and args to PHY instance. */ 7578c2ecf20Sopenharmony_cistatic struct phy *xpsgtr_xlate(struct device *dev, 7588c2ecf20Sopenharmony_ci struct of_phandle_args *args) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); 7618c2ecf20Sopenharmony_ci struct xpsgtr_phy *gtr_phy; 7628c2ecf20Sopenharmony_ci unsigned int phy_instance; 7638c2ecf20Sopenharmony_ci unsigned int phy_lane; 7648c2ecf20Sopenharmony_ci unsigned int phy_type; 7658c2ecf20Sopenharmony_ci unsigned int refclk; 7668c2ecf20Sopenharmony_ci unsigned int i; 7678c2ecf20Sopenharmony_ci int ret; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (args->args_count != 4) { 7708c2ecf20Sopenharmony_ci dev_err(dev, "Invalid number of cells in 'phy' property\n"); 7718c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci /* 7758c2ecf20Sopenharmony_ci * Get the PHY parameters from the OF arguments and derive the lane 7768c2ecf20Sopenharmony_ci * type. 7778c2ecf20Sopenharmony_ci */ 7788c2ecf20Sopenharmony_ci phy_lane = args->args[0]; 7798c2ecf20Sopenharmony_ci if (phy_lane >= ARRAY_SIZE(gtr_dev->phys)) { 7808c2ecf20Sopenharmony_ci dev_err(dev, "Invalid lane number %u\n", phy_lane); 7818c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci gtr_phy = >r_dev->phys[phy_lane]; 7858c2ecf20Sopenharmony_ci phy_type = args->args[1]; 7868c2ecf20Sopenharmony_ci phy_instance = args->args[2]; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci ret = xpsgtr_set_lane_type(gtr_phy, phy_type, phy_instance); 7898c2ecf20Sopenharmony_ci if (ret < 0) { 7908c2ecf20Sopenharmony_ci dev_err(gtr_dev->dev, "Invalid PHY type and/or instance\n"); 7918c2ecf20Sopenharmony_ci return ERR_PTR(ret); 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci refclk = args->args[3]; 7958c2ecf20Sopenharmony_ci if (refclk >= ARRAY_SIZE(gtr_dev->refclk_sscs) || 7968c2ecf20Sopenharmony_ci !gtr_dev->refclk_sscs[refclk]) { 7978c2ecf20Sopenharmony_ci dev_err(dev, "Invalid reference clock number %u\n", refclk); 7988c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci gtr_phy->refclk = refclk; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci /* 8048c2ecf20Sopenharmony_ci * Ensure that the Interconnect Matrix is obeyed, i.e a given lane type 8058c2ecf20Sopenharmony_ci * is allowed to operate on the lane. 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_ci for (i = 0; i < CONTROLLERS_PER_LANE; i++) { 8088c2ecf20Sopenharmony_ci if (icm_matrix[phy_lane][i] == gtr_phy->type) 8098c2ecf20Sopenharmony_ci return gtr_phy->phy; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci/* 8168c2ecf20Sopenharmony_ci * Power Management 8178c2ecf20Sopenharmony_ci */ 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic int __maybe_unused xpsgtr_suspend(struct device *dev) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci /* Save the snapshot ICM_CFG registers. */ 8248c2ecf20Sopenharmony_ci gtr_dev->saved_icm_cfg0 = xpsgtr_read(gtr_dev, ICM_CFG0); 8258c2ecf20Sopenharmony_ci gtr_dev->saved_icm_cfg1 = xpsgtr_read(gtr_dev, ICM_CFG1); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci return 0; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic int __maybe_unused xpsgtr_resume(struct device *dev) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); 8338c2ecf20Sopenharmony_ci unsigned int icm_cfg0, icm_cfg1; 8348c2ecf20Sopenharmony_ci unsigned int i; 8358c2ecf20Sopenharmony_ci bool skip_phy_init; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci icm_cfg0 = xpsgtr_read(gtr_dev, ICM_CFG0); 8388c2ecf20Sopenharmony_ci icm_cfg1 = xpsgtr_read(gtr_dev, ICM_CFG1); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci /* Return if no GT lanes got configured before suspend. */ 8418c2ecf20Sopenharmony_ci if (!gtr_dev->saved_icm_cfg0 && !gtr_dev->saved_icm_cfg1) 8428c2ecf20Sopenharmony_ci return 0; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* Check if the ICM configurations changed after suspend. */ 8458c2ecf20Sopenharmony_ci if (icm_cfg0 == gtr_dev->saved_icm_cfg0 && 8468c2ecf20Sopenharmony_ci icm_cfg1 == gtr_dev->saved_icm_cfg1) 8478c2ecf20Sopenharmony_ci skip_phy_init = true; 8488c2ecf20Sopenharmony_ci else 8498c2ecf20Sopenharmony_ci skip_phy_init = false; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci /* Update the skip_phy_init for all gtr_phy instances. */ 8528c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(gtr_dev->phys); i++) 8538c2ecf20Sopenharmony_ci gtr_dev->phys[i].skip_phy_init = skip_phy_init; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci return 0; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cistatic const struct dev_pm_ops xpsgtr_pm_ops = { 8598c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(xpsgtr_suspend, xpsgtr_resume) 8608c2ecf20Sopenharmony_ci}; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci/* 8638c2ecf20Sopenharmony_ci * Probe & Platform Driver 8648c2ecf20Sopenharmony_ci */ 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci unsigned int refclk; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) { 8718c2ecf20Sopenharmony_ci unsigned long rate; 8728c2ecf20Sopenharmony_ci unsigned int i; 8738c2ecf20Sopenharmony_ci struct clk *clk; 8748c2ecf20Sopenharmony_ci char name[8]; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "ref%u", refclk); 8778c2ecf20Sopenharmony_ci clk = devm_clk_get_optional(gtr_dev->dev, name); 8788c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 8798c2ecf20Sopenharmony_ci if (PTR_ERR(clk) != -EPROBE_DEFER) 8808c2ecf20Sopenharmony_ci dev_err(gtr_dev->dev, 8818c2ecf20Sopenharmony_ci "Failed to get reference clock %u: %ld\n", 8828c2ecf20Sopenharmony_ci refclk, PTR_ERR(clk)); 8838c2ecf20Sopenharmony_ci return PTR_ERR(clk); 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (!clk) 8878c2ecf20Sopenharmony_ci continue; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci /* 8908c2ecf20Sopenharmony_ci * Get the spread spectrum (SSC) settings for the reference 8918c2ecf20Sopenharmony_ci * clock rate. 8928c2ecf20Sopenharmony_ci */ 8938c2ecf20Sopenharmony_ci rate = clk_get_rate(clk); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { 8968c2ecf20Sopenharmony_ci if (rate == ssc_lookup[i].refclk_rate) { 8978c2ecf20Sopenharmony_ci gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i]; 8988c2ecf20Sopenharmony_ci break; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(ssc_lookup)) { 9038c2ecf20Sopenharmony_ci dev_err(gtr_dev->dev, 9048c2ecf20Sopenharmony_ci "Invalid rate %lu for reference clock %u\n", 9058c2ecf20Sopenharmony_ci rate, refclk); 9068c2ecf20Sopenharmony_ci return -EINVAL; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci return 0; 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic int xpsgtr_probe(struct platform_device *pdev) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 9168c2ecf20Sopenharmony_ci struct xpsgtr_dev *gtr_dev; 9178c2ecf20Sopenharmony_ci struct phy_provider *provider; 9188c2ecf20Sopenharmony_ci unsigned int port; 9198c2ecf20Sopenharmony_ci int ret; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci gtr_dev = devm_kzalloc(&pdev->dev, sizeof(*gtr_dev), GFP_KERNEL); 9228c2ecf20Sopenharmony_ci if (!gtr_dev) 9238c2ecf20Sopenharmony_ci return -ENOMEM; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci gtr_dev->dev = &pdev->dev; 9268c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, gtr_dev); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci mutex_init(>r_dev->gtr_mutex); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "xlnx,zynqmp-psgtr")) 9318c2ecf20Sopenharmony_ci gtr_dev->tx_term_fix = 9328c2ecf20Sopenharmony_ci of_property_read_bool(np, "xlnx,tx-termination-fix"); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* Acquire resources. */ 9358c2ecf20Sopenharmony_ci gtr_dev->serdes = devm_platform_ioremap_resource_byname(pdev, "serdes"); 9368c2ecf20Sopenharmony_ci if (IS_ERR(gtr_dev->serdes)) 9378c2ecf20Sopenharmony_ci return PTR_ERR(gtr_dev->serdes); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci gtr_dev->siou = devm_platform_ioremap_resource_byname(pdev, "siou"); 9408c2ecf20Sopenharmony_ci if (IS_ERR(gtr_dev->siou)) 9418c2ecf20Sopenharmony_ci return PTR_ERR(gtr_dev->siou); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci ret = xpsgtr_get_ref_clocks(gtr_dev); 9448c2ecf20Sopenharmony_ci if (ret) 9458c2ecf20Sopenharmony_ci return ret; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci /* Create PHYs. */ 9488c2ecf20Sopenharmony_ci for (port = 0; port < ARRAY_SIZE(gtr_dev->phys); ++port) { 9498c2ecf20Sopenharmony_ci struct xpsgtr_phy *gtr_phy = >r_dev->phys[port]; 9508c2ecf20Sopenharmony_ci struct phy *phy; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci gtr_phy->lane = port; 9538c2ecf20Sopenharmony_ci gtr_phy->dev = gtr_dev; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci phy = devm_phy_create(&pdev->dev, np, &xpsgtr_phyops); 9568c2ecf20Sopenharmony_ci if (IS_ERR(phy)) { 9578c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to create PHY\n"); 9588c2ecf20Sopenharmony_ci return PTR_ERR(phy); 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci gtr_phy->phy = phy; 9628c2ecf20Sopenharmony_ci phy_set_drvdata(phy, gtr_phy); 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci /* Register the PHY provider. */ 9668c2ecf20Sopenharmony_ci provider = devm_of_phy_provider_register(&pdev->dev, xpsgtr_xlate); 9678c2ecf20Sopenharmony_ci if (IS_ERR(provider)) { 9688c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "registering provider failed\n"); 9698c2ecf20Sopenharmony_ci return PTR_ERR(provider); 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci return 0; 9728c2ecf20Sopenharmony_ci} 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic const struct of_device_id xpsgtr_of_match[] = { 9758c2ecf20Sopenharmony_ci { .compatible = "xlnx,zynqmp-psgtr", }, 9768c2ecf20Sopenharmony_ci { .compatible = "xlnx,zynqmp-psgtr-v1.1", }, 9778c2ecf20Sopenharmony_ci {}, 9788c2ecf20Sopenharmony_ci}; 9798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, xpsgtr_of_match); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_cistatic struct platform_driver xpsgtr_driver = { 9828c2ecf20Sopenharmony_ci .probe = xpsgtr_probe, 9838c2ecf20Sopenharmony_ci .driver = { 9848c2ecf20Sopenharmony_ci .name = "xilinx-psgtr", 9858c2ecf20Sopenharmony_ci .of_match_table = xpsgtr_of_match, 9868c2ecf20Sopenharmony_ci .pm = &xpsgtr_pm_ops, 9878c2ecf20Sopenharmony_ci }, 9888c2ecf20Sopenharmony_ci}; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_cimodule_platform_driver(xpsgtr_driver); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ciMODULE_AUTHOR("Xilinx Inc."); 9938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 9948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xilinx ZynqMP High speed Gigabit Transceiver"); 995