From 1d50d2ce6337724d30055bdbe082236cc86e6785 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:19 +0530 Subject: irqchip: crossbar: Dont use '0' to mark reserved interrupts Today '0' is actually reserved, but may not be the same in the future. So, use a flag to mark the GIC interrupts that are reserved. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-2-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 3d15d16a7088..20105bcdf408 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -17,6 +17,7 @@ #include #define IRQ_FREE -1 +#define IRQ_RESERVED -2 #define GIC_IRQ_START 32 /* @@ -139,7 +140,7 @@ static int __init crossbar_of_init(struct device_node *node) pr_err("Invalid reserved entry\n"); goto err3; } - cb->irq_map[entry] = 0; + cb->irq_map[entry] = IRQ_RESERVED; } } @@ -170,7 +171,7 @@ static int __init crossbar_of_init(struct device_node *node) * reserved irqs. so find and store the offsets once. */ for (i = 0; i < max; i++) { - if (!cb->irq_map[i]) + if (cb->irq_map[i] == IRQ_RESERVED) continue; cb->register_offsets[i] = reserved; -- cgit v1.2.3 From 6f16fc878a51572a998655e5ef1c396cb269648d Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:20 +0530 Subject: irqchip: crossbar: Check for premapped crossbar before allocating If irq_of_parse_and_map is executed twice, the same crossbar is mapped to two different GIC interrupts. This is completely undesirable. Instead, check if the requested crossbar event is pre-allocated and provide that GIC mapping back to caller if already allocated. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-3-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 20105bcdf408..51d4b878e5d6 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -51,6 +51,17 @@ static inline 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 = 0; i < cb->int_max; i++) + if (cb->irq_map[i] == cb_no) + return i; + + return -ENODEV; +} + static inline int allocate_free_irq(int cb_no) { int i; @@ -88,11 +99,16 @@ static int crossbar_domain_xlate(struct irq_domain *d, { unsigned long ret; + ret = get_prev_map_irq(intspec[1]); + if (!IS_ERR_VALUE(ret)) + goto found; + ret = allocate_free_irq(intspec[1]); if (IS_ERR_VALUE(ret)) return ret; +found: *out_hwirq = ret + GIC_IRQ_START; return 0; } -- cgit v1.2.3 From 64e0f8ba5cae74471f72e0cb218c67915e365f47 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:21 +0530 Subject: irqchip: crossbar: Introduce ti, irqs-skip to skip irqs that bypass crossbar When, in the system due to varied reasons, interrupts might be unusable due to hardware behavior, but register maps do exist, then those interrupts should be skipped while mapping irq to crossbars. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-4-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- .../devicetree/bindings/arm/omap/crossbar.txt | 6 ++++++ drivers/irqchip/irq-crossbar.c | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/omap/crossbar.txt b/Documentation/devicetree/bindings/arm/omap/crossbar.txt index fb88585cfb93..079576573ec0 100644 --- a/Documentation/devicetree/bindings/arm/omap/crossbar.txt +++ b/Documentation/devicetree/bindings/arm/omap/crossbar.txt @@ -17,6 +17,11 @@ Required properties: so crossbar bar driver should not consider them as free lines. +Optional properties: +- ti,irqs-skip: This is similar to "ti,irqs-reserved", but these are for + SOC-specific hard-wiring of those irqs which unexpectedly bypasses the + crossbar. These irqs have a crossbar register, but still cannot be used. + Examples: crossbar_mpu: @4a020000 { compatible = "ti,irq-crossbar"; @@ -24,4 +29,5 @@ Examples: ti,max-irqs = <160>; ti,reg-size = <2>; ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>; + ti,irqs-skip = <10 133 139 140>; }; diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 51d4b878e5d6..0533a71fa86f 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -18,6 +18,7 @@ #define IRQ_FREE -1 #define IRQ_RESERVED -2 +#define IRQ_SKIP -3 #define GIC_IRQ_START 32 /* @@ -160,6 +161,25 @@ static int __init crossbar_of_init(struct device_node *node) } } + /* Skip irqs hardwired to bypass the crossbar */ + irqsr = of_get_property(node, "ti,irqs-skip", &size); + if (irqsr) { + size /= sizeof(__be32); + + for (i = 0; i < size; i++) { + of_property_read_u32_index(node, + "ti,irqs-skip", + i, &entry); + if (entry > max) { + pr_err("Invalid skip entry\n"); + ret = -EINVAL; + goto err3; + } + cb->irq_map[entry] = IRQ_SKIP; + } + } + + cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL); if (!cb->register_offsets) goto err3; -- cgit v1.2.3 From a35057d1dcb11ae67c9347ef7987cf65ac743c36 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:22 +0530 Subject: irqchip: crossbar: Initialise the crossbar with a safe value Since crossbar is s/w configurable, the initial settings of the crossbar cannot be assumed to be sane. This implies that: a) On initialization all un-reserved crossbars must be initialized to a known 'safe' value. b) When unmapping the interrupt, the safe value must be written to ensure that the crossbar mapping matches with interrupt controller usage. So provide a safe value in the dt data to map if '0' is not safe for the platform and use it during init and unmap While at this, fix the below checkpatch warning. Fixes checkpatch warning: WARNING: Unnecessary space before function pointer arguments #37: FILE: drivers/irqchip/irq-crossbar.c:37: + void (*write) (int, int); Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-5-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- .../devicetree/bindings/arm/omap/crossbar.txt | 3 +++ drivers/irqchip/irq-crossbar.c | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/omap/crossbar.txt b/Documentation/devicetree/bindings/arm/omap/crossbar.txt index 079576573ec0..5f45c78e31a9 100644 --- a/Documentation/devicetree/bindings/arm/omap/crossbar.txt +++ b/Documentation/devicetree/bindings/arm/omap/crossbar.txt @@ -22,6 +22,9 @@ Optional properties: SOC-specific hard-wiring of those irqs which unexpectedly bypasses the crossbar. These irqs have a crossbar register, but still cannot be used. +- ti,irqs-safe-map: integer which maps to a safe configuration to use + when the interrupt controller irq is unused (when not provided, default is 0) + Examples: crossbar_mpu: @4a020000 { compatible = "ti,irq-crossbar"; diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 0533a71fa86f..4be30c00f041 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -23,16 +23,18 @@ /* * @int_max: maximum number of supported interrupts + * @safe_map: safe default value to initialize the crossbar * @irq_map: array of interrupts to crossbar number mapping * @crossbar_base: crossbar base address * @register_offsets: offsets for each irq number */ struct crossbar_device { uint int_max; + uint safe_map; uint *irq_map; void __iomem *crossbar_base; int *register_offsets; - void (*write) (int, int); + void (*write)(int, int); }; static struct crossbar_device *cb; @@ -88,8 +90,10 @@ static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) { irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; - if (hw > GIC_IRQ_START) + if (hw > GIC_IRQ_START) { cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; + cb->write(hw - GIC_IRQ_START, cb->safe_map); + } } static int crossbar_domain_xlate(struct irq_domain *d, @@ -214,6 +218,17 @@ static int __init crossbar_of_init(struct device_node *node) reserved += size; } + of_property_read_u32(node, "ti,irqs-safe-map", &cb->safe_map); + + /* Initialize the crossbar with safe map to start with */ + for (i = 0; i < max; i++) { + if (cb->irq_map[i] == IRQ_RESERVED || + cb->irq_map[i] == IRQ_SKIP) + continue; + + cb->write(i, cb->safe_map); + } + register_routable_domain_ops(&routable_irq_domain_ops); return 0; -- cgit v1.2.3 From ddee0fb46d26174e71ee1df225b9f9feaff05e10 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:23 +0530 Subject: irqchip: crossbar: Change allocation logic by reversing search for free irqs Reverse the search algorithm to ensure that address mapping and IRQ allocation logics are proper. This makes the below bugs visible sooner. class 1. address space errors -> example: reg = ti,max-irqs = is a wrong parameter class 2: irq-reserved list - which decides which entries in the address space is not actually wired in class 3: wrong list of routable-irqs. In general allocating from max to min tends to have benefits in ensuring the different issues that may be present in dts is easily caught at definition time, rather than at a later point in time. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-6-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 4be30c00f041..a39cb316d2b0 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -58,7 +58,7 @@ static inline int get_prev_map_irq(int cb_no) { int i; - for (i = 0; i < cb->int_max; i++) + for (i = cb->int_max - 1; i >= 0; i--) if (cb->irq_map[i] == cb_no) return i; @@ -69,7 +69,7 @@ static inline int allocate_free_irq(int cb_no) { int i; - for (i = 0; i < cb->int_max; i++) { + for (i = cb->int_max - 1; i >= 0; i--) { if (cb->irq_map[i] == IRQ_FREE) { cb->irq_map[i] = cb_no; return i; -- cgit v1.2.3 From d4922a95a70f0c8c5e45e22af629a5b5f370b867 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:24 +0530 Subject: irqchip: crossbar: Remove IS_ERR_VALUE check IS_ERR_VALUE makes sense only *if* there could be valid values in negative error range. But in the cases that we do use it, there is no such case. Just remove the same. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-7-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index a39cb316d2b0..88fbe0ff1dde 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -102,15 +102,15 @@ static int crossbar_domain_xlate(struct irq_domain *d, unsigned long *out_hwirq, unsigned int *out_type) { - unsigned long ret; + int ret; ret = get_prev_map_irq(intspec[1]); - if (!IS_ERR_VALUE(ret)) + if (ret >= 0) goto found; ret = allocate_free_irq(intspec[1]); - if (IS_ERR_VALUE(ret)) + if (ret < 0) return ret; found: -- cgit v1.2.3 From 4dbf45e3c2f3acfd2096cb6a731d159492ddca99 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:25 +0530 Subject: irqchip: crossbar: Fix sparse and checkpatch warnings There is absolutely no need for crossbar driver to expose functions and variables into global namespace. So make them all static Also fix a couple of checkpatch warnings. Fixes sparse warnings: drivers/irqchip/irq-crossbar.c:129:29: warning: symbol 'routable_irq_domain_ops' was not declared. Should it be static? drivers/irqchip/irq-crossbar.c:261:12: warning: symbol 'irqcrossbar_init' was not declared. Should it be static? Checkpatch warnings: WARNING: Prefer kcalloc over kzalloc with multiply + cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL); WARNING: Prefer kcalloc over kzalloc with multiply + cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL); Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-8-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 88fbe0ff1dde..f7eda9f5997e 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -15,6 +15,7 @@ #include #include #include +#include #define IRQ_FREE -1 #define IRQ_RESERVED -2 @@ -118,7 +119,7 @@ found: return 0; } -const struct irq_domain_ops routable_irq_domain_ops = { +static const struct irq_domain_ops routable_irq_domain_ops = { .map = crossbar_domain_map, .unmap = crossbar_domain_unmap, .xlate = crossbar_domain_xlate @@ -139,7 +140,7 @@ static int __init crossbar_of_init(struct device_node *node) goto err1; of_property_read_u32(node, "ti,max-irqs", &max); - cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL); + cb->irq_map = kcalloc(max, sizeof(int), GFP_KERNEL); if (!cb->irq_map) goto err2; @@ -184,7 +185,7 @@ static int __init crossbar_of_init(struct device_node *node) } - cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL); + cb->register_offsets = kcalloc(max, sizeof(int), GFP_KERNEL); if (!cb->register_offsets) goto err3; -- cgit v1.2.3 From e30ef8abb383903f2b7e1bced971b98fd30cafc3 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:26 +0530 Subject: irqchip: crossbar: Fix kerneldoc warning Adding missing properties for kerneldoc (@write) and cleanup of harmless warnings while we are here. kerneldoc warnings: Warning(drivers/irqchip/irq-crossbar.c:27): missing initial short description on line: * struct crossbar_device: crossbar device description Info(drivers/irqchip/irq-crossbar.c:27): Scanning doc for struct Warning(drivers/irqchip/irq-crossbar.c:39): No description found for parameter 'write' 2 warnings Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-9-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index f7eda9f5997e..cee556bf603c 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -22,12 +22,14 @@ #define IRQ_SKIP -3 #define GIC_IRQ_START 32 -/* +/** + * struct crossbar_device - crossbar device description * @int_max: maximum number of supported interrupts * @safe_map: safe default value to initialize the crossbar * @irq_map: array of interrupts to crossbar number mapping * @crossbar_base: crossbar base address * @register_offsets: offsets for each irq number + * @write: register write function pointer */ struct crossbar_device { uint int_max; -- cgit v1.2.3 From edb442def98c3c64acc316b6b1a64791c138ab07 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:27 +0530 Subject: irqchip: crossbar: Return proper error value crossbar_of_init always returns -ENOMEM in case of errors. There can be other causes of failure like invalid data from DT. So return a appropriate error value for that case. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-10-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index cee556bf603c..10d723dfa348 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -129,19 +129,25 @@ static const struct irq_domain_ops routable_irq_domain_ops = { static int __init crossbar_of_init(struct device_node *node) { - int i, size, max, reserved = 0, entry; + int i, size, max = 0, reserved = 0, entry; const __be32 *irqsr; + int ret = -ENOMEM; cb = kzalloc(sizeof(*cb), GFP_KERNEL); if (!cb) - return -ENOMEM; + return ret; cb->crossbar_base = of_iomap(node, 0); if (!cb->crossbar_base) goto err1; of_property_read_u32(node, "ti,max-irqs", &max); + if (!max) { + pr_err("missing 'ti,max-irqs' property\n"); + ret = -EINVAL; + goto err2; + } cb->irq_map = kcalloc(max, sizeof(int), GFP_KERNEL); if (!cb->irq_map) goto err2; @@ -162,6 +168,7 @@ static int __init crossbar_of_init(struct device_node *node) i, &entry); if (entry > max) { pr_err("Invalid reserved entry\n"); + ret = -EINVAL; goto err3; } cb->irq_map[entry] = IRQ_RESERVED; @@ -205,6 +212,7 @@ static int __init crossbar_of_init(struct device_node *node) break; default: pr_err("Invalid reg-size property\n"); + ret = -EINVAL; goto err4; break; } @@ -243,7 +251,7 @@ err2: iounmap(cb->crossbar_base); err1: kfree(cb); - return -ENOMEM; + return ret; } static const struct of_device_id crossbar_match[] __initconst = { -- cgit v1.2.3 From 3c44d5151246947442f2fe8eede842e3f76dd2f1 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:28 +0530 Subject: irqchip: crossbar: Change the goto naming Using err1,2,3,4 etc makes it hard to ensure a new exit path in the middle will not result in spurious changes, so rename the error paths as per the function it does. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-11-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 10d723dfa348..afc35fd1d9aa 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -140,17 +140,17 @@ static int __init crossbar_of_init(struct device_node *node) cb->crossbar_base = of_iomap(node, 0); if (!cb->crossbar_base) - goto err1; + goto err_cb; of_property_read_u32(node, "ti,max-irqs", &max); if (!max) { pr_err("missing 'ti,max-irqs' property\n"); ret = -EINVAL; - goto err2; + goto err_base; } cb->irq_map = kcalloc(max, sizeof(int), GFP_KERNEL); if (!cb->irq_map) - goto err2; + goto err_base; cb->int_max = max; @@ -169,7 +169,7 @@ static int __init crossbar_of_init(struct device_node *node) if (entry > max) { pr_err("Invalid reserved entry\n"); ret = -EINVAL; - goto err3; + goto err_irq_map; } cb->irq_map[entry] = IRQ_RESERVED; } @@ -187,7 +187,7 @@ static int __init crossbar_of_init(struct device_node *node) if (entry > max) { pr_err("Invalid skip entry\n"); ret = -EINVAL; - goto err3; + goto err_irq_map; } cb->irq_map[entry] = IRQ_SKIP; } @@ -196,7 +196,7 @@ static int __init crossbar_of_init(struct device_node *node) cb->register_offsets = kcalloc(max, sizeof(int), GFP_KERNEL); if (!cb->register_offsets) - goto err3; + goto err_irq_map; of_property_read_u32(node, "ti,reg-size", &size); @@ -213,7 +213,7 @@ static int __init crossbar_of_init(struct device_node *node) default: pr_err("Invalid reg-size property\n"); ret = -EINVAL; - goto err4; + goto err_reg_offset; break; } @@ -230,7 +230,6 @@ static int __init crossbar_of_init(struct device_node *node) } of_property_read_u32(node, "ti,irqs-safe-map", &cb->safe_map); - /* Initialize the crossbar with safe map to start with */ for (i = 0; i < max; i++) { if (cb->irq_map[i] == IRQ_RESERVED || @@ -243,13 +242,13 @@ static int __init crossbar_of_init(struct device_node *node) register_routable_domain_ops(&routable_irq_domain_ops); return 0; -err4: +err_reg_offset: kfree(cb->register_offsets); -err3: +err_irq_map: kfree(cb->irq_map); -err2: +err_base: iounmap(cb->crossbar_base); -err1: +err_cb: kfree(cb); return ret; } -- cgit v1.2.3 From 99e37d0ea607623f286b4841cc355e65b297170c Mon Sep 17 00:00:00 2001 From: Sricharan R Date: Thu, 26 Jun 2014 12:40:29 +0530 Subject: irqchip: crossbar: Set cb pointer to null in case of error If crossbar_of_init returns with a error, then set the cb pointer to null. Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-12-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index afc35fd1d9aa..a8c615692803 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -250,6 +250,8 @@ err_base: iounmap(cb->crossbar_base); err_cb: kfree(cb); + + cb = NULL; return ret; } -- cgit v1.2.3 From 8b09a45dc12f83f2312a47f0f0087ec4004ebacc Mon Sep 17 00:00:00 2001 From: Sricharan R Date: Thu, 26 Jun 2014 12:40:30 +0530 Subject: irqchip: crossbar: Add kerneldoc for crossbar_domain_unmap callback Adding kerneldoc for unmap callback function. Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-13-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index a8c615692803..518d712c475a 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -89,6 +89,17 @@ static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, return 0; } +/** + * crossbar_domain_unmap - unmap a crossbar<->irq connection + * @d: domain of irq to unmap + * @irq: virq number + * + * 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 + * unmapped. This is because unmap is called during irq_dispose_mapping(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) { irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; -- cgit v1.2.3 From 2f7d2fb71dd0c14f9c0fe66f2ed7b4685fa745e2 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:31 +0530 Subject: irqchip: crossbar: Introduce ti, max-crossbar-sources to identify valid crossbar mapping Currently we attempt to map any crossbar value to an IRQ, however, this is not correct from hardware perspective. There is a max crossbar event number upto which hardware supports. So describe the same in device tree using 'ti,max-crossbar-sources' property and use it to validate requests. [ jac - remove MAX_SOURCES from binding doc, use integer because we shouldn't put implementation details in the binding docs ] Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-14-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- .../devicetree/bindings/arm/omap/crossbar.txt | 2 ++ drivers/irqchip/irq-crossbar.c | 21 +++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/omap/crossbar.txt b/Documentation/devicetree/bindings/arm/omap/crossbar.txt index 5f45c78e31a9..a6e462f88889 100644 --- a/Documentation/devicetree/bindings/arm/omap/crossbar.txt +++ b/Documentation/devicetree/bindings/arm/omap/crossbar.txt @@ -10,6 +10,7 @@ Required properties: - compatible : Should be "ti,irq-crossbar" - reg: Base address and the size of the crossbar registers. - ti,max-irqs: Total number of irqs available at the interrupt controller. +- ti,max-crossbar-sources: Maximum number of crossbar sources that can be routed. - ti,reg-size: Size of a individual register in bytes. Every individual register is assumed to be of same size. Valid sizes are 1, 2, 4. - ti,irqs-reserved: List of the reserved irq lines that are not muxed using @@ -30,6 +31,7 @@ Examples: compatible = "ti,irq-crossbar"; reg = <0x4a002a48 0x130>; ti,max-irqs = <160>; + ti,max-crossbar-sources = <400>; ti,reg-size = <2>; ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>; ti,irqs-skip = <10 133 139 140>; diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 518d712c475a..c9f068ca7bc9 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -26,6 +26,7 @@ * struct crossbar_device - crossbar device description * @int_max: maximum number of supported interrupts * @safe_map: safe default value to initialize the crossbar + * @max_crossbar_sources: Maximum number of crossbar sources * @irq_map: array of interrupts to crossbar number mapping * @crossbar_base: crossbar base address * @register_offsets: offsets for each irq number @@ -34,6 +35,7 @@ struct crossbar_device { uint int_max; uint safe_map; + uint max_crossbar_sources; uint *irq_map; void __iomem *crossbar_base; int *register_offsets; @@ -117,12 +119,19 @@ static int crossbar_domain_xlate(struct irq_domain *d, unsigned int *out_type) { int ret; + int req_num = intspec[1]; - ret = get_prev_map_irq(intspec[1]); + if (req_num >= cb->max_crossbar_sources) { + 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(intspec[1]); + ret = allocate_free_irq(req_num); if (ret < 0) return ret; @@ -153,6 +162,14 @@ static int __init crossbar_of_init(struct device_node *node) if (!cb->crossbar_base) goto err_cb; + of_property_read_u32(node, "ti,max-crossbar-sources", + &cb->max_crossbar_sources); + if (!cb->max_crossbar_sources) { + pr_err("missing 'ti,max-crossbar-sources' property\n"); + ret = -EINVAL; + goto err_base; + } + of_property_read_u32(node, "ti,max-irqs", &max); if (!max) { pr_err("missing 'ti,max-irqs' property\n"); -- cgit v1.2.3 From 29918b6790d92456cab87ff41a01224e0882d04a Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:32 +0530 Subject: irqchip: crossbar: Introduce centralized check for crossbar write This is a basic check to ensure that crossbar register needs to be written. This ensures that we have a common check which is used in both map and unmap logic. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-15-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index c9f068ca7bc9..83f803bfab76 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -84,10 +84,20 @@ static inline int allocate_free_irq(int cb_no) return -ENODEV; } +static inline bool needs_crossbar_write(irq_hw_number_t hw) +{ + if (hw > GIC_IRQ_START) + return true; + + return false; +} + static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { - cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); + if (needs_crossbar_write(hw)) + cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); + return 0; } @@ -106,7 +116,7 @@ static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) { irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; - if (hw > GIC_IRQ_START) { + if (needs_crossbar_write(hw)) { cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; cb->write(hw - GIC_IRQ_START, cb->safe_map); } -- cgit v1.2.3 From 9a34f73fb75531507760b957407fb55278b38ae8 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:33 +0530 Subject: documentation: dt: omap: crossbar: Add description for interrupt consumer The current crossbar description does not include the description required for the consumer of the crossbar, a.k.a devices whoes events pass through the crossbar into the GIC interrupt controller. So, provide documentation for the same. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-16-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- Documentation/devicetree/bindings/arm/omap/crossbar.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/omap/crossbar.txt b/Documentation/devicetree/bindings/arm/omap/crossbar.txt index a6e462f88889..ce7d01d86705 100644 --- a/Documentation/devicetree/bindings/arm/omap/crossbar.txt +++ b/Documentation/devicetree/bindings/arm/omap/crossbar.txt @@ -36,3 +36,20 @@ Examples: ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>; ti,irqs-skip = <10 133 139 140>; }; + +Consumer: +======== +See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt and +Documentation/devicetree/bindings/arm/gic.txt for further details. + +An interrupt consumer on an SoC using crossbar will use: + interrupts = +request number shall be between 0 to that described by +"ti,max-crossbar-sources" + +Example: + device_x@0x4a023000 { + /* Crossbar 8 used */ + interrupts = ; + ... + }; -- cgit v1.2.3 From d360892d37b5d0e82595001c4be6d49311e2c265 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:34 +0530 Subject: irqchip: crossbar: Allow for quirky hardware with direct hardwiring of GIC On certain platforms such as DRA7, SPIs 0, 1, 2, 3, 5, 6, 10, 131, 132, 133 are direct wired to hardware blocks bypassing crossbar. This quirky implementation is *NOT* supposed to be the expectation of crossbar hardware usage. However, these are already marked in our description of the hardware with SKIP and RESERVED where appropriate. Unfortunately, we need to be able to refer to these hardwired IRQs. So, to request these, crossbar driver can use the existing information from it's table that these SKIP/RESERVED maps are direct wired sources and generic allocation/programming of crossbar should be avoided. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-17-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- .../devicetree/bindings/arm/omap/crossbar.txt | 12 ++++++++++-- drivers/irqchip/irq-crossbar.c | 20 ++++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/omap/crossbar.txt b/Documentation/devicetree/bindings/arm/omap/crossbar.txt index ce7d01d86705..4139db353d0a 100644 --- a/Documentation/devicetree/bindings/arm/omap/crossbar.txt +++ b/Documentation/devicetree/bindings/arm/omap/crossbar.txt @@ -44,8 +44,10 @@ Documentation/devicetree/bindings/arm/gic.txt for further details. An interrupt consumer on an SoC using crossbar will use: interrupts = -request number shall be between 0 to that described by -"ti,max-crossbar-sources" +When the request number is between 0 to that described by +"ti,max-crossbar-sources", it is assumed to be a crossbar mapping. If the +request_number is greater than "ti,max-crossbar-sources", then it is mapped as a +quirky hardware mapping direct to GIC. Example: device_x@0x4a023000 { @@ -53,3 +55,9 @@ Example: interrupts = ; ... }; + + device_y@0x4a033000 { + /* Direct mapped GIC SPI 1 used */ + interrupts = ; + ... + }; diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 83f803bfab76..85c2985d8bcb 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -86,8 +86,13 @@ static inline int allocate_free_irq(int cb_no) static inline bool needs_crossbar_write(irq_hw_number_t hw) { - if (hw > GIC_IRQ_START) - return true; + int cb_no; + + 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; + } return false; } @@ -130,8 +135,19 @@ static int crossbar_domain_xlate(struct irq_domain *d, { 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; -- cgit v1.2.3 From 4db8e6d20ccb158de2e06d30a1a421f4d053e429 Mon Sep 17 00:00:00 2001 From: Stefan Kristiansson Date: Mon, 26 May 2014 23:31:42 +0300 Subject: irqchip: or1k-pic: Migrate from arch/openrisc/ In addition to consolidating the or1k-pic with other interrupt controllers, this makes OpenRISC less tied to its on-cpu interrupt controller. All or1k-pic specific parts are moved out of irq.c and into drivers/irqchip/irq-or1k-pic.c In that transition, the functionality have been divided into three chip variants. One that handles level triggered interrupts, one that handles edge triggered interrupts and one that handles the interrupt controller that is present in the or1200 OpenRISC cpu implementation. Signed-off-by: Stefan Kristiansson Link: https://lkml.kernel.org/r/1401136302-27654-1-git-send-email-stefan.kristiansson@saunalahti.fi Acked-by: Jonas Bonn Signed-off-by: Jason Cooper --- .../interrupt-controller/opencores,or1k-pic.txt | 23 +++ arch/openrisc/Kconfig | 1 + arch/openrisc/include/asm/irq.h | 3 + arch/openrisc/kernel/irq.c | 146 ++--------------- drivers/irqchip/Kconfig | 4 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-or1k-pic.c | 182 +++++++++++++++++++++ 7 files changed, 227 insertions(+), 133 deletions(-) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/opencores,or1k-pic.txt create mode 100644 drivers/irqchip/irq-or1k-pic.c diff --git a/Documentation/devicetree/bindings/interrupt-controller/opencores,or1k-pic.txt b/Documentation/devicetree/bindings/interrupt-controller/opencores,or1k-pic.txt new file mode 100644 index 000000000000..55c04faa3f3f --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/opencores,or1k-pic.txt @@ -0,0 +1,23 @@ +OpenRISC 1000 Programmable Interrupt Controller + +Required properties: + +- compatible : should be "opencores,or1k-pic-level" for variants with + level triggered interrupt lines, "opencores,or1k-pic-edge" for variants with + edge triggered interrupt lines or "opencores,or1200-pic" for machines + with the non-spec compliant or1200 type implementation. + + "opencores,or1k-pic" is also provided as an alias to "opencores,or1200-pic", + but this is only for backwards compatibility. + +- interrupt-controller : Identifies the node as an interrupt controller +- #interrupt-cells : Specifies the number of cells needed to encode an + interrupt source. The value shall be 1. + +Example: + +intc: interrupt-controller { + compatible = "opencores,or1k-pic-level"; + interrupt-controller; + #interrupt-cells = <1>; +}; diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index e71d712afb79..88e83368bbf5 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig @@ -22,6 +22,7 @@ config OPENRISC select GENERIC_STRNLEN_USER select MODULES_USE_ELF_RELA select HAVE_DEBUG_STACKOVERFLOW + select OR1K_PIC config MMU def_bool y diff --git a/arch/openrisc/include/asm/irq.h b/arch/openrisc/include/asm/irq.h index eb612b1865d2..b84634cc95eb 100644 --- a/arch/openrisc/include/asm/irq.h +++ b/arch/openrisc/include/asm/irq.h @@ -24,4 +24,7 @@ #define NO_IRQ (-1) +void handle_IRQ(unsigned int, struct pt_regs *); +extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); + #endif /* __ASM_OPENRISC_IRQ_H__ */ diff --git a/arch/openrisc/kernel/irq.c b/arch/openrisc/kernel/irq.c index 8ec77bc9f1e7..967eb1430203 100644 --- a/arch/openrisc/kernel/irq.c +++ b/arch/openrisc/kernel/irq.c @@ -16,11 +16,10 @@ #include #include -#include #include #include +#include #include -#include #include /* read interrupt enabled status */ @@ -37,150 +36,31 @@ void arch_local_irq_restore(unsigned long flags) } EXPORT_SYMBOL(arch_local_irq_restore); - -/* OR1K PIC implementation */ - -/* We're a couple of cycles faster than the generic implementations with - * these 'fast' versions. - */ - -static void or1k_pic_mask(struct irq_data *data) -{ - mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); -} - -static void or1k_pic_unmask(struct irq_data *data) -{ - mtspr(SPR_PICMR, mfspr(SPR_PICMR) | (1UL << data->hwirq)); -} - -static void or1k_pic_ack(struct irq_data *data) -{ - /* EDGE-triggered interrupts need to be ack'ed in order to clear - * the latch. - * LEVEL-triggered interrupts do not need to be ack'ed; however, - * ack'ing the interrupt has no ill-effect and is quicker than - * trying to figure out what type it is... - */ - - /* The OpenRISC 1000 spec says to write a 1 to the bit to ack the - * interrupt, but the OR1200 does this backwards and requires a 0 - * to be written... - */ - -#ifdef CONFIG_OR1K_1200 - /* There are two oddities with the OR1200 PIC implementation: - * i) LEVEL-triggered interrupts are latched and need to be cleared - * ii) the interrupt latch is cleared by writing a 0 to the bit, - * as opposed to a 1 as mandated by the spec - */ - - mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq)); -#else - WARN(1, "Interrupt handling possibly broken\n"); - mtspr(SPR_PICSR, (1UL << data->hwirq)); -#endif -} - -static void or1k_pic_mask_ack(struct irq_data *data) -{ - /* Comments for pic_ack apply here, too */ - -#ifdef CONFIG_OR1K_1200 - mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); - mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq)); -#else - WARN(1, "Interrupt handling possibly broken\n"); - mtspr(SPR_PICMR, (1UL << data->hwirq)); - mtspr(SPR_PICSR, (1UL << data->hwirq)); -#endif -} - -#if 0 -static int or1k_pic_set_type(struct irq_data *data, unsigned int flow_type) -{ - /* There's nothing to do in the PIC configuration when changing - * flow type. Level and edge-triggered interrupts are both - * supported, but it's PIC-implementation specific which type - * is handled. */ - - return irq_setup_alt_chip(data, flow_type); -} -#endif - -static struct irq_chip or1k_dev = { - .name = "or1k-PIC", - .irq_unmask = or1k_pic_unmask, - .irq_mask = or1k_pic_mask, - .irq_ack = or1k_pic_ack, - .irq_mask_ack = or1k_pic_mask_ack, -}; - -static struct irq_domain *root_domain; - -static inline int pic_get_irq(int first) -{ - int hwirq; - - hwirq = ffs(mfspr(SPR_PICSR) >> first); - if (!hwirq) - return NO_IRQ; - else - hwirq = hwirq + first -1; - - return irq_find_mapping(root_domain, hwirq); -} - - -static int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) +void __init init_IRQ(void) { - irq_set_chip_and_handler_name(irq, &or1k_dev, - handle_level_irq, "level"); - irq_set_status_flags(irq, IRQ_LEVEL | IRQ_NOPROBE); - - return 0; + irqchip_init(); } -static const struct irq_domain_ops or1k_irq_domain_ops = { - .xlate = irq_domain_xlate_onecell, - .map = or1k_map, -}; - -/* - * This sets up the IRQ domain for the PIC built in to the OpenRISC - * 1000 CPU. This is the "root" domain as these are the interrupts - * that directly trigger an exception in the CPU. - */ -static void __init or1k_irq_init(void) -{ - struct device_node *intc = NULL; - - /* The interrupt controller device node is mandatory */ - intc = of_find_compatible_node(NULL, NULL, "opencores,or1k-pic"); - BUG_ON(!intc); - - /* Disable all interrupts until explicitly requested */ - mtspr(SPR_PICMR, (0UL)); - - root_domain = irq_domain_add_linear(intc, 32, - &or1k_irq_domain_ops, NULL); -} +static void (*handle_arch_irq)(struct pt_regs *); -void __init init_IRQ(void) +void __init set_handle_irq(void (*handle_irq)(struct pt_regs *)) { - or1k_irq_init(); + handle_arch_irq = handle_irq; } -void __irq_entry do_IRQ(struct pt_regs *regs) +void handle_IRQ(unsigned int irq, struct pt_regs *regs) { - int irq = -1; struct pt_regs *old_regs = set_irq_regs(regs); irq_enter(); - while ((irq = pic_get_irq(irq + 1)) != NO_IRQ) - generic_handle_irq(irq); + generic_handle_irq(irq); irq_exit(); set_irq_regs(old_regs); } + +void __irq_entry do_IRQ(struct pt_regs *regs) +{ + handle_arch_irq(regs); +} diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index bbb746e35500..131f18562d7d 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -53,6 +53,10 @@ config CLPS711X_IRQCHIP select SPARSE_IRQ default y +config OR1K_PIC + bool + select IRQ_DOMAIN + config ORION_IRQCHIP bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 62a13e5ef98f..7fba336c4daf 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_METAG) += irq-metag-ext.o obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o +obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o diff --git a/drivers/irqchip/irq-or1k-pic.c b/drivers/irqchip/irq-or1k-pic.c new file mode 100644 index 000000000000..17ff033d9925 --- /dev/null +++ b/drivers/irqchip/irq-or1k-pic.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2010-2011 Jonas Bonn + * Copyright (C) 2014 Stefan Kristansson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include + +#include "irqchip.h" + +/* OR1K PIC implementation */ + +struct or1k_pic_dev { + struct irq_chip chip; + irq_flow_handler_t handle; + unsigned long flags; +}; + +/* + * We're a couple of cycles faster than the generic implementations with + * these 'fast' versions. + */ + +static void or1k_pic_mask(struct irq_data *data) +{ + mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); +} + +static void or1k_pic_unmask(struct irq_data *data) +{ + mtspr(SPR_PICMR, mfspr(SPR_PICMR) | (1UL << data->hwirq)); +} + +static void or1k_pic_ack(struct irq_data *data) +{ + mtspr(SPR_PICSR, (1UL << data->hwirq)); +} + +static void or1k_pic_mask_ack(struct irq_data *data) +{ + mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); + mtspr(SPR_PICSR, (1UL << data->hwirq)); +} + +/* + * There are two oddities with the OR1200 PIC implementation: + * i) LEVEL-triggered interrupts are latched and need to be cleared + * ii) the interrupt latch is cleared by writing a 0 to the bit, + * as opposed to a 1 as mandated by the spec + */ +static void or1k_pic_or1200_ack(struct irq_data *data) +{ + mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq)); +} + +static void or1k_pic_or1200_mask_ack(struct irq_data *data) +{ + mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); + mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq)); +} + +static struct or1k_pic_dev or1k_pic_level = { + .chip = { + .name = "or1k-PIC-level", + .irq_unmask = or1k_pic_unmask, + .irq_mask = or1k_pic_mask, + .irq_mask_ack = or1k_pic_mask, + }, + .handle = handle_level_irq, + .flags = IRQ_LEVEL | IRQ_NOPROBE, +}; + +static struct or1k_pic_dev or1k_pic_edge = { + .chip = { + .name = "or1k-PIC-edge", + .irq_unmask = or1k_pic_unmask, + .irq_mask = or1k_pic_mask, + .irq_ack = or1k_pic_ack, + .irq_mask_ack = or1k_pic_mask_ack, + }, + .handle = handle_edge_irq, + .flags = IRQ_LEVEL | IRQ_NOPROBE, +}; + +static struct or1k_pic_dev or1k_pic_or1200 = { + .chip = { + .name = "or1200-PIC", + .irq_unmask = or1k_pic_unmask, + .irq_mask = or1k_pic_mask, + .irq_ack = or1k_pic_or1200_ack, + .irq_mask_ack = or1k_pic_or1200_mask_ack, + }, + .handle = handle_level_irq, + .flags = IRQ_LEVEL | IRQ_NOPROBE, +}; + +static struct irq_domain *root_domain; + +static inline int pic_get_irq(int first) +{ + int hwirq; + + hwirq = ffs(mfspr(SPR_PICSR) >> first); + if (!hwirq) + return NO_IRQ; + else + hwirq = hwirq + first - 1; + + return irq_find_mapping(root_domain, hwirq); +} + +static void or1k_pic_handle_irq(struct pt_regs *regs) +{ + int irq = -1; + + while ((irq = pic_get_irq(irq + 1)) != NO_IRQ) + handle_IRQ(irq, regs); +} + +static int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) +{ + struct or1k_pic_dev *pic = d->host_data; + + irq_set_chip_and_handler(irq, &pic->chip, pic->handle); + irq_set_status_flags(irq, pic->flags); + + return 0; +} + +static const struct irq_domain_ops or1k_irq_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = or1k_map, +}; + +/* + * This sets up the IRQ domain for the PIC built in to the OpenRISC + * 1000 CPU. This is the "root" domain as these are the interrupts + * that directly trigger an exception in the CPU. + */ +static int __init or1k_pic_init(struct device_node *node, + struct or1k_pic_dev *pic) +{ + /* Disable all interrupts until explicitly requested */ + mtspr(SPR_PICMR, (0UL)); + + root_domain = irq_domain_add_linear(node, 32, &or1k_irq_domain_ops, + pic); + + set_handle_irq(or1k_pic_handle_irq); + + return 0; +} + +static int __init or1k_pic_or1200_init(struct device_node *node, + struct device_node *parent) +{ + return or1k_pic_init(node, &or1k_pic_or1200); +} +IRQCHIP_DECLARE(or1k_pic_or1200, "opencores,or1200-pic", or1k_pic_or1200_init); +IRQCHIP_DECLARE(or1k_pic, "opencores,or1k-pic", or1k_pic_or1200_init); + +static int __init or1k_pic_level_init(struct device_node *node, + struct device_node *parent) +{ + return or1k_pic_init(node, &or1k_pic_level); +} +IRQCHIP_DECLARE(or1k_pic_level, "opencores,or1k-pic-level", + or1k_pic_level_init); + +static int __init or1k_pic_edge_init(struct device_node *node, + struct device_node *parent) +{ + return or1k_pic_init(node, &or1k_pic_edge); +} +IRQCHIP_DECLARE(or1k_pic_edge, "opencores,or1k-pic-edge", or1k_pic_edge_init); -- cgit v1.2.3