summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/mscc/ocelot.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/mscc/ocelot.c')
-rw-r--r--drivers/net/ethernet/mscc/ocelot.c193
1 files changed, 130 insertions, 63 deletions
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index 46e5c9136bac..adfb9781799e 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -6,6 +6,7 @@
*/
#include <linux/dsa/ocelot.h>
#include <linux/if_bridge.h>
+#include <linux/ptp_classify.h>
#include <soc/mscc/ocelot_vcap.h>
#include "ocelot.h"
#include "ocelot_vcap.h"
@@ -378,6 +379,7 @@ static u32 ocelot_read_eq_avail(struct ocelot *ocelot, int port)
int ocelot_port_flush(struct ocelot *ocelot, int port)
{
+ unsigned int pause_ena;
int err, val;
/* Disable dequeuing from the egress queues */
@@ -386,6 +388,7 @@ int ocelot_port_flush(struct ocelot *ocelot, int port)
QSYS_PORT_MODE, port);
/* Disable flow control */
+ ocelot_fields_read(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, &pause_ena);
ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 0);
/* Disable priority flow control */
@@ -421,6 +424,9 @@ int ocelot_port_flush(struct ocelot *ocelot, int port)
/* Clear flushing again. */
ocelot_rmw_gix(ocelot, 0, REW_PORT_CFG_FLUSH_ENA, REW_PORT_CFG, port);
+ /* Re-enable flow control */
+ ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, pause_ena);
+
return err;
}
EXPORT_SYMBOL(ocelot_port_flush);
@@ -484,7 +490,8 @@ void ocelot_adjust_link(struct ocelot *ocelot, int port,
DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG);
/* Take MAC, Port, Phy (intern) and PCS (SGMII/Serdes) clock out of
- * reset */
+ * reset
+ */
ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(speed),
DEV_CLOCK_CFG);
@@ -529,22 +536,92 @@ void ocelot_port_disable(struct ocelot *ocelot, int port)
}
EXPORT_SYMBOL(ocelot_port_disable);
-void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port,
- struct sk_buff *clone)
+static void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port,
+ struct sk_buff *clone)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
spin_lock(&ocelot_port->ts_id_lock);
skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS;
- /* Store timestamp ID in cb[0] of sk_buff */
- clone->cb[0] = ocelot_port->ts_id;
+ /* Store timestamp ID in OCELOT_SKB_CB(clone)->ts_id */
+ OCELOT_SKB_CB(clone)->ts_id = ocelot_port->ts_id;
ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
skb_queue_tail(&ocelot_port->tx_skbs, clone);
spin_unlock(&ocelot_port->ts_id_lock);
}
-EXPORT_SYMBOL(ocelot_port_add_txtstamp_skb);
+
+u32 ocelot_ptp_rew_op(struct sk_buff *skb)
+{
+ struct sk_buff *clone = OCELOT_SKB_CB(skb)->clone;
+ u8 ptp_cmd = OCELOT_SKB_CB(skb)->ptp_cmd;
+ u32 rew_op = 0;
+
+ if (ptp_cmd == IFH_REW_OP_TWO_STEP_PTP && clone) {
+ rew_op = ptp_cmd;
+ rew_op |= OCELOT_SKB_CB(clone)->ts_id << 3;
+ } else if (ptp_cmd == IFH_REW_OP_ORIGIN_PTP) {
+ rew_op = ptp_cmd;
+ }
+
+ return rew_op;
+}
+EXPORT_SYMBOL(ocelot_ptp_rew_op);
+
+static bool ocelot_ptp_is_onestep_sync(struct sk_buff *skb)
+{
+ struct ptp_header *hdr;
+ unsigned int ptp_class;
+ u8 msgtype, twostep;
+
+ ptp_class = ptp_classify_raw(skb);
+ if (ptp_class == PTP_CLASS_NONE)
+ return false;
+
+ hdr = ptp_parse_header(skb, ptp_class);
+ if (!hdr)
+ return false;
+
+ msgtype = ptp_get_msgtype(hdr, ptp_class);
+ twostep = hdr->flag_field[0] & 0x2;
+
+ if (msgtype == PTP_MSGTYPE_SYNC && twostep == 0)
+ return true;
+
+ return false;
+}
+
+int ocelot_port_txtstamp_request(struct ocelot *ocelot, int port,
+ struct sk_buff *skb,
+ struct sk_buff **clone)
+{
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
+ u8 ptp_cmd = ocelot_port->ptp_cmd;
+
+ /* Store ptp_cmd in OCELOT_SKB_CB(skb)->ptp_cmd */
+ if (ptp_cmd == IFH_REW_OP_ORIGIN_PTP) {
+ if (ocelot_ptp_is_onestep_sync(skb)) {
+ OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd;
+ return 0;
+ }
+
+ /* Fall back to two-step timestamping */
+ ptp_cmd = IFH_REW_OP_TWO_STEP_PTP;
+ }
+
+ if (ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
+ *clone = skb_clone_sk(skb);
+ if (!(*clone))
+ return -ENOMEM;
+
+ ocelot_port_add_txtstamp_skb(ocelot, port, *clone);
+ OCELOT_SKB_CB(skb)->ptp_cmd = ptp_cmd;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ocelot_port_txtstamp_request);
static void ocelot_get_hwtimestamp(struct ocelot *ocelot,
struct timespec64 *ts)
@@ -603,7 +680,7 @@ void ocelot_get_txtstamp(struct ocelot *ocelot)
spin_lock_irqsave(&port->tx_skbs.lock, flags);
skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) {
- if (skb->cb[0] != id)
+ if (OCELOT_SKB_CB(skb)->ts_id != id)
continue;
__skb_unlink(skb, &port->tx_skbs);
skb_match = skb;
@@ -687,7 +764,7 @@ static int ocelot_xtr_poll_xfh(struct ocelot *ocelot, int grp, u32 *xfh)
int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
{
struct skb_shared_hwtstamps *shhwtstamps;
- u64 tod_in_ns, full_ts_in_ns, cpuq;
+ u64 tod_in_ns, full_ts_in_ns;
u64 timestamp, src_port, len;
u32 xfh[OCELOT_TAG_LEN / 4];
struct net_device *dev;
@@ -704,7 +781,6 @@ int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
ocelot_xfh_get_src_port(xfh, &src_port);
ocelot_xfh_get_len(xfh, &len);
ocelot_xfh_get_rew_val(xfh, &timestamp);
- ocelot_xfh_get_cpuq(xfh, &cpuq);
if (WARN_ON(src_port >= ocelot->num_phys_ports))
return -EINVAL;
@@ -767,17 +843,11 @@ int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
/* Everything we see on an interface that is in the HW bridge
* has already been forwarded.
*/
- if (ocelot->bridge_mask & BIT(src_port))
+ if (ocelot->ports[src_port]->bridge)
skb->offload_fwd_mark = 1;
skb->protocol = eth_type_trans(skb, dev);
-#if IS_ENABLED(CONFIG_BRIDGE_MRP)
- if (skb->protocol == cpu_to_be16(ETH_P_MRP) &&
- cpuq & BIT(OCELOT_MRP_CPUQ))
- skb->offload_fwd_mark = 0;
-#endif
-
*nskb = skb;
return 0;
@@ -1190,6 +1260,26 @@ static u32 ocelot_get_bond_mask(struct ocelot *ocelot, struct net_device *bond,
return mask;
}
+static u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot,
+ struct net_device *bridge)
+{
+ u32 mask = 0;
+ int port;
+
+ for (port = 0; port < ocelot->num_phys_ports; port++) {
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
+
+ if (!ocelot_port)
+ continue;
+
+ if (ocelot_port->stp_state == BR_STATE_FORWARDING &&
+ ocelot_port->bridge == bridge)
+ mask |= BIT(port);
+ }
+
+ return mask;
+}
+
static u32 ocelot_get_dsa_8021q_cpu_mask(struct ocelot *ocelot)
{
u32 mask = 0;
@@ -1239,10 +1329,12 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot)
*/
mask = GENMASK(ocelot->num_phys_ports - 1, 0);
mask &= ~cpu_fwd_mask;
- } else if (ocelot->bridge_fwd_mask & BIT(port)) {
+ } else if (ocelot_port->bridge) {
+ struct net_device *bridge = ocelot_port->bridge;
struct net_device *bond = ocelot_port->bond;
- mask = ocelot->bridge_fwd_mask & ~BIT(port);
+ mask = ocelot_get_bridge_fwd_mask(ocelot, bridge);
+ mask &= ~BIT(port);
if (bond) {
mask &= ~ocelot_get_bond_mask(ocelot, bond,
false);
@@ -1263,29 +1355,16 @@ EXPORT_SYMBOL(ocelot_apply_bridge_fwd_mask);
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
- u32 port_cfg;
+ u32 learn_ena = 0;
- if (!(BIT(port) & ocelot->bridge_mask))
- return;
+ ocelot_port->stp_state = state;
- port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, port);
+ if ((state == BR_STATE_LEARNING || state == BR_STATE_FORWARDING) &&
+ ocelot_port->learn_ena)
+ learn_ena = ANA_PORT_PORT_CFG_LEARN_ENA;
- switch (state) {
- case BR_STATE_FORWARDING:
- ocelot->bridge_fwd_mask |= BIT(port);
- fallthrough;
- case BR_STATE_LEARNING:
- if (ocelot_port->learn_ena)
- port_cfg |= ANA_PORT_PORT_CFG_LEARN_ENA;
- break;
-
- default:
- port_cfg &= ~ANA_PORT_PORT_CFG_LEARN_ENA;
- ocelot->bridge_fwd_mask &= ~BIT(port);
- break;
- }
-
- ocelot_write_gix(ocelot, port_cfg, ANA_PORT_PORT_CFG, port);
+ ocelot_rmw_gix(ocelot, learn_ena, ANA_PORT_PORT_CFG_LEARN_ENA,
+ ANA_PORT_PORT_CFG, port);
ocelot_apply_bridge_fwd_mask(ocelot);
}
@@ -1512,43 +1591,28 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
}
EXPORT_SYMBOL(ocelot_port_mdb_del);
-int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
- struct net_device *bridge)
+void ocelot_port_bridge_join(struct ocelot *ocelot, int port,
+ struct net_device *bridge)
{
- if (!ocelot->bridge_mask) {
- ocelot->hw_bridge_dev = bridge;
- } else {
- if (ocelot->hw_bridge_dev != bridge)
- /* This is adding the port to a second bridge, this is
- * unsupported */
- return -ENODEV;
- }
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
- ocelot->bridge_mask |= BIT(port);
+ ocelot_port->bridge = bridge;
- return 0;
+ ocelot_apply_bridge_fwd_mask(ocelot);
}
EXPORT_SYMBOL(ocelot_port_bridge_join);
-int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
- struct net_device *bridge)
+void ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
+ struct net_device *bridge)
{
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
struct ocelot_vlan pvid = {0}, native_vlan = {0};
- int ret;
- ocelot->bridge_mask &= ~BIT(port);
-
- if (!ocelot->bridge_mask)
- ocelot->hw_bridge_dev = NULL;
-
- ret = ocelot_port_vlan_filtering(ocelot, port, false);
- if (ret)
- return ret;
+ ocelot_port->bridge = NULL;
ocelot_port_set_pvid(ocelot, port, pvid);
ocelot_port_set_native_vlan(ocelot, port, native_vlan);
-
- return 0;
+ ocelot_apply_bridge_fwd_mask(ocelot);
}
EXPORT_SYMBOL(ocelot_port_bridge_leave);
@@ -2051,6 +2115,9 @@ int ocelot_init(struct ocelot *ocelot)
ocelot_write_rix(ocelot, val, ANA_PGID_PGID, i);
}
+
+ ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_BLACKHOLE);
+
/* Allow broadcast and unknown L2 multicast to the CPU. */
ocelot_rmw_rix(ocelot, ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)),
ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)),