diff options
Diffstat (limited to 'kernel/drivers/irqchip/irq-gic-v3-its.c')
-rw-r--r-- | kernel/drivers/irqchip/irq-gic-v3-its.c | 241 |
1 files changed, 119 insertions, 122 deletions
diff --git a/kernel/drivers/irqchip/irq-gic-v3-its.c b/kernel/drivers/irqchip/irq-gic-v3-its.c index c00e2db35..a159529f9 100644 --- a/kernel/drivers/irqchip/irq-gic-v3-its.c +++ b/kernel/drivers/irqchip/irq-gic-v3-its.c @@ -30,15 +30,17 @@ #include <linux/percpu.h> #include <linux/slab.h> +#include <linux/irqchip.h> #include <linux/irqchip/arm-gic-v3.h> #include <asm/cacheflush.h> #include <asm/cputype.h> #include <asm/exception.h> -#include "irqchip.h" +#include "irq-gic-common.h" -#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1 << 0) +#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0) +#define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1) #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0) @@ -54,14 +56,12 @@ struct its_collection { /* * The ITS structure - contains most of the infrastructure, with the - * msi_controller, the command queue, the collections, and the list of - * devices writing to it. + * top-level MSI domain, the command queue, the collections, and the + * list of devices writing to it. */ struct its_node { raw_spinlock_t lock; struct list_head entry; - struct msi_controller msi_chip; - struct irq_domain *domain; void __iomem *base; unsigned long phys_base; struct its_cmd_block *cmd_base; @@ -597,11 +597,6 @@ static void its_unmask_irq(struct irq_data *d) lpi_set_config(d, true); } -static void its_eoi_irq(struct irq_data *d) -{ - gic_write_eoir(d->hwirq); -} - static int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) { @@ -638,31 +633,11 @@ static struct irq_chip its_irq_chip = { .name = "ITS", .irq_mask = its_mask_irq, .irq_unmask = its_unmask_irq, - .irq_eoi = its_eoi_irq, + .irq_eoi = irq_chip_eoi_parent, .irq_set_affinity = its_set_affinity, .irq_compose_msi_msg = its_irq_compose_msi_msg, }; -static void its_mask_msi_irq(struct irq_data *d) -{ - pci_msi_mask_irq(d); - irq_chip_mask_parent(d); -} - -static void its_unmask_msi_irq(struct irq_data *d) -{ - pci_msi_unmask_irq(d); - irq_chip_unmask_parent(d); -} - -static struct irq_chip its_msi_irq_chip = { - .name = "ITS-MSI", - .irq_unmask = its_unmask_msi_irq, - .irq_mask = its_mask_msi_irq, - .irq_eoi = irq_chip_eoi_parent, - .irq_write_msi_msg = pci_msi_domain_write_msg, -}; - /* * How we allocate LPIs: * @@ -742,6 +717,9 @@ static unsigned long *its_lpi_alloc_chunks(int nr_irqs, int *base, int *nr_ids) out: spin_unlock(&lpi_lock); + if (!bitmap) + *base = *nr_ids = 0; + return bitmap; } @@ -831,13 +809,28 @@ static void its_free_tables(struct its_node *its) } } -static int its_alloc_tables(struct its_node *its) +static int its_alloc_tables(const char *node_name, struct its_node *its) { int err; int i; int psz = SZ_64K; u64 shr = GITS_BASER_InnerShareable; - u64 cache = GITS_BASER_WaWb; + u64 cache; + u64 typer; + u32 ids; + + if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) { + /* + * erratum 22375: only alloc 8MB table size + * erratum 24313: ignore memory access type + */ + cache = 0; + ids = 0x14; /* 20 bits, 8MB */ + } else { + cache = GITS_BASER_WaWb; + typer = readq_relaxed(its->base + GITS_TYPER); + ids = GITS_TYPER_DEVBITS(typer); + } for (i = 0; i < GITS_BASER_NR_REGS; i++) { u64 val = readq_relaxed(its->base + GITS_BASER + i * 8); @@ -845,6 +838,7 @@ static int its_alloc_tables(struct its_node *its) u64 entry_size = GITS_BASER_ENTRY_SIZE(val); int order = get_order(psz); int alloc_size; + int alloc_pages; u64 tmp; void *base; @@ -860,9 +854,6 @@ static int its_alloc_tables(struct its_node *its) * For other tables, only allocate a single page. */ if (type == GITS_BASER_TYPE_DEVICE) { - u64 typer = readq_relaxed(its->base + GITS_TYPER); - u32 ids = GITS_TYPER_DEVBITS(typer); - /* * 'order' was initialized earlier to the default page * granule of the the ITS. We can't have an allocation @@ -874,11 +865,19 @@ static int its_alloc_tables(struct its_node *its) if (order >= MAX_ORDER) { order = MAX_ORDER - 1; pr_warn("%s: Device Table too large, reduce its page order to %u\n", - its->msi_chip.of_node->full_name, order); + node_name, order); } } alloc_size = (1 << order) * PAGE_SIZE; + alloc_pages = (alloc_size / psz); + if (alloc_pages > GITS_BASER_PAGES_MAX) { + alloc_pages = GITS_BASER_PAGES_MAX; + order = get_order(GITS_BASER_PAGES_MAX * psz); + pr_warn("%s: Device Table too large, reduce its page order to %u (%u pages)\n", + node_name, order, alloc_pages); + } + base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order); if (!base) { err = -ENOMEM; @@ -907,7 +906,7 @@ retry_baser: break; } - val |= (alloc_size / psz) - 1; + val |= alloc_pages - 1; writeq_relaxed(val, its->base + GITS_BASER + i * 8); tmp = readq_relaxed(its->base + GITS_BASER + i * 8); @@ -921,8 +920,10 @@ retry_baser: * non-cacheable as well. */ shr = tmp & GITS_BASER_SHAREABILITY_MASK; - if (!shr) + if (!shr) { cache = GITS_BASER_nC; + __flush_dcache_area(base, alloc_size); + } goto retry_baser; } @@ -944,7 +945,7 @@ retry_baser: if (val != tmp) { pr_err("ITS: %s: GITS_BASER%d doesn't stick: %lx %lx\n", - its->msi_chip.of_node->full_name, i, + node_name, i, (unsigned long) val, (unsigned long) tmp); err = -ENXIO; goto out_free; @@ -1163,6 +1164,8 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, return NULL; } + __flush_dcache_area(itt, sz); + dev->its = its; dev->itt = itt; dev->nr_ites = nr_ites; @@ -1209,98 +1212,67 @@ static int its_alloc_device_irq(struct its_device *dev, irq_hw_number_t *hwirq) return 0; } -struct its_pci_alias { - struct pci_dev *pdev; - u32 dev_id; - u32 count; -}; - -static int its_pci_msi_vec_count(struct pci_dev *pdev) -{ - int msi, msix; - - msi = max(pci_msi_vec_count(pdev), 0); - msix = max(pci_msix_vec_count(pdev), 0); - - return max(msi, msix); -} - -static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data) -{ - struct its_pci_alias *dev_alias = data; - - dev_alias->dev_id = alias; - if (pdev != dev_alias->pdev) - dev_alias->count += its_pci_msi_vec_count(dev_alias->pdev); - - return 0; -} - static int its_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec, msi_alloc_info_t *info) { - struct pci_dev *pdev; struct its_node *its; struct its_device *its_dev; - struct its_pci_alias dev_alias; - - if (!dev_is_pci(dev)) - return -EINVAL; + struct msi_domain_info *msi_info; + u32 dev_id; - pdev = to_pci_dev(dev); - dev_alias.pdev = pdev; - dev_alias.count = nvec; + /* + * We ignore "dev" entierely, and rely on the dev_id that has + * been passed via the scratchpad. This limits this domain's + * usefulness to upper layers that definitely know that they + * are built on top of the ITS. + */ + dev_id = info->scratchpad[0].ul; - pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias); - its = domain->parent->host_data; + msi_info = msi_get_domain_info(domain); + its = msi_info->data; - its_dev = its_find_device(its, dev_alias.dev_id); + its_dev = its_find_device(its, dev_id); if (its_dev) { /* * We already have seen this ID, probably through * another alias (PCI bridge of some sort). No need to * create the device. */ - dev_dbg(dev, "Reusing ITT for devID %x\n", dev_alias.dev_id); + pr_debug("Reusing ITT for devID %x\n", dev_id); goto out; } - its_dev = its_create_device(its, dev_alias.dev_id, dev_alias.count); + its_dev = its_create_device(its, dev_id, nvec); if (!its_dev) return -ENOMEM; - dev_dbg(&pdev->dev, "ITT %d entries, %d bits\n", - dev_alias.count, ilog2(dev_alias.count)); + pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec)); out: info->scratchpad[0].ptr = its_dev; - info->scratchpad[1].ptr = dev; return 0; } -static struct msi_domain_ops its_pci_msi_ops = { +static struct msi_domain_ops its_msi_domain_ops = { .msi_prepare = its_msi_prepare, }; -static struct msi_domain_info its_pci_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), - .ops = &its_pci_msi_ops, - .chip = &its_msi_irq_chip, -}; - static int its_irq_gic_domain_alloc(struct irq_domain *domain, unsigned int virq, irq_hw_number_t hwirq) { - struct of_phandle_args args; + struct irq_fwspec fwspec; - args.np = domain->parent->of_node; - args.args_count = 3; - args.args[0] = GIC_IRQ_TYPE_LPI; - args.args[1] = hwirq; - args.args[2] = IRQ_TYPE_EDGE_RISING; + if (irq_domain_get_of_node(domain->parent)) { + fwspec.fwnode = domain->parent->fwnode; + fwspec.param_count = 3; + fwspec.param[0] = GIC_IRQ_TYPE_LPI; + fwspec.param[1] = hwirq; + fwspec.param[2] = IRQ_TYPE_EDGE_RISING; + } else { + return -EINVAL; + } - return irq_domain_alloc_irqs_parent(domain, virq, 1, &args); + return irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); } static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, @@ -1323,9 +1295,9 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq, &its_irq_chip, its_dev); - dev_dbg(info->scratchpad[1].ptr, "ID:%d pID:%d vID:%d\n", - (int)(hwirq - its_dev->event_map.lpi_base), - (int)hwirq, virq + i); + pr_debug("ID:%d pID:%d vID:%d\n", + (int)(hwirq - its_dev->event_map.lpi_base), + (int) hwirq, virq + i); } return 0; @@ -1421,11 +1393,39 @@ static int its_force_quiescent(void __iomem *base) } } +static void __maybe_unused its_enable_quirk_cavium_22375(void *data) +{ + struct its_node *its = data; + + its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375; +} + +static const struct gic_quirk its_quirks[] = { +#ifdef CONFIG_CAVIUM_ERRATUM_22375 + { + .desc = "ITS: Cavium errata 22375, 24313", + .iidr = 0xa100034c, /* ThunderX pass 1.x */ + .mask = 0xffff0fff, + .init = its_enable_quirk_cavium_22375, + }, +#endif + { + } +}; + +static void its_enable_quirks(struct its_node *its) +{ + u32 iidr = readl_relaxed(its->base + GITS_IIDR); + + gic_enable_quirks(iidr, its_quirks, its); +} + static int its_probe(struct device_node *node, struct irq_domain *parent) { struct resource res; struct its_node *its; void __iomem *its_base; + struct irq_domain *inner_domain; u32 val; u64 baser, tmp; int err; @@ -1469,7 +1469,6 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) INIT_LIST_HEAD(&its->its_device_list); its->base = its_base; its->phys_base = res.start; - its->msi_chip.of_node = node; its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1; its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL); @@ -1479,7 +1478,9 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) } its->cmd_write = its->cmd_base; - err = its_alloc_tables(its); + its_enable_quirks(its); + + err = its_alloc_tables(node->full_name, its); if (err) goto out_free_cmd; @@ -1515,26 +1516,27 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) writeq_relaxed(0, its->base + GITS_CWRITER); writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR); - if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) { - its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its); - if (!its->domain) { + if (of_property_read_bool(node, "msi-controller")) { + struct msi_domain_info *info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { err = -ENOMEM; goto out_free_tables; } - its->domain->parent = parent; - - its->msi_chip.domain = pci_msi_create_irq_domain(node, - &its_pci_msi_domain_info, - its->domain); - if (!its->msi_chip.domain) { + inner_domain = irq_domain_add_tree(node, &its_domain_ops, its); + if (!inner_domain) { err = -ENOMEM; - goto out_free_domains; + kfree(info); + goto out_free_tables; } - err = of_pci_msi_chip_add(&its->msi_chip); - if (err) - goto out_free_domains; + inner_domain->parent = parent; + inner_domain->bus_token = DOMAIN_BUS_NEXUS; + info->ops = &its_msi_domain_ops; + info->data = its; + inner_domain->host_data = info; } spin_lock(&its_lock); @@ -1543,11 +1545,6 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) return 0; -out_free_domains: - if (its->msi_chip.domain) - irq_domain_remove(its->msi_chip.domain); - if (its->domain) - irq_domain_remove(its->domain); out_free_tables: its_free_tables(its); out_free_cmd: |