File Index
Files and directories
Sign in
FILE

pcs-639x.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/dsa/mv88e6xxx/pcs-639x.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Marvell 88E6352 family SERDES PCS support
  4 *
  5 * Copyright (c) 2008 Marvell Semiconductor
  6 *
  7 * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>
  8 */
  9#include <linux/interrupt.h>
 10#include <linux/irqdomain.h>
 11#include <linux/mii.h>
 12#include <linux/string_choices.h>
 13
 14#include "chip.h"
 15#include "global2.h"
 16#include "phy.h"
 17#include "port.h"
 18#include "serdes.h"
 19
 20struct mv88e639x_pcs {
 21	struct mdio_device mdio;
 22	struct phylink_pcs sgmii_pcs;
 23	struct phylink_pcs xg_pcs;
 24	bool erratum_3_14;
 25	bool supports_5g;
 26	phy_interface_t interface;
 27	unsigned int irq;
 28	char name[64];
 29	irqreturn_t (*handle_irq)(struct mv88e639x_pcs *mpcs);
 30};
 31
 32static int mv88e639x_read(struct mv88e639x_pcs *mpcs, u16 regnum, u16 *val)
 33{
 34	int err;
 35
 36	err = mdiodev_c45_read(&mpcs->mdio, MDIO_MMD_PHYXS, regnum);
 37	if (err < 0)
 38		return err;
 39
 40	*val = err;
 41
 42	return 0;
 43}
 44
 45static int mv88e639x_write(struct mv88e639x_pcs *mpcs, u16 regnum, u16 val)
 46{
 47	return mdiodev_c45_write(&mpcs->mdio, MDIO_MMD_PHYXS, regnum, val);
 48}
 49
 50static int mv88e639x_modify(struct mv88e639x_pcs *mpcs, u16 regnum, u16 mask,
 51			    u16 val)
 52{
 53	return mdiodev_c45_modify(&mpcs->mdio, MDIO_MMD_PHYXS, regnum, mask,
 54				  val);
 55}
 56
 57static int mv88e639x_modify_changed(struct mv88e639x_pcs *mpcs, u16 regnum,
 58				    u16 mask, u16 set)
 59{
 60	return mdiodev_c45_modify_changed(&mpcs->mdio, MDIO_MMD_PHYXS, regnum,
 61					  mask, set);
 62}
 63
 64static struct mv88e639x_pcs *
 65mv88e639x_pcs_alloc(struct device *dev, struct mii_bus *bus, unsigned int addr,
 66		    int port)
 67{
 68	struct mv88e639x_pcs *mpcs;
 69
 70	mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
 71	if (!mpcs)
 72		return NULL;
 73
 74	mpcs->mdio.dev.parent = dev;
 75	mpcs->mdio.bus = bus;
 76	mpcs->mdio.addr = addr;
 77
 78	snprintf(mpcs->name, sizeof(mpcs->name),
 79		 "mv88e6xxx-%s-serdes-%d", dev_name(dev), port);
 80
 81	return mpcs;
 82}
 83
 84static irqreturn_t mv88e639x_pcs_handle_irq(int irq, void *dev_id)
 85{
 86	struct mv88e639x_pcs *mpcs = dev_id;
 87	irqreturn_t (*handler)(struct mv88e639x_pcs *);
 88
 89	handler = READ_ONCE(mpcs->handle_irq);
 90	if (!handler)
 91		return IRQ_NONE;
 92
 93	return handler(mpcs);
 94}
 95
 96static int mv88e639x_pcs_setup_irq(struct mv88e639x_pcs *mpcs,
 97				   struct mv88e6xxx_chip *chip, int port)
 98{
 99	unsigned int irq;
100
101	irq = mv88e6xxx_serdes_irq_mapping(chip, port);
102	if (!irq) {
103		/* Use polling mode */
104		mpcs->sgmii_pcs.poll = true;
105		mpcs->xg_pcs.poll = true;
106		return 0;
107	}
108
109	mpcs->irq = irq;
110
111	return request_threaded_irq(irq, NULL, mv88e639x_pcs_handle_irq,
112				    IRQF_ONESHOT, mpcs->name, mpcs);
113}
114
115static void mv88e639x_pcs_teardown(struct mv88e6xxx_chip *chip, int port)
116{
117	struct mv88e639x_pcs *mpcs = chip->ports[port].pcs_private;
118
119	if (!mpcs)
120		return;
121
122	if (mpcs->irq)
123		free_irq(mpcs->irq, mpcs);
124
125	kfree(mpcs);
126
127	chip->ports[port].pcs_private = NULL;
128}
129
130static struct mv88e639x_pcs *sgmii_pcs_to_mv88e639x_pcs(struct phylink_pcs *pcs)
131{
132	return container_of(pcs, struct mv88e639x_pcs, sgmii_pcs);
133}
134
135static irqreturn_t mv88e639x_sgmii_handle_irq(struct mv88e639x_pcs *mpcs)
136{
137	u16 int_status;
138	int err;
139
140	err = mv88e639x_read(mpcs, MV88E6390_SGMII_INT_STATUS, &int_status);
141	if (err)
142		return IRQ_NONE;
143
144	if (int_status & (MV88E6390_SGMII_INT_LINK_DOWN |
145			  MV88E6390_SGMII_INT_LINK_UP)) {
146		phylink_pcs_change(&mpcs->sgmii_pcs,
147				   int_status & MV88E6390_SGMII_INT_LINK_UP);
148
149		return IRQ_HANDLED;
150	}
151
152	return IRQ_NONE;
153}
154
155static int mv88e639x_sgmii_pcs_control_irq(struct mv88e639x_pcs *mpcs,
156					   bool enable)
157{
158	u16 val = 0;
159
160	if (enable)
161		val |= MV88E6390_SGMII_INT_LINK_DOWN |
162		       MV88E6390_SGMII_INT_LINK_UP;
163
164	return mv88e639x_modify(mpcs, MV88E6390_SGMII_INT_ENABLE,
165				MV88E6390_SGMII_INT_LINK_DOWN |
166				MV88E6390_SGMII_INT_LINK_UP, val);
167}
168
169static int mv88e639x_sgmii_pcs_control_pwr(struct mv88e639x_pcs *mpcs,
170					   bool enable)
171{
172	u16 mask, val;
173
174	if (enable) {
175		mask = BMCR_RESET | BMCR_LOOPBACK | BMCR_PDOWN;
176		val = 0;
177	} else {
178		mask = val = BMCR_PDOWN;
179	}
180
181	return mv88e639x_modify(mpcs, MV88E6390_SGMII_BMCR, mask, val);
182}
183
184static int mv88e639x_sgmii_pcs_enable(struct phylink_pcs *pcs)
185{
186	struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs);
187
188	/* power enable done in post_config */
189	mpcs->handle_irq = mv88e639x_sgmii_handle_irq;
190
191	return mv88e639x_sgmii_pcs_control_irq(mpcs, !!mpcs->irq);
192}
193
194static void mv88e639x_sgmii_pcs_disable(struct phylink_pcs *pcs)
195{
196	struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs);
197
198	mv88e639x_sgmii_pcs_control_irq(mpcs, false);
199	mv88e639x_sgmii_pcs_control_pwr(mpcs, false);
200}
201
202static void mv88e639x_sgmii_pcs_pre_config(struct phylink_pcs *pcs,
203					   phy_interface_t interface)
204{
205	struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs);
206
207	mv88e639x_sgmii_pcs_control_pwr(mpcs, false);
208}
209
210static int mv88e6390_erratum_3_14(struct mv88e639x_pcs *mpcs)
211{
212	static const int lanes[] = { MV88E6390_PORT9_LANE0, MV88E6390_PORT9_LANE1,
213		MV88E6390_PORT9_LANE2, MV88E6390_PORT9_LANE3,
214		MV88E6390_PORT10_LANE0, MV88E6390_PORT10_LANE1,
215		MV88E6390_PORT10_LANE2, MV88E6390_PORT10_LANE3 };
216	int err, i;
217
218	/* 88e6190x and 88e6390x errata 3.14:
219	 * After chip reset, SERDES reconfiguration or SERDES core
220	 * Software Reset, the SERDES lanes may not be properly aligned
221	 * resulting in CRC errors
222	 */
223
224	for (i = 0; i < ARRAY_SIZE(lanes); i++) {
225		err = mdiobus_c45_write(mpcs->mdio.bus, lanes[i],
226					MDIO_MMD_PHYXS,
227					0xf054, 0x400C);
228		if (err)
229			return err;
230
231		err = mdiobus_c45_write(mpcs->mdio.bus, lanes[i],
232					MDIO_MMD_PHYXS,
233					0xf054, 0x4000);
234		if (err)
235			return err;
236	}
237
238	return 0;
239}
240
241static int mv88e639x_sgmii_pcs_post_config(struct phylink_pcs *pcs,
242					   phy_interface_t interface)
243{
244	struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs);
245	int err;
246
247	mv88e639x_sgmii_pcs_control_pwr(mpcs, true);
248
249	if (mpcs->erratum_3_14) {
250		err = mv88e6390_erratum_3_14(mpcs);
251		if (err)
252			dev_err(mpcs->mdio.dev.parent,
253				"failed to apply erratum 3.14: %pe\n",
254				ERR_PTR(err));
255	}
256
257	return 0;
258}
259
260static void mv88e639x_sgmii_pcs_get_state(struct phylink_pcs *pcs,
261					  unsigned int neg_mode,
262					  struct phylink_link_state *state)
263{
264	struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs);
265	u16 bmsr, lpa, status;
266	int err;
267
268	err = mv88e639x_read(mpcs, MV88E6390_SGMII_BMSR, &bmsr);
269	if (err) {
270		dev_err(mpcs->mdio.dev.parent,
271			"can't read Serdes PHY %s: %pe\n",
272			"BMSR", ERR_PTR(err));
273		state->link = false;
274		return;
275	}
276
277	err = mv88e639x_read(mpcs, MV88E6390_SGMII_LPA, &lpa);
278	if (err) {
279		dev_err(mpcs->mdio.dev.parent,
280			"can't read Serdes PHY %s: %pe\n",
281			"LPA", ERR_PTR(err));
282		state->link = false;
283		return;
284	}
285
286	err = mv88e639x_read(mpcs, MV88E6390_SGMII_PHY_STATUS, &status);
287	if (err) {
288		dev_err(mpcs->mdio.dev.parent,
289			"can't read Serdes PHY %s: %pe\n",
290			"status", ERR_PTR(err));
291		state->link = false;
292		return;
293	}
294
295	mv88e6xxx_pcs_decode_state(mpcs->mdio.dev.parent, bmsr, lpa, status,
296				   state);
297}
298
299static int mv88e639x_sgmii_pcs_config(struct phylink_pcs *pcs,
300				      unsigned int neg_mode,
301				      phy_interface_t interface,
302				      const unsigned long *advertising,
303				      bool permit_pause_to_mac)
304{
305	struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs);
306	u16 val, bmcr;
307	bool changed;
308	int adv, err;
309
310	adv = phylink_mii_c22_pcs_encode_advertisement(interface, advertising);
311	if (adv < 0)
312		return 0;
313
314	mpcs->interface = interface;
315
316	err = mv88e639x_modify_changed(mpcs, MV88E6390_SGMII_ADVERTISE,
317				       0xffff, adv);
318	if (err < 0)
319		return err;
320
321	changed = err > 0;
322
323	err = mv88e639x_read(mpcs, MV88E6390_SGMII_BMCR, &val);
324	if (err)
325		return err;
326
327	if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
328		bmcr = val | BMCR_ANENABLE;
329	else
330		bmcr = val & ~BMCR_ANENABLE;
331
332	/* setting ANENABLE triggers a restart of negotiation */
333	if (bmcr == val)
334		return changed;
335
336	return mv88e639x_write(mpcs, MV88E6390_SGMII_BMCR, bmcr);
337}
338
339static void mv88e639x_sgmii_pcs_an_restart(struct phylink_pcs *pcs)
340{
341	struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs);
342
343	mv88e639x_modify(mpcs, MV88E6390_SGMII_BMCR,
344			 BMCR_ANRESTART, BMCR_ANRESTART);
345}
346
347static void mv88e639x_sgmii_pcs_link_up(struct phylink_pcs *pcs,
348					unsigned int mode,
349					phy_interface_t interface,
350					int speed, int duplex)
351{
352	struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs);
353	u16 bmcr;
354	int err;
355
356	if (phylink_autoneg_inband(mode))
357		return;
358
359	bmcr = mii_bmcr_encode_fixed(speed, duplex);
360
361	err = mv88e639x_modify(mpcs, MV88E6390_SGMII_BMCR,
362			       BMCR_SPEED1000 | BMCR_SPEED100 | BMCR_FULLDPLX,
363			       bmcr);
364	if (err)
365		dev_err(mpcs->mdio.dev.parent,
366			"can't access Serdes PHY %s: %pe\n",
367			"BMCR", ERR_PTR(err));
368}
369
370static const struct phylink_pcs_ops mv88e639x_sgmii_pcs_ops = {
371	.pcs_enable = mv88e639x_sgmii_pcs_enable,
372	.pcs_disable = mv88e639x_sgmii_pcs_disable,
373	.pcs_pre_config = mv88e639x_sgmii_pcs_pre_config,
374	.pcs_post_config = mv88e639x_sgmii_pcs_post_config,
375	.pcs_get_state = mv88e639x_sgmii_pcs_get_state,
376	.pcs_an_restart = mv88e639x_sgmii_pcs_an_restart,
377	.pcs_config = mv88e639x_sgmii_pcs_config,
378	.pcs_link_up = mv88e639x_sgmii_pcs_link_up,
379};
380
381static struct mv88e639x_pcs *xg_pcs_to_mv88e639x_pcs(struct phylink_pcs *pcs)
382{
383	return container_of(pcs, struct mv88e639x_pcs, xg_pcs);
384}
385
386static int mv88e639x_xg_pcs_enable(struct mv88e639x_pcs *mpcs)
387{
388	return mv88e639x_modify(mpcs, MV88E6390_10G_CTRL1,
389				MDIO_CTRL1_RESET | MDIO_PCS_CTRL1_LOOPBACK |
390				MDIO_CTRL1_LPOWER, 0);
391}
392
393static void mv88e639x_xg_pcs_disable(struct mv88e639x_pcs *mpcs)
394{
395	mv88e639x_modify(mpcs, MV88E6390_10G_CTRL1, MDIO_CTRL1_LPOWER,
396			 MDIO_CTRL1_LPOWER);
397}
398
399static void mv88e639x_xg_pcs_get_state(struct phylink_pcs *pcs,
400				       unsigned int neg_mode,
401				       struct phylink_link_state *state)
402{
403	struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs);
404	u16 status;
405	int err;
406
407	state->link = false;
408
409	err = mv88e639x_read(mpcs, MV88E6390_10G_STAT1, &status);
410	if (err) {
411		dev_err(mpcs->mdio.dev.parent,
412			"can't read Serdes PHY %s: %pe\n",
413			"STAT1", ERR_PTR(err));
414		return;
415	}
416
417	state->link = !!(status & MDIO_STAT1_LSTATUS);
418	if (state->link) {
419		switch (state->interface) {
420		case PHY_INTERFACE_MODE_5GBASER:
421			state->speed = SPEED_5000;
422			break;
423
424		case PHY_INTERFACE_MODE_10GBASER:
425		case PHY_INTERFACE_MODE_RXAUI:
426		case PHY_INTERFACE_MODE_XAUI:
427			state->speed = SPEED_10000;
428			break;
429
430		default:
431			state->link = false;
432			return;
433		}
434
435		state->duplex = DUPLEX_FULL;
436	}
437}
438
439static int mv88e639x_xg_pcs_config(struct phylink_pcs *pcs,
440				   unsigned int neg_mode,
441				   phy_interface_t interface,
442				   const unsigned long *advertising,
443				   bool permit_pause_to_mac)
444{
445	return 0;
446}
447
448static struct phylink_pcs *
449mv88e639x_pcs_select(struct mv88e6xxx_chip *chip, int port,
450		     phy_interface_t mode)
451{
452	struct mv88e639x_pcs *mpcs;
453
454	mpcs = chip->ports[port].pcs_private;
455	if (!mpcs)
456		return NULL;
457
458	switch (mode) {
459	case PHY_INTERFACE_MODE_SGMII:
460	case PHY_INTERFACE_MODE_1000BASEX:
461	case PHY_INTERFACE_MODE_2500BASEX:
462		return &mpcs->sgmii_pcs;
463
464	case PHY_INTERFACE_MODE_5GBASER:
465		if (!mpcs->supports_5g)
466			return NULL;
467		fallthrough;
468	case PHY_INTERFACE_MODE_10GBASER:
469	case PHY_INTERFACE_MODE_XAUI:
470	case PHY_INTERFACE_MODE_RXAUI:
471	case PHY_INTERFACE_MODE_USXGMII:
472		return &mpcs->xg_pcs;
473
474	default:
475		return NULL;
476	}
477}
478
479/* Marvell 88E6390 Specific support */
480
481static irqreturn_t mv88e6390_xg_handle_irq(struct mv88e639x_pcs *mpcs)
482{
483	u16 int_status;
484	int err;
485
486	err = mv88e639x_read(mpcs, MV88E6390_10G_INT_STATUS, &int_status);
487	if (err)
488		return IRQ_NONE;
489
490	if (int_status & (MV88E6390_10G_INT_LINK_DOWN |
491			  MV88E6390_10G_INT_LINK_UP)) {
492		phylink_pcs_change(&mpcs->xg_pcs,
493				   int_status & MV88E6390_10G_INT_LINK_UP);
494
495		return IRQ_HANDLED;
496	}
497
498	return IRQ_NONE;
499}
500
501static int mv88e6390_xg_control_irq(struct mv88e639x_pcs *mpcs, bool enable)
502{
503	u16 val = 0;
504
505	if (enable)
506		val = MV88E6390_10G_INT_LINK_DOWN | MV88E6390_10G_INT_LINK_UP;
507
508	return mv88e639x_modify(mpcs, MV88E6390_10G_INT_ENABLE,
509				MV88E6390_10G_INT_LINK_DOWN |
510				MV88E6390_10G_INT_LINK_UP, val);
511}
512
513static int mv88e6390_xg_pcs_enable(struct phylink_pcs *pcs)
514{
515	struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs);
516	int err;
517
518	err = mv88e639x_xg_pcs_enable(mpcs);
519	if (err)
520		return err;
521
522	mpcs->handle_irq = mv88e6390_xg_handle_irq;
523
524	return mv88e6390_xg_control_irq(mpcs, !!mpcs->irq);
525}
526
527static void mv88e6390_xg_pcs_disable(struct phylink_pcs *pcs)
528{
529	struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs);
530
531	mv88e6390_xg_control_irq(mpcs, false);
532	mv88e639x_xg_pcs_disable(mpcs);
533}
534
535static const struct phylink_pcs_ops mv88e6390_xg_pcs_ops = {
536	.pcs_enable = mv88e6390_xg_pcs_enable,
537	.pcs_disable = mv88e6390_xg_pcs_disable,
538	.pcs_get_state = mv88e639x_xg_pcs_get_state,
539	.pcs_config = mv88e639x_xg_pcs_config,
540};
541
542static int mv88e6390_pcs_enable_checker(struct mv88e639x_pcs *mpcs)
543{
544	return mv88e639x_modify(mpcs, MV88E6390_PG_CONTROL,
545				MV88E6390_PG_CONTROL_ENABLE_PC,
546				MV88E6390_PG_CONTROL_ENABLE_PC);
547}
548
549static int mv88e6390_pcs_init(struct mv88e6xxx_chip *chip, int port)
550{
551	struct mv88e639x_pcs *mpcs;
552	struct mii_bus *bus;
553	struct device *dev;
554	int lane, err;
555
556	lane = mv88e6xxx_serdes_get_lane(chip, port);
557	if (lane < 0)
558		return 0;
559
560	bus = mv88e6xxx_default_mdio_bus(chip);
561	dev = chip->dev;
562
563	mpcs = mv88e639x_pcs_alloc(dev, bus, lane, port);
564	if (!mpcs)
565		return -ENOMEM;
566
567	mpcs->sgmii_pcs.ops = &mv88e639x_sgmii_pcs_ops;
568	mpcs->xg_pcs.ops = &mv88e6390_xg_pcs_ops;
569
570	if (chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6190X ||
571	    chip->info->prod_num == MV88E6XXX_PORT_SWITCH_ID_PROD_6390X)
572		mpcs->erratum_3_14 = true;
573
574	err = mv88e639x_pcs_setup_irq(mpcs, chip, port);
575	if (err)
576		goto err_free;
577
578	/* 6390 and 6390x has the checker, 6393x doesn't appear to? */
579	/* This is to enable gathering the statistics. Maybe this
580	 * should call out to a helper? Or we could do this at init time.
581	 */
582	err = mv88e6390_pcs_enable_checker(mpcs);
583	if (err)
584		goto err_free;
585
586	chip->ports[port].pcs_private = mpcs;
587
588	return 0;
589
590err_free:
591	kfree(mpcs);
592	return err;
593}
594
595const struct mv88e6xxx_pcs_ops mv88e6390_pcs_ops = {
596	.pcs_init = mv88e6390_pcs_init,
597	.pcs_teardown = mv88e639x_pcs_teardown,
598	.pcs_select = mv88e639x_pcs_select,
599};
600
601/* Marvell 88E6393X Specific support */
602
603static int mv88e6393x_power_lane(struct mv88e639x_pcs *mpcs, bool enable)
604{
605	u16 val = MV88E6393X_SERDES_CTRL1_TX_PDOWN |
606		  MV88E6393X_SERDES_CTRL1_RX_PDOWN;
607
608	return mv88e639x_modify(mpcs, MV88E6393X_SERDES_CTRL1, val,
609				enable ? 0 : val);
610}
611
612/* mv88e6393x family errata 4.6:
613 * Cannot clear PwrDn bit on SERDES if device is configured CPU_MGD mode or
614 * P0_mode is configured for [x]MII.
615 * Workaround: Set SERDES register 4.F002 bit 5=0 and bit 15=1.
616 *
617 * It seems that after this workaround the SERDES is automatically powered up
618 * (the bit is cleared), so power it down.
619 */
620static int mv88e6393x_erratum_4_6(struct mv88e639x_pcs *mpcs)
621{
622	int err;
623
624	err = mv88e639x_modify(mpcs, MV88E6393X_SERDES_POC,
625			       MV88E6393X_SERDES_POC_PDOWN |
626			       MV88E6393X_SERDES_POC_RESET,
627			       MV88E6393X_SERDES_POC_RESET);
628	if (err)
629		return err;
630
631	err = mv88e639x_modify(mpcs, MV88E6390_SGMII_BMCR,
632			       BMCR_PDOWN, BMCR_PDOWN);
633	if (err)
634		return err;
635
636	err = mv88e639x_sgmii_pcs_control_pwr(mpcs, false);
637	if (err)
638		return err;
639
640	return mv88e6393x_power_lane(mpcs, false);
641}
642
643/* mv88e6393x family errata 4.8:
644 * When a SERDES port is operating in 1000BASE-X or SGMII mode link may not
645 * come up after hardware reset or software reset of SERDES core. Workaround
646 * is to write SERDES register 4.F074.14=1 for only those modes and 0 in all
647 * other modes.
648 */
649static int mv88e6393x_erratum_4_8(struct mv88e639x_pcs *mpcs)
650{
651	u16 reg, poc;
652	int err;
653
654	err = mv88e639x_read(mpcs, MV88E6393X_SERDES_POC, &poc);
655	if (err)
656		return err;
657
658	poc &= MV88E6393X_SERDES_POC_PCS_MASK;
659	if (poc == MV88E6393X_SERDES_POC_PCS_1000BASEX ||
660	    poc == MV88E6393X_SERDES_POC_PCS_SGMII_PHY ||
661	    poc == MV88E6393X_SERDES_POC_PCS_SGMII_MAC)
662		reg = MV88E6393X_ERRATA_4_8_BIT;
663	else
664		reg = 0;
665
666	return mv88e639x_modify(mpcs, MV88E6393X_ERRATA_4_8_REG,
667				MV88E6393X_ERRATA_4_8_BIT, reg);
668}
669
670/* mv88e6393x family errata 5.2:
671 * For optimal signal integrity the following sequence should be applied to
672 * SERDES operating in 10G mode. These registers only apply to 10G operation
673 * and have no effect on other speeds.
674 */
675static int mv88e6393x_erratum_5_2(struct mv88e639x_pcs *mpcs)
676{
677	static const struct {
678		u16 dev, reg, val, mask;
679	} fixes[] = {
680		{ MDIO_MMD_VEND1, 0x8093, 0xcb5a, 0xffff },
681		{ MDIO_MMD_VEND1, 0x8171, 0x7088, 0xffff },
682		{ MDIO_MMD_VEND1, 0x80c9, 0x311a, 0xffff },
683		{ MDIO_MMD_VEND1, 0x80a2, 0x8000, 0xff7f },
684		{ MDIO_MMD_VEND1, 0x80a9, 0x0000, 0xfff0 },
685		{ MDIO_MMD_VEND1, 0x80a3, 0x0000, 0xf8ff },
686		{ MDIO_MMD_PHYXS, MV88E6393X_SERDES_POC,
687		  MV88E6393X_SERDES_POC_RESET, MV88E6393X_SERDES_POC_RESET },
688	};
689	int err, i;
690
691	for (i = 0; i < ARRAY_SIZE(fixes); ++i) {
692		err = mdiodev_c45_modify(&mpcs->mdio, fixes[i].dev,
693					 fixes[i].reg, fixes[i].mask,
694					 fixes[i].val);
695		if (err)
696			return err;
697	}
698
699	return 0;
700}
701
702/* Inband AN is broken on Amethyst in 2500base-x mode when set by standard
703 * mechanism (via cmode).
704 * We can get around this by configuring the PCS mode to 1000base-x and then
705 * writing value 0x58 to register 1e.8000. (This must be done while SerDes
706 * receiver and transmitter are disabled, which is, when this function is
707 * called.)
708 * It seem that when we do this configuration to 2500base-x mode (by changing
709 * PCS mode to 1000base-x and frequency to 3.125 GHz from 1.25 GHz) and then
710 * configure to sgmii or 1000base-x, the device thinks that it already has
711 * SerDes at 1.25 GHz and does not change the 1e.8000 register, leaving SerDes
712 * at 3.125 GHz.
713 * To avoid this, change PCS mode back to 2500base-x when disabling SerDes from
714 * 2500base-x mode.
715 */
716static int mv88e6393x_fix_2500basex_an(struct mv88e639x_pcs *mpcs, bool on)
717{
718	u16 reg;
719	int err;
720
721	if (on)
722		reg = MV88E6393X_SERDES_POC_PCS_1000BASEX |
723		      MV88E6393X_SERDES_POC_AN;
724	else
725		reg = MV88E6393X_SERDES_POC_PCS_2500BASEX;
726
727	reg |= MV88E6393X_SERDES_POC_RESET;
728
729	err = mv88e639x_modify(mpcs, MV88E6393X_SERDES_POC,
730			       MV88E6393X_SERDES_POC_PCS_MASK |
731			       MV88E6393X_SERDES_POC_AN |
732			       MV88E6393X_SERDES_POC_RESET, reg);
733	if (err)
734		return err;
735
736	return mdiodev_c45_write(&mpcs->mdio, MDIO_MMD_VEND1, 0x8000, 0x58);
737}
738
739static int mv88e6393x_sgmii_apply_2500basex_an(struct mv88e639x_pcs *mpcs,
740					       phy_interface_t interface,
741					       bool enable)
742{
743	int err;
744
745	if (interface != PHY_INTERFACE_MODE_2500BASEX)
746		return 0;
747
748	err = mv88e6393x_fix_2500basex_an(mpcs, enable);
749	if (err)
750		dev_err(mpcs->mdio.dev.parent,
751			"failed to %s 2500basex fix: %pe\n",
752			str_enable_disable(enable), ERR_PTR(err));
753
754	return err;
755}
756
757static void mv88e6393x_sgmii_pcs_disable(struct phylink_pcs *pcs)
758{
759	struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs);
760
761	mv88e639x_sgmii_pcs_disable(pcs);
762	mv88e6393x_power_lane(mpcs, false);
763	mv88e6393x_sgmii_apply_2500basex_an(mpcs, mpcs->interface, false);
764}
765
766static void mv88e6393x_sgmii_pcs_pre_config(struct phylink_pcs *pcs,
767					    phy_interface_t interface)
768{
769	struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs);
770
771	mv88e639x_sgmii_pcs_pre_config(pcs, interface);
772	mv88e6393x_power_lane(mpcs, false);
773	mv88e6393x_sgmii_apply_2500basex_an(mpcs, mpcs->interface, false);
774}
775
776static int mv88e6393x_sgmii_pcs_post_config(struct phylink_pcs *pcs,
777					    phy_interface_t interface)
778{
779	struct mv88e639x_pcs *mpcs = sgmii_pcs_to_mv88e639x_pcs(pcs);
780	int err;
781
782	err = mv88e6393x_erratum_4_8(mpcs);
783	if (err)
784		return err;
785
786	err = mv88e6393x_sgmii_apply_2500basex_an(mpcs, interface, true);
787	if (err)
788		return err;
789
790	err = mv88e6393x_power_lane(mpcs, true);
791	if (err)
792		return err;
793
794	return mv88e639x_sgmii_pcs_post_config(pcs, interface);
795}
796
797static const struct phylink_pcs_ops mv88e6393x_sgmii_pcs_ops = {
798	.pcs_enable = mv88e639x_sgmii_pcs_enable,
799	.pcs_disable = mv88e6393x_sgmii_pcs_disable,
800	.pcs_pre_config = mv88e6393x_sgmii_pcs_pre_config,
801	.pcs_post_config = mv88e6393x_sgmii_pcs_post_config,
802	.pcs_get_state = mv88e639x_sgmii_pcs_get_state,
803	.pcs_an_restart = mv88e639x_sgmii_pcs_an_restart,
804	.pcs_config = mv88e639x_sgmii_pcs_config,
805	.pcs_link_up = mv88e639x_sgmii_pcs_link_up,
806};
807
808static irqreturn_t mv88e6393x_xg_handle_irq(struct mv88e639x_pcs *mpcs)
809{
810	u16 int_status, stat1;
811	bool link_down;
812	int err;
813
814	err = mv88e639x_read(mpcs, MV88E6393X_10G_INT_STATUS, &int_status);
815	if (err)
816		return IRQ_NONE;
817
818	if (int_status & MV88E6393X_10G_INT_LINK_CHANGE) {
819		err = mv88e639x_read(mpcs, MV88E6390_10G_STAT1, &stat1);
820		if (err)
821			return IRQ_NONE;
822
823		link_down = !(stat1 & MDIO_STAT1_LSTATUS);
824
825		phylink_pcs_change(&mpcs->xg_pcs, !link_down);
826
827		return IRQ_HANDLED;
828	}
829
830	return IRQ_NONE;
831}
832
833static int mv88e6393x_xg_control_irq(struct mv88e639x_pcs *mpcs, bool enable)
834{
835	u16 val = 0;
836
837	if (enable)
838		val = MV88E6393X_10G_INT_LINK_CHANGE;
839
840	return mv88e639x_modify(mpcs, MV88E6393X_10G_INT_ENABLE,
841				MV88E6393X_10G_INT_LINK_CHANGE, val);
842}
843
844static int mv88e6393x_xg_pcs_enable(struct phylink_pcs *pcs)
845{
846	struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs);
847
848	mpcs->handle_irq = mv88e6393x_xg_handle_irq;
849
850	return mv88e6393x_xg_control_irq(mpcs, !!mpcs->irq);
851}
852
853static void mv88e6393x_xg_pcs_disable(struct phylink_pcs *pcs)
854{
855	struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs);
856
857	mv88e6393x_xg_control_irq(mpcs, false);
858	mv88e639x_xg_pcs_disable(mpcs);
859	mv88e6393x_power_lane(mpcs, false);
860}
861
862/* The PCS has to be powered down while CMODE is changed */
863static void mv88e6393x_xg_pcs_pre_config(struct phylink_pcs *pcs,
864					 phy_interface_t interface)
865{
866	struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs);
867
868	mv88e639x_xg_pcs_disable(mpcs);
869	mv88e6393x_power_lane(mpcs, false);
870}
871
872static int mv88e6393x_xg_pcs_post_config(struct phylink_pcs *pcs,
873					 phy_interface_t interface)
874{
875	struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs);
876	int err;
877
878	if (interface == PHY_INTERFACE_MODE_10GBASER ||
879	    interface == PHY_INTERFACE_MODE_USXGMII) {
880		err = mv88e6393x_erratum_5_2(mpcs);
881		if (err)
882			return err;
883	}
884
885	err = mv88e6393x_power_lane(mpcs, true);
886	if (err)
887		return err;
888
889	return mv88e639x_xg_pcs_enable(mpcs);
890}
891
892static void mv88e6393x_xg_pcs_get_state(struct phylink_pcs *pcs,
893					unsigned int neg_mode,
894					struct phylink_link_state *state)
895{
896	struct mv88e639x_pcs *mpcs = xg_pcs_to_mv88e639x_pcs(pcs);
897	u16 status, lp_status;
898	int err;
899
900	if (state->interface != PHY_INTERFACE_MODE_USXGMII)
901		return mv88e639x_xg_pcs_get_state(pcs, neg_mode, state);
902
903	state->link = false;
904
905	err = mv88e639x_read(mpcs, MV88E6390_USXGMII_PHY_STATUS, &status);
906	err = err ? : mv88e639x_read(mpcs, MV88E6390_USXGMII_LP_STATUS, &lp_status);
907	if (err) {
908		dev_err(mpcs->mdio.dev.parent,
909			"can't read USXGMII status: %pe\n", ERR_PTR(err));
910		return;
911	}
912
913	state->link = !!(status & MDIO_USXGMII_LINK);
914	state->an_complete = state->link;
915	phylink_decode_usxgmii_word(state, lp_status);
916}
917
918static const struct phylink_pcs_ops mv88e6393x_xg_pcs_ops = {
919	.pcs_enable = mv88e6393x_xg_pcs_enable,
920	.pcs_disable = mv88e6393x_xg_pcs_disable,
921	.pcs_pre_config = mv88e6393x_xg_pcs_pre_config,
922	.pcs_post_config = mv88e6393x_xg_pcs_post_config,
923	.pcs_get_state = mv88e6393x_xg_pcs_get_state,
924	.pcs_config = mv88e639x_xg_pcs_config,
925};
926
927static int mv88e6393x_pcs_init(struct mv88e6xxx_chip *chip, int port)
928{
929	struct mv88e639x_pcs *mpcs;
930	struct mii_bus *bus;
931	struct device *dev;
932	int lane, err;
933
934	lane = mv88e6xxx_serdes_get_lane(chip, port);
935	if (lane < 0)
936		return 0;
937
938	bus = mv88e6xxx_default_mdio_bus(chip);
939	dev = chip->dev;
940
941	mpcs = mv88e639x_pcs_alloc(dev, bus, lane, port);
942	if (!mpcs)
943		return -ENOMEM;
944
945	mpcs->sgmii_pcs.ops = &mv88e6393x_sgmii_pcs_ops;
946	mpcs->xg_pcs.ops = &mv88e6393x_xg_pcs_ops;
947	mpcs->supports_5g = true;
948
949	err = mv88e6393x_erratum_4_6(mpcs);
950	if (err)
951		goto err_free;
952
953	err = mv88e639x_pcs_setup_irq(mpcs, chip, port);
954	if (err)
955		goto err_free;
956
957	chip->ports[port].pcs_private = mpcs;
958
959	return 0;
960
961err_free:
962	kfree(mpcs);
963	return err;
964}
965
966const struct mv88e6xxx_pcs_ops mv88e6393x_pcs_ops = {
967	.pcs_init = mv88e6393x_pcs_init,
968	.pcs_teardown = mv88e639x_pcs_teardown,
969	.pcs_select = mv88e639x_pcs_select,
970};
~/ Read-only file preview.