18f73b37cSIoana Ciornei // SPDX-License-Identifier: GPL-2.0+
28f73b37cSIoana Ciornei /* Copyright (c) 2021-2022 NXP. */
38f73b37cSIoana Ciornei
48f73b37cSIoana Ciornei #include <linux/module.h>
57559e757SRob Herring #include <linux/of.h>
68f73b37cSIoana Ciornei #include <linux/phy.h>
78f73b37cSIoana Ciornei #include <linux/phy/phy.h>
88f73b37cSIoana Ciornei #include <linux/platform_device.h>
98f73b37cSIoana Ciornei #include <linux/workqueue.h>
108f73b37cSIoana Ciornei
118f73b37cSIoana Ciornei #define LYNX_28G_NUM_LANE 8
128f73b37cSIoana Ciornei #define LYNX_28G_NUM_PLL 2
138f73b37cSIoana Ciornei
148f73b37cSIoana Ciornei /* General registers per SerDes block */
158f73b37cSIoana Ciornei #define LYNX_28G_PCC8 0x10a0
168f73b37cSIoana Ciornei #define LYNX_28G_PCC8_SGMII 0x1
178f73b37cSIoana Ciornei #define LYNX_28G_PCC8_SGMII_DIS 0x0
188f73b37cSIoana Ciornei
198f73b37cSIoana Ciornei #define LYNX_28G_PCCC 0x10b0
208f73b37cSIoana Ciornei #define LYNX_28G_PCCC_10GBASER 0x9
218f73b37cSIoana Ciornei #define LYNX_28G_PCCC_USXGMII 0x1
228f73b37cSIoana Ciornei #define LYNX_28G_PCCC_SXGMII_DIS 0x0
238f73b37cSIoana Ciornei
248f73b37cSIoana Ciornei #define LYNX_28G_LNa_PCC_OFFSET(lane) (4 * (LYNX_28G_NUM_LANE - (lane->id) - 1))
258f73b37cSIoana Ciornei
268f73b37cSIoana Ciornei /* Per PLL registers */
278f73b37cSIoana Ciornei #define LYNX_28G_PLLnRSTCTL(pll) (0x400 + (pll) * 0x100 + 0x0)
288f73b37cSIoana Ciornei #define LYNX_28G_PLLnRSTCTL_DIS(rstctl) (((rstctl) & BIT(24)) >> 24)
298f73b37cSIoana Ciornei #define LYNX_28G_PLLnRSTCTL_LOCK(rstctl) (((rstctl) & BIT(23)) >> 23)
308f73b37cSIoana Ciornei
318f73b37cSIoana Ciornei #define LYNX_28G_PLLnCR0(pll) (0x400 + (pll) * 0x100 + 0x4)
328f73b37cSIoana Ciornei #define LYNX_28G_PLLnCR0_REFCLK_SEL(cr0) (((cr0) & GENMASK(20, 16)))
338f73b37cSIoana Ciornei #define LYNX_28G_PLLnCR0_REFCLK_SEL_100MHZ 0x0
348f73b37cSIoana Ciornei #define LYNX_28G_PLLnCR0_REFCLK_SEL_125MHZ 0x10000
358f73b37cSIoana Ciornei #define LYNX_28G_PLLnCR0_REFCLK_SEL_156MHZ 0x20000
368f73b37cSIoana Ciornei #define LYNX_28G_PLLnCR0_REFCLK_SEL_150MHZ 0x30000
378f73b37cSIoana Ciornei #define LYNX_28G_PLLnCR0_REFCLK_SEL_161MHZ 0x40000
388f73b37cSIoana Ciornei
398f73b37cSIoana Ciornei #define LYNX_28G_PLLnCR1(pll) (0x400 + (pll) * 0x100 + 0x8)
408f73b37cSIoana Ciornei #define LYNX_28G_PLLnCR1_FRATE_SEL(cr1) (((cr1) & GENMASK(28, 24)))
418f73b37cSIoana Ciornei #define LYNX_28G_PLLnCR1_FRATE_5G_10GVCO 0x0
428f73b37cSIoana Ciornei #define LYNX_28G_PLLnCR1_FRATE_5G_25GVCO 0x10000000
438f73b37cSIoana Ciornei #define LYNX_28G_PLLnCR1_FRATE_10G_20GVCO 0x6000000
448f73b37cSIoana Ciornei
458f73b37cSIoana Ciornei /* Per SerDes lane registers */
468f73b37cSIoana Ciornei /* Lane a General Control Register */
478f73b37cSIoana Ciornei #define LYNX_28G_LNaGCR0(lane) (0x800 + (lane) * 0x100 + 0x0)
488f73b37cSIoana Ciornei #define LYNX_28G_LNaGCR0_PROTO_SEL_MSK GENMASK(7, 3)
498f73b37cSIoana Ciornei #define LYNX_28G_LNaGCR0_PROTO_SEL_SGMII 0x8
508f73b37cSIoana Ciornei #define LYNX_28G_LNaGCR0_PROTO_SEL_XFI 0x50
518f73b37cSIoana Ciornei #define LYNX_28G_LNaGCR0_IF_WIDTH_MSK GENMASK(2, 0)
528f73b37cSIoana Ciornei #define LYNX_28G_LNaGCR0_IF_WIDTH_10_BIT 0x0
538f73b37cSIoana Ciornei #define LYNX_28G_LNaGCR0_IF_WIDTH_20_BIT 0x2
548f73b37cSIoana Ciornei
558f73b37cSIoana Ciornei /* Lane a Tx Reset Control Register */
568f73b37cSIoana Ciornei #define LYNX_28G_LNaTRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x20)
578f73b37cSIoana Ciornei #define LYNX_28G_LNaTRSTCTL_HLT_REQ BIT(27)
588f73b37cSIoana Ciornei #define LYNX_28G_LNaTRSTCTL_RST_DONE BIT(30)
598f73b37cSIoana Ciornei #define LYNX_28G_LNaTRSTCTL_RST_REQ BIT(31)
608f73b37cSIoana Ciornei
618f73b37cSIoana Ciornei /* Lane a Tx General Control Register */
628f73b37cSIoana Ciornei #define LYNX_28G_LNaTGCR0(lane) (0x800 + (lane) * 0x100 + 0x24)
638f73b37cSIoana Ciornei #define LYNX_28G_LNaTGCR0_USE_PLLF 0x0
648f73b37cSIoana Ciornei #define LYNX_28G_LNaTGCR0_USE_PLLS BIT(28)
658f73b37cSIoana Ciornei #define LYNX_28G_LNaTGCR0_USE_PLL_MSK BIT(28)
668f73b37cSIoana Ciornei #define LYNX_28G_LNaTGCR0_N_RATE_FULL 0x0
678f73b37cSIoana Ciornei #define LYNX_28G_LNaTGCR0_N_RATE_HALF 0x1000000
688f73b37cSIoana Ciornei #define LYNX_28G_LNaTGCR0_N_RATE_QUARTER 0x2000000
698f73b37cSIoana Ciornei #define LYNX_28G_LNaTGCR0_N_RATE_MSK GENMASK(26, 24)
708f73b37cSIoana Ciornei
718f73b37cSIoana Ciornei #define LYNX_28G_LNaTECR0(lane) (0x800 + (lane) * 0x100 + 0x30)
728f73b37cSIoana Ciornei
738f73b37cSIoana Ciornei /* Lane a Rx Reset Control Register */
748f73b37cSIoana Ciornei #define LYNX_28G_LNaRRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x40)
758f73b37cSIoana Ciornei #define LYNX_28G_LNaRRSTCTL_HLT_REQ BIT(27)
768f73b37cSIoana Ciornei #define LYNX_28G_LNaRRSTCTL_RST_DONE BIT(30)
778f73b37cSIoana Ciornei #define LYNX_28G_LNaRRSTCTL_RST_REQ BIT(31)
788f73b37cSIoana Ciornei #define LYNX_28G_LNaRRSTCTL_CDR_LOCK BIT(12)
798f73b37cSIoana Ciornei
808f73b37cSIoana Ciornei /* Lane a Rx General Control Register */
818f73b37cSIoana Ciornei #define LYNX_28G_LNaRGCR0(lane) (0x800 + (lane) * 0x100 + 0x44)
828f73b37cSIoana Ciornei #define LYNX_28G_LNaRGCR0_USE_PLLF 0x0
838f73b37cSIoana Ciornei #define LYNX_28G_LNaRGCR0_USE_PLLS BIT(28)
848f73b37cSIoana Ciornei #define LYNX_28G_LNaRGCR0_USE_PLL_MSK BIT(28)
858f73b37cSIoana Ciornei #define LYNX_28G_LNaRGCR0_N_RATE_MSK GENMASK(26, 24)
868f73b37cSIoana Ciornei #define LYNX_28G_LNaRGCR0_N_RATE_FULL 0x0
878f73b37cSIoana Ciornei #define LYNX_28G_LNaRGCR0_N_RATE_HALF 0x1000000
888f73b37cSIoana Ciornei #define LYNX_28G_LNaRGCR0_N_RATE_QUARTER 0x2000000
898f73b37cSIoana Ciornei #define LYNX_28G_LNaRGCR0_N_RATE_MSK GENMASK(26, 24)
908f73b37cSIoana Ciornei
918f73b37cSIoana Ciornei #define LYNX_28G_LNaRGCR1(lane) (0x800 + (lane) * 0x100 + 0x48)
928f73b37cSIoana Ciornei
938f73b37cSIoana Ciornei #define LYNX_28G_LNaRECR0(lane) (0x800 + (lane) * 0x100 + 0x50)
948f73b37cSIoana Ciornei #define LYNX_28G_LNaRECR1(lane) (0x800 + (lane) * 0x100 + 0x54)
958f73b37cSIoana Ciornei #define LYNX_28G_LNaRECR2(lane) (0x800 + (lane) * 0x100 + 0x58)
968f73b37cSIoana Ciornei
978f73b37cSIoana Ciornei #define LYNX_28G_LNaRSCCR0(lane) (0x800 + (lane) * 0x100 + 0x74)
988f73b37cSIoana Ciornei
998f73b37cSIoana Ciornei #define LYNX_28G_LNaPSS(lane) (0x1000 + (lane) * 0x4)
1008f73b37cSIoana Ciornei #define LYNX_28G_LNaPSS_TYPE(pss) (((pss) & GENMASK(30, 24)) >> 24)
1018f73b37cSIoana Ciornei #define LYNX_28G_LNaPSS_TYPE_SGMII 0x4
1028f73b37cSIoana Ciornei #define LYNX_28G_LNaPSS_TYPE_XFI 0x28
1038f73b37cSIoana Ciornei
1048f73b37cSIoana Ciornei #define LYNX_28G_SGMIIaCR1(lane) (0x1804 + (lane) * 0x10)
1058f73b37cSIoana Ciornei #define LYNX_28G_SGMIIaCR1_SGPCS_EN BIT(11)
1068f73b37cSIoana Ciornei #define LYNX_28G_SGMIIaCR1_SGPCS_DIS 0x0
1078f73b37cSIoana Ciornei #define LYNX_28G_SGMIIaCR1_SGPCS_MSK BIT(11)
1088f73b37cSIoana Ciornei
1098f73b37cSIoana Ciornei struct lynx_28g_priv;
1108f73b37cSIoana Ciornei
1118f73b37cSIoana Ciornei struct lynx_28g_pll {
1128f73b37cSIoana Ciornei struct lynx_28g_priv *priv;
1138f73b37cSIoana Ciornei u32 rstctl, cr0, cr1;
1148f73b37cSIoana Ciornei int id;
1158f73b37cSIoana Ciornei DECLARE_PHY_INTERFACE_MASK(supported);
1168f73b37cSIoana Ciornei };
1178f73b37cSIoana Ciornei
1188f73b37cSIoana Ciornei struct lynx_28g_lane {
1198f73b37cSIoana Ciornei struct lynx_28g_priv *priv;
1208f73b37cSIoana Ciornei struct phy *phy;
1218f73b37cSIoana Ciornei bool powered_up;
1228f73b37cSIoana Ciornei bool init;
1238f73b37cSIoana Ciornei unsigned int id;
1248f73b37cSIoana Ciornei phy_interface_t interface;
1258f73b37cSIoana Ciornei };
1268f73b37cSIoana Ciornei
1278f73b37cSIoana Ciornei struct lynx_28g_priv {
1288f73b37cSIoana Ciornei void __iomem *base;
1298f73b37cSIoana Ciornei struct device *dev;
130*139ad114SVladimir Oltean /* Serialize concurrent access to registers shared between lanes,
131*139ad114SVladimir Oltean * like PCCn
132*139ad114SVladimir Oltean */
133*139ad114SVladimir Oltean spinlock_t pcc_lock;
1348f73b37cSIoana Ciornei struct lynx_28g_pll pll[LYNX_28G_NUM_PLL];
1358f73b37cSIoana Ciornei struct lynx_28g_lane lane[LYNX_28G_NUM_LANE];
1368f73b37cSIoana Ciornei
1378f73b37cSIoana Ciornei struct delayed_work cdr_check;
1388f73b37cSIoana Ciornei };
1398f73b37cSIoana Ciornei
lynx_28g_rmw(struct lynx_28g_priv * priv,unsigned long off,u32 val,u32 mask)1408f73b37cSIoana Ciornei static void lynx_28g_rmw(struct lynx_28g_priv *priv, unsigned long off,
1418f73b37cSIoana Ciornei u32 val, u32 mask)
1428f73b37cSIoana Ciornei {
1438f73b37cSIoana Ciornei void __iomem *reg = priv->base + off;
1448f73b37cSIoana Ciornei u32 orig, tmp;
1458f73b37cSIoana Ciornei
1468f73b37cSIoana Ciornei orig = ioread32(reg);
1478f73b37cSIoana Ciornei tmp = orig & ~mask;
1488f73b37cSIoana Ciornei tmp |= val;
1498f73b37cSIoana Ciornei iowrite32(tmp, reg);
1508f73b37cSIoana Ciornei }
1518f73b37cSIoana Ciornei
1528f73b37cSIoana Ciornei #define lynx_28g_lane_rmw(lane, reg, val, mask) \
1538f73b37cSIoana Ciornei lynx_28g_rmw((lane)->priv, LYNX_28G_##reg(lane->id), \
1548f73b37cSIoana Ciornei LYNX_28G_##reg##_##val, LYNX_28G_##reg##_##mask)
1558f73b37cSIoana Ciornei #define lynx_28g_lane_read(lane, reg) \
1568f73b37cSIoana Ciornei ioread32((lane)->priv->base + LYNX_28G_##reg((lane)->id))
1578f73b37cSIoana Ciornei #define lynx_28g_pll_read(pll, reg) \
1588f73b37cSIoana Ciornei ioread32((pll)->priv->base + LYNX_28G_##reg((pll)->id))
1598f73b37cSIoana Ciornei
lynx_28g_supports_interface(struct lynx_28g_priv * priv,int intf)1608f73b37cSIoana Ciornei static bool lynx_28g_supports_interface(struct lynx_28g_priv *priv, int intf)
1618f73b37cSIoana Ciornei {
1628f73b37cSIoana Ciornei int i;
1638f73b37cSIoana Ciornei
1648f73b37cSIoana Ciornei for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
1658f73b37cSIoana Ciornei if (LYNX_28G_PLLnRSTCTL_DIS(priv->pll[i].rstctl))
1668f73b37cSIoana Ciornei continue;
1678f73b37cSIoana Ciornei
1688f73b37cSIoana Ciornei if (test_bit(intf, priv->pll[i].supported))
1698f73b37cSIoana Ciornei return true;
1708f73b37cSIoana Ciornei }
1718f73b37cSIoana Ciornei
1728f73b37cSIoana Ciornei return false;
1738f73b37cSIoana Ciornei }
1748f73b37cSIoana Ciornei
lynx_28g_pll_get(struct lynx_28g_priv * priv,phy_interface_t intf)1758f73b37cSIoana Ciornei static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv,
1768f73b37cSIoana Ciornei phy_interface_t intf)
1778f73b37cSIoana Ciornei {
1788f73b37cSIoana Ciornei struct lynx_28g_pll *pll;
1798f73b37cSIoana Ciornei int i;
1808f73b37cSIoana Ciornei
1818f73b37cSIoana Ciornei for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
1828f73b37cSIoana Ciornei pll = &priv->pll[i];
1838f73b37cSIoana Ciornei
1848f73b37cSIoana Ciornei if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl))
1858f73b37cSIoana Ciornei continue;
1868f73b37cSIoana Ciornei
1878f73b37cSIoana Ciornei if (test_bit(intf, pll->supported))
1888f73b37cSIoana Ciornei return pll;
1898f73b37cSIoana Ciornei }
1908f73b37cSIoana Ciornei
1918f73b37cSIoana Ciornei return NULL;
1928f73b37cSIoana Ciornei }
1938f73b37cSIoana Ciornei
lynx_28g_lane_set_nrate(struct lynx_28g_lane * lane,struct lynx_28g_pll * pll,phy_interface_t intf)1948f73b37cSIoana Ciornei static void lynx_28g_lane_set_nrate(struct lynx_28g_lane *lane,
1958f73b37cSIoana Ciornei struct lynx_28g_pll *pll,
1968f73b37cSIoana Ciornei phy_interface_t intf)
1978f73b37cSIoana Ciornei {
1988f73b37cSIoana Ciornei switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) {
1998f73b37cSIoana Ciornei case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO:
2008f73b37cSIoana Ciornei case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO:
2018f73b37cSIoana Ciornei switch (intf) {
2028f73b37cSIoana Ciornei case PHY_INTERFACE_MODE_SGMII:
2038f73b37cSIoana Ciornei case PHY_INTERFACE_MODE_1000BASEX:
2048f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_QUARTER, N_RATE_MSK);
2058f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_QUARTER, N_RATE_MSK);
2068f73b37cSIoana Ciornei break;
2078f73b37cSIoana Ciornei default:
2088f73b37cSIoana Ciornei break;
2098f73b37cSIoana Ciornei }
2108f73b37cSIoana Ciornei break;
2118f73b37cSIoana Ciornei case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO:
2128f73b37cSIoana Ciornei switch (intf) {
2138f73b37cSIoana Ciornei case PHY_INTERFACE_MODE_10GBASER:
2148f73b37cSIoana Ciornei case PHY_INTERFACE_MODE_USXGMII:
2158f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_FULL, N_RATE_MSK);
2168f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_FULL, N_RATE_MSK);
2178f73b37cSIoana Ciornei break;
2188f73b37cSIoana Ciornei default:
2198f73b37cSIoana Ciornei break;
2208f73b37cSIoana Ciornei }
2218f73b37cSIoana Ciornei break;
2228f73b37cSIoana Ciornei default:
2238f73b37cSIoana Ciornei break;
2248f73b37cSIoana Ciornei }
2258f73b37cSIoana Ciornei }
2268f73b37cSIoana Ciornei
lynx_28g_lane_set_pll(struct lynx_28g_lane * lane,struct lynx_28g_pll * pll)2278f73b37cSIoana Ciornei static void lynx_28g_lane_set_pll(struct lynx_28g_lane *lane,
2288f73b37cSIoana Ciornei struct lynx_28g_pll *pll)
2298f73b37cSIoana Ciornei {
2308f73b37cSIoana Ciornei if (pll->id == 0) {
2318f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLF, USE_PLL_MSK);
2328f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLF, USE_PLL_MSK);
2338f73b37cSIoana Ciornei } else {
2348f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLS, USE_PLL_MSK);
2358f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLS, USE_PLL_MSK);
2368f73b37cSIoana Ciornei }
2378f73b37cSIoana Ciornei }
2388f73b37cSIoana Ciornei
lynx_28g_cleanup_lane(struct lynx_28g_lane * lane)2398f73b37cSIoana Ciornei static void lynx_28g_cleanup_lane(struct lynx_28g_lane *lane)
2408f73b37cSIoana Ciornei {
2418f73b37cSIoana Ciornei u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
2428f73b37cSIoana Ciornei struct lynx_28g_priv *priv = lane->priv;
2438f73b37cSIoana Ciornei
2448f73b37cSIoana Ciornei /* Cleanup the protocol configuration registers of the current protocol */
2458f73b37cSIoana Ciornei switch (lane->interface) {
2468f73b37cSIoana Ciornei case PHY_INTERFACE_MODE_10GBASER:
2478f73b37cSIoana Ciornei lynx_28g_rmw(priv, LYNX_28G_PCCC,
2488f73b37cSIoana Ciornei LYNX_28G_PCCC_SXGMII_DIS << lane_offset,
2498f73b37cSIoana Ciornei GENMASK(3, 0) << lane_offset);
2508f73b37cSIoana Ciornei break;
2518f73b37cSIoana Ciornei case PHY_INTERFACE_MODE_SGMII:
2528f73b37cSIoana Ciornei case PHY_INTERFACE_MODE_1000BASEX:
2538f73b37cSIoana Ciornei lynx_28g_rmw(priv, LYNX_28G_PCC8,
2548f73b37cSIoana Ciornei LYNX_28G_PCC8_SGMII_DIS << lane_offset,
2558f73b37cSIoana Ciornei GENMASK(3, 0) << lane_offset);
2568f73b37cSIoana Ciornei break;
2578f73b37cSIoana Ciornei default:
2588f73b37cSIoana Ciornei break;
2598f73b37cSIoana Ciornei }
2608f73b37cSIoana Ciornei }
2618f73b37cSIoana Ciornei
lynx_28g_lane_set_sgmii(struct lynx_28g_lane * lane)2628f73b37cSIoana Ciornei static void lynx_28g_lane_set_sgmii(struct lynx_28g_lane *lane)
2638f73b37cSIoana Ciornei {
2648f73b37cSIoana Ciornei u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
2658f73b37cSIoana Ciornei struct lynx_28g_priv *priv = lane->priv;
2668f73b37cSIoana Ciornei struct lynx_28g_pll *pll;
2678f73b37cSIoana Ciornei
2688f73b37cSIoana Ciornei lynx_28g_cleanup_lane(lane);
2698f73b37cSIoana Ciornei
2708f73b37cSIoana Ciornei /* Setup the lane to run in SGMII */
2718f73b37cSIoana Ciornei lynx_28g_rmw(priv, LYNX_28G_PCC8,
2728f73b37cSIoana Ciornei LYNX_28G_PCC8_SGMII << lane_offset,
2738f73b37cSIoana Ciornei GENMASK(3, 0) << lane_offset);
2748f73b37cSIoana Ciornei
2758f73b37cSIoana Ciornei /* Setup the protocol select and SerDes parallel interface width */
2768f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_SGMII, PROTO_SEL_MSK);
2778f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_10_BIT, IF_WIDTH_MSK);
2788f73b37cSIoana Ciornei
2798f73b37cSIoana Ciornei /* Switch to the PLL that works with this interface type */
2808f73b37cSIoana Ciornei pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_SGMII);
2818f73b37cSIoana Ciornei lynx_28g_lane_set_pll(lane, pll);
2828f73b37cSIoana Ciornei
2838f73b37cSIoana Ciornei /* Choose the portion of clock net to be used on this lane */
2848f73b37cSIoana Ciornei lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_SGMII);
2858f73b37cSIoana Ciornei
2868f73b37cSIoana Ciornei /* Enable the SGMII PCS */
2878f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_EN, SGPCS_MSK);
2888f73b37cSIoana Ciornei
2898f73b37cSIoana Ciornei /* Configure the appropriate equalization parameters for the protocol */
2908f73b37cSIoana Ciornei iowrite32(0x00808006, priv->base + LYNX_28G_LNaTECR0(lane->id));
2918f73b37cSIoana Ciornei iowrite32(0x04310000, priv->base + LYNX_28G_LNaRGCR1(lane->id));
2928f73b37cSIoana Ciornei iowrite32(0x9f800000, priv->base + LYNX_28G_LNaRECR0(lane->id));
2938f73b37cSIoana Ciornei iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id));
2948f73b37cSIoana Ciornei iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR2(lane->id));
2958f73b37cSIoana Ciornei iowrite32(0x00000000, priv->base + LYNX_28G_LNaRSCCR0(lane->id));
2968f73b37cSIoana Ciornei }
2978f73b37cSIoana Ciornei
lynx_28g_lane_set_10gbaser(struct lynx_28g_lane * lane)2988f73b37cSIoana Ciornei static void lynx_28g_lane_set_10gbaser(struct lynx_28g_lane *lane)
2998f73b37cSIoana Ciornei {
3008f73b37cSIoana Ciornei u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
3018f73b37cSIoana Ciornei struct lynx_28g_priv *priv = lane->priv;
3028f73b37cSIoana Ciornei struct lynx_28g_pll *pll;
3038f73b37cSIoana Ciornei
3048f73b37cSIoana Ciornei lynx_28g_cleanup_lane(lane);
3058f73b37cSIoana Ciornei
3068f73b37cSIoana Ciornei /* Enable the SXGMII lane */
3078f73b37cSIoana Ciornei lynx_28g_rmw(priv, LYNX_28G_PCCC,
3088f73b37cSIoana Ciornei LYNX_28G_PCCC_10GBASER << lane_offset,
3098f73b37cSIoana Ciornei GENMASK(3, 0) << lane_offset);
3108f73b37cSIoana Ciornei
3118f73b37cSIoana Ciornei /* Setup the protocol select and SerDes parallel interface width */
3128f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_XFI, PROTO_SEL_MSK);
3138f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_20_BIT, IF_WIDTH_MSK);
3148f73b37cSIoana Ciornei
3158f73b37cSIoana Ciornei /* Switch to the PLL that works with this interface type */
3168f73b37cSIoana Ciornei pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_10GBASER);
3178f73b37cSIoana Ciornei lynx_28g_lane_set_pll(lane, pll);
3188f73b37cSIoana Ciornei
3198f73b37cSIoana Ciornei /* Choose the portion of clock net to be used on this lane */
3208f73b37cSIoana Ciornei lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_10GBASER);
3218f73b37cSIoana Ciornei
3228f73b37cSIoana Ciornei /* Disable the SGMII PCS */
3238f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_DIS, SGPCS_MSK);
3248f73b37cSIoana Ciornei
3258f73b37cSIoana Ciornei /* Configure the appropriate equalization parameters for the protocol */
3268f73b37cSIoana Ciornei iowrite32(0x10808307, priv->base + LYNX_28G_LNaTECR0(lane->id));
3278f73b37cSIoana Ciornei iowrite32(0x10000000, priv->base + LYNX_28G_LNaRGCR1(lane->id));
3288f73b37cSIoana Ciornei iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR0(lane->id));
3298f73b37cSIoana Ciornei iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id));
3308f73b37cSIoana Ciornei iowrite32(0x81000020, priv->base + LYNX_28G_LNaRECR2(lane->id));
3318f73b37cSIoana Ciornei iowrite32(0x00002000, priv->base + LYNX_28G_LNaRSCCR0(lane->id));
3328f73b37cSIoana Ciornei }
3338f73b37cSIoana Ciornei
lynx_28g_power_off(struct phy * phy)3348f73b37cSIoana Ciornei static int lynx_28g_power_off(struct phy *phy)
3358f73b37cSIoana Ciornei {
3368f73b37cSIoana Ciornei struct lynx_28g_lane *lane = phy_get_drvdata(phy);
3378f73b37cSIoana Ciornei u32 trstctl, rrstctl;
3388f73b37cSIoana Ciornei
3398f73b37cSIoana Ciornei if (!lane->powered_up)
3408f73b37cSIoana Ciornei return 0;
3418f73b37cSIoana Ciornei
3428f73b37cSIoana Ciornei /* Issue a halt request */
3438f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaTRSTCTL, HLT_REQ, HLT_REQ);
3448f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaRRSTCTL, HLT_REQ, HLT_REQ);
3458f73b37cSIoana Ciornei
3468f73b37cSIoana Ciornei /* Wait until the halting process is complete */
3478f73b37cSIoana Ciornei do {
3488f73b37cSIoana Ciornei trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL);
3498f73b37cSIoana Ciornei rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
3508f73b37cSIoana Ciornei } while ((trstctl & LYNX_28G_LNaTRSTCTL_HLT_REQ) ||
3518f73b37cSIoana Ciornei (rrstctl & LYNX_28G_LNaRRSTCTL_HLT_REQ));
3528f73b37cSIoana Ciornei
3538f73b37cSIoana Ciornei lane->powered_up = false;
3548f73b37cSIoana Ciornei
3558f73b37cSIoana Ciornei return 0;
3568f73b37cSIoana Ciornei }
3578f73b37cSIoana Ciornei
lynx_28g_power_on(struct phy * phy)3588f73b37cSIoana Ciornei static int lynx_28g_power_on(struct phy *phy)
3598f73b37cSIoana Ciornei {
3608f73b37cSIoana Ciornei struct lynx_28g_lane *lane = phy_get_drvdata(phy);
3618f73b37cSIoana Ciornei u32 trstctl, rrstctl;
3628f73b37cSIoana Ciornei
3638f73b37cSIoana Ciornei if (lane->powered_up)
3648f73b37cSIoana Ciornei return 0;
3658f73b37cSIoana Ciornei
3668f73b37cSIoana Ciornei /* Issue a reset request on the lane */
3678f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaTRSTCTL, RST_REQ, RST_REQ);
3688f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ);
3698f73b37cSIoana Ciornei
3708f73b37cSIoana Ciornei /* Wait until the reset sequence is completed */
3718f73b37cSIoana Ciornei do {
3728f73b37cSIoana Ciornei trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL);
3738f73b37cSIoana Ciornei rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
3748f73b37cSIoana Ciornei } while (!(trstctl & LYNX_28G_LNaTRSTCTL_RST_DONE) ||
3758f73b37cSIoana Ciornei !(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE));
3768f73b37cSIoana Ciornei
3778f73b37cSIoana Ciornei lane->powered_up = true;
3788f73b37cSIoana Ciornei
3798f73b37cSIoana Ciornei return 0;
3808f73b37cSIoana Ciornei }
3818f73b37cSIoana Ciornei
lynx_28g_set_mode(struct phy * phy,enum phy_mode mode,int submode)3828f73b37cSIoana Ciornei static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
3838f73b37cSIoana Ciornei {
3848f73b37cSIoana Ciornei struct lynx_28g_lane *lane = phy_get_drvdata(phy);
3858f73b37cSIoana Ciornei struct lynx_28g_priv *priv = lane->priv;
3868f73b37cSIoana Ciornei int powered_up = lane->powered_up;
3878f73b37cSIoana Ciornei int err = 0;
3888f73b37cSIoana Ciornei
3898f73b37cSIoana Ciornei if (mode != PHY_MODE_ETHERNET)
3908f73b37cSIoana Ciornei return -EOPNOTSUPP;
3918f73b37cSIoana Ciornei
3928f73b37cSIoana Ciornei if (lane->interface == PHY_INTERFACE_MODE_NA)
3938f73b37cSIoana Ciornei return -EOPNOTSUPP;
3948f73b37cSIoana Ciornei
3958f73b37cSIoana Ciornei if (!lynx_28g_supports_interface(priv, submode))
3968f73b37cSIoana Ciornei return -EOPNOTSUPP;
3978f73b37cSIoana Ciornei
3988f73b37cSIoana Ciornei /* If the lane is powered up, put the lane into the halt state while
3998f73b37cSIoana Ciornei * the reconfiguration is being done.
4008f73b37cSIoana Ciornei */
4018f73b37cSIoana Ciornei if (powered_up)
4028f73b37cSIoana Ciornei lynx_28g_power_off(phy);
4038f73b37cSIoana Ciornei
404*139ad114SVladimir Oltean spin_lock(&priv->pcc_lock);
405*139ad114SVladimir Oltean
4068f73b37cSIoana Ciornei switch (submode) {
4078f73b37cSIoana Ciornei case PHY_INTERFACE_MODE_SGMII:
4088f73b37cSIoana Ciornei case PHY_INTERFACE_MODE_1000BASEX:
4098f73b37cSIoana Ciornei lynx_28g_lane_set_sgmii(lane);
4108f73b37cSIoana Ciornei break;
4118f73b37cSIoana Ciornei case PHY_INTERFACE_MODE_10GBASER:
4128f73b37cSIoana Ciornei lynx_28g_lane_set_10gbaser(lane);
4138f73b37cSIoana Ciornei break;
4148f73b37cSIoana Ciornei default:
4158f73b37cSIoana Ciornei err = -EOPNOTSUPP;
4168f73b37cSIoana Ciornei goto out;
4178f73b37cSIoana Ciornei }
4188f73b37cSIoana Ciornei
4198f73b37cSIoana Ciornei lane->interface = submode;
4208f73b37cSIoana Ciornei
4218f73b37cSIoana Ciornei out:
422*139ad114SVladimir Oltean spin_unlock(&priv->pcc_lock);
423*139ad114SVladimir Oltean
4248f73b37cSIoana Ciornei /* Power up the lane if necessary */
4258f73b37cSIoana Ciornei if (powered_up)
4268f73b37cSIoana Ciornei lynx_28g_power_on(phy);
4278f73b37cSIoana Ciornei
4288f73b37cSIoana Ciornei return err;
4298f73b37cSIoana Ciornei }
4308f73b37cSIoana Ciornei
lynx_28g_validate(struct phy * phy,enum phy_mode mode,int submode,union phy_configure_opts * opts __always_unused)4318f73b37cSIoana Ciornei static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode,
4328f73b37cSIoana Ciornei union phy_configure_opts *opts __always_unused)
4338f73b37cSIoana Ciornei {
4348f73b37cSIoana Ciornei struct lynx_28g_lane *lane = phy_get_drvdata(phy);
4358f73b37cSIoana Ciornei struct lynx_28g_priv *priv = lane->priv;
4368f73b37cSIoana Ciornei
4378f73b37cSIoana Ciornei if (mode != PHY_MODE_ETHERNET)
4388f73b37cSIoana Ciornei return -EOPNOTSUPP;
4398f73b37cSIoana Ciornei
4408f73b37cSIoana Ciornei if (!lynx_28g_supports_interface(priv, submode))
4418f73b37cSIoana Ciornei return -EOPNOTSUPP;
4428f73b37cSIoana Ciornei
4438f73b37cSIoana Ciornei return 0;
4448f73b37cSIoana Ciornei }
4458f73b37cSIoana Ciornei
lynx_28g_init(struct phy * phy)4468f73b37cSIoana Ciornei static int lynx_28g_init(struct phy *phy)
4478f73b37cSIoana Ciornei {
4488f73b37cSIoana Ciornei struct lynx_28g_lane *lane = phy_get_drvdata(phy);
4498f73b37cSIoana Ciornei
4508f73b37cSIoana Ciornei /* Mark the fact that the lane was init */
4518f73b37cSIoana Ciornei lane->init = true;
4528f73b37cSIoana Ciornei
4538f73b37cSIoana Ciornei /* SerDes lanes are powered on at boot time. Any lane that is managed
4548f73b37cSIoana Ciornei * by this driver will get powered down at init time aka at dpaa2-eth
4558f73b37cSIoana Ciornei * probe time.
4568f73b37cSIoana Ciornei */
4578f73b37cSIoana Ciornei lane->powered_up = true;
4588f73b37cSIoana Ciornei lynx_28g_power_off(phy);
4598f73b37cSIoana Ciornei
4608f73b37cSIoana Ciornei return 0;
4618f73b37cSIoana Ciornei }
4628f73b37cSIoana Ciornei
4638f73b37cSIoana Ciornei static const struct phy_ops lynx_28g_ops = {
4648f73b37cSIoana Ciornei .init = lynx_28g_init,
4658f73b37cSIoana Ciornei .power_on = lynx_28g_power_on,
4668f73b37cSIoana Ciornei .power_off = lynx_28g_power_off,
4678f73b37cSIoana Ciornei .set_mode = lynx_28g_set_mode,
4688f73b37cSIoana Ciornei .validate = lynx_28g_validate,
4698f73b37cSIoana Ciornei .owner = THIS_MODULE,
4708f73b37cSIoana Ciornei };
4718f73b37cSIoana Ciornei
lynx_28g_pll_read_configuration(struct lynx_28g_priv * priv)4728f73b37cSIoana Ciornei static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv)
4738f73b37cSIoana Ciornei {
4748f73b37cSIoana Ciornei struct lynx_28g_pll *pll;
4758f73b37cSIoana Ciornei int i;
4768f73b37cSIoana Ciornei
4778f73b37cSIoana Ciornei for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
4788f73b37cSIoana Ciornei pll = &priv->pll[i];
4798f73b37cSIoana Ciornei pll->priv = priv;
4808f73b37cSIoana Ciornei pll->id = i;
4818f73b37cSIoana Ciornei
4828f73b37cSIoana Ciornei pll->rstctl = lynx_28g_pll_read(pll, PLLnRSTCTL);
4838f73b37cSIoana Ciornei pll->cr0 = lynx_28g_pll_read(pll, PLLnCR0);
4848f73b37cSIoana Ciornei pll->cr1 = lynx_28g_pll_read(pll, PLLnCR1);
4858f73b37cSIoana Ciornei
4868f73b37cSIoana Ciornei if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl))
4878f73b37cSIoana Ciornei continue;
4888f73b37cSIoana Ciornei
4898f73b37cSIoana Ciornei switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) {
4908f73b37cSIoana Ciornei case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO:
4918f73b37cSIoana Ciornei case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO:
4928f73b37cSIoana Ciornei /* 5GHz clock net */
4938f73b37cSIoana Ciornei __set_bit(PHY_INTERFACE_MODE_1000BASEX, pll->supported);
4948f73b37cSIoana Ciornei __set_bit(PHY_INTERFACE_MODE_SGMII, pll->supported);
4958f73b37cSIoana Ciornei break;
4968f73b37cSIoana Ciornei case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO:
4978f73b37cSIoana Ciornei /* 10.3125GHz clock net */
4988f73b37cSIoana Ciornei __set_bit(PHY_INTERFACE_MODE_10GBASER, pll->supported);
4998f73b37cSIoana Ciornei break;
5008f73b37cSIoana Ciornei default:
5018f73b37cSIoana Ciornei /* 6GHz, 12.890625GHz, 8GHz */
5028f73b37cSIoana Ciornei break;
5038f73b37cSIoana Ciornei }
5048f73b37cSIoana Ciornei }
5058f73b37cSIoana Ciornei }
5068f73b37cSIoana Ciornei
5078f73b37cSIoana Ciornei #define work_to_lynx(w) container_of((w), struct lynx_28g_priv, cdr_check.work)
5088f73b37cSIoana Ciornei
lynx_28g_cdr_lock_check(struct work_struct * work)5098f73b37cSIoana Ciornei static void lynx_28g_cdr_lock_check(struct work_struct *work)
5108f73b37cSIoana Ciornei {
5118f73b37cSIoana Ciornei struct lynx_28g_priv *priv = work_to_lynx(work);
5128f73b37cSIoana Ciornei struct lynx_28g_lane *lane;
5138f73b37cSIoana Ciornei u32 rrstctl;
5148f73b37cSIoana Ciornei int i;
5158f73b37cSIoana Ciornei
5168f73b37cSIoana Ciornei for (i = 0; i < LYNX_28G_NUM_LANE; i++) {
5178f73b37cSIoana Ciornei lane = &priv->lane[i];
5188f73b37cSIoana Ciornei
5190ac87fe5SVladimir Oltean mutex_lock(&lane->phy->mutex);
5208f73b37cSIoana Ciornei
5210ac87fe5SVladimir Oltean if (!lane->init || !lane->powered_up) {
5220ac87fe5SVladimir Oltean mutex_unlock(&lane->phy->mutex);
5238f73b37cSIoana Ciornei continue;
5240ac87fe5SVladimir Oltean }
5258f73b37cSIoana Ciornei
5268f73b37cSIoana Ciornei rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
5278f73b37cSIoana Ciornei if (!(rrstctl & LYNX_28G_LNaRRSTCTL_CDR_LOCK)) {
5288f73b37cSIoana Ciornei lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ);
5298f73b37cSIoana Ciornei do {
5308f73b37cSIoana Ciornei rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
5318f73b37cSIoana Ciornei } while (!(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE));
5328f73b37cSIoana Ciornei }
5330ac87fe5SVladimir Oltean
5340ac87fe5SVladimir Oltean mutex_unlock(&lane->phy->mutex);
5358f73b37cSIoana Ciornei }
5368f73b37cSIoana Ciornei queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
5378f73b37cSIoana Ciornei msecs_to_jiffies(1000));
5388f73b37cSIoana Ciornei }
5398f73b37cSIoana Ciornei
lynx_28g_lane_read_configuration(struct lynx_28g_lane * lane)5408f73b37cSIoana Ciornei static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane)
5418f73b37cSIoana Ciornei {
5428f73b37cSIoana Ciornei u32 pss, protocol;
5438f73b37cSIoana Ciornei
5448f73b37cSIoana Ciornei pss = lynx_28g_lane_read(lane, LNaPSS);
5458f73b37cSIoana Ciornei protocol = LYNX_28G_LNaPSS_TYPE(pss);
5468f73b37cSIoana Ciornei switch (protocol) {
5478f73b37cSIoana Ciornei case LYNX_28G_LNaPSS_TYPE_SGMII:
5488f73b37cSIoana Ciornei lane->interface = PHY_INTERFACE_MODE_SGMII;
5498f73b37cSIoana Ciornei break;
5508f73b37cSIoana Ciornei case LYNX_28G_LNaPSS_TYPE_XFI:
5518f73b37cSIoana Ciornei lane->interface = PHY_INTERFACE_MODE_10GBASER;
5528f73b37cSIoana Ciornei break;
5538f73b37cSIoana Ciornei default:
5548f73b37cSIoana Ciornei lane->interface = PHY_INTERFACE_MODE_NA;
5558f73b37cSIoana Ciornei }
5568f73b37cSIoana Ciornei }
5578f73b37cSIoana Ciornei
lynx_28g_xlate(struct device * dev,struct of_phandle_args * args)5588f73b37cSIoana Ciornei static struct phy *lynx_28g_xlate(struct device *dev,
5598f73b37cSIoana Ciornei struct of_phandle_args *args)
5608f73b37cSIoana Ciornei {
5618f73b37cSIoana Ciornei struct lynx_28g_priv *priv = dev_get_drvdata(dev);
5628f73b37cSIoana Ciornei int idx = args->args[0];
5638f73b37cSIoana Ciornei
5648f73b37cSIoana Ciornei if (WARN_ON(idx >= LYNX_28G_NUM_LANE))
5658f73b37cSIoana Ciornei return ERR_PTR(-EINVAL);
5668f73b37cSIoana Ciornei
5678f73b37cSIoana Ciornei return priv->lane[idx].phy;
5688f73b37cSIoana Ciornei }
5698f73b37cSIoana Ciornei
lynx_28g_probe(struct platform_device * pdev)5708f73b37cSIoana Ciornei static int lynx_28g_probe(struct platform_device *pdev)
5718f73b37cSIoana Ciornei {
5728f73b37cSIoana Ciornei struct device *dev = &pdev->dev;
5738f73b37cSIoana Ciornei struct phy_provider *provider;
5748f73b37cSIoana Ciornei struct lynx_28g_priv *priv;
5758f73b37cSIoana Ciornei int i;
5768f73b37cSIoana Ciornei
5778f73b37cSIoana Ciornei priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
5788f73b37cSIoana Ciornei if (!priv)
5798f73b37cSIoana Ciornei return -ENOMEM;
5808f73b37cSIoana Ciornei priv->dev = &pdev->dev;
5818f73b37cSIoana Ciornei
5828f73b37cSIoana Ciornei priv->base = devm_platform_ioremap_resource(pdev, 0);
5838f73b37cSIoana Ciornei if (IS_ERR(priv->base))
5848f73b37cSIoana Ciornei return PTR_ERR(priv->base);
5858f73b37cSIoana Ciornei
5868f73b37cSIoana Ciornei lynx_28g_pll_read_configuration(priv);
5878f73b37cSIoana Ciornei
5888f73b37cSIoana Ciornei for (i = 0; i < LYNX_28G_NUM_LANE; i++) {
5898f73b37cSIoana Ciornei struct lynx_28g_lane *lane = &priv->lane[i];
5908f73b37cSIoana Ciornei struct phy *phy;
5918f73b37cSIoana Ciornei
5928f73b37cSIoana Ciornei memset(lane, 0, sizeof(*lane));
5938f73b37cSIoana Ciornei
5948f73b37cSIoana Ciornei phy = devm_phy_create(&pdev->dev, NULL, &lynx_28g_ops);
5958f73b37cSIoana Ciornei if (IS_ERR(phy))
5968f73b37cSIoana Ciornei return PTR_ERR(phy);
5978f73b37cSIoana Ciornei
5988f73b37cSIoana Ciornei lane->priv = priv;
5998f73b37cSIoana Ciornei lane->phy = phy;
6008f73b37cSIoana Ciornei lane->id = i;
6018f73b37cSIoana Ciornei phy_set_drvdata(phy, lane);
6028f73b37cSIoana Ciornei lynx_28g_lane_read_configuration(lane);
6038f73b37cSIoana Ciornei }
6048f73b37cSIoana Ciornei
6058f73b37cSIoana Ciornei dev_set_drvdata(dev, priv);
6068f73b37cSIoana Ciornei
607*139ad114SVladimir Oltean spin_lock_init(&priv->pcc_lock);
6088f73b37cSIoana Ciornei INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check);
6098f73b37cSIoana Ciornei
6108f73b37cSIoana Ciornei queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
6118f73b37cSIoana Ciornei msecs_to_jiffies(1000));
6128f73b37cSIoana Ciornei
6138f73b37cSIoana Ciornei dev_set_drvdata(&pdev->dev, priv);
6148f73b37cSIoana Ciornei provider = devm_of_phy_provider_register(&pdev->dev, lynx_28g_xlate);
6158f73b37cSIoana Ciornei
6168f73b37cSIoana Ciornei return PTR_ERR_OR_ZERO(provider);
6178f73b37cSIoana Ciornei }
6188f73b37cSIoana Ciornei
lynx_28g_remove(struct platform_device * pdev)619f200bab3SIoana Ciornei static void lynx_28g_remove(struct platform_device *pdev)
620f200bab3SIoana Ciornei {
621f200bab3SIoana Ciornei struct device *dev = &pdev->dev;
622f200bab3SIoana Ciornei struct lynx_28g_priv *priv = dev_get_drvdata(dev);
623f200bab3SIoana Ciornei
624f200bab3SIoana Ciornei cancel_delayed_work_sync(&priv->cdr_check);
625f200bab3SIoana Ciornei }
626f200bab3SIoana Ciornei
6278f73b37cSIoana Ciornei static const struct of_device_id lynx_28g_of_match_table[] = {
6288f73b37cSIoana Ciornei { .compatible = "fsl,lynx-28g" },
6298f73b37cSIoana Ciornei { },
6308f73b37cSIoana Ciornei };
6318f73b37cSIoana Ciornei MODULE_DEVICE_TABLE(of, lynx_28g_of_match_table);
6328f73b37cSIoana Ciornei
6338f73b37cSIoana Ciornei static struct platform_driver lynx_28g_driver = {
6348f73b37cSIoana Ciornei .probe = lynx_28g_probe,
635f200bab3SIoana Ciornei .remove_new = lynx_28g_remove,
6368f73b37cSIoana Ciornei .driver = {
6378f73b37cSIoana Ciornei .name = "lynx-28g",
6388f73b37cSIoana Ciornei .of_match_table = lynx_28g_of_match_table,
6398f73b37cSIoana Ciornei },
6408f73b37cSIoana Ciornei };
6418f73b37cSIoana Ciornei module_platform_driver(lynx_28g_driver);
6428f73b37cSIoana Ciornei
6438f73b37cSIoana Ciornei MODULE_AUTHOR("Ioana Ciornei <ioana.ciornei@nxp.com>");
6448f73b37cSIoana Ciornei MODULE_DESCRIPTION("Lynx 28G SerDes PHY driver for Layerscape SoCs");
6458f73b37cSIoana Ciornei MODULE_LICENSE("GPL v2");
646