root/drivers/net/ethernet/ti/icssg/icssg_classifier.c
// SPDX-License-Identifier: GPL-2.0
/* Texas Instruments ICSSG Ethernet Driver
 *
 * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/
 *
 */

#include <linux/etherdevice.h>
#include <linux/types.h>
#include <linux/regmap.h>

#include "icssg_prueth.h"

#define ICSSG_NUM_CLASSIFIERS   16
#define ICSSG_NUM_FT1_SLOTS     8
#define ICSSG_NUM_FT3_SLOTS     16

#define ICSSG_NUM_CLASSIFIERS_IN_USE    5

/* Filter 1 - FT1 */
#define FT1_NUM_SLOTS   8
#define FT1_SLOT_SIZE   0x10    /* bytes */

/* offsets from FT1 slot base i.e. slot 1 start */
#define FT1_DA0         0x0
#define FT1_DA1         0x4
#define FT1_DA0_MASK    0x8
#define FT1_DA1_MASK    0xc

#define FT1_N_REG(slize, n, reg)        \
        (offs[slice].ft1_slot_base + FT1_SLOT_SIZE * (n) + (reg))

#define FT1_LEN_MASK            GENMASK(19, 16)
#define FT1_LEN_SHIFT           16
#define FT1_LEN(len)            (((len) << FT1_LEN_SHIFT) & FT1_LEN_MASK)
#define FT1_START_MASK          GENMASK(14, 0)
#define FT1_START(start)        ((start) & FT1_START_MASK)
#define FT1_MATCH_SLOT(n)       (GENMASK(23, 16) & (BIT(n) << 16))

/* FT1 config type */
enum ft1_cfg_type {
        FT1_CFG_TYPE_DISABLED = 0,
        FT1_CFG_TYPE_EQ,
        FT1_CFG_TYPE_GT,
        FT1_CFG_TYPE_LT,
};

#define FT1_CFG_SHIFT(n)        (2 * (n))
#define FT1_CFG_MASK(n)         (0x3 << FT1_CFG_SHIFT((n)))

/* Filter 3 -  FT3 */
#define FT3_NUM_SLOTS   16
#define FT3_SLOT_SIZE   0x20    /* bytes */

/* offsets from FT3 slot n's base */
#define FT3_START               0
#define FT3_START_AUTO          0x4
#define FT3_START_OFFSET        0x8
#define FT3_JUMP_OFFSET         0xc
#define FT3_LEN                 0x10
#define FT3_CFG                 0x14
#define FT3_T                   0x18
#define FT3_T_MASK              0x1c

#define FT3_N_REG(slize, n, reg)        \
        (offs[slice].ft3_slot_base + FT3_SLOT_SIZE * (n) + (reg))

/* offsets from rx_class n's base */
#define RX_CLASS_AND_EN         0
#define RX_CLASS_OR_EN          0x4
#define RX_CLASS_NUM_SLOTS      16
#define RX_CLASS_EN_SIZE        0x8     /* bytes */

#define RX_CLASS_N_REG(slice, n, reg)   \
        (offs[slice].rx_class_base + RX_CLASS_EN_SIZE * (n) + (reg))

/* RX Class Gates */
#define RX_CLASS_GATES_SIZE     0x4     /* bytes */

#define RX_CLASS_GATES_N_REG(slice, n)  \
        (offs[slice].rx_class_gates_base + RX_CLASS_GATES_SIZE * (n))

#define RX_CLASS_GATES_ALLOW_MASK       BIT(6)
#define RX_CLASS_GATES_RAW_MASK         BIT(5)
#define RX_CLASS_GATES_PHASE_MASK       BIT(4)

/* RX Class traffic data matching bits */
#define RX_CLASS_FT_UC                          BIT(31)
#define RX_CLASS_FT_MC                  BIT(30)
#define RX_CLASS_FT_BC                  BIT(29)
#define RX_CLASS_FT_FW                  BIT(28)
#define RX_CLASS_FT_RCV                 BIT(27)
#define RX_CLASS_FT_VLAN                BIT(26)
#define RX_CLASS_FT_DA_P                BIT(25)
#define RX_CLASS_FT_DA_I                BIT(24)
#define RX_CLASS_FT_FT1_MATCH_MASK      GENMASK(23, 16)
#define RX_CLASS_FT_FT1_MATCH_SHIFT     16
#define RX_CLASS_FT_FT3_MATCH_MASK      GENMASK(15, 0)
#define RX_CLASS_FT_FT3_MATCH_SHIFT     0

#define RX_CLASS_FT_FT1_MATCH(slot)     \
        ((BIT(slot) << RX_CLASS_FT_FT1_MATCH_SHIFT) & \
        RX_CLASS_FT_FT1_MATCH_MASK)

