#define MCDI_RPC_TIMEOUT       (10 * HZ)
 
-#define MCDI_PDU(efx)                                                  \
-       (efx_port_num(efx) ? MC_SMEM_P1_PDU_OFST : MC_SMEM_P0_PDU_OFST)
-#define MCDI_DOORBELL(efx)                                             \
-       (efx_port_num(efx) ? MC_SMEM_P1_DOORBELL_OFST : MC_SMEM_P0_DOORBELL_OFST)
-#define MCDI_STATUS(efx)                                               \
-       (efx_port_num(efx) ? MC_SMEM_P1_STATUS_OFST : MC_SMEM_P0_STATUS_OFST)
-
 /* A reboot/assertion causes the MCDI status word to be set after the
  * command word is set or a REBOOT event is sent. If we notice a reboot
  * via these mechanisms then wait 10ms for the status word to be set. */
 
 static inline struct efx_mcdi_iface *efx_mcdi(struct efx_nic *efx)
 {
-       struct siena_nic_data *nic_data;
-       EFX_BUG_ON_PARANOID(efx_nic_rev(efx) < EFX_REV_SIENA_A0);
-       nic_data = efx->nic_data;
-       return &nic_data->mcdi;
+       EFX_BUG_ON_PARANOID(!efx->mcdi);
+       return &efx->mcdi->iface;
 }
 
 int efx_mcdi_init(struct efx_nic *efx)
 {
        struct efx_mcdi_iface *mcdi;
 
+       efx->mcdi = kzalloc(sizeof(*efx->mcdi), GFP_KERNEL);
+       if (!efx->mcdi)
+               return -ENOMEM;
+
        mcdi = efx_mcdi(efx);
        init_waitqueue_head(&mcdi->wq);
        spin_lock_init(&mcdi->iface_lock);
        return efx_mcdi_handle_assertion(efx);
 }
 
+void efx_mcdi_fini(struct efx_nic *efx)
+{
+       BUG_ON(efx->mcdi &&
+              atomic_read(&efx->mcdi->iface.state) != MCDI_STATE_QUIESCENT);
+       kfree(efx->mcdi);
+}
+
 static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
                            const efx_dword_t *inbuf, size_t inlen)
 {
        struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
-       unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
-       unsigned doorbell = FR_CZ_MC_TREG_SMEM + MCDI_DOORBELL(efx);
-       unsigned int i;
        efx_dword_t hdr;
        u32 xflags, seqno;
-       unsigned int inlen_dw = DIV_ROUND_UP(inlen, 4);
 
        BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT);
        BUG_ON(inlen > MCDI_CTL_SDU_LEN_MAX_V1);
                             MCDI_HEADER_SEQ, seqno,
                             MCDI_HEADER_XFLAGS, xflags);
 
-       efx_writed(efx, &hdr, pdu);
-
-       for (i = 0; i < inlen_dw; i++)
-               efx_writed(efx, &inbuf[i], pdu + 4 + 4 * i);
-
-       /* Ensure the payload is written out before the header */
-       wmb();
-
-       /* ring the doorbell with a distinctive value */
-       _efx_writed(efx, (__force __le32) 0x45789abc, doorbell);
+       efx->type->mcdi_request(efx, &hdr, 4, inbuf, inlen);
 }
 
 static void
 efx_mcdi_copyout(struct efx_nic *efx, efx_dword_t *outbuf, size_t outlen)
 {
        struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
-       unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
-       unsigned int outlen_dw = DIV_ROUND_UP(outlen, 4);
-       int i;
 
        BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT);
        BUG_ON(outlen > MCDI_CTL_SDU_LEN_MAX_V1);
 
-       for (i = 0; i < outlen_dw; i++)
-               efx_readd(efx, &outbuf[i], pdu + 4 + 4 * i);
+       efx->type->mcdi_read_response(efx, outbuf, 4, outlen);
 }
 
 static int efx_mcdi_poll(struct efx_nic *efx)
        struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
        unsigned long time, finish;
        unsigned int respseq, respcmd, error;
