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;
1308f73b37cSIoana Ciornei 	struct lynx_28g_pll pll[LYNX_28G_NUM_PLL];
1318f73b37cSIoana Ciornei 	struct lynx_28g_lane lane[LYNX_28G_NUM_LANE];
1328f73b37cSIoana Ciornei 
1338f73b37cSIoana Ciornei 	struct delayed_work cdr_check;
1348f73b37cSIoana Ciornei };
1358f73b37cSIoana Ciornei 
1368f73b37cSIoana Ciornei static void lynx_28g_rmw(struct lynx_28g_priv *priv, unsigned long off,
1378f73b37cSIoana Ciornei 			 u32 val, u32 mask)
1388f73b37cSIoana Ciornei {
1398f73b37cSIoana Ciornei 	void __iomem *reg = priv->base + off;
1408f73b37cSIoana Ciornei 	u32 orig, tmp;
1418f73b37cSIoana Ciornei 
1428f73b37cSIoana Ciornei 	orig = ioread32(reg);
1438f73b37cSIoana Ciornei 	tmp = orig & ~mask;
1448f73b37cSIoana Ciornei 	tmp |= val;
1458f73b37cSIoana Ciornei 	iowrite32(tmp, reg);
1468f73b37cSIoana Ciornei }
1478f73b37cSIoana Ciornei 
1488f73b37cSIoana Ciornei #define lynx_28g_lane_rmw(lane, reg, val, mask)	\
1498f73b37cSIoana Ciornei 	lynx_28g_rmw((lane)->priv, LYNX_28G_##reg(lane->id), \
1508f73b37cSIoana Ciornei 		     LYNX_28G_##reg##_##val, LYNX_28G_##reg##_##mask)
1518f73b37cSIoana Ciornei #define lynx_28g_lane_read(lane, reg)			\
1528f73b37cSIoana Ciornei 	ioread32((lane)->priv->base + LYNX_28G_##reg((lane)->id))
1538f73b37cSIoana Ciornei #define lynx_28g_pll_read(pll, reg)			\
1548f73b37cSIoana Ciornei 	ioread32((pll)->priv->base + LYNX_28G_##reg((pll)->id))
1558f73b37cSIoana Ciornei 
1568f73b37cSIoana Ciornei static bool lynx_28g_supports_interface(struct lynx_28g_priv *priv, int intf)
1578f73b37cSIoana Ciornei {
1588f73b37cSIoana Ciornei 	int i;
1598f73b37cSIoana Ciornei 
1608f73b37cSIoana Ciornei 	for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
1618f73b37cSIoana Ciornei 		if (LYNX_28G_PLLnRSTCTL_DIS(priv->pll[i].rstctl))
1628f73b37cSIoana Ciornei 			continue;
1638f73b37cSIoana Ciornei 
1648f73b37cSIoana Ciornei 		if (test_bit(intf, priv->pll[i].supported))
1658f73b37cSIoana Ciornei 			return true;
1668f73b37cSIoana Ciornei 	}
1678f73b37cSIoana Ciornei 
1688f73b37cSIoana Ciornei 	return false;
1698f73b37cSIoana Ciornei }
1708f73b37cSIoana Ciornei 
1718f73b37cSIoana Ciornei static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv,
1728f73b37cSIoana Ciornei 					     phy_interface_t intf)
1738f73b37cSIoana Ciornei {
1748f73b37cSIoana Ciornei 	struct lynx_28g_pll *pll;
1758f73b37cSIoana Ciornei 	int i;
1768f73b37cSIoana Ciornei 
1778f73b37cSIoana Ciornei 	for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
1788f73b37cSIoana Ciornei 		pll = &priv->pll[i];
1798f73b37cSIoana Ciornei 
1808f73b37cSIoana Ciornei 		if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl))
1818f73b37cSIoana Ciornei 			continue;
1828f73b37cSIoana Ciornei 
1838f73b37cSIoana Ciornei 		if (test_bit(intf, pll->supported))
1848f73b37cSIoana Ciornei 			return pll;
1858f73b37cSIoana Ciornei 	}
1868f73b37cSIoana Ciornei 
1878f73b37cSIoana Ciornei 	return NULL;
1888f73b37cSIoana Ciornei }
1898f73b37cSIoana Ciornei 
1908f73b37cSIoana Ciornei static void lynx_28g_lane_set_nrate(struct lynx_28g_lane *lane,
1918f73b37cSIoana Ciornei 				    struct lynx_28g_pll *pll,
1928f73b37cSIoana Ciornei 				    phy_interface_t intf)
1938f73b37cSIoana Ciornei {
1948f73b37cSIoana Ciornei 	switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) {
1958f73b37cSIoana Ciornei 	case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO:
1968f73b37cSIoana Ciornei 	case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO:
1978f73b37cSIoana Ciornei 		switch (intf) {
1988f73b37cSIoana Ciornei 		case PHY_INTERFACE_MODE_SGMII:
1998f73b37cSIoana Ciornei 		case PHY_INTERFACE_MODE_1000BASEX:
2008f73b37cSIoana Ciornei 			lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_QUARTER, N_RATE_MSK);
2018f73b37cSIoana Ciornei 			lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_QUARTER, N_RATE_MSK);
2028f73b37cSIoana Ciornei 			break;
2038f73b37cSIoana Ciornei 		default:
2048f73b37cSIoana Ciornei 			break;
2058f73b37cSIoana Ciornei 		}
2068f73b37cSIoana Ciornei 		break;
2078f73b37cSIoana Ciornei 	case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO:
2088f73b37cSIoana Ciornei 		switch (intf) {
2098f73b37cSIoana Ciornei 		case PHY_INTERFACE_MODE_10GBASER:
2108f73b37cSIoana Ciornei 		case PHY_INTERFACE_MODE_USXGMII:
2118f73b37cSIoana Ciornei 			lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_FULL, N_RATE_MSK);
2128f73b37cSIoana Ciornei 			lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_FULL, N_RATE_MSK);
2138f73b37cSIoana Ciornei 			break;
2148f73b37cSIoana Ciornei 		default:
2158f73b37cSIoana Ciornei 			break;
2168f73b37cSIoana Ciornei 		}
2178f73b37cSIoana Ciornei 		break;
2188f73b37cSIoana Ciornei 	default:
2198f73b37cSIoana Ciornei 		break;
2208f73b37cSIoana Ciornei 	}
2218f73b37cSIoana Ciornei }
2228f73b37cSIoana Ciornei 
2238f73b37cSIoana Ciornei static void lynx_28g_lane_set_pll(struct lynx_28g_lane *lane,
2248f73b37cSIoana Ciornei 				  struct lynx_28g_pll *pll)
2258f73b37cSIoana Ciornei {
2268f73b37cSIoana Ciornei 	if (pll->id == 0) {
2278f73b37cSIoana Ciornei 		lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLF, USE_PLL_MSK);
2288f73b37cSIoana Ciornei 		lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLF, USE_PLL_MSK);
2298f73b37cSIoana Ciornei 	} else {
2308f73b37cSIoana Ciornei 		lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLS, USE_PLL_MSK);
2318f73b37cSIoana Ciornei 		lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLS, USE_PLL_MSK);
2328f73b37cSIoana Ciornei 	}
2338f73b37cSIoana Ciornei }
2348f73b37cSIoana Ciornei 
2358f73b37cSIoana Ciornei static void lynx_28g_cleanup_lane(struct lynx_28g_lane *lane)
2368f73b37cSIoana Ciornei {
2378f73b37cSIoana Ciornei 	u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
2388f73b37cSIoana Ciornei 	struct lynx_28g_priv *priv = lane->priv;
2398f73b37cSIoana Ciornei 
2408f73b37cSIoana Ciornei 	/* Cleanup the protocol configuration registers of the current protocol */
2418f73b37cSIoana Ciornei 	switch (lane->interface) {
2428f73b37cSIoana Ciornei 	case PHY_INTERFACE_MODE_10GBASER:
2438f73b37cSIoana Ciornei 		lynx_28g_rmw(priv, LYNX_28G_PCCC,
2448f73b37cSIoana Ciornei 			     LYNX_28G_PCCC_SXGMII_DIS << lane_offset,
2458f73b37cSIoana Ciornei 			     GENMASK(3, 0) << lane_offset);
2468f73b37cSIoana Ciornei 		break;
2478f73b37cSIoana Ciornei 	case PHY_INTERFACE_MODE_SGMII:
2488f73b37cSIoana Ciornei 	case PHY_INTERFACE_MODE_1000BASEX:
2498f73b37cSIoana Ciornei 		lynx_28g_rmw(priv, LYNX_28G_PCC8,
2508f73b37cSIoana Ciornei 			     LYNX_28G_PCC8_SGMII_DIS << lane_offset,
2518f73b37cSIoana Ciornei 			     GENMASK(3, 0) << lane_offset);
2528f73b37cSIoana Ciornei 		break;
2538f73b37cSIoana Ciornei 	default:
2548f73b37cSIoana Ciornei 		break;
2558f73b37cSIoana Ciornei 	}
2568f73b37cSIoana Ciornei }
2578f73b37cSIoana Ciornei 
2588f73b37cSIoana Ciornei static void lynx_28g_lane_set_sgmii(struct lynx_28g_lane *lane)
2598f73b37cSIoana Ciornei {
2608f73b37cSIoana Ciornei 	u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
2618f73b37cSIoana Ciornei 	struct lynx_28g_priv *priv = lane->priv;
2628f73b37cSIoana Ciornei 	struct lynx_28g_pll *pll;
2638f73b37cSIoana Ciornei 
2648f73b37cSIoana Ciornei 	lynx_28g_cleanup_lane(lane);
2658f73b37cSIoana Ciornei 
2668f73b37cSIoana Ciornei 	/* Setup the lane to run in SGMII */
2678f73b37cSIoana Ciornei 	lynx_28g_rmw(priv, LYNX_28G_PCC8,
2688f73b37cSIoana Ciornei 		     LYNX_28G_PCC8_SGMII << lane_offset,
2698f73b37cSIoana Ciornei 		     GENMASK(3, 0) << lane_offset);
2708f73b37cSIoana Ciornei 
2718f73b37cSIoana Ciornei 	/* Setup the protocol select and SerDes parallel interface width */
2728f73b37cSIoana Ciornei 	lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_SGMII, PROTO_SEL_MSK);
2738f73b37cSIoana Ciornei 	lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_10_BIT, IF_WIDTH_MSK);
2748f73b37cSIoana Ciornei 
2758f73b37cSIoana Ciornei 	/* Switch to the PLL that works with this interface type */
2768f73b37cSIoana Ciornei 	pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_SGMII);
2778f73b37cSIoana Ciornei 	lynx_28g_lane_set_pll(lane, pll);
2788f73b37cSIoana Ciornei 
2798f73b37cSIoana Ciornei 	/* Choose the portion of clock net to be used on this lane */
2808f73b37cSIoana Ciornei 	lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_SGMII);
2818f73b37cSIoana Ciornei 
2828f73b37cSIoana Ciornei 	/* Enable the SGMII PCS */
2838f73b37cSIoana Ciornei 	lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_EN, SGPCS_MSK);
2848f73b37cSIoana Ciornei 
2858f73b37cSIoana Ciornei 	/* Configure the appropriate equalization parameters for the protocol */
2868f73b37cSIoana Ciornei 	iowrite32(0x00808006, priv->base + LYNX_28G_LNaTECR0(lane->id));
2878f73b37cSIoana Ciornei 	iowrite32(0x04310000, priv->base + LYNX_28G_LNaRGCR1(lane->id));
2888f73b37cSIoana Ciornei 	iowrite32(0x9f800000, priv->base + LYNX_28G_LNaRECR0(lane->id));
2898f73b37cSIoana Ciornei 	iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id));
2908f73b37cSIoana Ciornei 	iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR2(lane->id));
2918f73b37cSIoana Ciornei 	iowrite32(0x00000000, priv->base + LYNX_28G_LNaRSCCR0(lane->id));
2928f73b37cSIoana Ciornei }
2938f73b37cSIoana Ciornei 
2948f73b37cSIoana Ciornei static void lynx_28g_lane_set_10gbaser(struct lynx_28g_lane *lane)
2958f73b37cSIoana Ciornei {
2968f73b37cSIoana Ciornei 	u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
2978f73b37cSIoana Ciornei 	struct lynx_28g_priv *priv = lane->priv;
2988f73b37cSIoana Ciornei 	struct lynx_28g_pll *pll;
2998f73b37cSIoana Ciornei 
3008f73b37cSIoana Ciornei 	lynx_28g_cleanup_lane(lane);
3018f73b37cSIoana Ciornei 
3028f73b37cSIoana Ciornei 	/* Enable the SXGMII lane */
3038f73b37cSIoana Ciornei 	lynx_28g_rmw(priv, LYNX_28G_PCCC,
3048f73b37cSIoana Ciornei 		     LYNX_28G_PCCC_10GBASER << lane_offset,
3058f73b37cSIoana Ciornei 		     GENMASK(3, 0) << lane_offset);
3068f73b37cSIoana Ciornei 
3078f73b37cSIoana Ciornei 	/* Setup the protocol select and SerDes parallel interface width */
3088f73b37cSIoana Ciornei 	lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_XFI, PROTO_SEL_MSK);
3098f73b37cSIoana Ciornei 	lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_20_BIT, IF_WIDTH_MSK);
3108f73b37cSIoana Ciornei 
3118f73b37cSIoana Ciornei 	/* Switch to the PLL that works with this interface type */
3128f73b37cSIoana Ciornei 	pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_10GBASER);
3138f73b37cSIoana Ciornei 	lynx_28g_lane_set_pll(lane, pll);
3148f73b37cSIoana Ciornei 
3158f73b37cSIoana Ciornei 	/* Choose the portion of clock net to be used on this lane */
3168f73b37cSIoana Ciornei 	lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_10GBASER);
3178f73b37cSIoana Ciornei 
3188f73b37cSIoana Ciornei 	/* Disable the SGMII PCS */
3198f73b37cSIoana Ciornei 	lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_DIS, SGPCS_MSK);
3208f73b37cSIoana Ciornei 
3218f73b37cSIoana Ciornei 	/* Configure the appropriate equalization parameters for the protocol */
3228f73b37cSIoana Ciornei 	iowrite32(0x10808307, priv->base + LYNX_28G_LNaTECR0(lane->id));
3238f73b37cSIoana Ciornei 	iowrite32(0x10000000, priv->base + LYNX_28G_LNaRGCR1(lane->id));
3248f73b37cSIoana Ciornei 	iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR0(lane->id));
3258f73b37cSIoana Ciornei 	iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id));
3268f73b37cSIoana Ciornei 	iowrite32(0x81000020, priv->base + LYNX_28G_LNaRECR2(lane->id));
3278f73b37cSIoana Ciornei 	iowrite32(0x00002000, priv->base + LYNX_28G_LNaRSCCR0(lane->id));
3288f73b37cSIoana Ciornei }
3298f73b37cSIoana Ciornei 
3308f73b37cSIoana Ciornei static int lynx_28g_power_off(struct phy *phy)
3318f73b37cSIoana Ciornei {
3328f73b37cSIoana Ciornei 	struct lynx_28g_lane *lane = phy_get_drvdata(phy);
3338f73b37cSIoana Ciornei 	u32 trstctl, rrstctl;
3348f73b37cSIoana Ciornei 
3358f73b37cSIoana Ciornei 	if (!lane->powered_up)
3368f73b37cSIoana Ciornei 		return 0;
3378f73b37cSIoana Ciornei 
3388f73b37cSIoana Ciornei 	/* Issue a halt request */
3398f73b37cSIoana Ciornei 	lynx_28g_lane_rmw(lane, LNaTRSTCTL, HLT_REQ, HLT_REQ);
3408f73b37cSIoana Ciornei 	lynx_28g_lane_rmw(lane, LNaRRSTCTL, HLT_REQ, HLT_REQ);
3418f73b37cSIoana Ciornei 
3428f73b37cSIoana Ciornei 	/* Wait until the halting process is complete */
3438f73b37cSIoana Ciornei 	do {
3448f73b37cSIoana Ciornei 		trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL);
3458f73b37cSIoana Ciornei 		rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
3468f73b37cSIoana Ciornei 	} while ((trstctl & LYNX_28G_LNaTRSTCTL_HLT_REQ) ||
3478f73b37cSIoana Ciornei 		 (rrstctl & LYNX_28G_LNaRRSTCTL_HLT_REQ));
3488f73b37cSIoana Ciornei 
3498f73b37cSIoana Ciornei 	lane->powered_up = false;
3508f73b37cSIoana Ciornei 
3518f73b37cSIoana Ciornei 	return 0;
3528f73b37cSIoana Ciornei }
3538f73b37cSIoana Ciornei 
3548f73b37cSIoana Ciornei static int lynx_28g_power_on(struct phy *phy)
3558f73b37cSIoana Ciornei {
3568f73b37cSIoana Ciornei 	struct lynx_28g_lane *lane = phy_get_drvdata(phy);
3578f73b37cSIoana Ciornei 	u32 trstctl, rrstctl;
3588f73b37cSIoana Ciornei 
3598f73b37cSIoana Ciornei 	if (lane->powered_up)
3608f73b37cSIoana Ciornei 		return 0;
3618f73b37cSIoana Ciornei 
3628f73b37cSIoana Ciornei 	/* Issue a reset request on the lane */
3638f73b37cSIoana Ciornei 	lynx_28g_lane_rmw(lane, LNaTRSTCTL, RST_REQ, RST_REQ);
3648f73b37cSIoana Ciornei 	lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ);
3658f73b37cSIoana Ciornei 
3668f73b37cSIoana Ciornei 	/* Wait until the reset sequence is completed */
3678f73b37cSIoana Ciornei 	do {
3688f73b37cSIoana Ciornei 		trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL);
3698f73b37cSIoana Ciornei 		rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
3708f73b37cSIoana Ciornei 	} while (!(trstctl & LYNX_28G_LNaTRSTCTL_RST_DONE) ||
3718f73b37cSIoana Ciornei 		 !(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE));
3728f73b37cSIoana Ciornei 
3738f73b37cSIoana Ciornei 	lane->powered_up = true;
3748f73b37cSIoana Ciornei 
3758f73b37cSIoana Ciornei 	return 0;
3768f73b37cSIoana Ciornei }
3778f73b37cSIoana Ciornei 
3788f73b37cSIoana Ciornei static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
3798f73b37cSIoana Ciornei {
3808f73b37cSIoana Ciornei 	struct lynx_28g_lane *lane = phy_get_drvdata(phy);
3818f73b37cSIoana Ciornei 	struct lynx_28g_priv *priv = lane->priv;
3828f73b37cSIoana Ciornei 	int powered_up = lane->powered_up;
3838f73b37cSIoana Ciornei 	int err = 0;
3848f73b37cSIoana Ciornei 
3858f73b37cSIoana Ciornei 	if (mode != PHY_MODE_ETHERNET)
3868f73b37cSIoana Ciornei 		return -EOPNOTSUPP;
3878f73b37cSIoana Ciornei 
3888f73b37cSIoana Ciornei 	if (lane->interface == PHY_INTERFACE_MODE_NA)
3898f73b37cSIoana Ciornei 		return -EOPNOTSUPP;
3908f73b37cSIoana Ciornei 
3918f73b37cSIoana Ciornei 	if (!lynx_28g_supports_interface(priv, submode))
3928f73b37cSIoana Ciornei 		return -EOPNOTSUPP;
3938f73b37cSIoana Ciornei 
3948f73b37cSIoana Ciornei 	/* If the lane is powered up, put the lane into the halt state while
3958f73b37cSIoana Ciornei 	 * the reconfiguration is being done.
3968f73b37cSIoana Ciornei 	 */
3978f73b37cSIoana Ciornei 	if (powered_up)
3988f73b37cSIoana Ciornei 		lynx_28g_power_off(phy);
3998f73b37cSIoana Ciornei 
4008f73b37cSIoana Ciornei 	switch (submode) {
4018f73b37cSIoana Ciornei 	case PHY_INTERFACE_MODE_SGMII:
4028f73b37cSIoana Ciornei 	case PHY_INTERFACE_MODE_1000BASEX:
4038f73b37cSIoana Ciornei 		lynx_28g_lane_set_sgmii(lane);
4048f73b37cSIoana Ciornei 		break;
4058f73b37cSIoana Ciornei 	case PHY_INTERFACE_MODE_10GBASER:
4068f73b37cSIoana Ciornei 		lynx_28g_lane_set_10gbaser(lane);
4078f73b37cSIoana Ciornei 		break;
4088f73b37cSIoana Ciornei 	default:
4098f73b37cSIoana Ciornei 		err = -EOPNOTSUPP;
4108f73b37cSIoana Ciornei 		goto out;
4118f73b37cSIoana Ciornei 	}
4128f73b37cSIoana Ciornei 
4138f73b37cSIoana Ciornei 	lane->interface = submode;
4148f73b37cSIoana Ciornei 
4158f73b37cSIoana Ciornei out:
4168f73b37cSIoana Ciornei 	/* Power up the lane if necessary */
4178f73b37cSIoana Ciornei 	if (powered_up)
4188f73b37cSIoana Ciornei 		lynx_28g_power_on(phy);
4198f73b37cSIoana Ciornei 
4208f73b37cSIoana Ciornei 	return err;
4218f73b37cSIoana Ciornei }
4228f73b37cSIoana Ciornei 
4238f73b37cSIoana Ciornei static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode,
4248f73b37cSIoana Ciornei 			     union phy_configure_opts *opts __always_unused)
4258f73b37cSIoana Ciornei {
4268f73b37cSIoana Ciornei 	struct lynx_28g_lane *lane = phy_get_drvdata(phy);
4278f73b37cSIoana Ciornei 	struct lynx_28g_priv *priv = lane->priv;
4288f73b37cSIoana Ciornei 
4298f73b37cSIoana Ciornei 	if (mode != PHY_MODE_ETHERNET)
4308f73b37cSIoana Ciornei 		return -EOPNOTSUPP;
4318f73b37cSIoana Ciornei 
4328f73b37cSIoana Ciornei 	if (!lynx_28g_supports_interface(priv, submode))
4338f73b37cSIoana Ciornei 		return -EOPNOTSUPP;
4348f73b37cSIoana Ciornei 
4358f73b37cSIoana Ciornei 	return 0;
4368f73b37cSIoana Ciornei }
4378f73b37cSIoana Ciornei 
4388f73b37cSIoana Ciornei static int lynx_28g_init(struct phy *phy)
4398f73b37cSIoana Ciornei {
4408f73b37cSIoana Ciornei 	struct lynx_28g_lane *lane = phy_get_drvdata(phy);
4418f73b37cSIoana Ciornei 
4428f73b37cSIoana Ciornei 	/* Mark the fact that the lane was init */
4438f73b37cSIoana Ciornei 	lane->init = true;
4448f73b37cSIoana Ciornei 
4458f73b37cSIoana Ciornei 	/* SerDes lanes are powered on at boot time.  Any lane that is managed
4468f73b37cSIoana Ciornei 	 * by this driver will get powered down at init time aka at dpaa2-eth
4478f73b37cSIoana Ciornei 	 * probe time.
4488f73b37cSIoana Ciornei 	 */
4498f73b37cSIoana Ciornei 	lane->powered_up = true;
4508f73b37cSIoana Ciornei 	lynx_28g_power_off(phy);
4518f73b37cSIoana Ciornei 
4528f73b37cSIoana Ciornei 	return 0;
4538f73b37cSIoana Ciornei }
4548f73b37cSIoana Ciornei 
4558f73b37cSIoana Ciornei static const struct phy_ops lynx_28g_ops = {
4568f73b37cSIoana Ciornei 	.init		= lynx_28g_init,
4578f73b37cSIoana Ciornei 	.power_on	= lynx_28g_power_on,
4588f73b37cSIoana Ciornei 	.power_off	= lynx_28g_power_off,
4598f73b37cSIoana Ciornei 	.set_mode	= lynx_28g_set_mode,
4608f73b37cSIoana Ciornei 	.validate	= lynx_28g_validate,
4618f73b37cSIoana Ciornei 	.owner		= THIS_MODULE,
4628f73b37cSIoana Ciornei };
4638f73b37cSIoana Ciornei 
4648f73b37cSIoana Ciornei static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv)
4658f73b37cSIoana Ciornei {
4668f73b37cSIoana Ciornei 	struct lynx_28g_pll *pll;
4678f73b37cSIoana Ciornei 	int i;
4688f73b37cSIoana Ciornei 
4698f73b37cSIoana Ciornei 	for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
4708f73b37cSIoana Ciornei 		pll = &priv->pll[i];
4718f73b37cSIoana Ciornei 		pll->priv = priv;
4728f73b37cSIoana Ciornei 		pll->id = i;
4738f73b37cSIoana Ciornei 
4748f73b37cSIoana Ciornei 		pll->rstctl = lynx_28g_pll_read(pll, PLLnRSTCTL);
4758f73b37cSIoana Ciornei 		pll->cr0 = lynx_28g_pll_read(pll, PLLnCR0);
4768f73b37cSIoana Ciornei 		pll->cr1 = lynx_28g_pll_read(pll, PLLnCR1);
4778f73b37cSIoana Ciornei 
4788f73b37cSIoana Ciornei 		if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl))
4798f73b37cSIoana Ciornei 			continue;
4808f73b37cSIoana Ciornei 
4818f73b37cSIoana Ciornei 		switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) {
4828f73b37cSIoana Ciornei 		case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO:
4838f73b37cSIoana Ciornei 		case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO:
4848f73b37cSIoana Ciornei 			/* 5GHz clock net */
4858f73b37cSIoana Ciornei 			__set_bit(PHY_INTERFACE_MODE_1000BASEX, pll->supported);
4868f73b37cSIoana Ciornei 			__set_bit(PHY_INTERFACE_MODE_SGMII, pll->supported);
4878f73b37cSIoana Ciornei 			break;
4888f73b37cSIoana Ciornei 		case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO:
4898f73b37cSIoana Ciornei 			/* 10.3125GHz clock net */
4908f73b37cSIoana Ciornei 			__set_bit(PHY_INTERFACE_MODE_10GBASER, pll->supported);
4918f73b37cSIoana Ciornei 			break;
4928f73b37cSIoana Ciornei 		default:
4938f73b37cSIoana Ciornei 			/* 6GHz, 12.890625GHz, 8GHz */
4948f73b37cSIoana Ciornei 			break;
4958f73b37cSIoana Ciornei 		}
4968f73b37cSIoana Ciornei 	}
4978f73b37cSIoana Ciornei }
4988f73b37cSIoana Ciornei 
4998f73b37cSIoana Ciornei #define work_to_lynx(w) container_of((w), struct lynx_28g_priv, cdr_check.work)
5008f73b37cSIoana Ciornei 
5018f73b37cSIoana Ciornei static void lynx_28g_cdr_lock_check(struct work_struct *work)
5028f73b37cSIoana Ciornei {
5038f73b37cSIoana Ciornei 	struct lynx_28g_priv *priv = work_to_lynx(work);
5048f73b37cSIoana Ciornei 	struct lynx_28g_lane *lane;
5058f73b37cSIoana Ciornei 	u32 rrstctl;
5068f73b37cSIoana Ciornei 	int i;
5078f73b37cSIoana Ciornei 
5088f73b37cSIoana Ciornei 	for (i = 0; i < LYNX_28G_NUM_LANE; i++) {
5098f73b37cSIoana Ciornei 		lane = &priv->lane[i];
5108f73b37cSIoana Ciornei 
5118f73b37cSIoana Ciornei 		if (!lane->init)
5128f73b37cSIoana Ciornei 			continue;
5138f73b37cSIoana Ciornei 
5148f73b37cSIoana Ciornei 		if (!lane->powered_up)
5158f73b37cSIoana Ciornei 			continue;
5168f73b37cSIoana Ciornei 
5178f73b37cSIoana Ciornei 		rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
5188f73b37cSIoana Ciornei 		if (!(rrstctl & LYNX_28G_LNaRRSTCTL_CDR_LOCK)) {
5198f73b37cSIoana Ciornei 			lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ);
5208f73b37cSIoana Ciornei 			do {
5218f73b37cSIoana Ciornei 				rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
5228f73b37cSIoana Ciornei 			} while (!(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE));
5238f73b37cSIoana Ciornei 		}
5248f73b37cSIoana Ciornei 	}
5258f73b37cSIoana Ciornei 	queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
5268f73b37cSIoana Ciornei 			   msecs_to_jiffies(1000));
5278f73b37cSIoana Ciornei }
5288f73b37cSIoana Ciornei 
5298f73b37cSIoana Ciornei static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane)
5308f73b37cSIoana Ciornei {
5318f73b37cSIoana Ciornei 	u32 pss, protocol;
5328f73b37cSIoana Ciornei 
5338f73b37cSIoana Ciornei 	pss = lynx_28g_lane_read(lane, LNaPSS);
5348f73b37cSIoana Ciornei 	protocol = LYNX_28G_LNaPSS_TYPE(pss);
5358f73b37cSIoana Ciornei 	switch (protocol) {
5368f73b37cSIoana Ciornei 	case LYNX_28G_LNaPSS_TYPE_SGMII:
5378f73b37cSIoana Ciornei 		lane->interface = PHY_INTERFACE_MODE_SGMII;
5388f73b37cSIoana Ciornei 		break;
5398f73b37cSIoana Ciornei 	case LYNX_28G_LNaPSS_TYPE_XFI:
5408f73b37cSIoana Ciornei 		lane->interface = PHY_INTERFACE_MODE_10GBASER;
5418f73b37cSIoana Ciornei 		break;
5428f73b37cSIoana Ciornei 	default:
5438f73b37cSIoana Ciornei 		lane->interface = PHY_INTERFACE_MODE_NA;
5448f73b37cSIoana Ciornei 	}
5458f73b37cSIoana Ciornei }
5468f73b37cSIoana Ciornei 
5478f73b37cSIoana Ciornei static struct phy *lynx_28g_xlate(struct device *dev,
5488f73b37cSIoana Ciornei 				  struct of_phandle_args *args)
5498f73b37cSIoana Ciornei {
5508f73b37cSIoana Ciornei 	struct lynx_28g_priv *priv = dev_get_drvdata(dev);
5518f73b37cSIoana Ciornei 	int idx = args->args[0];
5528f73b37cSIoana Ciornei 
5538f73b37cSIoana Ciornei 	if (WARN_ON(idx >= LYNX_28G_NUM_LANE))
5548f73b37cSIoana Ciornei 		return ERR_PTR(-EINVAL);
5558f73b37cSIoana Ciornei 
5568f73b37cSIoana Ciornei 	return priv->lane[idx].phy;
5578f73b37cSIoana Ciornei }
5588f73b37cSIoana Ciornei 
5598f73b37cSIoana Ciornei static int lynx_28g_probe(struct platform_device *pdev)
5608f73b37cSIoana Ciornei {
5618f73b37cSIoana Ciornei 	struct device *dev = &pdev->dev;
5628f73b37cSIoana Ciornei 	struct phy_provider *provider;
5638f73b37cSIoana Ciornei 	struct lynx_28g_priv *priv;
5648f73b37cSIoana Ciornei 	int i;
5658f73b37cSIoana Ciornei 
5668f73b37cSIoana Ciornei 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
5678f73b37cSIoana Ciornei 	if (!priv)
5688f73b37cSIoana Ciornei 		return -ENOMEM;
5698f73b37cSIoana Ciornei 	priv->dev = &pdev->dev;
5708f73b37cSIoana Ciornei 
5718f73b37cSIoana Ciornei 	priv->base = devm_platform_ioremap_resource(pdev, 0);
5728f73b37cSIoana Ciornei 	if (IS_ERR(priv->base))
5738f73b37cSIoana Ciornei 		return PTR_ERR(priv->base);
5748f73b37cSIoana Ciornei 
5758f73b37cSIoana Ciornei 	lynx_28g_pll_read_configuration(priv);
5768f73b37cSIoana Ciornei 
5778f73b37cSIoana Ciornei 	for (i = 0; i < LYNX_28G_NUM_LANE; i++) {
5788f73b37cSIoana Ciornei 		struct lynx_28g_lane *lane = &priv->lane[i];
5798f73b37cSIoana Ciornei 		struct phy *phy;
5808f73b37cSIoana Ciornei 
5818f73b37cSIoana Ciornei 		memset(lane, 0, sizeof(*lane));
5828f73b37cSIoana Ciornei 
5838f73b37cSIoana Ciornei 		phy = devm_phy_create(&pdev->dev, NULL, &lynx_28g_ops);
5848f73b37cSIoana Ciornei 		if (IS_ERR(phy))
5858f73b37cSIoana Ciornei 			return PTR_ERR(phy);
5868f73b37cSIoana Ciornei 
5878f73b37cSIoana Ciornei 		lane->priv = priv;
5888f73b37cSIoana Ciornei 		lane->phy = phy;
5898f73b37cSIoana Ciornei 		lane->id = i;
5908f73b37cSIoana Ciornei 		phy_set_drvdata(phy, lane);
5918f73b37cSIoana Ciornei 		lynx_28g_lane_read_configuration(lane);
5928f73b37cSIoana Ciornei 	}
5938f73b37cSIoana Ciornei 
5948f73b37cSIoana Ciornei 	dev_set_drvdata(dev, priv);
5958f73b37cSIoana Ciornei 
5968f73b37cSIoana Ciornei 	INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check);
5978f73b37cSIoana Ciornei 
5988f73b37cSIoana Ciornei 	queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
5998f73b37cSIoana Ciornei 			   msecs_to_jiffies(1000));
6008f73b37cSIoana Ciornei 
6018f73b37cSIoana Ciornei 	dev_set_drvdata(&pdev->dev, priv);
6028f73b37cSIoana Ciornei 	provider = devm_of_phy_provider_register(&pdev->dev, lynx_28g_xlate);
6038f73b37cSIoana Ciornei 
6048f73b37cSIoana Ciornei 	return PTR_ERR_OR_ZERO(provider);
6058f73b37cSIoana Ciornei }
6068f73b37cSIoana Ciornei 
607*f200bab3SIoana Ciornei static void lynx_28g_remove(struct platform_device *pdev)
608*f200bab3SIoana Ciornei {
609*f200bab3SIoana Ciornei 	struct device *dev = &pdev->dev;
610*f200bab3SIoana Ciornei 	struct lynx_28g_priv *priv = dev_get_drvdata(dev);
611*f200bab3SIoana Ciornei 
612*f200bab3SIoana Ciornei 	cancel_delayed_work_sync(&priv->cdr_check);
613*f200bab3SIoana Ciornei }
614*f200bab3SIoana Ciornei 
6158f73b37cSIoana Ciornei static const struct of_device_id lynx_28g_of_match_table[] = {
6168f73b37cSIoana Ciornei 	{ .compatible = "fsl,lynx-28g" },
6178f73b37cSIoana Ciornei 	{ },
6188f73b37cSIoana Ciornei };
6198f73b37cSIoana Ciornei MODULE_DEVICE_TABLE(of, lynx_28g_of_match_table);
6208f73b37cSIoana Ciornei 
6218f73b37cSIoana Ciornei static struct platform_driver lynx_28g_driver = {
6228f73b37cSIoana Ciornei 	.probe	= lynx_28g_probe,
623*f200bab3SIoana Ciornei 	.remove_new = lynx_28g_remove,
6248f73b37cSIoana Ciornei 	.driver	= {
6258f73b37cSIoana Ciornei 		.name = "lynx-28g",
6268f73b37cSIoana Ciornei 		.of_match_table = lynx_28g_of_match_table,
6278f73b37cSIoana Ciornei 	},
6288f73b37cSIoana Ciornei };
6298f73b37cSIoana Ciornei module_platform_driver(lynx_28g_driver);
6308f73b37cSIoana Ciornei 
6318f73b37cSIoana Ciornei MODULE_AUTHOR("Ioana Ciornei <ioana.ciornei@nxp.com>");
6328f73b37cSIoana Ciornei MODULE_DESCRIPTION("Lynx 28G SerDes PHY driver for Layerscape SoCs");
6338f73b37cSIoana Ciornei MODULE_LICENSE("GPL v2");
634