/* RX class type */
enum rx_class_sel_type {
        RX_CLASS_SEL_TYPE_OR = 0,
        RX_CLASS_SEL_TYPE_AND = 1,
        RX_CLASS_SEL_TYPE_OR_AND_AND = 2,
        RX_CLASS_SEL_TYPE_OR_OR_AND = 3,
};

#define FT1_CFG_SHIFT(n)        (2 * (n))
#define FT1_CFG_MASK(n)         (0x3 << FT1_CFG_SHIFT((n)))

#define RX_CLASS_SEL_SHIFT(n)   (2 * (n))
#define RX_CLASS_SEL_MASK(n)    (0x3 << RX_CLASS_SEL_SHIFT((n)))

#define ICSSG_CFG_OFFSET        0
#define MAC_INTERFACE_0         0x18
#define MAC_INTERFACE_1         0x1c

#define ICSSG_CFG_RX_L2_G_EN    BIT(2)

/* These are register offsets per PRU */
struct miig_rt_offsets {
        u32 mac0;
        u32 mac1;
        u32 ft1_start_len;
        u32 ft1_cfg;
        u32 ft1_slot_base;
        u32 ft3_slot_base;
        u32 ft3_p_base;
        u32 ft_rx_ptr;
        u32 rx_class_base;
        u32 rx_class_cfg1;
        u32 rx_class_cfg2;
        u32 rx_class_gates_base;
        u32 rx_green;
        u32 rx_rate_cfg_base;
        u32 rx_rate_src_sel0;
        u32 rx_rate_src_sel1;
        u32 tx_rate_cfg_base;
        u32 stat_base;
        u32 tx_hsr_tag;
        u32 tx_hsr_seq;
        u32 tx_vlan_type;
        u32 tx_vlan_ins;
};

/* These are the offset values for miig_rt_offsets registers */
static const struct miig_rt_offsets offs[] = {
        /* PRU0 */
        {
                0x8,
                0xc,
                0x80,
                0x84,
                0x88,
                0x108,
                0x308,
                0x408,
                0x40c,
                0x48c,
                0x490,
                0x494,
                0x4d4,
                0x4e4,
                0x504,
                0x508,
                0x50c,
                0x54c,
                0x63c,
                0x640,
                0x644,
                0x648,
        },
        /* PRU1 */
        {
                0x10,
                0x14,
                0x64c,
                0x650,
                0x654,
                0x6d4,
                0x8d4,
                0x9d4,
                0x9d8,
                0xa58,
                0xa5c,
                0xa60,
                0xaa0,
                0xab0,
                0xad0,
                0xad4,
                0xad8,
                0xb18,
                0xc08,
                0xc0c,
                0xc10,
                0xc14,
        },
};

static void rx_class_ft1_set_start_len(struct regmap *miig_rt, int slice,
                                       u16 start, u8 len)
{
        u32 offset, val;

        offset = offs[slice].ft1_start_len;
        val = FT1_LEN(len) | FT1_START(start);
        regmap_write(miig_rt, offset, val);
}

static void rx_class_ft1_set_da(struct regmap *miig_rt, int slice,
                                int n, const u8 *addr)
{
        u32 offset;

        offset = FT1_N_REG(slice, n, FT1_DA0);
        regmap_write(miig_rt, offset, (u32)(addr[0] | addr[1] << 8 |
                     addr[2] << 16 | addr[3] << 24));
        offset = FT1_N_REG(slice, n, FT1_DA1);
        regmap_write(miig_rt, offset, (u32)(addr[4] | addr[5] << 8));
}

static void rx_class_ft1_set_da_mask(struct regmap *miig_rt, int slice,
                                     int n, const u8 *addr)
{
        u32 offset;

        offset = FT1_N_REG(slice, n, FT1_DA0_MASK);
        regmap_write(miig_rt, offset, (u32)(addr[0] | addr[1] << 8 |
                     addr[2] << 16 | addr[3] << 24));
        offset = FT1_N_REG(slice, n, FT1_DA1_MASK);
        regmap_write(miig_rt, offset, (u32)(addr[4] | addr[5] << 8));
}

static void rx_class_ft1_cfg_set_type(struct regmap *miig_rt, int slice, int n,
                                      enum ft1_cfg_type type)
{
        u32 offset;

        offset = offs[slice].ft1_cfg;
        regmap_update_bits(miig_rt, offset, FT1_CFG_MASK(n),
                           type << FT1_CFG_SHIFT(n));
}

static void rx_class_sel_set_type(struct regmap *miig_rt, int slice, int n,
                                  enum rx_class_sel_type type)
{
        u32 offset;

