Commit fe2b8a88 authored by David S. Miller's avatar David S. Miller

Merge branch 'Accomodate-DSA-front-end-into-Ocelot'

Vladimir Oltean says:

====================
Accomodate DSA front-end into Ocelot

After the nice "change-my-mind" discussion about Ocelot, Felix and
LS1028A (which can be read here: https://lkml.org/lkml/2019/6/21/630),
we have decided to take the route of reworking the Ocelot implementation
in a way that is DSA-compatible.

This is a large series, but hopefully is easy enough to digest, since it
contains mostly code refactoring. What needs to be changed:
- The struct net_device, phy_device needs to be isolated from Ocelot
  private structures (struct ocelot, struct ocelot_port). These will
  live as 1-to-1 equivalents to struct dsa_switch and struct dsa_port.
- The function prototypes need to be compatible with DSA (of course,
  struct dsa_switch will become struct ocelot).
- The CPU port needs to be assigned via a higher-level API, not
  hardcoded in the driver.

What is going to be interesting is that the new DSA front-end of Ocelot
will need to have features in lockstep with the DSA core itself. At the
moment, some more advanced tc offloading features of Ocelot (tc-flower,
etc) are not available in the DSA front-end due to lack of API in the
DSA core. It also means that Ocelot practically re-implements large
parts of DSA (although it is not a DSA switch per se) - see the FDB API
for example.

The code has been only compile-tested on Ocelot, since I don't have
access to any VSC7514 hardware. It was proven to work on NXP LS1028A,
which instantiates a DSA derivative of Ocelot. So I would like to ask
Alex Belloni if you could confirm this series causes no regression on
the Ocelot MIPS SoC.

The goal is to get this rework upstream as quickly as possible,
precisely because it is a large volume of code that risks gaining merge
conflicts if we keep it for too long.

This is but the first chunk of the LS1028A Felix DSA driver upstreaming.
For those who are interested, the concept can be seen on my private
Github repo, the user of this reworked Ocelot driver living under
drivers/net/dsa/vitesse/:
https://github.com/vladimiroltean/ls1028ardb-linux
====================
Acked-by: default avatarHoratiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c82488df c9d2203b
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <net/netevent.h> #include <net/netevent.h>
#include <net/rtnetlink.h> #include <net/rtnetlink.h>
#include <net/switchdev.h> #include <net/switchdev.h>
#include <net/dsa.h>
#include "ocelot.h" #include "ocelot.h"
#include "ocelot_ace.h" #include "ocelot_ace.h"
...@@ -132,11 +133,11 @@ static void ocelot_mact_init(struct ocelot *ocelot) ...@@ -132,11 +133,11 @@ static void ocelot_mact_init(struct ocelot *ocelot)
ocelot_write(ocelot, MACACCESS_CMD_INIT, ANA_TABLES_MACACCESS); ocelot_write(ocelot, MACACCESS_CMD_INIT, ANA_TABLES_MACACCESS);
} }
static void ocelot_vcap_enable(struct ocelot *ocelot, struct ocelot_port *port) static void ocelot_vcap_enable(struct ocelot *ocelot, int port)
{ {
ocelot_write_gix(ocelot, ANA_PORT_VCAP_S2_CFG_S2_ENA | ocelot_write_gix(ocelot, ANA_PORT_VCAP_S2_CFG_S2_ENA |
ANA_PORT_VCAP_S2_CFG_S2_IP6_CFG(0xa), ANA_PORT_VCAP_S2_CFG_S2_IP6_CFG(0xa),
ANA_PORT_VCAP_S2_CFG, port->chip_port); ANA_PORT_VCAP_S2_CFG, port);
} }
static inline u32 ocelot_vlant_read_vlanaccess(struct ocelot *ocelot) static inline u32 ocelot_vlant_read_vlanaccess(struct ocelot *ocelot)
...@@ -169,144 +170,190 @@ static int ocelot_vlant_set_mask(struct ocelot *ocelot, u16 vid, u32 mask) ...@@ -169,144 +170,190 @@ static int ocelot_vlant_set_mask(struct ocelot *ocelot, u16 vid, u32 mask)
return ocelot_vlant_wait_for_completion(ocelot); return ocelot_vlant_wait_for_completion(ocelot);
} }
static void ocelot_vlan_mode(struct ocelot_port *port, static void ocelot_vlan_mode(struct ocelot *ocelot, int port,
netdev_features_t features) netdev_features_t features)
{ {
struct ocelot *ocelot = port->ocelot;
u8 p = port->chip_port;
u32 val; u32 val;
/* Filtering */ /* Filtering */
val = ocelot_read(ocelot, ANA_VLANMASK); val = ocelot_read(ocelot, ANA_VLANMASK);
if (features & NETIF_F_HW_VLAN_CTAG_FILTER) if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
val |= BIT(p); val |= BIT(port);
else else
val &= ~BIT(p); val &= ~BIT(port);
ocelot_write(ocelot, val, ANA_VLANMASK); ocelot_write(ocelot, val, ANA_VLANMASK);
} }
static void ocelot_vlan_port_apply(struct ocelot *ocelot, static void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
struct ocelot_port *port) bool vlan_aware)
{ {
struct ocelot_port *ocelot_port = ocelot->ports[port];
u32 val; u32 val;
/* Ingress clasification (ANA_PORT_VLAN_CFG) */ if (vlan_aware)
/* Default vlan to clasify for untagged frames (may be zero) */ val = ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
val = ANA_PORT_VLAN_CFG_VLAN_VID(port->pvid);
if (port->vlan_aware)
val |= ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1); ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1);
else
val = 0;
ocelot_rmw_gix(ocelot, val, ocelot_rmw_gix(ocelot, val,
ANA_PORT_VLAN_CFG_VLAN_VID_M |
ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA | ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M, ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M,
ANA_PORT_VLAN_CFG, port->chip_port); ANA_PORT_VLAN_CFG, port);
/* Drop frames with multicast source address */ if (vlan_aware && !ocelot_port->vid)
val = ANA_PORT_DROP_CFG_DROP_MC_SMAC_ENA;
if (port->vlan_aware && !port->vid)
/* If port is vlan-aware and tagged, drop untagged and priority /* If port is vlan-aware and tagged, drop untagged and priority
* tagged frames. * tagged frames.
*/ */
val |= ANA_PORT_DROP_CFG_DROP_UNTAGGED_ENA | val = ANA_PORT_DROP_CFG_DROP_UNTAGGED_ENA |
ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA | ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA; ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA;
ocelot_write_gix(ocelot, val, ANA_PORT_DROP_CFG, port->chip_port); else
val = 0;
/* Egress configuration (REW_TAG_CFG): VLAN tag type to 8021Q. */ ocelot_rmw_gix(ocelot, val,
val = REW_TAG_CFG_TAG_TPID_CFG(0); ANA_PORT_DROP_CFG_DROP_UNTAGGED_ENA |
ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA,
ANA_PORT_DROP_CFG, port);
if (port->vlan_aware) { if (vlan_aware) {
if (port->vid) if (ocelot_port->vid)
/* Tag all frames except when VID == DEFAULT_VLAN */ /* Tag all frames except when VID == DEFAULT_VLAN */
val |= REW_TAG_CFG_TAG_CFG(1); val |= REW_TAG_CFG_TAG_CFG(1);
else else
/* Tag all frames */ /* Tag all frames */
val |= REW_TAG_CFG_TAG_CFG(3); val |= REW_TAG_CFG_TAG_CFG(3);
} else {
/* Port tagging disabled. */
val = REW_TAG_CFG_TAG_CFG(0);
} }
ocelot_rmw_gix(ocelot, val, ocelot_rmw_gix(ocelot, val,
REW_TAG_CFG_TAG_TPID_CFG_M |
REW_TAG_CFG_TAG_CFG_M, REW_TAG_CFG_TAG_CFG_M,
REW_TAG_CFG, port->chip_port); REW_TAG_CFG, port);
}
/* Set default VLAN and tag type to 8021Q. */ static int ocelot_port_set_native_vlan(struct ocelot *ocelot, int port,
val = REW_PORT_VLAN_CFG_PORT_TPID(ETH_P_8021Q) | u16 vid)
REW_PORT_VLAN_CFG_PORT_VID(port->vid); {
ocelot_rmw_gix(ocelot, val, struct ocelot_port *ocelot_port = ocelot->ports[port];
REW_PORT_VLAN_CFG_PORT_TPID_M |
if (ocelot_port->vid != vid) {
/* Always permit deleting the native VLAN (vid = 0) */
if (ocelot_port->vid && vid) {
dev_err(ocelot->dev,
"Port already has a native VLAN: %d\n",
ocelot_port->vid);
return -EBUSY;
}
ocelot_port->vid = vid;
}
ocelot_rmw_gix(ocelot, REW_PORT_VLAN_CFG_PORT_VID(vid),
REW_PORT_VLAN_CFG_PORT_VID_M, REW_PORT_VLAN_CFG_PORT_VID_M,
REW_PORT_VLAN_CFG, port->chip_port); REW_PORT_VLAN_CFG, port);
return 0;
} }
static int ocelot_vlan_vid_add(struct net_device *dev, u16 vid, bool pvid, /* Default vlan to clasify for untagged frames (may be zero) */
static void ocelot_port_set_pvid(struct ocelot *ocelot, int port, u16 pvid)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
ocelot_rmw_gix(ocelot,
ANA_PORT_VLAN_CFG_VLAN_VID(pvid),
ANA_PORT_VLAN_CFG_VLAN_VID_M,
ANA_PORT_VLAN_CFG, port);
ocelot_port->pvid = pvid;
}
static int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
bool untagged) bool untagged)
{ {
struct ocelot_port *port = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot;
int ret; int ret;
/* Add the port MAC address to with the right VLAN information */
ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, vid,
ENTRYTYPE_LOCKED);
/* Make the port a member of the VLAN */ /* Make the port a member of the VLAN */
ocelot->vlan_mask[vid] |= BIT(port->chip_port); ocelot->vlan_mask[vid] |= BIT(port);
ret = ocelot_vlant_set_mask(ocelot, vid, ocelot->vlan_mask[vid]); ret = ocelot_vlant_set_mask(ocelot, vid, ocelot->vlan_mask[vid]);
if (ret) if (ret)
return ret; return ret;
/* Default ingress vlan classification */ /* Default ingress vlan classification */
if (pvid) if (pvid)
port->pvid = vid; ocelot_port_set_pvid(ocelot, port, vid);
/* Untagged egress vlan clasification */ /* Untagged egress vlan clasification */
if (untagged && port->vid != vid) { if (untagged) {
if (port->vid) { ret = ocelot_port_set_native_vlan(ocelot, port, vid);
dev_err(ocelot->dev, if (ret)
"Port already has a native VLAN: %d\n", return ret;
port->vid);
return -EBUSY;
}
port->vid = vid;
} }
ocelot_vlan_port_apply(ocelot, port);
return 0; return 0;
} }
static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid) static int ocelot_vlan_vid_add(struct net_device *dev, u16 vid, bool pvid,
bool untagged)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot; struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;
int ret; int ret;
/* 8021q removes VID 0 on module unload for all interfaces ret = ocelot_vlan_add(ocelot, port, vid, pvid, untagged);
* with VLAN filtering feature. We need to keep it to receive if (ret)
* untagged traffic. return ret;
*/
if (vid == 0) /* Add the port MAC address to with the right VLAN information */
ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, vid,
ENTRYTYPE_LOCKED);
return 0; return 0;
}
/* Del the port MAC address to with the right VLAN information */ static int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid)
ocelot_mact_forget(ocelot, dev->dev_addr, vid); {
struct ocelot_port *ocelot_port = ocelot->ports[port];
int ret;
/* Stop the port from being a member of the vlan */ /* Stop the port from being a member of the vlan */
ocelot->vlan_mask[vid] &= ~BIT(port->chip_port); ocelot->vlan_mask[vid] &= ~BIT(port);
ret = ocelot_vlant_set_mask(ocelot, vid, ocelot->vlan_mask[vid]); ret = ocelot_vlant_set_mask(ocelot, vid, ocelot->vlan_mask[vid]);
if (ret) if (ret)
return ret; return ret;
/* Ingress */ /* Ingress */
if (port->pvid == vid) if (ocelot_port->pvid == vid)
port->pvid = 0; ocelot_port_set_pvid(ocelot, port, 0);
/* Egress */ /* Egress */
if (port->vid == vid) if (ocelot_port->vid == vid)
port->vid = 0; ocelot_port_set_native_vlan(ocelot, port, 0);
ocelot_vlan_port_apply(ocelot, port); return 0;
}
static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
int ret;
/* 8021q removes VID 0 on module unload for all interfaces
* with VLAN filtering feature. We need to keep it to receive
* untagged traffic.
*/
if (vid == 0)
return 0;
ret = ocelot_vlan_del(ocelot, port, vid);
if (ret)
return ret;
/* Del the port MAC address to with the right VLAN information */
ocelot_mact_forget(ocelot, dev->dev_addr, vid);
return 0; return 0;
} }
...@@ -333,16 +380,11 @@ static void ocelot_vlan_init(struct ocelot *ocelot) ...@@ -333,16 +380,11 @@ static void ocelot_vlan_init(struct ocelot *ocelot)
ocelot->vlan_mask[0] = GENMASK(ocelot->num_phys_ports - 1, 0); ocelot->vlan_mask[0] = GENMASK(ocelot->num_phys_ports - 1, 0);
ocelot_vlant_set_mask(ocelot, 0, ocelot->vlan_mask[0]); ocelot_vlant_set_mask(ocelot, 0, ocelot->vlan_mask[0]);
/* Configure the CPU port to be VLAN aware */
ocelot_write_gix(ocelot, ANA_PORT_VLAN_CFG_VLAN_VID(0) |
ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1),
ANA_PORT_VLAN_CFG, ocelot->num_phys_ports);
/* Set vlan ingress filter mask to all ports but the CPU port by /* Set vlan ingress filter mask to all ports but the CPU port by
* default. * default.
*/ */
ocelot_write(ocelot, GENMASK(9, 0), ANA_VLANMASK); ocelot_write(ocelot, GENMASK(ocelot->num_phys_ports - 1, 0),
ANA_VLANMASK);
for (port = 0; port < ocelot->num_phys_ports; port++) { for (port = 0; port < ocelot->num_phys_ports; port++) {
ocelot_write_gix(ocelot, 0, REW_PORT_VLAN_CFG, port); ocelot_write_gix(ocelot, 0, REW_PORT_VLAN_CFG, port);
...@@ -362,14 +404,13 @@ static u16 ocelot_wm_enc(u16 value) ...@@ -362,14 +404,13 @@ static u16 ocelot_wm_enc(u16 value)
return value; return value;
} }
static void ocelot_port_adjust_link(struct net_device *dev) static void ocelot_adjust_link(struct ocelot *ocelot, int port,
struct phy_device *phydev)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port *ocelot_port = ocelot->ports[port];
struct ocelot *ocelot = port->ocelot;
u8 p = port->chip_port;
int speed, atop_wm, mode = 0; int speed, atop_wm, mode = 0;
switch (dev->phydev->speed) { switch (phydev->speed) {
case SPEED_10: case SPEED_10:
speed = OCELOT_SPEED_10; speed = OCELOT_SPEED_10;
break; break;
...@@ -385,73 +426,77 @@ static void ocelot_port_adjust_link(struct net_device *dev) ...@@ -385,73 +426,77 @@ static void ocelot_port_adjust_link(struct net_device *dev)
mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA; mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA;
break; break;
default: default:
netdev_err(dev, "Unsupported PHY speed: %d\n", dev_err(ocelot->dev, "Unsupported PHY speed on port %d: %d\n",
dev->phydev->speed); port, phydev->speed);
return; return;
} }
phy_print_status(dev->phydev); phy_print_status(phydev);
if (!dev->phydev->link) if (!phydev->link)
return; return;
/* Only full duplex supported for now */ /* Only full duplex supported for now */
ocelot_port_writel(port, DEV_MAC_MODE_CFG_FDX_ENA | ocelot_port_writel(ocelot_port, DEV_MAC_MODE_CFG_FDX_ENA |
mode, DEV_MAC_MODE_CFG); mode, DEV_MAC_MODE_CFG);
/* Set MAC IFG Gaps /* Set MAC IFG Gaps
* FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 0 * FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 0
* !FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 5 * !FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 5
*/ */
ocelot_port_writel(port, DEV_MAC_IFG_CFG_TX_IFG(5), DEV_MAC_IFG_CFG); ocelot_port_writel(ocelot_port, DEV_MAC_IFG_CFG_TX_IFG(5),
DEV_MAC_IFG_CFG);
/* Load seed (0) and set MAC HDX late collision */ /* Load seed (0) and set MAC HDX late collision */
ocelot_port_writel(port, DEV_MAC_HDX_CFG_LATE_COL_POS(67) | ocelot_port_writel(ocelot_port, DEV_MAC_HDX_CFG_LATE_COL_POS(67) |
DEV_MAC_HDX_CFG_SEED_LOAD, DEV_MAC_HDX_CFG_SEED_LOAD,
DEV_MAC_HDX_CFG); DEV_MAC_HDX_CFG);
mdelay(1); mdelay(1);
ocelot_port_writel(port, DEV_MAC_HDX_CFG_LATE_COL_POS(67), ocelot_port_writel(ocelot_port, DEV_MAC_HDX_CFG_LATE_COL_POS(67),
DEV_MAC_HDX_CFG); DEV_MAC_HDX_CFG);
/* Disable HDX fast control */ /* Disable HDX fast control */
ocelot_port_writel(port, DEV_PORT_MISC_HDX_FAST_DIS, DEV_PORT_MISC); ocelot_port_writel(ocelot_port, DEV_PORT_MISC_HDX_FAST_DIS,
DEV_PORT_MISC);
/* SGMII only for now */ /* SGMII only for now */
ocelot_port_writel(port, PCS1G_MODE_CFG_SGMII_MODE_ENA, PCS1G_MODE_CFG); ocelot_port_writel(ocelot_port, PCS1G_MODE_CFG_SGMII_MODE_ENA,
ocelot_port_writel(port, PCS1G_SD_CFG_SD_SEL, PCS1G_SD_CFG); PCS1G_MODE_CFG);
ocelot_port_writel(ocelot_port, PCS1G_SD_CFG_SD_SEL, PCS1G_SD_CFG);
/* Enable PCS */ /* Enable PCS */
ocelot_port_writel(port, PCS1G_CFG_PCS_ENA, PCS1G_CFG); ocelot_port_writel(ocelot_port, PCS1G_CFG_PCS_ENA, PCS1G_CFG);
/* No aneg on SGMII */ /* No aneg on SGMII */
ocelot_port_writel(port, 0, PCS1G_ANEG_CFG); ocelot_port_writel(ocelot_port, 0, PCS1G_ANEG_CFG);
/* No loopback */ /* No loopback */
ocelot_port_writel(port, 0, PCS1G_LB_CFG); ocelot_port_writel(ocelot_port, 0, PCS1G_LB_CFG);
/* Set Max Length and maximum tags allowed */ /* Set Max Length and maximum tags allowed */
ocelot_port_writel(port, VLAN_ETH_FRAME_LEN, DEV_MAC_MAXLEN_CFG); ocelot_port_writel(ocelot_port, VLAN_ETH_FRAME_LEN,
ocelot_port_writel(port, DEV_MAC_TAGS_CFG_TAG_ID(ETH_P_8021AD) | DEV_MAC_MAXLEN_CFG);
ocelot_port_writel(ocelot_port, DEV_MAC_TAGS_CFG_TAG_ID(ETH_P_8021AD) |
DEV_MAC_TAGS_CFG_VLAN_AWR_ENA | DEV_MAC_TAGS_CFG_VLAN_AWR_ENA |
DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA, DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA,
DEV_MAC_TAGS_CFG); DEV_MAC_TAGS_CFG);
/* Enable MAC module */ /* Enable MAC module */
ocelot_port_writel(port, DEV_MAC_ENA_CFG_RX_ENA | ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA |
DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG); DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG);
/* Take MAC, Port, Phy (intern) and PCS (SGMII/Serdes) clock out of /* Take MAC, Port, Phy (intern) and PCS (SGMII/Serdes) clock out of
* reset */ * reset */
ocelot_port_writel(port, DEV_CLOCK_CFG_LINK_SPEED(speed), ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(speed),
DEV_CLOCK_CFG); DEV_CLOCK_CFG);
/* Set SMAC of Pause frame (00:00:00:00:00:00) */ /* Set SMAC of Pause frame (00:00:00:00:00:00) */
ocelot_port_writel(port, 0, DEV_MAC_FC_MAC_HIGH_CFG); ocelot_port_writel(ocelot_port, 0, DEV_MAC_FC_MAC_HIGH_CFG);
ocelot_port_writel(port, 0, DEV_MAC_FC_MAC_LOW_CFG); ocelot_port_writel(ocelot_port, 0, DEV_MAC_FC_MAC_LOW_CFG);
/* No PFC */ /* No PFC */
ocelot_write_gix(ocelot, ANA_PFC_PFC_CFG_FC_LINK_SPEED(speed), ocelot_write_gix(ocelot, ANA_PFC_PFC_CFG_FC_LINK_SPEED(speed),
ANA_PFC_PFC_CFG, p); ANA_PFC_PFC_CFG, port);
/* Set Pause WM hysteresis /* Set Pause WM hysteresis
* 152 = 6 * VLAN_ETH_FRAME_LEN / OCELOT_BUFFER_CELL_SZ * 152 = 6 * VLAN_ETH_FRAME_LEN / OCELOT_BUFFER_CELL_SZ
...@@ -459,13 +504,13 @@ static void ocelot_port_adjust_link(struct net_device *dev) ...@@ -459,13 +504,13 @@ static void ocelot_port_adjust_link(struct net_device *dev)
*/ */
ocelot_write_rix(ocelot, SYS_PAUSE_CFG_PAUSE_ENA | ocelot_write_rix(ocelot, SYS_PAUSE_CFG_PAUSE_ENA |
SYS_PAUSE_CFG_PAUSE_STOP(101) | SYS_PAUSE_CFG_PAUSE_STOP(101) |
SYS_PAUSE_CFG_PAUSE_START(152), SYS_PAUSE_CFG, p); SYS_PAUSE_CFG_PAUSE_START(152), SYS_PAUSE_CFG, port);
/* Core: Enable port for frame transfer */ /* Core: Enable port for frame transfer */
ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE | ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE |
QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) | QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) |
QSYS_SWITCH_PORT_MODE_PORT_ENA, QSYS_SWITCH_PORT_MODE_PORT_ENA,
QSYS_SWITCH_PORT_MODE, p); QSYS_SWITCH_PORT_MODE, port);
/* Flow control */ /* Flow control */
ocelot_write_rix(ocelot, SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) | ocelot_write_rix(ocelot, SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) |
...@@ -473,64 +518,91 @@ static void ocelot_port_adjust_link(struct net_device *dev) ...@@ -473,64 +518,91 @@ static void ocelot_port_adjust_link(struct net_device *dev)
SYS_MAC_FC_CFG_ZERO_PAUSE_ENA | SYS_MAC_FC_CFG_ZERO_PAUSE_ENA |
SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) | SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) |
SYS_MAC_FC_CFG_FC_LINK_SPEED(speed), SYS_MAC_FC_CFG_FC_LINK_SPEED(speed),
SYS_MAC_FC_CFG, p); SYS_MAC_FC_CFG, port);
ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, p); ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port);
/* Tail dropping watermark */ /* Tail dropping watermark */
atop_wm = (ocelot->shared_queue_sz - 9 * VLAN_ETH_FRAME_LEN) / OCELOT_BUFFER_CELL_SZ; atop_wm = (ocelot->shared_queue_sz - 9 * VLAN_ETH_FRAME_LEN) / OCELOT_BUFFER_CELL_SZ;
ocelot_write_rix(ocelot, ocelot_wm_enc(9 * VLAN_ETH_FRAME_LEN), ocelot_write_rix(ocelot, ocelot_wm_enc(9 * VLAN_ETH_FRAME_LEN),
SYS_ATOP, p); SYS_ATOP, port);
ocelot_write(ocelot, ocelot_wm_enc(atop_wm), SYS_ATOP_TOT_CFG); ocelot_write(ocelot, ocelot_wm_enc(atop_wm), SYS_ATOP_TOT_CFG);
} }
static int ocelot_port_open(struct net_device *dev) static void ocelot_port_adjust_link(struct net_device *dev)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot; struct ocelot *ocelot = priv->port.ocelot;
int err; int port = priv->chip_port;
ocelot_adjust_link(ocelot, port, dev->phydev);
}
static void ocelot_port_enable(struct ocelot *ocelot, int port,
struct phy_device *phy)
{
/* Enable receiving frames on the port, and activate auto-learning of /* Enable receiving frames on the port, and activate auto-learning of
* MAC addresses. * MAC addresses.
*/ */
ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_LEARNAUTO | ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_LEARNAUTO |
ANA_PORT_PORT_CFG_RECV_ENA | ANA_PORT_PORT_CFG_RECV_ENA |
ANA_PORT_PORT_CFG_PORTID_VAL(port->chip_port), ANA_PORT_PORT_CFG_PORTID_VAL(port),
ANA_PORT_PORT_CFG, port->chip_port); ANA_PORT_PORT_CFG, port);
}
if (port->serdes) { static int ocelot_port_open(struct net_device *dev)
err = phy_set_mode_ext(port->serdes, PHY_MODE_ETHERNET, {
port->phy_mode); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
int err;
if (priv->serdes) {
err = phy_set_mode_ext(priv->serdes, PHY_MODE_ETHERNET,
priv->phy_mode);
if (err) { if (err) {
netdev_err(dev, "Could not set mode of SerDes\n"); netdev_err(dev, "Could not set mode of SerDes\n");
return err; return err;
} }
} }
err = phy_connect_direct(dev, port->phy, &ocelot_port_adjust_link, err = phy_connect_direct(dev, priv->phy, &ocelot_port_adjust_link,
port->phy_mode); priv->phy_mode);
if (err) { if (err) {
netdev_err(dev, "Could not attach to PHY\n"); netdev_err(dev, "Could not attach to PHY\n");
return err; return err;
} }
dev->phydev = port->phy; dev->phydev = priv->phy;
phy_attached_info(priv->phy);
phy_start(priv->phy);
ocelot_port_enable(ocelot, port, priv->phy);
phy_attached_info(port->phy);
phy_start(port->phy);
return 0; return 0;
} }
static void ocelot_port_disable(struct ocelot *ocelot, int port)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
ocelot_port_writel(ocelot_port, 0, DEV_MAC_ENA_CFG);
ocelot_rmw_rix(ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA,
QSYS_SWITCH_PORT_MODE, port);
}
static int ocelot_port_stop(struct net_device *dev) static int ocelot_port_stop(struct net_device *dev)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
phy_disconnect(port->phy); phy_disconnect(priv->phy);
dev->phydev = NULL; dev->phydev = NULL;
ocelot_port_writel(port, 0, DEV_MAC_ENA_CFG); ocelot_port_disable(ocelot, port);
ocelot_rmw_rix(port->ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA,
QSYS_SWITCH_PORT_MODE, port->chip_port);
return 0; return 0;
} }
...@@ -556,13 +628,15 @@ static int ocelot_gen_ifh(u32 *ifh, struct frame_info *info) ...@@ -556,13 +628,15 @@ static int ocelot_gen_ifh(u32 *ifh, struct frame_info *info)
static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
{ {
struct ocelot_port_private *priv = netdev_priv(dev);
struct skb_shared_info *shinfo = skb_shinfo(skb); struct skb_shared_info *shinfo = skb_shinfo(skb);
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = port->ocelot; struct ocelot *ocelot = ocelot_port->ocelot;
u32 val, ifh[IFH_LEN];
struct frame_info info = {}; struct frame_info info = {};
u8 grp = 0; /* Send everything on CPU group 0 */ u8 grp = 0; /* Send everything on CPU group 0 */
unsigned int i, count, last; unsigned int i, count, last;
int port = priv->chip_port;
u32 val, ifh[IFH_LEN];
val = ocelot_read(ocelot, QS_INJ_STATUS); val = ocelot_read(ocelot, QS_INJ_STATUS);
if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) || if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
...@@ -572,15 +646,15 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -572,15 +646,15 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) | ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp); QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp);
info.port = BIT(port->chip_port); info.port = BIT(port);
info.tag_type = IFH_TAG_TYPE_C; info.tag_type = IFH_TAG_TYPE_C;
info.vid = skb_vlan_tag_get(skb); info.vid = skb_vlan_tag_get(skb);
/* Check if timestamping is needed */ /* Check if timestamping is needed */
if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) { if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
info.rew_op = port->ptp_cmd; info.rew_op = ocelot_port->ptp_cmd;
if (port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
info.rew_op |= (port->ts_id % 4) << 3; info.rew_op |= (ocelot_port->ts_id % 4) << 3;
} }
ocelot_gen_ifh(ifh, &info); ocelot_gen_ifh(ifh, &info);
...@@ -615,7 +689,7 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -615,7 +689,7 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
dev->stats.tx_bytes += skb->len; dev->stats.tx_bytes += skb->len;
if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP && if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
struct ocelot_skb *oskb = struct ocelot_skb *oskb =
kzalloc(sizeof(struct ocelot_skb), GFP_ATOMIC); kzalloc(sizeof(struct ocelot_skb), GFP_ATOMIC);
...@@ -625,10 +699,10 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -625,10 +699,10 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
oskb->skb = skb; oskb->skb = skb;
oskb->id = port->ts_id % 4; oskb->id = ocelot_port->ts_id % 4;
port->ts_id++; ocelot_port->ts_id++;
list_add_tail(&oskb->head, &port->skbs); list_add_tail(&oskb->head, &ocelot_port->skbs);
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
...@@ -667,25 +741,29 @@ EXPORT_SYMBOL(ocelot_get_hwtimestamp); ...@@ -667,25 +741,29 @@ EXPORT_SYMBOL(ocelot_get_hwtimestamp);
static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr) static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
return ocelot_mact_forget(port->ocelot, addr, port->pvid); return ocelot_mact_forget(ocelot, addr, ocelot_port->pvid);
} }
static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr) static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
return ocelot_mact_learn(port->ocelot, PGID_CPU, addr, port->pvid, return ocelot_mact_learn(ocelot, PGID_CPU, addr, ocelot_port->pvid,
ENTRYTYPE_LOCKED); ENTRYTYPE_LOCKED);
} }
static void ocelot_set_rx_mode(struct net_device *dev) static void ocelot_set_rx_mode(struct net_device *dev)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot; struct ocelot *ocelot = priv->port.ocelot;
int i;
u32 val; u32 val;
int i;
/* This doesn't handle promiscuous mode because the bridge core is /* This doesn't handle promiscuous mode because the bridge core is
* setting IFF_PROMISC on all slave interfaces and all frames would be * setting IFF_PROMISC on all slave interfaces and all frames would be
...@@ -701,10 +779,11 @@ static void ocelot_set_rx_mode(struct net_device *dev) ...@@ -701,10 +779,11 @@ static void ocelot_set_rx_mode(struct net_device *dev)
static int ocelot_port_get_phys_port_name(struct net_device *dev, static int ocelot_port_get_phys_port_name(struct net_device *dev,
char *buf, size_t len) char *buf, size_t len)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
int port = priv->chip_port;
int ret; int ret;
ret = snprintf(buf, len, "p%d", port->chip_port); ret = snprintf(buf, len, "p%d", port);
if (ret >= len) if (ret >= len)
return -EINVAL; return -EINVAL;
...@@ -713,15 +792,16 @@ static int ocelot_port_get_phys_port_name(struct net_device *dev, ...@@ -713,15 +792,16 @@ static int ocelot_port_get_phys_port_name(struct net_device *dev,
static int ocelot_port_set_mac_address(struct net_device *dev, void *p) static int ocelot_port_set_mac_address(struct net_device *dev, void *p)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot; struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
const struct sockaddr *addr = p; const struct sockaddr *addr = p;
/* Learn the new net device MAC address in the mac table. */ /* Learn the new net device MAC address in the mac table. */
ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, port->pvid, ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, ocelot_port->pvid,
ENTRYTYPE_LOCKED); ENTRYTYPE_LOCKED);
/* Then forget the previous one. */ /* Then forget the previous one. */
ocelot_mact_forget(ocelot, dev->dev_addr, port->pvid); ocelot_mact_forget(ocelot, dev->dev_addr, ocelot_port->pvid);
ether_addr_copy(dev->dev_addr, addr->sa_data); ether_addr_copy(dev->dev_addr, addr->sa_data);
return 0; return 0;
...@@ -730,11 +810,12 @@ static int ocelot_port_set_mac_address(struct net_device *dev, void *p) ...@@ -730,11 +810,12 @@ static int ocelot_port_set_mac_address(struct net_device *dev, void *p)
static void ocelot_get_stats64(struct net_device *dev, static void ocelot_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *stats) struct rtnl_link_stats64 *stats)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot; struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
/* Configure the port to read the stats from */ /* Configure the port to read the stats from */
ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port->chip_port), ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port),
SYS_STAT_CFG); SYS_STAT_CFG);
/* Get Rx stats */ /* Get Rx stats */
...@@ -765,21 +846,19 @@ static void ocelot_get_stats64(struct net_device *dev, ...@@ -765,21 +846,19 @@ static void ocelot_get_stats64(struct net_device *dev,
stats->collisions = ocelot_read(ocelot, SYS_COUNT_TX_COLLISION); stats->collisions = ocelot_read(ocelot, SYS_COUNT_TX_COLLISION);
} }
static int ocelot_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], static int ocelot_fdb_add(struct ocelot *ocelot, int port,
struct net_device *dev, const unsigned char *addr, const unsigned char *addr, u16 vid,
u16 vid, u16 flags, bool vlan_aware)
struct netlink_ext_ack *extack)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port *ocelot_port = ocelot->ports[port];
struct ocelot *ocelot = port->ocelot;
if (!vid) { if (!vid) {
if (!port->vlan_aware) if (!vlan_aware)
/* If the bridge is not VLAN aware and no VID was /* If the bridge is not VLAN aware and no VID was
* provided, set it to pvid to ensure the MAC entry * provided, set it to pvid to ensure the MAC entry
* matches incoming untagged packets * matches incoming untagged packets
*/ */
vid = port->pvid; vid = ocelot_port->pvid;
else else
/* If the bridge is VLAN aware a VID must be provided as /* If the bridge is VLAN aware a VID must be provided as
* otherwise the learnt entry wouldn't match any frame. * otherwise the learnt entry wouldn't match any frame.
...@@ -787,20 +866,39 @@ static int ocelot_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], ...@@ -787,20 +866,39 @@ static int ocelot_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
return -EINVAL; return -EINVAL;
} }
return ocelot_mact_learn(ocelot, port->chip_port, addr, vid, return ocelot_mact_learn(ocelot, port, addr, vid, ENTRYTYPE_LOCKED);
ENTRYTYPE_LOCKED);
} }
static int ocelot_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev, struct net_device *dev,
const unsigned char *addr, u16 vid) const unsigned char *addr,
u16 vid, u16 flags,
struct netlink_ext_ack *extack)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot; struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
return ocelot_fdb_add(ocelot, port, addr, vid, priv->vlan_aware);
}
static int ocelot_fdb_del(struct ocelot *ocelot, int port,
const unsigned char *addr, u16 vid)
{
return ocelot_mact_forget(ocelot, addr, vid); return ocelot_mact_forget(ocelot, addr, vid);
} }
static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
return ocelot_fdb_del(ocelot, port, addr, vid);
}
struct ocelot_dump_ctx { struct ocelot_dump_ctx {
struct net_device *dev; struct net_device *dev;
struct sk_buff *skb; struct sk_buff *skb;
...@@ -808,9 +906,10 @@ struct ocelot_dump_ctx { ...@@ -808,9 +906,10 @@ struct ocelot_dump_ctx {
int idx; int idx;
}; };
static int ocelot_fdb_do_dump(struct ocelot_mact_entry *entry, static int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
struct ocelot_dump_ctx *dump) bool is_static, void *data)
{ {
struct ocelot_dump_ctx *dump = data;
u32 portid = NETLINK_CB(dump->cb->skb).portid; u32 portid = NETLINK_CB(dump->cb->skb).portid;
u32 seq = dump->cb->nlh->nlmsg_seq; u32 seq = dump->cb->nlh->nlmsg_seq;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
...@@ -831,12 +930,12 @@ static int ocelot_fdb_do_dump(struct ocelot_mact_entry *entry, ...@@ -831,12 +930,12 @@ static int ocelot_fdb_do_dump(struct ocelot_mact_entry *entry,
ndm->ndm_flags = NTF_SELF; ndm->ndm_flags = NTF_SELF;
ndm->ndm_type = 0; ndm->ndm_type = 0;
ndm->ndm_ifindex = dump->dev->ifindex; ndm->ndm_ifindex = dump->dev->ifindex;
ndm->ndm_state = NUD_REACHABLE; ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, entry->mac)) if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr))
goto nla_put_failure; goto nla_put_failure;
if (entry->vid && nla_put_u16(dump->skb, NDA_VLAN, entry->vid)) if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid))
goto nla_put_failure; goto nla_put_failure;
nlmsg_end(dump->skb, nlh); nlmsg_end(dump->skb, nlh);
...@@ -850,12 +949,11 @@ static int ocelot_fdb_do_dump(struct ocelot_mact_entry *entry, ...@@ -850,12 +949,11 @@ static int ocelot_fdb_do_dump(struct ocelot_mact_entry *entry,
return -EMSGSIZE; return -EMSGSIZE;
} }
static inline int ocelot_mact_read(struct ocelot_port *port, int row, int col, static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col,
struct ocelot_mact_entry *entry) struct ocelot_mact_entry *entry)
{ {
struct ocelot *ocelot = port->ocelot;
char mac[ETH_ALEN];
u32 val, dst, macl, mach; u32 val, dst, macl, mach;
char mac[ETH_ALEN];
/* Set row and column to read from */ /* Set row and column to read from */
ocelot_field_write(ocelot, ANA_TABLES_MACTINDX_M_INDEX, row); ocelot_field_write(ocelot, ANA_TABLES_MACTINDX_M_INDEX, row);
...@@ -878,7 +976,7 @@ static inline int ocelot_mact_read(struct ocelot_port *port, int row, int col, ...@@ -878,7 +976,7 @@ static inline int ocelot_mact_read(struct ocelot_port *port, int row, int col,
* do not report it. * do not report it.
*/ */
dst = (val & ANA_TABLES_MACACCESS_DEST_IDX_M) >> 3; dst = (val & ANA_TABLES_MACACCESS_DEST_IDX_M) >> 3;
if (dst != port->chip_port) if (dst != port)
return -EINVAL; return -EINVAL;
/* Get the entry's MAC address and VLAN id */ /* Get the entry's MAC address and VLAN id */
...@@ -898,43 +996,60 @@ static inline int ocelot_mact_read(struct ocelot_port *port, int row, int col, ...@@ -898,43 +996,60 @@ static inline int ocelot_mact_read(struct ocelot_port *port, int row, int col,
return 0; return 0;
} }
static int ocelot_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, static int ocelot_fdb_dump(struct ocelot *ocelot, int port,
struct net_device *dev, dsa_fdb_dump_cb_t *cb, void *data)
struct net_device *filter_dev, int *idx)
{ {
struct ocelot_port *port = netdev_priv(dev); int i, j;
int i, j, ret = 0;
struct ocelot_dump_ctx dump = {
.dev = dev,
.skb = skb,
.cb = cb,
.idx = *idx,
};
struct ocelot_mact_entry entry;
/* Loop through all the mac tables entries. There are 1024 rows of 4 /* Loop through all the mac tables entries. There are 1024 rows of 4
* entries. * entries.
*/ */
for (i = 0; i < 1024; i++) { for (i = 0; i < 1024; i++) {
for (j = 0; j < 4; j++) { for (j = 0; j < 4; j++) {
ret = ocelot_mact_read(port, i, j, &entry); struct ocelot_mact_entry entry;
bool is_static;
int ret;
ret = ocelot_mact_read(ocelot, port, i, j, &entry);
/* If the entry is invalid (wrong port, invalid...), /* If the entry is invalid (wrong port, invalid...),
* skip it. * skip it.
*/ */
if (ret == -EINVAL) if (ret == -EINVAL)
continue; continue;
else if (ret) else if (ret)
goto end; return ret;
is_static = (entry.type == ENTRYTYPE_LOCKED);
ret = ocelot_fdb_do_dump(&entry, &dump); ret = cb(entry.mac, entry.vid, is_static, data);
if (ret) if (ret)
goto end; return ret;
} }
} }
end: return 0;
}
static int ocelot_port_fdb_dump(struct sk_buff *skb,
struct netlink_callback *cb,
struct net_device *dev,
struct net_device *filter_dev, int *idx)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
struct ocelot_dump_ctx dump = {
.dev = dev,
.skb = skb,
.cb = cb,
.idx = *idx,
};
int port = priv->chip_port;
int ret;
ret = ocelot_fdb_dump(ocelot, port, ocelot_port_fdb_do_dump, &dump);
*idx = dump.idx; *idx = dump.idx;
return ret; return ret;
} }
...@@ -953,18 +1068,20 @@ static int ocelot_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, ...@@ -953,18 +1068,20 @@ static int ocelot_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,
static int ocelot_set_features(struct net_device *dev, static int ocelot_set_features(struct net_device *dev,
netdev_features_t features) netdev_features_t features)
{ {
struct ocelot_port *port = netdev_priv(dev);
netdev_features_t changed = dev->features ^ features; netdev_features_t changed = dev->features ^ features;
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC) && if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC) &&
port->tc.offload_cnt) { priv->tc.offload_cnt) {
netdev_err(dev, netdev_err(dev,
"Cannot disable HW TC offload while offloads active\n"); "Cannot disable HW TC offload while offloads active\n");
return -EBUSY; return -EBUSY;
} }
if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) if (changed & NETIF_F_HW_VLAN_CTAG_FILTER)
ocelot_vlan_mode(port, features); ocelot_vlan_mode(ocelot, port, features);
return 0; return 0;
} }
...@@ -972,8 +1089,8 @@ static int ocelot_set_features(struct net_device *dev, ...@@ -972,8 +1089,8 @@ static int ocelot_set_features(struct net_device *dev,
static int ocelot_get_port_parent_id(struct net_device *dev, static int ocelot_get_port_parent_id(struct net_device *dev,
struct netdev_phys_item_id *ppid) struct netdev_phys_item_id *ppid)
{ {
struct ocelot_port *ocelot_port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = ocelot_port->ocelot; struct ocelot *ocelot = priv->port.ocelot;
ppid->id_len = sizeof(ocelot->base_mac); ppid->id_len = sizeof(ocelot->base_mac);
memcpy(&ppid->id, &ocelot->base_mac, ppid->id_len); memcpy(&ppid->id, &ocelot->base_mac, ppid->id_len);
...@@ -981,17 +1098,17 @@ static int ocelot_get_port_parent_id(struct net_device *dev, ...@@ -981,17 +1098,17 @@ static int ocelot_get_port_parent_id(struct net_device *dev,
return 0; return 0;
} }
static int ocelot_hwstamp_get(struct ocelot_port *port, struct ifreq *ifr) static int ocelot_hwstamp_get(struct ocelot *ocelot, int port,
struct ifreq *ifr)
{ {
struct ocelot *ocelot = port->ocelot;
return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config, return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config,
sizeof(ocelot->hwtstamp_config)) ? -EFAULT : 0; sizeof(ocelot->hwtstamp_config)) ? -EFAULT : 0;
} }
static int ocelot_hwstamp_set(struct ocelot_port *port, struct ifreq *ifr) static int ocelot_hwstamp_set(struct ocelot *ocelot, int port,
struct ifreq *ifr)
{ {
struct ocelot *ocelot = port->ocelot; struct ocelot_port *ocelot_port = ocelot->ports[port];
struct hwtstamp_config cfg; struct hwtstamp_config cfg;
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
...@@ -1004,16 +1121,16 @@ static int ocelot_hwstamp_set(struct ocelot_port *port, struct ifreq *ifr) ...@@ -1004,16 +1121,16 @@ static int ocelot_hwstamp_set(struct ocelot_port *port, struct ifreq *ifr)
/* Tx type sanity check */ /* Tx type sanity check */
switch (cfg.tx_type) { switch (cfg.tx_type) {
case HWTSTAMP_TX_ON: case HWTSTAMP_TX_ON:
port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP; ocelot_port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP;
break; break;
case HWTSTAMP_TX_ONESTEP_SYNC: case HWTSTAMP_TX_ONESTEP_SYNC:
/* IFH_REW_OP_ONE_STEP_PTP updates the correctional field, we /* IFH_REW_OP_ONE_STEP_PTP updates the correctional field, we
* need to update the origin time. * need to update the origin time.
*/ */
port->ptp_cmd = IFH_REW_OP_ORIGIN_PTP; ocelot_port->ptp_cmd = IFH_REW_OP_ORIGIN_PTP;
break; break;
case HWTSTAMP_TX_OFF: case HWTSTAMP_TX_OFF:
port->ptp_cmd = 0; ocelot_port->ptp_cmd = 0;
break; break;
default: default:
return -ERANGE; return -ERANGE;
...@@ -1055,8 +1172,9 @@ static int ocelot_hwstamp_set(struct ocelot_port *port, struct ifreq *ifr) ...@@ -1055,8 +1172,9 @@ static int ocelot_hwstamp_set(struct ocelot_port *port, struct ifreq *ifr)
static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot; struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
/* The function is only used for PTP operations for now */ /* The function is only used for PTP operations for now */
if (!ocelot->ptp) if (!ocelot->ptp)
...@@ -1064,9 +1182,9 @@ static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ...@@ -1064,9 +1182,9 @@ static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
switch (cmd) { switch (cmd) {
case SIOCSHWTSTAMP: case SIOCSHWTSTAMP:
return ocelot_hwstamp_set(port, ifr); return ocelot_hwstamp_set(ocelot, port, ifr);
case SIOCGHWTSTAMP: case SIOCGHWTSTAMP:
return ocelot_hwstamp_get(port, ifr); return ocelot_hwstamp_get(ocelot, port, ifr);
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -1080,9 +1198,9 @@ static const struct net_device_ops ocelot_port_netdev_ops = { ...@@ -1080,9 +1198,9 @@ static const struct net_device_ops ocelot_port_netdev_ops = {
.ndo_get_phys_port_name = ocelot_port_get_phys_port_name, .ndo_get_phys_port_name = ocelot_port_get_phys_port_name,
.ndo_set_mac_address = ocelot_port_set_mac_address, .ndo_set_mac_address = ocelot_port_set_mac_address,
.ndo_get_stats64 = ocelot_get_stats64, .ndo_get_stats64 = ocelot_get_stats64,
.ndo_fdb_add = ocelot_fdb_add, .ndo_fdb_add = ocelot_port_fdb_add,
.ndo_fdb_del = ocelot_fdb_del, .ndo_fdb_del = ocelot_port_fdb_del,
.ndo_fdb_dump = ocelot_fdb_dump, .ndo_fdb_dump = ocelot_port_fdb_dump,
.ndo_vlan_rx_add_vid = ocelot_vlan_rx_add_vid, .ndo_vlan_rx_add_vid = ocelot_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = ocelot_vlan_rx_kill_vid, .ndo_vlan_rx_kill_vid = ocelot_vlan_rx_kill_vid,
.ndo_set_features = ocelot_set_features, .ndo_set_features = ocelot_set_features,
...@@ -1091,10 +1209,9 @@ static const struct net_device_ops ocelot_port_netdev_ops = { ...@@ -1091,10 +1209,9 @@ static const struct net_device_ops ocelot_port_netdev_ops = {
.ndo_do_ioctl = ocelot_ioctl, .ndo_do_ioctl = ocelot_ioctl,
}; };
static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data) static void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset,
u8 *data)
{ {
struct ocelot_port *port = netdev_priv(netdev);
struct ocelot *ocelot = port->ocelot;
int i; int i;
if (sset != ETH_SS_STATS) if (sset != ETH_SS_STATS)
...@@ -1105,6 +1222,16 @@ static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data) ...@@ -1105,6 +1222,16 @@ static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data)
ETH_GSTRING_LEN); ETH_GSTRING_LEN);
} }
static void ocelot_port_get_strings(struct net_device *netdev, u32 sset,
u8 *data)
{
struct ocelot_port_private *priv = netdev_priv(netdev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
ocelot_get_strings(ocelot, port, sset, data);
}
static void ocelot_update_stats(struct ocelot *ocelot) static void ocelot_update_stats(struct ocelot *ocelot)
{ {
int i, j; int i, j;
...@@ -1145,11 +1272,8 @@ static void ocelot_check_stats_work(struct work_struct *work) ...@@ -1145,11 +1272,8 @@ static void ocelot_check_stats_work(struct work_struct *work)
OCELOT_STATS_CHECK_DELAY); OCELOT_STATS_CHECK_DELAY);
} }
static void ocelot_get_ethtool_stats(struct net_device *dev, static void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data)
struct ethtool_stats *stats, u64 *data)
{ {
struct ocelot_port *port = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot;
int i; int i;
/* check and update now */ /* check and update now */
...@@ -1157,28 +1281,40 @@ static void ocelot_get_ethtool_stats(struct net_device *dev, ...@@ -1157,28 +1281,40 @@ static void ocelot_get_ethtool_stats(struct net_device *dev,
/* Copy all counters */ /* Copy all counters */
for (i = 0; i < ocelot->num_stats; i++) for (i = 0; i < ocelot->num_stats; i++)
*data++ = ocelot->stats[port->chip_port * ocelot->num_stats + i]; *data++ = ocelot->stats[port * ocelot->num_stats + i];
} }
static int ocelot_get_sset_count(struct net_device *dev, int sset) static void ocelot_port_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats,
u64 *data)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot; struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
ocelot_get_ethtool_stats(ocelot, port, data);
}
static int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
{
if (sset != ETH_SS_STATS) if (sset != ETH_SS_STATS)
return -EOPNOTSUPP; return -EOPNOTSUPP;
return ocelot->num_stats; return ocelot->num_stats;
} }
static int ocelot_get_ts_info(struct net_device *dev, static int ocelot_port_get_sset_count(struct net_device *dev, int sset)
struct ethtool_ts_info *info)
{ {
struct ocelot_port *ocelot_port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = ocelot_port->ocelot; struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
if (!ocelot->ptp) return ocelot_get_sset_count(ocelot, port, sset);
return ethtool_op_get_ts_info(dev, info); }
static int ocelot_get_ts_info(struct ocelot *ocelot, int port,
struct ethtool_ts_info *info)
{
info->phc_index = ocelot->ptp_clock ? info->phc_index = ocelot->ptp_clock ?
ptp_clock_index(ocelot->ptp_clock) : -1; ptp_clock_index(ocelot->ptp_clock) : -1;
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE | info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
...@@ -1194,35 +1330,42 @@ static int ocelot_get_ts_info(struct net_device *dev, ...@@ -1194,35 +1330,42 @@ static int ocelot_get_ts_info(struct net_device *dev,
return 0; return 0;
} }
static int ocelot_port_get_ts_info(struct net_device *dev,
struct ethtool_ts_info *info)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
if (!ocelot->ptp)
return ethtool_op_get_ts_info(dev, info);
return ocelot_get_ts_info(ocelot, port, info);
}
static const struct ethtool_ops ocelot_ethtool_ops = { static const struct ethtool_ops ocelot_ethtool_ops = {
.get_strings = ocelot_get_strings, .get_strings = ocelot_port_get_strings,
.get_ethtool_stats = ocelot_get_ethtool_stats, .get_ethtool_stats = ocelot_port_get_ethtool_stats,
.get_sset_count = ocelot_get_sset_count, .get_sset_count = ocelot_port_get_sset_count,
.get_link_ksettings = phy_ethtool_get_link_ksettings, .get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = phy_ethtool_set_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings,
.get_ts_info = ocelot_get_ts_info, .get_ts_info = ocelot_port_get_ts_info,
}; };
static int ocelot_port_attr_stp_state_set(struct ocelot_port *ocelot_port, static void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port,
struct switchdev_trans *trans,
u8 state) u8 state)
{ {
struct ocelot *ocelot = ocelot_port->ocelot;
u32 port_cfg; u32 port_cfg;
int port, i; int p, i;
if (switchdev_trans_ph_prepare(trans))
return 0;
if (!(BIT(ocelot_port->chip_port) & ocelot->bridge_mask)) if (!(BIT(port) & ocelot->bridge_mask))
return 0; return;
port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, port);
ocelot_port->chip_port);
switch (state) { switch (state) {
case BR_STATE_FORWARDING: case BR_STATE_FORWARDING:
ocelot->bridge_fwd_mask |= BIT(ocelot_port->chip_port); ocelot->bridge_fwd_mask |= BIT(port);
/* Fallthrough */ /* Fallthrough */
case BR_STATE_LEARNING: case BR_STATE_LEARNING:
port_cfg |= ANA_PORT_PORT_CFG_LEARN_ENA; port_cfg |= ANA_PORT_PORT_CFG_LEARN_ENA;
...@@ -1230,19 +1373,18 @@ static int ocelot_port_attr_stp_state_set(struct ocelot_port *ocelot_port, ...@@ -1230,19 +1373,18 @@ static int ocelot_port_attr_stp_state_set(struct ocelot_port *ocelot_port,
default: default:
port_cfg &= ~ANA_PORT_PORT_CFG_LEARN_ENA; port_cfg &= ~ANA_PORT_PORT_CFG_LEARN_ENA;
ocelot->bridge_fwd_mask &= ~BIT(ocelot_port->chip_port); ocelot->bridge_fwd_mask &= ~BIT(port);
break; break;
} }
ocelot_write_gix(ocelot, port_cfg, ANA_PORT_PORT_CFG, ocelot_write_gix(ocelot, port_cfg, ANA_PORT_PORT_CFG, port);
ocelot_port->chip_port);
/* Apply FWD mask. The loop is needed to add/remove the current port as /* Apply FWD mask. The loop is needed to add/remove the current port as
* a source for the other ports. * a source for the other ports.
*/ */
for (port = 0; port < ocelot->num_phys_ports; port++) { for (p = 0; p < ocelot->num_phys_ports; p++) {
if (ocelot->bridge_fwd_mask & BIT(port)) { if (p == ocelot->cpu || (ocelot->bridge_fwd_mask & BIT(p))) {
unsigned long mask = ocelot->bridge_fwd_mask & ~BIT(port); unsigned long mask = ocelot->bridge_fwd_mask & ~BIT(p);
for (i = 0; i < ocelot->num_phys_ports; i++) { for (i = 0; i < ocelot->num_phys_ports; i++) {
unsigned long bond_mask = ocelot->lags[i]; unsigned long bond_mask = ocelot->lags[i];
...@@ -1250,78 +1392,91 @@ static int ocelot_port_attr_stp_state_set(struct ocelot_port *ocelot_port, ...@@ -1250,78 +1392,91 @@ static int ocelot_port_attr_stp_state_set(struct ocelot_port *ocelot_port,
if (!bond_mask) if (!bond_mask)
continue; continue;
if (bond_mask & BIT(port)) { if (bond_mask & BIT(p)) {
mask &= ~bond_mask; mask &= ~bond_mask;
break; break;
} }
} }
ocelot_write_rix(ocelot, /* Avoid the NPI port from looping back to itself */
BIT(ocelot->num_phys_ports) | mask, if (p != ocelot->cpu)
ANA_PGID_PGID, PGID_SRC + port); mask |= BIT(ocelot->cpu);
ocelot_write_rix(ocelot, mask,
ANA_PGID_PGID, PGID_SRC + p);
} else { } else {
/* Only the CPU port, this is compatible with link /* Only the CPU port, this is compatible with link
* aggregation. * aggregation.
*/ */
ocelot_write_rix(ocelot, ocelot_write_rix(ocelot,
BIT(ocelot->num_phys_ports), BIT(ocelot->cpu),
ANA_PGID_PGID, PGID_SRC + port); ANA_PGID_PGID, PGID_SRC + p);
} }
} }
}
return 0; static void ocelot_port_attr_stp_state_set(struct ocelot *ocelot, int port,
struct switchdev_trans *trans,
u8 state)
{
if (switchdev_trans_ph_prepare(trans))
return;
ocelot_bridge_stp_state_set(ocelot, port, state);
}
static void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs)
{
ocelot_write(ocelot, ANA_AUTOAGE_AGE_PERIOD(msecs / 2),
ANA_AUTOAGE);
} }
static void ocelot_port_attr_ageing_set(struct ocelot_port *ocelot_port, static void ocelot_port_attr_ageing_set(struct ocelot *ocelot, int port,
unsigned long ageing_clock_t) unsigned long ageing_clock_t)
{ {
struct ocelot *ocelot = ocelot_port->ocelot;
unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t); unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000; u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000;
ocelot_write(ocelot, ANA_AUTOAGE_AGE_PERIOD(ageing_time / 2), ocelot_set_ageing_time(ocelot, ageing_time);
ANA_AUTOAGE);
} }
static void ocelot_port_attr_mc_set(struct ocelot_port *port, bool mc) static void ocelot_port_attr_mc_set(struct ocelot *ocelot, int port, bool mc)
{ {
struct ocelot *ocelot = port->ocelot; u32 cpu_fwd_mcast = ANA_PORT_CPU_FWD_CFG_CPU_IGMP_REDIR_ENA |
u32 val = ocelot_read_gix(ocelot, ANA_PORT_CPU_FWD_CFG,
port->chip_port);
if (mc)
val |= ANA_PORT_CPU_FWD_CFG_CPU_IGMP_REDIR_ENA |
ANA_PORT_CPU_FWD_CFG_CPU_MLD_REDIR_ENA | ANA_PORT_CPU_FWD_CFG_CPU_MLD_REDIR_ENA |
ANA_PORT_CPU_FWD_CFG_CPU_IPMC_CTRL_COPY_ENA; ANA_PORT_CPU_FWD_CFG_CPU_IPMC_CTRL_COPY_ENA;
else u32 val = 0;
val &= ~(ANA_PORT_CPU_FWD_CFG_CPU_IGMP_REDIR_ENA |
ANA_PORT_CPU_FWD_CFG_CPU_MLD_REDIR_ENA |
ANA_PORT_CPU_FWD_CFG_CPU_IPMC_CTRL_COPY_ENA);
ocelot_write_gix(ocelot, val, ANA_PORT_CPU_FWD_CFG, port->chip_port); if (mc)
val = cpu_fwd_mcast;
ocelot_rmw_gix(ocelot, val, cpu_fwd_mcast,
ANA_PORT_CPU_FWD_CFG, port);
} }
static int ocelot_port_attr_set(struct net_device *dev, static int ocelot_port_attr_set(struct net_device *dev,
const struct switchdev_attr *attr, const struct switchdev_attr *attr,
struct switchdev_trans *trans) struct switchdev_trans *trans)
{ {
struct ocelot_port *ocelot_port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
int err = 0; int err = 0;
switch (attr->id) { switch (attr->id) {
case SWITCHDEV_ATTR_ID_PORT_STP_STATE: case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
ocelot_port_attr_stp_state_set(ocelot_port, trans, ocelot_port_attr_stp_state_set(ocelot, port, trans,
attr->u.stp_state); attr->u.stp_state);
break; break;
case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
ocelot_port_attr_ageing_set(ocelot_port, attr->u.ageing_time); ocelot_port_attr_ageing_set(ocelot, port, attr->u.ageing_time);
break; break;
case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
ocelot_port->vlan_aware = attr->u.vlan_filtering; priv->vlan_aware = attr->u.vlan_filtering;
ocelot_vlan_port_apply(ocelot_port->ocelot, ocelot_port); ocelot_port_vlan_filtering(ocelot, port, priv->vlan_aware);
break; break;
case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED: case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
ocelot_port_attr_mc_set(ocelot_port, !attr->u.mc_disabled); ocelot_port_attr_mc_set(ocelot, port, !attr->u.mc_disabled);
break; break;
default: default:
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
...@@ -1383,15 +1538,17 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev, ...@@ -1383,15 +1538,17 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev,
const struct switchdev_obj_port_mdb *mdb, const struct switchdev_obj_port_mdb *mdb,
struct switchdev_trans *trans) struct switchdev_trans *trans)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot; struct ocelot_port *ocelot_port = &priv->port;
struct ocelot_multicast *mc; struct ocelot *ocelot = ocelot_port->ocelot;
unsigned char addr[ETH_ALEN]; unsigned char addr[ETH_ALEN];
struct ocelot_multicast *mc;
int port = priv->chip_port;
u16 vid = mdb->vid; u16 vid = mdb->vid;
bool new = false; bool new = false;
if (!vid) if (!vid)
vid = port->pvid; vid = ocelot_port->pvid;
mc = ocelot_multicast_get(ocelot, mdb->addr, vid); mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
if (!mc) { if (!mc) {
...@@ -1415,7 +1572,7 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev, ...@@ -1415,7 +1572,7 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev,
ocelot_mact_forget(ocelot, addr, vid); ocelot_mact_forget(ocelot, addr, vid);
} }
mc->ports |= BIT(port->chip_port); mc->ports |= BIT(port);
addr[2] = mc->ports << 0; addr[2] = mc->ports << 0;
addr[1] = mc->ports << 8; addr[1] = mc->ports << 8;
...@@ -1425,14 +1582,16 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev, ...@@ -1425,14 +1582,16 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev,
static int ocelot_port_obj_del_mdb(struct net_device *dev, static int ocelot_port_obj_del_mdb(struct net_device *dev,
const struct switchdev_obj_port_mdb *mdb) const struct switchdev_obj_port_mdb *mdb)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = port->ocelot; struct ocelot_port *ocelot_port = &priv->port;
struct ocelot_multicast *mc; struct ocelot *ocelot = ocelot_port->ocelot;
unsigned char addr[ETH_ALEN]; unsigned char addr[ETH_ALEN];
struct ocelot_multicast *mc;
int port = priv->chip_port;
u16 vid = mdb->vid; u16 vid = mdb->vid;
if (!vid) if (!vid)
vid = port->pvid; vid = ocelot_port->pvid;
mc = ocelot_multicast_get(ocelot, mdb->addr, vid); mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
if (!mc) if (!mc)
...@@ -1444,7 +1603,7 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev, ...@@ -1444,7 +1603,7 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev,
addr[0] = 0; addr[0] = 0;
ocelot_mact_forget(ocelot, addr, vid); ocelot_mact_forget(ocelot, addr, vid);
mc->ports &= ~BIT(port->chip_port); mc->ports &= ~BIT(port);
if (!mc->ports) { if (!mc->ports) {
list_del(&mc->list); list_del(&mc->list);
devm_kfree(ocelot->dev, mc); devm_kfree(ocelot->dev, mc);
...@@ -1501,11 +1660,9 @@ static int ocelot_port_obj_del(struct net_device *dev, ...@@ -1501,11 +1660,9 @@ static int ocelot_port_obj_del(struct net_device *dev,
return ret; return ret;
} }
static int ocelot_port_bridge_join(struct ocelot_port *ocelot_port, static int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
struct net_device *bridge) struct net_device *bridge)
{ {
struct ocelot *ocelot = ocelot_port->ocelot;
if (!ocelot->bridge_mask) { if (!ocelot->bridge_mask) {
ocelot->hw_bridge_dev = bridge; ocelot->hw_bridge_dev = bridge;
} else { } else {
...@@ -1515,25 +1672,22 @@ static int ocelot_port_bridge_join(struct ocelot_port *ocelot_port, ...@@ -1515,25 +1672,22 @@ static int ocelot_port_bridge_join(struct ocelot_port *ocelot_port,
return -ENODEV; return -ENODEV;
} }
ocelot->bridge_mask |= BIT(ocelot_port->chip_port); ocelot->bridge_mask |= BIT(port);
return 0; return 0;
} }
static void ocelot_port_bridge_leave(struct ocelot_port *ocelot_port, static int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
struct net_device *bridge) struct net_device *bridge)
{ {
struct ocelot *ocelot = ocelot_port->ocelot; ocelot->bridge_mask &= ~BIT(port);
ocelot->bridge_mask &= ~BIT(ocelot_port->chip_port);
if (!ocelot->bridge_mask) if (!ocelot->bridge_mask)
ocelot->hw_bridge_dev = NULL; ocelot->hw_bridge_dev = NULL;
/* Clear bridge vlan settings before calling ocelot_vlan_port_apply */ ocelot_port_vlan_filtering(ocelot, port, 0);
ocelot_port->vlan_aware = 0; ocelot_port_set_pvid(ocelot, port, 0);
ocelot_port->pvid = 0; return ocelot_port_set_native_vlan(ocelot, port, 0);
ocelot_port->vid = 0;
} }
static void ocelot_set_aggr_pgids(struct ocelot *ocelot) static void ocelot_set_aggr_pgids(struct ocelot *ocelot)
...@@ -1594,20 +1748,18 @@ static void ocelot_setup_lag(struct ocelot *ocelot, int lag) ...@@ -1594,20 +1748,18 @@ static void ocelot_setup_lag(struct ocelot *ocelot, int lag)
} }
} }
static int ocelot_port_lag_join(struct ocelot_port *ocelot_port, static int ocelot_port_lag_join(struct ocelot *ocelot, int port,
struct net_device *bond) struct net_device *bond)
{ {
struct ocelot *ocelot = ocelot_port->ocelot;
int p = ocelot_port->chip_port;
int lag, lp;
struct net_device *ndev; struct net_device *ndev;
u32 bond_mask = 0; u32 bond_mask = 0;
int lag, lp;
rcu_read_lock(); rcu_read_lock();
for_each_netdev_in_bond_rcu(bond, ndev) { for_each_netdev_in_bond_rcu(bond, ndev) {
struct ocelot_port *port = netdev_priv(ndev); struct ocelot_port_private *priv = netdev_priv(ndev);
bond_mask |= BIT(port->chip_port); bond_mask |= BIT(priv->chip_port);
} }
rcu_read_unlock(); rcu_read_unlock();
...@@ -1616,17 +1768,17 @@ static int ocelot_port_lag_join(struct ocelot_port *ocelot_port, ...@@ -1616,17 +1768,17 @@ static int ocelot_port_lag_join(struct ocelot_port *ocelot_port,
/* If the new port is the lowest one, use it as the logical port from /* If the new port is the lowest one, use it as the logical port from
* now on * now on
*/ */
if (p == lp) { if (port == lp) {
lag = p; lag = port;
ocelot->lags[p] = bond_mask; ocelot->lags[port] = bond_mask;
bond_mask &= ~BIT(p); bond_mask &= ~BIT(port);
if (bond_mask) { if (bond_mask) {
lp = __ffs(bond_mask); lp = __ffs(bond_mask);
ocelot->lags[lp] = 0; ocelot->lags[lp] = 0;
} }
} else { } else {
lag = lp; lag = lp;
ocelot->lags[lp] |= BIT(p); ocelot->lags[lp] |= BIT(port);
} }
ocelot_setup_lag(ocelot, lag); ocelot_setup_lag(ocelot, lag);
...@@ -1635,34 +1787,32 @@ static int ocelot_port_lag_join(struct ocelot_port *ocelot_port, ...@@ -1635,34 +1787,32 @@ static int ocelot_port_lag_join(struct ocelot_port *ocelot_port,
return 0; return 0;
} }
static void ocelot_port_lag_leave(struct ocelot_port *ocelot_port, static void ocelot_port_lag_leave(struct ocelot *ocelot, int port,
struct net_device *bond) struct net_device *bond)
{ {
struct ocelot *ocelot = ocelot_port->ocelot;
int p = ocelot_port->chip_port;
u32 port_cfg; u32 port_cfg;
int i; int i;
/* Remove port from any lag */ /* Remove port from any lag */
for (i = 0; i < ocelot->num_phys_ports; i++) for (i = 0; i < ocelot->num_phys_ports; i++)
ocelot->lags[i] &= ~BIT(ocelot_port->chip_port); ocelot->lags[i] &= ~BIT(port);
/* if it was the logical port of the lag, move the lag config to the /* if it was the logical port of the lag, move the lag config to the
* next port * next port
*/ */
if (ocelot->lags[p]) { if (ocelot->lags[port]) {
int n = __ffs(ocelot->lags[p]); int n = __ffs(ocelot->lags[port]);
ocelot->lags[n] = ocelot->lags[p]; ocelot->lags[n] = ocelot->lags[port];
ocelot->lags[p] = 0; ocelot->lags[port] = 0;
ocelot_setup_lag(ocelot, n); ocelot_setup_lag(ocelot, n);
} }
port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, p); port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, port);
port_cfg &= ~ANA_PORT_PORT_CFG_PORTID_VAL_M; port_cfg &= ~ANA_PORT_PORT_CFG_PORTID_VAL_M;
ocelot_write_gix(ocelot, port_cfg | ANA_PORT_PORT_CFG_PORTID_VAL(p), ocelot_write_gix(ocelot, port_cfg | ANA_PORT_PORT_CFG_PORTID_VAL(port),
ANA_PORT_PORT_CFG, p); ANA_PORT_PORT_CFG, port);
ocelot_set_aggr_pgids(ocelot); ocelot_set_aggr_pgids(ocelot);
} }
...@@ -1677,28 +1827,30 @@ static int ocelot_netdevice_port_event(struct net_device *dev, ...@@ -1677,28 +1827,30 @@ static int ocelot_netdevice_port_event(struct net_device *dev,
unsigned long event, unsigned long event,
struct netdev_notifier_changeupper_info *info) struct netdev_notifier_changeupper_info *info)
{ {
struct ocelot_port *ocelot_port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;
int err = 0; int err = 0;
switch (event) { switch (event) {
case NETDEV_CHANGEUPPER: case NETDEV_CHANGEUPPER:
if (netif_is_bridge_master(info->upper_dev)) { if (netif_is_bridge_master(info->upper_dev)) {
if (info->linking) if (info->linking) {
err = ocelot_port_bridge_join(ocelot_port, err = ocelot_port_bridge_join(ocelot, port,
info->upper_dev); info->upper_dev);
else } else {
ocelot_port_bridge_leave(ocelot_port, err = ocelot_port_bridge_leave(ocelot, port,
info->upper_dev); info->upper_dev);
priv->vlan_aware = false;
ocelot_vlan_port_apply(ocelot_port->ocelot, }
ocelot_port);
} }
if (netif_is_lag_master(info->upper_dev)) { if (netif_is_lag_master(info->upper_dev)) {
if (info->linking) if (info->linking)
err = ocelot_port_lag_join(ocelot_port, err = ocelot_port_lag_join(ocelot, port,
info->upper_dev); info->upper_dev);
else else
ocelot_port_lag_leave(ocelot_port, ocelot_port_lag_leave(ocelot, port,
info->upper_dev); info->upper_dev);
} }
break; break;
...@@ -2001,24 +2153,48 @@ static int ocelot_init_timestamp(struct ocelot *ocelot) ...@@ -2001,24 +2153,48 @@ static int ocelot_init_timestamp(struct ocelot *ocelot)
return 0; return 0;
} }
static void ocelot_init_port(struct ocelot *ocelot, int port)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
INIT_LIST_HEAD(&ocelot_port->skbs);
/* Basic L2 initialization */
/* Drop frames with multicast source address */
ocelot_rmw_gix(ocelot, ANA_PORT_DROP_CFG_DROP_MC_SMAC_ENA,
ANA_PORT_DROP_CFG_DROP_MC_SMAC_ENA,
ANA_PORT_DROP_CFG, port);
/* Set default VLAN and tag type to 8021Q. */
ocelot_rmw_gix(ocelot, REW_PORT_VLAN_CFG_PORT_TPID(ETH_P_8021Q),
REW_PORT_VLAN_CFG_PORT_TPID_M,
REW_PORT_VLAN_CFG, port);
/* Enable vcap lookups */
ocelot_vcap_enable(ocelot, port);
}
int ocelot_probe_port(struct ocelot *ocelot, u8 port, int ocelot_probe_port(struct ocelot *ocelot, u8 port,
void __iomem *regs, void __iomem *regs,
struct phy_device *phy) struct phy_device *phy)
{ {
struct ocelot_port_private *priv;
struct ocelot_port *ocelot_port; struct ocelot_port *ocelot_port;
struct net_device *dev; struct net_device *dev;
int err; int err;
dev = alloc_etherdev(sizeof(struct ocelot_port)); dev = alloc_etherdev(sizeof(struct ocelot_port_private));
if (!dev) if (!dev)
return -ENOMEM; return -ENOMEM;
SET_NETDEV_DEV(dev, ocelot->dev); SET_NETDEV_DEV(dev, ocelot->dev);
ocelot_port = netdev_priv(dev); priv = netdev_priv(dev);
ocelot_port->dev = dev; priv->dev = dev;
priv->phy = phy;
priv->chip_port = port;
ocelot_port = &priv->port;
ocelot_port->ocelot = ocelot; ocelot_port->ocelot = ocelot;
ocelot_port->regs = regs; ocelot_port->regs = regs;
ocelot_port->chip_port = port;
ocelot_port->phy = phy;
ocelot->ports[port] = ocelot_port; ocelot->ports[port] = ocelot_port;
dev->netdev_ops = &ocelot_port_netdev_ops; dev->netdev_ops = &ocelot_port_netdev_ops;
...@@ -2033,33 +2209,64 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port, ...@@ -2033,33 +2209,64 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, ocelot_port->pvid, ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, ocelot_port->pvid,
ENTRYTYPE_LOCKED); ENTRYTYPE_LOCKED);
INIT_LIST_HEAD(&ocelot_port->skbs); ocelot_init_port(ocelot, port);
err = register_netdev(dev); err = register_netdev(dev);
if (err) { if (err) {
dev_err(ocelot->dev, "register_netdev failed\n"); dev_err(ocelot->dev, "register_netdev failed\n");
goto err_register_netdev; free_netdev(dev);
} }
/* Basic L2 initialization */ return err;
ocelot_vlan_port_apply(ocelot, ocelot_port); }
EXPORT_SYMBOL(ocelot_probe_port);
/* Enable vcap lookups */ void ocelot_set_cpu_port(struct ocelot *ocelot, int cpu,
ocelot_vcap_enable(ocelot, ocelot_port); enum ocelot_tag_prefix injection,
enum ocelot_tag_prefix extraction)
{
/* Configure and enable the CPU port. */
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, cpu);
ocelot_write_rix(ocelot, BIT(cpu), ANA_PGID_PGID, PGID_CPU);
ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_RECV_ENA |
ANA_PORT_PORT_CFG_PORTID_VAL(cpu),
ANA_PORT_PORT_CFG, cpu);
return 0; /* If the CPU port is a physical port, set up the port in Node
* Processor Interface (NPI) mode. This is the mode through which
* frames can be injected from and extracted to an external CPU.
* Only one port can be an NPI at the same time.
*/
if (cpu < ocelot->num_phys_ports) {
ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M |
QSYS_EXT_CPU_CFG_EXT_CPU_PORT(cpu),
QSYS_EXT_CPU_CFG);
}
err_register_netdev: /* CPU port Injection/Extraction configuration */
free_netdev(dev); ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE |
return err; QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) |
QSYS_SWITCH_PORT_MODE_PORT_ENA,
QSYS_SWITCH_PORT_MODE, cpu);
ocelot_write_rix(ocelot, SYS_PORT_MODE_INCL_XTR_HDR(extraction) |
SYS_PORT_MODE_INCL_INJ_HDR(injection),
SYS_PORT_MODE, cpu);
/* Configure the CPU port to be VLAN aware */
ocelot_write_gix(ocelot, ANA_PORT_VLAN_CFG_VLAN_VID(0) |
ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1),
ANA_PORT_VLAN_CFG, cpu);
ocelot->cpu = cpu;
} }
EXPORT_SYMBOL(ocelot_probe_port); EXPORT_SYMBOL(ocelot_set_cpu_port);
int ocelot_init(struct ocelot *ocelot) int ocelot_init(struct ocelot *ocelot)
{ {
u32 port;
int i, ret, cpu = ocelot->num_phys_ports;
char queue_name[32]; char queue_name[32];
int i, ret;
u32 port;
ocelot->lags = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports, ocelot->lags = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
sizeof(u32), GFP_KERNEL); sizeof(u32), GFP_KERNEL);
...@@ -2081,6 +2288,7 @@ int ocelot_init(struct ocelot *ocelot) ...@@ -2081,6 +2288,7 @@ int ocelot_init(struct ocelot *ocelot)
if (!ocelot->stats_queue) if (!ocelot->stats_queue)
return -ENOMEM; return -ENOMEM;
INIT_LIST_HEAD(&ocelot->multicast);
ocelot_mact_init(ocelot); ocelot_mact_init(ocelot);
ocelot_vlan_init(ocelot); ocelot_vlan_init(ocelot);
ocelot_ace_init(ocelot); ocelot_ace_init(ocelot);
...@@ -2138,13 +2346,6 @@ int ocelot_init(struct ocelot *ocelot) ...@@ -2138,13 +2346,6 @@ int ocelot_init(struct ocelot *ocelot)
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_SRC + port); ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_SRC + port);
} }
/* Configure and enable the CPU port. */
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, cpu);
ocelot_write_rix(ocelot, BIT(cpu), ANA_PGID_PGID, PGID_CPU);
ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_RECV_ENA |
ANA_PORT_PORT_CFG_PORTID_VAL(cpu),
ANA_PORT_PORT_CFG, cpu);
/* Allow broadcast MAC frames. */ /* Allow broadcast MAC frames. */
for (i = ocelot->num_phys_ports + 1; i < PGID_CPU; i++) { for (i = ocelot->num_phys_ports + 1; i < PGID_CPU; i++) {
u32 val = ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports - 1, 0)); u32 val = ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports - 1, 0));
...@@ -2157,13 +2358,6 @@ int ocelot_init(struct ocelot *ocelot) ...@@ -2157,13 +2358,6 @@ int ocelot_init(struct ocelot *ocelot)
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV4); ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV4);
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV6); ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV6);
/* CPU port Injection/Extraction configuration */
ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE |
QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) |
QSYS_SWITCH_PORT_MODE_PORT_ENA,
QSYS_SWITCH_PORT_MODE, cpu);
ocelot_write_rix(ocelot, SYS_PORT_MODE_INCL_XTR_HDR(1) |
SYS_PORT_MODE_INCL_INJ_HDR(1), SYS_PORT_MODE, cpu);
/* Allow manual injection via DEVCPU_QS registers, and byte swap these /* Allow manual injection via DEVCPU_QS registers, and byte swap these
* registers endianness. * registers endianness.
*/ */
......
...@@ -427,6 +427,13 @@ struct ocelot_multicast { ...@@ -427,6 +427,13 @@ struct ocelot_multicast {
u16 ports; u16 ports;
}; };
enum ocelot_tag_prefix {
OCELOT_TAG_PREFIX_DISABLED = 0,
OCELOT_TAG_PREFIX_NONE,
OCELOT_TAG_PREFIX_SHORT,
OCELOT_TAG_PREFIX_LONG,
};
struct ocelot_port; struct ocelot_port;
struct ocelot_stat_layout { struct ocelot_stat_layout {
...@@ -455,6 +462,7 @@ struct ocelot { ...@@ -455,6 +462,7 @@ struct ocelot {
u8 num_phys_ports; u8 num_phys_ports;
u8 num_cpu_ports; u8 num_cpu_ports;
u8 cpu;
struct ocelot_port **ports; struct ocelot_port **ports;
u32 *lags; u32 *lags;
...@@ -479,11 +487,9 @@ struct ocelot { ...@@ -479,11 +487,9 @@ struct ocelot {
}; };
struct ocelot_port { struct ocelot_port {
struct net_device *dev;
struct ocelot *ocelot; struct ocelot *ocelot;
struct phy_device *phy;
void __iomem *regs; void __iomem *regs;
u8 chip_port;
/* Ingress default VLAN (pvid) */ /* Ingress default VLAN (pvid) */
u16 pvid; u16 pvid;
...@@ -491,18 +497,23 @@ struct ocelot_port { ...@@ -491,18 +497,23 @@ struct ocelot_port {
/* Egress default VLAN (vid) */ /* Egress default VLAN (vid) */
u16 vid; u16 vid;
u8 vlan_aware; u8 ptp_cmd;
struct list_head skbs;
u8 ts_id;
};
u64 *stats; struct ocelot_port_private {
struct ocelot_port port;
struct net_device *dev;
struct phy_device *phy;
u8 chip_port;
u8 vlan_aware;
phy_interface_t phy_mode; phy_interface_t phy_mode;
struct phy *serdes; struct phy *serdes;
struct ocelot_port_tc tc; struct ocelot_port_tc tc;
u8 ptp_cmd;
struct list_head skbs;
u8 ts_id;
}; };
struct ocelot_skb { struct ocelot_skb {
...@@ -549,6 +560,10 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port, ...@@ -549,6 +560,10 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port,
void __iomem *regs, void __iomem *regs,
struct phy_device *phy); struct phy_device *phy);
void ocelot_set_cpu_port(struct ocelot *ocelot, int cpu,
enum ocelot_tag_prefix injection,
enum ocelot_tag_prefix extraction);
extern struct notifier_block ocelot_netdevice_nb; extern struct notifier_block ocelot_netdevice_nb;
extern struct notifier_block ocelot_switchdev_nb; extern struct notifier_block ocelot_switchdev_nb;
extern struct notifier_block ocelot_switchdev_blocking_nb; extern struct notifier_block ocelot_switchdev_blocking_nb;
......
...@@ -224,9 +224,9 @@ int ocelot_ace_rule_stats_update(struct ocelot_ace_rule *rule); ...@@ -224,9 +224,9 @@ int ocelot_ace_rule_stats_update(struct ocelot_ace_rule *rule);
int ocelot_ace_init(struct ocelot *ocelot); int ocelot_ace_init(struct ocelot *ocelot);
void ocelot_ace_deinit(void); void ocelot_ace_deinit(void);
int ocelot_setup_tc_block_flower_bind(struct ocelot_port *port, int ocelot_setup_tc_block_flower_bind(struct ocelot_port_private *priv,
struct flow_block_offload *f); struct flow_block_offload *f);
void ocelot_setup_tc_block_flower_unbind(struct ocelot_port *port, void ocelot_setup_tc_block_flower_unbind(struct ocelot_port_private *priv,
struct flow_block_offload *f); struct flow_block_offload *f);
#endif /* _MSCC_OCELOT_ACE_H_ */ #endif /* _MSCC_OCELOT_ACE_H_ */
...@@ -95,6 +95,8 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg) ...@@ -95,6 +95,8 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
do { do {
struct skb_shared_hwtstamps *shhwtstamps; struct skb_shared_hwtstamps *shhwtstamps;
struct ocelot_port_private *priv;
struct ocelot_port *ocelot_port;
u64 tod_in_ns, full_ts_in_ns; u64 tod_in_ns, full_ts_in_ns;
struct frame_info info = {}; struct frame_info info = {};
struct net_device *dev; struct net_device *dev;
...@@ -114,7 +116,10 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg) ...@@ -114,7 +116,10 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
ocelot_parse_ifh(ifh, &info); ocelot_parse_ifh(ifh, &info);
dev = ocelot->ports[info.port]->dev; ocelot_port = ocelot->ports[info.port];
priv = container_of(ocelot_port, struct ocelot_port_private,
port);
dev = priv->dev;
skb = netdev_alloc_skb(dev, info.len); skb = netdev_alloc_skb(dev, info.len);
...@@ -359,10 +364,13 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ...@@ -359,10 +364,13 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports, ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
sizeof(struct ocelot_port *), GFP_KERNEL); sizeof(struct ocelot_port *), GFP_KERNEL);
INIT_LIST_HEAD(&ocelot->multicast);
ocelot_init(ocelot); ocelot_init(ocelot);
ocelot_set_cpu_port(ocelot, ocelot->num_phys_ports,
OCELOT_TAG_PREFIX_NONE, OCELOT_TAG_PREFIX_NONE);
for_each_available_child_of_node(ports, portnp) { for_each_available_child_of_node(ports, portnp) {
struct ocelot_port_private *priv;
struct ocelot_port *ocelot_port;
struct device_node *phy_node; struct device_node *phy_node;
phy_interface_t phy_mode; phy_interface_t phy_mode;
struct phy_device *phy; struct phy_device *phy;
...@@ -398,13 +406,17 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ...@@ -398,13 +406,17 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
goto out_put_ports; goto out_put_ports;
} }
ocelot_port = ocelot->ports[port];
priv = container_of(ocelot_port, struct ocelot_port_private,
port);
err = of_get_phy_mode(portnp, &phy_mode); err = of_get_phy_mode(portnp, &phy_mode);
if (err && err != -ENODEV) if (err && err != -ENODEV)
goto out_put_ports; goto out_put_ports;
ocelot->ports[port]->phy_mode = phy_mode; priv->phy_mode = phy_mode;
switch (ocelot->ports[port]->phy_mode) { switch (priv->phy_mode) {
case PHY_INTERFACE_MODE_NA: case PHY_INTERFACE_MODE_NA:
continue; continue;
case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_SGMII:
...@@ -413,7 +425,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ...@@ -413,7 +425,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
/* Ensure clock signals and speed is set on all /* Ensure clock signals and speed is set on all
* QSGMII links * QSGMII links
*/ */
ocelot_port_writel(ocelot->ports[port], ocelot_port_writel(ocelot_port,
DEV_CLOCK_CFG_LINK_SPEED DEV_CLOCK_CFG_LINK_SPEED
(OCELOT_SPEED_1000), (OCELOT_SPEED_1000),
DEV_CLOCK_CFG); DEV_CLOCK_CFG);
...@@ -441,7 +453,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ...@@ -441,7 +453,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
goto out_put_ports; goto out_put_ports;
} }
ocelot->ports[port]->serdes = serdes; priv->serdes = serdes;
} }
register_netdevice_notifier(&ocelot_netdevice_nb); register_netdevice_notifier(&ocelot_netdevice_nb);
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
struct ocelot_port_block { struct ocelot_port_block {
struct ocelot_acl_block *block; struct ocelot_acl_block *block;
struct ocelot_port *port; struct ocelot_port_private *priv;
}; };
static int ocelot_flower_parse_action(struct flow_cls_offload *f, static int ocelot_flower_parse_action(struct flow_cls_offload *f,
...@@ -177,8 +177,8 @@ struct ocelot_ace_rule *ocelot_ace_rule_create(struct flow_cls_offload *f, ...@@ -177,8 +177,8 @@ struct ocelot_ace_rule *ocelot_ace_rule_create(struct flow_cls_offload *f,
if (!rule) if (!rule)
return NULL; return NULL;
rule->port = block->port; rule->port = &block->priv->port;
rule->chip_port = block->port->chip_port; rule->chip_port = block->priv->chip_port;
return rule; return rule;
} }
...@@ -202,7 +202,7 @@ static int ocelot_flower_replace(struct flow_cls_offload *f, ...@@ -202,7 +202,7 @@ static int ocelot_flower_replace(struct flow_cls_offload *f,
if (ret) if (ret)
return ret; return ret;
port_block->port->tc.offload_cnt++; port_block->priv->tc.offload_cnt++;
return 0; return 0;
} }
...@@ -213,14 +213,14 @@ static int ocelot_flower_destroy(struct flow_cls_offload *f, ...@@ -213,14 +213,14 @@ static int ocelot_flower_destroy(struct flow_cls_offload *f,
int ret; int ret;
rule.prio = f->common.prio; rule.prio = f->common.prio;
rule.port = port_block->port; rule.port = &port_block->priv->port;
rule.id = f->cookie; rule.id = f->cookie;
ret = ocelot_ace_rule_offload_del(&rule); ret = ocelot_ace_rule_offload_del(&rule);
if (ret) if (ret)
return ret; return ret;
port_block->port->tc.offload_cnt--; port_block->priv->tc.offload_cnt--;
return 0; return 0;
} }
...@@ -231,7 +231,7 @@ static int ocelot_flower_stats_update(struct flow_cls_offload *f, ...@@ -231,7 +231,7 @@ static int ocelot_flower_stats_update(struct flow_cls_offload *f,
int ret; int ret;
rule.prio = f->common.prio; rule.prio = f->common.prio;
rule.port = port_block->port; rule.port = &port_block->priv->port;
rule.id = f->cookie; rule.id = f->cookie;
ret = ocelot_ace_rule_stats_update(&rule); ret = ocelot_ace_rule_stats_update(&rule);
if (ret) if (ret)
...@@ -261,7 +261,7 @@ static int ocelot_setup_tc_block_cb_flower(enum tc_setup_type type, ...@@ -261,7 +261,7 @@ static int ocelot_setup_tc_block_cb_flower(enum tc_setup_type type,
{ {
struct ocelot_port_block *port_block = cb_priv; struct ocelot_port_block *port_block = cb_priv;
if (!tc_cls_can_offload_and_chain0(port_block->port->dev, type_data)) if (!tc_cls_can_offload_and_chain0(port_block->priv->dev, type_data))
return -EOPNOTSUPP; return -EOPNOTSUPP;
switch (type) { switch (type) {
...@@ -275,7 +275,7 @@ static int ocelot_setup_tc_block_cb_flower(enum tc_setup_type type, ...@@ -275,7 +275,7 @@ static int ocelot_setup_tc_block_cb_flower(enum tc_setup_type type,
} }
static struct ocelot_port_block* static struct ocelot_port_block*
ocelot_port_block_create(struct ocelot_port *port) ocelot_port_block_create(struct ocelot_port_private *priv)
{ {
struct ocelot_port_block *port_block; struct ocelot_port_block *port_block;
...@@ -283,7 +283,7 @@ ocelot_port_block_create(struct ocelot_port *port) ...@@ -283,7 +283,7 @@ ocelot_port_block_create(struct ocelot_port *port)
if (!port_block) if (!port_block)
return NULL; return NULL;
port_block->port = port; port_block->priv = priv;
return port_block; return port_block;
} }
...@@ -300,7 +300,7 @@ static void ocelot_tc_block_unbind(void *cb_priv) ...@@ -300,7 +300,7 @@ static void ocelot_tc_block_unbind(void *cb_priv)
ocelot_port_block_destroy(port_block); ocelot_port_block_destroy(port_block);
} }
int ocelot_setup_tc_block_flower_bind(struct ocelot_port *port, int ocelot_setup_tc_block_flower_bind(struct ocelot_port_private *priv,
struct flow_block_offload *f) struct flow_block_offload *f)
{ {
struct ocelot_port_block *port_block; struct ocelot_port_block *port_block;
...@@ -311,14 +311,14 @@ int ocelot_setup_tc_block_flower_bind(struct ocelot_port *port, ...@@ -311,14 +311,14 @@ int ocelot_setup_tc_block_flower_bind(struct ocelot_port *port,
return -EOPNOTSUPP; return -EOPNOTSUPP;
block_cb = flow_block_cb_lookup(f->block, block_cb = flow_block_cb_lookup(f->block,
ocelot_setup_tc_block_cb_flower, port); ocelot_setup_tc_block_cb_flower, priv);
if (!block_cb) { if (!block_cb) {
port_block = ocelot_port_block_create(port); port_block = ocelot_port_block_create(priv);
if (!port_block) if (!port_block)
return -ENOMEM; return -ENOMEM;
block_cb = flow_block_cb_alloc(ocelot_setup_tc_block_cb_flower, block_cb = flow_block_cb_alloc(ocelot_setup_tc_block_cb_flower,
port, port_block, priv, port_block,
ocelot_tc_block_unbind); ocelot_tc_block_unbind);
if (IS_ERR(block_cb)) { if (IS_ERR(block_cb)) {
ret = PTR_ERR(block_cb); ret = PTR_ERR(block_cb);
...@@ -339,13 +339,13 @@ int ocelot_setup_tc_block_flower_bind(struct ocelot_port *port, ...@@ -339,13 +339,13 @@ int ocelot_setup_tc_block_flower_bind(struct ocelot_port *port,
return ret; return ret;
} }
void ocelot_setup_tc_block_flower_unbind(struct ocelot_port *port, void ocelot_setup_tc_block_flower_unbind(struct ocelot_port_private *priv,
struct flow_block_offload *f) struct flow_block_offload *f)
{ {
struct flow_block_cb *block_cb; struct flow_block_cb *block_cb;
block_cb = flow_block_cb_lookup(f->block, block_cb = flow_block_cb_lookup(f->block,
ocelot_setup_tc_block_cb_flower, port); ocelot_setup_tc_block_cb_flower, priv);
if (!block_cb) if (!block_cb)
return; return;
......
...@@ -40,13 +40,12 @@ struct qos_policer_conf { ...@@ -40,13 +40,12 @@ struct qos_policer_conf {
u8 ipg; /* Size of IPG when MSCC_QOS_RATE_MODE_LINE is chosen */ u8 ipg; /* Size of IPG when MSCC_QOS_RATE_MODE_LINE is chosen */
}; };
static int qos_policer_conf_set(struct ocelot_port *port, u32 pol_ix, static int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix,
struct qos_policer_conf *conf) struct qos_policer_conf *conf)
{ {
u32 cf = 0, cir_ena = 0, frm_mode = POL_MODE_LINERATE; u32 cf = 0, cir_ena = 0, frm_mode = POL_MODE_LINERATE;
u32 cir = 0, cbs = 0, pir = 0, pbs = 0; u32 cir = 0, cbs = 0, pir = 0, pbs = 0;
bool cir_discard = 0, pir_discard = 0; bool cir_discard = 0, pir_discard = 0;
struct ocelot *ocelot = port->ocelot;
u32 pbs_max = 0, cbs_max = 0; u32 pbs_max = 0, cbs_max = 0;
u8 ipg = 20; u8 ipg = 20;
u32 value; u32 value;
...@@ -123,22 +122,26 @@ static int qos_policer_conf_set(struct ocelot_port *port, u32 pol_ix, ...@@ -123,22 +122,26 @@ static int qos_policer_conf_set(struct ocelot_port *port, u32 pol_ix,
/* Check limits */ /* Check limits */
if (pir > GENMASK(15, 0)) { if (pir > GENMASK(15, 0)) {
netdev_err(port->dev, "Invalid pir\n"); dev_err(ocelot->dev, "Invalid pir for port %d: %u (max %lu)\n",
port, pir, GENMASK(15, 0));
return -EINVAL; return -EINVAL;
} }
if (cir > GENMASK(15, 0)) { if (cir > GENMASK(15, 0)) {
netdev_err(port->dev, "Invalid cir\n"); dev_err(ocelot->dev, "Invalid cir for port %d: %u (max %lu)\n",
port, cir, GENMASK(15, 0));
return -EINVAL; return -EINVAL;
} }
if (pbs > pbs_max) { if (pbs > pbs_max) {
netdev_err(port->dev, "Invalid pbs\n"); dev_err(ocelot->dev, "Invalid pbs for port %d: %u (max %u)\n",
port, pbs, pbs_max);
return -EINVAL; return -EINVAL;
} }
if (cbs > cbs_max) { if (cbs > cbs_max) {
netdev_err(port->dev, "Invalid cbs\n"); dev_err(ocelot->dev, "Invalid cbs for port %d: %u (max %u)\n",
port, cbs, cbs_max);
return -EINVAL; return -EINVAL;
} }
...@@ -171,10 +174,9 @@ static int qos_policer_conf_set(struct ocelot_port *port, u32 pol_ix, ...@@ -171,10 +174,9 @@ static int qos_policer_conf_set(struct ocelot_port *port, u32 pol_ix,
return 0; return 0;
} }
int ocelot_port_policer_add(struct ocelot_port *port, int ocelot_port_policer_add(struct ocelot *ocelot, int port,
struct ocelot_policer *pol) struct ocelot_policer *pol)
{ {
struct ocelot *ocelot = port->ocelot;
struct qos_policer_conf pp = { 0 }; struct qos_policer_conf pp = { 0 };
int err; int err;
...@@ -185,11 +187,10 @@ int ocelot_port_policer_add(struct ocelot_port *port, ...@@ -185,11 +187,10 @@ int ocelot_port_policer_add(struct ocelot_port *port,
pp.pir = pol->rate; pp.pir = pol->rate;
pp.pbs = pol->burst; pp.pbs = pol->burst;
netdev_dbg(port->dev, dev_dbg(ocelot->dev, "%s: port %u pir %u kbps, pbs %u bytes\n",
"%s: port %u pir %u kbps, pbs %u bytes\n", __func__, port, pp.pir, pp.pbs);
__func__, port->chip_port, pp.pir, pp.pbs);
err = qos_policer_conf_set(port, POL_IX_PORT + port->chip_port, &pp); err = qos_policer_conf_set(ocelot, port, POL_IX_PORT + port, &pp);
if (err) if (err)
return err; return err;
...@@ -198,22 +199,21 @@ int ocelot_port_policer_add(struct ocelot_port *port, ...@@ -198,22 +199,21 @@ int ocelot_port_policer_add(struct ocelot_port *port,
ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER), ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
ANA_PORT_POL_CFG_PORT_POL_ENA | ANA_PORT_POL_CFG_PORT_POL_ENA |
ANA_PORT_POL_CFG_POL_ORDER_M, ANA_PORT_POL_CFG_POL_ORDER_M,
ANA_PORT_POL_CFG, port->chip_port); ANA_PORT_POL_CFG, port);
return 0; return 0;
} }
int ocelot_port_policer_del(struct ocelot_port *port) int ocelot_port_policer_del(struct ocelot *ocelot, int port)
{ {
struct ocelot *ocelot = port->ocelot;
struct qos_policer_conf pp = { 0 }; struct qos_policer_conf pp = { 0 };
int err; int err;
netdev_dbg(port->dev, "%s: port %u\n", __func__, port->chip_port); dev_dbg(ocelot->dev, "%s: port %u\n", __func__, port);
pp.mode = MSCC_QOS_RATE_MODE_DISABLED; pp.mode = MSCC_QOS_RATE_MODE_DISABLED;
err = qos_policer_conf_set(port, POL_IX_PORT + port->chip_port, &pp); err = qos_policer_conf_set(ocelot, port, POL_IX_PORT + port, &pp);
if (err) if (err)
return err; return err;
...@@ -221,7 +221,7 @@ int ocelot_port_policer_del(struct ocelot_port *port) ...@@ -221,7 +221,7 @@ int ocelot_port_policer_del(struct ocelot_port *port)
ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER), ANA_PORT_POL_CFG_POL_ORDER(POL_ORDER),
ANA_PORT_POL_CFG_PORT_POL_ENA | ANA_PORT_POL_CFG_PORT_POL_ENA |
ANA_PORT_POL_CFG_POL_ORDER_M, ANA_PORT_POL_CFG_POL_ORDER_M,
ANA_PORT_POL_CFG, port->chip_port); ANA_PORT_POL_CFG, port);
return 0; return 0;
} }
...@@ -14,9 +14,9 @@ struct ocelot_policer { ...@@ -14,9 +14,9 @@ struct ocelot_policer {
u32 burst; /* bytes */ u32 burst; /* bytes */
}; };
int ocelot_port_policer_add(struct ocelot_port *port, int ocelot_port_policer_add(struct ocelot *ocelot, int port,
struct ocelot_policer *pol); struct ocelot_policer *pol);
int ocelot_port_policer_del(struct ocelot_port *port); int ocelot_port_policer_del(struct ocelot *ocelot, int port);
#endif /* _MSCC_OCELOT_POLICE_H_ */ #endif /* _MSCC_OCELOT_POLICE_H_ */
...@@ -9,17 +9,19 @@ ...@@ -9,17 +9,19 @@
#include "ocelot_ace.h" #include "ocelot_ace.h"
#include <net/pkt_cls.h> #include <net/pkt_cls.h>
static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port, static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv,
struct tc_cls_matchall_offload *f, struct tc_cls_matchall_offload *f,
bool ingress) bool ingress)
{ {
struct netlink_ext_ack *extack = f->common.extack; struct netlink_ext_ack *extack = f->common.extack;
struct ocelot *ocelot = priv->port.ocelot;
struct ocelot_policer pol = { 0 }; struct ocelot_policer pol = { 0 };
struct flow_action_entry *action; struct flow_action_entry *action;
int port = priv->chip_port;
int err; int err;
netdev_dbg(port->dev, "%s: port %u command %d cookie %lu\n", netdev_dbg(priv->dev, "%s: port %u command %d cookie %lu\n",
__func__, port->chip_port, f->command, f->cookie); __func__, port, f->command, f->cookie);
if (!ingress) { if (!ingress) {
NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported"); NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
...@@ -34,7 +36,7 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port, ...@@ -34,7 +36,7 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
if (port->tc.block_shared) { if (priv->tc.block_shared) {
NL_SET_ERR_MSG_MOD(extack, NL_SET_ERR_MSG_MOD(extack,
"Rate limit is not supported on shared blocks"); "Rate limit is not supported on shared blocks");
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -47,7 +49,7 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port, ...@@ -47,7 +49,7 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
if (port->tc.police_id && port->tc.police_id != f->cookie) { if (priv->tc.police_id && priv->tc.police_id != f->cookie) {
NL_SET_ERR_MSG_MOD(extack, NL_SET_ERR_MSG_MOD(extack,
"Only one policer per port is supported\n"); "Only one policer per port is supported\n");
return -EEXIST; return -EEXIST;
...@@ -58,27 +60,27 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port, ...@@ -58,27 +60,27 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port,
PSCHED_NS2TICKS(action->police.burst), PSCHED_NS2TICKS(action->police.burst),
PSCHED_TICKS_PER_SEC); PSCHED_TICKS_PER_SEC);
err = ocelot_port_policer_add(port, &pol); err = ocelot_port_policer_add(ocelot, port, &pol);
if (err) { if (err) {
NL_SET_ERR_MSG_MOD(extack, "Could not add policer\n"); NL_SET_ERR_MSG_MOD(extack, "Could not add policer\n");
return err; return err;
} }
port->tc.police_id = f->cookie; priv->tc.police_id = f->cookie;
port->tc.offload_cnt++; priv->tc.offload_cnt++;
return 0; return 0;
case TC_CLSMATCHALL_DESTROY: case TC_CLSMATCHALL_DESTROY:
if (port->tc.police_id != f->cookie) if (priv->tc.police_id != f->cookie)
return -ENOENT; return -ENOENT;
err = ocelot_port_policer_del(port); err = ocelot_port_policer_del(ocelot, port);
if (err) { if (err) {
NL_SET_ERR_MSG_MOD(extack, NL_SET_ERR_MSG_MOD(extack,
"Could not delete policer\n"); "Could not delete policer\n");
return err; return err;
} }
port->tc.police_id = 0; priv->tc.police_id = 0;
port->tc.offload_cnt--; priv->tc.offload_cnt--;
return 0; return 0;
case TC_CLSMATCHALL_STATS: /* fall through */ case TC_CLSMATCHALL_STATS: /* fall through */
default: default:
...@@ -90,21 +92,21 @@ static int ocelot_setup_tc_block_cb(enum tc_setup_type type, ...@@ -90,21 +92,21 @@ static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
void *type_data, void *type_data,
void *cb_priv, bool ingress) void *cb_priv, bool ingress)
{ {
struct ocelot_port *port = cb_priv; struct ocelot_port_private *priv = cb_priv;
if (!tc_cls_can_offload_and_chain0(port->dev, type_data)) if (!tc_cls_can_offload_and_chain0(priv->dev, type_data))
return -EOPNOTSUPP; return -EOPNOTSUPP;
switch (type) { switch (type) {
case TC_SETUP_CLSMATCHALL: case TC_SETUP_CLSMATCHALL:
netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSMATCHALL %s\n", netdev_dbg(priv->dev, "tc_block_cb: TC_SETUP_CLSMATCHALL %s\n",
ingress ? "ingress" : "egress"); ingress ? "ingress" : "egress");
return ocelot_setup_tc_cls_matchall(port, type_data, ingress); return ocelot_setup_tc_cls_matchall(priv, type_data, ingress);
case TC_SETUP_CLSFLOWER: case TC_SETUP_CLSFLOWER:
return 0; return 0;
default: default:
netdev_dbg(port->dev, "tc_block_cb: type %d %s\n", netdev_dbg(priv->dev, "tc_block_cb: type %d %s\n",
type, type,
ingress ? "ingress" : "egress"); ingress ? "ingress" : "egress");
...@@ -130,19 +132,19 @@ static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type, ...@@ -130,19 +132,19 @@ static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
static LIST_HEAD(ocelot_block_cb_list); static LIST_HEAD(ocelot_block_cb_list);
static int ocelot_setup_tc_block(struct ocelot_port *port, static int ocelot_setup_tc_block(struct ocelot_port_private *priv,
struct flow_block_offload *f) struct flow_block_offload *f)
{ {
struct flow_block_cb *block_cb; struct flow_block_cb *block_cb;
flow_setup_cb_t *cb; flow_setup_cb_t *cb;
int err; int err;
netdev_dbg(port->dev, "tc_block command %d, binder_type %d\n", netdev_dbg(priv->dev, "tc_block command %d, binder_type %d\n",
f->command, f->binder_type); f->command, f->binder_type);
if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) { if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) {
cb = ocelot_setup_tc_block_cb_ig; cb = ocelot_setup_tc_block_cb_ig;
port->tc.block_shared = f->block_shared; priv->tc.block_shared = f->block_shared;
} else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) { } else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
cb = ocelot_setup_tc_block_cb_eg; cb = ocelot_setup_tc_block_cb_eg;
} else { } else {
...@@ -153,14 +155,14 @@ static int ocelot_setup_tc_block(struct ocelot_port *port, ...@@ -153,14 +155,14 @@ static int ocelot_setup_tc_block(struct ocelot_port *port,
switch (f->command) { switch (f->command) {
case FLOW_BLOCK_BIND: case FLOW_BLOCK_BIND:
if (flow_block_cb_is_busy(cb, port, &ocelot_block_cb_list)) if (flow_block_cb_is_busy(cb, priv, &ocelot_block_cb_list))
return -EBUSY; return -EBUSY;
block_cb = flow_block_cb_alloc(cb, port, port, NULL); block_cb = flow_block_cb_alloc(cb, priv, priv, NULL);
if (IS_ERR(block_cb)) if (IS_ERR(block_cb))
return PTR_ERR(block_cb); return PTR_ERR(block_cb);
err = ocelot_setup_tc_block_flower_bind(port, f); err = ocelot_setup_tc_block_flower_bind(priv, f);
if (err < 0) { if (err < 0) {
flow_block_cb_free(block_cb); flow_block_cb_free(block_cb);
return err; return err;
...@@ -169,11 +171,11 @@ static int ocelot_setup_tc_block(struct ocelot_port *port, ...@@ -169,11 +171,11 @@ static int ocelot_setup_tc_block(struct ocelot_port *port,
list_add_tail(&block_cb->driver_list, f->driver_block_list); list_add_tail(&block_cb->driver_list, f->driver_block_list);
return 0; return 0;
case FLOW_BLOCK_UNBIND: case FLOW_BLOCK_UNBIND:
block_cb = flow_block_cb_lookup(f->block, cb, port); block_cb = flow_block_cb_lookup(f->block, cb, priv);
if (!block_cb) if (!block_cb)
return -ENOENT; return -ENOENT;
ocelot_setup_tc_block_flower_unbind(port, f); ocelot_setup_tc_block_flower_unbind(priv, f);
flow_block_cb_remove(block_cb, f); flow_block_cb_remove(block_cb, f);
list_del(&block_cb->driver_list); list_del(&block_cb->driver_list);
return 0; return 0;
...@@ -185,11 +187,11 @@ static int ocelot_setup_tc_block(struct ocelot_port *port, ...@@ -185,11 +187,11 @@ static int ocelot_setup_tc_block(struct ocelot_port *port,
int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type, int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data) void *type_data)
{ {
struct ocelot_port *port = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
switch (type) { switch (type) {
case TC_SETUP_BLOCK: case TC_SETUP_BLOCK:
return ocelot_setup_tc_block(port, type_data); return ocelot_setup_tc_block(priv, type_data);
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment