summaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-spear/shirq.c
diff options
context:
space:
mode:
authorShiraz Hashim <shiraz.hashim@st.com>2012-08-03 15:33:10 +0530
committerViresh Kumar <viresh.kumar@linaro.org>2012-11-26 16:55:32 +0530
commit80515a5a2e3c35e2994105f19af27650e8a16c51 (patch)
treeb117ab7aa811d0da139fc03aa4f906cbc5b560cb /arch/arm/plat-spear/shirq.c
parent300a6856324a56955ab909e1dca93dabb8464c8a (diff)
downloadop-kernel-dev-80515a5a2e3c35e2994105f19af27650e8a16c51.zip
op-kernel-dev-80515a5a2e3c35e2994105f19af27650e8a16c51.tar.gz
ARM: SPEAr3xx: shirq: simplify and move the shared irq multiplexor to DT
SPEAr3xx architecture includes shared/multiplexed irqs for certain set of devices. The multiplexor provides a single interrupt to parent interrupt controller (VIC) on behalf of a group of devices. There can be multiple groups available on SPEAr3xx variants but not exceeding 4. The number of devices in a group can differ, further they may share same set of status/mask registers spanning across different bit masks. Also in some cases the group may not have enable or other registers. This makes software little complex. Present implementation was non-DT and had few complex data structures to decipher banks, number of irqs supported, mask and registers involved. This patch simplifies the overall design and convert it in to DT. It also removes all registration from individual SoC files and bring them in to common shirq.c. Also updated the corresponding documentation for DT binding of shirq. Signed-off-by: Shiraz Hashim <shiraz.hashim@st.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Diffstat (limited to 'arch/arm/plat-spear/shirq.c')
-rw-r--r--arch/arm/plat-spear/shirq.c297
1 files changed, 247 insertions, 50 deletions
diff --git a/arch/arm/plat-spear/shirq.c b/arch/arm/plat-spear/shirq.c
index 853e891e..955c724 100644
--- a/arch/arm/plat-spear/shirq.c
+++ b/arch/arm/plat-spear/shirq.c
@@ -10,56 +10,182 @@
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/spinlock.h>
#include <plat/shirq.h>
-struct spear_shirq *shirq;
static DEFINE_SPINLOCK(lock);
-static void shirq_irq_mask(struct irq_data *d)
+/* spear300 shared irq registers offsets and masks */
+#define SPEAR300_INT_ENB_MASK_REG 0x54
+#define SPEAR300_INT_STS_MASK_REG 0x58
+
+static struct spear_shirq spear300_shirq_ras1 = {
+ .irq_nr = 9,
+ .irq_bit_off = 0,
+ .regs = {
+ .enb_reg = SPEAR300_INT_ENB_MASK_REG,
+ .status_reg = SPEAR300_INT_STS_MASK_REG,
+ .clear_reg = -1,
+ },
+};
+
+static struct spear_shirq *spear300_shirq_blocks[] = {
+ &spear300_shirq_ras1,
+};
+
+/* spear310 shared irq registers offsets and masks */
+#define SPEAR310_INT_STS_MASK_REG 0x04
+
+static struct spear_shirq spear310_shirq_ras1 = {
+ .irq_nr = 8,
+ .irq_bit_off = 0,
+ .regs = {
+ .enb_reg = -1,
+ .status_reg = SPEAR310_INT_STS_MASK_REG,
+ .clear_reg = -1,
+ },
+};
+
+static struct spear_shirq spear310_shirq_ras2 = {
+ .irq_nr = 5,
+ .irq_bit_off = 8,
+ .regs = {
+ .enb_reg = -1,
+ .status_reg = SPEAR310_INT_STS_MASK_REG,
+ .clear_reg = -1,
+ },
+};
+
+static struct spear_shirq spear310_shirq_ras3 = {
+ .irq_nr = 1,
+ .irq_bit_off = 13,
+ .regs = {
+ .enb_reg = -1,
+ .status_reg = SPEAR310_INT_STS_MASK_REG,
+ .clear_reg = -1,
+ },
+};
+
+static struct spear_shirq spear310_shirq_intrcomm_ras = {
+ .irq_nr = 3,
+ .irq_bit_off = 14,
+ .regs = {
+ .enb_reg = -1,
+ .status_reg = SPEAR310_INT_STS_MASK_REG,
+ .clear_reg = -1,
+ },
+};
+
+static struct spear_shirq *spear310_shirq_blocks[] = {
+ &spear310_shirq_ras1,
+ &spear310_shirq_ras2,
+ &spear310_shirq_ras3,
+ &spear310_shirq_intrcomm_ras,
+};
+
+/* spear320 shared irq registers offsets and masks */
+#define SPEAR320_INT_STS_MASK_REG 0x04
+#define SPEAR320_INT_CLR_MASK_REG 0x04
+#define SPEAR320_INT_ENB_MASK_REG 0x08
+
+static struct spear_shirq spear320_shirq_ras1 = {
+ .irq_nr = 3,
+ .irq_bit_off = 7,
+ .regs = {
+ .enb_reg = -1,
+ .status_reg = SPEAR320_INT_STS_MASK_REG,
+ .clear_reg = SPEAR320_INT_CLR_MASK_REG,
+ .reset_to_clear = 1,
+ },
+};
+
+static struct spear_shirq spear320_shirq_ras2 = {
+ .irq_nr = 1,
+ .irq_bit_off = 10,
+ .regs = {
+ .enb_reg = -1,
+ .status_reg = SPEAR320_INT_STS_MASK_REG,
+ .clear_reg = SPEAR320_INT_CLR_MASK_REG,
+ .reset_to_clear = 1,
+ },
+};
+
+static struct spear_shirq spear320_shirq_ras3 = {
+ .irq_nr = 3,
+ .irq_bit_off = 0,
+ .invalid_irq = 1,
+ .regs = {
+ .enb_reg = SPEAR320_INT_ENB_MASK_REG,
+ .reset_to_enb = 1,
+ .status_reg = SPEAR320_INT_STS_MASK_REG,
+ .clear_reg = SPEAR320_INT_CLR_MASK_REG,
+ .reset_to_clear = 1,
+ },
+};
+
+static struct spear_shirq spear320_shirq_intrcomm_ras = {
+ .irq_nr = 11,
+ .irq_bit_off = 11,
+ .regs = {
+ .enb_reg = -1,
+ .status_reg = SPEAR320_INT_STS_MASK_REG,
+ .clear_reg = SPEAR320_INT_CLR_MASK_REG,
+ .reset_to_clear = 1,
+ },
+};
+
+static struct spear_shirq *spear320_shirq_blocks[] = {
+ &spear320_shirq_ras3,
+ &spear320_shirq_ras1,
+ &spear320_shirq_ras2,
+ &spear320_shirq_intrcomm_ras,
+};
+
+static void shirq_irq_mask_unmask(struct irq_data *d, bool mask)
{
struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
- u32 val, id = d->irq - shirq->dev_config[0].virq;
+ u32 val, offset = d->irq - shirq->irq_base;
unsigned long flags;
- if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1)
+ if (shirq->regs.enb_reg == -1)
return;
spin_lock_irqsave(&lock, flags);
- val = readl(shirq->regs.base + shirq->regs.enb_reg);
- if (shirq->regs.reset_to_enb)
- val |= shirq->dev_config[id].enb_mask;
+ val = readl(shirq->base + shirq->regs.enb_reg);
+
+ if (mask ^ shirq->regs.reset_to_enb)
+ val &= ~(0x1 << shirq->irq_bit_off << offset);
else
- val &= ~(shirq->dev_config[id].enb_mask);
- writel(val, shirq->regs.base + shirq->regs.enb_reg);
+ val |= 0x1 << shirq->irq_bit_off << offset;
+
+ writel(val, shirq->base + shirq->regs.enb_reg);
spin_unlock_irqrestore(&lock, flags);
+
}
-static void shirq_irq_unmask(struct irq_data *d)
+static void shirq_irq_mask(struct irq_data *d)
{
- struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
- u32 val, id = d->irq - shirq->dev_config[0].virq;
- unsigned long flags;
-
- if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1)
- return;
+ shirq_irq_mask_unmask(d, 1);
+}
- spin_lock_irqsave(&lock, flags);
- val = readl(shirq->regs.base + shirq->regs.enb_reg);
- if (shirq->regs.reset_to_enb)
- val &= ~(shirq->dev_config[id].enb_mask);
- else
- val |= shirq->dev_config[id].enb_mask;
- writel(val, shirq->regs.base + shirq->regs.enb_reg);
- spin_unlock_irqrestore(&lock, flags);
+static void shirq_irq_unmask(struct irq_data *d)
+{
+ shirq_irq_mask_unmask(d, 0);
}
static struct irq_chip shirq_chip = {
- .name = "spear_shirq",
+ .name = "spear-shirq",
.irq_ack = shirq_irq_mask,
.irq_mask = shirq_irq_mask,
.irq_unmask = shirq_irq_unmask,
@@ -67,52 +193,123 @@ static struct irq_chip shirq_chip = {
static void shirq_handler(unsigned irq, struct irq_desc *desc)
{
- u32 i, val, mask;
+ u32 i, j, val, mask, tmp;
+ struct irq_chip *chip;
struct spear_shirq *shirq = irq_get_handler_data(irq);
- desc->irq_data.chip->irq_ack(&desc->irq_data);
- while ((val = readl(shirq->regs.base + shirq->regs.status_reg) &
- shirq->regs.status_reg_mask)) {
- for (i = 0; (i < shirq->dev_count) && val; i++) {
- if (!(shirq->dev_config[i].status_mask & val))
+ chip = irq_get_chip(irq);
+ chip->irq_ack(&desc->irq_data);
+
+ mask = ((0x1 << shirq->irq_nr) - 1) << shirq->irq_bit_off;
+ while ((val = readl(shirq->base + shirq->regs.status_reg) &
+ mask)) {
+
+ val >>= shirq->irq_bit_off;
+ for (i = 0, j = 1; i < shirq->irq_nr; i++, j <<= 1) {
+
+ if (!(j & val))
continue;
- generic_handle_irq(shirq->dev_config[i].virq);
+ generic_handle_irq(shirq->irq_base + i);
/* clear interrupt */
- val &= ~shirq->dev_config[i].status_mask;
- if ((shirq->regs.clear_reg == -1) ||
- shirq->dev_config[i].clear_mask == -1)
+ if (shirq->regs.clear_reg == -1)
continue;
- mask = readl(shirq->regs.base + shirq->regs.clear_reg);
+
+ tmp = readl(shirq->base + shirq->regs.clear_reg);
if (shirq->regs.reset_to_clear)
- mask &= ~shirq->dev_config[i].clear_mask;
+ tmp &= ~(j << shirq->irq_bit_off);
else
- mask |= shirq->dev_config[i].clear_mask;
- writel(mask, shirq->regs.base + shirq->regs.clear_reg);
+ tmp |= (j << shirq->irq_bit_off);
+ writel(tmp, shirq->base + shirq->regs.clear_reg);
}
}
- desc->irq_data.chip->irq_unmask(&desc->irq_data);
+ chip->irq_unmask(&desc->irq_data);
}
-int spear_shirq_register(struct spear_shirq *shirq)
+static void __init spear_shirq_register(struct spear_shirq *shirq)
{
int i;
- if (!shirq || !shirq->dev_config || !shirq->regs.base)
- return -EFAULT;
-
- if (!shirq->dev_count)
- return -EINVAL;
+ if (shirq->invalid_irq)
+ return;
irq_set_chained_handler(shirq->irq, shirq_handler);
- for (i = 0; i < shirq->dev_count; i++) {
- irq_set_chip_and_handler(shirq->dev_config[i].virq,
+ for (i = 0; i < shirq->irq_nr; i++) {
+ irq_set_chip_and_handler(shirq->irq_base + i,
&shirq_chip, handle_simple_irq);
- set_irq_flags(shirq->dev_config[i].virq, IRQF_VALID);
- irq_set_chip_data(shirq->dev_config[i].virq, shirq);
+ set_irq_flags(shirq->irq_base + i, IRQF_VALID);
+ irq_set_chip_data(shirq->irq_base + i, shirq);
}
irq_set_handler_data(shirq->irq, shirq);
+}
+
+static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr,
+ struct device_node *np)
+{
+ int i, irq_base, hwirq = 0, irq_nr = 0;
+ static struct irq_domain *shirq_domain;
+ void __iomem *base;
+
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("%s: failed to map shirq registers\n", __func__);
+ return -ENXIO;
+ }
+
+ for (i = 0; i < block_nr; i++)
+ irq_nr += shirq_blocks[i]->irq_nr;
+
+ irq_base = irq_alloc_descs(-1, 0, irq_nr, 0);
+ if (IS_ERR_VALUE(irq_base)) {
+ pr_err("%s: irq desc alloc failed\n", __func__);
+ goto err_unmap;
+ }
+
+ shirq_domain = irq_domain_add_legacy(np, irq_nr, irq_base, 0,
+ &irq_domain_simple_ops, NULL);
+ if (WARN_ON(!shirq_domain)) {
+ pr_warn("%s: irq domain init failed\n", __func__);
+ goto err_free_desc;
+ }
+
+ for (i = 0; i < block_nr; i++) {
+ shirq_blocks[i]->base = base;
+ shirq_blocks[i]->irq_base = irq_find_mapping(shirq_domain,
+ hwirq);
+ shirq_blocks[i]->irq = irq_of_parse_and_map(np, i);
+
+ spear_shirq_register(shirq_blocks[i]);
+ hwirq += shirq_blocks[i]->irq_nr;
+ }
+
return 0;
+
+err_free_desc:
+ irq_free_descs(irq_base, irq_nr);
+err_unmap:
+ iounmap(base);
+ return -ENXIO;
+}
+
+int __init spear300_shirq_of_init(struct device_node *np,
+ struct device_node *parent)
+{
+ return shirq_init(spear300_shirq_blocks,
+ ARRAY_SIZE(spear300_shirq_blocks), np);
+}
+
+int __init spear310_shirq_of_init(struct device_node *np,
+ struct device_node *parent)
+{
+ return shirq_init(spear310_shirq_blocks,
+ ARRAY_SIZE(spear310_shirq_blocks), np);
+}
+
+int __init spear320_shirq_of_init(struct device_node *np,
+ struct device_node *parent)
+{
+ return shirq_init(spear320_shirq_blocks,
+ ARRAY_SIZE(spear320_shirq_blocks), np);
}
OpenPOWER on IntegriCloud