[][Add direct MSI support for MT7986]
[Description]
Add direct MSI support for MT7986.
[Release-log]
N/A
Change-Id: Iec6120a126886ab9ffa89b1e3f126066477bc570
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/4946737
diff --git a/target/linux/mediatek/files-5.4/drivers/pci/controller/pcie-mediatek-gen3.c b/target/linux/mediatek/files-5.4/drivers/pci/controller/pcie-mediatek-gen3.c
index 0654b4c..0e01fde 100644
--- a/target/linux/mediatek/files-5.4/drivers/pci/controller/pcie-mediatek-gen3.c
+++ b/target/linux/mediatek/files-5.4/drivers/pci/controller/pcie-mediatek-gen3.c
@@ -73,6 +73,7 @@
#define PCIE_MSI_SET_OFFSET 0x10
#define PCIE_MSI_SET_STATUS_OFFSET 0x04
#define PCIE_MSI_SET_ENABLE_OFFSET 0x08
+#define PCIE_MSI_SET_GRP1_ENABLE_OFFSET 0x0c
#define PCIE_MSI_SET_ADDR_HI_BASE 0xc80
#define PCIE_MSI_SET_ADDR_HI_OFFSET 0x04
@@ -141,6 +142,8 @@
int num_clks;
int irq;
+ int direct_msi_enable;
+ int direct_msi[PCIE_MSI_IRQS_PER_SET];
u32 saved_irq_state;
raw_spinlock_t irq_lock;
struct irq_domain *intx_domain;
@@ -269,9 +272,11 @@
val |= PCIE_MSI_SET_ENABLE;
writel_relaxed(val, port->base + PCIE_MSI_SET_ENABLE_REG);
- val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG);
- val |= PCIE_MSI_ENABLE;
- writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG);
+ if (!port->direct_msi_enable) {
+ val = readl_relaxed(port->base + PCIE_INT_ENABLE_REG);
+ val |= PCIE_MSI_ENABLE;
+ writel_relaxed(val, port->base + PCIE_INT_ENABLE_REG);
+ }
}
static int mtk_pcie_startup_port(struct mtk_pcie_port *port)
@@ -363,6 +368,29 @@
return 0;
}
+static int mtk_pcie_set_msi_affinity(struct irq_data *data,
+ const struct cpumask *mask, bool force)
+{
+ struct mtk_pcie_port *port = data->domain->host_data;
+ struct irq_data *port_data;
+ struct irq_chip *port_chip;
+ int msi_bit, irq, ret;
+
+ msi_bit = data->hwirq % PCIE_MSI_IRQS_PER_SET;
+ irq = port->direct_msi[msi_bit];
+
+ port_data = irq_get_irq_data(irq);
+ port_chip = irq_data_get_irq_chip(port_data);
+ if (!port_chip || !port_chip->irq_set_affinity)
+ return -EINVAL;
+
+ ret = port_chip->irq_set_affinity(port_data, mask, force);
+
+ irq_data_update_effective_affinity(data, mask);
+
+ return ret;
+}
+
static int mtk_pcie_set_affinity(struct irq_data *data,
const struct cpumask *mask, bool force)
{
@@ -429,9 +457,17 @@
hwirq = data->hwirq % PCIE_MSI_IRQS_PER_SET;
raw_spin_lock_irqsave(&port->irq_lock, flags);
- val = readl_relaxed(msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET);
- val &= ~BIT(hwirq);
- writel_relaxed(val, msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET);
+ if (port->direct_msi_enable) {
+ val = readl_relaxed(msi_set->base +
+ PCIE_MSI_SET_GRP1_ENABLE_OFFSET);
+ val &= ~BIT(hwirq);
+ writel_relaxed(val, msi_set->base +
+ PCIE_MSI_SET_GRP1_ENABLE_OFFSET);
+ } else {
+ val = readl_relaxed(msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET);
+ val &= ~BIT(hwirq);
+ writel_relaxed(val, msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET);
+ }
raw_spin_unlock_irqrestore(&port->irq_lock, flags);
}
@@ -445,9 +481,17 @@
hwirq = data->hwirq % PCIE_MSI_IRQS_PER_SET;
raw_spin_lock_irqsave(&port->irq_lock, flags);
- val = readl_relaxed(msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET);
- val |= BIT(hwirq);
- writel_relaxed(val, msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET);
+ if (port->direct_msi_enable) {
+ val = readl_relaxed(msi_set->base +
+ PCIE_MSI_SET_GRP1_ENABLE_OFFSET);
+ val |= BIT(hwirq);
+ writel_relaxed(val, msi_set->base +
+ PCIE_MSI_SET_GRP1_ENABLE_OFFSET);
+ } else {
+ val = readl_relaxed(msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET);
+ val |= BIT(hwirq);
+ writel_relaxed(val, msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET);
+ }
raw_spin_unlock_irqrestore(&port->irq_lock, flags);
}
@@ -616,6 +660,11 @@
goto err_msi_domain;
}
+ if (of_find_property(node, "direct_msi", NULL))
+ port->direct_msi_enable = true;
+ else
+ port->direct_msi_enable = false;
+
return 0;
err_msi_domain:
@@ -695,11 +744,52 @@
chained_irq_exit(irqchip, desc);
}
+static void mtk_pcie_direct_msi_handler(struct irq_desc *desc)
+{
+ struct mtk_pcie_port *port = irq_desc_get_handler_data(desc);
+ struct irq_chip *irqchip = irq_desc_get_chip(desc);
+ unsigned long msi_enable, msi_status;
+ unsigned int virq;
+ irq_hw_number_t hwirq;
+ int i, msi_bit = -EINVAL;
+
+ for (i = 0; i < PCIE_MSI_IRQS_PER_SET; i++) {
+ if (port->direct_msi[i] == irq_desc_get_irq(desc)) {
+ msi_bit = i;
+ break;
+ }
+ }
+
+ if (msi_bit == -EINVAL)
+ return;
+
+ chained_irq_enter(irqchip, desc);
+
+ for (i = 0; i < PCIE_MSI_SET_NUM; i++) {
+ struct mtk_msi_set *msi_set = &port->msi_sets[i];
+
+ msi_status = readl_relaxed(msi_set->base +
+ PCIE_MSI_SET_STATUS_OFFSET);
+ msi_enable = readl_relaxed(msi_set->base +
+ PCIE_MSI_SET_GRP1_ENABLE_OFFSET);
+ msi_status &= msi_enable;
+ msi_status &= BIT(msi_bit);
+ if (!msi_status)
+ continue;
+
+ hwirq = msi_bit + i * PCIE_MSI_IRQS_PER_SET;
+ virq = irq_find_mapping(port->msi_bottom_domain, hwirq);
+ generic_handle_irq(virq);
+ }
+
+ chained_irq_exit(irqchip, desc);
+}
+
static int mtk_pcie_setup_irq(struct mtk_pcie_port *port)
{
struct device *dev = port->dev;
struct platform_device *pdev = to_platform_device(dev);
- int err;
+ int err, i;
err = mtk_pcie_init_irq_domains(port);
if (err)
@@ -711,6 +801,17 @@
irq_set_chained_handler_and_data(port->irq, mtk_pcie_irq_handler, port);
+ if (port->direct_msi_enable) {
+ mtk_msi_bottom_irq_chip.irq_set_affinity =
+ mtk_pcie_set_msi_affinity;
+
+ for (i = 0; i < PCIE_MSI_IRQS_PER_SET; i++) {
+ port->direct_msi[i] = platform_get_irq(pdev, i + 1);
+ irq_set_chained_handler_and_data(port->direct_msi[i],
+ mtk_pcie_direct_msi_handler, port);
+ }
+ }
+
return 0;
}
@@ -927,8 +1028,12 @@
for (i = 0; i < PCIE_MSI_SET_NUM; i++) {
struct mtk_msi_set *msi_set = &port->msi_sets[i];
- msi_set->saved_irq_state = readl_relaxed(msi_set->base +
- PCIE_MSI_SET_ENABLE_OFFSET);
+ if (port->direct_msi_enable)
+ msi_set->saved_irq_state = readl_relaxed(msi_set->base +
+ PCIE_MSI_SET_GRP1_ENABLE_OFFSET);
+ else
+ msi_set->saved_irq_state = readl_relaxed(msi_set->base +
+ PCIE_MSI_SET_ENABLE_OFFSET);
}
raw_spin_unlock(&port->irq_lock);
@@ -945,8 +1050,12 @@
for (i = 0; i < PCIE_MSI_SET_NUM; i++) {
struct mtk_msi_set *msi_set = &port->msi_sets[i];
- writel_relaxed(msi_set->saved_irq_state,
- msi_set->base + PCIE_MSI_SET_ENABLE_OFFSET);
+ if (port->direct_msi_enable)
+ writel_relaxed(msi_set->saved_irq_state, msi_set->base +
+ PCIE_MSI_SET_GRP1_ENABLE_OFFSET);
+ else
+ writel_relaxed(msi_set->saved_irq_state, msi_set->base +
+ PCIE_MSI_SET_ENABLE_OFFSET);
}
raw_spin_unlock(&port->irq_lock);