#define PCIE_CSR_ASPM_L1_EN(x) BIT(1 + (x) * 8)
 
 /* PCIe V2 per-port registers */
+#define PCIE_MSI_VECTOR                0x0c0
 #define PCIE_INT_MASK          0x420
 #define INTX_MASK              GENMASK(19, 16)
 #define INTX_SHIFT             16
 #define INTX_NUM               4
 #define PCIE_INT_STATUS                0x424
+#define MSI_STATUS             BIT(23)
+#define PCIE_IMSI_STATUS       0x42c
+#define PCIE_IMSI_ADDR         0x430
+#define MSI_MASK               BIT(23)
+#define MTK_MSI_IRQS_NUM       32
 
 #define PCIE_AHB_TRANS_BASE0_L 0x438
 #define PCIE_AHB_TRANS_BASE0_H 0x43c
 
 /**
  * struct mtk_pcie_soc - differentiate between host generations
+ * @has_msi: whether this host supports MSI interrupts or not
  * @ops: pointer to configuration access functions
  * @startup: pointer to controller setting functions
  * @setup_irq: pointer to initialize IRQ functions
  */
 struct mtk_pcie_soc {
+       bool has_msi;
        struct pci_ops *ops;
        int (*startup)(struct mtk_pcie_port *port);
        int (*setup_irq)(struct mtk_pcie_port *port, struct device_node *node);
  * @lane: lane count
  * @slot: port slot
  * @irq_domain: legacy INTx IRQ domain
+ * @msi_domain: MSI IRQ domain
+ * @msi_irq_in_use: bit map for assigned MSI IRQ
  */
 struct mtk_pcie_port {
        void __iomem *base;
        u32 lane;
        u32 slot;
        struct irq_domain *irq_domain;
+       struct irq_domain *msi_domain;
+       DECLARE_BITMAP(msi_irq_in_use, MTK_MSI_IRQS_NUM);
 };
 
 /**
        return 0;
 }
 
+static int mtk_pcie_msi_alloc(struct mtk_pcie_port *port)
+{
+       int msi;
+
+       msi = find_first_zero_bit(port->msi_irq_in_use, MTK_MSI_IRQS_NUM);
+       if (msi < MTK_MSI_IRQS_NUM)
+               set_bit(msi, port->msi_irq_in_use);
+       else
+               return -ENOSPC;
+
+       return msi;
+}
+
+static void mtk_pcie_msi_free(struct mtk_pcie_port *port, unsigned long hwirq)
+{
+       clear_bit(hwirq, port->msi_irq_in_use);
+}
+
+static int mtk_pcie_msi_setup_irq(struct msi_controller *chip,
+                                 struct pci_dev *pdev, struct msi_desc *desc)
+{
+       struct mtk_pcie_port *port;
+       struct msi_msg msg;
+       unsigned int irq;
+       int hwirq;
+       phys_addr_t msg_addr;
+
+       port = mtk_pcie_find_port(pdev->bus, pdev->devfn);
+       if (!port)
+               return -EINVAL;
+
+       hwirq = mtk_pcie_msi_alloc(port);
+       if (hwirq < 0)
+               return hwirq;
+
+       irq = irq_create_mapping(port->msi_domain, hwirq);
+       if (!irq) {
+               mtk_pcie_msi_free(port, hwirq);
+               return -EINVAL;
+       }
+
+       chip->dev = &pdev->dev;
+
+       irq_set_msi_desc(irq, desc);
+
+       /* MT2712/MT7622 only support 32-bit MSI addresses */
+       msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR);
+       msg.address_hi = 0;
+       msg.address_lo = lower_32_bits(msg_addr);
+       msg.data = hwirq;
+
+       pci_write_msi_msg(irq, &msg);
+
+       return 0;
+}
+
+static void mtk_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
+{
+       struct pci_dev *pdev = to_pci_dev(chip->dev);
+       struct irq_data *d = irq_get_irq_data(irq);
+       irq_hw_number_t hwirq = irqd_to_hwirq(d);
+       struct mtk_pcie_port *port;
+
+       port = mtk_pcie_find_port(pdev->bus, pdev->devfn);
+       if (!port)
+               return;
+
+       irq_dispose_mapping(irq);
+       mtk_pcie_msi_free(port, hwirq);
+}
+
+static struct msi_controller mtk_pcie_msi_chip = {
+       .setup_irq = mtk_pcie_msi_setup_irq,
+       .teardown_irq = mtk_msi_teardown_irq,
+};
+
+static struct irq_chip mtk_msi_irq_chip = {
+       .name = "MTK PCIe MSI",
+       .irq_enable = pci_msi_unmask_irq,
+       .irq_disable = pci_msi_mask_irq,
+       .irq_mask = pci_msi_mask_irq,
+       .irq_unmask = pci_msi_unmask_irq,
+};
+
+static int mtk_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
+                           irq_hw_number_t hwirq)
+{
+       irq_set_chip_and_handler(irq, &mtk_msi_irq_chip, handle_simple_irq);
+       irq_set_chip_data(irq, domain->host_data);
+
+       return 0;
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+       .map = mtk_pcie_msi_map,
+};
+
+static void mtk_pcie_enable_msi(struct mtk_pcie_port *port)
+{
+       u32 val;
+       phys_addr_t msg_addr;
+
+       msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR);
+       val = lower_32_bits(msg_addr);
+       writel(val, port->base + PCIE_IMSI_ADDR);
+
+       val = readl(port->base + PCIE_INT_MASK);
+       val &= ~MSI_MASK;
+       writel(val, port->base + PCIE_INT_MASK);
+}
+
 static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
                             irq_hw_number_t hwirq)
 {
                return -ENODEV;
        }
 
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               port->msi_domain = irq_domain_add_linear(node, MTK_MSI_IRQS_NUM,
+                                                        &msi_domain_ops,
+                                                        &mtk_pcie_msi_chip);
+               if (!port->msi_domain) {
+                       dev_err(dev, "failed to create MSI IRQ domain\n");
+                       return -ENODEV;
+               }
+               mtk_pcie_enable_msi(port);
+       }
+
        return 0;
 }
 
                }
        }
 
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               while ((status = readl(port->base + PCIE_INT_STATUS)) & MSI_STATUS) {
+                       unsigned long imsi_status;
+
+                       while ((imsi_status = readl(port->base + PCIE_IMSI_STATUS))) {
+                               for_each_set_bit(bit, &imsi_status, MTK_MSI_IRQS_NUM) {
+                                       /* Clear the MSI */
+                                       writel(1 << bit, port->base + PCIE_IMSI_STATUS);
+                                       virq = irq_find_mapping(port->msi_domain, bit);
+                                       generic_handle_irq(virq);
+                               }
+                       }
+                       /* Clear MSI interrupt status */
+                       writel(MSI_STATUS, port->base + PCIE_INT_STATUS);
+               }
+       }
+
        return IRQ_HANDLED;
 }
 
 
        err = mtk_pcie_init_irq_domain(port, node);
        if (err) {
-               dev_err(dev, "failed to init PCIe legacy IRQ domain\n");
+               dev_err(dev, "failed to init PCIe IRQ domain\n");
                return err;
        }
 
        host->map_irq = of_irq_parse_and_map_pci;
        host->swizzle_irq = pci_common_swizzle;
        host->sysdata = pcie;
+       if (IS_ENABLED(CONFIG_PCI_MSI) && pcie->soc->has_msi)
+               host->msi = &mtk_pcie_msi_chip;
 
        err = pci_scan_root_bus_bridge(host);
        if (err < 0)
 };
 
 static const struct mtk_pcie_soc mtk_pcie_soc_v2 = {
+       .has_msi = true,
        .ops = &mtk_pcie_ops_v2,
        .startup = mtk_pcie_startup_port_v2,
        .setup_irq = mtk_pcie_setup_irq,