From 070884845baaeaa41a11eccc2fc2a4080de64946 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 18 Feb 2015 15:13:58 +0000 Subject: irqchip: st: Supply new driver for STi based devices This driver is used to enable System Configuration Register controlled External, CTI (Core Sight), PMU (Performance Management), and PL310 L2 Cache IRQs prior to use. Signed-off-by: Lee Jones Link: https://lkml.kernel.org/r/1424272444-16230-3-git-send-email-lee.jones@linaro.org Signed-off-by: Jason Cooper --- drivers/irqchip/Kconfig | 7 ++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-st.c | 206 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 drivers/irqchip/irq-st.c (limited to 'drivers') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index cc79d2a5a8c2..c8d260e33a90 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -110,6 +110,13 @@ config RENESAS_IRQC bool select IRQ_DOMAIN +config ST_IRQCHIP + bool + select REGMAP + select MFD_SYSCON + help + Enables SysCfg Controlled IRQs on STi based platforms. + config TB10X_IRQC bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 42965d2476bb..9bb4fd191e94 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o +obj-$(CONFIG_ST_IRQCHIP) += irq-st.o obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o diff --git a/drivers/irqchip/irq-st.c b/drivers/irqchip/irq-st.c new file mode 100644 index 000000000000..9af48a85c16f --- /dev/null +++ b/drivers/irqchip/irq-st.c @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2014 STMicroelectronics – All Rights Reserved + * + * Author: Lee Jones + * + * This is a re-write of Christophe Kerello's PMU driver. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define STIH415_SYSCFG_642 0x0a8 +#define STIH416_SYSCFG_7543 0x87c +#define STIH407_SYSCFG_5102 0x198 +#define STID127_SYSCFG_734 0x088 + +#define ST_A9_IRQ_MASK 0x001FFFFF +#define ST_A9_IRQ_MAX_CHANS 2 + +#define ST_A9_IRQ_EN_CTI_0 BIT(0) +#define ST_A9_IRQ_EN_CTI_1 BIT(1) +#define ST_A9_IRQ_EN_PMU_0 BIT(2) +#define ST_A9_IRQ_EN_PMU_1 BIT(3) +#define ST_A9_IRQ_EN_PL310_L2 BIT(4) +#define ST_A9_IRQ_EN_EXT_0 BIT(5) +#define ST_A9_IRQ_EN_EXT_1 BIT(6) +#define ST_A9_IRQ_EN_EXT_2 BIT(7) + +#define ST_A9_FIQ_N_SEL(dev, chan) (dev << (8 + (chan * 3))) +#define ST_A9_IRQ_N_SEL(dev, chan) (dev << (14 + (chan * 3))) +#define ST_A9_EXTIRQ_INV_SEL(dev) (dev << 20) + +struct st_irq_syscfg { + struct regmap *regmap; + unsigned int syscfg; + unsigned int config; + bool ext_inverted; +}; + +static const struct of_device_id st_irq_syscfg_match[] = { + { + .compatible = "st,stih415-irq-syscfg", + .data = (void *)STIH415_SYSCFG_642, + }, + { + .compatible = "st,stih416-irq-syscfg", + .data = (void *)STIH416_SYSCFG_7543, + }, + { + .compatible = "st,stih407-irq-syscfg", + .data = (void *)STIH407_SYSCFG_5102, + }, + { + .compatible = "st,stid127-irq-syscfg", + .data = (void *)STID127_SYSCFG_734, + }, + {} +}; + +static int st_irq_xlate(struct platform_device *pdev, + int device, int channel, bool irq) +{ + struct st_irq_syscfg *ddata = dev_get_drvdata(&pdev->dev); + + /* Set the device enable bit. */ + switch (device) { + case ST_IRQ_SYSCFG_EXT_0: + ddata->config |= ST_A9_IRQ_EN_EXT_0; + break; + case ST_IRQ_SYSCFG_EXT_1: + ddata->config |= ST_A9_IRQ_EN_EXT_1; + break; + case ST_IRQ_SYSCFG_EXT_2: + ddata->config |= ST_A9_IRQ_EN_EXT_2; + break; + case ST_IRQ_SYSCFG_CTI_0: + ddata->config |= ST_A9_IRQ_EN_CTI_0; + break; + case ST_IRQ_SYSCFG_CTI_1: + ddata->config |= ST_A9_IRQ_EN_CTI_1; + break; + case ST_IRQ_SYSCFG_PMU_0: + ddata->config |= ST_A9_IRQ_EN_PMU_0; + break; + case ST_IRQ_SYSCFG_PMU_1: + ddata->config |= ST_A9_IRQ_EN_PMU_1; + break; + case ST_IRQ_SYSCFG_pl310_L2: + ddata->config |= ST_A9_IRQ_EN_PL310_L2; + break; + case ST_IRQ_SYSCFG_DISABLED: + return 0; + default: + dev_err(&pdev->dev, "Unrecognised device %d\n", device); + return -EINVAL; + } + + /* Select IRQ/FIQ channel for device. */ + ddata->config |= irq ? + ST_A9_IRQ_N_SEL(device, channel) : + ST_A9_FIQ_N_SEL(device, channel); + + return 0; +} + +static int st_irq_syscfg_enable(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct st_irq_syscfg *ddata = dev_get_drvdata(&pdev->dev); + int channels, ret, i; + u32 device, invert; + + channels = of_property_count_u32_elems(np, "st,irq-device"); + if (channels != ST_A9_IRQ_MAX_CHANS) { + dev_err(&pdev->dev, "st,enable-irq-device must have 2 elems\n"); + return -EINVAL; + } + + channels = of_property_count_u32_elems(np, "st,fiq-device"); + if (channels != ST_A9_IRQ_MAX_CHANS) { + dev_err(&pdev->dev, "st,enable-fiq-device must have 2 elems\n"); + return -EINVAL; + } + + for (i = 0; i < ST_A9_IRQ_MAX_CHANS; i++) { + of_property_read_u32_index(np, "st,irq-device", i, &device); + + ret = st_irq_xlate(pdev, device, i, true); + if (ret) + return ret; + + of_property_read_u32_index(np, "st,fiq-device", i, &device); + + ret = st_irq_xlate(pdev, device, i, false); + if (ret) + return ret; + } + + /* External IRQs may be inverted. */ + of_property_read_u32(np, "st,invert-ext", &invert); + ddata->config |= ST_A9_EXTIRQ_INV_SEL(invert); + + return regmap_update_bits(ddata->regmap, ddata->syscfg, + ST_A9_IRQ_MASK, ddata->config); +} + +static int st_irq_syscfg_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + struct st_irq_syscfg *ddata; + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + match = of_match_device(st_irq_syscfg_match, &pdev->dev); + if (!match) + return -ENODEV; + + ddata->syscfg = (unsigned int)match->data; + + ddata->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(ddata->regmap)) { + dev_err(&pdev->dev, "syscfg phandle missing\n"); + return PTR_ERR(ddata->regmap); + } + + dev_set_drvdata(&pdev->dev, ddata); + + return st_irq_syscfg_enable(pdev); +} + +static int st_irq_syscfg_resume(struct device *dev) +{ + struct st_irq_syscfg *ddata = dev_get_drvdata(dev); + + return regmap_update_bits(ddata->regmap, ddata->syscfg, + ST_A9_IRQ_MASK, ddata->config); +} + +static SIMPLE_DEV_PM_OPS(st_irq_syscfg_pm_ops, NULL, st_irq_syscfg_resume); + +static struct platform_driver st_irq_syscfg_driver = { + .driver = { + .name = "st_irq_syscfg", + .pm = &st_irq_syscfg_pm_ops, + .of_match_table = st_irq_syscfg_match, + }, + .probe = st_irq_syscfg_probe, +}; + +static int __init st_irq_syscfg_init(void) +{ + return platform_driver_register(&st_irq_syscfg_driver); +} +core_initcall(st_irq_syscfg_init); -- cgit v1.2.3 From f791e3c101fea2c0964c0cd5684352f4bc9ffa2b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 26 Feb 2015 11:43:32 +0100 Subject: irqchip: renesas-irqc: Use u32 to store 32-bit register values Signed-off-by: Geert Uytterhoeven Link: https://lkml.kernel.org/r/1424947412-8061-1-git-send-email-geert+renesas@glider.be Signed-off-by: Jason Cooper --- drivers/irqchip/irq-renesas-irqc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c index 384e6ed61d7c..2ea3412fdf8c 100644 --- a/drivers/irqchip/irq-renesas-irqc.c +++ b/drivers/irqchip/irq-renesas-irqc.c @@ -94,7 +94,7 @@ static int irqc_irq_set_type(struct irq_data *d, unsigned int type) struct irqc_priv *p = irq_data_get_irq_chip_data(d); int hw_irq = irqd_to_hwirq(d); unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK]; - unsigned long tmp; + u32 tmp; irqc_dbg(&p->irq[hw_irq], "sense"); @@ -112,7 +112,7 @@ static irqreturn_t irqc_irq_handler(int irq, void *dev_id) { struct irqc_irq *i = dev_id; struct irqc_priv *p = i->p; - unsigned long bit = BIT(i->hw_irq); + u32 bit = BIT(i->hw_irq); irqc_dbg(i, "demux1"); -- cgit v1.2.3 From d8bf368d0631d4bc2612d8bf2e4e8e74e620d0cc Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Thu, 5 Mar 2015 15:23:08 +0100 Subject: genirq: Remove the deprecated 'IRQF_DISABLED' request_irq() flag entirely The IRQF_DISABLED flag is a NOOP and has been scheduled for removal since Linux v2.6.36 by commit 6932bf37bed4 ("genirq: Remove IRQF_DISABLED from core code"). According to commit e58aa3d2d0cc ("genirq: Run irq handlers with interrupts disabled"), running IRQ handlers with interrupts enabled can cause stack overflows when the interrupt line of the issuing device is still active. This patch ends the grace period for IRQF_DISABLED (i.e., SA_INTERRUPT in older versions of Linux) and removes the definition and all remaining usages of this flag. There's still a few non-functional references left in the kernel source: - The bigger hunk in Documentation/scsi/ncr53c8xx.txt is removed entirely as IRQF_DISABLED is gone now; the usage in older kernel versions (including the old SA_INTERRUPT flag) should be discouraged. The trouble of using IRQF_SHARED is a general problem and not specific to any driver. - I left the reference in Documentation/PCI/MSI-HOWTO.txt untouched since it has already been removed in linux-next. - All remaining references are changelogs that I suggest to keep. Signed-off-by: Valentin Rothberg Cc: Afzal Mohammed Cc: Arnd Bergmann Cc: Brian Norris Cc: Christoph Hellwig Cc: Dan Carpenter Cc: David Woodhouse Cc: Ewan Milne Cc: Eyal Perry Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: H. Peter Anvin Cc: Hannes Reinecke Cc: Hongliang Tao Cc: Huacai Chen Cc: Jiri Kosina Cc: Jonathan Corbet Cc: Keerthy Cc: Laurent Pinchart Cc: Linus Torvalds Cc: Nishanth Menon Cc: Paul Bolle Cc: Peter Ujfalusi Cc: Peter Zijlstra Cc: Quentin Lambert Cc: Rajendra Nayak Cc: Ralf Baechle Cc: Santosh Shilimkar Cc: Sricharan R Cc: Thomas Gleixner Cc: Tony Lindgren Cc: Zhou Wang Cc: iss_storagedev@hp.com Cc: linux-mips@linux-mips.org Cc: linux-mtd@lists.infradead.org Link: http://lkml.kernel.org/r/1425565425-12604-1-git-send-email-valentinrothberg@gmail.com Signed-off-by: Ingo Molnar --- Documentation/scsi/ncr53c8xx.txt | 25 ------------------------- Documentation/scsi/tmscsim.txt | 4 ---- arch/mips/loongson/loongson-3/hpet.c | 2 +- drivers/block/cpqarray.c | 4 ++-- drivers/bus/omap_l3_noc.c | 4 ++-- drivers/bus/omap_l3_smx.c | 10 ++++------ drivers/mtd/nand/hisi504_nand.c | 3 +-- drivers/usb/isp1760/isp1760-core.c | 3 +-- drivers/usb/isp1760/isp1760-udc.c | 4 ++-- include/linux/interrupt.h | 3 --- 10 files changed, 13 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/Documentation/scsi/ncr53c8xx.txt b/Documentation/scsi/ncr53c8xx.txt index 1d508dcbf859..8586efff1e99 100644 --- a/Documentation/scsi/ncr53c8xx.txt +++ b/Documentation/scsi/ncr53c8xx.txt @@ -786,7 +786,6 @@ port address 0x1400. irqm:1 same as initial settings (assumed BIOS settings) irqm:2 always totem pole irqm:0x10 driver will not use IRQF_SHARED flag when requesting irq - irqm:0x20 driver will not use IRQF_DISABLED flag when requesting irq (Bits 0x10 and 0x20 can be combined with hardware irq mode option) @@ -1231,30 +1230,6 @@ they only refer to system buffers that are well aligned. So, a work around may only be needed under Linux when a scatter/gather list is not used and when the SCSI DATA IN phase is reentered after a phase mismatch. -14.5 IRQ sharing problems - -When an IRQ is shared by devices that are handled by different drivers, it -may happen that one driver complains about the request of the IRQ having -failed. Inder Linux-2.0, this may be due to one driver having requested the -IRQ using the IRQF_DISABLED flag but some other having requested the same IRQ -without this flag. Under both Linux-2.0 and linux-2.2, this may be caused by -one driver not having requested the IRQ with the IRQF_SHARED flag. - -By default, the ncr53c8xx and sym53c8xx drivers request IRQs with both the -IRQF_DISABLED and the IRQF_SHARED flag under Linux-2.0 and with only the IRQF_SHARED -flag under Linux-2.2. - -Under Linux-2.0, you can disable use of IRQF_DISABLED flag from the boot -command line by using the following option: - - ncr53c8xx=irqm:0x20 (for the generic ncr53c8xx driver) - sym53c8xx=irqm:0x20 (for the sym53c8xx driver) - -If this does not fix the problem, then you may want to check how all other -drivers are requesting the IRQ and report the problem. Note that if at least -a single driver does not request the IRQ with the IRQF_SHARED flag (share IRQ), -then the request of the IRQ obviously will not succeed for all the drivers. - 15. SCSI problem troubleshooting 15.1 Problem tracking diff --git a/Documentation/scsi/tmscsim.txt b/Documentation/scsi/tmscsim.txt index 0810132772a8..0e0322bf0020 100644 --- a/Documentation/scsi/tmscsim.txt +++ b/Documentation/scsi/tmscsim.txt @@ -107,10 +107,6 @@ produced errors and started to corrupt my disks. So don't do that! A 37.50 MHz PCI bus works for me, though, but I don't recommend using higher clocks than the 33.33 MHz being in the PCI spec. -If you want to share the IRQ with another device and the driver refuses to -do so, you might succeed with changing the DC390_IRQ type in tmscsim.c to -IRQF_SHARED | IRQF_DISABLED. - 3.Features ---------- diff --git a/arch/mips/loongson/loongson-3/hpet.c b/arch/mips/loongson/loongson-3/hpet.c index e898d68668a9..5c21cd3bd339 100644 --- a/arch/mips/loongson/loongson-3/hpet.c +++ b/arch/mips/loongson/loongson-3/hpet.c @@ -162,7 +162,7 @@ static irqreturn_t hpet_irq_handler(int irq, void *data) static struct irqaction hpet_irq = { .handler = hpet_irq_handler, - .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TIMER, + .flags = IRQF_NOBALANCING | IRQF_TIMER, .name = "hpet", }; diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 2b9440384536..f749df9e15cd 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -405,8 +405,8 @@ static int cpqarray_register_ctlr(int i, struct pci_dev *pdev) goto Enomem4; } hba[i]->access.set_intr_mask(hba[i], 0); - if (request_irq(hba[i]->intr, do_ida_intr, - IRQF_DISABLED|IRQF_SHARED, hba[i]->devname, hba[i])) + if (request_irq(hba[i]->intr, do_ida_intr, IRQF_SHARED, + hba[i]->devname, hba[i])) { printk(KERN_ERR "cpqarray: Unable to get irq %d for %s\n", hba[i]->intr, hba[i]->devname); diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 029bc73de001..11f7982cbdb3 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -284,7 +284,7 @@ static int omap_l3_probe(struct platform_device *pdev) */ l3->debug_irq = platform_get_irq(pdev, 0); ret = devm_request_irq(l3->dev, l3->debug_irq, l3_interrupt_handler, - IRQF_DISABLED, "l3-dbg-irq", l3); + 0x0, "l3-dbg-irq", l3); if (ret) { dev_err(l3->dev, "request_irq failed for %d\n", l3->debug_irq); @@ -293,7 +293,7 @@ static int omap_l3_probe(struct platform_device *pdev) l3->app_irq = platform_get_irq(pdev, 1); ret = devm_request_irq(l3->dev, l3->app_irq, l3_interrupt_handler, - IRQF_DISABLED, "l3-app-irq", l3); + 0x0, "l3-app-irq", l3); if (ret) dev_err(l3->dev, "request_irq failed for %d\n", l3->app_irq); diff --git a/drivers/bus/omap_l3_smx.c b/drivers/bus/omap_l3_smx.c index 597fdaee7315..360a5c0a4ee0 100644 --- a/drivers/bus/omap_l3_smx.c +++ b/drivers/bus/omap_l3_smx.c @@ -251,18 +251,16 @@ static int omap3_l3_probe(struct platform_device *pdev) } l3->debug_irq = platform_get_irq(pdev, 0); - ret = request_irq(l3->debug_irq, omap3_l3_app_irq, - IRQF_DISABLED | IRQF_TRIGGER_RISING, - "l3-debug-irq", l3); + ret = request_irq(l3->debug_irq, omap3_l3_app_irq, IRQF_TRIGGER_RISING, + "l3-debug-irq", l3); if (ret) { dev_err(&pdev->dev, "couldn't request debug irq\n"); goto err1; } l3->app_irq = platform_get_irq(pdev, 1); - ret = request_irq(l3->app_irq, omap3_l3_app_irq, - IRQF_DISABLED | IRQF_TRIGGER_RISING, - "l3-app-irq", l3); + ret = request_irq(l3->app_irq, omap3_l3_app_irq, IRQF_TRIGGER_RISING, + "l3-app-irq", l3); if (ret) { dev_err(&pdev->dev, "couldn't request app irq\n"); goto err2; diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c index 289ad3ac3e80..8dcc7b8fee40 100644 --- a/drivers/mtd/nand/hisi504_nand.c +++ b/drivers/mtd/nand/hisi504_nand.c @@ -758,8 +758,7 @@ static int hisi_nfc_probe(struct platform_device *pdev) hisi_nfc_host_init(host); - ret = devm_request_irq(dev, irq, hinfc_irq_handle, IRQF_DISABLED, - "nandc", host); + ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host); if (ret) { dev_err(dev, "failed to request IRQ\n"); goto err_res; diff --git a/drivers/usb/isp1760/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c index b9827556455f..5c37f40f6122 100644 --- a/drivers/usb/isp1760/isp1760-core.c +++ b/drivers/usb/isp1760/isp1760-core.c @@ -151,8 +151,7 @@ int isp1760_register(struct resource *mem, int irq, unsigned long irqflags, } if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && !udc_disabled) { - ret = isp1760_udc_register(isp, irq, irqflags | IRQF_SHARED | - IRQF_DISABLED); + ret = isp1760_udc_register(isp, irq, irqflags | IRQF_SHARED); if (ret < 0) { isp1760_hcd_unregister(&isp->hcd); return ret; diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c index 9612d7990565..0b46ff01299f 100644 --- a/drivers/usb/isp1760/isp1760-udc.c +++ b/drivers/usb/isp1760/isp1760-udc.c @@ -1451,8 +1451,8 @@ int isp1760_udc_register(struct isp1760_device *isp, int irq, sprintf(udc->irqname, "%s (udc)", devname); - ret = request_irq(irq, isp1760_udc_irq, IRQF_SHARED | IRQF_DISABLED | - irqflags, udc->irqname, udc); + ret = request_irq(irq, isp1760_udc_irq, IRQF_SHARED | irqflags, + udc->irqname, udc); if (ret < 0) goto error; diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 3bb01b9a379c..2cee1761c77d 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -39,8 +39,6 @@ * These flags used only by the kernel as part of the * irq handling routines. * - * IRQF_DISABLED - keep irqs disabled when calling the action handler. - * DEPRECATED. This flag is a NOOP and scheduled to be removed * IRQF_SHARED - allow sharing the irq among several devices * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur * IRQF_TIMER - Flag to mark this interrupt as timer interrupt @@ -58,7 +56,6 @@ * IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device * resume time. */ -#define IRQF_DISABLED 0x00000020 #define IRQF_SHARED 0x00000080 #define IRQF_PROBE_SHARED 0x00000100 #define __IRQF_TIMER 0x00000200 -- cgit v1.2.3 From 0494e11aafc7855b1600fe19f04fadf682e52da9 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sun, 1 Mar 2015 23:41:27 +0100 Subject: irqchip: vf610-mscm-ir: Add support for Vybrid MSCM interrupt router This adds support for Vybrid's interrupt router. On VF6xx models, almost all peripherals can be used by either of the two CPU's, the Cortex-A5 or the Cortex-M4. The interrupt router routes the peripheral interrupts to the configured CPU. This IRQ chip driver configures the interrupt router to route the requested interrupt to the CPU the kernel is running on. The driver makes use of the irqdomain hierarchy support. The parent is given by the device tree. This should be one of the two possible parents either ARM GIC or the ARM NVIC interrupt controller. The latter is currently not yet supported. Note that there is no resource control mechnism implemented to avoid concurrent access of the same peripheral. The user needs to make sure to use device trees which assign the peripherals orthogonally. However, this driver warns the user in case the interrupt is already configured for the other CPU. This provides a poor man's resource controller. Acked-by: Marc Zyngier Signed-off-by: Stefan Agner Link: https://lkml.kernel.org/r/1425249689-32354-2-git-send-email-stefan@agner.ch Signed-off-by: Jason Cooper --- arch/arm/mach-imx/Kconfig | 1 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-vf610-mscm-ir.c | 212 ++++++++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 drivers/irqchip/irq-vf610-mscm-ir.c (limited to 'drivers') diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index e8627e04e1e6..c8dffcee9736 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -631,6 +631,7 @@ config SOC_IMX6SX config SOC_VF610 bool "Vybrid Family VF610 support" + select IRQ_DOMAIN_HIERARCHY select ARM_GIC select PINCTRL_VF610 select PL310_ERRATA_769419 if CACHE_L2X0 diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 42965d2476bb..9176c76eb164 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o +obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o diff --git a/drivers/irqchip/irq-vf610-mscm-ir.c b/drivers/irqchip/irq-vf610-mscm-ir.c new file mode 100644 index 000000000000..9521057d4744 --- /dev/null +++ b/drivers/irqchip/irq-vf610-mscm-ir.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2014-2015 Toradex AG + * Author: Stefan Agner + * + * 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. + * + * + * IRQ chip driver for MSCM interrupt router available on Vybrid SoC's. + * The interrupt router is between the CPU's interrupt controller and the + * peripheral. The router allows to route the peripheral interrupts to + * one of the two available CPU's on Vybrid VF6xx SoC's (Cortex-A5 or + * Cortex-M4). The router will be configured transparently on a IRQ + * request. + * + * o All peripheral interrupts of the Vybrid SoC can be routed to + * CPU 0, CPU 1 or both. The routing is useful for dual-core + * variants of Vybrid SoC such as VF6xx. This driver routes the + * requested interrupt to the CPU currently running on. + * + * o It is required to setup the interrupt router even on single-core + * variants of Vybrid. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "irqchip.h" + +#define MSCM_CPxNUM 0x4 + +#define MSCM_IRSPRC(n) (0x80 + 2 * (n)) +#define MSCM_IRSPRC_CPEN_MASK 0x3 + +#define MSCM_IRSPRC_NUM 112 + +struct vf610_mscm_ir_chip_data { + void __iomem *mscm_ir_base; + u16 cpu_mask; + u16 saved_irsprc[MSCM_IRSPRC_NUM]; +}; + +static struct vf610_mscm_ir_chip_data *mscm_ir_data; + +static inline void vf610_mscm_ir_save(struct vf610_mscm_ir_chip_data *data) +{ + int i; + + for (i = 0; i < MSCM_IRSPRC_NUM; i++) + data->saved_irsprc[i] = readw_relaxed(data->mscm_ir_base + MSCM_IRSPRC(i)); +} + +static inline void vf610_mscm_ir_restore(struct vf610_mscm_ir_chip_data *data) +{ + int i; + + for (i = 0; i < MSCM_IRSPRC_NUM; i++) + writew_relaxed(data->saved_irsprc[i], data->mscm_ir_base + MSCM_IRSPRC(i)); +} + +static int vf610_mscm_ir_notifier(struct notifier_block *self, + unsigned long cmd, void *v) +{ + switch (cmd) { + case CPU_CLUSTER_PM_ENTER: + vf610_mscm_ir_save(mscm_ir_data); + break; + case CPU_CLUSTER_PM_ENTER_FAILED: + case CPU_CLUSTER_PM_EXIT: + vf610_mscm_ir_restore(mscm_ir_data); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block mscm_ir_notifier_block = { + .notifier_call = vf610_mscm_ir_notifier, +}; + +static void vf610_mscm_ir_enable(struct irq_data *data) +{ + irq_hw_number_t hwirq = data->hwirq; + struct vf610_mscm_ir_chip_data *chip_data = data->chip_data; + u16 irsprc; + + irsprc = readw_relaxed(chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq)); + irsprc &= MSCM_IRSPRC_CPEN_MASK; + + WARN_ON(irsprc & ~chip_data->cpu_mask); + + writew_relaxed(chip_data->cpu_mask, + chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq)); + + irq_chip_unmask_parent(data); +} + +static void vf610_mscm_ir_disable(struct irq_data *data) +{ + irq_hw_number_t hwirq = data->hwirq; + struct vf610_mscm_ir_chip_data *chip_data = data->chip_data; + + writew_relaxed(0x0, chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq)); + + irq_chip_mask_parent(data); +} + +static struct irq_chip vf610_mscm_ir_irq_chip = { + .name = "mscm-ir", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_enable = vf610_mscm_ir_enable, + .irq_disable = vf610_mscm_ir_disable, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_affinity = irq_chip_set_affinity_parent, +}; + +static int vf610_mscm_ir_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + int i; + irq_hw_number_t hwirq; + struct of_phandle_args *irq_data = arg; + struct of_phandle_args gic_data; + + if (irq_data->args_count != 2) + return -EINVAL; + + hwirq = irq_data->args[0]; + for (i = 0; i < nr_irqs; i++) + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &vf610_mscm_ir_irq_chip, + domain->host_data); + + gic_data.np = domain->parent->of_node; + gic_data.args_count = 3; + gic_data.args[0] = GIC_SPI; + gic_data.args[1] = irq_data->args[0]; + gic_data.args[2] = irq_data->args[1]; + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data); +} + +static const struct irq_domain_ops mscm_irq_domain_ops = { + .xlate = irq_domain_xlate_twocell, + .alloc = vf610_mscm_ir_domain_alloc, + .free = irq_domain_free_irqs_common, +}; + +static int __init vf610_mscm_ir_of_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_domain *domain, *domain_parent; + struct regmap *mscm_cp_regmap; + int ret, cpuid; + + domain_parent = irq_find_host(parent); + if (!domain_parent) { + pr_err("vf610_mscm_ir: interrupt-parent not found\n"); + return -EINVAL; + } + + mscm_ir_data = kzalloc(sizeof(*mscm_ir_data), GFP_KERNEL); + if (!mscm_ir_data) + return -ENOMEM; + + mscm_ir_data->mscm_ir_base = of_io_request_and_map(node, 0, "mscm-ir"); + + if (!mscm_ir_data->mscm_ir_base) { + pr_err("vf610_mscm_ir: unable to map mscm register\n"); + ret = -ENOMEM; + goto out_free; + } + + mscm_cp_regmap = syscon_regmap_lookup_by_phandle(node, "fsl,cpucfg"); + if (IS_ERR(mscm_cp_regmap)) { + ret = PTR_ERR(mscm_cp_regmap); + pr_err("vf610_mscm_ir: regmap lookup for cpucfg failed\n"); + goto out_unmap; + } + + regmap_read(mscm_cp_regmap, MSCM_CPxNUM, &cpuid); + mscm_ir_data->cpu_mask = 0x1 << cpuid; + + domain = irq_domain_add_hierarchy(domain_parent, 0, + MSCM_IRSPRC_NUM, node, + &mscm_irq_domain_ops, mscm_ir_data); + if (!domain) { + ret = -ENOMEM; + goto out_unmap; + } + + cpu_pm_register_notifier(&mscm_ir_notifier_block); + + return 0; + +out_unmap: + iounmap(mscm_ir_data->mscm_ir_base); +out_free: + kfree(mscm_ir_data); + return ret; +} +IRQCHIP_DECLARE(vf610_mscm_ir, "fsl,vf610-mscm-ir", vf610_mscm_ir_of_init); -- cgit v1.2.3 From 933a24b06b42617ef9fffece1857c5c4b23329aa Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 3 Mar 2015 11:43:14 +0100 Subject: irqchip: armada-370-xp: Initialize per cpu registers when CONFIG_SMP=N The irqchip driver called armada_xp_mpic_smp_cpu_init() when CONFIG_SMP=Y to initialize some per cpu registers. The function is called on each CPU by calling it explicitly on the boot CPU and then using a CPU notifier for the non boot CPUs. This commit removes the CONFIG_SMP constrain, so the per cpu registers are also initialized when CONFIG_SMP=N, which is the right thing to do. Signed-off-by: Ezequiel Garcia Signed-off-by: Maxime Ripard Acked-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1425379400-4346-2-git-send-email-maxime.ripard@free-electrons.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-armada-370-xp.c | 47 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 4387dae14e45..b455c876974e 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -308,28 +308,6 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h, return 0; } -#ifdef CONFIG_SMP -static void armada_mpic_send_doorbell(const struct cpumask *mask, - unsigned int irq) -{ - int cpu; - unsigned long map = 0; - - /* Convert our logical CPU mask into a physical one. */ - for_each_cpu(cpu, mask) - map |= 1 << cpu_logical_map(cpu); - - /* - * Ensure that stores to Normal memory are visible to the - * other CPUs before issuing the IPI. - */ - dsb(); - - /* submit softirq */ - writel((map << 8) | irq, main_int_base + - ARMADA_370_XP_SW_TRIG_INT_OFFS); -} - static void armada_xp_mpic_smp_cpu_init(void) { u32 control; @@ -352,6 +330,28 @@ static void armada_xp_mpic_smp_cpu_init(void) writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); } +#ifdef CONFIG_SMP +static void armada_mpic_send_doorbell(const struct cpumask *mask, + unsigned int irq) +{ + int cpu; + unsigned long map = 0; + + /* Convert our logical CPU mask into a physical one. */ + for_each_cpu(cpu, mask) + map |= 1 << cpu_logical_map(cpu); + + /* + * Ensure that stores to Normal memory are visible to the + * other CPUs before issuing the IPI. + */ + dsb(); + + /* submit softirq */ + writel((map << 8) | irq, main_int_base + + ARMADA_370_XP_SW_TRIG_INT_OFFS); +} + static int armada_xp_mpic_secondary_init(struct notifier_block *nfb, unsigned long action, void *hcpu) { @@ -588,9 +588,8 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, BUG_ON(!armada_370_xp_mpic_domain); -#ifdef CONFIG_SMP + /* Setup for the boot CPU */ armada_xp_mpic_smp_cpu_init(); -#endif armada_370_xp_msi_init(node, main_int_res.start); -- cgit v1.2.3 From 2c299de527ca2f39d231a257c00d09c498bb8447 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 3 Mar 2015 11:43:15 +0100 Subject: irqchip: armada-370-xp: Introduce a is_percpu_irq() helper for readability This commit introduces a helper function is_percpu_irq(), to be used when interrupts are mapped to decide which ones are set as per CPU. This change will allow to extend the list of per cpu interrupts in a less intrusive fashion; also, it makes the code slightly more readable by keeping a list of the per CPU interrupts. Signed-off-by: Ezequiel Garcia Signed-off-by: Maxime Ripard Acked-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1425379400-4346-3-git-send-email-maxime.ripard@free-electrons.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-armada-370-xp.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index b455c876974e..ea57fba263cf 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -77,6 +77,16 @@ static DEFINE_MUTEX(msi_used_lock); static phys_addr_t msi_doorbell_addr; #endif +static inline bool is_percpu_irq(irq_hw_number_t irq) +{ + switch (irq) { + case ARMADA_370_XP_TIMER0_PER_CPU_IRQ: + return true; + default: + return false; + } +} + /* * In SMP mode: * For shared global interrupts, mask/unmask global enable bit @@ -86,7 +96,7 @@ static void armada_370_xp_irq_mask(struct irq_data *d) { irq_hw_number_t hwirq = irqd_to_hwirq(d); - if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) + if (!is_percpu_irq(hwirq)) writel(hwirq, main_int_base + ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); else @@ -98,7 +108,7 @@ static void armada_370_xp_irq_unmask(struct irq_data *d) { irq_hw_number_t hwirq = irqd_to_hwirq(d); - if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) + if (!is_percpu_irq(hwirq)) writel(hwirq, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); else @@ -287,14 +297,14 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { armada_370_xp_irq_mask(irq_get_irq_data(virq)); - if (hw != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) + if (!is_percpu_irq(hw)) writel(hw, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); else writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); irq_set_status_flags(virq, IRQ_LEVEL); - if (hw == ARMADA_370_XP_TIMER0_PER_CPU_IRQ) { + if (is_percpu_irq(hw)) { irq_set_percpu_devid(virq); irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, handle_percpu_devid_irq); -- cgit v1.2.3 From 28da06dfd9e4b04577c517f9c4b52aaa73e3d1c7 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 3 Mar 2015 11:43:16 +0100 Subject: irqchip: armada-370-xp: Enable the PMU interrupts In order to let the Performance Monitoring Unit interrupts flowing in the MPIC, we need to unmask these interrupts in the Coherency Fabric Local Interrupt Mask Register. Since this register is a CPU-local register, unmasking this interrupt needs to be done on the boot CPU when the driver initializes, but also on the secondary CPU when they are brought up. Signed-off-by: Maxime Ripard Acked-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1425379400-4346-4-git-send-email-maxime.ripard@free-electrons.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-armada-370-xp.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index ea57fba263cf..b36373c019ba 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -38,6 +38,8 @@ /* Interrupt Controller Registers Map */ #define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48) #define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C) +#define ARMADA_370_XP_INT_FABRIC_MASK_OFFS (0x54) +#define ARMADA_370_XP_INT_CAUSE_PERF(cpu) (1 << cpu) #define ARMADA_370_XP_INT_CONTROL (0x00) #define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30) @@ -56,6 +58,7 @@ #define ARMADA_370_XP_MAX_PER_CPU_IRQS (28) #define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5) +#define ARMADA_370_XP_FABRIC_IRQ (3) #define IPI_DOORBELL_START (0) #define IPI_DOORBELL_END (8) @@ -81,6 +84,7 @@ static inline bool is_percpu_irq(irq_hw_number_t irq) { switch (irq) { case ARMADA_370_XP_TIMER0_PER_CPU_IRQ: + case ARMADA_370_XP_FABRIC_IRQ: return true; default: return false; @@ -340,6 +344,15 @@ static void armada_xp_mpic_smp_cpu_init(void) writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); } +static void armada_xp_mpic_perf_init(void) +{ + unsigned long cpuid = cpu_logical_map(smp_processor_id()); + + /* Enable Performance Counter Overflow interrupts */ + writel(ARMADA_370_XP_INT_CAUSE_PERF(cpuid), + per_cpu_int_base + ARMADA_370_XP_INT_FABRIC_MASK_OFFS); +} + #ifdef CONFIG_SMP static void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq) @@ -365,8 +378,10 @@ static void armada_mpic_send_doorbell(const struct cpumask *mask, static int armada_xp_mpic_secondary_init(struct notifier_block *nfb, unsigned long action, void *hcpu) { - if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) + if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) { + armada_xp_mpic_perf_init(); armada_xp_mpic_smp_cpu_init(); + } return NOTIFY_OK; } @@ -379,8 +394,10 @@ static struct notifier_block armada_370_xp_mpic_cpu_notifier = { static int mpic_cascaded_secondary_init(struct notifier_block *nfb, unsigned long action, void *hcpu) { - if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) + if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) { + armada_xp_mpic_perf_init(); enable_percpu_irq(parent_irq, IRQ_TYPE_NONE); + } return NOTIFY_OK; } @@ -389,7 +406,6 @@ static struct notifier_block mpic_cascaded_cpu_notifier = { .notifier_call = mpic_cascaded_secondary_init, .priority = 100, }; - #endif /* CONFIG_SMP */ static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { @@ -599,6 +615,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, BUG_ON(!armada_370_xp_mpic_domain); /* Setup for the boot CPU */ + armada_xp_mpic_perf_init(); armada_xp_mpic_smp_cpu_init(); armada_370_xp_msi_init(node, main_int_res.start); -- cgit v1.2.3 From de3ce0804916a9b4f3b58e4e78727d5483c4df04 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 11 Mar 2015 15:42:59 +0000 Subject: irqchip: tegra: Add DT-based support for legacy interrupt controller Tegra's LIC (Legacy Interrupt Controller) has been so far only supported as a weird extension of the GIC, which is not exactly pretty. The stacked IRQ domain framework fits this pretty well, and allows the LIC code to be turned into a standalone irqchip. In the process, make the driver DT aware, something that was sorely missing from the mach-tegra implementation. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1426088583-15097-3-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-tegra.c | 371 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 372 insertions(+) create mode 100644 drivers/irqchip/irq-tegra.c (limited to 'drivers') diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 42965d2476bb..c6d607f1ec50 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o obj-$(CONFIG_ARCH_MMP) += irq-mmp.o obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o obj-$(CONFIG_ARCH_MXS) += irq-mxs.o +obj-$(CONFIG_ARCH_TEGRA) += irq-tegra.o obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o obj-$(CONFIG_METAG) += irq-metag-ext.o diff --git a/drivers/irqchip/irq-tegra.c b/drivers/irqchip/irq-tegra.c new file mode 100644 index 000000000000..d919ecf29cf4 --- /dev/null +++ b/drivers/irqchip/irq-tegra.c @@ -0,0 +1,371 @@ +/* + * Driver code for Tegra's Legacy Interrupt Controller + * + * Author: Marc Zyngier + * + * Heavily based on the original arch/arm/mach-tegra/irq.c code: + * Copyright (C) 2011 Google, Inc. + * + * Author: + * Colin Cross + * + * Copyright (C) 2010,2013, NVIDIA Corporation + * + * 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 +#include +#include +#include +#include +#include + +#include + +#include "irqchip.h" + +#define ICTLR_CPU_IEP_VFIQ 0x08 +#define ICTLR_CPU_IEP_FIR 0x14 +#define ICTLR_CPU_IEP_FIR_SET 0x18 +#define ICTLR_CPU_IEP_FIR_CLR 0x1c + +#define ICTLR_CPU_IER 0x20 +#define ICTLR_CPU_IER_SET 0x24 +#define ICTLR_CPU_IER_CLR 0x28 +#define ICTLR_CPU_IEP_CLASS 0x2C + +#define ICTLR_COP_IER 0x30 +#define ICTLR_COP_IER_SET 0x34 +#define ICTLR_COP_IER_CLR 0x38 +#define ICTLR_COP_IEP_CLASS 0x3c + +#define TEGRA_MAX_NUM_ICTLRS 5 + +static unsigned int num_ictlrs; + +struct tegra_ictlr_soc { + unsigned int num_ictlrs; +}; + +static const struct tegra_ictlr_soc tegra20_ictlr_soc = { + .num_ictlrs = 4, +}; + +static const struct tegra_ictlr_soc tegra30_ictlr_soc = { + .num_ictlrs = 5, +}; + +static const struct of_device_id ictlr_matches[] = { + { .compatible = "nvidia,tegra30-ictlr", .data = &tegra30_ictlr_soc }, + { .compatible = "nvidia,tegra20-ictlr", .data = &tegra20_ictlr_soc }, + { } +}; + +struct tegra_ictlr_info { + void __iomem *base[TEGRA_MAX_NUM_ICTLRS]; +#ifdef CONFIG_PM_SLEEP + u32 cop_ier[TEGRA_MAX_NUM_ICTLRS]; + u32 cop_iep[TEGRA_MAX_NUM_ICTLRS]; + u32 cpu_ier[TEGRA_MAX_NUM_ICTLRS]; + u32 cpu_iep[TEGRA_MAX_NUM_ICTLRS]; + + u32 ictlr_wake_mask[TEGRA_MAX_NUM_ICTLRS]; +#endif +}; + +static struct tegra_ictlr_info *lic; + +static inline void tegra_ictlr_write_mask(struct irq_data *d, unsigned long reg) +{ + void __iomem *base = d->chip_data; + u32 mask; + + mask = BIT(d->hwirq % 32); + writel_relaxed(mask, base + reg); +} + +static void tegra_mask(struct irq_data *d) +{ + tegra_ictlr_write_mask(d, ICTLR_CPU_IER_CLR); + irq_chip_mask_parent(d); +} + +static void tegra_unmask(struct irq_data *d) +{ + tegra_ictlr_write_mask(d, ICTLR_CPU_IER_SET); + irq_chip_unmask_parent(d); +} + +static void tegra_eoi(struct irq_data *d) +{ + tegra_ictlr_write_mask(d, ICTLR_CPU_IEP_FIR_CLR); + irq_chip_eoi_parent(d); +} + +static int tegra_retrigger(struct irq_data *d) +{ + tegra_ictlr_write_mask(d, ICTLR_CPU_IEP_FIR_SET); + return irq_chip_retrigger_hierarchy(d); +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_set_wake(struct irq_data *d, unsigned int enable) +{ + u32 irq = d->hwirq; + u32 index, mask; + + index = (irq / 32); + mask = BIT(irq % 32); + if (enable) + lic->ictlr_wake_mask[index] |= mask; + else + lic->ictlr_wake_mask[index] &= ~mask; + + /* + * Do *not* call into the parent, as the GIC doesn't have any + * wake-up facility... + */ + return 0; +} + +static int tegra_ictlr_suspend(void) +{ + unsigned long flags; + unsigned int i; + + local_irq_save(flags); + for (i = 0; i < num_ictlrs; i++) { + void __iomem *ictlr = lic->base[i]; + + /* Save interrupt state */ + lic->cpu_ier[i] = readl_relaxed(ictlr + ICTLR_CPU_IER); + lic->cpu_iep[i] = readl_relaxed(ictlr + ICTLR_CPU_IEP_CLASS); + lic->cop_ier[i] = readl_relaxed(ictlr + ICTLR_COP_IER); + lic->cop_iep[i] = readl_relaxed(ictlr + ICTLR_COP_IEP_CLASS); + + /* Disable COP interrupts */ + writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR); + + /* Disable CPU interrupts */ + writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR); + + /* Enable the wakeup sources of ictlr */ + writel_relaxed(lic->ictlr_wake_mask[i], ictlr + ICTLR_CPU_IER_SET); + } + local_irq_restore(flags); + + return 0; +} + +static void tegra_ictlr_resume(void) +{ + unsigned long flags; + unsigned int i; + + local_irq_save(flags); + for (i = 0; i < num_ictlrs; i++) { + void __iomem *ictlr = lic->base[i]; + + writel_relaxed(lic->cpu_iep[i], + ictlr + ICTLR_CPU_IEP_CLASS); + writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR); + writel_relaxed(lic->cpu_ier[i], + ictlr + ICTLR_CPU_IER_SET); + writel_relaxed(lic->cop_iep[i], + ictlr + ICTLR_COP_IEP_CLASS); + writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR); + writel_relaxed(lic->cop_ier[i], + ictlr + ICTLR_COP_IER_SET); + } + local_irq_restore(flags); +} + +static struct syscore_ops tegra_ictlr_syscore_ops = { + .suspend = tegra_ictlr_suspend, + .resume = tegra_ictlr_resume, +}; + +static void tegra_ictlr_syscore_init(void) +{ + register_syscore_ops(&tegra_ictlr_syscore_ops); +} +#else +#define tegra_set_wake NULL +static inline void tegra_ictlr_syscore_init(void) {} +#endif + +static struct irq_chip tegra_ictlr_chip = { + .name = "LIC", + .irq_eoi = tegra_eoi, + .irq_mask = tegra_mask, + .irq_unmask = tegra_unmask, + .irq_retrigger = tegra_retrigger, + .irq_set_wake = tegra_set_wake, + .flags = IRQCHIP_MASK_ON_SUSPEND, +#ifdef CONFIG_SMP + .irq_set_affinity = irq_chip_set_affinity_parent, +#endif +}; + +static int tegra_ictlr_domain_xlate(struct irq_domain *domain, + struct device_node *controller, + const u32 *intspec, + unsigned int intsize, + unsigned long *out_hwirq, + unsigned int *out_type) +{ + if (domain->of_node != controller) + return -EINVAL; /* Shouldn't happen, really... */ + if (intsize != 3) + return -EINVAL; /* Not GIC compliant */ + if (intspec[0] != GIC_SPI) + return -EINVAL; /* No PPI should point to this domain */ + + *out_hwirq = intspec[1]; + *out_type = intspec[2]; + return 0; +} + +static int tegra_ictlr_domain_alloc(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs, void *data) +{ + struct of_phandle_args *args = data; + struct of_phandle_args parent_args; + struct tegra_ictlr_info *info = domain->host_data; + irq_hw_number_t hwirq; + unsigned int i; + + if (args->args_count != 3) + return -EINVAL; /* Not GIC compliant */ + if (args->args[0] != GIC_SPI) + return -EINVAL; /* No PPI should point to this domain */ + + hwirq = args->args[1]; + if (hwirq >= (num_ictlrs * 32)) + return -EINVAL; + + for (i = 0; i < nr_irqs; i++) { + int ictlr = (hwirq + i) / 32; + + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &tegra_ictlr_chip, + &info->base[ictlr]); + } + + parent_args = *args; + parent_args.np = domain->parent->of_node; + return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args); +} + +static void tegra_ictlr_domain_free(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs) +{ + unsigned int i; + + for (i = 0; i < nr_irqs; i++) { + struct irq_data *d = irq_domain_get_irq_data(domain, virq + i); + irq_domain_reset_irq_data(d); + } +} + +static const struct irq_domain_ops tegra_ictlr_domain_ops = { + .xlate = tegra_ictlr_domain_xlate, + .alloc = tegra_ictlr_domain_alloc, + .free = tegra_ictlr_domain_free, +}; + +static int __init tegra_ictlr_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_domain *parent_domain, *domain; + const struct of_device_id *match; + const struct tegra_ictlr_soc *soc; + unsigned int i; + int err; + + if (!parent) { + pr_err("%s: no parent, giving up\n", node->full_name); + return -ENODEV; + } + + parent_domain = irq_find_host(parent); + if (!parent_domain) { + pr_err("%s: unable to obtain parent domain\n", node->full_name); + return -ENXIO; + } + + match = of_match_node(ictlr_matches, node); + if (!match) /* Should never happen... */ + return -ENODEV; + + soc = match->data; + + lic = kzalloc(sizeof(*lic), GFP_KERNEL); + if (!lic) + return -ENOMEM; + + for (i = 0; i < TEGRA_MAX_NUM_ICTLRS; i++) { + void __iomem *base; + + base = of_iomap(node, i); + if (!base) + break; + + lic->base[i] = base; + + /* Disable all interrupts */ + writel_relaxed(~0UL, base + ICTLR_CPU_IER_CLR); + /* All interrupts target IRQ */ + writel_relaxed(0, base + ICTLR_CPU_IEP_CLASS); + + num_ictlrs++; + } + + if (!num_ictlrs) { + pr_err("%s: no valid regions, giving up\n", node->full_name); + err = -ENOMEM; + goto out_free; + } + + WARN(num_ictlrs != soc->num_ictlrs, + "%s: Found %u interrupt controllers in DT; expected %u.\n", + node->full_name, num_ictlrs, soc->num_ictlrs); + + + domain = irq_domain_add_hierarchy(parent_domain, 0, num_ictlrs * 32, + node, &tegra_ictlr_domain_ops, + lic); + if (!domain) { + pr_err("%s: failed to allocated domain\n", node->full_name); + err = -ENOMEM; + goto out_unmap; + } + + tegra_ictlr_syscore_init(); + + pr_info("%s: %d interrupts forwarded to %s\n", + node->full_name, num_ictlrs * 32, parent->full_name); + + return 0; + +out_unmap: + for (i = 0; i < num_ictlrs; i++) + iounmap(lic->base[i]); +out_free: + kfree(lic); + return err; +} + +IRQCHIP_DECLARE(tegra20_ictlr, "nvidia,tegra20-ictlr", tegra_ictlr_init); +IRQCHIP_DECLARE(tegra30_ictlr, "nvidia,tegra30-ictlr", tegra_ictlr_init); -- cgit v1.2.3 From 783d31863fb826f1a3754a2d5959a022a1b12d54 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 11 Mar 2015 15:43:44 +0000 Subject: irqchip: crossbar: Convert dra7 crossbar to stacked domains Support for the TI crossbar used on the DRA7 family of chips is implemented as an ugly hack on the side of the GIC. Converting it to stacked domains makes it slightly more palatable, as it results in a cleanup. Unfortunately, as the DT bindings failed to acknowledge the fact that this is actually yet another interrupt controller (the third, actually), we have yet another breakage. Oh well. Acked-by: Tony Lindgren Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1426088629-15377-3-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- arch/arm/boot/dts/am57xx-beagle-x15.dts | 3 +- arch/arm/boot/dts/dra7-evm.dts | 2 +- arch/arm/boot/dts/dra7.dtsi | 35 +++--- arch/arm/boot/dts/dra72-evm.dts | 1 - arch/arm/boot/dts/dra72x.dtsi | 3 +- arch/arm/boot/dts/dra74x.dtsi | 5 +- arch/arm/mach-omap2/omap4-common.c | 4 - drivers/irqchip/irq-crossbar.c | 210 +++++++++++++++++++------------- include/linux/irqchip/irq-crossbar.h | 11 -- 9 files changed, 149 insertions(+), 125 deletions(-) delete mode 100644 include/linux/irqchip/irq-crossbar.h (limited to 'drivers') diff --git a/arch/arm/boot/dts/am57xx-beagle-x15.dts b/arch/arm/boot/dts/am57xx-beagle-x15.dts index 03750af3b49a..170fbf953e5d 100644 --- a/arch/arm/boot/dts/am57xx-beagle-x15.dts +++ b/arch/arm/boot/dts/am57xx-beagle-x15.dts @@ -454,7 +454,6 @@ mcp_rtc: rtc@6f { compatible = "microchip,mcp7941x"; reg = <0x6f>; - interrupt-parent = <&gic>; interrupts = ; /* IRQ_SYS_1N */ pinctrl-names = "default"; @@ -477,7 +476,7 @@ &uart3 { status = "okay"; - interrupts-extended = <&gic GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>, + interrupts-extended = <&crossbar_mpu GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>, <&dra7_pmx_core 0x248>; pinctrl-names = "default"; diff --git a/arch/arm/boot/dts/dra7-evm.dts b/arch/arm/boot/dts/dra7-evm.dts index 746cddb1b8f5..789ee58ba47e 100644 --- a/arch/arm/boot/dts/dra7-evm.dts +++ b/arch/arm/boot/dts/dra7-evm.dts @@ -446,7 +446,7 @@ status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&uart1_pins>; - interrupts-extended = <&gic GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>, + interrupts-extended = <&crossbar_mpu GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>, <&dra7_pmx_core 0x3e0>; }; diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi index 5827fedafd43..850f949d409a 100644 --- a/arch/arm/boot/dts/dra7.dtsi +++ b/arch/arm/boot/dts/dra7.dtsi @@ -13,14 +13,13 @@ #include "skeleton.dtsi" #define MAX_SOURCES 400 -#define DIRECT_IRQ(irq) (MAX_SOURCES + irq) / { #address-cells = <1>; #size-cells = <1>; compatible = "ti,dra7xx"; - interrupt-parent = <&gic>; + interrupt-parent = <&crossbar_mpu>; aliases { i2c0 = &i2c1; @@ -50,18 +49,19 @@ , , ; + interrupt-parent = <&gic>; }; gic: interrupt-controller@48211000 { compatible = "arm,cortex-a15-gic"; interrupt-controller; #interrupt-cells = <3>; - arm,routable-irqs = <192>; reg = <0x48211000 0x1000>, <0x48212000 0x1000>, <0x48214000 0x2000>, <0x48216000 0x2000>; interrupts = ; + interrupt-parent = <&gic>; }; /* @@ -91,8 +91,8 @@ ti,hwmods = "l3_main_1", "l3_main_2"; reg = <0x44000000 0x1000000>, <0x45000000 0x1000>; - interrupts = , - ; + interrupts-extended = <&crossbar_mpu GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>, + <&gic GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>; prm: prm@4ae06000 { compatible = "ti,dra7-prm"; @@ -344,7 +344,7 @@ uart1: serial@4806a000 { compatible = "ti,omap4-uart"; reg = <0x4806a000 0x100>; - interrupts-extended = <&gic GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>; + interrupts-extended = <&crossbar_mpu GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>; ti,hwmods = "uart1"; clock-frequency = <48000000>; status = "disabled"; @@ -355,7 +355,7 @@ uart2: serial@4806c000 { compatible = "ti,omap4-uart"; reg = <0x4806c000 0x100>; - interrupts-extended = <&gic GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; ti,hwmods = "uart2"; clock-frequency = <48000000>; status = "disabled"; @@ -366,7 +366,7 @@ uart3: serial@48020000 { compatible = "ti,omap4-uart"; reg = <0x48020000 0x100>; - interrupts-extended = <&gic GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; ti,hwmods = "uart3"; clock-frequency = <48000000>; status = "disabled"; @@ -377,7 +377,7 @@ uart4: serial@4806e000 { compatible = "ti,omap4-uart"; reg = <0x4806e000 0x100>; - interrupts-extended = <&gic GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; ti,hwmods = "uart4"; clock-frequency = <48000000>; status = "disabled"; @@ -388,7 +388,7 @@ uart5: serial@48066000 { compatible = "ti,omap4-uart"; reg = <0x48066000 0x100>; - interrupts-extended = <&gic GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; ti,hwmods = "uart5"; clock-frequency = <48000000>; status = "disabled"; @@ -399,7 +399,7 @@ uart6: serial@48068000 { compatible = "ti,omap4-uart"; reg = <0x48068000 0x100>; - interrupts-extended = <&gic GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; ti,hwmods = "uart6"; clock-frequency = <48000000>; status = "disabled"; @@ -410,7 +410,7 @@ uart7: serial@48420000 { compatible = "ti,omap4-uart"; reg = <0x48420000 0x100>; - interrupts-extended = <&gic GIC_SPI 218 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; ti,hwmods = "uart7"; clock-frequency = <48000000>; status = "disabled"; @@ -419,7 +419,7 @@ uart8: serial@48422000 { compatible = "ti,omap4-uart"; reg = <0x48422000 0x100>; - interrupts-extended = <&gic GIC_SPI 219 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; ti,hwmods = "uart8"; clock-frequency = <48000000>; status = "disabled"; @@ -428,7 +428,7 @@ uart9: serial@48424000 { compatible = "ti,omap4-uart"; reg = <0x48424000 0x100>; - interrupts-extended = <&gic GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; ti,hwmods = "uart9"; clock-frequency = <48000000>; status = "disabled"; @@ -437,7 +437,7 @@ uart10: serial@4ae2b000 { compatible = "ti,omap4-uart"; reg = <0x4ae2b000 0x100>; - interrupts-extended = <&gic GIC_SPI 221 IRQ_TYPE_LEVEL_HIGH>; + interrupts = ; ti,hwmods = "uart10"; clock-frequency = <48000000>; status = "disabled"; @@ -1337,9 +1337,12 @@ status = "disabled"; }; - crossbar_mpu: crossbar@4a020000 { + crossbar_mpu: crossbar@4a002a48 { compatible = "ti,irq-crossbar"; reg = <0x4a002a48 0x130>; + interrupt-controller; + interrupt-parent = <&gic>; + #interrupt-cells = <3>; ti,max-irqs = <160>; ti,max-crossbar-sources = ; ti,reg-size = <2>; diff --git a/arch/arm/boot/dts/dra72-evm.dts b/arch/arm/boot/dts/dra72-evm.dts index 4d8711713610..2373054f588d 100644 --- a/arch/arm/boot/dts/dra72-evm.dts +++ b/arch/arm/boot/dts/dra72-evm.dts @@ -160,7 +160,6 @@ pinctrl-0 = <&tps65917_pins_default>; interrupts = ; /* IRQ_SYS_1N */ - interrupt-parent = <&gic>; interrupt-controller; #interrupt-cells = <2>; diff --git a/arch/arm/boot/dts/dra72x.dtsi b/arch/arm/boot/dts/dra72x.dtsi index e5a3d23a3df1..e782bf1dd863 100644 --- a/arch/arm/boot/dts/dra72x.dtsi +++ b/arch/arm/boot/dts/dra72x.dtsi @@ -25,6 +25,7 @@ pmu { compatible = "arm,cortex-a15-pmu"; - interrupts = ; + interrupt-parent = <&gic>; + interrupts = ; }; }; diff --git a/arch/arm/boot/dts/dra74x.dtsi b/arch/arm/boot/dts/dra74x.dtsi index 10173fab1a15..0fc758db55f5 100644 --- a/arch/arm/boot/dts/dra74x.dtsi +++ b/arch/arm/boot/dts/dra74x.dtsi @@ -41,8 +41,9 @@ pmu { compatible = "arm,cortex-a15-pmu"; - interrupts = , - ; + interrupt-parent = <&gic>; + interrupts = , + ; }; ocp { diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c index cee0fe1ee6ff..cf7aafb27fd1 100644 --- a/arch/arm/mach-omap2/omap4-common.c +++ b/arch/arm/mach-omap2/omap4-common.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -292,8 +291,5 @@ void __init omap_gic_of_init(void) skip_errata_init: omap_wakeupgen_init(); -#ifdef CONFIG_IRQ_CROSSBAR - irqcrossbar_init(); -#endif irqchip_init(); } diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index bbbaf5de65d2..692fe2bc8197 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -11,11 +11,12 @@ */ #include #include +#include #include #include #include -#include -#include + +#include "irqchip.h" #define IRQ_FREE -1 #define IRQ_RESERVED -2 @@ -24,6 +25,7 @@ /** * struct crossbar_device - crossbar device description + * @lock: spinlock serializing access to @irq_map * @int_max: maximum number of supported interrupts * @safe_map: safe default value to initialize the crossbar * @max_crossbar_sources: Maximum number of crossbar sources @@ -33,6 +35,7 @@ * @write: register write function pointer */ struct crossbar_device { + raw_spinlock_t lock; uint int_max; uint safe_map; uint max_crossbar_sources; @@ -44,72 +47,101 @@ struct crossbar_device { static struct crossbar_device *cb; -static inline void crossbar_writel(int irq_no, int cb_no) +static void crossbar_writel(int irq_no, int cb_no) { writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); } -static inline void crossbar_writew(int irq_no, int cb_no) +static void crossbar_writew(int irq_no, int cb_no) { writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); } -static inline void crossbar_writeb(int irq_no, int cb_no) +static void crossbar_writeb(int irq_no, int cb_no) { writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); } -static inline int get_prev_map_irq(int cb_no) -{ - int i; - - for (i = cb->int_max - 1; i >= 0; i--) - if (cb->irq_map[i] == cb_no) - return i; - - return -ENODEV; -} +static struct irq_chip crossbar_chip = { + .name = "CBAR", + .irq_eoi = irq_chip_eoi_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_wake = irq_chip_set_wake_parent, +#ifdef CONFIG_SMP + .irq_set_affinity = irq_chip_set_affinity_parent, +#endif +}; -static inline int allocate_free_irq(int cb_no) +static int allocate_gic_irq(struct irq_domain *domain, unsigned virq, + irq_hw_number_t hwirq) { + struct of_phandle_args args; int i; + int err; + raw_spin_lock(&cb->lock); for (i = cb->int_max - 1; i >= 0; i--) { if (cb->irq_map[i] == IRQ_FREE) { - cb->irq_map[i] = cb_no; - return i; + cb->irq_map[i] = hwirq; + break; } } + raw_spin_unlock(&cb->lock); - return -ENODEV; -} + if (i < 0) + return -ENODEV; -static inline bool needs_crossbar_write(irq_hw_number_t hw) -{ - int cb_no; + args.np = domain->parent->of_node; + args.args_count = 3; + args.args[0] = 0; /* SPI */ + args.args[1] = i; + args.args[2] = IRQ_TYPE_LEVEL_HIGH; - if (hw > GIC_IRQ_START) { - cb_no = cb->irq_map[hw - GIC_IRQ_START]; - if (cb_no != IRQ_RESERVED && cb_no != IRQ_SKIP) - return true; - } + err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args); + if (err) + cb->irq_map[i] = IRQ_FREE; + else + cb->write(i, hwirq); - return false; + return err; } -static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hw) +static int crossbar_domain_alloc(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs, void *data) { - if (needs_crossbar_write(hw)) - cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); + struct of_phandle_args *args = data; + irq_hw_number_t hwirq; + int i; + + if (args->args_count != 3) + return -EINVAL; /* Not GIC compliant */ + if (args->args[0] != 0) + return -EINVAL; /* No PPI should point to this domain */ + + hwirq = args->args[1]; + if ((hwirq + nr_irqs) > cb->max_crossbar_sources) + return -EINVAL; /* Can't deal with this */ + + for (i = 0; i < nr_irqs; i++) { + int err = allocate_gic_irq(d, virq + i, hwirq + i); + + if (err) + return err; + + irq_domain_set_hwirq_and_chip(d, virq + i, hwirq + i, + &crossbar_chip, NULL); + } return 0; } /** - * crossbar_domain_unmap - unmap a crossbar<->irq connection - * @d: domain of irq to unmap - * @irq: virq number + * crossbar_domain_free - unmap/free a crossbar<->irq connection + * @domain: domain of irq to unmap + * @virq: virq number + * @nr_irqs: number of irqs to free * * We do not maintain a use count of total number of map/unmap * calls for a particular irq to find out if a irq can be really @@ -117,14 +149,20 @@ static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, * after which irq is anyways unusable. So an explicit map has to be called * after that. */ -static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) +static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) { - irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; + int i; - if (needs_crossbar_write(hw)) { - cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; - cb->write(hw - GIC_IRQ_START, cb->safe_map); + raw_spin_lock(&cb->lock); + for (i = 0; i < nr_irqs; i++) { + struct irq_data *d = irq_domain_get_irq_data(domain, virq + i); + + irq_domain_reset_irq_data(d); + cb->irq_map[d->hwirq] = IRQ_FREE; + cb->write(d->hwirq, cb->safe_map); } + raw_spin_unlock(&cb->lock); } static int crossbar_domain_xlate(struct irq_domain *d, @@ -133,44 +171,22 @@ static int crossbar_domain_xlate(struct irq_domain *d, unsigned long *out_hwirq, unsigned int *out_type) { - int ret; - int req_num = intspec[1]; - int direct_map_num; - - if (req_num >= cb->max_crossbar_sources) { - direct_map_num = req_num - cb->max_crossbar_sources; - if (direct_map_num < cb->int_max) { - ret = cb->irq_map[direct_map_num]; - if (ret == IRQ_RESERVED || ret == IRQ_SKIP) { - /* We use the interrupt num as h/w irq num */ - ret = direct_map_num; - goto found; - } - } - - pr_err("%s: requested crossbar number %d > max %d\n", - __func__, req_num, cb->max_crossbar_sources); - return -EINVAL; - } - - ret = get_prev_map_irq(req_num); - if (ret >= 0) - goto found; - - ret = allocate_free_irq(req_num); - - if (ret < 0) - return ret; - -found: - *out_hwirq = ret + GIC_IRQ_START; + if (d->of_node != controller) + return -EINVAL; /* Shouldn't happen, really... */ + if (intsize != 3) + return -EINVAL; /* Not GIC compliant */ + if (intspec[0] != 0) + return -EINVAL; /* No PPI should point to this domain */ + + *out_hwirq = intspec[1]; + *out_type = intspec[2]; return 0; } -static const struct irq_domain_ops routable_irq_domain_ops = { - .map = crossbar_domain_map, - .unmap = crossbar_domain_unmap, - .xlate = crossbar_domain_xlate +static const struct irq_domain_ops crossbar_domain_ops = { + .alloc = crossbar_domain_alloc, + .free = crossbar_domain_free, + .xlate = crossbar_domain_xlate, }; static int __init crossbar_of_init(struct device_node *node) @@ -293,7 +309,8 @@ static int __init crossbar_of_init(struct device_node *node) cb->write(i, cb->safe_map); } - register_routable_domain_ops(&routable_irq_domain_ops); + raw_spin_lock_init(&cb->lock); + return 0; err_reg_offset: @@ -309,18 +326,37 @@ err_cb: return ret; } -static const struct of_device_id crossbar_match[] __initconst = { - { .compatible = "ti,irq-crossbar" }, - {} -}; - -int __init irqcrossbar_init(void) +static int __init irqcrossbar_init(struct device_node *node, + struct device_node *parent) { - struct device_node *np; - np = of_find_matching_node(NULL, crossbar_match); - if (!np) + struct irq_domain *parent_domain, *domain; + int err; + + if (!parent) { + pr_err("%s: no parent, giving up\n", node->full_name); return -ENODEV; + } + + parent_domain = irq_find_host(parent); + if (!parent_domain) { + pr_err("%s: unable to obtain parent domain\n", node->full_name); + return -ENXIO; + } + + err = crossbar_of_init(node); + if (err) + return err; + + domain = irq_domain_add_hierarchy(parent_domain, 0, + cb->max_crossbar_sources, + node, &crossbar_domain_ops, + NULL); + if (!domain) { + pr_err("%s: failed to allocated domain\n", node->full_name); + return -ENOMEM; + } - crossbar_of_init(np); return 0; } + +IRQCHIP_DECLARE(ti_irqcrossbar, "ti,irq-crossbar", irqcrossbar_init); diff --git a/include/linux/irqchip/irq-crossbar.h b/include/linux/irqchip/irq-crossbar.h deleted file mode 100644 index e5537b81df8d..000000000000 --- a/include/linux/irqchip/irq-crossbar.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * drivers/irqchip/irq-crossbar.h - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com - * - * 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. - * - */ -int irqcrossbar_init(void); -- cgit v1.2.3 From a5561c3e845cae41ae40c15689a7f26e90b000c8 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 11 Mar 2015 15:43:46 +0000 Subject: irqchip: gic: Get rid of routable domain The only user of the so called "routable domain" functionality now being fixed, let's clean up the GIC. Acked-by: Tony Lindgren Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1426088629-15377-5-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-gic.c | 59 ++++------------------------------------- include/linux/irqchip/arm-gic.h | 6 ----- 2 files changed, 5 insertions(+), 60 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 4634cf7d0ec3..e3ca6da4314e 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -798,15 +798,12 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, handle_fasteoi_irq, NULL, NULL); set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); - - gic_routable_irq_domain_ops->map(d, irq, hw); } return 0; } static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq) { - gic_routable_irq_domain_ops->unmap(d, irq); } static int gic_irq_domain_xlate(struct irq_domain *d, @@ -825,16 +822,8 @@ static int gic_irq_domain_xlate(struct irq_domain *d, *out_hwirq = intspec[1] + 16; /* For SPIs, we need to add 16 more to get the GIC irq ID number */ - if (!intspec[0]) { - ret = gic_routable_irq_domain_ops->xlate(d, controller, - intspec, - intsize, - out_hwirq, - out_type); - - if (IS_ERR_VALUE(ret)) - return ret; - } + if (!intspec[0]) + *out_hwirq += 16; *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; @@ -891,37 +880,6 @@ static const struct irq_domain_ops gic_irq_domain_ops = { .xlate = gic_irq_domain_xlate, }; -/* Default functions for routable irq domain */ -static int gic_routable_irq_domain_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hw) -{ - return 0; -} - -static void gic_routable_irq_domain_unmap(struct irq_domain *d, - unsigned int irq) -{ -} - -static int gic_routable_irq_domain_xlate(struct irq_domain *d, - struct device_node *controller, - const u32 *intspec, unsigned int intsize, - unsigned long *out_hwirq, - unsigned int *out_type) -{ - *out_hwirq += 16; - return 0; -} - -static const struct irq_domain_ops gic_default_routable_irq_domain_ops = { - .map = gic_routable_irq_domain_map, - .unmap = gic_routable_irq_domain_unmap, - .xlate = gic_routable_irq_domain_xlate, -}; - -const struct irq_domain_ops *gic_routable_irq_domain_ops = - &gic_default_routable_irq_domain_ops; - void __init gic_init_bases(unsigned int gic_nr, int irq_start, void __iomem *dist_base, void __iomem *cpu_base, u32 percpu_offset, struct device_node *node) @@ -929,7 +887,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, irq_hw_number_t hwirq_base; struct gic_chip_data *gic; int gic_irqs, irq_base, i; - int nr_routable_irqs; BUG_ON(gic_nr >= MAX_GIC_NR); @@ -985,15 +942,9 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, gic->gic_irqs = gic_irqs; if (node) { /* DT case */ - const struct irq_domain_ops *ops = &gic_irq_domain_hierarchy_ops; - - if (!of_property_read_u32(node, "arm,routable-irqs", - &nr_routable_irqs)) { - ops = &gic_irq_domain_ops; - gic_irqs = nr_routable_irqs; - } - - gic->domain = irq_domain_add_linear(node, gic_irqs, ops, gic); + gic->domain = irq_domain_add_linear(node, gic_irqs, + &gic_irq_domain_hierarchy_ops, + gic); } else { /* Non-DT case */ /* * For primary GICs, skip over SGIs. diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 71d706d5f169..3978c5be4edc 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -115,11 +115,5 @@ int gic_get_cpu_id(unsigned int cpu); void gic_migrate_target(unsigned int new_cpu_id); unsigned long gic_get_sgir_physaddr(void); -extern const struct irq_domain_ops *gic_routable_irq_domain_ops; -static inline void __init register_routable_domain_ops - (const struct irq_domain_ops *ops) -{ - gic_routable_irq_domain_ops = ops; -} #endif /* __ASSEMBLY */ #endif -- cgit v1.2.3 From 49869be2d9d0e9195706da7050c81bc909f41f4f Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 11 Mar 2015 15:45:34 +0000 Subject: irqchip: gic: Add an entry point to set up irqchip flags A common use of gic_arch_extn is to set up additional flags to the GIC irqchip. It looks like a benign enough hack that doesn't really require the users of that feature to be converted to stacked domains. Add a gic_set_irqchip_flags() function that platform code can call instead of using the dreaded gic_arch_extn. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1426088737-15817-2-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-gic.c | 5 +++++ include/linux/irqchip/arm-gic.h | 1 + 2 files changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 4634cf7d0ec3..072432c22207 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -922,6 +922,11 @@ static const struct irq_domain_ops gic_default_routable_irq_domain_ops = { const struct irq_domain_ops *gic_routable_irq_domain_ops = &gic_default_routable_irq_domain_ops; +void gic_set_irqchip_flags(unsigned long flags) +{ + gic_chip.flags |= flags; +} + void __init gic_init_bases(unsigned int gic_nr, int irq_start, void __iomem *dist_base, void __iomem *cpu_base, u32 percpu_offset, struct device_node *node) diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 71d706d5f169..3bca864fd6fc 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -97,6 +97,7 @@ struct device_node; extern struct irq_chip gic_arch_extn; +void gic_set_irqchip_flags(unsigned long flags); void gic_init_bases(unsigned int, int, void __iomem *, void __iomem *, u32 offset, struct device_node *); void gic_cascade_irq(unsigned int gic_nr, unsigned int irq); -- cgit v1.2.3 From 6e3aca4419e1363ff9abb3e1710c52858fc45b66 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 11 Mar 2015 23:21:31 -0700 Subject: irqchip: gic: Don't complain in gic_get_cpumask() if UP system In a uniprocessor implementation the interrupt processor targets registers are read-as-zero/write-ignored (RAZ/WI). Unfortunately gic_get_cpumask() will print a critical message saying GIC CPU mask not found - kernel will fail to boot. if these registers all read as zero, but there won't actually be a problem on uniprocessor systems and the kernel will boot just fine. Skip this check if we're running a UP kernel or if we detect that the hardware only supports a single processor. Acked-by: Nicolas Pitre Acked-by: Felipe Balbi Acked-by: Nishanth Menon Acked-by: Stefan Agner Cc: Russell King Signed-off-by: Stephen Boyd Link: https://lkml.kernel.org/r/1426141291-21641-1-git-send-email-sboyd@codeaurora.org Signed-off-by: Jason Cooper --- drivers/irqchip/irq-gic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 4634cf7d0ec3..50e70a8c7ec4 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -349,7 +349,7 @@ static u8 gic_get_cpumask(struct gic_chip_data *gic) break; } - if (!mask) + if (!mask && num_possible_cpus() > 1) pr_crit("GIC CPU mask not found - kernel will fail to boot.\n"); return mask; -- cgit v1.2.3 From 1cd5ec73306d6e361b90e27ae17bd5f87e009567 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 18 Mar 2015 19:55:55 +0100 Subject: irqchip: renesas-irqc: Add more register documentation Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman Link: https://lkml.kernel.org/r/1426704961-27322-2-git-send-email-geert+renesas@glider.be Signed-off-by: Jason Cooper --- drivers/irqchip/irq-renesas-irqc.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c index 384e6ed61d7c..44fe04a8da95 100644 --- a/drivers/irqchip/irq-renesas-irqc.c +++ b/drivers/irqchip/irq-renesas-irqc.c @@ -30,14 +30,24 @@ #include #include -#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ +#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ -#define IRQC_REQ_STS 0x00 -#define IRQC_EN_STS 0x04 -#define IRQC_EN_SET 0x08 +#define IRQC_REQ_STS 0x00 /* Interrupt Request Status Register */ +#define IRQC_EN_STS 0x04 /* Interrupt Enable Status Register */ +#define IRQC_EN_SET 0x08 /* Interrupt Enable Set Register */ #define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10)) -#define DETECT_STATUS 0x100 + /* SYS-CPU vs. RT-CPU */ +#define DETECT_STATUS 0x100 /* IRQn Detect Status Register */ +#define MONITOR 0x104 /* IRQn Signal Level Monitor Register */ +#define HLVL_STS 0x108 /* IRQn High Level Detect Status Register */ +#define LLVL_STS 0x10c /* IRQn Low Level Detect Status Register */ +#define S_R_EDGE_STS 0x110 /* IRQn Sync Rising Edge Detect Status Reg. */ +#define S_F_EDGE_STS 0x114 /* IRQn Sync Falling Edge Detect Status Reg. */ +#define A_R_EDGE_STS 0x118 /* IRQn Async Rising Edge Detect Status Reg. */ +#define A_F_EDGE_STS 0x11c /* IRQn Async Falling Edge Detect Status Reg. */ +#define CHTEN_STS 0x120 /* Chattering Reduction Status Register */ #define IRQC_CONFIG(n) (0x180 + ((n) * 0x04)) + /* IRQn Configuration Register */ struct irqc_irq { int hw_irq; -- cgit v1.2.3 From 51b05f6b8a7dfa2c85988e506d3107144a13b975 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 18 Mar 2015 19:55:56 +0100 Subject: irqchip: renesas-irqc: Add minimal runtime PM support This is just enough to let pm_clk_*() enable the functional clock, and manage it for suspend/resume, if present. Before, it was assumed enabled by the bootloader or reset state. Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman Link: https://lkml.kernel.org/r/1426704961-27322-3-git-send-email-geert+renesas@glider.be Signed-off-by: Jason Cooper --- drivers/irqchip/irq-renesas-irqc.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c index 44fe04a8da95..2fe9612041b0 100644 --- a/drivers/irqchip/irq-renesas-irqc.c +++ b/drivers/irqchip/irq-renesas-irqc.c @@ -29,6 +29,7 @@ #include #include #include +#include #define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ @@ -180,6 +181,9 @@ static int irqc_probe(struct platform_device *pdev) p->pdev = pdev; platform_set_drvdata(pdev, p); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + /* get hold of manadatory IOMEM */ io = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!io) { @@ -260,6 +264,8 @@ err3: err2: iounmap(p->iomem); err1: + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); kfree(p); err0: return ret; @@ -275,6 +281,8 @@ static int irqc_remove(struct platform_device *pdev) irq_domain_remove(p->irq_domain); iounmap(p->iomem); + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); kfree(p); return 0; } -- cgit v1.2.3 From 00e6743b5b7333a589c7607aa0ed20416831034d Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Sun, 22 Mar 2015 09:39:45 +0200 Subject: irqchip: digicolor: Move digicolor_set_gc to init section The digicolor_set_gc() routine is only called from __init annotated digicolor_of_init(). Annotate digicolor_set_gc() with __init as well to save a few bytes at run time. Signed-off-by: Baruch Siach Link: https://lkml.kernel.org/r/a3b57ecdbe0b07f55c20c07ff98f1f694275722d.1427009985.git.baruch@tkos.co.il Signed-off-by: Jason Cooper --- drivers/irqchip/irq-digicolor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-digicolor.c b/drivers/irqchip/irq-digicolor.c index 930a2a2fac7f..3cbc658afe27 100644 --- a/drivers/irqchip/irq-digicolor.c +++ b/drivers/irqchip/irq-digicolor.c @@ -55,8 +55,8 @@ static void __exception_irq_entry digicolor_handle_irq(struct pt_regs *regs) } while (1); } -static void digicolor_set_gc(void __iomem *reg_base, unsigned irq_base, - unsigned en_reg, unsigned ack_reg) +static void __init digicolor_set_gc(void __iomem *reg_base, unsigned irq_base, + unsigned en_reg, unsigned ack_reg) { struct irq_chip_generic *gc; -- cgit v1.2.3 From 1eec582158e2d1d1d3978449d2d01da2d1c3feb8 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 23 Mar 2015 11:26:19 +0100 Subject: irqchip: tegra: Add Tegra210 support Tegra210 uses the same legacy interrupt controller as older generations but it adds a sixth instance. Signed-off-by: Thierry Reding Link: https://lkml.kernel.org/r/1427106379-14037-1-git-send-email-thierry.reding@gmail.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-tegra.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-tegra.c b/drivers/irqchip/irq-tegra.c index d919ecf29cf4..51c485d9a877 100644 --- a/drivers/irqchip/irq-tegra.c +++ b/drivers/irqchip/irq-tegra.c @@ -48,7 +48,7 @@ #define ICTLR_COP_IER_CLR 0x38 #define ICTLR_COP_IEP_CLASS 0x3c -#define TEGRA_MAX_NUM_ICTLRS 5 +#define TEGRA_MAX_NUM_ICTLRS 6 static unsigned int num_ictlrs; @@ -64,7 +64,12 @@ static const struct tegra_ictlr_soc tegra30_ictlr_soc = { .num_ictlrs = 5, }; +static const struct tegra_ictlr_soc tegra210_ictlr_soc = { + .num_ictlrs = 6, +}; + static const struct of_device_id ictlr_matches[] = { + { .compatible = "nvidia,tegra210-ictlr", .data = &tegra210_ictlr_soc }, { .compatible = "nvidia,tegra30-ictlr", .data = &tegra30_ictlr_soc }, { .compatible = "nvidia,tegra20-ictlr", .data = &tegra20_ictlr_soc }, { } @@ -369,3 +374,4 @@ out_free: IRQCHIP_DECLARE(tegra20_ictlr, "nvidia,tegra20-ictlr", tegra_ictlr_init); IRQCHIP_DECLARE(tegra30_ictlr, "nvidia,tegra30-ictlr", tegra_ictlr_init); +IRQCHIP_DECLARE(tegra210_ictlr, "nvidia,tegra210-ictlr", tegra_ictlr_init); -- cgit v1.2.3 From fa6ed4cb6fd44ef4c6fad4d9572119d22381f32c Mon Sep 17 00:00:00 2001 From: Markos Chandras Date: Mon, 23 Mar 2015 12:32:01 +0000 Subject: irqchip: mips-gic: Add new functions to start/stop the GIC counter We add new functions to start and stop the GIC counter since there are no guarantees the counter will be running after a CPU reset. The GIC counter is stopped by setting the 29th bit on the GIC Config register and it is started by clearing that bit. Cc: Thomas Gleixner Cc: Jason Cooper Cc: Andrew Bresticker Cc: Qais Yousef Cc: Signed-off-by: Markos Chandras Link: https://lkml.kernel.org/r/1427113923-9840-2-git-send-email-markos.chandras@imgtec.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-mips-gic.c | 21 +++++++++++++++++++++ include/linux/irqchip/mips-gic.h | 2 ++ 2 files changed, 23 insertions(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index 9acdc080e7ec..f2d269bca789 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -166,6 +166,27 @@ cycle_t gic_read_compare(void) return (((cycle_t) hi) << 32) + lo; } + +void gic_start_count(void) +{ + u32 gicconfig; + + /* Start the counter */ + gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG)); + gicconfig &= ~(1 << GIC_SH_CONFIG_COUNTSTOP_SHF); + gic_write(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig); +} + +void gic_stop_count(void) +{ + u32 gicconfig; + + /* Stop the counter */ + gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG)); + gicconfig |= 1 << GIC_SH_CONFIG_COUNTSTOP_SHF; + gic_write(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig); +} + #endif static bool gic_local_irq_is_routable(int intr) diff --git a/include/linux/irqchip/mips-gic.h b/include/linux/irqchip/mips-gic.h index e6a6aac451db..3ea2e4754c40 100644 --- a/include/linux/irqchip/mips-gic.h +++ b/include/linux/irqchip/mips-gic.h @@ -240,6 +240,8 @@ extern unsigned int gic_get_count_width(void); extern cycle_t gic_read_compare(void); extern void gic_write_compare(cycle_t cnt); extern void gic_write_cpu_compare(cycle_t cnt, int cpu); +extern void gic_start_count(void); +extern void gic_stop_count(void); extern void gic_send_ipi(unsigned int intr); extern unsigned int plat_ipi_call_int_xlate(unsigned int); extern unsigned int plat_ipi_resched_int_xlate(unsigned int); -- cgit v1.2.3 From 0d8e1d80bbe9852f42784072880df002534c6e0f Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 30 Mar 2015 16:04:37 +0200 Subject: irqchip: armada-370-xp: Allow using wakeup source On the Armada 370/XP SoCs, in standby mode the SoC stay powered and it is possible to wake-up from any interrupt sources. This patch adds flag to the MPIC irqchip driver to let linux know this. Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1427724278-12379-5-git-send-email-gregory.clement@free-electrons.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-armada-370-xp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index b36373c019ba..daccc8bdbb42 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -295,6 +295,7 @@ static struct irq_chip armada_370_xp_irq_chip = { #ifdef CONFIG_SMP .irq_set_affinity = armada_xp_set_affinity, #endif + .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, }; static int armada_370_xp_mpic_irq_map(struct irq_domain *h, -- cgit v1.2.3 From 6f46aedb9c85873b67f6186a8b0f9d7a769b6b2c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 1 Apr 2015 14:00:06 +0200 Subject: irqchip: renesas-irqc: Add wake-up support The IRQC module clock is managed through Runtime PM and PM Domains. If wake-up is enabled, this clock must not be disabled during system suspend. Hence implement irq_chip.irq_set_wake(), which increments/decrements the clock's enable_count when needed. This fixes wake-up by gpio-keys on r8a73a4/ape6evm. Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman Link: https://lkml.kernel.org/r/1427889606-18671-1-git-send-email-geert+renesas@glider.be Signed-off-by: Jason Cooper --- drivers/irqchip/irq-renesas-irqc.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c index 2fe9612041b0..7be2fdf69924 100644 --- a/drivers/irqchip/irq-renesas-irqc.c +++ b/drivers/irqchip/irq-renesas-irqc.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include #include @@ -66,6 +67,7 @@ struct irqc_priv { struct platform_device *pdev; struct irq_chip irq_chip; struct irq_domain *irq_domain; + struct clk *clk; }; static void irqc_dbg(struct irqc_irq *i, char *str) @@ -119,6 +121,21 @@ static int irqc_irq_set_type(struct irq_data *d, unsigned int type) return 0; } +static int irqc_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct irqc_priv *p = irq_data_get_irq_chip_data(d); + + if (!p->clk) + return 0; + + if (on) + clk_enable(p->clk); + else + clk_disable(p->clk); + + return 0; +} + static irqreturn_t irqc_irq_handler(int irq, void *dev_id) { struct irqc_irq *i = dev_id; @@ -181,6 +198,12 @@ static int irqc_probe(struct platform_device *pdev) p->pdev = pdev; platform_set_drvdata(pdev, p); + p->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(p->clk)) { + dev_warn(&pdev->dev, "unable to get clock\n"); + p->clk = NULL; + } + pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); @@ -224,7 +247,8 @@ static int irqc_probe(struct platform_device *pdev) irq_chip->irq_mask = irqc_irq_disable; irq_chip->irq_unmask = irqc_irq_enable; irq_chip->irq_set_type = irqc_irq_set_type; - irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; + irq_chip->irq_set_wake = irqc_irq_set_wake; + irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND; p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, p->number_of_irqs, -- cgit v1.2.3 From 567178077c369ae6a32f90926fb4172f7cf1f87f Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 18 Mar 2015 11:01:23 +0000 Subject: irqchip: GIC: Add support for irq_[get, set]_irqchip_state() Add the required hooks for the internal state of an interrupt to be exposed to other subsystems. Signed-off-by: Marc Zyngier Cc: linux-arm-kernel@lists.infradead.org Cc: Abhijeet Dharmapurikar Cc: Stephen Boyd Cc: Phong Vo Cc: Linus Walleij Cc: Tin Huynh Cc: Y Vo Cc: Toan Le Cc: Bjorn Andersson Cc: Jason Cooper Cc: Arnd Bergmann Link: http://lkml.kernel.org/r/1426676484-21812-3-git-send-email-marc.zyngier@arm.com Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-gic.c | 69 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 471e1cdc1933..0b4a4d0238e7 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -151,13 +151,24 @@ static inline unsigned int gic_irq(struct irq_data *d) /* * Routines to acknowledge, disable and enable interrupts */ -static void gic_mask_irq(struct irq_data *d) +static void gic_poke_irq(struct irq_data *d, u32 offset) +{ + u32 mask = 1 << (gic_irq(d) % 32); + writel_relaxed(mask, gic_dist_base(d) + offset + (gic_irq(d) / 32) * 4); +} + +static int gic_peek_irq(struct irq_data *d, u32 offset) { u32 mask = 1 << (gic_irq(d) % 32); + return !!(readl_relaxed(gic_dist_base(d) + offset + (gic_irq(d) / 32) * 4) & mask); +} + +static void gic_mask_irq(struct irq_data *d) +{ unsigned long flags; raw_spin_lock_irqsave(&irq_controller_lock, flags); - writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4); + gic_poke_irq(d, GIC_DIST_ENABLE_CLEAR); if (gic_arch_extn.irq_mask) gic_arch_extn.irq_mask(d); raw_spin_unlock_irqrestore(&irq_controller_lock, flags); @@ -165,13 +176,12 @@ static void gic_mask_irq(struct irq_data *d) static void gic_unmask_irq(struct irq_data *d) { - u32 mask = 1 << (gic_irq(d) % 32); unsigned long flags; raw_spin_lock_irqsave(&irq_controller_lock, flags); if (gic_arch_extn.irq_unmask) gic_arch_extn.irq_unmask(d); - writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4); + gic_poke_irq(d, GIC_DIST_ENABLE_SET); raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } @@ -186,6 +196,55 @@ static void gic_eoi_irq(struct irq_data *d) writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI); } +static int gic_irq_set_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, bool val) +{ + u32 reg; + + switch (which) { + case IRQCHIP_STATE_PENDING: + reg = val ? GIC_DIST_PENDING_SET : GIC_DIST_PENDING_CLEAR; + break; + + case IRQCHIP_STATE_ACTIVE: + reg = val ? GIC_DIST_ACTIVE_SET : GIC_DIST_ACTIVE_CLEAR; + break; + + case IRQCHIP_STATE_MASKED: + reg = val ? GIC_DIST_ENABLE_CLEAR : GIC_DIST_ENABLE_SET; + break; + + default: + return -EINVAL; + } + + gic_poke_irq(d, reg); + return 0; +} + +static int gic_irq_get_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, bool *val) +{ + switch (which) { + case IRQCHIP_STATE_PENDING: + *val = gic_peek_irq(d, GIC_DIST_PENDING_SET); + break; + + case IRQCHIP_STATE_ACTIVE: + *val = gic_peek_irq(d, GIC_DIST_ACTIVE_SET); + break; + + case IRQCHIP_STATE_MASKED: + *val = !gic_peek_irq(d, GIC_DIST_ENABLE_SET); + break; + + default: + return -EINVAL; + } + + return 0; +} + static int gic_set_type(struct irq_data *d, unsigned int type) { void __iomem *base = gic_dist_base(d); @@ -329,6 +388,8 @@ static struct irq_chip gic_chip = { .irq_set_affinity = gic_set_affinity, #endif .irq_set_wake = gic_set_wake, + .irq_get_irqchip_state = gic_irq_get_irqchip_state, + .irq_set_irqchip_state = gic_irq_set_irqchip_state, }; void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) -- cgit v1.2.3 From b594c6e20c7ff65e0f0775cb1866e97501c96846 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 18 Mar 2015 11:01:24 +0000 Subject: irqchip: GICv3: Add support for irq_[get, set]_irqchip_state() Add the required hooks for the internal state of an interrupt to be exposed to other subsystems. Signed-off-by: Marc Zyngier Cc: linux-arm-kernel@lists.infradead.org Cc: Abhijeet Dharmapurikar Cc: Stephen Boyd Cc: Phong Vo Cc: Linus Walleij Cc: Tin Huynh Cc: Y Vo Cc: Toan Le Cc: Bjorn Andersson Cc: Jason Cooper Cc: Arnd Bergmann Link: http://lkml.kernel.org/r/1426676484-21812-4-git-send-email-marc.zyngier@arm.com Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-gic-v3.c | 83 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index fd8850def1b8..4f2fb62e6f37 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -195,6 +195,19 @@ static void gic_enable_redist(bool enable) /* * Routines to disable, enable, EOI and route interrupts */ +static int gic_peek_irq(struct irq_data *d, u32 offset) +{ + u32 mask = 1 << (gic_irq(d) % 32); + void __iomem *base; + + if (gic_irq_in_rdist(d)) + base = gic_data_rdist_sgi_base(); + else + base = gic_data.dist_base; + + return !!(readl_relaxed(base + offset + (gic_irq(d) / 32) * 4) & mask); +} + static void gic_poke_irq(struct irq_data *d, u32 offset) { u32 mask = 1 << (gic_irq(d) % 32); @@ -223,6 +236,61 @@ static void gic_unmask_irq(struct irq_data *d) gic_poke_irq(d, GICD_ISENABLER); } +static int gic_irq_set_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, bool val) +{ + u32 reg; + + if (d->hwirq >= gic_data.irq_nr) /* PPI/SPI only */ + return -EINVAL; + + switch (which) { + case IRQCHIP_STATE_PENDING: + reg = val ? GICD_ISPENDR : GICD_ICPENDR; + break; + + case IRQCHIP_STATE_ACTIVE: + reg = val ? GICD_ISACTIVER : GICD_ICACTIVER; + break; + + case IRQCHIP_STATE_MASKED: + reg = val ? GICD_ICENABLER : GICD_ISENABLER; + break; + + default: + return -EINVAL; + } + + gic_poke_irq(d, reg); + return 0; +} + +static int gic_irq_get_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, bool *val) +{ + if (d->hwirq >= gic_data.irq_nr) /* PPI/SPI only */ + return -EINVAL; + + switch (which) { + case IRQCHIP_STATE_PENDING: + *val = gic_peek_irq(d, GICD_ISPENDR); + break; + + case IRQCHIP_STATE_ACTIVE: + *val = gic_peek_irq(d, GICD_ISACTIVER); + break; + + case IRQCHIP_STATE_MASKED: + *val = !gic_peek_irq(d, GICD_ISENABLER); + break; + + default: + return -EINVAL; + } + + return 0; +} + static void gic_eoi_irq(struct irq_data *d) { gic_write_eoir(gic_irq(d)); @@ -418,19 +486,6 @@ static void gic_cpu_init(void) } #ifdef CONFIG_SMP -static int gic_peek_irq(struct irq_data *d, u32 offset) -{ - u32 mask = 1 << (gic_irq(d) % 32); - void __iomem *base; - - if (gic_irq_in_rdist(d)) - base = gic_data_rdist_sgi_base(); - else - base = gic_data.dist_base; - - return !!(readl_relaxed(base + offset + (gic_irq(d) / 32) * 4) & mask); -} - static int gic_secondary_init(struct notifier_block *nfb, unsigned long action, void *hcpu) { @@ -601,6 +656,8 @@ static struct irq_chip gic_chip = { .irq_eoi = gic_eoi_irq, .irq_set_type = gic_set_type, .irq_set_affinity = gic_set_affinity, + .irq_get_irqchip_state = gic_irq_get_irqchip_state, + .irq_set_irqchip_state = gic_irq_set_irqchip_state, }; #define GIC_ID_NR (1U << gic_data.rdists.id_bits) -- cgit v1.2.3