firmware/br-ext-chip-ingenic/board/t40/kernel/patches/00000-drivers_irqchip_irq-i...

354 lines
11 KiB
Diff

diff -drupN a/drivers/irqchip/irq-ingenic.c b/drivers/irqchip/irq-ingenic.c
--- a/drivers/irqchip/irq-ingenic.c 2017-10-21 18:09:07.000000000 +0300
+++ b/drivers/irqchip/irq-ingenic.c 2022-06-09 05:02:29.000000000 +0300
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
- * JZ4740 platform IRQ support
+ * ingenic platform IRQ support
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -18,159 +18,253 @@
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
-#include <linux/irqchip.h>
-#include <linux/irqchip/ingenic.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/of.h>
#include <linux/timex.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/irqchip.h>
#include <asm/io.h>
-#include <asm/mach-jz4740/irq.h>
+#include <asm/mipsregs.h>
+#include <asm/irq_cpu.h>
+#include <asm/irq.h>
-struct ingenic_intc_data {
- void __iomem *base;
- unsigned num_chips;
-};
+static void __iomem *intc_base;
+static unsigned ingenic_num_chips;
-#define JZ_REG_INTC_STATUS 0x00
-#define JZ_REG_INTC_MASK 0x04
-#define JZ_REG_INTC_SET_MASK 0x08
-#define JZ_REG_INTC_CLEAR_MASK 0x0c
-#define JZ_REG_INTC_PENDING 0x10
-#define CHIP_SIZE 0x20
+#define TRACE_IRQ 1
+#define PART_OFF 0x20
-static irqreturn_t intc_cascade(int irq, void *data)
+#define ISR_OFF (0x00)
+#define IMR_OFF (0x04)
+#define IMSR_OFF (0x08)
+#define IMCR_OFF (0x0c)
+#define IPR_OFF (0x10)
+
+#define DSR0_OFF (0x34)
+#define DMR0_OFF (0x38)
+#define DPR0_OFF (0x3c)
+#define DSR1_OFF (0x40)
+#define DMR1_OFF (0x44)
+#define DPR1_OFF (0x48)
+
+static irqreturn_t ingenic_cascade(int irq, void *data)
{
- struct ingenic_intc_data *intc = irq_get_handler_data(irq);
uint32_t irq_reg;
unsigned i;
- for (i = 0; i < intc->num_chips; i++) {
- irq_reg = readl(intc->base + (i * CHIP_SIZE) +
- JZ_REG_INTC_PENDING);
+ for (i = 0; i < ingenic_num_chips; i++) {
+ irq_reg = readl(intc_base + (i * PART_OFF) + IPR_OFF);
if (!irq_reg)
continue;
-
- generic_handle_irq(__fls(irq_reg) + (i * 32) + JZ4740_IRQ_BASE);
+ generic_handle_irq(__fls(irq_reg) + (i * 32) + XBURST_SOC_IRQ_BASE);
}
return IRQ_HANDLED;
}
-static void intc_irq_set_mask(struct irq_chip_generic *gc, uint32_t mask)
+static void ingenic_irq_pm_set_mask(struct irq_chip_generic *gc, uint32_t unmask)
{
struct irq_chip_regs *regs = &gc->chip_types->regs;
- writel(mask, gc->reg_base + regs->enable);
- writel(~mask, gc->reg_base + regs->disable);
+ writel(unmask, gc->reg_base + regs->enable);
+ writel(~unmask, gc->reg_base + regs->disable);
}
-void ingenic_intc_irq_suspend(struct irq_data *data)
+void ingenic_irq_suspend(struct irq_data *data)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
- intc_irq_set_mask(gc, gc->wake_active);
+ ingenic_irq_pm_set_mask(gc, gc->wake_active);
}
-void ingenic_intc_irq_resume(struct irq_data *data)
+void ingenic_irq_resume(struct irq_data *data)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
- intc_irq_set_mask(gc, gc->mask_cache);
+ ingenic_irq_pm_set_mask(gc, gc->mask_cache);
}
-static struct irqaction intc_cascade_action = {
- .handler = intc_cascade,
- .name = "SoC intc cascade interrupt",
+static struct irqaction ingenic_cascade_action = {
+ .handler = ingenic_cascade,
+ .name = "ingenic cascade interrupt",
};
-static int __init ingenic_intc_of_init(struct device_node *node,
- unsigned num_chips)
+static int __init ingenic_intc_of_init(struct device_node *node, unsigned num_chips)
{
- struct ingenic_intc_data *intc;
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
struct irq_domain *domain;
- int parent_irq, err = 0;
+ struct resource res;
+ int ret, parent_irq;
unsigned i;
- intc = kzalloc(sizeof(*intc), GFP_KERNEL);
- if (!intc) {
- err = -ENOMEM;
- goto out_err;
- }
-
parent_irq = irq_of_parse_and_map(node, 0);
- if (!parent_irq) {
- err = -EINVAL;
- goto out_free;
- }
+ if (!parent_irq)
+ return -EINVAL;
- err = irq_set_handler_data(parent_irq, intc);
- if (err)
- goto out_unmap_irq;
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret < 0)
+ return ret;
- intc->num_chips = num_chips;
- intc->base = of_iomap(node, 0);
- if (!intc->base) {
- err = -ENODEV;
- goto out_unmap_irq;
- }
+ ingenic_num_chips = num_chips;
+ intc_base = ioremap(res.start,
+ ((num_chips - 1) * PART_OFF) + 0x14);
+
+ domain = irq_domain_add_legacy(node, num_chips * 32, XBURST_SOC_IRQ_BASE, 0,
+ &irq_domain_simple_ops, NULL);
+ if (!domain)
+ pr_warn("unable to register IRQ domain\n");
for (i = 0; i < num_chips; i++) {
/* Mask all irqs */
- writel(0xffffffff, intc->base + (i * CHIP_SIZE) +
- JZ_REG_INTC_SET_MASK);
+ writel(0xffffffff, intc_base + (i * PART_OFF) + IMSR_OFF);
- gc = irq_alloc_generic_chip("INTC", 1,
- JZ4740_IRQ_BASE + (i * 32),
- intc->base + (i * CHIP_SIZE),
+ gc = irq_alloc_generic_chip("INTC", 1, XBURST_SOC_IRQ_BASE + (i * 32),
+ intc_base + (i * PART_OFF),
handle_level_irq);
-
gc->wake_enabled = IRQ_MSK(32);
-
ct = gc->chip_types;
- ct->regs.enable = JZ_REG_INTC_CLEAR_MASK;
- ct->regs.disable = JZ_REG_INTC_SET_MASK;
+ ct->regs.enable = IMCR_OFF;
+ ct->regs.disable = IMSR_OFF;
ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
ct->chip.irq_mask = irq_gc_mask_disable_reg;
ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
ct->chip.irq_set_wake = irq_gc_set_wake;
- ct->chip.irq_suspend = ingenic_intc_irq_suspend;
- ct->chip.irq_resume = ingenic_intc_irq_resume;
-
- irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0,
- IRQ_NOPROBE | IRQ_LEVEL);
+ ct->chip.irq_suspend = ingenic_irq_suspend;
+ ct->chip.irq_resume = ingenic_irq_resume;
+#ifdef CONFIG_SPARSE_IRQ
+ irq_alloc_descs(gc->irq_base, -1, gc->num_ct, of_node_to_nid(domain->of_node));
+#endif
+ irq_setup_generic_chip(gc, IRQ_MSK(32), 0, 0, IRQ_NOPROBE | IRQ_LEVEL);
}
- domain = irq_domain_add_legacy(node, num_chips * 32, JZ4740_IRQ_BASE, 0,
- &irq_domain_simple_ops, NULL);
- if (!domain)
- pr_warn("unable to register IRQ domain\n");
+ setup_irq(parent_irq, &ingenic_cascade_action);
- setup_irq(parent_irq, &intc_cascade_action);
+ /* enable cpu interrupt mask */
+ set_c0_status(IE_IRQ0 | IE_IRQ1);
return 0;
+}
-out_unmap_irq:
- irq_dispose_mapping(parent_irq);
-out_free:
- kfree(intc);
-out_err:
- return err;
+static int __init ingenic_intc_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return ingenic_intc_of_init(node, INTC_CHIP_NUM);
}
+IRQCHIP_DECLARE(ingenic_intc, "ingenic,ingenic-intc", ingenic_intc_init);
-static int __init intc_1chip_of_init(struct device_node *node,
- struct device_node *parent)
+#if 0
+/*************************************************************************
+if nand was config:
+ After cpu was woke up,the mcu should be handler
+those interruptions of GPIO0,so that it was convenient to read/write nand,
+the process of GPIO's interruption as follows
+ |----------------------------------|
+ | GPIO |
+ | | |
+ | |--****--INTC---------->| |
+ | | MASK UNMASK | |
+ | CPU MCU |
+ | | |
+ | MAILEBOX |
+ | | <---------------------| |
+ | | |
+ | ingenicdma |
+ | | |
+ | soft intc |
+ | | |
+ | handler func of intc |
+ |__________________________________|
+
+ On the contraty, when cpu will go to sleep, the cpu should be handler
+GPIO0's interruptions, it was convenient to the next step, which is that cpu was
+woke up.
+ GPIO
+ |
+ |---------INTC-------->|
+ | unmask mask |
+ CPU MCU
+ |
+ |
+ handler func of intc
+************************************************************************/
+
+#define IRQ_IS_GGPIO(irq) (irq >= IRQ_GPIO0 && irq <= IRQ_GPIO_END)
+extern void mcu_gpio_register(unsigned int ggpio, unsigned int reg);
+extern void mcu_gpio_unregister(unsigned int ggpio);
+
+void ingenic_change_irq_destination(int irq, bool cpu_to_mcu, unsigned gpio_pending_reg)
{
- return ingenic_intc_of_init(node, 1);
+ int reg = 0;
+ if (WARN((irq < 0 || irq > 63),
+ "irq %d not support switch between cpu and mcu\n",
+ irq))
+ return;
+
+ if (cpu_to_mcu) {
+ if (irq < 32) {
+ writel((1 << irq), intc_base + IMSR_OFF);
+ if (IRQ_IS_GGPIO(irq))
+ mcu_gpio_register((irq-IRQ_GPIO0), gpio_pending_reg);
+ reg = readl(intc_base + DMR0_OFF);
+ writel((reg & (~(1 << irq))), intc_base + DMR0_OFF);
+ } else {
+ writel((1 << (irq - 32)), intc_base + PART_OFF + IMSR_OFF);
+ reg = readl(intc_base + DMR1_OFF);
+ writel((reg & (~(1 << (irq - 32)))), intc_base + DMR1_OFF);
+ }
+ } else {
+ if (irq < 32) {
+ reg = readl(intc_base + DMR0_OFF);
+ writel((reg | (1 << irq)), intc_base + DMR0_OFF);
+ if (IRQ_IS_GGPIO(irq))
+ mcu_gpio_unregister((irq-IRQ_GPIO0));
+ writel((1 << irq), intc_base + IMCR_OFF);
+ } else {
+ reg = readl(intc_base + DMR1_OFF);
+ writel((reg | (1 << (irq - 32))), intc_base + DMR1_OFF);
+ writel((1 << (irq - 32)), intc_base + PART_OFF + IMCR_OFF);
+ }
+ }
+ return;
+}
+EXPORT_SYMBOL_GPL(ingenic_change_irq_destination);
+#endif
+#ifdef CONFIG_DEBUG_FS
+static inline void intc_seq_reg(struct seq_file *s, const char *name,
+ unsigned int reg)
+{
+ seq_printf(s, "%s:\t\t%08x\n", name, readl(intc_base + reg));
}
-IRQCHIP_DECLARE(jz4740_intc, "ingenic,jz4740-intc", intc_1chip_of_init);
-static int __init intc_2chip_of_init(struct device_node *node,
- struct device_node *parent)
+static int intc_regs_show(struct seq_file *s, void *unused)
{
- return ingenic_intc_of_init(node, 2);
+ intc_seq_reg(s, "Status", ISR_OFF);
+ intc_seq_reg(s, "Mask", IMR_OFF);
+ intc_seq_reg(s, "Pending", IPR_OFF);
+
+ return 0;
}
-IRQCHIP_DECLARE(jz4770_intc, "ingenic,jz4770-intc", intc_2chip_of_init);
-IRQCHIP_DECLARE(jz4775_intc, "ingenic,jz4775-intc", intc_2chip_of_init);
-IRQCHIP_DECLARE(jz4780_intc, "ingenic,jz4780-intc", intc_2chip_of_init);
+
+static int intc_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, intc_regs_show, NULL);
+}
+
+static const struct file_operations intc_regs_operations = {
+ .open = intc_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init intc_debugfs_init(void)
+{
+ (void) debugfs_create_file("intc_regs", S_IFREG | S_IRUGO,
+ NULL, NULL, &intc_regs_operations);
+ return 0;
+}
+subsys_initcall(intc_debugfs_init);
+#endif