net: move tcam from swconfig to dsa mv88e6xxx

Signed-off-by: Andreas Schmidt <andreas.schmidt@wago.com>
This commit is contained in:
Andreas Schmidt
2018-09-26 13:36:48 +02:00
committed by Oleg Karfich
parent 284700b29a
commit 78a7d314d4
4 changed files with 1173 additions and 2 deletions
+1 -2
View File
@@ -1,5 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o
obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o mv88e6321_tcam.o
mv88e6xxx-objs := chip.o
mv88e6xxx-objs += devlink.o
mv88e6xxx-objs += global1.o
+969
View File
@@ -0,0 +1,969 @@
#define pr_fmt(fmt) "mv88e6321: tcam:" fmt
#include <linux/delay.h>
#include "mv88e6321_tcam.h"
#include "mv88e6xxx.h"
#define OF_TCAM_ENTRIES_PROP_READ_U8(__np, __name, __out, __ret, __goto) do { \
u32 val__; \
(__ret) = of_property_read_u32(__np, __name, &val__); \
if ((__ret) < 0) { \
pr_err("no %s property found for tcam entry %s ret: (%d)\n", \
__name, __np->name, __ret); \
goto __goto; \
} \
tcam_entries->__out = (u8)val__; \
pr_debug("tcam set %s = 0x%04X\n", __name, val__); \
} while (0)
#define OF_TCAM_ENTRIES_PROP_READ_OPT_U8(__np, __name, __out, __ret, __goto) \
do { \
u32 val__; \
if (of_property_read_u32(__np, __name, &val__) >= 0) { \
tcam_entries->__out = (u8)val__; \
pr_debug("tcam set %s = 0x%04X\n", __name, val__); \
} \
} while (0)
#define OF_TCAM_PROP_READ_U8(__np, __name, __out, __ret, __goto) do { \
u32 val__; \
(__ret) = of_property_read_u32(__np, __name, &val__); \
if ((__ret) < 0) { \
pr_err("no %s property found for tcam entry %s ret: (%d)\n", \
__name, __np->name, __ret); \
kfree(tcam_entry); \
goto __goto; \
} \
tcam_entry->__out = (u8)val__; \
pr_debug("tcam set %s = 0x%04X\n", __name, val__); \
} while (0)
#define OF_TCAM_PROP_READ_OPT_U8(__np, __name, __out) do { \
u32 val__; \
if (of_property_read_u32(__np, __name, &val__) >= 0) { \
tcam_entry->__out = (u8)val__; \
pr_debug("tcam set %s = 0x%04X\n", __name, val__); \
} \
} while (0)
#define OF_TCAM_PROP_READ_U16(__np, __name, __out, __ret, __goto) do { \
u32 val__; \
__ret = of_property_read_u32(__np, __name, &val__); \
if ((__ret) < 0) { \
pr_err("no %s property found for tcam entry %s\n", \
__name, __np->name); \
kfree(tcam_entry); \
goto __goto; \
} \
tcam_entry->__out = (u16)val__; \
pr_debug("tcam set %s = 0x%04X\n", __name, val__); \
} while (0)
#define OF_TCAM_PROP_READ_COND_U8(__np, __name, __mask, __out, __ret, __goto) \
do { \
if (tcam_entry->__mask) \
OF_TCAM_PROP_READ_U8(__np, __name, __out, __ret, __goto); \
} while (0)
#define OF_TCAM_PROP_READ_COND_U16(__np, __name, __mask, __out, __ret, __goto) \
do { \
if (tcam_entry->__mask) \
OF_TCAM_PROP_READ_U16(__np, __name, __out, __ret, __goto); \
} while (0)
#define OF_TCAM_PROP_READ_BOOL(__np, __name, __out) do {\
if (of_property_read_bool((__np), (__name))) {\
tcam_entry->__out = 1; \
pr_debug("tcam set %s = 1\n", (__name)); \
} \
} while (0)
static int of_set_frame_data(struct tcam_entry *tcam_entry,
struct device_node *np)
{
int ret;
u8 buf[TCAM_FRAME_DATA_MAX_SIZE];
u8 is_mask = 0;
u8 is_96_mask = 0;
/* get frame data mask */
ret = of_property_read_variable_u8_array(np, "frame-data-mask", buf, 0,
TCAM_FRAME_DATA_MAX_SIZE);
if (ret < 0)
return ret;
if (!ret)
return -ENODATA;
while (ret--) {
pr_debug("frame_data[%d].mask = 0x%02X\n", ret, buf[ret]);
tcam_entry->frame_data[ret].mask = buf[ret];
is_mask |= buf[ret];
if (ret >= 48)
is_96_mask |= buf[ret];
}
tcam_entry->is96frame = (is_96_mask) ? 1 : 0;
if (!is_mask)
return 0;
/* get frame data */
ret = of_property_read_variable_u8_array(np, "frame-data", buf, 0,
TCAM_FRAME_DATA_MAX_SIZE);
if (ret < 0)
return ret;
if (!ret)
return -ENODATA;
while (ret--) {
pr_debug("frame_data[%d].data = 0x%02X\n", ret, buf[ret]);
tcam_entry->frame_data[ret].data = buf[ret];
}
return 0;
}
int of_get_tcam_entry(struct tcam_entries *tcam_entries, struct device_node *np)
{
int ret;
struct device_node *tcam_np;
struct device_node *child = NULL;
struct tcam_entry *tcam_entry = NULL;
struct tcam_entry *tcam_entry_tmp = NULL;
tcam_np = of_find_compatible_node(np, NULL, "mv88e6321,tcam");
if (!tcam_np)
return -ENODEV;
OF_TCAM_ENTRIES_PROP_READ_U8(tcam_np, "tcam-mode-port-mask", port_mask,
ret, out);
OF_TCAM_ENTRIES_PROP_READ_OPT_U8(tcam_np, "debug-port", debug_port,
ret, out);
for_each_child_of_node(tcam_np, child) {
pr_info("process tcam entry node %s\n", child->name);
tcam_entry = kzalloc(sizeof(*tcam_entry), GFP_KERNEL);
if (!tcam_entry) {
ret = -ENOMEM;
goto out_free;
}
tcam_entry->title = child->name;
OF_TCAM_PROP_READ_U8(child, "id", orig_id, ret, out_free);
OF_TCAM_PROP_READ_OPT_U8(child, "frame-type-mask",
mask_frame_type);
OF_TCAM_PROP_READ_COND_U8(child, "frame-type", mask_frame_type,
frame_type, ret, out_free);
OF_TCAM_PROP_READ_U8(child, "ppri-mask", mask_ppri, ret,
out_free);
OF_TCAM_PROP_READ_COND_U8(child, "ppri", mask_ppri, orig_ppri,
ret, out_free);
OF_TCAM_PROP_READ_U8(child, "pvid-mask", mask_pvid, ret,
out_free);
OF_TCAM_PROP_READ_COND_U16(child, "pvid", mask_pvid, orig_pvid,
ret, out_free);
OF_TCAM_PROP_READ_OPT_U8(child, "spv-mask", mask_spv);
OF_TCAM_PROP_READ_COND_U8(child, "spv", mask_spv, spv, ret,
out_free);
OF_TCAM_PROP_READ_BOOL(child, "vid-override", vid_override);
OF_TCAM_PROP_READ_COND_U16(child, "vid-data", vid_override, vid,
ret, out_free);
OF_TCAM_PROP_READ_BOOL(child, "interrupt", interrupt);
OF_TCAM_PROP_READ_BOOL(child, "inc-tcam-ctr", inc_tcam_ctr);
OF_TCAM_PROP_READ_BOOL(child, "fpri-override", fpri_override);
OF_TCAM_PROP_READ_COND_U8(child, "fpri-data", fpri_override,
fpri, ret, out_free);
OF_TCAM_PROP_READ_BOOL(child, "qpri-override", qpri_override);
OF_TCAM_PROP_READ_COND_U8(child, "qpri-data", qpri_override,
qpri, ret, out_free);
OF_TCAM_PROP_READ_U8(child, "next-id", next_id, ret, out_free);
OF_TCAM_PROP_READ_BOOL(child, "dpv-override", dpv_override);
OF_TCAM_PROP_READ_COND_U8(child, "dpv-data", dpv_override, dpv,
ret, out_free);
OF_TCAM_PROP_READ_BOOL(child, "ld-balance-override",
ld_balance_override);
OF_TCAM_PROP_READ_COND_U8(child, "ld-balance-data",
ld_balance_override, ld_balance, ret,
out_free);
OF_TCAM_PROP_READ_BOOL(child, "action-override",
action_override);
OF_TCAM_PROP_READ_COND_U16(child, "action-data",
action_override, action, ret,
out_free);
ret = of_set_frame_data(tcam_entry, child);
if (ret) {
kfree(tcam_entry);
goto out_free;
}
/* let every tcam_entry have same debug port */
tcam_entry->debug_port = tcam_entries->debug_port;
list_add(&tcam_entry->list, &tcam_entries->head.list);
}
return 0;
out_free:
list_for_each_entry_safe(tcam_entry, tcam_entry_tmp,
&tcam_entries->head.list, list) {
list_del(&tcam_entry->list);
kfree(tcam_entry);
}
out:
return ret;
}
static inline int mv88e6321_write_tcam_pg_hdr(struct mii_bus *bus,
struct tcam_entry *tcam_entry)
{
tcam_entry->busy = 1;
return mdiobus_write(bus, TCAM_ADDR, TCAM_REG_PG_HDR,
tcam_entry->reg_pg_hdr);
}
static inline int mv88e6321_wait_tcam_ready(struct mii_bus *bus)
{
int ret;
int attempt;
for (attempt = 0; attempt < 16; ++attempt) {
ret = mdiobus_read(bus, TCAM_ADDR, TCAM_REG_PG_HDR);
if (ret < 0)
return ret;
if (!(ret & TCAM_PG_HDR_BUSY))
return ret;
usleep_range(1000, 2000);
}
pr_err("timeout while waiting for tcam ready\n");
return -ETIMEDOUT;
}
static int mv88e6321_load_tcam_page2(struct mii_bus *bus,
struct tcam_entry *tcam_entry,
int entry_no)
{
int ret;
tcam_entry->pg = 2;
pr_debug("load page 2\n");
ret = mv88e6321_wait_tcam_ready(bus);
if (ret < 0)
return ret;
if (entry_no == 0 && tcam_entry->is96frame)
tcam_entry->cnt = 1;
else
tcam_entry->cnt = 0;
ret = mdiobus_write(bus, TCAM_ADDR, TCAM_REG_PG2_ACTION_4,
tcam_entry->reg_action_4);
if (ret < 0)
return ret;
ret = mdiobus_write(bus, TCAM_ADDR, TCAM_REG_PG2_ACTION_3,
tcam_entry->reg_action_3);
if (ret < 0)
return ret;
ret = mdiobus_write(bus, TCAM_ADDR, TCAM_REG_PG2_ACTION_2,
tcam_entry->reg_action_2);
if (ret < 0)
return ret;
ret = mdiobus_write(bus, TCAM_ADDR, TCAM_REG_PG2_ACTION_1,
tcam_entry->reg_action_1);
if (ret < 0)
return ret;
ret = mdiobus_write(bus, TCAM_ADDR, TCAM_REG_PG2_DEBUG_PORT,
tcam_entry->reg_debug);
if (ret < 0)
return ret;
return mv88e6321_write_tcam_pg_hdr(bus, tcam_entry);
}
static int mv88e6321_load_tcam_page1(struct mii_bus *bus,
struct tcam_entry *tcam_entry,
int entry_no)
{
int ret;
int i = 22;
int max = 0;
int reg = 0;
tcam_entry->pg = 1;
pr_debug("load page 1\n");
ret = mv88e6321_wait_tcam_ready(bus);
if (ret < 0)
return ret;
if (entry_no > 0)
i = 70;
max = i + 26;
for (reg = 2; i < max; ++i, ++reg) {
ret = mdiobus_write(bus, TCAM_ADDR, reg,
tcam_entry->frame_data[i].reg_data);
if (ret < 0)
return ret;
}
return 0;
}
static int mv88e6321_load_tcam_page0(struct mii_bus *bus,
struct tcam_entry *tcam_entry, int entry_no)
{
int ret;
int i = 0;
int max = 0;
int reg = 0;
u16 reg_frame_type = 0;
tcam_entry->pg = 0;
pr_debug("load page 0\n");
if (tcam_entry->frame_type == TCAM_FT_PROV_TAG && entry_no == 0) {
tcam_entry->id_pvid = (tcam_entry->orig_pvid & 0xFF);
tcam_entry->pvid = ((tcam_entry->orig_pvid >> 8) & 0xF);
tcam_entry->mask_id_pvid = (tcam_entry->mask_pvid & 0xFF);
tcam_entry->mask_ppri_pvid = (((u16)tcam_entry->mask_ppri) << 8);
tcam_entry->mask_ppri_pvid += ((tcam_entry->mask_pvid >> 8) & 0xF);
}
if (entry_no > 0) {
reg_frame_type = 0xC000;
reg_frame_type += TCAM_FT_CONTINUE;
} else {
reg_frame_type = tcam_entry->reg_frame_type;
}
ret = mv88e6321_wait_tcam_ready(bus);
if (ret < 0)
return ret;
ret = mdiobus_write(bus, TCAM_ADDR, TCAM_REG_PG0_KEY_1, reg_frame_type);
if (ret < 0)
return ret;
ret = mdiobus_write(bus, TCAM_ADDR, TCAM_REG_PG0_KEY_2,
tcam_entry->reg_spv);
if (ret < 0)
return ret;
ret = mdiobus_write(bus, TCAM_ADDR, TCAM_REG_PG0_KEY_3,
tcam_entry->reg_ppri_pvid);
if (ret < 0)
return ret;
ret = mdiobus_write(bus, TCAM_ADDR, TCAM_REG_PG0_KEY_4,
tcam_entry->reg_id_ppri);
if (ret < 0)
return ret;
if (entry_no > 0)
i = 48;
max = i + 22;
for (reg = 6; i < max; ++i, ++reg) {
ret = mdiobus_write(bus, TCAM_ADDR, reg,
tcam_entry->frame_data[i].reg_data);
if (ret < 0)
return ret;
}
return mv88e6321_write_tcam_pg_hdr(bus, tcam_entry);
}
int mv88e6321_load_tcam(struct mii_bus *bus,
struct tcam_entry *tcam_entry)
{
int ret;
int entry_no = 1;
if (!bus)
return -ENODEV;
tcam_entry->op = TCAM_OP_LOAD;
if (tcam_entry->is96frame)
entry_no = 2;
while (entry_no--) {
if (entry_no > 0) {
tcam_entry->id_pvid = tcam_entry->next_id;
tcam_entry->mask_id_pvid = 0xFF;
tcam_entry->id = tcam_entry->next_id;
tcam_entry->next_id = 0;
} else {
tcam_entry->id_pvid = 0;
tcam_entry->mask_id_pvid = 0;
tcam_entry->next_id = tcam_entry->id;
tcam_entry->id = tcam_entry->orig_id;
}
ret = mv88e6321_load_tcam_page2(bus, tcam_entry, entry_no);
if (ret < 0)
return ret;
ret = mv88e6321_load_tcam_page1(bus, tcam_entry, entry_no);
if (ret < 0)
return ret;
ret = mv88e6321_load_tcam_page0(bus, tcam_entry, entry_no);
if (ret < 0)
return ret;
}
return 0;
}
static int mv88e6321_get_tcam_pg0(struct mii_bus *bus, struct tcam_entry
*tcam_entry, int entry_no)
{
int ret;
int i = 0;
int reg = 0;
int max = 0;
pr_debug("read tcam page 0\n");
tcam_entry->pg = 0;
ret = mv88e6321_write_tcam_pg_hdr(bus, tcam_entry);
if (ret < 0)
return ret;
if (entry_no == 0) {
ret = mdiobus_read(bus, TCAM_ADDR, TCAM_REG_PG0_KEY_1);
if (ret < 0)
return ret;
tcam_entry->reg_frame_type = ((u16)ret);
if (tcam_entry->reg_frame_type != 0x00FF)
tcam_entry->is_valid = 1;
else
return ret;
ret = mdiobus_read(bus, TCAM_ADDR, TCAM_REG_PG0_KEY_2);
if (ret < 0)
return ret;
tcam_entry->reg_spv = ((u16)ret);
ret = mdiobus_read(bus, TCAM_ADDR, TCAM_REG_PG0_KEY_3);
if (ret < 0)
return ret;
tcam_entry->reg_ppri_pvid = ((u16)ret);
tcam_entry->mask_ppri = (((u8)tcam_entry->mask_ppri_pvid) >> 4);
tcam_entry->orig_pvid = tcam_entry->pvid;
tcam_entry->orig_pvid <<= 8;
tcam_entry->mask_pvid = (tcam_entry->mask_ppri_pvid & 0xF);
tcam_entry->mask_pvid <<= 8;
ret = mdiobus_read(bus, TCAM_ADDR, TCAM_REG_PG0_KEY_4);
if (ret < 0)
return ret;
tcam_entry->reg_id_ppri = ((u16)ret);
tcam_entry->orig_pvid += tcam_entry->id_pvid;
tcam_entry->mask_pvid += tcam_entry->mask_id_pvid;
}
if (entry_no > 0)
i = 48;
max = i + 22;
for (reg = 6; i < max; ++i, ++reg) {
ret = mdiobus_read(bus, TCAM_ADDR, reg);
if (ret < 0)
return ret;
tcam_entry->frame_data[i].reg_data = ((u16)ret);
}
return 0;
}
static int mv88e6321_get_tcam_pg1(struct mii_bus *bus, struct tcam_entry
*tcam_entry, int entry_no)
{
int ret;
int i = 22;
int reg = 0;
int max = 0;
pr_debug("read tcam page 1\n");
tcam_entry->pg = 1;
ret = mv88e6321_write_tcam_pg_hdr(bus, tcam_entry);
if (ret < 0)
return ret;
if (entry_no > 0)
i = 70;
max = i + 26;
for (reg = 2; i < max; ++i, ++reg) {
ret = mdiobus_read(bus, TCAM_ADDR, reg);
if (ret < 0)
return ret;
tcam_entry->frame_data[i].reg_data = ((u16)ret);
}
return 0;
}
static int mv88e6321_get_tcam_pg2(struct mii_bus *bus, struct tcam_entry
*tcam_entry, int entry_no)
{
int ret;
pr_debug("read tcam page 2\n");
tcam_entry->pg = 2;
if (entry_no)
return 0;
ret = mv88e6321_write_tcam_pg_hdr(bus, tcam_entry);
if (ret < 0)
return ret;
ret = mdiobus_read(bus, TCAM_ADDR, TCAM_REG_PG2_ACTION_1);
if (ret < 0)
return ret;
tcam_entry->reg_action_1 = ((u16)ret);
ret = mdiobus_read(bus, TCAM_ADDR, TCAM_REG_PG2_ACTION_2);
if (ret < 0)
return ret;
tcam_entry->reg_action_2 = ((u16)ret);
ret = mdiobus_read(bus, TCAM_ADDR, TCAM_REG_PG2_ACTION_3);
if (ret < 0)
return ret;
tcam_entry->reg_action_3 = ((u16)ret);
ret = mdiobus_read(bus, TCAM_ADDR, TCAM_REG_PG2_ACTION_4);
if (ret < 0)
return ret;
tcam_entry->reg_action_4 = ((u16)ret);
ret = mdiobus_read(bus, TCAM_ADDR, TCAM_REG_PG2_DEBUG_PORT);
if (ret < 0)
return ret;
tcam_entry->reg_debug = ((u16)ret);
ret = mdiobus_read(bus, TCAM_ADDR, TCAM_REG_PG2_DEBUG);
if (ret < 0)
return ret;
tcam_entry->reg_hit = ((u16)ret);
return 0;
}
int mv88e6321_get_tcam(struct mii_bus *bus, u8 id,
struct tcam_entry *tcam_entry)
{
int ret;
int i = 0;
if (!bus)
return -ENODEV;
tcam_entry->orig_id = id;
tcam_entry->id = id;
tcam_entry->op = TCAM_OP_READ;
for (i = 0; i < 2; ++i) {
if (i > 0)
tcam_entry->id = tcam_entry->next_id;
ret = mv88e6321_get_tcam_pg0(bus, tcam_entry, i);
if (ret < 0 || !tcam_entry->is_valid)
return ret;
ret = mv88e6321_get_tcam_pg1(bus, tcam_entry, i);
if (ret < 0)
return ret;
ret = mv88e6321_get_tcam_pg2(bus, tcam_entry, i);
if (ret < 0)
return ret;
if (!tcam_entry->cnt)
break;
}
return 0;
}
int mv88e6321_tcam_to_string(struct tcam_entry *tcam_entry,
char *buffer, size_t size)
{
int i = 0;
char *p = buffer;
if (!tcam_entry->is_valid) {
p += sprintf(p, "TCAM with ID %d is disabled", tcam_entry->id);
return 0;
}
p += sprintf(p, "\n\t\tTCAM ID: %d\n", tcam_entry->orig_id);
p += sprintf(p, "\t\tTCAM frame type: 0x%02X mask: 0x%02X\n",
tcam_entry->frame_type, tcam_entry->mask_frame_type);
p += sprintf(p, "\t\tTCAM spv: 0x%02X mask: 0x%02X\n",
tcam_entry->spv, tcam_entry->mask_spv);
p += sprintf(p, "\t\tTCAM ppri: 0x%02X mask: 0x%02X\n",
tcam_entry->ppri, tcam_entry->mask_ppri);
if (tcam_entry->mask_frame_type &&
tcam_entry->frame_type == TCAM_FT_PROV_TAG) {
p += sprintf(p, "\t\tTCAM pvid: 0x%04X mask: 0x%04X\n",
tcam_entry->orig_pvid, tcam_entry->mask_pvid);
}
p += sprintf(p, "\t\tTCAM cnt id: 0x%02X mask: 0x%02X\n",
tcam_entry->id_pvid, tcam_entry->mask_id_pvid);
p += sprintf(p, "\t\tTCAM frame data: [MASK:DATA]");
for (i = 0; i < 48; ++i) {
if (!(i % 16))
p += sprintf(p, "\n\t\t\t");
p += sprintf(p, "%02X:%02X ", tcam_entry->frame_data[i].mask,
tcam_entry->frame_data[i].data);
}
if (tcam_entry->cnt) {
for (i = 48; i < TCAM_FRAME_DATA_MAX_SIZE; ++i) {
if (!(i % 16))
p += sprintf(p, "\n\t\t\t");
p += sprintf(p, "%02X:%02X ",
tcam_entry->frame_data[i].mask,
tcam_entry->frame_data[i].data);
}
}
p += sprintf(p, "\n\t\tTCAM continue: %d\n", tcam_entry->cnt);
p += sprintf(p, "\t\tTCAM interrupt: %d\n", tcam_entry->interrupt);
p += sprintf(p, "\t\tTCAM inc ctrl: %d\n",
tcam_entry->inc_tcam_ctr);
p += sprintf(p, "\t\tTCAM override vid: %d vid: 0x%04X\n",
tcam_entry->vid_override, tcam_entry->vid);
p += sprintf(p, "\t\tTCAM next index: %d\n", tcam_entry->next_id);
p += sprintf(p, "\t\tTCAM override qpri: %d qpri: 0x%02X\n",
tcam_entry->qpri_override, tcam_entry->qpri);
p += sprintf(p, "\t\tTCAM override fpri: %d fpri: 0x%02X\n",
tcam_entry->fpri_override, tcam_entry->fpri);
p += sprintf(p, "\t\tTACM override dpv: %d dpv: 0x%04X\n",
tcam_entry->dpv_override, tcam_entry->dpv);
p += sprintf(p, "\t\tTCAM override act: %d action: 0x%04X\n",
tcam_entry->action_override, tcam_entry->action);
p += sprintf(p, "\t\tTCAM override lb: %d load balance: 0x%02X\n",
tcam_entry->ld_balance_override, tcam_entry->ld_balance);
p += sprintf(p, "\t\tTCAM debug port: %d\n", tcam_entry->debug_port);
p += sprintf(p, "\t\tTCAM hit: 0x%04X\n",
tcam_entry->reg_hit);
return 0;
}
#define MV88E6321_PORTS 7
#define PORT_REG(__reg) (0x10 + (__reg))
static inline int mv88e6321_set_port_state(struct mii_bus *bus, u16 addr,
u16 state, u16 *old_reg_val)
{
int ret;
u16 reg = PORT_CONTROL;
ret = mdiobus_read(bus, addr, reg);
if (ret < 0)
return ret;
if (old_reg_val)
*old_reg_val = (u16)ret;
ret &= ~PORT_CONTROL_STATE_MASK;
ret |= state;
ret = mdiobus_write(bus, addr, reg, ret);
if (ret < 0)
return ret;
return 0;
}
static int mv88e6321_enable_tcam_mode(struct mii_bus *bus, u16 addr,
u16 port_prio_val)
{
int ret;
u16 reg = PORT_PRI_OVERRIDE;
u16 port_ctrl_val = 0;
/*TODO: currently this function support only 96byte tcam mode.
* This mode includes 48byte tcam mode too. In future only
* 48byte mode could be implemented.
*/
/* disable port before chage tcam mode */
ret = mv88e6321_set_port_state(bus, addr,
PORT_CONTROL_STATE_DISABLED,
&port_ctrl_val);
if (ret < 0)
return ret;
port_prio_val &= ~PORT_PRI_OVERRIDE_TCAM_MODE_MASK;
port_prio_val |= PORT_PRI_OVERRIDE_TCAM_MODE_96;
ret = mdiobus_write(bus, addr, reg, port_prio_val);
if (ret < 0)
return ret;
/* set old port state after tcam mode changing */
return mv88e6321_set_port_state(bus, addr, port_ctrl_val &
PORT_CONTROL_STATE_MASK, NULL);
}
int mv88e6321_set_tcam_mode(struct mii_bus *bus,
struct tcam_entries *tcam_entries)
{
int ret;
int addr = PORT_REG(0);
u8 mask = 1;
if (!bus)
return -ENODEV;
for (; addr < PORT_REG(MV88E6321_PORTS); ++addr) {
u16 reg = PORT_PRI_OVERRIDE;
if (mask & tcam_entries->port_mask) {
ret = mdiobus_read(bus, addr, reg);
if (ret < 0)
return ret;
if ((ret & PORT_PRI_OVERRIDE_TCAM_MODE_MASK) !=
PORT_PRI_OVERRIDE_TCAM_MODE_96) {
ret = mv88e6321_enable_tcam_mode(bus, addr,
ret);
}
}
mask <<= 1;
}
return 0;
}
int mv88e6321_flush_tcam(struct mii_bus *bus, int id)
{
int ret;
struct tcam_entry tcam_entry;
if (!bus)
return -ENODEV;
memset(&tcam_entry, 0, sizeof(struct tcam_entry));
if (id < 0)
tcam_entry.op = TCAM_OP_FLUSH_ALL;
else if (id >= 0 && id <= 255)
tcam_entry.op = TCAM_OP_FLUSH;
else
return 0;
tcam_entry.id = id;
if (id < 0)
pr_debug("flush all TCAM\n");
else
pr_debug("flush TCAM ID:%d\n", id);
ret = mv88e6321_wait_tcam_ready(bus);
if (ret < 0)
return ret;
ret = mv88e6321_write_tcam_pg_hdr(bus, &tcam_entry);
if (ret < 0)
return ret;
return mv88e6321_wait_tcam_ready(bus);
}
int mv88e6321_enable_tcam(struct mii_bus *bus, u8 id, u16 reg_frame_type)
{
int ret;
struct tcam_entry tcam_entry;
if (!bus)
return -ENODEV;
memset(&tcam_entry, 0, sizeof(struct tcam_entry));
tcam_entry.op = TCAM_OP_READ;
tcam_entry.pg = 0;
tcam_entry.id = id;
ret = mv88e6321_wait_tcam_ready(bus);
if (ret < 0)
return ret;
ret = mv88e6321_write_tcam_pg_hdr(bus, &tcam_entry);
if (ret < 0)
return ret;
ret = mv88e6321_wait_tcam_ready(bus);
if (ret < 0)
return ret;
ret = mdiobus_write(bus, TCAM_ADDR, TCAM_REG_PG0_KEY_1, reg_frame_type);
if (ret < 0)
return ret;
tcam_entry.op = TCAM_OP_LOAD;
ret = mv88e6321_write_tcam_pg_hdr(bus, &tcam_entry);
if (ret < 0)
return ret;
return mv88e6321_wait_tcam_ready(bus);
}
int mv88e6321_disable_tcam(struct mii_bus *bus, u8 id)
{
int ret = 0;
struct tcam_entry tcam_entry;
if (!bus)
return -ENODEV;
memset(&tcam_entry, 0, sizeof(struct tcam_entry));
tcam_entry.op = TCAM_OP_READ;
tcam_entry.pg = 0;
tcam_entry.id = id;
ret = mv88e6321_wait_tcam_ready(bus);
if (ret < 0)
return ret;
ret = mv88e6321_write_tcam_pg_hdr(bus, &tcam_entry);
if (ret < 0)
return ret;
ret = mv88e6321_wait_tcam_ready(bus);
if (ret < 0)
return ret;
ret = mdiobus_write(bus, TCAM_ADDR, TCAM_REG_PG0_KEY_1, 0xFF);
if (ret < 0)
return ret;
tcam_entry.op = TCAM_OP_LOAD;
ret = mv88e6321_write_tcam_pg_hdr(bus, &tcam_entry);
if (ret < 0)
return ret;
return mv88e6321_wait_tcam_ready(bus);
}
int mv88e6321_is_tcam_enabled(struct mii_bus *bus, u8 id)
{
int ret = 0;
struct tcam_entry tcam_entry;
if (!bus)
return -ENODEV;
memset(&tcam_entry, 0, sizeof(struct tcam_entry));
tcam_entry.op = TCAM_OP_READ;
tcam_entry.pg = 0;
tcam_entry.id = id;
ret = mv88e6321_wait_tcam_ready(bus);
if (ret < 0)
return ret;
ret = mv88e6321_write_tcam_pg_hdr(bus, &tcam_entry);
if (ret < 0)
return ret;
ret = mv88e6321_wait_tcam_ready(bus);
if (ret < 0)
return ret;
ret = mdiobus_read(bus, TCAM_ADDR, TCAM_REG_PG0_KEY_1);
if (ret < 0)
return ret;
return (ret == 0xFF) ? 0 : 1;
}
int mv88e6321_get_tcam_debug(struct mii_bus *bus, u8 id)
{
int ret = 0;
struct tcam_entry tcam_entry;
if (!bus)
return -ENODEV;
memset(&tcam_entry, 0, sizeof(struct tcam_entry));
tcam_entry.op = TCAM_OP_READ;
tcam_entry.pg = 2;
tcam_entry.id = id;
ret = mv88e6321_wait_tcam_ready(bus);
if (ret < 0)
return ret;
ret = mv88e6321_write_tcam_pg_hdr(bus, &tcam_entry);
if (ret < 0)
return ret;
ret = mv88e6321_wait_tcam_ready(bus);
if (ret < 0)
return ret;
return mdiobus_read(bus, TCAM_ADDR, TCAM_REG_PG2_DEBUG);
}
+202
View File
@@ -0,0 +1,202 @@
#ifndef __MV88E6321_TCAM__
#define __MV88E6321_TCAM__
#include <linux/mdio.h>
#include <linux/list.h>
#include <linux/of.h>
#define TCAM_FRAME_DATA_MAX_SIZE 96
struct tcam_frame_data {
union {
struct {
u16 data:8;
u16 mask:8;
};
u16 reg_data;
};
};
struct tcam_entry {
const char *title;
struct list_head list;
u8 orig_id;
u8 is_valid;
union {
struct {
u16 id:8;
u16 res0:2;
u16 pg:2;
u16 op:3;
u16 busy:1;
};
u16 reg_pg_hdr;
};
union {
struct {
u16 res1:6;
u16 frame_type:2;
u16 res2:6;
u16 mask_frame_type:2;
};
u16 reg_frame_type;
};
union {
struct {
u16 spv:7;
u16 res3:1;
u16 mask_spv:7;
};
u16 reg_spv;
};
u16 orig_pvid;
u8 orig_ppri;
u16 mask_pvid;
u8 mask_ppri;
union {
struct {
u16 pvid:4;
u16 ppri:4;
u16 mask_ppri_pvid:8;
};
u16 reg_ppri_pvid;
};
union {
struct {
u16 id_pvid:8;
u16 mask_id_pvid:8;
};
u16 reg_id_ppri;
};
u8 is96frame;
struct tcam_frame_data frame_data[TCAM_FRAME_DATA_MAX_SIZE];
union {
struct {
u16 vid:12;
u16 vid_override:1;
u16 inc_tcam_ctr:1;
u16 interrupt:1;
u16 cnt:1;
};
u16 reg_action_1;
};
union {
struct {
u16 fpri:3;
u16 fpri_override:1;
u16 qpri:2;
u16 res4:1;
u16 qpri_override:1;
u16 next_id:8;
};
u16 reg_action_2;
};
union {
struct {
u16 dpv:7;
u16 res5:4;
u16 dpv_override:1;
};
u16 reg_action_3;
};
union {
struct {
u16 ld_balance:3;
u16 ld_balance_override:1;
u16 action:11;
u16 action_override:1;
};
u16 reg_action_4;
};
union {
struct {
u16 debug_port:4;
};
u16 reg_debug;
};
union {
struct {
u16 hit_low:8;
u16 hit_high:8;
};
u16 reg_hit;
};
};
struct tcam_entries {
u8 port_mask;
u8 debug_port;
struct tcam_entry head;
};
#define TCAM_OP_NOP 0
#define TCAM_OP_FLUSH_ALL 1
#define TCAM_OP_FLUSH 2
#define TCAM_OP_LOAD 3
#define TCAM_OP_GET_NEXT 4
#define TCAM_OP_READ 5
#define TCAM_FT_NORM_NET 0
#define TCAM_FT_CONTINUE 0
#define TCAM_FT_DSA_TAG 1
#define TCAM_FT_PROV_TAG 2
#define TCAM_ADDR 0x1D
#define TCAM_REG_PG_HDR 0
#define TCAM_REG_PG0_KEY_1 0x2
#define TCAM_REG_PG0_KEY_2 0x3
#define TCAM_REG_PG0_KEY_3 0x4
#define TCAM_REG_PG0_KEY_4 0x5
#define TCAM_REG_PG2_ACTION_1 0x2
#define TCAM_REG_PG2_ACTION_2 0x3
#define TCAM_REG_PG2_ACTION_3 0x4
#define TCAM_REG_PG2_ACTION_4 0x5
#define TCAM_REG_PG2_DEBUG_PORT 0x1C
#define TCAM_REG_PG2_DEBUG 0x1F
#define TCAM_PG_HDR_BUSY ((u16)1 << 15)
int of_get_tcam_entry(struct tcam_entries *tcam_entries,
struct device_node *np);
int mv88e6321_set_tcam_mode(struct mii_bus *bus,
struct tcam_entries *tcam_entries);
int mv88e6321_load_tcam(struct mii_bus *bus,
struct tcam_entry *tcam_entry);
int mv88e6321_get_tcam(struct mii_bus *bus, u8 id,
struct tcam_entry *tcam_entry);
int mv88e6321_flush_tcam(struct mii_bus *bus, int id);
int mv88e6321_enable_tcam(struct mii_bus *bus, u8 id, u16 reg_frame_type);
int mv88e6321_disable_tcam(struct mii_bus *bus, u8 id);
int mv88e6321_is_tcam_enabled(struct mii_bus *bus, u8 id);
int mv88e6321_get_tcam_debug(struct mii_bus *bus, u8 id);
int mv88e6321_tcam_to_string(struct tcam_entry *tcam_entry,
char *buffer, size_t size);
#endif
+1
View File
@@ -95,3 +95,4 @@ obj-$(CONFIG_STE10XP) += ste10Xp.o
obj-$(CONFIG_TERANETICS_PHY) += teranetics.o
obj-$(CONFIG_VITESSE_PHY) += vitesse.o
obj-$(CONFIG_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
obj-$(CONFIG_SWCFG_MV88E6321) += mv88e6321-cfg.o