Hi Anurag, On Wed, Sep 5, 2018 at 10:14 PM Anurag Kumar Vulisha <anurag.kumar.vulisha@xxxxxxxxxx> wrote: > > ZynqMP SoC has a Gigabit Transceiver with four lanes. All the high speed > peripherals such as USB, SATA, PCIE, Display Port and Ethernet SGMII can > rely on any of the four GT lanes for PHY layer. This patch adds driver > for that ZynqMP GT core. > > Signed-off-by: Anurag Kumar Vulisha <anurag.kumar.vulisha@xxxxxxxxxx> > --- > Changes in v3: > 1. Corrected the Documentation as suggested by Vivek Gautam > > Changes in v2: > 1. Fixed the compilation error when compiled phy-zynqmp.c as a module > 2. Added CONFIG_PM macro in phy-zynqmp.c driver > --- > drivers/phy/Kconfig | 8 + > drivers/phy/Makefile | 1 + > drivers/phy/phy-zynqmp.c | 1581 ++++++++++++++++++++++++++++++++++++++++ > include/dt-bindings/phy/phy.h | 2 + > include/linux/phy/phy-zynqmp.h | 52 ++ > 5 files changed, 1644 insertions(+) > create mode 100644 drivers/phy/phy-zynqmp.c > create mode 100644 include/linux/phy/phy-zynqmp.h > > diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig > index 5c8d452..14cf3330 100644 > --- a/drivers/phy/Kconfig > +++ b/drivers/phy/Kconfig > @@ -40,6 +40,14 @@ config PHY_XGENE > help > This option enables support for APM X-Gene SoC multi-purpose PHY. > > +config PHY_XILINX_ZYNQMP > + tristate "Xilinx ZynqMP PHY driver" > + depends on ARCH_ZYNQMP > + select GENERIC_PHY > + help > + Enable this to support ZynqMP High Speed Gigabit Transceiver > + that is part of ZynqMP SoC. > + > source "drivers/phy/allwinner/Kconfig" > source "drivers/phy/amlogic/Kconfig" > source "drivers/phy/broadcom/Kconfig" > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile > index 84e3bd9..f2a8d27 100644 > --- a/drivers/phy/Makefile > +++ b/drivers/phy/Makefile > @@ -7,6 +7,7 @@ obj-$(CONFIG_GENERIC_PHY) += phy-core.o > obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o > obj-$(CONFIG_PHY_XGENE) += phy-xgene.o > obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o > +obj-$(CONFIG_PHY_XILINX_ZYNQMP) += phy-zynqmp.o > obj-$(CONFIG_ARCH_SUNXI) += allwinner/ > obj-$(CONFIG_ARCH_MESON) += amlogic/ > obj-$(CONFIG_LANTIQ) += lantiq/ > diff --git a/drivers/phy/phy-zynqmp.c b/drivers/phy/phy-zynqmp.c > new file mode 100644 > index 0000000..84efc3d > --- /dev/null > +++ b/drivers/phy/phy-zynqmp.c > @@ -0,0 +1,1581 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * phy-zynqmp.c - PHY driver for Xilinx ZynqMP GT. > + * > + * Copyright (C) 2018 Xilinx Inc. > + * > + * Author: Anurag Kumar Vulisha <anuragku@xxxxxxxxxx> This driver was initially written by me and sent for review till v2: https://lore.kernel.org/patchwork/patch/635317/ I remember there was no support for reset that time and could not test DP. I guess you should add my name too. Subbaraya Sundeep <sundeep.lkml@xxxxxxxxx>. Thanks, Sundeep > + * > + * This driver is tested for USB, SATA and Display Port currently. > + * Other controllers PCIe and SGMII should also work but that is > + * experimental as of now. > + */ > + > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/io.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/of_platform.h> > +#include <linux/of_address.h> > +#include <linux/phy/phy.h> > +#include <linux/phy/phy-zynqmp.h> > +#include <linux/platform_device.h> > +#include <linux/delay.h> > +#include <dt-bindings/phy/phy.h> > +#include <linux/reset.h> > +#include <linux/list.h> > +#include <linux/slab.h> > + > +/* Inter Connect Matrix parameters */ > +#define ICM_CFG0 0x10010 > +#define ICM_CFG1 0x10014 > +#define ICM_CFG0_L0_MASK 0x07 > +#define ICM_CFG0_L1_MASK 0x70 > +#define ICM_CFG1_L2_MASK 0x07 > +#define ICM_CFG2_L3_MASK 0x70 > +#define ICM_CFG_SHIFT 4 > + > +/* Inter Connect Matrix allowed protocols */ > +#define ICM_PROTOCOL_PD 0x0 > +#define ICM_PROTOCOL_PCIE 0x1 > +#define ICM_PROTOCOL_SATA 0x2 > +#define ICM_PROTOCOL_USB 0x3 > +#define ICM_PROTOCOL_DP 0x4 > +#define ICM_PROTOCOL_SGMII 0x5 > + > +/* Test Mode common reset control parameters */ > +#define TM_CMN_RST 0x10018 > +#define TM_CMN_RST_EN 0x1 > +#define TM_CMN_RST_SET 0x2 > +#define TM_CMN_RST_MASK 0x3 > + > +/* Refclk selection parameters */ > +#define PLL_REF_SEL0 0x10000 > +#define PLL_REF_OFFSET 0x4 > +#define PLL_FREQ_MASK 0x1F > +#define PLL_STATUS_READ_OFFSET 0x4000 > +#define PLL_STATUS_LOCKED 0x10 > + > +/* PLL SSC step size offsets */ > +#define L0_L0_REF_CLK_SEL 0x2860 > +#define L0_PLL_SS_STEPS_0_LSB 0x2368 > +#define L0_PLL_SS_STEPS_1_MSB 0x236C > +#define L0_PLL_SS_STEP_SIZE_0_LSB 0x2370 > +#define L0_PLL_SS_STEP_SIZE_1 0x2374 > +#define L0_PLL_SS_STEP_SIZE_2 0x2378 > +#define L0_PLL_SS_STEP_SIZE_3_MSB 0x237C > +#define L0_PLL_STATUS_READ_1 0x23E4 > + > +/* SSC step size parameters */ > +#define STEP_SIZE_OFFSET 0x4000 > +#define STEP_SIZE_0_MASK 0xFF > +#define STEP_SIZE_1_MASK 0xFF > +#define STEP_SIZE_2_MASK 0xFF > +#define STEP_SIZE_3_MASK 0x3 > +#define STEP_SIZE_SHIFT 8 > +#define FORCE_STEP_SIZE 0x10 > +#define FORCE_STEPS 0x20 > +#define STEPS_OFFSET 0x4000 > +#define STEPS_0_MASK 0xFF > +#define STEPS_1_MASK 0x07 > + > +/* BG calibration parameters */ > +#define BGCAL_REF_SEL 0x10028 > +#define BGCAL_REF_VALUE 0x0C > + > +/* Calibration digital logic parameters */ > +#define L3_TM_CALIB_DIG19 0xEC4C > +#define L3_CALIB_DONE_STATUS 0xEF14 > +#define L3_TM_CALIB_DIG18 0xEC48 > +#define L3_TM_CALIB_DIG19_NSW 0x07 > +#define L3_TM_CALIB_DIG18_NSW 0xE0 > +#define L3_TM_OVERRIDE_NSW_CODE 0x20 > +#define L3_CALIB_DONE 0x02 > +#define L3_NSW_SHIFT 5 > +#define L3_NSW_PIPE_SHIFT 4 > +#define L3_NSW_CALIB_SHIFT 3 > + > +/* DN Resistor calibration code parameters */ > +#define L0_TXPMA_ST_3 0x0B0C > +#define L0_DN_CALIB_CODE 0x3F > + > +/* PLL Test Mode register parameters */ > +#define L0_TM_PLL_DIG_37 0x2094 > +#define L0_TM_PLL_DIG_37_OFFSET 0x4000 > +#define L0_TM_COARSE_CODE_LIMIT 0x10 > + > +/* PCS control parameters */ > +#define L0_TM_DIG_6 0x106C > +#define L0_TX_DIG_61 0x00F4 > +#define L0_TM_DIG_6_OFFSET 0x4000 > +#define L0_TX_DIG_61_OFFSET 0x4000 > +#define L0_TM_DIS_DESCRAMBLE_DECODER 0x0F > +#define L0_TM_DISABLE_SCRAMBLE_ENCODER 0x0F > + > +/* TX De-emphasis parameters */ > +#define L0_TX_ANA_TM_18 0x0048 > +#define L0_TX_ANA_TM_118 0x01D8 > +#define L0_TX_ANA_TM_18_OFFSET 0x4000 > +#define L0_TX_ANA_TM_118_OFFSET 0x4000 > +#define L0_TX_ANA_TM_118_FORCE_17_0 BIT(0) > + > +/* PMA control parameters */ > +#define L0_TXPMD_TM_45 0x0CB4 > +#define L0_TXPMD_TM_48 0x0CC0 > +#define L0_TXPMD_TM_45_OFFSET 0x4000 > +#define L0_TXPMD_TM_48_OFFSET 0x4000 > +#define L0_TXPMD_TM_45_OVER_DP_MAIN BIT(0) > +#define L0_TXPMD_TM_45_ENABLE_DP_MAIN BIT(1) > +#define L0_TXPMD_TM_45_OVER_DP_POST1 BIT(2) > +#define L0_TXPMD_TM_45_ENABLE_DP_POST1 BIT(3) > +#define L0_TXPMD_TM_45_OVER_DP_POST2 BIT(4) > +#define L0_TXPMD_TM_45_ENABLE_DP_POST2 BIT(5) > + > +/* Bus width parameters */ > +#define TX_PROT_BUS_WIDTH 0x10040 > +#define RX_PROT_BUS_WIDTH 0x10044 > +#define PROT_BUS_WIDTH_10 0x0 > +#define PROT_BUS_WIDTH_20 0x1 > +#define PROT_BUS_WIDTH_40 0x2 > +#define PROT_BUS_WIDTH_SHIFT 2 > + > +/* Max number of GT lanes */ > +#define MAX_LANES 4 > + > +/* Max Allowed refclk frequencies */ > +#define MAX_REFCLK 13 > + > +/* Lane CLK sharing mask */ > +#define LANE_CLK_SHARE_MASK 0x8F > + > +/* SIOU SATA control register */ > +#define SATA_CONTROL_OFFSET 0x0100 > + > +/* Total number of controllers */ > +#define CONTROLLERS_PER_LANE 5 > + > +/* USB pipe control parameters */ > +#define PIPE_CLK_OFFSET 0x7c > +#define PIPE_POWER_OFFSET 0x80 > +#define PIPE_CLK_ON 1 > +#define PIPE_CLK_OFF 0 > +#define PIPE_POWER_ON 1 > +#define PIPE_POWER_OFF 0 > + > +/* Protocol Type Pparameters */ > +#define XPSGTR_TYPE_USB0 0 /* USB controller 0 */ > +#define XPSGTR_TYPE_USB1 1 /* USB controller 1 */ > +#define XPSGTR_TYPE_SATA_0 2 /* SATA controller lane 0 */ > +#define XPSGTR_TYPE_SATA_1 3 /* SATA controller lane 1 */ > +#define XPSGTR_TYPE_PCIE_0 4 /* PCIe controller lane 0 */ > +#define XPSGTR_TYPE_PCIE_1 5 /* PCIe controller lane 1 */ > +#define XPSGTR_TYPE_PCIE_2 6 /* PCIe controller lane 2 */ > +#define XPSGTR_TYPE_PCIE_3 7 /* PCIe controller lane 3 */ > +#define XPSGTR_TYPE_DP_0 8 /* Display Port controller lane 0 */ > +#define XPSGTR_TYPE_DP_1 9 /* Display Port controller lane 1 */ > +#define XPSGTR_TYPE_SGMII0 10 /* Ethernet SGMII controller 0 */ > +#define XPSGTR_TYPE_SGMII1 11 /* Ethernet SGMII controller 1 */ > +#define XPSGTR_TYPE_SGMII2 12 /* Ethernet SGMII controller 2 */ > +#define XPSGTR_TYPE_SGMII3 13 /* Ethernet SGMII controller 3 */ > + > +/* Timeout values */ > +#define RST_TIMEOUT_MS 1000 > +#define TIMEOUT_US 1000 > + > +/* > + * This table holds the valid combinations of controllers and > + * lanes(Interconnect Matrix). > + */ > +static unsigned int icm_matrix[MAX_LANES][CONTROLLERS_PER_LANE] = { > + { XPSGTR_TYPE_PCIE_0, XPSGTR_TYPE_SATA_0, XPSGTR_TYPE_USB0, > + XPSGTR_TYPE_DP_1, XPSGTR_TYPE_SGMII0 }, > + { XPSGTR_TYPE_PCIE_1, XPSGTR_TYPE_SATA_1, XPSGTR_TYPE_USB0, > + XPSGTR_TYPE_DP_0, XPSGTR_TYPE_SGMII1 }, > + { XPSGTR_TYPE_PCIE_2, XPSGTR_TYPE_SATA_0, XPSGTR_TYPE_USB0, > + XPSGTR_TYPE_DP_1, XPSGTR_TYPE_SGMII2 }, > + { XPSGTR_TYPE_PCIE_3, XPSGTR_TYPE_SATA_1, XPSGTR_TYPE_USB1, > + XPSGTR_TYPE_DP_0, XPSGTR_TYPE_SGMII3 } > +}; > + > +/* Allowed PLL reference clock frequencies */ > +enum pll_frequencies { > + REF_19_2M = 0, > + REF_20M, > + REF_24M, > + REF_26M, > + REF_27M, > + REF_38_4M, > + REF_40M, > + REF_52M, > + REF_100M, > + REF_108M, > + REF_125M, > + REF_135M, > + REF_150M, > +}; > + > +/** > + * struct xpsgtr_phy - representation of a lane > + * @phy: pointer to the kernel PHY device > + * @type: controller which uses this lane > + * @lane: lane number > + * @protocol: protocol in which the lane operates > + * @ref_clk: enum of allowed ref clock rates for this lane PLL > + * @pll_lock: PLL status > + * @skip_phy_init: skip phy_init() if true > + * @data: pointer to hold private data > + * @refclk_rate: PLL reference clock frequency > + * @share_laneclk: lane number of the clock to be shared > + */ > +struct xpsgtr_phy { > + struct phy *phy; > + u8 type; > + u8 lane; > + u8 protocol; > + enum pll_frequencies ref_clk; > + bool pll_lock; > + bool skip_phy_init; > + void *data; > + u32 refclk_rate; > + u32 share_laneclk; > +}; > + > +/** > + * struct xpsgtr_ssc - structure to hold SSC settings for a lane > + * @refclk_rate: PLL reference clock frequency > + * @pll_ref_clk: value to be written to register for corresponding ref clk rate > + * @steps: number of steps of SSC (Spread Spectrum Clock) > + * @step_size: step size of each step > + */ > +struct xpsgtr_ssc { > + u32 refclk_rate; > + u8 pll_ref_clk; > + u32 steps; > + u32 step_size; > +}; > + > +/* lookup table to hold all settings needed for a ref clock frequency */ > +static struct xpsgtr_ssc ssc_lookup[MAX_REFCLK] = { > + {19200000, 0x05, 608, 264020}, > + {20000000, 0x06, 634, 243454}, > + {24000000, 0x07, 760, 168973}, > + {26000000, 0x08, 824, 143860}, > + {27000000, 0x09, 856, 86551}, > + {38400000, 0x0A, 1218, 65896}, > + {40000000, 0x0B, 634, 243454}, > + {52000000, 0x0C, 824, 143860}, > + {100000000, 0x0D, 1058, 87533}, > + {108000000, 0x0E, 856, 86551}, > + {125000000, 0x0F, 992, 119497}, > + {135000000, 0x10, 1070, 55393}, > + {150000000, 0x11, 792, 187091} > +}; > + > +/** > + * struct xpsgtr_dev - representation of a ZynMP GT device > + * @dev: pointer to device > + * @serdes: serdes base address > + * @siou: siou base address > + * @gtr_mutex: mutex for locking > + * @phys: pointer to all the lanes > + * @tx_term_fix: fix for GT issue > + * @saved_icm_cfg0: stored value of ICM CFG0 register > + * @saved_icm_cfg1: stored value of ICM CFG1 register > + * @sata_rst: a reset control for SATA > + * @dp_rst: a reset control for DP > + * @usb0_crst: a reset control for usb0 core > + * @usb1_crst: a reset control for usb1 core > + * @usb0_hibrst: a reset control for usb0 hibernation module > + * @usb1_hibrst: a reset control for usb1 hibernation module > + * @usb0_apbrst: a reset control for usb0 apb bus > + * @usb1_apbrst: a reset control for usb1 apb bus > + * @gem0_rst: a reset control for gem0 > + * @gem1_rst: a reset control for gem1 > + * @gem2_rst: a reset control for gem2 > + * @gem3_rst: a reset control for gem3 > + */ > +struct xpsgtr_dev { > + struct device *dev; > + void __iomem *serdes; > + void __iomem *siou; > + struct mutex gtr_mutex; /* mutex for locking */ > + struct xpsgtr_phy **phys; > + bool tx_term_fix; > + unsigned int saved_icm_cfg0; > + unsigned int saved_icm_cfg1; > + struct reset_control *sata_rst; > + struct reset_control *dp_rst; > + struct reset_control *usb0_crst; > + struct reset_control *usb1_crst; > + struct reset_control *usb0_hibrst; > + struct reset_control *usb1_hibrst; > + struct reset_control *usb0_apbrst; > + struct reset_control *usb1_apbrst; > + struct reset_control *gem0_rst; > + struct reset_control *gem1_rst; > + struct reset_control *gem2_rst; > + struct reset_control *gem3_rst; > +}; > + > +/** > + * xpsgtr_override_deemph - override PIPE TX de-emphasis > + * @phy: pointer to phy > + * @plvl: pre-emphasis level > + * @vlvl: voltage swing level > + * > + * Return: None > + */ > +void xpsgtr_override_deemph(struct phy *phy, u8 plvl, u8 vlvl) > +{ > + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + static u8 pe[4][4] = { { 0x2, 0x2, 0x2, 0x2 }, > + { 0x1, 0x1, 0x1, 0xFF }, > + { 0x0, 0x0, 0xFF, 0xFF }, > + { 0xFF, 0xFF, 0xFF, 0xFF } }; > + > + writel(pe[plvl][vlvl], > + gtr_dev->serdes + gtr_phy->lane * L0_TX_ANA_TM_18_OFFSET + > + L0_TX_ANA_TM_18); > +} > +EXPORT_SYMBOL_GPL(xpsgtr_override_deemph); > + > +/** > + * xpsgtr_margining_factor - adjust margining factor value > + * @phy: pointer to phy > + * @plvl: pre-emphasis level > + * @vlvl: voltage swing level > + * > + * Return: None > + */ > +void xpsgtr_margining_factor(struct phy *phy, u8 plvl, u8 vlvl) > +{ > + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + static u8 vs[4][4] = { { 0x2a, 0x27, 0x24, 0x20 }, > + { 0x27, 0x23, 0x20, 0xFF }, > + { 0x24, 0x20, 0xFF, 0xFF }, > + { 0xFF, 0xFF, 0xFF, 0xFF } }; > + > + writel(vs[plvl][vlvl], > + gtr_dev->serdes + gtr_phy->lane * L0_TXPMD_TM_48_OFFSET + > + L0_TXPMD_TM_48); > +} > +EXPORT_SYMBOL_GPL(xpsgtr_margining_factor); > + > +/** > + * xpsgtr_configure_pll - configures SSC settings for a lane > + * @gtr_phy: pointer to lane > + * > + * Return: None > + */ > +static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) > +{ > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + u32 reg; > + u32 offset; > + u32 steps; > + u32 size; > + u8 pll_ref_clk; > + > + steps = ssc_lookup[gtr_phy->ref_clk].steps; > + size = ssc_lookup[gtr_phy->ref_clk].step_size; > + pll_ref_clk = ssc_lookup[gtr_phy->ref_clk].pll_ref_clk; > + > + offset = gtr_phy->lane * PLL_REF_OFFSET + PLL_REF_SEL0; > + reg = readl(gtr_dev->serdes + offset); > + reg = (reg & ~PLL_FREQ_MASK) | pll_ref_clk; > + writel(reg, gtr_dev->serdes + offset); > + > + /* Enable lane clock sharing, if required */ > + if (gtr_phy->share_laneclk != gtr_phy->lane) { > + /* Lane3 Ref Clock Selection Register */ > + offset = gtr_phy->lane * PLL_REF_OFFSET + L0_L0_REF_CLK_SEL; > + reg = readl(gtr_dev->serdes + offset); > + reg = (reg & ~LANE_CLK_SHARE_MASK) | > + (1 << gtr_phy->share_laneclk); > + writel(reg, gtr_dev->serdes + offset); > + } > + > + /* SSC step size [7:0] */ > + offset = gtr_phy->lane * STEP_SIZE_OFFSET + L0_PLL_SS_STEP_SIZE_0_LSB; > + reg = readl(gtr_dev->serdes + offset); > + reg = (reg & ~STEP_SIZE_0_MASK) | > + (size & STEP_SIZE_0_MASK); > + writel(reg, gtr_dev->serdes + offset); > + > + /* SSC step size [15:8] */ > + size = size >> STEP_SIZE_SHIFT; > + offset = gtr_phy->lane * STEP_SIZE_OFFSET + L0_PLL_SS_STEP_SIZE_1; > + reg = readl(gtr_dev->serdes + offset); > + reg = (reg & ~STEP_SIZE_1_MASK) | > + (size & STEP_SIZE_1_MASK); > + writel(reg, gtr_dev->serdes + offset); > + > + /* SSC step size [23:16] */ > + size = size >> STEP_SIZE_SHIFT; > + offset = gtr_phy->lane * STEP_SIZE_OFFSET + L0_PLL_SS_STEP_SIZE_2; > + reg = readl(gtr_dev->serdes + offset); > + reg = (reg & ~STEP_SIZE_2_MASK) | > + (size & STEP_SIZE_2_MASK); > + writel(reg, gtr_dev->serdes + offset); > + > + /* SSC steps [7:0] */ > + offset = gtr_phy->lane * STEPS_OFFSET + L0_PLL_SS_STEPS_0_LSB; > + reg = readl(gtr_dev->serdes + offset); > + reg = (reg & ~STEPS_0_MASK) | > + (steps & STEPS_0_MASK); > + writel(reg, gtr_dev->serdes + offset); > + > + /* SSC steps [10:8] */ > + steps = steps >> STEP_SIZE_SHIFT; > + offset = gtr_phy->lane * STEPS_OFFSET + L0_PLL_SS_STEPS_1_MSB; > + reg = readl(gtr_dev->serdes + offset); > + reg = (reg & ~STEPS_1_MASK) | > + (steps & STEPS_1_MASK); > + writel(reg, gtr_dev->serdes + offset); > + > + /* SSC step size [24:25] */ > + size = size >> STEP_SIZE_SHIFT; > + offset = gtr_phy->lane * STEP_SIZE_OFFSET + L0_PLL_SS_STEP_SIZE_3_MSB; > + reg = readl(gtr_dev->serdes + offset); > + reg = (reg & ~STEP_SIZE_3_MASK) | > + (size & STEP_SIZE_3_MASK); > + reg |= FORCE_STEP_SIZE | FORCE_STEPS; > + writel(reg, gtr_dev->serdes + offset); > +} > + > +/** > + * xpsgtr_lane_setprotocol - sets required protocol in ICM registers > + * @gtr_phy: pointer to lane > + * > + * Return: None > + */ > +static void xpsgtr_lane_setprotocol(struct xpsgtr_phy *gtr_phy) > +{ > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + u32 reg; > + u8 protocol = gtr_phy->protocol; > + > + switch (gtr_phy->lane) { > + case 0: > + reg = readl(gtr_dev->serdes + ICM_CFG0); > + reg = (reg & ~ICM_CFG0_L0_MASK) | protocol; > + writel(reg, gtr_dev->serdes + ICM_CFG0); > + break; > + case 1: > + reg = readl(gtr_dev->serdes + ICM_CFG0); > + reg = (reg & ~ICM_CFG0_L1_MASK) | (protocol << ICM_CFG_SHIFT); > + writel(reg, gtr_dev->serdes + ICM_CFG0); > + break; > + case 2: > + reg = readl(gtr_dev->serdes + ICM_CFG1); > + reg = (reg & ~ICM_CFG0_L0_MASK) | protocol; > + writel(reg, gtr_dev->serdes + ICM_CFG1); > + break; > + case 3: > + reg = readl(gtr_dev->serdes + ICM_CFG1); > + reg = (reg & ~ICM_CFG0_L1_MASK) | (protocol << ICM_CFG_SHIFT); > + writel(reg, gtr_dev->serdes + ICM_CFG1); > + break; > + default: > + /* We already checked 0 <= lane <= 3 */ > + break; > + } > +} > + > +/** > + * xpsgtr_get_ssc - gets the required ssc settings based on clk rate > + * @gtr_phy: pointer to lane > + * > + * Return: 0 on success or error on failure > + */ > +static int xpsgtr_get_ssc(struct xpsgtr_phy *gtr_phy) > +{ > + u32 i; > + > + /* > + * Assign the required spread spectrum(SSC) settings > + * from lane refernce clk rate > + */ > + for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { > + if (gtr_phy->refclk_rate == ssc_lookup[i].refclk_rate) { > + gtr_phy->ref_clk = i; > + return 0; > + } > + } > + > + /* Did not get valid ssc settings*/ > + return -EINVAL; > +} > + > +/** > + * xpsgtr_configure_lane - configures SSC settings for a lane > + * @gtr_phy: pointer to lane > + * > + * Return: 0 on success or error on failure > + */ > +static int xpsgtr_configure_lane(struct xpsgtr_phy *gtr_phy) > +{ > + switch (gtr_phy->type) { > + case XPSGTR_TYPE_USB0: > + case XPSGTR_TYPE_USB1: > + gtr_phy->protocol = ICM_PROTOCOL_USB; > + break; > + case XPSGTR_TYPE_SATA_0: > + case XPSGTR_TYPE_SATA_1: > + gtr_phy->protocol = ICM_PROTOCOL_SATA; > + break; > + case XPSGTR_TYPE_DP_0: > + case XPSGTR_TYPE_DP_1: > + gtr_phy->protocol = ICM_PROTOCOL_DP; > + break; > + case XPSGTR_TYPE_PCIE_0: > + case XPSGTR_TYPE_PCIE_1: > + case XPSGTR_TYPE_PCIE_2: > + case XPSGTR_TYPE_PCIE_3: > + gtr_phy->protocol = ICM_PROTOCOL_PCIE; > + break; > + case XPSGTR_TYPE_SGMII0: > + case XPSGTR_TYPE_SGMII1: > + case XPSGTR_TYPE_SGMII2: > + case XPSGTR_TYPE_SGMII3: > + gtr_phy->protocol = ICM_PROTOCOL_SGMII; > + break; > + default: > + gtr_phy->protocol = ICM_PROTOCOL_PD; > + break; > + } > + > + /* Get SSC settinsg for refernce clk rate */ > + if (xpsgtr_get_ssc(gtr_phy) < 0) > + return -EINVAL; > + > + return 0; > +} > + > +/** > + * xpsgtr_config_usbpipe - configures the PIPE3 signals for USB > + * @gtr_phy: pointer to gtr phy device > + * > + * Return: None > + */ > +static void xpsgtr_config_usbpipe(struct xpsgtr_phy *gtr_phy) > +{ > + struct phy *phy = gtr_phy->phy; > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + void __iomem *regs = dev_get_platdata(&phy->dev); > + > + if (regs) { > + /* Set PIPE power present signal */ > + writel(PIPE_POWER_ON, regs + PIPE_POWER_OFFSET); > + > + /* Clear PIPE CLK signal */ > + writel(PIPE_CLK_OFF, regs + PIPE_CLK_OFFSET); > + } else { > + dev_info(gtr_dev->dev, > + "%s: No valid Platform_data found\n", __func__); > + } > +} > + > +/** > + * xpsgtr_reset_assert - asserts reset using reset framework > + * @rstc: pointer to reset_control > + * > + * Return: 0 on success or error on failure > + */ > +static int xpsgtr_reset_assert(struct reset_control *rstc) > +{ > + unsigned long loop_time = msecs_to_jiffies(RST_TIMEOUT_MS); > + unsigned long timeout; > + > + reset_control_assert(rstc); > + > + /* wait until reset is asserted or timeout */ > + timeout = jiffies + loop_time; > + > + while (!time_after_eq(jiffies, timeout)) { > + if (reset_control_status(rstc) > 0) > + return 0; > + > + cpu_relax(); > + } > + > + return -ETIMEDOUT; > +} > + > +/** > + * xpsgtr_reset_release - de-asserts reset using reset framework > + * @rstc: pointer to reset_control > + * > + * Return: 0 on success or error on failure > + */ > +static int xpsgtr_reset_release(struct reset_control *rstc) > +{ > + unsigned long loop_time = msecs_to_jiffies(RST_TIMEOUT_MS); > + unsigned long timeout; > + > + reset_control_deassert(rstc); > + > + /* wait until reset is de-asserted or timeout */ > + timeout = jiffies + loop_time; > + while (!time_after_eq(jiffies, timeout)) { > + if (!reset_control_status(rstc)) > + return 0; > + > + cpu_relax(); > + } > + > + return -ETIMEDOUT; > +} > + > +/** > + * xpsgtr_controller_reset - puts controller in reset > + * @gtr_phy: pointer to lane > + * > + * Return: 0 on success or error on failure > + */ > +static int xpsgtr_controller_reset(struct xpsgtr_phy *gtr_phy) > +{ > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + int ret; > + > + switch (gtr_phy->type) { > + case XPSGTR_TYPE_USB0: > + ret = xpsgtr_reset_assert(gtr_dev->usb0_crst); > + if (ret != 0) > + break; > + > + ret = xpsgtr_reset_assert(gtr_dev->usb0_hibrst); > + if (ret != 0) > + break; > + > + ret = xpsgtr_reset_assert(gtr_dev->usb0_apbrst); > + break; > + case XPSGTR_TYPE_USB1: > + ret = xpsgtr_reset_assert(gtr_dev->usb1_crst); > + if (ret != 0) > + break; > + > + ret = xpsgtr_reset_assert(gtr_dev->usb1_hibrst); > + if (ret != 0) > + break; > + > + ret = xpsgtr_reset_assert(gtr_dev->usb1_apbrst); > + break; > + case XPSGTR_TYPE_SATA_0: > + case XPSGTR_TYPE_SATA_1: > + ret = xpsgtr_reset_assert(gtr_dev->sata_rst); > + break; > + case XPSGTR_TYPE_DP_0: > + case XPSGTR_TYPE_DP_1: > + ret = xpsgtr_reset_assert(gtr_dev->dp_rst); > + break; > + case XPSGTR_TYPE_SGMII0: > + ret = xpsgtr_reset_assert(gtr_dev->gem0_rst); > + break; > + case XPSGTR_TYPE_SGMII1: > + ret = xpsgtr_reset_assert(gtr_dev->gem1_rst); > + break; > + case XPSGTR_TYPE_SGMII2: > + ret = xpsgtr_reset_assert(gtr_dev->gem2_rst); > + break; > + case XPSGTR_TYPE_SGMII3: > + ret = xpsgtr_reset_assert(gtr_dev->gem3_rst); > + break; > + default: > + ret = -EINVAL; > + break; > + } > + > + return ret; > +} > + > +/** > + * xpsgtr_controller_release_reset - releases controller from reset > + * @gtr_phy: pointer to lane > + * > + * Return: 0 on success or error on failure > + */ > +static int xpsgtr_controller_release_reset(struct xpsgtr_phy *gtr_phy) > +{ > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + int ret; > + > + switch (gtr_phy->type) { > + case XPSGTR_TYPE_USB0: > + ret = xpsgtr_reset_release(gtr_dev->usb0_apbrst); > + if (ret != 0) > + break; > + > + /* Config PIPE3 signals after releasing APB reset */ > + xpsgtr_config_usbpipe(gtr_phy); > + > + ret = xpsgtr_reset_release(gtr_dev->usb0_crst); > + if (ret != 0) > + break; > + > + ret = xpsgtr_reset_release(gtr_dev->usb0_hibrst); > + break; > + case XPSGTR_TYPE_USB1: > + ret = xpsgtr_reset_release(gtr_dev->usb1_apbrst); > + if (ret != 0) > + break; > + > + /* Config PIPE3 signals after releasing APB reset */ > + xpsgtr_config_usbpipe(gtr_phy); > + > + ret = xpsgtr_reset_release(gtr_dev->usb1_crst); > + if (ret != 0) > + break; > + > + ret = xpsgtr_reset_release(gtr_dev->usb1_hibrst); > + break; > + case XPSGTR_TYPE_SATA_0: > + case XPSGTR_TYPE_SATA_1: > + ret = xpsgtr_reset_release(gtr_dev->sata_rst); > + break; > + case XPSGTR_TYPE_DP_0: > + case XPSGTR_TYPE_DP_1: > + ret = xpsgtr_reset_release(gtr_dev->dp_rst); > + break; > + case XPSGTR_TYPE_SGMII0: > + ret = xpsgtr_reset_release(gtr_dev->gem0_rst); > + break; > + case XPSGTR_TYPE_SGMII1: > + ret = xpsgtr_reset_release(gtr_dev->gem1_rst); > + break; > + case XPSGTR_TYPE_SGMII2: > + ret = xpsgtr_reset_release(gtr_dev->gem2_rst); > + break; > + case XPSGTR_TYPE_SGMII3: > + ret = xpsgtr_reset_release(gtr_dev->gem3_rst); > + break; > + default: > + ret = -EINVAL; > + break; > + } > + > + return ret; > +} > + > +/** > + * xpsgtr_usb_rst_assert - assert USB core reset > + * @phy: pointer to phy > + * > + * Return: 0 on success or error on failure > + */ > +int xpsgtr_usb_crst_assert(struct phy *phy) > +{ > + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + int ret; > + > + switch (gtr_phy->type) { > + case XPSGTR_TYPE_USB0: > + ret = xpsgtr_reset_assert(gtr_dev->usb0_crst); > + break; > + case XPSGTR_TYPE_USB1: > + ret = xpsgtr_reset_assert(gtr_dev->usb1_crst); > + break; > + default: > + ret = -EINVAL; > + break; > + } > + > + return ret; > +} > +EXPORT_SYMBOL(xpsgtr_usb_crst_assert); > + > +/** > + * xpsgtr_usb_rst_release - release USB core reset > + * @phy: pointer to phy > + * > + * Return: 0 on success or error on failure > + */ > +int xpsgtr_usb_crst_release(struct phy *phy) > +{ > + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + int ret; > + > + switch (gtr_phy->type) { > + case XPSGTR_TYPE_USB0: > + ret = xpsgtr_reset_release(gtr_dev->usb0_crst); > + break; > + case XPSGTR_TYPE_USB1: > + ret = xpsgtr_reset_release(gtr_dev->usb1_crst); > + break; > + default: > + ret = -EINVAL; > + break; > + } > + > + return ret; > +} > +EXPORT_SYMBOL(xpsgtr_usb_crst_release); > + > +/** > + * xpsgtr_wait_pll_lock - Waits until PLL is locked or timedout > + * @phy: pointer to phy > + * > + * Return: 0 on success or error on failure > + */ > +int xpsgtr_wait_pll_lock(struct phy *phy) > +{ > + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + u32 offset, reg; > + u32 timeout = TIMEOUT_US; > + int ret = 0; > + > + /* Check pll is locked */ > + offset = gtr_phy->lane * PLL_STATUS_READ_OFFSET + L0_PLL_STATUS_READ_1; > + dev_dbg(gtr_dev->dev, "Waiting for PLL lock...\n"); > + > + do { > + reg = readl(gtr_dev->serdes + offset); > + if ((reg & PLL_STATUS_LOCKED) == PLL_STATUS_LOCKED) > + break; > + > + if (!--timeout) { > + dev_err(gtr_dev->dev, "PLL lock time out\n"); > + ret = -ETIMEDOUT; > + break; > + } > + > + udelay(1); > + } while (timeout > 0); > + > + if (ret == 0) > + gtr_phy->pll_lock = true; > + > + dev_info(gtr_dev->dev, "Lane:%d type:%d protocol:%d pll_locked:%s\n", > + gtr_phy->lane, gtr_phy->type, gtr_phy->protocol, > + gtr_phy->pll_lock ? "yes" : "no"); > + return ret; > +} > +EXPORT_SYMBOL_GPL(xpsgtr_wait_pll_lock); > + > +/** > + * xpsgtr_set_txwidth - This function sets the tx bus width of the lane > + * @gtr_phy: pointer to lane > + * @width: tx bus width size > + * > + * Return: None > + */ > +static void xpsgtr_set_txwidth(struct xpsgtr_phy *gtr_phy, u32 width) > +{ > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + > + writel(gtr_phy->lane * PROT_BUS_WIDTH_SHIFT >> width, > + gtr_dev->serdes + TX_PROT_BUS_WIDTH); > +} > + > +/** > + * xpsgtr_set_rxwidth - This function sets the rx bus width of the lane > + * @gtr_phy: pointer to lane > + * @width: rx bus width size > + * > + * Return: None > + */ > +static void xpsgtr_set_rxwidth(struct xpsgtr_phy *gtr_phy, u32 width) > +{ > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + > + writel(gtr_phy->lane * PROT_BUS_WIDTH_SHIFT >> width, > + gtr_dev->serdes + RX_PROT_BUS_WIDTH); > +} > + > +/** > + * xpsgtr_bypass_scramenc - This bypasses scrambler and 8b/10b encoder feature > + * @gtr_phy: pointer to lane > + * > + * Return: None > + */ > +static void xpsgtr_bypass_scramenc(struct xpsgtr_phy *gtr_phy) > +{ > + u32 offset; > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + > + /* bypass Scrambler and 8b/10b Encoder */ > + offset = gtr_phy->lane * L0_TX_DIG_61_OFFSET + L0_TX_DIG_61; > + writel(L0_TM_DISABLE_SCRAMBLE_ENCODER, gtr_dev->serdes + offset); > +} > + > +/** > + * xpsgtr_bypass_descramdec - bypasses descrambler and 8b/10b encoder feature > + * @gtr_phy: pointer to lane > + * > + * Return: None > + */ > +static void xpsgtr_bypass_descramdec(struct xpsgtr_phy *gtr_phy) > +{ > + u32 offset; > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + > + /* bypass Descrambler and 8b/10b decoder */ > + offset = gtr_phy->lane * L0_TM_DIG_6_OFFSET + L0_TM_DIG_6; > + writel(L0_TM_DIS_DESCRAMBLE_DECODER, gtr_dev->serdes + offset); > +} > + > +/** > + * xpsgtr_misc_sgmii - miscellaneous settings for SGMII > + * @gtr_phy: pointer to lane > + * > + * Return: None > + */ > +static void xpsgtr_misc_sgmii(struct xpsgtr_phy *gtr_phy) > +{ > + /* Set SGMII protocol tx bus width 10 bits */ > + xpsgtr_set_txwidth(gtr_phy, PROT_BUS_WIDTH_10); > + > + /* Set SGMII protocol rx bus width 10 bits */ > + xpsgtr_set_rxwidth(gtr_phy, PROT_BUS_WIDTH_10); > + > + /* bypass Descrambler and 8b/10b decoder */ > + xpsgtr_bypass_descramdec(gtr_phy); > + > + /* bypass Scrambler and 8b/10b Encoder */ > + xpsgtr_bypass_scramenc(gtr_phy); > +} > + > +/** > + * xpsgtr_misc_sata - miscellaneous settings for SATA > + * @gtr_phy: pointer to lane > + * > + * Return: None > + */ > +static void xpsgtr_misc_sata(struct xpsgtr_phy *gtr_phy) > +{ > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + > + /* bypass Descrambler and 8b/10b decoder */ > + xpsgtr_bypass_descramdec(gtr_phy); > + > + /* bypass Scrambler and 8b/10b Encoder */ > + xpsgtr_bypass_scramenc(gtr_phy); > + > + writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET); > +} > + > +/** > + * xpsgtr_phyinit_required - check if phy_init for the lane can be skipped > + * @gtr_phy: pointer to the phy lane > + * > + * Return: true if phy_init can be skipped or false > + */ > +static bool xpsgtr_phyinit_required(struct xpsgtr_phy *gtr_phy) > +{ > + /* > + * As USB may save the snapshot of the states during hibernation, doing > + * phy_init() will put the USB controller into reset, resulting in the > + * losing of the saved snapshot. So try to avoid phy_init() for USB > + * except when gtr_phy->skip_phy_init is false (this happens when FPD is > + * shutdown during suspend or when gt lane is changed from current one) > + */ > + if (gtr_phy->protocol == ICM_PROTOCOL_USB && gtr_phy->skip_phy_init) > + return true; > + else > + return false; > +} > + > +/** > + * xpsgtr_phy_init - initializes a lane > + * @phy: pointer to kernel PHY device > + * > + * Return: 0 on success or error on failure > + */ > +static int xpsgtr_phy_init(struct phy *phy) > +{ > + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); > + struct xpsgtr_dev *gtr_dev = gtr_phy->data; > + int ret = 0; > + u32 offset; > + u32 reg; > + u32 nsw; > + u32 timeout = TIMEOUT_US; > + > + mutex_lock(>r_dev->gtr_mutex); > + > + /* Check if phy_init() is required */ > + if (xpsgtr_phyinit_required(gtr_phy)) > + goto out; > + > + /* Put controller in reset */ > + ret = xpsgtr_controller_reset(gtr_phy); > + if (ret != 0) { > + dev_err(gtr_dev->dev, "Failed to assert reset\n"); > + goto out; > + } > + > + /* > + * There is a functional issue in the GT. The TX termination resistance > + * can be out of spec due to a issue in the calibration logic. Below is > + * the workaround to fix it. This below is required for XCZU9EG silicon. > + */ > + if (gtr_dev->tx_term_fix) { > + /* Enabling Test Mode control for CMN Rest */ > + reg = readl(gtr_dev->serdes + TM_CMN_RST); > + reg = (reg & ~TM_CMN_RST_MASK) | TM_CMN_RST_SET; > + writel(reg, gtr_dev->serdes + TM_CMN_RST); > + > + /* Set Test Mode reset */ > + reg = readl(gtr_dev->serdes + TM_CMN_RST); > + reg = (reg & ~TM_CMN_RST_MASK) | TM_CMN_RST_EN; > + writel(reg, gtr_dev->serdes + TM_CMN_RST); > + > + writel(0x00, gtr_dev->serdes + L3_TM_CALIB_DIG18); > + writel(L3_TM_OVERRIDE_NSW_CODE, gtr_dev->serdes + > + L3_TM_CALIB_DIG19); > + > + /* As a part of work around sequence for PMOS calibration fix, > + * we need to configure any lane ICM_CFG to valid protocol. This > + * will deassert the CMN_Resetn signal. > + */ > + xpsgtr_lane_setprotocol(gtr_phy); > + > + /* Clear Test Mode reset */ > + reg = readl(gtr_dev->serdes + TM_CMN_RST); > + reg = (reg & ~TM_CMN_RST_MASK) | TM_CMN_RST_SET; > + writel(reg, gtr_dev->serdes + TM_CMN_RST); > + > + dev_dbg(gtr_dev->dev, "calibrating...\n"); > + > + do { > + reg = readl(gtr_dev->serdes + L3_CALIB_DONE_STATUS); > + if ((reg & L3_CALIB_DONE) == L3_CALIB_DONE) > + break; > + > + if (!--timeout) { > + dev_err(gtr_dev->dev, "calibration time out\n"); > + ret = -ETIMEDOUT; > + goto out; > + } > + udelay(1); > + } while (timeout > 0); > + > + dev_dbg(gtr_dev->dev, "calibration done\n"); > + > + /* Reading NMOS Register Code */ > + nsw = readl(gtr_dev->serdes + L0_TXPMA_ST_3); > + > + /* Set Test Mode reset */ > + reg = readl(gtr_dev->serdes + TM_CMN_RST); > + reg = (reg & ~TM_CMN_RST_MASK) | TM_CMN_RST_EN; > + writel(reg, gtr_dev->serdes + TM_CMN_RST); > + > + nsw = nsw & L0_DN_CALIB_CODE; > + > + /* Writing NMOS register values back [5:3] */ > + reg = nsw >> L3_NSW_CALIB_SHIFT; > + writel(reg, gtr_dev->serdes + L3_TM_CALIB_DIG19); > + > + /* Writing NMOS register value [2:0] */ > + reg = ((nsw & L3_TM_CALIB_DIG19_NSW) << L3_NSW_SHIFT) | > + (1 << L3_NSW_PIPE_SHIFT); > + writel(reg, gtr_dev->serdes + L3_TM_CALIB_DIG18); > + > + /* Clear Test Mode reset */ > + reg = readl(gtr_dev->serdes + TM_CMN_RST); > + reg = (reg & ~TM_CMN_RST_MASK) | TM_CMN_RST_SET; > + writel(reg, gtr_dev->serdes + TM_CMN_RST); > + > + gtr_dev->tx_term_fix = false; > + } > + > + /* Enable coarse code saturation limiting logic */ > + offset = gtr_phy->lane * L0_TM_PLL_DIG_37_OFFSET + L0_TM_PLL_DIG_37; > + writel(L0_TM_COARSE_CODE_LIMIT, gtr_dev->serdes + offset); > + > + xpsgtr_configure_pll(gtr_phy); > + xpsgtr_lane_setprotocol(gtr_phy); > + > + if (gtr_phy->protocol == ICM_PROTOCOL_SATA) > + xpsgtr_misc_sata(gtr_phy); > + > + if (gtr_phy->protocol == ICM_PROTOCOL_SGMII) > + xpsgtr_misc_sgmii(gtr_phy); > + > + /* Bring controller out of reset */ > + ret = xpsgtr_controller_release_reset(gtr_phy); > + if (ret != 0) { > + dev_err(gtr_dev->dev, "Failed to release reset\n"); > + goto out; > + } > + > + /* Wait till pll is locked for all protocols except DP. For DP > + * pll locking function will be called from driver. > + */ > + if (gtr_phy->protocol != ICM_PROTOCOL_DP) { > + ret = xpsgtr_wait_pll_lock(phy); > + if (ret != 0) > + goto out; > + } else { > + offset = gtr_phy->lane * L0_TXPMD_TM_45_OFFSET + L0_TXPMD_TM_45; > + reg = L0_TXPMD_TM_45_OVER_DP_MAIN | > + L0_TXPMD_TM_45_ENABLE_DP_MAIN | > + L0_TXPMD_TM_45_OVER_DP_POST1 | > + L0_TXPMD_TM_45_OVER_DP_POST2 | > + L0_TXPMD_TM_45_ENABLE_DP_POST2; > + writel(reg, gtr_dev->serdes + offset); > + offset = gtr_phy->lane * L0_TX_ANA_TM_118_OFFSET + > + L0_TX_ANA_TM_118; > + writel(L0_TX_ANA_TM_118_FORCE_17_0, > + gtr_dev->serdes + offset); > + } > + > +out: > + mutex_unlock(>r_dev->gtr_mutex); > + return ret; > +} > + > +/** > + * xpsgtr_set_lanetype - derives lane type from dts arguments > + * @gtr_phy: pointer to lane > + * @controller: type of controller > + * @instance_num: instance number of the controller in case multilane controller > + * > + * Return: 0 on success or error on failure > + */ > +static int xpsgtr_set_lanetype(struct xpsgtr_phy *gtr_phy, u8 controller, > + u8 instance_num) > +{ > + switch (controller) { > + case PHY_TYPE_SATA: > + if (!instance_num) > + gtr_phy->type = XPSGTR_TYPE_SATA_0; > + else if (instance_num == 1) > + gtr_phy->type = XPSGTR_TYPE_SATA_1; > + else > + return -EINVAL; > + break; > + case PHY_TYPE_USB3: > + if (!instance_num) > + gtr_phy->type = XPSGTR_TYPE_USB0; > + else if (instance_num == 1) > + gtr_phy->type = XPSGTR_TYPE_USB1; > + else > + return -EINVAL; > + break; > + case PHY_TYPE_DP: > + if (!instance_num) > + gtr_phy->type = XPSGTR_TYPE_DP_0; > + else if (instance_num == 1) > + gtr_phy->type = XPSGTR_TYPE_DP_1; > + else > + return -EINVAL; > + break; > + case PHY_TYPE_PCIE: > + if (!instance_num) > + gtr_phy->type = XPSGTR_TYPE_PCIE_0; > + else if (instance_num == 1) > + gtr_phy->type = XPSGTR_TYPE_PCIE_1; > + else if (instance_num == 2) > + gtr_phy->type = XPSGTR_TYPE_PCIE_2; > + else if (instance_num == 3) > + gtr_phy->type = XPSGTR_TYPE_PCIE_3; > + else > + return -EINVAL; > + break; > + case PHY_TYPE_SGMII: > + if (!instance_num) > + gtr_phy->type = XPSGTR_TYPE_SGMII0; > + else if (instance_num == 1) > + gtr_phy->type = XPSGTR_TYPE_SGMII1; > + else if (instance_num == 2) > + gtr_phy->type = XPSGTR_TYPE_SGMII2; > + else if (instance_num == 3) > + gtr_phy->type = XPSGTR_TYPE_SGMII3; > + else > + return -EINVAL; > + break; > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > +/** > + * xpsgtr_xlate - provides a PHY specific to a controller > + * @dev: pointer to device > + * @args: arguments from dts > + * > + * Return: pointer to kernel PHY device or error on failure > + */ > +static struct phy *xpsgtr_xlate(struct device *dev, > + struct of_phandle_args *args) > +{ > + struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); > + struct xpsgtr_phy *gtr_phy = NULL; > + struct device_node *phynode = args->np; > + int index; > + int i; > + u8 controller; > + u8 instance_num; > + > + if (args->args_count != 4) { > + dev_err(dev, "Invalid number of cells in 'phy' property\n"); > + return ERR_PTR(-EINVAL); > + } > + if (!of_device_is_available(phynode)) { > + dev_warn(dev, "requested PHY is disabled\n"); > + return ERR_PTR(-ENODEV); > + } > + for (index = 0; index < of_get_child_count(dev->of_node); index++) { > + if (phynode == gtr_dev->phys[index]->phy->dev.of_node) { > + gtr_phy = gtr_dev->phys[index]; > + break; > + } > + } > + if (!gtr_phy) { > + dev_err(dev, "failed to find appropriate phy\n"); > + return ERR_PTR(-EINVAL); > + } > + > + /* get type of controller from phys */ > + controller = args->args[0]; > + > + /* get controller instance number */ > + instance_num = args->args[1]; > + > + /* Check if lane sharing is required */ > + gtr_phy->share_laneclk = args->args[2]; > + > + /* get the required clk rate for controller from phys */ > + gtr_phy->refclk_rate = args->args[3]; > + > + /* derive lane type */ > + if (xpsgtr_set_lanetype(gtr_phy, controller, instance_num) < 0) { > + dev_err(gtr_dev->dev, "Invalid lane type\n"); > + return ERR_PTR(-EINVAL); > + } > + > + /* configures SSC settings for a lane */ > + if (xpsgtr_configure_lane(gtr_phy) < 0) { > + dev_err(gtr_dev->dev, "Invalid clock rate: %d\n", > + gtr_phy->refclk_rate); > + return ERR_PTR(-EINVAL); > + } > + > + /* > + * Check Interconnect Matrix is obeyed i.e, given lane type > + * is allowed to operate on the lane. > + */ > + for (i = 0; i < CONTROLLERS_PER_LANE; i++) { > + if (icm_matrix[index][i] == gtr_phy->type) > + return gtr_phy->phy; > + } > + > + /* Should not reach here */ > + return ERR_PTR(-EINVAL); > +} > + > +/** > + * xpsgtr_phy_exit - clears previous initialized variables > + * @phy: pointer to kernel PHY device > + * > + * Return: 0 on success or error value on failure > + */ > +static int xpsgtr_phy_exit(struct phy *phy) > +{ > + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); > + > + if (!gtr_phy) > + return -EINVAL; > + > + /* As we are exiting, clear skip_phy_init flag */ > + gtr_phy->skip_phy_init = false; > + > + return 0; > +} > + > +static struct phy_ops xpsgtr_phyops = { > + .init = xpsgtr_phy_init, > + .exit = xpsgtr_phy_exit, > + .owner = THIS_MODULE, > +}; > + > +/* > + * xpsgtr_get_resets - Gets reset signals based on reset-names property > + * @gtr_dev: pointer to structure which stores reset information > + * > + * Return: 0 on success or error value on failure > + */ > +static int xpsgtr_get_resets(struct xpsgtr_dev *gtr_dev) > +{ > + char *name; > + struct reset_control *rst_temp; > + > + gtr_dev->sata_rst = devm_reset_control_get(gtr_dev->dev, "sata_rst"); > + if (IS_ERR(gtr_dev->sata_rst)) { > + name = "sata_rst"; > + rst_temp = gtr_dev->sata_rst; > + goto error; > + } > + > + gtr_dev->dp_rst = devm_reset_control_get(gtr_dev->dev, "dp_rst"); > + if (IS_ERR(gtr_dev->dp_rst)) { > + name = "dp_rst"; > + rst_temp = gtr_dev->dp_rst; > + goto error; > + } > + > + gtr_dev->usb0_crst = devm_reset_control_get(gtr_dev->dev, "usb0_crst"); > + if (IS_ERR(gtr_dev->usb0_crst)) { > + name = "usb0_crst"; > + rst_temp = gtr_dev->usb0_crst; > + goto error; > + } > + > + gtr_dev->usb1_crst = devm_reset_control_get(gtr_dev->dev, "usb1_crst"); > + if (IS_ERR(gtr_dev->usb1_crst)) { > + name = "usb1_crst"; > + rst_temp = gtr_dev->usb1_crst; > + goto error; > + } > + > + gtr_dev->usb0_hibrst = devm_reset_control_get(gtr_dev->dev, > + "usb0_hibrst"); > + if (IS_ERR(gtr_dev->usb0_hibrst)) { > + name = "usb0_hibrst"; > + rst_temp = gtr_dev->usb0_hibrst; > + goto error; > + } > + > + gtr_dev->usb1_hibrst = devm_reset_control_get(gtr_dev->dev, > + "usb1_hibrst"); > + if (IS_ERR(gtr_dev->usb1_hibrst)) { > + name = "usb1_hibrst"; > + rst_temp = gtr_dev->usb1_hibrst; > + goto error; > + } > + > + gtr_dev->usb0_apbrst = devm_reset_control_get(gtr_dev->dev, > + "usb0_apbrst"); > + if (IS_ERR(gtr_dev->usb0_apbrst)) { > + name = "usb0_apbrst"; > + rst_temp = gtr_dev->usb0_apbrst; > + goto error; > + } > + > + gtr_dev->usb1_apbrst = devm_reset_control_get(gtr_dev->dev, > + "usb1_apbrst"); > + if (IS_ERR(gtr_dev->usb1_apbrst)) { > + name = "usb1_apbrst"; > + rst_temp = gtr_dev->usb1_apbrst; > + goto error; > + } > + > + gtr_dev->gem0_rst = devm_reset_control_get(gtr_dev->dev, "gem0_rst"); > + if (IS_ERR(gtr_dev->gem0_rst)) { > + name = "gem0_rst"; > + rst_temp = gtr_dev->gem0_rst; > + goto error; > + } > + > + gtr_dev->gem1_rst = devm_reset_control_get(gtr_dev->dev, "gem1_rst"); > + if (IS_ERR(gtr_dev->gem1_rst)) { > + name = "gem1_rst"; > + rst_temp = gtr_dev->gem1_rst; > + goto error; > + } > + > + gtr_dev->gem2_rst = devm_reset_control_get(gtr_dev->dev, "gem2_rst"); > + if (IS_ERR(gtr_dev->gem2_rst)) { > + name = "gem2_rst"; > + rst_temp = gtr_dev->gem2_rst; > + goto error; > + } > + > + gtr_dev->gem3_rst = devm_reset_control_get(gtr_dev->dev, "gem3_rst"); > + if (IS_ERR(gtr_dev->gem3_rst)) { > + name = "gem3_rst"; > + rst_temp = gtr_dev->gem3_rst; > + goto error; > + } > + > + return 0; > +error: > + dev_err(gtr_dev->dev, "failed to get %s reset signal\n", name); > + return PTR_ERR(rst_temp); > +} > + > +/** > + * xpsgtr_probe - The device probe function for driver initialization. > + * @pdev: pointer to the platform device structure. > + * > + * Return: 0 for success and error value on failure > + */ > +static int xpsgtr_probe(struct platform_device *pdev) > +{ > + struct device_node *child, *np = pdev->dev.of_node; > + struct xpsgtr_dev *gtr_dev; > + struct phy_provider *provider; > + struct phy *phy; > + struct resource *res; > + int lanecount, port = 0, index = 0; > + int err; > + > + gtr_dev = devm_kzalloc(&pdev->dev, sizeof(*gtr_dev), GFP_KERNEL); > + if (!gtr_dev) > + return -ENOMEM; > + > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "serdes"); > + gtr_dev->serdes = devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(gtr_dev->serdes)) > + return PTR_ERR(gtr_dev->serdes); > + > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "siou"); > + gtr_dev->siou = devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(gtr_dev->siou)) > + return PTR_ERR(gtr_dev->siou); > + > + lanecount = of_get_child_count(np); > + if (lanecount > MAX_LANES || lanecount == 0) > + return -EINVAL; > + > + gtr_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * lanecount, > + GFP_KERNEL); > + if (!gtr_dev->phys) > + return -ENOMEM; > + > + gtr_dev->dev = &pdev->dev; > + platform_set_drvdata(pdev, gtr_dev); > + mutex_init(>r_dev->gtr_mutex); > + > + if (of_device_is_compatible(np, "xlnx,zynqmp-psgtr")) > + gtr_dev->tx_term_fix = > + of_property_read_bool(np, "xlnx,tx_termination_fix"); > + > + err = xpsgtr_get_resets(gtr_dev); > + if (err) { > + dev_err(&pdev->dev, "failed to get resets: %d\n", err); > + return err; > + } > + > + for_each_child_of_node(np, child) { > + struct xpsgtr_phy *gtr_phy; > + > + gtr_phy = devm_kzalloc(&pdev->dev, sizeof(*gtr_phy), > + GFP_KERNEL); > + if (!gtr_phy) > + return -ENOMEM; > + > + /* Assign lane number to gtr_phy instance */ > + gtr_phy->lane = index; > + > + /* Disable lane sharing as default */ > + gtr_phy->share_laneclk = -1; > + > + gtr_dev->phys[port] = gtr_phy; > + phy = devm_phy_create(&pdev->dev, child, &xpsgtr_phyops); > + if (IS_ERR(phy)) { > + dev_err(&pdev->dev, "failed to create PHY\n"); > + return PTR_ERR(phy); > + } > + gtr_dev->phys[port]->phy = phy; > + phy_set_drvdata(phy, gtr_dev->phys[port]); > + gtr_phy->data = gtr_dev; > + port++; > + index++; > + } > + provider = devm_of_phy_provider_register(&pdev->dev, xpsgtr_xlate); > + if (IS_ERR(provider)) { > + dev_err(&pdev->dev, "registering provider failed\n"); > + return PTR_ERR(provider); > + } > + return 0; > +} > + > +#ifdef CONFIG_PM > +/** > + * xpsgtr_suspend - The function for driver suspend. > + * @dev: pointer to the device structure. > + * > + * Return: 0 for success and error value on failure > + */ > +static int xpsgtr_suspend(struct device *dev) > +{ > + struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); > + > + if (!gtr_dev) > + return -EINVAL; > + > + /* Save the snapshot ICM_CFG registers */ > + gtr_dev->saved_icm_cfg0 = readl(gtr_dev->serdes + ICM_CFG0); > + gtr_dev->saved_icm_cfg1 = readl(gtr_dev->serdes + ICM_CFG1); > + > + return 0; > +} > + > +/** > + * xpsgtr_resume - The function for driver resume. > + * @dev: pointer to the device structure. > + * > + * Return: 0 for success and error value on failure > + */ > +static int xpsgtr_resume(struct device *dev) > +{ > + unsigned int icm_cfg0, icm_cfg1, index; > + bool skip_phy_init; > + struct xpsgtr_phy *gtr_phy; > + struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); > + > + if (!gtr_dev) > + return -EINVAL; > + > + icm_cfg0 = readl(gtr_dev->serdes + ICM_CFG0); > + icm_cfg1 = readl(gtr_dev->serdes + ICM_CFG1); > + > + /* Return if no gt lanes got configured before suspend */ > + if (!gtr_dev->saved_icm_cfg0 && !gtr_dev->saved_icm_cfg1) > + return 0; > + > + /* Check if the ICM configurations changed after suspend */ > + if (icm_cfg0 == gtr_dev->saved_icm_cfg0 && > + icm_cfg1 == gtr_dev->saved_icm_cfg1) > + skip_phy_init = true; > + else > + skip_phy_init = false; > + > + /* This below updates the skip_phy_init for all gtr_phy instances */ > + for (index = 0; index < of_get_child_count(dev->of_node); index++) { > + gtr_phy = gtr_dev->phys[index]; > + gtr_phy->skip_phy_init = skip_phy_init; > + } > + > + return 0; > +} > +#endif /* CONFIG_PM */ > + > +/* device PM ops */ > +static const struct dev_pm_ops xpsgtr_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(xpsgtr_suspend, xpsgtr_resume) > +}; > + > +/* Match table for of_platform binding */ > +static const struct of_device_id xpsgtr_of_match[] = { > + { .compatible = "xlnx,zynqmp-psgtr", }, > + { .compatible = "xlnx,zynqmp-psgtr-v1.1", }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, xpsgtr_of_match); > + > +static struct platform_driver xpsgtr_driver = { > + .probe = xpsgtr_probe, > + .driver = { > + .name = "xilinx-psgtr", > + .of_match_table = xpsgtr_of_match, > + .pm = &xpsgtr_pm_ops, > + }, > +}; > + > +module_platform_driver(xpsgtr_driver); > + > +MODULE_AUTHOR("Xilinx Inc."); > +MODULE_LICENSE("GPL v2"); > +MODULE_DESCRIPTION("Xilinx ZynqMP High speed Gigabit Transceiver"); > diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h > index d16e875..09cc0a6 100644 > --- a/include/dt-bindings/phy/phy.h > +++ b/include/dt-bindings/phy/phy.h > @@ -16,5 +16,7 @@ > #define PHY_TYPE_USB2 3 > #define PHY_TYPE_USB3 4 > #define PHY_TYPE_UFS 5 > +#define PHY_TYPE_DP 6 > +#define PHY_TYPE_SGMII 7 > > #endif /* _DT_BINDINGS_PHY */ > diff --git a/include/linux/phy/phy-zynqmp.h b/include/linux/phy/phy-zynqmp.h > new file mode 100644 > index 0000000..8dfd73f > --- /dev/null > +++ b/include/linux/phy/phy-zynqmp.h > @@ -0,0 +1,52 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Xilinx ZynqMP PHY header > + * > + * Copyright (C) 2018 Xilinx, Inc. > + * > + * Author: Anurag Kumar Vulisha <anuragku@xxxxxxxxxx> > + * Author: Hyun Woo Kwon <hyunk@xxxxxxxxxx> > + * > + */ > + > +#ifndef _PHY_ZYNQMP_H_ > +#define _PHY_ZYNQMP_H_ > + > +#include <linux/phy/phy.h> > + > +#if IS_ENABLED(CONFIG_PHY_XILINX_ZYNQMP) > +void xpsgtr_override_deemph(struct phy *phy, u8 plvl, u8 vlvl); > +void xpsgtr_margining_factor(struct phy *phy, u8 plvl, u8 vlvl); > +int xpsgtr_wait_pll_lock(struct phy *phy); > +int xpsgtr_usb_crst_assert(struct phy *phy); > +int xpsgtr_usb_crst_release(struct phy *phy); > +#else > + > +static inline int xpsgtr_override_deemph(struct phy *base, u8 plvl, u8 vlvl) > +{ > + return -ENODEV; > +} > + > +static inline int xpsgtr_margining_factor(struct phy *base, u8 plvl, u8 vlvl) > +{ > + return -ENODEV; > +} > + > +extern inline int xpsgtr_wait_pll_lock(struct phy *phy) > +{ > + return -ENODEV; > +} > + > +extern inline int xpsgtr_usb_crst_assert(struct phy *phy) > +{ > + return -ENODEV; > +} > + > +extern inline int xpsgtr_usb_crst_release(struct phy *phy) > +{ > + return -ENODEV; > +} > + > +#endif > + > +#endif /* _PHY_ZYNQMP_H_ */ > -- > 2.1.1 >