From: Sebastian Frias <sf84@laposte.net>
To: Marc Zyngier <marc.zyngier@arm.com>
Cc: "Thomas Gleixner" <tglx@linutronix.de>,
LKML <linux-kernel@vger.kernel.org>,
"Grygorii Strashko" <grygorii.strashko@ti.com>,
"Sricharan R" <r.sricharan@ti.com>, Mason <slash.tmp@free.fr>,
"Måns Rullgård" <mans@mansr.com>
Subject: Re: Using irq-crossbar.c
Date: Tue, 14 Jun 2016 18:39:07 +0200 [thread overview]
Message-ID: <5760332B.1060904@laposte.net> (raw)
In-Reply-To: <576032D3.5050108@laposte.net>
On 06/14/2016 06:37 PM, Sebastian Frias wrote:
>>>> Also, without seeing the code,
>>>> it is pretty difficult to make any meaningful comment...
>>>
>>> Base code is either 4.7rc1 or 4.4.
>>> The irq-crossbar code is not much different from TI, but you can find it attached.
>>
>> Please post it separately (and inline), the email client I have here
>> makes it hard to review attached patches.
>
> Ok, I'll post it in a separate email and inline.
>
Here it goes:
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#define IRQ_MUX_INPUT_LINES (128)
#define IRQ_MUX_OUTPUT_LINES (24)
#define IRQ_FREE (-1)
#define IRQ_RESERVED (-2)
/**
* struct tangox_irq_mux : irq mux private driver data
* @lock: spinlock serializing access to @irq_map
* @mux_inputs: inputs (irq lines entering) the mux
* @mux_outputs: outputs (irq lines exiting) the mux (connected to the GIC)
* @irq_map: irq input->output map
* @reg_base: mux base address
*/
struct tangox_irq_mux {
raw_spinlock_t lock;
uint mux_inputs;
uint mux_outputs;
uint *irq_map;
void __iomem *reg_base;
};
#define DBGLOG(__format, ...) \
do { \
pr_info("[%s:%d] %s(): " __format, __FILE__, __LINE__, __FUNCTION__ , ##__VA_ARGS__); \
} while (0)
static inline u32 intc_readl(int address)
{
u32 value = readl_relaxed((void __iomem *)address);
//DBGLOG("read 0x%x @ 0x%x\n", value, address);
return value;
}
static inline void intc_writel(int value, int address)
{
//DBGLOG("write 0x%x @ 0x%x\n", value, address);
writel_relaxed(value, (void __iomem *)address);
}
static inline void tangox_setup_irq_route(struct tangox_irq_mux *irq_mux_context, int irq_in, int irq_out)
{
u32 value = irq_out;
u32 offset = (irq_in * 4);
u32 address = irq_mux_context->reg_base + offset;
DBGLOG("route irq %2d (@ 0x%08x) => irq %2d\n", irq_in, address, value);
if (value)
value |= 0x80000000;
intc_writel(value, address);
}
static int tangox_allocate_gic_irq(struct irq_domain *domain,
unsigned virq,
irq_hw_number_t hwirq)
{
struct tangox_irq_mux *irq_mux_context = domain->host_data;
struct irq_fwspec fwspec;
int i;
int err;
DBGLOG("domain 0x%p, virq %d (0x%x) hwirq %d (0x%x)\n", domain, virq, virq, hwirq, hwirq);
if (!irq_domain_get_of_node(domain->parent))
return -EINVAL;
raw_spin_lock(&(irq_mux_context->lock));
for (i = irq_mux_context->mux_outputs - 1; i >= 0; i--) {
if (irq_mux_context->irq_map[i] == IRQ_FREE) {
irq_mux_context->irq_map[i] = hwirq;
break;
}
}
raw_spin_unlock(&(irq_mux_context->lock));
if (i < 0)
return -ENODEV;
fwspec.fwnode = domain->parent->fwnode;
fwspec.param_count = 3;
fwspec.param[0] = 0; /* SPI */
fwspec.param[1] = i;
fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH;
err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
if (err)
irq_mux_context->irq_map[i] = IRQ_FREE;
else
tangox_setup_irq_route(irq_mux_context, hwirq, i);
return err;
}
static struct irq_chip mux_chip = {
.name = "CBAR",
.irq_eoi = irq_chip_eoi_parent,
.irq_mask = irq_chip_mask_parent,
.irq_unmask = irq_chip_unmask_parent,
.irq_retrigger = irq_chip_retrigger_hierarchy,
.irq_set_type = irq_chip_set_type_parent,
.flags = IRQCHIP_MASK_ON_SUSPEND |
IRQCHIP_SKIP_SET_WAKE,
#ifdef CONFIG_SMP
.irq_set_affinity = irq_chip_set_affinity_parent,
#endif
};
/**
* tangox_irq_mux_domain_alloc - map/reserve a mux<->irq connection
* @domain: domain of irq to map
* @virq: virq number
* @nr_irqs: number of irqs to reserve
*
*/
static int tangox_irq_mux_domain_alloc(struct irq_domain *domain,
unsigned int virq,
unsigned int nr_irqs,
void *data)
{
struct tangox_irq_mux *irq_mux_context = domain->host_data;
struct irq_fwspec *fwspec = data;
irq_hw_number_t hwirq;
int i;
DBGLOG("domain 0x%p, virq %d (0x%x) nr_irqs %d\n", domain, virq, virq, nr_irqs);
if (fwspec->param_count != 3)
return -EINVAL; /* Not GIC compliant */
if (fwspec->param[0] != 0)
return -EINVAL; /* No PPI should point to this domain */
hwirq = fwspec->param[1];
if ((hwirq + nr_irqs) > irq_mux_context->mux_inputs)
return -EINVAL; /* Can't deal with this */
for (i = 0; i < nr_irqs; i++) {
int err = tangox_allocate_gic_irq(domain, virq + i, hwirq + i);
if (err)
return err;
irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
&mux_chip, NULL);
}
return 0;
}
/**
* tangox_irq_mux_domain_free - unmap/free a mux<->irq connection
* @domain: domain of irq to unmap
* @virq: virq number
* @nr_irqs: number of irqs to free
*
* We do not maintain a use count of total number of map/unmap
* calls for a particular irq to find out if a irq can be really
* unmapped. This is because unmap is called during irq_dispose_mapping(irq),
* after which irq is anyways unusable. So an explicit map has to be called
* after that.
*/
static void tangox_irq_mux_domain_free(struct irq_domain *domain,
unsigned int virq,
unsigned int nr_irqs)
{
int i;
struct tangox_irq_mux *irq_mux_context = domain->host_data;
DBGLOG("domain 0x%p, virq %d (0x%x) nr_irqs %d\n", domain, virq, virq, nr_irqs);
raw_spin_lock(&(irq_mux_context->lock));
for (i = 0; i < nr_irqs; i++) {
struct irq_data *irqdata = irq_domain_get_irq_data(domain, virq + i);
irq_domain_reset_irq_data(irqdata);
irq_mux_context->irq_map[irqdata->hwirq] = IRQ_FREE;
tangox_setup_irq_route(irq_mux_context, 0x0, irqdata->hwirq);
}
raw_spin_unlock(&(irq_mux_context->lock));
}
static int tangox_irq_mux_domain_translate(struct irq_domain *domain,
struct irq_fwspec *fwspec,
unsigned long *hwirq,
unsigned int *type)
{
DBGLOG("domain 0x%p\n", domain);
if (is_of_node(fwspec->fwnode)) {
if (fwspec->param_count != 3)
return -EINVAL;
/* No PPI should point to this domain */
if (fwspec->param[0] != 0)
return -EINVAL;
*hwirq = fwspec->param[1];
*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
return 0;
}
return -EINVAL;
}
static const struct irq_domain_ops tangox_irq_mux_domain_ops = {
.alloc = tangox_irq_mux_domain_alloc,
.free = tangox_irq_mux_domain_free,
.translate = tangox_irq_mux_domain_translate,
};
static int __init tangox_irq_mux_init(struct device_node *node,
struct tangox_irq_mux *irq_mux_context)
{
int i, per_irq_reg_size, max_in, max_out, offset, entry, entry_size;
const __be32 *reserved_irq_list;
int ret = -ENOMEM;
DBGLOG("init\n");
if (!irq_mux_context)
return -ENOMEM;
irq_mux_context->reg_base = of_iomap(node, 0);
if (!irq_mux_context->reg_base)
goto err_exit;
irq_mux_context->mux_inputs = IRQ_MUX_INPUT_LINES;
irq_mux_context->mux_outputs = IRQ_MUX_OUTPUT_LINES;
max_in = irq_mux_context->mux_inputs;
max_out = irq_mux_context->mux_outputs;
irq_mux_context->irq_map = kcalloc(max_out, sizeof(int), GFP_KERNEL);
if (!irq_mux_context->irq_map)
goto err_unmap_base;
for (i = 0; i < max_out; i++)
irq_mux_context->irq_map[i] = IRQ_FREE;
// mark reserved IRQ lines
reserved_irq_list = of_get_property(node, "irqs-reserved", &entry_size);
if (reserved_irq_list) {
entry_size /= sizeof(__be32);
DBGLOG("setting up %d reserved irqs\n", entry_size);
for (i = 0; i < entry_size; i++) {
of_property_read_u32_index(node,
"irqs-reserved",
i, &entry);
if (entry >= max_out) {
pr_err("Invalid reserved entry %d > %d: ignored\n", entry, max_out);
continue;
}
irq_mux_context->irq_map[entry] = IRQ_RESERVED;
}
}
DBGLOG("disabling free IRQs\n");
// disable free IRQs during initialisation
for (i = 0; i < max_in; i++) {
tangox_setup_irq_route(irq_mux_context, i, 0);
}
DBGLOG("init backward compatible map\n");
tangox_setup_irq_route(irq_mux_context, 125, 2);
tangox_setup_irq_route(irq_mux_context, 126, 3);
tangox_setup_irq_route(irq_mux_context, 127, 4);
raw_spin_lock_init(&irq_mux_context->lock);
return 0;
err_free_map:
kfree(irq_mux_context->irq_map);
err_unmap_base:
iounmap(irq_mux_context->reg_base);
err_exit:
return ret;
}
static int __init tangox_irq_mux_deinit(struct tangox_irq_mux *irq_mux_context)
{
if (!irq_mux_context)
return -ENOMEM;
if (irq_mux_context->reg_base)
iounmap(irq_mux_context->reg_base);
if (irq_mux_context->irq_map)
kfree(irq_mux_context->irq_map);
kfree(irq_mux_context);
return 0;
}
static int __init tangox_of_irq_mux_init(struct device_node *node,
struct device_node *parent)
{
struct tangox_irq_mux *irq_mux_context;
struct irq_domain *parent_domain, *domain;
int err;
DBGLOG("irqv2 begin\n");
if (!parent) {
pr_err("%s: no parent, giving up\n", node->full_name);
return -ENODEV;
}
parent_domain = irq_find_host(parent);
if (!parent_domain) {
pr_err("%s: unable to obtain parent domain\n", node->full_name);
return -ENXIO;
}
irq_mux_context = kzalloc(sizeof(*irq_mux_context), GFP_KERNEL);
err = tangox_irq_mux_init(node, irq_mux_context);
if (err) {
pr_err("%s: init failed (%d)\n", node->full_name, err);
tangox_irq_mux_deinit(irq_mux_context);
return err;
}
domain = irq_domain_add_hierarchy(parent_domain, 0,
irq_mux_context->mux_inputs,
node, &tangox_irq_mux_domain_ops,
irq_mux_context);
if (!domain) {
pr_err("%s: failed to allocated domain\n", node->full_name);
tangox_irq_mux_deinit(irq_mux_context);
return -ENOMEM;
}
return 0;
}
IRQCHIP_DECLARE(tangox_intc, "sigma,smp-irq-mux", tangox_of_irq_mux_init);
next prev parent reply other threads:[~2016-06-14 16:39 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-06-10 15:37 Using irq-crossbar.c Sebastian Frias
2016-06-10 16:05 ` Marc Zyngier
2016-06-10 19:36 ` Mason
2016-06-11 9:58 ` Marc Zyngier
2016-06-11 15:37 ` Mason
2016-06-12 10:00 ` Marc Zyngier
2016-06-12 13:50 ` Mason
2016-06-13 7:58 ` Marc Zyngier
2016-06-13 14:04 ` Lennart Sorensen
2016-06-13 14:57 ` Sebastian Frias
2016-06-13 15:42 ` Lennart Sorensen
2016-06-13 15:49 ` Mason
2016-06-13 15:57 ` Marc Zyngier
2016-06-13 17:55 ` Lennart Sorensen
2016-06-13 15:15 ` Sebastian Frias
2016-06-13 16:26 ` Marc Zyngier
2016-06-13 15:46 ` Sebastian Frias
2016-06-13 16:24 ` Marc Zyngier
2016-06-14 16:37 ` Sebastian Frias
2016-06-14 16:39 ` Sebastian Frias [this message]
2016-06-16 12:39 ` Sebastian Frias
2016-06-21 10:18 ` Marc Zyngier
2016-06-21 11:03 ` Sebastian Frias
2016-06-21 12:41 ` Marc Zyngier
2016-06-21 15:29 ` Sebastian Frias
2016-06-13 17:59 ` Lennart Sorensen
2016-06-10 16:06 ` Lennart Sorensen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5760332B.1060904@laposte.net \
--to=sf84@laposte.net \
--cc=grygorii.strashko@ti.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mans@mansr.com \
--cc=marc.zyngier@arm.com \
--cc=r.sricharan@ti.com \
--cc=slash.tmp@free.fr \
--cc=tglx@linutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox