blob: f42926b30dce7de68acb16090655dc0d70b4e5fc [file] [log] [blame]
/*
* (C) Copyright 2012 Quantenna Communications Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef __TOPAZ_FWT_CPUIF_PLATFORM_H
#define __TOPAZ_FWT_CPUIF_PLATFORM_H
#include <common/topaz_platform.h>
#include <qtn/mproc_sync_base.h>
#include <qtn/topaz_tqe_cpuif.h>
#include <qtn/qtn_net_packet.h>
#include <qtn/lhost_muc_comm.h>
#if defined(__linux__)
#define TOPAZ_FWT_LOCAL_CPU TOPAZ_FWT_LOOKUP_LHOST
#elif defined(ARCSHELL)
#define TOPAZ_FWT_LOCAL_CPU TOPAZ_FWT_LOOKUP_LHOST
#elif defined(MUC_BUILD)
#define TOPAZ_FWT_LOCAL_CPU TOPAZ_FWT_LOOKUP_MUC
#elif defined(DSP_BUILD)
#define TOPAZ_FWT_LOCAL_CPU TOPAZ_FWT_LOOKUP_DSP
#elif defined(AUC_BUILD)
#define TOPAZ_FWT_LOCAL_CPU TOPAZ_FWT_LOOKUP_AUC
#else
#error No TOPAZ_FWT_LOCAL_CPU set
#endif
#define TOPAZ_FWT_LOOKUP_REG __TOPAZ_FWT_LOOKUP_REG(TOPAZ_FWT_LOCAL_CPU)
#define TOPAZ_FWT_LOOKUP_MAC_LO __TOPAZ_FWT_LOOKUP_MAC_LO(TOPAZ_FWT_LOCAL_CPU)
#define TOPAZ_FWT_LOOKUP_MAC_HI __TOPAZ_FWT_LOOKUP_MAC_HI(TOPAZ_FWT_LOCAL_CPU)
/* Hardware limitation for node entries */
#define TOPAZ_FWT_MAX_NODE_ENTRY (6)
/** Macro to get a number with its 'bits' LSB bits set */
#define SET_LSB_BITS(bits) ((1 << (bits)) - 1)
/** Macro to update a bit-field with a new value */
#define TOPAZ_FWT_SET_BIT_FIELD(var, start_bit, width, value) \
(((var) & ~(SET_LSB_BITS(width) << (start_bit))) | \
((value) & SET_LSB_BITS(width)) << (start_bit))
/** Macro to extract a bit-field */
#define TOPAZ_FWT_GET_BIT_FIELD(data, start_offset, width)\
(((data) >> (start_offset)) & SET_LSB_BITS(width))
#if defined(ARCSHELL)
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
#define ETH_ALEN 6
#define inline _Inline
#define unlikely
#define likely
#elif defined(MUC_BUILD)
#define ETH_ALEN 6
#endif
/*
* Forwarding Table manipulation
*
* FWT has 2048 entries. First 1024 are mac address crc hash done by hardware.
* Next 1024 are linked list (nxt_entry) for when hash collision occurs.
*
* Basic forwarding table flow is:
* 1) Packet received
* 2) crc32 of incoming packet macaddr forms a 10 bit number [0 .. 1023]
* 3) This number is the index of the first FWT entry in the physical table. MAC address is compared.
* a) If MAC matches, this is the fwt entry
* b) If not, follow next entry index. Must be [1024 .. 2047], repeat.
* 4) Interpret FWT if present
*
* In sw maintain tailqs to know which slots are occupied
*/
/*
* FWT timestamp field is 10 bits. FWT clock is at 250MHz on BBIC4 ASIC.
* UNIT affects clock ticks per timer reg clock tick.
* SCALE affects shifting of the above register when applied to the timestamp field
* when updating it.
* ASIC: Values of 0xe unit and 13 (0xd) scale result in a time wrap time of 1 minute.
* FPGA: 0xc unit, 0xa scale results in very rougly 1m wrap
*/
#define TOPAZ_FWT_TIMESTAMP_BITS 10
#define TOPAZ_FWT_TIMESTAMP_MASK ((1 << TOPAZ_FWT_TIMESTAMP_BITS) - 1)
#if TOPAZ_FPGA_PLATFORM
#define TOPAZ_FWT_TIMESTAMP_UNIT 0xc
#define TOPAZ_FWT_TIMESTAMP_SCALE 0xa
#else
#define TOPAZ_FWT_TIMESTAMP_UNIT 0x1b /* (3500 ticks 14 usec) */
#define TOPAZ_FWT_TIMESTAMP_SCALE 0x13 /* (2^19) */
/*
* Resolution Calculation:
* (per tick) * (FWT clock [sec]) * (bits store location) = Resolution[sec]
* (3500) * (1 / (250*10^6)) * (2^19) = 7.34 [sec]
* */
#define TOPAZ_FWT_RESOLUTION_MSEC 7340 /* Derive from unit & scale */
#endif
RUBY_INLINE uint32_t topaz_fwt_get_scaled_timestamp(void)
{
#ifdef CONSOLE_TEST
return 0;
#else
uint32_t tsr = qtn_mproc_sync_mem_read(TOPAZ_FWT_TIME_STAMP_CNT);
return (tsr >> TOPAZ_FWT_TIMESTAMP_SCALE) & TOPAZ_FWT_TIMESTAMP_MASK;
#endif
}
union topaz_fwt_entry {
struct {
uint32_t word0; /* macaddr[30:1] */
uint32_t word1; /* valid bit(31), portal(30), next_index(27:16), macaddr[47:32](15:0) */
uint32_t word2; /* out_node + valid 0, 1, 2, 3 */
uint32_t word3; /* out_node + valid 4, 5, outport, timestamp(9:0) */
} raw;
struct {
uint8_t mac_le[ETH_ALEN];
uint16_t __unused1 :1,
next_index :11,
__unused2 :2,
portal :1,
valid :1;
uint8_t out_node_0 :7,
out_node_vld_0 :1;
uint8_t out_node_1 :7,
out_node_vld_1 :1;
uint8_t out_node_2 :7,
out_node_vld_2 :1;
uint8_t out_node_3 :7,
out_node_vld_3 :1;
uint16_t timestamp :10,
out_port :4,
__unused3 :2;
uint8_t out_node_4 :7,
out_node_vld_4 :1;
uint8_t out_node_5 :7,
out_node_vld_5 :1;
} data;
};
#define FWT_ZERO_ENTRY_INIT { { 0, 0, 0, 0 } }
RUBY_INLINE union topaz_fwt_entry *topaz_fwt_get_hw_entry(uint16_t index)
{
#ifdef CONSOLE_TEST
extern union topaz_fwt_entry test_hw_fwt[TOPAZ_FWT_HW_TOTAL_ENTRIES];
return &test_hw_fwt[index];
#else
union topaz_fwt_entry *e;
e = (union topaz_fwt_entry *)(TOPAZ_FWT_TABLE_BASE + index * sizeof(*e));
return e;
#endif
}
RUBY_INLINE int topaz_fwt_is_valid(const union topaz_fwt_entry *e)
{
return e->raw.word1 & TOPAZ_FWT_ENTRY_VALID;
}
RUBY_INLINE uint16_t topaz_fwt_next_index(const union topaz_fwt_entry *e)
{
return MS(e->raw.word1, TOPAZ_FWT_ENTRY_NXT_ENTRY);
}
RUBY_INLINE const uint8_t *topaz_fwt_macaddr(const union topaz_fwt_entry *e)
{
return (void *) e;
}
RUBY_INLINE void topaz_fwt_set_next_index(union topaz_fwt_entry *e, uint16_t index)
{
unsigned long word1 = e->raw.word1 & ~TOPAZ_FWT_ENTRY_NXT_ENTRY;
e->raw.word1 = word1 | SM(index, TOPAZ_FWT_ENTRY_NXT_ENTRY);
}
RUBY_INLINE void topaz_fwt_copy_entry(union topaz_fwt_entry *dest, const union topaz_fwt_entry *src,
int words)
{
int i;
#pragma Off(Behaved)
uint32_t *d = &dest->raw.word0;
const uint32_t *s = &src->raw.word0;
#pragma On(Behaved)
for (i = 0; i < words; i++) {
*d++ = *s++;
}
}
RUBY_INLINE void topaz_fwt_insert_entry(const union topaz_fwt_entry *newent, uint16_t index)
{
union topaz_fwt_entry *hw_ent = topaz_fwt_get_hw_entry(index);
topaz_fwt_copy_entry(hw_ent, newent, 4);
}
/*
* Software FWT mirror. Used by MuC for Rx path
* acceleration, without accessing the FWT memory
*/
union topaz_fwt_sw_entry {
uint16_t raw;
struct {
uint8_t valid :1,
mcast :1,
vsp :1,
portal :1,
port :4;
uint8_t __pad :1,
node :7;
} unicast;
struct {
uint16_t valid :1,
mcast :1,
index :14;
} multicast;
};
/* 23 bits of multicast ipv4 -> mac leaves 5 bits of ambiguity */
#define TOPAZ_FWT_SW_IP_ALIAS_ENTRIES 32
#define TOPAZ_FWT_SW_NODE_MAX 128
#define TOPAZ_BITS_PER_WD (32)
#define TOPAZ_FWT_SW_NODE_BITMAP_SIZE (TOPAZ_FWT_SW_NODE_MAX / TOPAZ_BITS_PER_WD)
struct topaz_fwt_sw_mcast_entry {
uint32_t node_bitmap[TOPAZ_FWT_SW_NODE_BITMAP_SIZE];
uint8_t port_bitmap;
uint8_t flood_forward;
uint8_t seen;
#ifdef CONFIG_TOPAZ_DBDC_HOST
uint8_t dev_bitmap;
#else
uint8_t __pad[1];
#endif
};
#ifdef CONFIG_TOPAZ_DBDC_HOST
RUBY_INLINE int topaz_fwt_sw_mcast_dev_is_set(struct topaz_fwt_sw_mcast_entry *const e,
const uint8_t dev_id)
{
return (e->dev_bitmap & (1 << dev_id));
}
RUBY_INLINE int topaz_fwt_sw_mcast_dev_is_empty(struct topaz_fwt_sw_mcast_entry *const e)
{
return (e->dev_bitmap == 0);
}
RUBY_INLINE void topaz_fwt_sw_mcast_dev_set(struct topaz_fwt_sw_mcast_entry *const e,
const uint8_t dev_id)
{
e->dev_bitmap |= (1 << dev_id);
}
RUBY_INLINE void topaz_fwt_sw_mcast_dev_clear(struct topaz_fwt_sw_mcast_entry *const e,
const uint8_t dev_id)
{
e->dev_bitmap &= ~(1 << dev_id);
}
#endif
struct topaz_fwt_sw_alias_table {
int16_t mcast_entry_index[TOPAZ_FWT_SW_IP_ALIAS_ENTRIES];
};
RUBY_INLINE int8_t topaz_fwt_mcast_to_ip_alias(const void *addr, uint16_t ether_type)
{
if (ether_type == htons(ETHERTYPE_IP)) {
return qtn_mcast_ipv4_alias(addr);
} else if (ether_type == htons(ETHERTYPE_IPV6)) {
return 0;
} else {
return -1;
}
}
RUBY_INLINE int topaz_fwt_sw_alias_table_index_valid(int16_t index)
{
return index >= 0 && index < TOPAZ_FWT_MCAST_ENTRIES;
}
RUBY_INLINE int topaz_fwt_sw_mcast_entry_index_valid(int16_t index)
{
return index >= 0 && index < TOPAZ_FWT_MCAST_ENTRIES;
}
RUBY_INLINE uint8_t topaz_fwt_sw_mcast_entry_nodes_clear(const struct topaz_fwt_sw_mcast_entry *e)
{
unsigned int i;
for (i = 0; i < TOPAZ_FWT_SW_NODE_BITMAP_SIZE; i++) {
if (e->node_bitmap[i]) {
return 0;
}
}
return 1;
}
RUBY_INLINE int topaz_fwt_sw_alias_table_empty(const struct topaz_fwt_sw_alias_table *alias_table)
{
unsigned int i;
for (i = 0; i < TOPAZ_FWT_SW_IP_ALIAS_ENTRIES; i++) {
if (topaz_fwt_sw_mcast_entry_index_valid(alias_table->mcast_entry_index[i])) {
return 0;
}
}
return 1;
}
RUBY_INLINE int topaz_fwt_sw_mcast_port_is_set(const uint8_t port_bitmap, const uint8_t port)
{
return (port_bitmap & (1 << port));
}
RUBY_INLINE void topaz_fwt_sw_mcast_port_set(struct topaz_fwt_sw_mcast_entry *const e,
const uint8_t port)
{
e->port_bitmap |= (1 << port);
}
RUBY_INLINE void topaz_fwt_sw_mcast_port_clear(struct topaz_fwt_sw_mcast_entry *const e,
const uint8_t port)
{
e->port_bitmap &= ~(1 << port);
}
RUBY_INLINE int topaz_fwt_sw_mcast_port_has_nodes(const uint8_t port)
{
return (port == TOPAZ_TQE_WMAC_PORT);
}
RUBY_INLINE void
topaz_fwt_sw_mcast_flood_forward_set(struct topaz_fwt_sw_mcast_entry *const e, const uint8_t enable)
{
e->flood_forward = enable;
}
RUBY_INLINE int
topaz_fwt_sw_mcast_is_flood_forward(const struct topaz_fwt_sw_mcast_entry *const e)
{
return (e->flood_forward);
}
RUBY_INLINE uint32_t topaz_fwt_sw_mcast_do_per_node(
void (*handler)(const void *token1, void *token2, uint8_t node, uint8_t port, uint8_t tid),
const struct topaz_fwt_sw_mcast_entry *mcast_ent,
const void *token1, void *token2, uint8_t in_node, uint8_t port, uint8_t tid)
{
uint8_t node;
uint8_t node_cnt = 0;
uint32_t bitmap;
uint8_t i;
uint8_t j;
for (i = 0; i < TOPAZ_FWT_SW_NODE_BITMAP_SIZE; i++) {
bitmap = mcast_ent->node_bitmap[i];
j = 0;
while (bitmap) {
if (bitmap & 0x1) {
node = (i * TOPAZ_BITS_PER_WD) + j;
if ((in_node == 0) || (node != in_node)) {
handler(token1, token2, port, node, tid);
node_cnt++;
}
}
bitmap >>= 1;
j++;
}
}
return node_cnt;
}
RUBY_INLINE int topaz_fwt_sw_mcast_node_is_set(const struct topaz_fwt_sw_mcast_entry *const e,
const uint8_t port, const uint8_t node)
{
if (port == TOPAZ_TQE_WMAC_PORT) {
return (e->node_bitmap[node / TOPAZ_BITS_PER_WD] &
(1 << (node % TOPAZ_BITS_PER_WD)));
}
return 0;
}
RUBY_INLINE void topaz_fwt_sw_mcast_node_set(struct topaz_fwt_sw_mcast_entry *const e,
const uint8_t port, const uint8_t node)
{
if (port == TOPAZ_TQE_WMAC_PORT) {
e->node_bitmap[node / TOPAZ_BITS_PER_WD] |= (1 << (node % TOPAZ_BITS_PER_WD));
}
}
RUBY_INLINE void topaz_fwt_sw_mcast_node_clear(struct topaz_fwt_sw_mcast_entry *const e,
const uint8_t port, const uint8_t node)
{
if (port == TOPAZ_TQE_WMAC_PORT) {
e->node_bitmap[node / TOPAZ_BITS_PER_WD] &= ~(1 << (node % TOPAZ_BITS_PER_WD));
if (!topaz_fwt_sw_mcast_is_flood_forward(e) &&
topaz_fwt_sw_mcast_entry_nodes_clear(e)) {
topaz_fwt_sw_mcast_port_clear(e, port);
}
}
}
RUBY_INLINE union topaz_fwt_sw_entry *topaz_fwt_sw_entry_get(uint16_t index)
{
union topaz_fwt_sw_entry *fwt = (void *) (RUBY_SRAM_BEGIN + TOPAZ_FWT_SW_START);
return &fwt[index];
}
RUBY_INLINE struct topaz_fwt_sw_mcast_entry *topaz_fwt_sw_mcast_ff_entry_get(uint8_t vap_idx)
{
return (void *)(RUBY_DRAM_BEGIN + TOPAZ_FWT_MCAST_TQE_FF_BASE +
(vap_idx * TOPAZ_FWT_MCAST_TQE_ENT_SIZE));
}
RUBY_INLINE struct topaz_fwt_sw_mcast_entry *topaz_fwt_sw_mcast_entry_get(uint16_t index)
{
struct topaz_fwt_sw_mcast_entry *fwt = (void *) (RUBY_DRAM_BEGIN + TOPAZ_FWT_MCAST_TQE_BASE);
return &fwt[index];
}
RUBY_INLINE struct topaz_fwt_sw_alias_table *topaz_fwt_sw_alias_table_get(uint16_t index)
{
struct topaz_fwt_sw_alias_table *fwt = (void *) (RUBY_DRAM_BEGIN + TOPAZ_FWT_MCAST_IPMAP_BASE);
return &fwt[index];
}
RUBY_INLINE uint8_t topaz_fwt_sw_count_bits(uint32_t x)
{
uint8_t bits_set = 0;
while (x) {
bits_set++;
x &= x - 1;
}
return bits_set;
}
RUBY_INLINE uint32_t topaz_fwt_sw_mcast_enqueues(const struct topaz_fwt_sw_mcast_entry *const e,
uint8_t port_bitmap, const uint8_t in_port, const uint8_t in_node)
{
uint32_t enqueues = 0;
uint8_t i;
/* Exclude input port. If WMAC, the port doesn't contribute, only nodes. */
port_bitmap &= ~((1 << in_port) | (1 << TOPAZ_TQE_WMAC_PORT));
enqueues += topaz_fwt_sw_count_bits(port_bitmap);
/* add wmac nodes */
for (i = 0; i < ARRAY_SIZE(e->node_bitmap) ; i++) {
enqueues += topaz_fwt_sw_count_bits(e->node_bitmap[i]);
}
/* must exclude the input node */
if (topaz_fwt_sw_mcast_node_is_set(e, in_port, in_node)) {
--enqueues;
}
return enqueues;
}
RUBY_INLINE void __topaz_fwt_hash_set(int enable)
{
uint32_t reg = enable ? TOPAZ_FWT_HASH_CTRL_ENABLE : 0;
qtn_mproc_sync_mem_write(TOPAZ_FWT_HASH_CTRL, reg);
}
RUBY_INLINE void __topaz_fwt_hw_lookup_write_be(const uint8_t *mac_be)
{
uint32_t lo = mac_be[5] | (mac_be[4] << 8) | (mac_be[3] << 16) | (mac_be[2] << 24);
uint32_t hi = mac_be[1] | (mac_be[0] << 8);
qtn_mproc_sync_mem_write(TOPAZ_FWT_LOOKUP_MAC_LO, lo);
qtn_mproc_sync_mem_write(TOPAZ_FWT_LOOKUP_MAC_HI, hi);
qtn_mproc_sync_mem_write(TOPAZ_FWT_LOOKUP_REG, SM(1, TOPAZ_FWT_LOOKUP_TRIG));
}
RUBY_INLINE void __topaz_fwt_hw_lookup_write_le(const uint8_t *mac_le)
{
uint32_t lo = mac_le[0] | (mac_le[1] << 8) | (mac_le[2] << 16) | (mac_le[3] << 24);
uint32_t hi = mac_le[4] | (mac_le[5] << 8);
qtn_mproc_sync_mem_write(TOPAZ_FWT_LOOKUP_MAC_LO, lo);
qtn_mproc_sync_mem_write(TOPAZ_FWT_LOOKUP_MAC_HI, hi);
qtn_mproc_sync_mem_write(TOPAZ_FWT_LOOKUP_REG, SM(1, TOPAZ_FWT_LOOKUP_TRIG));
}
RUBY_INLINE void topaz_fwt_reverse_mac(uint8_t *dest, const uint8_t *src)
{
int i;
for (i = 0; i < ETH_ALEN; i++) {
dest[ETH_ALEN - i - 1] = src[i];
}
}
RUBY_INLINE void topaz_fwt_setup_entry(union topaz_fwt_entry *ent, const uint8_t *mac_be,
uint8_t out_port, const uint8_t *out_nodes,
unsigned int out_node_count, uint8_t portal)
{
ent->raw.word0 = 0;
ent->raw.word1 = 0;
ent->raw.word2 = 0;
ent->raw.word3 = 0;
#pragma Off(Behaved)
topaz_fwt_reverse_mac(ent->data.mac_le, mac_be);
#pragma On(Behaved)
ent->data.valid = 1;
ent->data.portal = portal;
#define __topaz_fwt_setup_entry_set_out_node(x) \
do { \
if (x < out_node_count) { \
ent->data.out_node_##x = out_nodes[x]; \
ent->data.out_node_vld_##x = 1; \
} \
} while(0)
__topaz_fwt_setup_entry_set_out_node(0);
__topaz_fwt_setup_entry_set_out_node(1);
__topaz_fwt_setup_entry_set_out_node(2);
__topaz_fwt_setup_entry_set_out_node(3);
__topaz_fwt_setup_entry_set_out_node(4);
__topaz_fwt_setup_entry_set_out_node(5);
ent->data.out_port = out_port;
ent->data.timestamp = topaz_fwt_get_scaled_timestamp();
}
union topaz_fwt_lookup {
struct {
uint32_t word0;
} raw;
struct {
uint32_t trig :1,
__unused :7,
hash_addr :10,
__unused2 :2,
entry_addr :11,
valid :1;
} data;
};
#define FWT_ZERO_LOOKUP_INIT { { 0 } }
RUBY_INLINE union topaz_fwt_lookup __topaz_fwt_hw_lookup_rd(void)
{
union topaz_fwt_lookup u;
u.raw.word0 = qtn_mproc_sync_mem_read(TOPAZ_FWT_LOOKUP_REG);
return u;
}
RUBY_INLINE union topaz_fwt_lookup __topaz_fwt_hw_lookup_wait_be(const uint8_t *mac_be, int *timeout)
{
unsigned long timeouts = 0;
union topaz_fwt_lookup u;
union topaz_fwt_lookup zero_lookup = FWT_ZERO_LOOKUP_INIT;
__topaz_fwt_hw_lookup_write_be(mac_be);
while (1) {
u = __topaz_fwt_hw_lookup_rd();
if (u.data.trig == 0) {
*timeout = 0;
return u;
} else {
qtn_pipeline_drain();
if (unlikely(timeouts++ > 1000)) {
*timeout = 1;
return zero_lookup;
}
}
}
return zero_lookup;
}
RUBY_INLINE union topaz_fwt_lookup topaz_fwt_hw_lookup_wait_be(const uint8_t *mac_be, int *timeout, uint8_t *false_miss)
{
#ifndef TOPAZ_DISABLE_FWT_WAR
/*
* This is to workaround the FWT lookup false issue:
* It seems when EMAC is under heavy VLAN traffic, Lhost and MuC
* may get false miss from FWT -- FWT returns invalid while a MAC
* address truly exists in it. A double check reduces count of false
* misses significantly.
*/
uint8_t retries = 1;
#else
uint8_t retries = 0;
#endif
union topaz_fwt_lookup u;
do {
u = __topaz_fwt_hw_lookup_wait_be(mac_be, timeout);
} while (!u.data.valid && retries--);
#if !defined(TOPAZ_DISABLE_FWT_WAR) && !defined(MUC_BUILD)
*false_miss += (retries == 0);
#endif
return u;
}
RUBY_INLINE uint32_t __topaz_fwt_cpu_access_rd(void)
{
return qtn_mproc_sync_mem_read(TOPAZ_FWT_CPU_ACCESS);
}
RUBY_INLINE void __topaz_fwt_set_4addrmode(union topaz_fwt_entry *ent, uint8_t portal)
{
ent->data.portal = !!portal;
}
RUBY_INLINE void __topaz_fwt_set_port(union topaz_fwt_entry *ent, uint8_t port)
{
ent->data.out_port = port;
}
RUBY_INLINE void __topaz_fwt_set_node(union topaz_fwt_entry *ent,
uint8_t node_index, uint8_t node_num, bool enable)
{
#define ____topaz_fwt_set_node(n) \
case n: do { \
ent->data.out_node_##n = node_num; \
ent->data.out_node_vld_##n = !!enable; \
} while(0); \
break
switch (node_index) {
____topaz_fwt_set_node(0);
____topaz_fwt_set_node(1);
____topaz_fwt_set_node(2);
____topaz_fwt_set_node(3);
____topaz_fwt_set_node(4);
____topaz_fwt_set_node(5);
default:
break;
}
}
RUBY_INLINE int __topaz_fwt_cpu_access_start_wait(void)
{
#ifndef CONSOLE_TEST
unsigned long timeouts = 0;
qtn_mproc_sync_mem_write(TOPAZ_FWT_CPU_ACCESS, TOPAZ_FWT_CPU_ACCESS_REQ);
while (timeouts++ < 1000) {
uint32_t reg = __topaz_fwt_cpu_access_rd();
if (MS(reg, TOPAZ_FWT_CPU_ACCESS_STATE) == TOPAZ_FWT_CPU_ACCESS_STATE_GRANTED) {
return 0;
}
qtn_pipeline_drain();
}
return -1;
#else
return 0;
#endif
}
RUBY_INLINE void __topaz_fwt_cpu_access_stop(void)
{
#ifndef CONSOLE_TEST
qtn_mproc_sync_mem_write(TOPAZ_FWT_CPU_ACCESS, 0);
#endif
}
RUBY_INLINE int topaz_fwt_cpu_access_lock_wait_irqsave(unsigned long *flags)
{
#ifndef CONSOLE_TEST
int rc;
local_irq_save(*flags);
rc = __topaz_fwt_cpu_access_start_wait();
if (rc) {
local_irq_restore(*flags);
}
return rc;
#else
(void)flags;
return 0;
#endif
}
RUBY_INLINE void topaz_fwt_cpu_access_unlock_irqrestore(unsigned long *flags)
{
#ifndef CONSOLE_TEST
__topaz_fwt_cpu_access_stop();
local_irq_restore(*flags);
#else
(void)flags;
#endif
}
#ifndef TOPAZ_TEST_ASSERT_EQUAL
# define TOPAZ_TEST_ASSERT_EQUAL(a, b) if ((a) != (b)) { return -1; }
#endif
RUBY_INLINE int topaz_fwt_lookup_bitfield_test(const union topaz_fwt_lookup *e)
{
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word0, TOPAZ_FWT_LOOKUP_TRIG), e->data.trig);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word0, TOPAZ_FWT_LOOKUP_ENTRY_ADDR), e->data.entry_addr);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word0, TOPAZ_FWT_LOOKUP_HASH_ADDR), e->data.hash_addr);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word0, TOPAZ_FWT_LOOKUP_VALID), e->data.valid);
return 0;
}
RUBY_INLINE int topaz_fwt_entry_bitfield_test(const union topaz_fwt_entry *e)
{
TOPAZ_TEST_ASSERT_EQUAL(!!topaz_fwt_is_valid(e), e->data.valid);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word1, TOPAZ_FWT_ENTRY_NXT_ENTRY), e->data.next_index);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word2, TOPAZ_FWT_ENTRY_OUT_NODE_0), e->data.out_node_0);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word2, TOPAZ_FWT_ENTRY_OUT_NODE_VLD_0), e->data.out_node_vld_0);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word2, TOPAZ_FWT_ENTRY_OUT_NODE_1), e->data.out_node_1);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word2, TOPAZ_FWT_ENTRY_OUT_NODE_VLD_1), e->data.out_node_vld_1);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word2, TOPAZ_FWT_ENTRY_OUT_NODE_2), e->data.out_node_2);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word2, TOPAZ_FWT_ENTRY_OUT_NODE_VLD_2), e->data.out_node_vld_2);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word2, TOPAZ_FWT_ENTRY_OUT_NODE_3), e->data.out_node_3);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word2, TOPAZ_FWT_ENTRY_OUT_NODE_VLD_3), e->data.out_node_vld_3);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word3, TOPAZ_FWT_ENTRY_OUT_NODE_4), e->data.out_node_4);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word3, TOPAZ_FWT_ENTRY_OUT_NODE_VLD_4), e->data.out_node_vld_4);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word3, TOPAZ_FWT_ENTRY_OUT_NODE_5), e->data.out_node_5);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word3, TOPAZ_FWT_ENTRY_OUT_NODE_VLD_5), e->data.out_node_vld_5);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word3, TOPAZ_FWT_ENTRY_OUT_PORT), e->data.out_port);
TOPAZ_TEST_ASSERT_EQUAL(MS(e->raw.word3, TOPAZ_FWT_ENTRY_TIMESTAMP), e->data.timestamp);
#ifdef __KERNEL_
TOPAZ_TEST_ASSERT_EQUAL(memcmp(topaz_fwt_macaddr(e), e->data.mac_le, ETH_ALEN), 0);
#endif
return 0;
}
struct topaz_ipmac_uc_entry {
struct topaz_ipmac_uc_entry *next;
union {
uint8_t ipv4_addr[4];
uint8_t ipv6_addr[16];
}u;
uint8_t mac_addr[MAC_ADDR_LEN];
uint16_t type;
struct topaz_ipmac_uc_entry *lhost_next;
};
#define TOPAZ_IPMAC_UC_HASH_SLOT 128
#define TOPAZ_IPMAC_UC_HASH_SLOT_MASK 0x7f
#define TOPAZ_IPMAC_UC_HASH_SIZE (TOPAZ_IPMAC_UC_HASH_SLOT * sizeof(void *))
#define TOPAZ_IPMAC_UC_ENTRY_SIZE sizeof(struct topaz_ipmac_uc_entry)
#define TOPAZ_IPMAC_UC_ENTRY_COUNT 31
struct topaz_ipmac_uc_table {
struct topaz_ipmac_uc_entry *slots[TOPAZ_IPMAC_UC_HASH_SLOT];
struct topaz_ipmac_uc_entry entries[TOPAZ_IPMAC_UC_ENTRY_COUNT];
uint32_t update_cnt_lhost;
uint32_t update_cnt_muc;
};
#define TOPAZ_IPMAC_UC_TBL_SIZE (sizeof(struct topaz_ipmac_uc_table))
/*
* The hash works under the assumption that in most cases, hosts behind
* the STA are in the same IP subnet. The host number differs so we have
* a good chance to have diverse MSB byte of a be IP address
*/
RUBY_INLINE uint16_t topaz_ipmac_uc_hash(__be32 ipaddr)
{
return ((ipaddr >> 24) & TOPAZ_IPMAC_UC_HASH_SLOT_MASK);
}
RUBY_INLINE uint16_t topaz_ipmac_ipv6uc_hash(const uint8_t *ipv6_addr)
{
return (ipv6_addr[15] & TOPAZ_IPMAC_UC_HASH_SLOT_MASK);
}
#endif /* __TOPAZ_FWT_CPUIF_PLATFORM_H */