diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-17 11:40:04 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-17 11:40:04 -0800 |
| commit | 9b725d5959a1d9bb670ccdb23add45f0eaafefb5 (patch) | |
| tree | e55a94fe0836bcebb1528d2c5bb8a9360a2b5edc /drivers/phy/freescale | |
| parent | 4e15e819710e3518ec28735a12e0f45b7550290d (diff) | |
| parent | dbeea86fecef7cf2b93aded4525d74f6277376ef (diff) | |
| download | linux-next-9b725d5959a1d9bb670ccdb23add45f0eaafefb5.tar.gz linux-next-9b725d5959a1d9bb670ccdb23add45f0eaafefb5.zip | |
Merge tag 'phy-for-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy
Pull phy updates from Vinod Koul:
"Core:
- Add suuport for "rx-polarity" and "tx-polarity" device tree
properties and phy common properties to manage this
New Support:
- Qualcomm Glymur PCIe Gen4 2-lanes PCIe phy, DP and edp phy, USB UNI
PHY and SMB2370 eUSB2 repeater. SC8280xp QMP UFS PHY, Kaanapali
PCIe phy and QMP PHY, QCS615 QMP USB3+DP PHY and driver support for
that.
- SpacemiT PCIe/combo PHY and K1 USB2 PHY driver.
- HDMI 2.1 FRL configuration support and driver enabling for rockchip
samsung-hdptx driver
- TI TCAN1046 phy
- Renesas RZ/V2H(P) and RZ/V2N usb3
- Mediatek MT8188 hdmi-phy
- Google Tensor SoC USB PHY driver
- Apple Type-C PHY
Updates:
- Subsystem conversion for clock round_rate() to determine_rate()
- TI USB3 DT schema conversion
- Samsung ExynosAutov920 usb3, combo hsphy and ssphy support"
* tag 'phy-for-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy: (143 commits)
phy: ti: phy-j721e-wiz: convert from divider_round_rate() to divider_determine_rate()
dt-bindings: phy: ti,control-phy-otghs: convert to DT schema
dt-bindings: phy: ti,phy-usb3: convert to DT schema
phy: tegra: xusb: Remove unused powered_on variable
phy: renesas: rcar-gen3-usb2: add regulator dependency
phy: GOOGLE_USB: add TYPEC dependency
phy: enter drivers/phy/Makefile even without CONFIG_GENERIC_PHY
phy: renesas: rcar-gen3-usb2: Use mux-state for phyrst management
phy: renesas: rcar-gen3-usb2: Add regulator for OTG VBUS control
phy: renesas: rcar-gen3-usb2: Use devm_pm_runtime_enable()
phy: renesas: rcar-gen3-usb2: Factor out VBUS control logic
dt-bindings: phy: renesas,usb2-phy: Document RZ/G3E SoC
dt-bindings: phy: renesas,usb2-phy: Document mux-states property
dt-bindings: phy: renesas,usb2-phy: Document USB VBUS regulator
phy: rockchip: samsung-hdptx: Add HDMI 2.1 FRL support
phy: rockchip: samsung-hdptx: Extend rk_hdptx_phy_verify_hdmi_config() helper
phy: rockchip: samsung-hdptx: Switch to driver specific HDMI config
phy: rockchip: samsung-hdptx: Drop hw_rate driver data
phy: rockchip: samsung-hdptx: Compute clk rate from PLL config
phy: rockchip: samsung-hdptx: Cleanup *_cmn_init_seq lists
...
Diffstat (limited to 'drivers/phy/freescale')
| -rw-r--r-- | drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 25 | ||||
| -rw-r--r-- | drivers/phy/freescale/phy-fsl-imx8qm-hsio.c | 2 | ||||
| -rw-r--r-- | drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c | 6 | ||||
| -rw-r--r-- | drivers/phy/freescale/phy-fsl-lynx-28g.c | 1156 | ||||
| -rw-r--r-- | drivers/phy/freescale/phy-fsl-samsung-hdmi.c | 13 |
5 files changed, 910 insertions, 292 deletions
diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c index 91b3e62743d3..b05d80e849a1 100644 --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c @@ -17,6 +17,10 @@ #define PHY_CTRL0_FSEL_MASK GENMASK(10, 5) #define PHY_CTRL0_FSEL_24M 0x2a #define PHY_CTRL0_FSEL_100M 0x27 +#define PHY_CTRL0_SSC_RANGE_MASK GENMASK(23, 21) +#define PHY_CTRL0_SSC_RANGE_4003PPM 0x2 +#define PHY_CTRL0_SSC_RANGE_4492PPM 0x1 +#define PHY_CTRL0_SSC_RANGE_4980PPM 0x0 #define PHY_CTRL1 0x4 #define PHY_CTRL1_RESET BIT(0) @@ -47,6 +51,7 @@ #define PHY_CTRL5_PCS_TX_SWING_FULL_MASK GENMASK(6, 0) #define PHY_CTRL6 0x18 +#define PHY_CTRL6_RXTERM_OVERRIDE_SEL BIT(29) #define PHY_CTRL6_ALT_CLK_EN BIT(1) #define PHY_CTRL6_ALT_CLK_SEL BIT(0) @@ -587,6 +592,9 @@ static int imx8mp_usb_phy_init(struct phy *phy) value = readl(imx_phy->base + PHY_CTRL0); value |= PHY_CTRL0_REF_SSP_EN; + value &= ~PHY_CTRL0_SSC_RANGE_MASK; + value |= FIELD_PREP(PHY_CTRL0_SSC_RANGE_MASK, + PHY_CTRL0_SSC_RANGE_4003PPM); writel(value, imx_phy->base + PHY_CTRL0); value = readl(imx_phy->base + PHY_CTRL2); @@ -610,6 +618,7 @@ static int imx8mp_usb_phy_init(struct phy *phy) static int imx8mq_phy_power_on(struct phy *phy) { struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); + u32 value; int ret; ret = regulator_enable(imx_phy->vbus); @@ -626,12 +635,23 @@ static int imx8mq_phy_power_on(struct phy *phy) return ret; } - return ret; + /* Disable rx term override */ + value = readl(imx_phy->base + PHY_CTRL6); + value &= ~PHY_CTRL6_RXTERM_OVERRIDE_SEL; + writel(value, imx_phy->base + PHY_CTRL6); + + return 0; } static int imx8mq_phy_power_off(struct phy *phy) { struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); + u32 value; + + /* Override rx term to be 0 */ + value = readl(imx_phy->base + PHY_CTRL6); + value |= PHY_CTRL6_RXTERM_OVERRIDE_SEL; + writel(value, imx_phy->base + PHY_CTRL6); clk_disable_unprepare(imx_phy->alt_clk); clk_disable_unprepare(imx_phy->clk); @@ -676,6 +696,8 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) if (!imx_phy) return -ENOMEM; + platform_set_drvdata(pdev, imx_phy); + imx_phy->clk = devm_clk_get(dev, "phy"); if (IS_ERR(imx_phy->clk)) { dev_err(dev, "failed to get imx8mq usb phy clock\n"); @@ -730,6 +752,7 @@ static struct platform_driver imx8mq_usb_phy_driver = { .driver = { .name = "imx8mq-usb-phy", .of_match_table = imx8mq_usb_phy_of_match, + .suppress_bind_attrs = true, } }; module_platform_driver(imx8mq_usb_phy_driver); diff --git a/drivers/phy/freescale/phy-fsl-imx8qm-hsio.c b/drivers/phy/freescale/phy-fsl-imx8qm-hsio.c index 977d21d753a5..279b8ac7822d 100644 --- a/drivers/phy/freescale/phy-fsl-imx8qm-hsio.c +++ b/drivers/phy/freescale/phy-fsl-imx8qm-hsio.c @@ -251,7 +251,7 @@ static void imx_hsio_configure_clk_pad(struct phy *phy) struct imx_hsio_lane *lane = phy_get_drvdata(phy); struct imx_hsio_priv *priv = lane->priv; - if (strncmp(priv->refclk_pad, "output", 6) == 0) { + if (priv->refclk_pad && strncmp(priv->refclk_pad, "output", 6) == 0) { pll = true; regmap_update_bits(priv->misc, HSIO_CTRL0, HSIO_IOB_A_0_TXOE | HSIO_IOB_A_0_M1M0_MASK, diff --git a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c index 7aef2f59e8eb..ece357443521 100644 --- a/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c +++ b/drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c @@ -286,11 +286,9 @@ static int mixel_lvds_phy_reset(struct device *dev) regmap_write(priv->regmap, PHY_CTRL, CTRL_RESET_VAL); - ret = pm_runtime_put(dev); - if (ret < 0) - dev_err(dev, "failed to put PM runtime: %d\n", ret); + pm_runtime_put(dev); - return ret; + return 0; } static struct phy *mixel_lvds_phy_xlate(struct device *dev, diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index c20d2636c5e9..2b0fd95ba62f 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* Copyright (c) 2021-2022 NXP. */ +#include <linux/bitfield.h> #include <linux/module.h> #include <linux/of.h> #include <linux/phy.h> @@ -11,100 +12,408 @@ #define LYNX_28G_NUM_LANE 8 #define LYNX_28G_NUM_PLL 2 -/* General registers per SerDes block */ -#define LYNX_28G_PCC8 0x10a0 -#define LYNX_28G_PCC8_SGMII 0x1 -#define LYNX_28G_PCC8_SGMII_DIS 0x0 - -#define LYNX_28G_PCCC 0x10b0 -#define LYNX_28G_PCCC_10GBASER 0x9 -#define LYNX_28G_PCCC_USXGMII 0x1 -#define LYNX_28G_PCCC_SXGMII_DIS 0x0 - -#define LYNX_28G_LNa_PCC_OFFSET(lane) (4 * (LYNX_28G_NUM_LANE - (lane->id) - 1)) +/* SoC IP wrapper for protocol converters */ +#define PCC8 0x10a0 +#define PCC8_SGMIIa_KX BIT(3) +#define PCC8_SGMIIa_CFG BIT(0) + +#define PCCC 0x10b0 +#define PCCC_SXGMIIn_XFI BIT(3) +#define PCCC_SXGMIIn_CFG BIT(0) + +#define PCCD 0x10b4 +#define PCCD_E25Gn_CFG BIT(0) + +#define PCCE 0x10b8 +#define PCCE_E40Gn_LRV BIT(3) +#define PCCE_E40Gn_CFG BIT(0) +#define PCCE_E50Gn_LRV BIT(3) +#define PCCE_E50GnCFG BIT(0) +#define PCCE_E100Gn_LRV BIT(3) +#define PCCE_E100Gn_CFG BIT(0) + +#define SGMII_CFG(id) (28 - (id) * 4) /* Offset into PCC8 */ +#define SXGMII_CFG(id) (28 - (id) * 4) /* Offset into PCCC */ +#define E25G_CFG(id) (28 - (id) * 4) /* Offset into PCCD */ +#define E40G_CFG(id) (28 - (id) * 4) /* Offset into PCCE */ +#define E50G_CFG(id) (20 - (id) * 4) /* Offset into PCCE */ +#define E100G_CFG(id) (12 - (id) * 4) /* Offset into PCCE */ /* Per PLL registers */ -#define LYNX_28G_PLLnRSTCTL(pll) (0x400 + (pll) * 0x100 + 0x0) -#define LYNX_28G_PLLnRSTCTL_DIS(rstctl) (((rstctl) & BIT(24)) >> 24) -#define LYNX_28G_PLLnRSTCTL_LOCK(rstctl) (((rstctl) & BIT(23)) >> 23) - -#define LYNX_28G_PLLnCR0(pll) (0x400 + (pll) * 0x100 + 0x4) -#define LYNX_28G_PLLnCR0_REFCLK_SEL(cr0) (((cr0) & GENMASK(20, 16))) -#define LYNX_28G_PLLnCR0_REFCLK_SEL_100MHZ 0x0 -#define LYNX_28G_PLLnCR0_REFCLK_SEL_125MHZ 0x10000 -#define LYNX_28G_PLLnCR0_REFCLK_SEL_156MHZ 0x20000 -#define LYNX_28G_PLLnCR0_REFCLK_SEL_150MHZ 0x30000 -#define LYNX_28G_PLLnCR0_REFCLK_SEL_161MHZ 0x40000 - -#define LYNX_28G_PLLnCR1(pll) (0x400 + (pll) * 0x100 + 0x8) -#define LYNX_28G_PLLnCR1_FRATE_SEL(cr1) (((cr1) & GENMASK(28, 24))) -#define LYNX_28G_PLLnCR1_FRATE_5G_10GVCO 0x0 -#define LYNX_28G_PLLnCR1_FRATE_5G_25GVCO 0x10000000 -#define LYNX_28G_PLLnCR1_FRATE_10G_20GVCO 0x6000000 +#define PLLnRSTCTL(pll) (0x400 + (pll) * 0x100 + 0x0) +#define PLLnRSTCTL_DIS(rstctl) (((rstctl) & BIT(24)) >> 24) +#define PLLnRSTCTL_LOCK(rstctl) (((rstctl) & BIT(23)) >> 23) + +#define PLLnCR0(pll) (0x400 + (pll) * 0x100 + 0x4) +#define PLLnCR0_REFCLK_SEL GENMASK(20, 16) +#define PLLnCR0_REFCLK_SEL_100MHZ 0x0 +#define PLLnCR0_REFCLK_SEL_125MHZ 0x1 +#define PLLnCR0_REFCLK_SEL_156MHZ 0x2 +#define PLLnCR0_REFCLK_SEL_150MHZ 0x3 +#define PLLnCR0_REFCLK_SEL_161MHZ 0x4 + +#define PLLnCR1(pll) (0x400 + (pll) * 0x100 + 0x8) +#define PLLnCR1_FRATE_SEL GENMASK(28, 24) +#define PLLnCR1_FRATE_5G_10GVCO 0x0 +#define PLLnCR1_FRATE_5G_25GVCO 0x10 +#define PLLnCR1_FRATE_10G_20GVCO 0x6 /* Per SerDes lane registers */ /* Lane a General Control Register */ -#define LYNX_28G_LNaGCR0(lane) (0x800 + (lane) * 0x100 + 0x0) -#define LYNX_28G_LNaGCR0_PROTO_SEL_MSK GENMASK(7, 3) -#define LYNX_28G_LNaGCR0_PROTO_SEL_SGMII 0x8 -#define LYNX_28G_LNaGCR0_PROTO_SEL_XFI 0x50 -#define LYNX_28G_LNaGCR0_IF_WIDTH_MSK GENMASK(2, 0) -#define LYNX_28G_LNaGCR0_IF_WIDTH_10_BIT 0x0 -#define LYNX_28G_LNaGCR0_IF_WIDTH_20_BIT 0x2 +#define LNaGCR0(lane) (0x800 + (lane) * 0x100 + 0x0) +#define LNaGCR0_PROTO_SEL GENMASK(7, 3) +#define LNaGCR0_PROTO_SEL_SGMII 0x1 +#define LNaGCR0_PROTO_SEL_XFI 0xa +#define LNaGCR0_IF_WIDTH GENMASK(2, 0) +#define LNaGCR0_IF_WIDTH_10_BIT 0x0 +#define LNaGCR0_IF_WIDTH_20_BIT 0x2 /* Lane a Tx Reset Control Register */ -#define LYNX_28G_LNaTRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x20) -#define LYNX_28G_LNaTRSTCTL_HLT_REQ BIT(27) -#define LYNX_28G_LNaTRSTCTL_RST_DONE BIT(30) -#define LYNX_28G_LNaTRSTCTL_RST_REQ BIT(31) +#define LNaTRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x20) +#define LNaTRSTCTL_HLT_REQ BIT(27) +#define LNaTRSTCTL_RST_DONE BIT(30) +#define LNaTRSTCTL_RST_REQ BIT(31) /* Lane a Tx General Control Register */ -#define LYNX_28G_LNaTGCR0(lane) (0x800 + (lane) * 0x100 + 0x24) -#define LYNX_28G_LNaTGCR0_USE_PLLF 0x0 -#define LYNX_28G_LNaTGCR0_USE_PLLS BIT(28) -#define LYNX_28G_LNaTGCR0_USE_PLL_MSK BIT(28) -#define LYNX_28G_LNaTGCR0_N_RATE_FULL 0x0 -#define LYNX_28G_LNaTGCR0_N_RATE_HALF 0x1000000 -#define LYNX_28G_LNaTGCR0_N_RATE_QUARTER 0x2000000 -#define LYNX_28G_LNaTGCR0_N_RATE_MSK GENMASK(26, 24) - -#define LYNX_28G_LNaTECR0(lane) (0x800 + (lane) * 0x100 + 0x30) +#define LNaTGCR0(lane) (0x800 + (lane) * 0x100 + 0x24) +#define LNaTGCR0_USE_PLL BIT(28) +#define LNaTGCR0_USE_PLLF 0x0 +#define LNaTGCR0_USE_PLLS 0x1 +#define LNaTGCR0_N_RATE GENMASK(26, 24) +#define LNaTGCR0_N_RATE_FULL 0x0 +#define LNaTGCR0_N_RATE_HALF 0x1 +#define LNaTGCR0_N_RATE_QUARTER 0x2 + +#define LNaTECR0(lane) (0x800 + (lane) * 0x100 + 0x30) +#define LNaTECR0_EQ_TYPE GENMASK(30, 28) +#define LNaTECR0_EQ_SGN_PREQ BIT(23) +#define LNaTECR0_EQ_PREQ GENMASK(19, 16) +#define LNaTECR0_EQ_SGN_POST1Q BIT(15) +#define LNaTECR0_EQ_POST1Q GENMASK(12, 8) +#define LNaTECR0_EQ_AMP_RED GENMASK(5, 0) + +#define LNaTECR1(lane) (0x800 + (lane) * 0x100 + 0x34) +#define LNaTECR1_EQ_ADPT_EQ_DRVR_DIS BIT(31) +#define LNaTECR1_EQ_ADPT_EQ GENMASK(29, 24) /* Lane a Rx Reset Control Register */ -#define LYNX_28G_LNaRRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x40) -#define LYNX_28G_LNaRRSTCTL_HLT_REQ BIT(27) -#define LYNX_28G_LNaRRSTCTL_RST_DONE BIT(30) -#define LYNX_28G_LNaRRSTCTL_RST_REQ BIT(31) -#define LYNX_28G_LNaRRSTCTL_CDR_LOCK BIT(12) +#define LNaRRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x40) +#define LNaRRSTCTL_HLT_REQ BIT(27) +#define LNaRRSTCTL_RST_DONE BIT(30) +#define LNaRRSTCTL_RST_REQ BIT(31) +#define LNaRRSTCTL_CDR_LOCK BIT(12) /* Lane a Rx General Control Register */ -#define LYNX_28G_LNaRGCR0(lane) (0x800 + (lane) * 0x100 + 0x44) -#define LYNX_28G_LNaRGCR0_USE_PLLF 0x0 -#define LYNX_28G_LNaRGCR0_USE_PLLS BIT(28) -#define LYNX_28G_LNaRGCR0_USE_PLL_MSK BIT(28) -#define LYNX_28G_LNaRGCR0_N_RATE_MSK GENMASK(26, 24) -#define LYNX_28G_LNaRGCR0_N_RATE_FULL 0x0 -#define LYNX_28G_LNaRGCR0_N_RATE_HALF 0x1000000 -#define LYNX_28G_LNaRGCR0_N_RATE_QUARTER 0x2000000 -#define LYNX_28G_LNaRGCR0_N_RATE_MSK GENMASK(26, 24) - -#define LYNX_28G_LNaRGCR1(lane) (0x800 + (lane) * 0x100 + 0x48) - -#define LYNX_28G_LNaRECR0(lane) (0x800 + (lane) * 0x100 + 0x50) -#define LYNX_28G_LNaRECR1(lane) (0x800 + (lane) * 0x100 + 0x54) -#define LYNX_28G_LNaRECR2(lane) (0x800 + (lane) * 0x100 + 0x58) - -#define LYNX_28G_LNaRSCCR0(lane) (0x800 + (lane) * 0x100 + 0x74) - -#define LYNX_28G_LNaPSS(lane) (0x1000 + (lane) * 0x4) -#define LYNX_28G_LNaPSS_TYPE(pss) (((pss) & GENMASK(30, 24)) >> 24) -#define LYNX_28G_LNaPSS_TYPE_SGMII 0x4 -#define LYNX_28G_LNaPSS_TYPE_XFI 0x28 - -#define LYNX_28G_SGMIIaCR1(lane) (0x1804 + (lane) * 0x10) -#define LYNX_28G_SGMIIaCR1_SGPCS_EN BIT(11) -#define LYNX_28G_SGMIIaCR1_SGPCS_DIS 0x0 -#define LYNX_28G_SGMIIaCR1_SGPCS_MSK BIT(11) +#define LNaRGCR0(lane) (0x800 + (lane) * 0x100 + 0x44) +#define LNaRGCR0_USE_PLL BIT(28) +#define LNaRGCR0_USE_PLLF 0x0 +#define LNaRGCR0_USE_PLLS 0x1 +#define LNaRGCR0_N_RATE GENMASK(26, 24) +#define LNaRGCR0_N_RATE_FULL 0x0 +#define LNaRGCR0_N_RATE_HALF 0x1 +#define LNaRGCR0_N_RATE_QUARTER 0x2 + +#define LNaRGCR1(lane) (0x800 + (lane) * 0x100 + 0x48) +#define LNaRGCR1_RX_ORD_ELECIDLE BIT(31) +#define LNaRGCR1_DATA_LOST_FLT BIT(30) +#define LNaRGCR1_DATA_LOST BIT(29) +#define LNaRGCR1_IDLE_CONFIG BIT(28) +#define LNaRGCR1_ENTER_IDLE_FLT_SEL GENMASK(26, 24) +#define LNaRGCR1_EXIT_IDLE_FLT_SEL GENMASK(22, 20) +#define LNaRGCR1_DATA_LOST_TH_SEL GENMASK(18, 16) +#define LNaRGCR1_EXT_REC_CLK_SEL GENMASK(10, 8) +#define LNaRGCR1_WAKE_TX_DIS BIT(5) +#define LNaRGCR1_PHY_RDY BIT(4) +#define LNaRGCR1_CHANGE_RX_CLK BIT(3) +#define LNaRGCR1_PWR_MGT GENMASK(2, 0) + +#define LNaRECR0(lane) (0x800 + (lane) * 0x100 + 0x50) +#define LNaRECR0_EQ_GAINK2_HF_OV_EN BIT(31) +#define LNaRECR0_EQ_GAINK2_HF_OV GENMASK(28, 24) +#define LNaRECR0_EQ_GAINK3_MF_OV_EN BIT(23) +#define LNaRECR0_EQ_GAINK3_MF_OV GENMASK(20, 16) +#define LNaRECR0_EQ_GAINK4_LF_OV_EN BIT(7) +#define LNaRECR0_EQ_GAINK4_LF_DIS BIT(6) +#define LNaRECR0_EQ_GAINK4_LF_OV GENMASK(4, 0) + +#define LNaRECR1(lane) (0x800 + (lane) * 0x100 + 0x54) +#define LNaRECR1_EQ_BLW_OV_EN BIT(31) +#define LNaRECR1_EQ_BLW_OV GENMASK(28, 24) +#define LNaRECR1_EQ_OFFSET_OV_EN BIT(23) +#define LNaRECR1_EQ_OFFSET_OV GENMASK(21, 16) + +#define LNaRECR2(lane) (0x800 + (lane) * 0x100 + 0x58) +#define LNaRECR2_EQ_OFFSET_RNG_DBL BIT(31) +#define LNaRECR2_EQ_BOOST GENMASK(29, 28) +#define LNaRECR2_EQ_BLW_SEL GENMASK(25, 24) +#define LNaRECR2_EQ_ZERO GENMASK(17, 16) +#define LNaRECR2_EQ_IND GENMASK(13, 12) +#define LNaRECR2_EQ_BIN_DATA_AVG_TC GENMASK(5, 4) +#define LNaRECR2_SPARE_IN GENMASK(1, 0) + +#define LNaRECR3(lane) (0x800 + (lane) * 0x100 + 0x5c) +#define LNaRECR3_EQ_SNAP_START BIT(31) +#define LNaRECR3_EQ_SNAP_DONE BIT(30) +#define LNaRECR3_EQ_GAINK2_HF_STAT GENMASK(28, 24) +#define LNaRECR3_EQ_GAINK3_MF_STAT GENMASK(20, 16) +#define LNaRECR3_SPARE_OUT GENMASK(13, 12) +#define LNaRECR3_EQ_GAINK4_LF_STAT GENMASK(4, 0) + +#define LNaRECR4(lane) (0x800 + (lane) * 0x100 + 0x60) +#define LNaRECR4_BLW_STAT GENMASK(28, 24) +#define LNaRECR4_EQ_OFFSET_STAT GENMASK(21, 16) +#define LNaRECR4_EQ_BIN_DATA_SEL GENMASK(15, 12) +#define LNaRECR4_EQ_BIN_DATA GENMASK(8, 0) /* bit 9 is reserved */ +#define LNaRECR4_EQ_BIN_DATA_SGN BIT(8) + +#define LNaRCCR0(lane) (0x800 + (lane) * 0x100 + 0x68) +#define LNaRCCR0_CAL_EN BIT(31) +#define LNaRCCR0_MEAS_EN BIT(30) +#define LNaRCCR0_CAL_BIN_SEL BIT(28) +#define LNaRCCR0_CAL_DC3_DIS BIT(27) +#define LNaRCCR0_CAL_DC2_DIS BIT(26) +#define LNaRCCR0_CAL_DC1_DIS BIT(25) +#define LNaRCCR0_CAL_DC0_DIS BIT(24) +#define LNaRCCR0_CAL_AC3_OV_EN BIT(15) +#define LNaRCCR0_CAL_AC3_OV GENMASK(11, 8) +#define LNaRCCR0_CAL_AC2_OV_EN BIT(7) + +#define LNaRSCCR0(lane) (0x800 + (lane) * 0x100 + 0x74) +#define LNaRSCCR0_SMP_OFF_EN BIT(31) +#define LNaRSCCR0_SMP_OFF_OV_EN BIT(30) +#define LNaRSCCR0_SMP_MAN_OFF_EN BIT(29) +#define LNaRSCCR0_SMP_OFF_RNG_OV_EN BIT(27) +#define LNaRSCCR0_SMP_OFF_RNG_4X_OV BIT(25) +#define LNaRSCCR0_SMP_OFF_RNG_2X_OV BIT(24) +#define LNaRSCCR0_SMP_AUTOZ_PD BIT(23) +#define LNaRSCCR0_SMP_AUTOZ_CTRL GENMASK(19, 16) +#define LNaRSCCR0_SMP_AUTOZ_D1R GENMASK(13, 12) +#define LNaRSCCR0_SMP_AUTOZ_D1F GENMASK(9, 8) +#define LNaRSCCR0_SMP_AUTOZ_EG1R GENMASK(5, 4) +#define LNaRSCCR0_SMP_AUTOZ_EG1F GENMASK(1, 0) + +#define LNaTTLCR0(lane) (0x800 + (lane) * 0x100 + 0x80) +#define LNaTTLCR0_TTL_FLT_SEL GENMASK(29, 24) +#define LNaTTLCR0_TTL_SLO_PM_BYP BIT(22) +#define LNaTTLCR0_STALL_DET_DIS BIT(21) +#define LNaTTLCR0_INACT_MON_DIS BIT(20) +#define LNaTTLCR0_CDR_OV GENMASK(18, 16) +#define LNaTTLCR0_DATA_IN_SSC BIT(15) +#define LNaTTLCR0_CDR_MIN_SMP_ON GENMASK(1, 0) + +#define LNaTCSR0(lane) (0x800 + (lane) * 0x100 + 0xa0) +#define LNaTCSR0_SD_STAT_OBS_EN BIT(31) +#define LNaTCSR0_SD_LPBK_SEL GENMASK(29, 28) + +#define LNaPSS(lane) (0x1000 + (lane) * 0x4) +#define LNaPSS_TYPE GENMASK(30, 24) +#define LNaPSS_TYPE_SGMII (PROTO_SEL_SGMII_BASEX_KX << 2) +#define LNaPSS_TYPE_XFI (PROTO_SEL_XFI_10GBASER_KR_SXGMII << 2) +#define LNaPSS_TYPE_40G ((PROTO_SEL_XFI_10GBASER_KR_SXGMII << 2) | 3) +#define LNaPSS_TYPE_25G (PROTO_SEL_25G_50G_100G << 2) +#define LNaPSS_TYPE_100G ((PROTO_SEL_25G_50G_100G << 2) | 2) + +/* MDEV_PORT is at the same bitfield address for all protocol converters */ +#define MDEV_PORT GENMASK(31, 27) + +#define SGMIIaCR0(lane) (0x1800 + (lane) * 0x10) +#define SGMIIaCR1(lane) (0x1804 + (lane) * 0x10) +#define SGMIIaCR1_SGPCS_EN BIT(11) + +#define ANLTaCR0(lane) (0x1a00 + (lane) * 0x10) +#define ANLTaCR1(lane) (0x1a04 + (lane) * 0x10) + +#define SXGMIIaCR0(lane) (0x1a80 + (lane) * 0x10) +#define SXGMIIaCR0_RST BIT(31) +#define SXGMIIaCR0_PD BIT(30) + +#define SXGMIIaCR1(lane) (0x1a84 + (lane) * 0x10) + +#define E25GaCR0(lane) (0x1b00 + (lane) * 0x10) +#define E25GaCR0_RST BIT(31) +#define E25GaCR0_PD BIT(30) + +#define E25GaCR1(lane) (0x1b04 + (lane) * 0x10) + +#define E25GaCR2(lane) (0x1b08 + (lane) * 0x10) +#define E25GaCR2_FEC_ENA BIT(23) +#define E25GaCR2_FEC_ERR_ENA BIT(22) +#define E25GaCR2_FEC91_ENA BIT(20) + +#define E40GaCR0(pcvt) (0x1b40 + (pcvt) * 0x20) +#define E40GaCR1(pcvt) (0x1b44 + (pcvt) * 0x20) + +#define E50GaCR1(pcvt) (0x1b84 + (pcvt) * 0x10) + +#define E100GaCR1(pcvt) (0x1c04 + (pcvt) * 0x20) + +#define CR(x) ((x) * 4) + +enum lynx_28g_eq_type { + EQ_TYPE_NO_EQ = 0, + EQ_TYPE_2TAP = 1, + EQ_TYPE_3TAP = 2, +}; + +enum lynx_28g_proto_sel { + PROTO_SEL_PCIE = 0, + PROTO_SEL_SGMII_BASEX_KX = 1, + PROTO_SEL_SATA = 2, + PROTO_SEL_XAUI = 4, + PROTO_SEL_XFI_10GBASER_KR_SXGMII = 0xa, + PROTO_SEL_25G_50G_100G = 0x1a, +}; + +enum lynx_lane_mode { + LANE_MODE_UNKNOWN, + LANE_MODE_1000BASEX_SGMII, + LANE_MODE_10GBASER, + LANE_MODE_USXGMII, + LANE_MODE_MAX, +}; + +struct lynx_28g_proto_conf { + /* LNaGCR0 */ + int proto_sel; + int if_width; + /* LNaTECR0 */ + int teq_type; + int sgn_preq; + int ratio_preq; + int sgn_post1q; + int ratio_post1q; + int amp_red; + /* LNaTECR1 */ + int adpt_eq; + /* LNaRGCR1 */ + int enter_idle_flt_sel; + int exit_idle_flt_sel; + int data_lost_th_sel; + /* LNaRECR0 */ + int gk2ovd; + int gk3ovd; + int gk4ovd; + int gk2ovd_en; + int gk3ovd_en; + int gk4ovd_en; + /* LNaRECR1 ? */ + int eq_offset_ovd; + int eq_offset_ovd_en; + /* LNaRECR2 */ + int eq_offset_rng_dbl; + int eq_blw_sel; + int eq_boost; + int spare_in; + /* LNaRSCCR0 */ + int smp_autoz_d1r; + int smp_autoz_eg1r; + /* LNaRCCR0 */ + int rccr0; + /* LNaTTLCR0 */ + int ttlcr0; +}; + +static const struct lynx_28g_proto_conf lynx_28g_proto_conf[LANE_MODE_MAX] = { + [LANE_MODE_1000BASEX_SGMII] = { + .proto_sel = LNaGCR0_PROTO_SEL_SGMII, + .if_width = LNaGCR0_IF_WIDTH_10_BIT, + .teq_type = EQ_TYPE_NO_EQ, + .sgn_preq = 1, + .ratio_preq = 0, + .sgn_post1q = 1, + .ratio_post1q = 0, + .amp_red = 6, + .adpt_eq = 48, + .enter_idle_flt_sel = 4, + .exit_idle_flt_sel = 3, + .data_lost_th_sel = 1, + .gk2ovd = 0x1f, + .gk3ovd = 0, + .gk4ovd = 0, + .gk2ovd_en = 1, + .gk3ovd_en = 1, + .gk4ovd_en = 0, + .eq_offset_ovd = 0x1f, + .eq_offset_ovd_en = 0, + .eq_offset_rng_dbl = 0, + .eq_blw_sel = 0, + .eq_boost = 0, + .spare_in = 0, + .smp_autoz_d1r = 0, + .smp_autoz_eg1r = 0, + .rccr0 = LNaRCCR0_CAL_EN, + .ttlcr0 = LNaTTLCR0_TTL_SLO_PM_BYP | + LNaTTLCR0_DATA_IN_SSC, + }, + [LANE_MODE_USXGMII] = { + .proto_sel = LNaGCR0_PROTO_SEL_XFI, + .if_width = LNaGCR0_IF_WIDTH_20_BIT, + .teq_type = EQ_TYPE_2TAP, + .sgn_preq = 1, + .ratio_preq = 0, + .sgn_post1q = 1, + .ratio_post1q = 3, + .amp_red = 7, + .adpt_eq = 48, + .enter_idle_flt_sel = 0, + .exit_idle_flt_sel = 0, + .data_lost_th_sel = 0, + .gk2ovd = 0, + .gk3ovd = 0, + .gk4ovd = 0, + .gk2ovd_en = 0, + .gk3ovd_en = 0, + .gk4ovd_en = 0, + .eq_offset_ovd = 0x1f, + .eq_offset_ovd_en = 0, + .eq_offset_rng_dbl = 1, + .eq_blw_sel = 1, + .eq_boost = 0, + .spare_in = 0, + .smp_autoz_d1r = 2, + .smp_autoz_eg1r = 0, + .rccr0 = LNaRCCR0_CAL_EN, + .ttlcr0 = LNaTTLCR0_TTL_SLO_PM_BYP | + LNaTTLCR0_DATA_IN_SSC, + }, + [LANE_MODE_10GBASER] = { + .proto_sel = LNaGCR0_PROTO_SEL_XFI, + .if_width = LNaGCR0_IF_WIDTH_20_BIT, + .teq_type = EQ_TYPE_2TAP, + .sgn_preq = 1, + .ratio_preq = 0, + .sgn_post1q = 1, + .ratio_post1q = 3, + .amp_red = 7, + .adpt_eq = 48, + .enter_idle_flt_sel = 0, + .exit_idle_flt_sel = 0, + .data_lost_th_sel = 0, + .gk2ovd = 0, + .gk3ovd = 0, + .gk4ovd = 0, + .gk2ovd_en = 0, + .gk3ovd_en = 0, + .gk4ovd_en = 0, + .eq_offset_ovd = 0x1f, + .eq_offset_ovd_en = 0, + .eq_offset_rng_dbl = 1, + .eq_blw_sel = 1, + .eq_boost = 0, + .spare_in = 0, + .smp_autoz_d1r = 2, + .smp_autoz_eg1r = 0, + .rccr0 = LNaRCCR0_CAL_EN, + .ttlcr0 = LNaTTLCR0_TTL_SLO_PM_BYP | + LNaTTLCR0_DATA_IN_SSC, + }, +}; + +struct lynx_pccr { + int offset; + int width; + int shift; +}; struct lynx_28g_priv; @@ -112,7 +421,7 @@ struct lynx_28g_pll { struct lynx_28g_priv *priv; u32 rstctl, cr0, cr1; int id; - DECLARE_PHY_INTERFACE_MASK(supported); + DECLARE_BITMAP(supported, LANE_MODE_MAX); }; struct lynx_28g_lane { @@ -121,7 +430,7 @@ struct lynx_28g_lane { bool powered_up; bool init; unsigned int id; - phy_interface_t interface; + enum lynx_lane_mode mode; }; struct lynx_28g_priv { @@ -149,23 +458,58 @@ static void lynx_28g_rmw(struct lynx_28g_priv *priv, unsigned long off, iowrite32(tmp, reg); } +#define lynx_28g_read(priv, off) \ + ioread32((priv)->base + (off)) +#define lynx_28g_write(priv, off, val) \ + iowrite32(val, (priv)->base + (off)) #define lynx_28g_lane_rmw(lane, reg, val, mask) \ - lynx_28g_rmw((lane)->priv, LYNX_28G_##reg(lane->id), \ - LYNX_28G_##reg##_##val, LYNX_28G_##reg##_##mask) + lynx_28g_rmw((lane)->priv, reg(lane->id), val, mask) #define lynx_28g_lane_read(lane, reg) \ - ioread32((lane)->priv->base + LYNX_28G_##reg((lane)->id)) + ioread32((lane)->priv->base + reg((lane)->id)) +#define lynx_28g_lane_write(lane, reg, val) \ + iowrite32(val, (lane)->priv->base + reg((lane)->id)) #define lynx_28g_pll_read(pll, reg) \ - ioread32((pll)->priv->base + LYNX_28G_##reg((pll)->id)) + ioread32((pll)->priv->base + reg((pll)->id)) -static bool lynx_28g_supports_interface(struct lynx_28g_priv *priv, int intf) +static const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode) +{ + switch (lane_mode) { + case LANE_MODE_1000BASEX_SGMII: + return "1000Base-X/SGMII"; + case LANE_MODE_10GBASER: + return "10GBase-R"; + case LANE_MODE_USXGMII: + return "USXGMII"; + default: + return "unknown"; + } +} + +static enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf) +{ + switch (intf) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + return LANE_MODE_1000BASEX_SGMII; + case PHY_INTERFACE_MODE_10GBASER: + return LANE_MODE_10GBASER; + case PHY_INTERFACE_MODE_USXGMII: + return LANE_MODE_USXGMII; + default: + return LANE_MODE_UNKNOWN; + } +} + +static bool lynx_28g_supports_lane_mode(struct lynx_28g_priv *priv, + enum lynx_lane_mode mode) { int i; for (i = 0; i < LYNX_28G_NUM_PLL; i++) { - if (LYNX_28G_PLLnRSTCTL_DIS(priv->pll[i].rstctl)) + if (PLLnRSTCTL_DIS(priv->pll[i].rstctl)) continue; - if (test_bit(intf, priv->pll[i].supported)) + if (test_bit(mode, priv->pll[i].supported)) return true; } @@ -173,7 +517,7 @@ static bool lynx_28g_supports_interface(struct lynx_28g_priv *priv, int intf) } static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv, - phy_interface_t intf) + enum lynx_lane_mode mode) { struct lynx_28g_pll *pll; int i; @@ -181,43 +525,51 @@ static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv, for (i = 0; i < LYNX_28G_NUM_PLL; i++) { pll = &priv->pll[i]; - if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl)) + if (PLLnRSTCTL_DIS(pll->rstctl)) continue; - if (test_bit(intf, pll->supported)) + if (test_bit(mode, pll->supported)) return pll; } /* no pll supports requested mode, either caller forgot to check * lynx_28g_supports_lane_mode, or this is a bug. */ - dev_WARN_ONCE(priv->dev, 1, "no pll for interface %s\n", phy_modes(intf)); + dev_WARN_ONCE(priv->dev, 1, "no pll for lane mode %s\n", + lynx_lane_mode_str(mode)); return NULL; } static void lynx_28g_lane_set_nrate(struct lynx_28g_lane *lane, struct lynx_28g_pll *pll, - phy_interface_t intf) + enum lynx_lane_mode lane_mode) { - switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) { - case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO: - case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO: - switch (intf) { - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_1000BASEX: - lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_QUARTER, N_RATE_MSK); - lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_QUARTER, N_RATE_MSK); + switch (FIELD_GET(PLLnCR1_FRATE_SEL, pll->cr1)) { + case PLLnCR1_FRATE_5G_10GVCO: + case PLLnCR1_FRATE_5G_25GVCO: + switch (lane_mode) { + case LANE_MODE_1000BASEX_SGMII: + lynx_28g_lane_rmw(lane, LNaTGCR0, + FIELD_PREP(LNaTGCR0_N_RATE, LNaTGCR0_N_RATE_QUARTER), + LNaTGCR0_N_RATE); + lynx_28g_lane_rmw(lane, LNaRGCR0, + FIELD_PREP(LNaRGCR0_N_RATE, LNaRGCR0_N_RATE_QUARTER), + LNaRGCR0_N_RATE); break; default: break; } break; - case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO: - switch (intf) { - case PHY_INTERFACE_MODE_10GBASER: - case PHY_INTERFACE_MODE_USXGMII: - lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_FULL, N_RATE_MSK); - lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_FULL, N_RATE_MSK); + case PLLnCR1_FRATE_10G_20GVCO: + switch (lane_mode) { + case LANE_MODE_10GBASER: + case LANE_MODE_USXGMII: + lynx_28g_lane_rmw(lane, LNaTGCR0, + FIELD_PREP(LNaTGCR0_N_RATE, LNaTGCR0_N_RATE_FULL), + LNaTGCR0_N_RATE); + lynx_28g_lane_rmw(lane, LNaRGCR0, + FIELD_PREP(LNaRGCR0_N_RATE, LNaRGCR0_N_RATE_FULL), + LNaRGCR0_N_RATE); break; default: break; @@ -232,163 +584,356 @@ static void lynx_28g_lane_set_pll(struct lynx_28g_lane *lane, struct lynx_28g_pll *pll) { if (pll->id == 0) { - lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLF, USE_PLL_MSK); - lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLF, USE_PLL_MSK); + lynx_28g_lane_rmw(lane, LNaTGCR0, + FIELD_PREP(LNaTGCR0_USE_PLL, LNaTGCR0_USE_PLLF), + LNaTGCR0_USE_PLL); + lynx_28g_lane_rmw(lane, LNaRGCR0, + FIELD_PREP(LNaRGCR0_USE_PLL, LNaRGCR0_USE_PLLF), + LNaRGCR0_USE_PLL); } else { - lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLS, USE_PLL_MSK); - lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLS, USE_PLL_MSK); + lynx_28g_lane_rmw(lane, LNaTGCR0, + FIELD_PREP(LNaTGCR0_USE_PLL, LNaTGCR0_USE_PLLS), + LNaTGCR0_USE_PLL); + lynx_28g_lane_rmw(lane, LNaRGCR0, + FIELD_PREP(LNaRGCR0_USE_PLL, LNaRGCR0_USE_PLLS), + LNaRGCR0_USE_PLL); } } -static void lynx_28g_cleanup_lane(struct lynx_28g_lane *lane) +static int lynx_28g_power_off(struct phy *phy) { - u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane); - struct lynx_28g_priv *priv = lane->priv; + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + u32 trstctl, rrstctl; - /* Cleanup the protocol configuration registers of the current protocol */ - switch (lane->interface) { - case PHY_INTERFACE_MODE_10GBASER: - lynx_28g_rmw(priv, LYNX_28G_PCCC, - LYNX_28G_PCCC_SXGMII_DIS << lane_offset, - GENMASK(3, 0) << lane_offset); + if (!lane->powered_up) + return 0; + + /* Issue a halt request */ + lynx_28g_lane_rmw(lane, LNaTRSTCTL, LNaTRSTCTL_HLT_REQ, + LNaTRSTCTL_HLT_REQ); + lynx_28g_lane_rmw(lane, LNaRRSTCTL, LNaRRSTCTL_HLT_REQ, + LNaRRSTCTL_HLT_REQ); + + /* Wait until the halting process is complete */ + do { + trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); + rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + } while ((trstctl & LNaTRSTCTL_HLT_REQ) || + (rrstctl & LNaRRSTCTL_HLT_REQ)); + + lane->powered_up = false; + + return 0; +} + +static int lynx_28g_power_on(struct phy *phy) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + u32 trstctl, rrstctl; + + if (lane->powered_up) + return 0; + + /* Issue a reset request on the lane */ + lynx_28g_lane_rmw(lane, LNaTRSTCTL, LNaTRSTCTL_RST_REQ, + LNaTRSTCTL_RST_REQ); + lynx_28g_lane_rmw(lane, LNaRRSTCTL, LNaRRSTCTL_RST_REQ, + LNaRRSTCTL_RST_REQ); + + /* Wait until the reset sequence is completed */ + do { + trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); + rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); + } while (!(trstctl & LNaTRSTCTL_RST_DONE) || + !(rrstctl & LNaRRSTCTL_RST_DONE)); + + lane->powered_up = true; + + return 0; +} + +static int lynx_28g_get_pccr(enum lynx_lane_mode lane_mode, int lane, + struct lynx_pccr *pccr) +{ + switch (lane_mode) { + case LANE_MODE_1000BASEX_SGMII: + pccr->offset = PCC8; + pccr->width = 4; + pccr->shift = SGMII_CFG(lane); break; - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_1000BASEX: - lynx_28g_rmw(priv, LYNX_28G_PCC8, - LYNX_28G_PCC8_SGMII_DIS << lane_offset, - GENMASK(3, 0) << lane_offset); + case LANE_MODE_USXGMII: + case LANE_MODE_10GBASER: + pccr->offset = PCCC; + pccr->width = 4; + pccr->shift = SXGMII_CFG(lane); break; default: - break; + return -EOPNOTSUPP; } + + return 0; } -static void lynx_28g_lane_set_sgmii(struct lynx_28g_lane *lane) +static int lynx_28g_get_pcvt_offset(int lane, enum lynx_lane_mode lane_mode) +{ + switch (lane_mode) { + case LANE_MODE_1000BASEX_SGMII: + return SGMIIaCR0(lane); + case LANE_MODE_USXGMII: + case LANE_MODE_10GBASER: + return SXGMIIaCR0(lane); + default: + return -EOPNOTSUPP; + } +} + +static int lynx_pccr_read(struct lynx_28g_lane *lane, enum lynx_lane_mode mode, + u32 *val) { - u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane); struct lynx_28g_priv *priv = lane->priv; - struct lynx_28g_pll *pll; + struct lynx_pccr pccr; + u32 tmp; + int err; - lynx_28g_cleanup_lane(lane); + err = lynx_28g_get_pccr(mode, lane->id, &pccr); + if (err) + return err; - /* Setup the lane to run in SGMII */ - lynx_28g_rmw(priv, LYNX_28G_PCC8, - LYNX_28G_PCC8_SGMII << lane_offset, - GENMASK(3, 0) << lane_offset); + tmp = lynx_28g_read(priv, pccr.offset); + *val = (tmp >> pccr.shift) & GENMASK(pccr.width - 1, 0); - /* Setup the protocol select and SerDes parallel interface width */ - lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_SGMII, PROTO_SEL_MSK); - lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_10_BIT, IF_WIDTH_MSK); + return 0; +} - /* Find the PLL that works with this interface type */ - pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_SGMII); - if (unlikely(pll == NULL)) - return; +static int lynx_pccr_write(struct lynx_28g_lane *lane, + enum lynx_lane_mode lane_mode, u32 val) +{ + struct lynx_28g_priv *priv = lane->priv; + struct lynx_pccr pccr; + u32 old, tmp, mask; + int err; - /* Switch to the PLL that works with this interface type */ - lynx_28g_lane_set_pll(lane, pll); + err = lynx_28g_get_pccr(lane_mode, lane->id, &pccr); + if (err) + return err; - /* Choose the portion of clock net to be used on this lane */ - lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_SGMII); - - /* Enable the SGMII PCS */ - lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_EN, SGPCS_MSK); - - /* Configure the appropriate equalization parameters for the protocol */ - iowrite32(0x00808006, priv->base + LYNX_28G_LNaTECR0(lane->id)); - iowrite32(0x04310000, priv->base + LYNX_28G_LNaRGCR1(lane->id)); - iowrite32(0x9f800000, priv->base + LYNX_28G_LNaRECR0(lane->id)); - iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id)); - iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR2(lane->id)); - iowrite32(0x00000000, priv->base + LYNX_28G_LNaRSCCR0(lane->id)); + old = lynx_28g_read(priv, pccr.offset); + mask = GENMASK(pccr.width - 1, 0) << pccr.shift; + tmp = (old & ~mask) | (val << pccr.shift); + lynx_28g_write(priv, pccr.offset, tmp); + + dev_dbg(&lane->phy->dev, "PCCR@0x%x: 0x%x -> 0x%x\n", + pccr.offset, old, tmp); + + return 0; } -static void lynx_28g_lane_set_10gbaser(struct lynx_28g_lane *lane) +static int lynx_pcvt_read(struct lynx_28g_lane *lane, + enum lynx_lane_mode lane_mode, int cr, u32 *val) { - u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane); struct lynx_28g_priv *priv = lane->priv; - struct lynx_28g_pll *pll; + int offset; + + offset = lynx_28g_get_pcvt_offset(lane->id, lane_mode); + if (offset < 0) + return offset; + + *val = lynx_28g_read(priv, offset + cr); + + return 0; +} + +static int lynx_pcvt_write(struct lynx_28g_lane *lane, + enum lynx_lane_mode lane_mode, int cr, u32 val) +{ + struct lynx_28g_priv *priv = lane->priv; + int offset; + + offset = lynx_28g_get_pcvt_offset(lane->id, lane_mode); + if (offset < 0) + return offset; - lynx_28g_cleanup_lane(lane); + lynx_28g_write(priv, offset + cr, val); - /* Enable the SXGMII lane */ - lynx_28g_rmw(priv, LYNX_28G_PCCC, - LYNX_28G_PCCC_10GBASER << lane_offset, - GENMASK(3, 0) << lane_offset); + return 0; +} + +static int lynx_pcvt_rmw(struct lynx_28g_lane *lane, + enum lynx_lane_mode lane_mode, + int cr, u32 val, u32 mask) +{ + int err; + u32 tmp; + + err = lynx_pcvt_read(lane, lane_mode, cr, &tmp); + if (err) + return err; + + tmp &= ~mask; + tmp |= val; - /* Setup the protocol select and SerDes parallel interface width */ - lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_XFI, PROTO_SEL_MSK); - lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_20_BIT, IF_WIDTH_MSK); + return lynx_pcvt_write(lane, lane_mode, cr, tmp); +} - /* Find the PLL that works with this interface type */ - pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_10GBASER); +static void lynx_28g_lane_remap_pll(struct lynx_28g_lane *lane, + enum lynx_lane_mode lane_mode) +{ + struct lynx_28g_priv *priv = lane->priv; + struct lynx_28g_pll *pll; + + /* Switch to the PLL that works with this interface type */ + pll = lynx_28g_pll_get(priv, lane_mode); if (unlikely(pll == NULL)) return; - /* Switch to the PLL that works with this interface type */ lynx_28g_lane_set_pll(lane, pll); /* Choose the portion of clock net to be used on this lane */ - lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_10GBASER); - - /* Disable the SGMII PCS */ - lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_DIS, SGPCS_MSK); - - /* Configure the appropriate equalization parameters for the protocol */ - iowrite32(0x10808307, priv->base + LYNX_28G_LNaTECR0(lane->id)); - iowrite32(0x10000000, priv->base + LYNX_28G_LNaRGCR1(lane->id)); - iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR0(lane->id)); - iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id)); - iowrite32(0x81000020, priv->base + LYNX_28G_LNaRECR2(lane->id)); - iowrite32(0x00002000, priv->base + LYNX_28G_LNaRSCCR0(lane->id)); + lynx_28g_lane_set_nrate(lane, pll, lane_mode); } -static int lynx_28g_power_off(struct phy *phy) +static void lynx_28g_lane_change_proto_conf(struct lynx_28g_lane *lane, + enum lynx_lane_mode lane_mode) { - struct lynx_28g_lane *lane = phy_get_drvdata(phy); - u32 trstctl, rrstctl; + const struct lynx_28g_proto_conf *conf = &lynx_28g_proto_conf[lane_mode]; + + lynx_28g_lane_rmw(lane, LNaGCR0, + FIELD_PREP(LNaGCR0_PROTO_SEL, conf->proto_sel) | + FIELD_PREP(LNaGCR0_IF_WIDTH, conf->if_width), + LNaGCR0_PROTO_SEL | LNaGCR0_IF_WIDTH); + + lynx_28g_lane_rmw(lane, LNaTECR0, + FIELD_PREP(LNaTECR0_EQ_TYPE, conf->teq_type) | + FIELD_PREP(LNaTECR0_EQ_SGN_PREQ, conf->sgn_preq) | + FIELD_PREP(LNaTECR0_EQ_PREQ, conf->ratio_preq) | + FIELD_PREP(LNaTECR0_EQ_SGN_POST1Q, conf->sgn_post1q) | + FIELD_PREP(LNaTECR0_EQ_POST1Q, conf->ratio_post1q) | + FIELD_PREP(LNaTECR0_EQ_AMP_RED, conf->amp_red), + LNaTECR0_EQ_TYPE | + LNaTECR0_EQ_SGN_PREQ | + LNaTECR0_EQ_PREQ | + LNaTECR0_EQ_SGN_POST1Q | + LNaTECR0_EQ_POST1Q | + LNaTECR0_EQ_AMP_RED); + + lynx_28g_lane_rmw(lane, LNaTECR1, + FIELD_PREP(LNaTECR1_EQ_ADPT_EQ, conf->adpt_eq), + LNaTECR1_EQ_ADPT_EQ); + + lynx_28g_lane_rmw(lane, LNaRGCR1, + FIELD_PREP(LNaRGCR1_ENTER_IDLE_FLT_SEL, conf->enter_idle_flt_sel) | + FIELD_PREP(LNaRGCR1_EXIT_IDLE_FLT_SEL, conf->exit_idle_flt_sel) | + FIELD_PREP(LNaRGCR1_DATA_LOST_TH_SEL, conf->data_lost_th_sel), + LNaRGCR1_ENTER_IDLE_FLT_SEL | + LNaRGCR1_EXIT_IDLE_FLT_SEL | + LNaRGCR1_DATA_LOST_TH_SEL); + + lynx_28g_lane_rmw(lane, LNaRECR0, + FIELD_PREP(LNaRECR0_EQ_GAINK2_HF_OV_EN, conf->gk2ovd_en) | + FIELD_PREP(LNaRECR0_EQ_GAINK3_MF_OV_EN, conf->gk3ovd_en) | + FIELD_PREP(LNaRECR0_EQ_GAINK4_LF_OV_EN, conf->gk4ovd_en) | + FIELD_PREP(LNaRECR0_EQ_GAINK2_HF_OV, conf->gk2ovd) | + FIELD_PREP(LNaRECR0_EQ_GAINK3_MF_OV, conf->gk3ovd) | + FIELD_PREP(LNaRECR0_EQ_GAINK4_LF_OV, conf->gk4ovd), + LNaRECR0_EQ_GAINK2_HF_OV | + LNaRECR0_EQ_GAINK3_MF_OV | + LNaRECR0_EQ_GAINK4_LF_OV | + LNaRECR0_EQ_GAINK2_HF_OV_EN | + LNaRECR0_EQ_GAINK3_MF_OV_EN | + LNaRECR0_EQ_GAINK4_LF_OV_EN); + + lynx_28g_lane_rmw(lane, LNaRECR1, + FIELD_PREP(LNaRECR1_EQ_OFFSET_OV, conf->eq_offset_ovd) | + FIELD_PREP(LNaRECR1_EQ_OFFSET_OV_EN, conf->eq_offset_ovd_en), + LNaRECR1_EQ_OFFSET_OV | + LNaRECR1_EQ_OFFSET_OV_EN); + + lynx_28g_lane_rmw(lane, LNaRECR2, + FIELD_PREP(LNaRECR2_EQ_OFFSET_RNG_DBL, conf->eq_offset_rng_dbl) | + FIELD_PREP(LNaRECR2_EQ_BLW_SEL, conf->eq_blw_sel) | + FIELD_PREP(LNaRECR2_EQ_BOOST, conf->eq_boost) | + FIELD_PREP(LNaRECR2_SPARE_IN, conf->spare_in), + LNaRECR2_EQ_OFFSET_RNG_DBL | + LNaRECR2_EQ_BLW_SEL | + LNaRECR2_EQ_BOOST | + LNaRECR2_SPARE_IN); + + lynx_28g_lane_rmw(lane, LNaRSCCR0, + FIELD_PREP(LNaRSCCR0_SMP_AUTOZ_D1R, conf->smp_autoz_d1r) | + FIELD_PREP(LNaRSCCR0_SMP_AUTOZ_EG1R, conf->smp_autoz_eg1r), + LNaRSCCR0_SMP_AUTOZ_D1R | + LNaRSCCR0_SMP_AUTOZ_EG1R); + + lynx_28g_lane_write(lane, LNaRCCR0, conf->rccr0); + lynx_28g_lane_write(lane, LNaTTLCR0, conf->ttlcr0); +} - if (!lane->powered_up) - return 0; +static int lynx_28g_lane_disable_pcvt(struct lynx_28g_lane *lane, + enum lynx_lane_mode lane_mode) +{ + struct lynx_28g_priv *priv = lane->priv; + int err; - /* Issue a halt request */ - lynx_28g_lane_rmw(lane, LNaTRSTCTL, HLT_REQ, HLT_REQ); - lynx_28g_lane_rmw(lane, LNaRRSTCTL, HLT_REQ, HLT_REQ); + spin_lock(&priv->pcc_lock); - /* Wait until the halting process is complete */ - do { - trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); - rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); - } while ((trstctl & LYNX_28G_LNaTRSTCTL_HLT_REQ) || - (rrstctl & LYNX_28G_LNaRRSTCTL_HLT_REQ)); + err = lynx_pccr_write(lane, lane_mode, 0); + if (err) + goto out; - lane->powered_up = false; + switch (lane_mode) { + case LANE_MODE_1000BASEX_SGMII: + err = lynx_pcvt_rmw(lane, lane_mode, CR(1), 0, + SGMIIaCR1_SGPCS_EN); + break; + default: + err = 0; + } - return 0; +out: + spin_unlock(&priv->pcc_lock); + + return err; } -static int lynx_28g_power_on(struct phy *phy) +static int lynx_28g_lane_enable_pcvt(struct lynx_28g_lane *lane, + enum lynx_lane_mode lane_mode) { - struct lynx_28g_lane *lane = phy_get_drvdata(phy); - u32 trstctl, rrstctl; + struct lynx_28g_priv *priv = lane->priv; + u32 val; + int err; - if (lane->powered_up) - return 0; + spin_lock(&priv->pcc_lock); - /* Issue a reset request on the lane */ - lynx_28g_lane_rmw(lane, LNaTRSTCTL, RST_REQ, RST_REQ); - lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ); + switch (lane_mode) { + case LANE_MODE_1000BASEX_SGMII: + err = lynx_pcvt_rmw(lane, lane_mode, CR(1), SGMIIaCR1_SGPCS_EN, + SGMIIaCR1_SGPCS_EN); + break; + default: + err = 0; + } - /* Wait until the reset sequence is completed */ - do { - trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL); - rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); - } while (!(trstctl & LYNX_28G_LNaTRSTCTL_RST_DONE) || - !(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE)); + val = 0; - lane->powered_up = true; + switch (lane_mode) { + case LANE_MODE_1000BASEX_SGMII: + val |= PCC8_SGMIIa_CFG; + break; + case LANE_MODE_10GBASER: + val |= PCCC_SXGMIIn_XFI; + fallthrough; + case LANE_MODE_USXGMII: + val |= PCCC_SXGMIIn_CFG; + break; + default: + break; + } - return 0; + err = lynx_pccr_write(lane, lane_mode, val); + + spin_unlock(&priv->pcc_lock); + + return err; } static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode) @@ -396,44 +941,39 @@ static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode) struct lynx_28g_lane *lane = phy_get_drvdata(phy); struct lynx_28g_priv *priv = lane->priv; int powered_up = lane->powered_up; + enum lynx_lane_mode lane_mode; int err = 0; if (mode != PHY_MODE_ETHERNET) return -EOPNOTSUPP; - if (lane->interface == PHY_INTERFACE_MODE_NA) + if (lane->mode == LANE_MODE_UNKNOWN) return -EOPNOTSUPP; - if (!lynx_28g_supports_interface(priv, submode)) + lane_mode = phy_interface_to_lane_mode(submode); + if (!lynx_28g_supports_lane_mode(priv, lane_mode)) return -EOPNOTSUPP; + if (lane_mode == lane->mode) + return 0; + /* If the lane is powered up, put the lane into the halt state while * the reconfiguration is being done. */ if (powered_up) lynx_28g_power_off(phy); - spin_lock(&priv->pcc_lock); - - switch (submode) { - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_1000BASEX: - lynx_28g_lane_set_sgmii(lane); - break; - case PHY_INTERFACE_MODE_10GBASER: - lynx_28g_lane_set_10gbaser(lane); - break; - default: - err = -EOPNOTSUPP; + err = lynx_28g_lane_disable_pcvt(lane, lane->mode); + if (err) goto out; - } - lane->interface = submode; + lynx_28g_lane_change_proto_conf(lane, lane_mode); + lynx_28g_lane_remap_pll(lane, lane_mode); + WARN_ON(lynx_28g_lane_enable_pcvt(lane, lane_mode)); -out: - spin_unlock(&priv->pcc_lock); + lane->mode = lane_mode; - /* Power up the lane if necessary */ +out: if (powered_up) lynx_28g_power_on(phy); @@ -445,11 +985,13 @@ static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode, { struct lynx_28g_lane *lane = phy_get_drvdata(phy); struct lynx_28g_priv *priv = lane->priv; + enum lynx_lane_mode lane_mode; if (mode != PHY_MODE_ETHERNET) return -EOPNOTSUPP; - if (!lynx_28g_supports_interface(priv, submode)) + lane_mode = phy_interface_to_lane_mode(submode); + if (!lynx_28g_supports_lane_mode(priv, lane_mode)) return -EOPNOTSUPP; return 0; @@ -495,19 +1037,19 @@ static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv) pll->cr0 = lynx_28g_pll_read(pll, PLLnCR0); pll->cr1 = lynx_28g_pll_read(pll, PLLnCR1); - if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl)) + if (PLLnRSTCTL_DIS(pll->rstctl)) continue; - switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) { - case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO: - case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO: + switch (FIELD_GET(PLLnCR1_FRATE_SEL, pll->cr1)) { + case PLLnCR1_FRATE_5G_10GVCO: + case PLLnCR1_FRATE_5G_25GVCO: /* 5GHz clock net */ - __set_bit(PHY_INTERFACE_MODE_1000BASEX, pll->supported); - __set_bit(PHY_INTERFACE_MODE_SGMII, pll->supported); + __set_bit(LANE_MODE_1000BASEX_SGMII, pll->supported); break; - case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO: + case PLLnCR1_FRATE_10G_20GVCO: /* 10.3125GHz clock net */ - __set_bit(PHY_INTERFACE_MODE_10GBASER, pll->supported); + __set_bit(LANE_MODE_10GBASER, pll->supported); + __set_bit(LANE_MODE_USXGMII, pll->supported); break; default: /* 6GHz, 12.890625GHz, 8GHz */ @@ -536,11 +1078,12 @@ static void lynx_28g_cdr_lock_check(struct work_struct *work) } rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); - if (!(rrstctl & LYNX_28G_LNaRRSTCTL_CDR_LOCK)) { - lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ); + if (!(rrstctl & LNaRRSTCTL_CDR_LOCK)) { + lynx_28g_lane_rmw(lane, LNaRRSTCTL, LNaRRSTCTL_RST_REQ, + LNaRRSTCTL_RST_REQ); do { rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL); - } while (!(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE)); + } while (!(rrstctl & LNaRRSTCTL_RST_DONE)); } mutex_unlock(&lane->phy->mutex); @@ -551,19 +1094,23 @@ static void lynx_28g_cdr_lock_check(struct work_struct *work) static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane) { - u32 pss, protocol; + u32 pccr, pss, protocol; pss = lynx_28g_lane_read(lane, LNaPSS); - protocol = LYNX_28G_LNaPSS_TYPE(pss); + protocol = FIELD_GET(LNaPSS_TYPE, pss); switch (protocol) { - case LYNX_28G_LNaPSS_TYPE_SGMII: - lane->interface = PHY_INTERFACE_MODE_SGMII; + case LNaPSS_TYPE_SGMII: + lane->mode = LANE_MODE_1000BASEX_SGMII; break; - case LYNX_28G_LNaPSS_TYPE_XFI: - lane->interface = PHY_INTERFACE_MODE_10GBASER; + case LNaPSS_TYPE_XFI: + lynx_pccr_read(lane, LANE_MODE_10GBASER, &pccr); + if (pccr & PCCC_SXGMIIn_XFI) + lane->mode = LANE_MODE_10GBASER; + else + lane->mode = LANE_MODE_USXGMII; break; default: - lane->interface = PHY_INTERFACE_MODE_NA; + lane->mode = LANE_MODE_UNKNOWN; } } @@ -571,7 +1118,14 @@ static struct phy *lynx_28g_xlate(struct device *dev, const struct of_phandle_args *args) { struct lynx_28g_priv *priv = dev_get_drvdata(dev); - int idx = args->args[0]; + int idx; + + if (args->args_count == 0) + return of_phy_simple_xlate(dev, args); + else if (args->args_count != 1) + return ERR_PTR(-ENODEV); + + idx = args->args[0]; if (WARN_ON(idx >= LYNX_28G_NUM_LANE)) return ERR_PTR(-EINVAL); @@ -579,17 +1133,41 @@ static struct phy *lynx_28g_xlate(struct device *dev, return priv->lane[idx].phy; } +static int lynx_28g_probe_lane(struct lynx_28g_priv *priv, int id, + struct device_node *dn) +{ + struct lynx_28g_lane *lane = &priv->lane[id]; + struct phy *phy; + + phy = devm_phy_create(priv->dev, dn, &lynx_28g_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + lane->priv = priv; + lane->phy = phy; + lane->id = id; + phy_set_drvdata(phy, lane); + lynx_28g_lane_read_configuration(lane); + + return 0; +} + static int lynx_28g_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct phy_provider *provider; struct lynx_28g_priv *priv; - int i; + struct device_node *dn; + int err; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - priv->dev = &pdev->dev; + + priv->dev = dev; + dev_set_drvdata(dev, priv); + spin_lock_init(&priv->pcc_lock); + INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check); priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) @@ -597,35 +1175,51 @@ static int lynx_28g_probe(struct platform_device *pdev) lynx_28g_pll_read_configuration(priv); - for (i = 0; i < LYNX_28G_NUM_LANE; i++) { - struct lynx_28g_lane *lane = &priv->lane[i]; - struct phy *phy; - - memset(lane, 0, sizeof(*lane)); - - phy = devm_phy_create(&pdev->dev, NULL, &lynx_28g_ops); - if (IS_ERR(phy)) - return PTR_ERR(phy); - - lane->priv = priv; - lane->phy = phy; - lane->id = i; - phy_set_drvdata(phy, lane); - lynx_28g_lane_read_configuration(lane); + dn = dev_of_node(dev); + if (of_get_child_count(dn)) { + struct device_node *child; + + for_each_available_child_of_node(dn, child) { + u32 reg; + + /* PHY subnode name must be 'phy'. */ + if (!(of_node_name_eq(child, "phy"))) + continue; + + if (of_property_read_u32(child, "reg", ®)) { + dev_err(dev, "No \"reg\" property for %pOF\n", child); + of_node_put(child); + return -EINVAL; + } + + if (reg >= LYNX_28G_NUM_LANE) { + dev_err(dev, "\"reg\" property out of range for %pOF\n", child); + of_node_put(child); + return -EINVAL; + } + + err = lynx_28g_probe_lane(priv, reg, child); + if (err) { + of_node_put(child); + return err; + } + } + } else { + for (int i = 0; i < LYNX_28G_NUM_LANE; i++) { + err = lynx_28g_probe_lane(priv, i, NULL); + if (err) + return err; + } } - dev_set_drvdata(dev, priv); - - spin_lock_init(&priv->pcc_lock); - INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check); + provider = devm_of_phy_provider_register(dev, lynx_28g_xlate); + if (IS_ERR(provider)) + return PTR_ERR(provider); queue_delayed_work(system_power_efficient_wq, &priv->cdr_check, msecs_to_jiffies(1000)); - dev_set_drvdata(&pdev->dev, priv); - provider = devm_of_phy_provider_register(&pdev->dev, lynx_28g_xlate); - - return PTR_ERR_OR_ZERO(provider); + return 0; } static void lynx_28g_remove(struct platform_device *pdev) diff --git a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c index 191c282246d9..d010fec15671 100644 --- a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c +++ b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c @@ -570,17 +570,20 @@ const struct phy_config *fsl_samsung_hdmi_phy_find_settings(struct fsl_samsung_h return fract_div_phy; } -static long fsl_samsung_hdmi_phy_clk_round_rate(struct clk_hw *hw, - unsigned long rate, unsigned long *parent_rate) +static int fsl_samsung_hdmi_phy_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw); - const struct phy_config *target_settings = fsl_samsung_hdmi_phy_find_settings(phy, rate); + const struct phy_config *target_settings = fsl_samsung_hdmi_phy_find_settings(phy, + req->rate); if (target_settings == NULL) return -EINVAL; dev_dbg(phy->dev, "round_rate, closest rate = %u\n", target_settings->pixclk); - return target_settings->pixclk; + req->rate = target_settings->pixclk; + + return 0; } static int fsl_samsung_hdmi_phy_clk_set_rate(struct clk_hw *hw, @@ -599,7 +602,7 @@ static int fsl_samsung_hdmi_phy_clk_set_rate(struct clk_hw *hw, static const struct clk_ops phy_clk_ops = { .recalc_rate = phy_clk_recalc_rate, - .round_rate = fsl_samsung_hdmi_phy_clk_round_rate, + .determine_rate = fsl_samsung_hdmi_phy_clk_determine_rate, .set_rate = fsl_samsung_hdmi_phy_clk_set_rate, }; |
