summaryrefslogtreecommitdiff
path: root/drivers/platform/sprd/adie_irq.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/platform/sprd/adie_irq.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/platform/sprd/adie_irq.c')
-rw-r--r--drivers/platform/sprd/adie_irq.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/drivers/platform/sprd/adie_irq.c b/drivers/platform/sprd/adie_irq.c
new file mode 100644
index 00000000..df4ac0ca
--- /dev/null
+++ b/drivers/platform/sprd/adie_irq.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ * Author: steve.zhan <steve.zhan@spreadtrum.com>
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/irqflags.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#ifdef CONFIG_OF
+#include <linux/irqdomain.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#endif
+
+#include <asm/delay.h>
+
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/globalregs.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/irqs.h>
+#include <soc/sprd/sci_glb_regs.h>
+
+//#include <mach/irqs.h>
+
+/* registers definitions for controller ANA_CTL_INT */
+#define ANA_REG_INT_MASK_STATUS (ANA_CTL_INT_BASE + 0x0000)
+#define ANA_REG_INT_RAW_STATUS (ANA_CTL_INT_BASE + 0x0004)
+#define ANA_REG_INT_EN (ANA_CTL_INT_BASE + 0x0008)
+#define ANA_REG_INT_MASK_STATUS_SYNC (ANA_CTL_INT_BASE + 0x000c)
+
+/* bits definitions for register REG_INT_MASK_STATUS */
+#if defined(CONFIG_ARCH_SC8825)
+/* vars definitions for controller ANA_CTL_INT */
+#define MASK_ANA_INT ( 0xFF )
+#elif defined(CONFIG_ARCH_SCX15)
+/* vars definitions for controller ANA_CTL_INT */
+#define MASK_ANA_INT ( 0x3FF )
+#elif defined(CONFIG_ARCH_SCX35)
+/* vars definitions for controller ANA_CTL_INT */
+#define MASK_ANA_INT ( 0x7FF )
+#else
+#error "pls define interrupt number mask value"
+#endif
+
+/* used to define the start irq in xlate fun */
+#define ADIE_SUB_IRQ_START 32
+
+/* used to save vitual irq num */
+static int *virq_table = NULL;
+
+/* the total irq num of adie domain */
+static int irqnums = 0;
+
+static struct irq_domain *irq_domain;
+void sprd_ack_ana_irq(struct irq_data *data)
+{
+ /* nothing to do... */
+}
+
+static void sprd_mask_ana_irq(struct irq_data *data)
+{
+ int offset;
+#ifdef CONFIG_OF
+ int i;
+
+ for (i = 0; i < irqnums; i++) {
+ if (virq_table[i] == data->irq) {
+ offset = i;
+ break;
+ }
+ }
+ if (i == irqnums)
+ return;
+#else
+ offset = data->irq - IRQ_ANA_INT_START;
+#endif
+ pr_debug("%s %d\n", __FUNCTION__, data->irq);
+ sci_adi_write(ANA_REG_INT_EN, 0, BIT(offset) & MASK_ANA_INT);
+}
+
+static void sprd_unmask_ana_irq(struct irq_data *data)
+{
+ int offset;
+#ifdef CONFIG_OF
+ int i;
+
+ for (i = 0; i < irqnums; i++) {
+ if (virq_table[i] == data->irq) {
+ offset = i;
+ break;
+ }
+ }
+ if (i == irqnums)
+ return;
+#else
+ offset = data->irq - IRQ_ANA_INT_START;
+#endif
+ pr_debug("%s %d\n", __FUNCTION__, data->irq);
+ sci_adi_write(ANA_REG_INT_EN, BIT(offset) & MASK_ANA_INT, 0);
+}
+
+static struct irq_chip sprd_muxed_ana_chip = {
+ .name = "irq-ANA",
+ .irq_ack = sprd_ack_ana_irq,
+ .irq_mask = sprd_mask_ana_irq,
+ .irq_unmask = sprd_unmask_ana_irq,
+};
+
+static irqreturn_t sprd_muxed_ana_handler(int irq, void *dev_id)
+{
+ uint32_t irq_ana, status;
+ int i;
+
+ status = sci_adi_read(ANA_REG_INT_MASK_STATUS) & MASK_ANA_INT;
+ pr_debug("%s %d 0x%08x\n", __FUNCTION__, irq, status);
+ while (status) {
+ i = __ffs(status);
+ status &= ~(1 << i);
+#ifndef CONFIG_OF
+ irq_ana = IRQ_ANA_INT_START + i;
+#else
+ irq_ana = virq_table[i];
+#endif
+ pr_debug("%s generic_handle_irq %d\n", __FUNCTION__, irq_ana);
+ generic_handle_irq(irq_ana);
+ }
+ return IRQ_HANDLED;
+}
+
+static struct irqaction __adie_mux_irq = {
+ .name = "adie_mux",
+ .flags = IRQF_DISABLED | IRQF_NO_SUSPEND,
+ .handler = sprd_muxed_ana_handler,
+};
+
+void __init ana_init_irq(void)
+{
+ int n;
+
+ for (n = IRQ_ANA_INT_START; n < IRQ_ANA_INT_START + NR_ANA_IRQS; n++) {
+ irq_set_chip_and_handler(n, &sprd_muxed_ana_chip,
+ handle_level_irq);
+ set_irq_flags(n, IRQF_VALID);
+ }
+ setup_irq(IRQ_ANA_INT, &__adie_mux_irq);
+
+}
+
+#ifdef CONFIG_OF
+#define IRQCHIP_DECLARE(name,compstr,fn) \
+ static const struct of_device_id irqchip_of_match_##name \
+ __used __section(__irqchip_of_table) \
+ = { .compatible = compstr, .data = fn }
+
+static int adie_irq_domain_xlate(struct irq_domain *d,
+ struct device_node *ctrlr,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq, unsigned int *out_type)
+{
+ if (WARN_ON(intsize < 1))
+ return -EINVAL;
+ *out_hwirq = intspec[0] + ADIE_SUB_IRQ_START;
+ *out_type = (intsize > 1) ? intspec[1] : IRQ_TYPE_NONE;
+ return 0;
+}
+
+static int adie_irq_domain_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ virq_table[hw-ADIE_SUB_IRQ_START] = virq;
+ irq_set_chip_and_handler(virq, &sprd_muxed_ana_chip,
+ handle_level_irq);
+ set_irq_flags(virq, IRQF_VALID);
+
+ return 0;
+}
+
+const struct irq_domain_ops adie_irq_domain_ops = {
+ .xlate = adie_irq_domain_xlate,
+ .map = adie_irq_domain_map,
+};
+int __init adi_of_init(struct device_node *node, struct device_node *parent)
+{
+ struct resource res;
+ int irq;
+
+ if (WARN_ON(!node))
+ return -ENODEV;
+
+ if(of_address_to_resource(node, 0, &res)){
+ pr_err("no reg of property specified for adi\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(node, "sprd,irqnums", &irqnums)){
+ pr_err("no sprd,irqnums of property specified");
+ BUG();
+ }
+
+ irq_domain = irq_domain_add_linear(node, irqnums + ADIE_SUB_IRQ_START,
+ &adie_irq_domain_ops, NULL);
+ BUG_ON(!irq_domain);
+
+ virq_table = kmalloc(sizeof(int)*irqnums, GFP_KERNEL);
+ BUG_ON(!virq_table);
+
+ if (parent) {
+ irq = irq_of_parse_and_map(node, 0);
+ setup_irq(irq, &__adie_mux_irq);
+ }
+
+ return 0;
+}
+IRQCHIP_DECLARE(sprd_adi, "sprd,adi-bus", adi_of_init);
+
+#endif