#include "rtl8365mb_table.h"
#include <linux/regmap.h>
#define RTL8365MB_TABLE_CTRL_REG 0x0500
#define RTL8365MB_TABLE_CTRL_TABLE_MASK GENMASK(2, 0)
#define RTL8365MB_TABLE_CTRL_OP_MASK GENMASK(3, 3)
#define RTL8365MB_TABLE_CTRL_METHOD_MASK GENMASK(6, 4)
#define RTL8365MB_TABLE_CTRL_PORT_MASK GENMASK(11, 8)
#define RTL8365MB_TABLE_ACCESS_ADDR_REG 0x0501
#define RTL8365MB_TABLE_ADDR_MASK GENMASK(12, 0)
#define RTL8365MB_TABLE_STATUS_REG 0x0502
#define RTL8365MB_TABLE_STATUS_ADDRESS_MASK GENMASK(10, 0)
#define RTL8365MB_TABLE_STATUS_ADDR_TYPE_MASK GENMASK(11, 11)
#define RTL8365MB_TABLE_STATUS_HIT_STATUS_MASK GENMASK(12, 12)
#define RTL8365MB_TABLE_STATUS_BUSY_FLAG_MASK GENMASK(13, 13)
#define RTL8365MB_TABLE_STATUS_ADDRESS_EXT_MASK GENMASK(14, 14)
#define RTL8365MB_TABLE_WRITE_BASE 0x0510
#define RTL8365MB_TABLE_WRITE_REG(_x) \
(RTL8365MB_TABLE_WRITE_BASE + (_x))
#define RTL8365MB_TABLE_READ_BASE 0x0520
#define RTL8365MB_TABLE_READ_REG(_x) \
(RTL8365MB_TABLE_READ_BASE + (_x))
#define RTL8365MB_TABLE_10TH_DATA_MASK GENMASK(3, 0)
#define RTL8365MB_TABLE_WRITE_10TH_REG \
RTL8365MB_TABLE_WRITE_REG(RTL8365MB_TABLE_ENTRY_MAX_SIZE - 1)
static int rtl8365mb_table_poll_busy(struct realtek_priv *priv)
{
u32 val;
return regmap_read_poll_timeout(priv->map_nolock,
RTL8365MB_TABLE_STATUS_REG, val,
!FIELD_GET(RTL8365MB_TABLE_STATUS_BUSY_FLAG_MASK, val),
10, 10000);
}
int rtl8365mb_table_query(struct realtek_priv *priv,
enum rtl8365mb_table table,
enum rtl8365mb_table_op op, u16 *addr,
enum rtl8365mb_table_l2_method method,
u16 port, u16 *data, size_t size)
{
bool addr_as_input = true;
bool write_data = false;
int ret = 0;
u32 cmd;
u32 val;
u32 hit;
cmd = 0;
cmd |= FIELD_PREP(RTL8365MB_TABLE_CTRL_TABLE_MASK, table);
cmd |= FIELD_PREP(RTL8365MB_TABLE_CTRL_OP_MASK, op);
if (op == RTL8365MB_TABLE_OP_READ && table == RTL8365MB_TABLE_L2) {
cmd |= FIELD_PREP(RTL8365MB_TABLE_CTRL_METHOD_MASK, method);
switch (method) {
case RTL8365MB_TABLE_L2_METHOD_MAC:
write_data = true;
addr_as_input = false;
break;
case RTL8365MB_TABLE_L2_METHOD_ADDR:
case RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT:
case RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC:
case RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_MC:
break;
case RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT:
cmd |= FIELD_PREP(RTL8365MB_TABLE_CTRL_PORT_MASK, port);
break;
default:
return -EINVAL;
}
} else if (op == RTL8365MB_TABLE_OP_WRITE) {
write_data = true;
if (table == RTL8365MB_TABLE_L2)
addr_as_input = false;
}
mutex_lock(&priv->map_lock);
ret = rtl8365mb_table_poll_busy(priv);
if (ret)
goto out;
if (write_data) {
ret = regmap_bulk_write(priv->map_nolock,
RTL8365MB_TABLE_WRITE_BASE,
data,
min_t(size_t, size,
RTL8365MB_TABLE_ENTRY_MAX_SIZE -
1));
if (ret)
goto out;
if (size == RTL8365MB_TABLE_ENTRY_MAX_SIZE) {
val = FIELD_PREP(RTL8365MB_TABLE_10TH_DATA_MASK,
data[size - 1]);
ret = regmap_update_bits(priv->map_nolock,
RTL8365MB_TABLE_WRITE_10TH_REG,
RTL8365MB_TABLE_10TH_DATA_MASK,
val);
}
if (ret)
goto out;
}
if (addr_as_input) {
ret = regmap_write(priv->map_nolock,
RTL8365MB_TABLE_ACCESS_ADDR_REG,
FIELD_PREP(RTL8365MB_TABLE_ADDR_MASK,
*addr));
if (ret)
goto out;
}
ret = regmap_write(priv->map_nolock, RTL8365MB_TABLE_CTRL_REG, cmd);
if (ret)
goto out;
ret = rtl8365mb_table_poll_busy(priv);
if (ret)
goto out;
if (table == RTL8365MB_TABLE_L2) {
ret = regmap_read(priv->map_nolock, RTL8365MB_TABLE_STATUS_REG,
&val);
if (ret)
goto out;
hit = FIELD_GET(RTL8365MB_TABLE_STATUS_HIT_STATUS_MASK, val);
if (!hit) {
ret = -ENOENT;
goto out;
}
*addr = 0;
*addr |= FIELD_GET(RTL8365MB_TABLE_STATUS_ADDRESS_MASK, val);
*addr |= FIELD_GET(RTL8365MB_TABLE_STATUS_ADDRESS_EXT_MASK, val)
<< 11;
*addr |= FIELD_GET(RTL8365MB_TABLE_STATUS_ADDR_TYPE_MASK, val)
<< 12;
}
if (op == RTL8365MB_TABLE_OP_READ) {
ret = regmap_bulk_read(priv->map_nolock,
RTL8365MB_TABLE_READ_BASE,
data, size);
if (ret)
goto out;
if (size == RTL8365MB_TABLE_ENTRY_MAX_SIZE) {
val = FIELD_GET(RTL8365MB_TABLE_10TH_DATA_MASK,
data[size - 1]);
data[size - 1] = val;
}
}
out:
mutex_unlock(&priv->map_lock);
return ret;
}