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

Merge branch 'Ocelot-Felix-driver-cleanup'

Vladimir Oltean says:

====================
Ocelot/Felix driver cleanup

Some of the code in the mscc felix and ocelot drivers was added while in
a bit of a hurry. Let's take a moment and put things in relative order.

First 3 patches are sparse warning fixes.

Patches 4-9 perform some further splitting between mscc_felix,
mscc_ocelot, and the common hardware library they share. Meaning that
some code is being moved from the library into just the mscc_ocelot
module.

Patches 10-12 refactor the naming conventions in the existing VCAP code
(for tc-flower offload), since we're going to be adding some more code
for VCAP IS1 (previous tentatives already submitted here:
https://patchwork.ozlabs.org/project/netdev/cover/20200602051828.5734-1-xiaoliang.yang_1@nxp.com/),
and that code would be confusing to read and maintain using current
naming conventions.

No functional modification is intended. I checked that the VCAP IS2 code
still works by applying a tc ingress filter with an EtherType key and
'drop' action.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 60cb8d3d c73b0ad3
...@@ -4,7 +4,9 @@ config NET_DSA_MSCC_FELIX ...@@ -4,7 +4,9 @@ config NET_DSA_MSCC_FELIX
depends on NET_DSA && PCI depends on NET_DSA && PCI
depends on NET_VENDOR_MICROSEMI depends on NET_VENDOR_MICROSEMI
depends on NET_VENDOR_FREESCALE depends on NET_VENDOR_FREESCALE
select MSCC_OCELOT_SWITCH depends on HAS_IOMEM
depends on REGMAP_MMIO
select MSCC_OCELOT_SWITCH_LIB
select NET_DSA_TAG_OCELOT select NET_DSA_TAG_OCELOT
select FSL_ENETC_MDIO select FSL_ENETC_MDIO
help help
......
...@@ -557,7 +557,7 @@ static const struct ocelot_stat_layout vsc9959_stats_layout[] = { ...@@ -557,7 +557,7 @@ static const struct ocelot_stat_layout vsc9959_stats_layout[] = {
{ .offset = 0x111, .name = "drop_green_prio_7", }, { .offset = 0x111, .name = "drop_green_prio_7", },
}; };
struct vcap_field vsc9959_vcap_is2_keys[] = { static struct vcap_field vsc9959_vcap_is2_keys[] = {
/* Common: 41 bits */ /* Common: 41 bits */
[VCAP_IS2_TYPE] = { 0, 4}, [VCAP_IS2_TYPE] = { 0, 4},
[VCAP_IS2_HK_FIRST] = { 4, 1}, [VCAP_IS2_HK_FIRST] = { 4, 1},
...@@ -637,7 +637,7 @@ struct vcap_field vsc9959_vcap_is2_keys[] = { ...@@ -637,7 +637,7 @@ struct vcap_field vsc9959_vcap_is2_keys[] = {
[VCAP_IS2_HK_OAM_IS_Y1731] = {182, 1}, [VCAP_IS2_HK_OAM_IS_Y1731] = {182, 1},
}; };
struct vcap_field vsc9959_vcap_is2_actions[] = { static struct vcap_field vsc9959_vcap_is2_actions[] = {
[VCAP_IS2_ACT_HIT_ME_ONCE] = { 0, 1}, [VCAP_IS2_ACT_HIT_ME_ONCE] = { 0, 1},
[VCAP_IS2_ACT_CPU_COPY_ENA] = { 1, 1}, [VCAP_IS2_ACT_CPU_COPY_ENA] = { 1, 1},
[VCAP_IS2_ACT_CPU_QU_NUM] = { 2, 3}, [VCAP_IS2_ACT_CPU_QU_NUM] = { 2, 3},
......
...@@ -11,22 +11,24 @@ config NET_VENDOR_MICROSEMI ...@@ -11,22 +11,24 @@ config NET_VENDOR_MICROSEMI
if NET_VENDOR_MICROSEMI if NET_VENDOR_MICROSEMI
# Users should depend on NET_SWITCHDEV, HAS_IOMEM, PHYLIB and REGMAP_MMIO
config MSCC_OCELOT_SWITCH_LIB
tristate
help
This is a hardware support library for Ocelot network switches. It is
used by switchdev as well as by DSA drivers.
config MSCC_OCELOT_SWITCH config MSCC_OCELOT_SWITCH
tristate "Ocelot switch driver" tristate "Ocelot switch driver"
depends on NET_SWITCHDEV depends on NET_SWITCHDEV
depends on HAS_IOMEM
select PHYLIB
select REGMAP_MMIO
help
This driver supports the Ocelot network switch device.
config MSCC_OCELOT_SWITCH_OCELOT
tristate "Ocelot switch driver on Ocelot"
depends on MSCC_OCELOT_SWITCH
depends on GENERIC_PHY depends on GENERIC_PHY
depends on REGMAP_MMIO
depends on HAS_IOMEM
depends on PHYLIB
depends on OF_NET depends on OF_NET
select MSCC_OCELOT_SWITCH_LIB
help help
This driver supports the Ocelot network switch device as present on This driver supports the Ocelot network switch device as present on
the Ocelot SoCs. the Ocelot SoCs (VSC7514).
endif # NET_VENDOR_MICROSEMI endif # NET_VENDOR_MICROSEMI
# SPDX-License-Identifier: (GPL-2.0 OR MIT) # SPDX-License-Identifier: (GPL-2.0 OR MIT)
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot_common.o obj-$(CONFIG_MSCC_OCELOT_SWITCH_LIB) += mscc_ocelot_switch_lib.o
mscc_ocelot_common-y := ocelot.o ocelot_io.o mscc_ocelot_switch_lib-y := \
mscc_ocelot_common-y += ocelot_regs.o ocelot_tc.o ocelot_police.o ocelot_ace.o ocelot_flower.o ocelot_ptp.o ocelot.o \
obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += ocelot_board.o ocelot_io.o \
ocelot_police.o \
ocelot_vcap.o \
ocelot_flower.o \
ocelot_ptp.o
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot.o
mscc_ocelot-y := \
ocelot_vsc7514.o \
ocelot_net.o
...@@ -4,42 +4,13 @@ ...@@ -4,42 +4,13 @@
* *
* Copyright (c) 2017 Microsemi Corporation * Copyright (c) 2017 Microsemi Corporation
*/ */
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/if_bridge.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>
#include <linux/skbuff.h>
#include <linux/iopoll.h>
#include <net/arp.h>
#include <net/netevent.h>
#include <net/rtnetlink.h>
#include <net/switchdev.h>
#include "ocelot.h" #include "ocelot.h"
#include "ocelot_ace.h" #include "ocelot_vcap.h"
#define TABLE_UPDATE_SLEEP_US 10 #define TABLE_UPDATE_SLEEP_US 10
#define TABLE_UPDATE_TIMEOUT_US 100000 #define TABLE_UPDATE_TIMEOUT_US 100000
/* 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 { struct ocelot_mact_entry {
u8 mac[ETH_ALEN]; u8 mac[ETH_ALEN];
u16 vid; u16 vid;
...@@ -84,10 +55,9 @@ static void ocelot_mact_select(struct ocelot *ocelot, ...@@ -84,10 +55,9 @@ static void ocelot_mact_select(struct ocelot *ocelot,
} }
static int ocelot_mact_learn(struct ocelot *ocelot, int port, int ocelot_mact_learn(struct ocelot *ocelot, int port,
const unsigned char mac[ETH_ALEN], const unsigned char mac[ETH_ALEN],
unsigned int vid, unsigned int vid, enum macaccess_entry_type type)
enum macaccess_entry_type type)
{ {
ocelot_mact_select(ocelot, mac, vid); ocelot_mact_select(ocelot, mac, vid);
...@@ -100,10 +70,10 @@ static int ocelot_mact_learn(struct ocelot *ocelot, int port, ...@@ -100,10 +70,10 @@ static int ocelot_mact_learn(struct ocelot *ocelot, int port,
return ocelot_mact_wait_for_completion(ocelot); return ocelot_mact_wait_for_completion(ocelot);
} }
EXPORT_SYMBOL(ocelot_mact_learn);
static int ocelot_mact_forget(struct ocelot *ocelot, int ocelot_mact_forget(struct ocelot *ocelot,
const unsigned char mac[ETH_ALEN], const unsigned char mac[ETH_ALEN], unsigned int vid)
unsigned int vid)
{ {
ocelot_mact_select(ocelot, mac, vid); ocelot_mact_select(ocelot, mac, vid);
...@@ -114,6 +84,7 @@ static int ocelot_mact_forget(struct ocelot *ocelot, ...@@ -114,6 +84,7 @@ static int ocelot_mact_forget(struct ocelot *ocelot,
return ocelot_mact_wait_for_completion(ocelot); return ocelot_mact_wait_for_completion(ocelot);
} }
EXPORT_SYMBOL(ocelot_mact_forget);
static void ocelot_mact_init(struct ocelot *ocelot) static void ocelot_mact_init(struct ocelot *ocelot)
{ {
...@@ -168,20 +139,6 @@ static int ocelot_vlant_set_mask(struct ocelot *ocelot, u16 vid, u32 mask) ...@@ -168,20 +139,6 @@ static int ocelot_vlant_set_mask(struct ocelot *ocelot, u16 vid, u32 mask)
return ocelot_vlant_wait_for_completion(ocelot); return ocelot_vlant_wait_for_completion(ocelot);
} }
static void ocelot_vlan_mode(struct ocelot *ocelot, int port,
netdev_features_t features)
{
u32 val;
/* Filtering */
val = ocelot_read(ocelot, ANA_VLANMASK);
if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
val |= BIT(port);
else
val &= ~BIT(port);
ocelot_write(ocelot, val, ANA_VLANMASK);
}
static int ocelot_port_set_native_vlan(struct ocelot *ocelot, int port, static int ocelot_port_set_native_vlan(struct ocelot *ocelot, int port,
u16 vid) u16 vid)
{ {
...@@ -295,26 +252,6 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid, ...@@ -295,26 +252,6 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
} }
EXPORT_SYMBOL(ocelot_vlan_add); EXPORT_SYMBOL(ocelot_vlan_add);
static int ocelot_vlan_vid_add(struct net_device *dev, u16 vid, bool pvid,
bool untagged)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;
int ret;
ret = ocelot_vlan_add(ocelot, port, vid, pvid, untagged);
if (ret)
return ret;
/* 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;
}
int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid) int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid)
{ {
struct ocelot_port *ocelot_port = ocelot->ports[port]; struct ocelot_port *ocelot_port = ocelot->ports[port];
...@@ -338,30 +275,6 @@ int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid) ...@@ -338,30 +275,6 @@ int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid)
} }
EXPORT_SYMBOL(ocelot_vlan_del); EXPORT_SYMBOL(ocelot_vlan_del);
static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
int ret;
/* 8021q removes VID 0 on module unload for all interfaces
* with VLAN filtering feature. We need to keep it to receive
* untagged traffic.
*/
if (vid == 0)
return 0;
ret = ocelot_vlan_del(ocelot, port, vid);
if (ret)
return ret;
/* Del the port MAC address to with the right VLAN information */
ocelot_mact_forget(ocelot, dev->dev_addr, vid);
return 0;
}
static void ocelot_vlan_init(struct ocelot *ocelot) static void ocelot_vlan_init(struct ocelot *ocelot)
{ {
u16 port, vid; u16 port, vid;
...@@ -492,15 +405,6 @@ void ocelot_adjust_link(struct ocelot *ocelot, int port, ...@@ -492,15 +405,6 @@ void ocelot_adjust_link(struct ocelot *ocelot, int port,
} }
EXPORT_SYMBOL(ocelot_adjust_link); EXPORT_SYMBOL(ocelot_adjust_link);
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);
}
void ocelot_port_enable(struct ocelot *ocelot, int port, void ocelot_port_enable(struct ocelot *ocelot, int port,
struct phy_device *phy) struct phy_device *phy)
{ {
...@@ -514,40 +418,6 @@ void ocelot_port_enable(struct ocelot *ocelot, int port, ...@@ -514,40 +418,6 @@ void ocelot_port_enable(struct ocelot *ocelot, int port,
} }
EXPORT_SYMBOL(ocelot_port_enable); EXPORT_SYMBOL(ocelot_port_enable);
static int ocelot_port_open(struct net_device *dev)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;
int err;
if (priv->serdes) {
err = phy_set_mode_ext(priv->serdes, PHY_MODE_ETHERNET,
ocelot_port->phy_mode);
if (err) {
netdev_err(dev, "Could not set mode of SerDes\n");
return err;
}
}
err = phy_connect_direct(dev, priv->phy, &ocelot_port_adjust_link,
ocelot_port->phy_mode);
if (err) {
netdev_err(dev, "Could not attach to PHY\n");
return err;
}
dev->phydev = priv->phy;
phy_attached_info(priv->phy);
phy_start(priv->phy);
ocelot_port_enable(ocelot, port, priv->phy);
return 0;
}
void ocelot_port_disable(struct ocelot *ocelot, int port) void ocelot_port_disable(struct ocelot *ocelot, int port)
{ {
struct ocelot_port *ocelot_port = ocelot->ports[port]; struct ocelot_port *ocelot_port = ocelot->ports[port];
...@@ -558,41 +428,6 @@ void ocelot_port_disable(struct ocelot *ocelot, int port) ...@@ -558,41 +428,6 @@ void ocelot_port_disable(struct ocelot *ocelot, int port)
} }
EXPORT_SYMBOL(ocelot_port_disable); EXPORT_SYMBOL(ocelot_port_disable);
static int ocelot_port_stop(struct net_device *dev)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
phy_disconnect(priv->phy);
dev->phydev = NULL;
ocelot_port_disable(ocelot, port);
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)
{
ifh[0] = IFH_INJ_BYPASS | ((0x1ff & info->rew_op) << 21);
ifh[1] = (0xf00 & info->port) >> 8;
ifh[2] = (0xff & info->port) << 24;
ifh[3] = (info->tag_type << 16) | info->vid;
return 0;
}
int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port, int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
struct sk_buff *skb) struct sk_buff *skb)
{ {
...@@ -611,77 +446,6 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port, ...@@ -611,77 +446,6 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
} }
EXPORT_SYMBOL(ocelot_port_add_txtstamp_skb); EXPORT_SYMBOL(ocelot_port_add_txtstamp_skb);
static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct skb_shared_info *shinfo = skb_shinfo(skb);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
u32 val, ifh[OCELOT_TAG_LEN / 4];
struct frame_info info = {};
u8 grp = 0; /* Send everything on CPU group 0 */
unsigned int i, count, last;
int port = priv->chip_port;
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);
info.port = BIT(port);
info.tag_type = IFH_TAG_TYPE_C;
info.vid = skb_vlan_tag_get(skb);
/* Check if timestamping is needed */
if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
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;
}
ocelot_gen_ifh(ifh, &info);
for (i = 0; i < OCELOT_TAG_LEN / 4; i++)
ocelot_write_rix(ocelot, (__force u32)cpu_to_be32(ifh[i]),
QS_INJ_WR, grp);
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;
if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
ocelot_port->ts_id++;
return NETDEV_TX_OK;
}
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
static void ocelot_get_hwtimestamp(struct ocelot *ocelot, static void ocelot_get_hwtimestamp(struct ocelot *ocelot,
struct timespec64 *ts) struct timespec64 *ts)
{ {
...@@ -767,113 +531,6 @@ void ocelot_get_txtstamp(struct ocelot *ocelot) ...@@ -767,113 +531,6 @@ void ocelot_get_txtstamp(struct ocelot *ocelot)
} }
EXPORT_SYMBOL(ocelot_get_txtstamp); EXPORT_SYMBOL(ocelot_get_txtstamp);
static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
return ocelot_mact_forget(ocelot, addr, ocelot_port->pvid);
}
static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
return ocelot_mact_learn(ocelot, PGID_CPU, addr, ocelot_port->pvid,
ENTRYTYPE_LOCKED);
}
static void ocelot_set_rx_mode(struct net_device *dev)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
u32 val;
int i;
/* 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);
__dev_mc_sync(dev, ocelot_mc_sync, ocelot_mc_unsync);
}
static int ocelot_port_get_phys_port_name(struct net_device *dev,
char *buf, size_t len)
{
struct ocelot_port_private *priv = netdev_priv(dev);
int port = priv->chip_port;
int ret;
ret = snprintf(buf, len, "p%d", port);
if (ret >= len)
return -EINVAL;
return 0;
}
static int ocelot_port_set_mac_address(struct net_device *dev, void *p)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
const struct sockaddr *addr = p;
/* Learn the new net device MAC address in the mac table. */
ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, ocelot_port->pvid,
ENTRYTYPE_LOCKED);
/* Then forget the previous one. */
ocelot_mact_forget(ocelot, dev->dev_addr, ocelot_port->pvid);
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)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
/* Configure the port to read the stats from */
ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port),
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);
}
int ocelot_fdb_add(struct ocelot *ocelot, int port, int ocelot_fdb_add(struct ocelot *ocelot, int port,
const unsigned char *addr, u16 vid) const unsigned char *addr, u16 vid)
{ {
...@@ -897,19 +554,6 @@ int ocelot_fdb_add(struct ocelot *ocelot, int port, ...@@ -897,19 +554,6 @@ int ocelot_fdb_add(struct ocelot *ocelot, int port,
} }
EXPORT_SYMBOL(ocelot_fdb_add); EXPORT_SYMBOL(ocelot_fdb_add);
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)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
return ocelot_fdb_add(ocelot, port, addr, vid);
}
int ocelot_fdb_del(struct ocelot *ocelot, int port, int ocelot_fdb_del(struct ocelot *ocelot, int port,
const unsigned char *addr, u16 vid) const unsigned char *addr, u16 vid)
{ {
...@@ -917,25 +561,7 @@ int ocelot_fdb_del(struct ocelot *ocelot, int port, ...@@ -917,25 +561,7 @@ int ocelot_fdb_del(struct ocelot *ocelot, int port,
} }
EXPORT_SYMBOL(ocelot_fdb_del); EXPORT_SYMBOL(ocelot_fdb_del);
static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
struct net_device *dev,
const unsigned char *addr, u16 vid)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
return ocelot_fdb_del(ocelot, port, addr, vid);
}
struct ocelot_dump_ctx {
struct net_device *dev;
struct sk_buff *skb;
struct netlink_callback *cb;
int idx;
};
static int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
bool is_static, void *data) bool is_static, void *data)
{ {
struct ocelot_dump_ctx *dump = data; struct ocelot_dump_ctx *dump = data;
...@@ -977,6 +603,7 @@ static int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid, ...@@ -977,6 +603,7 @@ static int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
nlmsg_cancel(dump->skb, nlh); nlmsg_cancel(dump->skb, nlh);
return -EMSGSIZE; return -EMSGSIZE;
} }
EXPORT_SYMBOL(ocelot_port_fdb_do_dump);
static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col, static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col,
struct ocelot_mact_entry *entry) struct ocelot_mact_entry *entry)
...@@ -1058,74 +685,6 @@ int ocelot_fdb_dump(struct ocelot *ocelot, int port, ...@@ -1058,74 +685,6 @@ int ocelot_fdb_dump(struct ocelot *ocelot, int port,
} }
EXPORT_SYMBOL(ocelot_fdb_dump); EXPORT_SYMBOL(ocelot_fdb_dump);
static int ocelot_port_fdb_dump(struct sk_buff *skb,
struct netlink_callback *cb,
struct net_device *dev,
struct net_device *filter_dev, int *idx)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
struct ocelot_dump_ctx dump = {
.dev = dev,
.skb = skb,
.cb = cb,
.idx = *idx,
};
int port = priv->chip_port;
int ret;
ret = ocelot_fdb_dump(ocelot, port, ocelot_port_fdb_do_dump, &dump);
*idx = dump.idx;
return ret;
}
static int ocelot_vlan_rx_add_vid(struct net_device *dev, __be16 proto,
u16 vid)
{
return ocelot_vlan_vid_add(dev, vid, false, false);
}
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;
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC) &&
priv->tc.offload_cnt) {
netdev_err(dev,
"Cannot disable HW TC offload while offloads active\n");
return -EBUSY;
}
if (changed & NETIF_F_HW_VLAN_CTAG_FILTER)
ocelot_vlan_mode(ocelot, port, features);
return 0;
}
static int ocelot_get_port_parent_id(struct net_device *dev,
struct netdev_phys_item_id *ppid)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
ppid->id_len = sizeof(ocelot->base_mac);
memcpy(&ppid->id, &ocelot->base_mac, ppid->id_len);
return 0;
}
int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr) int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr)
{ {
return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config, return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config,
...@@ -1198,46 +757,6 @@ int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr) ...@@ -1198,46 +757,6 @@ int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr)
} }
EXPORT_SYMBOL(ocelot_hwstamp_set); EXPORT_SYMBOL(ocelot_hwstamp_set);
static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
/* If the attached PHY device isn't capable of timestamping operations,
* use our own (when possible).
*/
if (!phy_has_hwtstamp(dev->phydev) && ocelot->ptp) {
switch (cmd) {
case SIOCSHWTSTAMP:
return ocelot_hwstamp_set(ocelot, port, ifr);
case SIOCGHWTSTAMP:
return ocelot_hwstamp_get(ocelot, port, ifr);
}
}
return phy_mii_ioctl(dev->phydev, ifr, cmd);
}
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,
.ndo_fdb_add = ocelot_port_fdb_add,
.ndo_fdb_del = ocelot_port_fdb_del,
.ndo_fdb_dump = ocelot_port_fdb_dump,
.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,
.ndo_get_port_parent_id = ocelot_get_port_parent_id,
.ndo_setup_tc = ocelot_setup_tc,
.ndo_do_ioctl = ocelot_ioctl,
};
void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data)
{ {
int i; int i;
...@@ -1251,16 +770,6 @@ void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) ...@@ -1251,16 +770,6 @@ void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data)
} }
EXPORT_SYMBOL(ocelot_get_strings); EXPORT_SYMBOL(ocelot_get_strings);
static void ocelot_port_get_strings(struct net_device *netdev, u32 sset,
u8 *data)
{
struct ocelot_port_private *priv = netdev_priv(netdev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
ocelot_get_strings(ocelot, port, sset, data);
}
static void ocelot_update_stats(struct ocelot *ocelot) static void ocelot_update_stats(struct ocelot *ocelot)
{ {
int i, j; int i, j;
...@@ -1314,17 +823,6 @@ void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data) ...@@ -1314,17 +823,6 @@ void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data)
} }
EXPORT_SYMBOL(ocelot_get_ethtool_stats); EXPORT_SYMBOL(ocelot_get_ethtool_stats);
static void ocelot_port_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats,
u64 *data)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
ocelot_get_ethtool_stats(ocelot, port, data);
}
int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset) int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
{ {
if (sset != ETH_SS_STATS) if (sset != ETH_SS_STATS)
...@@ -1334,15 +832,6 @@ int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset) ...@@ -1334,15 +832,6 @@ int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
} }
EXPORT_SYMBOL(ocelot_get_sset_count); EXPORT_SYMBOL(ocelot_get_sset_count);
static int ocelot_port_get_sset_count(struct net_device *dev, int sset)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
return ocelot_get_sset_count(ocelot, port, sset);
}
int ocelot_get_ts_info(struct ocelot *ocelot, int port, int ocelot_get_ts_info(struct ocelot *ocelot, int port,
struct ethtool_ts_info *info) struct ethtool_ts_info *info)
{ {
...@@ -1368,28 +857,6 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port, ...@@ -1368,28 +857,6 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,
} }
EXPORT_SYMBOL(ocelot_get_ts_info); EXPORT_SYMBOL(ocelot_get_ts_info);
static int ocelot_port_get_ts_info(struct net_device *dev,
struct ethtool_ts_info *info)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
if (!ocelot->ptp)
return ethtool_op_get_ts_info(dev, info);
return ocelot_get_ts_info(ocelot, port, info);
}
static const struct ethtool_ops ocelot_ethtool_ops = {
.get_strings = ocelot_port_get_strings,
.get_ethtool_stats = ocelot_port_get_ethtool_stats,
.get_sset_count = ocelot_port_get_sset_count,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = phy_ethtool_set_link_ksettings,
.get_ts_info = ocelot_port_get_ts_info,
};
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state) void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
{ {
u32 port_cfg; u32 port_cfg;
...@@ -1445,16 +912,6 @@ void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state) ...@@ -1445,16 +912,6 @@ void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
} }
EXPORT_SYMBOL(ocelot_bridge_stp_state_set); EXPORT_SYMBOL(ocelot_bridge_stp_state_set);
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);
}
void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs) void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs)
{ {
unsigned int age_period = ANA_AUTOAGE_AGE_PERIOD(msecs / 2000); unsigned int age_period = ANA_AUTOAGE_AGE_PERIOD(msecs / 2000);
...@@ -1469,95 +926,6 @@ void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs) ...@@ -1469,95 +926,6 @@ void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs)
} }
EXPORT_SYMBOL(ocelot_set_ageing_time); EXPORT_SYMBOL(ocelot_set_ageing_time);
static void ocelot_port_attr_ageing_set(struct ocelot *ocelot, int port,
unsigned long ageing_clock_t)
{
unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
u32 ageing_time = jiffies_to_msecs(ageing_jiffies);
ocelot_set_ageing_time(ocelot, ageing_time);
}
static void ocelot_port_attr_mc_set(struct ocelot *ocelot, int port, bool mc)
{
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;
if (mc)
val = cpu_fwd_mcast;
ocelot_rmw_gix(ocelot, val, cpu_fwd_mcast,
ANA_PORT_CPU_FWD_CFG, port);
}
static int ocelot_port_attr_set(struct net_device *dev,
const struct switchdev_attr *attr,
struct switchdev_trans *trans)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
int err = 0;
switch (attr->id) {
case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
ocelot_port_attr_stp_state_set(ocelot, port, trans,
attr->u.stp_state);
break;
case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
ocelot_port_attr_ageing_set(ocelot, port, attr->u.ageing_time);
break;
case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
ocelot_port_vlan_filtering(ocelot, port,
attr->u.vlan_filtering);
break;
case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
ocelot_port_attr_mc_set(ocelot, port, !attr->u.mc_disabled);
break;
default:
err = -EOPNOTSUPP;
break;
}
return err;
}
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;
}
static struct ocelot_multicast *ocelot_multicast_get(struct ocelot *ocelot, static struct ocelot_multicast *ocelot_multicast_get(struct ocelot *ocelot,
const unsigned char *addr, const unsigned char *addr,
u16 vid) u16 vid)
...@@ -1572,7 +940,7 @@ static struct ocelot_multicast *ocelot_multicast_get(struct ocelot *ocelot, ...@@ -1572,7 +940,7 @@ static struct ocelot_multicast *ocelot_multicast_get(struct ocelot *ocelot,
return NULL; return NULL;
} }
static int ocelot_port_obj_add_mdb(struct net_device *dev, int ocelot_port_obj_add_mdb(struct net_device *dev,
const struct switchdev_obj_port_mdb *mdb, const struct switchdev_obj_port_mdb *mdb,
struct switchdev_trans *trans) struct switchdev_trans *trans)
{ {
...@@ -1616,8 +984,9 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev, ...@@ -1616,8 +984,9 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev,
return ocelot_mact_learn(ocelot, 0, addr, vid, ENTRYTYPE_MACv4); return ocelot_mact_learn(ocelot, 0, addr, vid, ENTRYTYPE_MACv4);
} }
EXPORT_SYMBOL(ocelot_port_obj_add_mdb);
static int ocelot_port_obj_del_mdb(struct net_device *dev, int ocelot_port_obj_del_mdb(struct net_device *dev,
const struct switchdev_obj_port_mdb *mdb) const struct switchdev_obj_port_mdb *mdb)
{ {
struct ocelot_port_private *priv = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
...@@ -1653,50 +1022,7 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev, ...@@ -1653,50 +1022,7 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev,
return ocelot_mact_learn(ocelot, 0, addr, vid, ENTRYTYPE_MACv4); return ocelot_mact_learn(ocelot, 0, addr, vid, ENTRYTYPE_MACv4);
} }
EXPORT_SYMBOL(ocelot_port_obj_del_mdb);
static int ocelot_port_obj_add(struct net_device *dev,
const struct switchdev_obj *obj,
struct switchdev_trans *trans,
struct netlink_ext_ack *extack)
{
int ret = 0;
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
ret = ocelot_port_obj_add_vlan(dev,
SWITCHDEV_OBJ_PORT_VLAN(obj),
trans);
break;
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) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
ret = ocelot_port_vlan_del_vlan(dev,
SWITCHDEV_OBJ_PORT_VLAN(obj));
break;
case SWITCHDEV_OBJ_ID_PORT_MDB:
ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
break;
default:
return -EOPNOTSUPP;
}
return ret;
}
int ocelot_port_bridge_join(struct ocelot *ocelot, int port, int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
struct net_device *bridge) struct net_device *bridge)
...@@ -1788,7 +1114,7 @@ static void ocelot_setup_lag(struct ocelot *ocelot, int lag) ...@@ -1788,7 +1114,7 @@ static void ocelot_setup_lag(struct ocelot *ocelot, int lag)
} }
} }
static int ocelot_port_lag_join(struct ocelot *ocelot, int port, int ocelot_port_lag_join(struct ocelot *ocelot, int port,
struct net_device *bond) struct net_device *bond)
{ {
struct net_device *ndev; struct net_device *ndev;
...@@ -1826,8 +1152,9 @@ static int ocelot_port_lag_join(struct ocelot *ocelot, int port, ...@@ -1826,8 +1152,9 @@ static int ocelot_port_lag_join(struct ocelot *ocelot, int port,
return 0; return 0;
} }
EXPORT_SYMBOL(ocelot_port_lag_join);
static void ocelot_port_lag_leave(struct ocelot *ocelot, int port, void ocelot_port_lag_leave(struct ocelot *ocelot, int port,
struct net_device *bond) struct net_device *bond)
{ {
u32 port_cfg; u32 port_cfg;
...@@ -1856,151 +1183,7 @@ static void ocelot_port_lag_leave(struct ocelot *ocelot, int port, ...@@ -1856,151 +1183,7 @@ static void ocelot_port_lag_leave(struct ocelot *ocelot, int port,
ocelot_set_aggr_pgids(ocelot); ocelot_set_aggr_pgids(ocelot);
} }
EXPORT_SYMBOL(ocelot_port_lag_leave);
/* 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)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;
int err = 0;
switch (event) {
case NETDEV_CHANGEUPPER:
if (netif_is_bridge_master(info->upper_dev)) {
if (info->linking) {
err = ocelot_port_bridge_join(ocelot, port,
info->upper_dev);
} else {
err = ocelot_port_bridge_leave(ocelot, port,
info->upper_dev);
}
}
if (netif_is_lag_master(info->upper_dev)) {
if (info->linking)
err = ocelot_port_lag_join(ocelot, port,
info->upper_dev);
else
ocelot_port_lag_leave(ocelot, port,
info->upper_dev);
}
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);
int ret = 0;
if (!ocelot_netdevice_dev_check(dev))
return 0;
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;
if (lag_upper_info &&
lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
extack = netdev_notifier_info_to_extack(&info->info);
NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type");
ret = -EINVAL;
goto notify;
}
}
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);
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);
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);
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_blocking_nb __read_mostly = {
.notifier_call = ocelot_switchdev_blocking_event,
};
EXPORT_SYMBOL(ocelot_switchdev_blocking_nb);
/* Configure the maximum SDU (L2 payload) on RX to the value specified in @sdu. /* 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. * The length of VLAN tags is accounted for automatically via DEV_MAC_TAGS_CFG.
...@@ -2109,52 +1292,6 @@ void ocelot_init_port(struct ocelot *ocelot, int port) ...@@ -2109,52 +1292,6 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
} }
EXPORT_SYMBOL(ocelot_init_port); EXPORT_SYMBOL(ocelot_init_port);
int ocelot_probe_port(struct ocelot *ocelot, u8 port,
void __iomem *regs,
struct phy_device *phy)
{
struct ocelot_port_private *priv;
struct ocelot_port *ocelot_port;
struct net_device *dev;
int err;
dev = alloc_etherdev(sizeof(struct ocelot_port_private));
if (!dev)
return -ENOMEM;
SET_NETDEV_DEV(dev, ocelot->dev);
priv = netdev_priv(dev);
priv->dev = dev;
priv->phy = phy;
priv->chip_port = port;
ocelot_port = &priv->port;
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;
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;
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);
ocelot_init_port(ocelot, port);
err = register_netdev(dev);
if (err) {
dev_err(ocelot->dev, "register_netdev failed\n");
free_netdev(dev);
}
return err;
}
EXPORT_SYMBOL(ocelot_probe_port);
/* Configure and enable the CPU port module, which is a set of queues. /* 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 * 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 * to the Node Processor Interface (NPI). This is the mode through which
...@@ -2255,7 +1392,7 @@ int ocelot_init(struct ocelot *ocelot) ...@@ -2255,7 +1392,7 @@ int ocelot_init(struct ocelot *ocelot)
INIT_LIST_HEAD(&ocelot->multicast); INIT_LIST_HEAD(&ocelot->multicast);
ocelot_mact_init(ocelot); ocelot_mact_init(ocelot);
ocelot_vlan_init(ocelot); ocelot_vlan_init(ocelot);
ocelot_ace_init(ocelot); ocelot_vcap_init(ocelot);
for (port = 0; port < ocelot->num_phys_ports; port++) { for (port = 0; port < ocelot->num_phys_ports; port++) {
/* Clear all counters (5 groups) */ /* Clear all counters (5 groups) */
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
#include <soc/mscc/ocelot.h> #include <soc/mscc/ocelot.h>
#include "ocelot_rew.h" #include "ocelot_rew.h"
#include "ocelot_qs.h" #include "ocelot_qs.h"
#include "ocelot_tc.h"
#define OCELOT_BUFFER_CELL_SZ 60 #define OCELOT_BUFFER_CELL_SZ 60
...@@ -49,6 +48,13 @@ struct ocelot_multicast { ...@@ -49,6 +48,13 @@ struct ocelot_multicast {
u16 ports; u16 ports;
}; };
struct ocelot_port_tc {
bool block_shared;
unsigned long offload_cnt;
unsigned long police_id;
};
struct ocelot_port_private { struct ocelot_port_private {
struct ocelot_port port; struct ocelot_port port;
struct net_device *dev; struct net_device *dev;
...@@ -60,13 +66,49 @@ struct ocelot_port_private { ...@@ -60,13 +66,49 @@ struct ocelot_port_private {
struct ocelot_port_tc tc; struct ocelot_port_tc tc;
}; };
struct ocelot_dump_ctx {
struct net_device *dev;
struct sk_buff *skb;
struct netlink_callback *cb;
int idx;
};
/* 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,
};
int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
bool is_static, void *data);
int ocelot_mact_learn(struct ocelot *ocelot, int port,
const unsigned char mac[ETH_ALEN],
unsigned int vid, enum macaccess_entry_type type);
int ocelot_mact_forget(struct ocelot *ocelot,
const unsigned char mac[ETH_ALEN], unsigned int vid);
int ocelot_port_lag_join(struct ocelot *ocelot, int port,
struct net_device *bond);
void ocelot_port_lag_leave(struct ocelot *ocelot, int port,
struct net_device *bond);
int ocelot_port_obj_del_mdb(struct net_device *dev,
const struct switchdev_obj_port_mdb *mdb);
int ocelot_port_obj_add_mdb(struct net_device *dev,
const struct switchdev_obj_port_mdb *mdb,
struct switchdev_trans *trans);
u32 ocelot_port_readl(struct ocelot_port *port, u32 reg); u32 ocelot_port_readl(struct ocelot_port *port, u32 reg);
void ocelot_port_writel(struct ocelot_port *port, u32 val, u32 reg); void ocelot_port_writel(struct ocelot_port *port, u32 val, u32 reg);
#define ocelot_field_write(ocelot, reg, val) regmap_field_write((ocelot)->regfields[(reg)], (val)) #define ocelot_field_write(ocelot, reg, val) regmap_field_write((ocelot)->regfields[(reg)], (val))
#define ocelot_field_read(ocelot, reg, val) regmap_field_read((ocelot)->regfields[(reg)], (val)) #define ocelot_field_read(ocelot, reg, val) regmap_field_read((ocelot)->regfields[(reg)], (val))
int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops);
int ocelot_probe_port(struct ocelot *ocelot, u8 port, int ocelot_probe_port(struct ocelot *ocelot, u8 port,
void __iomem *regs, void __iomem *regs,
struct phy_device *phy); struct phy_device *phy);
......
...@@ -6,10 +6,10 @@ ...@@ -6,10 +6,10 @@
#include <net/pkt_cls.h> #include <net/pkt_cls.h>
#include <net/tc_act/tc_gact.h> #include <net/tc_act/tc_gact.h>
#include "ocelot_ace.h" #include "ocelot_vcap.h"
static int ocelot_flower_parse_action(struct flow_cls_offload *f, static int ocelot_flower_parse_action(struct flow_cls_offload *f,
struct ocelot_ace_rule *ace) struct ocelot_vcap_filter *filter)
{ {
const struct flow_action_entry *a; const struct flow_action_entry *a;
s64 burst; s64 burst;
...@@ -26,17 +26,17 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f, ...@@ -26,17 +26,17 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f,
flow_action_for_each(i, a, &f->rule->action) { flow_action_for_each(i, a, &f->rule->action) {
switch (a->id) { switch (a->id) {
case FLOW_ACTION_DROP: case FLOW_ACTION_DROP:
ace->action = OCELOT_ACL_ACTION_DROP; filter->action = OCELOT_VCAP_ACTION_DROP;
break; break;
case FLOW_ACTION_TRAP: case FLOW_ACTION_TRAP:
ace->action = OCELOT_ACL_ACTION_TRAP; filter->action = OCELOT_VCAP_ACTION_TRAP;
break; break;
case FLOW_ACTION_POLICE: case FLOW_ACTION_POLICE:
ace->action = OCELOT_ACL_ACTION_POLICE; filter->action = OCELOT_VCAP_ACTION_POLICE;
rate = a->police.rate_bytes_ps; rate = a->police.rate_bytes_ps;
ace->pol.rate = div_u64(rate, 1000) * 8; filter->pol.rate = div_u64(rate, 1000) * 8;
burst = rate * PSCHED_NS2TICKS(a->police.burst); burst = rate * PSCHED_NS2TICKS(a->police.burst);
ace->pol.burst = div_u64(burst, PSCHED_TICKS_PER_SEC); filter->pol.burst = div_u64(burst, PSCHED_TICKS_PER_SEC);
break; break;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -47,7 +47,7 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f, ...@@ -47,7 +47,7 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f,
} }
static int ocelot_flower_parse(struct flow_cls_offload *f, static int ocelot_flower_parse(struct flow_cls_offload *f,
struct ocelot_ace_rule *ace) struct ocelot_vcap_filter *filter)
{ {
struct flow_rule *rule = flow_cls_offload_flow_rule(f); struct flow_rule *rule = flow_cls_offload_flow_rule(f);
struct flow_dissector *dissector = rule->match.dissector; struct flow_dissector *dissector = rule->match.dissector;
...@@ -88,14 +88,14 @@ static int ocelot_flower_parse(struct flow_cls_offload *f, ...@@ -88,14 +88,14 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
return -EOPNOTSUPP; return -EOPNOTSUPP;
flow_rule_match_eth_addrs(rule, &match); flow_rule_match_eth_addrs(rule, &match);
ace->type = OCELOT_ACE_TYPE_ETYPE; filter->key_type = OCELOT_VCAP_KEY_ETYPE;
ether_addr_copy(ace->frame.etype.dmac.value, ether_addr_copy(filter->key.etype.dmac.value,
match.key->dst); match.key->dst);
ether_addr_copy(ace->frame.etype.smac.value, ether_addr_copy(filter->key.etype.smac.value,
match.key->src); match.key->src);
ether_addr_copy(ace->frame.etype.dmac.mask, ether_addr_copy(filter->key.etype.dmac.mask,
match.mask->dst); match.mask->dst);
ether_addr_copy(ace->frame.etype.smac.mask, ether_addr_copy(filter->key.etype.smac.mask,
match.mask->src); match.mask->src);
goto finished_key_parsing; goto finished_key_parsing;
} }
...@@ -105,18 +105,18 @@ static int ocelot_flower_parse(struct flow_cls_offload *f, ...@@ -105,18 +105,18 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
flow_rule_match_basic(rule, &match); flow_rule_match_basic(rule, &match);
if (ntohs(match.key->n_proto) == ETH_P_IP) { if (ntohs(match.key->n_proto) == ETH_P_IP) {
ace->type = OCELOT_ACE_TYPE_IPV4; filter->key_type = OCELOT_VCAP_KEY_IPV4;
ace->frame.ipv4.proto.value[0] = filter->key.ipv4.proto.value[0] =
match.key->ip_proto; match.key->ip_proto;
ace->frame.ipv4.proto.mask[0] = filter->key.ipv4.proto.mask[0] =
match.mask->ip_proto; match.mask->ip_proto;
match_protocol = false; match_protocol = false;
} }
if (ntohs(match.key->n_proto) == ETH_P_IPV6) { if (ntohs(match.key->n_proto) == ETH_P_IPV6) {
ace->type = OCELOT_ACE_TYPE_IPV6; filter->key_type = OCELOT_VCAP_KEY_IPV6;
ace->frame.ipv6.proto.value[0] = filter->key.ipv6.proto.value[0] =
match.key->ip_proto; match.key->ip_proto;
ace->frame.ipv6.proto.mask[0] = filter->key.ipv6.proto.mask[0] =
match.mask->ip_proto; match.mask->ip_proto;
match_protocol = false; match_protocol = false;
} }
...@@ -128,16 +128,16 @@ static int ocelot_flower_parse(struct flow_cls_offload *f, ...@@ -128,16 +128,16 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
u8 *tmp; u8 *tmp;
flow_rule_match_ipv4_addrs(rule, &match); flow_rule_match_ipv4_addrs(rule, &match);
tmp = &ace->frame.ipv4.sip.value.addr[0]; tmp = &filter->key.ipv4.sip.value.addr[0];
memcpy(tmp, &match.key->src, 4); memcpy(tmp, &match.key->src, 4);
tmp = &ace->frame.ipv4.sip.mask.addr[0]; tmp = &filter->key.ipv4.sip.mask.addr[0];
memcpy(tmp, &match.mask->src, 4); memcpy(tmp, &match.mask->src, 4);
tmp = &ace->frame.ipv4.dip.value.addr[0]; tmp = &filter->key.ipv4.dip.value.addr[0];
memcpy(tmp, &match.key->dst, 4); memcpy(tmp, &match.key->dst, 4);
tmp = &ace->frame.ipv4.dip.mask.addr[0]; tmp = &filter->key.ipv4.dip.mask.addr[0];
memcpy(tmp, &match.mask->dst, 4); memcpy(tmp, &match.mask->dst, 4);
match_protocol = false; match_protocol = false;
} }
...@@ -151,10 +151,10 @@ static int ocelot_flower_parse(struct flow_cls_offload *f, ...@@ -151,10 +151,10 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
struct flow_match_ports match; struct flow_match_ports match;
flow_rule_match_ports(rule, &match); flow_rule_match_ports(rule, &match);
ace->frame.ipv4.sport.value = ntohs(match.key->src); filter->key.ipv4.sport.value = ntohs(match.key->src);
ace->frame.ipv4.sport.mask = ntohs(match.mask->src); filter->key.ipv4.sport.mask = ntohs(match.mask->src);
ace->frame.ipv4.dport.value = ntohs(match.key->dst); filter->key.ipv4.dport.value = ntohs(match.key->dst);
ace->frame.ipv4.dport.mask = ntohs(match.mask->dst); filter->key.ipv4.dport.mask = ntohs(match.mask->dst);
match_protocol = false; match_protocol = false;
} }
...@@ -162,11 +162,11 @@ static int ocelot_flower_parse(struct flow_cls_offload *f, ...@@ -162,11 +162,11 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
struct flow_match_vlan match; struct flow_match_vlan match;
flow_rule_match_vlan(rule, &match); flow_rule_match_vlan(rule, &match);
ace->type = OCELOT_ACE_TYPE_ANY; filter->key_type = OCELOT_VCAP_KEY_ANY;
ace->vlan.vid.value = match.key->vlan_id; filter->vlan.vid.value = match.key->vlan_id;
ace->vlan.vid.mask = match.mask->vlan_id; filter->vlan.vid.mask = match.mask->vlan_id;
ace->vlan.pcp.value[0] = match.key->vlan_priority; filter->vlan.pcp.value[0] = match.key->vlan_priority;
ace->vlan.pcp.mask[0] = match.mask->vlan_priority; filter->vlan.pcp.mask[0] = match.mask->vlan_priority;
match_protocol = false; match_protocol = false;
} }
...@@ -175,99 +175,77 @@ static int ocelot_flower_parse(struct flow_cls_offload *f, ...@@ -175,99 +175,77 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
/* TODO: support SNAP, LLC etc */ /* TODO: support SNAP, LLC etc */
if (proto < ETH_P_802_3_MIN) if (proto < ETH_P_802_3_MIN)
return -EOPNOTSUPP; return -EOPNOTSUPP;
ace->type = OCELOT_ACE_TYPE_ETYPE; filter->key_type = OCELOT_VCAP_KEY_ETYPE;
*(u16 *)ace->frame.etype.etype.value = htons(proto); *(__be16 *)filter->key.etype.etype.value = htons(proto);
*(u16 *)ace->frame.etype.etype.mask = 0xffff; *(__be16 *)filter->key.etype.etype.mask = htons(0xffff);
} }
/* else, a rule of type OCELOT_ACE_TYPE_ANY is implicitly added */ /* else, a filter of type OCELOT_VCAP_KEY_ANY is implicitly added */
ace->prio = f->common.prio; filter->prio = f->common.prio;
ace->id = f->cookie; filter->id = f->cookie;
return ocelot_flower_parse_action(f, ace); return ocelot_flower_parse_action(f, filter);
} }
static static struct ocelot_vcap_filter
struct ocelot_ace_rule *ocelot_ace_rule_create(struct ocelot *ocelot, int port, *ocelot_vcap_filter_create(struct ocelot *ocelot, int port,
struct flow_cls_offload *f) struct flow_cls_offload *f)
{ {
struct ocelot_ace_rule *ace; struct ocelot_vcap_filter *filter;
ace = kzalloc(sizeof(*ace), GFP_KERNEL); filter = kzalloc(sizeof(*filter), GFP_KERNEL);
if (!ace) if (!filter)
return NULL; return NULL;
ace->ingress_port_mask = BIT(port); filter->ingress_port_mask = BIT(port);
return ace; return filter;
} }
int ocelot_cls_flower_replace(struct ocelot *ocelot, int port, int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
struct flow_cls_offload *f, bool ingress) struct flow_cls_offload *f, bool ingress)
{ {
struct ocelot_ace_rule *ace; struct ocelot_vcap_filter *filter;
int ret; int ret;
ace = ocelot_ace_rule_create(ocelot, port, f); filter = ocelot_vcap_filter_create(ocelot, port, f);
if (!ace) if (!filter)
return -ENOMEM; return -ENOMEM;
ret = ocelot_flower_parse(f, ace); ret = ocelot_flower_parse(f, filter);
if (ret) { if (ret) {
kfree(ace); kfree(filter);
return ret; return ret;
} }
return ocelot_ace_rule_offload_add(ocelot, ace, f->common.extack); return ocelot_vcap_filter_add(ocelot, filter, f->common.extack);
} }
EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace); EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);
int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port, int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
struct flow_cls_offload *f, bool ingress) struct flow_cls_offload *f, bool ingress)
{ {
struct ocelot_ace_rule ace; struct ocelot_vcap_filter filter;
ace.prio = f->common.prio; filter.prio = f->common.prio;
ace.id = f->cookie; filter.id = f->cookie;
return ocelot_ace_rule_offload_del(ocelot, &ace); return ocelot_vcap_filter_del(ocelot, &filter);
} }
EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy); EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);
int ocelot_cls_flower_stats(struct ocelot *ocelot, int port, int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
struct flow_cls_offload *f, bool ingress) struct flow_cls_offload *f, bool ingress)
{ {
struct ocelot_ace_rule ace; struct ocelot_vcap_filter filter;
int ret; int ret;
ace.prio = f->common.prio; filter.prio = f->common.prio;
ace.id = f->cookie; filter.id = f->cookie;
ret = ocelot_ace_rule_stats_update(ocelot, &ace); ret = ocelot_vcap_filter_stats_update(ocelot, &filter);
if (ret) if (ret)
return ret; return ret;
flow_stats_update(&f->stats, 0x0, ace.stats.pkts, 0, 0x0, flow_stats_update(&f->stats, 0x0, filter.stats.pkts, 0, 0x0,
FLOW_ACTION_HW_STATS_IMMEDIATE); FLOW_ACTION_HW_STATS_IMMEDIATE);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(ocelot_cls_flower_stats); EXPORT_SYMBOL_GPL(ocelot_cls_flower_stats);
int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv,
struct flow_cls_offload *f,
bool ingress)
{
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
if (!ingress)
return -EOPNOTSUPP;
switch (f->command) {
case FLOW_CLS_REPLACE:
return ocelot_cls_flower_replace(ocelot, port, f, ingress);
case FLOW_CLS_DESTROY:
return ocelot_cls_flower_destroy(ocelot, port, f, ingress);
case FLOW_CLS_STATS:
return ocelot_cls_flower_stats(ocelot, port, f, ingress);
default:
return -EOPNOTSUPP;
}
}
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/* Microsemi Ocelot Switch driver
*
* Copyright (c) 2017, 2019 Microsemi Corporation
*/
#include <linux/if_bridge.h>
#include "ocelot.h"
#include "ocelot_vcap.h"
int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv,
struct flow_cls_offload *f,
bool ingress)
{
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
if (!ingress)
return -EOPNOTSUPP;
switch (f->command) {
case FLOW_CLS_REPLACE:
return ocelot_cls_flower_replace(ocelot, port, f, ingress);
case FLOW_CLS_DESTROY:
return ocelot_cls_flower_destroy(ocelot, port, f, ingress);
case FLOW_CLS_STATS:
return ocelot_cls_flower_stats(ocelot, port, f, ingress);
default:
return -EOPNOTSUPP;
}
}
static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv,
struct tc_cls_matchall_offload *f,
bool ingress)
{
struct netlink_ext_ack *extack = f->common.extack;
struct ocelot *ocelot = priv->port.ocelot;
struct ocelot_policer pol = { 0 };
struct flow_action_entry *action;
int port = priv->chip_port;
int err;
if (!ingress) {
NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
return -EOPNOTSUPP;
}
switch (f->command) {
case TC_CLSMATCHALL_REPLACE:
if (!flow_offload_has_one_action(&f->rule->action)) {
NL_SET_ERR_MSG_MOD(extack,
"Only one action is supported");
return -EOPNOTSUPP;
}
if (priv->tc.block_shared) {
NL_SET_ERR_MSG_MOD(extack,
"Rate limit is not supported on shared blocks");
return -EOPNOTSUPP;
}
action = &f->rule->action.entries[0];
if (action->id != FLOW_ACTION_POLICE) {
NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
return -EOPNOTSUPP;
}
if (priv->tc.police_id && priv->tc.police_id != f->cookie) {
NL_SET_ERR_MSG_MOD(extack,
"Only one policer per port is supported");
return -EEXIST;
}
pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
pol.burst = (u32)div_u64(action->police.rate_bytes_ps *
PSCHED_NS2TICKS(action->police.burst),
PSCHED_TICKS_PER_SEC);
err = ocelot_port_policer_add(ocelot, port, &pol);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Could not add policer");
return err;
}
priv->tc.police_id = f->cookie;
priv->tc.offload_cnt++;
return 0;
case TC_CLSMATCHALL_DESTROY:
if (priv->tc.police_id != f->cookie)
return -ENOENT;
err = ocelot_port_policer_del(ocelot, port);
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Could not delete policer");
return err;
}
priv->tc.police_id = 0;
priv->tc.offload_cnt--;
return 0;
case TC_CLSMATCHALL_STATS:
default:
return -EOPNOTSUPP;
}
}
static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
void *type_data,
void *cb_priv, bool ingress)
{
struct ocelot_port_private *priv = cb_priv;
if (!tc_cls_can_offload_and_chain0(priv->dev, type_data))
return -EOPNOTSUPP;
switch (type) {
case TC_SETUP_CLSMATCHALL:
return ocelot_setup_tc_cls_matchall(priv, type_data, ingress);
case TC_SETUP_CLSFLOWER:
return ocelot_setup_tc_cls_flower(priv, type_data, ingress);
default:
return -EOPNOTSUPP;
}
}
static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,
void *type_data,
void *cb_priv)
{
return ocelot_setup_tc_block_cb(type, type_data,
cb_priv, true);
}
static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
void *type_data,
void *cb_priv)
{
return ocelot_setup_tc_block_cb(type, type_data,
cb_priv, false);
}
static LIST_HEAD(ocelot_block_cb_list);
static int ocelot_setup_tc_block(struct ocelot_port_private *priv,
struct flow_block_offload *f)
{
struct flow_block_cb *block_cb;
flow_setup_cb_t *cb;
if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) {
cb = ocelot_setup_tc_block_cb_ig;
priv->tc.block_shared = f->block_shared;
} else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
cb = ocelot_setup_tc_block_cb_eg;
} else {
return -EOPNOTSUPP;
}
f->driver_block_list = &ocelot_block_cb_list;
switch (f->command) {
case FLOW_BLOCK_BIND:
if (flow_block_cb_is_busy(cb, priv, &ocelot_block_cb_list))
return -EBUSY;
block_cb = flow_block_cb_alloc(cb, priv, priv, NULL);
if (IS_ERR(block_cb))
return PTR_ERR(block_cb);
flow_block_cb_add(block_cb, f);
list_add_tail(&block_cb->driver_list, f->driver_block_list);
return 0;
case FLOW_BLOCK_UNBIND:
block_cb = flow_block_cb_lookup(f->block, cb, priv);
if (!block_cb)
return -ENOENT;
flow_block_cb_remove(block_cb, f);
list_del(&block_cb->driver_list);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
{
struct ocelot_port_private *priv = netdev_priv(dev);
switch (type) {
case TC_SETUP_BLOCK:
return ocelot_setup_tc_block(priv, type_data);
default:
return -EOPNOTSUPP;
}
return 0;
}
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);
}
static int ocelot_vlan_vid_add(struct net_device *dev, u16 vid, bool pvid,
bool untagged)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;
int ret;
ret = ocelot_vlan_add(ocelot, port, vid, pvid, untagged);
if (ret)
return ret;
/* 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;
}
static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
int ret;
/* 8021q removes VID 0 on module unload for all interfaces
* with VLAN filtering feature. We need to keep it to receive
* untagged traffic.
*/
if (vid == 0)
return 0;
ret = ocelot_vlan_del(ocelot, port, vid);
if (ret)
return ret;
/* Del the port MAC address to with the right VLAN information */
ocelot_mact_forget(ocelot, dev->dev_addr, vid);
return 0;
}
static int ocelot_port_open(struct net_device *dev)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;
int err;
if (priv->serdes) {
err = phy_set_mode_ext(priv->serdes, PHY_MODE_ETHERNET,
ocelot_port->phy_mode);
if (err) {
netdev_err(dev, "Could not set mode of SerDes\n");
return err;
}
}
err = phy_connect_direct(dev, priv->phy, &ocelot_port_adjust_link,
ocelot_port->phy_mode);
if (err) {
netdev_err(dev, "Could not attach to PHY\n");
return err;
}
dev->phydev = priv->phy;
phy_attached_info(priv->phy);
phy_start(priv->phy);
ocelot_port_enable(ocelot, port, priv->phy);
return 0;
}
static int ocelot_port_stop(struct net_device *dev)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
phy_disconnect(priv->phy);
dev->phydev = NULL;
ocelot_port_disable(ocelot, port);
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)
{
ifh[0] = IFH_INJ_BYPASS | ((0x1ff & info->rew_op) << 21);
ifh[1] = (0xf00 & info->port) >> 8;
ifh[2] = (0xff & info->port) << 24;
ifh[3] = (info->tag_type << 16) | info->vid;
return 0;
}
static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct skb_shared_info *shinfo = skb_shinfo(skb);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
u32 val, ifh[OCELOT_TAG_LEN / 4];
struct frame_info info = {};
u8 grp = 0; /* Send everything on CPU group 0 */
unsigned int i, count, last;
int port = priv->chip_port;
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);
info.port = BIT(port);
info.tag_type = IFH_TAG_TYPE_C;
info.vid = skb_vlan_tag_get(skb);
/* Check if timestamping is needed */
if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
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;
}
ocelot_gen_ifh(ifh, &info);
for (i = 0; i < OCELOT_TAG_LEN / 4; i++)
ocelot_write_rix(ocelot, (__force u32)cpu_to_be32(ifh[i]),
QS_INJ_WR, grp);
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;
if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
ocelot_port->ts_id++;
return NETDEV_TX_OK;
}
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
return ocelot_mact_forget(ocelot, addr, ocelot_port->pvid);
}
static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
return ocelot_mact_learn(ocelot, PGID_CPU, addr, ocelot_port->pvid,
ENTRYTYPE_LOCKED);
}
static void ocelot_set_rx_mode(struct net_device *dev)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
u32 val;
int i;
/* 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);
__dev_mc_sync(dev, ocelot_mc_sync, ocelot_mc_unsync);
}
static int ocelot_port_get_phys_port_name(struct net_device *dev,
char *buf, size_t len)
{
struct ocelot_port_private *priv = netdev_priv(dev);
int port = priv->chip_port;
int ret;
ret = snprintf(buf, len, "p%d", port);
if (ret >= len)
return -EINVAL;
return 0;
}
static int ocelot_port_set_mac_address(struct net_device *dev, void *p)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
const struct sockaddr *addr = p;
/* Learn the new net device MAC address in the mac table. */
ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, ocelot_port->pvid,
ENTRYTYPE_LOCKED);
/* Then forget the previous one. */
ocelot_mact_forget(ocelot, dev->dev_addr, ocelot_port->pvid);
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)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
/* Configure the port to read the stats from */
ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port),
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);
}
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)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
return ocelot_fdb_add(ocelot, port, addr, vid);
}
static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
return ocelot_fdb_del(ocelot, port, addr, vid);
}
static int ocelot_port_fdb_dump(struct sk_buff *skb,
struct netlink_callback *cb,
struct net_device *dev,
struct net_device *filter_dev, int *idx)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
struct ocelot_dump_ctx dump = {
.dev = dev,
.skb = skb,
.cb = cb,
.idx = *idx,
};
int port = priv->chip_port;
int ret;
ret = ocelot_fdb_dump(ocelot, port, ocelot_port_fdb_do_dump, &dump);
*idx = dump.idx;
return ret;
}
static int ocelot_vlan_rx_add_vid(struct net_device *dev, __be16 proto,
u16 vid)
{
return ocelot_vlan_vid_add(dev, vid, false, false);
}
static int ocelot_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,
u16 vid)
{
return ocelot_vlan_vid_del(dev, vid);
}
static void ocelot_vlan_mode(struct ocelot *ocelot, int port,
netdev_features_t features)
{
u32 val;
/* Filtering */
val = ocelot_read(ocelot, ANA_VLANMASK);
if (features & NETIF_F_HW_VLAN_CTAG_FILTER)
val |= BIT(port);
else
val &= ~BIT(port);
ocelot_write(ocelot, val, ANA_VLANMASK);
}
static int ocelot_set_features(struct net_device *dev,
netdev_features_t features)
{
netdev_features_t changed = dev->features ^ features;
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC) &&
priv->tc.offload_cnt) {
netdev_err(dev,
"Cannot disable HW TC offload while offloads active\n");
return -EBUSY;
}
if (changed & NETIF_F_HW_VLAN_CTAG_FILTER)
ocelot_vlan_mode(ocelot, port, features);
return 0;
}
static int ocelot_get_port_parent_id(struct net_device *dev,
struct netdev_phys_item_id *ppid)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
ppid->id_len = sizeof(ocelot->base_mac);
memcpy(&ppid->id, &ocelot->base_mac, ppid->id_len);
return 0;
}
static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
/* If the attached PHY device isn't capable of timestamping operations,
* use our own (when possible).
*/
if (!phy_has_hwtstamp(dev->phydev) && ocelot->ptp) {
switch (cmd) {
case SIOCSHWTSTAMP:
return ocelot_hwstamp_set(ocelot, port, ifr);
case SIOCGHWTSTAMP:
return ocelot_hwstamp_get(ocelot, port, ifr);
}
}
return phy_mii_ioctl(dev->phydev, ifr, cmd);
}
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,
.ndo_fdb_add = ocelot_port_fdb_add,
.ndo_fdb_del = ocelot_port_fdb_del,
.ndo_fdb_dump = ocelot_port_fdb_dump,
.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,
.ndo_get_port_parent_id = ocelot_get_port_parent_id,
.ndo_setup_tc = ocelot_setup_tc,
.ndo_do_ioctl = ocelot_ioctl,
};
static void ocelot_port_get_strings(struct net_device *netdev, u32 sset,
u8 *data)
{
struct ocelot_port_private *priv = netdev_priv(netdev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
ocelot_get_strings(ocelot, port, sset, data);
}
static void ocelot_port_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats,
u64 *data)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
ocelot_get_ethtool_stats(ocelot, port, data);
}
static int ocelot_port_get_sset_count(struct net_device *dev, int sset)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
return ocelot_get_sset_count(ocelot, port, sset);
}
static int ocelot_port_get_ts_info(struct net_device *dev,
struct ethtool_ts_info *info)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
if (!ocelot->ptp)
return ethtool_op_get_ts_info(dev, info);
return ocelot_get_ts_info(ocelot, port, info);
}
static const struct ethtool_ops ocelot_ethtool_ops = {
.get_strings = ocelot_port_get_strings,
.get_ethtool_stats = ocelot_port_get_ethtool_stats,
.get_sset_count = ocelot_port_get_sset_count,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = phy_ethtool_set_link_ksettings,
.get_ts_info = ocelot_port_get_ts_info,
};
static void ocelot_port_attr_stp_state_set(struct ocelot *ocelot, int port,
struct switchdev_trans *trans,
u8 state)
{
if (switchdev_trans_ph_prepare(trans))
return;
ocelot_bridge_stp_state_set(ocelot, port, state);
}
static void ocelot_port_attr_ageing_set(struct ocelot *ocelot, int port,
unsigned long ageing_clock_t)
{
unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
u32 ageing_time = jiffies_to_msecs(ageing_jiffies);
ocelot_set_ageing_time(ocelot, ageing_time);
}
static void ocelot_port_attr_mc_set(struct ocelot *ocelot, int port, bool mc)
{
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;
if (mc)
val = cpu_fwd_mcast;
ocelot_rmw_gix(ocelot, val, cpu_fwd_mcast,
ANA_PORT_CPU_FWD_CFG, port);
}
static int ocelot_port_attr_set(struct net_device *dev,
const struct switchdev_attr *attr,
struct switchdev_trans *trans)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
int err = 0;
switch (attr->id) {
case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
ocelot_port_attr_stp_state_set(ocelot, port, trans,
attr->u.stp_state);
break;
case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
ocelot_port_attr_ageing_set(ocelot, port, attr->u.ageing_time);
break;
case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
ocelot_port_vlan_filtering(ocelot, port,
attr->u.vlan_filtering);
break;
case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
ocelot_port_attr_mc_set(ocelot, port, !attr->u.mc_disabled);
break;
default:
err = -EOPNOTSUPP;
break;
}
return err;
}
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;
}
static int ocelot_port_obj_add(struct net_device *dev,
const struct switchdev_obj *obj,
struct switchdev_trans *trans,
struct netlink_ext_ack *extack)
{
int ret = 0;
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
ret = ocelot_port_obj_add_vlan(dev,
SWITCHDEV_OBJ_PORT_VLAN(obj),
trans);
break;
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) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
ret = ocelot_port_vlan_del_vlan(dev,
SWITCHDEV_OBJ_PORT_VLAN(obj));
break;
case SWITCHDEV_OBJ_ID_PORT_MDB:
ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
break;
default:
return -EOPNOTSUPP;
}
return ret;
}
/* 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)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->chip_port;
int err = 0;
switch (event) {
case NETDEV_CHANGEUPPER:
if (netif_is_bridge_master(info->upper_dev)) {
if (info->linking) {
err = ocelot_port_bridge_join(ocelot, port,
info->upper_dev);
} else {
err = ocelot_port_bridge_leave(ocelot, port,
info->upper_dev);
}
}
if (netif_is_lag_master(info->upper_dev)) {
if (info->linking)
err = ocelot_port_lag_join(ocelot, port,
info->upper_dev);
else
ocelot_port_lag_leave(ocelot, port,
info->upper_dev);
}
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);
int ret = 0;
if (!ocelot_netdevice_dev_check(dev))
return 0;
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;
if (lag_upper_info &&
lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
extack = netdev_notifier_info_to_extack(&info->info);
NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type");
ret = -EINVAL;
goto notify;
}
}
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);
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);
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);
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_blocking_nb __read_mostly = {
.notifier_call = ocelot_switchdev_blocking_event,
};
EXPORT_SYMBOL(ocelot_switchdev_blocking_nb);
int ocelot_probe_port(struct ocelot *ocelot, u8 port,
void __iomem *regs,
struct phy_device *phy)
{
struct ocelot_port_private *priv;
struct ocelot_port *ocelot_port;
struct net_device *dev;
int err;
dev = alloc_etherdev(sizeof(struct ocelot_port_private));
if (!dev)
return -ENOMEM;
SET_NETDEV_DEV(dev, ocelot->dev);
priv = netdev_priv(dev);
priv->dev = dev;
priv->phy = phy;
priv->chip_port = port;
ocelot_port = &priv->port;
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;
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;
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);
ocelot_init_port(ocelot, port);
err = register_netdev(dev);
if (err) {
dev_err(ocelot->dev, "register_netdev failed\n");
free_netdev(dev);
}
return err;
}
EXPORT_SYMBOL(ocelot_probe_port);
...@@ -7,16 +7,6 @@ ...@@ -7,16 +7,6 @@
#include <soc/mscc/ocelot.h> #include <soc/mscc/ocelot.h>
#include "ocelot_police.h" #include "ocelot_police.h"
enum mscc_qos_rate_mode {
MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */
MSCC_QOS_RATE_MODE_LINE, /* Measure line rate in kbps incl. IPG */
MSCC_QOS_RATE_MODE_DATA, /* Measures data rate in kbps excl. IPG */
MSCC_QOS_RATE_MODE_FRAME, /* Measures frame rate in fps */
__MSCC_QOS_RATE_MODE_END,
NUM_MSCC_QOS_RATE_MODE = __MSCC_QOS_RATE_MODE_END,
MSCC_QOS_RATE_MODE_MAX = __MSCC_QOS_RATE_MODE_END - 1,
};
/* Types for ANA:POL[0-192]:POL_MODE_CFG.FRM_MODE */ /* Types for ANA:POL[0-192]:POL_MODE_CFG.FRM_MODE */
#define POL_MODE_LINERATE 0 /* Incl IPG. Unit: 33 1/3 kbps, 4096 bytes */ #define POL_MODE_LINERATE 0 /* Incl IPG. Unit: 33 1/3 kbps, 4096 bytes */
#define POL_MODE_DATARATE 1 /* Excl IPG. Unit: 33 1/3 kbps, 4096 bytes */ #define POL_MODE_DATARATE 1 /* Excl IPG. Unit: 33 1/3 kbps, 4096 bytes */
...@@ -30,18 +20,7 @@ enum mscc_qos_rate_mode { ...@@ -30,18 +20,7 @@ enum mscc_qos_rate_mode {
/* Default policer order */ /* Default policer order */
#define POL_ORDER 0x1d3 /* Ocelot policer order: Serial (QoS -> Port -> VCAP) */ #define POL_ORDER 0x1d3 /* Ocelot policer order: Serial (QoS -> Port -> VCAP) */
struct qos_policer_conf { int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix,
enum mscc_qos_rate_mode mode;
bool dlb; /* Enable DLB (dual leaky bucket mode */
bool cf; /* Coupling flag (ignored in SLB mode) */
u32 cir; /* CIR in kbps/fps (ignored in SLB mode) */
u32 cbs; /* CBS in bytes/frames (ignored in SLB mode) */
u32 pir; /* PIR in kbps/fps */
u32 pbs; /* PBS in bytes/frames */
u8 ipg; /* Size of IPG when MSCC_QOS_RATE_MODE_LINE is chosen */
};
static int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix,
struct qos_policer_conf *conf) struct qos_policer_conf *conf)
{ {
u32 cf = 0, cir_ena = 0, frm_mode = POL_MODE_LINERATE; u32 cf = 0, cir_ena = 0, frm_mode = POL_MODE_LINERATE;
...@@ -228,27 +207,3 @@ int ocelot_port_policer_del(struct ocelot *ocelot, int port) ...@@ -228,27 +207,3 @@ int ocelot_port_policer_del(struct ocelot *ocelot, int port)
return 0; return 0;
} }
EXPORT_SYMBOL(ocelot_port_policer_del); EXPORT_SYMBOL(ocelot_port_policer_del);
int ocelot_ace_policer_add(struct ocelot *ocelot, u32 pol_ix,
struct ocelot_policer *pol)
{
struct qos_policer_conf pp = { 0 };
if (!pol)
return -EINVAL;
pp.mode = MSCC_QOS_RATE_MODE_DATA;
pp.pir = pol->rate;
pp.pbs = pol->burst;
return qos_policer_conf_set(ocelot, 0, pol_ix, &pp);
}
int ocelot_ace_policer_del(struct ocelot *ocelot, u32 pol_ix)
{
struct qos_policer_conf pp = { 0 };
pp.mode = MSCC_QOS_RATE_MODE_DISABLED;
return qos_policer_conf_set(ocelot, 0, pol_ix, &pp);
}
...@@ -9,9 +9,28 @@ ...@@ -9,9 +9,28 @@
#include "ocelot.h" #include "ocelot.h"
int ocelot_ace_policer_add(struct ocelot *ocelot, u32 pol_ix, enum mscc_qos_rate_mode {
struct ocelot_policer *pol); MSCC_QOS_RATE_MODE_DISABLED, /* Policer/shaper disabled */
MSCC_QOS_RATE_MODE_LINE, /* Measure line rate in kbps incl. IPG */
MSCC_QOS_RATE_MODE_DATA, /* Measures data rate in kbps excl. IPG */
MSCC_QOS_RATE_MODE_FRAME, /* Measures frame rate in fps */
__MSCC_QOS_RATE_MODE_END,
NUM_MSCC_QOS_RATE_MODE = __MSCC_QOS_RATE_MODE_END,
MSCC_QOS_RATE_MODE_MAX = __MSCC_QOS_RATE_MODE_END - 1,
};
int ocelot_ace_policer_del(struct ocelot *ocelot, u32 pol_ix); struct qos_policer_conf {
enum mscc_qos_rate_mode mode;
bool dlb; /* Enable DLB (dual leaky bucket mode */
bool cf; /* Coupling flag (ignored in SLB mode) */
u32 cir; /* CIR in kbps/fps (ignored in SLB mode) */
u32 cbs; /* CBS in bytes/frames (ignored in SLB mode) */
u32 pir; /* PIR in kbps/fps */
u32 pbs; /* PBS in bytes/frames */
u8 ipg; /* Size of IPG when MSCC_QOS_RATE_MODE_LINE is chosen */
};
int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix,
struct qos_policer_conf *conf);
#endif /* _MSCC_OCELOT_POLICE_H_ */ #endif /* _MSCC_OCELOT_POLICE_H_ */
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Microsemi Ocelot Switch driver
*
* Copyright (c) 2017 Microsemi Corporation
*/
#include "ocelot.h"
#include <soc/mscc/ocelot_hsio.h>
static const u32 ocelot_ana_regmap[] = {
REG(ANA_ADVLEARN, 0x009000),
REG(ANA_VLANMASK, 0x009004),
REG(ANA_PORT_B_DOMAIN, 0x009008),
REG(ANA_ANAGEFIL, 0x00900c),
REG(ANA_ANEVENTS, 0x009010),
REG(ANA_STORMLIMIT_BURST, 0x009014),
REG(ANA_STORMLIMIT_CFG, 0x009018),
REG(ANA_ISOLATED_PORTS, 0x009028),
REG(ANA_COMMUNITY_PORTS, 0x00902c),
REG(ANA_AUTOAGE, 0x009030),
REG(ANA_MACTOPTIONS, 0x009034),
REG(ANA_LEARNDISC, 0x009038),
REG(ANA_AGENCTRL, 0x00903c),
REG(ANA_MIRRORPORTS, 0x009040),
REG(ANA_EMIRRORPORTS, 0x009044),
REG(ANA_FLOODING, 0x009048),
REG(ANA_FLOODING_IPMC, 0x00904c),
REG(ANA_SFLOW_CFG, 0x009050),
REG(ANA_PORT_MODE, 0x009080),
REG(ANA_PGID_PGID, 0x008c00),
REG(ANA_TABLES_ANMOVED, 0x008b30),
REG(ANA_TABLES_MACHDATA, 0x008b34),
REG(ANA_TABLES_MACLDATA, 0x008b38),
REG(ANA_TABLES_MACACCESS, 0x008b3c),
REG(ANA_TABLES_MACTINDX, 0x008b40),
REG(ANA_TABLES_VLANACCESS, 0x008b44),
REG(ANA_TABLES_VLANTIDX, 0x008b48),
REG(ANA_TABLES_ISDXACCESS, 0x008b4c),
REG(ANA_TABLES_ISDXTIDX, 0x008b50),
REG(ANA_TABLES_ENTRYLIM, 0x008b00),
REG(ANA_TABLES_PTP_ID_HIGH, 0x008b54),
REG(ANA_TABLES_PTP_ID_LOW, 0x008b58),
REG(ANA_MSTI_STATE, 0x008e00),
REG(ANA_PORT_VLAN_CFG, 0x007000),
REG(ANA_PORT_DROP_CFG, 0x007004),
REG(ANA_PORT_QOS_CFG, 0x007008),
REG(ANA_PORT_VCAP_CFG, 0x00700c),
REG(ANA_PORT_VCAP_S1_KEY_CFG, 0x007010),
REG(ANA_PORT_VCAP_S2_CFG, 0x00701c),
REG(ANA_PORT_PCP_DEI_MAP, 0x007020),
REG(ANA_PORT_CPU_FWD_CFG, 0x007060),
REG(ANA_PORT_CPU_FWD_BPDU_CFG, 0x007064),
REG(ANA_PORT_CPU_FWD_GARP_CFG, 0x007068),
REG(ANA_PORT_CPU_FWD_CCM_CFG, 0x00706c),
REG(ANA_PORT_PORT_CFG, 0x007070),
REG(ANA_PORT_POL_CFG, 0x007074),
REG(ANA_PORT_PTP_CFG, 0x007078),
REG(ANA_PORT_PTP_DLY1_CFG, 0x00707c),
REG(ANA_OAM_UPM_LM_CNT, 0x007c00),
REG(ANA_PORT_PTP_DLY2_CFG, 0x007080),
REG(ANA_PFC_PFC_CFG, 0x008800),
REG(ANA_PFC_PFC_TIMER, 0x008804),
REG(ANA_IPT_OAM_MEP_CFG, 0x008000),
REG(ANA_IPT_IPT, 0x008004),
REG(ANA_PPT_PPT, 0x008ac0),
REG(ANA_FID_MAP_FID_MAP, 0x000000),
REG(ANA_AGGR_CFG, 0x0090b4),
REG(ANA_CPUQ_CFG, 0x0090b8),
REG(ANA_CPUQ_CFG2, 0x0090bc),
REG(ANA_CPUQ_8021_CFG, 0x0090c0),
REG(ANA_DSCP_CFG, 0x009100),
REG(ANA_DSCP_REWR_CFG, 0x009200),
REG(ANA_VCAP_RNG_TYPE_CFG, 0x009240),
REG(ANA_VCAP_RNG_VAL_CFG, 0x009260),
REG(ANA_VRAP_CFG, 0x009280),
REG(ANA_VRAP_HDR_DATA, 0x009284),
REG(ANA_VRAP_HDR_MASK, 0x009288),
REG(ANA_DISCARD_CFG, 0x00928c),
REG(ANA_FID_CFG, 0x009290),
REG(ANA_POL_PIR_CFG, 0x004000),
REG(ANA_POL_CIR_CFG, 0x004004),
REG(ANA_POL_MODE_CFG, 0x004008),
REG(ANA_POL_PIR_STATE, 0x00400c),
REG(ANA_POL_CIR_STATE, 0x004010),
REG(ANA_POL_STATE, 0x004014),
REG(ANA_POL_FLOWC, 0x008b80),
REG(ANA_POL_HYST, 0x008bec),
REG(ANA_POL_MISC_CFG, 0x008bf0),
};
static const u32 ocelot_qs_regmap[] = {
REG(QS_XTR_GRP_CFG, 0x000000),
REG(QS_XTR_RD, 0x000008),
REG(QS_XTR_FRM_PRUNING, 0x000010),
REG(QS_XTR_FLUSH, 0x000018),
REG(QS_XTR_DATA_PRESENT, 0x00001c),
REG(QS_XTR_CFG, 0x000020),
REG(QS_INJ_GRP_CFG, 0x000024),
REG(QS_INJ_WR, 0x00002c),
REG(QS_INJ_CTRL, 0x000034),
REG(QS_INJ_STATUS, 0x00003c),
REG(QS_INJ_ERR, 0x000040),
REG(QS_INH_DBG, 0x000048),
};
static const u32 ocelot_qsys_regmap[] = {
REG(QSYS_PORT_MODE, 0x011200),
REG(QSYS_SWITCH_PORT_MODE, 0x011234),
REG(QSYS_STAT_CNT_CFG, 0x011264),
REG(QSYS_EEE_CFG, 0x011268),
REG(QSYS_EEE_THRES, 0x011294),
REG(QSYS_IGR_NO_SHARING, 0x011298),
REG(QSYS_EGR_NO_SHARING, 0x01129c),
REG(QSYS_SW_STATUS, 0x0112a0),
REG(QSYS_EXT_CPU_CFG, 0x0112d0),
REG(QSYS_PAD_CFG, 0x0112d4),
REG(QSYS_CPU_GROUP_MAP, 0x0112d8),
REG(QSYS_QMAP, 0x0112dc),
REG(QSYS_ISDX_SGRP, 0x011400),
REG(QSYS_TIMED_FRAME_ENTRY, 0x014000),
REG(QSYS_TFRM_MISC, 0x011310),
REG(QSYS_TFRM_PORT_DLY, 0x011314),
REG(QSYS_TFRM_TIMER_CFG_1, 0x011318),
REG(QSYS_TFRM_TIMER_CFG_2, 0x01131c),
REG(QSYS_TFRM_TIMER_CFG_3, 0x011320),
REG(QSYS_TFRM_TIMER_CFG_4, 0x011324),
REG(QSYS_TFRM_TIMER_CFG_5, 0x011328),
REG(QSYS_TFRM_TIMER_CFG_6, 0x01132c),
REG(QSYS_TFRM_TIMER_CFG_7, 0x011330),
REG(QSYS_TFRM_TIMER_CFG_8, 0x011334),
REG(QSYS_RED_PROFILE, 0x011338),
REG(QSYS_RES_QOS_MODE, 0x011378),
REG(QSYS_RES_CFG, 0x012000),
REG(QSYS_RES_STAT, 0x012004),
REG(QSYS_EGR_DROP_MODE, 0x01137c),
REG(QSYS_EQ_CTRL, 0x011380),
REG(QSYS_EVENTS_CORE, 0x011384),
REG(QSYS_CIR_CFG, 0x000000),
REG(QSYS_EIR_CFG, 0x000004),
REG(QSYS_SE_CFG, 0x000008),
REG(QSYS_SE_DWRR_CFG, 0x00000c),
REG(QSYS_SE_CONNECT, 0x00003c),
REG(QSYS_SE_DLB_SENSE, 0x000040),
REG(QSYS_CIR_STATE, 0x000044),
REG(QSYS_EIR_STATE, 0x000048),
REG(QSYS_SE_STATE, 0x00004c),
REG(QSYS_HSCH_MISC_CFG, 0x011388),
};
static const u32 ocelot_rew_regmap[] = {
REG(REW_PORT_VLAN_CFG, 0x000000),
REG(REW_TAG_CFG, 0x000004),
REG(REW_PORT_CFG, 0x000008),
REG(REW_DSCP_CFG, 0x00000c),
REG(REW_PCP_DEI_QOS_MAP_CFG, 0x000010),
REG(REW_PTP_CFG, 0x000050),
REG(REW_PTP_DLY1_CFG, 0x000054),
REG(REW_DSCP_REMAP_DP1_CFG, 0x000690),
REG(REW_DSCP_REMAP_CFG, 0x000790),
REG(REW_STAT_CFG, 0x000890),
REG(REW_PPT, 0x000680),
};
static const u32 ocelot_sys_regmap[] = {
REG(SYS_COUNT_RX_OCTETS, 0x000000),
REG(SYS_COUNT_RX_UNICAST, 0x000004),
REG(SYS_COUNT_RX_MULTICAST, 0x000008),
REG(SYS_COUNT_RX_BROADCAST, 0x00000c),
REG(SYS_COUNT_RX_SHORTS, 0x000010),
REG(SYS_COUNT_RX_FRAGMENTS, 0x000014),
REG(SYS_COUNT_RX_JABBERS, 0x000018),
REG(SYS_COUNT_RX_CRC_ALIGN_ERRS, 0x00001c),
REG(SYS_COUNT_RX_SYM_ERRS, 0x000020),
REG(SYS_COUNT_RX_64, 0x000024),
REG(SYS_COUNT_RX_65_127, 0x000028),
REG(SYS_COUNT_RX_128_255, 0x00002c),
REG(SYS_COUNT_RX_256_1023, 0x000030),
REG(SYS_COUNT_RX_1024_1526, 0x000034),
REG(SYS_COUNT_RX_1527_MAX, 0x000038),
REG(SYS_COUNT_RX_PAUSE, 0x00003c),
REG(SYS_COUNT_RX_CONTROL, 0x000040),
REG(SYS_COUNT_RX_LONGS, 0x000044),
REG(SYS_COUNT_RX_CLASSIFIED_DROPS, 0x000048),
REG(SYS_COUNT_TX_OCTETS, 0x000100),
REG(SYS_COUNT_TX_UNICAST, 0x000104),
REG(SYS_COUNT_TX_MULTICAST, 0x000108),
REG(SYS_COUNT_TX_BROADCAST, 0x00010c),
REG(SYS_COUNT_TX_COLLISION, 0x000110),
REG(SYS_COUNT_TX_DROPS, 0x000114),
REG(SYS_COUNT_TX_PAUSE, 0x000118),
REG(SYS_COUNT_TX_64, 0x00011c),
REG(SYS_COUNT_TX_65_127, 0x000120),
REG(SYS_COUNT_TX_128_511, 0x000124),
REG(SYS_COUNT_TX_512_1023, 0x000128),
REG(SYS_COUNT_TX_1024_1526, 0x00012c),
REG(SYS_COUNT_TX_1527_MAX, 0x000130),
REG(SYS_COUNT_TX_AGING, 0x000170),
REG(SYS_RESET_CFG, 0x000508),
REG(SYS_CMID, 0x00050c),
REG(SYS_VLAN_ETYPE_CFG, 0x000510),
REG(SYS_PORT_MODE, 0x000514),
REG(SYS_FRONT_PORT_MODE, 0x000548),
REG(SYS_FRM_AGING, 0x000574),
REG(SYS_STAT_CFG, 0x000578),
REG(SYS_SW_STATUS, 0x00057c),
REG(SYS_MISC_CFG, 0x0005ac),
REG(SYS_REW_MAC_HIGH_CFG, 0x0005b0),
REG(SYS_REW_MAC_LOW_CFG, 0x0005dc),
REG(SYS_CM_ADDR, 0x000500),
REG(SYS_CM_DATA, 0x000504),
REG(SYS_PAUSE_CFG, 0x000608),
REG(SYS_PAUSE_TOT_CFG, 0x000638),
REG(SYS_ATOP, 0x00063c),
REG(SYS_ATOP_TOT_CFG, 0x00066c),
REG(SYS_MAC_FC_CFG, 0x000670),
REG(SYS_MMGT, 0x00069c),
REG(SYS_MMGT_FAST, 0x0006a0),
REG(SYS_EVENTS_DIF, 0x0006a4),
REG(SYS_EVENTS_CORE, 0x0006b4),
REG(SYS_CNT, 0x000000),
REG(SYS_PTP_STATUS, 0x0006b8),
REG(SYS_PTP_TXSTAMP, 0x0006bc),
REG(SYS_PTP_NXT, 0x0006c0),
REG(SYS_PTP_CFG, 0x0006c4),
};
static const u32 ocelot_s2_regmap[] = {
REG(S2_CORE_UPDATE_CTRL, 0x000000),
REG(S2_CORE_MV_CFG, 0x000004),
REG(S2_CACHE_ENTRY_DAT, 0x000008),
REG(S2_CACHE_MASK_DAT, 0x000108),
REG(S2_CACHE_ACTION_DAT, 0x000208),
REG(S2_CACHE_CNT_DAT, 0x000308),
REG(S2_CACHE_TG_DAT, 0x000388),
};
static const u32 ocelot_ptp_regmap[] = {
REG(PTP_PIN_CFG, 0x000000),
REG(PTP_PIN_TOD_SEC_MSB, 0x000004),
REG(PTP_PIN_TOD_SEC_LSB, 0x000008),
REG(PTP_PIN_TOD_NSEC, 0x00000c),
REG(PTP_PIN_WF_HIGH_PERIOD, 0x000014),
REG(PTP_PIN_WF_LOW_PERIOD, 0x000018),
REG(PTP_CFG_MISC, 0x0000a0),
REG(PTP_CLK_CFG_ADJ_CFG, 0x0000a4),
REG(PTP_CLK_CFG_ADJ_FREQ, 0x0000a8),
};
static const u32 *ocelot_regmap[] = {
[ANA] = ocelot_ana_regmap,
[QS] = ocelot_qs_regmap,
[QSYS] = ocelot_qsys_regmap,
[REW] = ocelot_rew_regmap,
[SYS] = ocelot_sys_regmap,
[S2] = ocelot_s2_regmap,
[PTP] = ocelot_ptp_regmap,
};
static const struct reg_field ocelot_regfields[] = {
[ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 11, 11),
[ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 10),
[ANA_ANEVENTS_MSTI_DROP] = REG_FIELD(ANA_ANEVENTS, 27, 27),
[ANA_ANEVENTS_ACLKILL] = REG_FIELD(ANA_ANEVENTS, 26, 26),
[ANA_ANEVENTS_ACLUSED] = REG_FIELD(ANA_ANEVENTS, 25, 25),
[ANA_ANEVENTS_AUTOAGE] = REG_FIELD(ANA_ANEVENTS, 24, 24),
[ANA_ANEVENTS_VS2TTL1] = REG_FIELD(ANA_ANEVENTS, 23, 23),
[ANA_ANEVENTS_STORM_DROP] = REG_FIELD(ANA_ANEVENTS, 22, 22),
[ANA_ANEVENTS_LEARN_DROP] = REG_FIELD(ANA_ANEVENTS, 21, 21),
[ANA_ANEVENTS_AGED_ENTRY] = REG_FIELD(ANA_ANEVENTS, 20, 20),
[ANA_ANEVENTS_CPU_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 19, 19),
[ANA_ANEVENTS_AUTO_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 18, 18),
[ANA_ANEVENTS_LEARN_REMOVE] = REG_FIELD(ANA_ANEVENTS, 17, 17),
[ANA_ANEVENTS_AUTO_LEARNED] = REG_FIELD(ANA_ANEVENTS, 16, 16),
[ANA_ANEVENTS_AUTO_MOVED] = REG_FIELD(ANA_ANEVENTS, 15, 15),
[ANA_ANEVENTS_DROPPED] = REG_FIELD(ANA_ANEVENTS, 14, 14),
[ANA_ANEVENTS_CLASSIFIED_DROP] = REG_FIELD(ANA_ANEVENTS, 13, 13),
[ANA_ANEVENTS_CLASSIFIED_COPY] = REG_FIELD(ANA_ANEVENTS, 12, 12),
[ANA_ANEVENTS_VLAN_DISCARD] = REG_FIELD(ANA_ANEVENTS, 11, 11),
[ANA_ANEVENTS_FWD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 10, 10),
[ANA_ANEVENTS_MULTICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 9, 9),
[ANA_ANEVENTS_UNICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 8, 8),
[ANA_ANEVENTS_DEST_KNOWN] = REG_FIELD(ANA_ANEVENTS, 7, 7),
[ANA_ANEVENTS_BUCKET3_MATCH] = REG_FIELD(ANA_ANEVENTS, 6, 6),
[ANA_ANEVENTS_BUCKET2_MATCH] = REG_FIELD(ANA_ANEVENTS, 5, 5),
[ANA_ANEVENTS_BUCKET1_MATCH] = REG_FIELD(ANA_ANEVENTS, 4, 4),
[ANA_ANEVENTS_BUCKET0_MATCH] = REG_FIELD(ANA_ANEVENTS, 3, 3),
[ANA_ANEVENTS_CPU_OPERATION] = REG_FIELD(ANA_ANEVENTS, 2, 2),
[ANA_ANEVENTS_DMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 1, 1),
[ANA_ANEVENTS_SMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 0, 0),
[ANA_TABLES_MACACCESS_B_DOM] = REG_FIELD(ANA_TABLES_MACACCESS, 18, 18),
[ANA_TABLES_MACTINDX_BUCKET] = REG_FIELD(ANA_TABLES_MACTINDX, 10, 11),
[ANA_TABLES_MACTINDX_M_INDEX] = REG_FIELD(ANA_TABLES_MACTINDX, 0, 9),
[QSYS_TIMED_FRAME_ENTRY_TFRM_VLD] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 20, 20),
[QSYS_TIMED_FRAME_ENTRY_TFRM_FP] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 8, 19),
[QSYS_TIMED_FRAME_ENTRY_TFRM_PORTNO] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 4, 7),
[QSYS_TIMED_FRAME_ENTRY_TFRM_TM_SEL] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 1, 3),
[QSYS_TIMED_FRAME_ENTRY_TFRM_TM_T] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 0, 0),
[SYS_RESET_CFG_CORE_ENA] = REG_FIELD(SYS_RESET_CFG, 2, 2),
[SYS_RESET_CFG_MEM_ENA] = REG_FIELD(SYS_RESET_CFG, 1, 1),
[SYS_RESET_CFG_MEM_INIT] = REG_FIELD(SYS_RESET_CFG, 0, 0),
};
static const struct ocelot_stat_layout ocelot_stats_layout[] = {
{ .name = "rx_octets", .offset = 0x00, },
{ .name = "rx_unicast", .offset = 0x01, },
{ .name = "rx_multicast", .offset = 0x02, },
{ .name = "rx_broadcast", .offset = 0x03, },
{ .name = "rx_shorts", .offset = 0x04, },
{ .name = "rx_fragments", .offset = 0x05, },
{ .name = "rx_jabbers", .offset = 0x06, },
{ .name = "rx_crc_align_errs", .offset = 0x07, },
{ .name = "rx_sym_errs", .offset = 0x08, },
{ .name = "rx_frames_below_65_octets", .offset = 0x09, },
{ .name = "rx_frames_65_to_127_octets", .offset = 0x0A, },
{ .name = "rx_frames_128_to_255_octets", .offset = 0x0B, },
{ .name = "rx_frames_256_to_511_octets", .offset = 0x0C, },
{ .name = "rx_frames_512_to_1023_octets", .offset = 0x0D, },
{ .name = "rx_frames_1024_to_1526_octets", .offset = 0x0E, },
{ .name = "rx_frames_over_1526_octets", .offset = 0x0F, },
{ .name = "rx_pause", .offset = 0x10, },
{ .name = "rx_control", .offset = 0x11, },
{ .name = "rx_longs", .offset = 0x12, },
{ .name = "rx_classified_drops", .offset = 0x13, },
{ .name = "rx_red_prio_0", .offset = 0x14, },
{ .name = "rx_red_prio_1", .offset = 0x15, },
{ .name = "rx_red_prio_2", .offset = 0x16, },
{ .name = "rx_red_prio_3", .offset = 0x17, },
{ .name = "rx_red_prio_4", .offset = 0x18, },
{ .name = "rx_red_prio_5", .offset = 0x19, },
{ .name = "rx_red_prio_6", .offset = 0x1A, },
{ .name = "rx_red_prio_7", .offset = 0x1B, },
{ .name = "rx_yellow_prio_0", .offset = 0x1C, },
{ .name = "rx_yellow_prio_1", .offset = 0x1D, },
{ .name = "rx_yellow_prio_2", .offset = 0x1E, },
{ .name = "rx_yellow_prio_3", .offset = 0x1F, },
{ .name = "rx_yellow_prio_4", .offset = 0x20, },
{ .name = "rx_yellow_prio_5", .offset = 0x21, },
{ .name = "rx_yellow_prio_6", .offset = 0x22, },
{ .name = "rx_yellow_prio_7", .offset = 0x23, },
{ .name = "rx_green_prio_0", .offset = 0x24, },
{ .name = "rx_green_prio_1", .offset = 0x25, },
{ .name = "rx_green_prio_2", .offset = 0x26, },
{ .name = "rx_green_prio_3", .offset = 0x27, },
{ .name = "rx_green_prio_4", .offset = 0x28, },
{ .name = "rx_green_prio_5", .offset = 0x29, },
{ .name = "rx_green_prio_6", .offset = 0x2A, },
{ .name = "rx_green_prio_7", .offset = 0x2B, },
{ .name = "tx_octets", .offset = 0x40, },
{ .name = "tx_unicast", .offset = 0x41, },
{ .name = "tx_multicast", .offset = 0x42, },
{ .name = "tx_broadcast", .offset = 0x43, },
{ .name = "tx_collision", .offset = 0x44, },
{ .name = "tx_drops", .offset = 0x45, },
{ .name = "tx_pause", .offset = 0x46, },
{ .name = "tx_frames_below_65_octets", .offset = 0x47, },
{ .name = "tx_frames_65_to_127_octets", .offset = 0x48, },
{ .name = "tx_frames_128_255_octets", .offset = 0x49, },
{ .name = "tx_frames_256_511_octets", .offset = 0x4A, },
{ .name = "tx_frames_512_1023_octets", .offset = 0x4B, },
{ .name = "tx_frames_1024_1526_octets", .offset = 0x4C, },
{ .name = "tx_frames_over_1526_octets", .offset = 0x4D, },
{ .name = "tx_yellow_prio_0", .offset = 0x4E, },
{ .name = "tx_yellow_prio_1", .offset = 0x4F, },
{ .name = "tx_yellow_prio_2", .offset = 0x50, },
{ .name = "tx_yellow_prio_3", .offset = 0x51, },
{ .name = "tx_yellow_prio_4", .offset = 0x52, },
{ .name = "tx_yellow_prio_5", .offset = 0x53, },
{ .name = "tx_yellow_prio_6", .offset = 0x54, },
{ .name = "tx_yellow_prio_7", .offset = 0x55, },
{ .name = "tx_green_prio_0", .offset = 0x56, },
{ .name = "tx_green_prio_1", .offset = 0x57, },
{ .name = "tx_green_prio_2", .offset = 0x58, },
{ .name = "tx_green_prio_3", .offset = 0x59, },
{ .name = "tx_green_prio_4", .offset = 0x5A, },
{ .name = "tx_green_prio_5", .offset = 0x5B, },
{ .name = "tx_green_prio_6", .offset = 0x5C, },
{ .name = "tx_green_prio_7", .offset = 0x5D, },
{ .name = "tx_aged", .offset = 0x5E, },
{ .name = "drop_local", .offset = 0x80, },
{ .name = "drop_tail", .offset = 0x81, },
{ .name = "drop_yellow_prio_0", .offset = 0x82, },
{ .name = "drop_yellow_prio_1", .offset = 0x83, },
{ .name = "drop_yellow_prio_2", .offset = 0x84, },
{ .name = "drop_yellow_prio_3", .offset = 0x85, },
{ .name = "drop_yellow_prio_4", .offset = 0x86, },
{ .name = "drop_yellow_prio_5", .offset = 0x87, },
{ .name = "drop_yellow_prio_6", .offset = 0x88, },
{ .name = "drop_yellow_prio_7", .offset = 0x89, },
{ .name = "drop_green_prio_0", .offset = 0x8A, },
{ .name = "drop_green_prio_1", .offset = 0x8B, },
{ .name = "drop_green_prio_2", .offset = 0x8C, },
{ .name = "drop_green_prio_3", .offset = 0x8D, },
{ .name = "drop_green_prio_4", .offset = 0x8E, },
{ .name = "drop_green_prio_5", .offset = 0x8F, },
{ .name = "drop_green_prio_6", .offset = 0x90, },
{ .name = "drop_green_prio_7", .offset = 0x91, },
};
static void ocelot_pll5_init(struct ocelot *ocelot)
{
/* Configure PLL5. This will need a proper CCF driver
* The values are coming from the VTSS API for Ocelot
*/
regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG4,
HSIO_PLL5G_CFG4_IB_CTRL(0x7600) |
HSIO_PLL5G_CFG4_IB_BIAS_CTRL(0x8));
regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG0,
HSIO_PLL5G_CFG0_CORE_CLK_DIV(0x11) |
HSIO_PLL5G_CFG0_CPU_CLK_DIV(2) |
HSIO_PLL5G_CFG0_ENA_BIAS |
HSIO_PLL5G_CFG0_ENA_VCO_BUF |
HSIO_PLL5G_CFG0_ENA_CP1 |
HSIO_PLL5G_CFG0_SELCPI(2) |
HSIO_PLL5G_CFG0_LOOP_BW_RES(0xe) |
HSIO_PLL5G_CFG0_SELBGV820(4) |
HSIO_PLL5G_CFG0_DIV4 |
HSIO_PLL5G_CFG0_ENA_CLKTREE |
HSIO_PLL5G_CFG0_ENA_LANE);
regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG2,
HSIO_PLL5G_CFG2_EN_RESET_FRQ_DET |
HSIO_PLL5G_CFG2_EN_RESET_OVERRUN |
HSIO_PLL5G_CFG2_GAIN_TEST(0x8) |
HSIO_PLL5G_CFG2_ENA_AMPCTRL |
HSIO_PLL5G_CFG2_PWD_AMPCTRL_N |
HSIO_PLL5G_CFG2_AMPC_SEL(0x10));
}
int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops)
{
int ret;
ocelot->map = ocelot_regmap;
ocelot->stats_layout = ocelot_stats_layout;
ocelot->num_stats = ARRAY_SIZE(ocelot_stats_layout);
ocelot->shared_queue_sz = 224 * 1024;
ocelot->num_mact_rows = 1024;
ocelot->ops = ops;
ret = ocelot_regfields_init(ocelot, ocelot_regfields);
if (ret)
return ret;
ocelot_pll5_init(ocelot);
eth_random_addr(ocelot->base_mac);
ocelot->base_mac[5] &= 0xf0;
return 0;
}
EXPORT_SYMBOL(ocelot_chip_init);
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/* Microsemi Ocelot Switch TC driver
*
* Copyright (c) 2019 Microsemi Corporation
*/
#include <soc/mscc/ocelot.h>
#include "ocelot_tc.h"
#include "ocelot_ace.h"
#include <net/pkt_cls.h>
static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv,
struct tc_cls_matchall_offload *f,
bool ingress)
{
struct netlink_ext_ack *extack = f->common.extack;
struct ocelot *ocelot = priv->port.ocelot;
struct ocelot_policer pol = { 0 };
struct flow_action_entry *action;
int port = priv->chip_port;
int err;
if (!ingress) {
NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
return -EOPNOTSUPP;
}
switch (f->command) {
case TC_CLSMATCHALL_REPLACE:
if (!flow_offload_has_one_action(&f->rule->action)) {
NL_SET_ERR_MSG_MOD(extack,
"Only one action is supported");
return -EOPNOTSUPP;
}
if (priv->tc.block_shared) {
NL_SET_ERR_MSG_MOD(extack,
"Rate limit is not supported on shared blocks");
return -EOPNOTSUPP;
}
action = &f->rule->action.entries[0];
if (action->id != FLOW_ACTION_POLICE) {
NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
return -EOPNOTSUPP;
}
if (priv->tc.police_id && priv->tc.police_id != f->cookie) {
NL_SET_ERR_MSG_MOD(extack,
"Only one policer per port is supported");
return -EEXIST;
}
pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
pol.burst = (u32)div_u64(action->police.rate_bytes_ps *
PSCHED_NS2TICKS(action->police.burst),
PSCHED_TICKS_PER_SEC);
err = ocelot_port_policer_add(ocelot, port, &pol);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Could not add policer");
return err;
}
priv->tc.police_id = f->cookie;
priv->tc.offload_cnt++;
return 0;
case TC_CLSMATCHALL_DESTROY:
if (priv->tc.police_id != f->cookie)
return -ENOENT;
err = ocelot_port_policer_del(ocelot, port);
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"Could not delete policer");
return err;
}
priv->tc.police_id = 0;
priv->tc.offload_cnt--;
return 0;
case TC_CLSMATCHALL_STATS: /* fall through */
default:
return -EOPNOTSUPP;
}
}
static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
void *type_data,
void *cb_priv, bool ingress)
{
struct ocelot_port_private *priv = cb_priv;
if (!tc_cls_can_offload_and_chain0(priv->dev, type_data))
return -EOPNOTSUPP;
switch (type) {
case TC_SETUP_CLSMATCHALL:
return ocelot_setup_tc_cls_matchall(priv, type_data, ingress);
case TC_SETUP_CLSFLOWER:
return ocelot_setup_tc_cls_flower(priv, type_data, ingress);
default:
return -EOPNOTSUPP;
}
}
static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,
void *type_data,
void *cb_priv)
{
return ocelot_setup_tc_block_cb(type, type_data,
cb_priv, true);
}
static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
void *type_data,
void *cb_priv)
{
return ocelot_setup_tc_block_cb(type, type_data,
cb_priv, false);
}
static LIST_HEAD(ocelot_block_cb_list);
static int ocelot_setup_tc_block(struct ocelot_port_private *priv,
struct flow_block_offload *f)
{
struct flow_block_cb *block_cb;
flow_setup_cb_t *cb;
if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) {
cb = ocelot_setup_tc_block_cb_ig;
priv->tc.block_shared = f->block_shared;
} else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
cb = ocelot_setup_tc_block_cb_eg;
} else {
return -EOPNOTSUPP;
}
f->driver_block_list = &ocelot_block_cb_list;
switch (f->command) {
case FLOW_BLOCK_BIND:
if (flow_block_cb_is_busy(cb, priv, &ocelot_block_cb_list))
return -EBUSY;
block_cb = flow_block_cb_alloc(cb, priv, priv, NULL);
if (IS_ERR(block_cb))
return PTR_ERR(block_cb);
flow_block_cb_add(block_cb, f);
list_add_tail(&block_cb->driver_list, f->driver_block_list);
return 0;
case FLOW_BLOCK_UNBIND:
block_cb = flow_block_cb_lookup(f->block, cb, priv);
if (!block_cb)
return -ENOENT;
flow_block_cb_remove(block_cb, f);
list_del(&block_cb->driver_list);
return 0;
default:
return -EOPNOTSUPP;
}
}
int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
{
struct ocelot_port_private *priv = netdev_priv(dev);
switch (type) {
case TC_SETUP_BLOCK:
return ocelot_setup_tc_block(priv, type_data);
default:
return -EOPNOTSUPP;
}
return 0;
}
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
/* Microsemi Ocelot Switch driver
*
* Copyright (c) 2019 Microsemi Corporation
*/
#ifndef _MSCC_OCELOT_TC_H_
#define _MSCC_OCELOT_TC_H_
#include <linux/netdevice.h>
struct ocelot_port_tc {
bool block_shared;
unsigned long offload_cnt;
unsigned long police_id;
};
int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data);
#endif /* _MSCC_OCELOT_TC_H_ */
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
#include <soc/mscc/ocelot_vcap.h> #include <soc/mscc/ocelot_vcap.h>
#include "ocelot_police.h" #include "ocelot_police.h"
#include "ocelot_ace.h" #include "ocelot_vcap.h"
#include "ocelot_s2.h" #include "ocelot_s2.h"
#define OCELOT_POLICER_DISCARD 0x17f #define OCELOT_POLICER_DISCARD 0x17f
...@@ -119,7 +119,8 @@ static void vcap_cache2entry(struct ocelot *ocelot, struct vcap_data *data) ...@@ -119,7 +119,8 @@ static void vcap_cache2entry(struct ocelot *ocelot, struct vcap_data *data)
static void vcap_action2cache(struct ocelot *ocelot, struct vcap_data *data) static void vcap_action2cache(struct ocelot *ocelot, struct vcap_data *data)
{ {
const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2];
u32 action_words, i, width, mask; u32 action_words, mask;
int i, width;
/* Encode action type */ /* Encode action type */
width = vcap_is2->action_type_width; width = vcap_is2->action_type_width;
...@@ -141,7 +142,8 @@ static void vcap_action2cache(struct ocelot *ocelot, struct vcap_data *data) ...@@ -141,7 +142,8 @@ static void vcap_action2cache(struct ocelot *ocelot, struct vcap_data *data)
static void vcap_cache2action(struct ocelot *ocelot, struct vcap_data *data) static void vcap_cache2action(struct ocelot *ocelot, struct vcap_data *data)
{ {
const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2];
u32 action_words, i, width; u32 action_words;
int i, width;
action_words = DIV_ROUND_UP(vcap_is2->action_width, ENTRY_WIDTH); action_words = DIV_ROUND_UP(vcap_is2->action_width, ENTRY_WIDTH);
...@@ -161,8 +163,8 @@ static void vcap_cache2action(struct ocelot *ocelot, struct vcap_data *data) ...@@ -161,8 +163,8 @@ static void vcap_cache2action(struct ocelot *ocelot, struct vcap_data *data)
static void is2_data_get(struct ocelot *ocelot, struct vcap_data *data, int ix) static void is2_data_get(struct ocelot *ocelot, struct vcap_data *data, int ix)
{ {
const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2];
u32 i, col, offset, count, cnt, base; int i, col, offset, count, cnt, base;
u32 width = vcap_is2->tg_width; int width = vcap_is2->tg_width;
count = (data->tg_sw == VCAP_TG_HALF ? 2 : 4); count = (data->tg_sw == VCAP_TG_HALF ? 2 : 4);
col = (ix % 2); col = (ix % 2);
...@@ -300,10 +302,10 @@ static void vcap_action_set(struct ocelot *ocelot, struct vcap_data *data, ...@@ -300,10 +302,10 @@ static void vcap_action_set(struct ocelot *ocelot, struct vcap_data *data,
} }
static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data, static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data,
struct ocelot_ace_rule *ace) struct ocelot_vcap_filter *filter)
{ {
switch (ace->action) { switch (filter->action) {
case OCELOT_ACL_ACTION_DROP: case OCELOT_VCAP_ACTION_DROP:
vcap_action_set(ocelot, data, VCAP_IS2_ACT_PORT_MASK, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_PORT_MASK, 0);
vcap_action_set(ocelot, data, VCAP_IS2_ACT_MASK_MODE, 1); vcap_action_set(ocelot, data, VCAP_IS2_ACT_MASK_MODE, 1);
vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_ENA, 1); vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_ENA, 1);
...@@ -312,7 +314,7 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data, ...@@ -312,7 +314,7 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data,
vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_QU_NUM, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_QU_NUM, 0);
vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_COPY_ENA, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_COPY_ENA, 0);
break; break;
case OCELOT_ACL_ACTION_TRAP: case OCELOT_VCAP_ACTION_TRAP:
vcap_action_set(ocelot, data, VCAP_IS2_ACT_PORT_MASK, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_PORT_MASK, 0);
vcap_action_set(ocelot, data, VCAP_IS2_ACT_MASK_MODE, 1); vcap_action_set(ocelot, data, VCAP_IS2_ACT_MASK_MODE, 1);
vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_ENA, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_ENA, 0);
...@@ -320,12 +322,12 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data, ...@@ -320,12 +322,12 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data,
vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_QU_NUM, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_QU_NUM, 0);
vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_COPY_ENA, 1); vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_COPY_ENA, 1);
break; break;
case OCELOT_ACL_ACTION_POLICE: case OCELOT_VCAP_ACTION_POLICE:
vcap_action_set(ocelot, data, VCAP_IS2_ACT_PORT_MASK, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_PORT_MASK, 0);
vcap_action_set(ocelot, data, VCAP_IS2_ACT_MASK_MODE, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_MASK_MODE, 0);
vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_ENA, 1); vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_ENA, 1);
vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_IDX, vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_IDX,
ace->pol_ix); filter->pol_ix);
vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_QU_NUM, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_QU_NUM, 0);
vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_COPY_ENA, 0); vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_COPY_ENA, 0);
break; break;
...@@ -333,11 +335,11 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data, ...@@ -333,11 +335,11 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data,
} }
static void is2_entry_set(struct ocelot *ocelot, int ix, static void is2_entry_set(struct ocelot *ocelot, int ix,
struct ocelot_ace_rule *ace) struct ocelot_vcap_filter *filter)
{ {
const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2];
struct ocelot_vcap_key_vlan *tag = &filter->vlan;
u32 val, msk, type, type_mask = 0xf, i, count; u32 val, msk, type, type_mask = 0xf, i, count;
struct ocelot_ace_vlan *tag = &ace->vlan;
struct ocelot_vcap_u64 payload; struct ocelot_vcap_u64 payload;
struct vcap_data data; struct vcap_data data;
int row = (ix / 2); int row = (ix / 2);
...@@ -353,19 +355,19 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, ...@@ -353,19 +355,19 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
data.tg_sw = VCAP_TG_HALF; data.tg_sw = VCAP_TG_HALF;
is2_data_get(ocelot, &data, ix); is2_data_get(ocelot, &data, ix);
data.tg = (data.tg & ~data.tg_mask); data.tg = (data.tg & ~data.tg_mask);
if (ace->prio != 0) if (filter->prio != 0)
data.tg |= data.tg_value; data.tg |= data.tg_value;
data.type = IS2_ACTION_TYPE_NORMAL; data.type = IS2_ACTION_TYPE_NORMAL;
vcap_key_set(ocelot, &data, VCAP_IS2_HK_PAG, 0, 0); vcap_key_set(ocelot, &data, VCAP_IS2_HK_PAG, 0, 0);
vcap_key_set(ocelot, &data, VCAP_IS2_HK_IGR_PORT_MASK, 0, vcap_key_set(ocelot, &data, VCAP_IS2_HK_IGR_PORT_MASK, 0,
~ace->ingress_port_mask); ~filter->ingress_port_mask);
vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_FIRST, OCELOT_VCAP_BIT_1); vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_FIRST, OCELOT_VCAP_BIT_1);
vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_HOST_MATCH, vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_HOST_MATCH,
OCELOT_VCAP_BIT_ANY); OCELOT_VCAP_BIT_ANY);
vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L2_MC, ace->dmac_mc); vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L2_MC, filter->dmac_mc);
vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L2_BC, ace->dmac_bc); vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L2_BC, filter->dmac_bc);
vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_VLAN_TAGGED, tag->tagged); vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_VLAN_TAGGED, tag->tagged);
vcap_key_set(ocelot, &data, VCAP_IS2_HK_VID, vcap_key_set(ocelot, &data, VCAP_IS2_HK_VID,
tag->vid.value, tag->vid.mask); tag->vid.value, tag->vid.mask);
...@@ -373,9 +375,9 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, ...@@ -373,9 +375,9 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
tag->pcp.value[0], tag->pcp.mask[0]); tag->pcp.value[0], tag->pcp.mask[0]);
vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_DEI, tag->dei); vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_DEI, tag->dei);
switch (ace->type) { switch (filter->key_type) {
case OCELOT_ACE_TYPE_ETYPE: { case OCELOT_VCAP_KEY_ETYPE: {
struct ocelot_ace_frame_etype *etype = &ace->frame.etype; struct ocelot_vcap_key_etype *etype = &filter->key.etype;
type = IS2_TYPE_ETYPE; type = IS2_TYPE_ETYPE;
vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_DMAC, vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_DMAC,
...@@ -396,8 +398,8 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, ...@@ -396,8 +398,8 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
etype->data.value, etype->data.mask); etype->data.value, etype->data.mask);
break; break;
} }
case OCELOT_ACE_TYPE_LLC: { case OCELOT_VCAP_KEY_LLC: {
struct ocelot_ace_frame_llc *llc = &ace->frame.llc; struct ocelot_vcap_key_llc *llc = &filter->key.llc;
type = IS2_TYPE_LLC; type = IS2_TYPE_LLC;
vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_DMAC, vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_DMAC,
...@@ -412,8 +414,8 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, ...@@ -412,8 +414,8 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
payload.value, payload.mask); payload.value, payload.mask);
break; break;
} }
case OCELOT_ACE_TYPE_SNAP: { case OCELOT_VCAP_KEY_SNAP: {
struct ocelot_ace_frame_snap *snap = &ace->frame.snap; struct ocelot_vcap_key_snap *snap = &filter->key.snap;
type = IS2_TYPE_SNAP; type = IS2_TYPE_SNAP;
vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_DMAC, vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_DMAC,
...@@ -421,12 +423,12 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, ...@@ -421,12 +423,12 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_SMAC, vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_SMAC,
snap->smac.value, snap->smac.mask); snap->smac.value, snap->smac.mask);
vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_MAC_SNAP_L2_SNAP, vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_MAC_SNAP_L2_SNAP,
ace->frame.snap.snap.value, filter->key.snap.snap.value,
ace->frame.snap.snap.mask); filter->key.snap.snap.mask);
break; break;
} }
case OCELOT_ACE_TYPE_ARP: { case OCELOT_VCAP_KEY_ARP: {
struct ocelot_ace_frame_arp *arp = &ace->frame.arp; struct ocelot_vcap_key_arp *arp = &filter->key.arp;
type = IS2_TYPE_ARP; type = IS2_TYPE_ARP;
vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_MAC_ARP_SMAC, vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_MAC_ARP_SMAC,
...@@ -467,20 +469,20 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, ...@@ -467,20 +469,20 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
0, 0); 0, 0);
break; break;
} }
case OCELOT_ACE_TYPE_IPV4: case OCELOT_VCAP_KEY_IPV4:
case OCELOT_ACE_TYPE_IPV6: { case OCELOT_VCAP_KEY_IPV6: {
enum ocelot_vcap_bit sip_eq_dip, sport_eq_dport, seq_zero, tcp; enum ocelot_vcap_bit sip_eq_dip, sport_eq_dport, seq_zero, tcp;
enum ocelot_vcap_bit ttl, fragment, options, tcp_ack, tcp_urg; enum ocelot_vcap_bit ttl, fragment, options, tcp_ack, tcp_urg;
enum ocelot_vcap_bit tcp_fin, tcp_syn, tcp_rst, tcp_psh; enum ocelot_vcap_bit tcp_fin, tcp_syn, tcp_rst, tcp_psh;
struct ocelot_ace_frame_ipv4 *ipv4 = NULL; struct ocelot_vcap_key_ipv4 *ipv4 = NULL;
struct ocelot_ace_frame_ipv6 *ipv6 = NULL; struct ocelot_vcap_key_ipv6 *ipv6 = NULL;
struct ocelot_vcap_udp_tcp *sport, *dport; struct ocelot_vcap_udp_tcp *sport, *dport;
struct ocelot_vcap_ipv4 sip, dip; struct ocelot_vcap_ipv4 sip, dip;
struct ocelot_vcap_u8 proto, ds; struct ocelot_vcap_u8 proto, ds;
struct ocelot_vcap_u48 *ip_data; struct ocelot_vcap_u48 *ip_data;
if (ace->type == OCELOT_ACE_TYPE_IPV4) { if (filter->key_type == OCELOT_VCAP_KEY_IPV4) {
ipv4 = &ace->frame.ipv4; ipv4 = &filter->key.ipv4;
ttl = ipv4->ttl; ttl = ipv4->ttl;
fragment = ipv4->fragment; fragment = ipv4->fragment;
options = ipv4->options; options = ipv4->options;
...@@ -501,7 +503,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, ...@@ -501,7 +503,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
sport_eq_dport = ipv4->sport_eq_dport; sport_eq_dport = ipv4->sport_eq_dport;
seq_zero = ipv4->seq_zero; seq_zero = ipv4->seq_zero;
} else { } else {
ipv6 = &ace->frame.ipv6; ipv6 = &filter->key.ipv6;
ttl = ipv6->ttl; ttl = ipv6->ttl;
fragment = OCELOT_VCAP_BIT_ANY; fragment = OCELOT_VCAP_BIT_ANY;
options = OCELOT_VCAP_BIT_ANY; options = OCELOT_VCAP_BIT_ANY;
...@@ -605,7 +607,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, ...@@ -605,7 +607,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
} }
break; break;
} }
case OCELOT_ACE_TYPE_ANY: case OCELOT_VCAP_KEY_ANY:
default: default:
type = 0; type = 0;
type_mask = 0; type_mask = 0;
...@@ -621,9 +623,9 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, ...@@ -621,9 +623,9 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
} }
vcap_key_set(ocelot, &data, VCAP_IS2_TYPE, type, type_mask); vcap_key_set(ocelot, &data, VCAP_IS2_TYPE, type, type_mask);
is2_action_set(ocelot, &data, ace); is2_action_set(ocelot, &data, filter);
vcap_data_set(data.counter, data.counter_offset, vcap_data_set(data.counter, data.counter_offset,
vcap_is2->counter_width, ace->stats.pkts); vcap_is2->counter_width, filter->stats.pkts);
/* Write row */ /* Write row */
vcap_entry2cache(ocelot, &data); vcap_entry2cache(ocelot, &data);
...@@ -631,7 +633,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, ...@@ -631,7 +633,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
vcap_row_cmd(ocelot, row, VCAP_CMD_WRITE, VCAP_SEL_ALL); vcap_row_cmd(ocelot, row, VCAP_CMD_WRITE, VCAP_SEL_ALL);
} }
static void is2_entry_get(struct ocelot *ocelot, struct ocelot_ace_rule *rule, static void is2_entry_get(struct ocelot *ocelot, struct ocelot_vcap_filter *filter,
int ix) int ix)
{ {
const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2];
...@@ -646,55 +648,99 @@ static void is2_entry_get(struct ocelot *ocelot, struct ocelot_ace_rule *rule, ...@@ -646,55 +648,99 @@ static void is2_entry_get(struct ocelot *ocelot, struct ocelot_ace_rule *rule,
cnt = vcap_data_get(data.counter, data.counter_offset, cnt = vcap_data_get(data.counter, data.counter_offset,
vcap_is2->counter_width); vcap_is2->counter_width);
rule->stats.pkts = cnt; filter->stats.pkts = cnt;
} }
static void ocelot_ace_rule_add(struct ocelot *ocelot, static int ocelot_vcap_policer_add(struct ocelot *ocelot, u32 pol_ix,
struct ocelot_acl_block *block, struct ocelot_policer *pol)
struct ocelot_ace_rule *rule)
{ {
struct ocelot_ace_rule *tmp; struct qos_policer_conf pp = { 0 };
if (!pol)
return -EINVAL;
pp.mode = MSCC_QOS_RATE_MODE_DATA;
pp.pir = pol->rate;
pp.pbs = pol->burst;
return qos_policer_conf_set(ocelot, 0, pol_ix, &pp);
}
static void ocelot_vcap_policer_del(struct ocelot *ocelot,
struct ocelot_vcap_block *block,
u32 pol_ix)
{
struct ocelot_vcap_filter *filter;
struct qos_policer_conf pp = {0};
int index = -1;
if (pol_ix < block->pol_lpr)
return;
list_for_each_entry(filter, &block->rules, list) {
index++;
if (filter->action == OCELOT_VCAP_ACTION_POLICE &&
filter->pol_ix < pol_ix) {
filter->pol_ix += 1;
ocelot_vcap_policer_add(ocelot, filter->pol_ix,
&filter->pol);
is2_entry_set(ocelot, index, filter);
}
}
pp.mode = MSCC_QOS_RATE_MODE_DISABLED;
qos_policer_conf_set(ocelot, 0, pol_ix, &pp);
block->pol_lpr++;
}
static void ocelot_vcap_filter_add_to_block(struct ocelot *ocelot,
struct ocelot_vcap_block *block,
struct ocelot_vcap_filter *filter)
{
struct ocelot_vcap_filter *tmp;
struct list_head *pos, *n; struct list_head *pos, *n;
if (rule->action == OCELOT_ACL_ACTION_POLICE) { if (filter->action == OCELOT_VCAP_ACTION_POLICE) {
block->pol_lpr--; block->pol_lpr--;
rule->pol_ix = block->pol_lpr; filter->pol_ix = block->pol_lpr;
ocelot_ace_policer_add(ocelot, rule->pol_ix, &rule->pol); ocelot_vcap_policer_add(ocelot, filter->pol_ix, &filter->pol);
} }
block->count++; block->count++;
if (list_empty(&block->rules)) { if (list_empty(&block->rules)) {
list_add(&rule->list, &block->rules); list_add(&filter->list, &block->rules);
return; return;
} }
list_for_each_safe(pos, n, &block->rules) { list_for_each_safe(pos, n, &block->rules) {
tmp = list_entry(pos, struct ocelot_ace_rule, list); tmp = list_entry(pos, struct ocelot_vcap_filter, list);
if (rule->prio < tmp->prio) if (filter->prio < tmp->prio)
break; break;
} }
list_add(&rule->list, pos->prev); list_add(&filter->list, pos->prev);
} }
static int ocelot_ace_rule_get_index_id(struct ocelot_acl_block *block, static int ocelot_vcap_block_get_filter_index(struct ocelot_vcap_block *block,
struct ocelot_ace_rule *rule) struct ocelot_vcap_filter *filter)
{ {
struct ocelot_ace_rule *tmp; struct ocelot_vcap_filter *tmp;
int index = -1; int index = -1;
list_for_each_entry(tmp, &block->rules, list) { list_for_each_entry(tmp, &block->rules, list) {
++index; ++index;
if (rule->id == tmp->id) if (filter->id == tmp->id)
break; break;
} }
return index; return index;
} }
static struct ocelot_ace_rule* static struct ocelot_vcap_filter*
ocelot_ace_rule_get_rule_index(struct ocelot_acl_block *block, int index) ocelot_vcap_block_find_filter(struct ocelot_vcap_block *block,
int index)
{ {
struct ocelot_ace_rule *tmp; struct ocelot_vcap_filter *tmp;
int i = 0; int i = 0;
list_for_each_entry(tmp, &block->rules, list) { list_for_each_entry(tmp, &block->rules, list) {
...@@ -737,15 +783,16 @@ static void ocelot_match_all_as_mac_etype(struct ocelot *ocelot, int port, ...@@ -737,15 +783,16 @@ static void ocelot_match_all_as_mac_etype(struct ocelot *ocelot, int port,
ANA_PORT_VCAP_S2_CFG, port); ANA_PORT_VCAP_S2_CFG, port);
} }
static bool ocelot_ace_is_problematic_mac_etype(struct ocelot_ace_rule *ace) static bool
ocelot_vcap_is_problematic_mac_etype(struct ocelot_vcap_filter *filter)
{ {
u16 proto, mask; u16 proto, mask;
if (ace->type != OCELOT_ACE_TYPE_ETYPE) if (filter->key_type != OCELOT_VCAP_KEY_ETYPE)
return false; return false;
proto = ntohs(*(u16 *)ace->frame.etype.etype.value); proto = ntohs(*(__be16 *)filter->key.etype.etype.value);
mask = ntohs(*(u16 *)ace->frame.etype.etype.mask); mask = ntohs(*(__be16 *)filter->key.etype.etype.mask);
/* ETH_P_ALL match, so all protocols below are included */ /* ETH_P_ALL match, so all protocols below are included */
if (mask == 0) if (mask == 0)
...@@ -760,49 +807,51 @@ static bool ocelot_ace_is_problematic_mac_etype(struct ocelot_ace_rule *ace) ...@@ -760,49 +807,51 @@ static bool ocelot_ace_is_problematic_mac_etype(struct ocelot_ace_rule *ace)
return false; return false;
} }
static bool ocelot_ace_is_problematic_non_mac_etype(struct ocelot_ace_rule *ace) static bool
ocelot_vcap_is_problematic_non_mac_etype(struct ocelot_vcap_filter *filter)
{ {
if (ace->type == OCELOT_ACE_TYPE_SNAP) if (filter->key_type == OCELOT_VCAP_KEY_SNAP)
return true; return true;
if (ace->type == OCELOT_ACE_TYPE_ARP) if (filter->key_type == OCELOT_VCAP_KEY_ARP)
return true; return true;
if (ace->type == OCELOT_ACE_TYPE_IPV4) if (filter->key_type == OCELOT_VCAP_KEY_IPV4)
return true; return true;
if (ace->type == OCELOT_ACE_TYPE_IPV6) if (filter->key_type == OCELOT_VCAP_KEY_IPV6)
return true; return true;
return false; return false;
} }
static bool ocelot_exclusive_mac_etype_ace_rules(struct ocelot *ocelot, static bool
struct ocelot_ace_rule *ace) ocelot_exclusive_mac_etype_filter_rules(struct ocelot *ocelot,
struct ocelot_vcap_filter *filter)
{ {
struct ocelot_acl_block *block = &ocelot->acl_block; struct ocelot_vcap_block *block = &ocelot->block;
struct ocelot_ace_rule *tmp; struct ocelot_vcap_filter *tmp;
unsigned long port; unsigned long port;
int i; int i;
if (ocelot_ace_is_problematic_mac_etype(ace)) { if (ocelot_vcap_is_problematic_mac_etype(filter)) {
/* Search for any non-MAC_ETYPE rules on the port */ /* Search for any non-MAC_ETYPE rules on the port */
for (i = 0; i < block->count; i++) { for (i = 0; i < block->count; i++) {
tmp = ocelot_ace_rule_get_rule_index(block, i); tmp = ocelot_vcap_block_find_filter(block, i);
if (tmp->ingress_port_mask & ace->ingress_port_mask && if (tmp->ingress_port_mask & filter->ingress_port_mask &&
ocelot_ace_is_problematic_non_mac_etype(tmp)) ocelot_vcap_is_problematic_non_mac_etype(tmp))
return false; return false;
} }
for_each_set_bit(port, &ace->ingress_port_mask, for_each_set_bit(port, &filter->ingress_port_mask,
ocelot->num_phys_ports) ocelot->num_phys_ports)
ocelot_match_all_as_mac_etype(ocelot, port, true); ocelot_match_all_as_mac_etype(ocelot, port, true);
} else if (ocelot_ace_is_problematic_non_mac_etype(ace)) { } else if (ocelot_vcap_is_problematic_non_mac_etype(filter)) {
/* Search for any MAC_ETYPE rules on the port */ /* Search for any MAC_ETYPE rules on the port */
for (i = 0; i < block->count; i++) { for (i = 0; i < block->count; i++) {
tmp = ocelot_ace_rule_get_rule_index(block, i); tmp = ocelot_vcap_block_find_filter(block, i);
if (tmp->ingress_port_mask & ace->ingress_port_mask && if (tmp->ingress_port_mask & filter->ingress_port_mask &&
ocelot_ace_is_problematic_mac_etype(tmp)) ocelot_vcap_is_problematic_mac_etype(tmp))
return false; return false;
} }
for_each_set_bit(port, &ace->ingress_port_mask, for_each_set_bit(port, &filter->ingress_port_mask,
ocelot->num_phys_ports) ocelot->num_phys_ports)
ocelot_match_all_as_mac_etype(ocelot, port, false); ocelot_match_all_as_mac_etype(ocelot, port, false);
} }
...@@ -810,74 +859,50 @@ static bool ocelot_exclusive_mac_etype_ace_rules(struct ocelot *ocelot, ...@@ -810,74 +859,50 @@ static bool ocelot_exclusive_mac_etype_ace_rules(struct ocelot *ocelot,
return true; return true;
} }
int ocelot_ace_rule_offload_add(struct ocelot *ocelot, int ocelot_vcap_filter_add(struct ocelot *ocelot,
struct ocelot_ace_rule *rule, struct ocelot_vcap_filter *filter,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct ocelot_acl_block *block = &ocelot->acl_block; struct ocelot_vcap_block *block = &ocelot->block;
struct ocelot_ace_rule *ace;
int i, index; int i, index;
if (!ocelot_exclusive_mac_etype_ace_rules(ocelot, rule)) { if (!ocelot_exclusive_mac_etype_filter_rules(ocelot, filter)) {
NL_SET_ERR_MSG_MOD(extack, NL_SET_ERR_MSG_MOD(extack,
"Cannot mix MAC_ETYPE with non-MAC_ETYPE rules"); "Cannot mix MAC_ETYPE with non-MAC_ETYPE rules");
return -EBUSY; return -EBUSY;
} }
/* Add rule to the linked list */ /* Add filter to the linked list */
ocelot_ace_rule_add(ocelot, block, rule); ocelot_vcap_filter_add_to_block(ocelot, block, filter);
/* Get the index of the inserted rule */ /* Get the index of the inserted filter */
index = ocelot_ace_rule_get_index_id(block, rule); index = ocelot_vcap_block_get_filter_index(block, filter);
/* Move down the rules to make place for the new rule */ /* Move down the rules to make place for the new filter */
for (i = block->count - 1; i > index; i--) { for (i = block->count - 1; i > index; i--) {
ace = ocelot_ace_rule_get_rule_index(block, i); struct ocelot_vcap_filter *tmp;
is2_entry_set(ocelot, i, ace);
}
/* Now insert the new rule */
is2_entry_set(ocelot, index, rule);
return 0;
}
static void ocelot_ace_police_del(struct ocelot *ocelot,
struct ocelot_acl_block *block,
u32 ix)
{
struct ocelot_ace_rule *ace;
int index = -1;
if (ix < block->pol_lpr)
return;
list_for_each_entry(ace, &block->rules, list) { tmp = ocelot_vcap_block_find_filter(block, i);
index++; is2_entry_set(ocelot, i, tmp);
if (ace->action == OCELOT_ACL_ACTION_POLICE &&
ace->pol_ix < ix) {
ace->pol_ix += 1;
ocelot_ace_policer_add(ocelot, ace->pol_ix,
&ace->pol);
is2_entry_set(ocelot, index, ace);
}
} }
ocelot_ace_policer_del(ocelot, block->pol_lpr); /* Now insert the new filter */
block->pol_lpr++; is2_entry_set(ocelot, index, filter);
return 0;
} }
static void ocelot_ace_rule_del(struct ocelot *ocelot, static void ocelot_vcap_block_remove_filter(struct ocelot *ocelot,
struct ocelot_acl_block *block, struct ocelot_vcap_block *block,
struct ocelot_ace_rule *rule) struct ocelot_vcap_filter *filter)
{ {
struct ocelot_ace_rule *tmp; struct ocelot_vcap_filter *tmp;
struct list_head *pos, *q; struct list_head *pos, *q;
list_for_each_safe(pos, q, &block->rules) { list_for_each_safe(pos, q, &block->rules) {
tmp = list_entry(pos, struct ocelot_ace_rule, list); tmp = list_entry(pos, struct ocelot_vcap_filter, list);
if (tmp->id == rule->id) { if (tmp->id == filter->id) {
if (tmp->action == OCELOT_ACL_ACTION_POLICE) if (tmp->action == OCELOT_VCAP_ACTION_POLICE)
ocelot_ace_police_del(ocelot, block, ocelot_vcap_policer_del(ocelot, block,
tmp->pol_ix); tmp->pol_ix);
list_del(pos); list_del(pos);
...@@ -888,56 +913,57 @@ static void ocelot_ace_rule_del(struct ocelot *ocelot, ...@@ -888,56 +913,57 @@ static void ocelot_ace_rule_del(struct ocelot *ocelot,
block->count--; block->count--;
} }
int ocelot_ace_rule_offload_del(struct ocelot *ocelot, int ocelot_vcap_filter_del(struct ocelot *ocelot,
struct ocelot_ace_rule *rule) struct ocelot_vcap_filter *filter)
{ {
struct ocelot_acl_block *block = &ocelot->acl_block; struct ocelot_vcap_block *block = &ocelot->block;
struct ocelot_ace_rule del_ace; struct ocelot_vcap_filter del_filter;
struct ocelot_ace_rule *ace;
int i, index; int i, index;
memset(&del_ace, 0, sizeof(del_ace)); memset(&del_filter, 0, sizeof(del_filter));
/* Gets index of the rule */ /* Gets index of the filter */
index = ocelot_ace_rule_get_index_id(block, rule); index = ocelot_vcap_block_get_filter_index(block, filter);
/* Delete rule */ /* Delete filter */
ocelot_ace_rule_del(ocelot, block, rule); ocelot_vcap_block_remove_filter(ocelot, block, filter);
/* Move up all the blocks over the deleted rule */ /* Move up all the blocks over the deleted filter */
for (i = index; i < block->count; i++) { for (i = index; i < block->count; i++) {
ace = ocelot_ace_rule_get_rule_index(block, i); struct ocelot_vcap_filter *tmp;
is2_entry_set(ocelot, i, ace);
tmp = ocelot_vcap_block_find_filter(block, i);
is2_entry_set(ocelot, i, tmp);
} }
/* Now delete the last rule, because it is duplicated */ /* Now delete the last filter, because it is duplicated */
is2_entry_set(ocelot, block->count, &del_ace); is2_entry_set(ocelot, block->count, &del_filter);
return 0; return 0;
} }
int ocelot_ace_rule_stats_update(struct ocelot *ocelot, int ocelot_vcap_filter_stats_update(struct ocelot *ocelot,
struct ocelot_ace_rule *rule) struct ocelot_vcap_filter *filter)
{ {
struct ocelot_acl_block *block = &ocelot->acl_block; struct ocelot_vcap_block *block = &ocelot->block;
struct ocelot_ace_rule *tmp; struct ocelot_vcap_filter *tmp;
int index; int index;
index = ocelot_ace_rule_get_index_id(block, rule); index = ocelot_vcap_block_get_filter_index(block, filter);
is2_entry_get(ocelot, rule, index); is2_entry_get(ocelot, filter, index);
/* After we get the result we need to clear the counters */ /* After we get the result we need to clear the counters */
tmp = ocelot_ace_rule_get_rule_index(block, index); tmp = ocelot_vcap_block_find_filter(block, index);
tmp->stats.pkts = 0; tmp->stats.pkts = 0;
is2_entry_set(ocelot, index, tmp); is2_entry_set(ocelot, index, tmp);
return 0; return 0;
} }
int ocelot_ace_init(struct ocelot *ocelot) int ocelot_vcap_init(struct ocelot *ocelot)
{ {
const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2];
struct ocelot_acl_block *block = &ocelot->acl_block; struct ocelot_vcap_block *block = &ocelot->block;
struct vcap_data data; struct vcap_data data;
memset(&data, 0, sizeof(data)); memset(&data, 0, sizeof(data));
...@@ -968,7 +994,7 @@ int ocelot_ace_init(struct ocelot *ocelot) ...@@ -968,7 +994,7 @@ int ocelot_ace_init(struct ocelot *ocelot)
block->pol_lpr = OCELOT_POLICER_DISCARD - 1; block->pol_lpr = OCELOT_POLICER_DISCARD - 1;
INIT_LIST_HEAD(&ocelot->acl_block.rules); INIT_LIST_HEAD(&ocelot->block.rules);
return 0; return 0;
} }
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
* Copyright (c) 2019 Microsemi Corporation * Copyright (c) 2019 Microsemi Corporation
*/ */
#ifndef _MSCC_OCELOT_ACE_H_ #ifndef _MSCC_OCELOT_VCAP_H_
#define _MSCC_OCELOT_ACE_H_ #define _MSCC_OCELOT_VCAP_H_
#include "ocelot.h" #include "ocelot.h"
#include "ocelot_police.h" #include "ocelot_police.h"
...@@ -76,31 +76,31 @@ struct ocelot_vcap_udp_tcp { ...@@ -76,31 +76,31 @@ struct ocelot_vcap_udp_tcp {
u16 mask; u16 mask;
}; };
enum ocelot_ace_type { enum ocelot_vcap_key_type {
OCELOT_ACE_TYPE_ANY, OCELOT_VCAP_KEY_ANY,
OCELOT_ACE_TYPE_ETYPE, OCELOT_VCAP_KEY_ETYPE,
OCELOT_ACE_TYPE_LLC, OCELOT_VCAP_KEY_LLC,
OCELOT_ACE_TYPE_SNAP, OCELOT_VCAP_KEY_SNAP,
OCELOT_ACE_TYPE_ARP, OCELOT_VCAP_KEY_ARP,
OCELOT_ACE_TYPE_IPV4, OCELOT_VCAP_KEY_IPV4,
OCELOT_ACE_TYPE_IPV6 OCELOT_VCAP_KEY_IPV6
}; };
struct ocelot_ace_vlan { struct ocelot_vcap_key_vlan {
struct ocelot_vcap_vid vid; /* VLAN ID (12 bit) */ struct ocelot_vcap_vid vid; /* VLAN ID (12 bit) */
struct ocelot_vcap_u8 pcp; /* PCP (3 bit) */ struct ocelot_vcap_u8 pcp; /* PCP (3 bit) */
enum ocelot_vcap_bit dei; /* DEI */ enum ocelot_vcap_bit dei; /* DEI */
enum ocelot_vcap_bit tagged; /* Tagged/untagged frame */ enum ocelot_vcap_bit tagged; /* Tagged/untagged frame */
}; };
struct ocelot_ace_frame_etype { struct ocelot_vcap_key_etype {
struct ocelot_vcap_u48 dmac; struct ocelot_vcap_u48 dmac;
struct ocelot_vcap_u48 smac; struct ocelot_vcap_u48 smac;
struct ocelot_vcap_u16 etype; struct ocelot_vcap_u16 etype;
struct ocelot_vcap_u16 data; /* MAC data */ struct ocelot_vcap_u16 data; /* MAC data */
}; };
struct ocelot_ace_frame_llc { struct ocelot_vcap_key_llc {
struct ocelot_vcap_u48 dmac; struct ocelot_vcap_u48 dmac;
struct ocelot_vcap_u48 smac; struct ocelot_vcap_u48 smac;
...@@ -108,7 +108,7 @@ struct ocelot_ace_frame_llc { ...@@ -108,7 +108,7 @@ struct ocelot_ace_frame_llc {
struct ocelot_vcap_u32 llc; struct ocelot_vcap_u32 llc;
}; };
struct ocelot_ace_frame_snap { struct ocelot_vcap_key_snap {
struct ocelot_vcap_u48 dmac; struct ocelot_vcap_u48 dmac;
struct ocelot_vcap_u48 smac; struct ocelot_vcap_u48 smac;
...@@ -116,7 +116,7 @@ struct ocelot_ace_frame_snap { ...@@ -116,7 +116,7 @@ struct ocelot_ace_frame_snap {
struct ocelot_vcap_u40 snap; struct ocelot_vcap_u40 snap;
}; };
struct ocelot_ace_frame_arp { struct ocelot_vcap_key_arp {
struct ocelot_vcap_u48 smac; struct ocelot_vcap_u48 smac;
enum ocelot_vcap_bit arp; /* Opcode ARP/RARP */ enum ocelot_vcap_bit arp; /* Opcode ARP/RARP */
enum ocelot_vcap_bit req; /* Opcode request/reply */ enum ocelot_vcap_bit req; /* Opcode request/reply */
...@@ -133,7 +133,7 @@ struct ocelot_ace_frame_arp { ...@@ -133,7 +133,7 @@ struct ocelot_ace_frame_arp {
struct ocelot_vcap_ipv4 dip; /* Target IP address */ struct ocelot_vcap_ipv4 dip; /* Target IP address */
}; };
struct ocelot_ace_frame_ipv4 { struct ocelot_vcap_key_ipv4 {
enum ocelot_vcap_bit ttl; /* TTL zero */ enum ocelot_vcap_bit ttl; /* TTL zero */
enum ocelot_vcap_bit fragment; /* Fragment */ enum ocelot_vcap_bit fragment; /* Fragment */
enum ocelot_vcap_bit options; /* Header options */ enum ocelot_vcap_bit options; /* Header options */
...@@ -155,7 +155,7 @@ struct ocelot_ace_frame_ipv4 { ...@@ -155,7 +155,7 @@ struct ocelot_ace_frame_ipv4 {
enum ocelot_vcap_bit seq_zero; /* TCP sequence number is zero */ enum ocelot_vcap_bit seq_zero; /* TCP sequence number is zero */
}; };
struct ocelot_ace_frame_ipv6 { struct ocelot_vcap_key_ipv6 {
struct ocelot_vcap_u8 proto; /* IPv6 protocol */ struct ocelot_vcap_u8 proto; /* IPv6 protocol */
struct ocelot_vcap_u128 sip; /* IPv6 source (byte 0-7 ignored) */ struct ocelot_vcap_u128 sip; /* IPv6 source (byte 0-7 ignored) */
enum ocelot_vcap_bit ttl; /* TTL zero */ enum ocelot_vcap_bit ttl; /* TTL zero */
...@@ -174,58 +174,58 @@ struct ocelot_ace_frame_ipv6 { ...@@ -174,58 +174,58 @@ struct ocelot_ace_frame_ipv6 {
enum ocelot_vcap_bit seq_zero; /* TCP sequence number is zero */ enum ocelot_vcap_bit seq_zero; /* TCP sequence number is zero */
}; };
enum ocelot_ace_action { enum ocelot_vcap_action {
OCELOT_ACL_ACTION_DROP, OCELOT_VCAP_ACTION_DROP,
OCELOT_ACL_ACTION_TRAP, OCELOT_VCAP_ACTION_TRAP,
OCELOT_ACL_ACTION_POLICE, OCELOT_VCAP_ACTION_POLICE,
}; };
struct ocelot_ace_stats { struct ocelot_vcap_stats {
u64 bytes; u64 bytes;
u64 pkts; u64 pkts;
u64 used; u64 used;
}; };
struct ocelot_ace_rule { struct ocelot_vcap_filter {
struct list_head list; struct list_head list;
u16 prio; u16 prio;
u32 id; u32 id;
enum ocelot_ace_action action; enum ocelot_vcap_action action;
struct ocelot_ace_stats stats; struct ocelot_vcap_stats stats;
unsigned long ingress_port_mask; unsigned long ingress_port_mask;
enum ocelot_vcap_bit dmac_mc; enum ocelot_vcap_bit dmac_mc;
enum ocelot_vcap_bit dmac_bc; enum ocelot_vcap_bit dmac_bc;
struct ocelot_ace_vlan vlan; struct ocelot_vcap_key_vlan vlan;
enum ocelot_ace_type type; enum ocelot_vcap_key_type key_type;
union { union {
/* ocelot_ACE_TYPE_ANY: No specific fields */ /* OCELOT_VCAP_KEY_ANY: No specific fields */
struct ocelot_ace_frame_etype etype; struct ocelot_vcap_key_etype etype;
struct ocelot_ace_frame_llc llc; struct ocelot_vcap_key_llc llc;
struct ocelot_ace_frame_snap snap; struct ocelot_vcap_key_snap snap;
struct ocelot_ace_frame_arp arp; struct ocelot_vcap_key_arp arp;
struct ocelot_ace_frame_ipv4 ipv4; struct ocelot_vcap_key_ipv4 ipv4;
struct ocelot_ace_frame_ipv6 ipv6; struct ocelot_vcap_key_ipv6 ipv6;
} frame; } key;
struct ocelot_policer pol; struct ocelot_policer pol;
u32 pol_ix; u32 pol_ix;
}; };
int ocelot_ace_rule_offload_add(struct ocelot *ocelot, int ocelot_vcap_filter_add(struct ocelot *ocelot,
struct ocelot_ace_rule *rule, struct ocelot_vcap_filter *rule,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
int ocelot_ace_rule_offload_del(struct ocelot *ocelot, int ocelot_vcap_filter_del(struct ocelot *ocelot,
struct ocelot_ace_rule *rule); struct ocelot_vcap_filter *rule);
int ocelot_ace_rule_stats_update(struct ocelot *ocelot, int ocelot_vcap_filter_stats_update(struct ocelot *ocelot,
struct ocelot_ace_rule *rule); struct ocelot_vcap_filter *rule);
int ocelot_ace_init(struct ocelot *ocelot); int ocelot_vcap_init(struct ocelot *ocelot);
int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv, int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv,
struct flow_cls_offload *f, struct flow_cls_offload *f,
bool ingress); bool ingress);
#endif /* _MSCC_OCELOT_ACE_H_ */ #endif /* _MSCC_OCELOT_VCAP_H_ */
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <net/switchdev.h> #include <net/switchdev.h>
#include <soc/mscc/ocelot_vcap.h> #include <soc/mscc/ocelot_vcap.h>
#include <soc/mscc/ocelot_hsio.h>
#include "ocelot.h" #include "ocelot.h"
#define IFH_EXTRACT_BITFIELD64(x, o, w) (((x) >> (o)) & GENMASK_ULL((w) - 1, 0)) #define IFH_EXTRACT_BITFIELD64(x, o, w) (((x) >> (o)) & GENMASK_ULL((w) - 1, 0))
...@@ -23,6 +24,447 @@ ...@@ -23,6 +24,447 @@
#define VSC7514_VCAP_IS2_ACTION_WIDTH 99 #define VSC7514_VCAP_IS2_ACTION_WIDTH 99
#define VSC7514_VCAP_PORT_CNT 11 #define VSC7514_VCAP_PORT_CNT 11
static const u32 ocelot_ana_regmap[] = {
REG(ANA_ADVLEARN, 0x009000),
REG(ANA_VLANMASK, 0x009004),
REG(ANA_PORT_B_DOMAIN, 0x009008),
REG(ANA_ANAGEFIL, 0x00900c),
REG(ANA_ANEVENTS, 0x009010),
REG(ANA_STORMLIMIT_BURST, 0x009014),
REG(ANA_STORMLIMIT_CFG, 0x009018),
REG(ANA_ISOLATED_PORTS, 0x009028),
REG(ANA_COMMUNITY_PORTS, 0x00902c),
REG(ANA_AUTOAGE, 0x009030),
REG(ANA_MACTOPTIONS, 0x009034),
REG(ANA_LEARNDISC, 0x009038),
REG(ANA_AGENCTRL, 0x00903c),
REG(ANA_MIRRORPORTS, 0x009040),
REG(ANA_EMIRRORPORTS, 0x009044),
REG(ANA_FLOODING, 0x009048),
REG(ANA_FLOODING_IPMC, 0x00904c),
REG(ANA_SFLOW_CFG, 0x009050),
REG(ANA_PORT_MODE, 0x009080),
REG(ANA_PGID_PGID, 0x008c00),
REG(ANA_TABLES_ANMOVED, 0x008b30),
REG(ANA_TABLES_MACHDATA, 0x008b34),
REG(ANA_TABLES_MACLDATA, 0x008b38),
REG(ANA_TABLES_MACACCESS, 0x008b3c),
REG(ANA_TABLES_MACTINDX, 0x008b40),
REG(ANA_TABLES_VLANACCESS, 0x008b44),
REG(ANA_TABLES_VLANTIDX, 0x008b48),
REG(ANA_TABLES_ISDXACCESS, 0x008b4c),
REG(ANA_TABLES_ISDXTIDX, 0x008b50),
REG(ANA_TABLES_ENTRYLIM, 0x008b00),
REG(ANA_TABLES_PTP_ID_HIGH, 0x008b54),
REG(ANA_TABLES_PTP_ID_LOW, 0x008b58),
REG(ANA_MSTI_STATE, 0x008e00),
REG(ANA_PORT_VLAN_CFG, 0x007000),
REG(ANA_PORT_DROP_CFG, 0x007004),
REG(ANA_PORT_QOS_CFG, 0x007008),
REG(ANA_PORT_VCAP_CFG, 0x00700c),
REG(ANA_PORT_VCAP_S1_KEY_CFG, 0x007010),
REG(ANA_PORT_VCAP_S2_CFG, 0x00701c),
REG(ANA_PORT_PCP_DEI_MAP, 0x007020),
REG(ANA_PORT_CPU_FWD_CFG, 0x007060),
REG(ANA_PORT_CPU_FWD_BPDU_CFG, 0x007064),
REG(ANA_PORT_CPU_FWD_GARP_CFG, 0x007068),
REG(ANA_PORT_CPU_FWD_CCM_CFG, 0x00706c),
REG(ANA_PORT_PORT_CFG, 0x007070),
REG(ANA_PORT_POL_CFG, 0x007074),
REG(ANA_PORT_PTP_CFG, 0x007078),
REG(ANA_PORT_PTP_DLY1_CFG, 0x00707c),
REG(ANA_OAM_UPM_LM_CNT, 0x007c00),
REG(ANA_PORT_PTP_DLY2_CFG, 0x007080),
REG(ANA_PFC_PFC_CFG, 0x008800),
REG(ANA_PFC_PFC_TIMER, 0x008804),
REG(ANA_IPT_OAM_MEP_CFG, 0x008000),
REG(ANA_IPT_IPT, 0x008004),
REG(ANA_PPT_PPT, 0x008ac0),
REG(ANA_FID_MAP_FID_MAP, 0x000000),
REG(ANA_AGGR_CFG, 0x0090b4),
REG(ANA_CPUQ_CFG, 0x0090b8),
REG(ANA_CPUQ_CFG2, 0x0090bc),
REG(ANA_CPUQ_8021_CFG, 0x0090c0),
REG(ANA_DSCP_CFG, 0x009100),
REG(ANA_DSCP_REWR_CFG, 0x009200),
REG(ANA_VCAP_RNG_TYPE_CFG, 0x009240),
REG(ANA_VCAP_RNG_VAL_CFG, 0x009260),
REG(ANA_VRAP_CFG, 0x009280),
REG(ANA_VRAP_HDR_DATA, 0x009284),
REG(ANA_VRAP_HDR_MASK, 0x009288),
REG(ANA_DISCARD_CFG, 0x00928c),
REG(ANA_FID_CFG, 0x009290),
REG(ANA_POL_PIR_CFG, 0x004000),
REG(ANA_POL_CIR_CFG, 0x004004),
REG(ANA_POL_MODE_CFG, 0x004008),
REG(ANA_POL_PIR_STATE, 0x00400c),
REG(ANA_POL_CIR_STATE, 0x004010),
REG(ANA_POL_STATE, 0x004014),
REG(ANA_POL_FLOWC, 0x008b80),
REG(ANA_POL_HYST, 0x008bec),
REG(ANA_POL_MISC_CFG, 0x008bf0),
};
static const u32 ocelot_qs_regmap[] = {
REG(QS_XTR_GRP_CFG, 0x000000),
REG(QS_XTR_RD, 0x000008),
REG(QS_XTR_FRM_PRUNING, 0x000010),
REG(QS_XTR_FLUSH, 0x000018),
REG(QS_XTR_DATA_PRESENT, 0x00001c),
REG(QS_XTR_CFG, 0x000020),
REG(QS_INJ_GRP_CFG, 0x000024),
REG(QS_INJ_WR, 0x00002c),
REG(QS_INJ_CTRL, 0x000034),
REG(QS_INJ_STATUS, 0x00003c),
REG(QS_INJ_ERR, 0x000040),
REG(QS_INH_DBG, 0x000048),
};
static const u32 ocelot_qsys_regmap[] = {
REG(QSYS_PORT_MODE, 0x011200),
REG(QSYS_SWITCH_PORT_MODE, 0x011234),
REG(QSYS_STAT_CNT_CFG, 0x011264),
REG(QSYS_EEE_CFG, 0x011268),
REG(QSYS_EEE_THRES, 0x011294),
REG(QSYS_IGR_NO_SHARING, 0x011298),
REG(QSYS_EGR_NO_SHARING, 0x01129c),
REG(QSYS_SW_STATUS, 0x0112a0),
REG(QSYS_EXT_CPU_CFG, 0x0112d0),
REG(QSYS_PAD_CFG, 0x0112d4),
REG(QSYS_CPU_GROUP_MAP, 0x0112d8),
REG(QSYS_QMAP, 0x0112dc),
REG(QSYS_ISDX_SGRP, 0x011400),
REG(QSYS_TIMED_FRAME_ENTRY, 0x014000),
REG(QSYS_TFRM_MISC, 0x011310),
REG(QSYS_TFRM_PORT_DLY, 0x011314),
REG(QSYS_TFRM_TIMER_CFG_1, 0x011318),
REG(QSYS_TFRM_TIMER_CFG_2, 0x01131c),
REG(QSYS_TFRM_TIMER_CFG_3, 0x011320),
REG(QSYS_TFRM_TIMER_CFG_4, 0x011324),
REG(QSYS_TFRM_TIMER_CFG_5, 0x011328),
REG(QSYS_TFRM_TIMER_CFG_6, 0x01132c),
REG(QSYS_TFRM_TIMER_CFG_7, 0x011330),
REG(QSYS_TFRM_TIMER_CFG_8, 0x011334),
REG(QSYS_RED_PROFILE, 0x011338),
REG(QSYS_RES_QOS_MODE, 0x011378),
REG(QSYS_RES_CFG, 0x012000),
REG(QSYS_RES_STAT, 0x012004),
REG(QSYS_EGR_DROP_MODE, 0x01137c),
REG(QSYS_EQ_CTRL, 0x011380),
REG(QSYS_EVENTS_CORE, 0x011384),
REG(QSYS_CIR_CFG, 0x000000),
REG(QSYS_EIR_CFG, 0x000004),
REG(QSYS_SE_CFG, 0x000008),
REG(QSYS_SE_DWRR_CFG, 0x00000c),
REG(QSYS_SE_CONNECT, 0x00003c),
REG(QSYS_SE_DLB_SENSE, 0x000040),
REG(QSYS_CIR_STATE, 0x000044),
REG(QSYS_EIR_STATE, 0x000048),
REG(QSYS_SE_STATE, 0x00004c),
REG(QSYS_HSCH_MISC_CFG, 0x011388),
};
static const u32 ocelot_rew_regmap[] = {
REG(REW_PORT_VLAN_CFG, 0x000000),
REG(REW_TAG_CFG, 0x000004),
REG(REW_PORT_CFG, 0x000008),
REG(REW_DSCP_CFG, 0x00000c),
REG(REW_PCP_DEI_QOS_MAP_CFG, 0x000010),
REG(REW_PTP_CFG, 0x000050),
REG(REW_PTP_DLY1_CFG, 0x000054),
REG(REW_DSCP_REMAP_DP1_CFG, 0x000690),
REG(REW_DSCP_REMAP_CFG, 0x000790),
REG(REW_STAT_CFG, 0x000890),
REG(REW_PPT, 0x000680),
};
static const u32 ocelot_sys_regmap[] = {
REG(SYS_COUNT_RX_OCTETS, 0x000000),
REG(SYS_COUNT_RX_UNICAST, 0x000004),
REG(SYS_COUNT_RX_MULTICAST, 0x000008),
REG(SYS_COUNT_RX_BROADCAST, 0x00000c),
REG(SYS_COUNT_RX_SHORTS, 0x000010),
REG(SYS_COUNT_RX_FRAGMENTS, 0x000014),
REG(SYS_COUNT_RX_JABBERS, 0x000018),
REG(SYS_COUNT_RX_CRC_ALIGN_ERRS, 0x00001c),
REG(SYS_COUNT_RX_SYM_ERRS, 0x000020),
REG(SYS_COUNT_RX_64, 0x000024),
REG(SYS_COUNT_RX_65_127, 0x000028),
REG(SYS_COUNT_RX_128_255, 0x00002c),
REG(SYS_COUNT_RX_256_1023, 0x000030),
REG(SYS_COUNT_RX_1024_1526, 0x000034),
REG(SYS_COUNT_RX_1527_MAX, 0x000038),
REG(SYS_COUNT_RX_PAUSE, 0x00003c),
REG(SYS_COUNT_RX_CONTROL, 0x000040),
REG(SYS_COUNT_RX_LONGS, 0x000044),
REG(SYS_COUNT_RX_CLASSIFIED_DROPS, 0x000048),
REG(SYS_COUNT_TX_OCTETS, 0x000100),
REG(SYS_COUNT_TX_UNICAST, 0x000104),
REG(SYS_COUNT_TX_MULTICAST, 0x000108),
REG(SYS_COUNT_TX_BROADCAST, 0x00010c),
REG(SYS_COUNT_TX_COLLISION, 0x000110),
REG(SYS_COUNT_TX_DROPS, 0x000114),
REG(SYS_COUNT_TX_PAUSE, 0x000118),
REG(SYS_COUNT_TX_64, 0x00011c),
REG(SYS_COUNT_TX_65_127, 0x000120),
REG(SYS_COUNT_TX_128_511, 0x000124),
REG(SYS_COUNT_TX_512_1023, 0x000128),
REG(SYS_COUNT_TX_1024_1526, 0x00012c),
REG(SYS_COUNT_TX_1527_MAX, 0x000130),
REG(SYS_COUNT_TX_AGING, 0x000170),
REG(SYS_RESET_CFG, 0x000508),
REG(SYS_CMID, 0x00050c),
REG(SYS_VLAN_ETYPE_CFG, 0x000510),
REG(SYS_PORT_MODE, 0x000514),
REG(SYS_FRONT_PORT_MODE, 0x000548),
REG(SYS_FRM_AGING, 0x000574),
REG(SYS_STAT_CFG, 0x000578),
REG(SYS_SW_STATUS, 0x00057c),
REG(SYS_MISC_CFG, 0x0005ac),
REG(SYS_REW_MAC_HIGH_CFG, 0x0005b0),
REG(SYS_REW_MAC_LOW_CFG, 0x0005dc),
REG(SYS_CM_ADDR, 0x000500),
REG(SYS_CM_DATA, 0x000504),
REG(SYS_PAUSE_CFG, 0x000608),
REG(SYS_PAUSE_TOT_CFG, 0x000638),
REG(SYS_ATOP, 0x00063c),
REG(SYS_ATOP_TOT_CFG, 0x00066c),
REG(SYS_MAC_FC_CFG, 0x000670),
REG(SYS_MMGT, 0x00069c),
REG(SYS_MMGT_FAST, 0x0006a0),
REG(SYS_EVENTS_DIF, 0x0006a4),
REG(SYS_EVENTS_CORE, 0x0006b4),
REG(SYS_CNT, 0x000000),
REG(SYS_PTP_STATUS, 0x0006b8),
REG(SYS_PTP_TXSTAMP, 0x0006bc),
REG(SYS_PTP_NXT, 0x0006c0),
REG(SYS_PTP_CFG, 0x0006c4),
};
static const u32 ocelot_s2_regmap[] = {
REG(S2_CORE_UPDATE_CTRL, 0x000000),
REG(S2_CORE_MV_CFG, 0x000004),
REG(S2_CACHE_ENTRY_DAT, 0x000008),
REG(S2_CACHE_MASK_DAT, 0x000108),
REG(S2_CACHE_ACTION_DAT, 0x000208),
REG(S2_CACHE_CNT_DAT, 0x000308),
REG(S2_CACHE_TG_DAT, 0x000388),
};
static const u32 ocelot_ptp_regmap[] = {
REG(PTP_PIN_CFG, 0x000000),
REG(PTP_PIN_TOD_SEC_MSB, 0x000004),
REG(PTP_PIN_TOD_SEC_LSB, 0x000008),
REG(PTP_PIN_TOD_NSEC, 0x00000c),
REG(PTP_PIN_WF_HIGH_PERIOD, 0x000014),
REG(PTP_PIN_WF_LOW_PERIOD, 0x000018),
REG(PTP_CFG_MISC, 0x0000a0),
REG(PTP_CLK_CFG_ADJ_CFG, 0x0000a4),
REG(PTP_CLK_CFG_ADJ_FREQ, 0x0000a8),
};
static const u32 *ocelot_regmap[] = {
[ANA] = ocelot_ana_regmap,
[QS] = ocelot_qs_regmap,
[QSYS] = ocelot_qsys_regmap,
[REW] = ocelot_rew_regmap,
[SYS] = ocelot_sys_regmap,
[S2] = ocelot_s2_regmap,
[PTP] = ocelot_ptp_regmap,
};
static const struct reg_field ocelot_regfields[] = {
[ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 11, 11),
[ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 10),
[ANA_ANEVENTS_MSTI_DROP] = REG_FIELD(ANA_ANEVENTS, 27, 27),
[ANA_ANEVENTS_ACLKILL] = REG_FIELD(ANA_ANEVENTS, 26, 26),
[ANA_ANEVENTS_ACLUSED] = REG_FIELD(ANA_ANEVENTS, 25, 25),
[ANA_ANEVENTS_AUTOAGE] = REG_FIELD(ANA_ANEVENTS, 24, 24),
[ANA_ANEVENTS_VS2TTL1] = REG_FIELD(ANA_ANEVENTS, 23, 23),
[ANA_ANEVENTS_STORM_DROP] = REG_FIELD(ANA_ANEVENTS, 22, 22),
[ANA_ANEVENTS_LEARN_DROP] = REG_FIELD(ANA_ANEVENTS, 21, 21),
[ANA_ANEVENTS_AGED_ENTRY] = REG_FIELD(ANA_ANEVENTS, 20, 20),
[ANA_ANEVENTS_CPU_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 19, 19),
[ANA_ANEVENTS_AUTO_LEARN_FAILED] = REG_FIELD(ANA_ANEVENTS, 18, 18),
[ANA_ANEVENTS_LEARN_REMOVE] = REG_FIELD(ANA_ANEVENTS, 17, 17),
[ANA_ANEVENTS_AUTO_LEARNED] = REG_FIELD(ANA_ANEVENTS, 16, 16),
[ANA_ANEVENTS_AUTO_MOVED] = REG_FIELD(ANA_ANEVENTS, 15, 15),
[ANA_ANEVENTS_DROPPED] = REG_FIELD(ANA_ANEVENTS, 14, 14),
[ANA_ANEVENTS_CLASSIFIED_DROP] = REG_FIELD(ANA_ANEVENTS, 13, 13),
[ANA_ANEVENTS_CLASSIFIED_COPY] = REG_FIELD(ANA_ANEVENTS, 12, 12),
[ANA_ANEVENTS_VLAN_DISCARD] = REG_FIELD(ANA_ANEVENTS, 11, 11),
[ANA_ANEVENTS_FWD_DISCARD] = REG_FIELD(ANA_ANEVENTS, 10, 10),
[ANA_ANEVENTS_MULTICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 9, 9),
[ANA_ANEVENTS_UNICAST_FLOOD] = REG_FIELD(ANA_ANEVENTS, 8, 8),
[ANA_ANEVENTS_DEST_KNOWN] = REG_FIELD(ANA_ANEVENTS, 7, 7),
[ANA_ANEVENTS_BUCKET3_MATCH] = REG_FIELD(ANA_ANEVENTS, 6, 6),
[ANA_ANEVENTS_BUCKET2_MATCH] = REG_FIELD(ANA_ANEVENTS, 5, 5),
[ANA_ANEVENTS_BUCKET1_MATCH] = REG_FIELD(ANA_ANEVENTS, 4, 4),
[ANA_ANEVENTS_BUCKET0_MATCH] = REG_FIELD(ANA_ANEVENTS, 3, 3),
[ANA_ANEVENTS_CPU_OPERATION] = REG_FIELD(ANA_ANEVENTS, 2, 2),
[ANA_ANEVENTS_DMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 1, 1),
[ANA_ANEVENTS_SMAC_LOOKUP] = REG_FIELD(ANA_ANEVENTS, 0, 0),
[ANA_TABLES_MACACCESS_B_DOM] = REG_FIELD(ANA_TABLES_MACACCESS, 18, 18),
[ANA_TABLES_MACTINDX_BUCKET] = REG_FIELD(ANA_TABLES_MACTINDX, 10, 11),
[ANA_TABLES_MACTINDX_M_INDEX] = REG_FIELD(ANA_TABLES_MACTINDX, 0, 9),
[QSYS_TIMED_FRAME_ENTRY_TFRM_VLD] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 20, 20),
[QSYS_TIMED_FRAME_ENTRY_TFRM_FP] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 8, 19),
[QSYS_TIMED_FRAME_ENTRY_TFRM_PORTNO] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 4, 7),
[QSYS_TIMED_FRAME_ENTRY_TFRM_TM_SEL] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 1, 3),
[QSYS_TIMED_FRAME_ENTRY_TFRM_TM_T] = REG_FIELD(QSYS_TIMED_FRAME_ENTRY, 0, 0),
[SYS_RESET_CFG_CORE_ENA] = REG_FIELD(SYS_RESET_CFG, 2, 2),
[SYS_RESET_CFG_MEM_ENA] = REG_FIELD(SYS_RESET_CFG, 1, 1),
[SYS_RESET_CFG_MEM_INIT] = REG_FIELD(SYS_RESET_CFG, 0, 0),
};
static const struct ocelot_stat_layout ocelot_stats_layout[] = {
{ .name = "rx_octets", .offset = 0x00, },
{ .name = "rx_unicast", .offset = 0x01, },
{ .name = "rx_multicast", .offset = 0x02, },
{ .name = "rx_broadcast", .offset = 0x03, },
{ .name = "rx_shorts", .offset = 0x04, },
{ .name = "rx_fragments", .offset = 0x05, },
{ .name = "rx_jabbers", .offset = 0x06, },
{ .name = "rx_crc_align_errs", .offset = 0x07, },
{ .name = "rx_sym_errs", .offset = 0x08, },
{ .name = "rx_frames_below_65_octets", .offset = 0x09, },
{ .name = "rx_frames_65_to_127_octets", .offset = 0x0A, },
{ .name = "rx_frames_128_to_255_octets", .offset = 0x0B, },
{ .name = "rx_frames_256_to_511_octets", .offset = 0x0C, },
{ .name = "rx_frames_512_to_1023_octets", .offset = 0x0D, },
{ .name = "rx_frames_1024_to_1526_octets", .offset = 0x0E, },
{ .name = "rx_frames_over_1526_octets", .offset = 0x0F, },
{ .name = "rx_pause", .offset = 0x10, },
{ .name = "rx_control", .offset = 0x11, },
{ .name = "rx_longs", .offset = 0x12, },
{ .name = "rx_classified_drops", .offset = 0x13, },
{ .name = "rx_red_prio_0", .offset = 0x14, },
{ .name = "rx_red_prio_1", .offset = 0x15, },
{ .name = "rx_red_prio_2", .offset = 0x16, },
{ .name = "rx_red_prio_3", .offset = 0x17, },
{ .name = "rx_red_prio_4", .offset = 0x18, },
{ .name = "rx_red_prio_5", .offset = 0x19, },
{ .name = "rx_red_prio_6", .offset = 0x1A, },
{ .name = "rx_red_prio_7", .offset = 0x1B, },
{ .name = "rx_yellow_prio_0", .offset = 0x1C, },
{ .name = "rx_yellow_prio_1", .offset = 0x1D, },
{ .name = "rx_yellow_prio_2", .offset = 0x1E, },
{ .name = "rx_yellow_prio_3", .offset = 0x1F, },
{ .name = "rx_yellow_prio_4", .offset = 0x20, },
{ .name = "rx_yellow_prio_5", .offset = 0x21, },
{ .name = "rx_yellow_prio_6", .offset = 0x22, },
{ .name = "rx_yellow_prio_7", .offset = 0x23, },
{ .name = "rx_green_prio_0", .offset = 0x24, },
{ .name = "rx_green_prio_1", .offset = 0x25, },
{ .name = "rx_green_prio_2", .offset = 0x26, },
{ .name = "rx_green_prio_3", .offset = 0x27, },
{ .name = "rx_green_prio_4", .offset = 0x28, },
{ .name = "rx_green_prio_5", .offset = 0x29, },
{ .name = "rx_green_prio_6", .offset = 0x2A, },
{ .name = "rx_green_prio_7", .offset = 0x2B, },
{ .name = "tx_octets", .offset = 0x40, },
{ .name = "tx_unicast", .offset = 0x41, },
{ .name = "tx_multicast", .offset = 0x42, },
{ .name = "tx_broadcast", .offset = 0x43, },
{ .name = "tx_collision", .offset = 0x44, },
{ .name = "tx_drops", .offset = 0x45, },
{ .name = "tx_pause", .offset = 0x46, },
{ .name = "tx_frames_below_65_octets", .offset = 0x47, },
{ .name = "tx_frames_65_to_127_octets", .offset = 0x48, },
{ .name = "tx_frames_128_255_octets", .offset = 0x49, },
{ .name = "tx_frames_256_511_octets", .offset = 0x4A, },
{ .name = "tx_frames_512_1023_octets", .offset = 0x4B, },
{ .name = "tx_frames_1024_1526_octets", .offset = 0x4C, },
{ .name = "tx_frames_over_1526_octets", .offset = 0x4D, },
{ .name = "tx_yellow_prio_0", .offset = 0x4E, },
{ .name = "tx_yellow_prio_1", .offset = 0x4F, },
{ .name = "tx_yellow_prio_2", .offset = 0x50, },
{ .name = "tx_yellow_prio_3", .offset = 0x51, },
{ .name = "tx_yellow_prio_4", .offset = 0x52, },
{ .name = "tx_yellow_prio_5", .offset = 0x53, },
{ .name = "tx_yellow_prio_6", .offset = 0x54, },
{ .name = "tx_yellow_prio_7", .offset = 0x55, },
{ .name = "tx_green_prio_0", .offset = 0x56, },
{ .name = "tx_green_prio_1", .offset = 0x57, },
{ .name = "tx_green_prio_2", .offset = 0x58, },
{ .name = "tx_green_prio_3", .offset = 0x59, },
{ .name = "tx_green_prio_4", .offset = 0x5A, },
{ .name = "tx_green_prio_5", .offset = 0x5B, },
{ .name = "tx_green_prio_6", .offset = 0x5C, },
{ .name = "tx_green_prio_7", .offset = 0x5D, },
{ .name = "tx_aged", .offset = 0x5E, },
{ .name = "drop_local", .offset = 0x80, },
{ .name = "drop_tail", .offset = 0x81, },
{ .name = "drop_yellow_prio_0", .offset = 0x82, },
{ .name = "drop_yellow_prio_1", .offset = 0x83, },
{ .name = "drop_yellow_prio_2", .offset = 0x84, },
{ .name = "drop_yellow_prio_3", .offset = 0x85, },
{ .name = "drop_yellow_prio_4", .offset = 0x86, },
{ .name = "drop_yellow_prio_5", .offset = 0x87, },
{ .name = "drop_yellow_prio_6", .offset = 0x88, },
{ .name = "drop_yellow_prio_7", .offset = 0x89, },
{ .name = "drop_green_prio_0", .offset = 0x8A, },
{ .name = "drop_green_prio_1", .offset = 0x8B, },
{ .name = "drop_green_prio_2", .offset = 0x8C, },
{ .name = "drop_green_prio_3", .offset = 0x8D, },
{ .name = "drop_green_prio_4", .offset = 0x8E, },
{ .name = "drop_green_prio_5", .offset = 0x8F, },
{ .name = "drop_green_prio_6", .offset = 0x90, },
{ .name = "drop_green_prio_7", .offset = 0x91, },
};
static void ocelot_pll5_init(struct ocelot *ocelot)
{
/* Configure PLL5. This will need a proper CCF driver
* The values are coming from the VTSS API for Ocelot
*/
regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG4,
HSIO_PLL5G_CFG4_IB_CTRL(0x7600) |
HSIO_PLL5G_CFG4_IB_BIAS_CTRL(0x8));
regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG0,
HSIO_PLL5G_CFG0_CORE_CLK_DIV(0x11) |
HSIO_PLL5G_CFG0_CPU_CLK_DIV(2) |
HSIO_PLL5G_CFG0_ENA_BIAS |
HSIO_PLL5G_CFG0_ENA_VCO_BUF |
HSIO_PLL5G_CFG0_ENA_CP1 |
HSIO_PLL5G_CFG0_SELCPI(2) |
HSIO_PLL5G_CFG0_LOOP_BW_RES(0xe) |
HSIO_PLL5G_CFG0_SELBGV820(4) |
HSIO_PLL5G_CFG0_DIV4 |
HSIO_PLL5G_CFG0_ENA_CLKTREE |
HSIO_PLL5G_CFG0_ENA_LANE);
regmap_write(ocelot->targets[HSIO], HSIO_PLL5G_CFG2,
HSIO_PLL5G_CFG2_EN_RESET_FRQ_DET |
HSIO_PLL5G_CFG2_EN_RESET_OVERRUN |
HSIO_PLL5G_CFG2_GAIN_TEST(0x8) |
HSIO_PLL5G_CFG2_ENA_AMPCTRL |
HSIO_PLL5G_CFG2_PWD_AMPCTRL_N |
HSIO_PLL5G_CFG2_AMPC_SEL(0x10));
}
static int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops)
{
int ret;
ocelot->map = ocelot_regmap;
ocelot->stats_layout = ocelot_stats_layout;
ocelot->num_stats = ARRAY_SIZE(ocelot_stats_layout);
ocelot->shared_queue_sz = 224 * 1024;
ocelot->num_mact_rows = 1024;
ocelot->ops = ops;
ret = ocelot_regfields_init(ocelot, ocelot_regfields);
if (ret)
return ret;
ocelot_pll5_init(ocelot);
eth_random_addr(ocelot->base_mac);
ocelot->base_mac[5] &= 0xf0;
return 0;
}
static int ocelot_parse_ifh(u32 *_ifh, struct frame_info *info) static int ocelot_parse_ifh(u32 *_ifh, struct frame_info *info)
{ {
u8 llen, wlen; u8 llen, wlen;
......
...@@ -470,7 +470,7 @@ struct ocelot_ops { ...@@ -470,7 +470,7 @@ struct ocelot_ops {
int (*reset)(struct ocelot *ocelot); int (*reset)(struct ocelot *ocelot);
}; };
struct ocelot_acl_block { struct ocelot_vcap_block {
struct list_head rules; struct list_head rules;
int count; int count;
int pol_lpr; int pol_lpr;
...@@ -535,7 +535,7 @@ struct ocelot { ...@@ -535,7 +535,7 @@ struct ocelot {
struct list_head multicast; struct list_head multicast;
struct ocelot_acl_block acl_block; struct ocelot_vcap_block block;
const struct vcap_field *vcap_is2_keys; const struct vcap_field *vcap_is2_keys;
const struct vcap_field *vcap_is2_actions; const struct vcap_field *vcap_is2_actions;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment