aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMagnus Damm <damm@opensource.se>2010-03-19 03:48:01 -0400
committerPaul Mundt <lethal@linux-sh.org>2010-03-19 03:48:01 -0400
commitdec710b77c2cf04bf512acada3c14a16f11708d9 (patch)
treee2e25d5afa00df0eccb7c2881b29167ad4ff3b63
parent01e9651a21bc0e6731da733593e4aaf4cf46b5e5 (diff)
sh: INTC ioremap support
Extend the INTC code with ioremap() support V2. Support INTC controllers that are not accessible through a 1:1 virt:phys window. Needed by SH-Mobile ARM INTCS. The INTC code behaves as usual if the io window resource is omitted. The slow phys->virt lookup only happens during setup. The fast path code operates on virtual addresses. Signed-off-by: Magnus Damm <damm@opensource.se> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r--drivers/sh/intc.c84
-rw-r--r--include/linux/sh_intc.h4
2 files changed, 78 insertions, 10 deletions
diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c
index d4aa4d1e8dce..a700dfec8dc3 100644
--- a/drivers/sh/intc.c
+++ b/drivers/sh/intc.c
@@ -43,6 +43,12 @@ struct intc_handle_int {
43 unsigned long handle; 43 unsigned long handle;
44}; 44};
45 45
46struct intc_window {
47 phys_addr_t phys;
48 void __iomem *virt;
49 unsigned long size;
50};
51
46struct intc_desc_int { 52struct intc_desc_int {
47 struct list_head list; 53 struct list_head list;
48 struct sys_device sysdev; 54 struct sys_device sysdev;
@@ -56,6 +62,8 @@ struct intc_desc_int {
56 unsigned int nr_prio; 62 unsigned int nr_prio;
57 struct intc_handle_int *sense; 63 struct intc_handle_int *sense;
58 unsigned int nr_sense; 64 unsigned int nr_sense;
65 struct intc_window *window;
66 unsigned int nr_windows;
59 struct irq_chip chip; 67 struct irq_chip chip;
60}; 68};
61 69
@@ -420,11 +428,39 @@ static int intc_set_sense(unsigned int irq, unsigned int type)
420 return 0; 428 return 0;
421} 429}
422 430
431static unsigned long intc_phys_to_virt(struct intc_desc_int *d,
432 unsigned long address)
433{
434 struct intc_window *window;
435 int k;
436
437 /* scan through physical windows and convert address */
438 for (k = 0; k < d->nr_windows; k++) {
439 window = d->window + k;
440
441 if (address < window->phys)
442 continue;
443
444 if (address >= (window->phys + window->size))
445 continue;
446
447 address -= window->phys;
448 address += (unsigned long)window->virt;
449
450 return address;
451 }
452
453 /* no windows defined, register must be 1:1 mapped virt:phys */
454 return address;
455}
456
423static unsigned int __init intc_get_reg(struct intc_desc_int *d, 457static unsigned int __init intc_get_reg(struct intc_desc_int *d,
424 unsigned long address) 458 unsigned long address)
425{ 459{
426 unsigned int k; 460 unsigned int k;
427 461
462 address = intc_phys_to_virt(d, address);
463
428 for (k = 0; k < d->nr_reg; k++) { 464 for (k = 0; k < d->nr_reg; k++) {
429 if (d->reg[k] == address) 465 if (d->reg[k] == address)
430 return k; 466 return k;
@@ -774,6 +810,8 @@ static unsigned int __init save_reg(struct intc_desc_int *d,
774 unsigned int smp) 810 unsigned int smp)
775{ 811{
776 if (value) { 812 if (value) {
813 value = intc_phys_to_virt(d, value);
814
777 d->reg[cnt] = value; 815 d->reg[cnt] = value;
778#ifdef CONFIG_SMP 816#ifdef CONFIG_SMP
779 d->smp[cnt] = smp; 817 d->smp[cnt] = smp;
@@ -794,6 +832,7 @@ int __init register_intc_controller(struct intc_desc *desc)
794 unsigned int i, k, smp; 832 unsigned int i, k, smp;
795 struct intc_hw_desc *hw = &desc->hw; 833 struct intc_hw_desc *hw = &desc->hw;
796 struct intc_desc_int *d; 834 struct intc_desc_int *d;
835 struct resource *res;
797 836
798 d = kzalloc(sizeof(*d), GFP_NOWAIT); 837 d = kzalloc(sizeof(*d), GFP_NOWAIT);
799 if (!d) 838 if (!d)
@@ -802,6 +841,25 @@ int __init register_intc_controller(struct intc_desc *desc)
802 INIT_LIST_HEAD(&d->list); 841 INIT_LIST_HEAD(&d->list);
803 list_add(&d->list, &intc_list); 842 list_add(&d->list, &intc_list);
804 843
844 if (desc->num_resources) {
845 d->nr_windows = desc->num_resources;
846 d->window = kzalloc(d->nr_windows * sizeof(*d->window),
847 GFP_NOWAIT);
848 if (!d->window)
849 goto err1;
850
851 for (k = 0; k < d->nr_windows; k++) {
852 res = desc->resource + k;
853 WARN_ON(resource_type(res) != IORESOURCE_MEM);
854 d->window[k].phys = res->start;
855 d->window[k].size = resource_size(res);
856 d->window[k].virt = ioremap_nocache(res->start,
857 resource_size(res));
858 if (!d->window[k].virt)
859 goto err2;
860 }
861 }
862
805 d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; 863 d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0;
806 d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; 864 d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0;
807 d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; 865 d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0;
@@ -809,12 +867,12 @@ int __init register_intc_controller(struct intc_desc *desc)
809 867
810 d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); 868 d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT);
811 if (!d->reg) 869 if (!d->reg)
812 goto err1; 870 goto err2;
813 871
814#ifdef CONFIG_SMP 872#ifdef CONFIG_SMP
815 d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); 873 d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT);
816 if (!d->smp) 874 if (!d->smp)
817 goto err2; 875 goto err3;
818#endif 876#endif
819 k = 0; 877 k = 0;
820 878
@@ -830,7 +888,7 @@ int __init register_intc_controller(struct intc_desc *desc)
830 d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio), 888 d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio),
831 GFP_NOWAIT); 889 GFP_NOWAIT);
832 if (!d->prio) 890 if (!d->prio)
833 goto err3; 891 goto err4;
834 892
835 for (i = 0; i < hw->nr_prio_regs; i++) { 893 for (i = 0; i < hw->nr_prio_regs; i++) {
836 smp = IS_SMP(hw->prio_regs[i]); 894 smp = IS_SMP(hw->prio_regs[i]);
@@ -843,7 +901,7 @@ int __init register_intc_controller(struct intc_desc *desc)
843 d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense), 901 d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense),
844 GFP_NOWAIT); 902 GFP_NOWAIT);
845 if (!d->sense) 903 if (!d->sense)
846 goto err4; 904 goto err5;
847 905
848 for (i = 0; i < hw->nr_sense_regs; i++) 906 for (i = 0; i < hw->nr_sense_regs; i++)
849 k += save_reg(d, k, hw->sense_regs[i].reg, 0); 907 k += save_reg(d, k, hw->sense_regs[i].reg, 0);
@@ -925,17 +983,23 @@ int __init register_intc_controller(struct intc_desc *desc)
925 intc_enable_disable_enum(desc, d, desc->force_enable, 1); 983 intc_enable_disable_enum(desc, d, desc->force_enable, 1);
926 984
927 return 0; 985 return 0;
928 err4: 986err5:
929 kfree(d->prio); 987 kfree(d->prio);
930 err3: 988err4:
931#ifdef CONFIG_SMP 989#ifdef CONFIG_SMP
932 kfree(d->smp); 990 kfree(d->smp);
933 err2: 991err3:
934#endif 992#endif
935 kfree(d->reg); 993 kfree(d->reg);
936 err1: 994err2:
995 for (k = 0; k < d->nr_windows; k++)
996 if (d->window[k].virt)
997 iounmap(d->window[k].virt);
998
999 kfree(d->window);
1000err1:
937 kfree(d); 1001 kfree(d);
938 err0: 1002err0:
939 pr_err("unable to allocate INTC memory\n"); 1003 pr_err("unable to allocate INTC memory\n");
940 1004
941 return -ENOMEM; 1005 return -ENOMEM;
diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h
index df3777035936..01d8168c5a1b 100644
--- a/include/linux/sh_intc.h
+++ b/include/linux/sh_intc.h
@@ -1,6 +1,8 @@
1#ifndef __SH_INTC_H 1#ifndef __SH_INTC_H
2#define __SH_INTC_H 2#define __SH_INTC_H
3 3
4#include <linux/ioport.h>
5
4typedef unsigned char intc_enum; 6typedef unsigned char intc_enum;
5 7
6struct intc_vect { 8struct intc_vect {
@@ -71,6 +73,8 @@ struct intc_hw_desc {
71 73
72struct intc_desc { 74struct intc_desc {
73 char *name; 75 char *name;
76 struct resource *resource;
77 unsigned int num_resources;
74 intc_enum force_enable; 78 intc_enum force_enable;
75 intc_enum force_disable; 79 intc_enum force_disable;
76 struct intc_hw_desc hw; 80 struct intc_hw_desc hw;