// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018-2025 Raspberry Pi Ltd. * * All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #define RP1_HW_IRQ_MASK GENMASK(5, 0) #define REG_SET 0x800 #define REG_CLR 0xc00 /* MSI-X CFG registers start at 0x8 */ #define MSIX_CFG(x) (0x8 + (4 * (x))) #define MSIX_CFG_IACK_EN BIT(3) #define MSIX_CFG_IACK BIT(2) #define MSIX_CFG_ENABLE BIT(0) /* Address map */ #define RP1_PCIE_APBS_BASE 0x108000 /* Interrupts */ #define RP1_INT_END 61 /* Embedded dtbo symbols created by cmd_wrap_S_dtb in scripts/Makefile.lib */ extern char __dtbo_rp1_pci_begin[]; extern char __dtbo_rp1_pci_end[]; struct rp1_dev { struct pci_dev *pdev; struct irq_domain *domain; struct irq_data *pcie_irqds[64]; void __iomem *bar1; int ovcs_id; /* overlay changeset id */ bool level_triggered_irq[RP1_INT_END]; }; static void msix_cfg_set(struct rp1_dev *rp1, unsigned int hwirq, u32 value) { iowrite32(value, rp1->bar1 + RP1_PCIE_APBS_BASE + REG_SET + MSIX_CFG(hwirq)); } static void msix_cfg_clr(struct rp1_dev *rp1, unsigned int hwirq, u32 value) { iowrite32(value, rp1->bar1 + RP1_PCIE_APBS_BASE + REG_CLR + MSIX_CFG(hwirq)); } static void rp1_mask_irq(struct irq_data *irqd) { struct rp1_dev *rp1 = irqd->domain->host_data; struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq]; pci_msi_mask_irq(pcie_irqd); } static void rp1_unmask_irq(struct irq_data *irqd) { struct rp1_dev *rp1 = irqd->domain->host_data; struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq]; pci_msi_unmask_irq(pcie_irqd); } static int rp1_irq_set_type(struct irq_data *irqd, unsigned int type) { struct rp1_dev *rp1 = irqd->domain->host_data; unsigned int hwirq = (unsigned int)irqd->hwirq; switch (type) { case IRQ_TYPE_LEVEL_HIGH: dev_dbg(&rp1->pdev->dev, "MSIX IACK EN for IRQ %u\n", hwirq); msix_cfg_set(rp1, hwirq, MSIX_CFG_IACK_EN); rp1->level_triggered_irq[hwirq] = true; break; case IRQ_TYPE_EDGE_RISING: msix_cfg_clr(rp1, hwirq, MSIX_CFG_IACK_EN); rp1->level_triggered_irq[hwirq] = false; break; default: return -EINVAL; } return 0; } static struct irq_chip rp1_irq_chip = { .name = "rp1_irq_chip", .irq_mask = rp1_mask_irq, .irq_unmask = rp1_unmask_irq, .irq_set_type = rp1_irq_set_type, }; static void rp1_chained_handle_irq(struct irq_desc *desc) { unsigned int hwirq = desc->irq_data.hwirq & RP1_HW_IRQ_MASK; struct rp1_dev *rp1 = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); unsigned int virq; chained_irq_enter(chip, desc); virq = irq_find_mapping(rp1->domain, hwirq); generic_handle_irq(virq); if (rp1->level_triggered_irq[hwirq]) msix_cfg_set(rp1, hwirq, MSIX_CFG_IACK); chained_irq_exit(chip, desc); } static int rp1_irq_xlate(struct irq_domain *d, struct device_node *node, const u32 *intspec, unsigned int intsize, unsigned long *out_hwirq, unsigned int *out_type) { struct rp1_dev *rp1 = d->host_data; struct irq_data *pcie_irqd; unsigned long hwirq; int pcie_irq; int ret; ret = irq_domain_xlate_twocell(d, node, intspec, intsize, &hwirq, out_type); if (ret) return ret; pcie_irq = pci_irq_vector(rp1->pdev, hwirq); pcie_irqd = irq_get_irq_data(pcie_irq); rp1->pcie_irqds[hwirq] = pcie_irqd; *out_hwirq = hwirq; return 0; } static int rp1_irq_activate(struct irq_domain *d, struct irq_data *irqd, bool reserve) { struct rp1_dev *rp1 = d->host_data; msix_cfg_set(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE); return 0; } static void rp1_irq_deactivate(struct irq_domain *d, struct irq_data *irqd) { struct rp1_dev *rp1 = d->host_data; msix_cfg_clr(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE); } static const struct irq_domain_ops rp1_domain_ops = { .xlate = rp1_irq_xlate, .activate = rp1_irq_activate, .deactivate = rp1_irq_deactivate, }; static void rp1_unregister_interrupts(struct pci_dev *pdev) { struct rp1_dev *rp1 = pci_get_drvdata(pdev); int irq, i; if (rp1->domain) { for (i = 0; i < RP1_INT_END; i++) { irq = irq_find_mapping(rp1->domain, i); irq_dispose_mapping(irq); } irq_domain_remove(rp1->domain); } pci_free_irq_vectors(pdev); } static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) { u32 dtbo_size = __dtbo_rp1_pci_end - __dtbo_rp1_pci_begin; void *dtbo_start = __dtbo_rp1_pci_begin; struct device *dev = &pdev->dev; struct device_node *rp1_node; bool skip_ovl = true; struct rp1_dev *rp1; int err = 0; int i; /* * Either use rp1_nexus node if already present in DT, or * set a flag to load it from overlay at runtime */ rp1_node = of_find_node_by_name(NULL, "rp1_nexus"); if (!rp1_node) { rp1_node = dev_of_node(dev); skip_ovl = false; } if (!rp1_node) { dev_err(dev, "Missing of_node for device\n"); err = -EINVAL; goto err_put_node; } rp1 = devm_kzalloc(&pdev->dev, sizeof(*rp1), GFP_KERNEL); if (!rp1) { err = -ENOMEM; goto err_put_node; } rp1->pdev = pdev; if (pci_resource_len(pdev, 1) <= 0x10000) { dev_err(&pdev->dev, "Not initialized - is the firmware running?\n"); err = -EINVAL; goto err_put_node; } err = pcim_enable_device(pdev); if (err < 0) { err = dev_err_probe(&pdev->dev, err, "Enabling PCI device has failed"); goto err_put_node; } rp1->bar1 = pcim_iomap(pdev, 1, 0); if (!rp1->bar1) { dev_err(&pdev->dev, "Cannot map PCI BAR\n"); err = -EIO; goto err_put_node; } pci_set_master(pdev); err = pci_alloc_irq_vectors(pdev, RP1_INT_END, RP1_INT_END, PCI_IRQ_MSIX); if (err < 0) { err = dev_err_probe(&pdev->dev, err, "Failed to allocate MSI-X vectors\n"); goto err_put_node; } else if (err != RP1_INT_END) { dev_err(&pdev->dev, "Cannot allocate enough interrupts\n"); err = -EINVAL; goto err_put_node; } pci_set_drvdata(pdev, rp1); rp1->domain = irq_domain_add_linear(rp1_node, RP1_INT_END, &rp1_domain_ops, rp1); if (!rp1->domain) { dev_err(&pdev->dev, "Error creating IRQ domain\n"); err = -ENOMEM; goto err_unregister_interrupts; } for (i = 0; i < RP1_INT_END; i++) { unsigned int irq = irq_create_mapping(rp1->domain, i); if (!irq) { dev_err(&pdev->dev, "Failed to create IRQ mapping\n"); err = -EINVAL; goto err_unregister_interrupts; } irq_set_chip_and_handler(irq, &rp1_irq_chip, handle_level_irq); irq_set_probe(irq); irq_set_chained_handler_and_data(pci_irq_vector(pdev, i), rp1_chained_handle_irq, rp1); } if (!skip_ovl) { err = of_overlay_fdt_apply(dtbo_start, dtbo_size, &rp1->ovcs_id, rp1_node); if (err) goto err_unregister_interrupts; } err = of_platform_default_populate(rp1_node, NULL, dev); if (err) { dev_err_probe(&pdev->dev, err, "Error populating devicetree\n"); goto err_unload_overlay; } return 0; err_unload_overlay: of_overlay_remove(&rp1->ovcs_id); err_unregister_interrupts: rp1_unregister_interrupts(pdev); err_put_node: if (skip_ovl) of_node_put(rp1_node); return err; } static void rp1_remove(struct pci_dev *pdev) { struct rp1_dev *rp1 = pci_get_drvdata(pdev); struct device *dev = &pdev->dev; of_platform_depopulate(dev); of_overlay_remove(&rp1->ovcs_id); rp1_unregister_interrupts(pdev); } static const struct pci_device_id dev_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RPI_RP1_C0), }, { } }; MODULE_DEVICE_TABLE(pci, dev_id_table); static struct pci_driver rp1_driver = { .name = KBUILD_MODNAME, .id_table = dev_id_table, .probe = rp1_probe, .remove = rp1_remove, }; module_pci_driver(rp1_driver); MODULE_AUTHOR("Phil Elwell "); MODULE_AUTHOR("Andrea della Porta "); MODULE_DESCRIPTION("RaspberryPi RP1 misc device"); MODULE_LICENSE("GPL");