        offset = offs[slice].rx_class_cfg1;
        regmap_update_bits(miig_rt, offset, RX_CLASS_SEL_MASK(n),
                           type << RX_CLASS_SEL_SHIFT(n));
}

static void rx_class_set_and(struct regmap *miig_rt, int slice, int n,
                             u32 data)
{
        u32 offset;

        offset = RX_CLASS_N_REG(slice, n, RX_CLASS_AND_EN);
        regmap_write(miig_rt, offset, data);
}

static void rx_class_set_or(struct regmap *miig_rt, int slice, int n,
                            u32 data)
{
        u32 offset;

        offset = RX_CLASS_N_REG(slice, n, RX_CLASS_OR_EN);
        regmap_write(miig_rt, offset, data);
}

static u32 rx_class_get_or(struct regmap *miig_rt, int slice, int n)
{
        u32 offset, val;

        offset = RX_CLASS_N_REG(slice, n, RX_CLASS_OR_EN);
        regmap_read(miig_rt, offset, &val);

        return val;
}

void icssg_class_set_host_mac_addr(struct regmap *miig_rt, const u8 *mac)
{
        regmap_write(miig_rt, MAC_INTERFACE_0, (u32)(mac[0] | mac[1] << 8 |
                     mac[2] << 16 | mac[3] << 24));
        regmap_write(miig_rt, MAC_INTERFACE_1, (u32)(mac[4] | mac[5] << 8));
}
EXPORT_SYMBOL_GPL(icssg_class_set_host_mac_addr);

void icssg_class_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac)
{
        regmap_write(miig_rt, offs[slice].mac0, (u32)(mac[0] | mac[1] << 8 |
                     mac[2] << 16 | mac[3] << 24));
        regmap_write(miig_rt, offs[slice].mac1, (u32)(mac[4] | mac[5] << 8));
}
EXPORT_SYMBOL_GPL(icssg_class_set_mac_addr);

static void icssg_class_ft1_add_mcast(struct regmap *miig_rt, int slice,
                                      int slot, const u8 *addr, const u8 *mask)
{
        u32 val;
        int i;

        WARN(slot >= FT1_NUM_SLOTS, "invalid slot: %d\n", slot);

        rx_class_ft1_set_da(miig_rt, slice, slot, addr);
        rx_class_ft1_set_da_mask(miig_rt, slice, slot, mask);
        rx_class_ft1_cfg_set_type(miig_rt, slice, slot, FT1_CFG_TYPE_EQ);

        /* Enable the FT1 slot in OR enable for all classifiers */
        for (i = 0; i < ICSSG_NUM_CLASSIFIERS_IN_USE; i++) {
                val = rx_class_get_or(miig_rt, slice, i);
                val |= RX_CLASS_FT_FT1_MATCH(slot);
                rx_class_set_or(miig_rt, slice, i, val);
        }
}

/* disable all RX traffic */
void icssg_class_disable(struct regmap *miig_rt, int slice)
{
        u32 data, offset;
        int n;

        /* Enable RX_L2_G */
        regmap_update_bits(miig_rt, ICSSG_CFG_OFFSET, ICSSG_CFG_RX_L2_G_EN,
                           ICSSG_CFG_RX_L2_G_EN);

        for (n = 0; n < ICSSG_NUM_CLASSIFIERS; n++) {
                /* AND_EN = 0 */
                rx_class_set_and(miig_rt, slice, n, 0);
                /* OR_EN = 0 */
                rx_class_set_or(miig_rt, slice, n, 0);

                /* set CFG1 to OR */
                rx_class_sel_set_type(miig_rt, slice, n, RX_CLASS_SEL_TYPE_OR);

                /* configure gate */
                offset = RX_CLASS_GATES_N_REG(slice, n);
                regmap_read(miig_rt, offset, &data);
                /* clear class_raw so we go through filters */
                data &= ~RX_CLASS_GATES_RAW_MASK;
                /* set allow and phase mask */
                data |= RX_CLASS_GATES_ALLOW_MASK | RX_CLASS_GATES_PHASE_MASK;
                regmap_write(miig_rt, offset, data);
        }

        /* FT1 Disabled */
        for (n = 0; n < ICSSG_NUM_FT1_SLOTS; n++) {
                const u8 addr[] = { 0, 0, 0, 0, 0, 0, };

                rx_class_ft1_cfg_set_type(miig_rt, slice, n,
                                          FT1_CFG_TYPE_DISABLED);
                rx_class_ft1_set_da(miig_rt, slice, n, addr);
                rx_class_ft1_set_da_mask(miig_rt, slice, n, addr);
        }

        /* clear CFG2 */
        regmap_write(miig_rt, offs[slice].rx_class_cfg2, 0);
}
EXPORT_SYMBOL_GPL(icssg_class_disable);

