diff mbox

[RFC] Using hierarchy irqdomian to implement MTK intpol.

Message ID 1411574696-1881-1-git-send-email-srv_yingjoe.chen@mediatek.com (mailing list archive)
State New, archived
Headers show

Commit Message

Yingjoe Chen Sept. 24, 2014, 4:04 p.m. UTC
From: "Joe.C" <yingjoe.chen@mediatek.com>

Here's the first draft of using hierarchy irqdomain to implement MTK intpol
support. I have tested it and intpol works fine. Before continue, I'd like
to get your comments. This is based on Jiang's hierarchy irqdomian [1] and
my mediatek SoC basic support [2].

Simplified block diagram for interrupt:

    ---------      ---------
 ---| CIRQ  |------|ARM GIC|
 ---|       |------|       |
 ---|       |------|       |
 ---|       |------|       |
 ---|       |------|       |
    ---------      ---------

In device tree, interrupt-parent for other devices is cirq, child of gic.
This describe HW better and allow device to specify polarity as it is sent
by the device. I'm using interrupt-parent to specify irq domain parent in
cirq now. Maybe adding another name like 'interrupt-domain-parent' will be
better.

Currently only set_type is implement in CIRQ, but I think this could extended
to cover all function gic_arch_extn provided.

In order to use hierarchy irq domain, gic can't use legacy irq domain anymore.
If arm,hierarchy-irq-domain property is specified, GIC will work in hierarchy
way and all interrupt must be set by device tree.

One thing worth mention is arg in irq_domain_alloc_irqs. On x86, msi is using
struct irq_alloc_info, to pass data between irq domain. In irq_create_of_mapping(),
of_phandle_args is used. All allocs in irq_domain chain will need to use same
interrupt-cells formats.


