On 09/10/14 16:29, Joe.C wrote: > From: "Joe.C" <yingjoe.chen@xxxxxxxxxxxx> > > Mediatek SoCs have interrupt polarity in sysirq which allows > to swap the polarity for given interrupts. Add this support > using hierarchy irq domain. > > Signed-off-by: Joe.C <yingjoe.chen@xxxxxxxxxxxx> > --- > arch/arm/mach-mediatek/Kconfig | 1 + > drivers/irqchip/Makefile | 1 + > drivers/irqchip/irq-mt65xx-sysirq.c | 170 ++++++++++++++++++++++++++++++++++++ > 3 files changed, 172 insertions(+) > create mode 100644 drivers/irqchip/irq-mt65xx-sysirq.c > > diff --git a/arch/arm/mach-mediatek/Kconfig b/arch/arm/mach-mediatek/Kconfig > index 2c043a2..7093859 100644 > --- a/arch/arm/mach-mediatek/Kconfig > +++ b/arch/arm/mach-mediatek/Kconfig > @@ -2,5 +2,6 @@ config ARCH_MEDIATEK > bool "Mediatek MT6589 SoC" if ARCH_MULTI_V7 > select ARM_GIC > select MTK_TIMER > + select IRQ_DOMAIN_HIERARCHY > help > Support for Mediatek Cortex-A7 Quad-Core-SoC MT6589. > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile > index 73052ba..809c9d5 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-sysirq.o > diff --git a/drivers/irqchip/irq-mt65xx-sysirq.c b/drivers/irqchip/irq-mt65xx-sysirq.c > new file mode 100644 > index 0000000..9e0eee5 > --- /dev/null > +++ b/drivers/irqchip/irq-mt65xx-sysirq.c > @@ -0,0 +1,170 @@ > +/* > + * Copyright (c) 2014 MediaTek Inc. > + * Author: Joe.C <yingjoe.chen@xxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * 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/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_INTPOL_NUM (224) > + > +struct mt_sysirq_chip_data { > + spinlock_t lock; > + void __iomem *intpol_base; > +}; > + > + > +static int mt_sysirq_set_type(struct irq_data *data, unsigned int type) Are the mt_sysirq_ prefixed functions special to mt65xx processors? In this case you should prefix them mt65xx_sysirq_ If not, please rename to mtk_sysirq_ to have a consistency in the kernel. > +{ > + irq_hw_number_t hwirq = data->hwirq; > + struct mt_sysirq_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) { > + if (type == IRQ_TYPE_LEVEL_LOW) > + type = IRQ_TYPE_LEVEL_HIGH; > + else > + type = 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_sysirq_chip = { > + .name = "MT_SYSIRQ", > + .irq_mask = irq_chip_mask_parent, > + .irq_unmask = irq_chip_unmask_parent, > + .irq_eoi = irq_chip_eoi_parent, > + .irq_set_type = mt_sysirq_set_type, > + .irq_retrigger = irq_chip_retrigger_hierarchy, > + .irq_set_affinity = irq_chip_set_affinity_parent, > +}; > + > +static int mt_sysirq_domain_alloc(struct irq_domain *domain, unsigned int virq, > + unsigned int nr_irqs, void *arg) Same here. > +{ > + int i, ret; > + irq_hw_number_t hwirq; > + struct of_phandle_args *irq_data = arg; > + unsigned int type; > + > + 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_sysirq_chip, domain->host_data); > + > + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); > + if (ret < 0) > + return ret; > + > + type = irq_data->args[2] & IRQ_TYPE_SENSE_MASK; > + > + /* Set type if specified and different than the current one */ > + if (type != IRQ_TYPE_NONE && > + type != irq_get_trigger_type(virq)) > + irq_set_irq_type(virq, type); > + > + return 0; > +} > + > +static void mt_sysirq_domain_free(struct irq_domain *domain, unsigned int virq, > + unsigned int nr_irqs) Same here. > +{ > + 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 struct irq_domain_ops sysirq_domain_ops = { > + .alloc = mt_sysirq_domain_alloc, > + .free = mt_sysirq_domain_free, > +}; > + > +static int __init mtk_sysirq_of_init(struct device_node *node, > + struct device_node *parent) > +{ > + struct device_node *parent_node; > + struct irq_domain *domain, *domain_parent = NULL; > + struct mt_sysirq_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_sysirq: 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_sysirq: unable to map sysirq register\n"); > + ret = -ENOMEM; > + goto out_free; > + } > + > + domain = irq_domain_add_linear(node, MT6577_SYS_INTPOL_NUM, > + &sysirq_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_sysirq, "mediatek,mt6577-sysirq", mtk_sysirq_of_init); If this is compatible to all mt65xx from mt6577 upwards, we should rename the file to irq-mt6577-sysirq.c to have consistency in the naming. > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html