static void megasas_mmio_write(void *opaque, hwaddr addr,

                               uint64_t val, unsigned size)

{

    MegasasState *s = opaque;

    PCIDevice *pci_dev = PCI_DEVICE(s);

    uint64_t frame_addr;

    uint32_t frame_count;

    int i;



    switch (addr) {

    case MFI_IDB:

        trace_megasas_mmio_writel("MFI_IDB", val);

        if (val & MFI_FWINIT_ABORT) {

            /* Abort all pending cmds */

            for (i = 0; i < s->fw_cmds; i++) {

                megasas_abort_command(&s->frames[i]);

            }

        }

        if (val & MFI_FWINIT_READY) {

            /* move to FW READY */

            megasas_soft_reset(s);

        }

        if (val & MFI_FWINIT_MFIMODE) {

            /* discard MFIs */

        }

        if (val & MFI_FWINIT_STOP_ADP) {

            /* Terminal error, stop processing */

            s->fw_state = MFI_FWSTATE_FAULT;

        }

        break;

    case MFI_OMSK:

        trace_megasas_mmio_writel("MFI_OMSK", val);

        s->intr_mask = val;

        if (!megasas_intr_enabled(s) &&

            !msi_enabled(pci_dev) &&

            !msix_enabled(pci_dev)) {

            trace_megasas_irq_lower();

            pci_irq_deassert(pci_dev);

        }

        if (megasas_intr_enabled(s)) {

            if (msix_enabled(pci_dev)) {

                trace_megasas_msix_enabled(0);

            } else if (msi_enabled(pci_dev)) {

                trace_megasas_msi_enabled(0);

            } else {

                trace_megasas_intr_enabled();

            }

        } else {

            trace_megasas_intr_disabled();

            megasas_soft_reset(s);

        }

        break;

    case MFI_ODCR0:

        trace_megasas_mmio_writel("MFI_ODCR0", val);

        s->doorbell = 0;

        if (megasas_intr_enabled(s)) {

            if (!msix_enabled(pci_dev) && !msi_enabled(pci_dev)) {

                trace_megasas_irq_lower();

                pci_irq_deassert(pci_dev);

            }

        }

        break;

    case MFI_IQPH:

        trace_megasas_mmio_writel("MFI_IQPH", val);

        /* Received high 32 bits of a 64 bit MFI frame address */

        s->frame_hi = val;

        break;

    case MFI_IQPL:

        trace_megasas_mmio_writel("MFI_IQPL", val);

        /* Received low 32 bits of a 64 bit MFI frame address */

        /* Fallthrough */

    case MFI_IQP:

        if (addr == MFI_IQP) {

            trace_megasas_mmio_writel("MFI_IQP", val);

            /* Received 64 bit MFI frame address */

            s->frame_hi = 0;

        }

        frame_addr = (val & ~0x1F);

        /* Add possible 64 bit offset */

        frame_addr |= ((uint64_t)s->frame_hi << 32);

        s->frame_hi = 0;

        frame_count = (val >> 1) & 0xF;

        megasas_handle_frame(s, frame_addr, frame_count);

        break;

    case MFI_SEQ:

        trace_megasas_mmio_writel("MFI_SEQ", val);

        /* Magic sequence to start ADP reset */

        if (adp_reset_seq[s->adp_reset] == val) {

            s->adp_reset++;

        } else {

            s->adp_reset = 0;

            s->diag = 0;

        }

        if (s->adp_reset == 6) {

            s->diag = MFI_DIAG_WRITE_ENABLE;

        }

        break;

    case MFI_DIAG:

        trace_megasas_mmio_writel("MFI_DIAG", val);

        /* ADP reset */

        if ((s->diag & MFI_DIAG_WRITE_ENABLE) &&

            (val & MFI_DIAG_RESET_ADP)) {

            s->diag |= MFI_DIAG_RESET_ADP;

            megasas_soft_reset(s);

            s->adp_reset = 0;

            s->diag = 0;

        }

        break;

    default:

        trace_megasas_mmio_invalid_writel(addr, val);

        break;

    }

}
