irqdomain: Port system to new API

The following merge commit chose the irq_domain implementation
from AU_LINUX_ANDROID_ICS.04.00.04.00.126 instead of the version
in v3.4.

commit f132c6cf77
Merge: 23016de 3f6240f
Author: Steve Muckle <smuckle@codeaurora.org>
Date:   Wed Jun 6 18:30:57 2012 -0700

    Merge commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126' into
    msm-3.4

Since this version is inconsistent with the upstream,
port the irq_domain framework to the version in v3.4 and
makes all necessary changes to clients that are out of spec.

Details of client ports are below.

-Update the qpnp-int driver for revmap irq_domain API. The revmap
irq_domain implementation introduces a reverse lookup scheme using
a radix tree. This scheme is useful for controllers like qpnp-int
that require a large range of hwirqs.

-Bring the ARM GIC driver up to v3.4, being careful
to port existing CAF changes.

-Partially port the gpio-msm-common driver to the new irq_domain API.
Enable the gpio-msm-common driver to work with the new irq_domain
API using a linear revmap. It is not a full port since irq_domain
is still only registered for Device Tree configurations. It should
be registered even for legacy configurations.

In addition, the irq_domains .map function should be setting all
the fields currently done in msm_gpio_probe(). That's not
currently possible since msm_gpio_probe is invoked
unconditionally - even from Device Tree configurations.

Finally, gpio-msm-common should be converted into a real
platform_device so that probe() is invoked due to driver and
device matching.

