Commit 4aabe35c authored by Russell King (Oracle)'s avatar Russell King (Oracle) Committed by David S. Miller
Browse files

net: dsa: mv88e6xxx: convert 88e6185 to phylink_pcs



Convert the 88E6185 SERDES code to use the phylink_pcs infrastructure.

Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarRussell King (Oracle) <rmk+kernel@armlinux.org.uk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 05407b0e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ mv88e6xxx-objs += global2.o
mv88e6xxx-objs += global2_avb.o
mv88e6xxx-objs += global2_scratch.o
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += hwtstamp.o
mv88e6xxx-objs += pcs-6185.o
mv88e6xxx-objs += phy.o
mv88e6xxx-objs += port.o
mv88e6xxx-objs += port_hidden.o
+3 −11
Original line number Diff line number Diff line
@@ -4230,15 +4230,13 @@ static const struct mv88e6xxx_ops mv88e6095_ops = {
	.stats_get_strings = mv88e6095_stats_get_strings,
	.stats_get_stats = mv88e6095_stats_get_stats,
	.mgmt_rsvd2cpu = mv88e6185_g2_mgmt_rsvd2cpu,
	.serdes_power = mv88e6185_serdes_power,
	.serdes_get_lane = mv88e6185_serdes_get_lane,
	.serdes_pcs_get_state = mv88e6185_serdes_pcs_get_state,
	.ppu_enable = mv88e6185_g1_ppu_enable,
	.ppu_disable = mv88e6185_g1_ppu_disable,
	.reset = mv88e6185_g1_reset,
	.vtu_getnext = mv88e6185_g1_vtu_getnext,
	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
	.phylink_get_caps = mv88e6095_phylink_get_caps,
	.pcs_ops = &mv88e6185_pcs_ops,
	.set_max_frame_size = mv88e6185_g1_set_max_frame_size,
};

@@ -4276,18 +4274,14 @@ static const struct mv88e6xxx_ops mv88e6097_ops = {
	.set_egress_port = mv88e6095_g1_set_egress_port,
	.watchdog_ops = &mv88e6097_watchdog_ops,
	.mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu,
	.serdes_power = mv88e6185_serdes_power,
	.serdes_get_lane = mv88e6185_serdes_get_lane,
	.serdes_pcs_get_state = mv88e6185_serdes_pcs_get_state,
	.serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
	.serdes_irq_enable = mv88e6097_serdes_irq_enable,
	.serdes_irq_status = mv88e6097_serdes_irq_status,
	.pot_clear = mv88e6xxx_g2_pot_clear,
	.reset = mv88e6352_g1_reset,
	.rmu_disable = mv88e6085_g1_rmu_disable,
	.vtu_getnext = mv88e6352_g1_vtu_getnext,
	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
	.phylink_get_caps = mv88e6095_phylink_get_caps,
	.pcs_ops = &mv88e6185_pcs_ops,
	.stu_getnext = mv88e6352_g1_stu_getnext,
	.stu_loadpurge = mv88e6352_g1_stu_loadpurge,
	.set_max_frame_size = mv88e6185_g1_set_max_frame_size,
@@ -4768,9 +4762,6 @@ static const struct mv88e6xxx_ops mv88e6185_ops = {
	.set_egress_port = mv88e6095_g1_set_egress_port,
	.watchdog_ops = &mv88e6097_watchdog_ops,
	.mgmt_rsvd2cpu = mv88e6185_g2_mgmt_rsvd2cpu,
	.serdes_power = mv88e6185_serdes_power,
	.serdes_get_lane = mv88e6185_serdes_get_lane,
	.serdes_pcs_get_state = mv88e6185_serdes_pcs_get_state,
	.set_cascade_port = mv88e6185_g1_set_cascade_port,
	.ppu_enable = mv88e6185_g1_ppu_enable,
	.ppu_disable = mv88e6185_g1_ppu_disable,
@@ -4778,6 +4769,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = {
	.vtu_getnext = mv88e6185_g1_vtu_getnext,
	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
	.phylink_get_caps = mv88e6185_phylink_get_caps,
	.pcs_ops = &mv88e6185_pcs_ops,
	.set_max_frame_size = mv88e6185_g1_set_max_frame_size,
};

+190 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Marvell 88E6185 family SERDES PCS support
 *
 * Copyright (c) 2008 Marvell Semiconductor
 *
 * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>
 */
#include <linux/phylink.h>

#include "global2.h"
#include "port.h"
#include "serdes.h"

struct mv88e6185_pcs {
	struct phylink_pcs phylink_pcs;
	unsigned int irq;
	char name[64];

	struct mv88e6xxx_chip *chip;
	int port;
};

static struct mv88e6185_pcs *pcs_to_mv88e6185_pcs(struct phylink_pcs *pcs)
{
	return container_of(pcs, struct mv88e6185_pcs, phylink_pcs);
}

static irqreturn_t mv88e6185_pcs_handle_irq(int irq, void *dev_id)
{
	struct mv88e6185_pcs *mpcs = dev_id;
	struct mv88e6xxx_chip *chip;
	irqreturn_t ret = IRQ_NONE;
	bool link_up;
	u16 status;
	int port;
	int err;

	chip = mpcs->chip;
	port = mpcs->port;

	mv88e6xxx_reg_lock(chip);
	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status);
	mv88e6xxx_reg_unlock(chip);

	if (!err) {
		link_up = !!(status & MV88E6XXX_PORT_STS_LINK);

		phylink_pcs_change(&mpcs->phylink_pcs, link_up);

		ret = IRQ_HANDLED;
	}

	return ret;
}

static void mv88e6185_pcs_get_state(struct phylink_pcs *pcs,
				    struct phylink_link_state *state)
{
	struct mv88e6185_pcs *mpcs = pcs_to_mv88e6185_pcs(pcs);
	struct mv88e6xxx_chip *chip = mpcs->chip;
	int port = mpcs->port;
	u16 status;
	int err;

	mv88e6xxx_reg_lock(chip);
	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status);
	mv88e6xxx_reg_unlock(chip);

	if (err)
		status = 0;

	state->link = !!(status & MV88E6XXX_PORT_STS_LINK);
	if (state->link) {
		state->duplex = status & MV88E6XXX_PORT_STS_DUPLEX ?
			DUPLEX_FULL : DUPLEX_HALF;

		switch (status & MV88E6XXX_PORT_STS_SPEED_MASK) {
		case MV88E6XXX_PORT_STS_SPEED_1000:
			state->speed = SPEED_1000;
			break;

		case MV88E6XXX_PORT_STS_SPEED_100:
			state->speed = SPEED_100;
			break;

		case MV88E6XXX_PORT_STS_SPEED_10:
			state->speed = SPEED_10;
			break;

		default:
			state->link = false;
			break;
		}
	}
}

