xref: /linux/drivers/phy/freescale/phy-fsl-lynx-core.c (revision 62cf248de32f061d99cf7cd1675419d739031c5e)
1ac0ffe7cSVladimir Oltean // SPDX-License-Identifier: GPL-2.0+
2ac0ffe7cSVladimir Oltean /* Copyright 2025-2026 NXP */
3ac0ffe7cSVladimir Oltean 
4ac0ffe7cSVladimir Oltean #include <linux/module.h>
5719a2da3SVladimir Oltean #include <linux/platform_device.h>
6ac0ffe7cSVladimir Oltean 
7ac0ffe7cSVladimir Oltean #include "phy-fsl-lynx-core.h"
8ac0ffe7cSVladimir Oltean 
9ac0ffe7cSVladimir Oltean const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode)
10ac0ffe7cSVladimir Oltean {
11ac0ffe7cSVladimir Oltean 	switch (lane_mode) {
12ac0ffe7cSVladimir Oltean 	case LANE_MODE_1000BASEX_SGMII:
13ac0ffe7cSVladimir Oltean 		return "1000Base-X/SGMII";
14*6dc92ba4SVladimir Oltean 	case LANE_MODE_2500BASEX:
15*6dc92ba4SVladimir Oltean 		return "2500Base-X";
16*6dc92ba4SVladimir Oltean 	case LANE_MODE_QSGMII:
17*6dc92ba4SVladimir Oltean 		return "QSGMII";
18*6dc92ba4SVladimir Oltean 	case LANE_MODE_10G_QXGMII:
19*6dc92ba4SVladimir Oltean 		return "10G-QXGMII";
20ac0ffe7cSVladimir Oltean 	case LANE_MODE_10GBASER:
21ac0ffe7cSVladimir Oltean 		return "10GBase-R";
22ac0ffe7cSVladimir Oltean 	case LANE_MODE_USXGMII:
23ac0ffe7cSVladimir Oltean 		return "USXGMII";
24ac0ffe7cSVladimir Oltean 	case LANE_MODE_25GBASER:
25ac0ffe7cSVladimir Oltean 		return "25GBase-R";
26ac0ffe7cSVladimir Oltean 	default:
27ac0ffe7cSVladimir Oltean 		return "unknown";
28ac0ffe7cSVladimir Oltean 	}
29ac0ffe7cSVladimir Oltean }
30ac0ffe7cSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_lane_mode_str, "PHY_FSL_LYNX");
31ac0ffe7cSVladimir Oltean 
32ac0ffe7cSVladimir Oltean enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf)
33ac0ffe7cSVladimir Oltean {
34ac0ffe7cSVladimir Oltean 	switch (intf) {
35ac0ffe7cSVladimir Oltean 	case PHY_INTERFACE_MODE_SGMII:
36ac0ffe7cSVladimir Oltean 	case PHY_INTERFACE_MODE_1000BASEX:
37ac0ffe7cSVladimir Oltean 		return LANE_MODE_1000BASEX_SGMII;
38*6dc92ba4SVladimir Oltean 	case PHY_INTERFACE_MODE_2500BASEX:
39*6dc92ba4SVladimir Oltean 		return LANE_MODE_2500BASEX;
40*6dc92ba4SVladimir Oltean 	case PHY_INTERFACE_MODE_QSGMII:
41*6dc92ba4SVladimir Oltean 		return LANE_MODE_QSGMII;
42*6dc92ba4SVladimir Oltean 	case PHY_INTERFACE_MODE_10G_QXGMII:
43*6dc92ba4SVladimir Oltean 		return LANE_MODE_10G_QXGMII;
44ac0ffe7cSVladimir Oltean 	case PHY_INTERFACE_MODE_10GBASER:
45ac0ffe7cSVladimir Oltean 		return LANE_MODE_10GBASER;
46ac0ffe7cSVladimir Oltean 	case PHY_INTERFACE_MODE_USXGMII:
47ac0ffe7cSVladimir Oltean 		return LANE_MODE_USXGMII;
48ac0ffe7cSVladimir Oltean 	case PHY_INTERFACE_MODE_25GBASER:
49ac0ffe7cSVladimir Oltean 		return LANE_MODE_25GBASER;
50ac0ffe7cSVladimir Oltean 	default:
51ac0ffe7cSVladimir Oltean 		return LANE_MODE_UNKNOWN;
52ac0ffe7cSVladimir Oltean 	}
53ac0ffe7cSVladimir Oltean }
54ac0ffe7cSVladimir Oltean EXPORT_SYMBOL_NS_GPL(phy_interface_to_lane_mode, "PHY_FSL_LYNX");
55ac0ffe7cSVladimir Oltean 
5609697afaSVladimir Oltean /* By default, assume that if we know how to get the PCCR register and
5709697afaSVladimir Oltean  * protocol converter for a lane, that protocol is supported.
5809697afaSVladimir Oltean  */
5909697afaSVladimir Oltean static bool lynx_lane_supports_mode_default(struct lynx_lane *lane,
6009697afaSVladimir Oltean 					    enum lynx_lane_mode mode)
6109697afaSVladimir Oltean {
6209697afaSVladimir Oltean 	struct lynx_priv *priv = lane->priv;
6309697afaSVladimir Oltean 	struct lynx_pccr pccr;
6409697afaSVladimir Oltean 
6509697afaSVladimir Oltean 	if (!priv->info->get_pccr || !priv->info->get_pcvt_offset)
6609697afaSVladimir Oltean 		return false;
6709697afaSVladimir Oltean 
6809697afaSVladimir Oltean 	if (priv->info->get_pccr(mode, lane->id, &pccr) < 0)
6909697afaSVladimir Oltean 		return false;
7009697afaSVladimir Oltean 
7109697afaSVladimir Oltean 	if (priv->info->get_pcvt_offset(lane->id, mode) < 0)
7209697afaSVladimir Oltean 		return false;
7309697afaSVladimir Oltean 
7409697afaSVladimir Oltean 	return true;
7509697afaSVladimir Oltean }
7609697afaSVladimir Oltean 
77b7021a4dSVladimir Oltean /* A lane mode is supported if we have a PLL that can provide its required
78b7021a4dSVladimir Oltean  * clock net, and if there is a protocol converter for that mode on that lane.
79b7021a4dSVladimir Oltean  */
80b7021a4dSVladimir Oltean bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode)
81b7021a4dSVladimir Oltean {
82b7021a4dSVladimir Oltean 	struct lynx_priv *priv = lane->priv;
83b7021a4dSVladimir Oltean 	int i;
84b7021a4dSVladimir Oltean 
8509697afaSVladimir Oltean 	if (priv->info->lane_supports_mode) {
86b7021a4dSVladimir Oltean 		if (!priv->info->lane_supports_mode(lane->id, mode))
87b7021a4dSVladimir Oltean 			return false;
8809697afaSVladimir Oltean 	} else if (!lynx_lane_supports_mode_default(lane, mode)) {
8909697afaSVladimir Oltean 		return false;
9009697afaSVladimir Oltean 	}
91b7021a4dSVladimir Oltean 
92b7021a4dSVladimir Oltean 	for (i = 0; i < LYNX_NUM_PLL; i++) {
93b7021a4dSVladimir Oltean 		if (!priv->pll[i].enabled)
94b7021a4dSVladimir Oltean 			continue;
95b7021a4dSVladimir Oltean 
96b7021a4dSVladimir Oltean 		if (test_bit(mode, priv->pll[i].supported))
97b7021a4dSVladimir Oltean 			return true;
98b7021a4dSVladimir Oltean 	}
99b7021a4dSVladimir Oltean 
100b7021a4dSVladimir Oltean 	return false;
101b7021a4dSVladimir Oltean }
102b7021a4dSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_lane_supports_mode, "PHY_FSL_LYNX");
103b7021a4dSVladimir Oltean 
104*6dc92ba4SVladimir Oltean /* The quad protocols are fixed because the lane has multiple consumers, and
105*6dc92ba4SVladimir Oltean  * one phy_set_mode_ext() affects the other consumers as well. We have no use
106*6dc92ba4SVladimir Oltean  * case for dynamic protocol changing here, so disallow it.
107*6dc92ba4SVladimir Oltean  */
108*6dc92ba4SVladimir Oltean static enum lynx_lane_mode lynx_fixed_protocols[] = {
109*6dc92ba4SVladimir Oltean 	LANE_MODE_QSGMII,
110*6dc92ba4SVladimir Oltean 	LANE_MODE_10G_QXGMII,
111*6dc92ba4SVladimir Oltean };
112*6dc92ba4SVladimir Oltean 
113*6dc92ba4SVladimir Oltean static bool lynx_lane_restrict_fixed_mode_change(struct lynx_lane *lane,
114*6dc92ba4SVladimir Oltean 						 enum lynx_lane_mode new)
115*6dc92ba4SVladimir Oltean {
116*6dc92ba4SVladimir Oltean 	enum lynx_lane_mode curr = lane->mode;
117*6dc92ba4SVladimir Oltean 
118*6dc92ba4SVladimir Oltean 	for (int i = 0; i < ARRAY_SIZE(lynx_fixed_protocols); i++)
119*6dc92ba4SVladimir Oltean 		if ((curr == lynx_fixed_protocols[i] ||
120*6dc92ba4SVladimir Oltean 		     new == lynx_fixed_protocols[i]) &&
121*6dc92ba4SVladimir Oltean 		     curr != new)
122*6dc92ba4SVladimir Oltean 			return true;
123*6dc92ba4SVladimir Oltean 
124*6dc92ba4SVladimir Oltean 	return false;
125*6dc92ba4SVladimir Oltean }
126*6dc92ba4SVladimir Oltean 
127f124b54bSVladimir Oltean /* Translate the mode/submode from phy_validate() and phy_set_mode_ext() to a
128f124b54bSVladimir Oltean  * lane_mode and return 0 if it is supported and we can transition to it from
129f124b54bSVladimir Oltean  * the current lane mode, or return negative error otherwise.
130f124b54bSVladimir Oltean  */
131f124b54bSVladimir Oltean int lynx_phy_mode_to_lane_mode(struct phy *phy, enum phy_mode mode,
132f124b54bSVladimir Oltean 			       int submode, enum lynx_lane_mode *lane_mode)
133f124b54bSVladimir Oltean {
134f124b54bSVladimir Oltean 	struct lynx_lane *lane = phy_get_drvdata(phy);
135f124b54bSVladimir Oltean 	enum lynx_lane_mode tmp_lane_mode;
136f124b54bSVladimir Oltean 
137f124b54bSVladimir Oltean 	/* The protocol configuration tables are incomplete for full lane
138f124b54bSVladimir Oltean 	 * reconfiguration from an arbitrary protocol.
139f124b54bSVladimir Oltean 	 */
140f124b54bSVladimir Oltean 	if (lane->mode == LANE_MODE_UNKNOWN)
141f124b54bSVladimir Oltean 		return -EINVAL;
142f124b54bSVladimir Oltean 
143f124b54bSVladimir Oltean 	if (mode != PHY_MODE_ETHERNET)
144f124b54bSVladimir Oltean 		return -EINVAL;
145f124b54bSVladimir Oltean 
146f124b54bSVladimir Oltean 	tmp_lane_mode = phy_interface_to_lane_mode(submode);
147f124b54bSVladimir Oltean 	if (!lynx_lane_supports_mode(lane, tmp_lane_mode))
148f124b54bSVladimir Oltean 		return -EINVAL;
149f124b54bSVladimir Oltean 
150*6dc92ba4SVladimir Oltean 	if (lynx_lane_restrict_fixed_mode_change(lane, tmp_lane_mode))
151*6dc92ba4SVladimir Oltean 		return -EINVAL;
152*6dc92ba4SVladimir Oltean 
153f124b54bSVladimir Oltean 	if (lane_mode)
154f124b54bSVladimir Oltean 		*lane_mode = tmp_lane_mode;
155f124b54bSVladimir Oltean 
156f124b54bSVladimir Oltean 	return 0;
157f124b54bSVladimir Oltean }
158f124b54bSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_phy_mode_to_lane_mode, "PHY_FSL_LYNX");
159f124b54bSVladimir Oltean 
160d4550ea4SVladimir Oltean struct lynx_pll *lynx_pll_get(struct lynx_priv *priv, enum lynx_lane_mode mode)
161d4550ea4SVladimir Oltean {
162d4550ea4SVladimir Oltean 	struct lynx_pll *pll;
163d4550ea4SVladimir Oltean 	int i;
164d4550ea4SVladimir Oltean 
165d4550ea4SVladimir Oltean 	for (i = 0; i < LYNX_NUM_PLL; i++) {
166d4550ea4SVladimir Oltean 		pll = &priv->pll[i];
167d4550ea4SVladimir Oltean 
168d4550ea4SVladimir Oltean 		if (!pll->enabled)
169d4550ea4SVladimir Oltean 			continue;
170d4550ea4SVladimir Oltean 
171d4550ea4SVladimir Oltean 		if (test_bit(mode, pll->supported))
172d4550ea4SVladimir Oltean 			return pll;
173d4550ea4SVladimir Oltean 	}
174d4550ea4SVladimir Oltean 
175d4550ea4SVladimir Oltean 	/* no pll supports requested mode, either caller forgot to check
176d4550ea4SVladimir Oltean 	 * lynx_lane_supports_mode(), or this is a bug.
177d4550ea4SVladimir Oltean 	 */
178d4550ea4SVladimir Oltean 	dev_WARN_ONCE(priv->dev, 1, "no pll for lane mode %s\n",
179d4550ea4SVladimir Oltean 		      lynx_lane_mode_str(mode));
180d4550ea4SVladimir Oltean 	return NULL;
181d4550ea4SVladimir Oltean }
182d4550ea4SVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_pll_get, "PHY_FSL_LYNX");
183d4550ea4SVladimir Oltean 
18451c25f4bSVladimir Oltean int lynx_pccr_read(struct lynx_lane *lane, enum lynx_lane_mode mode, u32 *val)
18551c25f4bSVladimir Oltean {
18651c25f4bSVladimir Oltean 	struct lynx_priv *priv = lane->priv;
18751c25f4bSVladimir Oltean 	struct lynx_pccr pccr;
18851c25f4bSVladimir Oltean 	u32 tmp;
18951c25f4bSVladimir Oltean 	int err;
19051c25f4bSVladimir Oltean 
19151c25f4bSVladimir Oltean 	err = priv->info->get_pccr(mode, lane->id, &pccr);
19251c25f4bSVladimir Oltean 	if (err)
19351c25f4bSVladimir Oltean 		return err;
19451c25f4bSVladimir Oltean 
19551c25f4bSVladimir Oltean 	tmp = lynx_read(priv, pccr.offset);
19651c25f4bSVladimir Oltean 	*val = (tmp >> pccr.shift) & GENMASK(pccr.width - 1, 0);
19751c25f4bSVladimir Oltean 
19851c25f4bSVladimir Oltean 	return 0;
19951c25f4bSVladimir Oltean }
20051c25f4bSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_pccr_read, "PHY_FSL_LYNX");
20151c25f4bSVladimir Oltean 
20251c25f4bSVladimir Oltean int lynx_pccr_write(struct lynx_lane *lane, enum lynx_lane_mode mode, u32 val)
20351c25f4bSVladimir Oltean {
20451c25f4bSVladimir Oltean 	struct lynx_priv *priv = lane->priv;
20551c25f4bSVladimir Oltean 	struct lynx_pccr pccr;
20651c25f4bSVladimir Oltean 	u32 old, tmp, mask;
20751c25f4bSVladimir Oltean 	int err;
20851c25f4bSVladimir Oltean 
20951c25f4bSVladimir Oltean 	err = priv->info->get_pccr(mode, lane->id, &pccr);
21051c25f4bSVladimir Oltean 	if (err)
21151c25f4bSVladimir Oltean 		return err;
21251c25f4bSVladimir Oltean 
21351c25f4bSVladimir Oltean 	old = lynx_read(priv, pccr.offset);
21451c25f4bSVladimir Oltean 	mask = GENMASK(pccr.width - 1, 0) << pccr.shift;
21551c25f4bSVladimir Oltean 	tmp = (old & ~mask) | (val << pccr.shift);
21651c25f4bSVladimir Oltean 	lynx_write(priv, pccr.offset, tmp);
21751c25f4bSVladimir Oltean 
21851c25f4bSVladimir Oltean 	dev_dbg(&lane->phy->dev, "PCCR@0x%x: 0x%x -> 0x%x\n",
21951c25f4bSVladimir Oltean 		pccr.offset, old, tmp);
22051c25f4bSVladimir Oltean 
22151c25f4bSVladimir Oltean 	return 0;
22251c25f4bSVladimir Oltean }
22351c25f4bSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_pccr_write, "PHY_FSL_LYNX");
22451c25f4bSVladimir Oltean 
22551c25f4bSVladimir Oltean int lynx_pcvt_read(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr,
22651c25f4bSVladimir Oltean 		   u32 *val)
22751c25f4bSVladimir Oltean {
22851c25f4bSVladimir Oltean 	struct lynx_priv *priv = lane->priv;
22951c25f4bSVladimir Oltean 	int offset;
23051c25f4bSVladimir Oltean 
23151c25f4bSVladimir Oltean 	offset = priv->info->get_pcvt_offset(lane->id, mode);
23251c25f4bSVladimir Oltean 	if (offset < 0)
23351c25f4bSVladimir Oltean 		return offset;
23451c25f4bSVladimir Oltean 
23551c25f4bSVladimir Oltean 	*val = lynx_read(priv, offset + cr);
23651c25f4bSVladimir Oltean 
23751c25f4bSVladimir Oltean 	return 0;
23851c25f4bSVladimir Oltean }
23951c25f4bSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_pcvt_read, "PHY_FSL_LYNX");
24051c25f4bSVladimir Oltean 
24151c25f4bSVladimir Oltean int lynx_pcvt_write(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr,
24251c25f4bSVladimir Oltean 		    u32 val)
24351c25f4bSVladimir Oltean {
24451c25f4bSVladimir Oltean 	struct lynx_priv *priv = lane->priv;
24551c25f4bSVladimir Oltean 	int offset;
24651c25f4bSVladimir Oltean 
24751c25f4bSVladimir Oltean 	offset = priv->info->get_pcvt_offset(lane->id, mode);
24851c25f4bSVladimir Oltean 	if (offset < 0)
24951c25f4bSVladimir Oltean 		return offset;
25051c25f4bSVladimir Oltean 
25151c25f4bSVladimir Oltean 	lynx_write(priv, offset + cr, val);
25251c25f4bSVladimir Oltean 
25351c25f4bSVladimir Oltean 	return 0;
25451c25f4bSVladimir Oltean }
25551c25f4bSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_pcvt_write, "PHY_FSL_LYNX");
25651c25f4bSVladimir Oltean 
25751c25f4bSVladimir Oltean int lynx_pcvt_rmw(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr,
25851c25f4bSVladimir Oltean 		  u32 val, u32 mask)
25951c25f4bSVladimir Oltean {
26051c25f4bSVladimir Oltean 	int err;
26151c25f4bSVladimir Oltean 	u32 tmp;
26251c25f4bSVladimir Oltean 
26351c25f4bSVladimir Oltean 	err = lynx_pcvt_read(lane, mode, cr, &tmp);
26451c25f4bSVladimir Oltean 	if (err)
26551c25f4bSVladimir Oltean 		return err;
26651c25f4bSVladimir Oltean 
26751c25f4bSVladimir Oltean 	tmp &= ~mask;
26851c25f4bSVladimir Oltean 	tmp |= val;
26951c25f4bSVladimir Oltean 
27051c25f4bSVladimir Oltean 	return lynx_pcvt_write(lane, mode, cr, tmp);
27151c25f4bSVladimir Oltean }
27251c25f4bSVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_pcvt_rmw, "PHY_FSL_LYNX");
27351c25f4bSVladimir Oltean 
274719a2da3SVladimir Oltean #define work_to_lynx(w) container_of((w), struct lynx_priv, cdr_check.work)
275719a2da3SVladimir Oltean 
276719a2da3SVladimir Oltean static void lynx_cdr_lock_check(struct work_struct *work)
277719a2da3SVladimir Oltean {
278719a2da3SVladimir Oltean 	struct lynx_priv *priv = work_to_lynx(work);
279719a2da3SVladimir Oltean 	struct lynx_lane *lane;
280719a2da3SVladimir Oltean 
281719a2da3SVladimir Oltean 	for (int i = priv->info->first_lane; i < priv->info->num_lanes; i++) {
282719a2da3SVladimir Oltean 		lane = &priv->lane[i];
283719a2da3SVladimir Oltean 		if (!lane->phy)
284719a2da3SVladimir Oltean 			continue;
285719a2da3SVladimir Oltean 
286719a2da3SVladimir Oltean 		mutex_lock(&lane->phy->mutex);
287719a2da3SVladimir Oltean 
288719a2da3SVladimir Oltean 		if (!lane->init || !lane->powered_up) {
289719a2da3SVladimir Oltean 			mutex_unlock(&lane->phy->mutex);
290719a2da3SVladimir Oltean 			continue;
291719a2da3SVladimir Oltean 		}
292719a2da3SVladimir Oltean 
293719a2da3SVladimir Oltean 		priv->info->cdr_lock_check(lane);
294719a2da3SVladimir Oltean 
295719a2da3SVladimir Oltean 		mutex_unlock(&lane->phy->mutex);
296719a2da3SVladimir Oltean 	}
297719a2da3SVladimir Oltean 
298719a2da3SVladimir Oltean 	queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
299719a2da3SVladimir Oltean 			   msecs_to_jiffies(1000));
300719a2da3SVladimir Oltean }
301719a2da3SVladimir Oltean 
302719a2da3SVladimir Oltean static struct phy *lynx_xlate(struct device *dev,
303719a2da3SVladimir Oltean 			      const struct of_phandle_args *args)
304719a2da3SVladimir Oltean {
305719a2da3SVladimir Oltean 	struct lynx_priv *priv = dev_get_drvdata(dev);
306719a2da3SVladimir Oltean 	int idx;
307719a2da3SVladimir Oltean 
308719a2da3SVladimir Oltean 	if (args->args_count == 0)
309719a2da3SVladimir Oltean 		return of_phy_simple_xlate(dev, args);
310719a2da3SVladimir Oltean 	else if (args->args_count != 1)
311719a2da3SVladimir Oltean 		return ERR_PTR(-ENODEV);
312719a2da3SVladimir Oltean 
313719a2da3SVladimir Oltean 	idx = args->args[0];
314719a2da3SVladimir Oltean 
315719a2da3SVladimir Oltean 	if (WARN_ON(idx >= priv->info->num_lanes ||
316719a2da3SVladimir Oltean 		    idx < priv->info->first_lane))
317719a2da3SVladimir Oltean 		return ERR_PTR(-EINVAL);
318719a2da3SVladimir Oltean 
319719a2da3SVladimir Oltean 	return priv->lane[idx].phy ?: ERR_PTR(-ENODEV);
320719a2da3SVladimir Oltean }
321719a2da3SVladimir Oltean 
322719a2da3SVladimir Oltean static int lynx_probe_lane(struct lynx_priv *priv, int id,
323719a2da3SVladimir Oltean 			   struct device_node *dn,
324719a2da3SVladimir Oltean 			   const struct phy_ops *phy_ops)
325719a2da3SVladimir Oltean {
326719a2da3SVladimir Oltean 	struct lynx_lane *lane = &priv->lane[id];
327719a2da3SVladimir Oltean 	struct phy *phy;
328719a2da3SVladimir Oltean 
329719a2da3SVladimir Oltean 	phy = devm_phy_create(priv->dev, dn, phy_ops);
330719a2da3SVladimir Oltean 	if (IS_ERR(phy))
331719a2da3SVladimir Oltean 		return PTR_ERR(phy);
332719a2da3SVladimir Oltean 
333719a2da3SVladimir Oltean 	lane->priv = priv;
334719a2da3SVladimir Oltean 	lane->phy = phy;
335719a2da3SVladimir Oltean 	lane->id = id;
336719a2da3SVladimir Oltean 	phy_set_drvdata(phy, lane);
337719a2da3SVladimir Oltean 	priv->info->lane_read_configuration(lane);
338719a2da3SVladimir Oltean 
339719a2da3SVladimir Oltean 	return 0;
340719a2da3SVladimir Oltean }
341719a2da3SVladimir Oltean 
342719a2da3SVladimir Oltean int lynx_probe(struct platform_device *pdev, const struct lynx_info *info,
343719a2da3SVladimir Oltean 	       const struct phy_ops *phy_ops)
344719a2da3SVladimir Oltean {
345719a2da3SVladimir Oltean 	struct device *dev = &pdev->dev;
346719a2da3SVladimir Oltean 	struct phy_provider *provider;
347719a2da3SVladimir Oltean 	struct device_node *dn;
348719a2da3SVladimir Oltean 	struct lynx_priv *priv;
349719a2da3SVladimir Oltean 	int err;
350719a2da3SVladimir Oltean 
351719a2da3SVladimir Oltean 	dn = dev_of_node(dev);
352719a2da3SVladimir Oltean 	if (!dn) {
353719a2da3SVladimir Oltean 		dev_err(dev, "Device requires an OF node\n");
354719a2da3SVladimir Oltean 		return -EINVAL;
355719a2da3SVladimir Oltean 	}
356719a2da3SVladimir Oltean 
357719a2da3SVladimir Oltean 	if (!info)
358719a2da3SVladimir Oltean 		return -ENODEV;
359719a2da3SVladimir Oltean 
360719a2da3SVladimir Oltean 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
361719a2da3SVladimir Oltean 	if (!priv)
362719a2da3SVladimir Oltean 		return -ENOMEM;
363719a2da3SVladimir Oltean 
364719a2da3SVladimir Oltean 	priv->dev = dev;
365719a2da3SVladimir Oltean 	priv->info = info;
366c6c1d7dfSVladimir Oltean 	priv->big_endian = device_property_read_bool(dev, "big-endian");
367719a2da3SVladimir Oltean 	dev_set_drvdata(dev, priv);
368719a2da3SVladimir Oltean 	spin_lock_init(&priv->pcc_lock);
369719a2da3SVladimir Oltean 	INIT_DELAYED_WORK(&priv->cdr_check, lynx_cdr_lock_check);
370719a2da3SVladimir Oltean 
371719a2da3SVladimir Oltean 	priv->lane = devm_kcalloc(dev, priv->info->num_lanes,
372719a2da3SVladimir Oltean 				  sizeof(*priv->lane), GFP_KERNEL);
373719a2da3SVladimir Oltean 	if (!priv->lane)
374719a2da3SVladimir Oltean 		return -ENOMEM;
375719a2da3SVladimir Oltean 
376719a2da3SVladimir Oltean 	priv->base = devm_platform_ioremap_resource(pdev, 0);
377719a2da3SVladimir Oltean 	if (IS_ERR(priv->base))
378719a2da3SVladimir Oltean 		return PTR_ERR(priv->base);
379719a2da3SVladimir Oltean 
380719a2da3SVladimir Oltean 	for (int i = 0; i < LYNX_NUM_PLL; i++) {
381719a2da3SVladimir Oltean 		struct lynx_pll *pll = &priv->pll[i];
382719a2da3SVladimir Oltean 
383719a2da3SVladimir Oltean 		pll->priv = priv;
384719a2da3SVladimir Oltean 		pll->id = i;
385719a2da3SVladimir Oltean 		priv->info->pll_read_configuration(pll);
386719a2da3SVladimir Oltean 	}
387719a2da3SVladimir Oltean 
388719a2da3SVladimir Oltean 	if (of_get_child_count(dn)) {
389719a2da3SVladimir Oltean 		struct device_node *child;
390719a2da3SVladimir Oltean 
391719a2da3SVladimir Oltean 		for_each_available_child_of_node(dn, child) {
392719a2da3SVladimir Oltean 			u32 reg;
393719a2da3SVladimir Oltean 
394719a2da3SVladimir Oltean 			/* PHY subnode name must be 'phy'. */
395719a2da3SVladimir Oltean 			if (!(of_node_name_eq(child, "phy")))
396719a2da3SVladimir Oltean 				continue;
397719a2da3SVladimir Oltean 
398719a2da3SVladimir Oltean 			if (of_property_read_u32(child, "reg", &reg)) {
399719a2da3SVladimir Oltean 				dev_err(dev, "No \"reg\" property for %pOF\n", child);
400719a2da3SVladimir Oltean 				of_node_put(child);
401719a2da3SVladimir Oltean 				return -EINVAL;
402719a2da3SVladimir Oltean 			}
403719a2da3SVladimir Oltean 
404719a2da3SVladimir Oltean 			if (reg < priv->info->first_lane || reg >= priv->info->num_lanes) {
405719a2da3SVladimir Oltean 				dev_err(dev, "\"reg\" property out of range for %pOF\n", child);
406719a2da3SVladimir Oltean 				of_node_put(child);
407719a2da3SVladimir Oltean 				return -EINVAL;
408719a2da3SVladimir Oltean 			}
409719a2da3SVladimir Oltean 
410719a2da3SVladimir Oltean 			err = lynx_probe_lane(priv, reg, child, phy_ops);
411719a2da3SVladimir Oltean 			if (err) {
412719a2da3SVladimir Oltean 				of_node_put(child);
413719a2da3SVladimir Oltean 				return err;
414719a2da3SVladimir Oltean 			}
415719a2da3SVladimir Oltean 		}
416719a2da3SVladimir Oltean 	} else {
417719a2da3SVladimir Oltean 		for (int i = priv->info->first_lane; i < priv->info->num_lanes; i++) {
418719a2da3SVladimir Oltean 			err = lynx_probe_lane(priv, i, NULL, phy_ops);
419719a2da3SVladimir Oltean 			if (err)
420719a2da3SVladimir Oltean 				return err;
421719a2da3SVladimir Oltean 		}
422719a2da3SVladimir Oltean 	}
423719a2da3SVladimir Oltean 
424719a2da3SVladimir Oltean 	provider = devm_of_phy_provider_register(dev, lynx_xlate);
425719a2da3SVladimir Oltean 	if (IS_ERR(provider))
426719a2da3SVladimir Oltean 		return PTR_ERR(provider);
427719a2da3SVladimir Oltean 
428719a2da3SVladimir Oltean 	queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
429719a2da3SVladimir Oltean 			   msecs_to_jiffies(1000));
430719a2da3SVladimir Oltean 
431719a2da3SVladimir Oltean 	return 0;
432719a2da3SVladimir Oltean }
433719a2da3SVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_probe, "PHY_FSL_LYNX");
434719a2da3SVladimir Oltean 
435719a2da3SVladimir Oltean void lynx_remove(struct platform_device *pdev)
436719a2da3SVladimir Oltean {
437719a2da3SVladimir Oltean 	struct device *dev = &pdev->dev;
438719a2da3SVladimir Oltean 	struct lynx_priv *priv = dev_get_drvdata(dev);
439719a2da3SVladimir Oltean 
440719a2da3SVladimir Oltean 	cancel_delayed_work_sync(&priv->cdr_check);
441719a2da3SVladimir Oltean }
442719a2da3SVladimir Oltean EXPORT_SYMBOL_NS_GPL(lynx_remove, "PHY_FSL_LYNX");
443719a2da3SVladimir Oltean 
444ac0ffe7cSVladimir Oltean MODULE_LICENSE("GPL");
445ac0ffe7cSVladimir Oltean MODULE_DESCRIPTION("Freescale Lynx SerDes core functionality");
446