提交 ff50eda4 编写于 作者: D David S. Miller

Merge branch 'More-complete-PHYLINK-support-for-mv88e6xxx'

Andrew Lunn says:

====================
More complete PHYLINK support for mv88e6xxx

Previous patches added sufficient PHYLINK support to the mv88e6xxx
that it did not break existing use cases, basically fixed-link phys.

This patchset builds out the support so that SFP modules, up to
2.5Gbps can be supported, on mv88e6390X, on ports 9 and 10. It also
provides a framework which can be extended to support SFPs on ports
2-8 of mv88e6390X, 10Gbps PHYs, and SFP support on the 6352 family.

Russell King did much of the initial work, implementing the validate
and mac_link_state calls. However, there is an important TODO in the
commit message:

needs to call phylink_mac_change() when the port link comes up/goes down.

The remaining patches implement this, by adding more support for the
SERDES interfaces, in particular, interrupt support so we get notified
when the SERDES gains/looses sync.

This has been tested on the ZII devel C, using a Clearfog as peer
device.
====================
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
此差异已折叠。
...@@ -191,12 +191,16 @@ struct mv88e6xxx_port_hwtstamp { ...@@ -191,12 +191,16 @@ struct mv88e6xxx_port_hwtstamp {
}; };
struct mv88e6xxx_port { struct mv88e6xxx_port {
struct mv88e6xxx_chip *chip;
int port;
u64 serdes_stats[2]; u64 serdes_stats[2];
u64 atu_member_violation; u64 atu_member_violation;
u64 atu_miss_violation; u64 atu_miss_violation;
u64 atu_full_violation; u64 atu_full_violation;
u64 vtu_member_violation; u64 vtu_member_violation;
u64 vtu_miss_violation; u64 vtu_miss_violation;
u8 cmode;
int serdes_irq;
}; };
struct mv88e6xxx_chip { struct mv88e6xxx_chip {
...@@ -351,6 +355,13 @@ struct mv88e6xxx_ops { ...@@ -351,6 +355,13 @@ struct mv88e6xxx_ops {
*/ */
int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup); int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup);
#define PAUSE_ON 1
#define PAUSE_OFF 0
/* Enable/disable sending Pause */
int (*port_set_pause)(struct mv88e6xxx_chip *chip, int port,
int pause);
#define SPEED_MAX INT_MAX #define SPEED_MAX INT_MAX
#define SPEED_UNFORCED -2 #define SPEED_UNFORCED -2
...@@ -383,12 +394,16 @@ struct mv88e6xxx_ops { ...@@ -383,12 +394,16 @@ struct mv88e6xxx_ops {
*/ */
int (*port_set_cmode)(struct mv88e6xxx_chip *chip, int port, int (*port_set_cmode)(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode); phy_interface_t mode);
int (*port_get_cmode)(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
/* Some devices have a per port register indicating what is /* Some devices have a per port register indicating what is
* the upstream port this port should forward to. * the upstream port this port should forward to.
*/ */
int (*port_set_upstream_port)(struct mv88e6xxx_chip *chip, int port, int (*port_set_upstream_port)(struct mv88e6xxx_chip *chip, int port,
int upstream_port); int upstream_port);
/* Return the port link state, as required by phylink */
int (*port_link_state)(struct mv88e6xxx_chip *chip, int port,
struct phylink_link_state *state);
/* Snapshot the statistics for a port. The statistics can then /* Snapshot the statistics for a port. The statistics can then
* be read back a leisure but still with a consistent view. * be read back a leisure but still with a consistent view.
...@@ -420,6 +435,10 @@ struct mv88e6xxx_ops { ...@@ -420,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,
...@@ -444,6 +463,11 @@ struct mv88e6xxx_ops { ...@@ -444,6 +463,11 @@ struct mv88e6xxx_ops {
/* Precision Time Protocol operations */ /* Precision Time Protocol operations */
const struct mv88e6xxx_ptp_ops *ptp_ops; const struct mv88e6xxx_ptp_ops *ptp_ops;
/* Phylink */
void (*phylink_validate)(struct mv88e6xxx_chip *chip, int port,
unsigned long *mask,
struct phylink_link_state *state);
}; };
struct mv88e6xxx_irq_ops { struct mv88e6xxx_irq_ops {
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "chip.h" #include "chip.h"
#include "port.h" #include "port.h"
#include "serdes.h"
int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
u16 *val) u16 *val)
...@@ -36,6 +37,29 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, ...@@ -36,6 +37,29 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
return mv88e6xxx_write(chip, addr, reg, val); return mv88e6xxx_write(chip, addr, reg, val);
} }
/* Offset 0x00: MAC (or PCS or Physical) Status Register
*
* For most devices, this is read only. However the 6185 has the MyPause
* bit read/write.
*/
int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port,
int pause)
{
u16 reg;
int err;
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
if (err)
return err;
if (pause)
reg |= MV88E6XXX_PORT_STS_MY_PAUSE;
else
reg &= ~MV88E6XXX_PORT_STS_MY_PAUSE;
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg);
}
/* Offset 0x01: MAC (or PCS or Physical) Control Register /* Offset 0x01: MAC (or PCS or Physical) Control Register
* *
* Link, Duplex and Flow Control have one force bit, one value bit. * Link, Duplex and Flow Control have one force bit, one value bit.
...@@ -318,8 +342,9 @@ int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) ...@@ -318,8 +342,9 @@ int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode) phy_interface_t mode)
{ {
u16 reg; int lane;
u16 cmode; u16 cmode;
u16 reg;
int err; int err;
if (mode == PHY_INTERFACE_MODE_NA) if (mode == PHY_INTERFACE_MODE_NA)
...@@ -349,6 +374,20 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, ...@@ -349,6 +374,20 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
cmode = 0; cmode = 0;
} }
lane = mv88e6390x_serdes_get_lane(chip, port);
if (lane < 0)
return lane;
if (chip->ports[port].serdes_irq) {
err = mv88e6390_serdes_irq_disable(chip, port, lane);
if (err)
return err;
}
err = mv88e6390_serdes_power(chip, port, false);
if (err)
return err;
if (cmode) { if (cmode) {
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg); err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
if (err) if (err)
...@@ -360,12 +399,38 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, ...@@ -360,12 +399,38 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg); err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg);
if (err) if (err)
return err; return err;
err = mv88e6390_serdes_power(chip, port, true);
if (err)
return err;
if (chip->ports[port].serdes_irq) {
err = mv88e6390_serdes_irq_enable(chip, port, lane);
if (err)
return err;
}
} }
chip->ports[port].cmode = cmode;
return 0; return 0;
} }
int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
{
int err;
u16 reg;
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
if (err)
return err;
*cmode = reg & MV88E6185_PORT_STS_CMODE_MASK;
return 0;
}
int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
{ {
int err; int err;
u16 reg; u16 reg;
...@@ -379,7 +444,7 @@ int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) ...@@ -379,7 +444,7 @@ int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
return 0; return 0;
} }
int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port, int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port,
struct phylink_link_state *state) struct phylink_link_state *state)
{ {
int err; int err;
...@@ -400,7 +465,7 @@ int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port, ...@@ -400,7 +465,7 @@ int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port,
state->speed = SPEED_1000; state->speed = SPEED_1000;
break; break;
case MV88E6XXX_PORT_STS_SPEED_10000: case MV88E6XXX_PORT_STS_SPEED_10000:
if ((reg &MV88E6XXX_PORT_STS_CMODE_MASK) == if ((reg & MV88E6XXX_PORT_STS_CMODE_MASK) ==
MV88E6XXX_PORT_STS_CMODE_2500BASEX) MV88E6XXX_PORT_STS_CMODE_2500BASEX)
state->speed = SPEED_2500; state->speed = SPEED_2500;
else else
...@@ -417,6 +482,42 @@ int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port, ...@@ -417,6 +482,42 @@ int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port,
return 0; return 0;
} }
int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port,
struct phylink_link_state *state)
{
if (state->interface == PHY_INTERFACE_MODE_1000BASEX) {
u8 cmode = chip->ports[port].cmode;
/* When a port is in "Cross-chip serdes" mode, it uses
* 1000Base-X full duplex mode, but there is no automatic
* link detection. Use the sync OK status for link (as it
* would do for 1000Base-X mode.)
*/
if (cmode == MV88E6185_PORT_STS_CMODE_SERDES) {
u16 mac;
int err;
err = mv88e6xxx_port_read(chip, port,
MV88E6XXX_PORT_MAC_CTL, &mac);
if (err)
return err;
state->link = !!(mac & MV88E6185_PORT_MAC_CTL_SYNC_OK);
state->an_enabled = 1;
state->an_complete =
!!(mac & MV88E6185_PORT_MAC_CTL_AN_DONE);
state->duplex =
state->link ? DUPLEX_FULL : DUPLEX_UNKNOWN;
state->speed =
state->link ? SPEED_1000 : SPEED_UNKNOWN;
return 0;
}
}
return mv88e6352_port_link_state(chip, port, state);
}
/* Offset 0x02: Jamming Control /* Offset 0x02: Jamming Control
* *
* Do not limit the period of time that this port can be paused for by * Do not limit the period of time that this port can be paused for by
......
...@@ -42,14 +42,28 @@ ...@@ -42,14 +42,28 @@
#define MV88E6XXX_PORT_STS_CMODE_2500BASEX 0x000b #define MV88E6XXX_PORT_STS_CMODE_2500BASEX 0x000b
#define MV88E6XXX_PORT_STS_CMODE_XAUI 0x000c #define MV88E6XXX_PORT_STS_CMODE_XAUI 0x000c
#define MV88E6XXX_PORT_STS_CMODE_RXAUI 0x000d #define MV88E6XXX_PORT_STS_CMODE_RXAUI 0x000d
#define MV88E6185_PORT_STS_CDUPLEX 0x0008
#define MV88E6185_PORT_STS_CMODE_MASK 0x0007
#define MV88E6185_PORT_STS_CMODE_GMII_FD 0x0000
#define MV88E6185_PORT_STS_CMODE_MII_100_FD_PS 0x0001
#define MV88E6185_PORT_STS_CMODE_MII_100 0x0002
#define MV88E6185_PORT_STS_CMODE_MII_10 0x0003
#define MV88E6185_PORT_STS_CMODE_SERDES 0x0004
#define MV88E6185_PORT_STS_CMODE_1000BASE_X 0x0005
#define MV88E6185_PORT_STS_CMODE_PHY 0x0006
#define MV88E6185_PORT_STS_CMODE_DISABLED 0x0007
/* Offset 0x01: MAC (or PCS or Physical) Control Register */ /* Offset 0x01: MAC (or PCS or Physical) Control Register */
#define MV88E6XXX_PORT_MAC_CTL 0x01 #define MV88E6XXX_PORT_MAC_CTL 0x01
#define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK 0x8000 #define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK 0x8000
#define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK 0x4000 #define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK 0x4000
#define MV88E6185_PORT_MAC_CTL_SYNC_OK 0x4000
#define MV88E6390_PORT_MAC_CTL_FORCE_SPEED 0x2000 #define MV88E6390_PORT_MAC_CTL_FORCE_SPEED 0x2000
#define MV88E6390_PORT_MAC_CTL_ALTSPEED 0x1000 #define MV88E6390_PORT_MAC_CTL_ALTSPEED 0x1000
#define MV88E6352_PORT_MAC_CTL_200BASE 0x1000 #define MV88E6352_PORT_MAC_CTL_200BASE 0x1000
#define MV88E6185_PORT_MAC_CTL_AN_EN 0x0400
#define MV88E6185_PORT_MAC_CTL_AN_RESTART 0x0200
#define MV88E6185_PORT_MAC_CTL_AN_DONE 0x0100
#define MV88E6XXX_PORT_MAC_CTL_FC 0x0080 #define MV88E6XXX_PORT_MAC_CTL_FC 0x0080
#define MV88E6XXX_PORT_MAC_CTL_FORCE_FC 0x0040 #define MV88E6XXX_PORT_MAC_CTL_FORCE_FC 0x0040
#define MV88E6XXX_PORT_MAC_CTL_LINK_UP 0x0020 #define MV88E6XXX_PORT_MAC_CTL_LINK_UP 0x0020
...@@ -242,6 +256,8 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, ...@@ -242,6 +256,8 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
u16 val); u16 val);
int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port,
int pause);
int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode); phy_interface_t mode);
int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
...@@ -295,8 +311,11 @@ int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in, ...@@ -295,8 +311,11 @@ int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
u8 out); u8 out);
int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode); phy_interface_t mode);
int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port, int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port,
struct phylink_link_state *state);
int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port,
struct phylink_link_state *state); struct phylink_link_state *state);
int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port); int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port);
int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
......
...@@ -11,6 +11,8 @@ ...@@ -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"
...@@ -35,6 +37,22 @@ static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg, ...@@ -35,6 +37,22 @@ static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg,
reg, val); reg, val);
} }
static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip,
int lane, int device, int reg, u16 *val)
{
int reg_c45 = MII_ADDR_C45 | device << 16 | reg;
return mv88e6xxx_phy_read(chip, lane, reg_c45, val);
}
static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip,
int lane, int device, int reg, u16 val)
{
int reg_c45 = MII_ADDR_C45 | device << 16 | reg;
return mv88e6xxx_phy_write(chip, lane, reg_c45, val);
}
static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on) static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on)
{ {
u16 val, new_val; u16 val, new_val;
...@@ -57,14 +75,7 @@ static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on) ...@@ -57,14 +75,7 @@ static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on)
static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
{ {
u8 cmode; u8 cmode = chip->ports[port].cmode;
int err;
err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
if (err) {
dev_err(chip->dev, "failed to read cmode\n");
return false;
}
if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) || if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) ||
(cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) || (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) ||
...@@ -174,16 +185,121 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, ...@@ -174,16 +185,121 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
return ARRAY_SIZE(mv88e6352_serdes_hw_stats); return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
} }
/* Return the SERDES lane address a port is using. Only Ports 9 and 10
* have SERDES lanes. Returns -ENODEV if a port does not have a lane.
*/
static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
{
u8 cmode = chip->ports[port].cmode;
switch (port) {
case 9:
if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
return MV88E6390_PORT9_LANE0;
return -ENODEV;
case 10:
if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
return MV88E6390_PORT10_LANE0;
return -ENODEV;
default:
return -ENODEV;
}
}
/* Return the SERDES lane address a port is using. Ports 9 and 10 can
* use multiple lanes. If so, return the first lane the port uses.
* Returns -ENODEV if a port does not have a lane.
*/
int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
{
u8 cmode_port9, cmode_port10, cmode_port;
cmode_port9 = chip->ports[9].cmode;
cmode_port10 = chip->ports[10].cmode;
cmode_port = chip->ports[port].cmode;
switch (port) {
case 2:
if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
return MV88E6390_PORT9_LANE1;
return -ENODEV;
case 3:
if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
return MV88E6390_PORT9_LANE2;
return -ENODEV;
case 4:
if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
return MV88E6390_PORT9_LANE3;
return -ENODEV;
case 5:
if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
return MV88E6390_PORT10_LANE1;
return -ENODEV;
case 6:
if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
return MV88E6390_PORT10_LANE2;
return -ENODEV;
case 7:
if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
return MV88E6390_PORT10_LANE3;
return -ENODEV;
case 9:
if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
return MV88E6390_PORT9_LANE0;
return -ENODEV;
case 10:
if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
return MV88E6390_PORT10_LANE0;
return -ENODEV;
default:
return -ENODEV;
}
}
/* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on) static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane,
bool on)
{ {
u16 val, new_val; u16 val, new_val;
int reg_c45;
int err; int err;
reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE | err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_PCS_CONTROL_1; MV88E6390_PCS_CONTROL_1, &val);
err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val);
if (err) if (err)
return err; return err;
...@@ -195,22 +311,21 @@ static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on) ...@@ -195,22 +311,21 @@ static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on)
new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN; new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN;
if (val != new_val) if (val != new_val)
err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val); err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_PCS_CONTROL_1, new_val);
return err; return err;
} }
/* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ /* Set the power on/off for SGMII and 1000Base-X */
static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr, static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane,
bool on) bool on)
{ {
u16 val, new_val; u16 val, new_val;
int reg_c45;
int err; int err;
reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE | err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_SGMII_CONTROL; MV88E6390_SGMII_CONTROL, &val);
err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val);
if (err) if (err)
return err; return err;
...@@ -222,127 +337,261 @@ static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr, ...@@ -222,127 +337,261 @@ static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr,
new_val = val | MV88E6390_SGMII_CONTROL_PDOWN; new_val = val | MV88E6390_SGMII_CONTROL_PDOWN;
if (val != new_val) if (val != new_val)
err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val); err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
MV88E6390_SGMII_CONTROL, new_val);
return err; return err;
} }
static int mv88e6390_serdes_lower(struct mv88e6xxx_chip *chip, u8 cmode, static int mv88e6390_serdes_power_lane(struct mv88e6xxx_chip *chip, int port,
int port_donor, int lane, bool rxaui, bool on) int lane, bool on)
{ {
int err; u8 cmode = chip->ports[port].cmode;
u8 cmode_donor;
err = mv88e6xxx_port_get_cmode(chip, port_donor, &cmode_donor);
if (err)
return err;
switch (cmode_donor) { switch (cmode) {
case MV88E6XXX_PORT_STS_CMODE_RXAUI:
if (!rxaui)
break;
/* Fall through */
case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
case MV88E6XXX_PORT_STS_CMODE_SGMII: case MV88E6XXX_PORT_STS_CMODE_SGMII:
case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
case MV88E6XXX_PORT_STS_CMODE_2500BASEX: case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || return mv88e6390_serdes_power_sgmii(chip, lane, on);
cmode == MV88E6XXX_PORT_STS_CMODE_SGMII) case MV88E6XXX_PORT_STS_CMODE_XAUI:
return mv88e6390_serdes_sgmii(chip, lane, on); case MV88E6XXX_PORT_STS_CMODE_RXAUI:
return mv88e6390_serdes_power_10g(chip, lane, on);
}
return 0;
}
int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
{
int lane;
lane = mv88e6390_serdes_get_lane(chip, port);
if (lane == -ENODEV)
return 0;
if (lane < 0)
return lane;
switch (port) {
case 9 ... 10:
return mv88e6390_serdes_power_lane(chip, port, lane, on);
} }
return 0;
}
int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
{
int lane;
lane = mv88e6390x_serdes_get_lane(chip, port);
if (lane == -ENODEV)
return 0;
if (lane < 0)
return lane;
switch (port) {
case 2 ... 4:
case 5 ... 7:
case 9 ... 10:
return mv88e6390_serdes_power_lane(chip, port, lane, on);
}
return 0; return 0;
} }
static int mv88e6390_serdes_port9(struct mv88e6xxx_chip *chip, u8 cmode, static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
bool on) 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) { switch (cmode) {
case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
case MV88E6XXX_PORT_STS_CMODE_SGMII: case MV88E6XXX_PORT_STS_CMODE_SGMII:
return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT9_LANE0, on); case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
case MV88E6XXX_PORT_STS_CMODE_XAUI:
case MV88E6XXX_PORT_STS_CMODE_RXAUI:
case MV88E6XXX_PORT_STS_CMODE_2500BASEX: case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
return mv88e6390_serdes_10g(chip, MV88E6390_PORT9_LANE0, on); err = mv88e6390_serdes_irq_enable_sgmii(chip, lane);
} }
return 0; return err;
} }
static int mv88e6390_serdes_port10(struct mv88e6xxx_chip *chip, u8 cmode, int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port,
bool on) int lane)
{ {
u8 cmode = chip->ports[port].cmode;
int err = 0;
switch (cmode) { switch (cmode) {
case MV88E6XXX_PORT_STS_CMODE_SGMII: case MV88E6XXX_PORT_STS_CMODE_SGMII:
return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT10_LANE0, on);
case MV88E6XXX_PORT_STS_CMODE_XAUI:
case MV88E6XXX_PORT_STS_CMODE_RXAUI:
case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
case MV88E6XXX_PORT_STS_CMODE_2500BASEX: case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
return mv88e6390_serdes_10g(chip, MV88E6390_PORT10_LANE0, on); err = mv88e6390_serdes_irq_disable_sgmii(chip, lane);
} }
return 0; return err;
} }
int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip,
int lane, u16 *status)
{ {
u8 cmode;
int err; int err;
err = mv88e6xxx_port_get_cmode(chip, port, &cmode); err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
if (err) MV88E6390_SGMII_INT_STATUS, status);
return err;
switch (port) { return err;
case 2: }
return mv88e6390_serdes_lower(chip, cmode, 9,
MV88E6390_PORT9_LANE1, static irqreturn_t mv88e6390_serdes_thread_fn(int irq, void *dev_id)
false, on); {
case 3: struct mv88e6xxx_port *port = dev_id;
return mv88e6390_serdes_lower(chip, cmode, 9, struct mv88e6xxx_chip *chip = port->chip;
MV88E6390_PORT9_LANE2, irqreturn_t ret = IRQ_NONE;
true, on); u8 cmode = port->cmode;
case 4: u16 status;
return mv88e6390_serdes_lower(chip, cmode, 9, int lane;
MV88E6390_PORT9_LANE3, int err;
true, on);
case 5: lane = mv88e6390x_serdes_get_lane(chip, port->port);
return mv88e6390_serdes_lower(chip, cmode, 10,
MV88E6390_PORT10_LANE1, mutex_lock(&chip->reg_lock);
false, on);
case 6: switch (cmode) {
return mv88e6390_serdes_lower(chip, cmode, 10, case MV88E6XXX_PORT_STS_CMODE_SGMII:
MV88E6390_PORT10_LANE2, case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
true, on); case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
case 7: err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status);
return mv88e6390_serdes_lower(chip, cmode, 10, if (err)
MV88E6390_PORT10_LANE3, goto out;
true, on); if (status && (MV88E6390_SGMII_INT_LINK_DOWN ||
case 9: MV88E6390_SGMII_INT_LINK_UP)) {
return mv88e6390_serdes_port9(chip, cmode, on); ret = IRQ_HANDLED;
case 10: mv88e6390_serdes_irq_link_sgmii(chip, port->port, lane);
return mv88e6390_serdes_port10(chip, cmode, on); }
} }
out:
mutex_unlock(&chip->reg_lock);
return 0; return ret;
} }
int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port)
{ {
int lane;
int err; int err;
u8 cmode;
if (port != 5) /* Only support ports 9 and 10 at the moment */
if (port < 9)
return 0; return 0;
err = mv88e6xxx_port_get_cmode(chip, port, &cmode); lane = mv88e6390x_serdes_get_lane(chip, port);
if (err)
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 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);
chip->ports[port].serdes_irq = 0;
}
int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
{
u8 cmode = chip->ports[port].cmode;
if (port != 5)
return 0;
if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
return mv88e6390_serdes_sgmii(chip, MV88E6341_ADDR_SERDES, on); return mv88e6390_serdes_power_sgmii(chip, MV88E6341_ADDR_SERDES,
on);
return 0; return 0;
} }
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
#define MV88E6390_PORT10_LANE1 0x15 #define MV88E6390_PORT10_LANE1 0x15
#define MV88E6390_PORT10_LANE2 0x16 #define MV88E6390_PORT10_LANE2 0x16
#define MV88E6390_PORT10_LANE3 0x17 #define MV88E6390_PORT10_LANE3 0x17
#define MV88E6390_SERDES_DEVICE (4 << 16)
/* 10GBASE-R and 10GBASE-X4/X2 */ /* 10GBASE-R and 10GBASE-X4/X2 */
#define MV88E6390_PCS_CONTROL_1 0x1000 #define MV88E6390_PCS_CONTROL_1 0x1000
...@@ -43,13 +42,36 @@ ...@@ -43,13 +42,36 @@
#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 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port);
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 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);
int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
uint64_t *data); uint64_t *data);
int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port,
int lane);
int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port,
int lane);
#endif #endif
...@@ -1688,4 +1688,34 @@ static const struct sfp_upstream_ops sfp_phylink_ops = { ...@@ -1688,4 +1688,34 @@ static const struct sfp_upstream_ops sfp_phylink_ops = {
.disconnect_phy = phylink_sfp_disconnect_phy, .disconnect_phy = phylink_sfp_disconnect_phy,
}; };
/* Helpers for MAC drivers */
/**
* phylink_helper_basex_speed() - 1000BaseX/2500BaseX helper
* @state: a pointer to a &struct phylink_link_state
*
* Inspect the interface mode, advertising mask or forced speed and
* decide whether to run at 2.5Gbit or 1Gbit appropriately, switching
* the interface mode to suit. @state->interface is appropriately
* updated, and the advertising mask has the "other" baseX_Full flag
* cleared.
*/
void phylink_helper_basex_speed(struct phylink_link_state *state)
{
if (phy_interface_mode_is_8023z(state->interface)) {
bool want_2500 = state->an_enabled ?
phylink_test(state->advertising, 2500baseX_Full) :
state->speed == SPEED_2500;
if (want_2500) {
phylink_clear(state->advertising, 1000baseX_Full);
state->interface = PHY_INTERFACE_MODE_2500BASEX;
} else {
phylink_clear(state->advertising, 2500baseX_Full);
state->interface = PHY_INTERFACE_MODE_1000BASEX;
}
}
}
EXPORT_SYMBOL_GPL(phylink_helper_basex_speed);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -234,5 +234,6 @@ int phylink_mii_ioctl(struct phylink *, struct ifreq *, int); ...@@ -234,5 +234,6 @@ int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
#define phylink_test(bm, mode) __phylink_do_bit(test_bit, bm, mode) #define phylink_test(bm, mode) __phylink_do_bit(test_bit, bm, mode)
void phylink_set_port_modes(unsigned long *bits); void phylink_set_port_modes(unsigned long *bits);
void phylink_helper_basex_speed(struct phylink_link_state *state);
#endif #endif
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册