realtek: Add phylink configuration routines for RTL93xx

This adds RTL93xx-specific MAC configuration routines that allow also configuration
of 10GBit links for phylink. There is support for the Realtek-specific HISGMI
protocol.

Signed-off-by: Birger Koblitz <git@birger-koblitz.de>
This commit is contained in:
Birger Koblitz 2021-09-08 16:13:18 +02:00 committed by John Crispin
parent a96b73a890
commit 9d9bf16aa8
3 changed files with 260 additions and 20 deletions

View File

@ -280,7 +280,7 @@ static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port,
phylink_set(mask, 1000baseX_Full);
/* On the RTL839x family of SoCs, ports 48 to 51 are SFP ports */
if (port >=48 && port <= 51 && priv->family_id == RTL8390_FAMILY_ID)
if (port >= 48 && port <= 51 && priv->family_id == RTL8390_FAMILY_ID)
phylink_set(mask, 1000baseX_Full);
phylink_set(mask, 10baseT_Half);
@ -294,6 +294,67 @@ static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port,
__ETHTOOL_LINK_MODE_MASK_NBITS);
}
static void rtl93xx_phylink_validate(struct dsa_switch *ds, int port,
unsigned long *supported,
struct phylink_link_state *state)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
pr_debug("In %s port %d, state is %d (%s)", __func__, port, state->interface,
phy_modes(state->interface));
if (!phy_interface_mode_is_rgmii(state->interface) &&
state->interface != PHY_INTERFACE_MODE_NA &&
state->interface != PHY_INTERFACE_MODE_1000BASEX &&
state->interface != PHY_INTERFACE_MODE_MII &&
state->interface != PHY_INTERFACE_MODE_REVMII &&
state->interface != PHY_INTERFACE_MODE_GMII &&
state->interface != PHY_INTERFACE_MODE_QSGMII &&
state->interface != PHY_INTERFACE_MODE_XGMII &&
state->interface != PHY_INTERFACE_MODE_HSGMII &&
state->interface != PHY_INTERFACE_MODE_10GKR &&
state->interface != PHY_INTERFACE_MODE_USXGMII &&
state->interface != PHY_INTERFACE_MODE_INTERNAL &&
state->interface != PHY_INTERFACE_MODE_SGMII) {
bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
dev_err(ds->dev,
"Unsupported interface: %d for port %d\n",
state->interface, port);
return;
}
/* Allow all the expected bits */
phylink_set(mask, Autoneg);
phylink_set_port_modes(mask);
phylink_set(mask, Pause);
phylink_set(mask, Asym_Pause);
/* With the exclusion of MII and Reverse MII, we support Gigabit,
* including Half duplex
*/
if (state->interface != PHY_INTERFACE_MODE_MII &&
state->interface != PHY_INTERFACE_MODE_REVMII) {
phylink_set(mask, 1000baseT_Full);
phylink_set(mask, 1000baseT_Half);
}
/* On the RTL9300 family of SoCs, ports 26 to 27 may be SFP ports TODO: take out of .dts */
if (port >= 26 && port <= 27)
phylink_set(mask, 1000baseX_Full);
if (port >= 26 && port <= 27)
phylink_set(mask, 10000baseKR_Full);
phylink_set(mask, 10baseT_Half);
phylink_set(mask, 10baseT_Full);
phylink_set(mask, 100baseT_Half);
phylink_set(mask, 100baseT_Full);
bitmap_and(supported, supported, mask,
__ETHTOOL_LINK_MODE_MASK_NBITS);
bitmap_and(state->advertising, state->advertising, mask,
__ETHTOOL_LINK_MODE_MASK_NBITS);
}
static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
struct phylink_link_state *state)
{
@ -304,15 +365,8 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
if (port < 0 || port > priv->cpu_port)
return -EINVAL;
/*
* On the RTL9300 for at least the RTL8226B PHY, the MAC-side link
* state needs to be read twice in order to read a correct result.
* This would not be necessary for ports connected e.g. to RTL8218D
* PHYs.
*/
state->link = 0;
link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
if (link & BIT_ULL(port))
state->link = 1;
pr_debug("%s: link state port %d: %llx\n", __func__, port, link & BIT_ULL(port));
@ -349,6 +403,70 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
return 1;
}
static int rtl93xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
struct phylink_link_state *state)
{
struct rtl838x_switch_priv *priv = ds->priv;
u64 speed;
u64 link;
if (port < 0 || port > priv->cpu_port)
return -EINVAL;
/*
* On the RTL9300 for at least the RTL8226B PHY, the MAC-side link
* state needs to be read twice in order to read a correct result.
* This would not be necessary for ports connected e.g. to RTL8218D
* PHYs.
*/
state->link = 0;
link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
if (link & BIT_ULL(port))
state->link = 1;
pr_debug("%s: link state port %d: %llx, media %08x\n", __func__, port,
link & BIT_ULL(port), sw_r32(RTL930X_MAC_LINK_MEDIA_STS));
state->duplex = 0;
if (priv->r->get_port_reg_le(priv->r->mac_link_dup_sts) & BIT_ULL(port))
state->duplex = 1;
speed = priv->r->get_port_reg_le(priv->r->mac_link_spd_sts(port));
speed >>= (port % 8) << 2;
switch (speed & 0xf) {
case 0:
state->speed = SPEED_10;
break;
case 1:
state->speed = SPEED_100;
break;
case 2:
case 7:
state->speed = SPEED_1000;
break;
case 4:
state->speed = SPEED_10000;
break;
case 5:
case 8:
state->speed = SPEED_2500;
break;
case 6:
state->speed = SPEED_5000;
break;
default:
pr_err("%s: unknown speed: %d\n", __func__, (u32)speed & 0xf);
}
pr_debug("%s: speed is: %d %d\n", __func__, (u32)speed & 0xf, state->speed);
state->pause &= (MLO_PAUSE_RX | MLO_PAUSE_TX);
if (priv->r->get_port_reg_le(priv->r->mac_rx_pause_sts) & BIT_ULL(port))
state->pause |= MLO_PAUSE_RX;
if (priv->r->get_port_reg_le(priv->r->mac_tx_pause_sts) & BIT_ULL(port))
state->pause |= MLO_PAUSE_TX;
return 1;
}
static void rtl83xx_config_interface(int port, phy_interface_t interface)
{
u32 old, int_shift, sds_shift;
@ -396,10 +514,6 @@ static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port,
pr_debug("%s port %d, mode %x\n", __func__, port, mode);
// BUG: Make this work on RTL93XX
if (priv->family_id >= RTL9300_FAMILY_ID)
return;
if (port == priv->cpu_port) {
/* Set Speed, duplex, flow control
* FORCE_EN | LINK_EN | NWAY_EN | DUP_SEL
@ -468,6 +582,81 @@ static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port,
sw_w32(reg, priv->r->mac_force_mode_ctrl(port));
}
static void rtl93xx_phylink_mac_config(struct dsa_switch *ds, int port,
unsigned int mode,
const struct phylink_link_state *state)
{
struct rtl838x_switch_priv *priv = ds->priv;
int sds_num, sds_mode;
u32 reg;
pr_debug("%s port %d, mode %x, phy-mode: %s, speed %d, link %d\n", __func__,
port, mode, phy_modes(state->interface), state->speed, state->link);
// Nothing to be done for the CPU-port
if (port == priv->cpu_port)
return;
reg = sw_r32(priv->r->mac_force_mode_ctrl(port));
reg &= ~(0xf << 3);
// On the RTL930X, ports 24 to 27 are using an internal SerDes
if (port >=24 && port <= 27) {
sds_num = port - 18; // Port 24 mapped to SerDes 6, 25 to 7 ...
switch (state->interface) {
case PHY_INTERFACE_MODE_HSGMII:
sds_mode = 0x12;
break;
case PHY_INTERFACE_MODE_1000BASEX:
sds_mode = 0x1b; // 10G 1000X Auto
break;
case PHY_INTERFACE_MODE_XGMII:
sds_mode = 0x10;
break;
case PHY_INTERFACE_MODE_10GKR:
sds_mode = 0x1a;
// We need to use media sel for fibre media:
reg |= BIT(16);
break;
case PHY_INTERFACE_MODE_USXGMII:
sds_mode = 0x0d;
break;
default:
pr_err("%s: unknown serdes mode: %s\n",
__func__, phy_modes(state->interface));
return;
}
rtl9300_sds_rst(sds_num, sds_mode);
}
switch (state->speed) {
case SPEED_10000:
reg |= 4 << 3;
break;
case SPEED_5000:
reg |= 6 << 3;
break;
case SPEED_2500:
reg |= 5 << 3;
break;
case SPEED_1000:
reg |= 2 << 3;
break;
default:
reg |= 2 << 3;
break;
}
if (state->link)
reg |= FORCE_LINK_EN;
if (state->duplex == DUPLEX_FULL)
reg |= BIT(2);
reg |= 1; // Force Link up
sw_w32(reg, priv->r->mac_force_mode_ctrl(port));
}
static void rtl83xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface)
@ -477,6 +666,18 @@ static void rtl83xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(port));
}
static void rtl93xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface)
{
struct rtl838x_switch_priv *priv = ds->priv;
/* Stop TX/RX to port */
sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(port));
// No longer force link
sw_w32_mask(3, 0, priv->r->mac_force_mode_ctrl(port));
}
static void rtl83xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface,
@ -487,6 +688,21 @@ static void rtl83xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
struct rtl838x_switch_priv *priv = ds->priv;
/* Restart TX/RX to port */
sw_w32_mask(0, 0x3, priv->r->mac_port_ctrl(port));
// TODO: Set speed/duplex/pauses
}
static void rtl93xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
unsigned int mode,
phy_interface_t interface,
struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
struct rtl838x_switch_priv *priv = ds->priv;
/* Restart TX/RX to port */
sw_w32_mask(0, 0x3, priv->r->mac_port_ctrl(port));
// TODO: Set speed/duplex/pauses
}
static void rtl83xx_get_strings(struct dsa_switch *ds,
@ -1183,7 +1399,10 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port,
if (!e.valid)
continue;
if (e.port == port) {
if (e.port == port || e.port == RTL930X_PORT_IGNORE) {
u64 seed;
u32 key;
fid = ((i >> 2) & 0x3ff) | (e.rvid & ~0x3ff);
mac = ether_addr_to_u64(&e.mac[0]);
pkey = priv->r->l2_hash_key(priv, priv->r->l2_hash_seed(mac, fid));
@ -1191,13 +1410,14 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port,
pr_info("-> index %d, key %x, bucket %d, dmac %016llx, fid: %x rvid: %x\n",
i, i >> 2, i & 0x3, mac, fid, e.rvid);
dump_l2_entry(&e);
u64 seed = priv->r->l2_hash_seed(mac, e.rvid);
u32 key = priv->r->l2_hash_key(priv, seed);
seed = priv->r->l2_hash_seed(mac, e.rvid);
key = priv->r->l2_hash_key(priv, seed);
pr_info("seed: %016llx, key based on rvid: %08x\n", seed, key);
cb(e.mac, e.vid, e.is_static, data);
}
if (e.type == L2_MULTICAST) {
u64 portmask = priv->r->read_mcast_pmask(e.mc_portmask_index);
if (portmask & BIT_ULL(port)) {
dump_l2_entry(&e);
pr_info(" PM: %016llx\n", portmask);
@ -1558,11 +1778,11 @@ const struct dsa_switch_ops rtl930x_switch_ops = {
.phy_read = dsa_phy_read,
.phy_write = dsa_phy_write,
.phylink_validate = rtl83xx_phylink_validate,
.phylink_mac_link_state = rtl83xx_phylink_mac_link_state,
.phylink_mac_config = rtl83xx_phylink_mac_config,
.phylink_mac_link_down = rtl83xx_phylink_mac_link_down,
.phylink_mac_link_up = rtl83xx_phylink_mac_link_up,
.phylink_validate = rtl93xx_phylink_validate,
.phylink_mac_link_state = rtl93xx_phylink_mac_link_state,
.phylink_mac_config = rtl93xx_phylink_mac_config,
.phylink_mac_link_down = rtl93xx_phylink_mac_link_down,
.phylink_mac_link_up = rtl93xx_phylink_mac_link_up,
.get_strings = rtl83xx_get_strings,
.get_ethtool_stats = rtl83xx_get_ethtool_stats,

View File

@ -145,6 +145,7 @@
#define RTL839X_MAC_RX_PAUSE_STS (0x03c0)
#define RTL930X_MAC_RX_PAUSE_STS (0xCB30)
#define RTL931X_MAC_RX_PAUSE_STS (0x0F00)
#define RTL930X_MAC_LINK_MEDIA_STS (0xCB14)
/* MAC link state bits */
#define FORCE_EN (1 << 0)

View File

@ -0,0 +1,19 @@
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -93,6 +93,7 @@
PHY_INTERFACE_MODE_SMII,
PHY_INTERFACE_MODE_XGMII,
PHY_INTERFACE_MODE_MOCA,
+ PHY_INTERFACE_MODE_HSGMII,
PHY_INTERFACE_MODE_QSGMII,
PHY_INTERFACE_MODE_TRGMII,
PHY_INTERFACE_MODE_1000BASEX,
@@ -163,6 +164,8 @@
return "xgmii";
case PHY_INTERFACE_MODE_MOCA:
return "moca";
+ case PHY_INTERFACE_MODE_HSGMII:
+ return "hsgmii";
case PHY_INTERFACE_MODE_QSGMII:
return "qsgmii";
case PHY_INTERFACE_MODE_TRGMII: