#include "linux/list.h"
#include "chip.h"
#include "tcam.h"
#define MV88E6XXX_TCAM_OP 0x00
#define MV88E6XXX_TCAM_OP_BUSY 0x8000
#define MV88E6XXX_TCAM_OP_OP_MASK 0x7000
#define MV88E6XXX_TCAM_OP_OP_FLUSH_ALL 0x1000
#define MV88E6XXX_TCAM_OP_OP_FLUSH 0x2000
#define MV88E6XXX_TCAM_OP_OP_LOAD 0x3000
#define MV88E6XXX_TCAM_OP_OP_GET_NEXT 0x4000
#define MV88E6XXX_TCAM_OP_OP_READ 0x5000
#define MV88E6XXX_TCAM_EXTENSION 0x01
#define MV88E6XXX_TCAM_KEYS1 0x02
#define MV88E6XXX_TCAM_KEYS1_FT_MASK 0xC000
#define MV88E6XXX_TCAM_KEYS1_SPV_MASK 0x0007
#define MV88E6XXX_TCAM_KEYS1_SPV_MASK_MASK 0x0700
#define MV88E6XXX_TCAM_KEYS2 0x03
#define MV88E6XXX_TCAM_KEYS2_SPV_MASK 0x00ff
#define MV88E6XXX_TCAM_KEYS2_SPV_MASK_MASK 0xff00
#define MV88E6XXX_TCAM_ING_ACT3 0x04
#define MV88E6XXX_TCAM_ING_ACT3_SF 0x0800
#define MV88E6XXX_TCAM_ING_ACT3_DPV_MASK 0x07ff
#define MV88E6XXX_TCAM_ING_ACT5 0x06
#define MV88E6XXX_TCAM_ING_ACT5_DPV_MODE_MASK 0xc000
static int mv88e6xxx_tcam_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
{
return mv88e6xxx_write(chip, chip->info->tcam_addr, reg, val);
}
static int mv88e6xxx_tcam_wait(struct mv88e6xxx_chip *chip)
{
int bit = __bf_shf(MV88E6XXX_TCAM_OP_BUSY);
return mv88e6xxx_wait_bit(chip, chip->info->tcam_addr,
MV88E6XXX_TCAM_OP, bit, 0);
}
static int mv88e6xxx_tcam_read_page(struct mv88e6xxx_chip *chip, u8 page,
u8 entry)
{
u16 val = MV88E6XXX_TCAM_OP_BUSY | MV88E6XXX_TCAM_OP_OP_READ |
(page & 0x3) << 10 | entry;
int err;
err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_OP, val);
if (err)
return err;
return mv88e6xxx_tcam_wait(chip);
}
static int mv88e6xxx_tcam_load_page(struct mv88e6xxx_chip *chip, u8 page,
u8 entry)
{
u16 val = MV88E6XXX_TCAM_OP_BUSY | MV88E6XXX_TCAM_OP_OP_LOAD |
(page & 0x3) << 10 | entry;
int err;
err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_OP, val);
if (err)
return err;
return mv88e6xxx_tcam_wait(chip);
}
static int mv88e6xxx_tcam_flush_entry(struct mv88e6xxx_chip *chip, u8 entry)
{
u16 val = MV88E6XXX_TCAM_OP_BUSY | MV88E6XXX_TCAM_OP_OP_FLUSH | entry;
int err;
err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_OP, val);
if (err)
return err;
return mv88e6xxx_tcam_wait(chip);
}
static int mv88e6xxx_tcam_flush_all(struct mv88e6xxx_chip *chip)
{
u16 val = MV88E6XXX_TCAM_OP_BUSY | MV88E6XXX_TCAM_OP_OP_FLUSH_ALL;
int err;
err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_OP, val);
if (err)
return err;
return mv88e6xxx_tcam_wait(chip);
}
struct mv88e6xxx_tcam_entry *
mv88e6xxx_tcam_entry_find(struct mv88e6xxx_chip *chip, unsigned long cookie)
{
struct mv88e6xxx_tcam_entry *entry;
list_for_each_entry(entry, &chip->tcam.entries, list)
if (entry->cookie == cookie)
return entry;
return NULL;
}
static int mv88e6xxx_tcam_insert_entry(struct mv88e6xxx_chip *chip,
struct mv88e6xxx_tcam_entry *entry)
{
struct mv88e6xxx_tcam_entry *elem;
struct list_head *hpos;
int err;
list_for_each_prev(hpos, &chip->tcam.entries) {
u8 move_idx;
elem = list_entry(hpos, struct mv88e6xxx_tcam_entry, list);
if (entry->prio >= elem->prio)
break;
move_idx = elem->hw_idx + 1;
err = mv88e6xxx_tcam_flush_entry(chip, move_idx);
if (err)
return err;
err = chip->info->ops->tcam_ops->entry_add(chip, elem,
move_idx);
if (err)
return err;
elem->hw_idx = move_idx;
}
if (list_is_head(hpos, &chip->tcam.entries)) {
entry->hw_idx = 0;
} else {
elem = list_entry(hpos, struct mv88e6xxx_tcam_entry, list);
entry->hw_idx = elem->hw_idx + 1;
}
list_add(&entry->list, hpos);
return 0;
}
int mv88e6xxx_tcam_entry_add(struct mv88e6xxx_chip *chip,
struct mv88e6xxx_tcam_entry *entry)
{
int err;
struct mv88e6xxx_tcam_entry *last;
last = list_last_entry_or_null(&chip->tcam.entries,
struct mv88e6xxx_tcam_entry, list);
if (last && last->hw_idx == chip->info->num_tcam_entries - 1)
return -ENOSPC;
err = mv88e6xxx_tcam_insert_entry(chip, entry);
if (err)
return err;
err = mv88e6xxx_tcam_flush_entry(chip, entry->hw_idx);
if (err)
goto unlink_out;
err = chip->info->ops->tcam_ops->entry_add(chip, entry, entry->hw_idx);
if (err)
goto unlink_out;
return 0;
unlink_out:
list_del(&entry->list);
return err;
}
int mv88e6xxx_tcam_entry_del(struct mv88e6xxx_chip *chip,
struct mv88e6xxx_tcam_entry *entry)
{
struct mv88e6xxx_tcam_entry *elem = entry;
u8 move_idx = entry->hw_idx;
int err;
err = mv88e6xxx_tcam_flush_entry(chip, entry->hw_idx);
if (err)
return err;
list_for_each_entry_continue(elem, &chip->tcam.entries, list) {
u8 tmp_idx = elem->hw_idx;
err = mv88e6xxx_tcam_flush_entry(chip, move_idx);
if (err)
break;
err = chip->info->ops->tcam_ops->entry_add(chip, elem,
move_idx);
if (err)
break;
elem->hw_idx = move_idx;
move_idx = tmp_idx;
if (list_is_last(&elem->list, &chip->tcam.entries))
err = mv88e6xxx_tcam_flush_entry(chip, tmp_idx);
}
list_del(&entry->list);
kfree(entry);
return err;
}
static int mv88e6390_tcam_entry_add(struct mv88e6xxx_chip *chip,
struct mv88e6xxx_tcam_entry *entry, u8 idx)
{
int err = 0;
int i;
u16 val, spv_mask, spv;
err = mv88e6xxx_tcam_read_page(chip, 2, idx);
if (err)
return err;
if (entry->action.dpv_mode != 0) {
val = MV88E6XXX_TCAM_ING_ACT3_SF |
(entry->action.dpv & MV88E6XXX_TCAM_ING_ACT3_DPV_MASK);
err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_ING_ACT3, val);
if (err)
return err;
val = entry->action.dpv_mode << 14;
err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_ING_ACT5, val);
if (err)
return err;
}
err = mv88e6xxx_tcam_load_page(chip, 2, idx);
if (err)
return err;
err = mv88e6xxx_tcam_read_page(chip, 1, idx);
if (err)
return err;
for (i = PAGE0_MATCH_SIZE;
i < PAGE0_MATCH_SIZE + PAGE1_MATCH_SIZE; i++) {
if (entry->key.frame_mask[i]) {
val = entry->key.frame_mask[i] << 8 |
entry->key.frame_data[i];
err = mv88e6xxx_tcam_write(chip,
i - PAGE0_MATCH_SIZE + 2,
val);
if (err)
return err;
}
}
err = mv88e6xxx_tcam_load_page(chip, 1, idx);
if (err)
return err;
err = mv88e6xxx_tcam_read_page(chip, 0, idx);
if (err)
return err;
for (i = 0; i < PAGE0_MATCH_SIZE; i++) {
if (entry->key.frame_mask[i]) {
val = entry->key.frame_mask[i] << 8 |
entry->key.frame_data[i];
err = mv88e6xxx_tcam_write(chip, i + 6, val);
if (err)
return err;
}
}
spv_mask = entry->key.spv_mask & mv88e6xxx_port_mask(chip);
spv = entry->key.spv & mv88e6xxx_port_mask(chip);
val = MV88E6XXX_TCAM_KEYS1_FT_MASK |
(spv_mask & MV88E6XXX_TCAM_KEYS1_SPV_MASK_MASK) |
((spv >> 8) & MV88E6XXX_TCAM_KEYS1_SPV_MASK);
err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_KEYS1, val);
if (err)
return err;
val = ((spv_mask << 8) & MV88E6XXX_TCAM_KEYS2_SPV_MASK_MASK) |
(spv & MV88E6XXX_TCAM_KEYS2_SPV_MASK);
err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_KEYS2, val);
if (err)
return err;
err = mv88e6xxx_tcam_load_page(chip, 0, idx);
if (err)
return err;
entry->hw_idx = idx;
return 0;
}
static int mv88e6393_tcam_entry_add(struct mv88e6xxx_chip *chip,
struct mv88e6xxx_tcam_entry *entry, u8 idx)
{
int err;
err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_EXTENSION, 0x00);
if (err)
return err;
return mv88e6390_tcam_entry_add(chip, entry, idx);
}
const struct mv88e6xxx_tcam_ops mv88e6390_tcam_ops = {
.entry_add = mv88e6390_tcam_entry_add,
.flush_tcam = mv88e6xxx_tcam_flush_all,
};
const struct mv88e6xxx_tcam_ops mv88e6393_tcam_ops = {
.entry_add = mv88e6393_tcam_entry_add,
.flush_tcam = mv88e6xxx_tcam_flush_all,
};