diff options
| author | Honghui Zhang <honghui.zhang@mediatek.com> | 2018-05-04 01:47:33 -0400 |
|---|---|---|
| committer | Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | 2018-05-21 09:43:45 -0400 |
| commit | 42fe2f91b4ebc07c815fb334ea1262e6dc23bf77 (patch) | |
| tree | 02144824061d7dce83d3fe2d945deac0b99c1cd9 | |
| parent | 101c92dc80c8947b92addb34c11ea7a47a1d2561 (diff) | |
PCI: mediatek: Implement chained IRQ handling setup
Implement irq_chip based solution for IRQs management in order to
comply with IRQ framework.
Signed-off-by: Honghui Zhang <honghui.zhang@mediatek.com>
[lorenzo.pieralisi@arm.com: updated commit log]
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: Ryder Lee <ryder.lee@mediatek.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
| -rw-r--r-- | drivers/pci/host/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/pci/host/pcie-mediatek.c | 206 |
2 files changed, 116 insertions, 91 deletions
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 0d0177ce436c..e2f323a52843 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig | |||
| @@ -194,6 +194,7 @@ config PCIE_MEDIATEK | |||
| 194 | depends on (ARM || ARM64) && (ARCH_MEDIATEK || COMPILE_TEST) | 194 | depends on (ARM || ARM64) && (ARCH_MEDIATEK || COMPILE_TEST) |
| 195 | depends on OF | 195 | depends on OF |
| 196 | depends on PCI | 196 | depends on PCI |
| 197 | depends on PCI_MSI_IRQ_DOMAIN | ||
| 197 | select PCIEPORTBUS | 198 | select PCIEPORTBUS |
| 198 | help | 199 | help |
| 199 | Say Y here if you want to enable PCIe controller support on | 200 | Say Y here if you want to enable PCIe controller support on |
diff --git a/drivers/pci/host/pcie-mediatek.c b/drivers/pci/host/pcie-mediatek.c index c3dc5499d1b7..dabf10869bcd 100644 --- a/drivers/pci/host/pcie-mediatek.c +++ b/drivers/pci/host/pcie-mediatek.c | |||
| @@ -11,8 +11,10 @@ | |||
| 11 | #include <linux/delay.h> | 11 | #include <linux/delay.h> |
| 12 | #include <linux/iopoll.h> | 12 | #include <linux/iopoll.h> |
| 13 | #include <linux/irq.h> | 13 | #include <linux/irq.h> |
| 14 | #include <linux/irqchip/chained_irq.h> | ||
| 14 | #include <linux/irqdomain.h> | 15 | #include <linux/irqdomain.h> |
| 15 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
| 17 | #include <linux/msi.h> | ||
| 16 | #include <linux/of_address.h> | 18 | #include <linux/of_address.h> |
| 17 | #include <linux/of_pci.h> | 19 | #include <linux/of_pci.h> |
| 18 | #include <linux/of_platform.h> | 20 | #include <linux/of_platform.h> |
| @@ -130,14 +132,12 @@ struct mtk_pcie_port; | |||
| 130 | /** | 132 | /** |
| 131 | * struct mtk_pcie_soc - differentiate between host generations | 133 | * struct mtk_pcie_soc - differentiate between host generations |
| 132 | * @need_fix_class_id: whether this host's class ID needed to be fixed or not | 134 | * @need_fix_class_id: whether this host's class ID needed to be fixed or not |
| 133 | * @has_msi: whether this host supports MSI interrupts or not | ||
| 134 | * @ops: pointer to configuration access functions | 135 | * @ops: pointer to configuration access functions |
| 135 | * @startup: pointer to controller setting functions | 136 | * @startup: pointer to controller setting functions |
| 136 | * @setup_irq: pointer to initialize IRQ functions | 137 | * @setup_irq: pointer to initialize IRQ functions |
| 137 | */ | 138 | */ |
| 138 | struct mtk_pcie_soc { | 139 | struct mtk_pcie_soc { |
| 139 | bool need_fix_class_id; | 140 | bool need_fix_class_id; |
| 140 | bool has_msi; | ||
| 141 | struct pci_ops *ops; | 141 | struct pci_ops *ops; |
| 142 | int (*startup)(struct mtk_pcie_port *port); | 142 | int (*startup)(struct mtk_pcie_port *port); |
| 143 | int (*setup_irq)(struct mtk_pcie_port *port, struct device_node *node); | 143 | int (*setup_irq)(struct mtk_pcie_port *port, struct device_node *node); |
| @@ -161,7 +161,9 @@ struct mtk_pcie_soc { | |||
| 161 | * @lane: lane count | 161 | * @lane: lane count |
| 162 | * @slot: port slot | 162 | * @slot: port slot |
| 163 | * @irq_domain: legacy INTx IRQ domain | 163 | * @irq_domain: legacy INTx IRQ domain |
| 164 | * @inner_domain: inner IRQ domain | ||
| 164 | * @msi_domain: MSI IRQ domain | 165 | * @msi_domain: MSI IRQ domain |
| 166 | * @lock: protect the msi_irq_in_use bitmap | ||
| 165 | * @msi_irq_in_use: bit map for assigned MSI IRQ | 167 | * @msi_irq_in_use: bit map for assigned MSI IRQ |
| 166 | */ | 168 | */ |
| 167 | struct mtk_pcie_port { | 169 | struct mtk_pcie_port { |
| @@ -179,7 +181,9 @@ struct mtk_pcie_port { | |||
| 179 | u32 lane; | 181 | u32 lane; |
| 180 | u32 slot; | 182 | u32 slot; |
| 181 | struct irq_domain *irq_domain; | 183 | struct irq_domain *irq_domain; |
| 184 | struct irq_domain *inner_domain; | ||
| 182 | struct irq_domain *msi_domain; | 185 | struct irq_domain *msi_domain; |
| 186 | struct mutex lock; | ||
| 183 | DECLARE_BITMAP(msi_irq_in_use, MTK_MSI_IRQS_NUM); | 187 | DECLARE_BITMAP(msi_irq_in_use, MTK_MSI_IRQS_NUM); |
| 184 | }; | 188 | }; |
| 185 | 189 | ||
| @@ -446,103 +450,130 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port) | |||
| 446 | return 0; | 450 | return 0; |
| 447 | } | 451 | } |
| 448 | 452 | ||
| 449 | static int mtk_pcie_msi_alloc(struct mtk_pcie_port *port) | 453 | static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) |
| 450 | { | 454 | { |
| 451 | int msi; | 455 | struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data); |
| 456 | phys_addr_t addr; | ||
| 452 | 457 | ||
| 453 | msi = find_first_zero_bit(port->msi_irq_in_use, MTK_MSI_IRQS_NUM); | 458 | /* MT2712/MT7622 only support 32-bit MSI addresses */ |
| 454 | if (msi < MTK_MSI_IRQS_NUM) | 459 | addr = virt_to_phys(port->base + PCIE_MSI_VECTOR); |
| 455 | set_bit(msi, port->msi_irq_in_use); | 460 | msg->address_hi = 0; |
| 456 | else | 461 | msg->address_lo = lower_32_bits(addr); |
| 457 | return -ENOSPC; | 462 | |
| 463 | msg->data = data->hwirq; | ||
| 458 | 464 | ||
| 459 | return msi; | 465 | dev_dbg(port->pcie->dev, "msi#%d address_hi %#x address_lo %#x\n", |
| 466 | (int)data->hwirq, msg->address_hi, msg->address_lo); | ||
| 460 | } | 467 | } |
| 461 | 468 | ||
| 462 | static void mtk_pcie_msi_free(struct mtk_pcie_port *port, unsigned long hwirq) | 469 | static int mtk_msi_set_affinity(struct irq_data *irq_data, |
| 470 | const struct cpumask *mask, bool force) | ||
| 463 | { | 471 | { |
| 464 | clear_bit(hwirq, port->msi_irq_in_use); | 472 | return -EINVAL; |
| 465 | } | 473 | } |
| 466 | 474 | ||
| 467 | static int mtk_pcie_msi_setup_irq(struct msi_controller *chip, | 475 | static void mtk_msi_ack_irq(struct irq_data *data) |
| 468 | struct pci_dev *pdev, struct msi_desc *desc) | ||
| 469 | { | 476 | { |
| 470 | struct mtk_pcie_port *port; | 477 | struct mtk_pcie_port *port = irq_data_get_irq_chip_data(data); |
| 471 | struct msi_msg msg; | 478 | u32 hwirq = data->hwirq; |
| 472 | unsigned int irq; | ||
| 473 | int hwirq; | ||
| 474 | phys_addr_t msg_addr; | ||
| 475 | 479 | ||
| 476 | port = mtk_pcie_find_port(pdev->bus, pdev->devfn); | 480 | writel(1 << hwirq, port->base + PCIE_IMSI_STATUS); |
| 477 | if (!port) | 481 | } |
| 478 | return -EINVAL; | ||
| 479 | 482 | ||
| 480 | hwirq = mtk_pcie_msi_alloc(port); | 483 | static struct irq_chip mtk_msi_bottom_irq_chip = { |
| 481 | if (hwirq < 0) | 484 | .name = "MTK MSI", |
| 482 | return hwirq; | 485 | .irq_compose_msi_msg = mtk_compose_msi_msg, |
| 486 | .irq_set_affinity = mtk_msi_set_affinity, | ||
| 487 | .irq_ack = mtk_msi_ack_irq, | ||
| 488 | }; | ||
| 483 | 489 | ||
| 484 | irq = irq_create_mapping(port->msi_domain, hwirq); | 490 | static int mtk_pcie_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, |
| 485 | if (!irq) { | 491 | unsigned int nr_irqs, void *args) |
| 486 | mtk_pcie_msi_free(port, hwirq); | 492 | { |
| 487 | return -EINVAL; | 493 | struct mtk_pcie_port *port = domain->host_data; |
| 488 | } | 494 | unsigned long bit; |
| 489 | 495 | ||
| 490 | chip->dev = &pdev->dev; | 496 | WARN_ON(nr_irqs != 1); |
| 497 | mutex_lock(&port->lock); | ||
| 491 | 498 | ||
| 492 | irq_set_msi_desc(irq, desc); | 499 | bit = find_first_zero_bit(port->msi_irq_in_use, MTK_MSI_IRQS_NUM); |
| 500 | if (bit >= MTK_MSI_IRQS_NUM) { | ||
| 501 | mutex_unlock(&port->lock); | ||
| 502 | return -ENOSPC; | ||
| 503 | } | ||
| 493 | 504 | ||
| 494 | /* MT2712/MT7622 only support 32-bit MSI addresses */ | 505 | __set_bit(bit, port->msi_irq_in_use); |
| 495 | msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR); | 506 | |
| 496 | msg.address_hi = 0; | 507 | mutex_unlock(&port->lock); |
| 497 | msg.address_lo = lower_32_bits(msg_addr); | ||
| 498 | msg.data = hwirq; | ||
| 499 | 508 | ||
| 500 | pci_write_msi_msg(irq, &msg); | 509 | irq_domain_set_info(domain, virq, bit, &mtk_msi_bottom_irq_chip, |
| 510 | domain->host_data, handle_edge_irq, | ||
| 511 | NULL, NULL); | ||
| 501 | 512 | ||
| 502 | return 0; | 513 | return 0; |
| 503 | } | 514 | } |
| 504 | 515 | ||
| 505 | static void mtk_msi_teardown_irq(struct msi_controller *chip, unsigned int irq) | 516 | static void mtk_pcie_irq_domain_free(struct irq_domain *domain, |
| 517 | unsigned int virq, unsigned int nr_irqs) | ||
| 506 | { | 518 | { |
| 507 | struct pci_dev *pdev = to_pci_dev(chip->dev); | 519 | struct irq_data *d = irq_domain_get_irq_data(domain, virq); |
| 508 | struct irq_data *d = irq_get_irq_data(irq); | 520 | struct mtk_pcie_port *port = irq_data_get_irq_chip_data(d); |
| 509 | irq_hw_number_t hwirq = irqd_to_hwirq(d); | ||
| 510 | struct mtk_pcie_port *port; | ||
| 511 | 521 | ||
| 512 | port = mtk_pcie_find_port(pdev->bus, pdev->devfn); | 522 | mutex_lock(&port->lock); |
| 513 | if (!port) | 523 | |
| 514 | return; | 524 | if (!test_bit(d->hwirq, port->msi_irq_in_use)) |
| 525 | dev_err(port->pcie->dev, "trying to free unused MSI#%lu\n", | <||
