mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 07:27:12 +08:00
This was done entirely with mindless brute force, using
git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'
to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.
Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.
For the same reason the 'flex' versions will be done as a separate
conversion.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
230 lines
5.7 KiB
C
230 lines
5.7 KiB
C
/*
|
|
* Copyright (C) 2016 Marvell
|
|
*
|
|
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
|
*
|
|
* This file is licensed under the terms of the GNU General Public
|
|
* License version 2. This program is licensed "as is" without any
|
|
* warranty of any kind, whether express or implied.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "GIC-ODMI: " fmt
|
|
|
|
#include <linux/irq.h>
|
|
#include <linux/irqchip.h>
|
|
#include <linux/irqdomain.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/msi.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/irqchip/irq-msi-lib.h>
|
|
|
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
|
|
|
#define GICP_ODMIN_SET 0x40
|
|
#define GICP_ODMI_INT_NUM_SHIFT 12
|
|
#define GICP_ODMIN_GM_EP_R0 0x110
|
|
#define GICP_ODMIN_GM_EP_R1 0x114
|
|
#define GICP_ODMIN_GM_EA_R0 0x108
|
|
#define GICP_ODMIN_GM_EA_R1 0x118
|
|
|
|
/*
|
|
* We don't support the group events, so we simply have 8 interrupts
|
|
* per frame.
|
|
*/
|
|
#define NODMIS_SHIFT 3
|
|
#define NODMIS_PER_FRAME (1 << NODMIS_SHIFT)
|
|
#define NODMIS_MASK (NODMIS_PER_FRAME - 1)
|
|
|
|
struct odmi_data {
|
|
struct resource res;
|
|
void __iomem *base;
|
|
unsigned int spi_base;
|
|
};
|
|
|
|
static struct odmi_data *odmis;
|
|
static unsigned long *odmis_bm;
|
|
static unsigned int odmis_count;
|
|
|
|
/* Protects odmis_bm */
|
|
static DEFINE_SPINLOCK(odmis_bm_lock);
|
|
|
|
static void odmi_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
|
|
{
|
|
struct odmi_data *odmi;
|
|
phys_addr_t addr;
|
|
unsigned int odmin;
|
|
|
|
if (WARN_ON(d->hwirq >= odmis_count * NODMIS_PER_FRAME))
|
|
return;
|
|
|
|
odmi = &odmis[d->hwirq >> NODMIS_SHIFT];
|
|
odmin = d->hwirq & NODMIS_MASK;
|
|
|
|
addr = odmi->res.start + GICP_ODMIN_SET;
|
|
|
|
msg->address_hi = upper_32_bits(addr);
|
|
msg->address_lo = lower_32_bits(addr);
|
|
msg->data = odmin << GICP_ODMI_INT_NUM_SHIFT;
|
|
}
|
|
|
|
static struct irq_chip odmi_irq_chip = {
|
|
.name = "ODMI",
|
|
.irq_mask = irq_chip_mask_parent,
|
|
.irq_unmask = irq_chip_unmask_parent,
|
|
.irq_eoi = irq_chip_eoi_parent,
|
|
.irq_set_affinity = irq_chip_set_affinity_parent,
|
|
.irq_compose_msi_msg = odmi_compose_msi_msg,
|
|
};
|
|
|
|
static int odmi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
|
unsigned int nr_irqs, void *args)
|
|
{
|
|
struct odmi_data *odmi = NULL;
|
|
struct irq_fwspec fwspec;
|
|
struct irq_data *d;
|
|
unsigned int hwirq, odmin;
|
|
int ret;
|
|
|
|
spin_lock(&odmis_bm_lock);
|
|
hwirq = find_first_zero_bit(odmis_bm, NODMIS_PER_FRAME * odmis_count);
|
|
if (hwirq >= NODMIS_PER_FRAME * odmis_count) {
|
|
spin_unlock(&odmis_bm_lock);
|
|
return -ENOSPC;
|
|
}
|
|
|
|
__set_bit(hwirq, odmis_bm);
|
|
spin_unlock(&odmis_bm_lock);
|
|
|
|
odmi = &odmis[hwirq >> NODMIS_SHIFT];
|
|
odmin = hwirq & NODMIS_MASK;
|
|
|
|
fwspec.fwnode = domain->parent->fwnode;
|
|
fwspec.param_count = 3;
|
|
fwspec.param[0] = GIC_SPI;
|
|
fwspec.param[1] = odmi->spi_base - 32 + odmin;
|
|
fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
|
|
|
|
ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
|
|
if (ret) {
|
|
pr_err("Cannot allocate parent IRQ\n");
|
|
spin_lock(&odmis_bm_lock);
|
|
__clear_bit(odmin, odmis_bm);
|
|
spin_unlock(&odmis_bm_lock);
|
|
return ret;
|
|
}
|
|
|
|
/* Configure the interrupt line to be edge */
|
|
d = irq_domain_get_irq_data(domain->parent, virq);
|
|
d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING);
|
|
|
|
irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
|
|
&odmi_irq_chip, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void odmi_irq_domain_free(struct irq_domain *domain,
|
|
unsigned int virq, unsigned int nr_irqs)
|
|
{
|
|
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
|
|
|
|
if (d->hwirq >= odmis_count * NODMIS_PER_FRAME) {
|
|
pr_err("Failed to teardown msi. Invalid hwirq %lu\n", d->hwirq);
|
|
return;
|
|
}
|
|
|
|
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
|
|
|
|
/* Actually free the MSI */
|
|
spin_lock(&odmis_bm_lock);
|
|
__clear_bit(d->hwirq, odmis_bm);
|
|
spin_unlock(&odmis_bm_lock);
|
|
}
|
|
|
|
static const struct irq_domain_ops odmi_domain_ops = {
|
|
.select = msi_lib_irq_domain_select,
|
|
.alloc = odmi_irq_domain_alloc,
|
|
.free = odmi_irq_domain_free,
|
|
};
|
|
|
|
#define ODMI_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
|
|
MSI_FLAG_USE_DEF_CHIP_OPS)
|
|
|
|
#define ODMI_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK)
|
|
|
|
static const struct msi_parent_ops odmi_msi_parent_ops = {
|
|
.supported_flags = ODMI_MSI_FLAGS_SUPPORTED,
|
|
.required_flags = ODMI_MSI_FLAGS_REQUIRED,
|
|
.chip_flags = MSI_CHIP_FLAG_SET_EOI,
|
|
.bus_select_token = DOMAIN_BUS_GENERIC_MSI,
|
|
.bus_select_mask = MATCH_PLATFORM_MSI,
|
|
.prefix = "ODMI-",
|
|
.init_dev_msi_info = msi_lib_init_dev_msi_info,
|
|
};
|
|
|
|
static int __init mvebu_odmi_init(struct device_node *node,
|
|
struct device_node *parent)
|
|
{
|
|
struct irq_domain_info info = {
|
|
.fwnode = of_fwnode_handle(node),
|
|
.ops = &odmi_domain_ops,
|
|
.size = odmis_count * NODMIS_PER_FRAME,
|
|
.parent = irq_find_host(parent),
|
|
};
|
|
int ret, i;
|
|
|
|
if (of_property_read_u32(node, "marvell,odmi-frames", &odmis_count))
|
|
return -EINVAL;
|
|
|
|
odmis = kzalloc_objs(struct odmi_data, odmis_count);
|
|
if (!odmis)
|
|
return -ENOMEM;
|
|
|
|
odmis_bm = bitmap_zalloc(odmis_count * NODMIS_PER_FRAME, GFP_KERNEL);
|
|
if (!odmis_bm) {
|
|
ret = -ENOMEM;
|
|
goto err_alloc;
|
|
}
|
|
|
|
for (i = 0; i < odmis_count; i++) {
|
|
struct odmi_data *odmi = &odmis[i];
|
|
|
|
ret = of_address_to_resource(node, i, &odmi->res);
|
|
if (ret)
|
|
goto err_unmap;
|
|
|
|
odmi->base = of_io_request_and_map(node, i, "odmi");
|
|
if (IS_ERR(odmi->base)) {
|
|
ret = PTR_ERR(odmi->base);
|
|
goto err_unmap;
|
|
}
|
|
|
|
if (of_property_read_u32_index(node, "marvell,spi-base",
|
|
i, &odmi->spi_base)) {
|
|
ret = -EINVAL;
|
|
goto err_unmap;
|
|
}
|
|
}
|
|
|
|
if (msi_create_parent_irq_domain(&info, &odmi_msi_parent_ops))
|
|
return 0;
|
|
|
|
ret = -ENOMEM;
|
|
|
|
err_unmap:
|
|
for (i = 0; i < odmis_count; i++) {
|
|
struct odmi_data *odmi = &odmis[i];
|
|
|
|
if (odmi->base && !IS_ERR(odmi->base))
|
|
iounmap(odmis[i].base);
|
|
}
|
|
bitmap_free(odmis_bm);
|
|
err_alloc:
|
|
kfree(odmis);
|
|
return ret;
|
|
}
|
|
|
|
IRQCHIP_DECLARE(mvebu_odmi, "marvell,odmi-controller", mvebu_odmi_init);
|