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 f132c6cf77251e011e1dad0ec88c0b1fda16d5aa
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>
diff --git a/drivers/spmi/qpnp-int.c b/drivers/spmi/qpnp-int.c
index 2998c01..f44d782 100644
--- a/drivers/spmi/qpnp-int.c
+++ b/drivers/spmi/qpnp-int.c
@@ -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_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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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;
}