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
depends on NET_DSA && PCI
depends on NET_VENDOR_MICROSEMI
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 FSL_ENETC_MDIO
help
......
......@@ -557,7 +557,7 @@ static const struct ocelot_stat_layout vsc9959_stats_layout[] = {
{ .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 */
[VCAP_IS2_TYPE] = { 0, 4},
[VCAP_IS2_HK_FIRST] = { 4, 1},
......@@ -637,7 +637,7 @@ struct vcap_field vsc9959_vcap_is2_keys[] = {
[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_CPU_COPY_ENA] = { 1, 1},
[VCAP_IS2_ACT_CPU_QU_NUM] = { 2, 3},
......
......@@ -11,22 +11,24 @@ config 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
tristate "Ocelot switch driver"
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 REGMAP_MMIO
depends on HAS_IOMEM
depends on PHYLIB
depends on OF_NET
select MSCC_OCELOT_SWITCH_LIB
help
This driver supports the Ocelot network switch device as present on
the Ocelot SoCs.
the Ocelot SoCs (VSC7514).
endif # NET_VENDOR_MICROSEMI
# SPDX-License-Identifier: (GPL-2.0 OR MIT)
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot_common.o
mscc_ocelot_common-y := ocelot.o ocelot_io.o
mscc_ocelot_common-y += ocelot_regs.o ocelot_tc.o ocelot_police.o ocelot_ace.o ocelot_flower.o ocelot_ptp.o
obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += ocelot_board.o
obj-$(CONFIG_MSCC_OCELOT_SWITCH_LIB) += mscc_ocelot_switch_lib.o
mscc_ocelot_switch_lib-y := \
ocelot.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 @@
*
* Copyright (c) 2017 Microsemi Corporation
*/
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/if_bridge.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#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_ace.h"
#include "ocelot_vcap.h"
#define TABLE_UPDATE_SLEEP_US 10
#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 {
u8 mac[ETH_ALEN];
u16 vid;
......@@ -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],
unsigned int vid,
enum macaccess_entry_type type)
unsigned int vid, enum macaccess_entry_type type)
{
ocelot_mact_select(ocelot, mac, vid);
......@@ -100,10 +70,10 @@ static int ocelot_mact_learn(struct ocelot *ocelot, int port,
return ocelot_mact_wait_for_completion(ocelot);
}
EXPORT_SYMBOL(ocelot_mact_learn);
static int ocelot_mact_forget(struct ocelot *ocelot,
const unsigned char mac[ETH_ALEN],
unsigned int vid)
int ocelot_mact_forget(struct ocelot *ocelot,
const unsigned char mac[ETH_ALEN], unsigned int vid)
{
ocelot_mact_select(ocelot, mac, vid);
......@@ -114,6 +84,7 @@ static int ocelot_mact_forget(struct ocelot *ocelot,
return ocelot_mact_wait_for_completion(ocelot);
}
EXPORT_SYMBOL(ocelot_mact_forget);
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)
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,
u16 vid)
{
......@@ -295,26 +252,6 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
}
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)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
......@@ -338,30 +275,6 @@ int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid)
}
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)
{
u16 port, vid;
......@@ -492,15 +405,6 @@ void ocelot_adjust_link(struct ocelot *ocelot, int port,
}
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,
struct phy_device *phy)
{
......@@ -514,40 +418,6 @@ void ocelot_port_enable(struct ocelot *ocelot, int port,
}
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)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
......@@ -558,41 +428,6 @@ void ocelot_port_disable(struct ocelot *ocelot, int port)
}
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,
struct sk_buff *skb)
{
......@@ -611,77 +446,6 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
}
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,
struct timespec64 *ts)
{
......@@ -767,113 +531,6 @@ void ocelot_get_txtstamp(struct ocelot *ocelot)
}
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,
const unsigned char *addr, u16 vid)
{
......@@ -897,19 +554,6 @@ int ocelot_fdb_add(struct ocelot *ocelot, int port,
}
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,
const unsigned char *addr, u16 vid)
{
......@@ -917,25 +561,7 @@ int ocelot_fdb_del(struct ocelot *ocelot, int port,
}
EXPORT_SYMBOL(ocelot_fdb_del);
static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot *ocelot = priv->port.ocelot;
int port = priv->chip_port;
return ocelot_fdb_del(ocelot, port, addr, vid);
}
struct ocelot_dump_ctx {
struct 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,
int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
bool is_static, void *data)
{
struct ocelot_dump_ctx *dump = data;
......@@ -977,6 +603,7 @@ static int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
nlmsg_cancel(dump->skb, nlh);
return -EMSGSIZE;
}
EXPORT_SYMBOL(ocelot_port_fdb_do_dump);
static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col,
struct ocelot_mact_entry *entry)
......@@ -1058,74 +685,6 @@ int ocelot_fdb_dump(struct ocelot *ocelot, int port,
}
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)
{
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)
}
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)
{
int i;
......@@ -1251,16 +770,6 @@ void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data)
}
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)
{
int i, j;
......@@ -1314,17 +823,6 @@ void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data)
}
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)
{
if (sset != ETH_SS_STATS)
......@@ -1334,15 +832,6 @@ int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
}
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,
struct ethtool_ts_info *info)
{
......@@ -1368,28 +857,6 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,
}
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)
{
u32 port_cfg;
......@@ -1445,16 +912,6 @@ void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
}
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)
{
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)
}
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,
const unsigned char *addr,
u16 vid)
......@@ -1572,7 +940,7 @@ static struct ocelot_multicast *ocelot_multicast_get(struct ocelot *ocelot,
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,
struct switchdev_trans *trans)
{
......@@ -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);
}
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)
{
struct ocelot_port_private *priv = netdev_priv(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);
}
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;
}
EXPORT_SYMBOL(ocelot_port_obj_del_mdb);
int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
struct net_device *bridge)
......@@ -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 *ndev;
......@@ -1826,8 +1152,9 @@ static int ocelot_port_lag_join(struct ocelot *ocelot, int port,
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)
{
u32 port_cfg;
......@@ -1856,151 +1183,7 @@ static void ocelot_port_lag_leave(struct ocelot *ocelot, int port,
ocelot_set_aggr_pgids(ocelot);
}
/* 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);
EXPORT_SYMBOL(ocelot_port_lag_leave);
/* 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.
......@@ -2109,52 +1292,6 @@ void ocelot_init_port(struct ocelot *ocelot, int 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.
* 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
......@@ -2255,7 +1392,7 @@ int ocelot_init(struct ocelot *ocelot)
INIT_LIST_HEAD(&ocelot->multicast);
ocelot_mact_init(ocelot);
ocelot_vlan_init(ocelot);
ocelot_ace_init(ocelot);
ocelot_vcap_init(ocelot);
for (port = 0; port < ocelot->num_phys_ports; port++) {
/* Clear all counters (5 groups) */
......
......@@ -25,7 +25,6 @@
#include <soc/mscc/ocelot.h>
#include "ocelot_rew.h"
#include "ocelot_qs.h"
#include "ocelot_tc.h"
#define OCELOT_BUFFER_CELL_SZ 60
......@@ -49,6 +48,13 @@ struct ocelot_multicast {
u16 ports;
};
struct ocelot_port_tc {
bool block_shared;
unsigned long offload_cnt;
unsigned long police_id;
};
struct ocelot_port_private {
struct ocelot_port port;
struct net_device *dev;
......@@ -60,13 +66,49 @@ struct ocelot_port_private {
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);
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_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,
void __iomem *regs,
struct phy_device *phy);
......
......@@ -6,10 +6,10 @@
#include <net/pkt_cls.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,
struct ocelot_ace_rule *ace)
struct ocelot_vcap_filter *filter)
{
const struct flow_action_entry *a;
s64 burst;
......@@ -26,17 +26,17 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f,
flow_action_for_each(i, a, &f->rule->action) {
switch (a->id) {
case FLOW_ACTION_DROP:
ace->action = OCELOT_ACL_ACTION_DROP;
filter->action = OCELOT_VCAP_ACTION_DROP;
break;
case FLOW_ACTION_TRAP:
ace->action = OCELOT_ACL_ACTION_TRAP;
filter->action = OCELOT_VCAP_ACTION_TRAP;
break;
case FLOW_ACTION_POLICE:
ace->action = OCELOT_ACL_ACTION_POLICE;
filter->action = OCELOT_VCAP_ACTION_POLICE;
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);
ace->pol.burst = div_u64(burst, PSCHED_TICKS_PER_SEC);
filter->pol.burst = div_u64(burst, PSCHED_TICKS_PER_SEC);
break;
default:
return -EOPNOTSUPP;
......@@ -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,
struct ocelot_ace_rule *ace)
struct ocelot_vcap_filter *filter)
{
struct flow_rule *rule = flow_cls_offload_flow_rule(f);
struct flow_dissector *dissector = rule->match.dissector;
......@@ -88,14 +88,14 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
return -EOPNOTSUPP;
flow_rule_match_eth_addrs(rule, &match);
ace->type = OCELOT_ACE_TYPE_ETYPE;
ether_addr_copy(ace->frame.etype.dmac.value,
filter->key_type = OCELOT_VCAP_KEY_ETYPE;
ether_addr_copy(filter->key.etype.dmac.value,
match.key->dst);
ether_addr_copy(ace->frame.etype.smac.value,
ether_addr_copy(filter->key.etype.smac.value,
match.key->src);
ether_addr_copy(ace->frame.etype.dmac.mask,
ether_addr_copy(filter->key.etype.dmac.mask,
match.mask->dst);
ether_addr_copy(ace->frame.etype.smac.mask,
ether_addr_copy(filter->key.etype.smac.mask,
match.mask->src);
goto finished_key_parsing;
}
......@@ -105,18 +105,18 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
flow_rule_match_basic(rule, &match);
if (ntohs(match.key->n_proto) == ETH_P_IP) {
ace->type = OCELOT_ACE_TYPE_IPV4;
ace->frame.ipv4.proto.value[0] =
filter->key_type = OCELOT_VCAP_KEY_IPV4;
filter->key.ipv4.proto.value[0] =
match.key->ip_proto;
ace->frame.ipv4.proto.mask[0] =
filter->key.ipv4.proto.mask[0] =
match.mask->ip_proto;
match_protocol = false;
}
if (ntohs(match.key->n_proto) == ETH_P_IPV6) {
ace->type = OCELOT_ACE_TYPE_IPV6;
ace->frame.ipv6.proto.value[0] =
filter->key_type = OCELOT_VCAP_KEY_IPV6;
filter->key.ipv6.proto.value[0] =
match.key->ip_proto;
ace->frame.ipv6.proto.mask[0] =
filter->key.ipv6.proto.mask[0] =
match.mask->ip_proto;
match_protocol = false;
}
......@@ -128,16 +128,16 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
u8 *tmp;
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);
tmp = &ace->frame.ipv4.sip.mask.addr[0];
tmp = &filter->key.ipv4.sip.mask.addr[0];
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);
tmp = &ace->frame.ipv4.dip.mask.addr[0];
tmp = &filter->key.ipv4.dip.mask.addr[0];
memcpy(tmp, &match.mask->dst, 4);
match_protocol = false;
}
......@@ -151,10 +151,10 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
struct flow_match_ports match;
flow_rule_match_ports(rule, &match);
ace->frame.ipv4.sport.value = ntohs(match.key->src);
ace->frame.ipv4.sport.mask = ntohs(match.mask->src);
ace->frame.ipv4.dport.value = ntohs(match.key->dst);
ace->frame.ipv4.dport.mask = ntohs(match.mask->dst);
filter->key.ipv4.sport.value = ntohs(match.key->src);
filter->key.ipv4.sport.mask = ntohs(match.mask->src);
filter->key.ipv4.dport.value = ntohs(match.key->dst);
filter->key.ipv4.dport.mask = ntohs(match.mask->dst);
match_protocol = false;
}
......@@ -162,11 +162,11 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
struct flow_match_vlan match;
flow_rule_match_vlan(rule, &match);
ace->type = OCELOT_ACE_TYPE_ANY;
ace->vlan.vid.value = match.key->vlan_id;
ace->vlan.vid.mask = match.mask->vlan_id;
ace->vlan.pcp.value[0] = match.key->vlan_priority;
ace->vlan.pcp.mask[0] = match.mask->vlan_priority;
filter->key_type = OCELOT_VCAP_KEY_ANY;
filter->vlan.vid.value = match.key->vlan_id;
filter->vlan.vid.mask = match.mask->vlan_id;
filter->vlan.pcp.value[0] = match.key->vlan_priority;
filter->vlan.pcp.mask[0] = match.mask->vlan_priority;
match_protocol = false;
}
......@@ -175,99 +175,77 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
/* TODO: support SNAP, LLC etc */
if (proto < ETH_P_802_3_MIN)
return -EOPNOTSUPP;
ace->type = OCELOT_ACE_TYPE_ETYPE;
*(u16 *)ace->frame.etype.etype.value = htons(proto);
*(u16 *)ace->frame.etype.etype.mask = 0xffff;
filter->key_type = OCELOT_VCAP_KEY_ETYPE;
*(__be16 *)filter->key.etype.etype.value = htons(proto);
*(__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;
ace->id = f->cookie;
return ocelot_flower_parse_action(f, ace);
filter->prio = f->common.prio;
filter->id = f->cookie;
return ocelot_flower_parse_action(f, filter);
}
static
struct ocelot_ace_rule *ocelot_ace_rule_create(struct ocelot *ocelot, int port,
static struct ocelot_vcap_filter
*ocelot_vcap_filter_create(struct ocelot *ocelot, int port,
struct flow_cls_offload *f)
{
struct ocelot_ace_rule *ace;
struct ocelot_vcap_filter *filter;
ace = kzalloc(sizeof(*ace), GFP_KERNEL);
if (!ace)
filter = kzalloc(sizeof(*filter), GFP_KERNEL);
if (!filter)
return NULL;
ace->ingress_port_mask = BIT(port);
return ace;
filter->ingress_port_mask = BIT(port);
return filter;
}
int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
struct flow_cls_offload *f, bool ingress)
{
struct ocelot_ace_rule *ace;
struct ocelot_vcap_filter *filter;
int ret;
ace = ocelot_ace_rule_create(ocelot, port, f);
if (!ace)
filter = ocelot_vcap_filter_create(ocelot, port, f);
if (!filter)
return -ENOMEM;
ret = ocelot_flower_parse(f, ace);
ret = ocelot_flower_parse(f, filter);
if (ret) {
kfree(ace);
kfree(filter);
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);
int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
struct flow_cls_offload *f, bool ingress)
{
struct ocelot_ace_rule ace;
struct ocelot_vcap_filter filter;
ace.prio = f->common.prio;
ace.id = f->cookie;
filter.prio = f->common.prio;
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);
int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
struct flow_cls_offload *f, bool ingress)
{
struct ocelot_ace_rule ace;
struct ocelot_vcap_filter filter;
int ret;
ace.prio = f->common.prio;
ace.id = f->cookie;
ret = ocelot_ace_rule_stats_update(ocelot, &ace);
filter.prio = f->common.prio;
filter.id = f->cookie;
ret = ocelot_vcap_filter_stats_update(ocelot, &filter);
if (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);
return 0;
}
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 @@
#include <soc/mscc/ocelot.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 */
#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 */
......@@ -30,18 +20,7 @@ enum mscc_qos_rate_mode {
/* Default policer order */
#define POL_ORDER 0x1d3 /* Ocelot policer order: Serial (QoS -> Port -> VCAP) */
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 */
};
static int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix,
int qos_policer_conf_set(struct ocelot *ocelot, int port, u32 pol_ix,
struct qos_policer_conf *conf)
{
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)
return 0;
}
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 @@
#include "ocelot.h"
int ocelot_ace_policer_add(struct ocelot *ocelot, u32 pol_ix,
struct ocelot_policer *pol);
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,
};
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_ */
// 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 @@
#include <soc/mscc/ocelot_vcap.h>
#include "ocelot_police.h"
#include "ocelot_ace.h"
#include "ocelot_vcap.h"
#include "ocelot_s2.h"
#define OCELOT_POLICER_DISCARD 0x17f
......@@ -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)
{
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 */
width = vcap_is2->action_type_width;
......@@ -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)
{
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);
......@@ -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)
{
const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2];
u32 i, col, offset, count, cnt, base;
u32 width = vcap_is2->tg_width;
int i, col, offset, count, cnt, base;
int width = vcap_is2->tg_width;
count = (data->tg_sw == VCAP_TG_HALF ? 2 : 4);
col = (ix % 2);
......@@ -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,
struct ocelot_ace_rule *ace)
struct ocelot_vcap_filter *filter)
{
switch (ace->action) {
case OCELOT_ACL_ACTION_DROP:
switch (filter->action) {
case OCELOT_VCAP_ACTION_DROP:
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_POLICE_ENA, 1);
......@@ -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_COPY_ENA, 0);
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_MASK_MODE, 1);
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,
vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_QU_NUM, 0);
vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_COPY_ENA, 1);
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_MASK_MODE, 0);
vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_ENA, 1);
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_COPY_ENA, 0);
break;
......@@ -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,
struct ocelot_ace_rule *ace)
struct ocelot_vcap_filter *filter)
{
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;
struct ocelot_ace_vlan *tag = &ace->vlan;
struct ocelot_vcap_u64 payload;
struct vcap_data data;
int row = (ix / 2);
......@@ -353,19 +355,19 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
data.tg_sw = VCAP_TG_HALF;
is2_data_get(ocelot, &data, ix);
data.tg = (data.tg & ~data.tg_mask);
if (ace->prio != 0)
if (filter->prio != 0)
data.tg |= data.tg_value;
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_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_HOST_MATCH,
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_BC, ace->dmac_bc);
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, filter->dmac_bc);
vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_VLAN_TAGGED, tag->tagged);
vcap_key_set(ocelot, &data, VCAP_IS2_HK_VID,
tag->vid.value, tag->vid.mask);
......@@ -373,9 +375,9 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
tag->pcp.value[0], tag->pcp.mask[0]);
vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_DEI, tag->dei);
switch (ace->type) {
case OCELOT_ACE_TYPE_ETYPE: {
struct ocelot_ace_frame_etype *etype = &ace->frame.etype;
switch (filter->key_type) {
case OCELOT_VCAP_KEY_ETYPE: {
struct ocelot_vcap_key_etype *etype = &filter->key.etype;
type = IS2_TYPE_ETYPE;
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,
etype->data.value, etype->data.mask);
break;
}
case OCELOT_ACE_TYPE_LLC: {
struct ocelot_ace_frame_llc *llc = &ace->frame.llc;
case OCELOT_VCAP_KEY_LLC: {
struct ocelot_vcap_key_llc *llc = &filter->key.llc;
type = IS2_TYPE_LLC;
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,
payload.value, payload.mask);
break;
}
case OCELOT_ACE_TYPE_SNAP: {
struct ocelot_ace_frame_snap *snap = &ace->frame.snap;
case OCELOT_VCAP_KEY_SNAP: {
struct ocelot_vcap_key_snap *snap = &filter->key.snap;
type = IS2_TYPE_SNAP;
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,
vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_SMAC,
snap->smac.value, snap->smac.mask);
vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_MAC_SNAP_L2_SNAP,
ace->frame.snap.snap.value,
ace->frame.snap.snap.mask);
filter->key.snap.snap.value,
filter->key.snap.snap.mask);
break;
}
case OCELOT_ACE_TYPE_ARP: {
struct ocelot_ace_frame_arp *arp = &ace->frame.arp;
case OCELOT_VCAP_KEY_ARP: {
struct ocelot_vcap_key_arp *arp = &filter->key.arp;
type = IS2_TYPE_ARP;
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,
0, 0);
break;
}
case OCELOT_ACE_TYPE_IPV4:
case OCELOT_ACE_TYPE_IPV6: {
case OCELOT_VCAP_KEY_IPV4:
case OCELOT_VCAP_KEY_IPV6: {
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 tcp_fin, tcp_syn, tcp_rst, tcp_psh;
struct ocelot_ace_frame_ipv4 *ipv4 = NULL;
struct ocelot_ace_frame_ipv6 *ipv6 = NULL;
struct ocelot_vcap_key_ipv4 *ipv4 = NULL;
struct ocelot_vcap_key_ipv6 *ipv6 = NULL;
struct ocelot_vcap_udp_tcp *sport, *dport;
struct ocelot_vcap_ipv4 sip, dip;
struct ocelot_vcap_u8 proto, ds;
struct ocelot_vcap_u48 *ip_data;
if (ace->type == OCELOT_ACE_TYPE_IPV4) {
ipv4 = &ace->frame.ipv4;
if (filter->key_type == OCELOT_VCAP_KEY_IPV4) {
ipv4 = &filter->key.ipv4;
ttl = ipv4->ttl;
fragment = ipv4->fragment;
options = ipv4->options;
......@@ -501,7 +503,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
sport_eq_dport = ipv4->sport_eq_dport;
seq_zero = ipv4->seq_zero;
} else {
ipv6 = &ace->frame.ipv6;
ipv6 = &filter->key.ipv6;
ttl = ipv6->ttl;
fragment = OCELOT_VCAP_BIT_ANY;
options = OCELOT_VCAP_BIT_ANY;
......@@ -605,7 +607,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix,
}
break;
}
case OCELOT_ACE_TYPE_ANY:
case OCELOT_VCAP_KEY_ANY:
default:
type = 0;
type_mask = 0;
......@@ -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);
is2_action_set(ocelot, &data, ace);
is2_action_set(ocelot, &data, filter);
vcap_data_set(data.counter, data.counter_offset,
vcap_is2->counter_width, ace->stats.pkts);
vcap_is2->counter_width, filter->stats.pkts);
/* Write row */
vcap_entry2cache(ocelot, &data);
......@@ -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);
}
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)
{
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,
cnt = vcap_data_get(data.counter, data.counter_offset,
vcap_is2->counter_width);
rule->stats.pkts = cnt;
filter->stats.pkts = cnt;
}
static void ocelot_ace_rule_add(struct ocelot *ocelot,
struct ocelot_acl_block *block,
struct ocelot_ace_rule *rule)
static int ocelot_vcap_policer_add(struct ocelot *ocelot, u32 pol_ix,
struct ocelot_policer *pol)
{
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;
if (rule->action == OCELOT_ACL_ACTION_POLICE) {
if (filter->action == OCELOT_VCAP_ACTION_POLICE) {
block->pol_lpr--;
rule->pol_ix = block->pol_lpr;
ocelot_ace_policer_add(ocelot, rule->pol_ix, &rule->pol);
filter->pol_ix = block->pol_lpr;
ocelot_vcap_policer_add(ocelot, filter->pol_ix, &filter->pol);
}
block->count++;
if (list_empty(&block->rules)) {
list_add(&rule->list, &block->rules);
list_add(&filter->list, &block->rules);
return;
}
list_for_each_safe(pos, n, &block->rules) {
tmp = list_entry(pos, struct ocelot_ace_rule, list);
if (rule->prio < tmp->prio)
tmp = list_entry(pos, struct ocelot_vcap_filter, list);
if (filter->prio < tmp->prio)
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,
struct ocelot_ace_rule *rule)
static int ocelot_vcap_block_get_filter_index(struct ocelot_vcap_block *block,
struct ocelot_vcap_filter *filter)
{
struct ocelot_ace_rule *tmp;
struct ocelot_vcap_filter *tmp;
int index = -1;
list_for_each_entry(tmp, &block->rules, list) {
++index;
if (rule->id == tmp->id)
if (filter->id == tmp->id)
break;
}
return index;
}
static struct ocelot_ace_rule*
ocelot_ace_rule_get_rule_index(struct ocelot_acl_block *block, int index)
static struct ocelot_vcap_filter*
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;
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,
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;
if (ace->type != OCELOT_ACE_TYPE_ETYPE)
if (filter->key_type != OCELOT_VCAP_KEY_ETYPE)
return false;
proto = ntohs(*(u16 *)ace->frame.etype.etype.value);
mask = ntohs(*(u16 *)ace->frame.etype.etype.mask);
proto = ntohs(*(__be16 *)filter->key.etype.etype.value);
mask = ntohs(*(__be16 *)filter->key.etype.etype.mask);
/* ETH_P_ALL match, so all protocols below are included */
if (mask == 0)
......@@ -760,49 +807,51 @@ static bool ocelot_ace_is_problematic_mac_etype(struct ocelot_ace_rule *ace)
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;
if (ace->type == OCELOT_ACE_TYPE_ARP)
if (filter->key_type == OCELOT_VCAP_KEY_ARP)
return true;
if (ace->type == OCELOT_ACE_TYPE_IPV4)
if (filter->key_type == OCELOT_VCAP_KEY_IPV4)
return true;
if (ace->type == OCELOT_ACE_TYPE_IPV6)
if (filter->key_type == OCELOT_VCAP_KEY_IPV6)
return true;
return false;
}
static bool ocelot_exclusive_mac_etype_ace_rules(struct ocelot *ocelot,
struct ocelot_ace_rule *ace)
static bool
ocelot_exclusive_mac_etype_filter_rules(struct ocelot *ocelot,
struct ocelot_vcap_filter *filter)
{
struct ocelot_acl_block *block = &ocelot->acl_block;
struct ocelot_ace_rule *tmp;
struct ocelot_vcap_block *block = &ocelot->block;
struct ocelot_vcap_filter *tmp;
unsigned long port;
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 */
for (i = 0; i < block->count; i++) {
tmp = ocelot_ace_rule_get_rule_index(block, i);
if (tmp->ingress_port_mask & ace->ingress_port_mask &&
ocelot_ace_is_problematic_non_mac_etype(tmp))
tmp = ocelot_vcap_block_find_filter(block, i);
if (tmp->ingress_port_mask & filter->ingress_port_mask &&
ocelot_vcap_is_problematic_non_mac_etype(tmp))
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_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 */
for (i = 0; i < block->count; i++) {
tmp = ocelot_ace_rule_get_rule_index(block, i);
if (tmp->ingress_port_mask & ace->ingress_port_mask &&
ocelot_ace_is_problematic_mac_etype(tmp))
tmp = ocelot_vcap_block_find_filter(block, i);
if (tmp->ingress_port_mask & filter->ingress_port_mask &&
ocelot_vcap_is_problematic_mac_etype(tmp))
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_match_all_as_mac_etype(ocelot, port, false);
}
......@@ -810,74 +859,50 @@ static bool ocelot_exclusive_mac_etype_ace_rules(struct ocelot *ocelot,
return true;
}
int ocelot_ace_rule_offload_add(struct ocelot *ocelot,
struct ocelot_ace_rule *rule,
int ocelot_vcap_filter_add(struct ocelot *ocelot,
struct ocelot_vcap_filter *filter,
struct netlink_ext_ack *extack)
{
struct ocelot_acl_block *block = &ocelot->acl_block;
struct ocelot_ace_rule *ace;
struct ocelot_vcap_block *block = &ocelot->block;
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,
"Cannot mix MAC_ETYPE with non-MAC_ETYPE rules");
return -EBUSY;
}
/* Add rule to the linked list */
ocelot_ace_rule_add(ocelot, block, rule);
/* Add filter to the linked list */
ocelot_vcap_filter_add_to_block(ocelot, block, filter);
/* Get the index of the inserted rule */
index = ocelot_ace_rule_get_index_id(block, rule);
/* Get the index of the inserted filter */
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--) {
ace = ocelot_ace_rule_get_rule_index(block, i);
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;
struct ocelot_vcap_filter *tmp;
list_for_each_entry(ace, &block->rules, list) {
index++;
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);
}
tmp = ocelot_vcap_block_find_filter(block, i);
is2_entry_set(ocelot, i, tmp);
}
ocelot_ace_policer_del(ocelot, block->pol_lpr);
block->pol_lpr++;
/* Now insert the new filter */
is2_entry_set(ocelot, index, filter);
return 0;
}
static void ocelot_ace_rule_del(struct ocelot *ocelot,
struct ocelot_acl_block *block,
struct ocelot_ace_rule *rule)
static void ocelot_vcap_block_remove_filter(struct ocelot *ocelot,
struct ocelot_vcap_block *block,
struct ocelot_vcap_filter *filter)
{
struct ocelot_ace_rule *tmp;
struct ocelot_vcap_filter *tmp;
struct list_head *pos, *q;
list_for_each_safe(pos, q, &block->rules) {
tmp = list_entry(pos, struct ocelot_ace_rule, list);
if (tmp->id == rule->id) {
if (tmp->action == OCELOT_ACL_ACTION_POLICE)
ocelot_ace_police_del(ocelot, block,
tmp = list_entry(pos, struct ocelot_vcap_filter, list);
if (tmp->id == filter->id) {
if (tmp->action == OCELOT_VCAP_ACTION_POLICE)
ocelot_vcap_policer_del(ocelot, block,
tmp->pol_ix);
list_del(pos);
......@@ -888,56 +913,57 @@ static void ocelot_ace_rule_del(struct ocelot *ocelot,
block->count--;
}
int ocelot_ace_rule_offload_del(struct ocelot *ocelot,
struct ocelot_ace_rule *rule)
int ocelot_vcap_filter_del(struct ocelot *ocelot,
struct ocelot_vcap_filter *filter)
{
struct ocelot_acl_block *block = &ocelot->acl_block;
struct ocelot_ace_rule del_ace;
struct ocelot_ace_rule *ace;
struct ocelot_vcap_block *block = &ocelot->block;
struct ocelot_vcap_filter del_filter;
int i, index;
memset(&del_ace, 0, sizeof(del_ace));
memset(&del_filter, 0, sizeof(del_filter));
/* Gets index of the rule */
index = ocelot_ace_rule_get_index_id(block, rule);
/* Gets index of the filter */
index = ocelot_vcap_block_get_filter_index(block, filter);
/* Delete rule */
ocelot_ace_rule_del(ocelot, block, rule);
/* Delete filter */
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++) {
ace = ocelot_ace_rule_get_rule_index(block, i);
is2_entry_set(ocelot, i, ace);
struct ocelot_vcap_filter *tmp;
tmp = ocelot_vcap_block_find_filter(block, i);
is2_entry_set(ocelot, i, tmp);
}
/* Now delete the last rule, because it is duplicated */
is2_entry_set(ocelot, block->count, &del_ace);
/* Now delete the last filter, because it is duplicated */
is2_entry_set(ocelot, block->count, &del_filter);
return 0;
}
int ocelot_ace_rule_stats_update(struct ocelot *ocelot,
struct ocelot_ace_rule *rule)
int ocelot_vcap_filter_stats_update(struct ocelot *ocelot,
struct ocelot_vcap_filter *filter)
{
struct ocelot_acl_block *block = &ocelot->acl_block;
struct ocelot_ace_rule *tmp;
struct ocelot_vcap_block *block = &ocelot->block;
struct ocelot_vcap_filter *tmp;
int index;
index = ocelot_ace_rule_get_index_id(block, rule);
is2_entry_get(ocelot, rule, index);
index = ocelot_vcap_block_get_filter_index(block, filter);
is2_entry_get(ocelot, filter, index);
/* 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;
is2_entry_set(ocelot, index, tmp);
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];
struct ocelot_acl_block *block = &ocelot->acl_block;
struct ocelot_vcap_block *block = &ocelot->block;
struct vcap_data data;
memset(&data, 0, sizeof(data));
......@@ -968,7 +994,7 @@ int ocelot_ace_init(struct ocelot *ocelot)
block->pol_lpr = OCELOT_POLICER_DISCARD - 1;
INIT_LIST_HEAD(&ocelot->acl_block.rules);
INIT_LIST_HEAD(&ocelot->block.rules);
return 0;
}
......@@ -3,8 +3,8 @@
* Copyright (c) 2019 Microsemi Corporation
*/
#ifndef _MSCC_OCELOT_ACE_H_
#define _MSCC_OCELOT_ACE_H_
#ifndef _MSCC_OCELOT_VCAP_H_
#define _MSCC_OCELOT_VCAP_H_
#include "ocelot.h"
#include "ocelot_police.h"
......@@ -76,31 +76,31 @@ struct ocelot_vcap_udp_tcp {
u16 mask;
};
enum ocelot_ace_type {
OCELOT_ACE_TYPE_ANY,
OCELOT_ACE_TYPE_ETYPE,
OCELOT_ACE_TYPE_LLC,
OCELOT_ACE_TYPE_SNAP,
OCELOT_ACE_TYPE_ARP,
OCELOT_ACE_TYPE_IPV4,
OCELOT_ACE_TYPE_IPV6
enum ocelot_vcap_key_type {
OCELOT_VCAP_KEY_ANY,
OCELOT_VCAP_KEY_ETYPE,
OCELOT_VCAP_KEY_LLC,
OCELOT_VCAP_KEY_SNAP,
OCELOT_VCAP_KEY_ARP,
OCELOT_VCAP_KEY_IPV4,
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_u8 pcp; /* PCP (3 bit) */
enum ocelot_vcap_bit dei; /* DEI */
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 smac;
struct ocelot_vcap_u16 etype;
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 smac;
......@@ -108,7 +108,7 @@ struct ocelot_ace_frame_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 smac;
......@@ -116,7 +116,7 @@ struct ocelot_ace_frame_snap {
struct ocelot_vcap_u40 snap;
};
struct ocelot_ace_frame_arp {
struct ocelot_vcap_key_arp {
struct ocelot_vcap_u48 smac;
enum ocelot_vcap_bit arp; /* Opcode ARP/RARP */
enum ocelot_vcap_bit req; /* Opcode request/reply */
......@@ -133,7 +133,7 @@ struct ocelot_ace_frame_arp {
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 fragment; /* Fragment */
enum ocelot_vcap_bit options; /* Header options */
......@@ -155,7 +155,7 @@ struct ocelot_ace_frame_ipv4 {
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_u128 sip; /* IPv6 source (byte 0-7 ignored) */
enum ocelot_vcap_bit ttl; /* TTL zero */
......@@ -174,58 +174,58 @@ struct ocelot_ace_frame_ipv6 {
enum ocelot_vcap_bit seq_zero; /* TCP sequence number is zero */
};
enum ocelot_ace_action {
OCELOT_ACL_ACTION_DROP,
OCELOT_ACL_ACTION_TRAP,
OCELOT_ACL_ACTION_POLICE,
enum ocelot_vcap_action {
OCELOT_VCAP_ACTION_DROP,
OCELOT_VCAP_ACTION_TRAP,
OCELOT_VCAP_ACTION_POLICE,
};
struct ocelot_ace_stats {
struct ocelot_vcap_stats {
u64 bytes;
u64 pkts;
u64 used;
};
struct ocelot_ace_rule {
struct ocelot_vcap_filter {
struct list_head list;
u16 prio;
u32 id;
enum ocelot_ace_action action;
struct ocelot_ace_stats stats;
enum ocelot_vcap_action action;
struct ocelot_vcap_stats stats;
unsigned long ingress_port_mask;
enum ocelot_vcap_bit dmac_mc;
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 {
/* ocelot_ACE_TYPE_ANY: No specific fields */
struct ocelot_ace_frame_etype etype;
struct ocelot_ace_frame_llc llc;
struct ocelot_ace_frame_snap snap;
struct ocelot_ace_frame_arp arp;
struct ocelot_ace_frame_ipv4 ipv4;
struct ocelot_ace_frame_ipv6 ipv6;
} frame;
/* OCELOT_VCAP_KEY_ANY: No specific fields */
struct ocelot_vcap_key_etype etype;
struct ocelot_vcap_key_llc llc;
struct ocelot_vcap_key_snap snap;
struct ocelot_vcap_key_arp arp;
struct ocelot_vcap_key_ipv4 ipv4;
struct ocelot_vcap_key_ipv6 ipv6;
} key;
struct ocelot_policer pol;
u32 pol_ix;
};
int ocelot_ace_rule_offload_add(struct ocelot *ocelot,
struct ocelot_ace_rule *rule,
int ocelot_vcap_filter_add(struct ocelot *ocelot,
struct ocelot_vcap_filter *rule,
struct netlink_ext_ack *extack);
int ocelot_ace_rule_offload_del(struct ocelot *ocelot,
struct ocelot_ace_rule *rule);
int ocelot_ace_rule_stats_update(struct ocelot *ocelot,
struct ocelot_ace_rule *rule);
int ocelot_vcap_filter_del(struct ocelot *ocelot,
struct ocelot_vcap_filter *rule);
int ocelot_vcap_filter_stats_update(struct ocelot *ocelot,
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,
struct flow_cls_offload *f,
bool ingress);
#endif /* _MSCC_OCELOT_ACE_H_ */
#endif /* _MSCC_OCELOT_VCAP_H_ */
......@@ -15,6 +15,7 @@
#include <net/switchdev.h>
#include <soc/mscc/ocelot_vcap.h>
#include <soc/mscc/ocelot_hsio.h>
#include "ocelot.h"
#define IFH_EXTRACT_BITFIELD64(x, o, w) (((x) >> (o)) & GENMASK_ULL((w) - 1, 0))
......@@ -23,6 +24,447 @@
#define VSC7514_VCAP_IS2_ACTION_WIDTH 99
#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)
{
u8 llen, wlen;
......
......@@ -470,7 +470,7 @@ struct ocelot_ops {
int (*reset)(struct ocelot *ocelot);
};
struct ocelot_acl_block {
struct ocelot_vcap_block {
struct list_head rules;
int count;
int pol_lpr;
......@@ -535,7 +535,7 @@ struct ocelot {
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_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