net: dsa: mv88e6xxx: Add SERDES phydev_mac_change up for 6390
phylink wants to know when the MAC layers notices a change in the link. For the 6390 family, this is a change in the SERDES state. Add interrupt support for the SERDES interface used to implement SGMII/1000Base-X/2500Base-X. This is currently limited to ports 9 and 10. Support for the 10G SERDES and other ports will be added later, building on this basic framework. Signed-off-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
7b898469b9
Коммит
efd1ba6af9
|
@ -2337,7 +2337,12 @@ static int mv88e6xxx_port_enable(struct dsa_switch *ds, int port,
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
mutex_lock(&chip->reg_lock);
|
mutex_lock(&chip->reg_lock);
|
||||||
|
|
||||||
err = mv88e6xxx_serdes_power(chip, port, true);
|
err = mv88e6xxx_serdes_power(chip, port, true);
|
||||||
|
|
||||||
|
if (!err && chip->info->ops->serdes_irq_setup)
|
||||||
|
err = chip->info->ops->serdes_irq_setup(chip, port);
|
||||||
|
|
||||||
mutex_unlock(&chip->reg_lock);
|
mutex_unlock(&chip->reg_lock);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -2349,8 +2354,13 @@ static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port,
|
||||||
struct mv88e6xxx_chip *chip = ds->priv;
|
struct mv88e6xxx_chip *chip = ds->priv;
|
||||||
|
|
||||||
mutex_lock(&chip->reg_lock);
|
mutex_lock(&chip->reg_lock);
|
||||||
|
|
||||||
|
if (chip->info->ops->serdes_irq_free)
|
||||||
|
chip->info->ops->serdes_irq_free(chip, port);
|
||||||
|
|
||||||
if (mv88e6xxx_serdes_power(chip, port, false))
|
if (mv88e6xxx_serdes_power(chip, port, false))
|
||||||
dev_err(chip->dev, "failed to power off SERDES\n");
|
dev_err(chip->dev, "failed to power off SERDES\n");
|
||||||
|
|
||||||
mutex_unlock(&chip->reg_lock);
|
mutex_unlock(&chip->reg_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3225,6 +3235,8 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
|
||||||
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
||||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||||
.serdes_power = mv88e6390_serdes_power,
|
.serdes_power = mv88e6390_serdes_power,
|
||||||
|
.serdes_irq_setup = mv88e6390_serdes_irq_setup,
|
||||||
|
.serdes_irq_free = mv88e6390_serdes_irq_free,
|
||||||
.gpio_ops = &mv88e6352_gpio_ops,
|
.gpio_ops = &mv88e6352_gpio_ops,
|
||||||
.phylink_validate = mv88e6390_phylink_validate,
|
.phylink_validate = mv88e6390_phylink_validate,
|
||||||
};
|
};
|
||||||
|
@ -3265,6 +3277,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
|
||||||
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
||||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||||
.serdes_power = mv88e6390x_serdes_power,
|
.serdes_power = mv88e6390x_serdes_power,
|
||||||
|
.serdes_irq_setup = mv88e6390_serdes_irq_setup,
|
||||||
|
.serdes_irq_free = mv88e6390_serdes_irq_free,
|
||||||
.gpio_ops = &mv88e6352_gpio_ops,
|
.gpio_ops = &mv88e6352_gpio_ops,
|
||||||
.phylink_validate = mv88e6390x_phylink_validate,
|
.phylink_validate = mv88e6390x_phylink_validate,
|
||||||
};
|
};
|
||||||
|
@ -3305,6 +3319,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
|
||||||
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
||||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||||
.serdes_power = mv88e6390_serdes_power,
|
.serdes_power = mv88e6390_serdes_power,
|
||||||
|
.serdes_irq_setup = mv88e6390_serdes_irq_setup,
|
||||||
|
.serdes_irq_free = mv88e6390_serdes_irq_free,
|
||||||
.avb_ops = &mv88e6390_avb_ops,
|
.avb_ops = &mv88e6390_avb_ops,
|
||||||
.ptp_ops = &mv88e6352_ptp_ops,
|
.ptp_ops = &mv88e6352_ptp_ops,
|
||||||
.phylink_validate = mv88e6390_phylink_validate,
|
.phylink_validate = mv88e6390_phylink_validate,
|
||||||
|
@ -3393,6 +3409,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
|
||||||
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
||||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||||
.serdes_power = mv88e6390_serdes_power,
|
.serdes_power = mv88e6390_serdes_power,
|
||||||
|
.serdes_irq_setup = mv88e6390_serdes_irq_setup,
|
||||||
|
.serdes_irq_free = mv88e6390_serdes_irq_free,
|
||||||
.gpio_ops = &mv88e6352_gpio_ops,
|
.gpio_ops = &mv88e6352_gpio_ops,
|
||||||
.avb_ops = &mv88e6390_avb_ops,
|
.avb_ops = &mv88e6390_avb_ops,
|
||||||
.ptp_ops = &mv88e6352_ptp_ops,
|
.ptp_ops = &mv88e6352_ptp_ops,
|
||||||
|
@ -3694,6 +3712,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
|
||||||
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
||||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||||
.serdes_power = mv88e6390_serdes_power,
|
.serdes_power = mv88e6390_serdes_power,
|
||||||
|
.serdes_irq_setup = mv88e6390_serdes_irq_setup,
|
||||||
|
.serdes_irq_free = mv88e6390_serdes_irq_free,
|
||||||
.gpio_ops = &mv88e6352_gpio_ops,
|
.gpio_ops = &mv88e6352_gpio_ops,
|
||||||
.avb_ops = &mv88e6390_avb_ops,
|
.avb_ops = &mv88e6390_avb_ops,
|
||||||
.ptp_ops = &mv88e6352_ptp_ops,
|
.ptp_ops = &mv88e6352_ptp_ops,
|
||||||
|
@ -3739,6 +3759,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
|
||||||
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
.vtu_getnext = mv88e6390_g1_vtu_getnext,
|
||||||
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
|
||||||
.serdes_power = mv88e6390x_serdes_power,
|
.serdes_power = mv88e6390x_serdes_power,
|
||||||
|
.serdes_irq_setup = mv88e6390_serdes_irq_setup,
|
||||||
|
.serdes_irq_free = mv88e6390_serdes_irq_free,
|
||||||
.gpio_ops = &mv88e6352_gpio_ops,
|
.gpio_ops = &mv88e6352_gpio_ops,
|
||||||
.avb_ops = &mv88e6390_avb_ops,
|
.avb_ops = &mv88e6390_avb_ops,
|
||||||
.ptp_ops = &mv88e6352_ptp_ops,
|
.ptp_ops = &mv88e6352_ptp_ops,
|
||||||
|
|
|
@ -200,6 +200,7 @@ struct mv88e6xxx_port {
|
||||||
u64 vtu_member_violation;
|
u64 vtu_member_violation;
|
||||||
u64 vtu_miss_violation;
|
u64 vtu_miss_violation;
|
||||||
u8 cmode;
|
u8 cmode;
|
||||||
|
int serdes_irq;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mv88e6xxx_chip {
|
struct mv88e6xxx_chip {
|
||||||
|
@ -434,6 +435,10 @@ struct mv88e6xxx_ops {
|
||||||
/* Power on/off a SERDES interface */
|
/* Power on/off a SERDES interface */
|
||||||
int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, bool on);
|
int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, bool on);
|
||||||
|
|
||||||
|
/* SERDES interrupt handling */
|
||||||
|
int (*serdes_irq_setup)(struct mv88e6xxx_chip *chip, int port);
|
||||||
|
void (*serdes_irq_free)(struct mv88e6xxx_chip *chip, int port);
|
||||||
|
|
||||||
/* Statistics from the SERDES interface */
|
/* Statistics from the SERDES interface */
|
||||||
int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port);
|
int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port);
|
||||||
int (*serdes_get_strings)(struct mv88e6xxx_chip *chip, int port,
|
int (*serdes_get_strings)(struct mv88e6xxx_chip *chip, int port,
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irqdomain.h>
|
||||||
#include <linux/mii.h>
|
#include <linux/mii.h>
|
||||||
|
|
||||||
#include "chip.h"
|
#include "chip.h"
|
||||||
|
@ -399,6 +401,183 @@ int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
|
||||||
|
int port, int lane)
|
||||||
|
{
|
||||||
|
struct dsa_switch *ds = chip->ds;
|
||||||
|
u16 status;
|
||||||
|
bool up;
|
||||||
|
|
||||||
|
mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
|
||||||
|
MV88E6390_SGMII_STATUS, &status);
|
||||||
|
|
||||||
|
/* Status must be read twice in order to give the current link
|
||||||
|
* status. Otherwise the change in link status since the last
|
||||||
|
* read of the register is returned.
|
||||||
|
*/
|
||||||
|
mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
|
||||||
|
MV88E6390_SGMII_STATUS, &status);
|
||||||
|
up = status & MV88E6390_SGMII_STATUS_LINK;
|
||||||
|
|
||||||
|
dsa_port_phylink_mac_change(ds, port, up);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip,
|
||||||
|
int lane)
|
||||||
|
{
|
||||||
|
return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
|
||||||
|
MV88E6390_SGMII_INT_ENABLE,
|
||||||
|
MV88E6390_SGMII_INT_LINK_DOWN |
|
||||||
|
MV88E6390_SGMII_INT_LINK_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mv88e6390_serdes_irq_disable_sgmii(struct mv88e6xxx_chip *chip,
|
||||||
|
int lane)
|
||||||
|
{
|
||||||
|
return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
|
||||||
|
MV88E6390_SGMII_INT_ENABLE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port,
|
||||||
|
int lane)
|
||||||
|
{
|
||||||
|
u8 cmode = chip->ports[port].cmode;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
switch (cmode) {
|
||||||
|
case MV88E6XXX_PORT_STS_CMODE_SGMII:
|
||||||
|
case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
|
||||||
|
case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
|
||||||
|
err = mv88e6390_serdes_irq_enable_sgmii(chip, lane);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port,
|
||||||
|
int lane)
|
||||||
|
{
|
||||||
|
u8 cmode = chip->ports[port].cmode;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
switch (cmode) {
|
||||||
|
case MV88E6XXX_PORT_STS_CMODE_SGMII:
|
||||||
|
case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
|
||||||
|
case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
|
||||||
|
err = mv88e6390_serdes_irq_disable_sgmii(chip, lane);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip,
|
||||||
|
int lane, u16 *status)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
|
||||||
|
MV88E6390_SGMII_INT_STATUS, status);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t mv88e6390_serdes_thread_fn(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct mv88e6xxx_port *port = dev_id;
|
||||||
|
struct mv88e6xxx_chip *chip = port->chip;
|
||||||
|
irqreturn_t ret = IRQ_NONE;
|
||||||
|
u8 cmode = port->cmode;
|
||||||
|
u16 status;
|
||||||
|
int lane;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
lane = mv88e6390x_serdes_get_lane(chip, port->port);
|
||||||
|
|
||||||
|
mutex_lock(&chip->reg_lock);
|
||||||
|
|
||||||
|
switch (cmode) {
|
||||||
|
case MV88E6XXX_PORT_STS_CMODE_SGMII:
|
||||||
|
case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
|
||||||
|
case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
|
||||||
|
err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
if (status && (MV88E6390_SGMII_INT_LINK_DOWN ||
|
||||||
|
MV88E6390_SGMII_INT_LINK_UP)) {
|
||||||
|
ret = IRQ_HANDLED;
|
||||||
|
mv88e6390_serdes_irq_link_sgmii(chip, port->port, lane);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
mutex_unlock(&chip->reg_lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port)
|
||||||
|
{
|
||||||
|
int lane;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Only support ports 9 and 10 at the moment */
|
||||||
|
if (port < 9)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
lane = mv88e6390x_serdes_get_lane(chip, port);
|
||||||
|
|
||||||
|
if (lane == -ENODEV)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (lane < 0)
|
||||||
|
return lane;
|
||||||
|
|
||||||
|
chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain,
|
||||||
|
port);
|
||||||
|
if (chip->ports[port].serdes_irq < 0) {
|
||||||
|
dev_err(chip->dev, "Unable to map SERDES irq: %d\n",
|
||||||
|
chip->ports[port].serdes_irq);
|
||||||
|
return chip->ports[port].serdes_irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Requesting the IRQ will trigger irq callbacks. So we cannot
|
||||||
|
* hold the reg_lock.
|
||||||
|
*/
|
||||||
|
mutex_unlock(&chip->reg_lock);
|
||||||
|
err = request_threaded_irq(chip->ports[port].serdes_irq, NULL,
|
||||||
|
mv88e6390_serdes_thread_fn,
|
||||||
|
IRQF_ONESHOT, "mv88e6xxx-serdes",
|
||||||
|
&chip->ports[port]);
|
||||||
|
mutex_lock(&chip->reg_lock);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n",
|
||||||
|
err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mv88e6390_serdes_irq_enable(chip, port, lane);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port)
|
||||||
|
{
|
||||||
|
int lane = mv88e6390x_serdes_get_lane(chip, port);
|
||||||
|
|
||||||
|
if (port < 9)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (lane < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mv88e6390_serdes_irq_disable(chip, port, lane);
|
||||||
|
|
||||||
|
/* Freeing the IRQ will trigger irq callbacks. So we cannot
|
||||||
|
* hold the reg_lock.
|
||||||
|
*/
|
||||||
|
mutex_unlock(&chip->reg_lock);
|
||||||
|
free_irq(chip->ports[port].serdes_irq, &chip->ports[port]);
|
||||||
|
mutex_lock(&chip->reg_lock);
|
||||||
|
}
|
||||||
|
|
||||||
int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
|
int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
|
||||||
{
|
{
|
||||||
u8 cmode = chip->ports[port].cmode;
|
u8 cmode = chip->ports[port].cmode;
|
||||||
|
|
|
@ -42,11 +42,27 @@
|
||||||
#define MV88E6390_SGMII_CONTROL_RESET BIT(15)
|
#define MV88E6390_SGMII_CONTROL_RESET BIT(15)
|
||||||
#define MV88E6390_SGMII_CONTROL_LOOPBACK BIT(14)
|
#define MV88E6390_SGMII_CONTROL_LOOPBACK BIT(14)
|
||||||
#define MV88E6390_SGMII_CONTROL_PDOWN BIT(11)
|
#define MV88E6390_SGMII_CONTROL_PDOWN BIT(11)
|
||||||
|
#define MV88E6390_SGMII_STATUS 0x2001
|
||||||
|
#define MV88E6390_SGMII_STATUS_AN_DONE BIT(5)
|
||||||
|
#define MV88E6390_SGMII_STATUS_REMOTE_FAULT BIT(4)
|
||||||
|
#define MV88E6390_SGMII_STATUS_LINK BIT(2)
|
||||||
|
#define MV88E6390_SGMII_INT_ENABLE 0xa001
|
||||||
|
#define MV88E6390_SGMII_INT_SPEED_CHANGE BIT(14)
|
||||||
|
#define MV88E6390_SGMII_INT_DUPLEX_CHANGE BIT(13)
|
||||||
|
#define MV88E6390_SGMII_INT_PAGE_RX BIT(12)
|
||||||
|
#define MV88E6390_SGMII_INT_AN_COMPLETE BIT(11)
|
||||||
|
#define MV88E6390_SGMII_INT_LINK_DOWN BIT(10)
|
||||||
|
#define MV88E6390_SGMII_INT_LINK_UP BIT(9)
|
||||||
|
#define MV88E6390_SGMII_INT_SYMBOL_ERROR BIT(8)
|
||||||
|
#define MV88E6390_SGMII_INT_FALSE_CARRIER BIT(7)
|
||||||
|
#define MV88E6390_SGMII_INT_STATUS 0xa002
|
||||||
|
|
||||||
int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
|
int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
|
||||||
int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
|
int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
|
||||||
int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
|
int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
|
||||||
int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
|
int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
|
||||||
|
int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port);
|
||||||
|
void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port);
|
||||||
int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port);
|
int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port);
|
||||||
int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
|
int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
|
||||||
int port, uint8_t *data);
|
int port, uint8_t *data);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче