--- drivers/net/ethernet/cavium/thunder/tbl_access.c | 262 +++++++++++++++++++++++ drivers/net/ethernet/cavium/thunder/tbl_access.h | 61 ++++++ 2 files changed, 323 insertions(+) create mode 100644 drivers/net/ethernet/cavium/thunder/tbl_access.c create mode 100644 drivers/net/ethernet/cavium/thunder/tbl_access.h
diff --git a/drivers/net/ethernet/cavium/thunder/tbl_access.c b/drivers/net/ethernet/cavium/thunder/tbl_access.c new file mode 100644 index 0000000..6be31eb --- /dev/null +++ b/drivers/net/ethernet/cavium/thunder/tbl_access.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2015 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#include <linux/uaccess.h> +#include "pf_globals.h" +#include "pf_locals.h" +#include "tbl_access.h" + +struct tns_table_s *get_table_information(int table_id) +{ + int i; + + for (i = 0; i < TNS_MAX_TABLE; i++) { + if (!tbl_info[i].sdata.valid) + continue; + + if (tbl_info[i].sdata.tbl_id == table_id) + return &tbl_info[i]; + } + + return NULL; +} + +int tbl_write(int node, int table_id, int tbl_index, void *key, void *mask, + void *data) +{ + int i; + struct tns_table_s *tbl = get_table_information(table_id); + int bck_cnt, data_index, data_offset; + u64 data_entry[4]; + + if (!tbl) { + filter_dbg(FERR, "Invalid Table ID: %d\n", table_id); + return TNS_ERR_INVALID_TBL_ID; + } + + bck_cnt = tbl->sdata.data_width / tbl->sdata.data_size; + data_index = (tbl_index / bck_cnt); + data_offset = (tbl_index % bck_cnt); + //TCAM Table, we need to parse key & mask into single array + if (tbl->sdata.tbl_type == TNS_TBL_TYPE_TT) { + struct filter_keymask_s *tk = (struct filter_keymask_s *)key; + struct filter_keymask_s *tm = (struct filter_keymask_s *)mask; + u8 km[32]; + u64 mod_key, mod_mask, temp_mask; + int index = 0, offset = 0; + + memset(km, 0x0, 32); + +/* TCAM truth table data creation. Translation from data/mask to following + * truth table: + * + * Mask Data Content + * 0 0 X + * 0 1 1 + * 1 0 0 + * 1 1 Always Mismatch + * + */ + mod_mask = ~tk->key_value; + temp_mask = tm->key_value; + mod_key = tk->key_value; + mod_key = mod_key & (~temp_mask); + mod_mask = mod_mask & (~temp_mask); + + for (i = 0; i < 64; i++) { + km[index] = km[index] | (((mod_mask >> i) & 0x1) << + offset); + km[index] = km[index] | (((mod_key >> i) & 0x1) << + (offset + 1)); + offset += 2; + if (offset == 8) { + offset = 0; + index += 1; + } + } + km[index] = 0x2; + if (tns_write_register_indirect(node, + (tbl->sdata.key_base_addr + + (tbl_index * 32)), 32, + (void *)&km[0])) { + filter_dbg(FERR, "key write failed node %d tbl ID %d", + node, table_id); + filter_dbg(FERR, " index %d\n", tbl_index); + return TNS_ERR_DRIVER_WRITE; + } + } + + /* Data Writes are ReadModifyWrite */ + if (tns_read_register_indirect(node, (tbl->sdata.data_base_addr + + (data_index * 32)), 32, + (void *)&data_entry[0])) { + filter_dbg(FERR, "data read failed node %d tbl ID %d idx %d\n", + node, table_id, tbl_index); + return TNS_ERR_DRIVER_READ; + } + memcpy(&data_entry[data_offset], data, tbl->sdata.data_size / 8); + if (tns_write_register_indirect(node, (tbl->sdata.data_base_addr + + (data_index * 32)), 32, + (void *)&data_entry[0])) { + filter_dbg(FERR, "data write failed node %d tbl ID %d idx %d\n", + node, table_id, tbl_index); + return TNS_ERR_DRIVER_WRITE; + } + + return TNS_NO_ERR; +} + +int tbl_read(int node, int table_id, int tbl_index, void *key, void *mask, + void *data) +{ + struct tns_table_s *tbl = get_table_information(table_id); + int i, bck_cnt, data_index, data_offset; + u64 data_entry[4]; + u8 km[32]; + + if (!tbl) { + filter_dbg(FERR, "Invalid Table ID: %d\n", table_id); + return TNS_ERR_INVALID_TBL_ID; + } + + bck_cnt = tbl->sdata.data_width / tbl->sdata.data_size; + data_index = (tbl_index / bck_cnt); + data_offset = (tbl_index % bck_cnt); + + //TCAM Table, we need to parse key & mask into single array + if (tbl->sdata.tbl_type == TNS_TBL_TYPE_TT) { + memset(km, 0x0, 32); + + if (tns_read_register_indirect(node, (tbl->sdata.key_base_addr + + (tbl_index * 32)), 32, + (void *)&km[0])) { + filter_dbg(FERR, "key read failed node %d tbl ID %d", + node, table_id); + filter_dbg(FERR, " idx %d\n", tbl_index); + return TNS_ERR_DRIVER_READ; + } + if (!(km[((tbl->sdata.key_size * 2) / 8)] == 0x2)) + return TNS_ERR_MAC_FILTER_INVALID_ENTRY; + } + + if (tns_read_register_indirect(node, (tbl->sdata.data_base_addr + + (data_index * 32)), 32, + (void *)&data_entry[0])) { + filter_dbg(FERR, "data read failed node %d tbl ID %d idx %d\n", + node, table_id, tbl_index); + return TNS_ERR_DRIVER_READ; + } + memcpy(data, (void *)(&data_entry[data_offset]), + (tbl->sdata.data_size / 8)); + + if (tbl->sdata.tbl_type == TNS_TBL_TYPE_TT) { + struct filter_keymask_s *tk = (struct filter_keymask_s *)key; + struct filter_keymask_s *tm = (struct filter_keymask_s *)mask; + u8 temp_km; + int index = 0, offset = 0; + + tk->key_value = 0x0ull; + tm->key_value = 0x0ull; + temp_km = km[0]; + for (i = 0; i < 64; i++) { + tm->key_value = tm->key_value | + ((temp_km & 0x1ull) << i); + temp_km >>= 1; + tk->key_value = tk->key_value | + ((temp_km & 0x1ull) << i); + temp_km >>= 1; + offset += 2; + if (offset == 8) { + offset = 0; + index += 1; + temp_km = km[index]; + } + } + tm->key_value = ~tm->key_value & ~tk->key_value; + tk->is_valid = 1; + tm->is_valid = 0; + } + + return TNS_NO_ERR; +} + +int invalidate_table_entry(int node, int table_id, int tbl_idx) +{ + struct tns_table_s *tbl = get_table_information(table_id); + + if (!tbl) { + filter_dbg(FERR, "Invalid Table ID: %d\n", table_id); + return TNS_ERR_INVALID_TBL_ID; + } + + if (tbl->sdata.tbl_type == TNS_TBL_TYPE_TT) { + u8 km[32]; + + memset(km, 0x0, 32); + km[((tbl->sdata.key_size * 2) / 8)] = 0x1; + + if (tns_write_register_indirect(node, + (tbl->sdata.key_base_addr + + (tbl_idx * 32)), 32, + (void *)&km[0])) { + filter_dbg(FERR, "%s failed node %d tbl ID %d idx %d\n", + __func__, node, table_id, tbl_idx); + return TNS_ERR_DRIVER_WRITE; + } + } + + return TNS_NO_ERR; +} + +int alloc_table_index(int node, int table_id, int *index) +{ + int err = 0; + struct tns_table_s *tbl = get_table_information(table_id); + + if (!tbl) { + filter_dbg(FERR, "%s Invalid TableID %d\n", __func__, table_id); + return TNS_ERR_INVALID_TBL_ID; + } + + if (*index == -1) { + *index = find_first_zero_bit(tbl->ddata[node].bitmap, + tbl->sdata.depth); + + if (*index < 0 || *index >= tbl->sdata.depth) + err = -ENOSPC; + else + __set_bit(*index, tbl->ddata[node].bitmap); + + return err; + } else if (*index < 0 || *index >= tbl->sdata.depth) { + filter_dbg(FERR, "%s Out of bound index %d requested[0...%d]\n", + __func__, *index, tbl->sdata.depth); + return TNS_ERR_MAC_FILTER_INVALID_ENTRY; + } + if (test_and_set_bit(*index, tbl->ddata[node].bitmap)) + filter_dbg(FDEBUG, "%s Entry Already exists\n", __func__); + + return err; +} + +void free_table_index(int node, int table_id, int index) +{ + struct tns_table_s *tbl = get_table_information(table_id); + + if (!tbl) { + filter_dbg(FERR, "%s Invalid TableID %d\n", __func__, table_id); + return; + } + if (index < 0 || index >= tbl->sdata.depth) { + filter_dbg(FERR, "%s Invalid Index %d Max Limit %d\n", + __func__, index, tbl->sdata.depth); + return; + } + + __clear_bit(index, tbl->ddata[node].bitmap); +} diff --git a/drivers/net/ethernet/cavium/thunder/tbl_access.h b/drivers/net/ethernet/cavium/thunder/tbl_access.h new file mode 100644 index 0000000..c098410 --- /dev/null +++ b/drivers/net/ethernet/cavium/thunder/tbl_access.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015 Cavium, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + */ + +#ifndef __TBL_ACCESS_H__ +#define __TBL_ACCESS_H__ + +#define TNS_MAX_TABLE 8 + +enum { + TNS_TBL_TYPE_DT, + TNS_TBL_TYPE_HT, + TNS_TBL_TYPE_TT, + TNS_TBL_TYPE_MAX +}; + +struct table_static_s { + u8 tbl_type; + u8 tbl_id; + u8 valid; + u8 rsvd; + u16 key_size; + u16 data_size; + u16 data_width; + u16 key_width; + u32 depth; + u64 key_base_addr; + u64 data_base_addr; + u8 tbl_name[32]; +}; + +struct table_dynamic_s { + unsigned long *bitmap; +}; + +struct tns_table_s { + struct table_static_s sdata; + struct table_dynamic_s ddata[MAX_NUMNODES]; +}; + +enum { + MAC_FILTER_TABLE = 102, + VLAN_FILTER_TABLE = 103, + MAC_EVIF_TABLE = 140, + VLAN_EVIF_TABLE = 201, + PORT_CONFIG_TABLE = 202, + TABLE_ID_END +}; + +extern struct tns_table_s tbl_info[TNS_MAX_TABLE]; + +struct filter_keymask_s { + u8 is_valid; + u64 key_value; +}; + +#endif /* __TBL_ACCESS_H__ */ -- 1.8.3.1