diff options
Diffstat (limited to 'drivers/sh')
-rw-r--r-- | drivers/sh/intc.c | 95 |
1 files changed, 93 insertions, 2 deletions
diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index a3d8677af6a5..f43850527645 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c | |||
@@ -44,6 +44,12 @@ struct intc_handle_int { | |||
44 | unsigned long handle; | 44 | unsigned long handle; |
45 | }; | 45 | }; |
46 | 46 | ||
47 | struct intc_window { | ||
48 | phys_addr_t phys; | ||
49 | void __iomem *virt; | ||
50 | unsigned long size; | ||
51 | }; | ||
52 | |||
47 | struct intc_desc_int { | 53 | struct intc_desc_int { |
48 | struct list_head list; | 54 | struct list_head list; |
49 | struct sys_device sysdev; | 55 | struct sys_device sysdev; |
@@ -57,6 +63,8 @@ struct intc_desc_int { | |||
57 | unsigned int nr_prio; | 63 | unsigned int nr_prio; |
58 | struct intc_handle_int *sense; | 64 | struct intc_handle_int *sense; |
59 | unsigned int nr_sense; | 65 | unsigned int nr_sense; |
66 | struct intc_window *window; | ||
67 | unsigned int nr_windows; | ||
60 | struct irq_chip chip; | 68 | struct irq_chip chip; |
61 | }; | 69 | }; |
62 | 70 | ||
@@ -446,11 +454,39 @@ static int intc_set_sense(unsigned int irq, unsigned int type) | |||
446 | return 0; | 454 | return 0; |
447 | } | 455 | } |
448 | 456 | ||
457 | static unsigned long intc_phys_to_virt(struct intc_desc_int *d, | ||
458 | unsigned long address) | ||
459 | { | ||
460 | struct intc_window *window; | ||
461 | int k; | ||
462 | |||
463 | /* scan through physical windows and convert address */ | ||
464 | for (k = 0; k < d->nr_windows; k++) { | ||
465 | window = d->window + k; | ||
466 | |||
467 | if (address < window->phys) | ||
468 | continue; | ||
469 | |||
470 | if (address >= (window->phys + window->size)) | ||
471 | continue; | ||
472 | |||
473 | address -= window->phys; | ||
474 | address += (unsigned long)window->virt; | ||
475 | |||
476 | return address; | ||
477 | } | ||
478 | |||
479 | /* no windows defined, register must be 1:1 mapped virt:phys */ | ||
480 | return address; | ||
481 | } | ||
482 | |||
449 | static unsigned int __init intc_get_reg(struct intc_desc_int *d, | 483 | static unsigned int __init intc_get_reg(struct intc_desc_int *d, |
450 | unsigned long address) | 484 | unsigned long address) |
451 | { | 485 | { |
452 | unsigned int k; | 486 | unsigned int k; |
453 | 487 | ||
488 | address = intc_phys_to_virt(d, address); | ||
489 | |||
454 | for (k = 0; k < d->nr_reg; k++) { | 490 | for (k = 0; k < d->nr_reg; k++) { |
455 | if (d->reg[k] == address) | 491 | if (d->reg[k] == address) |
456 | return k; | 492 | return k; |
@@ -800,6 +836,8 @@ static unsigned int __init save_reg(struct intc_desc_int *d, | |||
800 | unsigned int smp) | 836 | unsigned int smp) |
801 | { | 837 | { |
802 | if (value) { | 838 | if (value) { |
839 | value = intc_phys_to_virt(d, value); | ||
840 | |||
803 | d->reg[cnt] = value; | 841 | d->reg[cnt] = value; |
804 | #ifdef CONFIG_SMP | 842 | #ifdef CONFIG_SMP |
805 | d->smp[cnt] = smp; | 843 | d->smp[cnt] = smp; |
@@ -815,25 +853,52 @@ static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc) | |||
815 | generic_handle_irq((unsigned int)get_irq_data(irq)); | 853 | generic_handle_irq((unsigned int)get_irq_data(irq)); |
816 | } | 854 | } |
817 | 855 | ||
818 | void __init register_intc_controller(struct intc_desc *desc) | 856 | int __init register_intc_controller(struct intc_desc *desc) |
819 | { | 857 | { |
820 | unsigned int i, k, smp; | 858 | unsigned int i, k, smp; |
821 | struct intc_hw_desc *hw = &desc->hw; | 859 | struct intc_hw_desc *hw = &desc->hw; |
822 | struct intc_desc_int *d; | 860 | struct intc_desc_int *d; |
861 | struct resource *res; | ||
823 | 862 | ||
824 | d = kzalloc(sizeof(*d), GFP_NOWAIT); | 863 | d = kzalloc(sizeof(*d), GFP_NOWAIT); |
864 | if (!d) | ||
865 | goto err0; | ||
825 | 866 | ||
826 | INIT_LIST_HEAD(&d->list); | 867 | INIT_LIST_HEAD(&d->list); |
827 | list_add(&d->list, &intc_list); | 868 | list_add(&d->list, &intc_list); |
828 | 869 | ||
870 | if (desc->num_resources) { | ||
871 | d->nr_windows = desc->num_resources; | ||
872 | d->window = kzalloc(d->nr_windows * sizeof(*d->window), | ||
873 | GFP_NOWAIT); | ||
874 | if (!d->window) | ||
875 | goto err1; | ||
876 | |||
877 | for (k = 0; k < d->nr_windows; k++) { | ||
878 | res = desc->resource + k; | ||
879 | WARN_ON(resource_type(res) != IORESOURCE_MEM); | ||
880 | d->window[k].phys = res->start; | ||
881 | d->window[k].size = resource_size(res); | ||
882 | d->window[k].virt = ioremap_nocache(res->start, | ||
883 | resource_size(res)); | ||
884 | if (!d->window[k].virt) | ||
885 | goto err2; | ||
886 | } | ||
887 | } | ||
888 | |||
829 | d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; | 889 | d->nr_reg = hw->mask_regs ? hw->nr_mask_regs * 2 : 0; |
830 | d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; | 890 | d->nr_reg += hw->prio_regs ? hw->nr_prio_regs * 2 : 0; |
831 | d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; | 891 | d->nr_reg += hw->sense_regs ? hw->nr_sense_regs : 0; |
832 | d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; | 892 | d->nr_reg += hw->ack_regs ? hw->nr_ack_regs : 0; |
833 | 893 | ||
834 | d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); | 894 | d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT); |
895 | if (!d->reg) | ||
896 | goto err2; | ||
897 | |||
835 | #ifdef CONFIG_SMP | 898 | #ifdef CONFIG_SMP |
836 | d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); | 899 | d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT); |
900 | if (!d->smp) | ||
901 | goto err3; | ||
837 | #endif | 902 | #endif |
838 | k = 0; | 903 | k = 0; |
839 | 904 | ||
@@ -848,6 +913,8 @@ void __init register_intc_controller(struct intc_desc *desc) | |||
848 | if (hw->prio_regs) { | 913 | if (hw->prio_regs) { |
849 | d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio), | 914 | d->prio = kzalloc(hw->nr_vectors * sizeof(*d->prio), |
850 | GFP_NOWAIT); | 915 | GFP_NOWAIT); |
916 | if (!d->prio) | ||
917 | goto err4; | ||
851 | 918 | ||
852 | for (i = 0; i < hw->nr_prio_regs; i++) { | 919 | for (i = 0; i < hw->nr_prio_regs; i++) { |
853 | smp = IS_SMP(hw->prio_regs[i]); | 920 | smp = IS_SMP(hw->prio_regs[i]); |
@@ -859,6 +926,8 @@ void __init register_intc_controller(struct intc_desc *desc) | |||
859 | if (hw->sense_regs) { | 926 | if (hw->sense_regs) { |
860 | d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense), | 927 | d->sense = kzalloc(hw->nr_vectors * sizeof(*d->sense), |
861 | GFP_NOWAIT); | 928 | GFP_NOWAIT); |
929 | if (!d->sense) | ||
930 | goto err5; | ||
862 | 931 | ||
863 | for (i = 0; i < hw->nr_sense_regs; i++) | 932 | for (i = 0; i < hw->nr_sense_regs; i++) |
864 | k += save_reg(d, k, hw->sense_regs[i].reg, 0); | 933 | k += save_reg(d, k, hw->sense_regs[i].reg, 0); |
@@ -941,6 +1010,28 @@ void __init register_intc_controller(struct intc_desc *desc) | |||
941 | /* enable bits matching force_enable after registering irqs */ | 1010 | /* enable bits matching force_enable after registering irqs */ |
942 | if (desc->force_enable) | 1011 | if (desc->force_enable) |
943 | intc_enable_disable_enum(desc, d, desc->force_enable, 1); | 1012 | intc_enable_disable_enum(desc, d, desc->force_enable, 1); |
1013 | |||
1014 | return 0; | ||
1015 | err5: | ||
1016 | kfree(d->prio); | ||
1017 | err4: | ||
1018 | #ifdef CONFIG_SMP | ||
1019 | kfree(d->smp); | ||
1020 | err3: | ||
1021 | #endif | ||
1022 | kfree(d->reg); | ||
1023 | err2: | ||
1024 | for (k = 0; k < d->nr_windows; k++) | ||
1025 | if (d->window[k].virt) | ||
1026 | iounmap(d->window[k].virt); | ||
1027 | |||
1028 | kfree(d->window); | ||
1029 | err1: | ||
1030 | kfree(d); | ||
1031 | err0: | ||
1032 | pr_err("unable to allocate INTC memory\n"); | ||
1033 | |||
1034 | return -ENOMEM; | ||
944 | } | 1035 | } |
945 | 1036 | ||
946 | static int intc_suspend(struct sys_device *dev, pm_message_t state) | 1037 | static int intc_suspend(struct sys_device *dev, pm_message_t state) |