Change-Id: I19fa50171bd244759fb6076e3cddc70896d8727b
Signed-off-by: Michael Bohan <mbohan@codeaurora.org>
This commit is contained in:
Michael Bohan
2012-06-26 10:35:13 -07:00
parent 63e6e20a39
commit bb6b30f77b
9 changed files with 1058 additions and 541 deletions
+51 -70
View File
@@ -64,9 +64,7 @@ struct gic_chip_data {
u32 __percpu *saved_ppi_enable;
u32 __percpu *saved_ppi_conf;
#endif
#ifdef CONFIG_IRQ_DOMAIN
struct irq_domain domain;
#endif
struct irq_domain *domain;
unsigned int gic_irqs;
#ifdef CONFIG_GIC_NON_BANKED
void __iomem *(*get_base)(union gic_base *);
@@ -447,7 +445,7 @@ asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
irqnr = irqstat & ~0x1c00;
if (likely(irqnr > 15 && irqnr < 1021)) {
irqnr = irq_domain_to_irq(&gic->domain, irqnr);
irqnr = irq_find_mapping(gic->domain, irqnr);
handle_IRQ(irqnr, regs);
continue;
}
@@ -485,8 +483,8 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
if (gic_irq == 1023)
goto out;
cascade_irq = irq_domain_to_irq(&chip_data->domain, gic_irq);
if (unlikely(gic_irq < 32 || gic_irq > 1020 || cascade_irq >= NR_IRQS))
cascade_irq = irq_find_mapping(chip_data->domain, gic_irq);
if (unlikely(gic_irq < 32 || gic_irq > 1020))
do_bad_IRQ(cascade_irq, desc);
else
generic_handle_irq(cascade_irq);
@@ -520,10 +518,9 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
static void __init gic_dist_init(struct gic_chip_data *gic)
{
unsigned int i, irq;
unsigned int i;
u32 cpumask;
unsigned int gic_irqs = gic->gic_irqs;
struct irq_domain *domain = &gic->domain;
void __iomem *base = gic_data_dist_base(gic);
u32 cpu = cpu_logical_map(smp_processor_id());
@@ -566,23 +563,6 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
for (i = 32; i < gic_irqs; i += 32)
writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
/*
* Setup the Linux IRQ subsystem.
*/
irq_domain_for_each_irq(domain, i, irq) {
if (i < 32) {
irq_set_percpu_devid(irq);
irq_set_chip_and_handler(irq, &gic_chip,
handle_percpu_devid_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
} else {
irq_set_chip_and_handler(irq, &gic_chip,
handle_fasteoi_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
}
irq_set_chip_data(irq, gic);
}
gic->max_irq = gic_irqs;
if (is_cpu_secure())
@@ -833,11 +813,27 @@ static void __init gic_pm_init(struct gic_chip_data *gic)
}
#endif
#ifdef CONFIG_OF
static int gic_irq_domain_dt_translate(struct irq_domain *d,
struct device_node *controller,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type)
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
if (hw < 32) {
irq_set_percpu_devid(irq);
irq_set_chip_and_handler(irq, &gic_chip,
handle_percpu_devid_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
} else {
irq_set_chip_and_handler(irq, &gic_chip,
handle_fasteoi_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
}
irq_set_chip_data(irq, d->host_data);
return 0;
}
static int gic_irq_domain_xlate(struct irq_domain *d,
struct device_node *controller,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type)
{
if (d->of_node != controller)
return -EINVAL;
@@ -854,26 +850,23 @@ static int gic_irq_domain_dt_translate(struct irq_domain *d,
*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
return 0;
}
#endif
const struct irq_domain_ops gic_irq_domain_ops = {
#ifdef CONFIG_OF
.dt_translate = gic_irq_domain_dt_translate,
#endif
.map = gic_irq_domain_map,
.xlate = gic_irq_domain_xlate,
};
void __init gic_init_bases(unsigned int gic_nr, int irq_start,
void __iomem *dist_base, void __iomem *cpu_base,
u32 percpu_offset)
u32 percpu_offset, struct device_node *node)
{
irq_hw_number_t hwirq_base;
struct gic_chip_data *gic;
struct irq_domain *domain;
int gic_irqs, rc;
int gic_irqs, irq_base;
BUG_ON(gic_nr >= MAX_GIC_NR);
gic = &gic_data[gic_nr];
domain = &gic->domain;
#ifdef CONFIG_GIC_NON_BANKED
if (percpu_offset) { /* Frankein-GIC without banked registers... */
unsigned int cpu;
@@ -881,8 +874,11 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
gic->dist_base.percpu_base = alloc_percpu(void __iomem *);
gic->cpu_base.percpu_base = alloc_percpu(void __iomem *);
if (WARN_ON(!gic->dist_base.percpu_base ||
!gic->cpu_base.percpu_base))
goto init_bases_err;
!gic->cpu_base.percpu_base)) {
free_percpu(gic->dist_base.percpu_base);
free_percpu(gic->cpu_base.percpu_base);
return;
}
for_each_possible_cpu(cpu) {
unsigned long offset = percpu_offset * cpu_logical_map(cpu);
@@ -906,13 +902,12 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
* For primary GICs, skip over SGIs.
* For secondary GICs, skip over PPIs, too.
*/
domain->hwirq_base = 32;
if (gic_nr == 0) {
if ((irq_start & 31) > 0) {
domain->hwirq_base = 16;
if (irq_start != -1)
irq_start = (irq_start & ~31) + 16;
}
if (gic_nr == 0 && (irq_start & 31) > 0) {
hwirq_base = 16;
if (irq_start != -1)
irq_start = (irq_start & ~31) + 16;
} else {
hwirq_base = 32;
}
/*
@@ -925,33 +920,22 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
gic_irqs = 1020;
gic->gic_irqs = gic_irqs;
domain->nr_irq = gic_irqs - domain->hwirq_base;
domain->irq_base = irq_alloc_descs(irq_start, 16, domain->nr_irq,
numa_node_id());
if (IS_ERR_VALUE(domain->irq_base)) {
gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id());
if (IS_ERR_VALUE(irq_base)) {
WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
irq_start);
domain->irq_base = irq_start;
irq_base = irq_start;
}
domain->priv = gic;
domain->ops = &gic_irq_domain_ops;
rc = irq_domain_add(domain);
if (rc) {
WARN(1, "Unable to create irq_domain\n");
goto init_bases_err;
}
irq_domain_register(domain);
gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
hwirq_base, &gic_irq_domain_ops, gic);
if (WARN_ON(!gic->domain))
return;
gic_chip.flags |= gic_arch_extn.flags;
gic_dist_init(gic);
gic_cpu_init(gic);
gic_pm_init(gic);
return;
init_bases_err:
free_percpu(gic->dist_base.percpu_base);
free_percpu(gic->cpu_base.percpu_base);
}
void __cpuinit gic_secondary_init(unsigned int gic_nr)
@@ -1036,7 +1020,6 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent)
void __iomem *dist_base;
u32 percpu_offset;
int irq;
struct irq_domain *domain = &gic_data[gic_cnt].domain;
if (WARN_ON(!node))
return -ENODEV;
@@ -1050,9 +1033,7 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent)
if (of_property_read_u32(node, "cpu-offset", &percpu_offset))
percpu_offset = 0;
domain->of_node = of_node_get(node);
gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset);
gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node);
if (parent) {
irq = irq_of_parse_and_map(node, 0);
+2 -2
View File
@@ -40,7 +40,7 @@ struct device_node;
extern struct irq_chip gic_arch_extn;
void gic_init_bases(unsigned int, int, void __iomem *, void __iomem *,
u32 offset);
u32 offset, struct device_node *);
int gic_of_init(struct device_node *node, struct device_node *parent);
void gic_secondary_init(unsigned int);
void gic_handle_irq(struct pt_regs *regs);
@@ -56,7 +56,7 @@ static inline void gic_set_irq_secure(unsigned int irq) { }
static inline void gic_init(unsigned int nr, int start,
void __iomem *dist , void __iomem *cpu)
{
gic_init_bases(nr, start, dist, cpu, 0);
gic_init_bases(nr, start, dist, cpu, 0, NULL);
}
void gic_set_irq_secure(unsigned int irq);
#endif
+7 -3
View File
@@ -52,7 +52,8 @@ int __init qpnpint_of_init(struct device_node *node,
* Used by the PMIC Arbiter driver or equivalent to register
* callbacks for interrupt events.
*/
int qpnpint_register_controller(unsigned int busno,
int qpnpint_register_controller(struct device_node *node,
struct spmi_controller *ctrl,
struct qpnp_local_int *li_cb);
/**
@@ -68,8 +69,11 @@ static inline int __init qpnpint_of_init(struct device_node *node,
{
return -ENXIO;
}
static inline int qpnpint_register_controller(unsigned int busno,
struct qpnp_local_int *li_cb)
static inline int qpnpint_register_controller(struct device_node *node,
struct spmi_controller *ctrl,
struct qpnp_local_int *li_cb)
{
return -ENXIO;
}
+1
View File
@@ -18,6 +18,7 @@
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <asm/cacheflush.h>
#include <asm/hardware/gic.h>
+30 -30
View File
@@ -100,7 +100,7 @@ struct msm_gpio_dev {
DECLARE_BITMAP(enabled_irqs, NR_MSM_GPIOS);
DECLARE_BITMAP(wake_irqs, NR_MSM_GPIOS);
DECLARE_BITMAP(dual_edge_irqs, NR_MSM_GPIOS);
struct irq_domain domain;
struct irq_domain *domain;
};
static DEFINE_SPINLOCK(tlmm_lock);
@@ -152,15 +152,14 @@ static int msm_gpio_direction_output(struct gpio_chip *chip,
static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
struct irq_domain *domain = &g_dev->domain;
return domain->irq_base + (offset - chip->base);
struct irq_domain *domain = g_dev->domain;
return irq_linear_revmap(domain, offset - chip->base);
}
static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
{
struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
struct irq_domain *domain = &g_dev->domain;
return irq - domain->irq_base;
struct irq_data *irq_data = irq_get_irq_data(irq);
return irq_data->hwirq;
}
#else
static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
@@ -391,6 +390,7 @@ static struct irq_chip msm_gpio_irq_chip = {
*/
static struct lock_class_key msm_gpio_lock_class;
/* TODO: This should be a real platform_driver */
static int __devinit msm_gpio_probe(void)
{
int i, irq, ret;
@@ -573,12 +573,12 @@ int msm_gpio_install_direct_irq(unsigned gpio, unsigned irq,
EXPORT_SYMBOL(msm_gpio_install_direct_irq);
#ifdef CONFIG_OF
static int msm_gpio_domain_dt_translate(struct irq_domain *d,
struct device_node *controller,
const u32 *intspec,
unsigned int intsize,
unsigned long *out_hwirq,
unsigned int *out_type)
static int msm_gpio_irq_domain_xlate(struct irq_domain *d,
struct device_node *controller,
const u32 *intspec,
unsigned int intsize,
unsigned long *out_hwirq,
unsigned int *out_type)
{
if (d->of_node != controller)
return -EINVAL;
@@ -593,32 +593,32 @@ static int msm_gpio_domain_dt_translate(struct irq_domain *d,
return 0;
}
/*
* TODO: this really should be doing all the things that msm_gpio_probe() does,
* but since the msm_gpio_probe is called unconditionally for DT and non-DT
* configs, we can't duplicate it here. This should be fixed.
*/
int msm_gpio_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hwirq)
{
return 0;
}
static struct irq_domain_ops msm_gpio_irq_domain_ops = {
.dt_translate = msm_gpio_domain_dt_translate,
.xlate = msm_gpio_irq_domain_xlate,
.map = msm_gpio_irq_domain_map,
};
int __init msm_gpio_of_init(struct device_node *node,
struct device_node *parent)
{
struct irq_domain *domain = &msm_gpio.domain;
int start;
start = irq_domain_find_free_range(0, NR_MSM_GPIOS);
domain->irq_base = irq_alloc_descs(start, 0, NR_MSM_GPIOS,
numa_node_id());
if (IS_ERR_VALUE(domain->irq_base)) {
WARN(1, "Cannot allocate irq_descs @ IRQ%d\n", start);
return domain->irq_base;
msm_gpio.domain = irq_domain_add_linear(node, NR_MSM_GPIOS,
&msm_gpio_irq_domain_ops, &msm_gpio);
if (!msm_gpio.domain) {
WARN(1, "Cannot allocate irq_domain\n");
return -ENOMEM;
}
domain->nr_irq = NR_MSM_GPIOS;
domain->of_node = of_node_get(node);
domain->priv = &msm_gpio;
domain->ops = &msm_gpio_irq_domain_ops;
irq_domain_add(domain);
msm_gpio.gpio_chip.of_node = of_node_get(node);
pr_debug("%s: irq_base = %u\n", __func__, domain->irq_base);
return 0;
}
#endif
+125 -143
View File
@@ -31,8 +31,6 @@
#include <asm/mach/irq.h>
#include <mach/qpnp-int.h>
#define QPNPINT_MAX_BUSSES 1
/* 16 slave_ids, 256 per_ids per slave, and 8 ints per per_id */
#define QPNPINT_NR_IRQS (16 * 256 * 8)
@@ -66,13 +64,18 @@ struct q_irq_data {
struct q_chip_data {
int bus_nr;
struct irq_domain domain;
struct irq_domain *domain;
struct qpnp_local_int cb;
struct spmi_controller *spmi_ctrl;
struct radix_tree_root per_tree;
struct list_head list;
};
static struct q_chip_data chip_data[QPNPINT_MAX_BUSSES] __read_mostly;
static LIST_HEAD(qpnpint_chips);
static DEFINE_MUTEX(qpnpint_chips_mutex);
#define QPNPINT_MAX_BUSSES 4
struct q_chip_data *chip_lookup[QPNPINT_MAX_BUSSES];
/**
* qpnpint_encode_hwirq - translate between qpnp_irq_spec and
@@ -138,8 +141,7 @@ static void qpnpint_irq_mask(struct irq_data *d)
if (chip_d->cb.mask) {
rc = qpnpint_decode_hwirq(d->hwirq, &q_spec);
if (rc)
pr_err("%s: decode failed on hwirq %lu\n",
__func__, d->hwirq);
pr_err("decode failed on hwirq %lu\n", d->hwirq);
else
chip_d->cb.mask(chip_d->spmi_ctrl, &q_spec,
irq_d->priv_d);
@@ -150,8 +152,7 @@ static void qpnpint_irq_mask(struct irq_data *d)
rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_CLR,
(u8 *)&irq_d->mask_shift, 1);
if (rc)
pr_err("%s: spmi failure on irq %d\n",
__func__, d->irq);
pr_err("spmi failure on irq %d\n", d->irq);
}
static void qpnpint_irq_mask_ack(struct irq_data *d)
@@ -168,8 +169,7 @@ static void qpnpint_irq_mask_ack(struct irq_data *d)
if (chip_d->cb.mask) {
rc = qpnpint_decode_hwirq(d->hwirq, &q_spec);
if (rc)
pr_err("%s: decode failed on hwirq %lu\n",
__func__, d->hwirq);
pr_err("decode failed on hwirq %lu\n", d->hwirq);
else
chip_d->cb.mask(chip_d->spmi_ctrl, &q_spec,
irq_d->priv_d);
@@ -180,14 +180,12 @@ static void qpnpint_irq_mask_ack(struct irq_data *d)
rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_CLR,
&irq_d->mask_shift, 1);
if (rc)
pr_err("%s: spmi failure on irq %d\n",
__func__, d->irq);
pr_err("spmi failure on irq %d\n", d->irq);
rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_LATCHED_CLR,
&irq_d->mask_shift, 1);
if (rc)
pr_err("%s: spmi failure on irq %d\n",
__func__, d->irq);
pr_err("spmi failure on irq %d\n", d->irq);
}
static void qpnpint_irq_unmask(struct irq_data *d)
@@ -203,8 +201,7 @@ static void qpnpint_irq_unmask(struct irq_data *d)
if (chip_d->cb.unmask) {
rc = qpnpint_decode_hwirq(d->hwirq, &q_spec);
if (rc)
pr_err("%s: decode failed on hwirq %lu\n",
__func__, d->hwirq);
pr_err("decode failed on hwirq %lu\n", d->hwirq);
else
chip_d->cb.unmask(chip_d->spmi_ctrl, &q_spec,
irq_d->priv_d);
@@ -214,8 +211,7 @@ static void qpnpint_irq_unmask(struct irq_data *d)
rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_EN_SET,
&irq_d->mask_shift, 1);
if (rc)
pr_err("%s: spmi failure on irq %d\n",
__func__, d->irq);
pr_err("spmi failure on irq %d\n", d->irq);
}
static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
@@ -253,8 +249,7 @@ static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type)
rc = qpnpint_spmi_write(irq_d, QPNPINT_REG_SET_TYPE, &buf, 3);
if (rc)
pr_err("%s: spmi failure on irq %d\n",
__func__, d->irq);
pr_err("spmi failure on irq %d\n", d->irq);
return rc;
}
@@ -279,13 +274,16 @@ static int qpnpint_init_irq_data(struct q_chip_data *chip_d,
return rc;
irq_d->spmi_slave = q_spec.slave;
irq_d->spmi_offset = q_spec.per << 8;
irq_d->per_d->use_count++;
irq_d->chip_d = chip_d;
if (chip_d->cb.register_priv_data)
rc = chip_d->cb.register_priv_data(chip_d->spmi_ctrl, &q_spec,
&irq_d->priv_d);
return rc;
if (rc)
return rc;
irq_d->per_d->use_count++;
return 0;
}
static struct q_irq_data *qpnpint_alloc_irq_data(
@@ -307,8 +305,10 @@ static struct q_irq_data *qpnpint_alloc_irq_data(
per_d = radix_tree_lookup(&chip_d->per_tree, (hwirq & ~0x7));
if (!per_d) {
per_d = kzalloc(sizeof(struct q_perip_data), GFP_KERNEL);
if (!per_d)
if (!per_d) {
kfree(irq_d);
return ERR_PTR(-ENOMEM);
}
radix_tree_insert(&chip_d->per_tree,
(hwirq & ~0x7), per_d);
}
@@ -317,74 +317,6 @@ static struct q_irq_data *qpnpint_alloc_irq_data(
return irq_d;
}
static int qpnpint_register_int(uint32_t busno, unsigned long hwirq)
{
int irq, rc;
struct irq_domain *domain;
struct q_irq_data *irq_d;
pr_debug("busno = %u hwirq = %lu\n", busno, hwirq);
if (hwirq < 0 || hwirq >= 32768) {
pr_err("%s: hwirq %lu out of qpnp interrupt bounds\n",
__func__, hwirq);
return -EINVAL;
}
if (busno < 0 || busno > QPNPINT_MAX_BUSSES) {
pr_err("%s: invalid bus number %d\n", __func__, busno);
return -EINVAL;
}
domain = &chip_data[busno].domain;
irq = irq_domain_to_irq(domain, hwirq);
rc = irq_alloc_desc_at(irq, numa_node_id());
if (rc < 0) {
if (rc != -EEXIST)
pr_err("%s: failed to alloc irq at %d with "
"rc %d\n", __func__, irq, rc);
return rc;
}
irq_d = qpnpint_alloc_irq_data(&chip_data[busno], hwirq);
if (IS_ERR(irq_d)) {
pr_err("%s: failed to alloc irq data %d with "
"rc %d\n", __func__, irq, rc);
rc = PTR_ERR(irq_d);
goto register_err_cleanup;
}
rc = qpnpint_init_irq_data(&chip_data[busno], irq_d, hwirq);
if (rc) {
pr_err("%s: failed to init irq data %d with "
"rc %d\n", __func__, irq, rc);
goto register_err_cleanup;
}
irq_domain_register_irq(domain, hwirq);
irq_set_chip_and_handler(irq,
&qpnpint_chip,
handle_level_irq);
irq_set_chip_data(irq, irq_d);
#ifdef CONFIG_ARM
set_irq_flags(irq, IRQF_VALID);
#else
irq_set_noprobe(irq);
#endif
return 0;
register_err_cleanup:
irq_free_desc(irq);
if (!IS_ERR(irq_d)) {
if (irq_d->per_d->use_count == 1)
kfree(irq_d->per_d);
else
irq_d->per_d->use_count--;
kfree(irq_d);
}
return rc;
}
static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
struct device_node *controller,
const u32 *intspec, unsigned int intsize,
@@ -392,11 +324,10 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
unsigned int *out_type)
{
struct qpnp_irq_spec addr;
struct q_chip_data *chip_d = d->priv;
int ret;
pr_debug("%s: intspec[0] 0x%x intspec[1] 0x%x intspec[2] 0x%x\n",
__func__, intspec[0], intspec[1], intspec[2]);
pr_debug("intspec[0] 0x%x intspec[1] 0x%x intspec[2] 0x%x\n",
intspec[0], intspec[1], intspec[2]);
if (d->of_node != controller)
return -EINVAL;
@@ -409,41 +340,102 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
ret = qpnpint_encode_hwirq(&addr);
if (ret < 0) {
pr_err("%s: invalid intspec\n", __func__);
pr_err("invalid intspec\n");
return ret;
}
*out_hwirq = ret;
*out_type = IRQ_TYPE_NONE;
/**
* Register the interrupt if it's not already registered.
* This implies that mapping a qpnp interrupt allocates
* resources.
*/
ret = qpnpint_register_int(chip_d->bus_nr, *out_hwirq);
if (ret && ret != -EEXIST) {
pr_err("%s: Cannot register hwirq %lu\n", __func__, *out_hwirq);
return ret;
}
return 0;
}
static void qpnpint_free_irq_data(struct q_irq_data *irq_d)
{
if (irq_d->per_d->use_count == 1)
kfree(irq_d->per_d);
else
irq_d->per_d->use_count--;
kfree(irq_d);
}
static int qpnpint_irq_domain_map(struct irq_domain *d,
unsigned int virq, irq_hw_number_t hwirq)
{
struct q_chip_data *chip_d = d->host_data;
struct q_irq_data *irq_d;
int rc;
pr_debug("hwirq = %lu\n", hwirq);
if (hwirq < 0 || hwirq >= 32768) {
pr_err("hwirq %lu out of bounds\n", hwirq);
return -EINVAL;
}
irq_radix_revmap_insert(d, virq, hwirq);
irq_d = qpnpint_alloc_irq_data(chip_d, hwirq);
if (IS_ERR(irq_d)) {
pr_err("failed to alloc irq data for hwirq %lu\n", hwirq);
return PTR_ERR(irq_d);
}
rc = qpnpint_init_irq_data(chip_d, irq_d, hwirq);
if (rc) {
pr_err("failed to init irq data for hwirq %lu\n", hwirq);
goto map_err;
}
irq_set_chip_and_handler(virq,
&qpnpint_chip,
handle_level_irq);
irq_set_chip_data(virq, irq_d);
#ifdef CONFIG_ARM
set_irq_flags(virq, IRQF_VALID);
#else
irq_set_noprobe(virq);
#endif
return 0;
map_err:
qpnpint_free_irq_data(irq_d);
return rc;
}
void qpnpint_irq_domain_unmap(struct irq_domain *d, unsigned int virq)
{
struct q_irq_data *irq_d = irq_get_chip_data(virq);
if (WARN_ON(!irq_d))
return;
qpnpint_free_irq_data(irq_d);
}
const struct irq_domain_ops qpnpint_irq_domain_ops = {
.dt_translate = qpnpint_irq_domain_dt_translate,
.map = qpnpint_irq_domain_map,
.unmap = qpnpint_irq_domain_unmap,
.xlate = qpnpint_irq_domain_dt_translate,
};
int qpnpint_register_controller(unsigned int busno,
int qpnpint_register_controller(struct device_node *node,
struct spmi_controller *ctrl,
struct qpnp_local_int *li_cb)
{
if (busno >= QPNPINT_MAX_BUSSES)
return -EINVAL;
chip_data[busno].cb = *li_cb;
chip_data[busno].spmi_ctrl = spmi_busnum_to_ctrl(busno);
if (!chip_data[busno].spmi_ctrl)
return -ENOENT;
struct q_chip_data *chip_d;
return 0;
if (!node || !ctrl || ctrl->nr >= QPNPINT_MAX_BUSSES)
return -EINVAL;
list_for_each_entry(chip_d, &qpnpint_chips, list)
if (node == chip_d->domain->of_node) {
chip_d->cb = *li_cb;
chip_d->spmi_ctrl = ctrl;
chip_lookup[ctrl->nr] = chip_d;
return 0;
}
return -ENOENT;
}
EXPORT_SYMBOL(qpnpint_register_controller);
@@ -457,21 +449,18 @@ int qpnpint_handle_irq(struct spmi_controller *spmi_ctrl,
pr_debug("spec slave = %u per = %u irq = %u\n",
spec->slave, spec->per, spec->irq);
if (!spec || !spmi_ctrl)
return -EINVAL;
busno = spmi_ctrl->nr;
if (busno >= QPNPINT_MAX_BUSSES)
if (!spec || !spmi_ctrl || busno >= QPNPINT_MAX_BUSSES)
return -EINVAL;
hwirq = qpnpint_encode_hwirq(spec);
if (hwirq < 0) {
pr_err("%s: invalid irq spec passed\n", __func__);
pr_err("invalid irq spec passed\n");
return -EINVAL;
}
domain = &chip_data[busno].domain;
irq = irq_domain_to_irq(domain, hwirq);
domain = chip_lookup[busno]->domain;
irq = irq_radix_revmap_lookup(domain, hwirq);
generic_handle_irq(irq);
@@ -479,31 +468,24 @@ int qpnpint_handle_irq(struct spmi_controller *spmi_ctrl,
}
EXPORT_SYMBOL(qpnpint_handle_irq);
/**
* This assumes that there's a relationship between the order of the interrupt
* controllers specified to of_irq_match() is the SPMI device topology. If
* this ever turns out to be a bad assumption, then of_irq_init_cb_t should
* be modified to pass a parameter to this function.
*/
static int qpnpint_cnt __initdata;
int __init qpnpint_of_init(struct device_node *node, struct device_node *parent)
{
struct q_chip_data *chip_d = &chip_data[qpnpint_cnt];
struct irq_domain *domain = &chip_d->domain;
struct q_chip_data *chip_d;
chip_d = kzalloc(sizeof(struct q_chip_data), GFP_KERNEL);
if (!chip_d)
return -ENOMEM;
chip_d->domain = irq_domain_add_tree(node,
&qpnpint_irq_domain_ops, chip_d);
if (!chip_d->domain) {
pr_err("Unable to allocate irq_domain\n");
kfree(chip_d);
return -ENOMEM;
}
INIT_RADIX_TREE(&chip_d->per_tree, GFP_ATOMIC);
domain->irq_base = irq_domain_find_free_range(0, QPNPINT_NR_IRQS);
domain->nr_irq = QPNPINT_NR_IRQS;
domain->of_node = of_node_get(node);
domain->priv = chip_d;
domain->ops = &qpnpint_irq_domain_ops;
irq_domain_add(domain);
pr_info("irq_base = %d\n", domain->irq_base);
qpnpint_cnt++;
list_add(&chip_d->list, &qpnpint_chips);
return 0;
}
+10 -1
View File
@@ -664,7 +664,14 @@ static int __devinit spmi_pmic_arb_probe(struct platform_device *pdev)
goto err_add_controller;
/* Register the interrupt enable/disable functions */
qpnpint_register_controller(cell_index, &spmi_pmic_arb_intr_cb);
ret = qpnpint_register_controller(pmic_arb->controller.dev.of_node,
&pmic_arb->controller,
&spmi_pmic_arb_intr_cb);
if (ret) {
dev_err(&pdev->dev, "Unable to register controller %d\n",
cell_index);
goto err_reg_controller;
}
/* Register device(s) from the device tree */
of_spmi_register_devices(&pmic_arb->controller);
@@ -674,6 +681,8 @@ static int __devinit spmi_pmic_arb_probe(struct platform_device *pdev)
return 0;
err_reg_controller:
spmi_del_controller(&pmic_arb->controller);
err_add_controller:
platform_set_drvdata(pdev, NULL);
return ret;
+132 -60
View File
@@ -9,106 +9,178 @@
* representation into a hardware irq number that can be mapped back to a
* Linux irq number without any extra platform support code.
*
* irq_domain is expected to be embedded in an interrupt controller's private
* data structure.
* Interrupt controller "domain" data structure. This could be defined as a
* irq domain controller. That is, it handles the mapping between hardware
* and virtual interrupt numbers for a given interrupt domain. The domain
* structure is generally created by the PIC code for a given PIC instance
* (though a domain can cover more than one PIC if they have a flat number
* model). It's the domain callbacks that are responsible for setting the
* irq_chip on a given irq_desc after it's been mapped.
*
* The host code and data structures are agnostic to whether or not
* we use an open firmware device-tree. We do have references to struct
* device_node in two places: in irq_find_host() to find the host matching
* a given interrupt controller node, and of course as an argument to its
* counterpart domain->ops->match() callback. However, those are treated as
* generic pointers by the core and the fact that it's actually a device-node
* pointer is purely a convention between callers and implementation. This
* code could thus be used on other architectures by replacing those two
* by some sort of arch-specific void * "token" used to identify interrupt
* controllers.
*/
#ifndef _LINUX_IRQDOMAIN_H
#define _LINUX_IRQDOMAIN_H
#include <linux/irq.h>
#include <linux/mod_devicetable.h>
#include <linux/types.h>
#include <linux/radix-tree.h>
#ifdef CONFIG_IRQ_DOMAIN
struct device_node;
struct irq_domain;
struct of_device_id;
/* Number of irqs reserved for a legacy isa controller */
#define NUM_ISA_INTERRUPTS 16
/**
* struct irq_domain_ops - Methods for irq_domain objects
* @to_irq: (optional) given a local hardware irq number, return the linux
* irq number. If to_irq is not implemented, then the irq_domain
* will use this translation: irq = (domain->irq_base + hwirq)
* @dt_translate: Given a device tree node and interrupt specifier, decode
* the hardware irq number and linux irq type value.
* @match: Match an interrupt controller device node to a host, returns
* 1 on a match
* @map: Create or update a mapping between a virtual irq number and a hw
* irq number. This is called only once for a given mapping.
* @unmap: Dispose of such a mapping
* @xlate: Given a device tree node and interrupt specifier, decode
* the hardware irq number and linux irq type value.
*
* Functions below are provided by the driver and called whenever a new mapping
* is created or an old mapping is disposed. The driver can then proceed to
* whatever internal data structures management is required. It also needs
* to setup the irq_desc when returning from map().
*/
struct irq_domain_ops {
unsigned int (*to_irq)(struct irq_domain *d, unsigned long hwirq);
#ifdef CONFIG_OF
int (*dt_translate)(struct irq_domain *d, struct device_node *node,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type);
#endif /* CONFIG_OF */
int (*match)(struct irq_domain *d, struct device_node *node);
int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw);
void (*unmap)(struct irq_domain *d, unsigned int virq);
int (*xlate)(struct irq_domain *d, struct device_node *node,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type);
};
/**
* struct irq_domain - Hardware interrupt number translation object
* @list: Element in global irq_domain list.
* @link: Element in global irq_domain list.
* @revmap_type: Method used for reverse mapping hwirq numbers to linux irq. This
* will be one of the IRQ_DOMAIN_MAP_* values.
* @revmap_data: Revmap method specific data.
* @ops: pointer to irq_domain methods
* @host_data: private data pointer for use by owner. Not touched by irq_domain
* core code.
* @irq_base: Start of irq_desc range assigned to the irq_domain. The creator
* of the irq_domain is responsible for allocating the array of
* irq_desc structures.
* @nr_irq: Number of irqs managed by the irq domain
* @hwirq_base: Starting number for hwirqs managed by the irq domain
* @ops: pointer to irq_domain methods
* @priv: private data pointer for use by owner. Not touched by irq_domain
* core code.
* @of_node: (optional) Pointer to device tree nodes associated with the
* irq_domain. Used when decoding device tree interrupt specifiers.
*/
struct irq_domain {
struct list_head list;
unsigned int irq_base;
unsigned int nr_irq;
unsigned int hwirq_base;
struct list_head link;
/* type of reverse mapping_technique */
unsigned int revmap_type;
union {
struct {
unsigned int size;
unsigned int first_irq;
irq_hw_number_t first_hwirq;
} legacy;
struct {
unsigned int size;
unsigned int *revmap;
} linear;
struct {
unsigned int max_irq;
} nomap;
struct radix_tree_root tree;
} revmap_data;
const struct irq_domain_ops *ops;
void *priv;
void *host_data;
irq_hw_number_t inval_irq;
/* Optional device node pointer */
struct device_node *of_node;
};
/**
* irq_domain_to_irq() - Translate from a hardware irq to a linux irq number
*
* Returns the linux irq number associated with a hardware irq. By default,
* the mapping is irq == domain->irq_base + hwirq, but this mapping can
* be overridden if the irq_domain implements a .to_irq() hook.
*/
static inline unsigned int irq_domain_to_irq(struct irq_domain *d,
unsigned long hwirq)
#ifdef CONFIG_IRQ_DOMAIN
struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
unsigned int size,
unsigned int first_irq,
irq_hw_number_t first_hwirq,
const struct irq_domain_ops *ops,
void *host_data);
struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
unsigned int size,
const struct irq_domain_ops *ops,
void *host_data);
struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
unsigned int max_irq,
const struct irq_domain_ops *ops,
void *host_data);
struct irq_domain *irq_domain_add_tree(struct device_node *of_node,
const struct irq_domain_ops *ops,
void *host_data);
extern struct irq_domain *irq_find_host(struct device_node *node);
extern void irq_set_default_host(struct irq_domain *host);
static inline struct irq_domain *irq_domain_add_legacy_isa(
struct device_node *of_node,
const struct irq_domain_ops *ops,
void *host_data)
{
if (d->ops->to_irq)
return d->ops->to_irq(d, hwirq);
if (WARN_ON(hwirq < d->hwirq_base))
return 0;
return d->irq_base + hwirq - d->hwirq_base;
return irq_domain_add_legacy(of_node, NUM_ISA_INTERRUPTS, 0, 0, ops,
host_data);
}
extern struct irq_domain *irq_find_host(struct device_node *node);
extern void irq_set_default_host(struct irq_domain *host);
#define irq_domain_for_each_hwirq(d, hw) \
for (hw = d->hwirq_base; hw < d->hwirq_base + d->nr_irq; hw++)
#define irq_domain_for_each_irq(d, hw, irq) \
for (hw = d->hwirq_base, irq = irq_domain_to_irq(d, hw); \
hw < d->hwirq_base + d->nr_irq; \
hw++, irq = irq_domain_to_irq(d, hw))
extern unsigned int irq_create_mapping(struct irq_domain *host,
irq_hw_number_t hwirq);
extern void irq_dispose_mapping(unsigned int virq);
extern unsigned int irq_find_mapping(struct irq_domain *host,
irq_hw_number_t hwirq);
extern unsigned int irq_create_direct_mapping(struct irq_domain *host);
extern void irq_radix_revmap_insert(struct irq_domain *host, unsigned int virq,
irq_hw_number_t hwirq);
extern unsigned int irq_radix_revmap_lookup(struct irq_domain *host,
irq_hw_number_t hwirq);
extern unsigned int irq_linear_revmap(struct irq_domain *host,
irq_hw_number_t hwirq);
extern int irq_domain_add(struct irq_domain *domain);
extern void irq_domain_del(struct irq_domain *domain);
extern void irq_domain_register(struct irq_domain *domain);
extern void irq_domain_register_irq(struct irq_domain *domain, int hwirq);
extern void irq_domain_unregister(struct irq_domain *domain);
extern void irq_domain_unregister_irq(struct irq_domain *domain, int hwirq);
extern int irq_domain_find_free_range(unsigned int from, unsigned int cnt);
extern const struct irq_domain_ops irq_domain_simple_ops;
extern struct irq_domain_ops irq_domain_simple_ops;
#endif /* CONFIG_IRQ_DOMAIN */
/* stock xlate functions */
int irq_domain_xlate_onecell(struct irq_domain *d, struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type);
int irq_domain_xlate_twocell(struct irq_domain *d, struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type);
int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq, unsigned int *out_type);
#if defined(CONFIG_IRQ_DOMAIN) && defined(CONFIG_OF_IRQ)
extern void irq_domain_add_simple(struct device_node *controller, int irq_base);
#if defined(CONFIG_OF_IRQ)
extern void irq_domain_generate_simple(const struct of_device_id *match,
u64 phys_base, unsigned int irq_start);
#else /* CONFIG_IRQ_DOMAIN && CONFIG_OF_IRQ */
#else /* CONFIG_OF_IRQ */
static inline void irq_domain_generate_simple(const struct of_device_id *match,
u64 phys_base, unsigned int irq_start) { }
#endif /* CONFIG_IRQ_DOMAIN && CONFIG_OF_IRQ */
#endif /* !CONFIG_OF_IRQ */
#else /* CONFIG_IRQ_DOMAIN */
static inline void irq_dispose_mapping(unsigned int virq) { }
#endif /* !CONFIG_IRQ_DOMAIN */
#endif /* _LINUX_IRQDOMAIN_H */
+700 -232
View File
File diff suppressed because it is too large Load Diff