// SPDX-License-Identifier: GPL-2.0 /* * phy-zynqmp.c - PHY driver for Xilinx ZynqMP GT. * * Copyright (C) 2018-2020 Xilinx Inc. * * Author: Anurag Kumar Vulisha <anuragku@xilinx.com> * Author: Subbaraya Sundeep <sundeep.lkml@gmail.com> * Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com> * * This driver is tested for USB, SGMII, SATA and Display Port currently. * PCIe should also work but that is experimental as of now.
*/
/** * 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;
};
/** * struct xpsgtr_phy - representation of a lane * @phy: pointer to the kernel PHY device * @instance: instance of the protocol type (such as the lane within a * protocol, or the USB/Ethernet controller) * @lane: lane number * @protocol: protocol in which the lane operates * @skip_phy_init: skip phy_init() if true * @dev: pointer to the xpsgtr_dev instance * @refclk: reference clock index
*/ struct xpsgtr_phy { struct phy *phy;
u8 instance;
u8 lane;
u8 protocol; bool skip_phy_init; struct xpsgtr_dev *dev; unsignedint refclk;
};
/** * 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: PHY lanes * @clk: reference clocks * @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 * @saved_regs: registers to be saved/restored during suspend/resume
*/ struct xpsgtr_dev { struct device *dev; void __iomem *serdes; void __iomem *siou; struct mutex gtr_mutex; /* mutex for locking */ struct xpsgtr_phy phys[NUM_LANES]; struct clk *clk[NUM_LANES]; bool tx_term_fix; unsignedint saved_icm_cfg0; unsignedint saved_icm_cfg1;
u32 *saved_regs;
};
/** * xpsgtr_save_lane_regs - Saves registers on suspend * @gtr_dev: pointer to phy controller context structure
*/ staticvoid xpsgtr_save_lane_regs(struct xpsgtr_dev *gtr_dev)
{ int i;
for (i = 0; i < ARRAY_SIZE(save_reg_address); i++)
gtr_dev->saved_regs[i] = xpsgtr_read(gtr_dev,
save_reg_address[i]);
}
/** * xpsgtr_restore_lane_regs - Restores registers on resume * @gtr_dev: pointer to phy controller context structure
*/ staticvoid xpsgtr_restore_lane_regs(struct xpsgtr_dev *gtr_dev)
{ int i;
for (i = 0; i < ARRAY_SIZE(save_reg_address); i++)
xpsgtr_write(gtr_dev, save_reg_address[i],
gtr_dev->saved_regs[i]);
}
/* * Hardware Configuration
*/
/* Wait for the PLL to lock (with a timeout). */ staticint xpsgtr_wait_pll_lock(struct phy *phy)
{ struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); struct xpsgtr_dev *gtr_dev = gtr_phy->dev; unsignedint timeout = TIMEOUT_US;
u8 protocol = gtr_phy->protocol; int ret;
dev_dbg(gtr_dev->dev, "Waiting for PLL lock\n");
/* * For DP and PCIe, only the instance 0 PLL is used. Switch to that phy * so we wait on the right PLL.
*/ if ((protocol == ICM_PROTOCOL_DP || protocol == ICM_PROTOCOL_PCIE) &&
gtr_phy->instance) { int i;
for (i = 0; i < NUM_LANES; i++) {
gtr_phy = >r_dev->phys[i];
if (gtr_phy->protocol == protocol && !gtr_phy->instance) goto got_phy;
}
return -EBUSY;
}
got_phy: while (1) {
u32 reg = xpsgtr_read_phy(gtr_phy, L0_PLL_STATUS_READ_1);
if ((reg & PLL_STATUS_LOCKED) == PLL_STATUS_LOCKED) {
ret = 0; break;
}
staticbool xpsgtr_phy_init_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) returnfalse; else returntrue;
}
/* * 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. This is the workaround * to fix it, required for XCZU9EG silicon.
*/ staticint xpsgtr_phy_tx_term_fix(struct xpsgtr_phy *gtr_phy)
{ struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
u32 timeout = TIMEOUT_US;
u32 nsw;
/* Enabling Test Mode control for CMN Rest */
xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET);
/* Set Test Mode reset */
xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_EN);
/* * 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_set_protocol(gtr_phy);
/* Clear Test Mode reset */
xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET);
dev_dbg(gtr_dev->dev, "calibrating...\n");
do {
u32 reg = xpsgtr_read(gtr_dev, L3_CALIB_DONE_STATUS);
if ((reg & L3_CALIB_DONE) == L3_CALIB_DONE) break;
if (!--timeout) {
dev_err(gtr_dev->dev, "calibration time out\n"); return -ETIMEDOUT;
}
/* Set the lane protocol and instance based on the PHY type and instance number. */ staticint xpsgtr_set_lane_type(struct xpsgtr_phy *gtr_phy, u8 phy_type, unsignedint phy_instance)
{ unsignedint num_phy_types;
if (phy_instance >= num_phy_types) return -EINVAL;
gtr_phy->instance = phy_instance; return 0;
}
/* * Valid combinations of controllers and lanes (Interconnect Matrix). Each * "instance" represents one controller for a lane. For PCIe and DP, the * "instance" is the logical lane in the link. For SATA, USB, and SGMII, * the instance is the index of the controller. * * This information is only used to validate the devicetree reference, and is * not used when programming the hardware.
*/ staticconstunsignedint icm_matrix[NUM_LANES][CONTROLLERS_PER_LANE] = { /* PCIe, SATA, USB, DP, SGMII */
{ 0, 0, 0, 1, 0 }, /* Lane 0 */
{ 1, 1, 0, 0, 1 }, /* Lane 1 */
{ 2, 0, 0, 1, 2 }, /* Lane 2 */
{ 3, 1, 1, 0, 3 }, /* Lane 3 */
};
/* Translate OF phandle and args to PHY instance. */ staticstruct phy *xpsgtr_xlate(struct device *dev, conststruct of_phandle_args *args)
{ struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); struct xpsgtr_phy *gtr_phy; unsignedint phy_instance; unsignedint phy_lane; unsignedint phy_type; unsignedint refclk; unsignedint i; int ret;
if (args->args_count != 4) {
dev_err(dev, "Invalid number of cells in 'phy' property\n"); return ERR_PTR(-EINVAL);
}
/* * Get the PHY parameters from the OF arguments and derive the lane * type.
*/
phy_lane = args->args[0]; if (phy_lane >= ARRAY_SIZE(gtr_dev->phys)) {
dev_err(dev, "Invalid lane number %u\n", phy_lane); return ERR_PTR(-ENODEV);
}
guard(mutex)(>r_phy->phy->mutex);
ret = xpsgtr_set_lane_type(gtr_phy, phy_type, phy_instance); if (ret < 0) {
dev_err(gtr_dev->dev, "Invalid PHY type and/or instance\n"); return ERR_PTR(ret);
}
refclk = args->args[3]; if (refclk >= ARRAY_SIZE(gtr_dev->clk)) {
dev_err(dev, "Invalid reference clock number %u\n", refclk); return ERR_PTR(-EINVAL);
}
gtr_phy->refclk = refclk;
/* * Ensure that the Interconnect Matrix is obeyed, i.e a given lane type * is allowed to operate on the lane.
*/ for (i = 0; i < CONTROLLERS_PER_LANE; i++) { if (icm_matrix[phy_lane][i] == gtr_phy->instance) return gtr_phy->phy;
}
/* 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;
/* Update the skip_phy_init for all gtr_phy instances. */ for (i = 0; i < ARRAY_SIZE(gtr_dev->phys); i++)
gtr_dev->phys[i].skip_phy_init = skip_phy_init;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.