// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018-22 Raspberry Pi Ltd. * All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* TO DO: * 1. Occasional shutdown crash - RP1 being closed before its children? * 2. DT mode interrupt handling. */ #define RP1_DRIVER_NAME "rp1" #define PCI_VENDOR_ID_RPI 0x1de4 #define PCI_DEVICE_ID_RP1_C0 0x0001 #define PCI_DEVICE_REV_RP1_C0 2 #define RP1_ACTUAL_IRQS RP1_INT_END #define RP1_IRQS RP1_ACTUAL_IRQS #define RP1_SYSCLK_RATE 200000000 #define RP1_SYSCLK_FPGA_RATE 60000000 // Don't want to include the whole sysinfo reg header #define SYSINFO_CHIP_ID_OFFSET 0x00000000 #define SYSINFO_PLATFORM_OFFSET 0x00000004 #define REG_RW 0x000 #define REG_SET 0x800 #define REG_CLR 0xc00 // MSIX 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_TEST BIT(1) #define MSIX_CFG_ENABLE BIT(0) #define INTSTATL 0x108 #define INTSTATH 0x10c struct rp1_dev { struct pci_dev *pdev; struct device *dev; resource_size_t bar_start; resource_size_t bar_end; struct clk *sys_clk; struct irq_domain *domain; struct irq_data *pcie_irqds[64]; void __iomem *msix_cfg_regs; }; static bool rp1_level_triggered_irq[RP1_ACTUAL_IRQS] = { 0 }; static struct rp1_dev *g_rp1; static u32 g_chip_id, g_platform; static void dump_bar(struct pci_dev *pdev, unsigned int bar) { dev_info(&pdev->dev, "bar%d len 0x%llx, start 0x%llx, end 0x%llx, flags, 0x%lx\n", bar, pci_resource_len(pdev, bar), pci_resource_start(pdev, bar), pci_resource_end(pdev, bar), pci_resource_flags(pdev, bar)); } static void msix_cfg_set(struct rp1_dev *rp1, unsigned int hwirq, u32 value) { writel(value, rp1->msix_cfg_regs + REG_SET + MSIX_CFG(hwirq)); } static void msix_cfg_clr(struct rp1_dev *rp1, unsigned int hwirq, u32 value) { writel(value, rp1->msix_cfg_regs + 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; int ret = 0; switch (type) { case IRQ_TYPE_LEVEL_HIGH: dev_dbg(rp1->dev, "MSIX IACK EN for irq %d\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: ret = -EINVAL; break; } return ret; } static int rp1_irq_set_affinity(struct irq_data *irqd, const struct cpumask *dest, bool force) { struct rp1_dev *rp1 = irqd->domain->host_data; struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq]; return msi_domain_set_affinity(pcie_irqd, dest, force); } 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, .irq_set_affinity = rp1_irq_set_affinity, }; static void rp1_chained_handle_irq(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct rp1_dev *rp1 = desc->irq_data.chip_data; unsigned int hwirq = desc->irq_data.hwirq & 0x3f; int new_irq; rp1 = g_rp1; chained_irq_enter(chip, desc); new_irq = irq_find_mapping(rp1->domain, hwirq); generic_handle_irq(new_irq); 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) { 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 ret; } static int rp1_irq_activate(struct irq_domain *d, struct irq_data *irqd, bool reserve) { struct rp1_dev *rp1 = d->host_data; struct irq_data *pcie_irqd; pcie_irqd = rp1->pcie_irqds[irqd->hwirq]; msix_cfg_set(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE); return irq_domain_activate_irq(pcie_irqd, reserve); } static void rp1_irq_deactivate(struct irq_domain *d, struct irq_data *irqd) { struct rp1_dev *rp1 = d->host_data; struct irq_data *pcie_irqd; pcie_irqd = rp1->pcie_irqds[irqd->hwirq]; msix_cfg_clr(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE); return irq_domain_deactivate_irq(pcie_irqd); } static const struct irq_domain_ops rp1_domain_ops = { .xlate = rp1_irq_xlate, .activate = rp1_irq_activate, .deactivate = rp1_irq_deactivate, }; static inline dma_addr_t rp1_io_to_phys(struct rp1_dev *rp1, unsigned int offset) { return rp1->bar_start + offset; } static u32 rp1_reg_read(struct rp1_dev *rp1, unsigned int base_addr, u32 offset) { dma_addr_t phys = rp1_io_to_phys(rp1, base_addr); void __iomem *regblock = ioremap(phys, 0x1000); u32 value = readl(regblock + offset); iounmap(regblock); return value; } void rp1_get_platform(u32 *chip_id, u32 *platform) { if (chip_id) *chip_id = g_chip_id; if (platform) *platform = g_platform; } EXPORT_SYMBOL_GPL(rp1_get_platform); static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct reset_control *reset; struct platform_device *pcie_pdev; struct device_node *rp1_node; struct rp1_dev *rp1; int err = 0; int i; reset = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); if (IS_ERR(reset)) return PTR_ERR(reset); reset_control_reset(reset); dump_bar(pdev, 0); dump_bar(pdev, 1); if (pci_resource_len(pdev, 1) <= 0x10000) { dev_err(&pdev->dev, "Not initialised - is the firmware running?\n"); return -EINVAL; } /* enable pci device */ err = pcim_enable_device(pdev); if (err < 0) { dev_err(&pdev->dev, "Enabling PCI device has failed: %d", err); return err; } pci_set_master(pdev); err = pci_alloc_irq_vectors(pdev, RP1_IRQS, RP1_IRQS, PCI_IRQ_MSIX); if (err != RP1_IRQS) { dev_err(&pdev->dev, "pci_alloc_irq_vectors failed - %d\n", err); return err; } rp1 = devm_kzalloc(&pdev->dev, sizeof(*rp1), GFP_KERNEL); if (!rp1) return -ENOMEM; rp1->pdev = pdev; rp1->dev = &pdev->dev; pci_set_drvdata(pdev, rp1); rp1->bar_start = pci_resource_start(pdev, 1); rp1->bar_end = pci_resource_end(pdev, 1); // Get chip id g_chip_id = rp1_reg_read(rp1, RP1_SYSINFO_BASE, SYSINFO_CHIP_ID_OFFSET); g_platform = rp1_reg_read(rp1, RP1_SYSINFO_BASE, SYSINFO_PLATFORM_OFFSET); dev_info(&pdev->dev, "chip_id 0x%x%s\n", g_chip_id, (g_platform & RP1_PLATFORM_FPGA) ? " FPGA" : ""); if (g_chip_id != RP1_C0_CHIP_ID) { dev_err(&pdev->dev, "wrong chip id (%x)\n", g_chip_id); return -EINVAL; } rp1_node = of_find_node_by_name(NULL, "rp1"); if (!rp1_node) { dev_err(&pdev->dev, "failed to find RP1 DT node\n"); return -EINVAL; } pcie_pdev = of_find_device_by_node(rp1_node->parent); rp1->domain = irq_domain_add_linear(rp1_node, RP1_IRQS, &rp1_domain_ops, rp1); g_rp1 = rp1; /* TODO can this go in the rp1 device tree entry? */ rp1->msix_cfg_regs = ioremap(rp1_io_to_phys(rp1, RP1_PCIE_APBS_BASE), 0x1000); for (i = 0; i < RP1_IRQS; i++) { int irq = irq_create_mapping(rp1->domain, i); if (irq < 0) { dev_err(&pdev->dev, "failed to create irq mapping\n"); return irq; } irq_set_chip_data(irq, rp1); irq_set_chip_and_handler(irq, &rp1_irq_chip, handle_level_irq); irq_set_probe(irq); irq_set_chained_handler(pci_irq_vector(pdev, i), rp1_chained_handle_irq); } if (rp1_node) of_platform_populate(rp1_node, NULL, NULL, &pcie_pdev->dev); of_node_put(rp1_node); return 0; } static void rp1_remove(struct pci_dev *pdev) { struct rp1_dev *rp1 = pci_get_drvdata(pdev); mfd_remove_devices(&pdev->dev); clk_unregister(rp1->sys_clk); } static const struct pci_device_id dev_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RP1_C0), }, { 0, } }; static struct pci_driver rp1_driver = { .name = RP1_DRIVER_NAME, .id_table = dev_id_table, .probe = rp1_probe, .remove = rp1_remove, }; module_pci_driver(rp1_driver); MODULE_AUTHOR("Phil Elwell "); MODULE_DESCRIPTION("RP1 wrapper"); MODULE_LICENSE("GPL");