Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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 void lynx_pcs_get_state_usxgmii(struct mdio_device *pcs,
 39				       struct phylink_link_state *state)
 40{
 41	struct mii_bus *bus = pcs->bus;
 42	int addr = pcs->addr;
 43	int status, lpa;
 44
 45	status = mdiobus_c45_read(bus, addr, MDIO_MMD_VEND2, MII_BMSR);
 46	if (status < 0)
 47		return;
 48
 49	state->link = !!(status & MDIO_STAT1_LSTATUS);
 50	state->an_complete = !!(status & MDIO_AN_STAT1_COMPLETE);
 51	if (!state->link || !state->an_complete)
 52		return;
 53
 54	lpa = mdiobus_c45_read(bus, addr, MDIO_MMD_VEND2, MII_LPA);
 55	if (lpa < 0)
 56		return;
 57
 58	phylink_decode_usxgmii_word(state, lpa);
 59}
 60
 61static void lynx_pcs_get_state_2500basex(struct mdio_device *pcs,
 62					 struct phylink_link_state *state)
 63{
 64	int bmsr;
 65
 66	bmsr = mdiodev_read(pcs, MII_BMSR);
 67	if (bmsr < 0) {
 68		state->link = false;
 69		return;
 70	}
 71
 72	state->link = !!(bmsr & BMSR_LSTATUS);
 73	state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
 74	if (!state->link)
 75		return;
 76
 77	state->speed = SPEED_2500;
 78	state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX;
 79	state->duplex = DUPLEX_FULL;
 80}
 81
 82static void lynx_pcs_get_state(struct phylink_pcs *pcs,
 83			       struct phylink_link_state *state)
 84{
 85	struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
 86
 87	switch (state->interface) {
 88	case PHY_INTERFACE_MODE_1000BASEX:
 89	case PHY_INTERFACE_MODE_SGMII:
 90	case PHY_INTERFACE_MODE_QSGMII:
 91		phylink_mii_c22_pcs_get_state(lynx->mdio, state);
 92		break;
 93	case PHY_INTERFACE_MODE_2500BASEX:
 94		lynx_pcs_get_state_2500basex(lynx->mdio, state);
 95		break;
 96	case PHY_INTERFACE_MODE_USXGMII:
 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		if_mode = 0;
134	} else {
135		/* SGMII and QSGMII */
136		if_mode = IF_MODE_SGMII_EN;
137		if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
138			if_mode |= IF_MODE_USE_SGMII_AN;
139	}
140
141	err = mdiodev_modify(pcs, IF_MODE,
142			     IF_MODE_SGMII_EN | IF_MODE_USE_SGMII_AN,
143			     if_mode);
144	if (err)
145		return err;
146
147	return phylink_mii_c22_pcs_config(pcs, interface, advertising,
148					  neg_mode);
149}
150
151static int lynx_pcs_config_usxgmii(struct mdio_device *pcs,
152				   const unsigned long *advertising,
153				   unsigned int neg_mode)
154{
155	struct mii_bus *bus = pcs->bus;
156	int addr = pcs->addr;
157
158	if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) {
159		dev_err(&pcs->dev, "USXGMII only supports in-band AN for now\n");
160		return -EOPNOTSUPP;
161	}
162
163	/* Configure device ability for the USXGMII Replicator */
164	return mdiobus_c45_write(bus, addr, MDIO_MMD_VEND2, MII_ADVERTISE,
165				 MDIO_USXGMII_10G | MDIO_USXGMII_LINK |
166				 MDIO_USXGMII_FULL_DUPLEX |
167				 ADVERTISE_SGMII | ADVERTISE_LPACK);
168}
169
170static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
171			   phy_interface_t ifmode,
172			   const unsigned long *advertising, bool permit)
173{
174	struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
175
176	switch (ifmode) {
177	case PHY_INTERFACE_MODE_1000BASEX:
178	case PHY_INTERFACE_MODE_SGMII:
179	case PHY_INTERFACE_MODE_QSGMII:
180		return lynx_pcs_config_giga(lynx->mdio, ifmode, advertising,
181					    neg_mode);
182	case PHY_INTERFACE_MODE_2500BASEX:
183		if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
184			dev_err(&lynx->mdio->dev,
185				"AN not supported on 3.125GHz SerDes lane\n");
186			return -EOPNOTSUPP;
187		}
188		break;
189	case PHY_INTERFACE_MODE_USXGMII:
190		return lynx_pcs_config_usxgmii(lynx->mdio, advertising,
191					       neg_mode);
192	case PHY_INTERFACE_MODE_10GBASER:
193		/* Nothing to do here for 10GBASER */
194		break;
195	default:
196		return -EOPNOTSUPP;
197	}
198
199	return 0;
200}
201
202static void lynx_pcs_an_restart(struct phylink_pcs *pcs)
203{
204	struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
205
206	phylink_mii_c22_pcs_an_restart(lynx->mdio);
207}
208
209static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs,
210				   unsigned int neg_mode,
211				   int speed, int duplex)
212{
213	u16 if_mode = 0, sgmii_speed;
214
215	/* The PCS needs to be configured manually only
216	 * when not operating on in-band mode
217	 */
218	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
219		return;
220
221	if (duplex == DUPLEX_HALF)
222		if_mode |= IF_MODE_HALF_DUPLEX;
223
224	switch (speed) {
225	case SPEED_1000:
226		sgmii_speed = SGMII_SPEED_1000;
227		break;
228	case SPEED_100:
229		sgmii_speed = SGMII_SPEED_100;
230		break;
231	case SPEED_10:
232		sgmii_speed = SGMII_SPEED_10;
233		break;
234	case SPEED_UNKNOWN:
235		/* Silently don't do anything */
236		return;
237	default:
238		dev_err(&pcs->dev, "Invalid PCS speed %d\n", speed);
239		return;
240	}
241	if_mode |= IF_MODE_SPEED(sgmii_speed);
242
243	mdiodev_modify(pcs, IF_MODE,
244		       IF_MODE_HALF_DUPLEX | IF_MODE_SPEED_MSK,
245		       if_mode);
246}
247
248/* 2500Base-X is SerDes protocol 7 on Felix and 6 on ENETC. It is a SerDes lane
249 * clocked at 3.125 GHz which encodes symbols with 8b/10b and does not have
250 * auto-negotiation of any link parameters. Electrically it is compatible with
251 * a single lane of XAUI.
252 * The hardware reference manual wants to call this mode SGMII, but it isn't
253 * really, since the fundamental features of SGMII:
254 * - Downgrading the link speed by duplicating symbols
255 * - Auto-negotiation
256 * are not there.
257 * The speed is configured at 1000 in the IF_MODE because the clock frequency
258 * is actually given by a PLL configured in the Reset Configuration Word (RCW).
259 * Since there is no difference between fixed speed SGMII w/o AN and 802.3z w/o
260 * AN, we call this PHY interface type 2500Base-X. In case a PHY negotiates a
261 * lower link speed on line side, the system-side interface remains fixed at
262 * 2500 Mbps and we do rate adaptation through pause frames.
263 */
264static void lynx_pcs_link_up_2500basex(struct mdio_device *pcs,
265				       unsigned int neg_mode,
266				       int speed, int duplex)
267{
268	u16 if_mode = 0;
269
270	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
271		dev_err(&pcs->dev, "AN not supported for 2500BaseX\n");
272		return;
273	}
274
275	if (duplex == DUPLEX_HALF)
276		if_mode |= IF_MODE_HALF_DUPLEX;
277	if_mode |= IF_MODE_SPEED(SGMII_SPEED_2500);
278
279	mdiodev_modify(pcs, IF_MODE,
280		       IF_MODE_HALF_DUPLEX | IF_MODE_SPEED_MSK,
281		       if_mode);
282}
283
284static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
285			     phy_interface_t interface,
286			     int speed, int duplex)
287{
288	struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
289
290	switch (interface) {
291	case PHY_INTERFACE_MODE_SGMII:
292	case PHY_INTERFACE_MODE_QSGMII:
293		lynx_pcs_link_up_sgmii(lynx->mdio, neg_mode, speed, duplex);
294		break;
295	case PHY_INTERFACE_MODE_2500BASEX:
296		lynx_pcs_link_up_2500basex(lynx->mdio, neg_mode, speed, duplex);
297		break;
298	case PHY_INTERFACE_MODE_USXGMII:
299		/* At the moment, only in-band AN is supported for USXGMII
300		 * so nothing to do in link_up
301		 */
302		break;
303	default:
304		break;
305	}
306}
307
308static const struct phylink_pcs_ops lynx_pcs_phylink_ops = {
309	.pcs_get_state = lynx_pcs_get_state,
310	.pcs_config = lynx_pcs_config,
311	.pcs_an_restart = lynx_pcs_an_restart,
312	.pcs_link_up = lynx_pcs_link_up,
313};
314
315static struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio)
316{
317	struct lynx_pcs *lynx;
318
319	lynx = kzalloc(sizeof(*lynx), GFP_KERNEL);
320	if (!lynx)
321		return ERR_PTR(-ENOMEM);
322
323	mdio_device_get(mdio);
324	lynx->mdio = mdio;
325	lynx->pcs.ops = &lynx_pcs_phylink_ops;
326	lynx->pcs.neg_mode = true;
327	lynx->pcs.poll = true;
328
329	return lynx_to_phylink_pcs(lynx);
330}
331
332struct phylink_pcs *lynx_pcs_create_mdiodev(struct mii_bus *bus, int addr)
333{
334	struct mdio_device *mdio;
335	struct phylink_pcs *pcs;
336
337	mdio = mdio_device_create(bus, addr);
338	if (IS_ERR(mdio))
339		return ERR_CAST(mdio);
340
341	pcs = lynx_pcs_create(mdio);
342
343	/* lynx_create() has taken a refcount on the mdiodev if it was
344	 * successful. If lynx_create() fails, this will free the mdio
345	 * device here. In any case, we don't need to hold our reference
346	 * anymore, and putting it here will allow mdio_device_put() in
347	 * lynx_destroy() to automatically free the mdio device.
348	 */
349	mdio_device_put(mdio);
350
351	return pcs;
352}
353EXPORT_SYMBOL(lynx_pcs_create_mdiodev);
354
355/*
356 * lynx_pcs_create_fwnode() creates a lynx PCS instance from the fwnode
357 * device indicated by node.
358 *
359 * Returns:
360 *  -ENODEV if the fwnode is marked unavailable
361 *  -EPROBE_DEFER if we fail to find the device
362 *  -ENOMEM if we fail to allocate memory
363 *  pointer to a phylink_pcs on success
364 */
365struct phylink_pcs *lynx_pcs_create_fwnode(struct fwnode_handle *node)
366{
367	struct mdio_device *mdio;
368	struct phylink_pcs *pcs;
369
370	if (!fwnode_device_is_available(node))
371		return ERR_PTR(-ENODEV);
372
373	mdio = fwnode_mdio_find_device(node);
374	if (!mdio)
375		return ERR_PTR(-EPROBE_DEFER);
376
377	pcs = lynx_pcs_create(mdio);
378
379	/* lynx_create() has taken a refcount on the mdiodev if it was
380	 * successful. If lynx_create() fails, this will free the mdio
381	 * device here. In any case, we don't need to hold our reference
382	 * anymore, and putting it here will allow mdio_device_put() in
383	 * lynx_destroy() to automatically free the mdio device.
384	 */
385	mdio_device_put(mdio);
386
387	return pcs;
388}
389EXPORT_SYMBOL_GPL(lynx_pcs_create_fwnode);
390
391void lynx_pcs_destroy(struct phylink_pcs *pcs)
392{
393	struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
394
395	mdio_device_put(lynx->mdio);
396	kfree(lynx);
397}
398EXPORT_SYMBOL(lynx_pcs_destroy);
399
400MODULE_DESCRIPTION("NXP Lynx PCS phylink library");
401MODULE_LICENSE("Dual BSD/GPL");