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