void icssg_class_default(struct regmap *miig_rt, int slice, bool allmulti,
                         bool is_sr1)
{
        int num_classifiers = is_sr1 ? ICSSG_NUM_CLASSIFIERS_IN_USE : 1;
        u32 data;
        int n;

        /* defaults */
        icssg_class_disable(miig_rt, slice);

        /* Setup Classifier */
        for (n = 0; n < num_classifiers; n++) {
                /* match on Broadcast or MAC_PRU address */
                data = RX_CLASS_FT_BC | RX_CLASS_FT_DA_P;

                /* multicast */
                if (allmulti)
                        data |= RX_CLASS_FT_MC;

                rx_class_set_or(miig_rt, slice, n, data);

                /* set CFG1 for OR_OR_AND for classifier */
                rx_class_sel_set_type(miig_rt, slice, n,
                                      RX_CLASS_SEL_TYPE_OR_OR_AND);
        }

        /* clear CFG2 */
        regmap_write(miig_rt, offs[slice].rx_class_cfg2, 0);
}
EXPORT_SYMBOL_GPL(icssg_class_default);

void icssg_class_promiscuous_sr1(struct regmap *miig_rt, int slice)
{
        u32 data, offset;
        int n;

        /* defaults */
        icssg_class_disable(miig_rt, slice);

        /* Setup Classifier */
        for (n = 0; n < ICSSG_NUM_CLASSIFIERS_IN_USE; n++) {
                /* set RAW_MASK to bypass filters */
                offset = RX_CLASS_GATES_N_REG(slice, n);
                regmap_read(miig_rt, offset, &data);
                data |= RX_CLASS_GATES_RAW_MASK;
                regmap_write(miig_rt, offset, data);
        }
}
EXPORT_SYMBOL_GPL(icssg_class_promiscuous_sr1);

void icssg_class_add_mcast_sr1(struct regmap *miig_rt, int slice,
                               struct net_device *ndev)
{
        u8 mask_addr[6] = { 0, 0, 0, 0, 0, 0xff };
        struct netdev_hw_addr *ha;
        int slot = 2;

        rx_class_ft1_set_start_len(miig_rt, slice, 0, 6);
        /* reserve first 2 slots for
         *      1) 01-80-C2-00-00-XX Known Service Ethernet Multicast addresses
         *      2) 01-00-5e-00-00-XX Local Network Control Block
         *                            (224.0.0.0 - 224.0.0.255  (224.0.0/24))
         */
        icssg_class_ft1_add_mcast(miig_rt, slice, 0,
                                  eth_reserved_addr_base, mask_addr);
        icssg_class_ft1_add_mcast(miig_rt, slice, 1,
                                  eth_ipv4_mcast_addr_base, mask_addr);
        mask_addr[5] = 0;
        netdev_for_each_mc_addr(ha, ndev) {
                /* skip addresses matching reserved slots */
                if (!memcmp(eth_reserved_addr_base, ha->addr, 5) ||
                    !memcmp(eth_ipv4_mcast_addr_base, ha->addr, 5)) {
                        netdev_dbg(ndev, "mcast skip %pM\n", ha->addr);
                        continue;
                }

                if (slot >= FT1_NUM_SLOTS) {
                        netdev_dbg(ndev,
                                   "can't add more than %d MC addresses, enabling allmulti\n",
                                   FT1_NUM_SLOTS);
                        icssg_class_default(miig_rt, slice, 1, true);
                        break;
                }

                netdev_dbg(ndev, "mcast add %pM\n", ha->addr);
                icssg_class_ft1_add_mcast(miig_rt, slice, slot,
                                          ha->addr, mask_addr);
                slot++;
        }
}
EXPORT_SYMBOL_GPL(icssg_class_add_mcast_sr1);

/* required for SAV check */
void icssg_ft1_set_mac_addr(struct regmap *miig_rt, int slice, u8 *mac_addr)
{
        const u8 mask_addr[] = { 0, 0, 0, 0, 0, 0, };

        rx_class_ft1_set_start_len(miig_rt, slice, ETH_ALEN, ETH_ALEN);
        rx_class_ft1_set_da(miig_rt, slice, 0, mac_addr);
        rx_class_ft1_set_da_mask(miig_rt, slice, 0, mask_addr);
        rx_class_ft1_cfg_set_type(miig_rt, slice, 0, FT1_CFG_TYPE_EQ);
}
EXPORT_SYMBOL_GPL(icssg_ft1_set_mac_addr);