Hi Sundeep, >-----Original Message----- >From: sundeep subbaraya [mailto:sundeep.l...@gmail.com] >Sent: Thursday, November 01, 2018 10:31 PM >To: Anurag Kumar Vulisha <anura...@xilinx.com> >Cc: kis...@ti.com; Michal Simek <mich...@xilinx.com>; robh...@kernel.org; Mark >Rutland <mark.rutl...@arm.com>; vivek.gau...@codeaurora.org; >v.anuragku...@gmail.com; linux-kernel@vger.kernel.org; linux-arm- >ker...@lists.infradead.org; devicet...@vger.kernel.org >Subject: Re: [PATCH v3 1/2] phy: zynqmp: Add phy driver for xilinx zynqmp phy >core > >Hi Anurag, > >On Wed, Sep 5, 2018 at 10:14 PM Anurag Kumar Vulisha ><anurag.kumar.vuli...@xilinx.com> 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.vuli...@xilinx.com> >> --- >> 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 <anura...@xilinx.com> > >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.l...@gmail.com>. > >Thanks, >Sundeep >
As I was not aware of the mailing list that you have earlier initiated, I started a new thread. I think by the time I started working on this driver you have left our company and I continued working on this internal driver. Since I was not aware of your present mail id and the mailing list that you have initiated , I haven't added your name. Sorry for that. Since I got your mail id now, I will add your name too into this. Thanks, Anurag >> + * >> + * 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 <anura...@xilinx.com> >> + * Author: Hyun Woo Kwon <hy...@xilinx.com> >> + * >> + */ >> + >> +#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 >>