[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2014-September/286542.html
[2] http://lists.infradead.org/pipermail/linux-arm-kernel/2014-September/284553.html

Signed-off-by: Joe.C <yingjoe.chen@mediatek.com>
---
 arch/arm/boot/dts/mt8135.dtsi     |  14 +++-
 drivers/irqchip/Makefile          |   1 +
 drivers/irqchip/irq-gic.c         |  53 +++++++++++--
 drivers/irqchip/irq-mt65xx-cirq.c | 158 ++++++++++++++++++++++++++++++++++++++
 include/linux/irq.h               |   5 ++
 kernel/irq/chip.c                 |  28 +++++++
 kernel/irq/irqdomain.c            |  14 ++--
 7 files changed, 256 insertions(+), 17 deletions(-)
 create mode 100644 drivers/irqchip/irq-mt65xx-cirq.c

Comments

Yingjoe Chen Sept. 25, 2014, 2:16 a.m. UTC | #1
Jiang,

Please consider merge the following 2 changes into your next round.

On Thu, 2014-09-25 at 00:04 +0800, Joe.C wrote:
> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
> index e285f3a..01e852b 100644
> --- a/kernel/irq/irqdomain.c
> +++ b/kernel/irq/irqdomain.c
> @@ -467,7 +467,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
>  	struct irq_domain *domain;
>  	irq_hw_number_t hwirq;
>  	unsigned int type = IRQ_TYPE_NONE;
> -	unsigned int virq;
> +	int virq;
>  
>  	domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;
>  	if (!domain) {
> @@ -493,8 +493,8 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
>  	else
>  #endif
>  		virq = irq_create_mapping(domain, hwirq);
> -	if (!virq)
> -		return virq;
> +	if (virq <= 0)
> +		return 0;
>  
>  	/* Set type if specified and different than the current one */
>  	if (type != IRQ_TYPE_NONE &&

irq_of_parse_and_map()/of_irq_to_resource() expect
irq_create_of_mapping() to return 0 when fail. Return error code will
cause of_irq_to_resource crash.


> @@ -716,20 +716,20 @@ const struct irq_domain_ops irq_domain_simple_ops = {
>  };
>  EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
>  
> -static int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
> +static int irq_domain_alloc_descs(int virq, unsigned int cnt,
>  				  irq_hw_number_t hwirq, int node)
>  {
>  	unsigned int hint;
>  
>  	if (virq >= 0) {
> -		virq = irq_alloc_descs(virq, virq, nr_irqs, node);
> +		virq = irq_alloc_descs(virq, virq, cnt, node);
>  	} else {
>  		hint = hwirq % nr_irqs;
>  		if (hint == 0)
>  			hint++;
> -		virq = irq_alloc_descs_from(hint, nr_irqs, node);
> +		virq = irq_alloc_descs_from(hint, cnt, node);
>  		if (virq <= 0 && hint > 1)
> -			virq = irq_alloc_descs_from(1, nr_irqs, node);
> +			virq = irq_alloc_descs_from(1, cnt, node);
>  	}
>  
>  	return virq;

This come from irq_create_mapping(), the original code is using global
nr_irqs. Change to match original behavior.

Joe.C
Jiang Liu Sept. 25, 2014, 3:04 a.m. UTC | #2
Hi Joe,
	Thanks, I will merge them into my next version.
Regards!
Gerry

On 2014/9/25 10:16, Joe.C wrote:
> 
> Jiang,
> 
> Please consider merge the following 2 changes into your next round.
> 
> On Thu, 2014-09-25 at 00:04 +0800, Joe.C wrote:
>> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
>> index e285f3a..01e852b 100644
>> --- a/kernel/irq/irqdomain.c
>> +++ b/kernel/irq/irqdomain.c
>> @@ -467,7 +467,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
>>  	struct irq_domain *domain;
>>  	irq_hw_number_t hwirq;
>>  	unsigned int type = IRQ_TYPE_NONE;
>> -	unsigned int virq;
>> +	int virq;
>>  
>>  	domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;
>>  	if (!domain) {
>> @@ -493,8 +493,8 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
>>  	else
>>  #endif
>>  		virq = irq_create_mapping(domain, hwirq);
>> -	if (!virq)
>> -		return virq;
>> +	if (virq <= 0)
>> +		return 0;
>>  
>>  	/* Set type if specified and different than the current one */
>>  	if (type != IRQ_TYPE_NONE &&
> 
> irq_of_parse_and_map()/of_irq_to_resource() expect
> irq_create_of_mapping() to return 0 when fail. Return error code will
> cause of_irq_to_resource crash.
> 
> 
>> @@ -716,20 +716,20 @@ const struct irq_domain_ops irq_domain_simple_ops = {
>>  };
>>  EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
>>  
>> -static int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
>> +static int irq_domain_alloc_descs(int virq, unsigned int cnt,
>>  				  irq_hw_number_t hwirq, int node)
>>  {
>>  	unsigned int hint;
>>  
>>  	if (virq >= 0) {
>> -		virq = irq_alloc_descs(virq, virq, nr_irqs, node);
>> +		virq = irq_alloc_descs(virq, virq, cnt, node);
>>  	} else {
>>  		hint = hwirq % nr_irqs;
>>  		if (hint == 0)
>>  			hint++;
>> -		virq = irq_alloc_descs_from(hint, nr_irqs, node);
>> +		virq = irq_alloc_descs_from(hint, cnt, node);
>>  		if (virq <= 0 && hint > 1)
>> -			virq = irq_alloc_descs_from(1, nr_irqs, node);
>> +			virq = irq_alloc_descs_from(1, cnt, node);
>>  	}
>>  
>>  	return virq;
> 
> This come from irq_create_mapping(), the original code is using global
> nr_irqs. Change to match original behavior.
> 
> Joe.C
> 
>
Jiang Liu Sept. 25, 2014, 3:12 a.m. UTC | #3
On 2014/9/25 0:04, Joe.C wrote:
> From: "Joe.C" <yingjoe.chen@mediatek.com>
> 
> Here's the first draft of using hierarchy irqdomain to implement MTK intpol
> support. I have tested it and intpol works fine. Before continue, I'd like
> to get your comments. This is based on Jiang's hierarchy irqdomian [1] and
> my mediatek SoC basic support [2].
> 
> Simplified block diagram for interrupt:
> 
>     ---------      ---------
>  ---| CIRQ  |------|ARM GIC|
>  ---|       |------|       |
>  ---|       |------|       |
>  ---|       |------|       |
>  ---|       |------|       |
>     ---------      ---------
> 
> In device tree, interrupt-parent for other devices is cirq, child of gic.
> This describe HW better and allow device to specify polarity as it is sent
> by the device. I'm using interrupt-parent to specify irq domain parent in
> cirq now. Maybe adding another name like 'interrupt-domain-parent' will be
> better.
> 
> Currently only set_type is implement in CIRQ, but I think this could extended
> to cover all function gic_arch_extn provided.
> 
> In order to use hierarchy irq domain, gic can't use legacy irq domain anymore.
> If arm,hierarchy-irq-domain property is specified, GIC will work in hierarchy
> way and all interrupt must be set by device tree.
> 
> One thing worth mention is arg in irq_domain_alloc_irqs. On x86, msi is using
> struct irq_alloc_info, to pass data between irq domain. In irq_create_of_mapping(),
> of_phandle_args is used. All allocs in irq_domain chain will need to use same
> interrupt-cells formats.
> 
> 
> [1] http://lists.infradead.org/pipermail/linux-arm-kernel/2014-September/286542.html
> [2] http://lists.infradead.org/pipermail/linux-arm-kernel/2014-September/284553.html
> 
> Signed-off-by: Joe.C <yingjoe.chen@mediatek.com>
> ---
>  arch/arm/boot/dts/mt8135.dtsi     |  14 +++-
>  drivers/irqchip/Makefile          |   1 +
>  drivers/irqchip/irq-gic.c         |  53 +++++++++++--
>  drivers/irqchip/irq-mt65xx-cirq.c | 158 ++++++++++++++++++++++++++++++++++++++
>  include/linux/irq.h               |   5 ++
>  kernel/irq/chip.c                 |  28 +++++++
>  kernel/irq/irqdomain.c            |  14 ++--
>  7 files changed, 256 insertions(+), 17 deletions(-)
>  create mode 100644 drivers/irqchip/irq-mt65xx-cirq.c
> 
> diff --git a/arch/arm/boot/dts/mt8135.dtsi b/arch/arm/boot/dts/mt8135.dtsi
> index 90a56ad..e19ab1a 100644
> --- a/arch/arm/boot/dts/mt8135.dtsi
> +++ b/arch/arm/boot/dts/mt8135.dtsi
> @@ -18,7 +18,7 @@
>  
>  / {
>  	compatible = "mediatek,mt8135";
> -	interrupt-parent = <&gic>;
> +	interrupt-parent = <&cirq>;
>  
>  	cpu-map {
>  		cluster0 {
> @@ -97,15 +97,25 @@
>  		timer: timer@10008000 {
>  			compatible = "mediatek,mt8135-timer", "mediatek,mt6577-timer";
>  			reg = <0 0x10008000 0 0x80>;
> -			interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>;
> +			interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_LOW>;
>  			clocks = <&system_clk>, <&rtc_clk>;
>  			clock-names = "system-clk", "rtc-clk";
>  		};
>  
> +		cirq: interrupt-controller@10200030 {
> +			compatible = "mediatek,mt8135-cirq", "mediatek,mt6577-cirq";
> +			interrupt-controller;
> +			#interrupt-cells = <3>;
> +			interrupt-parent = <&gic>;
> +			reg = <0 0x10200030 0 0x1c>;
> +		};
> +
>  		gic: interrupt-controller@10211000 {
>  			compatible = "arm,cortex-a15-gic";
>  			interrupt-controller;
>  			#interrupt-cells = <3>;
> +			arm,hierarchy-irq-domain;
> +			interrupt-parent = <&gic>;
>  			reg = <0 0x10211000 0 0x1000>,
>  			      <0 0x10212000 0 0x1000>,
>  			      <0 0x10214000 0 0x2000>,
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index 73052ba..0b34675 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -34,3 +34,4 @@ obj-$(CONFIG_XTENSA)			+= irq-xtensa-pic.o
>  obj-$(CONFIG_XTENSA_MX)			+= irq-xtensa-mx.o
>  obj-$(CONFIG_IRQ_CROSSBAR)		+= irq-crossbar.o
>  obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
> +obj-$(CONFIG_ARCH_MEDIATEK)		+= irq-mt65xx-cirq.o
> diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
> index 4b959e6..d66d707 100644
> --- a/drivers/irqchip/irq-gic.c
> +++ b/drivers/irqchip/irq-gic.c
> @@ -767,22 +767,50 @@ void __init gic_init_physaddr(struct device_node *node)
>  static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
>  				irq_hw_number_t hw)
>  {
> +	irq_domain_set_hwirq_and_chip(d, irq, hw, &gic_chip, d->host_data);
Hi Joe,
	Seems you missed the change to enable Kconfig option
CONFIG_IRQ_DOMAIN_HIERARCHY. If CONFIG_IRQ_DOMAIN_HIERARCHY
may be off under certain conditions, above code may cause
building failure.
Regards!
Gerry

>  	if (hw < 32) {
>  		irq_set_percpu_devid(irq);
> -		irq_set_chip_and_handler(irq, &gic_chip,
> -					 handle_percpu_devid_irq);
> +		irq_set_handler(irq, handle_percpu_devid_irq);
>  		set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
>  	} else {
> -		irq_set_chip_and_handler(irq, &gic_chip,
> -					 handle_fasteoi_irq);
> +		irq_set_handler(irq, handle_fasteoi_irq);
>  		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
>  
>  		gic_routable_irq_domain_ops->map(d, irq, hw);
>  	}
> -	irq_set_chip_data(irq, d->host_data);
>  	return 0;
>  }
>  
> +static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +				unsigned int nr_irqs, void *arg)
> +{
> +	int i, ret;
> +	irq_hw_number_t hwirq;
> +	unsigned int type = IRQ_TYPE_NONE;
> +	struct of_phandle_args *irq_data = arg;
> +
> +	ret = domain->ops->xlate(domain, irq_data->np, irq_data->args,
> +				 irq_data->args_count, &hwirq, &type);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < nr_irqs; i++)
> +		gic_irq_domain_map(domain, virq+i, hwirq+i);
> +
> +	return 0;
> +}
> +
> +static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq,
> +				unsigned int nr_irqs)
> +{
> +	int i;
> +
> +	for (i = 0; i < nr_irqs; i++) {
> +		irq_set_handler(virq + i, NULL);
> +		irq_domain_set_hwirq_and_chip(domain, virq + i, 0, NULL, NULL);
> +	}
> +}
> +
>  static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
>  {
>  	gic_routable_irq_domain_ops->unmap(d, irq);
> @@ -795,8 +823,6 @@ static int gic_irq_domain_xlate(struct irq_domain *d,
>  {
>  	unsigned long ret = 0;
>  
> -	if (d->of_node != controller)
> -		return -EINVAL;
>  	if (intsize < 3)
>  		return -EINVAL;
>  
> @@ -839,6 +865,14 @@ static struct notifier_block gic_cpu_notifier = {
>  };
>  #endif
>  
> +static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = {
> +	.alloc = gic_irq_domain_alloc,
> +	.free = gic_irq_domain_free,
> +	.map = gic_irq_domain_map,
> +	.unmap = gic_irq_domain_unmap,
> +	.xlate = gic_irq_domain_xlate,
> +};
> +
>  static const struct irq_domain_ops gic_irq_domain_ops = {
>  	.map = gic_irq_domain_map,
>  	.unmap = gic_irq_domain_unmap,
> @@ -952,7 +986,10 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
>  
>  	gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
>  
> -	if (of_property_read_u32(node, "arm,routable-irqs",
> +	if (of_find_property(node, "arm,hierarchy-irq-domain", NULL))
> +		gic->domain = irq_domain_add_linear(node, gic_irqs,
> +					&gic_irq_domain_hierarchy_ops, gic);
> +	else if (of_property_read_u32(node, "arm,routable-irqs",
>  				 &nr_routable_irqs)) {
>  		irq_base = irq_alloc_descs(irq_start, 16, gic_irqs,
>  					   numa_node_id());
> diff --git a/drivers/irqchip/irq-mt65xx-cirq.c b/drivers/irqchip/irq-mt65xx-cirq.c
> new file mode 100644
> index 0000000..1243a7f
> --- /dev/null
> +++ b/drivers/irqchip/irq-mt65xx-cirq.c
> @@ -0,0 +1,158 @@
> +
> +#include <linux/irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_address.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +#include "irqchip.h"
> +
> +#define MT6577_SYS_CIRQ_NUM	(168)
> +
> +struct mt_cirq_chip_data {
> +	spinlock_t lock;
> +	void __iomem *intpol_base;
> +};
> +
> +
> +static int mt_cirq_set_type(struct irq_data *data, unsigned int type)
> +{
> +	irq_hw_number_t hwirq = data->hwirq;
> +	struct mt_cirq_chip_data *chip_data = data->chip_data;
> +	u32 offset, reg_index, value;
> +	unsigned long flags;
> +	int ret;
> +
> +	offset = hwirq & 0x1f;
> +	reg_index = hwirq >> 5;
> +
> +	spin_lock_irqsave(&chip_data->lock, flags);
> +	value = readl_relaxed(chip_data->intpol_base + reg_index * 4);
> +	if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_EDGE_FALLING) {
> +		type = (type == IRQ_TYPE_LEVEL_LOW) ?
> +			IRQ_TYPE_LEVEL_HIGH : IRQ_TYPE_EDGE_RISING;
> +		value |= (1 << offset);
> +	} else
> +		value &= ~(1 << offset);
> +	writel(value, chip_data->intpol_base + reg_index * 4);
> +
> +	data = data->parent_data;
> +	ret = data->chip->irq_set_type(data, type);
> +	spin_unlock_irqrestore(&chip_data->lock, flags);
> +	return ret;
> +}
> +
> +static struct irq_chip mt_cirq_chip = {
> +	.name			= "MT_CIRQ",
> +	.irq_mask		= irq_chip_mask_parent,
> +	.irq_unmask		= irq_chip_unmask_parent,
> +	.irq_eoi		= irq_chip_eoi_parent,
> +	.irq_set_type		= mt_cirq_set_type,
> +	.irq_retrigger		= irq_chip_retrigger_hierarchy,
> +	.irq_set_affinity	= irq_chip_set_affinity_parent,
> +};
> +
> +static int mt_cirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
> +				unsigned int nr_irqs, void *arg)
> +{
> +	int i, ret;
> +	irq_hw_number_t hwirq;
> +	struct of_phandle_args *irq_data = arg;
> +
> +	if (irq_data->args_count != 3)
> +		return -EINVAL;
> +
> +	hwirq = irq_data->args[1];
> +	if (irq_find_mapping(domain, hwirq) > 0)
> +		return -EEXIST;
> +
> +	for (i = 0; i < nr_irqs; i++)
> +		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
> +					      &mt_cirq_chip, domain->host_data);
> +
> +	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static void mt_cirq_domain_free(struct irq_domain *domain, unsigned int virq,
> +				unsigned int nr_irqs)
> +{
> +	int i;
> +
> +	for (i = 0; i < nr_irqs; i++) {
> +		irq_set_handler(virq + i, NULL);
> +		irq_domain_set_hwirq_and_chip(domain, virq + i, 0, NULL, NULL);
> +	}
> +	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> +}
> +
> +static int mt_cirq_domain_xlate(struct irq_domain *d,
> +				struct device_node *controller,
> +				const u32 *intspec, unsigned int intsize,
> +				unsigned long *out_hwirq,
> +				unsigned int *out_type)
> +{
> +	return d->parent->ops->xlate(d->parent, controller, intspec, intsize,
> +					out_hwirq, out_type);
> +}
> +
> +static struct irq_domain_ops cirq_domain_ops = {
> +	.alloc = mt_cirq_domain_alloc,
> +	.free = mt_cirq_domain_free,
> +	.xlate = mt_cirq_domain_xlate,
> +};
I'm trying to make change to irq_create_of_mapping() in next version,
which will affect you patch in two aspects:
1) no need to implement mt_cirq_domain_xlate?
2?OF hierarchy irqdomain callbacks need to implement following logic:
        if (type != IRQ_TYPE_NONE &&
            type != irq_get_trigger_type(virq))
                irq_set_irq_type(virq, type);

Regards!
Gerry
> +
> +static int __init mtk_cirq_of_init(struct device_node *node,
> +				   struct device_node *parent)
> +{
> +	struct device_node *parent_node;
> +	struct irq_domain *domain, *domain_parent = NULL;
> +	struct mt_cirq_chip_data *chip_data;
> +	int ret = 0;
> +
> +	parent_node = of_irq_find_parent(node);
> +	if (parent_node) {
> +		domain_parent = irq_find_host(parent_node);
> +		of_node_put(parent_node);
> +	}
> +
> +	if (!domain_parent) {
> +		pr_err("mtk_cirq: interrupt-parent not found\n");
> +		return -EINVAL;
> +	}
> +
> +	chip_data = kzalloc(sizeof(*chip_data), GFP_KERNEL);
> +	if (!chip_data)
> +		return -ENOMEM;
> +
> +	chip_data->intpol_base = of_io_request_and_map(node, 0, "intpol");
> +	if (!chip_data->intpol_base) {
> +		pr_err("mtk_cirq: unable to map cirq register\n");
> +		ret = -ENOMEM;
> +		goto out_free;
> +	}
> +
> +	domain = irq_domain_add_linear(node, MT6577_SYS_CIRQ_NUM,
> +				&cirq_domain_ops, chip_data);
> +	if (!domain) {
> +		ret = -ENOMEM;
> +		goto out_unmap;
> +	}
> +	domain->parent = domain_parent;
> +	spin_lock_init(&chip_data->lock);
> +
> +	return 0;
> +
> +out_unmap:
> +	iounmap(chip_data->intpol_base);
> +out_free:
> +	kfree(chip_data);
> +	return ret;
> +}
> +IRQCHIP_DECLARE(mtk_cirq, "mediatek,mt6577-cirq", mtk_cirq_of_init);
> diff --git a/include/linux/irq.h b/include/linux/irq.h
> index bfa027f..a877b88 100644
> --- a/include/linux/irq.h
> +++ b/include/linux/irq.h
> @@ -435,6 +435,11 @@ extern void handle_nested_irq(unsigned int irq);
>  
>  #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
>  extern void irq_chip_ack_parent(struct irq_data *data);
> +extern void irq_chip_mask_parent(struct irq_data *data);
> +extern void irq_chip_unmask_parent(struct irq_data *data);
> +extern void irq_chip_eoi_parent(struct irq_data *data);
> +extern int irq_chip_set_affinity_parent(struct irq_data *data,
> +				const struct cpumask *dest, bool force);
>  extern int irq_chip_retrigger_hierarchy(struct irq_data *data);
>  #endif
>  
> diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
> index b8ee27e..da3042b 100644
> --- a/kernel/irq/chip.c
> +++ b/kernel/irq/chip.c
> @@ -830,6 +830,34 @@ void irq_chip_ack_parent(struct irq_data *data)
>  		data->chip->irq_ack(data);
>  }
>  
> +void irq_chip_mask_parent(struct irq_data *data)
> +{
> +	data = data->parent_data;
> +	data->chip->irq_mask(data);
> +}
> +
> +void irq_chip_unmask_parent(struct irq_data *data)
> +{
> +	data = data->parent_data;
> +	data->chip->irq_unmask(data);
> +}
> +
> +void irq_chip_eoi_parent(struct irq_data *data)
> +{
> +	data = data->parent_data;
> +	data->chip->irq_eoi(data);
> +}
> +
> +int irq_chip_set_affinity_parent(struct irq_data *data,
> +				 const struct cpumask *dest, bool force)
> +{
> +	data = data->parent_data;
> +	if (data->chip->irq_set_affinity)
> +		return data->chip->irq_set_affinity(data, dest, force);
> +
> +	return -ENOSYS;
> +}
> +
>  int irq_chip_retrigger_hierarchy(struct irq_data *data)
>  {
>  	for (data = data->parent_data; data; data = data->parent_data)
> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
> index e285f3a..01e852b 100644
> --- a/kernel/irq/irqdomain.c
> +++ b/kernel/irq/irqdomain.c
> @@ -467,7 +467,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
>  	struct irq_domain *domain;
>  	irq_hw_number_t hwirq;
>  	unsigned int type = IRQ_TYPE_NONE;
> -	unsigned int virq;
> +	int virq;
>  
>  	domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;
>  	if (!domain) {
> @@ -493,8 +493,8 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
>  	else
>  #endif
>  		virq = irq_create_mapping(domain, hwirq);
> -	if (!virq)
> -		return virq;
> +	if (virq <= 0)
> +		return 0;
>  
>  	/* Set type if specified and different than the current one */
>  	if (type != IRQ_TYPE_NONE &&
> @@ -716,20 +716,20 @@ const struct irq_domain_ops irq_domain_simple_ops = {
>  };
>  EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
>  
> -static int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
> +static int irq_domain_alloc_descs(int virq, unsigned int cnt,
>  				  irq_hw_number_t hwirq, int node)
>  {
>  	unsigned int hint;
>  
>  	if (virq >= 0) {
> -		virq = irq_alloc_descs(virq, virq, nr_irqs, node);
> +		virq = irq_alloc_descs(virq, virq, cnt, node);
>  	} else {
>  		hint = hwirq % nr_irqs;
>  		if (hint == 0)
>  			hint++;
> -		virq = irq_alloc_descs_from(hint, nr_irqs, node);
> +		virq = irq_alloc_descs_from(hint, cnt, node);
>  		if (virq <= 0 && hint > 1)
> -			virq = irq_alloc_descs_from(1, nr_irqs, node);
> +			virq = irq_alloc_descs_from(1, cnt, node);
>  	}
>  
>  	return virq;
>
Jason Cooper Sept. 25, 2014, 3:17 a.m. UTC | #4
On Thu, Sep 25, 2014 at 11:04:37AM +0800, Jiang Liu wrote:
> Hi Joe,
> 	Thanks, I will merge them into my next version.
> Regards!
> Gerry
> 
> On 2014/9/25 10:16, Joe.C wrote:
> > 
> > Jiang,
> > 
> > Please consider merge the following 2 changes into your next round.

ummm.  I'm confused.  I'll admit I'm a bit liquored up atm, but it looks
like "Joe C." posted a patch 'From: Joe C.', and 'Signed-off-by: Joe C.'.
_Then_, Joe C. replied to himself and asked someone *else* to merge the
below changes into the originally posted patch.  The real kicker is that
someone responded and said they would do it. (?!)

Please tell me the scotch is making this look more fucked up than it
really is...

Also, if Joe.C could please fix his mailer and git config to produce a
complete, correct name, that would be appreciated.

thx,

Jason.

> > On Thu, 2014-09-25 at 00:04 +0800, Joe.C wrote:
> >> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
> >> index e285f3a..01e852b 100644
> >> --- a/kernel/irq/irqdomain.c
> >> +++ b/kernel/irq/irqdomain.c
> >> @@ -467,7 +467,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
> >>  	struct irq_domain *domain;
> >>  	irq_hw_number_t hwirq;
> >>  	unsigned int type = IRQ_TYPE_NONE;
> >> -	unsigned int virq;
> >> +	int virq;
> >>  
> >>  	domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;
> >>  	if (!domain) {
> >> @@ -493,8 +493,8 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
> >>  	else
> >>  #endif
> >>  		virq = irq_create_mapping(domain, hwirq);
> >> -	if (!virq)
> >> -		return virq;
> >> +	if (virq <= 0)
> >> +		return 0;
> >>  
> >>  	/* Set type if specified and different than the current one */
> >>  	if (type != IRQ_TYPE_NONE &&
> > 
> > irq_of_parse_and_map()/of_irq_to_resource() expect
> > irq_create_of_mapping() to return 0 when fail. Return error code will
> > cause of_irq_to_resource crash.
> > 
> > 
> >> @@ -716,20 +716,20 @@ const struct irq_domain_ops irq_domain_simple_ops = {
> >>  };
> >>  EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
> >>  
> >> -static int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
> >> +static int irq_domain_alloc_descs(int virq, unsigned int cnt,
> >>  				  irq_hw_number_t hwirq, int node)
> >>  {
> >>  	unsigned int hint;
> >>  
> >>  	if (virq >= 0) {
> >> -		virq = irq_alloc_descs(virq, virq, nr_irqs, node);
> >> +		virq = irq_alloc_descs(virq, virq, cnt, node);
> >>  	} else {
> >>  		hint = hwirq % nr_irqs;
> >>  		if (hint == 0)
> >>  			hint++;
> >> -		virq = irq_alloc_descs_from(hint, nr_irqs, node);
> >> +		virq = irq_alloc_descs_from(hint, cnt, node);
> >>  		if (virq <= 0 && hint > 1)
> >> -			virq = irq_alloc_descs_from(1, nr_irqs, node);
> >> +			virq = irq_alloc_descs_from(1, cnt, node);
> >>  	}
> >>  
> >>  	return virq;
> > 
> > This come from irq_create_mapping(), the original code is using global
> > nr_irqs. Change to match original behavior.
> > 
> > Joe.C
> > 
> >
Jiang Liu Sept. 25, 2014, 3:37 a.m. UTC | #5
On 2014/9/25 11:17, Jason Cooper wrote:
> On Thu, Sep 25, 2014 at 11:04:37AM +0800, Jiang Liu wrote:
>> Hi Joe,
>> 	Thanks, I will merge them into my next version.
>> Regards!
>> Gerry
>>
>> On 2014/9/25 10:16, Joe.C wrote:
>>>
>>> Jiang,
>>>
>>> Please consider merge the following 2 changes into your next round.
> 
> ummm.  I'm confused.  I'll admit I'm a bit liquored up atm, but it looks
> like "Joe C." posted a patch 'From: Joe C.', and 'Signed-off-by: Joe C.'.
> _Then_, Joe C. replied to himself and asked someone *else* to merge the
> below changes into the originally posted patch.  The real kicker is that
> someone responded and said they would do it. (?!)
> 
> Please tell me the scotch is making this look more fucked up than it
> really is...
> 
> Also, if Joe.C could please fix his mailer and git config to produce a
> complete, correct name, that would be appreciated.
Hi Jason,
	Sorry for the confusion:)
	We are trying to extend irqdomain to support hierarchy
irqdomain. The code was originally developed on x86 only, and it's
still in RFC stage. When Joe trying to use hierarchy irqdomain
interfaces on ARM, he found some bugs, so hope that I could take
those bugfixes in next version.
Regards!
Gerry
> 
> thx,
> 
> Jason.
> 
>>> On Thu, 2014-09-25 at 00:04 +0800, Joe.C wrote:
>>>> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
>>>> index e285f3a..01e852b 100644
>>>> --- a/kernel/irq/irqdomain.c
>>>> +++ b/kernel/irq/irqdomain.c
>>>> @@ -467,7 +467,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
>>>>  	struct irq_domain *domain;
>>>>  	irq_hw_number_t hwirq;
>>>>  	unsigned int type = IRQ_TYPE_NONE;
>>>> -	unsigned int virq;
>>>> +	int virq;
>>>>  
>>>>  	domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;
>>>>  	if (!domain) {
>>>> @@ -493,8 +493,8 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
>>>>  	else
>>>>  #endif
>>>>  		virq = irq_create_mapping(domain, hwirq);
>>>> -	if (!virq)
>>>> -		return virq;
>>>> +	if (virq <= 0)
>>>> +		return 0;
>>>>  
>>>>  	/* Set type if specified and different than the current one */
>>>>  	if (type != IRQ_TYPE_NONE &&
>>>
>>> irq_of_parse_and_map()/of_irq_to_resource() expect
>>> irq_create_of_mapping() to return 0 when fail. Return error code will
>>> cause of_irq_to_resource crash.
>>>
>>>
>>>> @@ -716,20 +716,20 @@ const struct irq_domain_ops irq_domain_simple_ops = {
>>>>  };
>>>>  EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
>>>>  
>>>> -static int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
>>>> +static int irq_domain_alloc_descs(int virq, unsigned int cnt,
>>>>  				  irq_hw_number_t hwirq, int node)
>>>>  {
>>>>  	unsigned int hint;
>>>>  
>>>>  	if (virq >= 0) {
>>>> -		virq = irq_alloc_descs(virq, virq, nr_irqs, node);
>>>> +		virq = irq_alloc_descs(virq, virq, cnt, node);
>>>>  	} else {
>>>>  		hint = hwirq % nr_irqs;
>>>>  		if (hint == 0)
>>>>  			hint++;
>>>> -		virq = irq_alloc_descs_from(hint, nr_irqs, node);
>>>> +		virq = irq_alloc_descs_from(hint, cnt, node);
>>>>  		if (virq <= 0 && hint > 1)
>>>> -			virq = irq_alloc_descs_from(1, nr_irqs, node);
>>>> +			virq = irq_alloc_descs_from(1, cnt, node);
>>>>  	}
>>>>  
>>>>  	return virq;
>>>
>>> This come from irq_create_mapping(), the original code is using global
>>> nr_irqs. Change to match original behavior.
>>>
>>> Joe.C
>>>
>>>
Thomas Gleixner Sept. 25, 2014, 2:27 p.m. UTC | #6
On Thu, 25 Sep 2014, Joe.C wrote:
> From: "Joe.C" <yingjoe.chen@mediatek.com>
> 
> Here's the first draft of using hierarchy irqdomain to implement MTK intpol
> support. I have tested it and intpol works fine. Before continue, I'd like
> to get your comments. This is based on Jiang's hierarchy irqdomian [1] and
> my mediatek SoC basic support [2].
> 
> Simplified block diagram for interrupt:
> 
>     ---------      ---------
>  ---| CIRQ  |------|ARM GIC|
>  ---|       |------|       |
>  ---|       |------|       |
>  ---|       |------|       |
>  ---|       |------|       |
>     ---------      ---------
>  
> In device tree, interrupt-parent for other devices is cirq, child of gic.
> This describe HW better and allow device to specify polarity as it is sent
> by the device. I'm using interrupt-parent to specify irq domain parent in
> cirq now. Maybe adding another name like 'interrupt-domain-parent' will be
> better.

I leave that to the DT folks.
 
> Currently only set_type is implement in CIRQ, but I think this could extended
> to cover all function gic_arch_extn provided.

Right. Marc, can you have a look at this whether you can see how that
might replace the rest of the arch_extn hackery?

At least the avoidance of the set_type mess looks pretty reasonable.
 
> In order to use hierarchy irq domain, gic can't use legacy irq domain anymore.
> If arm,hierarchy-irq-domain property is specified, GIC will work in hierarchy
> way and all interrupt must be set by device tree.
> 
> One thing worth mention is arg in irq_domain_alloc_irqs. On x86, msi is using
> struct irq_alloc_info, to pass data between irq domain. In irq_create_of_mapping(),
> of_phandle_args is used. All allocs in irq_domain chain will need to use same
> interrupt-cells formats.

Right, but we leave that to the architecture implementation. It needs
to be consistent within an architecture, so we should change the "void
*arg" to a well defined type. For OF architectures this would be
of_phandle_args, other architectures can define their own struct type
which allows to transport data between the domains. That ensures
compile time type checking.

Thanks,

	tglx
Arnd Bergmann Sept. 25, 2014, 7:20 p.m. UTC | #7
On Thursday 25 September 2014, Thomas Gleixner wrote:
> On Thu, 25 Sep 2014, Joe.C wrote:
> > From: "Joe.C" <yingjoe.chen@mediatek.com>
> > 
> > Here's the first draft of using hierarchy irqdomain to implement MTK intpol
> > support. I have tested it and intpol works fine. Before continue, I'd like
> > to get your comments. This is based on Jiang's hierarchy irqdomian [1] and
> > my mediatek SoC basic support [2].
> > 
> > Simplified block diagram for interrupt:
> > 
> >     ---------      ---------
> >  ---| CIRQ  |------|ARM GIC|
> >  ---|       |------|       |
> >  ---|       |------|       |
> >  ---|       |------|       |
> >  ---|       |------|       |
> >     ---------      ---------
> >  
> > In device tree, interrupt-parent for other devices is cirq, child of gic.
> > This describe HW better and allow device to specify polarity as it is sent
> > by the device. I'm using interrupt-parent to specify irq domain parent in
> > cirq now. Maybe adding another name like 'interrupt-domain-parent' will be
> > better.
> 
> I leave that to the DT folks.

interrupt-parent really defines a domain already, I think using this is
perfectly fine.


	Arnd
diff mbox

Patch

diff --git a/arch/arm/boot/dts/mt8135.dtsi b/arch/arm/boot/dts/mt8135.dtsi
index 90a56ad..e19ab1a 100644
--- a/arch/arm/boot/dts/mt8135.dtsi
+++ b/arch/arm/boot/dts/mt8135.dtsi
@@ -18,7 +18,7 @@ 
 
 / {
 	compatible = "mediatek,mt8135";
-	interrupt-parent = <&gic>;
+	interrupt-parent = <&cirq>;
 
 	cpu-map {
 		cluster0 {
@@ -97,15 +97,25 @@ 
 		timer: timer@10008000 {
 			compatible = "mediatek,mt8135-timer", "mediatek,mt6577-timer";
 			reg = <0 0x10008000 0 0x80>;
-			interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_LOW>;
 			clocks = <&system_clk>, <&rtc_clk>;
 			clock-names = "system-clk", "rtc-clk";
 		};
 
+		cirq: interrupt-controller@10200030 {
+			compatible = "mediatek,mt8135-cirq", "mediatek,mt6577-cirq";
+			interrupt-controller;
+			#interrupt-cells = <3>;
+			interrupt-parent = <&gic>;
+			reg = <0 0x10200030 0 0x1c>;
+		};
+
 		gic: interrupt-controller@10211000 {
 			compatible = "arm,cortex-a15-gic";
 			interrupt-controller;
 			#interrupt-cells = <3>;
+			arm,hierarchy-irq-domain;
+			interrupt-parent = <&gic>;
 			reg = <0 0x10211000 0 0x1000>,
 			      <0 0x10212000 0 0x1000>,
 			      <0 0x10214000 0 0x2000>,
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 73052ba..0b34675 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -34,3 +34,4 @@  obj-$(CONFIG_XTENSA)			+= irq-xtensa-pic.o
 obj-$(CONFIG_XTENSA_MX)			+= irq-xtensa-mx.o
 obj-$(CONFIG_IRQ_CROSSBAR)		+= irq-crossbar.o
 obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
+obj-$(CONFIG_ARCH_MEDIATEK)		+= irq-mt65xx-cirq.o
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 4b959e6..d66d707 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -767,22 +767,50 @@  void __init gic_init_physaddr(struct device_node *node)
 static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
 				irq_hw_number_t hw)
 {
+	irq_domain_set_hwirq_and_chip(d, irq, hw, &gic_chip, d->host_data);
 	if (hw < 32) {
 		irq_set_percpu_devid(irq);
-		irq_set_chip_and_handler(irq, &gic_chip,
-					 handle_percpu_devid_irq);
+		irq_set_handler(irq, handle_percpu_devid_irq);
 		set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
 	} else {
-		irq_set_chip_and_handler(irq, &gic_chip,
-					 handle_fasteoi_irq);
+		irq_set_handler(irq, handle_fasteoi_irq);
 		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
 
 		gic_routable_irq_domain_ops->map(d, irq, hw);
 	}
-	irq_set_chip_data(irq, d->host_data);
 	return 0;
 }
 
+static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				unsigned int nr_irqs, void *arg)
+{
+	int i, ret;
+	irq_hw_number_t hwirq;
+	unsigned int type = IRQ_TYPE_NONE;
+	struct of_phandle_args *irq_data = arg;
+
+	ret = domain->ops->xlate(domain, irq_data->np, irq_data->args,
+				 irq_data->args_count, &hwirq, &type);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < nr_irqs; i++)
+		gic_irq_domain_map(domain, virq+i, hwirq+i);
+
+	return 0;
+}
+
+static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq,
+				unsigned int nr_irqs)
+{
+	int i;
+
+	for (i = 0; i < nr_irqs; i++) {
+		irq_set_handler(virq + i, NULL);
+		irq_domain_set_hwirq_and_chip(domain, virq + i, 0, NULL, NULL);
+	}
+}
+
 static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
 {
 	gic_routable_irq_domain_ops->unmap(d, irq);
@@ -795,8 +823,6 @@  static int gic_irq_domain_xlate(struct irq_domain *d,
 {
 	unsigned long ret = 0;
 
-	if (d->of_node != controller)
-		return -EINVAL;
 	if (intsize < 3)
 		return -EINVAL;
 
@@ -839,6 +865,14 @@  static struct notifier_block gic_cpu_notifier = {
 };
 #endif
 
+static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = {
+	.alloc = gic_irq_domain_alloc,
+	.free = gic_irq_domain_free,
+	.map = gic_irq_domain_map,
+	.unmap = gic_irq_domain_unmap,
+	.xlate = gic_irq_domain_xlate,
+};
+
 static const struct irq_domain_ops gic_irq_domain_ops = {
 	.map = gic_irq_domain_map,
 	.unmap = gic_irq_domain_unmap,
@@ -952,7 +986,10 @@  void __init gic_init_bases(unsigned int gic_nr, int irq_start,
 
 	gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
 
-	if (of_property_read_u32(node, "arm,routable-irqs",
+	if (of_find_property(node, "arm,hierarchy-irq-domain", NULL))
+		gic->domain = irq_domain_add_linear(node, gic_irqs,
+					&gic_irq_domain_hierarchy_ops, gic);
+	else if (of_property_read_u32(node, "arm,routable-irqs",
 				 &nr_routable_irqs)) {
 		irq_base = irq_alloc_descs(irq_start, 16, gic_irqs,
 					   numa_node_id());
diff --git a/drivers/irqchip/irq-mt65xx-cirq.c b/drivers/irqchip/irq-mt65xx-cirq.c
new file mode 100644
index 0000000..1243a7f
--- /dev/null
+++ b/drivers/irqchip/irq-mt65xx-cirq.c
@@ -0,0 +1,158 @@ 
+
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "irqchip.h"
+
+#define MT6577_SYS_CIRQ_NUM	(168)
+
+struct mt_cirq_chip_data {
+	spinlock_t lock;
+	void __iomem *intpol_base;
+};
+
+
+static int mt_cirq_set_type(struct irq_data *data, unsigned int type)
+{
+	irq_hw_number_t hwirq = data->hwirq;
+	struct mt_cirq_chip_data *chip_data = data->chip_data;
+	u32 offset, reg_index, value;
+	unsigned long flags;
+	int ret;
+
+	offset = hwirq & 0x1f;
+	reg_index = hwirq >> 5;
+
+	spin_lock_irqsave(&chip_data->lock, flags);
+	value = readl_relaxed(chip_data->intpol_base + reg_index * 4);
+	if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_EDGE_FALLING) {
+		type = (type == IRQ_TYPE_LEVEL_LOW) ?
+			IRQ_TYPE_LEVEL_HIGH : IRQ_TYPE_EDGE_RISING;
+		value |= (1 << offset);
+	} else
+		value &= ~(1 << offset);
+	writel(value, chip_data->intpol_base + reg_index * 4);
+
+	data = data->parent_data;
+	ret = data->chip->irq_set_type(data, type);
+	spin_unlock_irqrestore(&chip_data->lock, flags);
+	return ret;
+}
+
+static struct irq_chip mt_cirq_chip = {
+	.name			= "MT_CIRQ",
+	.irq_mask		= irq_chip_mask_parent,
+	.irq_unmask		= irq_chip_unmask_parent,
+	.irq_eoi		= irq_chip_eoi_parent,
+	.irq_set_type		= mt_cirq_set_type,
+	.irq_retrigger		= irq_chip_retrigger_hierarchy,
+	.irq_set_affinity	= irq_chip_set_affinity_parent,
+};
+
+static int mt_cirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+				unsigned int nr_irqs, void *arg)
+{
+	int i, ret;
+	irq_hw_number_t hwirq;
+	struct of_phandle_args *irq_data = arg;
+
+	if (irq_data->args_count != 3)
+		return -EINVAL;
+
+	hwirq = irq_data->args[1];
+	if (irq_find_mapping(domain, hwirq) > 0)
+		return -EEXIST;
+
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+					      &mt_cirq_chip, domain->host_data);
+
+	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void mt_cirq_domain_free(struct irq_domain *domain, unsigned int virq,
+				unsigned int nr_irqs)
+{
+	int i;
+
+	for (i = 0; i < nr_irqs; i++) {
+		irq_set_handler(virq + i, NULL);
+		irq_domain_set_hwirq_and_chip(domain, virq + i, 0, NULL, NULL);
+	}
+	irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static int mt_cirq_domain_xlate(struct irq_domain *d,
+				struct device_node *controller,
+				const u32 *intspec, unsigned int intsize,
+				unsigned long *out_hwirq,
+				unsigned int *out_type)
+{
+	return d->parent->ops->xlate(d->parent, controller, intspec, intsize,
+					out_hwirq, out_type);
+}
+
+static struct irq_domain_ops cirq_domain_ops = {
+	.alloc = mt_cirq_domain_alloc,
+	.free = mt_cirq_domain_free,
+	.xlate = mt_cirq_domain_xlate,
+};
+
+static int __init mtk_cirq_of_init(struct device_node *node,
+				   struct device_node *parent)
+{
+	struct device_node *parent_node;
+	struct irq_domain *domain, *domain_parent = NULL;
+	struct mt_cirq_chip_data *chip_data;
+	int ret = 0;
+
+	parent_node = of_irq_find_parent(node);
+	if (parent_node) {
+		domain_parent = irq_find_host(parent_node);
+		of_node_put(parent_node);
+	}
+
+	if (!domain_parent) {
+		pr_err("mtk_cirq: interrupt-parent not found\n");
+		return -EINVAL;
+	}
+
+	chip_data = kzalloc(sizeof(*chip_data), GFP_KERNEL);
+	if (!chip_data)
+		return -ENOMEM;
+
+	chip_data->intpol_base = of_io_request_and_map(node, 0, "intpol");
+	if (!chip_data->intpol_base) {
+		pr_err("mtk_cirq: unable to map cirq register\n");
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	domain = irq_domain_add_linear(node, MT6577_SYS_CIRQ_NUM,
+				&cirq_domain_ops, chip_data);
+	if (!domain) {
+		ret = -ENOMEM;
+		goto out_unmap;
+	}
+	domain->parent = domain_parent;
+	spin_lock_init(&chip_data->lock);
+
+	return 0;
+
+out_unmap:
+	iounmap(chip_data->intpol_base);
+out_free:
+	kfree(chip_data);
+	return ret;
+}
+IRQCHIP_DECLARE(mtk_cirq, "mediatek,mt6577-cirq", mtk_cirq_of_init);
diff --git a/include/linux/irq.h b/include/linux/irq.h
index bfa027f..a877b88 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -435,6 +435,11 @@  extern void handle_nested_irq(unsigned int irq);
 
 #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
 extern void irq_chip_ack_parent(struct irq_data *data);
+extern void irq_chip_mask_parent(struct irq_data *data);
+extern void irq_chip_unmask_parent(struct irq_data *data);
+extern void irq_chip_eoi_parent(struct irq_data *data);
+extern int irq_chip_set_affinity_parent(struct irq_data *data,
+				const struct cpumask *dest, bool force);
 extern int irq_chip_retrigger_hierarchy(struct irq_data *data);
 #endif
 
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index b8ee27e..da3042b 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -830,6 +830,34 @@  void irq_chip_ack_parent(struct irq_data *data)
 		data->chip->irq_ack(data);
 }
 
+void irq_chip_mask_parent(struct irq_data *data)
+{
+	data = data->parent_data;
+	data->chip->irq_mask(data);
+}
+
+void irq_chip_unmask_parent(struct irq_data *data)
+{
+	data = data->parent_data;
+	data->chip->irq_unmask(data);
+}
+
+void irq_chip_eoi_parent(struct irq_data *data)
+{
+	data = data->parent_data;
+	data->chip->irq_eoi(data);
+}
+
+int irq_chip_set_affinity_parent(struct irq_data *data,
+				 const struct cpumask *dest, bool force)
+{
+	data = data->parent_data;
+	if (data->chip->irq_set_affinity)
+		return data->chip->irq_set_affinity(data, dest, force);
+
+	return -ENOSYS;
+}
+
 int irq_chip_retrigger_hierarchy(struct irq_data *data)
 {
 	for (data = data->parent_data; data; data = data->parent_data)
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index e285f3a..01e852b 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -467,7 +467,7 @@  unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
 	struct irq_domain *domain;
 	irq_hw_number_t hwirq;
 	unsigned int type = IRQ_TYPE_NONE;
-	unsigned int virq;
+	int virq;
 
 	domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;
 	if (!domain) {
@@ -493,8 +493,8 @@  unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
 	else
 #endif
 		virq = irq_create_mapping(domain, hwirq);
-	if (!virq)
-		return virq;
+	if (virq <= 0)
+		return 0;
 
 	/* Set type if specified and different than the current one */
 	if (type != IRQ_TYPE_NONE &&
@@ -716,20 +716,20 @@  const struct irq_domain_ops irq_domain_simple_ops = {
 };
 EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
 
-static int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
+static int irq_domain_alloc_descs(int virq, unsigned int cnt,
 				  irq_hw_number_t hwirq, int node)
 {
 	unsigned int hint;
 
 	if (virq >= 0) {
-		virq = irq_alloc_descs(virq, virq, nr_irqs, node);
+		virq = irq_alloc_descs(virq, virq, cnt, node);
 	} else {
 		hint = hwirq % nr_irqs;
 		if (hint == 0)
 			hint++;
-		virq = irq_alloc_descs_from(hint, nr_irqs, node);
+		virq = irq_alloc_descs_from(hint, cnt, node);
 		if (virq <= 0 && hint > 1)
-			virq = irq_alloc_descs_from(1, nr_irqs, node);
+			virq = irq_alloc_descs_from(1, cnt, node);
 	}
 
 	return virq;