-       unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
        unsigned int rc, spins;
        efx_dword_t reg;
 
                time = jiffies;
 
                rmb();
-               efx_readd(efx, ®, pdu);
-
-               /* All 1's indicates that shared memory is in reset (and is
-                * not a valid header). Wait for it to come out reset before
-                * completing the command */
-               if (EFX_DWORD_FIELD(reg, EFX_DWORD_0) != 0xffffffff &&
-                   EFX_DWORD_FIELD(reg, MCDI_HEADER_RESPONSE))
+               if (efx->type->mcdi_poll_response(efx))
                        break;
 
                if (time_after(time, finish))
                        return -ETIMEDOUT;
        }
 
+       efx->type->mcdi_read_response(efx, ®, 0, 4);
        mcdi->resplen = EFX_DWORD_FIELD(reg, MCDI_HEADER_DATALEN);
        respseq = EFX_DWORD_FIELD(reg, MCDI_HEADER_SEQ);
        respcmd = EFX_DWORD_FIELD(reg, MCDI_HEADER_CODE);
                          respseq, mcdi->seqno);
                rc = EIO;
        } else if (error) {
-               efx_readd(efx, ®, pdu + 4);
+               efx->type->mcdi_read_response(efx, ®, 4, 4);
                switch (EFX_DWORD_FIELD(reg, EFX_DWORD_0)) {
 #define TRANSLATE_ERROR(name)                                  \
                case MC_CMD_ERR_ ## name:                       \
  */
 int efx_mcdi_poll_reboot(struct efx_nic *efx)
 {
-       unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_STATUS(efx);
-       efx_dword_t reg;
-       uint32_t value;
-
-       if (efx_nic_rev(efx) < EFX_REV_SIENA_A0)
-               return false;
+       int rc;
 
-       efx_readd(efx, ®, addr);
-       value = EFX_DWORD_FIELD(reg, EFX_DWORD_0);
+       if (!efx->mcdi)
+               return 0;
 
-       if (value == 0)
+       rc = efx->type->mcdi_poll_reboot(efx);
+       if (!rc)
                return 0;
 
        /* MAC statistics have been cleared on the NIC; clear our copy
         */
        memset(&efx->mac_stats, 0, sizeof(efx->mac_stats));
 
-       EFX_ZERO_DWORD(reg);
-       efx_writed(efx, ®, addr);
-
-       if (value == MC_STATUS_DWORD_ASSERT)
-               return -EINTR;
-       else
-               return -EIO;
+       return rc;
 }
 
 static void efx_mcdi_acquire(struct efx_mcdi_iface *mcdi)
 {
        struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
 
-       BUG_ON(efx_nic_rev(efx) < EFX_REV_SIENA_A0);
-
        efx_mcdi_acquire(mcdi);
 
        /* Serialise with efx_mcdi_ev_cpl() and efx_mcdi_ev_death() */
        struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
        int rc;
 
-       BUG_ON(efx_nic_rev(efx) < EFX_REV_SIENA_A0);
-
        if (mcdi->mode == MCDI_MODE_POLL)
                rc = efx_mcdi_poll(efx);
        else
 {
        struct efx_mcdi_iface *mcdi;
 
-       if (efx_nic_rev(efx) < EFX_REV_SIENA_A0)
+       if (!efx->mcdi)
                return;
 
        mcdi = efx_mcdi(efx);
 {
        struct efx_mcdi_iface *mcdi;
 
-       if (efx_nic_rev(efx) < EFX_REV_SIENA_A0)
+       if (!efx->mcdi)
                return;
 
        mcdi = efx_mcdi(efx);
 
        unsigned int n_attrs;
 };
 
+/**
+ * struct efx_mcdi_data - extra state for NICs that implement MCDI
+ * @iface: Interface/protocol state
+ * @hwmon: Hardware monitor state
+ */
+struct efx_mcdi_data {
+       struct efx_mcdi_iface iface;
+#ifdef CONFIG_SFC_MCDI_MON
+       struct efx_mcdi_mon hwmon;
+#endif
+};
+
+#ifdef CONFIG_SFC_MCDI_MON
+static inline struct efx_mcdi_mon *efx_mcdi_mon(struct efx_nic *efx)
+{
+       EFX_BUG_ON_PARANOID(!efx->mcdi);
+       return &efx->mcdi->hwmon;
+}
+#endif
+
 extern int efx_mcdi_init(struct efx_nic *efx);
+extern void efx_mcdi_fini(struct efx_nic *efx);
 
 extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
                        const efx_dword_t *inbuf, size_t inlen,
 
  * @selftest_work: Work item for asynchronous self-test
  * @mtd_list: List of MTDs attached to the NIC
  * @nic_data: Hardware dependent state
+ * @mcdi: Management-Controller-to-Driver Interface state
  * @mac_lock: MAC access lock. Protects @port_enabled, @phy_mode,
  *     efx_monitor() and efx_reconfigure_port()
  * @port_enabled: Port enabled indicator.
 #endif
 
        void *nic_data;
+       struct efx_mcdi_data *mcdi;
 
        struct mutex mac_lock;
        struct work_struct mac_work;
  * @test_chip: Test registers.  Should use efx_nic_test_registers(), and is
  *     expected to reset the NIC.
  * @test_nvram: Test validity of NVRAM contents
+ * @mcdi_request: Send an MCDI request with the given header and SDU.
+ *     The SDU length may be any value from 0 up to the protocol-
+ *     defined maximum, but its buffer will be padded to a multiple
+ *     of 4 bytes.
+ * @mcdi_poll_response: Test whether an MCDI response is available.
+ * @mcdi_read_response: Read the MCDI response PDU.  The offset will
+ *     be a multiple of 4.  The length may not be, but the buffer
+ *     will be padded so it is safe to round up.
+ * @mcdi_poll_reboot: Test whether the MCDI has rebooted.  If so,
+ *     return an appropriate error code for aborting any current
+ *     request; otherwise return 0.
  * @revision: Hardware architecture revision
  * @mem_map_size: Memory BAR mapped size
  * @txd_ptr_tbl_base: TX descriptor ring base address
        void (*resume_wol)(struct efx_nic *efx);
        int (*test_chip)(struct efx_nic *efx, struct efx_self_tests *tests);
        int (*test_nvram)(struct efx_nic *efx);
+       void (*mcdi_request)(struct efx_nic *efx,
+                            const efx_dword_t *hdr, size_t hdr_len,
+                            const efx_dword_t *sdu, size_t sdu_len);
+       bool (*mcdi_poll_response)(struct efx_nic *efx);
+       void (*mcdi_read_response)(struct efx_nic *efx, efx_dword_t *pdu,
+                                  size_t pdu_offset, size_t pdu_len);
+       int (*mcdi_poll_reboot)(struct efx_nic *efx);
 
        int revision;
        unsigned int mem_map_size;
 
 
 /**
  * struct siena_nic_data - Siena NIC state
- * @mcdi: Management-Controller-to-Driver Interface
  * @wol_filter_id: Wake-on-LAN packet filter id
- * @hwmon: Hardware monitor state
  */
 struct siena_nic_data {
-       struct efx_mcdi_iface mcdi;
        int wol_filter_id;
-#ifdef CONFIG_SFC_MCDI_MON
-       struct efx_mcdi_mon hwmon;
-#endif
 };
 
-#ifdef CONFIG_SFC_MCDI_MON
-static inline struct efx_mcdi_mon *efx_mcdi_mon(struct efx_nic *efx)
-{
-       struct siena_nic_data *nic_data;
-       EFX_BUG_ON_PARANOID(efx_nic_rev(efx) < EFX_REV_SIENA_A0);
-       nic_data = efx->nic_data;
-       return &nic_data->hwmon;
-}
-#endif
-
 /*
  * On the SFC9000 family each port is associated with 1 PCI physical
  * function (PF) handled by sfc and a configurable number of virtual
 
 fail3:
        efx_mcdi_drv_attach(efx, false, NULL);
 fail2:
+       efx_mcdi_fini(efx);
 fail1:
        kfree(efx->nic_data);
        return rc;
        /* Tear down the private nic state */
        kfree(efx->nic_data);
        efx->nic_data = NULL;
+
+       efx_mcdi_fini(efx);
 }
 
 static int siena_try_update_nic_stats(struct efx_nic *efx)
        }
 }
 
+/**************************************************************************
+ *
+ * MCDI
+ *
+ **************************************************************************
+ */
+
+#define MCDI_PDU(efx)                                                  \
+       (efx_port_num(efx) ? MC_SMEM_P1_PDU_OFST : MC_SMEM_P0_PDU_OFST)
+#define MCDI_DOORBELL(efx)                                             \
+       (efx_port_num(efx) ? MC_SMEM_P1_DOORBELL_OFST : MC_SMEM_P0_DOORBELL_OFST)
+#define MCDI_STATUS(efx)                                               \
+       (efx_port_num(efx) ? MC_SMEM_P1_STATUS_OFST : MC_SMEM_P0_STATUS_OFST)
+
+static void siena_mcdi_request(struct efx_nic *efx,
+                              const efx_dword_t *hdr, size_t hdr_len,
+                              const efx_dword_t *sdu, size_t sdu_len)
+{
+       unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
+       unsigned doorbell = FR_CZ_MC_TREG_SMEM + MCDI_DOORBELL(efx);
+       unsigned int i;
+       unsigned int inlen_dw = DIV_ROUND_UP(sdu_len, 4);
+
+       EFX_BUG_ON_PARANOID(hdr_len != 4);
+
+       efx_writed(efx, hdr, pdu);
+
+       for (i = 0; i < inlen_dw; i++)
+               efx_writed(efx, &sdu[i], pdu + hdr_len + 4 * i);
+
+       /* Ensure the request is written out before the doorbell */
+       wmb();
+
+       /* ring the doorbell with a distinctive value */
+       _efx_writed(efx, (__force __le32) 0x45789abc, doorbell);
+}
+
+static bool siena_mcdi_poll_response(struct efx_nic *efx)
+{
+       unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
+       efx_dword_t hdr;
+
+       efx_readd(efx, &hdr, pdu);
+
+       /* All 1's indicates that shared memory is in reset (and is
+        * not a valid hdr). Wait for it to come out reset before
+        * completing the command
+        */
+       return EFX_DWORD_FIELD(hdr, EFX_DWORD_0) != 0xffffffff &&
+               EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE);
+}
+
+static void siena_mcdi_read_response(struct efx_nic *efx, efx_dword_t *outbuf,
+                                    size_t offset, size_t outlen)
+{
+       unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
+       unsigned int outlen_dw = DIV_ROUND_UP(outlen, 4);
+       int i;
+
+       for (i = 0; i < outlen_dw; i++)
+               efx_readd(efx, &outbuf[i], pdu + offset + 4 * i);
+}
+
+static int siena_mcdi_poll_reboot(struct efx_nic *efx)
+{
+       unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_STATUS(efx);
+       efx_dword_t reg;
+       u32 value;
+
+       efx_readd(efx, ®, addr);
+       value = EFX_DWORD_FIELD(reg, EFX_DWORD_0);
+
+       if (value == 0)
+               return 0;
+
+       EFX_ZERO_DWORD(reg);
+       efx_writed(efx, ®, addr);
+
+       if (value == MC_STATUS_DWORD_ASSERT)
+               return -EINTR;
+       else
+               return -EIO;
+}
 
 /**************************************************************************
  *
        .resume_wol = siena_init_wol,
        .test_chip = siena_test_chip,
        .test_nvram = efx_mcdi_nvram_test_all,
+       .mcdi_request = siena_mcdi_request,
+       .mcdi_poll_response = siena_mcdi_poll_response,
+       .mcdi_read_response = siena_mcdi_read_response,
+       .mcdi_poll_reboot = siena_mcdi_poll_reboot,
 
        .revision = EFX_REV_SIENA_A0,
        .mem_map_size = (FR_CZ_MC_TREG_SMEM +