mirror of https://github.com/OpenIPC/firmware.git
				
				
				
			
		
			
				
	
	
		
			354 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Diff
		
	
	
			
		
		
	
	
			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
 |