File Index
Files and directories
Sign in
FILE

pcs-lynx.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/pcs/pcs-lynx.c C
  1// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
  2/* Copyright 2020 NXP
  3 * Lynx PCS MDIO helpers
  4 */
  5
  6#include <linux/mdio.h>
  7#include <linux/phylink.h>
  8#include <linux/pcs-lynx.h>
  9#include <linux/property.h>
 10
 11#define SGMII_CLOCK_PERIOD_NS		8 /* PCS is clocked at 125 MHz */
 12#define LINK_TIMER_VAL(ns)		((u32)((ns) / SGMII_CLOCK_PERIOD_NS))
 13
 14#define LINK_TIMER_LO			0x12
 15#define LINK_TIMER_HI			0x13
 16#define IF_MODE				0x14
 17#define IF_MODE_SGMII_EN		BIT(0)
 18#define IF_MODE_USE_SGMII_AN		BIT(1)
 19#define IF_MODE_SPEED(x)		(((x) << 2) & GENMASK(3, 2))
 20#define IF_MODE_SPEED_MSK		GENMASK(3, 2)
 21#define IF_MODE_HALF_DUPLEX		BIT(4)
 22
 23struct lynx_pcs {
 24	struct phylink_pcs pcs;
 25	struct mdio_device *mdio;
 26};
 27
 28enum sgmii_speed {
 29	SGMII_SPEED_10		= 0,
 30	SGMII_SPEED_100		= 1,
 31	SGMII_SPEED_1000	= 2,
 32	SGMII_SPEED_2500	= 2,
 33};
 34
 35#define phylink_pcs_to_lynx(pl_pcs) container_of((pl_pcs), struct lynx_pcs, pcs)
 36#define lynx_to_phylink_pcs(lynx) (&(lynx)->pcs)
 37
 38static unsigned int lynx_pcs_inband_caps(struct phylink_pcs *pcs,
 39					 phy_interface_t interface)
 40{
 41	switch (interface) {
 42	case PHY_INTERFACE_MODE_1000BASEX:
 43	case PHY_INTERFACE_MODE_2500BASEX:
 44	case PHY_INTERFACE_MODE_SGMII:
 45	case PHY_INTERFACE_MODE_QSGMII:
 46		return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
 47
 48	case PHY_INTERFACE_MODE_10GBASER:
 49		return LINK_INBAND_DISABLE;
 50
 51	case PHY_INTERFACE_MODE_USXGMII:
 52	case PHY_INTERFACE_MODE_10G_QXGMII:
 53		return LINK_INBAND_ENABLE;
 54
 55	default:
 56		return 0;
 57	}
 58}
 59
 60static void lynx_pcs_get_state_usxgmii(struct mdio_device *pcs,
 61				       struct phylink_link_state *state)
 62{
 63	struct mii_bus *bus = pcs->bus;
 64	int addr = pcs->addr;
 65	int status, lpa;
 66
 67	status = mdiobus_c45_read(bus, addr, MDIO_MMD_VEND2, MII_BMSR);
 68	if (status < 0)
 69		return;
 70
 71	state->link = !!(status & MDIO_STAT1_LSTATUS);
 72	state->an_complete = !!(status & MDIO_AN_STAT1_COMPLETE);
 73	if (!state->link || !state->an_complete)
 74		return;
 75
 76	lpa = mdiobus_c45_read(bus, addr, MDIO_MMD_VEND2, MII_LPA);
 77	if (lpa < 0)
 78		return;
 79
 80	phylink_decode_usxgmii_word(state, lpa);
 81}
 82
 83static void lynx_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
 84			       struct phylink_link_state *state)
 85{
 86	struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
 87
 88	switch (state->interface) {
 89	case PHY_INTERFACE_MODE_1000BASEX:
 90	case PHY_INTERFACE_MODE_2500BASEX:
 91	case PHY_INTERFACE_MODE_SGMII:
 92	case PHY_INTERFACE_MODE_QSGMII:
 93		phylink_mii_c22_pcs_get_state(lynx->mdio, neg_mode, state);
 94		break;
 95	case PHY_INTERFACE_MODE_USXGMII:
 96	case PHY_INTERFACE_MODE_10G_QXGMII:
 97		lynx_pcs_get_state_usxgmii(lynx->mdio, state);
 98		break;
 99	case PHY_INTERFACE_MODE_10GBASER:
100		phylink_mii_c45_pcs_get_state(lynx->mdio, state);
101		break;
102	default:
103		break;
104	}
105
106	dev_dbg(&lynx->mdio->dev,
107		"mode=%s/%s/%s link=%u an_complete=%u\n",
108		phy_modes(state->interface),
109		phy_speed_to_str(state->speed),
110		phy_duplex_to_str(state->duplex),
111		state->link, state->an_complete);
112}
113
114static int lynx_pcs_config_giga(struct mdio_device *pcs,
115				phy_interface_t interface,
116				const unsigned long *advertising,
117				unsigned int neg_mode)
118{
119	int link_timer_ns;
120	u32 link_timer;
121	u16 if_mode;
122	int err;
123
124	link_timer_ns = phylink_get_link_timer_ns(interface);
125	if (link_timer_ns > 0) {
126		link_timer = LINK_TIMER_VAL(link_timer_ns);
127
128		mdiodev_write(pcs, LINK_TIMER_LO, link_timer & 0xffff);
129		mdiodev_write(pcs, LINK_TIMER_HI, link_timer >> 16);
130	}
131
132	if (interface == PHY_INTERFACE_MODE_1000BASEX ||
133	    interface == PHY_INTERFACE_MODE_2500BASEX) {
134		if_mode = 0;
135	} else {
136		/* SGMII and QSGMII */
137		if_mode = IF_MODE_SGMII_EN;
138		if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
139			if_mode |= IF_MODE_USE_SGMII_AN;
140	}
141
142	err = mdiodev_modify(pcs, IF_MODE,
143			     IF_MODE_SGMII_EN | IF_MODE_USE_SGMII_AN,
144			     if_mode);
145	if (err)
146		return err;
147
148	return phylink_mii_c22_pcs_config(pcs, interface, advertising,
149					  neg_mode);
150}
151
152static int lynx_pcs_config_usxgmii(struct mdio_device *pcs,
153				   phy_interface_t interface,
154				   const unsigned long *advertising,
155				   unsigned int neg_mode)
156{
157	struct mii_bus *bus = pcs->bus;
158	int addr = pcs->addr;
159
160	if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) {
161		dev_err(&pcs->dev, "%s only supports in-band AN for now\n",
162			phy_modes(interface));
163		return -EOPNOTSUPP;
164	}
165
166	/* Configure device ability for the USXGMII Replicator */
167	return mdiobus_c45_write(bus, addr, MDIO_MMD_VEND2, MII_ADVERTISE,
168				 MDIO_USXGMII_10G | MDIO_USXGMII_LINK |
169				 MDIO_USXGMII_FULL_DUPLEX |
170				 ADVERTISE_SGMII | ADVERTISE_LPACK);
171}
172
173static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
174			   phy_interface_t ifmode,
175			   const unsigned long *advertising, bool permit)
176{
177	struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
178
179	switch (ifmode) {
180	case PHY_INTERFACE_MODE_1000BASEX:
181	case PHY_INTERFACE_MODE_SGMII:
182	case PHY_INTERFACE_MODE_QSGMII:
183	case PHY_INTERFACE_MODE_2500BASEX:
184		return lynx_pcs_config_giga(lynx->mdio, ifmode, advertising,
185					    neg_mode);
186	case PHY_INTERFACE_MODE_USXGMII:
187	case PHY_INTERFACE_MODE_10G_QXGMII:
188		return lynx_pcs_config_usxgmii(lynx->mdio, ifmode, advertising,
189					       neg_mode);
190	case PHY_INTERFACE_MODE_10GBASER:
191		/* Nothing to do here for 10GBASER */
192		break;
193	default:
194		return -EOPNOTSUPP;
195	}
196
197	return 0;
198}
199
200static void lynx_pcs_an_restart(struct phylink_pcs *pcs)
201{
202	struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
203
204	phylink_mii_c22_pcs_an_restart(lynx->mdio);
205}
206
207static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs,
208				   unsigned int neg_mode,
209				   int speed, int duplex)
210{
211	u16 if_mode = 0, sgmii_speed;
212
213	/* The PCS needs to be configured manually only
214	 * when not operating on in-band mode
215	 */
216	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
217		return;
218
219	if (duplex == DUPLEX_HALF)
220		if_mode |= IF_MODE_HALF_DUPLEX;
221
222	switch (speed) {
223	case SPEED_1000:
224		sgmii_speed = SGMII_SPEED_1000;
225		break;
226	case SPEED_100:
227		sgmii_speed = SGMII_SPEED_100;
228		break;
229	case SPEED_10:
230		sgmii_speed = SGMII_SPEED_10;
231		break;
232	case SPEED_UNKNOWN:
233		/* Silently don't do anything */
234		return;
235	default:
236		dev_err(&pcs->dev, "Invalid PCS speed %d\n", speed);
237		return;
238	}
239	if_mode |= IF_MODE_SPEED(sgmii_speed);
240
241	mdiodev_modify(pcs, IF_MODE,
242		       IF_MODE_HALF_DUPLEX | IF_MODE_SPEED_MSK,
243		       if_mode);
244}
245
246static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
247			     phy_interface_t interface,
248			     int speed, int duplex)
249{
250	struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
251
252	switch (interface) {
253	case PHY_INTERFACE_MODE_SGMII:
254	case PHY_INTERFACE_MODE_QSGMII:
255		lynx_pcs_link_up_sgmii(lynx->mdio, neg_mode, speed, duplex);
256		break;
257	case PHY_INTERFACE_MODE_USXGMII:
258	case PHY_INTERFACE_MODE_10G_QXGMII:
259		/* At the moment, only in-band AN is supported for USXGMII
260		 * so nothing to do in link_up
261		 */
262		break;
263	default:
264		break;
265	}
266}
267
268static const struct phylink_pcs_ops lynx_pcs_phylink_ops = {
269	.pcs_inband_caps = lynx_pcs_inband_caps,
270	.pcs_get_state = lynx_pcs_get_state,
271	.pcs_config = lynx_pcs_config,
272	.pcs_an_restart = lynx_pcs_an_restart,
273	.pcs_link_up = lynx_pcs_link_up,
274};
275
276static const phy_interface_t lynx_interfaces[] = {
277	PHY_INTERFACE_MODE_SGMII,
278	PHY_INTERFACE_MODE_QSGMII,
279	PHY_INTERFACE_MODE_1000BASEX,
280	PHY_INTERFACE_MODE_2500BASEX,
281	PHY_INTERFACE_MODE_10GBASER,
282	PHY_INTERFACE_MODE_USXGMII,
283	PHY_INTERFACE_MODE_10G_QXGMII,
284};
285
286static struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio)
287{
288	struct lynx_pcs *lynx;
289	int i;
290
291	lynx = kzalloc(sizeof(*lynx), GFP_KERNEL);
292	if (!lynx)
293		return ERR_PTR(-ENOMEM);
294
295	mdio_device_get(mdio);
296	lynx->mdio = mdio;
297	lynx->pcs.ops = &lynx_pcs_phylink_ops;
298	lynx->pcs.poll = true;
299
300	for (i = 0; i < ARRAY_SIZE(lynx_interfaces); i++)
301		__set_bit(lynx_interfaces[i], lynx->pcs.supported_interfaces);
302
303	return lynx_to_phylink_pcs(lynx);
304}
305
306struct phylink_pcs *lynx_pcs_create_mdiodev(struct mii_bus *bus, int addr)
307{
308	struct mdio_device *mdio;
309	struct phylink_pcs *pcs;
310
311	mdio = mdio_device_create(bus, addr);
312	if (IS_ERR(mdio))
313		return ERR_CAST(mdio);
314
315	pcs = lynx_pcs_create(mdio);
316
317	/* lynx_create() has taken a refcount on the mdiodev if it was
318	 * successful. If lynx_create() fails, this will free the mdio
319	 * device here. In any case, we don't need to hold our reference
320	 * anymore, and putting it here will allow mdio_device_put() in
321	 * lynx_destroy() to automatically free the mdio device.
322	 */
323	mdio_device_put(mdio);
324
325	return pcs;
326}
327EXPORT_SYMBOL(lynx_pcs_create_mdiodev);
328
329/*
330 * lynx_pcs_create_fwnode() creates a lynx PCS instance from the fwnode
331 * device indicated by node.
332 *
333 * Returns:
334 *  -ENODEV if the fwnode is marked unavailable
335 *  -EPROBE_DEFER if we fail to find the device
336 *  -ENOMEM if we fail to allocate memory
337 *  pointer to a phylink_pcs on success
338 */
339struct phylink_pcs *lynx_pcs_create_fwnode(struct fwnode_handle *node)
340{
341	struct mdio_device *mdio;
342	struct phylink_pcs *pcs;
343
344	if (!fwnode_device_is_available(node))
345		return ERR_PTR(-ENODEV);
346
347	mdio = fwnode_mdio_find_device(node);
348	if (!mdio)
349		return ERR_PTR(-EPROBE_DEFER);
350
351	pcs = lynx_pcs_create(mdio);
352
353	/* lynx_create() has taken a refcount on the mdiodev if it was
354	 * successful. If lynx_create() fails, this will free the mdio
355	 * device here. In any case, we don't need to hold our reference
356	 * anymore, and putting it here will allow mdio_device_put() in
357	 * lynx_destroy() to automatically free the mdio device.
358	 */
359	mdio_device_put(mdio);
360
361	return pcs;
362}
363EXPORT_SYMBOL_GPL(lynx_pcs_create_fwnode);
364
365void lynx_pcs_destroy(struct phylink_pcs *pcs)
366{
367	struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
368
369	mdio_device_put(lynx->mdio);
370	kfree(lynx);
371}
372EXPORT_SYMBOL(lynx_pcs_destroy);
373
374MODULE_DESCRIPTION("NXP Lynx PCS phylink library");
375MODULE_LICENSE("Dual BSD/GPL");
~/ Read-only file preview.