diff options
| author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-07-03 13:49:45 -0400 |
|---|---|---|
| committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-07-03 13:49:45 -0400 |
| commit | 026477c1141b67e98e3bd8bdedb7d4b88a3ecd09 (patch) | |
| tree | 2624a44924c625c367f3cebf937853b9da2de282 /arch/powerpc/sysdev | |
| parent | 9f2fa466383ce100b90fe52cb4489d7a26bf72a9 (diff) | |
| parent | 29454dde27d8e340bb1987bad9aa504af7081eba (diff) | |
Merge branch 'master' of /home/trondmy/kernel/linux-2.6/
Diffstat (limited to 'arch/powerpc/sysdev')
| -rw-r--r-- | arch/powerpc/sysdev/Makefile | 2 | ||||
| -rw-r--r-- | arch/powerpc/sysdev/dart.h | 6 | ||||
| -rw-r--r-- | arch/powerpc/sysdev/dart_iommu.c | 50 | ||||
| -rw-r--r-- | arch/powerpc/sysdev/fsl_soc.c | 1 | ||||
| -rw-r--r-- | arch/powerpc/sysdev/i8259.c | 4 | ||||
| -rw-r--r-- | arch/powerpc/sysdev/ipic.c | 2 | ||||
| -rw-r--r-- | arch/powerpc/sysdev/mpic.c | 26 | ||||
| -rw-r--r-- | arch/powerpc/sysdev/todc.c | 392 | ||||
| -rw-r--r-- | arch/powerpc/sysdev/tsi108_dev.c | 145 | ||||
| -rw-r--r-- | arch/powerpc/sysdev/tsi108_pci.c | 412 |
10 files changed, 1016 insertions, 24 deletions
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index cef95b023730..054bd8b41ef5 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile | |||
| @@ -12,3 +12,5 @@ obj-$(CONFIG_U3_DART) += dart_iommu.o | |||
| 12 | obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o | 12 | obj-$(CONFIG_MMIO_NVRAM) += mmio_nvram.o |
| 13 | obj-$(CONFIG_PPC_83xx) += ipic.o | 13 | obj-$(CONFIG_PPC_83xx) += ipic.o |
| 14 | obj-$(CONFIG_FSL_SOC) += fsl_soc.o | 14 | obj-$(CONFIG_FSL_SOC) += fsl_soc.o |
| 15 | obj-$(CONFIG_PPC_TODC) += todc.o | ||
| 16 | obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o | ||
diff --git a/arch/powerpc/sysdev/dart.h b/arch/powerpc/sysdev/dart.h index c2d05763ccbe..1c8817c4835e 100644 --- a/arch/powerpc/sysdev/dart.h +++ b/arch/powerpc/sysdev/dart.h | |||
| @@ -47,8 +47,12 @@ | |||
| 47 | /* U4 registers */ | 47 | /* U4 registers */ |
| 48 | #define DART_BASE_U4_BASE_MASK 0xffffff | 48 | #define DART_BASE_U4_BASE_MASK 0xffffff |
| 49 | #define DART_BASE_U4_BASE_SHIFT 0 | 49 | #define DART_BASE_U4_BASE_SHIFT 0 |
| 50 | #define DART_CNTL_U4_FLUSHTLB 0x20000000 | ||
| 51 | #define DART_CNTL_U4_ENABLE 0x80000000 | 50 | #define DART_CNTL_U4_ENABLE 0x80000000 |
| 51 | #define DART_CNTL_U4_IONE 0x40000000 | ||
| 52 | #define DART_CNTL_U4_FLUSHTLB 0x20000000 | ||
| 53 | #define DART_CNTL_U4_IDLE 0x10000000 | ||
| 54 | #define DART_CNTL_U4_PAR_EN 0x08000000 | ||
| 55 | #define DART_CNTL_U4_IONE_MASK 0x07ffffff | ||
| 52 | #define DART_SIZE_U4_SIZE_MASK 0x1fff | 56 | #define DART_SIZE_U4_SIZE_MASK 0x1fff |
| 53 | #define DART_SIZE_U4_SIZE_SHIFT 0 | 57 | #define DART_SIZE_U4_SIZE_SHIFT 0 |
| 54 | 58 | ||
diff --git a/arch/powerpc/sysdev/dart_iommu.c b/arch/powerpc/sysdev/dart_iommu.c index 6232091cc72b..e32fadde1f77 100644 --- a/arch/powerpc/sysdev/dart_iommu.c +++ b/arch/powerpc/sysdev/dart_iommu.c | |||
| @@ -27,7 +27,6 @@ | |||
| 27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 28 | */ | 28 | */ |
| 29 | 29 | ||
| 30 | #include <linux/config.h> | ||
| 31 | #include <linux/init.h> | 30 | #include <linux/init.h> |
| 32 | #include <linux/types.h> | 31 | #include <linux/types.h> |
| 33 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
| @@ -101,8 +100,8 @@ retry: | |||
| 101 | if (l == (1L << limit)) { | 100 | if (l == (1L << limit)) { |
| 102 | if (limit < 4) { | 101 | if (limit < 4) { |
| 103 | limit++; | 102 | limit++; |
| 104 | reg = DART_IN(DART_CNTL); | 103 | reg = DART_IN(DART_CNTL); |
| 105 | reg &= ~inv_bit; | 104 | reg &= ~inv_bit; |
| 106 | DART_OUT(DART_CNTL, reg); | 105 | DART_OUT(DART_CNTL, reg); |
| 107 | goto retry; | 106 | goto retry; |
| 108 | } else | 107 | } else |
| @@ -111,11 +110,39 @@ retry: | |||
| 111 | } | 110 | } |
| 112 | } | 111 | } |
| 113 | 112 | ||
| 113 | static inline void dart_tlb_invalidate_one(unsigned long bus_rpn) | ||
| 114 | { | ||
| 115 | unsigned int reg; | ||
| 116 | unsigned int l, limit; | ||
| 117 | |||
| 118 | reg = DART_CNTL_U4_ENABLE | DART_CNTL_U4_IONE | | ||
| 119 | (bus_rpn & DART_CNTL_U4_IONE_MASK); | ||
| 120 | DART_OUT(DART_CNTL, reg); | ||
| 121 | |||
| 122 | limit = 0; | ||
| 123 | wait_more: | ||
| 124 | l = 0; | ||
| 125 | while ((DART_IN(DART_CNTL) & DART_CNTL_U4_IONE) && l < (1L << limit)) { | ||
| 126 | rmb(); | ||
| 127 | l++; | ||
| 128 | } | ||
| 129 | |||
| 130 | if (l == (1L << limit)) { | ||
| 131 | if (limit < 4) { | ||
| 132 | limit++; | ||
| 133 | goto wait_more; | ||
| 134 | } else | ||
| 135 | panic("DART: TLB did not flush after waiting a long " | ||
| 136 | "time. Buggy U4 ?"); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 114 | static void dart_flush(struct iommu_table *tbl) | 140 | static void dart_flush(struct iommu_table *tbl) |
| 115 | { | 141 | { |
| 116 | if (dart_dirty) | 142 | if (dart_dirty) { |
| 117 | dart_tlb_invalidate_all(); | 143 | dart_tlb_invalidate_all(); |
| 118 | dart_dirty = 0; | 144 | dart_dirty = 0; |
| 145 | } | ||
| 119 | } | 146 | } |
| 120 | 147 | ||
| 121 | static void dart_build(struct iommu_table *tbl, long index, | 148 | static void dart_build(struct iommu_table *tbl, long index, |
| @@ -124,6 +151,7 @@ static void dart_build(struct iommu_table *tbl, long index, | |||
| 124 | { | 151 | { |
| 125 | unsigned int *dp; | 152 | unsigned int *dp; |
| 126 | unsigned int rpn; | 153 | unsigned int rpn; |
| 154 | long l; | ||
| 127 | 155 | ||
| 128 | DBG("dart: build at: %lx, %lx, addr: %x\n", index, npages, uaddr); | 156 | DBG("dart: build at: %lx, %lx, addr: %x\n", index, npages, uaddr); |
| 129 | 157 | ||
| @@ -135,7 +163,8 @@ static void dart_build(struct iommu_table *tbl, long index, | |||
| 135 | /* On U3, all memory is contigous, so we can move this | 163 | /* On U3, all memory is contigous, so we can move this |
| 136 | * out of the loop. | 164 | * out of the loop. |
| 137 | */ | 165 | */ |
| 138 | while (npages--) { | 166 | l = npages; |
| 167 | while (l--) { | ||
| 139 | rpn = virt_to_abs(uaddr) >> DART_PAGE_SHIFT; | 168 | rpn = virt_to_abs(uaddr) >> DART_PAGE_SHIFT; |
| 140 | 169 | ||
| 141 | *(dp++) = DARTMAP_VALID | (rpn & DARTMAP_RPNMASK); | 170 | *(dp++) = DARTMAP_VALID | (rpn & DARTMAP_RPNMASK); |
| @@ -143,7 +172,14 @@ static void dart_build(struct iommu_table *tbl, long index, | |||
| 143 | uaddr += DART_PAGE_SIZE; | 172 | uaddr += DART_PAGE_SIZE; |
| 144 | } | 173 | } |
| 145 | 174 | ||
| 146 | dart_dirty = 1; | 175 | if (dart_is_u4) { |
| 176 | rpn = index; | ||
| 177 | mb(); /* make sure all updates have reached memory */ | ||
| 178 | while (npages--) | ||
| 179 | dart_tlb_invalidate_one(rpn++); | ||
| 180 | } else { | ||
| 181 | dart_dirty = 1; | ||
| 182 | } | ||
| 147 | } | 183 | } |
| 148 | 184 | ||
| 149 | 185 | ||
diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index 71a3275935ec..e983972132d8 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | * option) any later version. | 9 | * option) any later version. |
| 10 | */ | 10 | */ |
| 11 | 11 | ||
| 12 | #include <linux/config.h> | ||
| 13 | #include <linux/stddef.h> | 12 | #include <linux/stddef.h> |
| 14 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
| 15 | #include <linux/init.h> | 14 | #include <linux/init.h> |
diff --git a/arch/powerpc/sysdev/i8259.c b/arch/powerpc/sysdev/i8259.c index b7ac32fdd776..1a3ef1ab9d6e 100644 --- a/arch/powerpc/sysdev/i8259.c +++ b/arch/powerpc/sysdev/i8259.c | |||
| @@ -167,7 +167,7 @@ static struct resource pic_edgectrl_iores = { | |||
| 167 | 167 | ||
| 168 | static struct irqaction i8259_irqaction = { | 168 | static struct irqaction i8259_irqaction = { |
| 169 | .handler = no_action, | 169 | .handler = no_action, |
| 170 | .flags = SA_INTERRUPT, | 170 | .flags = IRQF_DISABLED, |
| 171 | .mask = CPU_MASK_NONE, | 171 | .mask = CPU_MASK_NONE, |
| 172 | .name = "82c59 secondary cascade", | 172 | .name = "82c59 secondary cascade", |
| 173 | }; | 173 | }; |
| @@ -208,7 +208,7 @@ void __init i8259_init(unsigned long intack_addr, int offset) | |||
| 208 | spin_unlock_irqrestore(&i8259_lock, flags); | 208 | spin_unlock_irqrestore(&i8259_lock, flags); |
| 209 | 209 | ||
| 210 | for (i = 0; i < NUM_ISA_INTERRUPTS; ++i) | 210 | for (i = 0; i < NUM_ISA_INTERRUPTS; ++i) |
| 211 | irq_desc[offset + i].handler = &i8259_pic; | 211 | irq_desc[offset + i].chip = &i8259_pic; |
| 212 | 212 | ||
| 213 | /* reserve our resources */ | 213 | /* reserve our resources */ |
| 214 | setup_irq(offset + 2, &i8259_irqaction); | 214 | setup_irq(offset + 2, &i8259_irqaction); |
diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c index 8f01e0f1d847..46801f5ec03f 100644 --- a/arch/powerpc/sysdev/ipic.c +++ b/arch/powerpc/sysdev/ipic.c | |||
| @@ -472,7 +472,7 @@ void __init ipic_init(phys_addr_t phys_addr, | |||
| 472 | ipic_write(primary_ipic->regs, IPIC_SEMSR, temp); | 472 | ipic_write(primary_ipic->regs, IPIC_SEMSR, temp); |
| 473 | 473 | ||
| 474 | for (i = 0 ; i < NR_IPIC_INTS ; i++) { | 474 | for (i = 0 ; i < NR_IPIC_INTS ; i++) { |
| 475 | irq_desc[i+irq_offset].handler = &ipic; | 475 | irq_desc[i+irq_offset].chip = &ipic; |
| 476 | irq_desc[i+irq_offset].status = IRQ_LEVEL; | 476 | irq_desc[i+irq_offset].status = IRQ_LEVEL; |
| 477 | } | 477 | } |
| 478 | 478 | ||
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index bffe50d02c99..7e469358895f 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c | |||
| @@ -17,7 +17,6 @@ | |||
| 17 | #undef DEBUG_IRQ | 17 | #undef DEBUG_IRQ |
| 18 | #undef DEBUG_LOW | 18 | #undef DEBUG_LOW |
| 19 | 19 | ||
| 20 | #include <linux/config.h> | ||
| 21 | #include <linux/types.h> | 20 | #include <linux/types.h> |
| 22 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
| 23 | #include <linux/init.h> | 22 | #include <linux/init.h> |
| @@ -379,14 +378,14 @@ static inline u32 mpic_physmask(u32 cpumask) | |||
| 379 | /* Get the mpic structure from the IPI number */ | 378 | /* Get the mpic structure from the IPI number */ |
| 380 | static inline struct mpic * mpic_from_ipi(unsigned int ipi) | 379 | static inline struct mpic * mpic_from_ipi(unsigned int ipi) |
| 381 | { | 380 | { |
| 382 | return container_of(irq_desc[ipi].handler, struct mpic, hc_ipi); | 381 | return container_of(irq_desc[ipi].chip, struct mpic, hc_ipi); |
| 383 | } | 382 | } |
| 384 | #endif | 383 | #endif |
| 385 | 384 | ||
| 386 | /* Get the mpic structure from the irq number */ | 385 | /* Get the mpic structure from the irq number */ |
| 387 | static inline struct mpic * mpic_from_irq(unsigned int irq) | 386 | static inline struct mpic * mpic_from_irq(unsigned int irq) |
| 388 | { | 387 | { |
| 389 | return container_of(irq_desc[irq].handler, struct mpic, hc_irq); | 388 | return container_of(irq_desc[irq].chip, struct mpic, hc_irq); |
| 390 | } | 389 | } |
| 391 | 390 | ||
| 392 | /* Send an EOI */ | 391 | /* Send an EOI */ |
| @@ -541,7 +540,7 @@ static void mpic_end_ipi(unsigned int irq) | |||
| 541 | * IPIs are marked IRQ_PER_CPU. This has the side effect of | 540 | * IPIs are marked IRQ_PER_CPU. This has the side effect of |
| 542 | * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from | 541 | * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from |
| 543 | * applying to them. We EOI them late to avoid re-entering. | 542 | * applying to them. We EOI them late to avoid re-entering. |
| 544 | * We mark IPI's with SA_INTERRUPT as they must run with | 543 | * We mark IPI's with IRQF_DISABLED as they must run with |
| 545 | * irqs disabled. | 544 | * irqs disabled. |
| 546 | */ | 545 | */ |
| 547 | mpic_eoi(mpic); | 546 | mpic_eoi(mpic); |
| @@ -752,7 +751,7 @@ void __init mpic_init(struct mpic *mpic) | |||
| 752 | if (!(mpic->flags & MPIC_PRIMARY)) | 751 | if (!(mpic->flags & MPIC_PRIMARY)) |
| 753 | continue; | 752 | continue; |
| 754 | irq_desc[mpic->ipi_offset+i].status |= IRQ_PER_CPU; | 753 | irq_desc[mpic->ipi_offset+i].status |= IRQ_PER_CPU; |
| 755 | irq_desc[mpic->ipi_offset+i].handler = &mpic->hc_ipi; | 754 | irq_desc[mpic->ipi_offset+i].chip = &mpic->hc_ipi; |
| 756 | #endif /* CONFIG_SMP */ | 755 | #endif /* CONFIG_SMP */ |
| 757 | } | 756 | } |
| 758 | 757 | ||
| @@ -813,7 +812,7 @@ void __init mpic_init(struct mpic *mpic) | |||
| 813 | /* init linux descriptors */ | 812 | /* init linux descriptors */ |
| 814 | if (i < mpic->irq_count) { | 813 | if (i < mpic->irq_count) { |
| 815 | irq_desc[mpic->irq_offset+i].status = level ? IRQ_LEVEL : 0; | 814 | irq_desc[mpic->irq_offset+i].status = level ? IRQ_LEVEL : 0; |
| 816 | irq_desc[mpic->irq_offset+i].handler = &mpic->hc_irq; | 815 | irq_desc[mpic->irq_offset+i].chip = &mpic->hc_irq; |
| 817 | } | 816 | } |
| 818 | } | 817 | } |
| 819 | 818 | ||
| @@ -906,7 +905,7 @@ void mpic_setup_this_cpu(void) | |||
| 906 | /* let the mpic know we want intrs. default affinity is 0xffffffff | 905 | /* let the mpic know we want intrs. default affinity is 0xffffffff |
| 907 | * until changed via /proc. That's how it's done on x86. If we want | 906 | * until changed via /proc. That's how it's done on x86. If we want |
| 908 | * it differently, then we should make sure we also change the default | 907 | * it differently, then we should make sure we also change the default |
| 909 | * values of irq_affinity in irq.c. | 908 | * values of irq_desc[].affinity in irq.c. |
| 910 | */ | 909 | */ |
| 911 | if (distribute_irqs) { | 910 | if (distribute_irqs) { |
| 912 | for (i = 0; i < mpic->num_sources ; i++) | 911 | for (i = 0; i < mpic->num_sources ; i++) |
| @@ -1028,14 +1027,17 @@ void mpic_request_ipis(void) | |||
| 1028 | 1027 | ||
| 1029 | printk("requesting IPIs ... \n"); | 1028 | printk("requesting IPIs ... \n"); |
| 1030 | 1029 | ||
| 1031 | /* IPIs are marked SA_INTERRUPT as they must run with irqs disabled */ | 1030 | /* |
| 1032 | request_irq(mpic->ipi_offset+0, mpic_ipi_action, SA_INTERRUPT, | 1031 | * IPIs are marked IRQF_DISABLED as they must run with irqs |
| 1032 | * disabled | ||
| 1033 | */ | ||
| 1034 | request_irq(mpic->ipi_offset+0, mpic_ipi_action, IRQF_DISABLED, | ||
| 1033 | "IPI0 (call function)", mpic); | 1035 | "IPI0 (call function)", mpic); |
| 1034 | request_irq(mpic->ipi_offset+1, mpic_ipi_action, SA_INTERRUPT, | 1036 | request_irq(mpic->ipi_offset+1, mpic_ipi_action, IRQF_DISABLED, |
| 1035 | "IPI1 (reschedule)", mpic); | 1037 | "IPI1 (reschedule)", mpic); |
| 1036 | request_irq(mpic->ipi_offset+2, mpic_ipi_action, SA_INTERRUPT, | 1038 | request_irq(mpic->ipi_offset+2, mpic_ipi_action, IRQF_DISABLED, |
| 1037 | "IPI2 (unused)", mpic); | 1039 | "IPI2 (unused)", mpic); |
| 1038 | request_irq(mpic->ipi_offset+3, mpic_ipi_action, SA_INTERRUPT, | 1040 | request_irq(mpic->ipi_offset+3, mpic_ipi_action, IRQF_DISABLED, |
| 1039 | "IPI3 (debugger break)", mpic); | 1041 | "IPI3 (debugger break)", mpic); |
| 1040 | 1042 | ||
| 1041 | printk("IPIs requested... \n"); | 1043 | printk("IPIs requested... \n"); |
diff --git a/arch/powerpc/sysdev/todc.c b/arch/powerpc/sysdev/todc.c new file mode 100644 index 000000000000..0a65980efb50 --- /dev/null +++ b/arch/powerpc/sysdev/todc.c | |||
| @@ -0,0 +1,392 @@ | |||
| 1 | /* | ||
| 2 | * Time of Day Clock support for the M48T35, M48T37, M48T59, and MC146818 | ||
| 3 | * Real Time Clocks/Timekeepers. | ||
| 4 | * | ||
| 5 | * Author: Mark A. Greer <mgreer@mvista.com> | ||
| 6 | * | ||
| 7 | * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under | ||
| 8 | * the terms of the GNU General Public License version 2. This program | ||
| 9 | * is licensed "as is" without any warranty of any kind, whether express | ||
| 10 | * or implied. | ||
| 11 | */ | ||
| 12 | #include <linux/errno.h> | ||
| 13 | #include <linux/init.h> | ||
| 14 | #include <linux/kernel.h> | ||
| 15 | #include <linux/time.h> | ||
| 16 | #include <linux/timex.h> | ||
| 17 | #include <linux/bcd.h> | ||
| 18 | #include <linux/mc146818rtc.h> | ||
| 19 | |||
| 20 | #include <asm/machdep.h> | ||
| 21 | #include <asm/io.h> | ||
| 22 | #include <asm/time.h> | ||
| 23 | #include <asm/todc.h> | ||
| 24 | |||
| 25 | /* | ||
| 26 | * Depending on the hardware on your board and your board design, the | ||
| 27 | * RTC/NVRAM may be accessed either directly (like normal memory) or via | ||
| 28 | * address/data registers. If your board uses the direct method, set | ||
| 29 | * 'nvram_data' to the base address of your nvram and leave 'nvram_as0' and | ||
| 30 | * 'nvram_as1' NULL. If your board uses address/data regs to access nvram, | ||
| 31 | * set 'nvram_as0' to the address of the lower byte, set 'nvram_as1' to the | ||
| 32 | * address of the upper byte (leave NULL if using mc146818), and set | ||
| 33 | * 'nvram_data' to the address of the 8-bit data register. | ||
| 34 | * | ||
| 35 | * Note: Even though the documentation for the various RTC chips say that it | ||
| 36 | * take up to a second before it starts updating once the 'R' bit is | ||
| 37 | * cleared, they always seem to update even though we bang on it many | ||
| 38 | * times a second. This is true, except for the Dallas Semi 1746/1747 | ||
| 39 | * (possibly others). Those chips seem to have a real problem whenever | ||
| 40 | * we set the 'R' bit before reading them, they basically stop counting. | ||
| 41 | * --MAG | ||
| 42 | */ | ||
| 43 | |||
| 44 | /* | ||
| 45 | * 'todc_info' should be initialized in your *_setup.c file to | ||
| 46 | * point to a fully initialized 'todc_info_t' structure. | ||
| 47 | * This structure holds all the register offsets for your particular | ||
| 48 | * TODC/RTC chip. | ||
| 49 | * TODC_ALLOC()/TODC_INIT() will allocate and initialize this table for you. | ||
| 50 | */ | ||
| 51 | |||
| 52 | #ifdef RTC_FREQ_SELECT | ||
| 53 | #undef RTC_FREQ_SELECT | ||
| 54 | #define RTC_FREQ_SELECT control_b /* Register A */ | ||
| 55 | #endif | ||
| 56 | |||
| 57 | #ifdef RTC_CONTROL | ||
| 58 | #undef RTC_CONTROL | ||
| 59 | #define RTC_CONTROL control_a /* Register B */ | ||
| 60 | #endif | ||
| 61 | |||
| 62 | #ifdef RTC_INTR_FLAGS | ||
| 63 | #undef RTC_INTR_FLAGS | ||
| 64 | #define RTC_INTR_FLAGS watchdog /* Register C */ | ||
| 65 | #endif | ||
| 66 | |||
| 67 | #ifdef RTC_VALID | ||
| 68 | #undef RTC_VALID | ||
| 69 | #define RTC_VALID interrupts /* Register D */ | ||
| 70 | #endif | ||
| 71 | |||
| 72 | /* Access routines when RTC accessed directly (like normal memory) */ | ||
| 73 | u_char | ||
| 74 | todc_direct_read_val(int addr) | ||
| 75 | { | ||
| 76 | return readb((void __iomem *)(todc_info->nvram_data + addr)); | ||
| 77 | } | ||
| 78 | |||
| 79 | void | ||
| 80 | todc_direct_write_val(int addr, unsigned char val) | ||
| 81 | { | ||
| 82 | writeb(val, (void __iomem *)(todc_info->nvram_data + addr)); | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | |||
| 86 | /* Access routines for accessing m48txx type chips via addr/data regs */ | ||
| 87 | u_char | ||
| 88 | todc_m48txx_read_val(int addr) | ||
| 89 | { | ||
| 90 | outb(addr, todc_info->nvram_as0); | ||
| 91 | outb(addr>>todc_info->as0_bits, todc_info->nvram_as1); | ||
| 92 | return inb(todc_info->nvram_data); | ||
| 93 | } | ||
| 94 | |||
| 95 | void | ||
| 96 | todc_m48txx_write_val(int addr, unsigned char val) | ||
| 97 | { | ||
| 98 | outb(addr, todc_info->nvram_as0); | ||
| 99 | outb(addr>>todc_info->as0_bits, todc_info->nvram_as1); | ||
| 100 | outb(val, todc_info->nvram_data); | ||
| 101 | return; | ||
| 102 | } | ||
| 103 | |||
| 104 | /* Access routines for accessing mc146818 type chips via addr/data regs */ | ||
| 105 | u_char | ||
| 106 | todc_mc146818_read_val(int addr) | ||
| 107 | { | ||
| 108 | outb_p(addr, todc_info->nvram_as0); | ||
| 109 | return inb_p(todc_info->nvram_data); | ||
| 110 | } | ||
| 111 | |||
| 112 | void | ||
| 113 | todc_mc146818_write_val(int addr, unsigned char val) | ||
| 114 | { | ||
| 115 | outb_p(addr, todc_info->nvram_as0); | ||
| 116 | outb_p(val, todc_info->nvram_data); | ||
| 117 | } | ||
| 118 | |||
| 119 | |||
| 120 | /* | ||
| 121 | * Routines to make RTC chips with NVRAM buried behind an addr/data pair | ||
| 122 | * have the NVRAM and clock regs appear at the same level. | ||
| 123 | * The NVRAM will appear to start at addr 0 and the clock regs will appear | ||
| 124 | * to start immediately after the NVRAM (actually, start at offset | ||
| 125 | * todc_info->nvram_size). | ||
| 126 | */ | ||
| 127 | static inline u_char | ||
| 128 | todc_read_val(int addr) | ||
| 129 | { | ||
| 130 | u_char val; | ||
| 131 | |||
| 132 | if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) { | ||
| 133 | if (addr < todc_info->nvram_size) { /* NVRAM */ | ||
| 134 | ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr); | ||
| 135 | val = ppc_md.rtc_read_val(todc_info->nvram_data_reg); | ||
| 136 | } else { /* Clock Reg */ | ||
| 137 | addr -= todc_info->nvram_size; | ||
| 138 | val = ppc_md.rtc_read_val(addr); | ||
| 139 | } | ||
| 140 | } else | ||
| 141 | val = ppc_md.rtc_read_val(addr); | ||
| 142 | |||
| 143 | return val; | ||
| 144 | } | ||
| 145 | |||
| 146 | static inline void | ||
| 147 | todc_write_val(int addr, u_char val) | ||
| 148 | { | ||
| 149 | if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) { | ||
| 150 | if (addr < todc_info->nvram_size) { /* NVRAM */ | ||
| 151 | ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr); | ||
| 152 | ppc_md.rtc_write_val(todc_info->nvram_data_reg, val); | ||
| 153 | } else { /* Clock Reg */ | ||
| 154 | addr -= todc_info->nvram_size; | ||
| 155 | ppc_md.rtc_write_val(addr, val); | ||
| 156 | } | ||
| 157 | } else | ||
| 158 | ppc_md.rtc_write_val(addr, val); | ||
| 159 | } | ||
| 160 | |||
| 161 | /* | ||
| 162 | * TODC routines | ||
| 163 | * | ||
| 164 | * There is some ugly stuff in that there are assumptions for the mc146818. | ||
| 165 | * | ||
| 166 | * Assumptions: | ||
| 167 | * - todc_info->control_a has the offset as mc146818 Register B reg | ||
| 168 | * - todc_info->control_b has the offset as mc146818 Register A reg | ||
| 169 | * - m48txx control reg's write enable or 'W' bit is same as | ||
| 170 | * mc146818 Register B 'SET' bit (i.e., 0x80) | ||
| 171 | * | ||
| 172 | * These assumptions were made to make the code simpler. | ||
| 173 | */ | ||
| 174 | long __init | ||
| 175 | todc_time_init(void) | ||
| 176 | { | ||
| 177 | u_char cntl_b; | ||
| 178 | |||
| 179 | if (!ppc_md.rtc_read_val) | ||
| 180 | ppc_md.rtc_read_val = ppc_md.nvram_read_val; | ||
| 181 | if (!ppc_md.rtc_write_val) | ||
| 182 | ppc_md.rtc_write_val = ppc_md.nvram_write_val; | ||
| 183 | |||
| 184 | cntl_b = todc_read_val(todc_info->control_b); | ||
| 185 | |||
| 186 | if (todc_info->rtc_type == TODC_TYPE_MC146818) { | ||
| 187 | if ((cntl_b & 0x70) != 0x20) { | ||
| 188 | printk(KERN_INFO "TODC real-time-clock was stopped." | ||
| 189 | " Now starting..."); | ||
| 190 | cntl_b &= ~0x70; | ||
| 191 | cntl_b |= 0x20; | ||
| 192 | } | ||
| 193 | |||
| 194 | todc_write_val(todc_info->control_b, cntl_b); | ||
| 195 | } else if (todc_info->rtc_type == TODC_TYPE_DS17285) { | ||
| 196 | u_char mode; | ||
| 197 | |||
| 198 | mode = todc_read_val(TODC_TYPE_DS17285_CNTL_A); | ||
| 199 | /* Make sure countdown clear is not set */ | ||
| 200 | mode &= ~0x40; | ||
| 201 | /* Enable oscillator, extended register set */ | ||
| 202 | mode |= 0x30; | ||
| 203 | todc_write_val(TODC_TYPE_DS17285_CNTL_A, mode); | ||
| 204 | |||
| 205 | } else if (todc_info->rtc_type == TODC_TYPE_DS1501) { | ||
| 206 | u_char month; | ||
| 207 | |||
| 208 | todc_info->enable_read = TODC_DS1501_CNTL_B_TE; | ||
| 209 | todc_info->enable_write = TODC_DS1501_CNTL_B_TE; | ||
| 210 | |||
| 211 | month = todc_read_val(todc_info->month); | ||
| 212 | |||
| 213 | if ((month & 0x80) == 0x80) { | ||
| 214 | printk(KERN_INFO "TODC %s %s\n", | ||
| 215 | "real-time-clock was stopped.", | ||
| 216 | "Now starting..."); | ||
| 217 | month &= ~0x80; | ||
| 218 | todc_write_val(todc_info->month, month); | ||
| 219 | } | ||
| 220 | |||
| 221 | cntl_b &= ~TODC_DS1501_CNTL_B_TE; | ||
| 222 | todc_write_val(todc_info->control_b, cntl_b); | ||
| 223 | } else { /* must be a m48txx type */ | ||
| 224 | u_char cntl_a; | ||
| 225 | |||
| 226 | todc_info->enable_read = TODC_MK48TXX_CNTL_A_R; | ||
| 227 | todc_info->enable_write = TODC_MK48TXX_CNTL_A_W; | ||
| 228 | |||
| 229 | cntl_a = todc_read_val(todc_info->control_a); | ||
| 230 | |||
| 231 | /* Check & clear STOP bit in control B register */ | ||
| 232 | if (cntl_b & TODC_MK48TXX_DAY_CB) { | ||
| 233 | printk(KERN_INFO "TODC %s %s\n", | ||
| 234 | "real-time-clock was stopped.", | ||
| 235 | "Now starting..."); | ||
| 236 | |||
| 237 | cntl_a |= todc_info->enable_write; | ||
| 238 | cntl_b &= ~TODC_MK48TXX_DAY_CB;/* Start Oscil */ | ||
| 239 | |||
| 240 | todc_write_val(todc_info->control_a, cntl_a); | ||
| 241 | todc_write_val(todc_info->control_b, cntl_b); | ||
| 242 | } | ||
| 243 | |||
| 244 | /* Make sure READ & WRITE bits are cleared. */ | ||
| 245 | cntl_a &= ~(todc_info->enable_write | todc_info->enable_read); | ||
| 246 | todc_write_val(todc_info->control_a, cntl_a); | ||
| 247 | } | ||
| 248 | |||
| 249 | return 0; | ||
| 250 | } | ||
| 251 | |||
| 252 | /* | ||
| 253 | * There is some ugly stuff in that there are assumptions that for a mc146818, | ||
| 254 | * the todc_info->control_a has the offset of the mc146818 Register B reg and | ||
| 255 | * that the register'ss 'SET' bit is the same as the m48txx's write enable | ||
| 256 | * bit in the control register of the m48txx (i.e., 0x80). | ||
| 257 | * | ||
| 258 | * It was done to make the code look simpler. | ||
| 259 | */ | ||
| 260 | void | ||
| 261 | todc_get_rtc_time(struct rtc_time *tm) | ||
| 262 | { | ||
| 263 | uint year = 0, mon = 0, mday = 0, hour = 0, min = 0, sec = 0; | ||
| 264 | uint limit, i; | ||
| 265 | u_char save_control, uip = 0; | ||
| 266 | extern void GregorianDay(struct rtc_time *); | ||
| 267 | |||
| 268 | spin_lock(&rtc_lock); | ||
| 269 | save_control = todc_read_val(todc_info->control_a); | ||
| 270 | |||
| 271 | if (todc_info->rtc_type != TODC_TYPE_MC146818) { | ||
| 272 | limit = 1; | ||
| 273 | |||
| 274 | switch (todc_info->rtc_type) { | ||
| 275 | case TODC_TYPE_DS1553: | ||
| 276 | case TODC_TYPE_DS1557: | ||
| 277 | case TODC_TYPE_DS1743: | ||
| 278 | case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ | ||
| 279 | case TODC_TYPE_DS1747: | ||
| 280 | case TODC_TYPE_DS17285: | ||
| 281 | break; | ||
| 282 | default: | ||
| 283 | todc_write_val(todc_info->control_a, | ||
| 284 | (save_control | todc_info->enable_read)); | ||
| 285 | } | ||
| 286 | } else | ||
| 287 | limit = 100000000; | ||
| 288 | |||
| 289 | for (i=0; i<limit; i++) { | ||
| 290 | if (todc_info->rtc_type == TODC_TYPE_MC146818) | ||
| 291 | uip = todc_read_val(todc_info->RTC_FREQ_SELECT); | ||
| 292 | |||
| 293 | sec = todc_read_val(todc_info->seconds) & 0x7f; | ||
| 294 | min = todc_read_val(todc_info->minutes) & 0x7f; | ||
| 295 | hour = todc_read_val(todc_info->hours) & 0x3f; | ||
| 296 | mday = todc_read_val(todc_info->day_of_month) & 0x3f; | ||
| 297 | mon = todc_read_val(todc_info->month) & 0x1f; | ||
| 298 | year = todc_read_val(todc_info->year) & 0xff; | ||
| 299 | |||
| 300 | if (todc_info->rtc_type == TODC_TYPE_MC146818) { | ||
| 301 | uip |= todc_read_val(todc_info->RTC_FREQ_SELECT); | ||
| 302 | if ((uip & RTC_UIP) == 0) | ||
| 303 | break; | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | if (todc_info->rtc_type != TODC_TYPE_MC146818) { | ||
| 308 | switch (todc_info->rtc_type) { | ||
| 309 | case TODC_TYPE_DS1553: | ||
| 310 | case TODC_TYPE_DS1557: | ||
| 311 | case TODC_TYPE_DS1743: | ||
| 312 | case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ | ||
| 313 | case TODC_TYPE_DS1747: | ||
| 314 | case TODC_TYPE_DS17285: | ||
| 315 | break; | ||
| 316 | default: | ||
| 317 | save_control &= ~(todc_info->enable_read); | ||
| 318 | todc_write_val(todc_info->control_a, save_control); | ||
| 319 | } | ||
| 320 | } | ||
| 321 | spin_unlock(&rtc_lock); | ||
| 322 | |||
| 323 | if ((todc_info->rtc_type != TODC_TYPE_MC146818) | ||
| 324 | || ((save_control & RTC_DM_BINARY) == 0) | ||
| 325 | || RTC_ALWAYS_BCD) { | ||
| 326 | BCD_TO_BIN(sec); | ||
| 327 | BCD_TO_BIN(min); | ||
| 328 | BCD_TO_BIN(hour); | ||
| 329 | BCD_TO_BIN(mday); | ||
| 330 | BCD_TO_BIN(mon); | ||
| 331 | BCD_TO_BIN(year); | ||
| 332 | } | ||
| 333 | |||
| 334 | if ((year + 1900) < 1970) { | ||
| 335 | year += 100; | ||
| 336 | } | ||
| 337 | |||
| 338 | tm->tm_sec = sec; | ||
| 339 | tm->tm_min = min; | ||
| 340 | tm->tm_hour = hour; | ||
| 341 | tm->tm_mday = mday; | ||
| 342 | tm->tm_mon = mon; | ||
| 343 | tm->tm_year = year; | ||
| 344 | |||
| 345 | GregorianDay(tm); | ||
| 346 | } | ||
| 347 | |||
| 348 | int | ||
| 349 | todc_set_rtc_time(struct rtc_time *tm) | ||
| 350 | { | ||
| 351 | u_char save_control, save_freq_select = 0; | ||
| 352 | |||
| 353 | spin_lock(&rtc_lock); | ||
| 354 | save_control = todc_read_val(todc_info->control_a); | ||
| 355 | |||
| 356 | /* Assuming MK48T59_RTC_CA_WRITE & RTC_SET are equal */ | ||
| 357 | todc_write_val(todc_info->control_a, | ||
| 358 | (save_control | todc_info->enable_write)); | ||
| 359 | save_control &= ~(todc_info->enable_write); /* in case it was set */ | ||
| 360 | |||
| 361 | if (todc_info->rtc_type == TODC_TYPE_MC146818) { | ||
| 362 | save_freq_select = todc_read_val(todc_info->RTC_FREQ_SELECT); | ||
| 363 | todc_write_val(todc_info->RTC_FREQ_SELECT, | ||
| 364 | save_freq_select | RTC_DIV_RESET2); | ||
| 365 | } | ||
| 366 | |||
| 367 | if ((todc_info->rtc_type != TODC_TYPE_MC146818) | ||
| 368 | || ((save_control & RTC_DM_BINARY) == 0) | ||
| 369 | || RTC_ALWAYS_BCD) { | ||
| 370 | BIN_TO_BCD(tm->tm_sec); | ||
| 371 | BIN_TO_BCD(tm->tm_min); | ||
| 372 | BIN_TO_BCD(tm->tm_hour); | ||
| 373 | BIN_TO_BCD(tm->tm_mon); | ||
| 374 | BIN_TO_BCD(tm->tm_mday); | ||
| 375 | BIN_TO_BCD(tm->tm_year); | ||
| 376 | } | ||
| 377 | |||
| 378 | todc_write_val(todc_info->seconds, tm->tm_sec); | ||
| 379 | todc_write_val(todc_info->minutes, tm->tm_min); | ||
| 380 | todc_write_val(todc_info->hours, tm->tm_hour); | ||
| 381 | todc_write_val(todc_info->month, tm->tm_mon); | ||
| 382 | todc_write_val(todc_info->day_of_month, tm->tm_mday); | ||
| 383 | todc_write_val(todc_info->year, tm->tm_year); | ||
| 384 | |||
| 385 | todc_write_val(todc_info->control_a, save_control); | ||
| 386 | |||
| 387 | if (todc_info->rtc_type == TODC_TYPE_MC146818) | ||
| 388 | todc_write_val(todc_info->RTC_FREQ_SELECT, save_freq_select); | ||
| 389 | |||
| 390 | spin_unlock(&rtc_lock); | ||
| 391 | return 0; | ||
| 392 | } | ||
diff --git a/arch/powerpc/sysdev/tsi108_dev.c b/arch/powerpc/sysdev/tsi108_dev.c new file mode 100644 index 000000000000..26a0cc820cde --- /dev/null +++ b/arch/powerpc/sysdev/tsi108_dev.c | |||
| @@ -0,0 +1,145 @@ | |||
| 1 | /* | ||
| 2 | * tsi108/109 device setup code | ||
| 3 | * | ||
| 4 | * Maintained by Roy Zang < tie-fei.zang@freescale.com > | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License as published by the | ||
| 8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 9 | * option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/config.h> | ||
| 13 | #include <linux/stddef.h> | ||
| 14 | #include <linux/kernel.h> | ||
| 15 | #include <linux/init.h> | ||
| 16 | #include <linux/errno.h> | ||
| 17 | #include <linux/major.h> | ||
| 18 | #include <linux/delay.h> | ||
| 19 | #include <linux/irq.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/device.h> | ||
| 22 | #include <linux/platform_device.h> | ||
| 23 | #include <asm/tsi108.h> | ||
| 24 | |||
| 25 | #include <asm/system.h> | ||
| 26 | #include <asm/atomic.h> | ||
| 27 | #include <asm/io.h> | ||
| 28 | #include <asm/irq.h> | ||
| 29 | #include <asm/prom.h> | ||
| 30 | #include <mm/mmu_decl.h> | ||
| 31 | |||
| 32 | #undef DEBUG | ||
| 33 | |||
| 34 | #ifdef DEBUG | ||
| 35 | #define DBG(fmt...) do { printk(fmt); } while(0) | ||
| 36 | #else | ||
| 37 | #define DBG(fmt...) do { } while(0) | ||
| 38 | #endif | ||
| 39 | |||
| 40 | static phys_addr_t tsi108_csr_base = -1; | ||
| 41 | |||
| 42 | phys_addr_t get_csrbase(void) | ||
| 43 | { | ||
| 44 | struct device_node *tsi; | ||
| 45 | |||
| 46 | if (tsi108_csr_base != -1) | ||
| 47 | return tsi108_csr_base; | ||
| 48 | |||
| 49 | tsi = of_find_node_by_type(NULL, "tsi-bridge"); | ||
| 50 | if (tsi) { | ||
| 51 | unsigned int size; | ||
| 52 | void *prop = get_property(tsi, "reg", &size); | ||
| 53 | tsi108_csr_base = of_translate_address(tsi, prop); | ||
| 54 | of_node_put(tsi); | ||
| 55 | }; | ||
| 56 | return tsi108_csr_base; | ||
| 57 | } | ||
| 58 | |||
| 59 | u32 get_vir_csrbase(void) | ||
| 60 | { | ||
| 61 | return (u32) (ioremap(get_csrbase(), 0x10000)); | ||
| 62 | } | ||
| 63 | |||
| 64 | EXPORT_SYMBOL(get_csrbase); | ||
| 65 | EXPORT_SYMBOL(get_vir_csrbase); | ||
| 66 | |||
| 67 | static int __init tsi108_eth_of_init(void) | ||
| 68 | { | ||
| 69 | struct device_node *np; | ||
| 70 | unsigned int i; | ||
| 71 | struct platform_device *tsi_eth_dev; | ||
| 72 | struct resource res; | ||
| 73 | int ret; | ||
| 74 | |||
| 75 | for (np = NULL, i = 0; | ||
| 76 | (np = of_find_compatible_node(np, "network", "tsi-ethernet")) != NULL; | ||
| 77 | i++) { | ||
| 78 | struct resource r[2]; | ||
| 79 | struct device_node *phy; | ||
| 80 | hw_info tsi_eth_data; | ||
| 81 | unsigned int *id; | ||
| 82 | unsigned int *phy_id; | ||
| 83 | void *mac_addr; | ||
| 84 | phandle *ph; | ||
| 85 | |||
| 86 | memset(r, 0, sizeof(r)); | ||
| 87 | memset(&tsi_eth_data, 0, sizeof(tsi_eth_data)); | ||
| 88 | |||
| 89 | ret = of_address_to_resource(np, 0, &r[0]); | ||
| 90 | DBG("%s: name:start->end = %s:0x%lx-> 0x%lx\n", | ||
| 91 | __FUNCTION__,r[0].name, r[0].start, r[0].end); | ||
| 92 | if (ret) | ||
| 93 | goto err; | ||
| 94 | |||
| 95 | r[1].name = "tx"; | ||
| 96 | r[1].start = np->intrs[0].line; | ||
| 97 | r[1].end = np->intrs[0].line; | ||
| 98 | r[1].flags = IORESOURCE_IRQ; | ||
| 99 | |||
| 100 | tsi_eth_dev = | ||
| 101 | platform_device_register_simple("tsi-ethernet", i, &r[0], | ||
| 102 | np->n_intrs + 1); | ||
| 103 | |||
| 104 | if (IS_ERR(tsi_eth_dev)) { | ||
| 105 | ret = PTR_ERR(tsi_eth_dev); | ||
| 106 | goto err; | ||
| 107 | } | ||
| 108 | |||
| 109 | mac_addr = get_property(np, "address", NULL); | ||
| 110 | memcpy(tsi_eth_data.mac_addr, mac_addr, 6); | ||
| 111 | |||
| 112 | ph = (phandle *) get_property(np, "phy-handle", NULL); | ||
| 113 | phy = of_find_node_by_phandle(*ph); | ||
| 114 | |||
| 115 | if (phy == NULL) { | ||
| 116 | ret = -ENODEV; | ||
| 117 | goto unreg; | ||
| 118 | } | ||
| 119 | |||
| 120 | id = (u32 *) get_property(phy, "reg", NULL); | ||
| 121 | phy_id = (u32 *) get_property(phy, "phy-id", NULL); | ||
| 122 | ret = of_address_to_resource(phy, 0, &res); | ||
| 123 | if (ret) { | ||
| 124 | of_node_put(phy); | ||
| 125 | goto unreg; | ||
| 126 | } | ||
| 127 | tsi_eth_data.regs = r[0].start; | ||
| 128 | tsi_eth_data.phyregs = res.start; | ||
| 129 | tsi_eth_data.phy = *phy_id; | ||
| 130 | tsi_eth_data.irq_num = np->intrs[0].line; | ||
| 131 | of_node_put(phy); | ||
| 132 | ret = | ||
| 133 | platform_device_add_data(tsi_eth_dev, &tsi_eth_data, | ||
| 134 | sizeof(hw_info)); | ||
| 135 | if (ret) | ||
| 136 | goto unreg; | ||
| 137 | } | ||
| 138 | return 0; | ||
| 139 | unreg: | ||
| 140 | platform_device_unregister(tsi_eth_dev); | ||
| 141 | err: | ||
| 142 | return ret; | ||
| 143 | } | ||
| 144 | |||
| 145 | arch_initcall(tsi108_eth_of_init); | ||
diff --git a/arch/powerpc/sysdev/tsi108_pci.c b/arch/powerpc/sysdev/tsi108_pci.c new file mode 100644 index 000000000000..3265d54c82ed --- /dev/null +++ b/arch/powerpc/sysdev/tsi108_pci.c | |||
| @@ -0,0 +1,412 @@ | |||
| 1 | /* | ||
| 2 | * Common routines for Tundra Semiconductor TSI108 host bridge. | ||
| 3 | * | ||
| 4 | * 2004-2005 (c) Tundra Semiconductor Corp. | ||
| 5 | * Author: Alex Bounine (alexandreb@tundra.com) | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify it | ||
| 8 | * under the terms of the GNU General Public License as published by the Free | ||
| 9 | * Software Foundation; either version 2 of the License, or (at your option) | ||
| 10 | * any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 15 | * more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License along with | ||
| 18 | * this program; if not, write to the Free Software Foundation, Inc., 59 | ||
| 19 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/kernel.h> | ||
| 23 | #include <linux/init.h> | ||
| 24 | #include <linux/pci.h> | ||
| 25 | #include <linux/slab.h> | ||
| 26 | #include <linux/irq.h> | ||
| 27 | #include <linux/interrupt.h> | ||
| 28 | |||
| 29 | |||
| 30 | #include <asm/byteorder.h> | ||
| 31 | #include <asm/io.h> | ||
| 32 | #include <asm/irq.h> | ||
| 33 | #include <asm/uaccess.h> | ||
| 34 | #include <asm/machdep.h> | ||
| 35 | #include <asm/pci-bridge.h> | ||
| 36 | #include <asm/tsi108.h> | ||
| 37 | #include <asm/tsi108_irq.h> | ||
| 38 | #include <asm/prom.h> | ||
| 39 | |||
| 40 | #undef DEBUG | ||
| 41 | #ifdef DEBUG | ||
| 42 | #define DBG(x...) printk(x) | ||
| 43 | #else | ||
| 44 | #define DBG(x...) | ||
| 45 | #endif | ||
| 46 | |||
| 47 | #define tsi_mk_config_addr(bus, devfunc, offset) \ | ||
| 48 | ((((bus)<<16) | ((devfunc)<<8) | (offset & 0xfc)) + tsi108_pci_cfg_base) | ||
| 49 | |||
| 50 | u32 tsi108_pci_cfg_base; | ||
| 51 | u32 tsi108_csr_vir_base; | ||
| 52 | |||
| 53 | extern u32 get_vir_csrbase(void); | ||
| 54 | extern u32 tsi108_read_reg(u32 reg_offset); | ||
| 55 | extern void tsi108_write_reg(u32 reg_offset, u32 val); | ||
| 56 | |||
| 57 | int | ||
| 58 | tsi108_direct_write_config(struct pci_bus *bus, unsigned int devfunc, | ||
| 59 | int offset, int len, u32 val) | ||
| 60 | { | ||
| 61 | volatile unsigned char *cfg_addr; | ||
| 62 | |||
| 63 | if (ppc_md.pci_exclude_device) | ||
| 64 | if (ppc_md.pci_exclude_device(bus->number, devfunc)) | ||
| 65 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
| 66 | |||
| 67 | cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number, | ||
| 68 | devfunc, offset) | | ||
| 69 | (offset & 0x03)); | ||
| 70 | |||
| 71 | #ifdef DEBUG | ||
| 72 | printk("PCI CFG write : "); | ||
| 73 | printk("%d:0x%x:0x%x ", bus->number, devfunc, offset); | ||
| 74 | printk("%d ADDR=0x%08x ", len, (uint) cfg_addr); | ||
| 75 | printk("data = 0x%08x\n", val); | ||
| 76 | #endif | ||
| 77 | |||
| 78 | switch (len) { | ||
| 79 | case 1: | ||
| 80 | out_8((u8 *) cfg_addr, val); | ||
| 81 | break; | ||
| 82 | case 2: | ||
| 83 | out_le16((u16 *) cfg_addr, val); | ||
| 84 | break; | ||
| 85 | default: | ||
| 86 | out_le32((u32 *) cfg_addr, val); | ||
| 87 | break; | ||
| 88 | } | ||
| 89 | |||
| 90 | return PCIBIOS_SUCCESSFUL; | ||
| 91 | } | ||
| 92 | |||
| 93 | void tsi108_clear_pci_error(u32 pci_cfg_base) | ||
| 94 | { | ||
| 95 | u32 err_stat, err_addr, pci_stat; | ||
| 96 | |||
| 97 | /* | ||
| 98 | * Quietly clear PB and PCI error flags set as result | ||
| 99 | * of PCI/X configuration read requests. | ||
| 100 | */ | ||
| 101 | |||
| 102 | /* Read PB Error Log Registers */ | ||
| 103 | |||
| 104 | err_stat = tsi108_read_reg(TSI108_PB_OFFSET + TSI108_PB_ERRCS); | ||
| 105 | err_addr = tsi108_read_reg(TSI108_PB_OFFSET + TSI108_PB_AERR); | ||
| 106 | |||
| 107 | if (err_stat & TSI108_PB_ERRCS_ES) { | ||
| 108 | /* Clear error flag */ | ||
| 109 | tsi108_write_reg(TSI108_PB_OFFSET + TSI108_PB_ERRCS, | ||
| 110 | TSI108_PB_ERRCS_ES); | ||
| 111 | |||
| 112 | /* Clear read error reported in PB_ISR */ | ||
| 113 | tsi108_write_reg(TSI108_PB_OFFSET + TSI108_PB_ISR, | ||
| 114 | TSI108_PB_ISR_PBS_RD_ERR); | ||
| 115 | |||
| 116 | /* Clear PCI/X bus cfg errors if applicable */ | ||
| 117 | if ((err_addr & 0xFF000000) == pci_cfg_base) { | ||
| 118 | pci_stat = | ||
| 119 | tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_CSR); | ||
| 120 | tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_CSR, | ||
| 121 | pci_stat); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | return; | ||
| 126 | } | ||
| 127 | |||
| 128 | #define __tsi108_read_pci_config(x, addr, op) \ | ||
| 129 | __asm__ __volatile__( \ | ||
| 130 | " "op" %0,0,%1\n" \ | ||
| 131 | "1: eieio\n" \ | ||
| 132 | "2:\n" \ | ||
| 133 | ".section .fixup,\"ax\"\n" \ | ||
| 134 | "3: li %0,-1\n" \ | ||
| 135 | " b 2b\n" \ | ||
| 136 | ".section __ex_table,\"a\"\n" \ | ||
| 137 | " .align 2\n" \ | ||
| 138 | " .long 1b,3b\n" \ | ||
| 139 | ".text" \ | ||
| 140 | : "=r"(x) : "r"(addr)) | ||
| 141 | |||
| 142 | int | ||
| 143 | tsi108_direct_read_config(struct pci_bus *bus, unsigned int devfn, int offset, | ||
| 144 | int len, u32 * val) | ||
| 145 | { | ||
| 146 | volatile unsigned char *cfg_addr; | ||
| 147 | u32 temp; | ||
| 148 | |||
| 149 | if (ppc_md.pci_exclude_device) | ||
| 150 | if (ppc_md.pci_exclude_device(bus->number, devfn)) | ||
| 151 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
| 152 | |||
| 153 | cfg_addr = (unsigned char *)(tsi_mk_config_addr(bus->number, | ||
| 154 | devfn, | ||
| 155 | offset) | (offset & | ||
| 156 | 0x03)); | ||
| 157 | |||
| 158 | switch (len) { | ||
| 159 | case 1: | ||
| 160 | __tsi108_read_pci_config(temp, cfg_addr, "lbzx"); | ||
| 161 | break; | ||
| 162 | case 2: | ||
| 163 | __tsi108_read_pci_config(temp, cfg_addr, "lhbrx"); | ||
| 164 | break; | ||
| 165 | default: | ||
| 166 | __tsi108_read_pci_config(temp, cfg_addr, "lwbrx"); | ||
| 167 | break; | ||
| 168 | } | ||
| 169 | |||
| 170 | *val = temp; | ||
| 171 | |||
| 172 | #ifdef DEBUG | ||
| 173 | if ((0xFFFFFFFF != temp) && (0xFFFF != temp) && (0xFF != temp)) { | ||
| 174 | printk("PCI CFG read : "); | ||
| 175 | printk("%d:0x%x:0x%x ", bus->number, devfn, offset); | ||
| 176 | printk("%d ADDR=0x%08x ", len, (uint) cfg_addr); | ||
| 177 | printk("data = 0x%x\n", *val); | ||
| 178 | } | ||
| 179 | #endif | ||
| 180 | return PCIBIOS_SUCCESSFUL; | ||
| 181 | } | ||
| 182 | |||
| 183 | void tsi108_clear_pci_cfg_error(void) | ||
| 184 | { | ||
| 185 | tsi108_clear_pci_error(TSI108_PCI_CFG_BASE_PHYS); | ||
| 186 | } | ||
| 187 | |||
| 188 | static struct pci_ops tsi108_direct_pci_ops = { | ||
| 189 | tsi108_direct_read_config, | ||
| 190 | tsi108_direct_write_config | ||
| 191 | }; | ||
| 192 | |||
| 193 | int __init tsi108_setup_pci(struct device_node *dev) | ||
| 194 | { | ||
| 195 | int len; | ||
| 196 | struct pci_controller *hose; | ||
| 197 | struct resource rsrc; | ||
| 198 | int *bus_range; | ||
| 199 | int primary = 0, has_address = 0; | ||
| 200 | |||
| 201 | /* PCI Config mapping */ | ||
| 202 | tsi108_pci_cfg_base = (u32)ioremap(TSI108_PCI_CFG_BASE_PHYS, | ||
| 203 | TSI108_PCI_CFG_SIZE); | ||
| 204 | DBG("TSI_PCI: %s tsi108_pci_cfg_base=0x%x\n", __FUNCTION__, | ||
| 205 | tsi108_pci_cfg_base); | ||
| 206 | |||
| 207 | /* Fetch host bridge registers address */ | ||
| 208 | has_address = (of_address_to_resource(dev, 0, &rsrc) == 0); | ||
| 209 | |||
| 210 | /* Get bus range if any */ | ||
| 211 | bus_range = (int *)get_property(dev, "bus-range", &len); | ||
| 212 | if (bus_range == NULL || len < 2 * sizeof(int)) { | ||
| 213 | printk(KERN_WARNING "Can't get bus-range for %s, assume" | ||
| 214 | " bus 0\n", dev->full_name); | ||
| 215 | } | ||
| 216 | |||
| 217 | hose = pcibios_alloc_controller(); | ||
| 218 | |||
| 219 | if (!hose) { | ||
| 220 | printk("PCI Host bridge init failed\n"); | ||
| 221 | return -ENOMEM; | ||
| 222 | } | ||
| 223 | hose->arch_data = dev; | ||
| 224 | hose->set_cfg_type = 1; | ||
| 225 | |||
| 226 | hose->first_busno = bus_range ? bus_range[0] : 0; | ||
| 227 | hose->last_busno = bus_range ? bus_range[1] : 0xff; | ||
| 228 | |||
| 229 | (hose)->ops = &tsi108_direct_pci_ops; | ||
| 230 | |||
| 231 | printk(KERN_INFO "Found tsi108 PCI host bridge at 0x%08lx. " | ||
| 232 | "Firmware bus number: %d->%d\n", | ||
| 233 | rsrc.start, hose->first_busno, hose->last_busno); | ||
| 234 | |||
| 235 | /* Interpret the "ranges" property */ | ||
| 236 | /* This also maps the I/O region and sets isa_io/mem_base */ | ||
| 237 | pci_process_bridge_OF_ranges(hose, dev, primary); | ||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | |||
| 241 | /* | ||
| 242 | * Low level utility functions | ||
| 243 | */ | ||
| 244 | |||
| 245 | static void tsi108_pci_int_mask(u_int irq) | ||
| 246 | { | ||
| 247 | u_int irp_cfg; | ||
| 248 | int int_line = (irq - IRQ_PCI_INTAD_BASE); | ||
| 249 | |||
| 250 | irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL); | ||
| 251 | mb(); | ||
| 252 | irp_cfg |= (1 << int_line); /* INTx_DIR = output */ | ||
| 253 | irp_cfg &= ~(3 << (8 + (int_line * 2))); /* INTx_TYPE = unused */ | ||
| 254 | tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg); | ||
| 255 | mb(); | ||
| 256 | irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL); | ||
| 257 | } | ||
| 258 | |||
| 259 | static void tsi108_pci_int_unmask(u_int irq) | ||
| 260 | { | ||
| 261 | u_int irp_cfg; | ||
| 262 | int int_line = (irq - IRQ_PCI_INTAD_BASE); | ||
| 263 | |||
| 264 | irp_cfg = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL); | ||
| 265 | mb(); | ||
| 266 | irp_cfg &= ~(1 << int_line); | ||
| 267 | irp_cfg |= (3 << (8 + (int_line * 2))); | ||
| 268 | tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, irp_cfg); | ||
| 269 | mb(); | ||
| 270 | } | ||
| 271 | |||
| 272 | static void init_pci_source(void) | ||
| 273 | { | ||
| 274 | tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL, | ||
| 275 | 0x0000ff00); | ||
| 276 | tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE, | ||
| 277 | TSI108_PCI_IRP_ENABLE_P_INT); | ||
| 278 | mb(); | ||
| 279 | } | ||
| 280 | |||
| 281 | static inline int get_pci_source(void) | ||
| 282 | { | ||
| 283 | u_int temp = 0; | ||
| 284 | int irq = -1; | ||
| 285 | int i; | ||
| 286 | u_int pci_irp_stat; | ||
| 287 | static int mask = 0; | ||
| 288 | |||
| 289 | /* Read PCI/X block interrupt status register */ | ||
| 290 | pci_irp_stat = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT); | ||
| 291 | mb(); | ||
| 292 | |||
| 293 | if (pci_irp_stat & TSI108_PCI_IRP_STAT_P_INT) { | ||
| 294 | /* Process Interrupt from PCI bus INTA# - INTD# lines */ | ||
| 295 | temp = | ||
| 296 | tsi108_read_reg(TSI108_PCI_OFFSET + | ||
| 297 | TSI108_PCI_IRP_INTAD) & 0xf; | ||
| 298 | mb(); | ||
| 299 | for (i = 0; i < 4; i++, mask++) { | ||
| 300 | if (temp & (1 << mask % 4)) { | ||
| 301 | irq = IRQ_PCI_INTA + mask % 4; | ||
| 302 | mask++; | ||
| 303 | break; | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | /* Disable interrupts from PCI block */ | ||
| 308 | temp = tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE); | ||
| 309 | tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE, | ||
| 310 | temp & ~TSI108_PCI_IRP_ENABLE_P_INT); | ||
| 311 | mb(); | ||
| 312 | (void)tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE); | ||
| 313 | mb(); | ||
| 314 | } | ||
| 315 | #ifdef DEBUG | ||
| 316 | else { | ||
| 317 | printk("TSI108_PIC: error in TSI108_PCI_IRP_STAT\n"); | ||
| 318 | pci_irp_stat = | ||
| 319 | tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_STAT); | ||
| 320 | temp = | ||
| 321 | tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_INTAD); | ||
| 322 | mb(); | ||
| 323 | printk(">> stat=0x%08x intad=0x%08x ", pci_irp_stat, temp); | ||
| 324 | temp = | ||
| 325 | tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_CFG_CTL); | ||
| 326 | mb(); | ||
| 327 | printk("cfg_ctl=0x%08x ", temp); | ||
| 328 | temp = | ||
| 329 | tsi108_read_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE); | ||
| 330 | mb(); | ||
| 331 | printk("irp_enable=0x%08x\n", temp); | ||
| 332 | } | ||
| 333 | #endif /* end of DEBUG */ | ||
| 334 | |||
| 335 | return irq; | ||
| 336 | } | ||
| 337 | |||
| 338 | |||
| 339 | /* | ||
| 340 | * Linux descriptor level callbacks | ||
| 341 | */ | ||
| 342 | |||
| 343 | static void tsi108_pci_irq_enable(u_int irq) | ||
| 344 | { | ||
| 345 | tsi108_pci_int_unmask(irq); | ||
| 346 | } | ||
| 347 | |||
| 348 | static void tsi108_pci_irq_disable(u_int irq) | ||
| 349 | { | ||
| 350 | tsi108_pci_int_mask(irq); | ||
| 351 | } | ||
| 352 | |||
| 353 | static void tsi108_pci_irq_ack(u_int irq) | ||
| 354 | { | ||
| 355 | tsi108_pci_int_mask(irq); | ||
| 356 | } | ||
| 357 | |||
| 358 | static void tsi108_pci_irq_end(u_int irq) | ||
| 359 | { | ||
| 360 | tsi108_pci_int_unmask(irq); | ||
| 361 | |||
| 362 | /* Enable interrupts from PCI block */ | ||
| 363 | tsi108_write_reg(TSI108_PCI_OFFSET + TSI108_PCI_IRP_ENABLE, | ||
| 364 | tsi108_read_reg(TSI108_PCI_OFFSET + | ||
| 365 | TSI108_PCI_IRP_ENABLE) | | ||
| 366 | TSI108_PCI_IRP_ENABLE_P_INT); | ||
| 367 | mb(); | ||
| 368 | } | ||
| 369 | |||
| 370 | /* | ||
| 371 | * Interrupt controller descriptor for cascaded PCI interrupt controller. | ||
| 372 | */ | ||
| 373 | |||
| 374 | struct hw_interrupt_type tsi108_pci_irq = { | ||
| 375 | .typename = "tsi108_PCI_int", | ||
| 376 | .enable = tsi108_pci_irq_enable, | ||
| 377 | .disable = tsi108_pci_irq_disable, | ||
| 378 | .ack = tsi108_pci_irq_ack, | ||
| 379 | .end = tsi108_pci_irq_end, | ||
| 380 | }; | ||
| 381 | |||
| 382 | /* | ||
| 383 | * Exported functions | ||
| 384 | */ | ||
| 385 | |||
| 386 | /* | ||
| 387 | * The Tsi108 PCI interrupts initialization routine. | ||
| 388 | * | ||
| 389 | * The INTA# - INTD# interrupts on the PCI bus are reported by the PCI block | ||
| 390 | * to the MPIC using single interrupt source (IRQ_TSI108_PCI). Therefore the | ||
| 391 | * PCI block has to be treated as a cascaded interrupt controller connected | ||
| 392 | * to the MPIC. | ||
| 393 | */ | ||
| 394 | |||
| 395 | void __init tsi108_pci_int_init(void) | ||
| 396 | { | ||
| 397 | u_int i; | ||
| 398 | |||
| 399 | DBG("Tsi108_pci_int_init: initializing PCI interrupts\n"); | ||
| 400 | |||
| 401 | for (i = 0; i < NUM_PCI_IRQS; i++) { | ||
| 402 | irq_desc[i + IRQ_PCI_INTAD_BASE].handler = &tsi108_pci_irq; | ||
| 403 | irq_desc[i + IRQ_PCI_INTAD_BASE].status |= IRQ_LEVEL; | ||
| 404 | } | ||
| 405 | |||
| 406 | init_pci_source(); | ||
| 407 | } | ||
| 408 | |||
| 409 | int tsi108_irq_cascade(struct pt_regs *regs, void *unused) | ||
| 410 | { | ||
| 411 | return get_pci_source(); | ||
| 412 | } | ||
