2018-05-15 04:04:57 +08:00
|
|
|
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
|
|
|
|
/*
|
|
|
|
* Microsemi Ocelot Switch driver
|
|
|
|
*
|
|
|
|
* Copyright (c) 2017 Microsemi Corporation
|
|
|
|
*/
|
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include <linux/ethtool.h>
|
|
|
|
#include <linux/if_bridge.h>
|
|
|
|
#include <linux/if_ether.h>
|
|
|
|
#include <linux/if_vlan.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/phy.h>
|
2019-08-12 22:45:37 +08:00
|
|
|
#include <linux/ptp_clock_kernel.h>
|
2018-05-15 04:04:57 +08:00
|
|
|
#include <linux/skbuff.h>
|
2018-12-20 21:16:31 +08:00
|
|
|
#include <linux/iopoll.h>
|
2018-05-15 04:04:57 +08:00
|
|
|
#include <net/arp.h>
|
|
|
|
#include <net/netevent.h>
|
|
|
|
#include <net/rtnetlink.h>
|
|
|
|
#include <net/switchdev.h>
|
|
|
|
|
|
|
|
#include "ocelot.h"
|
2019-05-31 15:16:56 +08:00
|
|
|
#include "ocelot_ace.h"
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2018-12-20 21:16:31 +08:00
|
|
|
#define TABLE_UPDATE_SLEEP_US 10
|
|
|
|
#define TABLE_UPDATE_TIMEOUT_US 100000
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
/* MAC table entry types.
|
|
|
|
* ENTRYTYPE_NORMAL is subject to aging.
|
|
|
|
* ENTRYTYPE_LOCKED is not subject to aging.
|
|
|
|
* ENTRYTYPE_MACv4 is not subject to aging. For IPv4 multicast.
|
|
|
|
* ENTRYTYPE_MACv6 is not subject to aging. For IPv6 multicast.
|
|
|
|
*/
|
|
|
|
enum macaccess_entry_type {
|
|
|
|
ENTRYTYPE_NORMAL = 0,
|
|
|
|
ENTRYTYPE_LOCKED,
|
|
|
|
ENTRYTYPE_MACv4,
|
|
|
|
ENTRYTYPE_MACv6,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ocelot_mact_entry {
|
|
|
|
u8 mac[ETH_ALEN];
|
|
|
|
u16 vid;
|
|
|
|
enum macaccess_entry_type type;
|
|
|
|
};
|
|
|
|
|
2018-12-20 21:16:31 +08:00
|
|
|
static inline u32 ocelot_mact_read_macaccess(struct ocelot *ocelot)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
2018-12-20 21:16:31 +08:00
|
|
|
return ocelot_read(ocelot, ANA_TABLES_MACACCESS);
|
|
|
|
}
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2018-12-20 21:16:31 +08:00
|
|
|
static inline int ocelot_mact_wait_for_completion(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
u32 val;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2018-12-20 21:16:31 +08:00
|
|
|
return readx_poll_timeout(ocelot_mact_read_macaccess,
|
|
|
|
ocelot, val,
|
|
|
|
(val & ANA_TABLES_MACACCESS_MAC_TABLE_CMD_M) ==
|
|
|
|
MACACCESS_CMD_IDLE,
|
|
|
|
TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ocelot_mact_select(struct ocelot *ocelot,
|
|
|
|
const unsigned char mac[ETH_ALEN],
|
|
|
|
unsigned int vid)
|
|
|
|
{
|
|
|
|
u32 macl = 0, mach = 0;
|
|
|
|
|
|
|
|
/* Set the MAC address to handle and the vlan associated in a format
|
|
|
|
* understood by the hardware.
|
|
|
|
*/
|
|
|
|
mach |= vid << 16;
|
|
|
|
mach |= mac[0] << 8;
|
|
|
|
mach |= mac[1] << 0;
|
|
|
|
macl |= mac[2] << 24;
|
|
|
|
macl |= mac[3] << 16;
|
|
|
|
macl |= mac[4] << 8;
|
|
|
|
macl |= mac[5] << 0;
|
|
|
|
|
|
|
|
ocelot_write(ocelot, macl, ANA_TABLES_MACLDATA);
|
|
|
|
ocelot_write(ocelot, mach, ANA_TABLES_MACHDATA);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_mact_learn(struct ocelot *ocelot, int port,
|
|
|
|
const unsigned char mac[ETH_ALEN],
|
|
|
|
unsigned int vid,
|
|
|
|
enum macaccess_entry_type type)
|
|
|
|
{
|
|
|
|
ocelot_mact_select(ocelot, mac, vid);
|
|
|
|
|
|
|
|
/* Issue a write command */
|
|
|
|
ocelot_write(ocelot, ANA_TABLES_MACACCESS_VALID |
|
|
|
|
ANA_TABLES_MACACCESS_DEST_IDX(port) |
|
|
|
|
ANA_TABLES_MACACCESS_ENTRYTYPE(type) |
|
|
|
|
ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_LEARN),
|
|
|
|
ANA_TABLES_MACACCESS);
|
|
|
|
|
|
|
|
return ocelot_mact_wait_for_completion(ocelot);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_mact_forget(struct ocelot *ocelot,
|
|
|
|
const unsigned char mac[ETH_ALEN],
|
|
|
|
unsigned int vid)
|
|
|
|
{
|
|
|
|
ocelot_mact_select(ocelot, mac, vid);
|
|
|
|
|
|
|
|
/* Issue a forget command */
|
|
|
|
ocelot_write(ocelot,
|
|
|
|
ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_FORGET),
|
|
|
|
ANA_TABLES_MACACCESS);
|
|
|
|
|
|
|
|
return ocelot_mact_wait_for_completion(ocelot);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ocelot_mact_init(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
/* Configure the learning mode entries attributes:
|
|
|
|
* - Do not copy the frame to the CPU extraction queues.
|
|
|
|
* - Use the vlan and mac_cpoy for dmac lookup.
|
|
|
|
*/
|
|
|
|
ocelot_rmw(ocelot, 0,
|
|
|
|
ANA_AGENCTRL_LEARN_CPU_COPY | ANA_AGENCTRL_IGNORE_DMAC_FLAGS
|
|
|
|
| ANA_AGENCTRL_LEARN_FWD_KILL
|
|
|
|
| ANA_AGENCTRL_LEARN_IGNORE_VLAN,
|
|
|
|
ANA_AGENCTRL);
|
|
|
|
|
|
|
|
/* Clear the MAC table */
|
|
|
|
ocelot_write(ocelot, MACACCESS_CMD_INIT, ANA_TABLES_MACACCESS);
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:52 +08:00
|
|
|
static void ocelot_vcap_enable(struct ocelot *ocelot, int port)
|
2019-05-31 15:16:56 +08:00
|
|
|
{
|
|
|
|
ocelot_write_gix(ocelot, ANA_PORT_VCAP_S2_CFG_S2_ENA |
|
|
|
|
ANA_PORT_VCAP_S2_CFG_S2_IP6_CFG(0xa),
|
2019-11-09 21:02:52 +08:00
|
|
|
ANA_PORT_VCAP_S2_CFG, port);
|
2019-05-31 15:16:56 +08:00
|
|
|
}
|
|
|
|
|
2018-12-20 21:16:31 +08:00
|
|
|
static inline u32 ocelot_vlant_read_vlanaccess(struct ocelot *ocelot)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
2018-12-20 21:16:31 +08:00
|
|
|
return ocelot_read(ocelot, ANA_TABLES_VLANACCESS);
|
|
|
|
}
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2018-12-20 21:16:31 +08:00
|
|
|
static inline int ocelot_vlant_wait_for_completion(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
return readx_poll_timeout(ocelot_vlant_read_vlanaccess,
|
|
|
|
ocelot,
|
|
|
|
val,
|
|
|
|
(val & ANA_TABLES_VLANACCESS_VLAN_TBL_CMD_M) ==
|
|
|
|
ANA_TABLES_VLANACCESS_CMD_IDLE,
|
|
|
|
TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
|
2018-06-26 20:28:49 +08:00
|
|
|
static int ocelot_vlant_set_mask(struct ocelot *ocelot, u16 vid, u32 mask)
|
|
|
|
{
|
|
|
|
/* Select the VID to configure */
|
|
|
|
ocelot_write(ocelot, ANA_TABLES_VLANTIDX_V_INDEX(vid),
|
|
|
|
ANA_TABLES_VLANTIDX);
|
|
|
|
/* Set the vlan port members mask and issue a write command */
|
|
|
|
ocelot_write(ocelot, ANA_TABLES_VLANACCESS_VLAN_PORT_MASK(mask) |
|
|
|
|
ANA_TABLES_VLANACCESS_CMD_WRITE,
|
|
|
|
ANA_TABLES_VLANACCESS);
|
|
|
|
|
|
|
|
return ocelot_vlant_wait_for_completion(ocelot);
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:52 +08:00
|
|
|
static void ocelot_vlan_mode(struct ocelot *ocelot, int port,
|
2018-06-26 20:28:49 +08:00
|
|
|
netdev_features_t features)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
/* Filtering */
|
|
|
|
val = ocelot_read(ocelot, ANA_VLANMASK);
|
|
|
|
if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
|
2019-11-09 21:02:52 +08:00
|
|
|
val |= BIT(port);
|
2018-06-26 20:28:49 +08:00
|
|
|
else
|
2019-11-09 21:02:52 +08:00
|
|
|
val &= ~BIT(port);
|
2018-06-26 20:28:49 +08:00
|
|
|
ocelot_write(ocelot, val, ANA_VLANMASK);
|
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
static int ocelot_port_set_native_vlan(struct ocelot *ocelot, int port,
|
|
|
|
u16 vid)
|
2018-06-26 20:28:49 +08:00
|
|
|
{
|
2019-11-09 21:02:47 +08:00
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
u32 val = 0;
|
2018-06-26 20:28:49 +08:00
|
|
|
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
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);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
if (ocelot_port->vlan_aware && !ocelot_port->vid)
|
2018-06-26 20:28:49 +08:00
|
|
|
/* If port is vlan-aware and tagged, drop untagged and priority
|
|
|
|
* tagged frames.
|
|
|
|
*/
|
2019-11-09 21:02:47 +08:00
|
|
|
val = 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;
|
|
|
|
ocelot_rmw_gix(ocelot, val,
|
|
|
|
ANA_PORT_DROP_CFG_DROP_UNTAGGED_ENA |
|
2018-06-26 20:28:49 +08:00
|
|
|
ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
|
2019-11-09 21:02:47 +08:00
|
|
|
ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA,
|
|
|
|
ANA_PORT_DROP_CFG, port);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
if (ocelot_port->vlan_aware) {
|
2019-11-09 21:02:47 +08:00
|
|
|
if (ocelot_port->vid)
|
2018-06-26 20:28:49 +08:00
|
|
|
/* Tag all frames except when VID == DEFAULT_VLAN */
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
val = REW_TAG_CFG_TAG_CFG(1);
|
2018-06-26 20:28:49 +08:00
|
|
|
else
|
|
|
|
/* Tag all frames */
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
val = REW_TAG_CFG_TAG_CFG(3);
|
2019-11-09 21:02:47 +08:00
|
|
|
} else {
|
|
|
|
/* Port tagging disabled. */
|
|
|
|
val = REW_TAG_CFG_TAG_CFG(0);
|
2018-06-26 20:28:49 +08:00
|
|
|
}
|
|
|
|
ocelot_rmw_gix(ocelot, val,
|
|
|
|
REW_TAG_CFG_TAG_CFG_M,
|
2019-11-09 21:02:47 +08:00
|
|
|
REW_TAG_CFG, port);
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
|
|
|
|
return 0;
|
2019-11-09 21:02:47 +08:00
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
|
|
|
|
bool vlan_aware)
|
2019-11-09 21:02:47 +08:00
|
|
|
{
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
u32 val;
|
2019-11-09 21:02:47 +08:00
|
|
|
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
ocelot_port->vlan_aware = vlan_aware;
|
2019-11-09 21:02:47 +08:00
|
|
|
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
if (vlan_aware)
|
|
|
|
val = ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1);
|
|
|
|
else
|
|
|
|
val = 0;
|
|
|
|
ocelot_rmw_gix(ocelot, val,
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M,
|
|
|
|
ANA_PORT_VLAN_CFG, port);
|
2019-11-09 21:02:47 +08:00
|
|
|
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
ocelot_port_set_native_vlan(ocelot, port, ocelot_port->vid);
|
2019-11-09 21:02:47 +08:00
|
|
|
}
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_vlan_filtering);
|
2019-11-09 21:02:47 +08:00
|
|
|
|
|
|
|
/* 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;
|
2018-06-26 20:28:49 +08:00
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
|
|
|
|
bool untagged)
|
2018-06-26 20:28:49 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Make the port a member of the VLAN */
|
2019-11-09 21:02:47 +08:00
|
|
|
ocelot->vlan_mask[vid] |= BIT(port);
|
2018-06-26 20:28:49 +08:00
|
|
|
ret = ocelot_vlant_set_mask(ocelot, vid, ocelot->vlan_mask[vid]);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Default ingress vlan classification */
|
|
|
|
if (pvid)
|
2019-11-09 21:02:47 +08:00
|
|
|
ocelot_port_set_pvid(ocelot, port, vid);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
|
|
|
/* Untagged egress vlan clasification */
|
2019-11-09 21:02:47 +08:00
|
|
|
if (untagged) {
|
|
|
|
ret = ocelot_port_set_native_vlan(ocelot, port, vid);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2019-10-27 02:04:27 +08:00
|
|
|
}
|
2018-06-26 20:28:49 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_vlan_add);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
2019-11-09 21:02:48 +08:00
|
|
|
static int ocelot_vlan_vid_add(struct net_device *dev, u16 vid, bool pvid,
|
|
|
|
bool untagged)
|
2018-06-26 20:28:49 +08:00
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot_port *ocelot_port = &priv->port;
|
2019-11-09 21:02:47 +08:00
|
|
|
struct ocelot *ocelot = ocelot_port->ocelot;
|
2019-11-09 21:02:53 +08:00
|
|
|
int port = priv->chip_port;
|
2018-06-26 20:28:49 +08:00
|
|
|
int ret;
|
|
|
|
|
2019-11-09 21:02:48 +08:00
|
|
|
ret = ocelot_vlan_add(ocelot, port, vid, pvid, untagged);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2018-06-26 20:28:49 +08:00
|
|
|
|
2019-11-09 21:02:48 +08:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid)
|
2019-11-09 21:02:48 +08:00
|
|
|
{
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
|
|
|
int ret;
|
2018-06-26 20:28:49 +08:00
|
|
|
|
|
|
|
/* Stop the port from being a member of the vlan */
|
2019-11-09 21:02:47 +08:00
|
|
|
ocelot->vlan_mask[vid] &= ~BIT(port);
|
2018-06-26 20:28:49 +08:00
|
|
|
ret = ocelot_vlant_set_mask(ocelot, vid, ocelot->vlan_mask[vid]);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Ingress */
|
2019-11-09 21:02:47 +08:00
|
|
|
if (ocelot_port->pvid == vid)
|
|
|
|
ocelot_port_set_pvid(ocelot, port, 0);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
|
|
|
/* Egress */
|
2019-11-09 21:02:47 +08:00
|
|
|
if (ocelot_port->vid == vid)
|
|
|
|
ocelot_port_set_native_vlan(ocelot, port, 0);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_vlan_del);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
2019-11-09 21:02:48 +08:00
|
|
|
static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot *ocelot = priv->port.ocelot;
|
|
|
|
int port = priv->chip_port;
|
2019-11-09 21:02:48 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
static void ocelot_vlan_init(struct ocelot *ocelot)
|
|
|
|
{
|
2018-06-26 20:28:49 +08:00
|
|
|
u16 port, vid;
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
/* Clear VLAN table, by default all ports are members of all VLANs */
|
|
|
|
ocelot_write(ocelot, ANA_TABLES_VLANACCESS_CMD_INIT,
|
|
|
|
ANA_TABLES_VLANACCESS);
|
|
|
|
ocelot_vlant_wait_for_completion(ocelot);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
|
|
|
/* Configure the port VLAN memberships */
|
|
|
|
for (vid = 1; vid < VLAN_N_VID; vid++) {
|
|
|
|
ocelot->vlan_mask[vid] = 0;
|
|
|
|
ocelot_vlant_set_mask(ocelot, vid, ocelot->vlan_mask[vid]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Because VLAN filtering is enabled, we need VID 0 to get untagged
|
|
|
|
* traffic. It is added automatically if 8021q module is loaded, but
|
|
|
|
* we can't rely on it since module may be not loaded.
|
|
|
|
*/
|
|
|
|
ocelot->vlan_mask[0] = GENMASK(ocelot->num_phys_ports - 1, 0);
|
|
|
|
ocelot_vlant_set_mask(ocelot, 0, ocelot->vlan_mask[0]);
|
|
|
|
|
|
|
|
/* Set vlan ingress filter mask to all ports but the CPU port by
|
|
|
|
* default.
|
|
|
|
*/
|
2019-11-09 21:02:55 +08:00
|
|
|
ocelot_write(ocelot, GENMASK(ocelot->num_phys_ports - 1, 0),
|
|
|
|
ANA_VLANMASK);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
|
|
|
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_TAG_CFG, port);
|
|
|
|
}
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Watermark encode
|
|
|
|
* Bit 8: Unit; 0:1, 1:16
|
|
|
|
* Bit 7-0: Value to be multiplied with unit
|
|
|
|
*/
|
|
|
|
static u16 ocelot_wm_enc(u16 value)
|
|
|
|
{
|
|
|
|
if (value >= BIT(8))
|
|
|
|
return BIT(8) | (value / 16);
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_adjust_link(struct ocelot *ocelot, int port,
|
|
|
|
struct phy_device *phydev)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
2019-11-09 21:02:59 +08:00
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
2019-11-14 23:03:22 +08:00
|
|
|
int speed, mode = 0;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:59 +08:00
|
|
|
switch (phydev->speed) {
|
2018-05-15 04:04:57 +08:00
|
|
|
case SPEED_10:
|
|
|
|
speed = OCELOT_SPEED_10;
|
|
|
|
break;
|
|
|
|
case SPEED_100:
|
|
|
|
speed = OCELOT_SPEED_100;
|
|
|
|
break;
|
|
|
|
case SPEED_1000:
|
|
|
|
speed = OCELOT_SPEED_1000;
|
|
|
|
mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA;
|
|
|
|
break;
|
|
|
|
case SPEED_2500:
|
|
|
|
speed = OCELOT_SPEED_2500;
|
|
|
|
mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA;
|
|
|
|
break;
|
|
|
|
default:
|
2019-11-09 21:02:59 +08:00
|
|
|
dev_err(ocelot->dev, "Unsupported PHY speed on port %d: %d\n",
|
|
|
|
port, phydev->speed);
|
2018-05-15 04:04:57 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:59 +08:00
|
|
|
phy_print_status(phydev);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:59 +08:00
|
|
|
if (!phydev->link)
|
2018-05-15 04:04:57 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* Only full duplex supported for now */
|
2019-11-09 21:02:53 +08:00
|
|
|
ocelot_port_writel(ocelot_port, DEV_MAC_MODE_CFG_FDX_ENA |
|
2018-05-15 04:04:57 +08:00
|
|
|
mode, DEV_MAC_MODE_CFG);
|
|
|
|
|
2020-02-29 22:31:11 +08:00
|
|
|
/* Disable HDX fast control */
|
|
|
|
ocelot_port_writel(ocelot_port, DEV_PORT_MISC_HDX_FAST_DIS,
|
|
|
|
DEV_PORT_MISC);
|
|
|
|
|
|
|
|
/* SGMII only for now */
|
|
|
|
ocelot_port_writel(ocelot_port, PCS1G_MODE_CFG_SGMII_MODE_ENA,
|
|
|
|
PCS1G_MODE_CFG);
|
|
|
|
ocelot_port_writel(ocelot_port, PCS1G_SD_CFG_SD_SEL, PCS1G_SD_CFG);
|
|
|
|
|
|
|
|
/* Enable PCS */
|
|
|
|
ocelot_port_writel(ocelot_port, PCS1G_CFG_PCS_ENA, PCS1G_CFG);
|
|
|
|
|
|
|
|
/* No aneg on SGMII */
|
|
|
|
ocelot_port_writel(ocelot_port, 0, PCS1G_ANEG_CFG);
|
|
|
|
|
|
|
|
/* No loopback */
|
|
|
|
ocelot_port_writel(ocelot_port, 0, PCS1G_LB_CFG);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* Enable MAC module */
|
2019-11-09 21:02:53 +08:00
|
|
|
ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA |
|
2018-05-15 04:04:57 +08:00
|
|
|
DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG);
|
|
|
|
|
|
|
|
/* Take MAC, Port, Phy (intern) and PCS (SGMII/Serdes) clock out of
|
|
|
|
* reset */
|
2019-11-09 21:02:53 +08:00
|
|
|
ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(speed),
|
2018-05-15 04:04:57 +08:00
|
|
|
DEV_CLOCK_CFG);
|
|
|
|
|
|
|
|
/* No PFC */
|
|
|
|
ocelot_write_gix(ocelot, ANA_PFC_PFC_CFG_FC_LINK_SPEED(speed),
|
2019-11-09 21:02:53 +08:00
|
|
|
ANA_PFC_PFC_CFG, port);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* Core: Enable port for frame transfer */
|
|
|
|
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,
|
2019-11-09 21:02:53 +08:00
|
|
|
QSYS_SWITCH_PORT_MODE, port);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* Flow control */
|
|
|
|
ocelot_write_rix(ocelot, SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) |
|
|
|
|
SYS_MAC_FC_CFG_RX_FC_ENA | SYS_MAC_FC_CFG_TX_FC_ENA |
|
|
|
|
SYS_MAC_FC_CFG_ZERO_PAUSE_ENA |
|
|
|
|
SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) |
|
|
|
|
SYS_MAC_FC_CFG_FC_LINK_SPEED(speed),
|
2019-11-09 21:02:53 +08:00
|
|
|
SYS_MAC_FC_CFG, port);
|
|
|
|
ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_adjust_link);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:59 +08:00
|
|
|
static void ocelot_port_adjust_link(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot *ocelot = priv->port.ocelot;
|
|
|
|
int port = priv->chip_port;
|
|
|
|
|
|
|
|
ocelot_adjust_link(ocelot, port, dev->phydev);
|
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_port_enable(struct ocelot *ocelot, int port,
|
|
|
|
struct phy_device *phy)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
/* Enable receiving frames on the port, and activate auto-learning of
|
|
|
|
* MAC addresses.
|
|
|
|
*/
|
|
|
|
ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_LEARNAUTO |
|
|
|
|
ANA_PORT_PORT_CFG_RECV_ENA |
|
2019-11-09 21:02:53 +08:00
|
|
|
ANA_PORT_PORT_CFG_PORTID_VAL(port),
|
|
|
|
ANA_PORT_PORT_CFG, port);
|
2019-11-09 21:02:57 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_enable);
|
2019-11-09 21:02:57 +08:00
|
|
|
|
|
|
|
static int ocelot_port_open(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
2020-01-06 09:34:15 +08:00
|
|
|
struct ocelot_port *ocelot_port = &priv->port;
|
|
|
|
struct ocelot *ocelot = ocelot_port->ocelot;
|
2019-11-09 21:02:57 +08:00
|
|
|
int port = priv->chip_port;
|
|
|
|
int err;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
if (priv->serdes) {
|
|
|
|
err = phy_set_mode_ext(priv->serdes, PHY_MODE_ETHERNET,
|
2020-01-06 09:34:15 +08:00
|
|
|
ocelot_port->phy_mode);
|
2018-10-04 20:22:08 +08:00
|
|
|
if (err) {
|
|
|
|
netdev_err(dev, "Could not set mode of SerDes\n");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
err = phy_connect_direct(dev, priv->phy, &ocelot_port_adjust_link,
|
2020-01-06 09:34:15 +08:00
|
|
|
ocelot_port->phy_mode);
|
2018-05-15 04:04:57 +08:00
|
|
|
if (err) {
|
|
|
|
netdev_err(dev, "Could not attach to PHY\n");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
dev->phydev = priv->phy;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
phy_attached_info(priv->phy);
|
|
|
|
phy_start(priv->phy);
|
2019-11-09 21:02:57 +08:00
|
|
|
|
|
|
|
ocelot_port_enable(ocelot, port, priv->phy);
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_port_disable(struct ocelot *ocelot, int port)
|
2019-11-09 21:02:57 +08:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_disable);
|
2019-11-09 21:02:57 +08:00
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
static int ocelot_port_stop(struct net_device *dev)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
2019-11-09 21:02:57 +08:00
|
|
|
struct ocelot *ocelot = priv->port.ocelot;
|
|
|
|
int port = priv->chip_port;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
phy_disconnect(priv->phy);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
dev->phydev = NULL;
|
|
|
|
|
2019-11-09 21:02:57 +08:00
|
|
|
ocelot_port_disable(ocelot, port);
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Generate the IFH for frame injection
|
|
|
|
*
|
|
|
|
* The IFH is a 128bit-value
|
|
|
|
* bit 127: bypass the analyzer processing
|
|
|
|
* bit 56-67: destination mask
|
|
|
|
* bit 28-29: pop_cnt: 3 disables all rewriting of the frame
|
|
|
|
* bit 20-27: cpu extraction queue mask
|
|
|
|
* bit 16: tag type 0: C-tag, 1: S-tag
|
|
|
|
* bit 0-11: VID
|
|
|
|
*/
|
|
|
|
static int ocelot_gen_ifh(u32 *ifh, struct frame_info *info)
|
|
|
|
{
|
2019-08-12 22:45:37 +08:00
|
|
|
ifh[0] = IFH_INJ_BYPASS | ((0x1ff & info->rew_op) << 21);
|
2018-06-20 16:50:46 +08:00
|
|
|
ifh[1] = (0xf00 & info->port) >> 8;
|
2018-05-15 04:04:57 +08:00
|
|
|
ifh[2] = (0xff & info->port) << 24;
|
2018-06-20 16:50:46 +08:00
|
|
|
ifh[3] = (info->tag_type << 16) | info->vid;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-20 16:23:16 +08:00
|
|
|
int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
|
|
|
|
struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct skb_shared_info *shinfo = skb_shinfo(skb);
|
|
|
|
struct ocelot *ocelot = ocelot_port->ocelot;
|
|
|
|
|
|
|
|
if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
|
|
|
|
ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
|
|
|
|
shinfo->tx_flags |= SKBTX_IN_PROGRESS;
|
2019-11-27 15:27:57 +08:00
|
|
|
/* Store timestamp ID in cb[0] of sk_buff */
|
|
|
|
skb->cb[0] = ocelot_port->ts_id % 4;
|
|
|
|
skb_queue_tail(&ocelot_port->tx_skbs, skb);
|
2019-11-20 16:23:16 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -ENODATA;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_port_add_txtstamp_skb);
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
2019-08-12 22:45:37 +08:00
|
|
|
struct skb_shared_info *shinfo = skb_shinfo(skb);
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port *ocelot_port = &priv->port;
|
|
|
|
struct ocelot *ocelot = ocelot_port->ocelot;
|
2019-11-14 23:03:24 +08:00
|
|
|
u32 val, ifh[OCELOT_TAG_LEN / 4];
|
2018-05-15 04:04:57 +08:00
|
|
|
struct frame_info info = {};
|
|
|
|
u8 grp = 0; /* Send everything on CPU group 0 */
|
|
|
|
unsigned int i, count, last;
|
2019-11-09 21:02:53 +08:00
|
|
|
int port = priv->chip_port;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
val = ocelot_read(ocelot, QS_INJ_STATUS);
|
|
|
|
if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
|
|
|
|
(val & QS_INJ_STATUS_WMARK_REACHED(BIT(grp))))
|
|
|
|
return NETDEV_TX_BUSY;
|
|
|
|
|
|
|
|
ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
|
|
|
|
QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp);
|
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
info.port = BIT(port);
|
2018-06-20 16:50:46 +08:00
|
|
|
info.tag_type = IFH_TAG_TYPE_C;
|
|
|
|
info.vid = skb_vlan_tag_get(skb);
|
2019-08-12 22:45:37 +08:00
|
|
|
|
|
|
|
/* Check if timestamping is needed */
|
|
|
|
if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
|
2019-11-09 21:02:53 +08:00
|
|
|
info.rew_op = ocelot_port->ptp_cmd;
|
|
|
|
if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
|
|
|
|
info.rew_op |= (ocelot_port->ts_id % 4) << 3;
|
2019-08-12 22:45:37 +08:00
|
|
|
}
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
ocelot_gen_ifh(ifh, &info);
|
|
|
|
|
2019-11-14 23:03:24 +08:00
|
|
|
for (i = 0; i < OCELOT_TAG_LEN / 4; i++)
|
2018-06-22 17:50:52 +08:00
|
|
|
ocelot_write_rix(ocelot, (__force u32)cpu_to_be32(ifh[i]),
|
|
|
|
QS_INJ_WR, grp);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
count = (skb->len + 3) / 4;
|
|
|
|
last = skb->len % 4;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
ocelot_write_rix(ocelot, ((u32 *)skb->data)[i], QS_INJ_WR, grp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add padding */
|
|
|
|
while (i < (OCELOT_BUFFER_CELL_SZ / 4)) {
|
|
|
|
ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Indicate EOF and valid bytes in last word */
|
|
|
|
ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
|
|
|
|
QS_INJ_CTRL_VLD_BYTES(skb->len < OCELOT_BUFFER_CELL_SZ ? 0 : last) |
|
|
|
|
QS_INJ_CTRL_EOF,
|
|
|
|
QS_INJ_CTRL, grp);
|
|
|
|
|
|
|
|
/* Add dummy CRC */
|
|
|
|
ocelot_write_rix(ocelot, 0, QS_INJ_WR, grp);
|
|
|
|
skb_tx_timestamp(skb);
|
|
|
|
|
|
|
|
dev->stats.tx_packets++;
|
|
|
|
dev->stats.tx_bytes += skb->len;
|
|
|
|
|
2019-11-20 16:23:16 +08:00
|
|
|
if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
|
2019-11-09 21:02:53 +08:00
|
|
|
ocelot_port->ts_id++;
|
2019-08-12 22:45:37 +08:00
|
|
|
return NETDEV_TX_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_kfree_skb_any(skb);
|
2018-05-15 04:04:57 +08:00
|
|
|
return NETDEV_TX_OK;
|
|
|
|
}
|
|
|
|
|
2019-11-20 16:23:15 +08:00
|
|
|
static void ocelot_get_hwtimestamp(struct ocelot *ocelot,
|
|
|
|
struct timespec64 *ts)
|
2019-08-12 22:45:37 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
|
|
|
|
|
|
|
|
/* Read current PTP time to get seconds */
|
|
|
|
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
|
|
|
|
|
|
|
|
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
|
|
|
|
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_SAVE);
|
|
|
|
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
|
|
|
|
ts->tv_sec = ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
|
|
|
|
|
|
|
|
/* Read packet HW timestamp from FIFO */
|
|
|
|
val = ocelot_read(ocelot, SYS_PTP_TXSTAMP);
|
|
|
|
ts->tv_nsec = SYS_PTP_TXSTAMP_PTP_TXSTAMP(val);
|
|
|
|
|
|
|
|
/* Sec has incremented since the ts was registered */
|
|
|
|
if ((ts->tv_sec & 0x1) != !!(val & SYS_PTP_TXSTAMP_PTP_TXSTAMP_SEC))
|
|
|
|
ts->tv_sec--;
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
|
|
|
|
}
|
2019-11-20 16:23:15 +08:00
|
|
|
|
|
|
|
void ocelot_get_txtstamp(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
int budget = OCELOT_PTP_QUEUE_SZ;
|
|
|
|
|
|
|
|
while (budget--) {
|
2019-11-27 15:27:57 +08:00
|
|
|
struct sk_buff *skb, *skb_tmp, *skb_match = NULL;
|
2019-11-20 16:23:15 +08:00
|
|
|
struct skb_shared_hwtstamps shhwtstamps;
|
|
|
|
struct ocelot_port *port;
|
|
|
|
struct timespec64 ts;
|
2019-11-27 15:27:57 +08:00
|
|
|
unsigned long flags;
|
2019-11-20 16:23:15 +08:00
|
|
|
u32 val, id, txport;
|
|
|
|
|
|
|
|
val = ocelot_read(ocelot, SYS_PTP_STATUS);
|
|
|
|
|
|
|
|
/* Check if a timestamp can be retrieved */
|
|
|
|
if (!(val & SYS_PTP_STATUS_PTP_MESS_VLD))
|
|
|
|
break;
|
|
|
|
|
|
|
|
WARN_ON(val & SYS_PTP_STATUS_PTP_OVFL);
|
|
|
|
|
|
|
|
/* Retrieve the ts ID and Tx port */
|
|
|
|
id = SYS_PTP_STATUS_PTP_MESS_ID_X(val);
|
|
|
|
txport = SYS_PTP_STATUS_PTP_MESS_TXPORT_X(val);
|
|
|
|
|
|
|
|
/* Retrieve its associated skb */
|
|
|
|
port = ocelot->ports[txport];
|
|
|
|
|
2019-11-27 15:27:57 +08:00
|
|
|
spin_lock_irqsave(&port->tx_skbs.lock, flags);
|
2019-11-20 16:23:15 +08:00
|
|
|
|
2019-11-27 15:27:57 +08:00
|
|
|
skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) {
|
|
|
|
if (skb->cb[0] != id)
|
|
|
|
continue;
|
|
|
|
__skb_unlink(skb, &port->tx_skbs);
|
|
|
|
skb_match = skb;
|
2019-11-27 15:27:56 +08:00
|
|
|
break;
|
2019-11-20 16:23:15 +08:00
|
|
|
}
|
|
|
|
|
2019-11-27 15:27:57 +08:00
|
|
|
spin_unlock_irqrestore(&port->tx_skbs.lock, flags);
|
|
|
|
|
2019-11-20 16:23:15 +08:00
|
|
|
/* Next ts */
|
|
|
|
ocelot_write(ocelot, SYS_PTP_NXT_PTP_NXT, SYS_PTP_NXT);
|
|
|
|
|
2019-11-27 15:27:57 +08:00
|
|
|
if (unlikely(!skb_match))
|
2019-11-20 16:23:15 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Get the h/w timestamp */
|
|
|
|
ocelot_get_hwtimestamp(ocelot, &ts);
|
|
|
|
|
|
|
|
/* Set the timestamp into the skb */
|
|
|
|
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
|
|
|
|
shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
|
2019-11-27 15:27:57 +08:00
|
|
|
skb_tstamp_tx(skb_match, &shhwtstamps);
|
2019-11-20 16:23:15 +08:00
|
|
|
|
2019-11-27 15:27:57 +08:00
|
|
|
dev_kfree_skb_any(skb_match);
|
2019-11-20 16:23:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_get_txtstamp);
|
2019-08-12 22:45:37 +08:00
|
|
|
|
2019-05-22 00:52:55 +08:00
|
|
|
static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot_port *ocelot_port = &priv->port;
|
|
|
|
struct ocelot *ocelot = ocelot_port->ocelot;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
return ocelot_mact_forget(ocelot, addr, ocelot_port->pvid);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
|
2019-05-22 00:52:55 +08:00
|
|
|
static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot_port *ocelot_port = &priv->port;
|
|
|
|
struct ocelot *ocelot = ocelot_port->ocelot;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
return ocelot_mact_learn(ocelot, PGID_CPU, addr, ocelot_port->pvid,
|
2019-05-22 00:52:55 +08:00
|
|
|
ENTRYTYPE_LOCKED);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ocelot_set_rx_mode(struct net_device *dev)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot *ocelot = priv->port.ocelot;
|
2018-05-15 04:04:57 +08:00
|
|
|
u32 val;
|
2019-11-09 21:02:53 +08:00
|
|
|
int i;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* This doesn't handle promiscuous mode because the bridge core is
|
|
|
|
* setting IFF_PROMISC on all slave interfaces and all frames would be
|
|
|
|
* forwarded to the CPU port.
|
|
|
|
*/
|
|
|
|
val = GENMASK(ocelot->num_phys_ports - 1, 0);
|
|
|
|
for (i = ocelot->num_phys_ports + 1; i < PGID_CPU; i++)
|
|
|
|
ocelot_write_rix(ocelot, val, ANA_PGID_PGID, i);
|
|
|
|
|
2019-05-22 00:52:55 +08:00
|
|
|
__dev_mc_sync(dev, ocelot_mc_sync, ocelot_mc_unsync);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_port_get_phys_port_name(struct net_device *dev,
|
|
|
|
char *buf, size_t len)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
int port = priv->chip_port;
|
2018-05-15 04:04:57 +08:00
|
|
|
int ret;
|
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
ret = snprintf(buf, len, "p%d", port);
|
2018-05-15 04:04:57 +08:00
|
|
|
if (ret >= len)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_port_set_mac_address(struct net_device *dev, void *p)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot_port *ocelot_port = &priv->port;
|
|
|
|
struct ocelot *ocelot = ocelot_port->ocelot;
|
2018-05-15 04:04:57 +08:00
|
|
|
const struct sockaddr *addr = p;
|
|
|
|
|
|
|
|
/* Learn the new net device MAC address in the mac table. */
|
2019-11-09 21:02:53 +08:00
|
|
|
ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, ocelot_port->pvid,
|
2018-05-15 04:04:57 +08:00
|
|
|
ENTRYTYPE_LOCKED);
|
|
|
|
/* Then forget the previous one. */
|
2019-11-09 21:02:53 +08:00
|
|
|
ocelot_mact_forget(ocelot, dev->dev_addr, ocelot_port->pvid);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
ether_addr_copy(dev->dev_addr, addr->sa_data);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ocelot_get_stats64(struct net_device *dev,
|
|
|
|
struct rtnl_link_stats64 *stats)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot *ocelot = priv->port.ocelot;
|
|
|
|
int port = priv->chip_port;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* Configure the port to read the stats from */
|
2019-11-09 21:02:53 +08:00
|
|
|
ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port),
|
2018-05-15 04:04:57 +08:00
|
|
|
SYS_STAT_CFG);
|
|
|
|
|
|
|
|
/* Get Rx stats */
|
|
|
|
stats->rx_bytes = ocelot_read(ocelot, SYS_COUNT_RX_OCTETS);
|
|
|
|
stats->rx_packets = ocelot_read(ocelot, SYS_COUNT_RX_SHORTS) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_RX_FRAGMENTS) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_RX_JABBERS) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_RX_LONGS) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_RX_64) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_RX_65_127) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_RX_128_255) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_RX_256_1023) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_RX_1024_1526) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_RX_1527_MAX);
|
|
|
|
stats->multicast = ocelot_read(ocelot, SYS_COUNT_RX_MULTICAST);
|
|
|
|
stats->rx_dropped = dev->stats.rx_dropped;
|
|
|
|
|
|
|
|
/* Get Tx stats */
|
|
|
|
stats->tx_bytes = ocelot_read(ocelot, SYS_COUNT_TX_OCTETS);
|
|
|
|
stats->tx_packets = ocelot_read(ocelot, SYS_COUNT_TX_64) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_TX_65_127) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_TX_128_511) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_TX_512_1023) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_TX_1024_1526) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_TX_1527_MAX);
|
|
|
|
stats->tx_dropped = ocelot_read(ocelot, SYS_COUNT_TX_DROPS) +
|
|
|
|
ocelot_read(ocelot, SYS_COUNT_TX_AGING);
|
|
|
|
stats->collisions = ocelot_read(ocelot, SYS_COUNT_TX_COLLISION);
|
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_fdb_add(struct ocelot *ocelot, int port,
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
const unsigned char *addr, u16 vid)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2018-06-26 20:28:49 +08:00
|
|
|
if (!vid) {
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
if (!ocelot_port->vlan_aware)
|
2018-06-26 20:28:49 +08:00
|
|
|
/* If the bridge is not VLAN aware and no VID was
|
|
|
|
* provided, set it to pvid to ensure the MAC entry
|
|
|
|
* matches incoming untagged packets
|
|
|
|
*/
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
vid = ocelot_port->pvid;
|
2018-06-26 20:28:49 +08:00
|
|
|
else
|
|
|
|
/* If the bridge is VLAN aware a VID must be provided as
|
|
|
|
* otherwise the learnt entry wouldn't match any frame.
|
|
|
|
*/
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
return ocelot_mact_learn(ocelot, port, addr, vid, ENTRYTYPE_LOCKED);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_fdb_add);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
|
|
|
|
struct net_device *dev,
|
|
|
|
const unsigned char *addr,
|
|
|
|
u16 vid, u16 flags,
|
|
|
|
struct netlink_ext_ack *extack)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot *ocelot = priv->port.ocelot;
|
|
|
|
int port = priv->chip_port;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
return ocelot_fdb_add(ocelot, port, addr, vid);
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_fdb_del(struct ocelot *ocelot, int port,
|
|
|
|
const unsigned char *addr, u16 vid)
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
{
|
2018-05-15 04:04:57 +08:00
|
|
|
return ocelot_mact_forget(ocelot, addr, vid);
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_fdb_del);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
|
|
|
|
struct net_device *dev,
|
|
|
|
const unsigned char *addr, u16 vid)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot *ocelot = priv->port.ocelot;
|
|
|
|
int port = priv->chip_port;
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
return ocelot_fdb_del(ocelot, port, addr, vid);
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
}
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
struct ocelot_dump_ctx {
|
|
|
|
struct net_device *dev;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct netlink_callback *cb;
|
|
|
|
int idx;
|
|
|
|
};
|
|
|
|
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
static int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
|
|
|
|
bool is_static, void *data)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
struct ocelot_dump_ctx *dump = data;
|
2018-05-15 04:04:57 +08:00
|
|
|
u32 portid = NETLINK_CB(dump->cb->skb).portid;
|
|
|
|
u32 seq = dump->cb->nlh->nlmsg_seq;
|
|
|
|
struct nlmsghdr *nlh;
|
|
|
|
struct ndmsg *ndm;
|
|
|
|
|
|
|
|
if (dump->idx < dump->cb->args[2])
|
|
|
|
goto skip;
|
|
|
|
|
|
|
|
nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
|
|
|
|
sizeof(*ndm), NLM_F_MULTI);
|
|
|
|
if (!nlh)
|
|
|
|
return -EMSGSIZE;
|
|
|
|
|
|
|
|
ndm = nlmsg_data(nlh);
|
|
|
|
ndm->ndm_family = AF_BRIDGE;
|
|
|
|
ndm->ndm_pad1 = 0;
|
|
|
|
ndm->ndm_pad2 = 0;
|
|
|
|
ndm->ndm_flags = NTF_SELF;
|
|
|
|
ndm->ndm_type = 0;
|
|
|
|
ndm->ndm_ifindex = dump->dev->ifindex;
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr))
|
2018-05-15 04:04:57 +08:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid))
|
2018-05-15 04:04:57 +08:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
nlmsg_end(dump->skb, nlh);
|
|
|
|
|
|
|
|
skip:
|
|
|
|
dump->idx++;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
nlmsg_cancel(dump->skb, nlh);
|
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col,
|
|
|
|
struct ocelot_mact_entry *entry)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
u32 val, dst, macl, mach;
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
char mac[ETH_ALEN];
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* Set row and column to read from */
|
|
|
|
ocelot_field_write(ocelot, ANA_TABLES_MACTINDX_M_INDEX, row);
|
|
|
|
ocelot_field_write(ocelot, ANA_TABLES_MACTINDX_BUCKET, col);
|
|
|
|
|
|
|
|
/* Issue a read command */
|
|
|
|
ocelot_write(ocelot,
|
|
|
|
ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_READ),
|
|
|
|
ANA_TABLES_MACACCESS);
|
|
|
|
|
|
|
|
if (ocelot_mact_wait_for_completion(ocelot))
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
|
|
|
|
/* Read the entry flags */
|
|
|
|
val = ocelot_read(ocelot, ANA_TABLES_MACACCESS);
|
|
|
|
if (!(val & ANA_TABLES_MACACCESS_VALID))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* If the entry read has another port configured as its destination,
|
|
|
|
* do not report it.
|
|
|
|
*/
|
|
|
|
dst = (val & ANA_TABLES_MACACCESS_DEST_IDX_M) >> 3;
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
if (dst != port)
|
2018-05-15 04:04:57 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Get the entry's MAC address and VLAN id */
|
|
|
|
macl = ocelot_read(ocelot, ANA_TABLES_MACLDATA);
|
|
|
|
mach = ocelot_read(ocelot, ANA_TABLES_MACHDATA);
|
|
|
|
|
|
|
|
mac[0] = (mach >> 8) & 0xff;
|
|
|
|
mac[1] = (mach >> 0) & 0xff;
|
|
|
|
mac[2] = (macl >> 24) & 0xff;
|
|
|
|
mac[3] = (macl >> 16) & 0xff;
|
|
|
|
mac[4] = (macl >> 8) & 0xff;
|
|
|
|
mac[5] = (macl >> 0) & 0xff;
|
|
|
|
|
|
|
|
entry->vid = (mach >> 16) & 0xfff;
|
|
|
|
ether_addr_copy(entry->mac, mac);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_fdb_dump(struct ocelot *ocelot, int port,
|
|
|
|
dsa_fdb_dump_cb_t *cb, void *data)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
int i, j;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* Loop through all the mac tables entries. There are 1024 rows of 4
|
|
|
|
* entries.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 1024; i++) {
|
|
|
|
for (j = 0; j < 4; j++) {
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
struct ocelot_mact_entry entry;
|
|
|
|
bool is_static;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ocelot_mact_read(ocelot, port, i, j, &entry);
|
2018-05-15 04:04:57 +08:00
|
|
|
/* If the entry is invalid (wrong port, invalid...),
|
|
|
|
* skip it.
|
|
|
|
*/
|
|
|
|
if (ret == -EINVAL)
|
|
|
|
continue;
|
|
|
|
else if (ret)
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
is_static = (entry.type == ENTRYTYPE_LOCKED);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
ret = cb(entry.mac, entry.vid, is_static, data);
|
2018-05-15 04:04:57 +08:00
|
|
|
if (ret)
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
return ret;
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_fdb_dump);
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot *ocelot = priv->port.ocelot;
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
struct ocelot_dump_ctx dump = {
|
|
|
|
.dev = dev,
|
|
|
|
.skb = skb,
|
|
|
|
.cb = cb,
|
|
|
|
.idx = *idx,
|
|
|
|
};
|
2019-11-09 21:02:53 +08:00
|
|
|
int port = priv->chip_port;
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
int ret;
|
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
ret = ocelot_fdb_dump(ocelot, port, ocelot_port_fdb_do_dump, &dump);
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
*idx = dump.idx;
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-06-26 20:28:49 +08:00
|
|
|
static int ocelot_vlan_rx_add_vid(struct net_device *dev, __be16 proto,
|
|
|
|
u16 vid)
|
|
|
|
{
|
net: mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is up
Background information: the driver operates the hardware in a mode where
a single VLAN can be transmitted as untagged on a particular egress
port. That is the "native VLAN on trunk port" use case. Its value is
held in port->vid.
Consider the following command sequence (no network manager, all
interfaces are down, debugging prints added by me):
$ ip link add dev br0 type bridge vlan_filtering 1
$ ip link set dev swp0 master br0
Kernel code path during last command:
br_add_slave -> ocelot_netdevice_port_event (NETDEV_CHANGEUPPER):
[ 21.401901] ocelot_vlan_port_apply: port 0 vlan aware 0 pvid 0 vid 0
br_add_slave -> nbp_vlan_init -> switchdev_port_attr_set -> ocelot_port_attr_set (SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING):
[ 21.413335] ocelot_vlan_port_apply: port 0 vlan aware 1 pvid 0 vid 0
br_add_slave -> nbp_vlan_init -> nbp_vlan_add -> br_switchdev_port_vlan_add -> switchdev_port_obj_add -> ocelot_port_obj_add -> ocelot_vlan_vid_add
[ 21.667421] ocelot_vlan_port_apply: port 0 vlan aware 1 pvid 1 vid 1
So far so good. The bridge has replaced the driver's default pvid used
in standalone mode (0) with its own default_pvid (1). The port's vid
(native VLAN) has also changed from 0 to 1.
$ ip link set dev swp0 up
[ 31.722956] 8021q: adding VLAN 0 to HW filter on device swp0
do_setlink -> dev_change_flags -> vlan_vid_add -> ocelot_vlan_rx_add_vid -> ocelot_vlan_vid_add:
[ 31.728700] ocelot_vlan_port_apply: port 0 vlan aware 1 pvid 1 vid 0
The 8021q module uses the .ndo_vlan_rx_add_vid API on .ndo_open to make
ports be able to transmit and receive 802.1p-tagged traffic by default.
This API is supposed to offload a VLAN sub-interface, which for a switch
port means to add a VLAN that is not a pvid, and tagged on egress.
But the driver implementation of .ndo_vlan_rx_add_vid is wrong: it adds
back vid 0 as "egress untagged". Now back to the initial paragraph:
there is a single untagged VID that the driver keeps track of, and that
has just changed from 1 (the pvid) to 0. So this breaks the bridge
core's expectation, because it has changed vid 1 from untagged to
tagged, when what the user sees is.
$ bridge vlan
port vlan ids
swp0 1 PVID Egress Untagged
br0 1 PVID Egress Untagged
But curiously, instead of manifesting itself as "untagged and
pvid-tagged traffic gets sent as tagged on egress", the bug:
- is hidden when vlan_filtering=0
- manifests as dropped traffic when vlan_filtering=1, due to this setting:
if (port->vlan_aware && !port->vid)
/* If port is vlan-aware and tagged, drop untagged and priority
* tagged frames.
*/
val |= 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;
which would have made sense if it weren't for this bug. The setting's
intention was "this is a trunk port with no native VLAN, so don't accept
untagged traffic". So the driver was never expecting to set VLAN 0 as
the value of the native VLAN, 0 was just encoding for "invalid".
So the fix is to not send 802.1p traffic as untagged, because that would
change the port's native vlan to 0, unbeknownst to the bridge, and
trigger unexpected code paths in the driver.
Cc: Antoine Tenart <antoine.tenart@bootlin.com>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Fixes: 7142529f1688 ("net: mscc: ocelot: add VLAN filtering")
Signed-off-by: Vladimir Oltean <olteanv@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-10-27 02:04:26 +08:00
|
|
|
return ocelot_vlan_vid_add(dev, vid, false, false);
|
2018-06-26 20:28:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,
|
|
|
|
u16 vid)
|
|
|
|
{
|
|
|
|
return ocelot_vlan_vid_del(dev, vid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_set_features(struct net_device *dev,
|
|
|
|
netdev_features_t features)
|
|
|
|
{
|
|
|
|
netdev_features_t changed = dev->features ^ features;
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot *ocelot = priv->port.ocelot;
|
|
|
|
int port = priv->chip_port;
|
2018-06-26 20:28:49 +08:00
|
|
|
|
2019-05-28 20:49:17 +08:00
|
|
|
if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC) &&
|
2019-11-09 21:02:53 +08:00
|
|
|
priv->tc.offload_cnt) {
|
2019-05-28 20:49:17 +08:00
|
|
|
netdev_err(dev,
|
|
|
|
"Cannot disable HW TC offload while offloads active\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2018-06-26 20:28:49 +08:00
|
|
|
if (changed & NETIF_F_HW_VLAN_CTAG_FILTER)
|
2019-11-09 21:02:52 +08:00
|
|
|
ocelot_vlan_mode(ocelot, port, features);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-02-07 01:45:40 +08:00
|
|
|
static int ocelot_get_port_parent_id(struct net_device *dev,
|
|
|
|
struct netdev_phys_item_id *ppid)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot *ocelot = priv->port.ocelot;
|
2019-02-07 01:45:40 +08:00
|
|
|
|
|
|
|
ppid->id_len = sizeof(ocelot->base_mac);
|
|
|
|
memcpy(&ppid->id, &ocelot->base_mac, ppid->id_len);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-20 16:23:14 +08:00
|
|
|
int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr)
|
2019-08-12 22:45:37 +08:00
|
|
|
{
|
|
|
|
return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config,
|
|
|
|
sizeof(ocelot->hwtstamp_config)) ? -EFAULT : 0;
|
|
|
|
}
|
2019-11-20 16:23:14 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_hwstamp_get);
|
2019-08-12 22:45:37 +08:00
|
|
|
|
2019-11-20 16:23:14 +08:00
|
|
|
int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr)
|
2019-08-12 22:45:37 +08:00
|
|
|
{
|
2019-11-09 21:02:50 +08:00
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
2019-08-12 22:45:37 +08:00
|
|
|
struct hwtstamp_config cfg;
|
|
|
|
|
|
|
|
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
/* reserved for future extensions */
|
|
|
|
if (cfg.flags)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Tx type sanity check */
|
|
|
|
switch (cfg.tx_type) {
|
|
|
|
case HWTSTAMP_TX_ON:
|
2019-11-09 21:02:50 +08:00
|
|
|
ocelot_port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP;
|
2019-08-12 22:45:37 +08:00
|
|
|
break;
|
|
|
|
case HWTSTAMP_TX_ONESTEP_SYNC:
|
|
|
|
/* IFH_REW_OP_ONE_STEP_PTP updates the correctional field, we
|
|
|
|
* need to update the origin time.
|
|
|
|
*/
|
2019-11-09 21:02:50 +08:00
|
|
|
ocelot_port->ptp_cmd = IFH_REW_OP_ORIGIN_PTP;
|
2019-08-12 22:45:37 +08:00
|
|
|
break;
|
|
|
|
case HWTSTAMP_TX_OFF:
|
2019-11-09 21:02:50 +08:00
|
|
|
ocelot_port->ptp_cmd = 0;
|
2019-08-12 22:45:37 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ERANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_lock(&ocelot->ptp_lock);
|
|
|
|
|
|
|
|
switch (cfg.rx_filter) {
|
|
|
|
case HWTSTAMP_FILTER_NONE:
|
|
|
|
break;
|
|
|
|
case HWTSTAMP_FILTER_ALL:
|
|
|
|
case HWTSTAMP_FILTER_SOME:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
|
|
|
|
case HWTSTAMP_FILTER_NTP_ALL:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_EVENT:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_SYNC:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
|
|
|
|
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
mutex_unlock(&ocelot->ptp_lock);
|
|
|
|
return -ERANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Commit back the result & save it */
|
|
|
|
memcpy(&ocelot->hwtstamp_config, &cfg, sizeof(cfg));
|
|
|
|
mutex_unlock(&ocelot->ptp_lock);
|
|
|
|
|
|
|
|
return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
|
|
|
|
}
|
2019-11-20 16:23:14 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_hwstamp_set);
|
2019-08-12 22:45:37 +08:00
|
|
|
|
|
|
|
static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot *ocelot = priv->port.ocelot;
|
|
|
|
int port = priv->chip_port;
|
2019-08-12 22:45:37 +08:00
|
|
|
|
|
|
|
/* The function is only used for PTP operations for now */
|
|
|
|
if (!ocelot->ptp)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SIOCSHWTSTAMP:
|
2019-11-09 21:02:50 +08:00
|
|
|
return ocelot_hwstamp_set(ocelot, port, ifr);
|
2019-08-12 22:45:37 +08:00
|
|
|
case SIOCGHWTSTAMP:
|
2019-11-09 21:02:50 +08:00
|
|
|
return ocelot_hwstamp_get(ocelot, port, ifr);
|
2019-08-12 22:45:37 +08:00
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
static const struct net_device_ops ocelot_port_netdev_ops = {
|
|
|
|
.ndo_open = ocelot_port_open,
|
|
|
|
.ndo_stop = ocelot_port_stop,
|
|
|
|
.ndo_start_xmit = ocelot_port_xmit,
|
|
|
|
.ndo_set_rx_mode = ocelot_set_rx_mode,
|
|
|
|
.ndo_get_phys_port_name = ocelot_port_get_phys_port_name,
|
|
|
|
.ndo_set_mac_address = ocelot_port_set_mac_address,
|
|
|
|
.ndo_get_stats64 = ocelot_get_stats64,
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
.ndo_fdb_add = ocelot_port_fdb_add,
|
|
|
|
.ndo_fdb_del = ocelot_port_fdb_del,
|
|
|
|
.ndo_fdb_dump = ocelot_port_fdb_dump,
|
2018-06-26 20:28:49 +08:00
|
|
|
.ndo_vlan_rx_add_vid = ocelot_vlan_rx_add_vid,
|
|
|
|
.ndo_vlan_rx_kill_vid = ocelot_vlan_rx_kill_vid,
|
|
|
|
.ndo_set_features = ocelot_set_features,
|
2019-02-07 01:45:40 +08:00
|
|
|
.ndo_get_port_parent_id = ocelot_get_port_parent_id,
|
2019-05-28 20:49:17 +08:00
|
|
|
.ndo_setup_tc = ocelot_setup_tc,
|
2019-08-12 22:45:37 +08:00
|
|
|
.ndo_do_ioctl = ocelot_ioctl,
|
2018-05-15 04:04:57 +08:00
|
|
|
};
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (sset != ETH_SS_STATS)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < ocelot->num_stats; i++)
|
|
|
|
memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name,
|
|
|
|
ETH_GSTRING_LEN);
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_get_strings);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:54 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-04-16 22:51:59 +08:00
|
|
|
static void ocelot_update_stats(struct ocelot *ocelot)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
mutex_lock(&ocelot->stats_lock);
|
|
|
|
|
|
|
|
for (i = 0; i < ocelot->num_phys_ports; i++) {
|
|
|
|
/* Configure the port to read the stats from */
|
|
|
|
ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(i), SYS_STAT_CFG);
|
|
|
|
|
|
|
|
for (j = 0; j < ocelot->num_stats; j++) {
|
|
|
|
u32 val;
|
|
|
|
unsigned int idx = i * ocelot->num_stats + j;
|
|
|
|
|
|
|
|
val = ocelot_read_rix(ocelot, SYS_COUNT_RX_OCTETS,
|
|
|
|
ocelot->stats_layout[j].offset);
|
|
|
|
|
|
|
|
if (val < (ocelot->stats[idx] & U32_MAX))
|
|
|
|
ocelot->stats[idx] += (u64)1 << 32;
|
|
|
|
|
|
|
|
ocelot->stats[idx] = (ocelot->stats[idx] &
|
|
|
|
~(u64)U32_MAX) + val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-16 22:51:59 +08:00
|
|
|
mutex_unlock(&ocelot->stats_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ocelot_check_stats_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct delayed_work *del_work = to_delayed_work(work);
|
|
|
|
struct ocelot *ocelot = container_of(del_work, struct ocelot,
|
|
|
|
stats_work);
|
|
|
|
|
|
|
|
ocelot_update_stats(ocelot);
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
|
|
|
|
OCELOT_STATS_CHECK_DELAY);
|
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* check and update now */
|
2019-04-16 22:51:59 +08:00
|
|
|
ocelot_update_stats(ocelot);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* Copy all counters */
|
|
|
|
for (i = 0; i < ocelot->num_stats; i++)
|
2019-11-09 21:02:53 +08:00
|
|
|
*data++ = ocelot->stats[port * ocelot->num_stats + i];
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_get_ethtool_stats);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:54 +08:00
|
|
|
static void ocelot_port_get_ethtool_stats(struct net_device *dev,
|
|
|
|
struct ethtool_stats *stats,
|
|
|
|
u64 *data)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot *ocelot = priv->port.ocelot;
|
2019-11-09 21:02:54 +08:00
|
|
|
int port = priv->chip_port;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:54 +08:00
|
|
|
ocelot_get_ethtool_stats(ocelot, port, data);
|
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
|
2019-11-09 21:02:54 +08:00
|
|
|
{
|
2018-05-15 04:04:57 +08:00
|
|
|
if (sset != ETH_SS_STATS)
|
|
|
|
return -EOPNOTSUPP;
|
2019-11-09 21:02:54 +08:00
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
return ocelot->num_stats;
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_get_sset_count);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:54 +08:00
|
|
|
static int ocelot_port_get_sset_count(struct net_device *dev, int sset)
|
2019-08-12 22:45:37 +08:00
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot *ocelot = priv->port.ocelot;
|
2019-11-09 21:02:54 +08:00
|
|
|
int port = priv->chip_port;
|
2019-08-12 22:45:37 +08:00
|
|
|
|
2019-11-09 21:02:54 +08:00
|
|
|
return ocelot_get_sset_count(ocelot, port, sset);
|
|
|
|
}
|
2019-08-12 22:45:37 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_get_ts_info(struct ocelot *ocelot, int port,
|
|
|
|
struct ethtool_ts_info *info)
|
2019-11-09 21:02:54 +08:00
|
|
|
{
|
2019-08-12 22:45:37 +08:00
|
|
|
info->phc_index = ocelot->ptp_clock ?
|
|
|
|
ptp_clock_index(ocelot->ptp_clock) : -1;
|
|
|
|
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
|
|
|
|
SOF_TIMESTAMPING_RX_SOFTWARE |
|
|
|
|
SOF_TIMESTAMPING_SOFTWARE |
|
|
|
|
SOF_TIMESTAMPING_TX_HARDWARE |
|
|
|
|
SOF_TIMESTAMPING_RX_HARDWARE |
|
|
|
|
SOF_TIMESTAMPING_RAW_HARDWARE;
|
|
|
|
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON) |
|
|
|
|
BIT(HWTSTAMP_TX_ONESTEP_SYNC);
|
|
|
|
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_get_ts_info);
|
2019-08-12 22:45:37 +08:00
|
|
|
|
2019-11-09 21:02:54 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
static const struct ethtool_ops ocelot_ethtool_ops = {
|
2019-11-09 21:02:54 +08:00
|
|
|
.get_strings = ocelot_port_get_strings,
|
|
|
|
.get_ethtool_stats = ocelot_port_get_ethtool_stats,
|
|
|
|
.get_sset_count = ocelot_port_get_sset_count,
|
2018-06-26 20:28:48 +08:00
|
|
|
.get_link_ksettings = phy_ethtool_get_link_ksettings,
|
|
|
|
.set_link_ksettings = phy_ethtool_set_link_ksettings,
|
2019-11-09 21:02:54 +08:00
|
|
|
.get_ts_info = ocelot_port_get_ts_info,
|
2018-05-15 04:04:57 +08:00
|
|
|
};
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
u32 port_cfg;
|
2019-11-09 21:02:51 +08:00
|
|
|
int p, i;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:51 +08:00
|
|
|
if (!(BIT(port) & ocelot->bridge_mask))
|
|
|
|
return;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:51 +08:00
|
|
|
port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, port);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case BR_STATE_FORWARDING:
|
2019-11-09 21:02:51 +08:00
|
|
|
ocelot->bridge_fwd_mask |= BIT(port);
|
2018-05-15 04:04:57 +08:00
|
|
|
/* Fallthrough */
|
|
|
|
case BR_STATE_LEARNING:
|
|
|
|
port_cfg |= ANA_PORT_PORT_CFG_LEARN_ENA;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
port_cfg &= ~ANA_PORT_PORT_CFG_LEARN_ENA;
|
2019-11-09 21:02:51 +08:00
|
|
|
ocelot->bridge_fwd_mask &= ~BIT(port);
|
2018-05-15 04:04:57 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:51 +08:00
|
|
|
ocelot_write_gix(ocelot, port_cfg, ANA_PORT_PORT_CFG, port);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* Apply FWD mask. The loop is needed to add/remove the current port as
|
|
|
|
* a source for the other ports.
|
|
|
|
*/
|
2019-11-09 21:02:51 +08:00
|
|
|
for (p = 0; p < ocelot->num_phys_ports; p++) {
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
if (ocelot->bridge_fwd_mask & BIT(p)) {
|
2019-11-09 21:02:51 +08:00
|
|
|
unsigned long mask = ocelot->bridge_fwd_mask & ~BIT(p);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
for (i = 0; i < ocelot->num_phys_ports; i++) {
|
|
|
|
unsigned long bond_mask = ocelot->lags[i];
|
|
|
|
|
|
|
|
if (!bond_mask)
|
|
|
|
continue;
|
|
|
|
|
2019-11-09 21:02:51 +08:00
|
|
|
if (bond_mask & BIT(p)) {
|
2018-05-15 04:04:57 +08:00
|
|
|
mask &= ~bond_mask;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:03:01 +08:00
|
|
|
ocelot_write_rix(ocelot, mask,
|
2019-11-09 21:02:51 +08:00
|
|
|
ANA_PGID_PGID, PGID_SRC + p);
|
2018-05-15 04:04:57 +08:00
|
|
|
} else {
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
ocelot_write_rix(ocelot, 0,
|
2019-11-09 21:02:51 +08:00
|
|
|
ANA_PGID_PGID, PGID_SRC + p);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
}
|
2019-11-09 21:02:51 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_bridge_stp_state_set);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:51 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs)
|
2019-11-09 21:02:51 +08:00
|
|
|
{
|
|
|
|
ocelot_write(ocelot, ANA_AUTOAGE_AGE_PERIOD(msecs / 2),
|
|
|
|
ANA_AUTOAGE);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_set_ageing_time);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:51 +08:00
|
|
|
static void ocelot_port_attr_ageing_set(struct ocelot *ocelot, int port,
|
2018-05-15 04:04:57 +08:00
|
|
|
unsigned long ageing_clock_t)
|
|
|
|
{
|
|
|
|
unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
|
|
|
|
u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000;
|
|
|
|
|
2019-11-09 21:02:51 +08:00
|
|
|
ocelot_set_ageing_time(ocelot, ageing_time);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:51 +08:00
|
|
|
static void ocelot_port_attr_mc_set(struct ocelot *ocelot, int port, bool mc)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
2019-11-09 21:02:51 +08:00
|
|
|
u32 cpu_fwd_mcast = 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;
|
|
|
|
u32 val = 0;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
if (mc)
|
2019-11-09 21:02:51 +08:00
|
|
|
val = cpu_fwd_mcast;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:51 +08:00
|
|
|
ocelot_rmw_gix(ocelot, val, cpu_fwd_mcast,
|
|
|
|
ANA_PORT_CPU_FWD_CFG, port);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_port_attr_set(struct net_device *dev,
|
|
|
|
const struct switchdev_attr *attr,
|
|
|
|
struct switchdev_trans *trans)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot *ocelot = priv->port.ocelot;
|
|
|
|
int port = priv->chip_port;
|
2018-05-15 04:04:57 +08:00
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
switch (attr->id) {
|
|
|
|
case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
|
2019-11-09 21:02:51 +08:00
|
|
|
ocelot_port_attr_stp_state_set(ocelot, port, trans,
|
2018-05-15 04:04:57 +08:00
|
|
|
attr->u.stp_state);
|
|
|
|
break;
|
|
|
|
case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
|
2019-11-09 21:02:51 +08:00
|
|
|
ocelot_port_attr_ageing_set(ocelot, port, attr->u.ageing_time);
|
2018-05-15 04:04:57 +08:00
|
|
|
break;
|
2018-06-26 20:28:49 +08:00
|
|
|
case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
ocelot_port_vlan_filtering(ocelot, port,
|
|
|
|
attr->u.vlan_filtering);
|
2018-06-26 20:28:49 +08:00
|
|
|
break;
|
2018-05-15 04:04:57 +08:00
|
|
|
case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
|
2019-11-09 21:02:51 +08:00
|
|
|
ocelot_port_attr_mc_set(ocelot, port, !attr->u.mc_disabled);
|
2018-05-15 04:04:57 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2018-06-26 20:28:49 +08:00
|
|
|
static int ocelot_port_obj_add_vlan(struct net_device *dev,
|
|
|
|
const struct switchdev_obj_port_vlan *vlan,
|
|
|
|
struct switchdev_trans *trans)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
u16 vid;
|
|
|
|
|
|
|
|
for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
|
|
|
|
ret = ocelot_vlan_vid_add(dev, vid,
|
|
|
|
vlan->flags & BRIDGE_VLAN_INFO_PVID,
|
|
|
|
vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_port_vlan_del_vlan(struct net_device *dev,
|
|
|
|
const struct switchdev_obj_port_vlan *vlan)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
u16 vid;
|
|
|
|
|
|
|
|
for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
|
|
|
|
ret = ocelot_vlan_vid_del(dev, vid);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
static struct ocelot_multicast *ocelot_multicast_get(struct ocelot *ocelot,
|
|
|
|
const unsigned char *addr,
|
|
|
|
u16 vid)
|
|
|
|
{
|
|
|
|
struct ocelot_multicast *mc;
|
|
|
|
|
|
|
|
list_for_each_entry(mc, &ocelot->multicast, list) {
|
|
|
|
if (ether_addr_equal(mc->addr, addr) && mc->vid == vid)
|
|
|
|
return mc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_port_obj_add_mdb(struct net_device *dev,
|
|
|
|
const struct switchdev_obj_port_mdb *mdb,
|
|
|
|
struct switchdev_trans *trans)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot_port *ocelot_port = &priv->port;
|
|
|
|
struct ocelot *ocelot = ocelot_port->ocelot;
|
2018-05-15 04:04:57 +08:00
|
|
|
unsigned char addr[ETH_ALEN];
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_multicast *mc;
|
|
|
|
int port = priv->chip_port;
|
2018-05-15 04:04:57 +08:00
|
|
|
u16 vid = mdb->vid;
|
|
|
|
bool new = false;
|
|
|
|
|
|
|
|
if (!vid)
|
2019-11-09 21:02:53 +08:00
|
|
|
vid = ocelot_port->pvid;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
|
|
|
|
if (!mc) {
|
|
|
|
mc = devm_kzalloc(ocelot->dev, sizeof(*mc), GFP_KERNEL);
|
|
|
|
if (!mc)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
memcpy(mc->addr, mdb->addr, ETH_ALEN);
|
|
|
|
mc->vid = vid;
|
|
|
|
|
|
|
|
list_add_tail(&mc->list, &ocelot->multicast);
|
|
|
|
new = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(addr, mc->addr, ETH_ALEN);
|
|
|
|
addr[0] = 0;
|
|
|
|
|
|
|
|
if (!new) {
|
|
|
|
addr[2] = mc->ports << 0;
|
|
|
|
addr[1] = mc->ports << 8;
|
|
|
|
ocelot_mact_forget(ocelot, addr, vid);
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
mc->ports |= BIT(port);
|
2018-05-15 04:04:57 +08:00
|
|
|
addr[2] = mc->ports << 0;
|
|
|
|
addr[1] = mc->ports << 8;
|
|
|
|
|
|
|
|
return ocelot_mact_learn(ocelot, 0, addr, vid, ENTRYTYPE_MACv4);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_port_obj_del_mdb(struct net_device *dev,
|
|
|
|
const struct switchdev_obj_port_mdb *mdb)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot_port *ocelot_port = &priv->port;
|
|
|
|
struct ocelot *ocelot = ocelot_port->ocelot;
|
2018-05-15 04:04:57 +08:00
|
|
|
unsigned char addr[ETH_ALEN];
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_multicast *mc;
|
|
|
|
int port = priv->chip_port;
|
2018-05-15 04:04:57 +08:00
|
|
|
u16 vid = mdb->vid;
|
|
|
|
|
|
|
|
if (!vid)
|
2019-11-09 21:02:53 +08:00
|
|
|
vid = ocelot_port->pvid;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
|
|
|
|
if (!mc)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
memcpy(addr, mc->addr, ETH_ALEN);
|
|
|
|
addr[2] = mc->ports << 0;
|
|
|
|
addr[1] = mc->ports << 8;
|
|
|
|
addr[0] = 0;
|
|
|
|
ocelot_mact_forget(ocelot, addr, vid);
|
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
mc->ports &= ~BIT(port);
|
2018-05-15 04:04:57 +08:00
|
|
|
if (!mc->ports) {
|
|
|
|
list_del(&mc->list);
|
|
|
|
devm_kfree(ocelot->dev, mc);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr[2] = mc->ports << 0;
|
|
|
|
addr[1] = mc->ports << 8;
|
|
|
|
|
|
|
|
return ocelot_mact_learn(ocelot, 0, addr, vid, ENTRYTYPE_MACv4);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_port_obj_add(struct net_device *dev,
|
|
|
|
const struct switchdev_obj *obj,
|
2018-12-13 01:02:56 +08:00
|
|
|
struct switchdev_trans *trans,
|
|
|
|
struct netlink_ext_ack *extack)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
switch (obj->id) {
|
2018-06-26 20:28:49 +08:00
|
|
|
case SWITCHDEV_OBJ_ID_PORT_VLAN:
|
|
|
|
ret = ocelot_port_obj_add_vlan(dev,
|
|
|
|
SWITCHDEV_OBJ_PORT_VLAN(obj),
|
|
|
|
trans);
|
|
|
|
break;
|
2018-05-15 04:04:57 +08:00
|
|
|
case SWITCHDEV_OBJ_ID_PORT_MDB:
|
|
|
|
ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj),
|
|
|
|
trans);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_port_obj_del(struct net_device *dev,
|
|
|
|
const struct switchdev_obj *obj)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
switch (obj->id) {
|
2018-06-26 20:28:49 +08:00
|
|
|
case SWITCHDEV_OBJ_ID_PORT_VLAN:
|
|
|
|
ret = ocelot_port_vlan_del_vlan(dev,
|
|
|
|
SWITCHDEV_OBJ_PORT_VLAN(obj));
|
|
|
|
break;
|
2018-05-15 04:04:57 +08:00
|
|
|
case SWITCHDEV_OBJ_ID_PORT_MDB:
|
|
|
|
ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
|
|
|
|
struct net_device *bridge)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
if (!ocelot->bridge_mask) {
|
|
|
|
ocelot->hw_bridge_dev = bridge;
|
|
|
|
} else {
|
|
|
|
if (ocelot->hw_bridge_dev != bridge)
|
|
|
|
/* This is adding the port to a second bridge, this is
|
|
|
|
* unsupported */
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:52 +08:00
|
|
|
ocelot->bridge_mask |= BIT(port);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_bridge_join);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
|
|
|
|
struct net_device *bridge)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
2019-11-09 21:02:47 +08:00
|
|
|
ocelot->bridge_mask &= ~BIT(port);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
if (!ocelot->bridge_mask)
|
|
|
|
ocelot->hw_bridge_dev = NULL;
|
2018-06-26 20:28:49 +08:00
|
|
|
|
2019-11-09 21:02:47 +08:00
|
|
|
ocelot_port_vlan_filtering(ocelot, port, 0);
|
|
|
|
ocelot_port_set_pvid(ocelot, port, 0);
|
|
|
|
return ocelot_port_set_native_vlan(ocelot, port, 0);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_bridge_leave);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2018-06-26 20:28:48 +08:00
|
|
|
static void ocelot_set_aggr_pgids(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
int i, port, lag;
|
|
|
|
|
|
|
|
/* Reset destination and aggregation PGIDS */
|
|
|
|
for (port = 0; port < ocelot->num_phys_ports; port++)
|
|
|
|
ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, port);
|
|
|
|
|
|
|
|
for (i = PGID_AGGR; i < PGID_SRC; i++)
|
|
|
|
ocelot_write_rix(ocelot, GENMASK(ocelot->num_phys_ports - 1, 0),
|
|
|
|
ANA_PGID_PGID, i);
|
|
|
|
|
|
|
|
/* Now, set PGIDs for each LAG */
|
|
|
|
for (lag = 0; lag < ocelot->num_phys_ports; lag++) {
|
|
|
|
unsigned long bond_mask;
|
|
|
|
int aggr_count = 0;
|
|
|
|
u8 aggr_idx[16];
|
|
|
|
|
|
|
|
bond_mask = ocelot->lags[lag];
|
|
|
|
if (!bond_mask)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for_each_set_bit(port, &bond_mask, ocelot->num_phys_ports) {
|
|
|
|
// Destination mask
|
|
|
|
ocelot_write_rix(ocelot, bond_mask,
|
|
|
|
ANA_PGID_PGID, port);
|
|
|
|
aggr_idx[aggr_count] = port;
|
|
|
|
aggr_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = PGID_AGGR; i < PGID_SRC; i++) {
|
|
|
|
u32 ac;
|
|
|
|
|
|
|
|
ac = ocelot_read_rix(ocelot, ANA_PGID_PGID, i);
|
|
|
|
ac &= ~bond_mask;
|
|
|
|
ac |= BIT(aggr_idx[i % aggr_count]);
|
|
|
|
ocelot_write_rix(ocelot, ac, ANA_PGID_PGID, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ocelot_setup_lag(struct ocelot *ocelot, int lag)
|
|
|
|
{
|
|
|
|
unsigned long bond_mask = ocelot->lags[lag];
|
|
|
|
unsigned int p;
|
|
|
|
|
|
|
|
for_each_set_bit(p, &bond_mask, ocelot->num_phys_ports) {
|
|
|
|
u32 port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, p);
|
|
|
|
|
|
|
|
port_cfg &= ~ANA_PORT_PORT_CFG_PORTID_VAL_M;
|
|
|
|
|
|
|
|
/* Use lag port as logical port for port i */
|
|
|
|
ocelot_write_gix(ocelot, port_cfg |
|
|
|
|
ANA_PORT_PORT_CFG_PORTID_VAL(lag),
|
|
|
|
ANA_PORT_PORT_CFG, p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:52 +08:00
|
|
|
static int ocelot_port_lag_join(struct ocelot *ocelot, int port,
|
2018-06-26 20:28:48 +08:00
|
|
|
struct net_device *bond)
|
|
|
|
{
|
|
|
|
struct net_device *ndev;
|
|
|
|
u32 bond_mask = 0;
|
2019-11-09 21:02:52 +08:00
|
|
|
int lag, lp;
|
2018-06-26 20:28:48 +08:00
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
for_each_netdev_in_bond_rcu(bond, ndev) {
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(ndev);
|
2018-06-26 20:28:48 +08:00
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
bond_mask |= BIT(priv->chip_port);
|
2018-06-26 20:28:48 +08:00
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
lp = __ffs(bond_mask);
|
|
|
|
|
|
|
|
/* If the new port is the lowest one, use it as the logical port from
|
|
|
|
* now on
|
|
|
|
*/
|
2019-11-09 21:02:52 +08:00
|
|
|
if (port == lp) {
|
|
|
|
lag = port;
|
|
|
|
ocelot->lags[port] = bond_mask;
|
|
|
|
bond_mask &= ~BIT(port);
|
2018-06-26 20:28:48 +08:00
|
|
|
if (bond_mask) {
|
|
|
|
lp = __ffs(bond_mask);
|
|
|
|
ocelot->lags[lp] = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lag = lp;
|
2019-11-09 21:02:52 +08:00
|
|
|
ocelot->lags[lp] |= BIT(port);
|
2018-06-26 20:28:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ocelot_setup_lag(ocelot, lag);
|
|
|
|
ocelot_set_aggr_pgids(ocelot);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:52 +08:00
|
|
|
static void ocelot_port_lag_leave(struct ocelot *ocelot, int port,
|
2018-06-26 20:28:48 +08:00
|
|
|
struct net_device *bond)
|
|
|
|
{
|
|
|
|
u32 port_cfg;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Remove port from any lag */
|
|
|
|
for (i = 0; i < ocelot->num_phys_ports; i++)
|
2019-11-09 21:02:52 +08:00
|
|
|
ocelot->lags[i] &= ~BIT(port);
|
2018-06-26 20:28:48 +08:00
|
|
|
|
|
|
|
/* if it was the logical port of the lag, move the lag config to the
|
|
|
|
* next port
|
|
|
|
*/
|
2019-11-09 21:02:52 +08:00
|
|
|
if (ocelot->lags[port]) {
|
|
|
|
int n = __ffs(ocelot->lags[port]);
|
2018-06-26 20:28:48 +08:00
|
|
|
|
2019-11-09 21:02:52 +08:00
|
|
|
ocelot->lags[n] = ocelot->lags[port];
|
|
|
|
ocelot->lags[port] = 0;
|
2018-06-26 20:28:48 +08:00
|
|
|
|
|
|
|
ocelot_setup_lag(ocelot, n);
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:52 +08:00
|
|
|
port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, port);
|
2018-06-26 20:28:48 +08:00
|
|
|
port_cfg &= ~ANA_PORT_PORT_CFG_PORTID_VAL_M;
|
2019-11-09 21:02:52 +08:00
|
|
|
ocelot_write_gix(ocelot, port_cfg | ANA_PORT_PORT_CFG_PORTID_VAL(port),
|
|
|
|
ANA_PORT_PORT_CFG, port);
|
2018-06-26 20:28:48 +08:00
|
|
|
|
|
|
|
ocelot_set_aggr_pgids(ocelot);
|
|
|
|
}
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
/* Checks if the net_device instance given to us originate from our driver. */
|
|
|
|
static bool ocelot_netdevice_dev_check(const struct net_device *dev)
|
|
|
|
{
|
|
|
|
return dev->netdev_ops == &ocelot_port_netdev_ops;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_netdevice_port_event(struct net_device *dev,
|
|
|
|
unsigned long event,
|
|
|
|
struct netdev_notifier_changeupper_info *info)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(dev);
|
|
|
|
struct ocelot_port *ocelot_port = &priv->port;
|
2019-11-09 21:02:52 +08:00
|
|
|
struct ocelot *ocelot = ocelot_port->ocelot;
|
2019-11-09 21:02:53 +08:00
|
|
|
int port = priv->chip_port;
|
2018-05-15 04:04:57 +08:00
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case NETDEV_CHANGEUPPER:
|
|
|
|
if (netif_is_bridge_master(info->upper_dev)) {
|
2019-11-09 21:02:53 +08:00
|
|
|
if (info->linking) {
|
2019-11-09 21:02:52 +08:00
|
|
|
err = ocelot_port_bridge_join(ocelot, port,
|
2018-05-15 04:04:57 +08:00
|
|
|
info->upper_dev);
|
2019-11-09 21:02:53 +08:00
|
|
|
} else {
|
2019-11-09 21:02:52 +08:00
|
|
|
err = ocelot_port_bridge_leave(ocelot, port,
|
2019-11-09 21:02:47 +08:00
|
|
|
info->upper_dev);
|
2019-11-09 21:02:53 +08:00
|
|
|
}
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
2018-06-26 20:28:48 +08:00
|
|
|
if (netif_is_lag_master(info->upper_dev)) {
|
|
|
|
if (info->linking)
|
2019-11-09 21:02:52 +08:00
|
|
|
err = ocelot_port_lag_join(ocelot, port,
|
2018-06-26 20:28:48 +08:00
|
|
|
info->upper_dev);
|
|
|
|
else
|
2019-11-09 21:02:52 +08:00
|
|
|
ocelot_port_lag_leave(ocelot, port,
|
2018-06-26 20:28:48 +08:00
|
|
|
info->upper_dev);
|
|
|
|
}
|
2018-05-15 04:04:57 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_netdevice_event(struct notifier_block *unused,
|
|
|
|
unsigned long event, void *ptr)
|
|
|
|
{
|
|
|
|
struct netdev_notifier_changeupper_info *info = ptr;
|
|
|
|
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
2018-06-07 21:10:30 +08:00
|
|
|
int ret = 0;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-06 05:50:13 +08:00
|
|
|
if (!ocelot_netdevice_dev_check(dev))
|
|
|
|
return 0;
|
|
|
|
|
2018-06-26 20:28:48 +08:00
|
|
|
if (event == NETDEV_PRECHANGEUPPER &&
|
|
|
|
netif_is_lag_master(info->upper_dev)) {
|
|
|
|
struct netdev_lag_upper_info *lag_upper_info = info->upper_info;
|
|
|
|
struct netlink_ext_ack *extack;
|
|
|
|
|
2019-11-06 05:50:14 +08:00
|
|
|
if (lag_upper_info &&
|
|
|
|
lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
|
2018-06-26 20:28:48 +08:00
|
|
|
extack = netdev_notifier_info_to_extack(&info->info);
|
|
|
|
NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type");
|
|
|
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto notify;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
if (netif_is_lag_master(dev)) {
|
|
|
|
struct net_device *slave;
|
|
|
|
struct list_head *iter;
|
|
|
|
|
|
|
|
netdev_for_each_lower_dev(dev, slave, iter) {
|
|
|
|
ret = ocelot_netdevice_port_event(slave, event, info);
|
|
|
|
if (ret)
|
|
|
|
goto notify;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ret = ocelot_netdevice_port_event(dev, event, info);
|
|
|
|
}
|
|
|
|
|
|
|
|
notify:
|
|
|
|
return notifier_from_errno(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct notifier_block ocelot_netdevice_nb __read_mostly = {
|
|
|
|
.notifier_call = ocelot_netdevice_event,
|
|
|
|
};
|
|
|
|
EXPORT_SYMBOL(ocelot_netdevice_nb);
|
|
|
|
|
2019-02-28 03:44:29 +08:00
|
|
|
static int ocelot_switchdev_event(struct notifier_block *unused,
|
|
|
|
unsigned long event, void *ptr)
|
|
|
|
{
|
|
|
|
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
|
|
|
|
int err;
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case SWITCHDEV_PORT_ATTR_SET:
|
|
|
|
err = switchdev_handle_port_attr_set(dev, ptr,
|
|
|
|
ocelot_netdevice_dev_check,
|
|
|
|
ocelot_port_attr_set);
|
|
|
|
return notifier_from_errno(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct notifier_block ocelot_switchdev_nb __read_mostly = {
|
|
|
|
.notifier_call = ocelot_switchdev_event,
|
|
|
|
};
|
|
|
|
EXPORT_SYMBOL(ocelot_switchdev_nb);
|
|
|
|
|
2018-11-23 07:30:11 +08:00
|
|
|
static int ocelot_switchdev_blocking_event(struct notifier_block *unused,
|
|
|
|
unsigned long event, void *ptr)
|
|
|
|
{
|
|
|
|
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
|
|
|
|
int err;
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
/* Blocking events. */
|
|
|
|
case SWITCHDEV_PORT_OBJ_ADD:
|
|
|
|
err = switchdev_handle_port_obj_add(dev, ptr,
|
|
|
|
ocelot_netdevice_dev_check,
|
|
|
|
ocelot_port_obj_add);
|
|
|
|
return notifier_from_errno(err);
|
|
|
|
case SWITCHDEV_PORT_OBJ_DEL:
|
|
|
|
err = switchdev_handle_port_obj_del(dev, ptr,
|
|
|
|
ocelot_netdevice_dev_check,
|
|
|
|
ocelot_port_obj_del);
|
|
|
|
return notifier_from_errno(err);
|
2019-02-28 03:44:29 +08:00
|
|
|
case SWITCHDEV_PORT_ATTR_SET:
|
|
|
|
err = switchdev_handle_port_attr_set(dev, ptr,
|
|
|
|
ocelot_netdevice_dev_check,
|
|
|
|
ocelot_port_attr_set);
|
|
|
|
return notifier_from_errno(err);
|
2018-11-23 07:30:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct notifier_block ocelot_switchdev_blocking_nb __read_mostly = {
|
|
|
|
.notifier_call = ocelot_switchdev_blocking_event,
|
|
|
|
};
|
|
|
|
EXPORT_SYMBOL(ocelot_switchdev_blocking_nb);
|
|
|
|
|
2019-08-12 22:45:37 +08:00
|
|
|
int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts)
|
|
|
|
{
|
|
|
|
struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
|
|
|
|
unsigned long flags;
|
|
|
|
time64_t s;
|
|
|
|
u32 val;
|
|
|
|
s64 ns;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
|
|
|
|
|
|
|
|
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
|
|
|
|
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
|
|
|
|
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_SAVE);
|
|
|
|
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
|
|
|
|
|
|
|
|
s = ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_MSB, TOD_ACC_PIN) & 0xffff;
|
|
|
|
s <<= 32;
|
|
|
|
s += ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
|
|
|
|
ns = ocelot_read_rix(ocelot, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
|
|
|
|
|
|
|
|
/* Deal with negative values */
|
|
|
|
if (ns >= 0x3ffffff0 && ns <= 0x3fffffff) {
|
|
|
|
s--;
|
|
|
|
ns &= 0xf;
|
|
|
|
ns += 999999984;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_normalized_timespec64(ts, s, ns);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_ptp_gettime64);
|
|
|
|
|
|
|
|
static int ocelot_ptp_settime64(struct ptp_clock_info *ptp,
|
|
|
|
const struct timespec64 *ts)
|
|
|
|
{
|
|
|
|
struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
|
|
|
|
unsigned long flags;
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
|
|
|
|
|
|
|
|
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
|
|
|
|
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
|
|
|
|
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
|
|
|
|
|
|
|
|
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
|
|
|
|
|
|
|
|
ocelot_write_rix(ocelot, lower_32_bits(ts->tv_sec), PTP_PIN_TOD_SEC_LSB,
|
|
|
|
TOD_ACC_PIN);
|
|
|
|
ocelot_write_rix(ocelot, upper_32_bits(ts->tv_sec), PTP_PIN_TOD_SEC_MSB,
|
|
|
|
TOD_ACC_PIN);
|
|
|
|
ocelot_write_rix(ocelot, ts->tv_nsec, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
|
|
|
|
|
|
|
|
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
|
|
|
|
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
|
|
|
|
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_LOAD);
|
|
|
|
|
|
|
|
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
|
|
|
{
|
|
|
|
if (delta > -(NSEC_PER_SEC / 2) && delta < (NSEC_PER_SEC / 2)) {
|
|
|
|
struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
|
|
|
|
unsigned long flags;
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
|
|
|
|
|
|
|
|
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
|
|
|
|
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
|
|
|
|
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
|
|
|
|
|
|
|
|
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
|
|
|
|
|
|
|
|
ocelot_write_rix(ocelot, 0, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
|
|
|
|
ocelot_write_rix(ocelot, 0, PTP_PIN_TOD_SEC_MSB, TOD_ACC_PIN);
|
|
|
|
ocelot_write_rix(ocelot, delta, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
|
|
|
|
|
|
|
|
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
|
|
|
|
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
|
|
|
|
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_DELTA);
|
|
|
|
|
|
|
|
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
|
|
|
|
} else {
|
|
|
|
/* Fall back using ocelot_ptp_settime64 which is not exact. */
|
|
|
|
struct timespec64 ts;
|
|
|
|
u64 now;
|
|
|
|
|
|
|
|
ocelot_ptp_gettime64(ptp, &ts);
|
|
|
|
|
|
|
|
now = ktime_to_ns(timespec64_to_ktime(ts));
|
|
|
|
ts = ns_to_timespec64(now + delta);
|
|
|
|
|
|
|
|
ocelot_ptp_settime64(ptp, &ts);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ocelot_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
|
|
|
{
|
|
|
|
struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
|
|
|
|
u32 unit = 0, direction = 0;
|
|
|
|
unsigned long flags;
|
|
|
|
u64 adj = 0;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
|
|
|
|
|
|
|
|
if (!scaled_ppm)
|
|
|
|
goto disable_adj;
|
|
|
|
|
|
|
|
if (scaled_ppm < 0) {
|
|
|
|
direction = PTP_CFG_CLK_ADJ_CFG_DIR;
|
|
|
|
scaled_ppm = -scaled_ppm;
|
|
|
|
}
|
|
|
|
|
|
|
|
adj = PSEC_PER_SEC << 16;
|
|
|
|
do_div(adj, scaled_ppm);
|
|
|
|
do_div(adj, 1000);
|
|
|
|
|
|
|
|
/* If the adjustment value is too large, use ns instead */
|
|
|
|
if (adj >= (1L << 30)) {
|
|
|
|
unit = PTP_CFG_CLK_ADJ_FREQ_NS;
|
|
|
|
do_div(adj, 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Still too big */
|
|
|
|
if (adj >= (1L << 30))
|
|
|
|
goto disable_adj;
|
|
|
|
|
|
|
|
ocelot_write(ocelot, unit | adj, PTP_CLK_CFG_ADJ_FREQ);
|
|
|
|
ocelot_write(ocelot, PTP_CFG_CLK_ADJ_CFG_ENA | direction,
|
|
|
|
PTP_CLK_CFG_ADJ_CFG);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
disable_adj:
|
|
|
|
ocelot_write(ocelot, 0, PTP_CLK_CFG_ADJ_CFG);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ptp_clock_info ocelot_ptp_clock_info = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.name = "ocelot ptp",
|
|
|
|
.max_adj = 0x7fffffff,
|
|
|
|
.n_alarm = 0,
|
|
|
|
.n_ext_ts = 0,
|
|
|
|
.n_per_out = 0,
|
|
|
|
.n_pins = 0,
|
|
|
|
.pps = 0,
|
|
|
|
.gettime64 = ocelot_ptp_gettime64,
|
|
|
|
.settime64 = ocelot_ptp_settime64,
|
|
|
|
.adjtime = ocelot_ptp_adjtime,
|
|
|
|
.adjfine = ocelot_ptp_adjfine,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ocelot_init_timestamp(struct ocelot *ocelot)
|
|
|
|
{
|
net: mscc: ocelot: unregister the PTP clock on deinit
Currently a switch driver deinit frees the regmaps, but the PTP clock is
still out there, available to user space via /dev/ptpN. Any PTP
operation is a ticking time bomb, since it will attempt to use the freed
regmaps and thus trigger kernel panics:
[ 4.291746] fsl_enetc 0000:00:00.2 eth1: error -22 setting up slave phy
[ 4.291871] mscc_felix 0000:00:00.5: Failed to register DSA switch: -22
[ 4.308666] mscc_felix: probe of 0000:00:00.5 failed with error -22
[ 6.358270] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000088
[ 6.367090] Mem abort info:
[ 6.369888] ESR = 0x96000046
[ 6.369891] EC = 0x25: DABT (current EL), IL = 32 bits
[ 6.369892] SET = 0, FnV = 0
[ 6.369894] EA = 0, S1PTW = 0
[ 6.369895] Data abort info:
[ 6.369897] ISV = 0, ISS = 0x00000046
[ 6.369899] CM = 0, WnR = 1
[ 6.369902] user pgtable: 4k pages, 48-bit VAs, pgdp=00000020d58c7000
[ 6.369904] [0000000000000088] pgd=00000020d5912003, pud=00000020d5915003, pmd=0000000000000000
[ 6.369914] Internal error: Oops: 96000046 [#1] PREEMPT SMP
[ 6.420443] Modules linked in:
[ 6.423506] CPU: 1 PID: 262 Comm: phc_ctl Not tainted 5.4.0-03625-gb7b2a5dadd7f #204
[ 6.431273] Hardware name: LS1028A RDB Board (DT)
[ 6.435989] pstate: 40000085 (nZcv daIf -PAN -UAO)
[ 6.440802] pc : css_release+0x24/0x58
[ 6.444561] lr : regmap_read+0x40/0x78
[ 6.448316] sp : ffff800010513cc0
[ 6.451636] x29: ffff800010513cc0 x28: ffff002055873040
[ 6.456963] x27: 0000000000000000 x26: 0000000000000000
[ 6.462289] x25: 0000000000000000 x24: 0000000000000000
[ 6.467617] x23: 0000000000000000 x22: 0000000000000080
[ 6.472944] x21: ffff800010513d44 x20: 0000000000000080
[ 6.478270] x19: 0000000000000000 x18: 0000000000000000
[ 6.483596] x17: 0000000000000000 x16: 0000000000000000
[ 6.488921] x15: 0000000000000000 x14: 0000000000000000
[ 6.494247] x13: 0000000000000000 x12: 0000000000000000
[ 6.499573] x11: 0000000000000000 x10: 0000000000000000
[ 6.504899] x9 : 0000000000000000 x8 : 0000000000000000
[ 6.510225] x7 : 0000000000000000 x6 : ffff800010513cf0
[ 6.515550] x5 : 0000000000000000 x4 : 0000000fffffffe0
[ 6.520876] x3 : 0000000000000088 x2 : ffff800010513d44
[ 6.526202] x1 : ffffcada668ea000 x0 : ffffcada64d8b0c0
[ 6.531528] Call trace:
[ 6.533977] css_release+0x24/0x58
[ 6.537385] regmap_read+0x40/0x78
[ 6.540795] __ocelot_read_ix+0x6c/0xa0
[ 6.544641] ocelot_ptp_gettime64+0x4c/0x110
[ 6.548921] ptp_clock_gettime+0x4c/0x58
[ 6.552853] pc_clock_gettime+0x5c/0xa8
[ 6.556699] __arm64_sys_clock_gettime+0x68/0xc8
[ 6.561331] el0_svc_common.constprop.2+0x7c/0x178
[ 6.566133] el0_svc_handler+0x34/0xa0
[ 6.569891] el0_sync_handler+0x114/0x1d0
[ 6.573908] el0_sync+0x140/0x180
[ 6.577232] Code: d503201f b00119a1 91022263 b27b7be4 (f9004663)
[ 6.583349] ---[ end trace d196b9b14cdae2da ]---
[ 6.587977] Kernel panic - not syncing: Fatal exception
[ 6.593216] SMP: stopping secondary CPUs
[ 6.597151] Kernel Offset: 0x4ada54400000 from 0xffff800010000000
[ 6.603261] PHYS_OFFSET: 0xffffd0a7c0000000
[ 6.607454] CPU features: 0x10002,21806008
[ 6.611558] Memory Limit: none
And now that ocelot->ptp_clock is checked at exit, prevent a potential
error where ptp_clock_register returned a pointer-encoded error, which
we are keeping in the ocelot private data structure. So now,
ocelot->ptp_clock is now either NULL or a valid pointer.
Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
Cc: Antoine Tenart <antoine.tenart@bootlin.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-12-03 23:45:35 +08:00
|
|
|
struct ptp_clock *ptp_clock;
|
|
|
|
|
2019-08-12 22:45:37 +08:00
|
|
|
ocelot->ptp_info = ocelot_ptp_clock_info;
|
net: mscc: ocelot: unregister the PTP clock on deinit
Currently a switch driver deinit frees the regmaps, but the PTP clock is
still out there, available to user space via /dev/ptpN. Any PTP
operation is a ticking time bomb, since it will attempt to use the freed
regmaps and thus trigger kernel panics:
[ 4.291746] fsl_enetc 0000:00:00.2 eth1: error -22 setting up slave phy
[ 4.291871] mscc_felix 0000:00:00.5: Failed to register DSA switch: -22
[ 4.308666] mscc_felix: probe of 0000:00:00.5 failed with error -22
[ 6.358270] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000088
[ 6.367090] Mem abort info:
[ 6.369888] ESR = 0x96000046
[ 6.369891] EC = 0x25: DABT (current EL), IL = 32 bits
[ 6.369892] SET = 0, FnV = 0
[ 6.369894] EA = 0, S1PTW = 0
[ 6.369895] Data abort info:
[ 6.369897] ISV = 0, ISS = 0x00000046
[ 6.369899] CM = 0, WnR = 1
[ 6.369902] user pgtable: 4k pages, 48-bit VAs, pgdp=00000020d58c7000
[ 6.369904] [0000000000000088] pgd=00000020d5912003, pud=00000020d5915003, pmd=0000000000000000
[ 6.369914] Internal error: Oops: 96000046 [#1] PREEMPT SMP
[ 6.420443] Modules linked in:
[ 6.423506] CPU: 1 PID: 262 Comm: phc_ctl Not tainted 5.4.0-03625-gb7b2a5dadd7f #204
[ 6.431273] Hardware name: LS1028A RDB Board (DT)
[ 6.435989] pstate: 40000085 (nZcv daIf -PAN -UAO)
[ 6.440802] pc : css_release+0x24/0x58
[ 6.444561] lr : regmap_read+0x40/0x78
[ 6.448316] sp : ffff800010513cc0
[ 6.451636] x29: ffff800010513cc0 x28: ffff002055873040
[ 6.456963] x27: 0000000000000000 x26: 0000000000000000
[ 6.462289] x25: 0000000000000000 x24: 0000000000000000
[ 6.467617] x23: 0000000000000000 x22: 0000000000000080
[ 6.472944] x21: ffff800010513d44 x20: 0000000000000080
[ 6.478270] x19: 0000000000000000 x18: 0000000000000000
[ 6.483596] x17: 0000000000000000 x16: 0000000000000000
[ 6.488921] x15: 0000000000000000 x14: 0000000000000000
[ 6.494247] x13: 0000000000000000 x12: 0000000000000000
[ 6.499573] x11: 0000000000000000 x10: 0000000000000000
[ 6.504899] x9 : 0000000000000000 x8 : 0000000000000000
[ 6.510225] x7 : 0000000000000000 x6 : ffff800010513cf0
[ 6.515550] x5 : 0000000000000000 x4 : 0000000fffffffe0
[ 6.520876] x3 : 0000000000000088 x2 : ffff800010513d44
[ 6.526202] x1 : ffffcada668ea000 x0 : ffffcada64d8b0c0
[ 6.531528] Call trace:
[ 6.533977] css_release+0x24/0x58
[ 6.537385] regmap_read+0x40/0x78
[ 6.540795] __ocelot_read_ix+0x6c/0xa0
[ 6.544641] ocelot_ptp_gettime64+0x4c/0x110
[ 6.548921] ptp_clock_gettime+0x4c/0x58
[ 6.552853] pc_clock_gettime+0x5c/0xa8
[ 6.556699] __arm64_sys_clock_gettime+0x68/0xc8
[ 6.561331] el0_svc_common.constprop.2+0x7c/0x178
[ 6.566133] el0_svc_handler+0x34/0xa0
[ 6.569891] el0_sync_handler+0x114/0x1d0
[ 6.573908] el0_sync+0x140/0x180
[ 6.577232] Code: d503201f b00119a1 91022263 b27b7be4 (f9004663)
[ 6.583349] ---[ end trace d196b9b14cdae2da ]---
[ 6.587977] Kernel panic - not syncing: Fatal exception
[ 6.593216] SMP: stopping secondary CPUs
[ 6.597151] Kernel Offset: 0x4ada54400000 from 0xffff800010000000
[ 6.603261] PHYS_OFFSET: 0xffffd0a7c0000000
[ 6.607454] CPU features: 0x10002,21806008
[ 6.611558] Memory Limit: none
And now that ocelot->ptp_clock is checked at exit, prevent a potential
error where ptp_clock_register returned a pointer-encoded error, which
we are keeping in the ocelot private data structure. So now,
ocelot->ptp_clock is now either NULL or a valid pointer.
Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
Cc: Antoine Tenart <antoine.tenart@bootlin.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-12-03 23:45:35 +08:00
|
|
|
ptp_clock = ptp_clock_register(&ocelot->ptp_info, ocelot->dev);
|
|
|
|
if (IS_ERR(ptp_clock))
|
|
|
|
return PTR_ERR(ptp_clock);
|
2019-08-12 22:45:37 +08:00
|
|
|
/* Check if PHC support is missing at the configuration level */
|
net: mscc: ocelot: unregister the PTP clock on deinit
Currently a switch driver deinit frees the regmaps, but the PTP clock is
still out there, available to user space via /dev/ptpN. Any PTP
operation is a ticking time bomb, since it will attempt to use the freed
regmaps and thus trigger kernel panics:
[ 4.291746] fsl_enetc 0000:00:00.2 eth1: error -22 setting up slave phy
[ 4.291871] mscc_felix 0000:00:00.5: Failed to register DSA switch: -22
[ 4.308666] mscc_felix: probe of 0000:00:00.5 failed with error -22
[ 6.358270] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000088
[ 6.367090] Mem abort info:
[ 6.369888] ESR = 0x96000046
[ 6.369891] EC = 0x25: DABT (current EL), IL = 32 bits
[ 6.369892] SET = 0, FnV = 0
[ 6.369894] EA = 0, S1PTW = 0
[ 6.369895] Data abort info:
[ 6.369897] ISV = 0, ISS = 0x00000046
[ 6.369899] CM = 0, WnR = 1
[ 6.369902] user pgtable: 4k pages, 48-bit VAs, pgdp=00000020d58c7000
[ 6.369904] [0000000000000088] pgd=00000020d5912003, pud=00000020d5915003, pmd=0000000000000000
[ 6.369914] Internal error: Oops: 96000046 [#1] PREEMPT SMP
[ 6.420443] Modules linked in:
[ 6.423506] CPU: 1 PID: 262 Comm: phc_ctl Not tainted 5.4.0-03625-gb7b2a5dadd7f #204
[ 6.431273] Hardware name: LS1028A RDB Board (DT)
[ 6.435989] pstate: 40000085 (nZcv daIf -PAN -UAO)
[ 6.440802] pc : css_release+0x24/0x58
[ 6.444561] lr : regmap_read+0x40/0x78
[ 6.448316] sp : ffff800010513cc0
[ 6.451636] x29: ffff800010513cc0 x28: ffff002055873040
[ 6.456963] x27: 0000000000000000 x26: 0000000000000000
[ 6.462289] x25: 0000000000000000 x24: 0000000000000000
[ 6.467617] x23: 0000000000000000 x22: 0000000000000080
[ 6.472944] x21: ffff800010513d44 x20: 0000000000000080
[ 6.478270] x19: 0000000000000000 x18: 0000000000000000
[ 6.483596] x17: 0000000000000000 x16: 0000000000000000
[ 6.488921] x15: 0000000000000000 x14: 0000000000000000
[ 6.494247] x13: 0000000000000000 x12: 0000000000000000
[ 6.499573] x11: 0000000000000000 x10: 0000000000000000
[ 6.504899] x9 : 0000000000000000 x8 : 0000000000000000
[ 6.510225] x7 : 0000000000000000 x6 : ffff800010513cf0
[ 6.515550] x5 : 0000000000000000 x4 : 0000000fffffffe0
[ 6.520876] x3 : 0000000000000088 x2 : ffff800010513d44
[ 6.526202] x1 : ffffcada668ea000 x0 : ffffcada64d8b0c0
[ 6.531528] Call trace:
[ 6.533977] css_release+0x24/0x58
[ 6.537385] regmap_read+0x40/0x78
[ 6.540795] __ocelot_read_ix+0x6c/0xa0
[ 6.544641] ocelot_ptp_gettime64+0x4c/0x110
[ 6.548921] ptp_clock_gettime+0x4c/0x58
[ 6.552853] pc_clock_gettime+0x5c/0xa8
[ 6.556699] __arm64_sys_clock_gettime+0x68/0xc8
[ 6.561331] el0_svc_common.constprop.2+0x7c/0x178
[ 6.566133] el0_svc_handler+0x34/0xa0
[ 6.569891] el0_sync_handler+0x114/0x1d0
[ 6.573908] el0_sync+0x140/0x180
[ 6.577232] Code: d503201f b00119a1 91022263 b27b7be4 (f9004663)
[ 6.583349] ---[ end trace d196b9b14cdae2da ]---
[ 6.587977] Kernel panic - not syncing: Fatal exception
[ 6.593216] SMP: stopping secondary CPUs
[ 6.597151] Kernel Offset: 0x4ada54400000 from 0xffff800010000000
[ 6.603261] PHYS_OFFSET: 0xffffd0a7c0000000
[ 6.607454] CPU features: 0x10002,21806008
[ 6.611558] Memory Limit: none
And now that ocelot->ptp_clock is checked at exit, prevent a potential
error where ptp_clock_register returned a pointer-encoded error, which
we are keeping in the ocelot private data structure. So now,
ocelot->ptp_clock is now either NULL or a valid pointer.
Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
Cc: Antoine Tenart <antoine.tenart@bootlin.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-12-03 23:45:35 +08:00
|
|
|
if (!ptp_clock)
|
2019-08-12 22:45:37 +08:00
|
|
|
return 0;
|
|
|
|
|
net: mscc: ocelot: unregister the PTP clock on deinit
Currently a switch driver deinit frees the regmaps, but the PTP clock is
still out there, available to user space via /dev/ptpN. Any PTP
operation is a ticking time bomb, since it will attempt to use the freed
regmaps and thus trigger kernel panics:
[ 4.291746] fsl_enetc 0000:00:00.2 eth1: error -22 setting up slave phy
[ 4.291871] mscc_felix 0000:00:00.5: Failed to register DSA switch: -22
[ 4.308666] mscc_felix: probe of 0000:00:00.5 failed with error -22
[ 6.358270] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000088
[ 6.367090] Mem abort info:
[ 6.369888] ESR = 0x96000046
[ 6.369891] EC = 0x25: DABT (current EL), IL = 32 bits
[ 6.369892] SET = 0, FnV = 0
[ 6.369894] EA = 0, S1PTW = 0
[ 6.369895] Data abort info:
[ 6.369897] ISV = 0, ISS = 0x00000046
[ 6.369899] CM = 0, WnR = 1
[ 6.369902] user pgtable: 4k pages, 48-bit VAs, pgdp=00000020d58c7000
[ 6.369904] [0000000000000088] pgd=00000020d5912003, pud=00000020d5915003, pmd=0000000000000000
[ 6.369914] Internal error: Oops: 96000046 [#1] PREEMPT SMP
[ 6.420443] Modules linked in:
[ 6.423506] CPU: 1 PID: 262 Comm: phc_ctl Not tainted 5.4.0-03625-gb7b2a5dadd7f #204
[ 6.431273] Hardware name: LS1028A RDB Board (DT)
[ 6.435989] pstate: 40000085 (nZcv daIf -PAN -UAO)
[ 6.440802] pc : css_release+0x24/0x58
[ 6.444561] lr : regmap_read+0x40/0x78
[ 6.448316] sp : ffff800010513cc0
[ 6.451636] x29: ffff800010513cc0 x28: ffff002055873040
[ 6.456963] x27: 0000000000000000 x26: 0000000000000000
[ 6.462289] x25: 0000000000000000 x24: 0000000000000000
[ 6.467617] x23: 0000000000000000 x22: 0000000000000080
[ 6.472944] x21: ffff800010513d44 x20: 0000000000000080
[ 6.478270] x19: 0000000000000000 x18: 0000000000000000
[ 6.483596] x17: 0000000000000000 x16: 0000000000000000
[ 6.488921] x15: 0000000000000000 x14: 0000000000000000
[ 6.494247] x13: 0000000000000000 x12: 0000000000000000
[ 6.499573] x11: 0000000000000000 x10: 0000000000000000
[ 6.504899] x9 : 0000000000000000 x8 : 0000000000000000
[ 6.510225] x7 : 0000000000000000 x6 : ffff800010513cf0
[ 6.515550] x5 : 0000000000000000 x4 : 0000000fffffffe0
[ 6.520876] x3 : 0000000000000088 x2 : ffff800010513d44
[ 6.526202] x1 : ffffcada668ea000 x0 : ffffcada64d8b0c0
[ 6.531528] Call trace:
[ 6.533977] css_release+0x24/0x58
[ 6.537385] regmap_read+0x40/0x78
[ 6.540795] __ocelot_read_ix+0x6c/0xa0
[ 6.544641] ocelot_ptp_gettime64+0x4c/0x110
[ 6.548921] ptp_clock_gettime+0x4c/0x58
[ 6.552853] pc_clock_gettime+0x5c/0xa8
[ 6.556699] __arm64_sys_clock_gettime+0x68/0xc8
[ 6.561331] el0_svc_common.constprop.2+0x7c/0x178
[ 6.566133] el0_svc_handler+0x34/0xa0
[ 6.569891] el0_sync_handler+0x114/0x1d0
[ 6.573908] el0_sync+0x140/0x180
[ 6.577232] Code: d503201f b00119a1 91022263 b27b7be4 (f9004663)
[ 6.583349] ---[ end trace d196b9b14cdae2da ]---
[ 6.587977] Kernel panic - not syncing: Fatal exception
[ 6.593216] SMP: stopping secondary CPUs
[ 6.597151] Kernel Offset: 0x4ada54400000 from 0xffff800010000000
[ 6.603261] PHYS_OFFSET: 0xffffd0a7c0000000
[ 6.607454] CPU features: 0x10002,21806008
[ 6.611558] Memory Limit: none
And now that ocelot->ptp_clock is checked at exit, prevent a potential
error where ptp_clock_register returned a pointer-encoded error, which
we are keeping in the ocelot private data structure. So now,
ocelot->ptp_clock is now either NULL or a valid pointer.
Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
Cc: Antoine Tenart <antoine.tenart@bootlin.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-12-03 23:45:35 +08:00
|
|
|
ocelot->ptp_clock = ptp_clock;
|
|
|
|
|
2019-08-12 22:45:37 +08:00
|
|
|
ocelot_write(ocelot, SYS_PTP_CFG_PTP_STAMP_WID(30), SYS_PTP_CFG);
|
|
|
|
ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_LOW);
|
|
|
|
ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_HIGH);
|
|
|
|
|
|
|
|
ocelot_write(ocelot, PTP_CFG_MISC_PTP_EN, PTP_CFG_MISC);
|
|
|
|
|
|
|
|
/* There is no device reconfiguration, PTP Rx stamping is always
|
|
|
|
* enabled.
|
|
|
|
*/
|
|
|
|
ocelot->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-10 09:28:18 +08:00
|
|
|
/* Configure the maximum SDU (L2 payload) on RX to the value specified in @sdu.
|
|
|
|
* The length of VLAN tags is accounted for automatically via DEV_MAC_TAGS_CFG.
|
2020-03-28 03:55:47 +08:00
|
|
|
* In the special case that it's the NPI port that we're configuring, the
|
|
|
|
* length of the tag and optional prefix needs to be accounted for privately,
|
|
|
|
* in order to be able to sustain communication at the requested @sdu.
|
2020-03-10 09:28:18 +08:00
|
|
|
*/
|
2020-03-28 03:55:47 +08:00
|
|
|
void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu)
|
2019-11-09 21:02:56 +08:00
|
|
|
{
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
2020-03-10 09:28:18 +08:00
|
|
|
int maxlen = sdu + ETH_HLEN + ETH_FCS_LEN;
|
2019-11-14 23:03:22 +08:00
|
|
|
int atop_wm;
|
2019-11-09 21:02:56 +08:00
|
|
|
|
2020-03-28 03:55:47 +08:00
|
|
|
if (port == ocelot->npi) {
|
|
|
|
maxlen += OCELOT_TAG_LEN;
|
|
|
|
|
|
|
|
if (ocelot->inj_prefix == OCELOT_TAG_PREFIX_SHORT)
|
|
|
|
maxlen += OCELOT_SHORT_PREFIX_LEN;
|
|
|
|
else if (ocelot->inj_prefix == OCELOT_TAG_PREFIX_LONG)
|
|
|
|
maxlen += OCELOT_LONG_PREFIX_LEN;
|
|
|
|
}
|
|
|
|
|
2020-03-10 09:28:18 +08:00
|
|
|
ocelot_port_writel(ocelot_port, maxlen, DEV_MAC_MAXLEN_CFG);
|
2019-11-14 23:03:23 +08:00
|
|
|
|
|
|
|
/* Set Pause WM hysteresis
|
2020-03-10 09:28:18 +08:00
|
|
|
* 152 = 6 * maxlen / OCELOT_BUFFER_CELL_SZ
|
|
|
|
* 101 = 4 * maxlen / OCELOT_BUFFER_CELL_SZ
|
2019-11-14 23:03:23 +08:00
|
|
|
*/
|
|
|
|
ocelot_write_rix(ocelot, SYS_PAUSE_CFG_PAUSE_ENA |
|
|
|
|
SYS_PAUSE_CFG_PAUSE_STOP(101) |
|
|
|
|
SYS_PAUSE_CFG_PAUSE_START(152), SYS_PAUSE_CFG, port);
|
|
|
|
|
|
|
|
/* Tail dropping watermark */
|
2020-03-10 09:28:18 +08:00
|
|
|
atop_wm = (ocelot->shared_queue_sz - 9 * maxlen) /
|
|
|
|
OCELOT_BUFFER_CELL_SZ;
|
|
|
|
ocelot_write_rix(ocelot, ocelot_wm_enc(9 * maxlen),
|
2019-11-14 23:03:23 +08:00
|
|
|
SYS_ATOP, port);
|
|
|
|
ocelot_write(ocelot, ocelot_wm_enc(atop_wm), SYS_ATOP_TOT_CFG);
|
|
|
|
}
|
2020-03-28 03:55:47 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_set_maxlen);
|
|
|
|
|
|
|
|
int ocelot_get_max_mtu(struct ocelot *ocelot, int port)
|
|
|
|
{
|
|
|
|
int max_mtu = 65535 - ETH_HLEN - ETH_FCS_LEN;
|
|
|
|
|
|
|
|
if (port == ocelot->npi) {
|
|
|
|
max_mtu -= OCELOT_TAG_LEN;
|
|
|
|
|
|
|
|
if (ocelot->inj_prefix == OCELOT_TAG_PREFIX_SHORT)
|
|
|
|
max_mtu -= OCELOT_SHORT_PREFIX_LEN;
|
|
|
|
else if (ocelot->inj_prefix == OCELOT_TAG_PREFIX_LONG)
|
|
|
|
max_mtu -= OCELOT_LONG_PREFIX_LEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
return max_mtu;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_get_max_mtu);
|
2019-11-14 23:03:23 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_init_port(struct ocelot *ocelot, int port)
|
2019-11-14 23:03:23 +08:00
|
|
|
{
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
|
|
|
|
2019-11-27 15:27:57 +08:00
|
|
|
skb_queue_head_init(&ocelot_port->tx_skbs);
|
2019-11-09 21:02:56 +08:00
|
|
|
|
|
|
|
/* Basic L2 initialization */
|
|
|
|
|
2019-11-14 23:03:22 +08:00
|
|
|
/* Set MAC IFG Gaps
|
|
|
|
* FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 0
|
|
|
|
* !FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 5
|
|
|
|
*/
|
|
|
|
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 */
|
|
|
|
ocelot_port_writel(ocelot_port, DEV_MAC_HDX_CFG_LATE_COL_POS(67) |
|
|
|
|
DEV_MAC_HDX_CFG_SEED_LOAD,
|
|
|
|
DEV_MAC_HDX_CFG);
|
|
|
|
mdelay(1);
|
|
|
|
ocelot_port_writel(ocelot_port, DEV_MAC_HDX_CFG_LATE_COL_POS(67),
|
|
|
|
DEV_MAC_HDX_CFG);
|
|
|
|
|
|
|
|
/* Set Max Length and maximum tags allowed */
|
2020-03-10 09:28:18 +08:00
|
|
|
ocelot_port_set_maxlen(ocelot, port, ETH_DATA_LEN);
|
2019-11-14 23:03:22 +08:00
|
|
|
ocelot_port_writel(ocelot_port, DEV_MAC_TAGS_CFG_TAG_ID(ETH_P_8021AD) |
|
|
|
|
DEV_MAC_TAGS_CFG_VLAN_AWR_ENA |
|
2020-03-10 09:28:18 +08:00
|
|
|
DEV_MAC_TAGS_CFG_VLAN_DBL_AWR_ENA |
|
2019-11-14 23:03:22 +08:00
|
|
|
DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA,
|
|
|
|
DEV_MAC_TAGS_CFG);
|
|
|
|
|
|
|
|
/* Set SMAC of Pause frame (00:00:00:00:00:00) */
|
|
|
|
ocelot_port_writel(ocelot_port, 0, DEV_MAC_FC_MAC_HIGH_CFG);
|
|
|
|
ocelot_port_writel(ocelot_port, 0, DEV_MAC_FC_MAC_LOW_CFG);
|
|
|
|
|
2019-11-09 21:02:56 +08:00
|
|
|
/* 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);
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_init_port);
|
2019-11-09 21:02:56 +08:00
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
int ocelot_probe_port(struct ocelot *ocelot, u8 port,
|
|
|
|
void __iomem *regs,
|
|
|
|
struct phy_device *phy)
|
|
|
|
{
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv;
|
2018-05-15 04:04:57 +08:00
|
|
|
struct ocelot_port *ocelot_port;
|
|
|
|
struct net_device *dev;
|
|
|
|
int err;
|
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
dev = alloc_etherdev(sizeof(struct ocelot_port_private));
|
2018-05-15 04:04:57 +08:00
|
|
|
if (!dev)
|
|
|
|
return -ENOMEM;
|
|
|
|
SET_NETDEV_DEV(dev, ocelot->dev);
|
2019-11-09 21:02:53 +08:00
|
|
|
priv = netdev_priv(dev);
|
|
|
|
priv->dev = dev;
|
|
|
|
priv->phy = phy;
|
|
|
|
priv->chip_port = port;
|
|
|
|
ocelot_port = &priv->port;
|
2018-05-15 04:04:57 +08:00
|
|
|
ocelot_port->ocelot = ocelot;
|
|
|
|
ocelot_port->regs = regs;
|
|
|
|
ocelot->ports[port] = ocelot_port;
|
|
|
|
|
|
|
|
dev->netdev_ops = &ocelot_port_netdev_ops;
|
|
|
|
dev->ethtool_ops = &ocelot_ethtool_ops;
|
|
|
|
|
2019-05-28 20:49:17 +08:00
|
|
|
dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS |
|
|
|
|
NETIF_F_HW_TC;
|
|
|
|
dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_TC;
|
2018-06-26 20:28:49 +08:00
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
memcpy(dev->dev_addr, ocelot->base_mac, ETH_ALEN);
|
|
|
|
dev->dev_addr[ETH_ALEN - 1] += port;
|
|
|
|
ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, ocelot_port->pvid,
|
|
|
|
ENTRYTYPE_LOCKED);
|
|
|
|
|
2019-11-09 21:02:56 +08:00
|
|
|
ocelot_init_port(ocelot, port);
|
2019-08-12 22:45:37 +08:00
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
err = register_netdev(dev);
|
|
|
|
if (err) {
|
|
|
|
dev_err(ocelot->dev, "register_netdev failed\n");
|
2019-11-09 21:02:56 +08:00
|
|
|
free_netdev(dev);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_probe_port);
|
|
|
|
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
/* Configure and enable the CPU port module, which is a set of queues.
|
|
|
|
* If @npi contains a valid port index, the CPU port module is connected
|
|
|
|
* to the Node Processor Interface (NPI). This is the mode through which
|
|
|
|
* frames can be injected from and extracted to an external CPU,
|
|
|
|
* over Ethernet.
|
|
|
|
*/
|
|
|
|
void ocelot_configure_cpu(struct ocelot *ocelot, int npi,
|
|
|
|
enum ocelot_tag_prefix injection,
|
|
|
|
enum ocelot_tag_prefix extraction)
|
2019-11-09 21:03:00 +08:00
|
|
|
{
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
int cpu = ocelot->num_phys_ports;
|
|
|
|
|
2020-03-28 03:55:47 +08:00
|
|
|
ocelot->npi = npi;
|
|
|
|
ocelot->inj_prefix = injection;
|
|
|
|
ocelot->xtr_prefix = extraction;
|
|
|
|
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
/* The unicast destination PGID for the CPU port module is unused */
|
2019-11-09 21:03:00 +08:00
|
|
|
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, cpu);
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
/* Instead set up a multicast destination PGID for traffic copied to
|
|
|
|
* the CPU. Whitelisted MAC addresses like the port netdevice MAC
|
|
|
|
* addresses will be copied to the CPU via this PGID.
|
|
|
|
*/
|
2019-11-09 21:03:00 +08:00
|
|
|
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);
|
|
|
|
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
if (npi >= 0 && npi < ocelot->num_phys_ports) {
|
2019-11-09 21:03:00 +08:00
|
|
|
ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M |
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
QSYS_EXT_CPU_CFG_EXT_CPU_PORT(npi),
|
2019-11-09 21:03:00 +08:00
|
|
|
QSYS_EXT_CPU_CFG);
|
2019-11-14 23:03:25 +08:00
|
|
|
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
/* Enable NPI port */
|
|
|
|
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, npi);
|
|
|
|
/* NPI port Injection/Extraction configuration */
|
|
|
|
ocelot_write_rix(ocelot,
|
|
|
|
SYS_PORT_MODE_INCL_XTR_HDR(extraction) |
|
|
|
|
SYS_PORT_MODE_INCL_INJ_HDR(injection),
|
|
|
|
SYS_PORT_MODE, npi);
|
2019-11-09 21:03:00 +08:00
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
/* Enable CPU port module */
|
2019-11-09 21:03:00 +08:00
|
|
|
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);
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
/* CPU port Injection/Extraction configuration */
|
2019-11-09 21:03:00 +08:00
|
|
|
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);
|
|
|
|
}
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_configure_cpu);
|
2019-11-09 21:03:00 +08:00
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
int ocelot_init(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
char queue_name[32];
|
2019-11-09 21:03:00 +08:00
|
|
|
int i, ret;
|
|
|
|
u32 port;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-14 23:03:26 +08:00
|
|
|
if (ocelot->ops->reset) {
|
|
|
|
ret = ocelot->ops->reset(ocelot);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(ocelot->dev, "Switch reset failed\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-26 20:28:48 +08:00
|
|
|
ocelot->lags = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
|
|
|
|
sizeof(u32), GFP_KERNEL);
|
|
|
|
if (!ocelot->lags)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
ocelot->stats = devm_kcalloc(ocelot->dev,
|
|
|
|
ocelot->num_phys_ports * ocelot->num_stats,
|
|
|
|
sizeof(u64), GFP_KERNEL);
|
|
|
|
if (!ocelot->stats)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
mutex_init(&ocelot->stats_lock);
|
2019-08-12 22:45:37 +08:00
|
|
|
mutex_init(&ocelot->ptp_lock);
|
|
|
|
spin_lock_init(&ocelot->ptp_clock_lock);
|
2018-05-15 04:04:57 +08:00
|
|
|
snprintf(queue_name, sizeof(queue_name), "%s-stats",
|
|
|
|
dev_name(ocelot->dev));
|
|
|
|
ocelot->stats_queue = create_singlethread_workqueue(queue_name);
|
|
|
|
if (!ocelot->stats_queue)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2019-11-09 21:02:58 +08:00
|
|
|
INIT_LIST_HEAD(&ocelot->multicast);
|
2018-05-15 04:04:57 +08:00
|
|
|
ocelot_mact_init(ocelot);
|
|
|
|
ocelot_vlan_init(ocelot);
|
2019-05-31 15:16:56 +08:00
|
|
|
ocelot_ace_init(ocelot);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
for (port = 0; port < ocelot->num_phys_ports; port++) {
|
|
|
|
/* Clear all counters (5 groups) */
|
|
|
|
ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port) |
|
|
|
|
SYS_STAT_CFG_STAT_CLEAR_SHOT(0x7f),
|
|
|
|
SYS_STAT_CFG);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Only use S-Tag */
|
|
|
|
ocelot_write(ocelot, ETH_P_8021AD, SYS_VLAN_ETYPE_CFG);
|
|
|
|
|
|
|
|
/* Aggregation mode */
|
|
|
|
ocelot_write(ocelot, ANA_AGGR_CFG_AC_SMAC_ENA |
|
|
|
|
ANA_AGGR_CFG_AC_DMAC_ENA |
|
|
|
|
ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA |
|
|
|
|
ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA, ANA_AGGR_CFG);
|
|
|
|
|
|
|
|
/* Set MAC age time to default value. The entry is aged after
|
|
|
|
* 2*AGE_PERIOD
|
|
|
|
*/
|
|
|
|
ocelot_write(ocelot,
|
|
|
|
ANA_AUTOAGE_AGE_PERIOD(BR_DEFAULT_AGEING_TIME / 2 / HZ),
|
|
|
|
ANA_AUTOAGE);
|
|
|
|
|
|
|
|
/* Disable learning for frames discarded by VLAN ingress filtering */
|
|
|
|
regmap_field_write(ocelot->regfields[ANA_ADVLEARN_VLAN_CHK], 1);
|
|
|
|
|
|
|
|
/* Setup frame ageing - fixed value "2 sec" - in 6.5 us units */
|
|
|
|
ocelot_write(ocelot, SYS_FRM_AGING_AGE_TX_ENA |
|
|
|
|
SYS_FRM_AGING_MAX_AGE(307692), SYS_FRM_AGING);
|
|
|
|
|
|
|
|
/* Setup flooding PGIDs */
|
|
|
|
ocelot_write_rix(ocelot, ANA_FLOODING_FLD_MULTICAST(PGID_MC) |
|
|
|
|
ANA_FLOODING_FLD_BROADCAST(PGID_MC) |
|
|
|
|
ANA_FLOODING_FLD_UNICAST(PGID_UC),
|
|
|
|
ANA_FLOODING, 0);
|
|
|
|
ocelot_write(ocelot, ANA_FLOODING_IPMC_FLD_MC6_DATA(PGID_MCIPV6) |
|
|
|
|
ANA_FLOODING_IPMC_FLD_MC6_CTRL(PGID_MC) |
|
|
|
|
ANA_FLOODING_IPMC_FLD_MC4_DATA(PGID_MCIPV4) |
|
|
|
|
ANA_FLOODING_IPMC_FLD_MC4_CTRL(PGID_MC),
|
|
|
|
ANA_FLOODING_IPMC);
|
|
|
|
|
|
|
|
for (port = 0; port < ocelot->num_phys_ports; port++) {
|
|
|
|
/* Transmit the frame to the local port. */
|
|
|
|
ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, port);
|
|
|
|
/* Do not forward BPDU frames to the front ports. */
|
|
|
|
ocelot_write_gix(ocelot,
|
|
|
|
ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0xffff),
|
|
|
|
ANA_PORT_CPU_FWD_BPDU_CFG,
|
|
|
|
port);
|
|
|
|
/* Ensure bridging is disabled */
|
|
|
|
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_SRC + port);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allow broadcast MAC frames. */
|
|
|
|
for (i = ocelot->num_phys_ports + 1; i < PGID_CPU; i++) {
|
|
|
|
u32 val = ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports - 1, 0));
|
|
|
|
|
|
|
|
ocelot_write_rix(ocelot, val, ANA_PGID_PGID, i);
|
|
|
|
}
|
|
|
|
ocelot_write_rix(ocelot,
|
|
|
|
ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports, 0)),
|
|
|
|
ANA_PGID_PGID, PGID_MC);
|
|
|
|
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV4);
|
|
|
|
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV6);
|
|
|
|
|
|
|
|
/* Allow manual injection via DEVCPU_QS registers, and byte swap these
|
|
|
|
* registers endianness.
|
|
|
|
*/
|
|
|
|
ocelot_write_rix(ocelot, QS_INJ_GRP_CFG_BYTE_SWAP |
|
|
|
|
QS_INJ_GRP_CFG_MODE(1), QS_INJ_GRP_CFG, 0);
|
|
|
|
ocelot_write_rix(ocelot, QS_XTR_GRP_CFG_BYTE_SWAP |
|
|
|
|
QS_XTR_GRP_CFG_MODE(1), QS_XTR_GRP_CFG, 0);
|
|
|
|
ocelot_write(ocelot, ANA_CPUQ_CFG_CPUQ_MIRROR(2) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_LRN(2) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_MAC_COPY(2) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_SRC_COPY(2) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_LOCKED_PORTMOVE(2) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_ALLBRIDGE(6) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_IPMC_CTRL(6) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_IGMP(6) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_MLD(6), ANA_CPUQ_CFG);
|
|
|
|
for (i = 0; i < 16; i++)
|
|
|
|
ocelot_write_rix(ocelot, ANA_CPUQ_8021_CFG_CPUQ_GARP_VAL(6) |
|
|
|
|
ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL(6),
|
|
|
|
ANA_CPUQ_8021_CFG, i);
|
|
|
|
|
2019-04-16 22:51:59 +08:00
|
|
|
INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work);
|
2018-05-15 04:04:57 +08:00
|
|
|
queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
|
|
|
|
OCELOT_STATS_CHECK_DELAY);
|
2019-08-12 22:45:37 +08:00
|
|
|
|
|
|
|
if (ocelot->ptp) {
|
|
|
|
ret = ocelot_init_timestamp(ocelot);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(ocelot->dev,
|
|
|
|
"Timestamp initialization failed\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_init);
|
|
|
|
|
|
|
|
void ocelot_deinit(struct ocelot *ocelot)
|
|
|
|
{
|
2019-08-12 22:45:37 +08:00
|
|
|
struct ocelot_port *port;
|
|
|
|
int i;
|
|
|
|
|
2019-07-25 21:33:18 +08:00
|
|
|
cancel_delayed_work(&ocelot->stats_work);
|
2018-05-15 04:04:57 +08:00
|
|
|
destroy_workqueue(ocelot->stats_queue);
|
|
|
|
mutex_destroy(&ocelot->stats_lock);
|
net: mscc: ocelot: unregister the PTP clock on deinit
Currently a switch driver deinit frees the regmaps, but the PTP clock is
still out there, available to user space via /dev/ptpN. Any PTP
operation is a ticking time bomb, since it will attempt to use the freed
regmaps and thus trigger kernel panics:
[ 4.291746] fsl_enetc 0000:00:00.2 eth1: error -22 setting up slave phy
[ 4.291871] mscc_felix 0000:00:00.5: Failed to register DSA switch: -22
[ 4.308666] mscc_felix: probe of 0000:00:00.5 failed with error -22
[ 6.358270] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000088
[ 6.367090] Mem abort info:
[ 6.369888] ESR = 0x96000046
[ 6.369891] EC = 0x25: DABT (current EL), IL = 32 bits
[ 6.369892] SET = 0, FnV = 0
[ 6.369894] EA = 0, S1PTW = 0
[ 6.369895] Data abort info:
[ 6.369897] ISV = 0, ISS = 0x00000046
[ 6.369899] CM = 0, WnR = 1
[ 6.369902] user pgtable: 4k pages, 48-bit VAs, pgdp=00000020d58c7000
[ 6.369904] [0000000000000088] pgd=00000020d5912003, pud=00000020d5915003, pmd=0000000000000000
[ 6.369914] Internal error: Oops: 96000046 [#1] PREEMPT SMP
[ 6.420443] Modules linked in:
[ 6.423506] CPU: 1 PID: 262 Comm: phc_ctl Not tainted 5.4.0-03625-gb7b2a5dadd7f #204
[ 6.431273] Hardware name: LS1028A RDB Board (DT)
[ 6.435989] pstate: 40000085 (nZcv daIf -PAN -UAO)
[ 6.440802] pc : css_release+0x24/0x58
[ 6.444561] lr : regmap_read+0x40/0x78
[ 6.448316] sp : ffff800010513cc0
[ 6.451636] x29: ffff800010513cc0 x28: ffff002055873040
[ 6.456963] x27: 0000000000000000 x26: 0000000000000000
[ 6.462289] x25: 0000000000000000 x24: 0000000000000000
[ 6.467617] x23: 0000000000000000 x22: 0000000000000080
[ 6.472944] x21: ffff800010513d44 x20: 0000000000000080
[ 6.478270] x19: 0000000000000000 x18: 0000000000000000
[ 6.483596] x17: 0000000000000000 x16: 0000000000000000
[ 6.488921] x15: 0000000000000000 x14: 0000000000000000
[ 6.494247] x13: 0000000000000000 x12: 0000000000000000
[ 6.499573] x11: 0000000000000000 x10: 0000000000000000
[ 6.504899] x9 : 0000000000000000 x8 : 0000000000000000
[ 6.510225] x7 : 0000000000000000 x6 : ffff800010513cf0
[ 6.515550] x5 : 0000000000000000 x4 : 0000000fffffffe0
[ 6.520876] x3 : 0000000000000088 x2 : ffff800010513d44
[ 6.526202] x1 : ffffcada668ea000 x0 : ffffcada64d8b0c0
[ 6.531528] Call trace:
[ 6.533977] css_release+0x24/0x58
[ 6.537385] regmap_read+0x40/0x78
[ 6.540795] __ocelot_read_ix+0x6c/0xa0
[ 6.544641] ocelot_ptp_gettime64+0x4c/0x110
[ 6.548921] ptp_clock_gettime+0x4c/0x58
[ 6.552853] pc_clock_gettime+0x5c/0xa8
[ 6.556699] __arm64_sys_clock_gettime+0x68/0xc8
[ 6.561331] el0_svc_common.constprop.2+0x7c/0x178
[ 6.566133] el0_svc_handler+0x34/0xa0
[ 6.569891] el0_sync_handler+0x114/0x1d0
[ 6.573908] el0_sync+0x140/0x180
[ 6.577232] Code: d503201f b00119a1 91022263 b27b7be4 (f9004663)
[ 6.583349] ---[ end trace d196b9b14cdae2da ]---
[ 6.587977] Kernel panic - not syncing: Fatal exception
[ 6.593216] SMP: stopping secondary CPUs
[ 6.597151] Kernel Offset: 0x4ada54400000 from 0xffff800010000000
[ 6.603261] PHYS_OFFSET: 0xffffd0a7c0000000
[ 6.607454] CPU features: 0x10002,21806008
[ 6.611558] Memory Limit: none
And now that ocelot->ptp_clock is checked at exit, prevent a potential
error where ptp_clock_register returned a pointer-encoded error, which
we are keeping in the ocelot private data structure. So now,
ocelot->ptp_clock is now either NULL or a valid pointer.
Fixes: 4e3b0468e6d7 ("net: mscc: PTP Hardware Clock (PHC) support")
Cc: Antoine Tenart <antoine.tenart@bootlin.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-12-03 23:45:35 +08:00
|
|
|
if (ocelot->ptp_clock)
|
|
|
|
ptp_clock_unregister(ocelot->ptp_clock);
|
2019-08-12 22:45:37 +08:00
|
|
|
|
|
|
|
for (i = 0; i < ocelot->num_phys_ports; i++) {
|
|
|
|
port = ocelot->ports[i];
|
2019-11-27 15:27:57 +08:00
|
|
|
skb_queue_purge(&port->tx_skbs);
|
2019-08-12 22:45:37 +08:00
|
|
|
}
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_deinit);
|
|
|
|
|
|
|
|
MODULE_LICENSE("Dual MIT/GPL");
|