static int mv88e6185_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
				phy_interface_t interface,
				const unsigned long *advertising,
				bool permit_pause_to_mac)
{
	return 0;
}

static void mv88e6185_pcs_an_restart(struct phylink_pcs *pcs)
{
}

static const struct phylink_pcs_ops mv88e6185_phylink_pcs_ops = {
	.pcs_get_state = mv88e6185_pcs_get_state,
	.pcs_config = mv88e6185_pcs_config,
	.pcs_an_restart = mv88e6185_pcs_an_restart,
};

static int mv88e6185_pcs_init(struct mv88e6xxx_chip *chip, int port)
{
	struct mv88e6185_pcs *mpcs;
	struct device *dev;
	unsigned int irq;
	int err;

	/* There are no configurable serdes lanes on this switch chip, so
	 * we use the static cmode configuration to determine whether we
	 * have a PCS or not.
	 */
	if (chip->ports[port].cmode != MV88E6185_PORT_STS_CMODE_SERDES &&
	    chip->ports[port].cmode != MV88E6185_PORT_STS_CMODE_1000BASE_X)
		return 0;

	dev = chip->dev;

	mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
	if (!mpcs)
		return -ENOMEM;

	mpcs->chip = chip;
	mpcs->port = port;
	mpcs->phylink_pcs.ops = &mv88e6185_phylink_pcs_ops;

	irq = mv88e6xxx_serdes_irq_mapping(chip, port);
	if (irq) {
		snprintf(mpcs->name, sizeof(mpcs->name),
			 "mv88e6xxx-%s-serdes-%d", dev_name(dev), port);

		err = request_threaded_irq(irq, NULL, mv88e6185_pcs_handle_irq,
					   IRQF_ONESHOT, mpcs->name, mpcs);
		if (err) {
			kfree(mpcs);
			return err;
		}

		mpcs->irq = irq;
	} else {
		mpcs->phylink_pcs.poll = true;
	}

	chip->ports[port].pcs_private = &mpcs->phylink_pcs;

	return 0;
}

static void mv88e6185_pcs_teardown(struct mv88e6xxx_chip *chip, int port)
{
	struct mv88e6185_pcs *mpcs;

	mpcs = chip->ports[port].pcs_private;
	if (!mpcs)
		return;

	if (mpcs->irq)
		free_irq(mpcs->irq, mpcs);

	kfree(mpcs);

	chip->ports[port].pcs_private = NULL;
}

static struct phylink_pcs *mv88e6185_pcs_select(struct mv88e6xxx_chip *chip,
						int port,
						phy_interface_t interface)
{
	return chip->ports[port].pcs_private;
}

const struct mv88e6xxx_pcs_ops mv88e6185_pcs_ops = {
	.pcs_init = mv88e6185_pcs_init,
	.pcs_teardown = mv88e6185_pcs_teardown,
	.pcs_select = mv88e6185_pcs_select,
};
+0 −109
Original line number Diff line number Diff line
@@ -460,115 +460,6 @@ int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
	return lane;
}

