aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/realtek/files-5.10/drivers/net
diff options
context:
space:
mode:
authorBirger Koblitz <git@birger-koblitz.de>2021-09-08 16:13:18 +0200
committerJohn Crispin <john@phrozen.org>2021-10-09 08:25:06 +0200
commit9d9bf16aa8d966834ac1280f96c37d22552c33d1 (patch)
tree7f3f2ef9f6172e146e6bac18aafc998fd37792de /target/linux/realtek/files-5.10/drivers/net
parenta96b73a8902854b4e810c4c24e1f0c35326722a4 (diff)
downloadupstream-9d9bf16aa8d966834ac1280f96c37d22552c33d1.tar.gz
upstream-9d9bf16aa8d966834ac1280f96c37d22552c33d1.tar.bz2
upstream-9d9bf16aa8d966834ac1280f96c37d22552c33d1.zip
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>
Diffstat (limited to 'target/linux/realtek/files-5.10/drivers/net')
-rw-r--r--target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c260
-rw-r--r--target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h1
2 files changed, 241 insertions, 20 deletions
diff --git a/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c
index c79b4b2c81..3a0806e70a 100644
--- a/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c
+++ b/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c
@@ -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,
diff --git a/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h b/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h
index a3e169f288..e695879da1 100644
--- a/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h
+++ b/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h
@@ -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)