brintos

brintos / linux-shallow public Read only

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