|
|
|
@@ -71,6 +71,7 @@ struct mlxsw_core {
|
|
|
|
|
struct list_head trans_list;
|
|
|
|
|
spinlock_t trans_list_lock; /* protects trans_list writes */
|
|
|
|
|
bool use_emad;
|
|
|
|
|
bool enable_string_tlv;
|
|
|
|
|
} emad;
|
|
|
|
|
struct {
|
|
|
|
|
u8 *mapping; /* lag_id+port_index to local_port mapping */
|
|
|
|
@@ -249,6 +250,25 @@ MLXSW_ITEM32(emad, op_tlv, class, 0x04, 0, 8);
|
|
|
|
|
*/
|
|
|
|
|
MLXSW_ITEM64(emad, op_tlv, tid, 0x08, 0, 64);
|
|
|
|
|
|
|
|
|
|
/* emad_string_tlv_type
|
|
|
|
|
* Type of the TLV.
|
|
|
|
|
* Must be set to 0x2 (string TLV).
|
|
|
|
|
*/
|
|
|
|
|
MLXSW_ITEM32(emad, string_tlv, type, 0x00, 27, 5);
|
|
|
|
|
|
|
|
|
|
/* emad_string_tlv_len
|
|
|
|
|
* Length of the string TLV in u32.
|
|
|
|
|
*/
|
|
|
|
|
MLXSW_ITEM32(emad, string_tlv, len, 0x00, 16, 11);
|
|
|
|
|
|
|
|
|
|
#define MLXSW_EMAD_STRING_TLV_STRING_LEN 128
|
|
|
|
|
|
|
|
|
|
/* emad_string_tlv_string
|
|
|
|
|
* String provided by the device's firmware in case of erroneous register access
|
|
|
|
|
*/
|
|
|
|
|
MLXSW_ITEM_BUF(emad, string_tlv, string, 0x04,
|
|
|
|
|
MLXSW_EMAD_STRING_TLV_STRING_LEN);
|
|
|
|
|
|
|
|
|
|
/* emad_reg_tlv_type
|
|
|
|
|
* Type of the TLV.
|
|
|
|
|
* Must be set to 0x3 (register TLV).
|
|
|
|
@@ -304,6 +324,12 @@ static void mlxsw_emad_pack_reg_tlv(char *reg_tlv,
|
|
|
|
|
memcpy(reg_tlv + sizeof(u32), payload, reg->len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mlxsw_emad_pack_string_tlv(char *string_tlv)
|
|
|
|
|
{
|
|
|
|
|
mlxsw_emad_string_tlv_type_set(string_tlv, MLXSW_EMAD_TLV_TYPE_STRING);
|
|
|
|
|
mlxsw_emad_string_tlv_len_set(string_tlv, MLXSW_EMAD_STRING_TLV_LEN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mlxsw_emad_pack_op_tlv(char *op_tlv,
|
|
|
|
|
const struct mlxsw_reg_info *reg,
|
|
|
|
|
enum mlxsw_core_reg_access_type type,
|
|
|
|
@@ -345,7 +371,7 @@ static void mlxsw_emad_construct(struct sk_buff *skb,
|
|
|
|
|
const struct mlxsw_reg_info *reg,
|
|
|
|
|
char *payload,
|
|
|
|
|
enum mlxsw_core_reg_access_type type,
|
|
|
|
|
u64 tid)
|
|
|
|
|
u64 tid, bool enable_string_tlv)
|
|
|
|
|
{
|
|
|
|
|
char *buf;
|
|
|
|
|
|
|
|
|
@@ -355,26 +381,82 @@ static void mlxsw_emad_construct(struct sk_buff *skb,
|
|
|
|
|
buf = skb_push(skb, reg->len + sizeof(u32));
|
|
|
|
|
mlxsw_emad_pack_reg_tlv(buf, reg, payload);
|
|
|
|
|
|
|
|
|
|
if (enable_string_tlv) {
|
|
|
|
|
buf = skb_push(skb, MLXSW_EMAD_STRING_TLV_LEN * sizeof(u32));
|
|
|
|
|
mlxsw_emad_pack_string_tlv(buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buf = skb_push(skb, MLXSW_EMAD_OP_TLV_LEN * sizeof(u32));
|
|
|
|
|
mlxsw_emad_pack_op_tlv(buf, reg, type, tid);
|
|
|
|
|
|
|
|
|
|
mlxsw_emad_construct_eth_hdr(skb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct mlxsw_emad_tlv_offsets {
|
|
|
|
|
u16 op_tlv;
|
|
|
|
|
u16 string_tlv;
|
|
|
|
|
u16 reg_tlv;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static bool mlxsw_emad_tlv_is_string_tlv(const char *tlv)
|
|
|
|
|
{
|
|
|
|
|
u8 tlv_type = mlxsw_emad_string_tlv_type_get(tlv);
|
|
|
|
|
|
|
|
|
|
return tlv_type == MLXSW_EMAD_TLV_TYPE_STRING;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mlxsw_emad_tlv_parse(struct sk_buff *skb)
|
|
|
|
|
{
|
|
|
|
|
struct mlxsw_emad_tlv_offsets *offsets =
|
|
|
|
|
(struct mlxsw_emad_tlv_offsets *) skb->cb;
|
|
|
|
|
|
|
|
|
|
offsets->op_tlv = MLXSW_EMAD_ETH_HDR_LEN;
|
|
|
|
|
offsets->string_tlv = 0;
|
|
|
|
|
offsets->reg_tlv = MLXSW_EMAD_ETH_HDR_LEN +
|
|
|
|
|
MLXSW_EMAD_OP_TLV_LEN * sizeof(u32);
|
|
|
|
|
|
|
|
|
|
/* If string TLV is present, it must come after the operation TLV. */
|
|
|
|
|
if (mlxsw_emad_tlv_is_string_tlv(skb->data + offsets->reg_tlv)) {
|
|
|
|
|
offsets->string_tlv = offsets->reg_tlv;
|
|
|
|
|
offsets->reg_tlv += MLXSW_EMAD_STRING_TLV_LEN * sizeof(u32);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *mlxsw_emad_op_tlv(const struct sk_buff *skb)
|
|
|
|
|
{
|
|
|
|
|
return ((char *) (skb->data + MLXSW_EMAD_ETH_HDR_LEN));
|
|
|
|
|
struct mlxsw_emad_tlv_offsets *offsets =
|
|
|
|
|
(struct mlxsw_emad_tlv_offsets *) skb->cb;
|
|
|
|
|
|
|
|
|
|
return ((char *) (skb->data + offsets->op_tlv));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *mlxsw_emad_string_tlv(const struct sk_buff *skb)
|
|
|
|
|
{
|
|
|
|
|
struct mlxsw_emad_tlv_offsets *offsets =
|
|
|
|
|
(struct mlxsw_emad_tlv_offsets *) skb->cb;
|
|
|
|
|
|
|
|
|
|
if (!offsets->string_tlv)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return ((char *) (skb->data + offsets->string_tlv));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *mlxsw_emad_reg_tlv(const struct sk_buff *skb)
|
|
|
|
|
{
|
|
|
|
|
return ((char *) (skb->data + MLXSW_EMAD_ETH_HDR_LEN +
|
|
|
|
|
MLXSW_EMAD_OP_TLV_LEN * sizeof(u32)));
|
|
|
|
|
struct mlxsw_emad_tlv_offsets *offsets =
|
|
|
|
|
(struct mlxsw_emad_tlv_offsets *) skb->cb;
|
|
|
|
|
|
|
|
|
|
return ((char *) (skb->data + offsets->reg_tlv));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *mlxsw_emad_reg_payload(const char *op_tlv)
|
|
|
|
|
static char *mlxsw_emad_reg_payload(const char *reg_tlv)
|
|
|
|
|
{
|
|
|
|
|
return ((char *) (op_tlv + (MLXSW_EMAD_OP_TLV_LEN + 1) * sizeof(u32)));
|
|
|
|
|
return ((char *) (reg_tlv + sizeof(u32)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *mlxsw_emad_reg_payload_cmd(const char *mbox)
|
|
|
|
|
{
|
|
|
|
|
return ((char *) (mbox + (MLXSW_EMAD_OP_TLV_LEN + 1) * sizeof(u32)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static u64 mlxsw_emad_get_tid(const struct sk_buff *skb)
|
|
|
|
@@ -440,10 +522,31 @@ struct mlxsw_reg_trans {
|
|
|
|
|
const struct mlxsw_reg_info *reg;
|
|
|
|
|
enum mlxsw_core_reg_access_type type;
|
|
|
|
|
int err;
|
|
|
|
|
char *emad_err_string;
|
|
|
|
|
enum mlxsw_emad_op_tlv_status emad_status;
|
|
|
|
|
struct rcu_head rcu;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void mlxsw_emad_process_string_tlv(const struct sk_buff *skb,
|
|
|
|
|
struct mlxsw_reg_trans *trans)
|
|
|
|
|
{
|
|
|
|
|
char *string_tlv;
|
|
|
|
|
char *string;
|
|
|
|
|
|
|
|
|
|
string_tlv = mlxsw_emad_string_tlv(skb);
|
|
|
|
|
if (!string_tlv)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
trans->emad_err_string = kzalloc(MLXSW_EMAD_STRING_TLV_STRING_LEN,
|
|
|
|
|
GFP_ATOMIC);
|
|
|
|
|
if (!trans->emad_err_string)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
string = mlxsw_emad_string_tlv_string_data(string_tlv);
|
|
|
|
|
strlcpy(trans->emad_err_string, string,
|
|
|
|
|
MLXSW_EMAD_STRING_TLV_STRING_LEN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define MLXSW_EMAD_TIMEOUT_DURING_FW_FLASH_MS 3000
|
|
|
|
|
#define MLXSW_EMAD_TIMEOUT_MS 200
|
|
|
|
|
|
|
|
|
@@ -535,12 +638,14 @@ static void mlxsw_emad_process_response(struct mlxsw_core *mlxsw_core,
|
|
|
|
|
mlxsw_emad_transmit_retry(mlxsw_core, trans);
|
|
|
|
|
} else {
|
|
|
|
|
if (err == 0) {
|
|
|
|
|
char *op_tlv = mlxsw_emad_op_tlv(skb);
|
|
|
|
|
char *reg_tlv = mlxsw_emad_reg_tlv(skb);
|
|
|
|
|
|
|
|
|
|
if (trans->cb)
|
|
|
|
|
trans->cb(mlxsw_core,
|
|
|
|
|
mlxsw_emad_reg_payload(op_tlv),
|
|
|
|
|
mlxsw_emad_reg_payload(reg_tlv),
|
|
|
|
|
trans->reg->len, trans->cb_priv);
|
|
|
|
|
} else {
|
|
|
|
|
mlxsw_emad_process_string_tlv(skb, trans);
|
|
|
|
|
}
|
|
|
|
|
mlxsw_emad_trans_finish(trans, err);
|
|
|
|
|
}
|
|
|
|
@@ -556,6 +661,8 @@ static void mlxsw_emad_rx_listener_func(struct sk_buff *skb, u8 local_port,
|
|
|
|
|
trace_devlink_hwmsg(priv_to_devlink(mlxsw_core), true, 0,
|
|
|
|
|
skb->data, skb->len);
|
|
|
|
|
|
|
|
|
|
mlxsw_emad_tlv_parse(skb);
|
|
|
|
|
|
|
|
|
|
if (!mlxsw_emad_is_resp(skb))
|
|
|
|
|
goto free_skb;
|
|
|
|
|
|
|
|
|
@@ -631,7 +738,7 @@ static void mlxsw_emad_fini(struct mlxsw_core *mlxsw_core)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct sk_buff *mlxsw_emad_alloc(const struct mlxsw_core *mlxsw_core,
|
|
|
|
|
u16 reg_len)
|
|
|
|
|
u16 reg_len, bool enable_string_tlv)
|
|
|
|
|
{
|
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
u16 emad_len;
|
|
|
|
@@ -639,6 +746,8 @@ static struct sk_buff *mlxsw_emad_alloc(const struct mlxsw_core *mlxsw_core,
|
|
|
|
|
emad_len = (reg_len + sizeof(u32) + MLXSW_EMAD_ETH_HDR_LEN +
|
|
|
|
|
(MLXSW_EMAD_OP_TLV_LEN + MLXSW_EMAD_END_TLV_LEN) *
|
|
|
|
|
sizeof(u32) + mlxsw_core->driver->txhdr_len);
|
|
|
|
|
if (enable_string_tlv)
|
|
|
|
|
emad_len += MLXSW_EMAD_STRING_TLV_LEN * sizeof(u32);
|
|
|
|
|
if (emad_len > MLXSW_EMAD_MAX_FRAME_LEN)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
@@ -660,6 +769,7 @@ static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core,
|
|
|
|
|
mlxsw_reg_trans_cb_t *cb,
|
|
|
|
|
unsigned long cb_priv, u64 tid)
|
|
|
|
|
{
|
|
|
|
|
bool enable_string_tlv;
|
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
@@ -667,7 +777,12 @@ static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core,
|
|
|
|
|
tid, reg->id, mlxsw_reg_id_str(reg->id),
|
|
|
|
|
mlxsw_core_reg_access_type_str(type));
|
|
|
|
|
|
|
|
|
|
skb = mlxsw_emad_alloc(mlxsw_core, reg->len);
|
|
|
|
|
/* Since this can be changed during emad_reg_access, read it once and
|
|
|
|
|
* use the value all the way.
|
|
|
|
|
*/
|
|
|
|
|
enable_string_tlv = mlxsw_core->emad.enable_string_tlv;
|
|
|
|
|
|
|
|
|
|
skb = mlxsw_emad_alloc(mlxsw_core, reg->len, enable_string_tlv);
|
|
|
|
|
if (!skb)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
@@ -684,7 +799,8 @@ static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core,
|
|
|
|
|
trans->reg = reg;
|
|
|
|
|
trans->type = type;
|
|
|
|
|
|
|
|
|
|
mlxsw_emad_construct(skb, reg, payload, type, trans->tid);
|
|
|
|
|
mlxsw_emad_construct(skb, reg, payload, type, trans->tid,
|
|
|
|
|
enable_string_tlv);
|
|
|
|
|
mlxsw_core->driver->txhdr_construct(skb, &trans->tx_info);
|
|
|
|
|
|
|
|
|
|
spin_lock_bh(&mlxsw_core->emad.trans_list_lock);
|
|
|
|
@@ -1395,12 +1511,16 @@ static void mlxsw_core_event_listener_func(struct sk_buff *skb, u8 local_port,
|
|
|
|
|
struct mlxsw_event_listener_item *event_listener_item = priv;
|
|
|
|
|
struct mlxsw_reg_info reg;
|
|
|
|
|
char *payload;
|
|
|
|
|
char *op_tlv = mlxsw_emad_op_tlv(skb);
|
|
|
|
|
char *reg_tlv = mlxsw_emad_reg_tlv(skb);
|
|
|
|
|
char *reg_tlv;
|
|
|
|
|
char *op_tlv;
|
|
|
|
|
|
|
|
|
|
mlxsw_emad_tlv_parse(skb);
|
|
|
|
|
op_tlv = mlxsw_emad_op_tlv(skb);
|
|
|
|
|
reg_tlv = mlxsw_emad_reg_tlv(skb);
|
|
|
|
|
|
|
|
|
|
reg.id = mlxsw_emad_op_tlv_register_id_get(op_tlv);
|
|
|
|
|
reg.len = (mlxsw_emad_reg_tlv_len_get(reg_tlv) - 1) * sizeof(u32);
|
|
|
|
|
payload = mlxsw_emad_reg_payload(op_tlv);
|
|
|
|
|
payload = mlxsw_emad_reg_payload(reg_tlv);
|
|
|
|
|
event_listener_item->el.func(®, payload, event_listener_item->priv);
|
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
|
}
|
|
|
|
@@ -1618,8 +1738,11 @@ int mlxsw_reg_trans_write(struct mlxsw_core *mlxsw_core,
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(mlxsw_reg_trans_write);
|
|
|
|
|
|
|
|
|
|
#define MLXSW_REG_TRANS_ERR_STRING_SIZE 256
|
|
|
|
|
|
|
|
|
|
static int mlxsw_reg_trans_wait(struct mlxsw_reg_trans *trans)
|
|
|
|
|
{
|
|
|
|
|
char err_string[MLXSW_REG_TRANS_ERR_STRING_SIZE];
|
|
|
|
|
struct mlxsw_core *mlxsw_core = trans->core;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
@@ -1637,9 +1760,17 @@ static int mlxsw_reg_trans_wait(struct mlxsw_reg_trans *trans)
|
|
|
|
|
mlxsw_core_reg_access_type_str(trans->type),
|
|
|
|
|
trans->emad_status,
|
|
|
|
|
mlxsw_emad_op_tlv_status_str(trans->emad_status));
|
|
|
|
|
|
|
|
|
|
snprintf(err_string, MLXSW_REG_TRANS_ERR_STRING_SIZE,
|
|
|
|
|
"(tid=%llx,reg_id=%x(%s)) %s (%s)\n", trans->tid,
|
|
|
|
|
trans->reg->id, mlxsw_reg_id_str(trans->reg->id),
|
|
|
|
|
mlxsw_emad_op_tlv_status_str(trans->emad_status),
|
|
|
|
|
trans->emad_err_string ? trans->emad_err_string : "");
|
|
|
|
|
|
|
|
|
|
trace_devlink_hwerr(priv_to_devlink(mlxsw_core),
|
|
|
|
|
trans->emad_status,
|
|
|
|
|
mlxsw_emad_op_tlv_status_str(trans->emad_status));
|
|
|
|
|
trans->emad_status, err_string);
|
|
|
|
|
|
|
|
|
|
kfree(trans->emad_err_string);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list_del(&trans->bulk_list);
|
|
|
|
@@ -1713,7 +1844,7 @@ retry:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!err)
|
|
|
|
|
memcpy(payload, mlxsw_emad_reg_payload(out_mbox),
|
|
|
|
|
memcpy(payload, mlxsw_emad_reg_payload_cmd(out_mbox),
|
|
|
|
|
reg->len);
|
|
|
|
|
|
|
|
|
|
mlxsw_cmd_mbox_free(out_mbox);
|
|
|
|
@@ -2210,6 +2341,12 @@ u32 mlxsw_core_read_frc_l(struct mlxsw_core *mlxsw_core)
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(mlxsw_core_read_frc_l);
|
|
|
|
|
|
|
|
|
|
void mlxsw_core_emad_string_tlv_enable(struct mlxsw_core *mlxsw_core)
|
|
|
|
|
{
|
|
|
|
|
mlxsw_core->emad.enable_string_tlv = true;
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(mlxsw_core_emad_string_tlv_enable);
|
|
|
|
|
|
|
|
|
|
static int __init mlxsw_core_module_init(void)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|