Commit a5a6858b authored by Russell King's avatar Russell King Committed by David S. Miller

net: dsa: mv88e6xxx: extend phylink to Serdes PHYs

Extend the mv88e6xxx phylink implementation down to Serdes PHYs, which
handle the PCS layer of such links.

- Implement phylink PCS link state reading, so that we can provide
  ethtool with the linkmodes and link speed in the expected manner.
  Note: this will only be called for in-band negotiation, which is
  only supported by the serdes interfaces.
- Implement phylink PCS configuration, so that the in-band AN and
  advertisement can be configured.
- Implement phylink PCS negotiation restart, so that the in-band AN
  can be restarted.
- Implement phylink PCS link up, so that when operating out-of-band,
  the Serdes can be configured for the appropriate fixed speed mode.
Signed-off-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 64d47d50
...@@ -419,9 +419,9 @@ static int mv88e6xxx_port_config_interface(struct mv88e6xxx_chip *chip, ...@@ -419,9 +419,9 @@ static int mv88e6xxx_port_config_interface(struct mv88e6xxx_chip *chip,
return 0; return 0;
} }
int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link, static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port,
int speed, int duplex, int pause, int link, int speed, int duplex, int pause,
phy_interface_t mode) phy_interface_t mode)
{ {
struct phylink_link_state state; struct phylink_link_state state;
int err; int err;
...@@ -488,6 +488,81 @@ static int mv88e6xxx_phy_is_internal(struct dsa_switch *ds, int port) ...@@ -488,6 +488,81 @@ static int mv88e6xxx_phy_is_internal(struct dsa_switch *ds, int port)
return port < chip->info->num_internal_phys; return port < chip->info->num_internal_phys;
} }
static int mv88e6xxx_serdes_pcs_get_state(struct dsa_switch *ds, int port,
struct phylink_link_state *state)
{
struct mv88e6xxx_chip *chip = ds->priv;
u8 lane;
int err;
mv88e6xxx_reg_lock(chip);
lane = mv88e6xxx_serdes_get_lane(chip, port);
if (lane && chip->info->ops->serdes_pcs_get_state)
err = chip->info->ops->serdes_pcs_get_state(chip, port, lane,
state);
else
err = -EOPNOTSUPP;
mv88e6xxx_reg_unlock(chip);
return err;
}
static int mv88e6xxx_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
unsigned int mode,
phy_interface_t interface,
const unsigned long *advertise)
{
const struct mv88e6xxx_ops *ops = chip->info->ops;
u8 lane;
if (ops->serdes_pcs_config) {
lane = mv88e6xxx_serdes_get_lane(chip, port);
if (lane)
return ops->serdes_pcs_config(chip, port, lane, mode,
interface, advertise);
}
return 0;
}
static void mv88e6xxx_serdes_pcs_an_restart(struct dsa_switch *ds, int port)
{
struct mv88e6xxx_chip *chip = ds->priv;
const struct mv88e6xxx_ops *ops;
int err = 0;
u8 lane;
ops = chip->info->ops;
if (ops->serdes_pcs_an_restart) {
mv88e6xxx_reg_lock(chip);
lane = mv88e6xxx_serdes_get_lane(chip, port);
if (lane)
err = ops->serdes_pcs_an_restart(chip, port, lane);
mv88e6xxx_reg_unlock(chip);
if (err)
dev_err(ds->dev, "p%d: failed to restart AN\n", port);
}
}
static int mv88e6xxx_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
unsigned int mode,
int speed, int duplex)
{
const struct mv88e6xxx_ops *ops = chip->info->ops;
u8 lane;
if (!phylink_autoneg_inband(mode) && ops->serdes_pcs_link_up) {
lane = mv88e6xxx_serdes_get_lane(chip, port);
if (lane)
return ops->serdes_pcs_link_up(chip, port, lane,
speed, duplex);
}
return 0;
}
static void mv88e6065_phylink_validate(struct mv88e6xxx_chip *chip, int port, static void mv88e6065_phylink_validate(struct mv88e6xxx_chip *chip, int port,
unsigned long *mask, unsigned long *mask,
struct phylink_link_state *state) struct phylink_link_state *state)
...@@ -592,22 +667,6 @@ static void mv88e6xxx_validate(struct dsa_switch *ds, int port, ...@@ -592,22 +667,6 @@ static void mv88e6xxx_validate(struct dsa_switch *ds, int port,
phylink_helper_basex_speed(state); phylink_helper_basex_speed(state);
} }
static int mv88e6xxx_link_state(struct dsa_switch *ds, int port,
struct phylink_link_state *state)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err;
mv88e6xxx_reg_lock(chip);
if (chip->info->ops->port_link_state)
err = chip->info->ops->port_link_state(chip, port, state);
else
err = -EOPNOTSUPP;
mv88e6xxx_reg_unlock(chip);
return err;
}
static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port,
unsigned int mode, unsigned int mode,
const struct phylink_link_state *state) const struct phylink_link_state *state)
...@@ -629,6 +688,18 @@ static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, ...@@ -629,6 +688,18 @@ static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port,
* gets in the way. * gets in the way.
*/ */
err = mv88e6xxx_port_config_interface(chip, port, state->interface); err = mv88e6xxx_port_config_interface(chip, port, state->interface);
if (err && err != -EOPNOTSUPP)
goto err_unlock;
err = mv88e6xxx_serdes_pcs_config(chip, port, mode, state->interface,
state->advertising);
/* FIXME: we should restart negotiation if something changed - which
* is something we get if we convert to using phylinks PCS operations.
*/
if (err > 0)
err = 0;
err_unlock:
mv88e6xxx_reg_unlock(chip); mv88e6xxx_reg_unlock(chip);
if (err && err != -EOPNOTSUPP) if (err && err != -EOPNOTSUPP)
...@@ -683,9 +754,14 @@ static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port, ...@@ -683,9 +754,14 @@ static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,
/* FIXME: for an automedia port, should we force the link /* FIXME: for an automedia port, should we force the link
* down here - what if the link comes up due to "other" media * down here - what if the link comes up due to "other" media
* while we're bringing the port up, how is the exclusivity * while we're bringing the port up, how is the exclusivity
* handled in the Marvell hardware? E.g. port 4 on 88E6532 * handled in the Marvell hardware? E.g. port 2 on 88E6390
* shared between internal PHY and Serdes. * shared between internal PHY and Serdes.
*/ */
err = mv88e6xxx_serdes_pcs_link_up(chip, port, mode, speed,
duplex);
if (err)
goto error;
if (ops->port_set_speed) { if (ops->port_set_speed) {
err = ops->port_set_speed(chip, port, speed); err = ops->port_set_speed(chip, port, speed);
if (err && err != -EOPNOTSUPP) if (err && err != -EOPNOTSUPP)
...@@ -3557,6 +3633,11 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { ...@@ -3557,6 +3633,11 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
.serdes_power = mv88e6390_serdes_power, .serdes_power = mv88e6390_serdes_power,
.serdes_get_lane = mv88e6341_serdes_get_lane, .serdes_get_lane = mv88e6341_serdes_get_lane,
/* Check status register pause & lpa register */
.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
.serdes_pcs_config = mv88e6390_serdes_pcs_config,
.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
.serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_enable = mv88e6390_serdes_irq_enable,
.serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_irq_status = mv88e6390_serdes_irq_status,
...@@ -3729,6 +3810,10 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { ...@@ -3729,6 +3810,10 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
.vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_getnext = mv88e6352_g1_vtu_getnext,
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
.serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_get_lane = mv88e6352_serdes_get_lane,
.serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state,
.serdes_pcs_config = mv88e6352_serdes_pcs_config,
.serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart,
.serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up,
.serdes_power = mv88e6352_serdes_power, .serdes_power = mv88e6352_serdes_power,
.serdes_get_regs_len = mv88e6352_serdes_get_regs_len, .serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
.serdes_get_regs = mv88e6352_serdes_get_regs, .serdes_get_regs = mv88e6352_serdes_get_regs,
...@@ -3822,6 +3907,10 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { ...@@ -3822,6 +3907,10 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
.vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_getnext = mv88e6352_g1_vtu_getnext,
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
.serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_get_lane = mv88e6352_serdes_get_lane,
.serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state,
.serdes_pcs_config = mv88e6352_serdes_pcs_config,
.serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart,
.serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up,
.serdes_power = mv88e6352_serdes_power, .serdes_power = mv88e6352_serdes_power,
.serdes_irq_mapping = mv88e6352_serdes_irq_mapping, .serdes_irq_mapping = mv88e6352_serdes_irq_mapping,
.serdes_irq_enable = mv88e6352_serdes_irq_enable, .serdes_irq_enable = mv88e6352_serdes_irq_enable,
...@@ -3912,6 +4001,11 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { ...@@ -3912,6 +4001,11 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
.serdes_power = mv88e6390_serdes_power, .serdes_power = mv88e6390_serdes_power,
.serdes_get_lane = mv88e6390_serdes_get_lane, .serdes_get_lane = mv88e6390_serdes_get_lane,
/* Check status register pause & lpa register */
.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
.serdes_pcs_config = mv88e6390_serdes_pcs_config,
.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
.serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_enable = mv88e6390_serdes_irq_enable,
.serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_irq_status = mv88e6390_serdes_irq_status,
...@@ -3968,6 +4062,11 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { ...@@ -3968,6 +4062,11 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
.serdes_power = mv88e6390_serdes_power, .serdes_power = mv88e6390_serdes_power,
.serdes_get_lane = mv88e6390x_serdes_get_lane, .serdes_get_lane = mv88e6390x_serdes_get_lane,
/* Check status register pause & lpa register */
.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
.serdes_pcs_config = mv88e6390_serdes_pcs_config,
.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
.serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_enable = mv88e6390_serdes_irq_enable,
.serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_irq_status = mv88e6390_serdes_irq_status,
...@@ -4023,6 +4122,11 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { ...@@ -4023,6 +4122,11 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
.serdes_power = mv88e6390_serdes_power, .serdes_power = mv88e6390_serdes_power,
.serdes_get_lane = mv88e6390_serdes_get_lane, .serdes_get_lane = mv88e6390_serdes_get_lane,
/* Check status register pause & lpa register */
.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
.serdes_pcs_config = mv88e6390_serdes_pcs_config,
.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
.serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_enable = mv88e6390_serdes_irq_enable,
.serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_irq_status = mv88e6390_serdes_irq_status,
...@@ -4080,6 +4184,10 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { ...@@ -4080,6 +4184,10 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
.vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_getnext = mv88e6352_g1_vtu_getnext,
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
.serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_get_lane = mv88e6352_serdes_get_lane,
.serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state,
.serdes_pcs_config = mv88e6352_serdes_pcs_config,
.serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart,
.serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up,
.serdes_power = mv88e6352_serdes_power, .serdes_power = mv88e6352_serdes_power,
.serdes_irq_mapping = mv88e6352_serdes_irq_mapping, .serdes_irq_mapping = mv88e6352_serdes_irq_mapping,
.serdes_irq_enable = mv88e6352_serdes_irq_enable, .serdes_irq_enable = mv88e6352_serdes_irq_enable,
...@@ -4176,6 +4284,11 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { ...@@ -4176,6 +4284,11 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
.serdes_power = mv88e6390_serdes_power, .serdes_power = mv88e6390_serdes_power,
.serdes_get_lane = mv88e6390_serdes_get_lane, .serdes_get_lane = mv88e6390_serdes_get_lane,
/* Check status register pause & lpa register */
.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
.serdes_pcs_config = mv88e6390_serdes_pcs_config,
.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
.serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_enable = mv88e6390_serdes_irq_enable,
.serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_irq_status = mv88e6390_serdes_irq_status,
...@@ -4319,6 +4432,11 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { ...@@ -4319,6 +4432,11 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
.serdes_power = mv88e6390_serdes_power, .serdes_power = mv88e6390_serdes_power,
.serdes_get_lane = mv88e6341_serdes_get_lane, .serdes_get_lane = mv88e6341_serdes_get_lane,
/* Check status register pause & lpa register */
.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
.serdes_pcs_config = mv88e6390_serdes_pcs_config,
.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
.serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_enable = mv88e6390_serdes_irq_enable,
.serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_irq_status = mv88e6390_serdes_irq_status,
...@@ -4458,6 +4576,10 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { ...@@ -4458,6 +4576,10 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
.vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_getnext = mv88e6352_g1_vtu_getnext,
.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
.serdes_get_lane = mv88e6352_serdes_get_lane, .serdes_get_lane = mv88e6352_serdes_get_lane,
.serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state,
.serdes_pcs_config = mv88e6352_serdes_pcs_config,
.serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart,
.serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up,
.serdes_power = mv88e6352_serdes_power, .serdes_power = mv88e6352_serdes_power,
.serdes_irq_mapping = mv88e6352_serdes_irq_mapping, .serdes_irq_mapping = mv88e6352_serdes_irq_mapping,
.serdes_irq_enable = mv88e6352_serdes_irq_enable, .serdes_irq_enable = mv88e6352_serdes_irq_enable,
...@@ -4519,6 +4641,11 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { ...@@ -4519,6 +4641,11 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
.serdes_power = mv88e6390_serdes_power, .serdes_power = mv88e6390_serdes_power,
.serdes_get_lane = mv88e6390_serdes_get_lane, .serdes_get_lane = mv88e6390_serdes_get_lane,
/* Check status register pause & lpa register */
.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
.serdes_pcs_config = mv88e6390_serdes_pcs_config,
.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
.serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_enable = mv88e6390_serdes_irq_enable,
.serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_irq_status = mv88e6390_serdes_irq_status,
...@@ -4579,6 +4706,10 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { ...@@ -4579,6 +4706,10 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
.serdes_power = mv88e6390_serdes_power, .serdes_power = mv88e6390_serdes_power,
.serdes_get_lane = mv88e6390x_serdes_get_lane, .serdes_get_lane = mv88e6390x_serdes_get_lane,
.serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
.serdes_pcs_config = mv88e6390_serdes_pcs_config,
.serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
.serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
.serdes_irq_mapping = mv88e6390_serdes_irq_mapping, .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
.serdes_irq_enable = mv88e6390_serdes_irq_enable, .serdes_irq_enable = mv88e6390_serdes_irq_enable,
.serdes_irq_status = mv88e6390_serdes_irq_status, .serdes_irq_status = mv88e6390_serdes_irq_status,
...@@ -5457,8 +5588,9 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { ...@@ -5457,8 +5588,9 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.setup = mv88e6xxx_setup, .setup = mv88e6xxx_setup,
.teardown = mv88e6xxx_teardown, .teardown = mv88e6xxx_teardown,
.phylink_validate = mv88e6xxx_validate, .phylink_validate = mv88e6xxx_validate,
.phylink_mac_link_state = mv88e6xxx_link_state, .phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state,
.phylink_mac_config = mv88e6xxx_mac_config, .phylink_mac_config = mv88e6xxx_mac_config,
.phylink_mac_an_restart = mv88e6xxx_serdes_pcs_an_restart,
.phylink_mac_link_down = mv88e6xxx_mac_link_down, .phylink_mac_link_down = mv88e6xxx_mac_link_down,
.phylink_mac_link_up = mv88e6xxx_mac_link_up, .phylink_mac_link_up = mv88e6xxx_mac_link_up,
.get_strings = mv88e6xxx_get_strings, .get_strings = mv88e6xxx_get_strings,
......
...@@ -502,6 +502,17 @@ struct mv88e6xxx_ops { ...@@ -502,6 +502,17 @@ struct mv88e6xxx_ops {
/* SERDES lane mapping */ /* SERDES lane mapping */
u8 (*serdes_get_lane)(struct mv88e6xxx_chip *chip, int port); u8 (*serdes_get_lane)(struct mv88e6xxx_chip *chip, int port);
int (*serdes_pcs_get_state)(struct mv88e6xxx_chip *chip, int port,
u8 lane, struct phylink_link_state *state);
int (*serdes_pcs_config)(struct mv88e6xxx_chip *chip, int port,
u8 lane, unsigned int mode,
phy_interface_t interface,
const unsigned long *advertise);
int (*serdes_pcs_an_restart)(struct mv88e6xxx_chip *chip, int port,
u8 lane);
int (*serdes_pcs_link_up)(struct mv88e6xxx_chip *chip, int port,
u8 lane, int speed, int duplex);
/* SERDES interrupt handling */ /* SERDES interrupt handling */
unsigned int (*serdes_irq_mapping)(struct mv88e6xxx_chip *chip, unsigned int (*serdes_irq_mapping)(struct mv88e6xxx_chip *chip,
int port); int port);
...@@ -669,9 +680,6 @@ int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg, ...@@ -669,9 +680,6 @@ int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg,
u16 mask, u16 val); u16 mask, u16 val);
int mv88e6xxx_wait_bit(struct mv88e6xxx_chip *chip, int addr, int reg, int mv88e6xxx_wait_bit(struct mv88e6xxx_chip *chip, int addr, int reg,
int bit, int val); int bit, int val);
int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link,
int speed, int duplex, int pause,
phy_interface_t mode);
struct mii_bus *mv88e6xxx_default_mdio_bus(struct mv88e6xxx_chip *chip); struct mii_bus *mv88e6xxx_default_mdio_bus(struct mv88e6xxx_chip *chip);
static inline void mv88e6xxx_reg_lock(struct mv88e6xxx_chip *chip) static inline void mv88e6xxx_reg_lock(struct mv88e6xxx_chip *chip)
......
...@@ -49,6 +49,52 @@ static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip, ...@@ -49,6 +49,52 @@ static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip,
return mv88e6xxx_phy_write(chip, lane, reg_c45, val); return mv88e6xxx_phy_write(chip, lane, reg_c45, val);
} }
static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip,
u16 status, u16 lpa,
struct phylink_link_state *state)
{
if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
state->duplex = status &
MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ?
DUPLEX_FULL : DUPLEX_HALF;
if (status & MV88E6390_SGMII_PHY_STATUS_TX_PAUSE)
state->pause |= MLO_PAUSE_TX;
if (status & MV88E6390_SGMII_PHY_STATUS_RX_PAUSE)
state->pause |= MLO_PAUSE_RX;
switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) {
case MV88E6390_SGMII_PHY_STATUS_SPEED_1000:
if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
state->speed = SPEED_2500;
else
state->speed = SPEED_1000;
break;
case MV88E6390_SGMII_PHY_STATUS_SPEED_100:
state->speed = SPEED_100;
break;
case MV88E6390_SGMII_PHY_STATUS_SPEED_10:
state->speed = SPEED_10;
break;
default:
dev_err(chip->dev, "invalid PHY speed\n");
return -EINVAL;
}
} else {
state->link = false;
}
if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
mii_lpa_mod_linkmode_x(state->lp_advertising, lpa,
ETHTOOL_LINK_MODE_2500baseX_Full_BIT);
else if (state->interface == PHY_INTERFACE_MODE_1000BASEX)
mii_lpa_mod_linkmode_x(state->lp_advertising, lpa,
ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
return 0;
}
int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
bool up) bool up)
{ {
...@@ -70,6 +116,120 @@ int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, ...@@ -70,6 +116,120 @@ int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
return err; return err;
} }
int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
u8 lane, unsigned int mode,
phy_interface_t interface,
const unsigned long *advertise)
{
u16 adv, bmcr, val;
bool changed;
int err;
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
adv = 0x0001;
break;
case PHY_INTERFACE_MODE_1000BASEX:
adv = linkmode_adv_to_mii_adv_x(advertise,
ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
break;
default:
return 0;
}
err = mv88e6352_serdes_read(chip, MII_ADVERTISE, &val);
if (err)
return err;
changed = val != adv;
if (changed) {
err = mv88e6352_serdes_write(chip, MII_ADVERTISE, adv);
if (err)
return err;
}
err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
if (err)
return err;
if (phylink_autoneg_inband(mode))
bmcr = val | BMCR_ANENABLE;
else
bmcr = val & ~BMCR_ANENABLE;
if (bmcr == val)
return changed;
return mv88e6352_serdes_write(chip, MII_BMCR, bmcr);
}
int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
u8 lane, struct phylink_link_state *state)
{
u16 lpa, status;
int err;
err = mv88e6352_serdes_read(chip, 0x11, &status);
if (err) {
dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err);
return err;
}
err = mv88e6352_serdes_read(chip, MII_LPA, &lpa);
if (err) {
dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err);
return err;
}
return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
}
int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
u8 lane)
{
u16 bmcr;
int err;
err = mv88e6352_serdes_read(chip, MII_BMCR, &bmcr);
if (err)
return err;
return mv88e6352_serdes_write(chip, MII_BMCR, bmcr | BMCR_ANRESTART);
}
int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
u8 lane, int speed, int duplex)
{
u16 val, bmcr;
int err;
err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
if (err)
return err;
bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000);
switch (speed) {
case SPEED_1000:
bmcr |= BMCR_SPEED1000;
break;
case SPEED_100:
bmcr |= BMCR_SPEED100;
break;
case SPEED_10:
break;
}
if (duplex == DUPLEX_FULL)
bmcr |= BMCR_FULLDPLX;
if (bmcr == val)
return 0;
return mv88e6352_serdes_write(chip, MII_BMCR, bmcr);
}
u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
{ {
u8 cmode = chip->ports[port].cmode; u8 cmode = chip->ports[port].cmode;
...@@ -538,71 +698,153 @@ int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, ...@@ -538,71 +698,153 @@ int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
return err; return err;
} }
static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
int port, u8 lane) u8 lane, unsigned int mode,
phy_interface_t interface,
const unsigned long *advertise)
{ {
u8 cmode = chip->ports[port].cmode; u16 val, bmcr, adv;
struct dsa_switch *ds = chip->ds; bool changed;
int duplex = DUPLEX_UNKNOWN; int err;
int speed = SPEED_UNKNOWN;
phy_interface_t mode; switch (interface) {
int link, err; case PHY_INTERFACE_MODE_SGMII:
u16 status; adv = 0x0001;
break;
case PHY_INTERFACE_MODE_1000BASEX:
adv = linkmode_adv_to_mii_adv_x(advertise,
ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
break;
case PHY_INTERFACE_MODE_2500BASEX:
adv = linkmode_adv_to_mii_adv_x(advertise,
ETHTOOL_LINK_MODE_2500baseX_Full_BIT);
break;
default:
return 0;
}
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_SGMII_ADVERTISE, &val);
if (err)
return err;
changed = val != adv;
if (changed) {
err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_SGMII_ADVERTISE, adv);
if (err)
return err;
}
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_SGMII_BMCR, &val);
if (err)
return err;
if (phylink_autoneg_inband(mode))
bmcr = val | BMCR_ANENABLE;
else
bmcr = val & ~BMCR_ANENABLE;
/* setting ANENABLE triggers a restart of negotiation */
if (bmcr == val)
return changed;
return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_SGMII_BMCR, bmcr);
}
int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
u8 lane, struct phylink_link_state *state)
{
u16 lpa, status;
int err;
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_SGMII_PHY_STATUS, &status); MV88E6390_SGMII_PHY_STATUS, &status);
if (err) { if (err) {
dev_err(chip->dev, "can't read SGMII PHY status: %d\n", err); dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err);
return; return err;
} }
link = status & MV88E6390_SGMII_PHY_STATUS_LINK ? err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
LINK_FORCED_UP : LINK_FORCED_DOWN; MV88E6390_SGMII_LPA, &lpa);
if (err) {
dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err);
return err;
}
if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) { return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
duplex = status & MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ? }
DUPLEX_FULL : DUPLEX_HALF;
switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) { int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
case MV88E6390_SGMII_PHY_STATUS_SPEED_1000: u8 lane)
if (cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) {
speed = SPEED_2500; u16 bmcr;
else int err;
speed = SPEED_1000;
break;
case MV88E6390_SGMII_PHY_STATUS_SPEED_100:
speed = SPEED_100;
break;
case MV88E6390_SGMII_PHY_STATUS_SPEED_10:
speed = SPEED_10;
break;
default:
dev_err(chip->dev, "invalid PHY speed\n");
return;
}
}
switch (cmode) { err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
case MV88E6XXX_PORT_STS_CMODE_SGMII: MV88E6390_SGMII_BMCR, &bmcr);
mode = PHY_INTERFACE_MODE_SGMII; if (err)
return err;
return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_SGMII_BMCR,
bmcr | BMCR_ANRESTART);
}
int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
u8 lane, int speed, int duplex)
{
u16 val, bmcr;
int err;
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_SGMII_BMCR, &val);
if (err)
return err;
bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000);
switch (speed) {
case SPEED_2500:
case SPEED_1000:
bmcr |= BMCR_SPEED1000;
break; break;
case MV88E6XXX_PORT_STS_CMODE_1000BASEX: case SPEED_100:
mode = PHY_INTERFACE_MODE_1000BASEX; bmcr |= BMCR_SPEED100;
break; break;
case MV88E6XXX_PORT_STS_CMODE_2500BASEX: case SPEED_10:
mode = PHY_INTERFACE_MODE_2500BASEX;
break; break;
default:
mode = PHY_INTERFACE_MODE_NA;
} }
err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex, if (duplex == DUPLEX_FULL)
PAUSE_OFF, mode); bmcr |= BMCR_FULLDPLX;
if (err)
dev_err(chip->dev, "can't propagate PHY settings to MAC: %d\n", if (bmcr == val)
err); return 0;
else
dsa_port_phylink_mac_change(ds, port, link == LINK_FORCED_UP); return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_SGMII_BMCR, bmcr);
}
static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
int port, u8 lane)
{
u16 status;
int err;
err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_SGMII_PHY_STATUS, &status);
if (err) {
dev_err(chip->dev, "can't read SGMII PHY status: %d\n", err);
return;
}
dsa_port_phylink_mac_change(chip->ds, port,
!!(status & MV88E6390_SGMII_PHY_STATUS_LINK));
} }
static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip, static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip,
......
...@@ -48,6 +48,8 @@ ...@@ -48,6 +48,8 @@
/* 1000BASE-X and SGMII */ /* 1000BASE-X and SGMII */
#define MV88E6390_SGMII_BMCR (0x2000 + MII_BMCR) #define MV88E6390_SGMII_BMCR (0x2000 + MII_BMCR)
#define MV88E6390_SGMII_ADVERTISE (0x2000 + MII_ADVERTISE)
#define MV88E6390_SGMII_LPA (0x2000 + MII_LPA)
#define MV88E6390_SGMII_INT_ENABLE 0xa001 #define MV88E6390_SGMII_INT_ENABLE 0xa001
#define MV88E6390_SGMII_INT_SPEED_CHANGE BIT(14) #define MV88E6390_SGMII_INT_SPEED_CHANGE BIT(14)
#define MV88E6390_SGMII_INT_DUPLEX_CHANGE BIT(13) #define MV88E6390_SGMII_INT_DUPLEX_CHANGE BIT(13)
...@@ -66,6 +68,8 @@ ...@@ -66,6 +68,8 @@
#define MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL BIT(13) #define MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL BIT(13)
#define MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID BIT(11) #define MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID BIT(11)
#define MV88E6390_SGMII_PHY_STATUS_LINK BIT(10) #define MV88E6390_SGMII_PHY_STATUS_LINK BIT(10)
#define MV88E6390_SGMII_PHY_STATUS_TX_PAUSE BIT(3)
#define MV88E6390_SGMII_PHY_STATUS_RX_PAUSE BIT(2)
/* Packet generator pad packet checker */ /* Packet generator pad packet checker */
#define MV88E6390_PG_CONTROL 0xf010 #define MV88E6390_PG_CONTROL 0xf010
...@@ -75,6 +79,26 @@ u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); ...@@ -75,6 +79,26 @@ u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
u8 lane, unsigned int mode,
phy_interface_t interface,
const unsigned long *advertise);
int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
u8 lane, unsigned int mode,
phy_interface_t interface,
const unsigned long *advertise);
int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
u8 lane, struct phylink_link_state *state);
int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
u8 lane, struct phylink_link_state *state);
int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
u8 lane);
int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
u8 lane);
int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
u8 lane, int speed, int duplex);
int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
u8 lane, int speed, int duplex);
unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip,
int port); int port);
unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment