#include <linux/seq_file.h>
 #include <linux/u64_stats_sync.h>
 #include <linux/netdevice.h>
+#include <linux/wait.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/string.h>
+#include <linux/gfp.h>
+#include <linux/random.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/rcupdate.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
 
 #include "core.h"
 #include "item.h"
 #include "cmd.h"
 #include "port.h"
 #include "trap.h"
+#include "emad.h"
+#include "reg.h"
 
 static LIST_HEAD(mlxsw_core_driver_list);
 static DEFINE_SPINLOCK(mlxsw_core_driver_list_lock);
        void *bus_priv;
        const struct mlxsw_bus_info *bus_info;
        struct list_head rx_listener_list;
+       struct list_head event_listener_list;
+       struct {
+               struct sk_buff *resp_skb;
+               u64 tid;
+               wait_queue_head_t wait;
+               bool trans_active;
+               struct mutex lock; /* One EMAD transaction at a time. */
+               bool use_emad;
+       } emad;
        struct mlxsw_core_pcpu_stats __percpu *pcpu_stats;
        struct dentry *dbg_dir;
        struct {
        void *priv;
 };
 
+struct mlxsw_event_listener_item {
+       struct list_head list;
+       struct mlxsw_event_listener el;
+       void *priv;
+};
+
+/******************
+ * EMAD processing
+ ******************/
+
+/* emad_eth_hdr_dmac
+ * Destination MAC in EMAD's Ethernet header.
+ * Must be set to 01:02:c9:00:00:01
+ */
+MLXSW_ITEM_BUF(emad, eth_hdr, dmac, 0x00, 6);
+
+/* emad_eth_hdr_smac
+ * Source MAC in EMAD's Ethernet header.
+ * Must be set to 00:02:c9:01:02:03
+ */
+MLXSW_ITEM_BUF(emad, eth_hdr, smac, 0x06, 6);
+
+/* emad_eth_hdr_ethertype
+ * Ethertype in EMAD's Ethernet header.
+ * Must be set to 0x8932
+ */
+MLXSW_ITEM32(emad, eth_hdr, ethertype, 0x0C, 16, 16);
+
+/* emad_eth_hdr_mlx_proto
+ * Mellanox protocol.
+ * Must be set to 0x0.
+ */
+MLXSW_ITEM32(emad, eth_hdr, mlx_proto, 0x0C, 8, 8);
+
+/* emad_eth_hdr_ver
+ * Mellanox protocol version.
+ * Must be set to 0x0.
+ */
+MLXSW_ITEM32(emad, eth_hdr, ver, 0x0C, 4, 4);
+
+/* emad_op_tlv_type
+ * Type of the TLV.
+ * Must be set to 0x1 (operation TLV).
+ */
+MLXSW_ITEM32(emad, op_tlv, type, 0x00, 27, 5);
+
+/* emad_op_tlv_len
+ * Length of the operation TLV in u32.
+ * Must be set to 0x4.
+ */
+MLXSW_ITEM32(emad, op_tlv, len, 0x00, 16, 11);
+
+/* emad_op_tlv_dr
+ * Direct route bit. Setting to 1 indicates the EMAD is a direct route
+ * EMAD. DR TLV must follow.
+ *
+ * Note: Currently not supported and must not be set.
+ */
+MLXSW_ITEM32(emad, op_tlv, dr, 0x00, 15, 1);
+
+/* emad_op_tlv_status
+ * Returned status in case of EMAD response. Must be set to 0 in case
+ * of EMAD request.
+ * 0x0 - success
+ * 0x1 - device is busy. Requester should retry
+ * 0x2 - Mellanox protocol version not supported
+ * 0x3 - unknown TLV
+ * 0x4 - register not supported
+ * 0x5 - operation class not supported
+ * 0x6 - EMAD method not supported
+ * 0x7 - bad parameter (e.g. port out of range)
+ * 0x8 - resource not available
+ * 0x9 - message receipt acknowledgment. Requester should retry
+ * 0x70 - internal error
+ */
+MLXSW_ITEM32(emad, op_tlv, status, 0x00, 8, 7);
+
+/* emad_op_tlv_register_id
+ * Register ID of register within register TLV.
+ */
+MLXSW_ITEM32(emad, op_tlv, register_id, 0x04, 16, 16);
+
+/* emad_op_tlv_r
+ * Response bit. Setting to 1 indicates Response, otherwise request.
+ */
+MLXSW_ITEM32(emad, op_tlv, r, 0x04, 15, 1);
+
+/* emad_op_tlv_method
+ * EMAD method type.
+ * 0x1 - query
+ * 0x2 - write
+ * 0x3 - send (currently not supported)
+ * 0x4 - event
+ */
+MLXSW_ITEM32(emad, op_tlv, method, 0x04, 8, 7);
+
+/* emad_op_tlv_class
+ * EMAD operation class. Must be set to 0x1 (REG_ACCESS).
+ */
+MLXSW_ITEM32(emad, op_tlv, class, 0x04, 0, 8);
+
+/* emad_op_tlv_tid
+ * EMAD transaction ID. Used for pairing request and response EMADs.
+ */
+MLXSW_ITEM64(emad, op_tlv, tid, 0x08, 0, 64);
+
+/* emad_reg_tlv_type
+ * Type of the TLV.
+ * Must be set to 0x3 (register TLV).
+ */
+MLXSW_ITEM32(emad, reg_tlv, type, 0x00, 27, 5);
+
+/* emad_reg_tlv_len
+ * Length of the operation TLV in u32.
+ */
+MLXSW_ITEM32(emad, reg_tlv, len, 0x00, 16, 11);
+
+/* emad_end_tlv_type
+ * Type of the TLV.
+ * Must be set to 0x0 (end TLV).
+ */
+MLXSW_ITEM32(emad, end_tlv, type, 0x00, 27, 5);
+
+/* emad_end_tlv_len
+ * Length of the end TLV in u32.
+ * Must be set to 1.
+ */
+MLXSW_ITEM32(emad, end_tlv, len, 0x00, 16, 11);
+
+enum mlxsw_core_reg_access_type {
+       MLXSW_CORE_REG_ACCESS_TYPE_QUERY,
+       MLXSW_CORE_REG_ACCESS_TYPE_WRITE,
+};
+
+static inline const char *
+mlxsw_core_reg_access_type_str(enum mlxsw_core_reg_access_type type)
+{
+       switch (type) {
+       case MLXSW_CORE_REG_ACCESS_TYPE_QUERY:
+               return "query";
+       case MLXSW_CORE_REG_ACCESS_TYPE_WRITE:
+               return "write";
+       }
+       BUG();
+}
+
+static void mlxsw_emad_pack_end_tlv(char *end_tlv)
+{
+       mlxsw_emad_end_tlv_type_set(end_tlv, MLXSW_EMAD_TLV_TYPE_END);
+       mlxsw_emad_end_tlv_len_set(end_tlv, MLXSW_EMAD_END_TLV_LEN);
+}
+
+static void mlxsw_emad_pack_reg_tlv(char *reg_tlv,
+                                   const struct mlxsw_reg_info *reg,
+                                   char *payload)
+{
+       mlxsw_emad_reg_tlv_type_set(reg_tlv, MLXSW_EMAD_TLV_TYPE_REG);
+       mlxsw_emad_reg_tlv_len_set(reg_tlv, reg->len / sizeof(u32) + 1);
+       memcpy(reg_tlv + sizeof(u32), payload, reg->len);
+}
+
+static void mlxsw_emad_pack_op_tlv(char *op_tlv,
+                                  const struct mlxsw_reg_info *reg,
+                                  enum mlxsw_core_reg_access_type type,
+                                  struct mlxsw_core *mlxsw_core)
+{
+       mlxsw_emad_op_tlv_type_set(op_tlv, MLXSW_EMAD_TLV_TYPE_OP);
+       mlxsw_emad_op_tlv_len_set(op_tlv, MLXSW_EMAD_OP_TLV_LEN);
+       mlxsw_emad_op_tlv_dr_set(op_tlv, 0);
+       mlxsw_emad_op_tlv_status_set(op_tlv, 0);
+       mlxsw_emad_op_tlv_register_id_set(op_tlv, reg->id);
+       mlxsw_emad_op_tlv_r_set(op_tlv, MLXSW_EMAD_OP_TLV_REQUEST);
+       if (MLXSW_CORE_REG_ACCESS_TYPE_QUERY == type)
+               mlxsw_emad_op_tlv_method_set(op_tlv,
+                                            MLXSW_EMAD_OP_TLV_METHOD_QUERY);
+       else
+               mlxsw_emad_op_tlv_method_set(op_tlv,
+                                            MLXSW_EMAD_OP_TLV_METHOD_WRITE);
+       mlxsw_emad_op_tlv_class_set(op_tlv,
+                                   MLXSW_EMAD_OP_TLV_CLASS_REG_ACCESS);
+       mlxsw_emad_op_tlv_tid_set(op_tlv, mlxsw_core->emad.tid);
+}
+
+static int mlxsw_emad_construct_eth_hdr(struct sk_buff *skb)
+{
+       char *eth_hdr = skb_push(skb, MLXSW_EMAD_ETH_HDR_LEN);
+
+       mlxsw_emad_eth_hdr_dmac_memcpy_to(eth_hdr, MLXSW_EMAD_EH_DMAC);
+       mlxsw_emad_eth_hdr_smac_memcpy_to(eth_hdr, MLXSW_EMAD_EH_SMAC);
+       mlxsw_emad_eth_hdr_ethertype_set(eth_hdr, MLXSW_EMAD_EH_ETHERTYPE);
+       mlxsw_emad_eth_hdr_mlx_proto_set(eth_hdr, MLXSW_EMAD_EH_MLX_PROTO);
+       mlxsw_emad_eth_hdr_ver_set(eth_hdr, MLXSW_EMAD_EH_PROTO_VERSION);
+
+       skb_reset_mac_header(skb);
+
+       return 0;
+}
+
+static void mlxsw_emad_construct(struct sk_buff *skb,
+                                const struct mlxsw_reg_info *reg,
+                                char *payload,
+                                enum mlxsw_core_reg_access_type type,
+                                struct mlxsw_core *mlxsw_core)
+{
+       char *buf;
+
+       buf = skb_push(skb, MLXSW_EMAD_END_TLV_LEN * sizeof(u32));
+       mlxsw_emad_pack_end_tlv(buf);
+
+       buf = skb_push(skb, reg->len + sizeof(u32));
+       mlxsw_emad_pack_reg_tlv(buf, reg, payload);
+
+       buf = skb_push(skb, MLXSW_EMAD_OP_TLV_LEN * sizeof(u32));
+       mlxsw_emad_pack_op_tlv(buf, reg, type, mlxsw_core);
+
+       mlxsw_emad_construct_eth_hdr(skb);
+}
+
+static char *mlxsw_emad_op_tlv(const struct sk_buff *skb)
+{
+       return ((char *) (skb->data + MLXSW_EMAD_ETH_HDR_LEN));
+}
+
+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)));
+}
+
+static char *mlxsw_emad_reg_payload(const char *op_tlv)
+{
+       return ((char *) (op_tlv + (MLXSW_EMAD_OP_TLV_LEN + 1) * sizeof(u32)));
+}
+
+static u64 mlxsw_emad_get_tid(const struct sk_buff *skb)
+{
+       char *op_tlv;
+
+       op_tlv = mlxsw_emad_op_tlv(skb);
+       return mlxsw_emad_op_tlv_tid_get(op_tlv);
+}
+
+static bool mlxsw_emad_is_resp(const struct sk_buff *skb)
+{
+       char *op_tlv;
+
+       op_tlv = mlxsw_emad_op_tlv(skb);
+       return (MLXSW_EMAD_OP_TLV_RESPONSE == mlxsw_emad_op_tlv_r_get(op_tlv));
+}
+
+#define MLXSW_EMAD_TIMEOUT_MS 200
+
+static int __mlxsw_emad_transmit(struct mlxsw_core *mlxsw_core,
+                                struct sk_buff *skb,
+                                const struct mlxsw_tx_info *tx_info)
+{
+       int err;
+       int ret;
+
+       err = mlxsw_core_skb_transmit(mlxsw_core->driver_priv, skb, tx_info);
+       if (err) {
+               dev_warn(mlxsw_core->bus_info->dev, "Failed to transmit EMAD (tid=%llx)\n",
+                        mlxsw_core->emad.tid);
+               dev_kfree_skb(skb);
+               return err;
+       }
+
+       mlxsw_core->emad.trans_active = true;
+       ret = wait_event_timeout(mlxsw_core->emad.wait,
+                                !(mlxsw_core->emad.trans_active),
+                                msecs_to_jiffies(MLXSW_EMAD_TIMEOUT_MS));
+       if (!ret) {
+               dev_warn(mlxsw_core->bus_info->dev, "EMAD timed-out (tid=%llx)\n",
+                        mlxsw_core->emad.tid);
+               mlxsw_core->emad.trans_active = false;
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int mlxsw_emad_process_status(struct mlxsw_core *mlxsw_core,
+                                    char *op_tlv)
+{
+       enum mlxsw_emad_op_tlv_status status;
+       u64 tid;
+
+       status = mlxsw_emad_op_tlv_status_get(op_tlv);
+       tid = mlxsw_emad_op_tlv_tid_get(op_tlv);
+
+       switch (status) {
+       case MLXSW_EMAD_OP_TLV_STATUS_SUCCESS:
+               return 0;
+       case MLXSW_EMAD_OP_TLV_STATUS_BUSY:
+       case MLXSW_EMAD_OP_TLV_STATUS_MESSAGE_RECEIPT_ACK:
+               dev_warn(mlxsw_core->bus_info->dev, "Reg access status again (tid=%llx,status=%x(%s))\n",
+                        tid, status, mlxsw_emad_op_tlv_status_str(status));
+               return -EAGAIN;
+       case MLXSW_EMAD_OP_TLV_STATUS_VERSION_NOT_SUPPORTED:
+       case MLXSW_EMAD_OP_TLV_STATUS_UNKNOWN_TLV:
+       case MLXSW_EMAD_OP_TLV_STATUS_REGISTER_NOT_SUPPORTED:
+       case MLXSW_EMAD_OP_TLV_STATUS_CLASS_NOT_SUPPORTED:
+       case MLXSW_EMAD_OP_TLV_STATUS_METHOD_NOT_SUPPORTED:
+       case MLXSW_EMAD_OP_TLV_STATUS_BAD_PARAMETER:
+       case MLXSW_EMAD_OP_TLV_STATUS_RESOURCE_NOT_AVAILABLE:
+       case MLXSW_EMAD_OP_TLV_STATUS_INTERNAL_ERROR:
+       default:
+               dev_err(mlxsw_core->bus_info->dev, "Reg access status failed (tid=%llx,status=%x(%s))\n",
+                       tid, status, mlxsw_emad_op_tlv_status_str(status));
+               return -EIO;
+       }
+}
+
+static int mlxsw_emad_process_status_skb(struct mlxsw_core *mlxsw_core,
+                                        struct sk_buff *skb)
+{
+       return mlxsw_emad_process_status(mlxsw_core, mlxsw_emad_op_tlv(skb));
+}
+
+static int mlxsw_emad_transmit(struct mlxsw_core *mlxsw_core,
+                              struct sk_buff *skb,
+                              const struct mlxsw_tx_info *tx_info)
+{
+       struct sk_buff *trans_skb;
+       int n_retry;
+       int err;
+
+       n_retry = 0;
+retry:
+       /* We copy the EMAD to a new skb, since we might need
+        * to retransmit it in case of failure.
+        */
+       trans_skb = skb_copy(skb, GFP_KERNEL);
+       if (!trans_skb) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       err = __mlxsw_emad_transmit(mlxsw_core, trans_skb, tx_info);
+       if (!err) {
+               struct sk_buff *resp_skb = mlxsw_core->emad.resp_skb;
+
+               err = mlxsw_emad_process_status_skb(mlxsw_core, resp_skb);
+               if (err)
+                       dev_kfree_skb(resp_skb);
+               if (!err || err != -EAGAIN)
+                       goto out;
+       }
+       if (n_retry++ < MLXSW_EMAD_MAX_RETRY)
+               goto retry;
+
+out:
+       dev_kfree_skb(skb);
+       mlxsw_core->emad.tid++;
+       return err;
+}
+
+static void mlxsw_emad_rx_listener_func(struct sk_buff *skb, u8 local_port,
+                                       void *priv)
+{
+       struct mlxsw_core *mlxsw_core = priv;
+
+       if (mlxsw_emad_is_resp(skb) &&
+           mlxsw_core->emad.trans_active &&
+           mlxsw_emad_get_tid(skb) == mlxsw_core->emad.tid) {
+               mlxsw_core->emad.resp_skb = skb;
+               mlxsw_core->emad.trans_active = false;
+               wake_up(&mlxsw_core->emad.wait);
+       } else {
+               dev_kfree_skb(skb);
+       }
+}
+
+static const struct mlxsw_rx_listener mlxsw_emad_rx_listener = {
+       .func = mlxsw_emad_rx_listener_func,
+       .local_port = MLXSW_PORT_DONT_CARE,
+       .trap_id = MLXSW_TRAP_ID_ETHEMAD,
+};
+
+static int mlxsw_emad_traps_set(struct mlxsw_core *mlxsw_core)
+{
+       char htgt_pl[MLXSW_REG_HTGT_LEN];
+       char hpkt_pl[MLXSW_REG_HPKT_LEN];
+       int err;
+
+       mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_EMAD);
+       err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl);
+       if (err)
+               return err;
+
+       mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU,
+                           MLXSW_REG_HTGT_TRAP_GROUP_EMAD,
+                           MLXSW_TRAP_ID_ETHEMAD);
+       return mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl);
+}
+
+static int mlxsw_emad_init(struct mlxsw_core *mlxsw_core)
+{
+       int err;
+
+       /* Set the upper 32 bits of the transaction ID field to a random
+        * number. This allows us to discard EMADs addressed to other
+        * devices.
+        */
+       get_random_bytes(&mlxsw_core->emad.tid, 4);
+       mlxsw_core->emad.tid = mlxsw_core->emad.tid << 32;
+
+       init_waitqueue_head(&mlxsw_core->emad.wait);
+       mlxsw_core->emad.trans_active = false;
+       mutex_init(&mlxsw_core->emad.lock);
+
+       err = mlxsw_core_rx_listener_register(mlxsw_core,
+                                             &mlxsw_emad_rx_listener,
+                                             mlxsw_core);
+       if (err)
+               return err;
+
+       err = mlxsw_emad_traps_set(mlxsw_core);
+       if (err)
+               goto err_emad_trap_set;
+
+       mlxsw_core->emad.use_emad = true;
+
+       return 0;
+
+err_emad_trap_set:
+       mlxsw_core_rx_listener_unregister(mlxsw_core,
+                                         &mlxsw_emad_rx_listener,
+                                         mlxsw_core);
+       return err;
+}
+
+static void mlxsw_emad_fini(struct mlxsw_core *mlxsw_core)
+{
+       char hpkt_pl[MLXSW_REG_HPKT_LEN];
+
+       mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_DISCARD,
+                           MLXSW_REG_HTGT_TRAP_GROUP_EMAD,
+                           MLXSW_TRAP_ID_ETHEMAD);
+       mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl);
+
+       mlxsw_core_rx_listener_unregister(mlxsw_core,
+                                         &mlxsw_emad_rx_listener,
+                                         mlxsw_core);
+}
+
+static struct sk_buff *mlxsw_emad_alloc(const struct mlxsw_core *mlxsw_core,
+                                       u16 reg_len)
+{
+       struct sk_buff *skb;
+       u16 emad_len;
+
+       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 (emad_len > MLXSW_EMAD_MAX_FRAME_LEN)
+               return NULL;
+
+       skb = netdev_alloc_skb(NULL, emad_len);
+       if (!skb)
+               return NULL;
+       memset(skb->data, 0, emad_len);
+       skb_reserve(skb, emad_len);
+
+       return skb;
+}
+
 /*****************
  * Core functions
  *****************/
        }
 
        INIT_LIST_HEAD(&mlxsw_core->rx_listener_list);