int mv88e6185_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
			   bool up)
{
	/* The serdes power can't be controlled on this switch chip but we need
	 * to supply this function to avoid returning -EOPNOTSUPP in
	 * mv88e6xxx_serdes_power_up/mv88e6xxx_serdes_power_down
	 */
	return 0;
}

int mv88e6185_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
{
	/* There are no configurable serdes lanes on this switch chip but we
	 * need to return a non-negative lane number so that callers of
	 * mv88e6xxx_serdes_get_lane() know this is a serdes port.
	 */
	switch (chip->ports[port].cmode) {
	case MV88E6185_PORT_STS_CMODE_SERDES:
	case MV88E6185_PORT_STS_CMODE_1000BASE_X:
		return 0;
	default:
		return -ENODEV;
	}
}

int mv88e6185_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
				   int lane, struct phylink_link_state *state)
{
	int err;
	u16 status;

	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status);
	if (err)
		return err;

	state->link = !!(status & MV88E6XXX_PORT_STS_LINK);

	if (state->link) {
		state->duplex = status & MV88E6XXX_PORT_STS_DUPLEX ? DUPLEX_FULL : DUPLEX_HALF;

		switch (status &  MV88E6XXX_PORT_STS_SPEED_MASK) {
		case MV88E6XXX_PORT_STS_SPEED_1000:
			state->speed = SPEED_1000;
			break;
		case MV88E6XXX_PORT_STS_SPEED_100:
			state->speed = SPEED_100;
			break;
		case MV88E6XXX_PORT_STS_SPEED_10:
			state->speed = SPEED_10;
			break;
		default:
			dev_err(chip->dev, "invalid PHY speed\n");
			return -EINVAL;
		}
	} else {
		state->duplex = DUPLEX_UNKNOWN;
		state->speed = SPEED_UNKNOWN;
	}

	return 0;
}

int mv88e6097_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
				bool enable)
{
	u8 cmode = chip->ports[port].cmode;

	/* The serdes interrupts are enabled in the G2_INT_MASK register. We
	 * need to return 0 to avoid returning -EOPNOTSUPP in
	 * mv88e6xxx_serdes_irq_enable/mv88e6xxx_serdes_irq_disable
	 */
	switch (cmode) {
	case MV88E6185_PORT_STS_CMODE_SERDES:
	case MV88E6185_PORT_STS_CMODE_1000BASE_X:
		return 0;
	}

	return -EOPNOTSUPP;
}

static void mv88e6097_serdes_irq_link(struct mv88e6xxx_chip *chip, int port)
{
	u16 status;
	int err;

	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status);
	if (err) {
		dev_err(chip->dev, "can't read port status: %d\n", err);
		return;
	}

	dsa_port_phylink_mac_change(chip->ds, port, !!(status & MV88E6XXX_PORT_STS_LINK));
}

irqreturn_t mv88e6097_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
					int lane)
{
	u8 cmode = chip->ports[port].cmode;

	switch (cmode) {
	case MV88E6185_PORT_STS_CMODE_SERDES:
	case MV88E6185_PORT_STS_CMODE_1000BASE_X:
		mv88e6097_serdes_irq_link(chip, port);
		return IRQ_HANDLED;
	}

	return IRQ_NONE;
}

int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
{
	u8 cmode = chip->ports[port].cmode;
+2 −9
Original line number Diff line number Diff line
@@ -112,7 +112,6 @@ struct phylink_link_state;
int mv88e6xxx_pcs_decode_state(struct device *dev, u16 bmsr, u16 lpa,
			       u16 status, struct phylink_link_state *state);

int mv88e6185_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
@@ -126,8 +125,6 @@ int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
				int lane, unsigned int mode,
				phy_interface_t interface,
				const unsigned long *advertise);
int mv88e6185_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
				   int lane, struct phylink_link_state *state);
int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
				   int lane, struct phylink_link_state *state);
int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
@@ -146,8 +143,6 @@ unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip,
					  int port);
unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip,
					  int port);
int mv88e6185_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
			   bool up);
int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
			   bool on);
int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
@@ -155,16 +150,12 @@ int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
			    bool on);
int mv88e6393x_serdes_setup_errata(struct mv88e6xxx_chip *chip);
int mv88e6097_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
				bool enable);
int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
				bool enable);
int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
				bool enable);
int mv88e6393x_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port,
				 int lane, bool enable);
irqreturn_t mv88e6097_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
					int lane);
irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
					int lane);
irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
@@ -254,4 +245,6 @@ mv88e6xxx_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, int lane)
	return chip->info->ops->serdes_irq_status(chip, port, lane);
}

extern const struct mv88e6xxx_pcs_ops mv88e6185_pcs_ops;

#endif