Commit dec710b77c2cf04bf512acada3c14a16f11708d9
Committed by
Paul Mundt
1 parent
01e9651a21
Exists in
master
and in
7 other branches
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>
Showing 2 changed files with 78 additions and 10 deletions Side-by-side Diff
drivers/sh/intc.c
... | ... | @@ -43,6 +43,12 @@ |
43 | 43 | unsigned long handle; |
44 | 44 | }; |
45 | 45 | |
46 | +struct intc_window { | |
47 | + phys_addr_t phys; | |
48 | + void __iomem *virt; | |
49 | + unsigned long size; | |
50 | +}; | |
51 | + | |
46 | 52 | struct intc_desc_int { |
47 | 53 | struct list_head list; |
48 | 54 | struct sys_device sysdev; |
... | ... | @@ -56,6 +62,8 @@ |
56 | 62 | unsigned int nr_prio; |
57 | 63 | struct intc_handle_int *sense; |
58 | 64 | unsigned int nr_sense; |
65 | + struct intc_window *window; | |
66 | + unsigned int nr_windows; | |
59 | 67 | struct irq_chip chip; |
60 | 68 | }; |
61 | 69 | |
62 | 70 | |
63 | 71 | |
... | ... | @@ -420,11 +428,39 @@ |
420 | 428 | return 0; |
421 | 429 | } |
422 | 430 | |
431 | +static 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 | + | |
423 | 457 | static unsigned int __init intc_get_reg(struct intc_desc_int *d, |
424 | - unsigned long address) | |
458 | + unsigned long address) | |
425 | 459 | { |
426 | 460 | unsigned int k; |
427 | 461 | |
462 | + address = intc_phys_to_virt(d, address); | |
463 | + | |
428 | 464 | for (k = 0; k < d->nr_reg; k++) { |
429 | 465 | if (d->reg[k] == address) |
430 | 466 | return k; |
... | ... | @@ -774,6 +810,8 @@ |
774 | 810 | unsigned int smp) |
775 | 811 | { |
776 | 812 | if (value) { |
813 | + value = intc_phys_to_virt(d, value); | |
814 | + | |
777 | 815 | d->reg[cnt] = value; |
778 | 816 | #ifdef CONFIG_SMP |
779 | 817 | d->smp[cnt] = smp; |
... | ... | @@ -794,6 +832,7 @@ |
794 | 832 | unsigned int i, k, smp; |
795 | 833 | struct intc_hw_desc *hw = &desc->hw; |
796 | 834 | struct intc_desc_int *d; |
835 | + struct resource *res; | |
797 | 836 | |
798 | 837 | d = kzalloc(sizeof(*d), GFP_NOWAIT); |
799 | 838 | if (!d) |
... | ... | @@ -802,6 +841,25 @@ |
802 | 841 | INIT_LIST_HEAD(&d->list); |
803 | 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 | 863 | d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; |
806 | 864 | d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; |
807 | 865 | d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; |
808 | 866 | |
... | ... | @@ -809,12 +867,12 @@ |
809 | 867 | |
810 | 868 | d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); |
811 | 869 | if (!d->reg) |
812 | - goto err1; | |
870 | + goto err2; | |
813 | 871 | |
814 | 872 | #ifdef CONFIG_SMP |
815 | 873 | d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); |
816 | 874 | if (!d->smp) |
817 | - goto err2; | |
875 | + goto err3; | |
818 | 876 | #endif |
819 | 877 | k = 0; |
820 | 878 | |
... | ... | @@ -830,7 +888,7 @@ |
830 | 888 | d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio), |
831 | 889 | GFP_NOWAIT); |
832 | 890 | if (!d->prio) |
833 | - goto err3; | |
891 | + goto err4; | |
834 | 892 | |
835 | 893 | for (i = 0; i < hw->nr_prio_regs; i++) { |
836 | 894 | smp = IS_SMP(hw->prio_regs[i]); |
... | ... | @@ -843,7 +901,7 @@ |
843 | 901 | d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense), |
844 | 902 | GFP_NOWAIT); |
845 | 903 | if (!d->sense) |
846 | - goto err4; | |
904 | + goto err5; | |
847 | 905 | |
848 | 906 | for (i = 0; i < hw->nr_sense_regs; i++) |
849 | 907 | k += save_reg(d, k, hw->sense_regs[i].reg, 0); |
850 | 908 | |
851 | 909 | |
852 | 910 | |
853 | 911 | |
... | ... | @@ -925,17 +983,23 @@ |
925 | 983 | intc_enable_disable_enum(desc, d, desc->force_enable, 1); |
926 | 984 | |
927 | 985 | return 0; |
928 | - err4: | |
986 | +err5: | |
929 | 987 | kfree(d->prio); |
930 | - err3: | |
988 | +err4: | |
931 | 989 | #ifdef CONFIG_SMP |
932 | 990 | kfree(d->smp); |
933 | - err2: | |
991 | +err3: | |
934 | 992 | #endif |
935 | 993 | kfree(d->reg); |
936 | - err1: | |
994 | +err2: | |
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); | |
1000 | +err1: | |
937 | 1001 | kfree(d); |
938 | - err0: | |
1002 | +err0: | |
939 | 1003 | pr_err("unable to allocate INTC memory\n"); |
940 | 1004 | |
941 | 1005 | return -ENOMEM; |
include/linux/sh_intc.h
1 | 1 | #ifndef __SH_INTC_H |
2 | 2 | #define __SH_INTC_H |
3 | 3 | |
4 | +#include <linux/ioport.h> | |
5 | + | |
4 | 6 | typedef unsigned char intc_enum; |
5 | 7 | |
6 | 8 | struct intc_vect { |
... | ... | @@ -71,6 +73,8 @@ |
71 | 73 | |
72 | 74 | struct intc_desc { |
73 | 75 | char *name; |
76 | + struct resource *resource; | |
77 | + unsigned int num_resources; | |
74 | 78 | intc_enum force_enable; |
75 | 79 | intc_enum force_disable; |
76 | 80 | struct intc_hw_desc hw; |