diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile index a9a9651187db..eb1df0bc61d3 100644 --- a/drivers/net/dsa/mv88e6xxx/Makefile +++ b/drivers/net/dsa/mv88e6xxx/Makefile @@ -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 diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6321_tcam.c b/drivers/net/dsa/mv88e6xxx/mv88e6321_tcam.c new file mode 100644 index 000000000000..19d90a138083 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/mv88e6321_tcam.c @@ -0,0 +1,969 @@ +#define pr_fmt(fmt) "mv88e6321: tcam:" fmt + +#include +#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); +} diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6321_tcam.h b/drivers/net/dsa/mv88e6xxx/mv88e6321_tcam.h new file mode 100644 index 000000000000..b153dba6722d --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/mv88e6321_tcam.h @@ -0,0 +1,202 @@ +#ifndef __MV88E6321_TCAM__ +#define __MV88E6321_TCAM__ + +#include +#include +#include + +#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 diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index c945ed9bd14b..6fc4eb2d41d8 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -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