+       INIT_LIST_HEAD(&mlxsw_core->event_listener_list);
        mlxsw_core->driver = mlxsw_driver;
        mlxsw_core->bus = mlxsw_bus;
        mlxsw_core->bus_priv = bus_priv;
                err = -ENOMEM;
                goto err_alloc_stats;
        }
+
        err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile);
        if (err)
                goto err_bus_init;
 
+       err = mlxsw_emad_init(mlxsw_core);
+       if (err)
+               goto err_emad_init;
+
        err = mlxsw_driver->init(mlxsw_core->driver_priv, mlxsw_core,
                                 mlxsw_bus_info);
        if (err)
 err_debugfs_init:
        mlxsw_core->driver->fini(mlxsw_core->driver_priv);
 err_driver_init:
+       mlxsw_emad_fini(mlxsw_core);
+err_emad_init:
        mlxsw_bus->fini(bus_priv);
 err_bus_init:
        free_percpu(mlxsw_core->pcpu_stats);
 
        mlxsw_core_debugfs_fini(mlxsw_core);
        mlxsw_core->driver->fini(mlxsw_core->driver_priv);
+       mlxsw_emad_fini(mlxsw_core);
        mlxsw_core->bus->fini(mlxsw_core->bus_priv);
        free_percpu(mlxsw_core->pcpu_stats);
        kfree(mlxsw_core);
 }
 EXPORT_SYMBOL(mlxsw_core_rx_listener_unregister);
 
