mirror of
https://github.com/openwrt/openwrt.git
synced 2025-03-11 06:54:21 +00:00
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:
parent
a96b73a890
commit
9d9bf16aa8
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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:
|
Loading…
x
Reference in New Issue
Block a user