diff options
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlxsw/core.c')
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/core.c | 1914 |
1 files changed, 1694 insertions, 220 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index ddedf8ab5b64..83c7cf3bbea3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -20,11 +20,13 @@ #include <linux/rcupdate.h> #include <linux/slab.h> #include <linux/workqueue.h> +#include <linux/firmware.h> #include <asm/byteorder.h> #include <net/devlink.h> #include <trace/events/devlink.h> #include "core.h" +#include "core_env.h" #include "item.h" #include "cmd.h" #include "port.h" @@ -32,6 +34,8 @@ #include "emad.h" #include "reg.h" #include "resources.h" +#include "../mlxfw/mlxfw.h" +#include "txheader.h" static LIST_HEAD(mlxsw_core_driver_list); static DEFINE_SPINLOCK(mlxsw_core_driver_list_lock); @@ -44,7 +48,8 @@ static struct workqueue_struct *mlxsw_owq; struct mlxsw_core_port { struct devlink_port devlink_port; void *port_driver_priv; - u8 local_port; + u16 local_port; + struct mlxsw_linecard *linecard; }; void *mlxsw_core_port_driver_priv(struct mlxsw_core_port *mlxsw_core_port) @@ -66,30 +71,78 @@ struct mlxsw_core { struct workqueue_struct *emad_wq; struct list_head rx_listener_list; struct list_head event_listener_list; + struct list_head irq_event_handler_list; + struct mutex irq_event_handler_lock; /* Locks access to handlers list */ struct { atomic64_t tid; struct list_head trans_list; spinlock_t trans_list_lock; /* protects trans_list writes */ bool use_emad; + bool enable_string_tlv; + bool enable_latency_tlv; } emad; struct { - u8 *mapping; /* lag_id+port_index to local_port mapping */ + u16 *mapping; /* lag_id+port_index to local_port mapping */ } lag; struct mlxsw_res res; struct mlxsw_hwmon *hwmon; struct mlxsw_thermal *thermal; + struct mlxsw_linecards *linecards; struct mlxsw_core_port *ports; unsigned int max_ports; - bool reload_fail; + atomic_t active_ports_count; bool fw_flash_in_progress; - unsigned long driver_priv[0]; + struct { + struct devlink_health_reporter *fw_fatal; + } health; + struct mlxsw_env *env; + unsigned long driver_priv[]; /* driver_priv has to be always the last item */ }; +struct mlxsw_linecards *mlxsw_core_linecards(struct mlxsw_core *mlxsw_core) +{ + return mlxsw_core->linecards; +} + +void mlxsw_core_linecards_set(struct mlxsw_core *mlxsw_core, + struct mlxsw_linecards *linecards) +{ + mlxsw_core->linecards = linecards; +} + #define MLXSW_PORT_MAX_PORTS_DEFAULT 0x40 -static int mlxsw_ports_init(struct mlxsw_core *mlxsw_core) +static u64 mlxsw_ports_occ_get(void *priv) +{ + struct mlxsw_core *mlxsw_core = priv; + + return atomic_read(&mlxsw_core->active_ports_count); +} + +static int mlxsw_core_resources_ports_register(struct mlxsw_core *mlxsw_core) { + struct devlink *devlink = priv_to_devlink(mlxsw_core); + struct devlink_resource_size_params ports_num_params; + u32 max_ports; + + max_ports = mlxsw_core->max_ports - 1; + devlink_resource_size_params_init(&ports_num_params, max_ports, + max_ports, 1, + DEVLINK_RESOURCE_UNIT_ENTRY); + + return devl_resource_register(devlink, + DEVLINK_RESOURCE_GENERIC_NAME_PORTS, + max_ports, MLXSW_CORE_RESOURCE_PORTS, + DEVLINK_RESOURCE_ID_PARENT_TOP, + &ports_num_params); +} + +static int mlxsw_ports_init(struct mlxsw_core *mlxsw_core, bool reload) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_core); + int err; + /* Switch ports are numbered from 1 to queried value */ if (MLXSW_CORE_RES_VALID(mlxsw_core, MAX_SYSTEM_PORT)) mlxsw_core->max_ports = MLXSW_CORE_RES_GET(mlxsw_core, @@ -102,11 +155,30 @@ static int mlxsw_ports_init(struct mlxsw_core *mlxsw_core) if (!mlxsw_core->ports) return -ENOMEM; + if (!reload) { + err = mlxsw_core_resources_ports_register(mlxsw_core); + if (err) + goto err_resources_ports_register; + } + atomic_set(&mlxsw_core->active_ports_count, 0); + devl_resource_occ_get_register(devlink, MLXSW_CORE_RESOURCE_PORTS, + mlxsw_ports_occ_get, mlxsw_core); + return 0; + +err_resources_ports_register: + kfree(mlxsw_core->ports); + return err; } -static void mlxsw_ports_fini(struct mlxsw_core *mlxsw_core) +static void mlxsw_ports_fini(struct mlxsw_core *mlxsw_core, bool reload) { + struct devlink *devlink = priv_to_devlink(mlxsw_core); + + devl_resource_occ_get_unregister(devlink, MLXSW_CORE_RESOURCE_PORTS); + if (!reload) + devl_resources_unregister(priv_to_devlink(mlxsw_core)); + kfree(mlxsw_core->ports); } @@ -116,24 +188,93 @@ unsigned int mlxsw_core_max_ports(const struct mlxsw_core *mlxsw_core) } EXPORT_SYMBOL(mlxsw_core_max_ports); +int mlxsw_core_max_lag(struct mlxsw_core *mlxsw_core, u16 *p_max_lag) +{ + struct mlxsw_driver *driver = mlxsw_core->driver; + + if (driver->profile->used_max_lag) { + *p_max_lag = driver->profile->max_lag; + return 0; + } + + if (!MLXSW_CORE_RES_VALID(mlxsw_core, MAX_LAG)) + return -EIO; + + *p_max_lag = MLXSW_CORE_RES_GET(mlxsw_core, MAX_LAG); + return 0; +} +EXPORT_SYMBOL(mlxsw_core_max_lag); + +enum mlxsw_cmd_mbox_config_profile_lag_mode +mlxsw_core_lag_mode(struct mlxsw_core *mlxsw_core) +{ + return mlxsw_core->bus->lag_mode(mlxsw_core->bus_priv); +} +EXPORT_SYMBOL(mlxsw_core_lag_mode); + +enum mlxsw_cmd_mbox_config_profile_flood_mode +mlxsw_core_flood_mode(struct mlxsw_core *mlxsw_core) +{ + return mlxsw_core->bus->flood_mode(mlxsw_core->bus_priv); +} +EXPORT_SYMBOL(mlxsw_core_flood_mode); + void *mlxsw_core_driver_priv(struct mlxsw_core *mlxsw_core) { return mlxsw_core->driver_priv; } EXPORT_SYMBOL(mlxsw_core_driver_priv); +bool +mlxsw_core_fw_rev_minor_subminor_validate(const struct mlxsw_fw_rev *rev, + const struct mlxsw_fw_rev *req_rev) +{ + return rev->minor > req_rev->minor || + (rev->minor == req_rev->minor && + rev->subminor >= req_rev->subminor); +} +EXPORT_SYMBOL(mlxsw_core_fw_rev_minor_subminor_validate); + struct mlxsw_rx_listener_item { struct list_head list; struct mlxsw_rx_listener rxl; void *priv; + bool enabled; }; struct mlxsw_event_listener_item { struct list_head list; + struct mlxsw_core *mlxsw_core; struct mlxsw_event_listener el; void *priv; }; +static const u8 mlxsw_core_trap_groups[] = { + MLXSW_REG_HTGT_TRAP_GROUP_EMAD, + MLXSW_REG_HTGT_TRAP_GROUP_CORE_EVENT, +}; + +static int mlxsw_core_trap_groups_set(struct mlxsw_core *mlxsw_core) +{ + char htgt_pl[MLXSW_REG_HTGT_LEN]; + int err; + int i; + + if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) + return 0; + + for (i = 0; i < ARRAY_SIZE(mlxsw_core_trap_groups); i++) { + mlxsw_reg_htgt_pack(htgt_pl, mlxsw_core_trap_groups[i], + MLXSW_REG_HTGT_INVALID_POLICER, + MLXSW_REG_HTGT_DEFAULT_PRIORITY, + MLXSW_REG_HTGT_DEFAULT_TC); + err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); + if (err) + return err; + } + return 0; +} + /****************** * EMAD processing ******************/ @@ -234,6 +375,41 @@ 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_latency_tlv_type + * Type of the TLV. + * Must be set to 0x4 (latency TLV). + */ +MLXSW_ITEM32(emad, latency_tlv, type, 0x00, 27, 5); + +/* emad_latency_tlv_len + * Length of the latency TLV in u32. + */ +MLXSW_ITEM32(emad, latency_tlv, len, 0x00, 16, 11); + +/* emad_latency_tlv_latency_time + * EMAD latency time in units of uSec. + */ +MLXSW_ITEM32(emad, latency_tlv, latency_time, 0x04, 0, 32); + /* emad_reg_tlv_type * Type of the TLV. * Must be set to 0x3 (register TLV). @@ -289,6 +465,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, @@ -311,6 +493,12 @@ static void mlxsw_emad_pack_op_tlv(char *op_tlv, mlxsw_emad_op_tlv_tid_set(op_tlv, tid); } +static void mlxsw_emad_pack_latency_tlv(char *latency_tlv) +{ + mlxsw_emad_latency_tlv_type_set(latency_tlv, MLXSW_EMAD_TLV_TYPE_LATENCY); + mlxsw_emad_latency_tlv_len_set(latency_tlv, MLXSW_EMAD_LATENCY_TLV_LEN); +} + static int mlxsw_emad_construct_eth_hdr(struct sk_buff *skb) { char *eth_hdr = skb_push(skb, MLXSW_EMAD_ETH_HDR_LEN); @@ -326,11 +514,11 @@ static int mlxsw_emad_construct_eth_hdr(struct sk_buff *skb) return 0; } -static void mlxsw_emad_construct(struct sk_buff *skb, +static void mlxsw_emad_construct(const struct mlxsw_core *mlxsw_core, + struct sk_buff *skb, const struct mlxsw_reg_info *reg, char *payload, - enum mlxsw_core_reg_access_type type, - u64 tid) + enum mlxsw_core_reg_access_type type, u64 tid) { char *buf; @@ -340,26 +528,102 @@ 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 (mlxsw_core->emad.enable_latency_tlv) { + buf = skb_push(skb, MLXSW_EMAD_LATENCY_TLV_LEN * sizeof(u32)); + mlxsw_emad_pack_latency_tlv(buf); + } + + if (mlxsw_core->emad.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 latency_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 bool mlxsw_emad_tlv_is_latency_tlv(const char *tlv) +{ + u8 tlv_type = mlxsw_emad_latency_tlv_type_get(tlv); + + return tlv_type == MLXSW_EMAD_TLV_TYPE_LATENCY; +} + +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->latency_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); + } + + if (mlxsw_emad_tlv_is_latency_tlv(skb->data + offsets->reg_tlv)) { + offsets->latency_tlv = offsets->reg_tlv; + offsets->reg_tlv += MLXSW_EMAD_LATENCY_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) @@ -414,7 +678,7 @@ struct mlxsw_reg_trans { struct list_head bulk_list; struct mlxsw_core *core; struct sk_buff *tx_skb; - struct mlxsw_tx_info tx_info; + struct mlxsw_txhdr_info txhdr_info; struct delayed_work timeout_dw; unsigned int retries; u64 tid; @@ -425,10 +689,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); + strscpy(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 @@ -439,7 +724,8 @@ static void mlxsw_emad_trans_timeout_schedule(struct mlxsw_reg_trans *trans) if (trans->core->fw_flash_in_progress) timeout = msecs_to_jiffies(MLXSW_EMAD_TIMEOUT_DURING_FW_FLASH_MS); - queue_delayed_work(trans->core->emad_wq, &trans->timeout_dw, timeout); + queue_delayed_work(trans->core->emad_wq, &trans->timeout_dw, + timeout << trans->retries); } static int mlxsw_emad_transmit(struct mlxsw_core *mlxsw_core, @@ -448,16 +734,15 @@ static int mlxsw_emad_transmit(struct mlxsw_core *mlxsw_core, struct sk_buff *skb; int err; - skb = skb_copy(trans->tx_skb, GFP_KERNEL); + skb = skb_clone(trans->tx_skb, GFP_KERNEL); if (!skb) return -ENOMEM; - trace_devlink_hwmsg(priv_to_devlink(mlxsw_core), false, 0, - skb->data + mlxsw_core->driver->txhdr_len, - skb->len - mlxsw_core->driver->txhdr_len); + trace_devlink_hwmsg(priv_to_devlink(mlxsw_core), false, 0, skb->data, + skb->len); atomic_set(&trans->active, 1); - err = mlxsw_core_skb_transmit(mlxsw_core, skb, &trans->tx_info); + err = mlxsw_core_skb_transmit(mlxsw_core, skb, &trans->txhdr_info); if (err) { dev_kfree_skb(skb); return err; @@ -488,6 +773,9 @@ static void mlxsw_emad_transmit_retry(struct mlxsw_core *mlxsw_core, err = mlxsw_emad_transmit(trans->core, trans); if (err == 0) return; + + if (!atomic_dec_and_test(&trans->active)) + return; } else { err = -EIO; } @@ -520,19 +808,21 @@ 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); } } /* called with rcu read lock held */ -static void mlxsw_emad_rx_listener_func(struct sk_buff *skb, u8 local_port, +static void mlxsw_emad_rx_listener_func(struct sk_buff *skb, u16 local_port, void *priv) { struct mlxsw_core *mlxsw_core = priv; @@ -541,6 +831,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; @@ -557,7 +849,33 @@ free_skb: static const struct mlxsw_listener mlxsw_emad_rx_listener = MLXSW_RXL(mlxsw_emad_rx_listener_func, ETHEMAD, TRAP_TO_CPU, false, - EMAD, DISCARD); + EMAD, FORWARD); + +static int mlxsw_emad_tlv_enable(struct mlxsw_core *mlxsw_core) +{ + char mgir_pl[MLXSW_REG_MGIR_LEN]; + bool string_tlv, latency_tlv; + int err; + + mlxsw_reg_mgir_pack(mgir_pl); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgir), mgir_pl); + if (err) + return err; + + string_tlv = mlxsw_reg_mgir_fw_info_string_tlv_get(mgir_pl); + mlxsw_core->emad.enable_string_tlv = string_tlv; + + latency_tlv = mlxsw_reg_mgir_fw_info_latency_tlv_get(mgir_pl); + mlxsw_core->emad.enable_latency_tlv = latency_tlv; + + return 0; +} + +static void mlxsw_emad_tlv_disable(struct mlxsw_core *mlxsw_core) +{ + mlxsw_core->emad.enable_latency_tlv = false; + mlxsw_core->emad.enable_string_tlv = false; +} static int mlxsw_emad_init(struct mlxsw_core *mlxsw_core) { @@ -568,7 +886,7 @@ static int mlxsw_emad_init(struct mlxsw_core *mlxsw_core) if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) return 0; - emad_wq = alloc_workqueue("mlxsw_core_emad", WQ_MEM_RECLAIM, 0); + emad_wq = alloc_workqueue("mlxsw_core_emad", WQ_PERCPU, 0); if (!emad_wq) return -ENOMEM; mlxsw_core->emad_wq = emad_wq; @@ -587,18 +905,20 @@ static int mlxsw_emad_init(struct mlxsw_core *mlxsw_core) err = mlxsw_core_trap_register(mlxsw_core, &mlxsw_emad_rx_listener, mlxsw_core); if (err) - return err; + goto err_trap_register; - err = mlxsw_core->driver->basic_trap_groups_set(mlxsw_core); + err = mlxsw_emad_tlv_enable(mlxsw_core); if (err) - goto err_emad_trap_set; + goto err_emad_tlv_enable; + mlxsw_core->emad.use_emad = true; return 0; -err_emad_trap_set: +err_emad_tlv_enable: mlxsw_core_trap_unregister(mlxsw_core, &mlxsw_emad_rx_listener, mlxsw_core); +err_trap_register: destroy_workqueue(mlxsw_core->emad_wq); return err; } @@ -610,6 +930,7 @@ static void mlxsw_emad_fini(struct mlxsw_core *mlxsw_core) return; mlxsw_core->emad.use_emad = false; + mlxsw_emad_tlv_disable(mlxsw_core); mlxsw_core_trap_unregister(mlxsw_core, &mlxsw_emad_rx_listener, mlxsw_core); destroy_workqueue(mlxsw_core->emad_wq); @@ -623,7 +944,11 @@ 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); + sizeof(u32) + MLXSW_TXHDR_LEN); + if (mlxsw_core->emad.enable_string_tlv) + emad_len += MLXSW_EMAD_STRING_TLV_LEN * sizeof(u32); + if (mlxsw_core->emad.enable_latency_tlv) + emad_len += MLXSW_EMAD_LATENCY_TLV_LEN * sizeof(u32); if (emad_len > MLXSW_EMAD_MAX_FRAME_LEN) return NULL; @@ -659,8 +984,8 @@ static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core, list_add_tail(&trans->bulk_list, bulk_list); trans->core = mlxsw_core; trans->tx_skb = skb; - trans->tx_info.local_port = MLXSW_PORT_CPU_PORT; - trans->tx_info.is_emad = true; + trans->txhdr_info.tx_info.local_port = MLXSW_PORT_CPU_PORT; + trans->txhdr_info.tx_info.is_emad = true; INIT_DELAYED_WORK(&trans->timeout_dw, mlxsw_emad_trans_timeout_work); trans->tid = tid; init_completion(&trans->completion); @@ -669,8 +994,7 @@ 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_core->driver->txhdr_construct(skb, &trans->tx_info); + mlxsw_emad_construct(mlxsw_core, skb, reg, payload, type, trans->tid); spin_lock_bh(&mlxsw_core->emad.trans_list_lock); list_add_tail_rcu(&trans->list, &mlxsw_core->emad.trans_list); @@ -731,36 +1055,329 @@ static struct mlxsw_driver *mlxsw_core_driver_get(const char *kind) return mlxsw_driver; } +int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core, + struct mlxfw_dev *mlxfw_dev, + const struct firmware *firmware, + struct netlink_ext_ack *extack) +{ + int err; + + mlxsw_core->fw_flash_in_progress = true; + err = mlxfw_firmware_flash(mlxfw_dev, firmware, extack); + mlxsw_core->fw_flash_in_progress = false; + + return err; +} + +struct mlxsw_core_fw_info { + struct mlxfw_dev mlxfw_dev; + struct mlxsw_core *mlxsw_core; +}; + +static int mlxsw_core_fw_component_query(struct mlxfw_dev *mlxfw_dev, + u16 component_index, u32 *p_max_size, + u8 *p_align_bits, u16 *p_max_write_size) +{ + struct mlxsw_core_fw_info *mlxsw_core_fw_info = + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; + char mcqi_pl[MLXSW_REG_MCQI_LEN]; + int err; + + mlxsw_reg_mcqi_pack(mcqi_pl, component_index); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcqi), mcqi_pl); + if (err) + return err; + mlxsw_reg_mcqi_unpack(mcqi_pl, p_max_size, p_align_bits, p_max_write_size); + + *p_align_bits = max_t(u8, *p_align_bits, 2); + *p_max_write_size = min_t(u16, *p_max_write_size, MLXSW_REG_MCDA_MAX_DATA_LEN); + return 0; +} + +static int mlxsw_core_fw_fsm_lock(struct mlxfw_dev *mlxfw_dev, u32 *fwhandle) +{ + struct mlxsw_core_fw_info *mlxsw_core_fw_info = + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; + char mcc_pl[MLXSW_REG_MCC_LEN]; + u8 control_state; + int err; + + mlxsw_reg_mcc_pack(mcc_pl, 0, 0, 0, 0); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcc), mcc_pl); + if (err) + return err; + + mlxsw_reg_mcc_unpack(mcc_pl, fwhandle, NULL, &control_state); + if (control_state != MLXFW_FSM_STATE_IDLE) + return -EBUSY; + + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_LOCK_UPDATE_HANDLE, 0, *fwhandle, 0); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl); +} + +static int mlxsw_core_fw_fsm_component_update(struct mlxfw_dev *mlxfw_dev, u32 fwhandle, + u16 component_index, u32 component_size) +{ + struct mlxsw_core_fw_info *mlxsw_core_fw_info = + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; + char mcc_pl[MLXSW_REG_MCC_LEN]; + + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_UPDATE_COMPONENT, + component_index, fwhandle, component_size); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl); +} + +static int mlxsw_core_fw_fsm_block_download(struct mlxfw_dev *mlxfw_dev, u32 fwhandle, + u8 *data, u16 size, u32 offset) +{ + struct mlxsw_core_fw_info *mlxsw_core_fw_info = + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; + char mcda_pl[MLXSW_REG_MCDA_LEN]; + + mlxsw_reg_mcda_pack(mcda_pl, fwhandle, offset, size, data); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcda), mcda_pl); +} + +static int mlxsw_core_fw_fsm_component_verify(struct mlxfw_dev *mlxfw_dev, u32 fwhandle, + u16 component_index) +{ + struct mlxsw_core_fw_info *mlxsw_core_fw_info = + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; + char mcc_pl[MLXSW_REG_MCC_LEN]; + + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_VERIFY_COMPONENT, + component_index, fwhandle, 0); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl); +} + +static int mlxsw_core_fw_fsm_activate(struct mlxfw_dev *mlxfw_dev, u32 fwhandle) +{ + struct mlxsw_core_fw_info *mlxsw_core_fw_info = + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; + char mcc_pl[MLXSW_REG_MCC_LEN]; + + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_ACTIVATE, 0, fwhandle, 0); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl); +} + +static int mlxsw_core_fw_fsm_query_state(struct mlxfw_dev *mlxfw_dev, u32 fwhandle, + enum mlxfw_fsm_state *fsm_state, + enum mlxfw_fsm_state_err *fsm_state_err) +{ + struct mlxsw_core_fw_info *mlxsw_core_fw_info = + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; + char mcc_pl[MLXSW_REG_MCC_LEN]; + u8 control_state; + u8 error_code; + int err; + + mlxsw_reg_mcc_pack(mcc_pl, 0, 0, fwhandle, 0); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcc), mcc_pl); + if (err) + return err; + + mlxsw_reg_mcc_unpack(mcc_pl, NULL, &error_code, &control_state); + *fsm_state = control_state; + *fsm_state_err = min_t(enum mlxfw_fsm_state_err, error_code, MLXFW_FSM_STATE_ERR_MAX); + return 0; +} + +static void mlxsw_core_fw_fsm_cancel(struct mlxfw_dev *mlxfw_dev, u32 fwhandle) +{ + struct mlxsw_core_fw_info *mlxsw_core_fw_info = + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; + char mcc_pl[MLXSW_REG_MCC_LEN]; + + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_CANCEL, 0, fwhandle, 0); + mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl); +} + +static void mlxsw_core_fw_fsm_release(struct mlxfw_dev *mlxfw_dev, u32 fwhandle) +{ + struct mlxsw_core_fw_info *mlxsw_core_fw_info = + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; + char mcc_pl[MLXSW_REG_MCC_LEN]; + + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_RELEASE_UPDATE_HANDLE, 0, fwhandle, 0); + mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl); +} + +static const struct mlxfw_dev_ops mlxsw_core_fw_mlxsw_dev_ops = { + .component_query = mlxsw_core_fw_component_query, + .fsm_lock = mlxsw_core_fw_fsm_lock, + .fsm_component_update = mlxsw_core_fw_fsm_component_update, + .fsm_block_download = mlxsw_core_fw_fsm_block_download, + .fsm_component_verify = mlxsw_core_fw_fsm_component_verify, + .fsm_activate = mlxsw_core_fw_fsm_activate, + .fsm_query_state = mlxsw_core_fw_fsm_query_state, + .fsm_cancel = mlxsw_core_fw_fsm_cancel, + .fsm_release = mlxsw_core_fw_fsm_release, +}; + +static int mlxsw_core_dev_fw_flash(struct mlxsw_core *mlxsw_core, + const struct firmware *firmware, + struct netlink_ext_ack *extack) +{ + struct mlxsw_core_fw_info mlxsw_core_fw_info = { + .mlxfw_dev = { + .ops = &mlxsw_core_fw_mlxsw_dev_ops, + .psid = mlxsw_core->bus_info->psid, + .psid_size = strlen(mlxsw_core->bus_info->psid), + .devlink = priv_to_devlink(mlxsw_core), + }, + .mlxsw_core = mlxsw_core + }; + + return mlxsw_core_fw_flash(mlxsw_core, &mlxsw_core_fw_info.mlxfw_dev, + firmware, extack); +} + +static int mlxsw_core_fw_rev_validate(struct mlxsw_core *mlxsw_core, + const struct mlxsw_bus_info *mlxsw_bus_info, + const struct mlxsw_fw_rev *req_rev, + const char *filename) +{ + const struct mlxsw_fw_rev *rev = &mlxsw_bus_info->fw_rev; + union devlink_param_value value; + const struct firmware *firmware; + int err; + + /* Don't check if driver does not require it */ + if (!req_rev || !filename) + return 0; + + /* Don't check if devlink 'fw_load_policy' param is 'flash' */ + err = devl_param_driverinit_value_get(priv_to_devlink(mlxsw_core), + DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY, + &value); + if (err) + return err; + if (value.vu8 == DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH) + return 0; + + /* Validate driver & FW are compatible */ + if (rev->major != req_rev->major) { + WARN(1, "Mismatch in major FW version [%d:%d] is never expected; Please contact support\n", + rev->major, req_rev->major); + return -EINVAL; + } + if (mlxsw_core_fw_rev_minor_subminor_validate(rev, req_rev)) + return 0; + + dev_err(mlxsw_bus_info->dev, "The firmware version %d.%d.%d is incompatible with the driver (required >= %d.%d.%d)\n", + rev->major, rev->minor, rev->subminor, req_rev->major, + req_rev->minor, req_rev->subminor); + dev_info(mlxsw_bus_info->dev, "Flashing firmware using file %s\n", filename); + + err = request_firmware_direct(&firmware, filename, mlxsw_bus_info->dev); + if (err) { + dev_err(mlxsw_bus_info->dev, "Could not request firmware file %s\n", filename); + return err; + } + + err = mlxsw_core_dev_fw_flash(mlxsw_core, firmware, NULL); + release_firmware(firmware); + if (err) + dev_err(mlxsw_bus_info->dev, "Could not upgrade firmware\n"); + + /* On FW flash success, tell the caller FW reset is needed + * if current FW supports it. + */ + if (rev->minor >= req_rev->can_reset_minor) + return err ? err : -EAGAIN; + else + return 0; +} + +static int mlxsw_core_fw_flash_update(struct mlxsw_core *mlxsw_core, + struct devlink_flash_update_params *params, + struct netlink_ext_ack *extack) +{ + return mlxsw_core_dev_fw_flash(mlxsw_core, params->fw, extack); +} + +static int mlxsw_core_devlink_param_fw_load_policy_validate(struct devlink *devlink, u32 id, + union devlink_param_value val, + struct netlink_ext_ack *extack) +{ + if (val.vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER && + val.vu8 != DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH) { + NL_SET_ERR_MSG_MOD(extack, "'fw_load_policy' must be 'driver' or 'flash'"); + return -EINVAL; + } + + return 0; +} + +static const struct devlink_param mlxsw_core_fw_devlink_params[] = { + DEVLINK_PARAM_GENERIC(FW_LOAD_POLICY, BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), NULL, NULL, + mlxsw_core_devlink_param_fw_load_policy_validate), +}; + +static int mlxsw_core_fw_params_register(struct mlxsw_core *mlxsw_core) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_core); + union devlink_param_value value; + int err; + + err = devl_params_register(devlink, mlxsw_core_fw_devlink_params, + ARRAY_SIZE(mlxsw_core_fw_devlink_params)); + if (err) + return err; + + value.vu8 = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER; + devl_param_driverinit_value_set(devlink, + DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY, + value); + return 0; +} + +static void mlxsw_core_fw_params_unregister(struct mlxsw_core *mlxsw_core) +{ + devl_params_unregister(priv_to_devlink(mlxsw_core), mlxsw_core_fw_devlink_params, + ARRAY_SIZE(mlxsw_core_fw_devlink_params)); +} + +static void *__dl_port(struct devlink_port *devlink_port) +{ + return container_of(devlink_port, struct mlxsw_core_port, devlink_port); +} + static int mlxsw_devlink_port_split(struct devlink *devlink, - unsigned int port_index, + struct devlink_port *port, unsigned int count, struct netlink_ext_ack *extack) { + struct mlxsw_core_port *mlxsw_core_port = __dl_port(port); struct mlxsw_core *mlxsw_core = devlink_priv(devlink); - if (port_index >= mlxsw_core->max_ports) { - NL_SET_ERR_MSG_MOD(extack, "Port index exceeds maximum number of ports"); - return -EINVAL; - } if (!mlxsw_core->driver->port_split) return -EOPNOTSUPP; - return mlxsw_core->driver->port_split(mlxsw_core, port_index, count, - extack); + return mlxsw_core->driver->port_split(mlxsw_core, + mlxsw_core_port->local_port, + count, extack); } static int mlxsw_devlink_port_unsplit(struct devlink *devlink, - unsigned int port_index, + struct devlink_port *port, struct netlink_ext_ack *extack) { + struct mlxsw_core_port *mlxsw_core_port = __dl_port(port); struct mlxsw_core *mlxsw_core = devlink_priv(devlink); - if (port_index >= mlxsw_core->max_ports) { - NL_SET_ERR_MSG_MOD(extack, "Port index exceeds maximum number of ports"); - return -EINVAL; - } if (!mlxsw_core->driver->port_unsplit) return -EOPNOTSUPP; - return mlxsw_core->driver->port_unsplit(mlxsw_core, port_index, + return mlxsw_core->driver->port_unsplit(mlxsw_core, + mlxsw_core_port->local_port, extack); } @@ -781,7 +1398,8 @@ mlxsw_devlink_sb_pool_get(struct devlink *devlink, static int mlxsw_devlink_sb_pool_set(struct devlink *devlink, unsigned int sb_index, u16 pool_index, u32 size, - enum devlink_sb_threshold_type threshold_type) + enum devlink_sb_threshold_type threshold_type, + struct netlink_ext_ack *extack) { struct mlxsw_core *mlxsw_core = devlink_priv(devlink); struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; @@ -789,27 +1407,8 @@ mlxsw_devlink_sb_pool_set(struct devlink *devlink, if (!mlxsw_driver->sb_pool_set) return -EOPNOTSUPP; return mlxsw_driver->sb_pool_set(mlxsw_core, sb_index, - pool_index, size, threshold_type); -} - -static void *__dl_port(struct devlink_port *devlink_port) -{ - return container_of(devlink_port, struct mlxsw_core_port, devlink_port); -} - -static int mlxsw_devlink_port_type_set(struct devlink_port *devlink_port, - enum devlink_port_type port_type) -{ - struct mlxsw_core *mlxsw_core = devlink_priv(devlink_port->devlink); - struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; - struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port); - - if (!mlxsw_driver->port_type_set) - return -EOPNOTSUPP; - - return mlxsw_driver->port_type_set(mlxsw_core, - mlxsw_core_port->local_port, - port_type); + pool_index, size, threshold_type, + extack); } static int mlxsw_devlink_sb_port_pool_get(struct devlink_port *devlink_port, @@ -829,7 +1428,8 @@ static int mlxsw_devlink_sb_port_pool_get(struct devlink_port *devlink_port, static int mlxsw_devlink_sb_port_pool_set(struct devlink_port *devlink_port, unsigned int sb_index, u16 pool_index, - u32 threshold) + u32 threshold, + struct netlink_ext_ack *extack) { struct mlxsw_core *mlxsw_core = devlink_priv(devlink_port->devlink); struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; @@ -839,7 +1439,7 @@ static int mlxsw_devlink_sb_port_pool_set(struct devlink_port *devlink_port, !mlxsw_core_port_check(mlxsw_core_port)) return -EOPNOTSUPP; return mlxsw_driver->sb_port_pool_set(mlxsw_core_port, sb_index, - pool_index, threshold); + pool_index, threshold, extack); } static int @@ -864,7 +1464,8 @@ static int mlxsw_devlink_sb_tc_pool_bind_set(struct devlink_port *devlink_port, unsigned int sb_index, u16 tc_index, enum devlink_sb_pool_type pool_type, - u16 pool_index, u32 threshold) + u16 pool_index, u32 threshold, + struct netlink_ext_ack *extack) { struct mlxsw_core *mlxsw_core = devlink_priv(devlink_port->devlink); struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; @@ -875,7 +1476,7 @@ mlxsw_devlink_sb_tc_pool_bind_set(struct devlink_port *devlink_port, return -EOPNOTSUPP; return mlxsw_driver->sb_tc_pool_bind_set(mlxsw_core_port, sb_index, tc_index, pool_type, - pool_index, threshold); + pool_index, threshold, extack); } static int mlxsw_devlink_sb_occ_snapshot(struct devlink *devlink, @@ -934,30 +1535,207 @@ mlxsw_devlink_sb_occ_tc_port_bind_get(struct devlink_port *devlink_port, pool_type, p_cur, p_max); } -static int mlxsw_devlink_core_bus_device_reload(struct devlink *devlink, - struct netlink_ext_ack *extack) +static int +mlxsw_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, + struct netlink_ext_ack *extack) { struct mlxsw_core *mlxsw_core = devlink_priv(devlink); + char fw_info_psid[MLXSW_REG_MGIR_FW_INFO_PSID_SIZE]; + u32 hw_rev, fw_major, fw_minor, fw_sub_minor; + char mgir_pl[MLXSW_REG_MGIR_LEN]; + char buf[32]; int err; + mlxsw_reg_mgir_pack(mgir_pl); + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgir), mgir_pl); + if (err) + return err; + mlxsw_reg_mgir_unpack(mgir_pl, &hw_rev, fw_info_psid, &fw_major, + &fw_minor, &fw_sub_minor); + + sprintf(buf, "%X", hw_rev); + err = devlink_info_version_fixed_put(req, "hw.revision", buf); + if (err) + return err; + + err = devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW_PSID, + fw_info_psid); + if (err) + return err; + + sprintf(buf, "%d.%d.%d", fw_major, fw_minor, fw_sub_minor); + err = devlink_info_version_running_put(req, "fw.version", buf); + if (err) + return err; + + return devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW, + buf); +} + +static int +mlxsw_devlink_core_bus_device_reload_down(struct devlink *devlink, + bool netns_change, enum devlink_reload_action action, + enum devlink_reload_limit limit, + struct netlink_ext_ack *extack) +{ + struct mlxsw_core *mlxsw_core = devlink_priv(devlink); + if (!(mlxsw_core->bus->features & MLXSW_BUS_F_RESET)) return -EOPNOTSUPP; mlxsw_core_bus_device_unregister(mlxsw_core, true); + return 0; +} + +static int +mlxsw_devlink_core_bus_device_reload_up(struct devlink *devlink, enum devlink_reload_action action, + enum devlink_reload_limit limit, u32 *actions_performed, + struct netlink_ext_ack *extack) +{ + struct mlxsw_core *mlxsw_core = devlink_priv(devlink); + int err; + + *actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) | + BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE); err = mlxsw_core_bus_device_register(mlxsw_core->bus_info, mlxsw_core->bus, mlxsw_core->bus_priv, true, - devlink); - mlxsw_core->reload_fail = !!err; - + devlink, extack); return err; } +static int mlxsw_devlink_flash_update(struct devlink *devlink, + struct devlink_flash_update_params *params, + struct netlink_ext_ack *extack) +{ + struct mlxsw_core *mlxsw_core = devlink_priv(devlink); + + return mlxsw_core_fw_flash_update(mlxsw_core, params, extack); +} + +static int mlxsw_devlink_trap_init(struct devlink *devlink, + const struct devlink_trap *trap, + void *trap_ctx) +{ + struct mlxsw_core *mlxsw_core = devlink_priv(devlink); + struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; + + if (!mlxsw_driver->trap_init) + return -EOPNOTSUPP; + return mlxsw_driver->trap_init(mlxsw_core, trap, trap_ctx); +} + +static void mlxsw_devlink_trap_fini(struct devlink *devlink, + const struct devlink_trap *trap, + void *trap_ctx) +{ + struct mlxsw_core *mlxsw_core = devlink_priv(devlink); + struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; + + if (!mlxsw_driver->trap_fini) + return; + mlxsw_driver->trap_fini(mlxsw_core, trap, trap_ctx); +} + +static int mlxsw_devlink_trap_action_set(struct devlink *devlink, + const struct devlink_trap *trap, + enum devlink_trap_action action, + struct netlink_ext_ack *extack) +{ + struct mlxsw_core *mlxsw_core = devlink_priv(devlink); + struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; + + if (!mlxsw_driver->trap_action_set) + return -EOPNOTSUPP; + return mlxsw_driver->trap_action_set(mlxsw_core, trap, action, extack); +} + +static int +mlxsw_devlink_trap_group_init(struct devlink *devlink, + const struct devlink_trap_group *group) +{ + struct mlxsw_core *mlxsw_core = devlink_priv(devlink); + struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; + + if (!mlxsw_driver->trap_group_init) + return -EOPNOTSUPP; + return mlxsw_driver->trap_group_init(mlxsw_core, group); +} + +static int +mlxsw_devlink_trap_group_set(struct devlink *devlink, + const struct devlink_trap_group *group, + const struct devlink_trap_policer *policer, + struct netlink_ext_ack *extack) +{ + struct mlxsw_core *mlxsw_core = devlink_priv(devlink); + struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; + + if (!mlxsw_driver->trap_group_set) + return -EOPNOTSUPP; + return mlxsw_driver->trap_group_set(mlxsw_core, group, policer, extack); +} + +static int +mlxsw_devlink_trap_policer_init(struct devlink *devlink, + const struct devlink_trap_policer *policer) +{ + struct mlxsw_core *mlxsw_core = devlink_priv(devlink); + struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; + + if (!mlxsw_driver->trap_policer_init) + return -EOPNOTSUPP; + return mlxsw_driver->trap_policer_init(mlxsw_core, policer); +} + +static void +mlxsw_devlink_trap_policer_fini(struct devlink *devlink, + const struct devlink_trap_policer *policer) +{ + struct mlxsw_core *mlxsw_core = devlink_priv(devlink); + struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; + + if (!mlxsw_driver->trap_policer_fini) + return; + mlxsw_driver->trap_policer_fini(mlxsw_core, policer); +} + +static int +mlxsw_devlink_trap_policer_set(struct devlink *devlink, + const struct devlink_trap_policer *policer, + u64 rate, u64 burst, + struct netlink_ext_ack *extack) +{ + struct mlxsw_core *mlxsw_core = devlink_priv(devlink); + struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; + + if (!mlxsw_driver->trap_policer_set) + return -EOPNOTSUPP; + return mlxsw_driver->trap_policer_set(mlxsw_core, policer, rate, burst, + extack); +} + +static int +mlxsw_devlink_trap_policer_counter_get(struct devlink *devlink, + const struct devlink_trap_policer *policer, + u64 *p_drops) +{ + struct mlxsw_core *mlxsw_core = devlink_priv(devlink); + struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; + + if (!mlxsw_driver->trap_policer_counter_get) + return -EOPNOTSUPP; + return mlxsw_driver->trap_policer_counter_get(mlxsw_core, policer, + p_drops); +} + static const struct devlink_ops mlxsw_devlink_ops = { - .reload = mlxsw_devlink_core_bus_device_reload, - .port_type_set = mlxsw_devlink_port_type_set, - .port_split = mlxsw_devlink_port_split, - .port_unsplit = mlxsw_devlink_port_unsplit, + .reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT) | + BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE), + .reload_down = mlxsw_devlink_core_bus_device_reload_down, + .reload_up = mlxsw_devlink_core_bus_device_reload_up, .sb_pool_get = mlxsw_devlink_sb_pool_get, .sb_pool_set = mlxsw_devlink_sb_pool_set, .sb_port_pool_get = mlxsw_devlink_sb_port_pool_get, @@ -968,19 +1746,363 @@ static const struct devlink_ops mlxsw_devlink_ops = { .sb_occ_max_clear = mlxsw_devlink_sb_occ_max_clear, .sb_occ_port_pool_get = mlxsw_devlink_sb_occ_port_pool_get, .sb_occ_tc_port_bind_get = mlxsw_devlink_sb_occ_tc_port_bind_get, + .info_get = mlxsw_devlink_info_get, + .flash_update = mlxsw_devlink_flash_update, + .trap_init = mlxsw_devlink_trap_init, + .trap_fini = mlxsw_devlink_trap_fini, + .trap_action_set = mlxsw_devlink_trap_action_set, + .trap_group_init = mlxsw_devlink_trap_group_init, + .trap_group_set = mlxsw_devlink_trap_group_set, + .trap_policer_init = mlxsw_devlink_trap_policer_init, + .trap_policer_fini = mlxsw_devlink_trap_policer_fini, + .trap_policer_set = mlxsw_devlink_trap_policer_set, + .trap_policer_counter_get = mlxsw_devlink_trap_policer_counter_get, +}; + +static int mlxsw_core_params_register(struct mlxsw_core *mlxsw_core) +{ + return mlxsw_core_fw_params_register(mlxsw_core); +} + +static void mlxsw_core_params_unregister(struct mlxsw_core *mlxsw_core) +{ + mlxsw_core_fw_params_unregister(mlxsw_core); +} + +struct mlxsw_core_health_event { + struct mlxsw_core *mlxsw_core; + char mfde_pl[MLXSW_REG_MFDE_LEN]; + struct work_struct work; +}; + +static void mlxsw_core_health_event_work(struct work_struct *work) +{ + struct mlxsw_core_health_event *event; + struct mlxsw_core *mlxsw_core; + + event = container_of(work, struct mlxsw_core_health_event, work); + mlxsw_core = event->mlxsw_core; + devlink_health_report(mlxsw_core->health.fw_fatal, "FW fatal event occurred", + event->mfde_pl); + kfree(event); +} + +static void mlxsw_core_health_listener_func(const struct mlxsw_reg_info *reg, + char *mfde_pl, void *priv) +{ + struct mlxsw_core_health_event *event; + struct mlxsw_core *mlxsw_core = priv; + + event = kmalloc(sizeof(*event), GFP_ATOMIC); + if (!event) + return; + event->mlxsw_core = mlxsw_core; + memcpy(event->mfde_pl, mfde_pl, sizeof(event->mfde_pl)); + INIT_WORK(&event->work, mlxsw_core_health_event_work); + mlxsw_core_schedule_work(&event->work); +} + +static const struct mlxsw_listener mlxsw_core_health_listener = + MLXSW_CORE_EVENTL(mlxsw_core_health_listener_func, MFDE); + +static void +mlxsw_core_health_fw_fatal_dump_fatal_cause(const char *mfde_pl, + struct devlink_fmsg *fmsg) +{ + u32 val, tile_v; + + val = mlxsw_reg_mfde_fatal_cause_id_get(mfde_pl); + devlink_fmsg_u32_pair_put(fmsg, "cause_id", val); + tile_v = mlxsw_reg_mfde_fatal_cause_tile_v_get(mfde_pl); + if (tile_v) { + val = mlxsw_reg_mfde_fatal_cause_tile_index_get(mfde_pl); + devlink_fmsg_u8_pair_put(fmsg, "tile_index", val); + } +} + +static void +mlxsw_core_health_fw_fatal_dump_fw_assert(const char *mfde_pl, + struct devlink_fmsg *fmsg) +{ + u32 val, tile_v; + + val = mlxsw_reg_mfde_fw_assert_var0_get(mfde_pl); + devlink_fmsg_u32_pair_put(fmsg, "var0", val); + val = mlxsw_reg_mfde_fw_assert_var1_get(mfde_pl); + devlink_fmsg_u32_pair_put(fmsg, "var1", val); + val = mlxsw_reg_mfde_fw_assert_var2_get(mfde_pl); + devlink_fmsg_u32_pair_put(fmsg, "var2", val); + val = mlxsw_reg_mfde_fw_assert_var3_get(mfde_pl); + devlink_fmsg_u32_pair_put(fmsg, "var3", val); + val = mlxsw_reg_mfde_fw_assert_var4_get(mfde_pl); + devlink_fmsg_u32_pair_put(fmsg, "var4", val); + val = mlxsw_reg_mfde_fw_assert_existptr_get(mfde_pl); + devlink_fmsg_u32_pair_put(fmsg, "existptr", val); + val = mlxsw_reg_mfde_fw_assert_callra_get(mfde_pl); + devlink_fmsg_u32_pair_put(fmsg, "callra", val); + val = mlxsw_reg_mfde_fw_assert_oe_get(mfde_pl); + devlink_fmsg_bool_pair_put(fmsg, "old_event", val); + tile_v = mlxsw_reg_mfde_fw_assert_tile_v_get(mfde_pl); + if (tile_v) { + val = mlxsw_reg_mfde_fw_assert_tile_index_get(mfde_pl); + devlink_fmsg_u8_pair_put(fmsg, "tile_index", val); + } + val = mlxsw_reg_mfde_fw_assert_ext_synd_get(mfde_pl); + devlink_fmsg_u32_pair_put(fmsg, "ext_synd", val); +} + +static void +mlxsw_core_health_fw_fatal_dump_kvd_im_stop(const char *mfde_pl, + struct devlink_fmsg *fmsg) +{ + u32 val; + + val = mlxsw_reg_mfde_kvd_im_stop_oe_get(mfde_pl); + devlink_fmsg_bool_pair_put(fmsg, "old_event", val); + val = mlxsw_reg_mfde_kvd_im_stop_pipes_mask_get(mfde_pl); + devlink_fmsg_u32_pair_put(fmsg, "pipes_mask", val); +} + +static void +mlxsw_core_health_fw_fatal_dump_crspace_to(const char *mfde_pl, + struct devlink_fmsg *fmsg) +{ + u32 val; + + val = mlxsw_reg_mfde_crspace_to_log_address_get(mfde_pl); + devlink_fmsg_u32_pair_put(fmsg, "log_address", val); + val = mlxsw_reg_mfde_crspace_to_oe_get(mfde_pl); + devlink_fmsg_bool_pair_put(fmsg, "old_event", val); + val = mlxsw_reg_mfde_crspace_to_log_id_get(mfde_pl); + devlink_fmsg_u8_pair_put(fmsg, "log_irisc_id", val); + val = mlxsw_reg_mfde_crspace_to_log_ip_get(mfde_pl); + devlink_fmsg_u64_pair_put(fmsg, "log_ip", val); +} + +static int mlxsw_core_health_fw_fatal_dump(struct devlink_health_reporter *reporter, + struct devlink_fmsg *fmsg, void *priv_ctx, + struct netlink_ext_ack *extack) +{ + char *mfde_pl = priv_ctx; + char *val_str; + u8 event_id; + u32 val; + + if (!priv_ctx) + /* User-triggered dumps are not possible */ + return -EOPNOTSUPP; + + val = mlxsw_reg_mfde_irisc_id_get(mfde_pl); + devlink_fmsg_u8_pair_put(fmsg, "irisc_id", val); + + devlink_fmsg_arr_pair_nest_start(fmsg, "event"); + event_id = mlxsw_reg_mfde_event_id_get(mfde_pl); + devlink_fmsg_u32_pair_put(fmsg, "id", event_id); + switch (event_id) { + case MLXSW_REG_MFDE_EVENT_ID_CRSPACE_TO: + val_str = "CR space timeout"; + break; + case MLXSW_REG_MFDE_EVENT_ID_KVD_IM_STOP: + val_str = "KVD insertion machine stopped"; + break; + case MLXSW_REG_MFDE_EVENT_ID_TEST: + val_str = "Test"; + break; + case MLXSW_REG_MFDE_EVENT_ID_FW_ASSERT: + val_str = "FW assert"; + break; + case MLXSW_REG_MFDE_EVENT_ID_FATAL_CAUSE: + val_str = "Fatal cause"; + break; + default: + val_str = NULL; + } + if (val_str) + devlink_fmsg_string_pair_put(fmsg, "desc", val_str); + devlink_fmsg_arr_pair_nest_end(fmsg); + + devlink_fmsg_arr_pair_nest_start(fmsg, "severity"); + val = mlxsw_reg_mfde_severity_get(mfde_pl); + devlink_fmsg_u8_pair_put(fmsg, "id", val); + switch (val) { + case MLXSW_REG_MFDE_SEVERITY_FATL: + val_str = "Fatal"; + break; + case MLXSW_REG_MFDE_SEVERITY_NRML: + val_str = "Normal"; + break; + case MLXSW_REG_MFDE_SEVERITY_INTR: + val_str = "Debug"; + break; + default: + val_str = NULL; + } + if (val_str) + devlink_fmsg_string_pair_put(fmsg, "desc", val_str); + devlink_fmsg_arr_pair_nest_end(fmsg); + + val = mlxsw_reg_mfde_method_get(mfde_pl); + switch (val) { + case MLXSW_REG_MFDE_METHOD_QUERY: + val_str = "query"; + break; + case MLXSW_REG_MFDE_METHOD_WRITE: + val_str = "write"; + break; + default: + val_str = NULL; + } + if (val_str) + devlink_fmsg_string_pair_put(fmsg, "method", val_str); + + val = mlxsw_reg_mfde_long_process_get(mfde_pl); + devlink_fmsg_bool_pair_put(fmsg, "long_process", val); + + val = mlxsw_reg_mfde_command_type_get(mfde_pl); + switch (val) { + case MLXSW_REG_MFDE_COMMAND_TYPE_MAD: + val_str = "mad"; + break; + case MLXSW_REG_MFDE_COMMAND_TYPE_EMAD: + val_str = "emad"; + break; + case MLXSW_REG_MFDE_COMMAND_TYPE_CMDIF: + val_str = "cmdif"; + break; + default: + val_str = NULL; + } + if (val_str) + devlink_fmsg_string_pair_put(fmsg, "command_type", val_str); + + val = mlxsw_reg_mfde_reg_attr_id_get(mfde_pl); + devlink_fmsg_u32_pair_put(fmsg, "reg_attr_id", val); + + switch (event_id) { + case MLXSW_REG_MFDE_EVENT_ID_CRSPACE_TO: + mlxsw_core_health_fw_fatal_dump_crspace_to(mfde_pl, fmsg); + break; + case MLXSW_REG_MFDE_EVENT_ID_KVD_IM_STOP: + mlxsw_core_health_fw_fatal_dump_kvd_im_stop(mfde_pl, fmsg); + break; + case MLXSW_REG_MFDE_EVENT_ID_FW_ASSERT: + mlxsw_core_health_fw_fatal_dump_fw_assert(mfde_pl, fmsg); + break; + case MLXSW_REG_MFDE_EVENT_ID_FATAL_CAUSE: + mlxsw_core_health_fw_fatal_dump_fatal_cause(mfde_pl, fmsg); + break; + } + + return 0; +} + +static int +mlxsw_core_health_fw_fatal_test(struct devlink_health_reporter *reporter, + struct netlink_ext_ack *extack) +{ + struct mlxsw_core *mlxsw_core = devlink_health_reporter_priv(reporter); + char mfgd_pl[MLXSW_REG_MFGD_LEN]; + int err; + + /* Read the register first to make sure no other bits are changed. */ + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mfgd), mfgd_pl); + if (err) + return err; + mlxsw_reg_mfgd_trigger_test_set(mfgd_pl, true); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mfgd), mfgd_pl); +} + +static const struct devlink_health_reporter_ops +mlxsw_core_health_fw_fatal_ops = { + .name = "fw_fatal", + .dump = mlxsw_core_health_fw_fatal_dump, + .test = mlxsw_core_health_fw_fatal_test, }; +static int mlxsw_core_health_fw_fatal_config(struct mlxsw_core *mlxsw_core, + bool enable) +{ + char mfgd_pl[MLXSW_REG_MFGD_LEN]; + int err; + + /* Read the register first to make sure no other bits are changed. */ + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mfgd), mfgd_pl); + if (err) + return err; + mlxsw_reg_mfgd_fatal_event_mode_set(mfgd_pl, enable); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mfgd), mfgd_pl); +} + +static int mlxsw_core_health_init(struct mlxsw_core *mlxsw_core) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_core); + struct devlink_health_reporter *fw_fatal; + int err; + + if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) + return 0; + + fw_fatal = devl_health_reporter_create(devlink, &mlxsw_core_health_fw_fatal_ops, + mlxsw_core); + if (IS_ERR(fw_fatal)) { + dev_err(mlxsw_core->bus_info->dev, "Failed to create fw fatal reporter"); + return PTR_ERR(fw_fatal); + } + mlxsw_core->health.fw_fatal = fw_fatal; + + err = mlxsw_core_trap_register(mlxsw_core, &mlxsw_core_health_listener, mlxsw_core); + if (err) + goto err_trap_register; + + err = mlxsw_core_health_fw_fatal_config(mlxsw_core, true); + if (err) + goto err_fw_fatal_config; + + return 0; + +err_fw_fatal_config: + mlxsw_core_trap_unregister(mlxsw_core, &mlxsw_core_health_listener, mlxsw_core); +err_trap_register: + devl_health_reporter_destroy(mlxsw_core->health.fw_fatal); + return err; +} + +static void mlxsw_core_health_fini(struct mlxsw_core *mlxsw_core) +{ + if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) + return; + + mlxsw_core_health_fw_fatal_config(mlxsw_core, false); + mlxsw_core_trap_unregister(mlxsw_core, &mlxsw_core_health_listener, mlxsw_core); + /* Make sure there is no more event work scheduled */ + mlxsw_core_flush_owq(); + devl_health_reporter_destroy(mlxsw_core->health.fw_fatal); +} + +static void mlxsw_core_irq_event_handler_init(struct mlxsw_core *mlxsw_core) +{ + INIT_LIST_HEAD(&mlxsw_core->irq_event_handler_list); + mutex_init(&mlxsw_core->irq_event_handler_lock); +} + +static void mlxsw_core_irq_event_handler_fini(struct mlxsw_core *mlxsw_core) +{ + mutex_destroy(&mlxsw_core->irq_event_handler_lock); + WARN_ON(!list_empty(&mlxsw_core->irq_event_handler_list)); +} + static int __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, const struct mlxsw_bus *mlxsw_bus, void *bus_priv, bool reload, - struct devlink *devlink) + struct devlink *devlink, + struct netlink_ext_ack *extack) { const char *device_kind = mlxsw_bus_info->device_kind; struct mlxsw_core *mlxsw_core; struct mlxsw_driver *mlxsw_driver; - struct mlxsw_res *res; size_t alloc_size; + u16 max_lag; int err; mlxsw_driver = mlxsw_core_driver_get(device_kind); @@ -989,11 +2111,14 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, if (!reload) { alloc_size = sizeof(*mlxsw_core) + mlxsw_driver->priv_size; - devlink = devlink_alloc(&mlxsw_devlink_ops, alloc_size); + devlink = devlink_alloc(&mlxsw_devlink_ops, alloc_size, + mlxsw_bus_info->dev); if (!devlink) { err = -ENOMEM; goto err_devlink_alloc; } + devl_lock(devlink); + devl_register(devlink); } mlxsw_core = devlink_priv(devlink); @@ -1003,9 +2128,10 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, mlxsw_core->bus = mlxsw_bus; mlxsw_core->bus_priv = bus_priv; mlxsw_core->bus_info = mlxsw_bus_info; + mlxsw_core_irq_event_handler_init(mlxsw_core); - res = mlxsw_driver->res_query_enabled ? &mlxsw_core->res : NULL; - err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile, res); + err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile, + &mlxsw_core->res); if (err) goto err_bus_init; @@ -1015,14 +2141,13 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, goto err_register_resources; } - err = mlxsw_ports_init(mlxsw_core); + err = mlxsw_ports_init(mlxsw_core, reload); if (err) goto err_ports_init; - if (MLXSW_CORE_RES_VALID(mlxsw_core, MAX_LAG) && - MLXSW_CORE_RES_VALID(mlxsw_core, MAX_LAG_MEMBERS)) { - alloc_size = sizeof(u8) * - MLXSW_CORE_RES_GET(mlxsw_core, MAX_LAG) * + err = mlxsw_core_max_lag(mlxsw_core, &max_lag); + if (!err && MLXSW_CORE_RES_VALID(mlxsw_core, MAX_LAG_MEMBERS)) { + alloc_size = sizeof(*mlxsw_core->lag.mapping) * max_lag * MLXSW_CORE_RES_GET(mlxsw_core, MAX_LAG_MEMBERS); mlxsw_core->lag.mapping = kzalloc(alloc_size, GFP_KERNEL); if (!mlxsw_core->lag.mapping) { @@ -1031,22 +2156,33 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, } } + err = mlxsw_core_trap_groups_set(mlxsw_core); + if (err) + goto err_trap_groups_set; + err = mlxsw_emad_init(mlxsw_core); if (err) goto err_emad_init; if (!reload) { - err = devlink_register(devlink, mlxsw_bus_info->dev); - if (err) - goto err_devlink_register; - } - - if (mlxsw_driver->params_register && !reload) { - err = mlxsw_driver->params_register(mlxsw_core); + err = mlxsw_core_params_register(mlxsw_core); if (err) goto err_register_params; } + err = mlxsw_core_fw_rev_validate(mlxsw_core, mlxsw_bus_info, mlxsw_driver->fw_req_rev, + mlxsw_driver->fw_filename); + if (err) + goto err_fw_rev_validate; + + err = mlxsw_linecards_init(mlxsw_core, mlxsw_bus_info); + if (err) + goto err_linecards_init; + + err = mlxsw_core_health_init(mlxsw_core); + if (err) + goto err_health_init; + err = mlxsw_hwmon_init(mlxsw_core, mlxsw_bus_info, &mlxsw_core->hwmon); if (err) goto err_hwmon_init; @@ -1056,38 +2192,53 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, if (err) goto err_thermal_init; + err = mlxsw_env_init(mlxsw_core, mlxsw_bus_info, &mlxsw_core->env); + if (err) + goto err_env_init; + if (mlxsw_driver->init) { - err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info); + err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info, extack); if (err) goto err_driver_init; } + if (!reload) + devl_unlock(devlink); return 0; err_driver_init: + mlxsw_env_fini(mlxsw_core->env); +err_env_init: mlxsw_thermal_fini(mlxsw_core->thermal); err_thermal_init: mlxsw_hwmon_fini(mlxsw_core->hwmon); err_hwmon_init: - if (mlxsw_driver->params_unregister && !reload) - mlxsw_driver->params_unregister(mlxsw_core); -err_register_params: + mlxsw_core_health_fini(mlxsw_core); +err_health_init: + mlxsw_linecards_fini(mlxsw_core); +err_linecards_init: +err_fw_rev_validate: if (!reload) - devlink_unregister(devlink); -err_devlink_register: + mlxsw_core_params_unregister(mlxsw_core); +err_register_params: mlxsw_emad_fini(mlxsw_core); err_emad_init: +err_trap_groups_set: kfree(mlxsw_core->lag.mapping); err_alloc_lag_mapping: - mlxsw_ports_fini(mlxsw_core); + mlxsw_ports_fini(mlxsw_core, reload); err_ports_init: if (!reload) - devlink_resources_unregister(devlink, NULL); + devl_resources_unregister(devlink); err_register_resources: mlxsw_bus->fini(bus_priv); err_bus_init: - if (!reload) + mlxsw_core_irq_event_handler_fini(mlxsw_core); + if (!reload) { + devl_unregister(devlink); + devl_unlock(devlink); devlink_free(devlink); + } err_devlink_alloc: return err; } @@ -1095,14 +2246,16 @@ err_devlink_alloc: int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, const struct mlxsw_bus *mlxsw_bus, void *bus_priv, bool reload, - struct devlink *devlink) + struct devlink *devlink, + struct netlink_ext_ack *extack) { bool called_again = false; int err; again: err = __mlxsw_core_bus_device_register(mlxsw_bus_info, mlxsw_bus, - bus_priv, reload, devlink); + bus_priv, reload, + devlink, extack); /* -EAGAIN is returned in case the FW was updated. FW needs * a reset, so lets try to call __mlxsw_core_bus_device_register() * again. @@ -1121,7 +2274,10 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, { struct devlink *devlink = priv_to_devlink(mlxsw_core); - if (mlxsw_core->reload_fail) { + if (!reload) + devl_lock(devlink); + + if (devlink_is_reload_failed(devlink)) { if (!reload) /* Only the parts that were not de-initialized in the * failed reload attempt need to be de-initialized. @@ -1133,26 +2289,33 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, if (mlxsw_core->driver->fini) mlxsw_core->driver->fini(mlxsw_core); + mlxsw_env_fini(mlxsw_core->env); mlxsw_thermal_fini(mlxsw_core->thermal); mlxsw_hwmon_fini(mlxsw_core->hwmon); - if (mlxsw_core->driver->params_unregister && !reload) - mlxsw_core->driver->params_unregister(mlxsw_core); + mlxsw_core_health_fini(mlxsw_core); + mlxsw_linecards_fini(mlxsw_core); if (!reload) - devlink_unregister(devlink); + mlxsw_core_params_unregister(mlxsw_core); mlxsw_emad_fini(mlxsw_core); kfree(mlxsw_core->lag.mapping); - mlxsw_ports_fini(mlxsw_core); + mlxsw_ports_fini(mlxsw_core, reload); if (!reload) - devlink_resources_unregister(devlink, NULL); + devl_resources_unregister(devlink); mlxsw_core->bus->fini(mlxsw_core->bus_priv); + mlxsw_core_irq_event_handler_fini(mlxsw_core); + if (!reload) { + devl_unregister(devlink); + devl_unlock(devlink); + devlink_free(devlink); + } return; reload_fail_deinit: - if (mlxsw_core->driver->params_unregister) - mlxsw_core->driver->params_unregister(mlxsw_core); - devlink_unregister(devlink); - devlink_resources_unregister(devlink, NULL); + mlxsw_core_params_unregister(mlxsw_core); + devl_resources_unregister(devlink); + devl_unregister(devlink); + devl_unlock(devlink); devlink_free(devlink); } EXPORT_SYMBOL(mlxsw_core_bus_device_unregister); @@ -1166,31 +2329,39 @@ bool mlxsw_core_skb_transmit_busy(struct mlxsw_core *mlxsw_core, EXPORT_SYMBOL(mlxsw_core_skb_transmit_busy); int mlxsw_core_skb_transmit(struct mlxsw_core *mlxsw_core, struct sk_buff *skb, - const struct mlxsw_tx_info *tx_info) + const struct mlxsw_txhdr_info *txhdr_info) { return mlxsw_core->bus->skb_transmit(mlxsw_core->bus_priv, skb, - tx_info); + txhdr_info); } EXPORT_SYMBOL(mlxsw_core_skb_transmit); +void mlxsw_core_ptp_transmitted(struct mlxsw_core *mlxsw_core, + struct sk_buff *skb, u16 local_port) +{ + if (mlxsw_core->driver->ptp_transmitted) + mlxsw_core->driver->ptp_transmitted(mlxsw_core, skb, + local_port); +} +EXPORT_SYMBOL(mlxsw_core_ptp_transmitted); + static bool __is_rx_listener_equal(const struct mlxsw_rx_listener *rxl_a, const struct mlxsw_rx_listener *rxl_b) { return (rxl_a->func == rxl_b->func && rxl_a->local_port == rxl_b->local_port && - rxl_a->trap_id == rxl_b->trap_id); + rxl_a->trap_id == rxl_b->trap_id && + rxl_a->mirror_reason == rxl_b->mirror_reason); } static struct mlxsw_rx_listener_item * __find_rx_listener_item(struct mlxsw_core *mlxsw_core, - const struct mlxsw_rx_listener *rxl, - void *priv) + const struct mlxsw_rx_listener *rxl) { struct mlxsw_rx_listener_item *rxl_item; list_for_each_entry(rxl_item, &mlxsw_core->rx_listener_list, list) { - if (__is_rx_listener_equal(&rxl_item->rxl, rxl) && - rxl_item->priv == priv) + if (__is_rx_listener_equal(&rxl_item->rxl, rxl)) return rxl_item; } return NULL; @@ -1198,11 +2369,11 @@ __find_rx_listener_item(struct mlxsw_core *mlxsw_core, int mlxsw_core_rx_listener_register(struct mlxsw_core *mlxsw_core, const struct mlxsw_rx_listener *rxl, - void *priv) + void *priv, bool enabled) { struct mlxsw_rx_listener_item *rxl_item; - rxl_item = __find_rx_listener_item(mlxsw_core, rxl, priv); + rxl_item = __find_rx_listener_item(mlxsw_core, rxl); if (rxl_item) return -EEXIST; rxl_item = kmalloc(sizeof(*rxl_item), GFP_KERNEL); @@ -1210,6 +2381,7 @@ int mlxsw_core_rx_listener_register(struct mlxsw_core *mlxsw_core, return -ENOMEM; rxl_item->rxl = *rxl; rxl_item->priv = priv; + rxl_item->enabled = enabled; list_add_rcu(&rxl_item->list, &mlxsw_core->rx_listener_list); return 0; @@ -1217,12 +2389,11 @@ int mlxsw_core_rx_listener_register(struct mlxsw_core *mlxsw_core, EXPORT_SYMBOL(mlxsw_core_rx_listener_register); void mlxsw_core_rx_listener_unregister(struct mlxsw_core *mlxsw_core, - const struct mlxsw_rx_listener *rxl, - void *priv) + const struct mlxsw_rx_listener *rxl) { struct mlxsw_rx_listener_item *rxl_item; - rxl_item = __find_rx_listener_item(mlxsw_core, rxl, priv); + rxl_item = __find_rx_listener_item(mlxsw_core, rxl); if (!rxl_item) return; list_del_rcu(&rxl_item->list); @@ -1231,18 +2402,40 @@ void mlxsw_core_rx_listener_unregister(struct mlxsw_core *mlxsw_core, } EXPORT_SYMBOL(mlxsw_core_rx_listener_unregister); -static void mlxsw_core_event_listener_func(struct sk_buff *skb, u8 local_port, +static void +mlxsw_core_rx_listener_state_set(struct mlxsw_core *mlxsw_core, + const struct mlxsw_rx_listener *rxl, + bool enabled) +{ + struct mlxsw_rx_listener_item *rxl_item; + + rxl_item = __find_rx_listener_item(mlxsw_core, rxl); + if (WARN_ON(!rxl_item)) + return; + rxl_item->enabled = enabled; +} + +static void mlxsw_core_event_listener_func(struct sk_buff *skb, u16 local_port, void *priv) { struct mlxsw_event_listener_item *event_listener_item = priv; + struct mlxsw_core *mlxsw_core; 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_core = event_listener_item->mlxsw_core; + trace_devlink_hwmsg(priv_to_devlink(mlxsw_core), true, 0, + skb->data, skb->len); + + 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); } @@ -1256,14 +2449,12 @@ static bool __is_event_listener_equal(const struct mlxsw_event_listener *el_a, static struct mlxsw_event_listener_item * __find_event_listener_item(struct mlxsw_core *mlxsw_core, - const struct mlxsw_event_listener *el, - void *priv) + const struct mlxsw_event_listener *el) { struct mlxsw_event_listener_item *el_item; list_for_each_entry(el_item, &mlxsw_core->event_listener_list, list) { - if (__is_event_listener_equal(&el_item->el, el) && - el_item->priv == priv) + if (__is_event_listener_equal(&el_item->el, el)) return el_item; } return NULL; @@ -1281,16 +2472,17 @@ int mlxsw_core_event_listener_register(struct mlxsw_core *mlxsw_core, .trap_id = el->trap_id, }; - el_item = __find_event_listener_item(mlxsw_core, el, priv); + el_item = __find_event_listener_item(mlxsw_core, el); if (el_item) return -EEXIST; el_item = kmalloc(sizeof(*el_item), GFP_KERNEL); if (!el_item) return -ENOMEM; + el_item->mlxsw_core = mlxsw_core; el_item->el = *el; el_item->priv = priv; - err = mlxsw_core_rx_listener_register(mlxsw_core, &rxl, el_item); + err = mlxsw_core_rx_listener_register(mlxsw_core, &rxl, el_item, true); if (err) goto err_rx_listener_register; @@ -1308,8 +2500,7 @@ err_rx_listener_register: EXPORT_SYMBOL(mlxsw_core_event_listener_register); void mlxsw_core_event_listener_unregister(struct mlxsw_core *mlxsw_core, - const struct mlxsw_event_listener *el, - void *priv) + const struct mlxsw_event_listener *el) { struct mlxsw_event_listener_item *el_item; const struct mlxsw_rx_listener rxl = { @@ -1318,10 +2509,10 @@ void mlxsw_core_event_listener_unregister(struct mlxsw_core *mlxsw_core, .trap_id = el->trap_id, }; - el_item = __find_event_listener_item(mlxsw_core, el, priv); + el_item = __find_event_listener_item(mlxsw_core, el); if (!el_item) return; - mlxsw_core_rx_listener_unregister(mlxsw_core, &rxl, el_item); + mlxsw_core_rx_listener_unregister(mlxsw_core, &rxl); list_del(&el_item->list); kfree(el_item); } @@ -1329,16 +2520,18 @@ EXPORT_SYMBOL(mlxsw_core_event_listener_unregister); static int mlxsw_core_listener_register(struct mlxsw_core *mlxsw_core, const struct mlxsw_listener *listener, - void *priv) + void *priv, bool enabled) { - if (listener->is_event) + if (listener->is_event) { + WARN_ON(!enabled); return mlxsw_core_event_listener_register(mlxsw_core, - &listener->u.event_listener, + &listener->event_listener, priv); - else + } else { return mlxsw_core_rx_listener_register(mlxsw_core, - &listener->u.rx_listener, - priv); + &listener->rx_listener, + priv, enabled); + } } static void mlxsw_core_listener_unregister(struct mlxsw_core *mlxsw_core, @@ -1347,26 +2540,34 @@ static void mlxsw_core_listener_unregister(struct mlxsw_core *mlxsw_core, { if (listener->is_event) mlxsw_core_event_listener_unregister(mlxsw_core, - &listener->u.event_listener, - priv); + &listener->event_listener); else mlxsw_core_rx_listener_unregister(mlxsw_core, - &listener->u.rx_listener, - priv); + &listener->rx_listener); } int mlxsw_core_trap_register(struct mlxsw_core *mlxsw_core, const struct mlxsw_listener *listener, void *priv) { + enum mlxsw_reg_htgt_trap_group trap_group; + enum mlxsw_reg_hpkt_action action; char hpkt_pl[MLXSW_REG_HPKT_LEN]; int err; - err = mlxsw_core_listener_register(mlxsw_core, listener, priv); + if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) + return 0; + + err = mlxsw_core_listener_register(mlxsw_core, listener, priv, + listener->enabled_on_register); if (err) return err; - mlxsw_reg_hpkt_pack(hpkt_pl, listener->action, listener->trap_id, - listener->trap_group, listener->is_ctrl); + action = listener->enabled_on_register ? listener->en_action : + listener->dis_action; + trap_group = listener->enabled_on_register ? listener->en_trap_group : + listener->dis_trap_group; + mlxsw_reg_hpkt_pack(hpkt_pl, action, listener->trap_id, + trap_group, listener->is_ctrl); err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); if (err) goto err_trap_set; @@ -1385,9 +2586,12 @@ void mlxsw_core_trap_unregister(struct mlxsw_core *mlxsw_core, { char hpkt_pl[MLXSW_REG_HPKT_LEN]; + if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX)) + return; + if (!listener->is_event) { - mlxsw_reg_hpkt_pack(hpkt_pl, listener->unreg_action, - listener->trap_id, listener->trap_group, + mlxsw_reg_hpkt_pack(hpkt_pl, listener->dis_action, + listener->trap_id, listener->dis_trap_group, listener->is_ctrl); mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); } @@ -1396,6 +2600,73 @@ void mlxsw_core_trap_unregister(struct mlxsw_core *mlxsw_core, } EXPORT_SYMBOL(mlxsw_core_trap_unregister); +int mlxsw_core_traps_register(struct mlxsw_core *mlxsw_core, + const struct mlxsw_listener *listeners, + size_t listeners_count, void *priv) +{ + int i, err; + + for (i = 0; i < listeners_count; i++) { + err = mlxsw_core_trap_register(mlxsw_core, + &listeners[i], + priv); + if (err) + goto err_listener_register; + } + return 0; + +err_listener_register: + for (i--; i >= 0; i--) { + mlxsw_core_trap_unregister(mlxsw_core, + &listeners[i], + priv); + } + return err; +} +EXPORT_SYMBOL(mlxsw_core_traps_register); + +void mlxsw_core_traps_unregister(struct mlxsw_core *mlxsw_core, + const struct mlxsw_listener *listeners, + size_t listeners_count, void *priv) +{ + int i; + + for (i = 0; i < listeners_count; i++) { + mlxsw_core_trap_unregister(mlxsw_core, + &listeners[i], + priv); + } +} +EXPORT_SYMBOL(mlxsw_core_traps_unregister); + +int mlxsw_core_trap_state_set(struct mlxsw_core *mlxsw_core, + const struct mlxsw_listener *listener, + bool enabled) +{ + enum mlxsw_reg_htgt_trap_group trap_group; + enum mlxsw_reg_hpkt_action action; + char hpkt_pl[MLXSW_REG_HPKT_LEN]; + int err; + + /* Not supported for event listener */ + if (WARN_ON(listener->is_event)) + return -EINVAL; + + action = enabled ? listener->en_action : listener->dis_action; + trap_group = enabled ? listener->en_trap_group : + listener->dis_trap_group; + mlxsw_reg_hpkt_pack(hpkt_pl, action, listener->trap_id, + trap_group, listener->is_ctrl); + err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); + if (err) + return err; + + mlxsw_core_rx_listener_state_set(mlxsw_core, &listener->rx_listener, + enabled); + return 0; +} +EXPORT_SYMBOL(mlxsw_core_trap_state_set); + static u64 mlxsw_core_tid_get(struct mlxsw_core *mlxsw_core) { return atomic64_inc_return(&mlxsw_core->emad.tid); @@ -1420,7 +2691,7 @@ static int mlxsw_core_reg_access_emad(struct mlxsw_core *mlxsw_core, err = mlxsw_emad_reg_access(mlxsw_core, reg, payload, type, trans, bulk_list, cb, cb_priv, tid); if (err) { - kfree(trans); + kfree_rcu(trans, rcu); return err; } return 0; @@ -1448,8 +2719,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; @@ -1460,7 +2734,7 @@ static int mlxsw_reg_trans_wait(struct mlxsw_reg_trans *trans) if (trans->retries) dev_warn(mlxsw_core->bus_info->dev, "EMAD retries (%d/%d) (tid=%llx)\n", trans->retries, MLXSW_EMAD_MAX_RETRY, trans->tid); - if (err) + if (err) { dev_err(mlxsw_core->bus_info->dev, "EMAD reg access failed (tid=%llx,reg_id=%x(%s),type=%s,status=%x(%s))\n", trans->tid, trans->reg->id, mlxsw_reg_id_str(trans->reg->id), @@ -1468,6 +2742,18 @@ static int mlxsw_reg_trans_wait(struct mlxsw_reg_trans *trans) 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, err_string); + + kfree(trans->emad_err_string); + } + list_del(&trans->bulk_list); kfree_rcu(trans, rcu); return err; @@ -1489,6 +2775,57 @@ int mlxsw_reg_trans_bulk_wait(struct list_head *bulk_list) } EXPORT_SYMBOL(mlxsw_reg_trans_bulk_wait); +struct mlxsw_core_irq_event_handler_item { + struct list_head list; + void (*cb)(struct mlxsw_core *mlxsw_core); +}; + +int mlxsw_core_irq_event_handler_register(struct mlxsw_core *mlxsw_core, + mlxsw_irq_event_cb_t cb) +{ + struct mlxsw_core_irq_event_handler_item *item; + + item = kzalloc(sizeof(*item), GFP_KERNEL); + if (!item) + return -ENOMEM; + item->cb = cb; + mutex_lock(&mlxsw_core->irq_event_handler_lock); + list_add_tail(&item->list, &mlxsw_core->irq_event_handler_list); + mutex_unlock(&mlxsw_core->irq_event_handler_lock); + return 0; +} +EXPORT_SYMBOL(mlxsw_core_irq_event_handler_register); + +void mlxsw_core_irq_event_handler_unregister(struct mlxsw_core *mlxsw_core, + mlxsw_irq_event_cb_t cb) +{ + struct mlxsw_core_irq_event_handler_item *item, *tmp; + + mutex_lock(&mlxsw_core->irq_event_handler_lock); + list_for_each_entry_safe(item, tmp, + &mlxsw_core->irq_event_handler_list, list) { + if (item->cb == cb) { + list_del(&item->list); + kfree(item); + } + } + mutex_unlock(&mlxsw_core->irq_event_handler_lock); +} +EXPORT_SYMBOL(mlxsw_core_irq_event_handler_unregister); + +void mlxsw_core_irq_event_handlers_call(struct mlxsw_core *mlxsw_core) +{ + struct mlxsw_core_irq_event_handler_item *item; + + mutex_lock(&mlxsw_core->irq_event_handler_lock); + list_for_each_entry(item, &mlxsw_core->irq_event_handler_list, list) { + if (item->cb) + item->cb(mlxsw_core); + } + mutex_unlock(&mlxsw_core->irq_event_handler_lock); +} +EXPORT_SYMBOL(mlxsw_core_irq_event_handlers_call); + static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core, const struct mlxsw_reg_info *reg, char *payload, @@ -1539,7 +2876,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); @@ -1607,7 +2944,7 @@ void mlxsw_core_skb_receive(struct mlxsw_core *mlxsw_core, struct sk_buff *skb, { struct mlxsw_rx_listener_item *rxl_item; const struct mlxsw_rx_listener *rxl; - u8 local_port; + u16 local_port; bool found = false; if (rx_info->is_lag) { @@ -1636,16 +2973,20 @@ void mlxsw_core_skb_receive(struct mlxsw_core *mlxsw_core, struct sk_buff *skb, rxl = &rxl_item->rxl; if ((rxl->local_port == MLXSW_PORT_DONT_CARE || rxl->local_port == local_port) && - rxl->trap_id == rx_info->trap_id) { - found = true; + rxl->trap_id == rx_info->trap_id && + rxl->mirror_reason == rx_info->mirror_reason) { + if (rxl_item->enabled) + found = true; break; } } - rcu_read_unlock(); - if (!found) + if (!found) { + rcu_read_unlock(); goto drop; + } rxl->func(skb, local_port, rxl_item->priv); + rcu_read_unlock(); return; drop: @@ -1661,7 +3002,7 @@ static int mlxsw_core_lag_mapping_index(struct mlxsw_core *mlxsw_core, } void mlxsw_core_lag_mapping_set(struct mlxsw_core *mlxsw_core, - u16 lag_id, u8 port_index, u8 local_port) + u16 lag_id, u8 port_index, u16 local_port) { int index = mlxsw_core_lag_mapping_index(mlxsw_core, lag_id, port_index); @@ -1670,8 +3011,8 @@ void mlxsw_core_lag_mapping_set(struct mlxsw_core *mlxsw_core, } EXPORT_SYMBOL(mlxsw_core_lag_mapping_set); -u8 mlxsw_core_lag_mapping_get(struct mlxsw_core *mlxsw_core, - u16 lag_id, u8 port_index) +u16 mlxsw_core_lag_mapping_get(struct mlxsw_core *mlxsw_core, + u16 lag_id, u8 port_index) { int index = mlxsw_core_lag_mapping_index(mlxsw_core, lag_id, port_index); @@ -1681,7 +3022,7 @@ u8 mlxsw_core_lag_mapping_get(struct mlxsw_core *mlxsw_core, EXPORT_SYMBOL(mlxsw_core_lag_mapping_get); void mlxsw_core_lag_mapping_clear(struct mlxsw_core *mlxsw_core, - u16 lag_id, u8 local_port) + u16 lag_id, u16 local_port) { int i; @@ -1709,94 +3050,166 @@ u64 mlxsw_core_res_get(struct mlxsw_core *mlxsw_core, } EXPORT_SYMBOL(mlxsw_core_res_get); -int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port) +static const struct devlink_port_ops mlxsw_devlink_port_ops = { + .port_split = mlxsw_devlink_port_split, + .port_unsplit = mlxsw_devlink_port_unsplit, +}; + +static int __mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u16 local_port, + enum devlink_port_flavour flavour, + u8 slot_index, u32 port_number, bool split, + u32 split_port_subnumber, + bool splittable, u32 lanes, + const unsigned char *switch_id, + unsigned char switch_id_len) { struct devlink *devlink = priv_to_devlink(mlxsw_core); struct mlxsw_core_port *mlxsw_core_port = &mlxsw_core->ports[local_port]; struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; + struct devlink_port_attrs attrs = {}; int err; + attrs.split = split; + attrs.lanes = lanes; + attrs.splittable = splittable; + attrs.flavour = flavour; + attrs.phys.port_number = port_number; + attrs.phys.split_subport_number = split_port_subnumber; + memcpy(attrs.switch_id.id, switch_id, switch_id_len); + attrs.switch_id.id_len = switch_id_len; mlxsw_core_port->local_port = local_port; - err = devlink_port_register(devlink, devlink_port, local_port); + devlink_port_attrs_set(devlink_port, &attrs); + if (slot_index) { + struct mlxsw_linecard *linecard; + + linecard = mlxsw_linecard_get(mlxsw_core->linecards, + slot_index); + mlxsw_core_port->linecard = linecard; + devlink_port_linecard_set(devlink_port, + linecard->devlink_linecard); + } + err = devl_port_register_with_ops(devlink, devlink_port, local_port, + &mlxsw_devlink_port_ops); if (err) memset(mlxsw_core_port, 0, sizeof(*mlxsw_core_port)); return err; } -EXPORT_SYMBOL(mlxsw_core_port_init); -void mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port) +static void __mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u16 local_port) { struct mlxsw_core_port *mlxsw_core_port = &mlxsw_core->ports[local_port]; struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; - devlink_port_unregister(devlink_port); + devl_port_unregister(devlink_port); memset(mlxsw_core_port, 0, sizeof(*mlxsw_core_port)); } + +int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u16 local_port, + u8 slot_index, u32 port_number, bool split, + u32 split_port_subnumber, + bool splittable, u32 lanes, + const unsigned char *switch_id, + unsigned char switch_id_len) +{ + int err; + + err = __mlxsw_core_port_init(mlxsw_core, local_port, + DEVLINK_PORT_FLAVOUR_PHYSICAL, slot_index, + port_number, split, split_port_subnumber, + splittable, lanes, + switch_id, switch_id_len); + if (err) + return err; + + atomic_inc(&mlxsw_core->active_ports_count); + return 0; +} +EXPORT_SYMBOL(mlxsw_core_port_init); + +void mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u16 local_port) +{ + atomic_dec(&mlxsw_core->active_ports_count); + + __mlxsw_core_port_fini(mlxsw_core, local_port); +} EXPORT_SYMBOL(mlxsw_core_port_fini); -void mlxsw_core_port_eth_set(struct mlxsw_core *mlxsw_core, u8 local_port, - void *port_driver_priv, struct net_device *dev, - u32 port_number, bool split, - u32 split_port_subnumber) +int mlxsw_core_cpu_port_init(struct mlxsw_core *mlxsw_core, + void *port_driver_priv, + const unsigned char *switch_id, + unsigned char switch_id_len) { struct mlxsw_core_port *mlxsw_core_port = - &mlxsw_core->ports[local_port]; - struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; + &mlxsw_core->ports[MLXSW_PORT_CPU_PORT]; + int err; + + err = __mlxsw_core_port_init(mlxsw_core, MLXSW_PORT_CPU_PORT, + DEVLINK_PORT_FLAVOUR_CPU, + 0, 0, false, 0, false, 0, + switch_id, switch_id_len); + if (err) + return err; mlxsw_core_port->port_driver_priv = port_driver_priv; - devlink_port_attrs_set(devlink_port, DEVLINK_PORT_FLAVOUR_PHYSICAL, - port_number, split, split_port_subnumber); - devlink_port_type_eth_set(devlink_port, dev); + return 0; } -EXPORT_SYMBOL(mlxsw_core_port_eth_set); +EXPORT_SYMBOL(mlxsw_core_cpu_port_init); -void mlxsw_core_port_ib_set(struct mlxsw_core *mlxsw_core, u8 local_port, - void *port_driver_priv) +void mlxsw_core_cpu_port_fini(struct mlxsw_core *mlxsw_core) +{ + __mlxsw_core_port_fini(mlxsw_core, MLXSW_PORT_CPU_PORT); +} +EXPORT_SYMBOL(mlxsw_core_cpu_port_fini); + +void mlxsw_core_port_netdev_link(struct mlxsw_core *mlxsw_core, u16 local_port, + void *port_driver_priv, struct net_device *dev) { struct mlxsw_core_port *mlxsw_core_port = &mlxsw_core->ports[local_port]; struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; mlxsw_core_port->port_driver_priv = port_driver_priv; - devlink_port_type_ib_set(devlink_port, NULL); + SET_NETDEV_DEVLINK_PORT(dev, devlink_port); } -EXPORT_SYMBOL(mlxsw_core_port_ib_set); +EXPORT_SYMBOL(mlxsw_core_port_netdev_link); -void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u8 local_port, - void *port_driver_priv) +struct devlink_port * +mlxsw_core_port_devlink_port_get(struct mlxsw_core *mlxsw_core, + u16 local_port) { struct mlxsw_core_port *mlxsw_core_port = &mlxsw_core->ports[local_port]; struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; - mlxsw_core_port->port_driver_priv = port_driver_priv; - devlink_port_type_clear(devlink_port); + return devlink_port; } -EXPORT_SYMBOL(mlxsw_core_port_clear); +EXPORT_SYMBOL(mlxsw_core_port_devlink_port_get); -enum devlink_port_type mlxsw_core_port_type_get(struct mlxsw_core *mlxsw_core, - u8 local_port) +struct mlxsw_linecard * +mlxsw_core_port_linecard_get(struct mlxsw_core *mlxsw_core, + u16 local_port) { struct mlxsw_core_port *mlxsw_core_port = &mlxsw_core->ports[local_port]; - struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; - return devlink_port->type; + return mlxsw_core_port->linecard; } -EXPORT_SYMBOL(mlxsw_core_port_type_get); -int mlxsw_core_port_get_phys_port_name(struct mlxsw_core *mlxsw_core, - u8 local_port, char *name, size_t len) +void mlxsw_core_ports_remove_selected(struct mlxsw_core *mlxsw_core, + bool (*selector)(void *priv, u16 local_port), + void *priv) { - struct mlxsw_core_port *mlxsw_core_port = - &mlxsw_core->ports[local_port]; - struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port; + if (WARN_ON_ONCE(!mlxsw_core->driver->ports_remove_selected)) + return; + mlxsw_core->driver->ports_remove_selected(mlxsw_core, selector, priv); +} - return devlink_port_get_phys_port_name(devlink_port, name, len); +struct mlxsw_env *mlxsw_core_env(const struct mlxsw_core *mlxsw_core) +{ + return mlxsw_core->env; } -EXPORT_SYMBOL(mlxsw_core_port_get_phys_port_name); static void mlxsw_core_buf_dump_dbg(struct mlxsw_core *mlxsw_core, const char *buf, size_t size) @@ -1896,26 +3309,84 @@ int mlxsw_core_kvd_sizes_get(struct mlxsw_core *mlxsw_core, } EXPORT_SYMBOL(mlxsw_core_kvd_sizes_get); -void mlxsw_core_fw_flash_start(struct mlxsw_core *mlxsw_core) +int mlxsw_core_resources_query(struct mlxsw_core *mlxsw_core, char *mbox, + struct mlxsw_res *res) { - mlxsw_core->fw_flash_in_progress = true; + int index, i; + u64 data; + u16 id; + int err; + + mlxsw_cmd_mbox_zero(mbox); + + for (index = 0; index < MLXSW_CMD_QUERY_RESOURCES_MAX_QUERIES; + index++) { + err = mlxsw_cmd_query_resources(mlxsw_core, mbox, index); + if (err) + return err; + + for (i = 0; i < MLXSW_CMD_QUERY_RESOURCES_PER_QUERY; i++) { + id = mlxsw_cmd_mbox_query_resource_id_get(mbox, i); + data = mlxsw_cmd_mbox_query_resource_data_get(mbox, i); + + if (id == MLXSW_CMD_QUERY_RESOURCES_TABLE_END_ID) + return 0; + + mlxsw_res_parse(res, id, data); + } + } + + /* If after MLXSW_RESOURCES_QUERY_MAX_QUERIES we still didn't get + * MLXSW_RESOURCES_TABLE_END_ID, something went bad in the FW. + */ + return -EIO; } -EXPORT_SYMBOL(mlxsw_core_fw_flash_start); +EXPORT_SYMBOL(mlxsw_core_resources_query); -void mlxsw_core_fw_flash_end(struct mlxsw_core *mlxsw_core) +u32 mlxsw_core_read_frc_h(struct mlxsw_core *mlxsw_core) { - mlxsw_core->fw_flash_in_progress = false; + return mlxsw_core->bus->read_frc_h(mlxsw_core->bus_priv); +} +EXPORT_SYMBOL(mlxsw_core_read_frc_h); + +u32 mlxsw_core_read_frc_l(struct mlxsw_core *mlxsw_core) +{ + return mlxsw_core->bus->read_frc_l(mlxsw_core->bus_priv); } -EXPORT_SYMBOL(mlxsw_core_fw_flash_end); +EXPORT_SYMBOL(mlxsw_core_read_frc_l); + +u32 mlxsw_core_read_utc_sec(struct mlxsw_core *mlxsw_core) +{ + return mlxsw_core->bus->read_utc_sec(mlxsw_core->bus_priv); +} +EXPORT_SYMBOL(mlxsw_core_read_utc_sec); + +u32 mlxsw_core_read_utc_nsec(struct mlxsw_core *mlxsw_core) +{ + return mlxsw_core->bus->read_utc_nsec(mlxsw_core->bus_priv); +} +EXPORT_SYMBOL(mlxsw_core_read_utc_nsec); + +bool mlxsw_core_sdq_supports_cqe_v2(struct mlxsw_core *mlxsw_core) +{ + return mlxsw_core->driver->sdq_supports_cqe_v2; +} +EXPORT_SYMBOL(mlxsw_core_sdq_supports_cqe_v2); static int __init mlxsw_core_module_init(void) { int err; - mlxsw_wq = alloc_workqueue(mlxsw_core_driver_name, WQ_MEM_RECLAIM, 0); - if (!mlxsw_wq) - return -ENOMEM; - mlxsw_owq = alloc_ordered_workqueue("%s_ordered", WQ_MEM_RECLAIM, + err = mlxsw_linecard_driver_register(); + if (err) + return err; + + mlxsw_wq = alloc_workqueue(mlxsw_core_driver_name, WQ_PERCPU, 0); + if (!mlxsw_wq) { + err = -ENOMEM; + goto err_alloc_workqueue; + } + mlxsw_owq = alloc_ordered_workqueue("%s_ordered", 0, mlxsw_core_driver_name); if (!mlxsw_owq) { err = -ENOMEM; @@ -1925,6 +3396,8 @@ static int __init mlxsw_core_module_init(void) err_alloc_ordered_workqueue: destroy_workqueue(mlxsw_wq); +err_alloc_workqueue: + mlxsw_linecard_driver_unregister(); return err; } @@ -1932,6 +3405,7 @@ static void __exit mlxsw_core_module_exit(void) { destroy_workqueue(mlxsw_owq); destroy_workqueue(mlxsw_wq); + mlxsw_linecard_driver_unregister(); } module_init(mlxsw_core_module_init); |