+static void mlxsw_core_event_listener_func(struct sk_buff *skb, u8 local_port,
+                                          void *priv)
+{
+       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);
+
+       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);
+       event_listener_item->el.func(®, payload, event_listener_item->priv);
+       dev_kfree_skb(skb);
+}
+
+static bool __is_event_listener_equal(const struct mlxsw_event_listener *el_a,
+                                     const struct mlxsw_event_listener *el_b)
+{
+       return (el_a->func == el_b->func &&
+               el_a->trap_id == el_b->trap_id);
+}
+
+static struct mlxsw_event_listener_item *
+__find_event_listener_item(struct mlxsw_core *mlxsw_core,
+                          const struct mlxsw_event_listener *el,
+                          void *priv)
+{
+       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)
+                       return el_item;
+       }
+       return NULL;
+}
+
+int mlxsw_core_event_listener_register(struct mlxsw_core *mlxsw_core,
+                                      const struct mlxsw_event_listener *el,
+                                      void *priv)
+{
+       int err;
+       struct mlxsw_event_listener_item *el_item;
+       const struct mlxsw_rx_listener rxl = {
+               .func = mlxsw_core_event_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = el->trap_id,
+       };
+
+       el_item = __find_event_listener_item(mlxsw_core, el, priv);
+       if (el_item)
+               return -EEXIST;
+       el_item = kmalloc(sizeof(*el_item), GFP_KERNEL);
+       if (!el_item)
+               return -ENOMEM;
+       el_item->el = *el;
+       el_item->priv = priv;
+
+       err = mlxsw_core_rx_listener_register(mlxsw_core, &rxl, el_item);
+       if (err)
+               goto err_rx_listener_register;
+
+       /* No reason to save item if we did not manage to register an RX
+        * listener for it.
+        */
+       list_add_rcu(&el_item->list, &mlxsw_core->event_listener_list);
+
+       return 0;
+
+err_rx_listener_register:
+       kfree(el_item);
+       return err;
+}
+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)
+{
+       struct mlxsw_event_listener_item *el_item;
+       const struct mlxsw_rx_listener rxl = {
+               .func = mlxsw_core_event_listener_func,
+               .local_port = MLXSW_PORT_DONT_CARE,
+               .trap_id = el->trap_id,
+       };
+
+       el_item = __find_event_listener_item(mlxsw_core, el, priv);
+       if (!el_item)
+               return;
+       mlxsw_core_rx_listener_unregister(mlxsw_core, &rxl, el_item);
+       list_del(&el_item->list);
+       kfree(el_item);
+}
+EXPORT_SYMBOL(mlxsw_core_event_listener_unregister);
+
+static int mlxsw_core_reg_access_emad(struct mlxsw_core *mlxsw_core,
+                                     const struct mlxsw_reg_info *reg,
+                                     char *payload,
+                                     enum mlxsw_core_reg_access_type type)
+{
+       int err;
+       char *op_tlv;
+       struct sk_buff *skb;
+       struct mlxsw_tx_info tx_info = {
+               .local_port = MLXSW_PORT_CPU_PORT,
+               .is_emad = true,
+       };
+
+       skb = mlxsw_emad_alloc(mlxsw_core, reg->len);
+       if (!skb)
+               return -ENOMEM;
+
+       mlxsw_emad_construct(skb, reg, payload, type, mlxsw_core);
+       mlxsw_core->driver->txhdr_construct(skb, &tx_info);
+
+       dev_dbg(mlxsw_core->bus_info->dev, "EMAD send (tid=%llx)\n",
+               mlxsw_core->emad.tid);
+       mlxsw_core_buf_dump_dbg(mlxsw_core, skb->data, skb->len);
+
+       err = mlxsw_emad_transmit(mlxsw_core, skb, &tx_info);
+       if (!err) {
+               op_tlv = mlxsw_emad_op_tlv(mlxsw_core->emad.resp_skb);
+               memcpy(payload, mlxsw_emad_reg_payload(op_tlv),
+                      reg->len);
+
+               dev_dbg(mlxsw_core->bus_info->dev, "EMAD recv (tid=%llx)\n",
+                       mlxsw_core->emad.tid - 1);
+               mlxsw_core_buf_dump_dbg(mlxsw_core,
+                                       mlxsw_core->emad.resp_skb->data,
+                                       skb->len);
+
+               dev_kfree_skb(mlxsw_core->emad.resp_skb);
+       }
+
+       return err;
+}
+
+static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core,
+                                    const struct mlxsw_reg_info *reg,
+                                    char *payload,
+                                    enum mlxsw_core_reg_access_type type)
+{
+       int err, n_retry;
+       char *in_mbox, *out_mbox, *tmp;
+
+       in_mbox = mlxsw_cmd_mbox_alloc();
+       if (!in_mbox)
+               return -ENOMEM;
+
+       out_mbox = mlxsw_cmd_mbox_alloc();
+       if (!out_mbox) {
+               err = -ENOMEM;
+               goto free_in_mbox;
+       }
+
+       mlxsw_emad_pack_op_tlv(in_mbox, reg, type, mlxsw_core);
+       tmp = in_mbox + MLXSW_EMAD_OP_TLV_LEN * sizeof(u32);
+       mlxsw_emad_pack_reg_tlv(tmp, reg, payload);
+
+       n_retry = 0;
+retry:
+       err = mlxsw_cmd_access_reg(mlxsw_core, in_mbox, out_mbox);
+       if (!err) {
+               err = mlxsw_emad_process_status(mlxsw_core, out_mbox);
+               if (err == -EAGAIN && n_retry++ < MLXSW_EMAD_MAX_RETRY)
+                       goto retry;
+       }
+
+       if (!err)
+               memcpy(payload, mlxsw_emad_reg_payload(out_mbox),
+                      reg->len);
+
+       mlxsw_core->emad.tid++;
+       mlxsw_cmd_mbox_free(out_mbox);
+free_in_mbox:
+       mlxsw_cmd_mbox_free(in_mbox);
+       return err;
+}
+
+static int mlxsw_core_reg_access(struct mlxsw_core *mlxsw_core,
+                                const struct mlxsw_reg_info *reg,
+                                char *payload,
+                                enum mlxsw_core_reg_access_type type)
+{
+       u64 cur_tid;
+       int err;
+
+       if (mutex_lock_interruptible(&mlxsw_core->emad.lock)) {
+               dev_err(mlxsw_core->bus_info->dev, "Reg access interrupted (reg_id=%x(%s),type=%s)\n",
+                       reg->id, mlxsw_reg_id_str(reg->id),
+                       mlxsw_core_reg_access_type_str(type));
+               return -EINTR;
+       }
+
+       cur_tid = mlxsw_core->emad.tid;
+       dev_dbg(mlxsw_core->bus_info->dev, "Reg access (tid=%llx,reg_id=%x(%s),type=%s)\n",
+               cur_tid, reg->id, mlxsw_reg_id_str(reg->id),
+               mlxsw_core_reg_access_type_str(type));
+
+       /* During initialization EMAD interface is not available to us,
+        * so we default to command interface. We switch to EMAD interface
+        * after setting the appropriate traps.
+        */
+       if (!mlxsw_core->emad.use_emad)
+               err = mlxsw_core_reg_access_cmd(mlxsw_core, reg,
+                                               payload, type);
+       else
+               err = mlxsw_core_reg_access_emad(mlxsw_core, reg,
+                                                payload, type);
+
+       if (err)
+               dev_err(mlxsw_core->bus_info->dev, "Reg access failed (tid=%llx,reg_id=%x(%s),type=%s)\n",
+                       cur_tid, reg->id, mlxsw_reg_id_str(reg->id),
+                       mlxsw_core_reg_access_type_str(type));
+
+       mutex_unlock(&mlxsw_core->emad.lock);
+       return err;
+}
+
+int mlxsw_reg_query(struct mlxsw_core *mlxsw_core,
+                   const struct mlxsw_reg_info *reg, char *payload)
+{
+       return mlxsw_core_reg_access(mlxsw_core, reg, payload,
+                                    MLXSW_CORE_REG_ACCESS_TYPE_QUERY);
+}
+EXPORT_SYMBOL(mlxsw_reg_query);
+
+int mlxsw_reg_write(struct mlxsw_core *mlxsw_core,
+                   const struct mlxsw_reg_info *reg, char *payload)
+{
+       return mlxsw_core_reg_access(mlxsw_core, reg, payload,
+                                    MLXSW_CORE_REG_ACCESS_TYPE_WRITE);
+}
+EXPORT_SYMBOL(mlxsw_reg_write);
+
 void mlxsw_core_skb_receive(struct mlxsw_core *mlxsw_core, struct sk_buff *skb,
                            struct mlxsw_rx_info *rx_info)
 {
 
--- /dev/null
+/*
+ * drivers/net/ethernet/mellanox/mlxsw/reg.h
+ * Copyright (c) 2015 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2015 Ido Schimmel <idosch@mellanox.com>
+ * Copyright (c) 2015 Elad Raz <eladr@mellanox.com>
+ * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MLXSW_REG_H
+#define _MLXSW_REG_H
+
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <linux/if_vlan.h>
+
+#include "item.h"
+#include "port.h"
+
+struct mlxsw_reg_info {
+       u16 id;
+       u16 len; /* In u8 */
+};
+
+#define MLXSW_REG(type) (&mlxsw_reg_##type)
+#define MLXSW_REG_LEN(type) MLXSW_REG(type)->len
+#define MLXSW_REG_ZERO(type, payload) memset(payload, 0, MLXSW_REG(type)->len)
+
+/* SGCR - Switch General Configuration Register
+ * --------------------------------------------
+ * This register is used for configuration of the switch capabilities.
+ */
+#define MLXSW_REG_SGCR_ID 0x2000
+#define MLXSW_REG_SGCR_LEN 0x10
+
+static const struct mlxsw_reg_info mlxsw_reg_sgcr = {
+       .id = MLXSW_REG_SGCR_ID,
+       .len = MLXSW_REG_SGCR_LEN,
+};
+
+/* reg_sgcr_llb
+ * Link Local Broadcast (Default=0)
+ * When set, all Link Local packets (224.0.0.X) will be treated as broadcast
+ * packets and ignore the IGMP snooping entries.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sgcr, llb, 0x04, 0, 1);
+
+static inline void mlxsw_reg_sgcr_pack(char *payload, bool llb)
+{
+       MLXSW_REG_ZERO(sgcr, payload);
+       mlxsw_reg_sgcr_llb_set(payload, !!llb);
+}
+
+/* SPAD - Switch Physical Address Register
+ * ---------------------------------------
+ * The SPAD register configures the switch physical MAC address.
+ */
+#define MLXSW_REG_SPAD_ID 0x2002
+#define MLXSW_REG_SPAD_LEN 0x10
+
+static const struct mlxsw_reg_info mlxsw_reg_spad = {
+       .id = MLXSW_REG_SPAD_ID,
+       .len = MLXSW_REG_SPAD_LEN,
+};
+
+/* reg_spad_base_mac
+ * Base MAC address for the switch partitions.
+ * Per switch partition MAC address is equal to:
+ * base_mac + swid
+ * Access: RW
+ */
+MLXSW_ITEM_BUF(reg, spad, base_mac, 0x02, 6);
+
+/* SMID - Switch Multicast ID
+ * --------------------------
+ * In multi-chip configuration, each device should maintain mapping between
+ * Multicast ID (MID) into a list of local ports. This mapping is used in all
+ * the devices other than the ingress device, and is implemented as part of the
+ * FDB. The MID record maps from a MID, which is a unique identi- fier of the
+ * multicast group within the stacking domain, into a list of local ports into
+ * which the packet is replicated.
+ */
+#define MLXSW_REG_SMID_ID 0x2007
+#define MLXSW_REG_SMID_LEN 0x420
+
+static const struct mlxsw_reg_info mlxsw_reg_smid = {
+       .id = MLXSW_REG_SMID_ID,
+       .len = MLXSW_REG_SMID_LEN,
+};
+
+/* reg_smid_swid
+ * Switch partition ID.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, smid, swid, 0x00, 24, 8);
+
+/* reg_smid_mid
+ * Multicast identifier - global identifier that represents the multicast group
+ * across all devices
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, smid, mid, 0x00, 0, 16);
+
+/* reg_smid_port
+ * Local port memebership (1 bit per port).
+ * Access: RW
+ */
+MLXSW_ITEM_BIT_ARRAY(reg, smid, port, 0x20, 0x20, 1);
+
+/* reg_smid_port_mask
+ * Local port mask (1 bit per port).
+ * Access: W
+ */
+MLXSW_ITEM_BIT_ARRAY(reg, smid, port_mask, 0x220, 0x20, 1);
+
+static inline void mlxsw_reg_smid_pack(char *payload, u16 mid)
+{
+       MLXSW_REG_ZERO(smid, payload);
+       mlxsw_reg_smid_swid_set(payload, 0);
+       mlxsw_reg_smid_mid_set(payload, mid);
+       mlxsw_reg_smid_port_set(payload, MLXSW_PORT_CPU_PORT, 1);
+       mlxsw_reg_smid_port_mask_set(payload, MLXSW_PORT_CPU_PORT, 1);
+}
+
+/* SPMS - Switch Port MSTP/RSTP State Register
+ * -------------------------------------------
+ * Configures the spanning tree state of a physical port.
+ */
+#define MLXSW_REG_SPMS_ID 0x200d
+#define MLXSW_REG_SPMS_LEN 0x404
+
+static const struct mlxsw_reg_info mlxsw_reg_spms = {
+       .id = MLXSW_REG_SPMS_ID,
+       .len = MLXSW_REG_SPMS_LEN,
+};
+
+/* reg_spms_local_port
+ * Local port number.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, spms, local_port, 0x00, 16, 8);
+
+enum mlxsw_reg_spms_state {
+       MLXSW_REG_SPMS_STATE_NO_CHANGE,
+       MLXSW_REG_SPMS_STATE_DISCARDING,
+       MLXSW_REG_SPMS_STATE_LEARNING,
+       MLXSW_REG_SPMS_STATE_FORWARDING,
+};
+
+/* reg_spms_state
+ * Spanning tree state of each VLAN ID (VID) of the local port.
+ * 0 - Do not change spanning tree state (used only when writing).
+ * 1 - Discarding. No learning or forwarding to/from this port (default).
+ * 2 - Learning. Port is learning, but not forwarding.
+ * 3 - Forwarding. Port is learning and forwarding.
+ * Access: RW
+ */
+MLXSW_ITEM_BIT_ARRAY(reg, spms, state, 0x04, 0x400, 2);
+
+static inline void mlxsw_reg_spms_pack(char *payload, u8 local_port, u16 vid,
+                                      enum mlxsw_reg_spms_state state)
+{
+       MLXSW_REG_ZERO(spms, payload);
+       mlxsw_reg_spms_local_port_set(payload, local_port);
+       mlxsw_reg_spms_state_set(payload, vid, state);
+}
+
+/* SFGC - Switch Flooding Group Configuration
+ * ------------------------------------------
+ * The following register controls the association of flooding tables and MIDs
+ * to packet types used for flooding.
+ */
+#define MLXSW_REG_SFGC_ID  0x2011
+#define MLXSW_REG_SFGC_LEN 0x10
+
+static const struct mlxsw_reg_info mlxsw_reg_sfgc = {
+       .id = MLXSW_REG_SFGC_ID,
+       .len = MLXSW_REG_SFGC_LEN,
+};
+
+enum mlxsw_reg_sfgc_type {
+       MLXSW_REG_SFGC_TYPE_BROADCAST = 0,
+       MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST = 1,
+       MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4 = 2,
+       MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6 = 3,
+       MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP = 5,
+       MLXSW_REG_SFGC_TYPE_IPV4_LINK_LOCAL = 6,
+       MLXSW_REG_SFGC_TYPE_IPV6_ALL_HOST = 7,
+};
+
+/* reg_sfgc_type
+ * The traffic type to reach the flooding table.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sfgc, type, 0x00, 0, 4);
+
+enum mlxsw_reg_sfgc_bridge_type {
+       MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID = 0,
+       MLXSW_REG_SFGC_BRIDGE_TYPE_VFID = 1,
+};
+
+/* reg_sfgc_bridge_type
+ * Access: Index
+ *
+ * Note: SwitchX-2 only supports 802.1Q mode.
+ */
+MLXSW_ITEM32(reg, sfgc, bridge_type, 0x04, 24, 3);
+
+enum mlxsw_flood_table_type {
+       MLXSW_REG_SFGC_TABLE_TYPE_VID = 1,
+       MLXSW_REG_SFGC_TABLE_TYPE_SINGLE = 2,
+       MLXSW_REG_SFGC_TABLE_TYPE_ANY = 0,
+       MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST = 3,
+       MLXSW_REG_SFGC_TABLE_TYPE_FID = 4,
+};
+
+/* reg_sfgc_table_type
+ * See mlxsw_flood_table_type
+ * Access: RW
+ *
+ * Note: FID offset and FID types are not supported in SwitchX-2.
+ */
+MLXSW_ITEM32(reg, sfgc, table_type, 0x04, 16, 3);
+
+/* reg_sfgc_flood_table
+ * Flooding table index to associate with the specific type on the specific
+ * switch partition.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sfgc, flood_table, 0x04, 0, 6);
+
+/* reg_sfgc_mid
+ * The multicast ID for the swid. Not supported for Spectrum
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sfgc, mid, 0x08, 0, 16);
+
+/* reg_sfgc_counter_set_type
+ * Counter Set Type for flow counters.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sfgc, counter_set_type, 0x0C, 24, 8);
+
+/* reg_sfgc_counter_index
+ * Counter Index for flow counters.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sfgc, counter_index, 0x0C, 0, 24);
+
+static inline void
+mlxsw_reg_sfgc_pack(char *payload, enum mlxsw_reg_sfgc_type type,
+                   enum mlxsw_reg_sfgc_bridge_type bridge_type,
+                   enum mlxsw_flood_table_type table_type,
+                   unsigned int flood_table)
+{
+       MLXSW_REG_ZERO(sfgc, payload);
+       mlxsw_reg_sfgc_type_set(payload, type);
+       mlxsw_reg_sfgc_bridge_type_set(payload, bridge_type);
+       mlxsw_reg_sfgc_table_type_set(payload, table_type);
+       mlxsw_reg_sfgc_flood_table_set(payload, flood_table);
+       mlxsw_reg_sfgc_mid_set(payload, MLXSW_PORT_MID);
+}
+
+/* SFTR - Switch Flooding Table Register
+ * -------------------------------------
+ * The switch flooding table is used for flooding packet replication. The table
+ * defines a bit mask of ports for packet replication.
+ */
+#define MLXSW_REG_SFTR_ID 0x2012
+#define MLXSW_REG_SFTR_LEN 0x420
+
+static const struct mlxsw_reg_info mlxsw_reg_sftr = {
+       .id = MLXSW_REG_SFTR_ID,
+       .len = MLXSW_REG_SFTR_LEN,
+};
+
+/* reg_sftr_swid
+ * Switch partition ID with which to associate the port.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sftr, swid, 0x00, 24, 8);
+
+/* reg_sftr_flood_table
+ * Flooding table index to associate with the specific type on the specific
+ * switch partition.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sftr, flood_table, 0x00, 16, 6);
+
+/* reg_sftr_index
+ * Index. Used as an index into the Flooding Table in case the table is
+ * configured to use VID / FID or FID Offset.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sftr, index, 0x00, 0, 16);
+
+/* reg_sftr_table_type
+ * See mlxsw_flood_table_type
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sftr, table_type, 0x04, 16, 3);
+
+/* reg_sftr_range
+ * Range of entries to update
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sftr, range, 0x04, 0, 16);
+
+/* reg_sftr_port
+ * Local port membership (1 bit per port).
+ * Access: RW
+ */
+MLXSW_ITEM_BIT_ARRAY(reg, sftr, port, 0x20, 0x20, 1);
+
+/* reg_sftr_cpu_port_mask
+ * CPU port mask (1 bit per port).
+ * Access: W
+ */
+MLXSW_ITEM_BIT_ARRAY(reg, sftr, port_mask, 0x220, 0x20, 1);
+
+static inline void mlxsw_reg_sftr_pack(char *payload,
+                                      unsigned int flood_table,
+                                      unsigned int index,
+                                      enum mlxsw_flood_table_type table_type,
+                                      unsigned int range)
+{
+       MLXSW_REG_ZERO(sftr, payload);
+       mlxsw_reg_sftr_swid_set(payload, 0);
+       mlxsw_reg_sftr_flood_table_set(payload, flood_table);
+       mlxsw_reg_sftr_index_set(payload, index);
+       mlxsw_reg_sftr_table_type_set(payload, table_type);
+       mlxsw_reg_sftr_range_set(payload, range);
+       mlxsw_reg_sftr_port_set(payload, MLXSW_PORT_CPU_PORT, 1);
+       mlxsw_reg_sftr_port_mask_set(payload, MLXSW_PORT_CPU_PORT, 1);
+}
+
+/* SPMLR - Switch Port MAC Learning Register
+ * -----------------------------------------
+ * Controls the Switch MAC learning policy per port.
+ */
+#define MLXSW_REG_SPMLR_ID 0x2018
+#define MLXSW_REG_SPMLR_LEN 0x8
+
+static const struct mlxsw_reg_info mlxsw_reg_spmlr = {
+       .id = MLXSW_REG_SPMLR_ID,
+       .len = MLXSW_REG_SPMLR_LEN,
+};
+
+/* reg_spmlr_local_port
+ * Local port number.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, spmlr, local_port, 0x00, 16, 8);
+
+/* reg_spmlr_sub_port
+ * Virtual port within the physical port.
+ * Should be set to 0 when virtual ports are not enabled on the port.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, spmlr, sub_port, 0x00, 8, 8);
+
+enum mlxsw_reg_spmlr_learn_mode {
+       MLXSW_REG_SPMLR_LEARN_MODE_DISABLE = 0,
+       MLXSW_REG_SPMLR_LEARN_MODE_ENABLE = 2,
+       MLXSW_REG_SPMLR_LEARN_MODE_SEC = 3,
+};
+
+/* reg_spmlr_learn_mode
+ * Learning mode on the port.
+ * 0 - Learning disabled.
+ * 2 - Learning enabled.
+ * 3 - Security mode.
+ *
+ * In security mode the switch does not learn MACs on the port, but uses the
+ * SMAC to see if it exists on another ingress port. If so, the packet is
+ * classified as a bad packet and is discarded unless the software registers
+ * to receive port security error packets usign HPKT.
+ */
+MLXSW_ITEM32(reg, spmlr, learn_mode, 0x04, 30, 2);
+
+static inline void mlxsw_reg_spmlr_pack(char *payload, u8 local_port,
+                                       enum mlxsw_reg_spmlr_learn_mode mode)
+{
+       MLXSW_REG_ZERO(spmlr, payload);
+       mlxsw_reg_spmlr_local_port_set(payload, local_port);
+       mlxsw_reg_spmlr_sub_port_set(payload, 0);
+       mlxsw_reg_spmlr_learn_mode_set(payload, mode);
+}
+
+/* PMLP - Ports Module to Local Port Register
+ * ------------------------------------------
+ * Configures the assignment of modules to local ports.
+ */
+#define MLXSW_REG_PMLP_ID 0x5002
+#define MLXSW_REG_PMLP_LEN 0x40
+
+static const struct mlxsw_reg_info mlxsw_reg_pmlp = {
+       .id = MLXSW_REG_PMLP_ID,
+       .len = MLXSW_REG_PMLP_LEN,
+};
+
+/* reg_pmlp_rxtx
+ * 0 - Tx value is used for both Tx and Rx.
+ * 1 - Rx value is taken from a separte field.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, pmlp, rxtx, 0x00, 31, 1);
+
+/* reg_pmlp_local_port
+ * Local port number.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, pmlp, local_port, 0x00, 16, 8);
+
+/* reg_pmlp_width
+ * 0 - Unmap local port.
+ * 1 - Lane 0 is used.
+ * 2 - Lanes 0 and 1 are used.
+ * 4 - Lanes 0, 1, 2 and 3 are used.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, pmlp, width, 0x00, 0, 8);
+
+/* reg_pmlp_module
+ * Module number.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, pmlp, module, 0x04, 0, 8, 0x04, 0, false);
+
+/* reg_pmlp_tx_lane
+ * Tx Lane. When rxtx field is cleared, this field is used for Rx as well.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, pmlp, tx_lane, 0x04, 16, 2, 0x04, 16, false);
+
+/* reg_pmlp_rx_lane
+ * Rx Lane. When rxtx field is cleared, this field is ignored and Rx lane is
+ * equal to Tx lane.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, pmlp, rx_lane, 0x04, 24, 2, 0x04, 24, false);
+
+static inline void mlxsw_reg_pmlp_pack(char *payload, u8 local_port)
+{
+       MLXSW_REG_ZERO(pmlp, payload);
+       mlxsw_reg_pmlp_local_port_set(payload, local_port);
+}
+
+/* PMTU - Port MTU Register
+ * ------------------------
+ * Configures and reports the port MTU.
+ */
+#define MLXSW_REG_PMTU_ID 0x5003
+#define MLXSW_REG_PMTU_LEN 0x10
+
+static const struct mlxsw_reg_info mlxsw_reg_pmtu = {
+       .id = MLXSW_REG_PMTU_ID,
+       .len = MLXSW_REG_PMTU_LEN,
+};
+
+/* reg_pmtu_local_port
+ * Local port number.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, pmtu, local_port, 0x00, 16, 8);
+
+/* reg_pmtu_max_mtu
+ * Maximum MTU.
+ * When port type (e.g. Ethernet) is configured, the relevant MTU is
+ * reported, otherwise the minimum between the max_mtu of the different
+ * types is reported.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, pmtu, max_mtu, 0x04, 16, 16);
+
+/* reg_pmtu_admin_mtu
+ * MTU value to set port to. Must be smaller or equal to max_mtu.
+ * Note: If port type is Infiniband, then port must be disabled, when its
+ * MTU is set.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, pmtu, admin_mtu, 0x08, 16, 16);
+
+/* reg_pmtu_oper_mtu
+ * The actual MTU configured on the port. Packets exceeding this size
+ * will be dropped.
+ * Note: In Ethernet and FC oper_mtu == admin_mtu, however, in Infiniband
+ * oper_mtu might be smaller than admin_mtu.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, pmtu, oper_mtu, 0x0C, 16, 16);
+
+static inline void mlxsw_reg_pmtu_pack(char *payload, u8 local_port,
+                                      u16 new_mtu)
+{
+       MLXSW_REG_ZERO(pmtu, payload);
+       mlxsw_reg_pmtu_local_port_set(payload, local_port);
+       mlxsw_reg_pmtu_max_mtu_set(payload, 0);
+       mlxsw_reg_pmtu_admin_mtu_set(payload, new_mtu);
+       mlxsw_reg_pmtu_oper_mtu_set(payload, 0);
+}
+
+/* PTYS - Port Type and Speed Register
+ * -----------------------------------
+ * Configures and reports the port speed type.
+ *
+ * Note: When set while the link is up, the changes will not take effect
+ * until the port transitions from down to up state.
+ */
+#define MLXSW_REG_PTYS_ID 0x5004
+#define MLXSW_REG_PTYS_LEN 0x40
+
+static const struct mlxsw_reg_info mlxsw_reg_ptys = {
+       .id = MLXSW_REG_PTYS_ID,
+       .len = MLXSW_REG_PTYS_LEN,
+};
+
+/* reg_ptys_local_port
+ * Local port number.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, ptys, local_port, 0x00, 16, 8);
+
+#define MLXSW_REG_PTYS_PROTO_MASK_ETH  BIT(2)
+
+/* reg_ptys_proto_mask
+ * Protocol mask. Indicates which protocol is used.
+ * 0 - Infiniband.
+ * 1 - Fibre Channel.
+ * 2 - Ethernet.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, ptys, proto_mask, 0x00, 0, 3);
+
+#define MLXSW_REG_PTYS_ETH_SPEED_SGMII                 BIT(0)
+#define MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX           BIT(1)
+#define MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CX4           BIT(2)
+#define MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4           BIT(3)
+#define MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR            BIT(4)
+#define MLXSW_REG_PTYS_ETH_SPEED_20GBASE_KR2           BIT(5)
+#define MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4           BIT(6)
+#define MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4           BIT(7)
+#define MLXSW_REG_PTYS_ETH_SPEED_56GBASE_R4            BIT(8)
+#define MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR            BIT(12)
+#define MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR            BIT(13)
+#define MLXSW_REG_PTYS_ETH_SPEED_10GBASE_ER_LR         BIT(14)
+#define MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4           BIT(15)
+#define MLXSW_REG_PTYS_ETH_SPEED_40GBASE_LR4_ER4       BIT(16)
+#define MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR4           BIT(19)
+#define MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4          BIT(20)
+#define MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4          BIT(21)
+#define MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4          BIT(22)
+#define MLXSW_REG_PTYS_ETH_SPEED_100GBASE_LR4_ER4      BIT(23)
+#define MLXSW_REG_PTYS_ETH_SPEED_100BASE_TX            BIT(24)
+#define MLXSW_REG_PTYS_ETH_SPEED_100BASE_T             BIT(25)
+#define MLXSW_REG_PTYS_ETH_SPEED_10GBASE_T             BIT(26)
+#define MLXSW_REG_PTYS_ETH_SPEED_25GBASE_CR            BIT(27)
+#define MLXSW_REG_PTYS_ETH_SPEED_25GBASE_KR            BIT(28)
+#define MLXSW_REG_PTYS_ETH_SPEED_25GBASE_SR            BIT(29)
+#define MLXSW_REG_PTYS_ETH_SPEED_50GBASE_CR2           BIT(30)
+#define MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR2           BIT(31)
+
+/* reg_ptys_eth_proto_cap
+ * Ethernet port supported speeds and protocols.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, ptys, eth_proto_cap, 0x0C, 0, 32);
+
+/* reg_ptys_eth_proto_admin
+ * Speed and protocol to set port to.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, ptys, eth_proto_admin, 0x18, 0, 32);
+
+/* reg_ptys_eth_proto_oper
+ * The current speed and protocol configured for the port.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, ptys, eth_proto_oper, 0x24, 0, 32);
+
+static inline void mlxsw_reg_ptys_pack(char *payload, u8 local_port,
+                                      u32 proto_admin)
+{
+       MLXSW_REG_ZERO(ptys, payload);
+       mlxsw_reg_ptys_local_port_set(payload, local_port);
+       mlxsw_reg_ptys_proto_mask_set(payload, MLXSW_REG_PTYS_PROTO_MASK_ETH);
+       mlxsw_reg_ptys_eth_proto_admin_set(payload, proto_admin);
+}
+
+static inline void mlxsw_reg_ptys_unpack(char *payload, u32 *p_eth_proto_cap,
+                                        u32 *p_eth_proto_adm,
+                                        u32 *p_eth_proto_oper)
+{
+       if (p_eth_proto_cap)
+               *p_eth_proto_cap = mlxsw_reg_ptys_eth_proto_cap_get(payload);
+       if (p_eth_proto_adm)
+               *p_eth_proto_adm = mlxsw_reg_ptys_eth_proto_admin_get(payload);
+       if (p_eth_proto_oper)
+               *p_eth_proto_oper = mlxsw_reg_ptys_eth_proto_oper_get(payload);
+}
+
+/* PPAD - Port Physical Address Register
+ * -------------------------------------
+ * The PPAD register configures the per port physical MAC address.
+ */
+#define MLXSW_REG_PPAD_ID 0x5005
+#define MLXSW_REG_PPAD_LEN 0x10
+
+static const struct mlxsw_reg_info mlxsw_reg_ppad = {
+       .id = MLXSW_REG_PPAD_ID,
+       .len = MLXSW_REG_PPAD_LEN,
+};
+
+/* reg_ppad_single_base_mac
+ * 0: base_mac, local port should be 0 and mac[7:0] is
+ * reserved. HW will set incremental
+ * 1: single_mac - mac of the local_port
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, ppad, single_base_mac, 0x00, 28, 1);
+
+/* reg_ppad_local_port
+ * port number, if single_base_mac = 0 then local_port is reserved
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, ppad, local_port, 0x00, 16, 8);
+
+/* reg_ppad_mac
+ * If single_base_mac = 0 - base MAC address, mac[7:0] is reserved.
+ * If single_base_mac = 1 - the per port MAC address
+ * Access: RW
+ */
+MLXSW_ITEM_BUF(reg, ppad, mac, 0x02, 6);
+
+static inline void mlxsw_reg_ppad_pack(char *payload, bool single_base_mac,
+                                      u8 local_port)
+{
+       MLXSW_REG_ZERO(ppad, payload);
+       mlxsw_reg_ppad_single_base_mac_set(payload, !!single_base_mac);
+       mlxsw_reg_ppad_local_port_set(payload, local_port);
+}
+
+/* PAOS - Ports Administrative and Operational Status Register
+ * -----------------------------------------------------------
+ * Configures and retrieves per port administrative and operational status.
+ */
+#define MLXSW_REG_PAOS_ID 0x5006
+#define MLXSW_REG_PAOS_LEN 0x10
+
+static const struct mlxsw_reg_info mlxsw_reg_paos = {
+       .id = MLXSW_REG_PAOS_ID,
+       .len = MLXSW_REG_PAOS_LEN,
+};
+
+/* reg_paos_swid
+ * Switch partition ID with which to associate the port.
+ * Note: while external ports uses unique local port numbers (and thus swid is
+ * redundant), router ports use the same local port number where swid is the
+ * only indication for the relevant port.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, paos, swid, 0x00, 24, 8);
+
+/* reg_paos_local_port
+ * Local port number.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, paos, local_port, 0x00, 16, 8);
+
+/* reg_paos_admin_status
+ * Port administrative state (the desired state of the port):
+ * 1 - Up.
+ * 2 - Down.
+ * 3 - Up once. This means that in case of link failure, the port won't go
+ *     into polling mode, but will wait to be re-enabled by software.
+ * 4 - Disabled by system. Can only be set by hardware.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, paos, admin_status, 0x00, 8, 4);
+
+/* reg_paos_oper_status
+ * Port operational state (the current state):
+ * 1 - Up.
+ * 2 - Down.
+ * 3 - Down by port failure. This means that the device will not let the
+ *     port up again until explicitly specified by software.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, paos, oper_status, 0x00, 0, 4);
+
+/* reg_paos_ase
+ * Admin state update enabled.
+ * Access: WO
+ */
+MLXSW_ITEM32(reg, paos, ase, 0x04, 31, 1);
+
+/* reg_paos_ee
+ * Event update enable. If this bit is set, event generation will be
+ * updated based on the e field.
+ * Access: WO
+ */
+MLXSW_ITEM32(reg, paos, ee, 0x04, 30, 1);
+
+/* reg_paos_e
+ * Event generation on operational state change:
+ * 0 - Do not generate event.
+ * 1 - Generate Event.
+ * 2 - Generate Single Event.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, paos, e, 0x04, 0, 2);
+
+static inline void mlxsw_reg_paos_pack(char *payload, u8 local_port,
+                                      enum mlxsw_port_admin_status status)
+{
+       MLXSW_REG_ZERO(paos, payload);
+       mlxsw_reg_paos_swid_set(payload, 0);
+       mlxsw_reg_paos_local_port_set(payload, local_port);
+       mlxsw_reg_paos_admin_status_set(payload, status);
+       mlxsw_reg_paos_oper_status_set(payload, 0);
+       mlxsw_reg_paos_ase_set(payload, 1);
+       mlxsw_reg_paos_ee_set(payload, 1);
+       mlxsw_reg_paos_e_set(payload, 1);
+}
+
+/* PPCNT - Ports Performance Counters Register
+ * -------------------------------------------
+ * The PPCNT register retrieves per port performance counters.
+ */
+#define MLXSW_REG_PPCNT_ID 0x5008
+#define MLXSW_REG_PPCNT_LEN 0x100
+
+static const struct mlxsw_reg_info mlxsw_reg_ppcnt = {
+       .id = MLXSW_REG_PPCNT_ID,
+       .len = MLXSW_REG_PPCNT_LEN,
+};
+
+/* reg_ppcnt_swid
+ * For HCA: must be always 0.
+ * Switch partition ID to associate port with.
+ * Switch partitions are numbered from 0 to 7 inclusively.
+ * Switch partition 254 indicates stacking ports.
+ * Switch partition 255 indicates all switch partitions.
+ * Only valid on Set() operation with local_port=255.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, ppcnt, swid, 0x00, 24, 8);
+
+/* reg_ppcnt_local_port
+ * Local port number.
+ * 255 indicates all ports on the device, and is only allowed
+ * for Set() operation.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, ppcnt, local_port, 0x00, 16, 8);
+
+/* reg_ppcnt_pnat
+ * Port number access type:
+ * 0 - Local port number
+ * 1 - IB port number
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, ppcnt, pnat, 0x00, 14, 2);
+
+/* reg_ppcnt_grp
+ * Performance counter group.
+ * Group 63 indicates all groups. Only valid on Set() operation with
+ * clr bit set.
+ * 0x0: IEEE 802.3 Counters
+ * 0x1: RFC 2863 Counters
+ * 0x2: RFC 2819 Counters
+ * 0x3: RFC 3635 Counters
+ * 0x5: Ethernet Extended Counters
+ * 0x8: Link Level Retransmission Counters
+ * 0x10: Per Priority Counters
+ * 0x11: Per Traffic Class Counters
+ * 0x12: Physical Layer Counters
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, ppcnt, grp, 0x00, 0, 6);
+
+/* reg_ppcnt_clr
+ * Clear counters. Setting the clr bit will reset the counter value
+ * for all counters in the counter group. This bit can be set
+ * for both Set() and Get() operation.
+ * Access: OP
+ */
+MLXSW_ITEM32(reg, ppcnt, clr, 0x04, 31, 1);
+
+/* reg_ppcnt_prio_tc
+ * Priority for counter set that support per priority, valid values: 0-7.
+ * Traffic class for counter set that support per traffic class,
+ * valid values: 0- cap_max_tclass-1 .
+ * For HCA: cap_max_tclass is always 8.
+ * Otherwise must be 0.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, ppcnt, prio_tc, 0x04, 0, 5);
+
+/* reg_ppcnt_a_frames_transmitted_ok
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_frames_transmitted_ok,
+            0x08 + 0x00, 0, 64);
+
+/* reg_ppcnt_a_frames_received_ok
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_frames_received_ok,
+            0x08 + 0x08, 0, 64);
+
+/* reg_ppcnt_a_frame_check_sequence_errors
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_frame_check_sequence_errors,
+            0x08 + 0x10, 0, 64);
+
+/* reg_ppcnt_a_alignment_errors
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_alignment_errors,
+            0x08 + 0x18, 0, 64);
+
+/* reg_ppcnt_a_octets_transmitted_ok
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_octets_transmitted_ok,
+            0x08 + 0x20, 0, 64);
+
+/* reg_ppcnt_a_octets_received_ok
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_octets_received_ok,
+            0x08 + 0x28, 0, 64);
+
+/* reg_ppcnt_a_multicast_frames_xmitted_ok
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_multicast_frames_xmitted_ok,
+            0x08 + 0x30, 0, 64);
+
+/* reg_ppcnt_a_broadcast_frames_xmitted_ok
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_broadcast_frames_xmitted_ok,
+            0x08 + 0x38, 0, 64);
+
+/* reg_ppcnt_a_multicast_frames_received_ok
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_multicast_frames_received_ok,
+            0x08 + 0x40, 0, 64);
+
+/* reg_ppcnt_a_broadcast_frames_received_ok
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_broadcast_frames_received_ok,
+            0x08 + 0x48, 0, 64);
+
+/* reg_ppcnt_a_in_range_length_errors
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_in_range_length_errors,
+            0x08 + 0x50, 0, 64);
+
+/* reg_ppcnt_a_out_of_range_length_field
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_out_of_range_length_field,
+            0x08 + 0x58, 0, 64);
+
+/* reg_ppcnt_a_frame_too_long_errors
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_frame_too_long_errors,
+            0x08 + 0x60, 0, 64);
+
+/* reg_ppcnt_a_symbol_error_during_carrier
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_symbol_error_during_carrier,
+            0x08 + 0x68, 0, 64);
+
+/* reg_ppcnt_a_mac_control_frames_transmitted
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_mac_control_frames_transmitted,
+            0x08 + 0x70, 0, 64);
+
+/* reg_ppcnt_a_mac_control_frames_received
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_mac_control_frames_received,
+            0x08 + 0x78, 0, 64);
+
+/* reg_ppcnt_a_unsupported_opcodes_received
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_unsupported_opcodes_received,
+            0x08 + 0x80, 0, 64);
+
+/* reg_ppcnt_a_pause_mac_ctrl_frames_received
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_pause_mac_ctrl_frames_received,
+            0x08 + 0x88, 0, 64);
+
+/* reg_ppcnt_a_pause_mac_ctrl_frames_transmitted
+ * Access: RO
+ */
+MLXSW_ITEM64(reg, ppcnt, a_pause_mac_ctrl_frames_transmitted,
+            0x08 + 0x90, 0, 64);
+
+static inline void mlxsw_reg_ppcnt_pack(char *payload, u8 local_port)
+{
+       MLXSW_REG_ZERO(ppcnt, payload);
+       mlxsw_reg_ppcnt_swid_set(payload, 0);
+       mlxsw_reg_ppcnt_local_port_set(payload, local_port);
+       mlxsw_reg_ppcnt_pnat_set(payload, 0);
+       mlxsw_reg_ppcnt_grp_set(payload, 0);
+       mlxsw_reg_ppcnt_clr_set(payload, 0);
+       mlxsw_reg_ppcnt_prio_tc_set(payload, 0);
+}
+
+/* PSPA - Port Switch Partition Allocation
+ * ---------------------------------------
+ * Controls the association of a port with a switch partition and enables
+ * configuring ports as stacking ports.
+ */
+#define MLXSW_REG_PSPA_ID 0x500d
+#define MLXSW_REG_PSPA_LEN 0x8
+
+static const struct mlxsw_reg_info mlxsw_reg_pspa = {
+       .id = MLXSW_REG_PSPA_ID,
+       .len = MLXSW_REG_PSPA_LEN,
+};
+
+/* reg_pspa_swid
+ * Switch partition ID.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, pspa, swid, 0x00, 24, 8);
+
+/* reg_pspa_local_port
+ * Local port number.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, pspa, local_port, 0x00, 16, 8);
+
+/* reg_pspa_sub_port
+ * Virtual port within the local port. Set to 0 when virtual ports are
+ * disabled on the local port.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, pspa, sub_port, 0x00, 8, 8);
+
+static inline void mlxsw_reg_pspa_pack(char *payload, u8 swid, u8 local_port)
+{
+       MLXSW_REG_ZERO(pspa, payload);
+       mlxsw_reg_pspa_swid_set(payload, swid);
+       mlxsw_reg_pspa_local_port_set(payload, local_port);
+       mlxsw_reg_pspa_sub_port_set(payload, 0);
+}
+
+/* HTGT - Host Trap Group Table
+ * ----------------------------
+ * Configures the properties for forwarding to CPU.
+ */
+#define MLXSW_REG_HTGT_ID 0x7002
+#define MLXSW_REG_HTGT_LEN 0x100
+
+static const struct mlxsw_reg_info mlxsw_reg_htgt = {
+       .id = MLXSW_REG_HTGT_ID,
+       .len = MLXSW_REG_HTGT_LEN,
+};
+
+/* reg_htgt_swid
+ * Switch partition ID.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, htgt, swid, 0x00, 24, 8);
+
+#define MLXSW_REG_HTGT_PATH_TYPE_LOCAL 0x0     /* For locally attached CPU */
+
+/* reg_htgt_type
+ * CPU path type.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, htgt, type, 0x00, 8, 4);
+
+#define MLXSW_REG_HTGT_TRAP_GROUP_EMAD 0x0
+#define MLXSW_REG_HTGT_TRAP_GROUP_RX   0x1
+
+/* reg_htgt_trap_group
+ * Trap group number. User defined number specifying which trap groups
+ * should be forwarded to the CPU. The mapping between trap IDs and trap
+ * groups is configured using HPKT register.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, htgt, trap_group, 0x00, 0, 8);
+
+enum {
+       MLXSW_REG_HTGT_POLICER_DISABLE,
+       MLXSW_REG_HTGT_POLICER_ENABLE,
+};
+
+/* reg_htgt_pide
+ * Enable policer ID specified using 'pid' field.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, htgt, pide, 0x04, 15, 1);
+
+/* reg_htgt_pid
+ * Policer ID for the trap group.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, htgt, pid, 0x04, 0, 8);
+
+#define MLXSW_REG_HTGT_TRAP_TO_CPU 0x0
+
+/* reg_htgt_mirror_action
+ * Mirror action to use.
+ * 0 - Trap to CPU.
+ * 1 - Trap to CPU and mirror to a mirroring agent.
+ * 2 - Mirror to a mirroring agent and do not trap to CPU.
+ * Access: RW
+ *
+ * Note: Mirroring to a mirroring agent is only supported in Spectrum.
+ */
+MLXSW_ITEM32(reg, htgt, mirror_action, 0x08, 8, 2);
+
+/* reg_htgt_mirroring_agent
+ * Mirroring agent.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, htgt, mirroring_agent, 0x08, 0, 3);
+
+/* reg_htgt_priority
+ * Trap group priority.
+ * In case a packet matches multiple classification rules, the packet will
+ * only be trapped once, based on the trap ID associated with the group (via
+ * register HPKT) with the highest priority.
+ * Supported values are 0-7, with 7 represnting the highest priority.
+ * Access: RW
+ *
+ * Note: In SwitchX-2 this field is ignored and the priority value is replaced
+ * by the 'trap_group' field.
+ */
+MLXSW_ITEM32(reg, htgt, priority, 0x0C, 0, 4);
+
+/* reg_htgt_local_path_cpu_tclass
+ * CPU ingress traffic class for the trap group.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, htgt, local_path_cpu_tclass, 0x10, 16, 6);
+
+#define MLXSW_REG_HTGT_LOCAL_PATH_RDQ_EMAD     0x15
+#define MLXSW_REG_HTGT_LOCAL_PATH_RDQ_RX       0x14
+
+/* reg_htgt_local_path_rdq
+ * Receive descriptor queue (RDQ) to use for the trap group.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, htgt, local_path_rdq, 0x10, 0, 6);
+
+static inline void mlxsw_reg_htgt_pack(char *payload, u8 trap_group)
+{
+       u8 swid, rdq;
+
+       MLXSW_REG_ZERO(htgt, payload);
+       if (MLXSW_REG_HTGT_TRAP_GROUP_EMAD == trap_group) {
+               swid = MLXSW_PORT_SWID_ALL_SWIDS;
+               rdq = MLXSW_REG_HTGT_LOCAL_PATH_RDQ_EMAD;
+       } else {
+               swid = 0;
+               rdq = MLXSW_REG_HTGT_LOCAL_PATH_RDQ_RX;
+       }
+       mlxsw_reg_htgt_swid_set(payload, swid);
+       mlxsw_reg_htgt_type_set(payload, MLXSW_REG_HTGT_PATH_TYPE_LOCAL);
+       mlxsw_reg_htgt_trap_group_set(payload, trap_group);
+       mlxsw_reg_htgt_pide_set(payload, MLXSW_REG_HTGT_POLICER_DISABLE);
+       mlxsw_reg_htgt_pid_set(payload, 0);
+       mlxsw_reg_htgt_mirror_action_set(payload, MLXSW_REG_HTGT_TRAP_TO_CPU);
+       mlxsw_reg_htgt_mirroring_agent_set(payload, 0);
+       mlxsw_reg_htgt_priority_set(payload, 0);
+       mlxsw_reg_htgt_local_path_cpu_tclass_set(payload, 7);
+       mlxsw_reg_htgt_local_path_rdq_set(payload, rdq);
+}
+
+/* HPKT - Host Packet Trap
+ * -----------------------
+ * Configures trap IDs inside trap groups.
+ */
+#define MLXSW_REG_HPKT_ID 0x7003
+#define MLXSW_REG_HPKT_LEN 0x10
+
+static const struct mlxsw_reg_info mlxsw_reg_hpkt = {
+       .id = MLXSW_REG_HPKT_ID,
+       .len = MLXSW_REG_HPKT_LEN,
+};
+
+enum {
+       MLXSW_REG_HPKT_ACK_NOT_REQUIRED,
+       MLXSW_REG_HPKT_ACK_REQUIRED,
+};
+
+/* reg_hpkt_ack
+ * Require acknowledgements from the host for events.
+ * If set, then the device will wait for the event it sent to be acknowledged
+ * by the host. This option is only relevant for event trap IDs.
+ * Access: RW
+ *
+ * Note: Currently not supported by firmware.
+ */
+MLXSW_ITEM32(reg, hpkt, ack, 0x00, 24, 1);
+
+enum mlxsw_reg_hpkt_action {
+       MLXSW_REG_HPKT_ACTION_FORWARD,
+       MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU,
+       MLXSW_REG_HPKT_ACTION_MIRROR_TO_CPU,
+       MLXSW_REG_HPKT_ACTION_DISCARD,
+       MLXSW_REG_HPKT_ACTION_SOFT_DISCARD,
+       MLXSW_REG_HPKT_ACTION_TRAP_AND_SOFT_DISCARD,
+};
+
+/* reg_hpkt_action
+ * Action to perform on packet when trapped.
+ * 0 - No action. Forward to CPU based on switching rules.
+ * 1 - Trap to CPU (CPU receives sole copy).
+ * 2 - Mirror to CPU (CPU receives a replica of the packet).
+ * 3 - Discard.
+ * 4 - Soft discard (allow other traps to act on the packet).
+ * 5 - Trap and soft discard (allow other traps to overwrite this trap).
+ * Access: RW
+ *
+ * Note: Must be set to 0 (forward) for event trap IDs, as they are already
+ * addressed to the CPU.
+ */
+MLXSW_ITEM32(reg, hpkt, action, 0x00, 20, 3);
+
+/* reg_hpkt_trap_group
+ * Trap group to associate the trap with.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, hpkt, trap_group, 0x00, 12, 6);
+
+/* reg_hpkt_trap_id
+ * Trap ID.
+ * Access: Index
+ *
+ * Note: A trap ID can only be associated with a single trap group. The device
+ * will associate the trap ID with the last trap group configured.
+ */
+MLXSW_ITEM32(reg, hpkt, trap_id, 0x00, 0, 9);
+
+enum {
+       MLXSW_REG_HPKT_CTRL_PACKET_DEFAULT,
+       MLXSW_REG_HPKT_CTRL_PACKET_NO_BUFFER,
+       MLXSW_REG_HPKT_CTRL_PACKET_USE_BUFFER,
+};
+
+/* reg_hpkt_ctrl
+ * Configure dedicated buffer resources for control packets.
+ * 0 - Keep factory defaults.
+ * 1 - Do not use control buffer for this trap ID.
+ * 2 - Use control buffer for this trap ID.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, hpkt, ctrl, 0x04, 16, 2);
+
+static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action,
+                                      u8 trap_group, u16 trap_id)
+{
+       MLXSW_REG_ZERO(hpkt, payload);
+       mlxsw_reg_hpkt_ack_set(payload, MLXSW_REG_HPKT_ACK_NOT_REQUIRED);
+       mlxsw_reg_hpkt_action_set(payload, action);
+       mlxsw_reg_hpkt_trap_group_set(payload, trap_group);
+       mlxsw_reg_hpkt_trap_id_set(payload, trap_id);
+       mlxsw_reg_hpkt_ctrl_set(payload, MLXSW_REG_HPKT_CTRL_PACKET_DEFAULT);
+}
+
+static inline const char *mlxsw_reg_id_str(u16 reg_id)
+{
+       switch (reg_id) {
+       case MLXSW_REG_SGCR_ID:
+               return "SGCR";
+       case MLXSW_REG_SPAD_ID:
+               return "SPAD";
+       case MLXSW_REG_SMID_ID:
+               return "SMID";
+       case MLXSW_REG_SPMS_ID:
+               return "SPMS";
+       case MLXSW_REG_SFGC_ID:
+               return "SFGC";
+       case MLXSW_REG_SFTR_ID:
+               return "SFTR";
+       case MLXSW_REG_SPMLR_ID:
+               return "SPMLR";
+       case MLXSW_REG_PMLP_ID:
+               return "PMLP";
+       case MLXSW_REG_PMTU_ID:
+               return "PMTU";
+       case MLXSW_REG_PTYS_ID:
+               return "PTYS";
+       case MLXSW_REG_PPAD_ID:
+               return "PPAD";
+       case MLXSW_REG_PAOS_ID:
+               return "PAOS";
+       case MLXSW_REG_PPCNT_ID:
+               return "PPCNT";
+       case MLXSW_REG_PSPA_ID:
+               return "PSPA";
+       case MLXSW_REG_HTGT_ID:
+               return "HTGT";
+       case MLXSW_REG_HPKT_ID:
+               return "HPKT";
+       default:
+               return "*UNKNOWN*";
+       }
+}
+
+/* PUDE - Port Up / Down Event
+ * ---------------------------
+ * Reports the operational state change of a port.
+ */
+#define MLXSW_REG_PUDE_LEN 0x10
+
+/* reg_pude_swid
+ * Switch partition ID with which to associate the port.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, pude, swid, 0x00, 24, 8);
+
+/* reg_pude_local_port
+ * Local port number.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, pude, local_port, 0x00, 16, 8);
+
+/* reg_pude_admin_status
+ * Port administrative state (the desired state).
+ * 1 - Up.
+ * 2 - Down.
+ * 3 - Up once. This means that in case of link failure, the port won't go
+ *     into polling mode, but will wait to be re-enabled by software.
+ * 4 - Disabled by system. Can only be set by hardware.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, pude, admin_status, 0x00, 8, 4);
+
+/* reg_pude_oper_status
+ * Port operatioanl state.
+ * 1 - Up.
+ * 2 - Down.
+ * 3 - Down by port failure. This means that the device will not let the
+ *     port up again until explicitly specified by software.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, pude, oper_status, 0x00, 0, 4);
+
+#endif