Commit 24665cd00dd06c741ef6268515ca9e6d3cda5c57
Exists in
master
and in
4 other branches
Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/paulus/ppc64-2.6
Showing 44 changed files Side-by-side Diff
- MAINTAINERS
- arch/ppc64/Kconfig
- arch/ppc64/Makefile
- arch/ppc64/kernel/Makefile
- arch/ppc64/kernel/bpa_iic.c
- arch/ppc64/kernel/bpa_iic.h
- arch/ppc64/kernel/bpa_iommu.c
- arch/ppc64/kernel/bpa_iommu.h
- arch/ppc64/kernel/bpa_nvram.c
- arch/ppc64/kernel/bpa_setup.c
- arch/ppc64/kernel/cpu_setup_power4.S
- arch/ppc64/kernel/cputable.c
- arch/ppc64/kernel/iSeries_setup.c
- arch/ppc64/kernel/irq.c
- arch/ppc64/kernel/maple_setup.c
- arch/ppc64/kernel/maple_time.c
- arch/ppc64/kernel/mpic.h
- arch/ppc64/kernel/pSeries_pci.c
- arch/ppc64/kernel/pSeries_setup.c
- arch/ppc64/kernel/pSeries_smp.c
- arch/ppc64/kernel/pci.c
- arch/ppc64/kernel/pci.h
- arch/ppc64/kernel/pmac_time.c
- arch/ppc64/kernel/proc_ppc64.c
- arch/ppc64/kernel/prom_init.c
- arch/ppc64/kernel/rtas-proc.c
- arch/ppc64/kernel/rtas.c
- arch/ppc64/kernel/rtas_pci.c
- arch/ppc64/kernel/rtc.c
- arch/ppc64/kernel/setup.c
- arch/ppc64/kernel/smp.c
- arch/ppc64/kernel/spider-pic.c
- arch/ppc64/kernel/time.c
- arch/ppc64/kernel/traps.c
- drivers/char/watchdog/Kconfig
- drivers/char/watchdog/Makefile
- drivers/char/watchdog/wdrtas.c
- include/asm-ppc64/machdep.h
- include/asm-ppc64/mmu.h
- include/asm-ppc64/nvram.h
- include/asm-ppc64/processor.h
- include/asm-ppc64/rtas.h
- include/asm-ppc64/smp.h
- include/asm-ppc64/time.h
MAINTAINERS
| ... | ... | @@ -504,6 +504,13 @@ |
| 504 | 504 | W: http://sourceforge.net/projects/bonding/ |
| 505 | 505 | S: Supported |
| 506 | 506 | |
| 507 | +BROADBAND PROCESSOR ARCHITECTURE | |
| 508 | +P: Arnd Bergmann | |
| 509 | +M: arnd@arndb.de | |
| 510 | +L: linuxppc64-dev@ozlabs.org | |
| 511 | +W: http://linuxppc64.org | |
| 512 | +S: Supported | |
| 513 | + | |
| 507 | 514 | BTTV VIDEO4LINUX DRIVER |
| 508 | 515 | P: Gerd Knorr |
| 509 | 516 | M: kraxel@bytesex.org |
arch/ppc64/Kconfig
| ... | ... | @@ -77,6 +77,10 @@ |
| 77 | 77 | bool " IBM pSeries & new iSeries" |
| 78 | 78 | default y |
| 79 | 79 | |
| 80 | +config PPC_BPA | |
| 81 | + bool " Broadband Processor Architecture" | |
| 82 | + depends on PPC_MULTIPLATFORM | |
| 83 | + | |
| 80 | 84 | config PPC_PMAC |
| 81 | 85 | depends on PPC_MULTIPLATFORM |
| 82 | 86 | bool " Apple G5 based machines" |
| ... | ... | @@ -106,6 +110,21 @@ |
| 106 | 110 | bool |
| 107 | 111 | default y |
| 108 | 112 | |
| 113 | +config XICS | |
| 114 | + depends on PPC_PSERIES | |
| 115 | + bool | |
| 116 | + default y | |
| 117 | + | |
| 118 | +config MPIC | |
| 119 | + depends on PPC_PSERIES || PPC_PMAC || PPC_MAPLE | |
| 120 | + bool | |
| 121 | + default y | |
| 122 | + | |
| 123 | +config BPA_IIC | |
| 124 | + depends on PPC_BPA | |
| 125 | + bool | |
| 126 | + default y | |
| 127 | + | |
| 109 | 128 | # VMX is pSeries only for now until somebody writes the iSeries |
| 110 | 129 | # exception vectors for it |
| 111 | 130 | config ALTIVEC |
| ... | ... | @@ -292,7 +311,7 @@ |
| 292 | 311 | |
| 293 | 312 | config PPC_RTAS |
| 294 | 313 | bool |
| 295 | - depends on PPC_PSERIES | |
| 314 | + depends on PPC_PSERIES || PPC_BPA | |
| 296 | 315 | default y |
| 297 | 316 | |
| 298 | 317 | config RTAS_PROC |
arch/ppc64/Makefile
| ... | ... | @@ -90,12 +90,14 @@ |
| 90 | 90 | boottarget-$(CONFIG_PPC_PSERIES) := zImage zImage.initrd |
| 91 | 91 | boottarget-$(CONFIG_PPC_MAPLE) := zImage zImage.initrd |
| 92 | 92 | boottarget-$(CONFIG_PPC_ISERIES) := vmlinux.sminitrd vmlinux.initrd vmlinux.sm |
| 93 | +boottarget-$(CONFIG_PPC_BPA) := zImage zImage.initrd | |
| 93 | 94 | $(boottarget-y): vmlinux |
| 94 | 95 | $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ |
| 95 | 96 | |
| 96 | 97 | bootimage-$(CONFIG_PPC_PSERIES) := $(boot)/zImage |
| 97 | 98 | bootimage-$(CONFIG_PPC_PMAC) := vmlinux |
| 98 | 99 | bootimage-$(CONFIG_PPC_MAPLE) := $(boot)/zImage |
| 100 | +bootimage-$(CONFIG_PPC_BPA) := zImage | |
| 99 | 101 | bootimage-$(CONFIG_PPC_ISERIES) := vmlinux |
| 100 | 102 | BOOTIMAGE := $(bootimage-y) |
| 101 | 103 | install: vmlinux |
arch/ppc64/kernel/Makefile
| ... | ... | @@ -27,17 +27,21 @@ |
| 27 | 27 | mf.o HvLpEvent.o iSeries_proc.o iSeries_htab.o \ |
| 28 | 28 | iSeries_iommu.o |
| 29 | 29 | |
| 30 | -obj-$(CONFIG_PPC_MULTIPLATFORM) += nvram.o i8259.o prom_init.o prom.o mpic.o | |
| 30 | +obj-$(CONFIG_PPC_MULTIPLATFORM) += nvram.o i8259.o prom_init.o prom.o | |
| 31 | 31 | |
| 32 | 32 | obj-$(CONFIG_PPC_PSERIES) += pSeries_pci.o pSeries_lpar.o pSeries_hvCall.o \ |
| 33 | 33 | pSeries_nvram.o rtasd.o ras.o pSeries_reconfig.o \ |
| 34 | - xics.o rtas.o pSeries_setup.o pSeries_iommu.o | |
| 34 | + pSeries_setup.o pSeries_iommu.o | |
| 35 | 35 | |
| 36 | +obj-$(CONFIG_PPC_BPA) += bpa_setup.o bpa_iommu.o bpa_nvram.o \ | |
| 37 | + bpa_iic.o spider-pic.o | |
| 38 | + | |
| 36 | 39 | obj-$(CONFIG_EEH) += eeh.o |
| 37 | 40 | obj-$(CONFIG_PROC_FS) += proc_ppc64.o |
| 38 | 41 | obj-$(CONFIG_RTAS_FLASH) += rtas_flash.o |
| 39 | 42 | obj-$(CONFIG_SMP) += smp.o |
| 40 | 43 | obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o |
| 44 | +obj-$(CONFIG_PPC_RTAS) += rtas.o rtas_pci.o | |
| 41 | 45 | obj-$(CONFIG_RTAS_PROC) += rtas-proc.o |
| 42 | 46 | obj-$(CONFIG_SCANLOG) += scanlog.o |
| 43 | 47 | obj-$(CONFIG_VIOPATH) += viopath.o |
| ... | ... | @@ -46,6 +50,8 @@ |
| 46 | 50 | obj-$(CONFIG_BOOTX_TEXT) += btext.o |
| 47 | 51 | obj-$(CONFIG_HVCS) += hvcserver.o |
| 48 | 52 | obj-$(CONFIG_IBMVIO) += vio.o |
| 53 | +obj-$(CONFIG_XICS) += xics.o | |
| 54 | +obj-$(CONFIG_MPIC) += mpic.o | |
| 49 | 55 | |
| 50 | 56 | obj-$(CONFIG_PPC_PMAC) += pmac_setup.o pmac_feature.o pmac_pci.o \ |
| 51 | 57 | pmac_time.o pmac_nvram.o pmac_low_i2c.o |
| ... | ... | @@ -58,6 +64,7 @@ |
| 58 | 64 | obj-$(CONFIG_PPC_PMAC) += pmac_smp.o smp-tbsync.o |
| 59 | 65 | obj-$(CONFIG_PPC_ISERIES) += iSeries_smp.o |
| 60 | 66 | obj-$(CONFIG_PPC_PSERIES) += pSeries_smp.o |
| 67 | +obj-$(CONFIG_PPC_BPA) += pSeries_smp.o | |
| 61 | 68 | obj-$(CONFIG_PPC_MAPLE) += smp-tbsync.o |
| 62 | 69 | endif |
| 63 | 70 |
arch/ppc64/kernel/bpa_iic.c
| 1 | +/* | |
| 2 | + * BPA Internal Interrupt Controller | |
| 3 | + * | |
| 4 | + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 | |
| 5 | + * | |
| 6 | + * Author: Arnd Bergmann <arndb@de.ibm.com> | |
| 7 | + * | |
| 8 | + * This program is free software; you can redistribute it and/or modify | |
| 9 | + * it under the terms of the GNU General Public License as published by | |
| 10 | + * the Free Software Foundation; either version 2, or (at your option) | |
| 11 | + * any later version. | |
| 12 | + * | |
| 13 | + * This program is distributed in the hope that it will be useful, | |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 16 | + * GNU General Public License for more details. | |
| 17 | + * | |
| 18 | + * You should have received a copy of the GNU General Public License | |
| 19 | + * along with this program; if not, write to the Free Software | |
| 20 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 21 | + */ | |
| 22 | + | |
| 23 | +#include <linux/config.h> | |
| 24 | +#include <linux/interrupt.h> | |
| 25 | +#include <linux/irq.h> | |
| 26 | +#include <linux/percpu.h> | |
| 27 | +#include <linux/types.h> | |
| 28 | + | |
| 29 | +#include <asm/io.h> | |
| 30 | +#include <asm/pgtable.h> | |
| 31 | +#include <asm/prom.h> | |
| 32 | +#include <asm/ptrace.h> | |
| 33 | + | |
| 34 | +#include "bpa_iic.h" | |
| 35 | + | |
| 36 | +struct iic_pending_bits { | |
| 37 | + u32 data; | |
| 38 | + u8 flags; | |
| 39 | + u8 class; | |
| 40 | + u8 source; | |
| 41 | + u8 prio; | |
| 42 | +}; | |
| 43 | + | |
| 44 | +enum iic_pending_flags { | |
| 45 | + IIC_VALID = 0x80, | |
| 46 | + IIC_IPI = 0x40, | |
| 47 | +}; | |
| 48 | + | |
| 49 | +struct iic_regs { | |
| 50 | + struct iic_pending_bits pending; | |
| 51 | + struct iic_pending_bits pending_destr; | |
| 52 | + u64 generate; | |
| 53 | + u64 prio; | |
| 54 | +}; | |
| 55 | + | |
| 56 | +struct iic { | |
| 57 | + struct iic_regs __iomem *regs; | |
| 58 | +}; | |
| 59 | + | |
| 60 | +static DEFINE_PER_CPU(struct iic, iic); | |
| 61 | + | |
| 62 | +void iic_local_enable(void) | |
| 63 | +{ | |
| 64 | + out_be64(&__get_cpu_var(iic).regs->prio, 0xff); | |
| 65 | +} | |
| 66 | + | |
| 67 | +void iic_local_disable(void) | |
| 68 | +{ | |
| 69 | + out_be64(&__get_cpu_var(iic).regs->prio, 0x0); | |
| 70 | +} | |
| 71 | + | |
| 72 | +static unsigned int iic_startup(unsigned int irq) | |
| 73 | +{ | |
| 74 | + return 0; | |
| 75 | +} | |
| 76 | + | |
| 77 | +static void iic_enable(unsigned int irq) | |
| 78 | +{ | |
| 79 | + iic_local_enable(); | |
| 80 | +} | |
| 81 | + | |
| 82 | +static void iic_disable(unsigned int irq) | |
| 83 | +{ | |
| 84 | +} | |
| 85 | + | |
| 86 | +static void iic_end(unsigned int irq) | |
| 87 | +{ | |
| 88 | + iic_local_enable(); | |
| 89 | +} | |
| 90 | + | |
| 91 | +static struct hw_interrupt_type iic_pic = { | |
| 92 | + .typename = " BPA-IIC ", | |
| 93 | + .startup = iic_startup, | |
| 94 | + .enable = iic_enable, | |
| 95 | + .disable = iic_disable, | |
| 96 | + .end = iic_end, | |
| 97 | +}; | |
| 98 | + | |
| 99 | +static int iic_external_get_irq(struct iic_pending_bits pending) | |
| 100 | +{ | |
| 101 | + int irq; | |
| 102 | + unsigned char node, unit; | |
| 103 | + | |
| 104 | + node = pending.source >> 4; | |
| 105 | + unit = pending.source & 0xf; | |
| 106 | + irq = -1; | |
| 107 | + | |
| 108 | + /* | |
| 109 | + * This mapping is specific to the Broadband | |
| 110 | + * Engine. We might need to get the numbers | |
| 111 | + * from the device tree to support future CPUs. | |
| 112 | + */ | |
| 113 | + switch (unit) { | |
| 114 | + case 0x00: | |
| 115 | + case 0x0b: | |
| 116 | + /* | |
| 117 | + * One of these units can be connected | |
| 118 | + * to an external interrupt controller. | |
| 119 | + */ | |
| 120 | + if (pending.prio > 0x3f || | |
| 121 | + pending.class != 2) | |
| 122 | + break; | |
| 123 | + irq = IIC_EXT_OFFSET | |
| 124 | + + spider_get_irq(pending.prio + node * IIC_NODE_STRIDE) | |
| 125 | + + node * IIC_NODE_STRIDE; | |
| 126 | + break; | |
| 127 | + case 0x01 ... 0x04: | |
| 128 | + case 0x07 ... 0x0a: | |
| 129 | + /* | |
| 130 | + * These units are connected to the SPEs | |
| 131 | + */ | |
| 132 | + if (pending.class > 2) | |
| 133 | + break; | |
| 134 | + irq = IIC_SPE_OFFSET | |
| 135 | + + pending.class * IIC_CLASS_STRIDE | |
| 136 | + + node * IIC_NODE_STRIDE | |
| 137 | + + unit; | |
| 138 | + break; | |
| 139 | + } | |
| 140 | + if (irq == -1) | |
| 141 | + printk(KERN_WARNING "Unexpected interrupt class %02x, " | |
| 142 | + "source %02x, prio %02x, cpu %02x\n", pending.class, | |
| 143 | + pending.source, pending.prio, smp_processor_id()); | |
| 144 | + return irq; | |
| 145 | +} | |
| 146 | + | |
| 147 | +/* Get an IRQ number from the pending state register of the IIC */ | |
| 148 | +int iic_get_irq(struct pt_regs *regs) | |
| 149 | +{ | |
| 150 | + struct iic *iic; | |
| 151 | + int irq; | |
| 152 | + struct iic_pending_bits pending; | |
| 153 | + | |
| 154 | + iic = &__get_cpu_var(iic); | |
| 155 | + *(unsigned long *) &pending = | |
| 156 | + in_be64((unsigned long __iomem *) &iic->regs->pending_destr); | |
| 157 | + | |
| 158 | + irq = -1; | |
| 159 | + if (pending.flags & IIC_VALID) { | |
| 160 | + if (pending.flags & IIC_IPI) { | |
| 161 | + irq = IIC_IPI_OFFSET + (pending.prio >> 4); | |
| 162 | +/* | |
| 163 | + if (irq > 0x80) | |
| 164 | + printk(KERN_WARNING "Unexpected IPI prio %02x" | |
| 165 | + "on CPU %02x\n", pending.prio, | |
| 166 | + smp_processor_id()); | |
| 167 | +*/ | |
| 168 | + } else { | |
| 169 | + irq = iic_external_get_irq(pending); | |
| 170 | + } | |
| 171 | + } | |
| 172 | + return irq; | |
| 173 | +} | |
| 174 | + | |
| 175 | +static struct iic_regs __iomem *find_iic(int cpu) | |
| 176 | +{ | |
| 177 | + struct device_node *np; | |
| 178 | + int nodeid = cpu / 2; | |
| 179 | + unsigned long regs; | |
| 180 | + struct iic_regs __iomem *iic_regs; | |
| 181 | + | |
| 182 | + for (np = of_find_node_by_type(NULL, "cpu"); | |
| 183 | + np; | |
| 184 | + np = of_find_node_by_type(np, "cpu")) { | |
| 185 | + if (nodeid == *(int *)get_property(np, "node-id", NULL)) | |
| 186 | + break; | |
| 187 | + } | |
| 188 | + | |
| 189 | + if (!np) { | |
| 190 | + printk(KERN_WARNING "IIC: CPU %d not found\n", cpu); | |
| 191 | + iic_regs = NULL; | |
| 192 | + } else { | |
| 193 | + regs = *(long *)get_property(np, "iic", NULL); | |
| 194 | + | |
| 195 | + /* hack until we have decided on the devtree info */ | |
| 196 | + regs += 0x400; | |
| 197 | + if (cpu & 1) | |
| 198 | + regs += 0x20; | |
| 199 | + | |
| 200 | + printk(KERN_DEBUG "IIC for CPU %d at %lx\n", cpu, regs); | |
| 201 | + iic_regs = __ioremap(regs, sizeof(struct iic_regs), | |
| 202 | + _PAGE_NO_CACHE); | |
| 203 | + } | |
| 204 | + return iic_regs; | |
| 205 | +} | |
| 206 | + | |
| 207 | +#ifdef CONFIG_SMP | |
| 208 | +void iic_setup_cpu(void) | |
| 209 | +{ | |
| 210 | + out_be64(&__get_cpu_var(iic).regs->prio, 0xff); | |
| 211 | +} | |
| 212 | + | |
| 213 | +void iic_cause_IPI(int cpu, int mesg) | |
| 214 | +{ | |
| 215 | + out_be64(&per_cpu(iic, cpu).regs->generate, mesg); | |
| 216 | +} | |
| 217 | + | |
| 218 | +static irqreturn_t iic_ipi_action(int irq, void *dev_id, struct pt_regs *regs) | |
| 219 | +{ | |
| 220 | + | |
| 221 | + smp_message_recv(irq - IIC_IPI_OFFSET, regs); | |
| 222 | + return IRQ_HANDLED; | |
| 223 | +} | |
| 224 | + | |
| 225 | +static void iic_request_ipi(int irq, const char *name) | |
| 226 | +{ | |
| 227 | + /* IPIs are marked SA_INTERRUPT as they must run with irqs | |
| 228 | + * disabled */ | |
| 229 | + get_irq_desc(irq)->handler = &iic_pic; | |
| 230 | + get_irq_desc(irq)->status |= IRQ_PER_CPU; | |
| 231 | + request_irq(irq, iic_ipi_action, SA_INTERRUPT, name, NULL); | |
| 232 | +} | |
| 233 | + | |
| 234 | +void iic_request_IPIs(void) | |
| 235 | +{ | |
| 236 | + iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_CALL_FUNCTION, "IPI-call"); | |
| 237 | + iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_RESCHEDULE, "IPI-resched"); | |
| 238 | +#ifdef CONFIG_DEBUGGER | |
| 239 | + iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_DEBUGGER_BREAK, "IPI-debug"); | |
| 240 | +#endif /* CONFIG_DEBUGGER */ | |
| 241 | +} | |
| 242 | +#endif /* CONFIG_SMP */ | |
| 243 | + | |
| 244 | +static void iic_setup_spe_handlers(void) | |
| 245 | +{ | |
| 246 | + int be, isrc; | |
| 247 | + | |
| 248 | + /* Assume two threads per BE are present */ | |
| 249 | + for (be=0; be < num_present_cpus() / 2; be++) { | |
| 250 | + for (isrc = 0; isrc < IIC_CLASS_STRIDE * 3; isrc++) { | |
| 251 | + int irq = IIC_NODE_STRIDE * be + IIC_SPE_OFFSET + isrc; | |
| 252 | + get_irq_desc(irq)->handler = &iic_pic; | |
| 253 | + } | |
| 254 | + } | |
| 255 | +} | |
| 256 | + | |
| 257 | +void iic_init_IRQ(void) | |
| 258 | +{ | |
| 259 | + int cpu, irq_offset; | |
| 260 | + struct iic *iic; | |
| 261 | + | |
| 262 | + irq_offset = 0; | |
| 263 | + for_each_cpu(cpu) { | |
| 264 | + iic = &per_cpu(iic, cpu); | |
| 265 | + iic->regs = find_iic(cpu); | |
| 266 | + if (iic->regs) | |
| 267 | + out_be64(&iic->regs->prio, 0xff); | |
| 268 | + } | |
| 269 | + iic_setup_spe_handlers(); | |
| 270 | +} |
arch/ppc64/kernel/bpa_iic.h
| 1 | +#ifndef ASM_BPA_IIC_H | |
| 2 | +#define ASM_BPA_IIC_H | |
| 3 | +#ifdef __KERNEL__ | |
| 4 | +/* | |
| 5 | + * Mapping of IIC pending bits into per-node | |
| 6 | + * interrupt numbers. | |
| 7 | + * | |
| 8 | + * IRQ FF CC SS PP FF CC SS PP Description | |
| 9 | + * | |
| 10 | + * 00-3f 80 02 +0 00 - 80 02 +0 3f South Bridge | |
| 11 | + * 00-3f 80 02 +b 00 - 80 02 +b 3f South Bridge | |
| 12 | + * 41-4a 80 00 +1 ** - 80 00 +a ** SPU Class 0 | |
| 13 | + * 51-5a 80 01 +1 ** - 80 01 +a ** SPU Class 1 | |
| 14 | + * 61-6a 80 02 +1 ** - 80 02 +a ** SPU Class 2 | |
| 15 | + * 70-7f C0 ** ** 00 - C0 ** ** 0f IPI | |
| 16 | + * | |
| 17 | + * F flags | |
| 18 | + * C class | |
| 19 | + * S source | |
| 20 | + * P Priority | |
| 21 | + * + node number | |
| 22 | + * * don't care | |
| 23 | + * | |
| 24 | + * A node consists of a Broadband Engine and an optional | |
| 25 | + * south bridge device providing a maximum of 64 IRQs. | |
| 26 | + * The south bridge may be connected to either IOIF0 | |
| 27 | + * or IOIF1. | |
| 28 | + * Each SPE is represented as three IRQ lines, one per | |
| 29 | + * interrupt class. | |
| 30 | + * 16 IRQ numbers are reserved for inter processor | |
| 31 | + * interruptions, although these are only used in the | |
| 32 | + * range of the first node. | |
| 33 | + * | |
| 34 | + * This scheme needs 128 IRQ numbers per BIF node ID, | |
| 35 | + * which means that with the total of 512 lines | |
| 36 | + * available, we can have a maximum of four nodes. | |
| 37 | + */ | |
| 38 | + | |
| 39 | +enum { | |
| 40 | + IIC_EXT_OFFSET = 0x00, /* Start of south bridge IRQs */ | |
| 41 | + IIC_NUM_EXT = 0x40, /* Number of south bridge IRQs */ | |
| 42 | + IIC_SPE_OFFSET = 0x40, /* Start of SPE interrupts */ | |
| 43 | + IIC_CLASS_STRIDE = 0x10, /* SPE IRQs per class */ | |
| 44 | + IIC_IPI_OFFSET = 0x70, /* Start of IPI IRQs */ | |
| 45 | + IIC_NUM_IPIS = 0x10, /* IRQs reserved for IPI */ | |
| 46 | + IIC_NODE_STRIDE = 0x80, /* Total IRQs per node */ | |
| 47 | +}; | |
| 48 | + | |
| 49 | +extern void iic_init_IRQ(void); | |
| 50 | +extern int iic_get_irq(struct pt_regs *regs); | |
| 51 | +extern void iic_cause_IPI(int cpu, int mesg); | |
| 52 | +extern void iic_request_IPIs(void); | |
| 53 | +extern void iic_setup_cpu(void); | |
| 54 | +extern void iic_local_enable(void); | |
| 55 | +extern void iic_local_disable(void); | |
| 56 | + | |
| 57 | + | |
| 58 | +extern void spider_init_IRQ(void); | |
| 59 | +extern int spider_get_irq(unsigned long int_pending); | |
| 60 | + | |
| 61 | +#endif | |
| 62 | +#endif /* ASM_BPA_IIC_H */ |
arch/ppc64/kernel/bpa_iommu.c
| 1 | +/* | |
| 2 | + * IOMMU implementation for Broadband Processor Architecture | |
| 3 | + * We just establish a linear mapping at boot by setting all the | |
| 4 | + * IOPT cache entries in the CPU. | |
| 5 | + * The mapping functions should be identical to pci_direct_iommu, | |
| 6 | + * except for the handling of the high order bit that is required | |
| 7 | + * by the Spider bridge. These should be split into a separate | |
| 8 | + * file at the point where we get a different bridge chip. | |
| 9 | + * | |
| 10 | + * Copyright (C) 2005 IBM Deutschland Entwicklung GmbH, | |
| 11 | + * Arnd Bergmann <arndb@de.ibm.com> | |
| 12 | + * | |
| 13 | + * Based on linear mapping | |
| 14 | + * Copyright (C) 2003 Benjamin Herrenschmidt (benh@kernel.crashing.org) | |
| 15 | + * | |
| 16 | + * This program is free software; you can redistribute it and/or | |
| 17 | + * modify it under the terms of the GNU General Public License | |
| 18 | + * as published by the Free Software Foundation; either version | |
| 19 | + * 2 of the License, or (at your option) any later version. | |
| 20 | + */ | |
| 21 | + | |
| 22 | +#undef DEBUG | |
| 23 | + | |
| 24 | +#include <linux/kernel.h> | |
| 25 | +#include <linux/pci.h> | |
| 26 | +#include <linux/delay.h> | |
| 27 | +#include <linux/string.h> | |
| 28 | +#include <linux/init.h> | |
| 29 | +#include <linux/bootmem.h> | |
| 30 | +#include <linux/mm.h> | |
| 31 | +#include <linux/dma-mapping.h> | |
| 32 | + | |
| 33 | +#include <asm/sections.h> | |
| 34 | +#include <asm/iommu.h> | |
| 35 | +#include <asm/io.h> | |
| 36 | +#include <asm/prom.h> | |
| 37 | +#include <asm/pci-bridge.h> | |
| 38 | +#include <asm/machdep.h> | |
| 39 | +#include <asm/pmac_feature.h> | |
| 40 | +#include <asm/abs_addr.h> | |
| 41 | +#include <asm/system.h> | |
| 42 | + | |
| 43 | +#include "pci.h" | |
| 44 | +#include "bpa_iommu.h" | |
| 45 | + | |
| 46 | +static inline unsigned long | |
| 47 | +get_iopt_entry(unsigned long real_address, unsigned long ioid, | |
| 48 | + unsigned long prot) | |
| 49 | +{ | |
| 50 | + return (prot & IOPT_PROT_MASK) | |
| 51 | + | (IOPT_COHERENT) | |
| 52 | + | (IOPT_ORDER_VC) | |
| 53 | + | (real_address & IOPT_RPN_MASK) | |
| 54 | + | (ioid & IOPT_IOID_MASK); | |
| 55 | +} | |
| 56 | + | |
| 57 | +typedef struct { | |
| 58 | + unsigned long val; | |
| 59 | +} ioste; | |
| 60 | + | |
| 61 | +static inline ioste | |
| 62 | +mk_ioste(unsigned long val) | |
| 63 | +{ | |
| 64 | + ioste ioste = { .val = val, }; | |
| 65 | + return ioste; | |
| 66 | +} | |
| 67 | + | |
| 68 | +static inline ioste | |
| 69 | +get_iost_entry(unsigned long iopt_base, unsigned long io_address, unsigned page_size) | |
| 70 | +{ | |
| 71 | + unsigned long ps; | |
| 72 | + unsigned long iostep; | |
| 73 | + unsigned long nnpt; | |
| 74 | + unsigned long shift; | |
| 75 | + | |
| 76 | + switch (page_size) { | |
| 77 | + case 0x1000000: | |
| 78 | + ps = IOST_PS_16M; | |
| 79 | + nnpt = 0; /* one page per segment */ | |
| 80 | + shift = 5; /* segment has 16 iopt entries */ | |
| 81 | + break; | |
| 82 | + | |
| 83 | + case 0x100000: | |
| 84 | + ps = IOST_PS_1M; | |
| 85 | + nnpt = 0; /* one page per segment */ | |
| 86 | + shift = 1; /* segment has 256 iopt entries */ | |
| 87 | + break; | |
| 88 | + | |
| 89 | + case 0x10000: | |
| 90 | + ps = IOST_PS_64K; | |
| 91 | + nnpt = 0x07; /* 8 pages per io page table */ | |
| 92 | + shift = 0; /* all entries are used */ | |
| 93 | + break; | |
| 94 | + | |
| 95 | + case 0x1000: | |
| 96 | + ps = IOST_PS_4K; | |
| 97 | + nnpt = 0x7f; /* 128 pages per io page table */ | |
| 98 | + shift = 0; /* all entries are used */ | |
| 99 | + break; | |
| 100 | + | |
| 101 | + default: /* not a known compile time constant */ | |
| 102 | + BUILD_BUG_ON(1); | |
| 103 | + break; | |
| 104 | + } | |
| 105 | + | |
| 106 | + iostep = iopt_base + | |
| 107 | + /* need 8 bytes per iopte */ | |
| 108 | + (((io_address / page_size * 8) | |
| 109 | + /* align io page tables on 4k page boundaries */ | |
| 110 | + << shift) | |
| 111 | + /* nnpt+1 pages go into each iopt */ | |
| 112 | + & ~(nnpt << 12)); | |
| 113 | + | |
| 114 | + nnpt++; /* this seems to work, but the documentation is not clear | |
| 115 | + about wether we put nnpt or nnpt-1 into the ioste bits. | |
| 116 | + In theory, this can't work for 4k pages. */ | |
| 117 | + return mk_ioste(IOST_VALID_MASK | |
| 118 | + | (iostep & IOST_PT_BASE_MASK) | |
| 119 | + | ((nnpt << 5) & IOST_NNPT_MASK) | |
| 120 | + | (ps & IOST_PS_MASK)); | |
| 121 | +} | |
| 122 | + | |
| 123 | +/* compute the address of an io pte */ | |
| 124 | +static inline unsigned long | |
| 125 | +get_ioptep(ioste iost_entry, unsigned long io_address) | |
| 126 | +{ | |
| 127 | + unsigned long iopt_base; | |
| 128 | + unsigned long page_size; | |
| 129 | + unsigned long page_number; | |
| 130 | + unsigned long iopt_offset; | |
| 131 | + | |
| 132 | + iopt_base = iost_entry.val & IOST_PT_BASE_MASK; | |
| 133 | + page_size = iost_entry.val & IOST_PS_MASK; | |
| 134 | + | |
| 135 | + /* decode page size to compute page number */ | |
| 136 | + page_number = (io_address & 0x0fffffff) >> (10 + 2 * page_size); | |
| 137 | + /* page number is an offset into the io page table */ | |
| 138 | + iopt_offset = (page_number << 3) & 0x7fff8ul; | |
| 139 | + return iopt_base + iopt_offset; | |
| 140 | +} | |
| 141 | + | |
| 142 | +/* compute the tag field of the iopt cache entry */ | |
| 143 | +static inline unsigned long | |
| 144 | +get_ioc_tag(ioste iost_entry, unsigned long io_address) | |
| 145 | +{ | |
| 146 | + unsigned long iopte = get_ioptep(iost_entry, io_address); | |
| 147 | + | |
| 148 | + return IOPT_VALID_MASK | |
| 149 | + | ((iopte & 0x00000000000000ff8ul) >> 3) | |
| 150 | + | ((iopte & 0x0000003fffffc0000ul) >> 9); | |
| 151 | +} | |
| 152 | + | |
| 153 | +/* compute the hashed 6 bit index for the 4-way associative pte cache */ | |
| 154 | +static inline unsigned long | |
| 155 | +get_ioc_hash(ioste iost_entry, unsigned long io_address) | |
| 156 | +{ | |
| 157 | + unsigned long iopte = get_ioptep(iost_entry, io_address); | |
| 158 | + | |
| 159 | + return ((iopte & 0x000000000000001f8ul) >> 3) | |
| 160 | + ^ ((iopte & 0x00000000000020000ul) >> 17) | |
| 161 | + ^ ((iopte & 0x00000000000010000ul) >> 15) | |
| 162 | + ^ ((iopte & 0x00000000000008000ul) >> 13) | |
| 163 | + ^ ((iopte & 0x00000000000004000ul) >> 11) | |
| 164 | + ^ ((iopte & 0x00000000000002000ul) >> 9) | |
| 165 | + ^ ((iopte & 0x00000000000001000ul) >> 7); | |
| 166 | +} | |
| 167 | + | |
| 168 | +/* same as above, but pretend that we have a simpler 1-way associative | |
| 169 | + pte cache with an 8 bit index */ | |
| 170 | +static inline unsigned long | |
| 171 | +get_ioc_hash_1way(ioste iost_entry, unsigned long io_address) | |
| 172 | +{ | |
| 173 | + unsigned long iopte = get_ioptep(iost_entry, io_address); | |
| 174 | + | |
| 175 | + return ((iopte & 0x000000000000001f8ul) >> 3) | |
| 176 | + ^ ((iopte & 0x00000000000020000ul) >> 17) | |
| 177 | + ^ ((iopte & 0x00000000000010000ul) >> 15) | |
| 178 | + ^ ((iopte & 0x00000000000008000ul) >> 13) | |
| 179 | + ^ ((iopte & 0x00000000000004000ul) >> 11) | |
| 180 | + ^ ((iopte & 0x00000000000002000ul) >> 9) | |
| 181 | + ^ ((iopte & 0x00000000000001000ul) >> 7) | |
| 182 | + ^ ((iopte & 0x0000000000000c000ul) >> 8); | |
| 183 | +} | |
| 184 | + | |
| 185 | +static inline ioste | |
| 186 | +get_iost_cache(void __iomem *base, unsigned long index) | |
| 187 | +{ | |
| 188 | + unsigned long __iomem *p = (base + IOC_ST_CACHE_DIR); | |
| 189 | + return mk_ioste(in_be64(&p[index])); | |
| 190 | +} | |
| 191 | + | |
| 192 | +static inline void | |
| 193 | +set_iost_cache(void __iomem *base, unsigned long index, ioste ste) | |
| 194 | +{ | |
| 195 | + unsigned long __iomem *p = (base + IOC_ST_CACHE_DIR); | |
| 196 | + pr_debug("ioste %02lx was %016lx, store %016lx", index, | |
| 197 | + get_iost_cache(base, index).val, ste.val); | |
| 198 | + out_be64(&p[index], ste.val); | |
| 199 | + pr_debug(" now %016lx\n", get_iost_cache(base, index).val); | |
| 200 | +} | |
| 201 | + | |
| 202 | +static inline unsigned long | |
| 203 | +get_iopt_cache(void __iomem *base, unsigned long index, unsigned long *tag) | |
| 204 | +{ | |
| 205 | + unsigned long __iomem *tags = (void *)(base + IOC_PT_CACHE_DIR); | |
| 206 | + unsigned long __iomem *p = (void *)(base + IOC_PT_CACHE_REG); | |
| 207 | + | |
| 208 | + *tag = tags[index]; | |
| 209 | + rmb(); | |
| 210 | + return *p; | |
| 211 | +} | |
| 212 | + | |
| 213 | +static inline void | |
| 214 | +set_iopt_cache(void __iomem *base, unsigned long index, | |
| 215 | + unsigned long tag, unsigned long val) | |
| 216 | +{ | |
| 217 | + unsigned long __iomem *tags = base + IOC_PT_CACHE_DIR; | |
| 218 | + unsigned long __iomem *p = base + IOC_PT_CACHE_REG; | |
| 219 | + pr_debug("iopt %02lx was v%016lx/t%016lx, store v%016lx/t%016lx\n", | |
| 220 | + index, get_iopt_cache(base, index, &oldtag), oldtag, val, tag); | |
| 221 | + | |
| 222 | + out_be64(p, val); | |
| 223 | + out_be64(&tags[index], tag); | |
| 224 | +} | |
| 225 | + | |
| 226 | +static inline void | |
| 227 | +set_iost_origin(void __iomem *base) | |
| 228 | +{ | |
| 229 | + unsigned long __iomem *p = base + IOC_ST_ORIGIN; | |
| 230 | + unsigned long origin = IOSTO_ENABLE | IOSTO_SW; | |
| 231 | + | |
| 232 | + pr_debug("iost_origin %016lx, now %016lx\n", in_be64(p), origin); | |
| 233 | + out_be64(p, origin); | |
| 234 | +} | |
| 235 | + | |
| 236 | +static inline void | |
| 237 | +set_iocmd_config(void __iomem *base) | |
| 238 | +{ | |
| 239 | + unsigned long __iomem *p = base + 0xc00; | |
| 240 | + unsigned long conf; | |
| 241 | + | |
| 242 | + conf = in_be64(p); | |
| 243 | + pr_debug("iost_conf %016lx, now %016lx\n", conf, conf | IOCMD_CONF_TE); | |
| 244 | + out_be64(p, conf | IOCMD_CONF_TE); | |
| 245 | +} | |
| 246 | + | |
| 247 | +/* FIXME: get these from the device tree */ | |
| 248 | +#define ioc_base 0x20000511000ull | |
| 249 | +#define ioc_mmio_base 0x20000510000ull | |
| 250 | +#define ioid 0x48a | |
| 251 | +#define iopt_phys_offset (- 0x20000000) /* We have a 512MB offset from the SB */ | |
| 252 | +#define io_page_size 0x1000000 | |
| 253 | + | |
| 254 | +static unsigned long map_iopt_entry(unsigned long address) | |
| 255 | +{ | |
| 256 | + switch (address >> 20) { | |
| 257 | + case 0x600: | |
| 258 | + address = 0x24020000000ull; /* spider i/o */ | |
| 259 | + break; | |
| 260 | + default: | |
| 261 | + address += iopt_phys_offset; | |
| 262 | + break; | |
| 263 | + } | |
| 264 | + | |
| 265 | + return get_iopt_entry(address, ioid, IOPT_PROT_RW); | |
| 266 | +} | |
| 267 | + | |
| 268 | +static void iommu_bus_setup_null(struct pci_bus *b) { } | |
| 269 | +static void iommu_dev_setup_null(struct pci_dev *d) { } | |
| 270 | + | |
| 271 | +/* initialize the iommu to support a simple linear mapping | |
| 272 | + * for each DMA window used by any device. For now, we | |
| 273 | + * happen to know that there is only one DMA window in use, | |
| 274 | + * starting at iopt_phys_offset. */ | |
| 275 | +static void bpa_map_iommu(void) | |
| 276 | +{ | |
| 277 | + unsigned long address; | |
| 278 | + void __iomem *base; | |
| 279 | + ioste ioste; | |
| 280 | + unsigned long index; | |
| 281 | + | |
| 282 | + base = __ioremap(ioc_base, 0x1000, _PAGE_NO_CACHE); | |
| 283 | + pr_debug("%lx mapped to %p\n", ioc_base, base); | |
| 284 | + set_iocmd_config(base); | |
| 285 | + iounmap(base); | |
| 286 | + | |
| 287 | + base = __ioremap(ioc_mmio_base, 0x1000, _PAGE_NO_CACHE); | |
| 288 | + pr_debug("%lx mapped to %p\n", ioc_mmio_base, base); | |
| 289 | + | |
| 290 | + set_iost_origin(base); | |
| 291 | + | |
| 292 | + for (address = 0; address < 0x100000000ul; address += io_page_size) { | |
| 293 | + ioste = get_iost_entry(0x10000000000ul, address, io_page_size); | |
| 294 | + if ((address & 0xfffffff) == 0) /* segment start */ | |
| 295 | + set_iost_cache(base, address >> 28, ioste); | |
| 296 | + index = get_ioc_hash_1way(ioste, address); | |
| 297 | + pr_debug("addr %08lx, index %02lx, ioste %016lx\n", | |
| 298 | + address, index, ioste.val); | |
| 299 | + set_iopt_cache(base, | |
| 300 | + get_ioc_hash_1way(ioste, address), | |
| 301 | + get_ioc_tag(ioste, address), | |
| 302 | + map_iopt_entry(address)); | |
| 303 | + } | |
| 304 | + iounmap(base); | |
| 305 | +} | |
| 306 | + | |
| 307 | + | |
| 308 | +static void *bpa_alloc_coherent(struct device *hwdev, size_t size, | |
| 309 | + dma_addr_t *dma_handle, unsigned int __nocast flag) | |
| 310 | +{ | |
| 311 | + void *ret; | |
| 312 | + | |
| 313 | + ret = (void *)__get_free_pages(flag, get_order(size)); | |
| 314 | + if (ret != NULL) { | |
| 315 | + memset(ret, 0, size); | |
| 316 | + *dma_handle = virt_to_abs(ret) | BPA_DMA_VALID; | |
| 317 | + } | |
| 318 | + return ret; | |
| 319 | +} | |
| 320 | + | |
| 321 | +static void bpa_free_coherent(struct device *hwdev, size_t size, | |
| 322 | + void *vaddr, dma_addr_t dma_handle) | |
| 323 | +{ | |
| 324 | + free_pages((unsigned long)vaddr, get_order(size)); | |
| 325 | +} | |
| 326 | + | |
| 327 | +static dma_addr_t bpa_map_single(struct device *hwdev, void *ptr, | |
| 328 | + size_t size, enum dma_data_direction direction) | |
| 329 | +{ | |
| 330 | + return virt_to_abs(ptr) | BPA_DMA_VALID; | |
| 331 | +} | |
| 332 | + | |
| 333 | +static void bpa_unmap_single(struct device *hwdev, dma_addr_t dma_addr, | |
| 334 | + size_t size, enum dma_data_direction direction) | |
| 335 | +{ | |
| 336 | +} | |
| 337 | + | |
| 338 | +static int bpa_map_sg(struct device *hwdev, struct scatterlist *sg, | |
| 339 | + int nents, enum dma_data_direction direction) | |
| 340 | +{ | |
| 341 | + int i; | |
| 342 | + | |
| 343 | + for (i = 0; i < nents; i++, sg++) { | |
| 344 | + sg->dma_address = (page_to_phys(sg->page) + sg->offset) | |
| 345 | + | BPA_DMA_VALID; | |
| 346 | + sg->dma_length = sg->length; | |
| 347 | + } | |
| 348 | + | |
| 349 | + return nents; | |
| 350 | +} | |
| 351 | + | |
| 352 | +static void bpa_unmap_sg(struct device *hwdev, struct scatterlist *sg, | |
| 353 | + int nents, enum dma_data_direction direction) | |
| 354 | +{ | |
| 355 | +} | |
| 356 | + | |
| 357 | +static int bpa_dma_supported(struct device *dev, u64 mask) | |
| 358 | +{ | |
| 359 | + return mask < 0x100000000ull; | |
| 360 | +} | |
| 361 | + | |
| 362 | +void bpa_init_iommu(void) | |
| 363 | +{ | |
| 364 | + bpa_map_iommu(); | |
| 365 | + | |
| 366 | + /* Direct I/O, IOMMU off */ | |
| 367 | + ppc_md.iommu_dev_setup = iommu_dev_setup_null; | |
| 368 | + ppc_md.iommu_bus_setup = iommu_bus_setup_null; | |
| 369 | + | |
| 370 | + pci_dma_ops.alloc_coherent = bpa_alloc_coherent; | |
| 371 | + pci_dma_ops.free_coherent = bpa_free_coherent; | |
| 372 | + pci_dma_ops.map_single = bpa_map_single; | |
| 373 | + pci_dma_ops.unmap_single = bpa_unmap_single; | |
| 374 | + pci_dma_ops.map_sg = bpa_map_sg; | |
| 375 | + pci_dma_ops.unmap_sg = bpa_unmap_sg; | |
| 376 | + pci_dma_ops.dma_supported = bpa_dma_supported; | |
| 377 | +} |
arch/ppc64/kernel/bpa_iommu.h
| 1 | +#ifndef BPA_IOMMU_H | |
| 2 | +#define BPA_IOMMU_H | |
| 3 | + | |
| 4 | +/* some constants */ | |
| 5 | +enum { | |
| 6 | + /* segment table entries */ | |
| 7 | + IOST_VALID_MASK = 0x8000000000000000ul, | |
| 8 | + IOST_TAG_MASK = 0x3000000000000000ul, | |
| 9 | + IOST_PT_BASE_MASK = 0x000003fffffff000ul, | |
| 10 | + IOST_NNPT_MASK = 0x0000000000000fe0ul, | |
| 11 | + IOST_PS_MASK = 0x000000000000000ful, | |
| 12 | + | |
| 13 | + IOST_PS_4K = 0x1, | |
| 14 | + IOST_PS_64K = 0x3, | |
| 15 | + IOST_PS_1M = 0x5, | |
| 16 | + IOST_PS_16M = 0x7, | |
| 17 | + | |
| 18 | + /* iopt tag register */ | |
| 19 | + IOPT_VALID_MASK = 0x0000000200000000ul, | |
| 20 | + IOPT_TAG_MASK = 0x00000001fffffffful, | |
| 21 | + | |
| 22 | + /* iopt cache register */ | |
| 23 | + IOPT_PROT_MASK = 0xc000000000000000ul, | |
| 24 | + IOPT_PROT_NONE = 0x0000000000000000ul, | |
| 25 | + IOPT_PROT_READ = 0x4000000000000000ul, | |
| 26 | + IOPT_PROT_WRITE = 0x8000000000000000ul, | |
| 27 | + IOPT_PROT_RW = 0xc000000000000000ul, | |
| 28 | + IOPT_COHERENT = 0x2000000000000000ul, | |
| 29 | + | |
| 30 | + IOPT_ORDER_MASK = 0x1800000000000000ul, | |
| 31 | + /* order access to same IOID/VC on same address */ | |
| 32 | + IOPT_ORDER_ADDR = 0x0800000000000000ul, | |
| 33 | + /* similar, but only after a write access */ | |
| 34 | + IOPT_ORDER_WRITES = 0x1000000000000000ul, | |
| 35 | + /* Order all accesses to same IOID/VC */ | |
| 36 | + IOPT_ORDER_VC = 0x1800000000000000ul, | |
| 37 | + | |
| 38 | + IOPT_RPN_MASK = 0x000003fffffff000ul, | |
| 39 | + IOPT_HINT_MASK = 0x0000000000000800ul, | |
| 40 | + IOPT_IOID_MASK = 0x00000000000007fful, | |
| 41 | + | |
| 42 | + IOSTO_ENABLE = 0x8000000000000000ul, | |
| 43 | + IOSTO_ORIGIN = 0x000003fffffff000ul, | |
| 44 | + IOSTO_HW = 0x0000000000000800ul, | |
| 45 | + IOSTO_SW = 0x0000000000000400ul, | |
| 46 | + | |
| 47 | + IOCMD_CONF_TE = 0x0000800000000000ul, | |
| 48 | + | |
| 49 | + /* memory mapped registers */ | |
| 50 | + IOC_PT_CACHE_DIR = 0x000, | |
| 51 | + IOC_ST_CACHE_DIR = 0x800, | |
| 52 | + IOC_PT_CACHE_REG = 0x910, | |
| 53 | + IOC_ST_ORIGIN = 0x918, | |
| 54 | + IOC_CONF = 0x930, | |
| 55 | + | |
| 56 | + /* The high bit needs to be set on every DMA address, | |
| 57 | + only 2GB are addressable */ | |
| 58 | + BPA_DMA_VALID = 0x80000000, | |
| 59 | + BPA_DMA_MASK = 0x7fffffff, | |
| 60 | +}; | |
| 61 | + | |
| 62 | + | |
| 63 | +void bpa_init_iommu(void); | |
| 64 | + | |
| 65 | +#endif |
arch/ppc64/kernel/bpa_nvram.c
| 1 | +/* | |
| 2 | + * NVRAM for CPBW | |
| 3 | + * | |
| 4 | + * (C) Copyright IBM Corp. 2005 | |
| 5 | + * | |
| 6 | + * Authors : Utz Bacher <utz.bacher@de.ibm.com> | |
| 7 | + * | |
| 8 | + * This program is free software; you can redistribute it and/or modify | |
| 9 | + * it under the terms of the GNU General Public License as published by | |
| 10 | + * the Free Software Foundation; either version 2, or (at your option) | |
| 11 | + * any later version. | |
| 12 | + * | |
| 13 | + * This program is distributed in the hope that it will be useful, | |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 16 | + * GNU General Public License for more details. | |
| 17 | + * | |
| 18 | + * You should have received a copy of the GNU General Public License | |
| 19 | + * along with this program; if not, write to the Free Software | |
| 20 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 21 | + */ | |
| 22 | + | |
| 23 | +#include <linux/fs.h> | |
| 24 | +#include <linux/init.h> | |
| 25 | +#include <linux/kernel.h> | |
| 26 | +#include <linux/spinlock.h> | |
| 27 | +#include <linux/types.h> | |
| 28 | + | |
| 29 | +#include <asm/machdep.h> | |
| 30 | +#include <asm/nvram.h> | |
| 31 | +#include <asm/prom.h> | |
| 32 | + | |
| 33 | +static void __iomem *bpa_nvram_start; | |
| 34 | +static long bpa_nvram_len; | |
| 35 | +static spinlock_t bpa_nvram_lock = SPIN_LOCK_UNLOCKED; | |
| 36 | + | |
| 37 | +static ssize_t bpa_nvram_read(char *buf, size_t count, loff_t *index) | |
| 38 | +{ | |
| 39 | + unsigned long flags; | |
| 40 | + | |
| 41 | + if (*index >= bpa_nvram_len) | |
| 42 | + return 0; | |
| 43 | + if (*index + count > bpa_nvram_len) | |
| 44 | + count = bpa_nvram_len - *index; | |
| 45 | + | |
| 46 | + spin_lock_irqsave(&bpa_nvram_lock, flags); | |
| 47 | + | |
| 48 | + memcpy_fromio(buf, bpa_nvram_start + *index, count); | |
| 49 | + | |
| 50 | + spin_unlock_irqrestore(&bpa_nvram_lock, flags); | |
| 51 | + | |
| 52 | + *index += count; | |
| 53 | + return count; | |
| 54 | +} | |
| 55 | + | |
| 56 | +static ssize_t bpa_nvram_write(char *buf, size_t count, loff_t *index) | |
| 57 | +{ | |
| 58 | + unsigned long flags; | |
| 59 | + | |
| 60 | + if (*index >= bpa_nvram_len) | |
| 61 | + return 0; | |
| 62 | + if (*index + count > bpa_nvram_len) | |
| 63 | + count = bpa_nvram_len - *index; | |
| 64 | + | |
| 65 | + spin_lock_irqsave(&bpa_nvram_lock, flags); | |
| 66 | + | |
| 67 | + memcpy_toio(bpa_nvram_start + *index, buf, count); | |
| 68 | + | |
| 69 | + spin_unlock_irqrestore(&bpa_nvram_lock, flags); | |
| 70 | + | |
| 71 | + *index += count; | |
| 72 | + return count; | |
| 73 | +} | |
| 74 | + | |
| 75 | +static ssize_t bpa_nvram_get_size(void) | |
| 76 | +{ | |
| 77 | + return bpa_nvram_len; | |
| 78 | +} | |
| 79 | + | |
| 80 | +int __init bpa_nvram_init(void) | |
| 81 | +{ | |
| 82 | + struct device_node *nvram_node; | |
| 83 | + unsigned long *buffer; | |
| 84 | + int proplen; | |
| 85 | + unsigned long nvram_addr; | |
| 86 | + int ret; | |
| 87 | + | |
| 88 | + ret = -ENODEV; | |
| 89 | + nvram_node = of_find_node_by_type(NULL, "nvram"); | |
| 90 | + if (!nvram_node) | |
| 91 | + goto out; | |
| 92 | + | |
| 93 | + ret = -EIO; | |
| 94 | + buffer = (unsigned long *)get_property(nvram_node, "reg", &proplen); | |
| 95 | + if (proplen != 2*sizeof(unsigned long)) | |
| 96 | + goto out; | |
| 97 | + | |
| 98 | + ret = -ENODEV; | |
| 99 | + nvram_addr = buffer[0]; | |
| 100 | + bpa_nvram_len = buffer[1]; | |
| 101 | + if ( (!bpa_nvram_len) || (!nvram_addr) ) | |
| 102 | + goto out; | |
| 103 | + | |
| 104 | + bpa_nvram_start = ioremap(nvram_addr, bpa_nvram_len); | |
| 105 | + if (!bpa_nvram_start) | |
| 106 | + goto out; | |
| 107 | + | |
| 108 | + printk(KERN_INFO "BPA NVRAM, %luk mapped to %p\n", | |
| 109 | + bpa_nvram_len >> 10, bpa_nvram_start); | |
| 110 | + | |
| 111 | + ppc_md.nvram_read = bpa_nvram_read; | |
| 112 | + ppc_md.nvram_write = bpa_nvram_write; | |
| 113 | + ppc_md.nvram_size = bpa_nvram_get_size; | |
| 114 | + | |
| 115 | +out: | |
| 116 | + of_node_put(nvram_node); | |
| 117 | + return ret; | |
| 118 | +} |
arch/ppc64/kernel/bpa_setup.c
| 1 | +/* | |
| 2 | + * linux/arch/ppc/kernel/bpa_setup.c | |
| 3 | + * | |
| 4 | + * Copyright (C) 1995 Linus Torvalds | |
| 5 | + * Adapted from 'alpha' version by Gary Thomas | |
| 6 | + * Modified by Cort Dougan (cort@cs.nmt.edu) | |
| 7 | + * Modified by PPC64 Team, IBM Corp | |
| 8 | + * Modified by BPA Team, IBM Deutschland Entwicklung GmbH | |
| 9 | + * | |
| 10 | + * This program is free software; you can redistribute it and/or | |
| 11 | + * modify it under the terms of the GNU General Public License | |
| 12 | + * as published by the Free Software Foundation; either version | |
| 13 | + * 2 of the License, or (at your option) any later version. | |
| 14 | + */ | |
| 15 | +#undef DEBUG | |
| 16 | + | |
| 17 | +#include <linux/config.h> | |
| 18 | +#include <linux/sched.h> | |
| 19 | +#include <linux/kernel.h> | |
| 20 | +#include <linux/mm.h> | |
| 21 | +#include <linux/stddef.h> | |
| 22 | +#include <linux/unistd.h> | |
| 23 | +#include <linux/slab.h> | |
| 24 | +#include <linux/user.h> | |
| 25 | +#include <linux/reboot.h> | |
| 26 | +#include <linux/init.h> | |
| 27 | +#include <linux/delay.h> | |
| 28 | +#include <linux/irq.h> | |
| 29 | +#include <linux/seq_file.h> | |
| 30 | +#include <linux/root_dev.h> | |
| 31 | +#include <linux/console.h> | |
| 32 | + | |
| 33 | +#include <asm/mmu.h> | |
| 34 | +#include <asm/processor.h> | |
| 35 | +#include <asm/io.h> | |
| 36 | +#include <asm/pgtable.h> | |
| 37 | +#include <asm/prom.h> | |
| 38 | +#include <asm/rtas.h> | |
| 39 | +#include <asm/pci-bridge.h> | |
| 40 | +#include <asm/iommu.h> | |
| 41 | +#include <asm/dma.h> | |
| 42 | +#include <asm/machdep.h> | |
| 43 | +#include <asm/time.h> | |
| 44 | +#include <asm/nvram.h> | |
| 45 | +#include <asm/cputable.h> | |
| 46 | + | |
| 47 | +#include "pci.h" | |
| 48 | +#include "bpa_iic.h" | |
| 49 | +#include "bpa_iommu.h" | |
| 50 | + | |
| 51 | +#ifdef DEBUG | |
| 52 | +#define DBG(fmt...) udbg_printf(fmt) | |
| 53 | +#else | |
| 54 | +#define DBG(fmt...) | |
| 55 | +#endif | |
| 56 | + | |
| 57 | +void bpa_get_cpuinfo(struct seq_file *m) | |
| 58 | +{ | |
| 59 | + struct device_node *root; | |
| 60 | + const char *model = ""; | |
| 61 | + | |
| 62 | + root = of_find_node_by_path("/"); | |
| 63 | + if (root) | |
| 64 | + model = get_property(root, "model", NULL); | |
| 65 | + seq_printf(m, "machine\t\t: BPA %s\n", model); | |
| 66 | + of_node_put(root); | |
| 67 | +} | |
| 68 | + | |
| 69 | +static void bpa_progress(char *s, unsigned short hex) | |
| 70 | +{ | |
| 71 | + printk("*** %04x : %s\n", hex, s ? s : ""); | |
| 72 | +} | |
| 73 | + | |
| 74 | +static void __init bpa_setup_arch(void) | |
| 75 | +{ | |
| 76 | + ppc_md.init_IRQ = iic_init_IRQ; | |
| 77 | + ppc_md.get_irq = iic_get_irq; | |
| 78 | + | |
| 79 | +#ifdef CONFIG_SMP | |
| 80 | + smp_init_pSeries(); | |
| 81 | +#endif | |
| 82 | + | |
| 83 | + /* init to some ~sane value until calibrate_delay() runs */ | |
| 84 | + loops_per_jiffy = 50000000; | |
| 85 | + | |
| 86 | + if (ROOT_DEV == 0) { | |
| 87 | + printk("No ramdisk, default root is /dev/hda2\n"); | |
| 88 | + ROOT_DEV = Root_HDA2; | |
| 89 | + } | |
| 90 | + | |
| 91 | + /* Find and initialize PCI host bridges */ | |
| 92 | + init_pci_config_tokens(); | |
| 93 | + find_and_init_phbs(); | |
| 94 | + spider_init_IRQ(); | |
| 95 | +#ifdef CONFIG_DUMMY_CONSOLE | |
| 96 | + conswitchp = &dummy_con; | |
| 97 | +#endif | |
| 98 | + | |
| 99 | + bpa_nvram_init(); | |
| 100 | +} | |
| 101 | + | |
| 102 | +/* | |
| 103 | + * Early initialization. Relocation is on but do not reference unbolted pages | |
| 104 | + */ | |
| 105 | +static void __init bpa_init_early(void) | |
| 106 | +{ | |
| 107 | + DBG(" -> bpa_init_early()\n"); | |
| 108 | + | |
| 109 | + hpte_init_native(); | |
| 110 | + | |
| 111 | + bpa_init_iommu(); | |
| 112 | + | |
| 113 | + ppc64_interrupt_controller = IC_BPA_IIC; | |
| 114 | + | |
| 115 | + DBG(" <- bpa_init_early()\n"); | |
| 116 | +} | |
| 117 | + | |
| 118 | + | |
| 119 | +static int __init bpa_probe(int platform) | |
| 120 | +{ | |
| 121 | + if (platform != PLATFORM_BPA) | |
| 122 | + return 0; | |
| 123 | + | |
| 124 | + return 1; | |
| 125 | +} | |
| 126 | + | |
| 127 | +struct machdep_calls __initdata bpa_md = { | |
| 128 | + .probe = bpa_probe, | |
| 129 | + .setup_arch = bpa_setup_arch, | |
| 130 | + .init_early = bpa_init_early, | |
| 131 | + .get_cpuinfo = bpa_get_cpuinfo, | |
| 132 | + .restart = rtas_restart, | |
| 133 | + .power_off = rtas_power_off, | |
| 134 | + .halt = rtas_halt, | |
| 135 | + .get_boot_time = rtas_get_boot_time, | |
| 136 | + .get_rtc_time = rtas_get_rtc_time, | |
| 137 | + .set_rtc_time = rtas_set_rtc_time, | |
| 138 | + .calibrate_decr = generic_calibrate_decr, | |
| 139 | + .progress = bpa_progress, | |
| 140 | +}; |
arch/ppc64/kernel/cpu_setup_power4.S
| ... | ... | @@ -73,7 +73,21 @@ |
| 73 | 73 | |
| 74 | 74 | _GLOBAL(__setup_cpu_power4) |
| 75 | 75 | blr |
| 76 | - | |
| 76 | + | |
| 77 | +_GLOBAL(__setup_cpu_be) | |
| 78 | + /* Set large page sizes LP=0: 16MB, LP=1: 64KB */ | |
| 79 | + addi r3, 0, 0 | |
| 80 | + ori r3, r3, HID6_LB | |
| 81 | + sldi r3, r3, 32 | |
| 82 | + nor r3, r3, r3 | |
| 83 | + mfspr r4, SPRN_HID6 | |
| 84 | + and r4, r4, r3 | |
| 85 | + addi r3, 0, 0x02000 | |
| 86 | + sldi r3, r3, 32 | |
| 87 | + or r4, r4, r3 | |
| 88 | + mtspr SPRN_HID6, r4 | |
| 89 | + blr | |
| 90 | + | |
| 77 | 91 | _GLOBAL(__setup_cpu_ppc970) |
| 78 | 92 | mfspr r0,SPRN_HID0 |
| 79 | 93 | li r11,5 /* clear DOZE and SLEEP */ |
arch/ppc64/kernel/cputable.c
| ... | ... | @@ -34,6 +34,7 @@ |
| 34 | 34 | extern void __setup_cpu_power3(unsigned long offset, struct cpu_spec* spec); |
| 35 | 35 | extern void __setup_cpu_power4(unsigned long offset, struct cpu_spec* spec); |
| 36 | 36 | extern void __setup_cpu_ppc970(unsigned long offset, struct cpu_spec* spec); |
| 37 | +extern void __setup_cpu_be(unsigned long offset, struct cpu_spec* spec); | |
| 37 | 38 | |
| 38 | 39 | |
| 39 | 40 | /* We only set the altivec features if the kernel was compiled with altivec |
| ... | ... | @@ -160,6 +161,16 @@ |
| 160 | 161 | COMMON_USER_PPC64, |
| 161 | 162 | 128, 128, |
| 162 | 163 | __setup_cpu_power4, |
| 164 | + COMMON_PPC64_FW | |
| 165 | + }, | |
| 166 | + { /* BE DD1.x */ | |
| 167 | + 0xffff0000, 0x00700000, "Broadband Engine", | |
| 168 | + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE | | |
| 169 | + CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_ALTIVEC_COMP | | |
| 170 | + CPU_FTR_SMT, | |
| 171 | + COMMON_USER_PPC64 | PPC_FEATURE_HAS_ALTIVEC_COMP, | |
| 172 | + 128, 128, | |
| 173 | + __setup_cpu_be, | |
| 163 | 174 | COMMON_PPC64_FW |
| 164 | 175 | }, |
| 165 | 176 | { /* default match */ |
arch/ppc64/kernel/iSeries_setup.c
| ... | ... | @@ -671,9 +671,6 @@ |
| 671 | 671 | } |
| 672 | 672 | } |
| 673 | 673 | |
| 674 | -extern unsigned long ppc_proc_freq; | |
| 675 | -extern unsigned long ppc_tb_freq; | |
| 676 | - | |
| 677 | 674 | /* |
| 678 | 675 | * Document me. |
| 679 | 676 | */ |
| ... | ... | @@ -771,8 +768,6 @@ |
| 771 | 768 | { |
| 772 | 769 | mf_power_off(); |
| 773 | 770 | } |
| 774 | - | |
| 775 | -extern void setup_default_decr(void); | |
| 776 | 771 | |
| 777 | 772 | /* |
| 778 | 773 | * void __init iSeries_calibrate_decr() |
arch/ppc64/kernel/irq.c
| ... | ... | @@ -395,6 +395,9 @@ |
| 395 | 395 | if (ppc64_interrupt_controller == IC_OPEN_PIC) |
| 396 | 396 | return real_irq; /* no mapping for openpic (for now) */ |
| 397 | 397 | |
| 398 | + if (ppc64_interrupt_controller == IC_BPA_IIC) | |
| 399 | + return real_irq; /* no mapping for iic either */ | |
| 400 | + | |
| 398 | 401 | /* don't map interrupts < MIN_VIRT_IRQ */ |
| 399 | 402 | if (real_irq < MIN_VIRT_IRQ) { |
| 400 | 403 | virt_irq_to_real_map[real_irq] = real_irq; |
arch/ppc64/kernel/maple_setup.c
| ... | ... | @@ -78,17 +78,77 @@ |
| 78 | 78 | extern void generic_find_legacy_serial_ports(u64 *physport, |
| 79 | 79 | unsigned int *default_speed); |
| 80 | 80 | |
| 81 | - | |
| 82 | 81 | static void maple_restart(char *cmd) |
| 83 | 82 | { |
| 83 | + unsigned int maple_nvram_base; | |
| 84 | + unsigned int maple_nvram_offset; | |
| 85 | + unsigned int maple_nvram_command; | |
| 86 | + struct device_node *rtcs; | |
| 87 | + | |
| 88 | + /* find NVRAM device */ | |
| 89 | + rtcs = find_compatible_devices("nvram", "AMD8111"); | |
| 90 | + if (rtcs && rtcs->addrs) { | |
| 91 | + maple_nvram_base = rtcs->addrs[0].address; | |
| 92 | + } else { | |
| 93 | + printk(KERN_EMERG "Maple: Unable to find NVRAM\n"); | |
| 94 | + printk(KERN_EMERG "Maple: Manual Restart Required\n"); | |
| 95 | + return; | |
| 96 | + } | |
| 97 | + | |
| 98 | + /* find service processor device */ | |
| 99 | + rtcs = find_devices("service-processor"); | |
| 100 | + if (!rtcs) { | |
| 101 | + printk(KERN_EMERG "Maple: Unable to find Service Processor\n"); | |
| 102 | + printk(KERN_EMERG "Maple: Manual Restart Required\n"); | |
| 103 | + return; | |
| 104 | + } | |
| 105 | + maple_nvram_offset = *(unsigned int*) get_property(rtcs, | |
| 106 | + "restart-addr", NULL); | |
| 107 | + maple_nvram_command = *(unsigned int*) get_property(rtcs, | |
| 108 | + "restart-value", NULL); | |
| 109 | + | |
| 110 | + /* send command */ | |
| 111 | + outb_p(maple_nvram_command, maple_nvram_base + maple_nvram_offset); | |
| 112 | + for (;;) ; | |
| 84 | 113 | } |
| 85 | 114 | |
| 86 | 115 | static void maple_power_off(void) |
| 87 | 116 | { |
| 117 | + unsigned int maple_nvram_base; | |
| 118 | + unsigned int maple_nvram_offset; | |
| 119 | + unsigned int maple_nvram_command; | |
| 120 | + struct device_node *rtcs; | |
| 121 | + | |
| 122 | + /* find NVRAM device */ | |
| 123 | + rtcs = find_compatible_devices("nvram", "AMD8111"); | |
| 124 | + if (rtcs && rtcs->addrs) { | |
| 125 | + maple_nvram_base = rtcs->addrs[0].address; | |
| 126 | + } else { | |
| 127 | + printk(KERN_EMERG "Maple: Unable to find NVRAM\n"); | |
| 128 | + printk(KERN_EMERG "Maple: Manual Power-Down Required\n"); | |
| 129 | + return; | |
| 130 | + } | |
| 131 | + | |
| 132 | + /* find service processor device */ | |
| 133 | + rtcs = find_devices("service-processor"); | |
| 134 | + if (!rtcs) { | |
| 135 | + printk(KERN_EMERG "Maple: Unable to find Service Processor\n"); | |
| 136 | + printk(KERN_EMERG "Maple: Manual Power-Down Required\n"); | |
| 137 | + return; | |
| 138 | + } | |
| 139 | + maple_nvram_offset = *(unsigned int*) get_property(rtcs, | |
| 140 | + "power-off-addr", NULL); | |
| 141 | + maple_nvram_command = *(unsigned int*) get_property(rtcs, | |
| 142 | + "power-off-value", NULL); | |
| 143 | + | |
| 144 | + /* send command */ | |
| 145 | + outb_p(maple_nvram_command, maple_nvram_base + maple_nvram_offset); | |
| 146 | + for (;;) ; | |
| 88 | 147 | } |
| 89 | 148 | |
| 90 | 149 | static void maple_halt(void) |
| 91 | 150 | { |
| 151 | + maple_power_off(); | |
| 92 | 152 | } |
| 93 | 153 | |
| 94 | 154 | #ifdef CONFIG_SMP |
| ... | ... | @@ -235,7 +295,7 @@ |
| 235 | 295 | .get_boot_time = maple_get_boot_time, |
| 236 | 296 | .set_rtc_time = maple_set_rtc_time, |
| 237 | 297 | .get_rtc_time = maple_get_rtc_time, |
| 238 | - .calibrate_decr = maple_calibrate_decr, | |
| 298 | + .calibrate_decr = generic_calibrate_decr, | |
| 239 | 299 | .progress = maple_progress, |
| 240 | 300 | }; |
arch/ppc64/kernel/maple_time.c
| ... | ... | @@ -42,11 +42,8 @@ |
| 42 | 42 | #define DBG(x...) |
| 43 | 43 | #endif |
| 44 | 44 | |
| 45 | -extern void setup_default_decr(void); | |
| 46 | 45 | extern void GregorianDay(struct rtc_time * tm); |
| 47 | 46 | |
| 48 | -extern unsigned long ppc_tb_freq; | |
| 49 | -extern unsigned long ppc_proc_freq; | |
| 50 | 47 | static int maple_rtc_addr; |
| 51 | 48 | |
| 52 | 49 | static int maple_clock_read(int addr) |
| ... | ... | @@ -174,54 +171,5 @@ |
| 174 | 171 | } |
| 175 | 172 | |
| 176 | 173 | maple_get_rtc_time(tm); |
| 177 | -} | |
| 178 | - | |
| 179 | -/* XXX FIXME: Some sane defaults: 125 MHz timebase, 1GHz processor */ | |
| 180 | -#define DEFAULT_TB_FREQ 125000000UL | |
| 181 | -#define DEFAULT_PROC_FREQ (DEFAULT_TB_FREQ * 8) | |
| 182 | - | |
| 183 | -void __init maple_calibrate_decr(void) | |
| 184 | -{ | |
| 185 | - struct device_node *cpu; | |
| 186 | - struct div_result divres; | |
| 187 | - unsigned int *fp = NULL; | |
| 188 | - | |
| 189 | - /* | |
| 190 | - * The cpu node should have a timebase-frequency property | |
| 191 | - * to tell us the rate at which the decrementer counts. | |
| 192 | - */ | |
| 193 | - cpu = of_find_node_by_type(NULL, "cpu"); | |
| 194 | - | |
| 195 | - ppc_tb_freq = DEFAULT_TB_FREQ; | |
| 196 | - if (cpu != 0) | |
| 197 | - fp = (unsigned int *)get_property(cpu, "timebase-frequency", NULL); | |
| 198 | - if (fp != NULL) | |
| 199 | - ppc_tb_freq = *fp; | |
| 200 | - else | |
| 201 | - printk(KERN_ERR "WARNING: Estimating decrementer frequency (not found)\n"); | |
| 202 | - fp = NULL; | |
| 203 | - ppc_proc_freq = DEFAULT_PROC_FREQ; | |
| 204 | - if (cpu != 0) | |
| 205 | - fp = (unsigned int *)get_property(cpu, "clock-frequency", NULL); | |
| 206 | - if (fp != NULL) | |
| 207 | - ppc_proc_freq = *fp; | |
| 208 | - else | |
| 209 | - printk(KERN_ERR "WARNING: Estimating processor frequency (not found)\n"); | |
| 210 | - | |
| 211 | - of_node_put(cpu); | |
| 212 | - | |
| 213 | - printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n", | |
| 214 | - ppc_tb_freq/1000000, ppc_tb_freq%1000000); | |
| 215 | - printk(KERN_INFO "time_init: processor frequency = %lu.%.6lu MHz\n", | |
| 216 | - ppc_proc_freq/1000000, ppc_proc_freq%1000000); | |
| 217 | - | |
| 218 | - tb_ticks_per_jiffy = ppc_tb_freq / HZ; | |
| 219 | - tb_ticks_per_sec = tb_ticks_per_jiffy * HZ; | |
| 220 | - tb_ticks_per_usec = ppc_tb_freq / 1000000; | |
| 221 | - tb_to_us = mulhwu_scale_factor(ppc_tb_freq, 1000000); | |
| 222 | - div128_by_32(1024*1024, 0, tb_ticks_per_sec, &divres); | |
| 223 | - tb_to_xs = divres.result_low; | |
| 224 | - | |
| 225 | - setup_default_decr(); | |
| 226 | 174 | } |
arch/ppc64/kernel/mpic.h
arch/ppc64/kernel/pSeries_pci.c
| 1 | 1 | /* |
| 2 | - * pSeries_pci.c | |
| 2 | + * arch/ppc64/kernel/pSeries_pci.c | |
| 3 | 3 | * |
| 4 | 4 | * Copyright (C) 2001 Dave Engebretsen, IBM Corporation |
| 5 | 5 | * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM |
| 6 | 6 | * |
| 7 | 7 | * pSeries specific routines for PCI. |
| 8 | 8 | * |
| 9 | - * Based on code from pci.c and chrp_pci.c | |
| 10 | - * | |
| 11 | 9 | * This program is free software; you can redistribute it and/or modify |
| 12 | 10 | * it under the terms of the GNU General Public License as published by |
| 13 | 11 | * the Free Software Foundation; either version 2 of the License, or |
| 14 | 12 | |
| 15 | 13 | |
| 16 | 14 | |
| 17 | 15 | |
| 18 | 16 | |
| 19 | 17 | |
| 20 | 18 | |
| ... | ... | @@ -23,431 +21,19 @@ |
| 23 | 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 24 | 22 | */ |
| 25 | 23 | |
| 24 | +#include <linux/init.h> | |
| 25 | +#include <linux/ioport.h> | |
| 26 | 26 | #include <linux/kernel.h> |
| 27 | -#include <linux/threads.h> | |
| 28 | 27 | #include <linux/pci.h> |
| 29 | 28 | #include <linux/string.h> |
| 30 | -#include <linux/init.h> | |
| 31 | -#include <linux/bootmem.h> | |
| 32 | 29 | |
| 33 | -#include <asm/io.h> | |
| 34 | -#include <asm/pgtable.h> | |
| 35 | -#include <asm/irq.h> | |
| 36 | -#include <asm/prom.h> | |
| 37 | -#include <asm/machdep.h> | |
| 38 | 30 | #include <asm/pci-bridge.h> |
| 39 | -#include <asm/iommu.h> | |
| 40 | -#include <asm/rtas.h> | |
| 31 | +#include <asm/prom.h> | |
| 41 | 32 | |
| 42 | -#include "mpic.h" | |
| 43 | 33 | #include "pci.h" |
| 44 | 34 | |
| 45 | -/* RTAS tokens */ | |
| 46 | -static int read_pci_config; | |
| 47 | -static int write_pci_config; | |
| 48 | -static int ibm_read_pci_config; | |
| 49 | -static int ibm_write_pci_config; | |
| 35 | +static int __initdata s7a_workaround = -1; | |
| 50 | 36 | |
| 51 | -static int s7a_workaround; | |
| 52 | - | |
| 53 | -extern struct mpic *pSeries_mpic; | |
| 54 | - | |
| 55 | -static int config_access_valid(struct device_node *dn, int where) | |
| 56 | -{ | |
| 57 | - if (where < 256) | |
| 58 | - return 1; | |
| 59 | - if (where < 4096 && dn->pci_ext_config_space) | |
| 60 | - return 1; | |
| 61 | - | |
| 62 | - return 0; | |
| 63 | -} | |
| 64 | - | |
| 65 | -static int rtas_read_config(struct device_node *dn, int where, int size, u32 *val) | |
| 66 | -{ | |
| 67 | - int returnval = -1; | |
| 68 | - unsigned long buid, addr; | |
| 69 | - int ret; | |
| 70 | - | |
| 71 | - if (!dn) | |
| 72 | - return PCIBIOS_DEVICE_NOT_FOUND; | |
| 73 | - if (!config_access_valid(dn, where)) | |
| 74 | - return PCIBIOS_BAD_REGISTER_NUMBER; | |
| 75 | - | |
| 76 | - addr = ((where & 0xf00) << 20) | (dn->busno << 16) | | |
| 77 | - (dn->devfn << 8) | (where & 0xff); | |
| 78 | - buid = dn->phb->buid; | |
| 79 | - if (buid) { | |
| 80 | - ret = rtas_call(ibm_read_pci_config, 4, 2, &returnval, | |
| 81 | - addr, buid >> 32, buid & 0xffffffff, size); | |
| 82 | - } else { | |
| 83 | - ret = rtas_call(read_pci_config, 2, 2, &returnval, addr, size); | |
| 84 | - } | |
| 85 | - *val = returnval; | |
| 86 | - | |
| 87 | - if (ret) | |
| 88 | - return PCIBIOS_DEVICE_NOT_FOUND; | |
| 89 | - | |
| 90 | - if (returnval == EEH_IO_ERROR_VALUE(size) | |
| 91 | - && eeh_dn_check_failure (dn, NULL)) | |
| 92 | - return PCIBIOS_DEVICE_NOT_FOUND; | |
| 93 | - | |
| 94 | - return PCIBIOS_SUCCESSFUL; | |
| 95 | -} | |
| 96 | - | |
| 97 | -static int rtas_pci_read_config(struct pci_bus *bus, | |
| 98 | - unsigned int devfn, | |
| 99 | - int where, int size, u32 *val) | |
| 100 | -{ | |
| 101 | - struct device_node *busdn, *dn; | |
| 102 | - | |
| 103 | - if (bus->self) | |
| 104 | - busdn = pci_device_to_OF_node(bus->self); | |
| 105 | - else | |
| 106 | - busdn = bus->sysdata; /* must be a phb */ | |
| 107 | - | |
| 108 | - /* Search only direct children of the bus */ | |
| 109 | - for (dn = busdn->child; dn; dn = dn->sibling) | |
| 110 | - if (dn->devfn == devfn) | |
| 111 | - return rtas_read_config(dn, where, size, val); | |
| 112 | - return PCIBIOS_DEVICE_NOT_FOUND; | |
| 113 | -} | |
| 114 | - | |
| 115 | -static int rtas_write_config(struct device_node *dn, int where, int size, u32 val) | |
| 116 | -{ | |
| 117 | - unsigned long buid, addr; | |
| 118 | - int ret; | |
| 119 | - | |
| 120 | - if (!dn) | |
| 121 | - return PCIBIOS_DEVICE_NOT_FOUND; | |
| 122 | - if (!config_access_valid(dn, where)) | |
| 123 | - return PCIBIOS_BAD_REGISTER_NUMBER; | |
| 124 | - | |
| 125 | - addr = ((where & 0xf00) << 20) | (dn->busno << 16) | | |
| 126 | - (dn->devfn << 8) | (where & 0xff); | |
| 127 | - buid = dn->phb->buid; | |
| 128 | - if (buid) { | |
| 129 | - ret = rtas_call(ibm_write_pci_config, 5, 1, NULL, addr, buid >> 32, buid & 0xffffffff, size, (ulong) val); | |
| 130 | - } else { | |
| 131 | - ret = rtas_call(write_pci_config, 3, 1, NULL, addr, size, (ulong)val); | |
| 132 | - } | |
| 133 | - | |
| 134 | - if (ret) | |
| 135 | - return PCIBIOS_DEVICE_NOT_FOUND; | |
| 136 | - | |
| 137 | - return PCIBIOS_SUCCESSFUL; | |
| 138 | -} | |
| 139 | - | |
| 140 | -static int rtas_pci_write_config(struct pci_bus *bus, | |
| 141 | - unsigned int devfn, | |
| 142 | - int where, int size, u32 val) | |
| 143 | -{ | |
| 144 | - struct device_node *busdn, *dn; | |
| 145 | - | |
| 146 | - if (bus->self) | |
| 147 | - busdn = pci_device_to_OF_node(bus->self); | |
| 148 | - else | |
| 149 | - busdn = bus->sysdata; /* must be a phb */ | |
| 150 | - | |
| 151 | - /* Search only direct children of the bus */ | |
| 152 | - for (dn = busdn->child; dn; dn = dn->sibling) | |
| 153 | - if (dn->devfn == devfn) | |
| 154 | - return rtas_write_config(dn, where, size, val); | |
| 155 | - return PCIBIOS_DEVICE_NOT_FOUND; | |
| 156 | -} | |
| 157 | - | |
| 158 | -struct pci_ops rtas_pci_ops = { | |
| 159 | - rtas_pci_read_config, | |
| 160 | - rtas_pci_write_config | |
| 161 | -}; | |
| 162 | - | |
| 163 | -int is_python(struct device_node *dev) | |
| 164 | -{ | |
| 165 | - char *model = (char *)get_property(dev, "model", NULL); | |
| 166 | - | |
| 167 | - if (model && strstr(model, "Python")) | |
| 168 | - return 1; | |
| 169 | - | |
| 170 | - return 0; | |
| 171 | -} | |
| 172 | - | |
| 173 | -static int get_phb_reg_prop(struct device_node *dev, | |
| 174 | - unsigned int addr_size_words, | |
| 175 | - struct reg_property64 *reg) | |
| 176 | -{ | |
| 177 | - unsigned int *ui_ptr = NULL, len; | |
| 178 | - | |
| 179 | - /* Found a PHB, now figure out where his registers are mapped. */ | |
| 180 | - ui_ptr = (unsigned int *)get_property(dev, "reg", &len); | |
| 181 | - if (ui_ptr == NULL) | |
| 182 | - return 1; | |
| 183 | - | |
| 184 | - if (addr_size_words == 1) { | |
| 185 | - reg->address = ((struct reg_property32 *)ui_ptr)->address; | |
| 186 | - reg->size = ((struct reg_property32 *)ui_ptr)->size; | |
| 187 | - } else { | |
| 188 | - *reg = *((struct reg_property64 *)ui_ptr); | |
| 189 | - } | |
| 190 | - | |
| 191 | - return 0; | |
| 192 | -} | |
| 193 | - | |
| 194 | -static void python_countermeasures(struct device_node *dev, | |
| 195 | - unsigned int addr_size_words) | |
| 196 | -{ | |
| 197 | - struct reg_property64 reg_struct; | |
| 198 | - void __iomem *chip_regs; | |
| 199 | - volatile u32 val; | |
| 200 | - | |
| 201 | - if (get_phb_reg_prop(dev, addr_size_words, ®_struct)) | |
| 202 | - return; | |
| 203 | - | |
| 204 | - /* Python's register file is 1 MB in size. */ | |
| 205 | - chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), 0x100000); | |
| 206 | - | |
| 207 | - /* | |
| 208 | - * Firmware doesn't always clear this bit which is critical | |
| 209 | - * for good performance - Anton | |
| 210 | - */ | |
| 211 | - | |
| 212 | -#define PRG_CL_RESET_VALID 0x00010000 | |
| 213 | - | |
| 214 | - val = in_be32(chip_regs + 0xf6030); | |
| 215 | - if (val & PRG_CL_RESET_VALID) { | |
| 216 | - printk(KERN_INFO "Python workaround: "); | |
| 217 | - val &= ~PRG_CL_RESET_VALID; | |
| 218 | - out_be32(chip_regs + 0xf6030, val); | |
| 219 | - /* | |
| 220 | - * We must read it back for changes to | |
| 221 | - * take effect | |
| 222 | - */ | |
| 223 | - val = in_be32(chip_regs + 0xf6030); | |
| 224 | - printk("reg0: %x\n", val); | |
| 225 | - } | |
| 226 | - | |
| 227 | - iounmap(chip_regs); | |
| 228 | -} | |
| 229 | - | |
| 230 | -void __init init_pci_config_tokens (void) | |
| 231 | -{ | |
| 232 | - read_pci_config = rtas_token("read-pci-config"); | |
| 233 | - write_pci_config = rtas_token("write-pci-config"); | |
| 234 | - ibm_read_pci_config = rtas_token("ibm,read-pci-config"); | |
| 235 | - ibm_write_pci_config = rtas_token("ibm,write-pci-config"); | |
| 236 | -} | |
| 237 | - | |
| 238 | -unsigned long __devinit get_phb_buid (struct device_node *phb) | |
| 239 | -{ | |
| 240 | - int addr_cells; | |
| 241 | - unsigned int *buid_vals; | |
| 242 | - unsigned int len; | |
| 243 | - unsigned long buid; | |
| 244 | - | |
| 245 | - if (ibm_read_pci_config == -1) return 0; | |
| 246 | - | |
| 247 | - /* PHB's will always be children of the root node, | |
| 248 | - * or so it is promised by the current firmware. */ | |
| 249 | - if (phb->parent == NULL) | |
| 250 | - return 0; | |
| 251 | - if (phb->parent->parent) | |
| 252 | - return 0; | |
| 253 | - | |
| 254 | - buid_vals = (unsigned int *) get_property(phb, "reg", &len); | |
| 255 | - if (buid_vals == NULL) | |
| 256 | - return 0; | |
| 257 | - | |
| 258 | - addr_cells = prom_n_addr_cells(phb); | |
| 259 | - if (addr_cells == 1) { | |
| 260 | - buid = (unsigned long) buid_vals[0]; | |
| 261 | - } else { | |
| 262 | - buid = (((unsigned long)buid_vals[0]) << 32UL) | | |
| 263 | - (((unsigned long)buid_vals[1]) & 0xffffffff); | |
| 264 | - } | |
| 265 | - return buid; | |
| 266 | -} | |
| 267 | - | |
| 268 | -static int phb_set_bus_ranges(struct device_node *dev, | |
| 269 | - struct pci_controller *phb) | |
| 270 | -{ | |
| 271 | - int *bus_range; | |
| 272 | - unsigned int len; | |
| 273 | - | |
| 274 | - bus_range = (int *) get_property(dev, "bus-range", &len); | |
| 275 | - if (bus_range == NULL || len < 2 * sizeof(int)) { | |
| 276 | - return 1; | |
| 277 | - } | |
| 278 | - | |
| 279 | - phb->first_busno = bus_range[0]; | |
| 280 | - phb->last_busno = bus_range[1]; | |
| 281 | - | |
| 282 | - return 0; | |
| 283 | -} | |
| 284 | - | |
| 285 | -static int __devinit setup_phb(struct device_node *dev, | |
| 286 | - struct pci_controller *phb, | |
| 287 | - unsigned int addr_size_words) | |
| 288 | -{ | |
| 289 | - pci_setup_pci_controller(phb); | |
| 290 | - | |
| 291 | - if (is_python(dev)) | |
| 292 | - python_countermeasures(dev, addr_size_words); | |
| 293 | - | |
| 294 | - if (phb_set_bus_ranges(dev, phb)) | |
| 295 | - return 1; | |
| 296 | - | |
| 297 | - phb->arch_data = dev; | |
| 298 | - phb->ops = &rtas_pci_ops; | |
| 299 | - phb->buid = get_phb_buid(dev); | |
| 300 | - | |
| 301 | - return 0; | |
| 302 | -} | |
| 303 | - | |
| 304 | -static void __devinit add_linux_pci_domain(struct device_node *dev, | |
| 305 | - struct pci_controller *phb, | |
| 306 | - struct property *of_prop) | |
| 307 | -{ | |
| 308 | - memset(of_prop, 0, sizeof(struct property)); | |
| 309 | - of_prop->name = "linux,pci-domain"; | |
| 310 | - of_prop->length = sizeof(phb->global_number); | |
| 311 | - of_prop->value = (unsigned char *)&of_prop[1]; | |
| 312 | - memcpy(of_prop->value, &phb->global_number, sizeof(phb->global_number)); | |
| 313 | - prom_add_property(dev, of_prop); | |
| 314 | -} | |
| 315 | - | |
| 316 | -static struct pci_controller * __init alloc_phb(struct device_node *dev, | |
| 317 | - unsigned int addr_size_words) | |
| 318 | -{ | |
| 319 | - struct pci_controller *phb; | |
| 320 | - struct property *of_prop; | |
| 321 | - | |
| 322 | - phb = alloc_bootmem(sizeof(struct pci_controller)); | |
| 323 | - if (phb == NULL) | |
| 324 | - return NULL; | |
| 325 | - | |
| 326 | - of_prop = alloc_bootmem(sizeof(struct property) + | |
| 327 | - sizeof(phb->global_number)); | |
| 328 | - if (!of_prop) | |
| 329 | - return NULL; | |
| 330 | - | |
| 331 | - if (setup_phb(dev, phb, addr_size_words)) | |
| 332 | - return NULL; | |
| 333 | - | |
| 334 | - add_linux_pci_domain(dev, phb, of_prop); | |
| 335 | - | |
| 336 | - return phb; | |
| 337 | -} | |
| 338 | - | |
| 339 | -static struct pci_controller * __devinit alloc_phb_dynamic(struct device_node *dev, unsigned int addr_size_words) | |
| 340 | -{ | |
| 341 | - struct pci_controller *phb; | |
| 342 | - | |
| 343 | - phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller), | |
| 344 | - GFP_KERNEL); | |
| 345 | - if (phb == NULL) | |
| 346 | - return NULL; | |
| 347 | - | |
| 348 | - if (setup_phb(dev, phb, addr_size_words)) | |
| 349 | - return NULL; | |
| 350 | - | |
| 351 | - phb->is_dynamic = 1; | |
| 352 | - | |
| 353 | - /* TODO: linux,pci-domain? */ | |
| 354 | - | |
| 355 | - return phb; | |
| 356 | -} | |
| 357 | - | |
| 358 | -unsigned long __init find_and_init_phbs(void) | |
| 359 | -{ | |
| 360 | - struct device_node *node; | |
| 361 | - struct pci_controller *phb; | |
| 362 | - unsigned int root_size_cells = 0; | |
| 363 | - unsigned int index; | |
| 364 | - unsigned int *opprop = NULL; | |
| 365 | - struct device_node *root = of_find_node_by_path("/"); | |
| 366 | - | |
| 367 | - if (ppc64_interrupt_controller == IC_OPEN_PIC) { | |
| 368 | - opprop = (unsigned int *)get_property(root, | |
| 369 | - "platform-open-pic", NULL); | |
| 370 | - } | |
| 371 | - | |
| 372 | - root_size_cells = prom_n_size_cells(root); | |
| 373 | - | |
| 374 | - index = 0; | |
| 375 | - | |
| 376 | - for (node = of_get_next_child(root, NULL); | |
| 377 | - node != NULL; | |
| 378 | - node = of_get_next_child(root, node)) { | |
| 379 | - if (node->type == NULL || strcmp(node->type, "pci") != 0) | |
| 380 | - continue; | |
| 381 | - | |
| 382 | - phb = alloc_phb(node, root_size_cells); | |
| 383 | - if (!phb) | |
| 384 | - continue; | |
| 385 | - | |
| 386 | - pci_process_bridge_OF_ranges(phb, node); | |
| 387 | - pci_setup_phb_io(phb, index == 0); | |
| 388 | - | |
| 389 | - if (ppc64_interrupt_controller == IC_OPEN_PIC && pSeries_mpic) { | |
| 390 | - int addr = root_size_cells * (index + 2) - 1; | |
| 391 | - mpic_assign_isu(pSeries_mpic, index, opprop[addr]); | |
| 392 | - } | |
| 393 | - | |
| 394 | - index++; | |
| 395 | - } | |
| 396 | - | |
| 397 | - of_node_put(root); | |
| 398 | - pci_devs_phb_init(); | |
| 399 | - | |
| 400 | - /* | |
| 401 | - * pci_probe_only and pci_assign_all_buses can be set via properties | |
| 402 | - * in chosen. | |
| 403 | - */ | |
| 404 | - if (of_chosen) { | |
| 405 | - int *prop; | |
| 406 | - | |
| 407 | - prop = (int *)get_property(of_chosen, "linux,pci-probe-only", | |
| 408 | - NULL); | |
| 409 | - if (prop) | |
| 410 | - pci_probe_only = *prop; | |
| 411 | - | |
| 412 | - prop = (int *)get_property(of_chosen, | |
| 413 | - "linux,pci-assign-all-buses", NULL); | |
| 414 | - if (prop) | |
| 415 | - pci_assign_all_buses = *prop; | |
| 416 | - } | |
| 417 | - | |
| 418 | - return 0; | |
| 419 | -} | |
| 420 | - | |
| 421 | -struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) | |
| 422 | -{ | |
| 423 | - struct device_node *root = of_find_node_by_path("/"); | |
| 424 | - unsigned int root_size_cells = 0; | |
| 425 | - struct pci_controller *phb; | |
| 426 | - struct pci_bus *bus; | |
| 427 | - int primary; | |
| 428 | - | |
| 429 | - root_size_cells = prom_n_size_cells(root); | |
| 430 | - | |
| 431 | - primary = list_empty(&hose_list); | |
| 432 | - phb = alloc_phb_dynamic(dn, root_size_cells); | |
| 433 | - if (!phb) | |
| 434 | - return NULL; | |
| 435 | - | |
| 436 | - pci_process_bridge_OF_ranges(phb, dn); | |
| 437 | - | |
| 438 | - pci_setup_phb_io_dynamic(phb, primary); | |
| 439 | - of_node_put(root); | |
| 440 | - | |
| 441 | - pci_devs_phb_init_dynamic(phb); | |
| 442 | - phb->last_busno = 0xff; | |
| 443 | - bus = pci_scan_bus(phb->first_busno, phb->ops, phb->arch_data); | |
| 444 | - phb->bus = bus; | |
| 445 | - phb->last_busno = bus->subordinate; | |
| 446 | - | |
| 447 | - return phb; | |
| 448 | -} | |
| 449 | -EXPORT_SYMBOL(init_phb_dynamic); | |
| 450 | - | |
| 451 | 37 | #if 0 |
| 452 | 38 | void pcibios_name_device(struct pci_dev *dev) |
| 453 | 39 | { |
| 454 | 40 | |
| ... | ... | @@ -474,11 +60,12 @@ |
| 474 | 60 | DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_name_device); |
| 475 | 61 | #endif |
| 476 | 62 | |
| 477 | -static void check_s7a(void) | |
| 63 | +static void __init check_s7a(void) | |
| 478 | 64 | { |
| 479 | 65 | struct device_node *root; |
| 480 | 66 | char *model; |
| 481 | 67 | |
| 68 | + s7a_workaround = 0; | |
| 482 | 69 | root = of_find_node_by_path("/"); |
| 483 | 70 | if (root) { |
| 484 | 71 | model = get_property(root, "model", NULL); |
| 485 | 72 | |
| 486 | 73 | |
| 487 | 74 | |
| 488 | 75 | |
| 489 | 76 | |
| ... | ... | @@ -488,55 +75,23 @@ |
| 488 | 75 | } |
| 489 | 76 | } |
| 490 | 77 | |
| 491 | -/* RPA-specific bits for removing PHBs */ | |
| 492 | -int pcibios_remove_root_bus(struct pci_controller *phb) | |
| 78 | +void __devinit pSeries_irq_bus_setup(struct pci_bus *bus) | |
| 493 | 79 | { |
| 494 | - struct pci_bus *b = phb->bus; | |
| 495 | - struct resource *res; | |
| 496 | - int rc, i; | |
| 80 | + struct pci_dev *dev; | |
| 497 | 81 | |
| 498 | - res = b->resource[0]; | |
| 499 | - if (!res->flags) { | |
| 500 | - printk(KERN_ERR "%s: no IO resource for PHB %s\n", __FUNCTION__, | |
| 501 | - b->name); | |
| 502 | - return 1; | |
| 503 | - } | |
| 504 | - | |
| 505 | - rc = unmap_bus_range(b); | |
| 506 | - if (rc) { | |
| 507 | - printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", | |
| 508 | - __FUNCTION__, b->name); | |
| 509 | - return 1; | |
| 510 | - } | |
| 511 | - | |
| 512 | - if (release_resource(res)) { | |
| 513 | - printk(KERN_ERR "%s: failed to release IO on bus %s\n", | |
| 514 | - __FUNCTION__, b->name); | |
| 515 | - return 1; | |
| 516 | - } | |
| 517 | - | |
| 518 | - for (i = 1; i < 3; ++i) { | |
| 519 | - res = b->resource[i]; | |
| 520 | - if (!res->flags && i == 0) { | |
| 521 | - printk(KERN_ERR "%s: no MEM resource for PHB %s\n", | |
| 522 | - __FUNCTION__, b->name); | |
| 523 | - return 1; | |
| 82 | + if (s7a_workaround < 0) | |
| 83 | + check_s7a(); | |
| 84 | + list_for_each_entry(dev, &bus->devices, bus_list) { | |
| 85 | + pci_read_irq_line(dev); | |
| 86 | + if (s7a_workaround) { | |
| 87 | + if (dev->irq > 16) { | |
| 88 | + dev->irq -= 3; | |
| 89 | + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, | |
| 90 | + dev->irq); | |
| 91 | + } | |
| 524 | 92 | } |
| 525 | - if (res->flags && release_resource(res)) { | |
| 526 | - printk(KERN_ERR | |
| 527 | - "%s: failed to release IO %d on bus %s\n", | |
| 528 | - __FUNCTION__, i, b->name); | |
| 529 | - return 1; | |
| 530 | - } | |
| 531 | 93 | } |
| 532 | - | |
| 533 | - list_del(&phb->list_node); | |
| 534 | - if (phb->is_dynamic) | |
| 535 | - kfree(phb); | |
| 536 | - | |
| 537 | - return 0; | |
| 538 | 94 | } |
| 539 | -EXPORT_SYMBOL(pcibios_remove_root_bus); | |
| 540 | 95 | |
| 541 | 96 | static void __init pSeries_request_regions(void) |
| 542 | 97 | { |
| ... | ... | @@ -553,20 +108,6 @@ |
| 553 | 108 | |
| 554 | 109 | void __init pSeries_final_fixup(void) |
| 555 | 110 | { |
| 556 | - struct pci_dev *dev = NULL; | |
| 557 | - | |
| 558 | - check_s7a(); | |
| 559 | - | |
| 560 | - for_each_pci_dev(dev) { | |
| 561 | - pci_read_irq_line(dev); | |
| 562 | - if (s7a_workaround) { | |
| 563 | - if (dev->irq > 16) { | |
| 564 | - dev->irq -= 3; | |
| 565 | - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); | |
| 566 | - } | |
| 567 | - } | |
| 568 | - } | |
| 569 | - | |
| 570 | 111 | phbs_remap_io(); |
| 571 | 112 | pSeries_request_regions(); |
| 572 | 113 |
arch/ppc64/kernel/pSeries_setup.c
| ... | ... | @@ -71,11 +71,6 @@ |
| 71 | 71 | #define DBG(fmt...) |
| 72 | 72 | #endif |
| 73 | 73 | |
| 74 | -extern void pSeries_final_fixup(void); | |
| 75 | - | |
| 76 | -extern void pSeries_get_boot_time(struct rtc_time *rtc_time); | |
| 77 | -extern void pSeries_get_rtc_time(struct rtc_time *rtc_time); | |
| 78 | -extern int pSeries_set_rtc_time(struct rtc_time *rtc_time); | |
| 79 | 74 | extern void find_udbg_vterm(void); |
| 80 | 75 | extern void system_reset_fwnmi(void); /* from head.S */ |
| 81 | 76 | extern void machine_check_fwnmi(void); /* from head.S */ |
| ... | ... | @@ -84,9 +79,6 @@ |
| 84 | 79 | |
| 85 | 80 | int fwnmi_active; /* TRUE if an FWNMI handler is present */ |
| 86 | 81 | |
| 87 | -extern unsigned long ppc_proc_freq; | |
| 88 | -extern unsigned long ppc_tb_freq; | |
| 89 | - | |
| 90 | 82 | extern void pSeries_system_reset_exception(struct pt_regs *regs); |
| 91 | 83 | extern int pSeries_machine_check_exception(struct pt_regs *regs); |
| 92 | 84 | |
| ... | ... | @@ -381,171 +373,6 @@ |
| 381 | 373 | } |
| 382 | 374 | |
| 383 | 375 | |
| 384 | -static void pSeries_progress(char *s, unsigned short hex) | |
| 385 | -{ | |
| 386 | - struct device_node *root; | |
| 387 | - int width, *p; | |
| 388 | - char *os; | |
| 389 | - static int display_character, set_indicator; | |
| 390 | - static int max_width; | |
| 391 | - static DEFINE_SPINLOCK(progress_lock); | |
| 392 | - static int pending_newline = 0; /* did last write end with unprinted newline? */ | |
| 393 | - | |
| 394 | - if (!rtas.base) | |
| 395 | - return; | |
| 396 | - | |
| 397 | - if (max_width == 0) { | |
| 398 | - if ((root = find_path_device("/rtas")) && | |
| 399 | - (p = (unsigned int *)get_property(root, | |
| 400 | - "ibm,display-line-length", | |
| 401 | - NULL))) | |
| 402 | - max_width = *p; | |
| 403 | - else | |
| 404 | - max_width = 0x10; | |
| 405 | - display_character = rtas_token("display-character"); | |
| 406 | - set_indicator = rtas_token("set-indicator"); | |
| 407 | - } | |
| 408 | - | |
| 409 | - if (display_character == RTAS_UNKNOWN_SERVICE) { | |
| 410 | - /* use hex display if available */ | |
| 411 | - if (set_indicator != RTAS_UNKNOWN_SERVICE) | |
| 412 | - rtas_call(set_indicator, 3, 1, NULL, 6, 0, hex); | |
| 413 | - return; | |
| 414 | - } | |
| 415 | - | |
| 416 | - spin_lock(&progress_lock); | |
| 417 | - | |
| 418 | - /* | |
| 419 | - * Last write ended with newline, but we didn't print it since | |
| 420 | - * it would just clear the bottom line of output. Print it now | |
| 421 | - * instead. | |
| 422 | - * | |
| 423 | - * If no newline is pending, print a CR to start output at the | |
| 424 | - * beginning of the line. | |
| 425 | - */ | |
| 426 | - if (pending_newline) { | |
| 427 | - rtas_call(display_character, 1, 1, NULL, '\r'); | |
| 428 | - rtas_call(display_character, 1, 1, NULL, '\n'); | |
| 429 | - pending_newline = 0; | |
| 430 | - } else { | |
| 431 | - rtas_call(display_character, 1, 1, NULL, '\r'); | |
| 432 | - } | |
| 433 | - | |
| 434 | - width = max_width; | |
| 435 | - os = s; | |
| 436 | - while (*os) { | |
| 437 | - if (*os == '\n' || *os == '\r') { | |
| 438 | - /* Blank to end of line. */ | |
| 439 | - while (width-- > 0) | |
| 440 | - rtas_call(display_character, 1, 1, NULL, ' '); | |
| 441 | - | |
| 442 | - /* If newline is the last character, save it | |
| 443 | - * until next call to avoid bumping up the | |
| 444 | - * display output. | |
| 445 | - */ | |
| 446 | - if (*os == '\n' && !os[1]) { | |
| 447 | - pending_newline = 1; | |
| 448 | - spin_unlock(&progress_lock); | |
| 449 | - return; | |
| 450 | - } | |
| 451 | - | |
| 452 | - /* RTAS wants CR-LF, not just LF */ | |
| 453 | - | |
| 454 | - if (*os == '\n') { | |
| 455 | - rtas_call(display_character, 1, 1, NULL, '\r'); | |
| 456 | - rtas_call(display_character, 1, 1, NULL, '\n'); | |
| 457 | - } else { | |
| 458 | - /* CR might be used to re-draw a line, so we'll | |
| 459 | - * leave it alone and not add LF. | |
| 460 | - */ | |
| 461 | - rtas_call(display_character, 1, 1, NULL, *os); | |
| 462 | - } | |
| 463 | - | |
| 464 | - width = max_width; | |
| 465 | - } else { | |
| 466 | - width--; | |
| 467 | - rtas_call(display_character, 1, 1, NULL, *os); | |
| 468 | - } | |
| 469 | - | |
| 470 | - os++; | |
| 471 | - | |
| 472 | - /* if we overwrite the screen length */ | |
| 473 | - if (width <= 0) | |
| 474 | - while ((*os != 0) && (*os != '\n') && (*os != '\r')) | |
| 475 | - os++; | |
| 476 | - } | |
| 477 | - | |
| 478 | - /* Blank to end of line. */ | |
| 479 | - while (width-- > 0) | |
| 480 | - rtas_call(display_character, 1, 1, NULL, ' '); | |
| 481 | - | |
| 482 | - spin_unlock(&progress_lock); | |
| 483 | -} | |
| 484 | - | |
| 485 | -extern void setup_default_decr(void); | |
| 486 | - | |
| 487 | -/* Some sane defaults: 125 MHz timebase, 1GHz processor */ | |
| 488 | -#define DEFAULT_TB_FREQ 125000000UL | |
| 489 | -#define DEFAULT_PROC_FREQ (DEFAULT_TB_FREQ * 8) | |
| 490 | - | |
| 491 | -static void __init pSeries_calibrate_decr(void) | |
| 492 | -{ | |
| 493 | - struct device_node *cpu; | |
| 494 | - struct div_result divres; | |
| 495 | - unsigned int *fp; | |
| 496 | - int node_found; | |
| 497 | - | |
| 498 | - /* | |
| 499 | - * The cpu node should have a timebase-frequency property | |
| 500 | - * to tell us the rate at which the decrementer counts. | |
| 501 | - */ | |
| 502 | - cpu = of_find_node_by_type(NULL, "cpu"); | |
| 503 | - | |
| 504 | - ppc_tb_freq = DEFAULT_TB_FREQ; /* hardcoded default */ | |
| 505 | - node_found = 0; | |
| 506 | - if (cpu != 0) { | |
| 507 | - fp = (unsigned int *)get_property(cpu, "timebase-frequency", | |
| 508 | - NULL); | |
| 509 | - if (fp != 0) { | |
| 510 | - node_found = 1; | |
| 511 | - ppc_tb_freq = *fp; | |
| 512 | - } | |
| 513 | - } | |
| 514 | - if (!node_found) | |
| 515 | - printk(KERN_ERR "WARNING: Estimating decrementer frequency " | |
| 516 | - "(not found)\n"); | |
| 517 | - | |
| 518 | - ppc_proc_freq = DEFAULT_PROC_FREQ; | |
| 519 | - node_found = 0; | |
| 520 | - if (cpu != 0) { | |
| 521 | - fp = (unsigned int *)get_property(cpu, "clock-frequency", | |
| 522 | - NULL); | |
| 523 | - if (fp != 0) { | |
| 524 | - node_found = 1; | |
| 525 | - ppc_proc_freq = *fp; | |
| 526 | - } | |
| 527 | - } | |
| 528 | - if (!node_found) | |
| 529 | - printk(KERN_ERR "WARNING: Estimating processor frequency " | |
| 530 | - "(not found)\n"); | |
| 531 | - | |
| 532 | - of_node_put(cpu); | |
| 533 | - | |
| 534 | - printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n", | |
| 535 | - ppc_tb_freq/1000000, ppc_tb_freq%1000000); | |
| 536 | - printk(KERN_INFO "time_init: processor frequency = %lu.%.6lu MHz\n", | |
| 537 | - ppc_proc_freq/1000000, ppc_proc_freq%1000000); | |
| 538 | - | |
| 539 | - tb_ticks_per_jiffy = ppc_tb_freq / HZ; | |
| 540 | - tb_ticks_per_sec = tb_ticks_per_jiffy * HZ; | |
| 541 | - tb_ticks_per_usec = ppc_tb_freq / 1000000; | |
| 542 | - tb_to_us = mulhwu_scale_factor(ppc_tb_freq, 1000000); | |
| 543 | - div128_by_32(1024*1024, 0, tb_ticks_per_sec, &divres); | |
| 544 | - tb_to_xs = divres.result_low; | |
| 545 | - | |
| 546 | - setup_default_decr(); | |
| 547 | -} | |
| 548 | - | |
| 549 | 376 | static int pSeries_check_legacy_ioport(unsigned int baseport) |
| 550 | 377 | { |
| 551 | 378 | struct device_node *np; |
| 552 | 379 | |
| ... | ... | @@ -596,16 +423,17 @@ |
| 596 | 423 | .get_cpuinfo = pSeries_get_cpuinfo, |
| 597 | 424 | .log_error = pSeries_log_error, |
| 598 | 425 | .pcibios_fixup = pSeries_final_fixup, |
| 426 | + .irq_bus_setup = pSeries_irq_bus_setup, | |
| 599 | 427 | .restart = rtas_restart, |
| 600 | 428 | .power_off = rtas_power_off, |
| 601 | 429 | .halt = rtas_halt, |
| 602 | 430 | .panic = rtas_os_term, |
| 603 | 431 | .cpu_die = pSeries_mach_cpu_die, |
| 604 | - .get_boot_time = pSeries_get_boot_time, | |
| 605 | - .get_rtc_time = pSeries_get_rtc_time, | |
| 606 | - .set_rtc_time = pSeries_set_rtc_time, | |
| 607 | - .calibrate_decr = pSeries_calibrate_decr, | |
| 608 | - .progress = pSeries_progress, | |
| 432 | + .get_boot_time = rtas_get_boot_time, | |
| 433 | + .get_rtc_time = rtas_get_rtc_time, | |
| 434 | + .set_rtc_time = rtas_set_rtc_time, | |
| 435 | + .calibrate_decr = generic_calibrate_decr, | |
| 436 | + .progress = rtas_progress, | |
| 609 | 437 | .check_legacy_ioport = pSeries_check_legacy_ioport, |
| 610 | 438 | .system_reset_exception = pSeries_system_reset_exception, |
| 611 | 439 | .machine_check_exception = pSeries_machine_check_exception, |
arch/ppc64/kernel/pSeries_smp.c
| 1 | 1 | /* |
| 2 | - * SMP support for pSeries machines. | |
| 2 | + * SMP support for pSeries and BPA machines. | |
| 3 | 3 | * |
| 4 | 4 | * Dave Engebretsen, Peter Bergner, and |
| 5 | 5 | * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com |
| ... | ... | @@ -47,6 +47,7 @@ |
| 47 | 47 | #include <asm/pSeries_reconfig.h> |
| 48 | 48 | |
| 49 | 49 | #include "mpic.h" |
| 50 | +#include "bpa_iic.h" | |
| 50 | 51 | |
| 51 | 52 | #ifdef DEBUG |
| 52 | 53 | #define DBG(fmt...) udbg_printf(fmt) |
| ... | ... | @@ -286,6 +287,7 @@ |
| 286 | 287 | return 1; |
| 287 | 288 | } |
| 288 | 289 | |
| 290 | +#ifdef CONFIG_XICS | |
| 289 | 291 | static inline void smp_xics_do_message(int cpu, int msg) |
| 290 | 292 | { |
| 291 | 293 | set_bit(msg, &xics_ipi_message[cpu].value); |
| 292 | 294 | |
| ... | ... | @@ -327,7 +329,38 @@ |
| 327 | 329 | cpu_clear(cpu, of_spin_map); |
| 328 | 330 | |
| 329 | 331 | } |
| 332 | +#endif /* CONFIG_XICS */ | |
| 333 | +#ifdef CONFIG_BPA_IIC | |
| 334 | +static void smp_iic_message_pass(int target, int msg) | |
| 335 | +{ | |
| 336 | + unsigned int i; | |
| 330 | 337 | |
| 338 | + if (target < NR_CPUS) { | |
| 339 | + iic_cause_IPI(target, msg); | |
| 340 | + } else { | |
| 341 | + for_each_online_cpu(i) { | |
| 342 | + if (target == MSG_ALL_BUT_SELF | |
| 343 | + && i == smp_processor_id()) | |
| 344 | + continue; | |
| 345 | + iic_cause_IPI(i, msg); | |
| 346 | + } | |
| 347 | + } | |
| 348 | +} | |
| 349 | + | |
| 350 | +static int __init smp_iic_probe(void) | |
| 351 | +{ | |
| 352 | + iic_request_IPIs(); | |
| 353 | + | |
| 354 | + return cpus_weight(cpu_possible_map); | |
| 355 | +} | |
| 356 | + | |
| 357 | +static void __devinit smp_iic_setup_cpu(int cpu) | |
| 358 | +{ | |
| 359 | + if (cpu != boot_cpuid) | |
| 360 | + iic_setup_cpu(); | |
| 361 | +} | |
| 362 | +#endif /* CONFIG_BPA_IIC */ | |
| 363 | + | |
| 331 | 364 | static DEFINE_SPINLOCK(timebase_lock); |
| 332 | 365 | static unsigned long timebase = 0; |
| 333 | 366 | |
| 334 | 367 | |
| ... | ... | @@ -381,14 +414,15 @@ |
| 381 | 414 | |
| 382 | 415 | return 1; |
| 383 | 416 | } |
| 384 | - | |
| 417 | +#ifdef CONFIG_MPIC | |
| 385 | 418 | static struct smp_ops_t pSeries_mpic_smp_ops = { |
| 386 | 419 | .message_pass = smp_mpic_message_pass, |
| 387 | 420 | .probe = smp_mpic_probe, |
| 388 | 421 | .kick_cpu = smp_pSeries_kick_cpu, |
| 389 | 422 | .setup_cpu = smp_mpic_setup_cpu, |
| 390 | 423 | }; |
| 391 | - | |
| 424 | +#endif | |
| 425 | +#ifdef CONFIG_XICS | |
| 392 | 426 | static struct smp_ops_t pSeries_xics_smp_ops = { |
| 393 | 427 | .message_pass = smp_xics_message_pass, |
| 394 | 428 | .probe = smp_xics_probe, |
| ... | ... | @@ -396,6 +430,16 @@ |
| 396 | 430 | .setup_cpu = smp_xics_setup_cpu, |
| 397 | 431 | .cpu_bootable = smp_pSeries_cpu_bootable, |
| 398 | 432 | }; |
| 433 | +#endif | |
| 434 | +#ifdef CONFIG_BPA_IIC | |
| 435 | +static struct smp_ops_t bpa_iic_smp_ops = { | |
| 436 | + .message_pass = smp_iic_message_pass, | |
| 437 | + .probe = smp_iic_probe, | |
| 438 | + .kick_cpu = smp_pSeries_kick_cpu, | |
| 439 | + .setup_cpu = smp_iic_setup_cpu, | |
| 440 | + .cpu_bootable = smp_pSeries_cpu_bootable, | |
| 441 | +}; | |
| 442 | +#endif | |
| 399 | 443 | |
| 400 | 444 | /* This is called very early */ |
| 401 | 445 | void __init smp_init_pSeries(void) |
| 402 | 446 | |
| 403 | 447 | |
| ... | ... | @@ -404,10 +448,25 @@ |
| 404 | 448 | |
| 405 | 449 | DBG(" -> smp_init_pSeries()\n"); |
| 406 | 450 | |
| 407 | - if (ppc64_interrupt_controller == IC_OPEN_PIC) | |
| 451 | + switch (ppc64_interrupt_controller) { | |
| 452 | +#ifdef CONFIG_MPIC | |
| 453 | + case IC_OPEN_PIC: | |
| 408 | 454 | smp_ops = &pSeries_mpic_smp_ops; |
| 409 | - else | |
| 455 | + break; | |
| 456 | +#endif | |
| 457 | +#ifdef CONFIG_XICS | |
| 458 | + case IC_PPC_XIC: | |
| 410 | 459 | smp_ops = &pSeries_xics_smp_ops; |
| 460 | + break; | |
| 461 | +#endif | |
| 462 | +#ifdef CONFIG_BPA_IIC | |
| 463 | + case IC_BPA_IIC: | |
| 464 | + smp_ops = &bpa_iic_smp_ops; | |
| 465 | + break; | |
| 466 | +#endif | |
| 467 | + default: | |
| 468 | + panic("Invalid interrupt controller"); | |
| 469 | + } | |
| 411 | 470 | |
| 412 | 471 | #ifdef CONFIG_HOTPLUG_CPU |
| 413 | 472 | smp_ops->cpu_disable = pSeries_cpu_disable; |
arch/ppc64/kernel/pci.c
arch/ppc64/kernel/pci.h
| ... | ... | @@ -40,9 +40,13 @@ |
| 40 | 40 | void pci_addr_cache_insert_device(struct pci_dev *dev); |
| 41 | 41 | void pci_addr_cache_remove_device(struct pci_dev *dev); |
| 42 | 42 | |
| 43 | -/* From pSeries_pci.h */ | |
| 43 | +/* From rtas_pci.h */ | |
| 44 | 44 | void init_pci_config_tokens (void); |
| 45 | 45 | unsigned long get_phb_buid (struct device_node *); |
| 46 | + | |
| 47 | +/* From pSeries_pci.h */ | |
| 48 | +extern void pSeries_final_fixup(void); | |
| 49 | +extern void pSeries_irq_bus_setup(struct pci_bus *bus); | |
| 46 | 50 | |
| 47 | 51 | extern unsigned long pci_probe_only; |
| 48 | 52 | extern unsigned long pci_assign_all_buses; |
arch/ppc64/kernel/pmac_time.c
| ... | ... | @@ -40,11 +40,6 @@ |
| 40 | 40 | #define DBG(x...) |
| 41 | 41 | #endif |
| 42 | 42 | |
| 43 | -extern void setup_default_decr(void); | |
| 44 | - | |
| 45 | -extern unsigned long ppc_tb_freq; | |
| 46 | -extern unsigned long ppc_proc_freq; | |
| 47 | - | |
| 48 | 43 | /* Apparently the RTC stores seconds since 1 Jan 1904 */ |
| 49 | 44 | #define RTC_OFFSET 2082844800 |
| 50 | 45 | |
| ... | ... | @@ -161,8 +156,7 @@ |
| 161 | 156 | |
| 162 | 157 | /* |
| 163 | 158 | * Query the OF and get the decr frequency. |
| 164 | - * This was taken from the pmac time_init() when merging the prep/pmac | |
| 165 | - * time functions. | |
| 159 | + * FIXME: merge this with generic_calibrate_decr | |
| 166 | 160 | */ |
| 167 | 161 | void __init pmac_calibrate_decr(void) |
| 168 | 162 | { |
arch/ppc64/kernel/proc_ppc64.c
arch/ppc64/kernel/prom_init.c
| ... | ... | @@ -1915,9 +1915,9 @@ |
| 1915 | 1915 | prom_send_capabilities(); |
| 1916 | 1916 | |
| 1917 | 1917 | /* |
| 1918 | - * On pSeries, copy the CPU hold code | |
| 1918 | + * On pSeries and BPA, copy the CPU hold code | |
| 1919 | 1919 | */ |
| 1920 | - if (RELOC(of_platform) & PLATFORM_PSERIES) | |
| 1920 | + if (RELOC(of_platform) & (PLATFORM_PSERIES | PLATFORM_BPA)) | |
| 1921 | 1921 | copy_and_flush(0, KERNELBASE - offset, 0x100, 0); |
| 1922 | 1922 | |
| 1923 | 1923 | /* |
arch/ppc64/kernel/rtas-proc.c
| ... | ... | @@ -371,11 +371,11 @@ |
| 371 | 371 | /* Lets see if the user passed hexdigits */ |
| 372 | 372 | hex = simple_strtoul(progress_led, NULL, 10); |
| 373 | 373 | |
| 374 | - ppc_md.progress ((char *)progress_led, hex); | |
| 374 | + rtas_progress ((char *)progress_led, hex); | |
| 375 | 375 | return count; |
| 376 | 376 | |
| 377 | 377 | /* clear the line */ |
| 378 | - /* ppc_md.progress(" ", 0xffff);*/ | |
| 378 | + /* rtas_progress(" ", 0xffff);*/ | |
| 379 | 379 | } |
| 380 | 380 | /* ****************************************************************** */ |
| 381 | 381 | static int ppc_rtas_progress_show(struct seq_file *m, void *v) |
arch/ppc64/kernel/rtas.c
| ... | ... | @@ -91,6 +91,123 @@ |
| 91 | 91 | } |
| 92 | 92 | } |
| 93 | 93 | |
| 94 | +void | |
| 95 | +rtas_progress(char *s, unsigned short hex) | |
| 96 | +{ | |
| 97 | + struct device_node *root; | |
| 98 | + int width, *p; | |
| 99 | + char *os; | |
| 100 | + static int display_character, set_indicator; | |
| 101 | + static int display_width, display_lines, *row_width, form_feed; | |
| 102 | + static DEFINE_SPINLOCK(progress_lock); | |
| 103 | + static int current_line; | |
| 104 | + static int pending_newline = 0; /* did last write end with unprinted newline? */ | |
| 105 | + | |
| 106 | + if (!rtas.base) | |
| 107 | + return; | |
| 108 | + | |
| 109 | + if (display_width == 0) { | |
| 110 | + display_width = 0x10; | |
| 111 | + if ((root = find_path_device("/rtas"))) { | |
| 112 | + if ((p = (unsigned int *)get_property(root, | |
| 113 | + "ibm,display-line-length", NULL))) | |
| 114 | + display_width = *p; | |
| 115 | + if ((p = (unsigned int *)get_property(root, | |
| 116 | + "ibm,form-feed", NULL))) | |
| 117 | + form_feed = *p; | |
| 118 | + if ((p = (unsigned int *)get_property(root, | |
| 119 | + "ibm,display-number-of-lines", NULL))) | |
| 120 | + display_lines = *p; | |
| 121 | + row_width = (unsigned int *)get_property(root, | |
| 122 | + "ibm,display-truncation-length", NULL); | |
| 123 | + } | |
| 124 | + display_character = rtas_token("display-character"); | |
| 125 | + set_indicator = rtas_token("set-indicator"); | |
| 126 | + } | |
| 127 | + | |
| 128 | + if (display_character == RTAS_UNKNOWN_SERVICE) { | |
| 129 | + /* use hex display if available */ | |
| 130 | + if (set_indicator != RTAS_UNKNOWN_SERVICE) | |
| 131 | + rtas_call(set_indicator, 3, 1, NULL, 6, 0, hex); | |
| 132 | + return; | |
| 133 | + } | |
| 134 | + | |
| 135 | + spin_lock(&progress_lock); | |
| 136 | + | |
| 137 | + /* | |
| 138 | + * Last write ended with newline, but we didn't print it since | |
| 139 | + * it would just clear the bottom line of output. Print it now | |
| 140 | + * instead. | |
| 141 | + * | |
| 142 | + * If no newline is pending and form feed is supported, clear the | |
| 143 | + * display with a form feed; otherwise, print a CR to start output | |
| 144 | + * at the beginning of the line. | |
| 145 | + */ | |
| 146 | + if (pending_newline) { | |
| 147 | + rtas_call(display_character, 1, 1, NULL, '\r'); | |
| 148 | + rtas_call(display_character, 1, 1, NULL, '\n'); | |
| 149 | + pending_newline = 0; | |
| 150 | + } else { | |
| 151 | + current_line = 0; | |
| 152 | + if (form_feed) | |
| 153 | + rtas_call(display_character, 1, 1, NULL, | |
| 154 | + (char)form_feed); | |
| 155 | + else | |
| 156 | + rtas_call(display_character, 1, 1, NULL, '\r'); | |
| 157 | + } | |
| 158 | + | |
| 159 | + if (row_width) | |
| 160 | + width = row_width[current_line]; | |
| 161 | + else | |
| 162 | + width = display_width; | |
| 163 | + os = s; | |
| 164 | + while (*os) { | |
| 165 | + if (*os == '\n' || *os == '\r') { | |
| 166 | + /* If newline is the last character, save it | |
| 167 | + * until next call to avoid bumping up the | |
| 168 | + * display output. | |
| 169 | + */ | |
| 170 | + if (*os == '\n' && !os[1]) { | |
| 171 | + pending_newline = 1; | |
| 172 | + current_line++; | |
| 173 | + if (current_line > display_lines-1) | |
| 174 | + current_line = display_lines-1; | |
| 175 | + spin_unlock(&progress_lock); | |
| 176 | + return; | |
| 177 | + } | |
| 178 | + | |
| 179 | + /* RTAS wants CR-LF, not just LF */ | |
| 180 | + | |
| 181 | + if (*os == '\n') { | |
| 182 | + rtas_call(display_character, 1, 1, NULL, '\r'); | |
| 183 | + rtas_call(display_character, 1, 1, NULL, '\n'); | |
| 184 | + } else { | |
| 185 | + /* CR might be used to re-draw a line, so we'll | |
| 186 | + * leave it alone and not add LF. | |
| 187 | + */ | |
| 188 | + rtas_call(display_character, 1, 1, NULL, *os); | |
| 189 | + } | |
| 190 | + | |
| 191 | + if (row_width) | |
| 192 | + width = row_width[current_line]; | |
| 193 | + else | |
| 194 | + width = display_width; | |
| 195 | + } else { | |
| 196 | + width--; | |
| 197 | + rtas_call(display_character, 1, 1, NULL, *os); | |
| 198 | + } | |
| 199 | + | |
| 200 | + os++; | |
| 201 | + | |
| 202 | + /* if we overwrite the screen length */ | |
| 203 | + if (width <= 0) | |
| 204 | + while ((*os != 0) && (*os != '\n') && (*os != '\r')) | |
| 205 | + os++; | |
| 206 | + } | |
| 207 | + | |
| 208 | + spin_unlock(&progress_lock); | |
| 209 | +} | |
| 210 | + | |
| 94 | 211 | int |
| 95 | 212 | rtas_token(const char *service) |
| 96 | 213 | { |
| ... | ... | @@ -425,8 +542,8 @@ |
| 425 | 542 | |
| 426 | 543 | printk(KERN_ALERT "FLASH: flash image is %ld bytes\n", image_size); |
| 427 | 544 | printk(KERN_ALERT "FLASH: performing flash and reboot\n"); |
| 428 | - ppc_md.progress("Flashing \n", 0x0); | |
| 429 | - ppc_md.progress("Please Wait... ", 0x0); | |
| 545 | + rtas_progress("Flashing \n", 0x0); | |
| 546 | + rtas_progress("Please Wait... ", 0x0); | |
| 430 | 547 | printk(KERN_ALERT "FLASH: this will take several minutes. Do not power off!\n"); |
| 431 | 548 | status = rtas_call(update_token, 1, 1, NULL, rtas_block_list); |
| 432 | 549 | switch (status) { /* should only get "bad" status */ |
arch/ppc64/kernel/rtas_pci.c
| 1 | +/* | |
| 2 | + * arch/ppc64/kernel/rtas_pci.c | |
| 3 | + * | |
| 4 | + * Copyright (C) 2001 Dave Engebretsen, IBM Corporation | |
| 5 | + * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM | |
| 6 | + * | |
| 7 | + * RTAS specific routines for PCI. | |
| 8 | + * | |
| 9 | + * Based on code from pci.c, chrp_pci.c and pSeries_pci.c | |
| 10 | + * | |
| 11 | + * This program is free software; you can redistribute it and/or modify | |
| 12 | + * it under the terms of the GNU General Public License as published by | |
| 13 | + * the Free Software Foundation; either version 2 of the License, or | |
| 14 | + * (at your option) any later version. | |
| 15 | + * | |
| 16 | + * This program is distributed in the hope that it will be useful, | |
| 17 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 18 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 19 | + * GNU General Public License for more details. | |
| 20 | + * | |
| 21 | + * You should have received a copy of the GNU General Public License | |
| 22 | + * along with this program; if not, write to the Free Software | |
| 23 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| 24 | + */ | |
| 25 | + | |
| 26 | +#include <linux/kernel.h> | |
| 27 | +#include <linux/threads.h> | |
| 28 | +#include <linux/pci.h> | |
| 29 | +#include <linux/string.h> | |
| 30 | +#include <linux/init.h> | |
| 31 | +#include <linux/bootmem.h> | |
| 32 | + | |
| 33 | +#include <asm/io.h> | |
| 34 | +#include <asm/pgtable.h> | |
| 35 | +#include <asm/irq.h> | |
| 36 | +#include <asm/prom.h> | |
| 37 | +#include <asm/machdep.h> | |
| 38 | +#include <asm/pci-bridge.h> | |
| 39 | +#include <asm/iommu.h> | |
| 40 | +#include <asm/rtas.h> | |
| 41 | + | |
| 42 | +#include "mpic.h" | |
| 43 | +#include "pci.h" | |
| 44 | + | |
| 45 | +/* RTAS tokens */ | |
| 46 | +static int read_pci_config; | |
| 47 | +static int write_pci_config; | |
| 48 | +static int ibm_read_pci_config; | |
| 49 | +static int ibm_write_pci_config; | |
| 50 | + | |
| 51 | +static int config_access_valid(struct device_node *dn, int where) | |
| 52 | +{ | |
| 53 | + if (where < 256) | |
| 54 | + return 1; | |
| 55 | + if (where < 4096 && dn->pci_ext_config_space) | |
| 56 | + return 1; | |
| 57 | + | |
| 58 | + return 0; | |
| 59 | +} | |
| 60 | + | |
| 61 | +static int rtas_read_config(struct device_node *dn, int where, int size, u32 *val) | |
| 62 | +{ | |
| 63 | + int returnval = -1; | |
| 64 | + unsigned long buid, addr; | |
| 65 | + int ret; | |
| 66 | + | |
| 67 | + if (!dn) | |
| 68 | + return PCIBIOS_DEVICE_NOT_FOUND; | |
| 69 | + if (!config_access_valid(dn, where)) | |
| 70 | + return PCIBIOS_BAD_REGISTER_NUMBER; | |
| 71 | + | |
| 72 | + addr = ((where & 0xf00) << 20) | (dn->busno << 16) | | |
| 73 | + (dn->devfn << 8) | (where & 0xff); | |
| 74 | + buid = dn->phb->buid; | |
| 75 | + if (buid) { | |
| 76 | + ret = rtas_call(ibm_read_pci_config, 4, 2, &returnval, | |
| 77 | + addr, buid >> 32, buid & 0xffffffff, size); | |
| 78 | + } else { | |
| 79 | + ret = rtas_call(read_pci_config, 2, 2, &returnval, addr, size); | |
| 80 | + } | |
| 81 | + *val = returnval; | |
| 82 | + | |
| 83 | + if (ret) | |
| 84 | + return PCIBIOS_DEVICE_NOT_FOUND; | |
| 85 | + | |
| 86 | + if (returnval == EEH_IO_ERROR_VALUE(size) | |
| 87 | + && eeh_dn_check_failure (dn, NULL)) | |
| 88 | + return PCIBIOS_DEVICE_NOT_FOUND; | |
| 89 | + | |
| 90 | + return PCIBIOS_SUCCESSFUL; | |
| 91 | +} | |
| 92 | + | |
| 93 | +static int rtas_pci_read_config(struct pci_bus *bus, | |
| 94 | + unsigned int devfn, | |
| 95 | + int where, int size, u32 *val) | |
| 96 | +{ | |
| 97 | + struct device_node *busdn, *dn; | |
| 98 | + | |
| 99 | + if (bus->self) | |
| 100 | + busdn = pci_device_to_OF_node(bus->self); | |
| 101 | + else | |
| 102 | + busdn = bus->sysdata; /* must be a phb */ | |
| 103 | + | |
| 104 | + /* Search only direct children of the bus */ | |
| 105 | + for (dn = busdn->child; dn; dn = dn->sibling) | |
| 106 | + if (dn->devfn == devfn) | |
| 107 | + return rtas_read_config(dn, where, size, val); | |
| 108 | + return PCIBIOS_DEVICE_NOT_FOUND; | |
| 109 | +} | |
| 110 | + | |
| 111 | +static int rtas_write_config(struct device_node *dn, int where, int size, u32 val) | |
| 112 | +{ | |
| 113 | + unsigned long buid, addr; | |
| 114 | + int ret; | |
| 115 | + | |
| 116 | + if (!dn) | |
| 117 | + return PCIBIOS_DEVICE_NOT_FOUND; | |
| 118 | + if (!config_access_valid(dn, where)) | |
| 119 | + return PCIBIOS_BAD_REGISTER_NUMBER; | |
| 120 | + | |
| 121 | + addr = ((where & 0xf00) << 20) | (dn->busno << 16) | | |
| 122 | + (dn->devfn << 8) | (where & 0xff); | |
| 123 | + buid = dn->phb->buid; | |
| 124 | + if (buid) { | |
| 125 | + ret = rtas_call(ibm_write_pci_config, 5, 1, NULL, addr, buid >> 32, buid & 0xffffffff, size, (ulong) val); | |
| 126 | + } else { | |
| 127 | + ret = rtas_call(write_pci_config, 3, 1, NULL, addr, size, (ulong)val); | |
| 128 | + } | |
| 129 | + | |
| 130 | + if (ret) | |
| 131 | + return PCIBIOS_DEVICE_NOT_FOUND; | |
| 132 | + | |
| 133 | + return PCIBIOS_SUCCESSFUL; | |
| 134 | +} | |
| 135 | + | |
| 136 | +static int rtas_pci_write_config(struct pci_bus *bus, | |
| 137 | + unsigned int devfn, | |
| 138 | + int where, int size, u32 val) | |
| 139 | +{ | |
| 140 | + struct device_node *busdn, *dn; | |
| 141 | + | |
| 142 | + if (bus->self) | |
| 143 | + busdn = pci_device_to_OF_node(bus->self); | |
| 144 | + else | |
| 145 | + busdn = bus->sysdata; /* must be a phb */ | |
| 146 | + | |
| 147 | + /* Search only direct children of the bus */ | |
| 148 | + for (dn = busdn->child; dn; dn = dn->sibling) | |
| 149 | + if (dn->devfn == devfn) | |
| 150 | + return rtas_write_config(dn, where, size, val); | |
| 151 | + return PCIBIOS_DEVICE_NOT_FOUND; | |
| 152 | +} | |
| 153 | + | |
| 154 | +struct pci_ops rtas_pci_ops = { | |
| 155 | + rtas_pci_read_config, | |
| 156 | + rtas_pci_write_config | |
| 157 | +}; | |
| 158 | + | |
| 159 | +int is_python(struct device_node *dev) | |
| 160 | +{ | |
| 161 | + char *model = (char *)get_property(dev, "model", NULL); | |
| 162 | + | |
| 163 | + if (model && strstr(model, "Python")) | |
| 164 | + return 1; | |
| 165 | + | |
| 166 | + return 0; | |
| 167 | +} | |
| 168 | + | |
| 169 | +static int get_phb_reg_prop(struct device_node *dev, | |
| 170 | + unsigned int addr_size_words, | |
| 171 | + struct reg_property64 *reg) | |
| 172 | +{ | |
| 173 | + unsigned int *ui_ptr = NULL, len; | |
| 174 | + | |
| 175 | + /* Found a PHB, now figure out where his registers are mapped. */ | |
| 176 | + ui_ptr = (unsigned int *)get_property(dev, "reg", &len); | |
| 177 | + if (ui_ptr == NULL) | |
| 178 | + return 1; | |
| 179 | + | |
| 180 | + if (addr_size_words == 1) { | |
| 181 | + reg->address = ((struct reg_property32 *)ui_ptr)->address; | |
| 182 | + reg->size = ((struct reg_property32 *)ui_ptr)->size; | |
| 183 | + } else { | |
| 184 | + *reg = *((struct reg_property64 *)ui_ptr); | |
| 185 | + } | |
| 186 | + | |
| 187 | + return 0; | |
| 188 | +} | |
| 189 | + | |
| 190 | +static void python_countermeasures(struct device_node *dev, | |
| 191 | + unsigned int addr_size_words) | |
| 192 | +{ | |
| 193 | + struct reg_property64 reg_struct; | |
| 194 | + void __iomem *chip_regs; | |
| 195 | + volatile u32 val; | |
| 196 | + | |
| 197 | + if (get_phb_reg_prop(dev, addr_size_words, ®_struct)) | |
| 198 | + return; | |
| 199 | + | |
| 200 | + /* Python's register file is 1 MB in size. */ | |
| 201 | + chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), 0x100000); | |
| 202 | + | |
| 203 | + /* | |
| 204 | + * Firmware doesn't always clear this bit which is critical | |
| 205 | + * for good performance - Anton | |
| 206 | + */ | |
| 207 | + | |
| 208 | +#define PRG_CL_RESET_VALID 0x00010000 | |
| 209 | + | |
| 210 | + val = in_be32(chip_regs + 0xf6030); | |
| 211 | + if (val & PRG_CL_RESET_VALID) { | |
| 212 | + printk(KERN_INFO "Python workaround: "); | |
| 213 | + val &= ~PRG_CL_RESET_VALID; | |
| 214 | + out_be32(chip_regs + 0xf6030, val); | |
| 215 | + /* | |
| 216 | + * We must read it back for changes to | |
| 217 | + * take effect | |
| 218 | + */ | |
| 219 | + val = in_be32(chip_regs + 0xf6030); | |
| 220 | + printk("reg0: %x\n", val); | |
| 221 | + } | |
| 222 | + | |
| 223 | + iounmap(chip_regs); | |
| 224 | +} | |
| 225 | + | |
| 226 | +void __init init_pci_config_tokens (void) | |
| 227 | +{ | |
| 228 | + read_pci_config = rtas_token("read-pci-config"); | |
| 229 | + write_pci_config = rtas_token("write-pci-config"); | |
| 230 | + ibm_read_pci_config = rtas_token("ibm,read-pci-config"); | |
| 231 | + ibm_write_pci_config = rtas_token("ibm,write-pci-config"); | |
| 232 | +} | |
| 233 | + | |
| 234 | +unsigned long __devinit get_phb_buid (struct device_node *phb) | |
| 235 | +{ | |
| 236 | + int addr_cells; | |
| 237 | + unsigned int *buid_vals; | |
| 238 | + unsigned int len; | |
| 239 | + unsigned long buid; | |
| 240 | + | |
| 241 | + if (ibm_read_pci_config == -1) return 0; | |
| 242 | + | |
| 243 | + /* PHB's will always be children of the root node, | |
| 244 | + * or so it is promised by the current firmware. */ | |
| 245 | + if (phb->parent == NULL) | |
| 246 | + return 0; | |
| 247 | + if (phb->parent->parent) | |
| 248 | + return 0; | |
| 249 | + | |
| 250 | + buid_vals = (unsigned int *) get_property(phb, "reg", &len); | |
| 251 | + if (buid_vals == NULL) | |
| 252 | + return 0; | |
| 253 | + | |
| 254 | + addr_cells = prom_n_addr_cells(phb); | |
| 255 | + if (addr_cells == 1) { | |
| 256 | + buid = (unsigned long) buid_vals[0]; | |
| 257 | + } else { | |
| 258 | + buid = (((unsigned long)buid_vals[0]) << 32UL) | | |
| 259 | + (((unsigned long)buid_vals[1]) & 0xffffffff); | |
| 260 | + } | |
| 261 | + return buid; | |
| 262 | +} | |
| 263 | + | |
| 264 | +static int phb_set_bus_ranges(struct device_node *dev, | |
| 265 | + struct pci_controller *phb) | |
| 266 | +{ | |
| 267 | + int *bus_range; | |
| 268 | + unsigned int len; | |
| 269 | + | |
| 270 | + bus_range = (int *) get_property(dev, "bus-range", &len); | |
| 271 | + if (bus_range == NULL || len < 2 * sizeof(int)) { | |
| 272 | + return 1; | |
| 273 | + } | |
| 274 | + | |
| 275 | + phb->first_busno = bus_range[0]; | |
| 276 | + phb->last_busno = bus_range[1]; | |
| 277 | + | |
| 278 | + return 0; | |
| 279 | +} | |
| 280 | + | |
| 281 | +static int __devinit setup_phb(struct device_node *dev, | |
| 282 | + struct pci_controller *phb, | |
| 283 | + unsigned int addr_size_words) | |
| 284 | +{ | |
| 285 | + pci_setup_pci_controller(phb); | |
| 286 | + | |
| 287 | + if (is_python(dev)) | |
| 288 | + python_countermeasures(dev, addr_size_words); | |
| 289 | + | |
| 290 | + if (phb_set_bus_ranges(dev, phb)) | |
| 291 | + return 1; | |
| 292 | + | |
| 293 | + phb->arch_data = dev; | |
| 294 | + phb->ops = &rtas_pci_ops; | |
| 295 | + phb->buid = get_phb_buid(dev); | |
| 296 | + | |
| 297 | + return 0; | |
| 298 | +} | |
| 299 | + | |
| 300 | +static void __devinit add_linux_pci_domain(struct device_node *dev, | |
| 301 | + struct pci_controller *phb, | |
| 302 | + struct property *of_prop) | |
| 303 | +{ | |
| 304 | + memset(of_prop, 0, sizeof(struct property)); | |
| 305 | + of_prop->name = "linux,pci-domain"; | |
| 306 | + of_prop->length = sizeof(phb->global_number); | |
| 307 | + of_prop->value = (unsigned char *)&of_prop[1]; | |
| 308 | + memcpy(of_prop->value, &phb->global_number, sizeof(phb->global_number)); | |
| 309 | + prom_add_property(dev, of_prop); | |
| 310 | +} | |
| 311 | + | |
| 312 | +static struct pci_controller * __init alloc_phb(struct device_node *dev, | |
| 313 | + unsigned int addr_size_words) | |
| 314 | +{ | |
| 315 | + struct pci_controller *phb; | |
| 316 | + struct property *of_prop; | |
| 317 | + | |
| 318 | + phb = alloc_bootmem(sizeof(struct pci_controller)); | |
| 319 | + if (phb == NULL) | |
| 320 | + return NULL; | |
| 321 | + | |
| 322 | + of_prop = alloc_bootmem(sizeof(struct property) + | |
| 323 | + sizeof(phb->global_number)); | |
| 324 | + if (!of_prop) | |
| 325 | + return NULL; | |
| 326 | + | |
| 327 | + if (setup_phb(dev, phb, addr_size_words)) | |
| 328 | + return NULL; | |
| 329 | + | |
| 330 | + add_linux_pci_domain(dev, phb, of_prop); | |
| 331 | + | |
| 332 | + return phb; | |
| 333 | +} | |
| 334 | + | |
| 335 | +static struct pci_controller * __devinit alloc_phb_dynamic(struct device_node *dev, unsigned int addr_size_words) | |
| 336 | +{ | |
| 337 | + struct pci_controller *phb; | |
| 338 | + | |
| 339 | + phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller), | |
| 340 | + GFP_KERNEL); | |
| 341 | + if (phb == NULL) | |
| 342 | + return NULL; | |
| 343 | + | |
| 344 | + if (setup_phb(dev, phb, addr_size_words)) | |
| 345 | + return NULL; | |
| 346 | + | |
| 347 | + phb->is_dynamic = 1; | |
| 348 | + | |
| 349 | + /* TODO: linux,pci-domain? */ | |
| 350 | + | |
| 351 | + return phb; | |
| 352 | +} | |
| 353 | + | |
| 354 | +unsigned long __init find_and_init_phbs(void) | |
| 355 | +{ | |
| 356 | + struct device_node *node; | |
| 357 | + struct pci_controller *phb; | |
| 358 | + unsigned int root_size_cells = 0; | |
| 359 | + unsigned int index; | |
| 360 | + unsigned int *opprop = NULL; | |
| 361 | + struct device_node *root = of_find_node_by_path("/"); | |
| 362 | + | |
| 363 | + if (ppc64_interrupt_controller == IC_OPEN_PIC) { | |
| 364 | + opprop = (unsigned int *)get_property(root, | |
| 365 | + "platform-open-pic", NULL); | |
| 366 | + } | |
| 367 | + | |
| 368 | + root_size_cells = prom_n_size_cells(root); | |
| 369 | + | |
| 370 | + index = 0; | |
| 371 | + | |
| 372 | + for (node = of_get_next_child(root, NULL); | |
| 373 | + node != NULL; | |
| 374 | + node = of_get_next_child(root, node)) { | |
| 375 | + if (node->type == NULL || strcmp(node->type, "pci") != 0) | |
| 376 | + continue; | |
| 377 | + | |
| 378 | + phb = alloc_phb(node, root_size_cells); | |
| 379 | + if (!phb) | |
| 380 | + continue; | |
| 381 | + | |
| 382 | + pci_process_bridge_OF_ranges(phb, node); | |
| 383 | + pci_setup_phb_io(phb, index == 0); | |
| 384 | +#ifdef CONFIG_PPC_PSERIES | |
| 385 | + if (ppc64_interrupt_controller == IC_OPEN_PIC && pSeries_mpic) { | |
| 386 | + int addr = root_size_cells * (index + 2) - 1; | |
| 387 | + mpic_assign_isu(pSeries_mpic, index, opprop[addr]); | |
| 388 | + } | |
| 389 | +#endif | |
| 390 | + index++; | |
| 391 | + } | |
| 392 | + | |
| 393 | + of_node_put(root); | |
| 394 | + pci_devs_phb_init(); | |
| 395 | + | |
| 396 | + /* | |
| 397 | + * pci_probe_only and pci_assign_all_buses can be set via properties | |
| 398 | + * in chosen. | |
| 399 | + */ | |
| 400 | + if (of_chosen) { | |
| 401 | + int *prop; | |
| 402 | + | |
| 403 | + prop = (int *)get_property(of_chosen, "linux,pci-probe-only", | |
| 404 | + NULL); | |
| 405 | + if (prop) | |
| 406 | + pci_probe_only = *prop; | |
| 407 | + | |
| 408 | + prop = (int *)get_property(of_chosen, | |
| 409 | + "linux,pci-assign-all-buses", NULL); | |
| 410 | + if (prop) | |
| 411 | + pci_assign_all_buses = *prop; | |
| 412 | + } | |
| 413 | + | |
| 414 | + return 0; | |
| 415 | +} | |
| 416 | + | |
| 417 | +struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) | |
| 418 | +{ | |
| 419 | + struct device_node *root = of_find_node_by_path("/"); | |
| 420 | + unsigned int root_size_cells = 0; | |
| 421 | + struct pci_controller *phb; | |
| 422 | + struct pci_bus *bus; | |
| 423 | + int primary; | |
| 424 | + | |
| 425 | + root_size_cells = prom_n_size_cells(root); | |
| 426 | + | |
| 427 | + primary = list_empty(&hose_list); | |
| 428 | + phb = alloc_phb_dynamic(dn, root_size_cells); | |
| 429 | + if (!phb) | |
| 430 | + return NULL; | |
| 431 | + | |
| 432 | + pci_process_bridge_OF_ranges(phb, dn); | |
| 433 | + | |
| 434 | + pci_setup_phb_io_dynamic(phb, primary); | |
| 435 | + of_node_put(root); | |
| 436 | + | |
| 437 | + pci_devs_phb_init_dynamic(phb); | |
| 438 | + phb->last_busno = 0xff; | |
| 439 | + bus = pci_scan_bus(phb->first_busno, phb->ops, phb->arch_data); | |
| 440 | + phb->bus = bus; | |
| 441 | + phb->last_busno = bus->subordinate; | |
| 442 | + | |
| 443 | + return phb; | |
| 444 | +} | |
| 445 | +EXPORT_SYMBOL(init_phb_dynamic); | |
| 446 | + | |
| 447 | +/* RPA-specific bits for removing PHBs */ | |
| 448 | +int pcibios_remove_root_bus(struct pci_controller *phb) | |
| 449 | +{ | |
| 450 | + struct pci_bus *b = phb->bus; | |
| 451 | + struct resource *res; | |
| 452 | + int rc, i; | |
| 453 | + | |
| 454 | + res = b->resource[0]; | |
| 455 | + if (!res->flags) { | |
| 456 | + printk(KERN_ERR "%s: no IO resource for PHB %s\n", __FUNCTION__, | |
| 457 | + b->name); | |
| 458 | + return 1; | |
| 459 | + } | |
| 460 | + | |
| 461 | + rc = unmap_bus_range(b); | |
| 462 | + if (rc) { | |
| 463 | + printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", | |
| 464 | + __FUNCTION__, b->name); | |
| 465 | + return 1; | |
| 466 | + } | |
| 467 | + | |
| 468 | + if (release_resource(res)) { | |
| 469 | + printk(KERN_ERR "%s: failed to release IO on bus %s\n", | |
| 470 | + __FUNCTION__, b->name); | |
| 471 | + return 1; | |
| 472 | + } | |
| 473 | + | |
| 474 | + for (i = 1; i < 3; ++i) { | |
| 475 | + res = b->resource[i]; | |
| 476 | + if (!res->flags && i == 0) { | |
| 477 | + printk(KERN_ERR "%s: no MEM resource for PHB %s\n", | |
| 478 | + __FUNCTION__, b->name); | |
| 479 | + return 1; | |
| 480 | + } | |
| 481 | + if (res->flags && release_resource(res)) { | |
| 482 | + printk(KERN_ERR | |
| 483 | + "%s: failed to release IO %d on bus %s\n", | |
| 484 | + __FUNCTION__, i, b->name); | |
| 485 | + return 1; | |
| 486 | + } | |
| 487 | + } | |
| 488 | + | |
| 489 | + list_del(&phb->list_node); | |
| 490 | + if (phb->is_dynamic) | |
| 491 | + kfree(phb); | |
| 492 | + | |
| 493 | + return 0; | |
| 494 | +} | |
| 495 | +EXPORT_SYMBOL(pcibios_remove_root_bus); |
arch/ppc64/kernel/rtc.c
| ... | ... | @@ -301,7 +301,7 @@ |
| 301 | 301 | #ifdef CONFIG_PPC_RTAS |
| 302 | 302 | #define MAX_RTC_WAIT 5000 /* 5 sec */ |
| 303 | 303 | #define RTAS_CLOCK_BUSY (-2) |
| 304 | -void pSeries_get_boot_time(struct rtc_time *rtc_tm) | |
| 304 | +void rtas_get_boot_time(struct rtc_time *rtc_tm) | |
| 305 | 305 | { |
| 306 | 306 | int ret[8]; |
| 307 | 307 | int error, wait_time; |
| ... | ... | @@ -336,7 +336,7 @@ |
| 336 | 336 | * and if a delay is needed to read the clock. In this case we just |
| 337 | 337 | * silently return without updating rtc_tm. |
| 338 | 338 | */ |
| 339 | -void pSeries_get_rtc_time(struct rtc_time *rtc_tm) | |
| 339 | +void rtas_get_rtc_time(struct rtc_time *rtc_tm) | |
| 340 | 340 | { |
| 341 | 341 | int ret[8]; |
| 342 | 342 | int error, wait_time; |
| ... | ... | @@ -371,7 +371,7 @@ |
| 371 | 371 | rtc_tm->tm_year = ret[0] - 1900; |
| 372 | 372 | } |
| 373 | 373 | |
| 374 | -int pSeries_set_rtc_time(struct rtc_time *tm) | |
| 374 | +int rtas_set_rtc_time(struct rtc_time *tm) | |
| 375 | 375 | { |
| 376 | 376 | int error, wait_time; |
| 377 | 377 | unsigned long max_wait_tb; |
arch/ppc64/kernel/setup.c
| ... | ... | @@ -344,6 +344,7 @@ |
| 344 | 344 | extern struct machdep_calls pSeries_md; |
| 345 | 345 | extern struct machdep_calls pmac_md; |
| 346 | 346 | extern struct machdep_calls maple_md; |
| 347 | +extern struct machdep_calls bpa_md; | |
| 347 | 348 | |
| 348 | 349 | /* Ultimately, stuff them in an elf section like initcalls... */ |
| 349 | 350 | static struct machdep_calls __initdata *machines[] = { |
| ... | ... | @@ -356,6 +357,9 @@ |
| 356 | 357 | #ifdef CONFIG_PPC_MAPLE |
| 357 | 358 | &maple_md, |
| 358 | 359 | #endif /* CONFIG_PPC_MAPLE */ |
| 360 | +#ifdef CONFIG_PPC_BPA | |
| 361 | + &bpa_md, | |
| 362 | +#endif | |
| 359 | 363 | NULL |
| 360 | 364 | }; |
| 361 | 365 | |
| ... | ... | @@ -679,6 +683,12 @@ |
| 679 | 683 | if (ppc_md.nvram_sync) |
| 680 | 684 | ppc_md.nvram_sync(); |
| 681 | 685 | ppc_md.restart(cmd); |
| 686 | +#ifdef CONFIG_SMP | |
| 687 | + smp_send_stop(); | |
| 688 | +#endif | |
| 689 | + printk(KERN_EMERG "System Halted, OK to turn off power\n"); | |
| 690 | + local_irq_disable(); | |
| 691 | + while (1) ; | |
| 682 | 692 | } |
| 683 | 693 | |
| 684 | 694 | EXPORT_SYMBOL(machine_restart); |
| ... | ... | @@ -688,6 +698,12 @@ |
| 688 | 698 | if (ppc_md.nvram_sync) |
| 689 | 699 | ppc_md.nvram_sync(); |
| 690 | 700 | ppc_md.power_off(); |
| 701 | +#ifdef CONFIG_SMP | |
| 702 | + smp_send_stop(); | |
| 703 | +#endif | |
| 704 | + printk(KERN_EMERG "System Halted, OK to turn off power\n"); | |
| 705 | + local_irq_disable(); | |
| 706 | + while (1) ; | |
| 691 | 707 | } |
| 692 | 708 | |
| 693 | 709 | EXPORT_SYMBOL(machine_power_off); |
| 694 | 710 | |
| ... | ... | @@ -697,13 +713,16 @@ |
| 697 | 713 | if (ppc_md.nvram_sync) |
| 698 | 714 | ppc_md.nvram_sync(); |
| 699 | 715 | ppc_md.halt(); |
| 716 | +#ifdef CONFIG_SMP | |
| 717 | + smp_send_stop(); | |
| 718 | +#endif | |
| 719 | + printk(KERN_EMERG "System Halted, OK to turn off power\n"); | |
| 720 | + local_irq_disable(); | |
| 721 | + while (1) ; | |
| 700 | 722 | } |
| 701 | 723 | |
| 702 | 724 | EXPORT_SYMBOL(machine_halt); |
| 703 | 725 | |
| 704 | -unsigned long ppc_proc_freq; | |
| 705 | -unsigned long ppc_tb_freq; | |
| 706 | - | |
| 707 | 726 | static int ppc64_panic_event(struct notifier_block *this, |
| 708 | 727 | unsigned long event, void *ptr) |
| 709 | 728 | { |
| 710 | 729 | |
| 711 | 730 | |
| ... | ... | @@ -1080,11 +1099,11 @@ |
| 1080 | 1099 | static void ppc64_do_msg(unsigned int src, const char *msg) |
| 1081 | 1100 | { |
| 1082 | 1101 | if (ppc_md.progress) { |
| 1083 | - char buf[32]; | |
| 1102 | + char buf[128]; | |
| 1084 | 1103 | |
| 1085 | - sprintf(buf, "%08x \n", src); | |
| 1104 | + sprintf(buf, "%08X\n", src); | |
| 1086 | 1105 | ppc_md.progress(buf, 0); |
| 1087 | - sprintf(buf, "%-16s", msg); | |
| 1106 | + snprintf(buf, 128, "%s", msg); | |
| 1088 | 1107 | ppc_md.progress(buf, 0); |
| 1089 | 1108 | } |
| 1090 | 1109 | } |
| ... | ... | @@ -1118,7 +1137,7 @@ |
| 1118 | 1137 | } |
| 1119 | 1138 | |
| 1120 | 1139 | /* This should only be called on processor 0 during calibrate decr */ |
| 1121 | -void setup_default_decr(void) | |
| 1140 | +void __init setup_default_decr(void) | |
| 1122 | 1141 | { |
| 1123 | 1142 | struct paca_struct *lpaca = get_paca(); |
| 1124 | 1143 |
arch/ppc64/kernel/smp.c
| ... | ... | @@ -71,7 +71,7 @@ |
| 71 | 71 | |
| 72 | 72 | int smt_enabled_at_boot = 1; |
| 73 | 73 | |
| 74 | -#ifdef CONFIG_PPC_MULTIPLATFORM | |
| 74 | +#ifdef CONFIG_MPIC | |
| 75 | 75 | void smp_mpic_message_pass(int target, int msg) |
| 76 | 76 | { |
| 77 | 77 | /* make sure we're sending something that translates to an IPI */ |
| ... | ... | @@ -128,7 +128,7 @@ |
| 128 | 128 | smp_mb(); |
| 129 | 129 | } |
| 130 | 130 | |
| 131 | -#endif /* CONFIG_PPC_MULTIPLATFORM */ | |
| 131 | +#endif /* CONFIG_MPIC */ | |
| 132 | 132 | |
| 133 | 133 | static void __init smp_space_timers(unsigned int max_cpus) |
| 134 | 134 | { |
arch/ppc64/kernel/spider-pic.c
| 1 | +/* | |
| 2 | + * External Interrupt Controller on Spider South Bridge | |
| 3 | + * | |
| 4 | + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 | |
| 5 | + * | |
| 6 | + * Author: Arnd Bergmann <arndb@de.ibm.com> | |
| 7 | + * | |
| 8 | + * This program is free software; you can redistribute it and/or modify | |
| 9 | + * it under the terms of the GNU General Public License as published by | |
| 10 | + * the Free Software Foundation; either version 2, or (at your option) | |
| 11 | + * any later version. | |
| 12 | + * | |
| 13 | + * This program is distributed in the hope that it will be useful, | |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 16 | + * GNU General Public License for more details. | |
| 17 | + * | |
| 18 | + * You should have received a copy of the GNU General Public License | |
| 19 | + * along with this program; if not, write to the Free Software | |
| 20 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 21 | + */ | |
| 22 | + | |
| 23 | +#include <linux/interrupt.h> | |
| 24 | +#include <linux/irq.h> | |
| 25 | + | |
| 26 | +#include <asm/pgtable.h> | |
| 27 | +#include <asm/prom.h> | |
| 28 | +#include <asm/io.h> | |
| 29 | + | |
| 30 | +#include "bpa_iic.h" | |
| 31 | + | |
| 32 | +/* register layout taken from Spider spec, table 7.4-4 */ | |
| 33 | +enum { | |
| 34 | + TIR_DEN = 0x004, /* Detection Enable Register */ | |
| 35 | + TIR_MSK = 0x084, /* Mask Level Register */ | |
| 36 | + TIR_EDC = 0x0c0, /* Edge Detection Clear Register */ | |
| 37 | + TIR_PNDA = 0x100, /* Pending Register A */ | |
| 38 | + TIR_PNDB = 0x104, /* Pending Register B */ | |
| 39 | + TIR_CS = 0x144, /* Current Status Register */ | |
| 40 | + TIR_LCSA = 0x150, /* Level Current Status Register A */ | |
| 41 | + TIR_LCSB = 0x154, /* Level Current Status Register B */ | |
| 42 | + TIR_LCSC = 0x158, /* Level Current Status Register C */ | |
| 43 | + TIR_LCSD = 0x15c, /* Level Current Status Register D */ | |
| 44 | + TIR_CFGA = 0x200, /* Setting Register A0 */ | |
| 45 | + TIR_CFGB = 0x204, /* Setting Register B0 */ | |
| 46 | + /* 0x208 ... 0x3ff Setting Register An/Bn */ | |
| 47 | + TIR_PPNDA = 0x400, /* Packet Pending Register A */ | |
| 48 | + TIR_PPNDB = 0x404, /* Packet Pending Register B */ | |
| 49 | + TIR_PIERA = 0x408, /* Packet Output Error Register A */ | |
| 50 | + TIR_PIERB = 0x40c, /* Packet Output Error Register B */ | |
| 51 | + TIR_PIEN = 0x444, /* Packet Output Enable Register */ | |
| 52 | + TIR_PIPND = 0x454, /* Packet Output Pending Register */ | |
| 53 | + TIRDID = 0x484, /* Spider Device ID Register */ | |
| 54 | + REISTIM = 0x500, /* Reissue Command Timeout Time Setting */ | |
| 55 | + REISTIMEN = 0x504, /* Reissue Command Timeout Setting */ | |
| 56 | + REISWAITEN = 0x508, /* Reissue Wait Control*/ | |
| 57 | +}; | |
| 58 | + | |
| 59 | +static void __iomem *spider_pics[4]; | |
| 60 | + | |
| 61 | +static void __iomem *spider_get_pic(int irq) | |
| 62 | +{ | |
| 63 | + int node = irq / IIC_NODE_STRIDE; | |
| 64 | + irq %= IIC_NODE_STRIDE; | |
| 65 | + | |
| 66 | + if (irq >= IIC_EXT_OFFSET && | |
| 67 | + irq < IIC_EXT_OFFSET + IIC_NUM_EXT && | |
| 68 | + spider_pics) | |
| 69 | + return spider_pics[node]; | |
| 70 | + return NULL; | |
| 71 | +} | |
| 72 | + | |
| 73 | +static int spider_get_nr(unsigned int irq) | |
| 74 | +{ | |
| 75 | + return (irq % IIC_NODE_STRIDE) - IIC_EXT_OFFSET; | |
| 76 | +} | |
| 77 | + | |
| 78 | +static void __iomem *spider_get_irq_config(int irq) | |
| 79 | +{ | |
| 80 | + void __iomem *pic; | |
| 81 | + pic = spider_get_pic(irq); | |
| 82 | + return pic + TIR_CFGA + 8 * spider_get_nr(irq); | |
| 83 | +} | |
| 84 | + | |
| 85 | +static void spider_enable_irq(unsigned int irq) | |
| 86 | +{ | |
| 87 | + void __iomem *cfg = spider_get_irq_config(irq); | |
| 88 | + irq = spider_get_nr(irq); | |
| 89 | + | |
| 90 | + out_be32(cfg, in_be32(cfg) | 0x3107000eu); | |
| 91 | + out_be32(cfg + 4, in_be32(cfg + 4) | 0x00020000u | irq); | |
| 92 | +} | |
| 93 | + | |
| 94 | +static void spider_disable_irq(unsigned int irq) | |
| 95 | +{ | |
| 96 | + void __iomem *cfg = spider_get_irq_config(irq); | |
| 97 | + irq = spider_get_nr(irq); | |
| 98 | + | |
| 99 | + out_be32(cfg, in_be32(cfg) & ~0x30000000u); | |
| 100 | +} | |
| 101 | + | |
| 102 | +static unsigned int spider_startup_irq(unsigned int irq) | |
| 103 | +{ | |
| 104 | + spider_enable_irq(irq); | |
| 105 | + return 0; | |
| 106 | +} | |
| 107 | + | |
| 108 | +static void spider_shutdown_irq(unsigned int irq) | |
| 109 | +{ | |
| 110 | + spider_disable_irq(irq); | |
| 111 | +} | |
| 112 | + | |
| 113 | +static void spider_end_irq(unsigned int irq) | |
| 114 | +{ | |
| 115 | + spider_enable_irq(irq); | |
| 116 | +} | |
| 117 | + | |
| 118 | +static void spider_ack_irq(unsigned int irq) | |
| 119 | +{ | |
| 120 | + spider_disable_irq(irq); | |
| 121 | + iic_local_enable(); | |
| 122 | +} | |
| 123 | + | |
| 124 | +static struct hw_interrupt_type spider_pic = { | |
| 125 | + .typename = " SPIDER ", | |
| 126 | + .startup = spider_startup_irq, | |
| 127 | + .shutdown = spider_shutdown_irq, | |
| 128 | + .enable = spider_enable_irq, | |
| 129 | + .disable = spider_disable_irq, | |
| 130 | + .ack = spider_ack_irq, | |
| 131 | + .end = spider_end_irq, | |
| 132 | +}; | |
| 133 | + | |
| 134 | + | |
| 135 | +int spider_get_irq(unsigned long int_pending) | |
| 136 | +{ | |
| 137 | + void __iomem *regs = spider_get_pic(int_pending); | |
| 138 | + unsigned long cs; | |
| 139 | + int irq; | |
| 140 | + | |
| 141 | + cs = in_be32(regs + TIR_CS); | |
| 142 | + | |
| 143 | + irq = cs >> 24; | |
| 144 | + if (irq != 63) | |
| 145 | + return irq; | |
| 146 | + | |
| 147 | + return -1; | |
| 148 | +} | |
| 149 | + | |
| 150 | +void spider_init_IRQ(void) | |
| 151 | +{ | |
| 152 | + int node; | |
| 153 | + struct device_node *dn; | |
| 154 | + unsigned int *property; | |
| 155 | + long spiderpic; | |
| 156 | + int n; | |
| 157 | + | |
| 158 | +/* FIXME: detect multiple PICs as soon as the device tree has them */ | |
| 159 | + for (node = 0; node < 1; node++) { | |
| 160 | + dn = of_find_node_by_path("/"); | |
| 161 | + n = prom_n_addr_cells(dn); | |
| 162 | + property = (unsigned int *) get_property(dn, | |
| 163 | + "platform-spider-pic", NULL); | |
| 164 | + | |
| 165 | + if (!property) | |
| 166 | + continue; | |
| 167 | + for (spiderpic = 0; n > 0; --n) | |
| 168 | + spiderpic = (spiderpic << 32) + *property++; | |
| 169 | + printk(KERN_DEBUG "SPIDER addr: %lx\n", spiderpic); | |
| 170 | + spider_pics[node] = __ioremap(spiderpic, 0x800, _PAGE_NO_CACHE); | |
| 171 | + for (n = 0; n < IIC_NUM_EXT; n++) { | |
| 172 | + int irq = n + IIC_EXT_OFFSET + node * IIC_NODE_STRIDE; | |
| 173 | + get_irq_desc(irq)->handler = &spider_pic; | |
| 174 | + | |
| 175 | + /* do not mask any interrupts because of level */ | |
| 176 | + out_be32(spider_pics[node] + TIR_MSK, 0x0); | |
| 177 | + | |
| 178 | + /* disable edge detection clear */ | |
| 179 | + /* out_be32(spider_pics[node] + TIR_EDC, 0x0); */ | |
| 180 | + | |
| 181 | + /* enable interrupt packets to be output */ | |
| 182 | + out_be32(spider_pics[node] + TIR_PIEN, | |
| 183 | + in_be32(spider_pics[node] + TIR_PIEN) | 0x1); | |
| 184 | + | |
| 185 | + /* Enable the interrupt detection enable bit. Do this last! */ | |
| 186 | + out_be32(spider_pics[node] + TIR_DEN, | |
| 187 | + in_be32(spider_pics[node] +TIR_DEN) | 0x1); | |
| 188 | + | |
| 189 | + } | |
| 190 | + } | |
| 191 | +} |
arch/ppc64/kernel/time.c
| ... | ... | @@ -107,6 +107,9 @@ |
| 107 | 107 | |
| 108 | 108 | static unsigned adjusting_time = 0; |
| 109 | 109 | |
| 110 | +unsigned long ppc_proc_freq; | |
| 111 | +unsigned long ppc_tb_freq; | |
| 112 | + | |
| 110 | 113 | static __inline__ void timer_check_rtc(void) |
| 111 | 114 | { |
| 112 | 115 | /* |
| ... | ... | @@ -471,6 +474,66 @@ |
| 471 | 474 | } |
| 472 | 475 | |
| 473 | 476 | EXPORT_SYMBOL(do_settimeofday); |
| 477 | + | |
| 478 | +#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_MAPLE) || defined(CONFIG_PPC_BPA) | |
| 479 | +void __init generic_calibrate_decr(void) | |
| 480 | +{ | |
| 481 | + struct device_node *cpu; | |
| 482 | + struct div_result divres; | |
| 483 | + unsigned int *fp; | |
| 484 | + int node_found; | |
| 485 | + | |
| 486 | + /* | |
| 487 | + * The cpu node should have a timebase-frequency property | |
| 488 | + * to tell us the rate at which the decrementer counts. | |
| 489 | + */ | |
| 490 | + cpu = of_find_node_by_type(NULL, "cpu"); | |
| 491 | + | |
| 492 | + ppc_tb_freq = DEFAULT_TB_FREQ; /* hardcoded default */ | |
| 493 | + node_found = 0; | |
| 494 | + if (cpu != 0) { | |
| 495 | + fp = (unsigned int *)get_property(cpu, "timebase-frequency", | |
| 496 | + NULL); | |
| 497 | + if (fp != 0) { | |
| 498 | + node_found = 1; | |
| 499 | + ppc_tb_freq = *fp; | |
| 500 | + } | |
| 501 | + } | |
| 502 | + if (!node_found) | |
| 503 | + printk(KERN_ERR "WARNING: Estimating decrementer frequency " | |
| 504 | + "(not found)\n"); | |
| 505 | + | |
| 506 | + ppc_proc_freq = DEFAULT_PROC_FREQ; | |
| 507 | + node_found = 0; | |
| 508 | + if (cpu != 0) { | |
| 509 | + fp = (unsigned int *)get_property(cpu, "clock-frequency", | |
| 510 | + NULL); | |
| 511 | + if (fp != 0) { | |
| 512 | + node_found = 1; | |
| 513 | + ppc_proc_freq = *fp; | |
| 514 | + } | |
| 515 | + } | |
| 516 | + if (!node_found) | |
| 517 | + printk(KERN_ERR "WARNING: Estimating processor frequency " | |
| 518 | + "(not found)\n"); | |
| 519 | + | |
| 520 | + of_node_put(cpu); | |
| 521 | + | |
| 522 | + printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n", | |
| 523 | + ppc_tb_freq/1000000, ppc_tb_freq%1000000); | |
| 524 | + printk(KERN_INFO "time_init: processor frequency = %lu.%.6lu MHz\n", | |
| 525 | + ppc_proc_freq/1000000, ppc_proc_freq%1000000); | |
| 526 | + | |
| 527 | + tb_ticks_per_jiffy = ppc_tb_freq / HZ; | |
| 528 | + tb_ticks_per_sec = tb_ticks_per_jiffy * HZ; | |
| 529 | + tb_ticks_per_usec = ppc_tb_freq / 1000000; | |
| 530 | + tb_to_us = mulhwu_scale_factor(ppc_tb_freq, 1000000); | |
| 531 | + div128_by_32(1024*1024, 0, tb_ticks_per_sec, &divres); | |
| 532 | + tb_to_xs = divres.result_low; | |
| 533 | + | |
| 534 | + setup_default_decr(); | |
| 535 | +} | |
| 536 | +#endif | |
| 474 | 537 | |
| 475 | 538 | void __init time_init(void) |
| 476 | 539 | { |
arch/ppc64/kernel/traps.c
drivers/char/watchdog/Kconfig
| ... | ... | @@ -414,6 +414,16 @@ |
| 414 | 414 | machines. The watchdog timeout period is normally one minute but |
| 415 | 415 | can be changed with a boot-time parameter. |
| 416 | 416 | |
| 417 | +# ppc64 RTAS watchdog | |
| 418 | +config WATCHDOG_RTAS | |
| 419 | + tristate "RTAS watchdog" | |
| 420 | + depends on WATCHDOG && PPC_RTAS | |
| 421 | + help | |
| 422 | + This driver adds watchdog support for the RTAS watchdog. | |
| 423 | + | |
| 424 | + To compile this driver as a module, choose M here. The module | |
| 425 | + will be called wdrtas. | |
| 426 | + | |
| 417 | 427 | # |
| 418 | 428 | # ISA-based Watchdog Cards |
| 419 | 429 | # |
drivers/char/watchdog/Makefile
| ... | ... | @@ -33,6 +33,7 @@ |
| 33 | 33 | obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o |
| 34 | 34 | obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o |
| 35 | 35 | obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o |
| 36 | +obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o | |
| 36 | 37 | |
| 37 | 38 | # Only one watchdog can succeed. We probe the hardware watchdog |
| 38 | 39 | # drivers first, then the softdog driver. This means if your hardware |
drivers/char/watchdog/wdrtas.c
| 1 | +/* | |
| 2 | + * FIXME: add wdrtas_get_status and wdrtas_get_boot_status as soon as | |
| 3 | + * RTAS calls are available | |
| 4 | + */ | |
| 5 | + | |
| 6 | +/* | |
| 7 | + * RTAS watchdog driver | |
| 8 | + * | |
| 9 | + * (C) Copyright IBM Corp. 2005 | |
| 10 | + * device driver to exploit watchdog RTAS functions | |
| 11 | + * | |
| 12 | + * Authors : Utz Bacher <utz.bacher@de.ibm.com> | |
| 13 | + * | |
| 14 | + * This program is free software; you can redistribute it and/or modify | |
| 15 | + * it under the terms of the GNU General Public License as published by | |
| 16 | + * the Free Software Foundation; either version 2, or (at your option) | |
| 17 | + * any later version. | |
| 18 | + * | |
| 19 | + * This program is distributed in the hope that it will be useful, | |
| 20 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 21 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 22 | + * GNU General Public License for more details. | |
| 23 | + * | |
| 24 | + * You should have received a copy of the GNU General Public License | |
| 25 | + * along with this program; if not, write to the Free Software | |
| 26 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 27 | + */ | |
| 28 | + | |
| 29 | +#include <linux/config.h> | |
| 30 | +#include <linux/fs.h> | |
| 31 | +#include <linux/init.h> | |
| 32 | +#include <linux/kernel.h> | |
| 33 | +#include <linux/miscdevice.h> | |
| 34 | +#include <linux/module.h> | |
| 35 | +#include <linux/notifier.h> | |
| 36 | +#include <linux/reboot.h> | |
| 37 | +#include <linux/types.h> | |
| 38 | +#include <linux/watchdog.h> | |
| 39 | + | |
| 40 | +#include <asm/rtas.h> | |
| 41 | +#include <asm/uaccess.h> | |
| 42 | + | |
| 43 | +#define WDRTAS_MAGIC_CHAR 42 | |
| 44 | +#define WDRTAS_SUPPORTED_MASK (WDIOF_SETTIMEOUT | \ | |
| 45 | + WDIOF_MAGICCLOSE) | |
| 46 | + | |
| 47 | +MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>"); | |
| 48 | +MODULE_DESCRIPTION("RTAS watchdog driver"); | |
| 49 | +MODULE_LICENSE("GPL"); | |
| 50 | +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | |
| 51 | +MODULE_ALIAS_MISCDEV(TEMP_MINOR); | |
| 52 | + | |
| 53 | +#ifdef CONFIG_WATCHDOG_NOWAYOUT | |
| 54 | +static int wdrtas_nowayout = 1; | |
| 55 | +#else | |
| 56 | +static int wdrtas_nowayout = 0; | |
| 57 | +#endif | |
| 58 | + | |
| 59 | +static atomic_t wdrtas_miscdev_open = ATOMIC_INIT(0); | |
| 60 | +static char wdrtas_expect_close = 0; | |
| 61 | + | |
| 62 | +static int wdrtas_interval; | |
| 63 | + | |
| 64 | +#define WDRTAS_THERMAL_SENSOR 3 | |
| 65 | +static int wdrtas_token_get_sensor_state; | |
| 66 | +#define WDRTAS_SURVEILLANCE_IND 9000 | |
| 67 | +static int wdrtas_token_set_indicator; | |
| 68 | +#define WDRTAS_SP_SPI 28 | |
| 69 | +static int wdrtas_token_get_sp; | |
| 70 | +static int wdrtas_token_event_scan; | |
| 71 | + | |
| 72 | +#define WDRTAS_DEFAULT_INTERVAL 300 | |
| 73 | + | |
| 74 | +#define WDRTAS_LOGBUFFER_LEN 128 | |
| 75 | +static char wdrtas_logbuffer[WDRTAS_LOGBUFFER_LEN]; | |
| 76 | + | |
| 77 | + | |
| 78 | +/*** watchdog access functions */ | |
| 79 | + | |
| 80 | +/** | |
| 81 | + * wdrtas_set_interval - sets the watchdog interval | |
| 82 | + * @interval: new interval | |
| 83 | + * | |
| 84 | + * returns 0 on success, <0 on failures | |
| 85 | + * | |
| 86 | + * wdrtas_set_interval sets the watchdog keepalive interval by calling the | |
| 87 | + * RTAS function set-indicator (surveillance). The unit of interval is | |
| 88 | + * seconds. | |
| 89 | + */ | |
| 90 | +static int | |
| 91 | +wdrtas_set_interval(int interval) | |
| 92 | +{ | |
| 93 | + long result; | |
| 94 | + static int print_msg = 10; | |
| 95 | + | |
| 96 | + /* rtas uses minutes */ | |
| 97 | + interval = (interval + 59) / 60; | |
| 98 | + | |
| 99 | + result = rtas_call(wdrtas_token_set_indicator, 3, 1, NULL, | |
| 100 | + WDRTAS_SURVEILLANCE_IND, 0, interval); | |
| 101 | + if ( (result < 0) && (print_msg) ) { | |
| 102 | + printk(KERN_ERR "wdrtas: setting the watchdog to %i " | |
| 103 | + "timeout failed: %li\n", interval, result); | |
| 104 | + print_msg--; | |
| 105 | + } | |
| 106 | + | |
| 107 | + return result; | |
| 108 | +} | |
| 109 | + | |
| 110 | +/** | |
| 111 | + * wdrtas_get_interval - returns the current watchdog interval | |
| 112 | + * @fallback_value: value (in seconds) to use, if the RTAS call fails | |
| 113 | + * | |
| 114 | + * returns the interval | |
| 115 | + * | |
| 116 | + * wdrtas_get_interval returns the current watchdog keepalive interval | |
| 117 | + * as reported by the RTAS function ibm,get-system-parameter. The unit | |
| 118 | + * of the return value is seconds. | |
| 119 | + */ | |
| 120 | +static int | |
| 121 | +wdrtas_get_interval(int fallback_value) | |
| 122 | +{ | |
| 123 | + long result; | |
| 124 | + char value[4]; | |
| 125 | + | |
| 126 | + result = rtas_call(wdrtas_token_get_sp, 3, 1, NULL, | |
| 127 | + WDRTAS_SP_SPI, (void *)__pa(&value), 4); | |
| 128 | + if ( (value[0] != 0) || (value[1] != 2) || (value[3] != 0) || | |
| 129 | + (result < 0) ) { | |
| 130 | + printk(KERN_WARNING "wdrtas: could not get sp_spi watchdog " | |
| 131 | + "timeout (%li). Continuing\n", result); | |
| 132 | + return fallback_value; | |
| 133 | + } | |
| 134 | + | |
| 135 | + /* rtas uses minutes */ | |
| 136 | + return ((int)value[2]) * 60; | |
| 137 | +} | |
| 138 | + | |
| 139 | +/** | |
| 140 | + * wdrtas_timer_start - starts watchdog | |
| 141 | + * | |
| 142 | + * wdrtas_timer_start starts the watchdog by calling the RTAS function | |
| 143 | + * set-interval (surveillance) | |
| 144 | + */ | |
| 145 | +static void | |
| 146 | +wdrtas_timer_start(void) | |
| 147 | +{ | |
| 148 | + wdrtas_set_interval(wdrtas_interval); | |
| 149 | +} | |
| 150 | + | |
| 151 | +/** | |
| 152 | + * wdrtas_timer_stop - stops watchdog | |
| 153 | + * | |
| 154 | + * wdrtas_timer_stop stops the watchdog timer by calling the RTAS function | |
| 155 | + * set-interval (surveillance) | |
| 156 | + */ | |
| 157 | +static void | |
| 158 | +wdrtas_timer_stop(void) | |
| 159 | +{ | |
| 160 | + wdrtas_set_interval(0); | |
| 161 | +} | |
| 162 | + | |
| 163 | +/** | |
| 164 | + * wdrtas_log_scanned_event - logs an event we received during keepalive | |
| 165 | + * | |
| 166 | + * wdrtas_log_scanned_event prints a message to the log buffer dumping | |
| 167 | + * the results of the last event-scan call | |
| 168 | + */ | |
| 169 | +static void | |
| 170 | +wdrtas_log_scanned_event(void) | |
| 171 | +{ | |
| 172 | + int i; | |
| 173 | + | |
| 174 | + for (i = 0; i < WDRTAS_LOGBUFFER_LEN; i += 16) | |
| 175 | + printk(KERN_INFO "wdrtas: dumping event (line %i/%i), data = " | |
| 176 | + "%02x %02x %02x %02x %02x %02x %02x %02x " | |
| 177 | + "%02x %02x %02x %02x %02x %02x %02x %02x\n", | |
| 178 | + (i / 16) + 1, (WDRTAS_LOGBUFFER_LEN / 16), | |
| 179 | + wdrtas_logbuffer[i + 0], wdrtas_logbuffer[i + 1], | |
| 180 | + wdrtas_logbuffer[i + 2], wdrtas_logbuffer[i + 3], | |
| 181 | + wdrtas_logbuffer[i + 4], wdrtas_logbuffer[i + 5], | |
| 182 | + wdrtas_logbuffer[i + 6], wdrtas_logbuffer[i + 7], | |
| 183 | + wdrtas_logbuffer[i + 8], wdrtas_logbuffer[i + 9], | |
| 184 | + wdrtas_logbuffer[i + 10], wdrtas_logbuffer[i + 11], | |
| 185 | + wdrtas_logbuffer[i + 12], wdrtas_logbuffer[i + 13], | |
| 186 | + wdrtas_logbuffer[i + 14], wdrtas_logbuffer[i + 15]); | |
| 187 | +} | |
| 188 | + | |
| 189 | +/** | |
| 190 | + * wdrtas_timer_keepalive - resets watchdog timer to keep system alive | |
| 191 | + * | |
| 192 | + * wdrtas_timer_keepalive restarts the watchdog timer by calling the | |
| 193 | + * RTAS function event-scan and repeats these calls as long as there are | |
| 194 | + * events available. All events will be dumped. | |
| 195 | + */ | |
| 196 | +static void | |
| 197 | +wdrtas_timer_keepalive(void) | |
| 198 | +{ | |
| 199 | + long result; | |
| 200 | + | |
| 201 | + do { | |
| 202 | + result = rtas_call(wdrtas_token_event_scan, 4, 1, NULL, | |
| 203 | + RTAS_EVENT_SCAN_ALL_EVENTS, 0, | |
| 204 | + (void *)__pa(wdrtas_logbuffer), | |
| 205 | + WDRTAS_LOGBUFFER_LEN); | |
| 206 | + if (result < 0) | |
| 207 | + printk(KERN_ERR "wdrtas: event-scan failed: %li\n", | |
| 208 | + result); | |
| 209 | + if (result == 0) | |
| 210 | + wdrtas_log_scanned_event(); | |
| 211 | + } while (result == 0); | |
| 212 | +} | |
| 213 | + | |
| 214 | +/** | |
| 215 | + * wdrtas_get_temperature - returns current temperature | |
| 216 | + * | |
| 217 | + * returns temperature or <0 on failures | |
| 218 | + * | |
| 219 | + * wdrtas_get_temperature returns the current temperature in Fahrenheit. It | |
| 220 | + * uses the RTAS call get-sensor-state, token 3 to do so | |
| 221 | + */ | |
| 222 | +static int | |
| 223 | +wdrtas_get_temperature(void) | |
| 224 | +{ | |
| 225 | + long result; | |
| 226 | + int temperature = 0; | |
| 227 | + | |
| 228 | + result = rtas_call(wdrtas_token_get_sensor_state, 2, 2, | |
| 229 | + (void *)__pa(&temperature), | |
| 230 | + WDRTAS_THERMAL_SENSOR, 0); | |
| 231 | + | |
| 232 | + if (result < 0) | |
| 233 | + printk(KERN_WARNING "wdrtas: reading the thermal sensor " | |
| 234 | + "faild: %li\n", result); | |
| 235 | + else | |
| 236 | + temperature = ((temperature * 9) / 5) + 32; /* fahrenheit */ | |
| 237 | + | |
| 238 | + return temperature; | |
| 239 | +} | |
| 240 | + | |
| 241 | +/** | |
| 242 | + * wdrtas_get_status - returns the status of the watchdog | |
| 243 | + * | |
| 244 | + * returns a bitmask of defines WDIOF_... as defined in | |
| 245 | + * include/linux/watchdog.h | |
| 246 | + */ | |
| 247 | +static int | |
| 248 | +wdrtas_get_status(void) | |
| 249 | +{ | |
| 250 | + return 0; /* TODO */ | |
| 251 | +} | |
| 252 | + | |
| 253 | +/** | |
| 254 | + * wdrtas_get_boot_status - returns the reason for the last boot | |
| 255 | + * | |
| 256 | + * returns a bitmask of defines WDIOF_... as defined in | |
| 257 | + * include/linux/watchdog.h, indicating why the watchdog rebooted the system | |
| 258 | + */ | |
| 259 | +static int | |
| 260 | +wdrtas_get_boot_status(void) | |
| 261 | +{ | |
| 262 | + return 0; /* TODO */ | |
| 263 | +} | |
| 264 | + | |
| 265 | +/*** watchdog API and operations stuff */ | |
| 266 | + | |
| 267 | +/* wdrtas_write - called when watchdog device is written to | |
| 268 | + * @file: file structure | |
| 269 | + * @buf: user buffer with data | |
| 270 | + * @len: amount to data written | |
| 271 | + * @ppos: position in file | |
| 272 | + * | |
| 273 | + * returns the number of successfully processed characters, which is always | |
| 274 | + * the number of bytes passed to this function | |
| 275 | + * | |
| 276 | + * wdrtas_write processes all the data given to it and looks for the magic | |
| 277 | + * character 'V'. This character allows the watchdog device to be closed | |
| 278 | + * properly. | |
| 279 | + */ | |
| 280 | +static ssize_t | |
| 281 | +wdrtas_write(struct file *file, const char __user *buf, | |
| 282 | + size_t len, loff_t *ppos) | |
| 283 | +{ | |
| 284 | + int i; | |
| 285 | + char c; | |
| 286 | + | |
| 287 | + if (!len) | |
| 288 | + goto out; | |
| 289 | + | |
| 290 | + if (!wdrtas_nowayout) { | |
| 291 | + wdrtas_expect_close = 0; | |
| 292 | + /* look for 'V' */ | |
| 293 | + for (i = 0; i < len; i++) { | |
| 294 | + if (get_user(c, buf + i)) | |
| 295 | + return -EFAULT; | |
| 296 | + /* allow to close device */ | |
| 297 | + if (c == 'V') | |
| 298 | + wdrtas_expect_close = WDRTAS_MAGIC_CHAR; | |
| 299 | + } | |
| 300 | + } | |
| 301 | + | |
| 302 | + wdrtas_timer_keepalive(); | |
| 303 | + | |
| 304 | +out: | |
| 305 | + return len; | |
| 306 | +} | |
| 307 | + | |
| 308 | +/** | |
| 309 | + * wdrtas_ioctl - ioctl function for the watchdog device | |
| 310 | + * @inode: inode structure | |
| 311 | + * @file: file structure | |
| 312 | + * @cmd: command for ioctl | |
| 313 | + * @arg: argument pointer | |
| 314 | + * | |
| 315 | + * returns 0 on success, <0 on failure | |
| 316 | + * | |
| 317 | + * wdrtas_ioctl implements the watchdog API ioctls | |
| 318 | + */ | |
| 319 | +static int | |
| 320 | +wdrtas_ioctl(struct inode *inode, struct file *file, | |
| 321 | + unsigned int cmd, unsigned long arg) | |
| 322 | +{ | |
| 323 | + int __user *argp = (void *)arg; | |
| 324 | + int i; | |
| 325 | + static struct watchdog_info wdinfo = { | |
| 326 | + .options = WDRTAS_SUPPORTED_MASK, | |
| 327 | + .firmware_version = 0, | |
| 328 | + .identity = "wdrtas" | |
| 329 | + }; | |
| 330 | + | |
| 331 | + switch (cmd) { | |
| 332 | + case WDIOC_GETSUPPORT: | |
| 333 | + if (copy_to_user(argp, &wdinfo, sizeof(wdinfo))) | |
| 334 | + return -EFAULT; | |
| 335 | + return 0; | |
| 336 | + | |
| 337 | + case WDIOC_GETSTATUS: | |
| 338 | + i = wdrtas_get_status(); | |
| 339 | + return put_user(i, argp); | |
| 340 | + | |
| 341 | + case WDIOC_GETBOOTSTATUS: | |
| 342 | + i = wdrtas_get_boot_status(); | |
| 343 | + return put_user(i, argp); | |
| 344 | + | |
| 345 | + case WDIOC_GETTEMP: | |
| 346 | + if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) | |
| 347 | + return -EOPNOTSUPP; | |
| 348 | + | |
| 349 | + i = wdrtas_get_temperature(); | |
| 350 | + return put_user(i, argp); | |
| 351 | + | |
| 352 | + case WDIOC_SETOPTIONS: | |
| 353 | + if (get_user(i, argp)) | |
| 354 | + return -EFAULT; | |
| 355 | + if (i & WDIOS_DISABLECARD) | |
| 356 | + wdrtas_timer_stop(); | |
| 357 | + if (i & WDIOS_ENABLECARD) { | |
| 358 | + wdrtas_timer_keepalive(); | |
| 359 | + wdrtas_timer_start(); | |
| 360 | + } | |
| 361 | + if (i & WDIOS_TEMPPANIC) { | |
| 362 | + /* not implemented. Done by H8 */ | |
| 363 | + } | |
| 364 | + return 0; | |
| 365 | + | |
| 366 | + case WDIOC_KEEPALIVE: | |
| 367 | + wdrtas_timer_keepalive(); | |
| 368 | + return 0; | |
| 369 | + | |
| 370 | + case WDIOC_SETTIMEOUT: | |
| 371 | + if (get_user(i, argp)) | |
| 372 | + return -EFAULT; | |
| 373 | + | |
| 374 | + if (wdrtas_set_interval(i)) | |
| 375 | + return -EINVAL; | |
| 376 | + | |
| 377 | + wdrtas_timer_keepalive(); | |
| 378 | + | |
| 379 | + if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) | |
| 380 | + wdrtas_interval = i; | |
| 381 | + else | |
| 382 | + wdrtas_interval = wdrtas_get_interval(i); | |
| 383 | + /* fallthrough */ | |
| 384 | + | |
| 385 | + case WDIOC_GETTIMEOUT: | |
| 386 | + return put_user(wdrtas_interval, argp); | |
| 387 | + | |
| 388 | + default: | |
| 389 | + return -ENOIOCTLCMD; | |
| 390 | + } | |
| 391 | +} | |
| 392 | + | |
| 393 | +/** | |
| 394 | + * wdrtas_open - open function of watchdog device | |
| 395 | + * @inode: inode structure | |
| 396 | + * @file: file structure | |
| 397 | + * | |
| 398 | + * returns 0 on success, -EBUSY if the file has been opened already, <0 on | |
| 399 | + * other failures | |
| 400 | + * | |
| 401 | + * function called when watchdog device is opened | |
| 402 | + */ | |
| 403 | +static int | |
| 404 | +wdrtas_open(struct inode *inode, struct file *file) | |
| 405 | +{ | |
| 406 | + /* only open once */ | |
| 407 | + if (atomic_inc_return(&wdrtas_miscdev_open) > 1) { | |
| 408 | + atomic_dec(&wdrtas_miscdev_open); | |
| 409 | + return -EBUSY; | |
| 410 | + } | |
| 411 | + | |
| 412 | + wdrtas_timer_start(); | |
| 413 | + wdrtas_timer_keepalive(); | |
| 414 | + | |
| 415 | + return nonseekable_open(inode, file); | |
| 416 | +} | |
| 417 | + | |
| 418 | +/** | |
| 419 | + * wdrtas_close - close function of watchdog device | |
| 420 | + * @inode: inode structure | |
| 421 | + * @file: file structure | |
| 422 | + * | |
| 423 | + * returns 0 on success | |
| 424 | + * | |
| 425 | + * close function. Always succeeds | |
| 426 | + */ | |
| 427 | +static int | |
| 428 | +wdrtas_close(struct inode *inode, struct file *file) | |
| 429 | +{ | |
| 430 | + /* only stop watchdog, if this was announced using 'V' before */ | |
| 431 | + if (wdrtas_expect_close == WDRTAS_MAGIC_CHAR) | |
| 432 | + wdrtas_timer_stop(); | |
| 433 | + else { | |
| 434 | + printk(KERN_WARNING "wdrtas: got unexpected close. Watchdog " | |
| 435 | + "not stopped.\n"); | |
| 436 | + wdrtas_timer_keepalive(); | |
| 437 | + } | |
| 438 | + | |
| 439 | + wdrtas_expect_close = 0; | |
| 440 | + atomic_dec(&wdrtas_miscdev_open); | |
| 441 | + return 0; | |
| 442 | +} | |
| 443 | + | |
| 444 | +/** | |
| 445 | + * wdrtas_temp_read - gives back the temperature in fahrenheit | |
| 446 | + * @file: file structure | |
| 447 | + * @buf: user buffer | |
| 448 | + * @count: number of bytes to be read | |
| 449 | + * @ppos: position in file | |
| 450 | + * | |
| 451 | + * returns always 1 or -EFAULT in case of user space copy failures, <0 on | |
| 452 | + * other failures | |
| 453 | + * | |
| 454 | + * wdrtas_temp_read gives the temperature to the users by copying this | |
| 455 | + * value as one byte into the user space buffer. The unit is Fahrenheit... | |
| 456 | + */ | |
| 457 | +static ssize_t | |
| 458 | +wdrtas_temp_read(struct file *file, char __user *buf, | |
| 459 | + size_t count, loff_t *ppos) | |
| 460 | +{ | |
| 461 | + int temperature = 0; | |
| 462 | + | |
| 463 | + temperature = wdrtas_get_temperature(); | |
| 464 | + if (temperature < 0) | |
| 465 | + return temperature; | |
| 466 | + | |
| 467 | + if (copy_to_user(buf, &temperature, 1)) | |
| 468 | + return -EFAULT; | |
| 469 | + | |
| 470 | + return 1; | |
| 471 | +} | |
| 472 | + | |
| 473 | +/** | |
| 474 | + * wdrtas_temp_open - open function of temperature device | |
| 475 | + * @inode: inode structure | |
| 476 | + * @file: file structure | |
| 477 | + * | |
| 478 | + * returns 0 on success, <0 on failure | |
| 479 | + * | |
| 480 | + * function called when temperature device is opened | |
| 481 | + */ | |
| 482 | +static int | |
| 483 | +wdrtas_temp_open(struct inode *inode, struct file *file) | |
| 484 | +{ | |
| 485 | + return nonseekable_open(inode, file); | |
| 486 | +} | |
| 487 | + | |
| 488 | +/** | |
| 489 | + * wdrtas_temp_close - close function of temperature device | |
| 490 | + * @inode: inode structure | |
| 491 | + * @file: file structure | |
| 492 | + * | |
| 493 | + * returns 0 on success | |
| 494 | + * | |
| 495 | + * close function. Always succeeds | |
| 496 | + */ | |
| 497 | +static int | |
| 498 | +wdrtas_temp_close(struct inode *inode, struct file *file) | |
| 499 | +{ | |
| 500 | + return 0; | |
| 501 | +} | |
| 502 | + | |
| 503 | +/** | |
| 504 | + * wdrtas_reboot - reboot notifier function | |
| 505 | + * @nb: notifier block structure | |
| 506 | + * @code: reboot code | |
| 507 | + * @ptr: unused | |
| 508 | + * | |
| 509 | + * returns NOTIFY_DONE | |
| 510 | + * | |
| 511 | + * wdrtas_reboot stops the watchdog in case of a reboot | |
| 512 | + */ | |
| 513 | +static int | |
| 514 | +wdrtas_reboot(struct notifier_block *this, unsigned long code, void *ptr) | |
| 515 | +{ | |
| 516 | + if ( (code==SYS_DOWN) || (code==SYS_HALT) ) | |
| 517 | + wdrtas_timer_stop(); | |
| 518 | + | |
| 519 | + return NOTIFY_DONE; | |
| 520 | +} | |
| 521 | + | |
| 522 | +/*** initialization stuff */ | |
| 523 | + | |
| 524 | +static struct file_operations wdrtas_fops = { | |
| 525 | + .owner = THIS_MODULE, | |
| 526 | + .llseek = no_llseek, | |
| 527 | + .write = wdrtas_write, | |
| 528 | + .ioctl = wdrtas_ioctl, | |
| 529 | + .open = wdrtas_open, | |
| 530 | + .release = wdrtas_close, | |
| 531 | +}; | |
| 532 | + | |
| 533 | +static struct miscdevice wdrtas_miscdev = { | |
| 534 | + .minor = WATCHDOG_MINOR, | |
| 535 | + .name = "watchdog", | |
| 536 | + .fops = &wdrtas_fops, | |
| 537 | +}; | |
| 538 | + | |
| 539 | +static struct file_operations wdrtas_temp_fops = { | |
| 540 | + .owner = THIS_MODULE, | |
| 541 | + .llseek = no_llseek, | |
| 542 | + .read = wdrtas_temp_read, | |
| 543 | + .open = wdrtas_temp_open, | |
| 544 | + .release = wdrtas_temp_close, | |
| 545 | +}; | |
| 546 | + | |
| 547 | +static struct miscdevice wdrtas_tempdev = { | |
| 548 | + .minor = TEMP_MINOR, | |
| 549 | + .name = "temperature", | |
| 550 | + .fops = &wdrtas_temp_fops, | |
| 551 | +}; | |
| 552 | + | |
| 553 | +static struct notifier_block wdrtas_notifier = { | |
| 554 | + .notifier_call = wdrtas_reboot, | |
| 555 | +}; | |
| 556 | + | |
| 557 | +/** | |
| 558 | + * wdrtas_get_tokens - reads in RTAS tokens | |
| 559 | + * | |
| 560 | + * returns 0 on succes, <0 on failure | |
| 561 | + * | |
| 562 | + * wdrtas_get_tokens reads in the tokens for the RTAS calls used in | |
| 563 | + * this watchdog driver. It tolerates, if "get-sensor-state" and | |
| 564 | + * "ibm,get-system-parameter" are not available. | |
| 565 | + */ | |
| 566 | +static int | |
| 567 | +wdrtas_get_tokens(void) | |
| 568 | +{ | |
| 569 | + wdrtas_token_get_sensor_state = rtas_token("get-sensor-state"); | |
| 570 | + if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) { | |
| 571 | + printk(KERN_WARNING "wdrtas: couldn't get token for " | |
| 572 | + "get-sensor-state. Trying to continue without " | |
| 573 | + "temperature support.\n"); | |
| 574 | + } | |
| 575 | + | |
| 576 | + wdrtas_token_get_sp = rtas_token("ibm,get-system-parameter"); | |
| 577 | + if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) { | |
| 578 | + printk(KERN_WARNING "wdrtas: couldn't get token for " | |
| 579 | + "ibm,get-system-parameter. Trying to continue with " | |
| 580 | + "a default timeout value of %i seconds.\n", | |
| 581 | + WDRTAS_DEFAULT_INTERVAL); | |
| 582 | + } | |
| 583 | + | |
| 584 | + wdrtas_token_set_indicator = rtas_token("set-indicator"); | |
| 585 | + if (wdrtas_token_set_indicator == RTAS_UNKNOWN_SERVICE) { | |
| 586 | + printk(KERN_ERR "wdrtas: couldn't get token for " | |
| 587 | + "set-indicator. Terminating watchdog code.\n"); | |
| 588 | + return -EIO; | |
| 589 | + } | |
| 590 | + | |
| 591 | + wdrtas_token_event_scan = rtas_token("event-scan"); | |
| 592 | + if (wdrtas_token_event_scan == RTAS_UNKNOWN_SERVICE) { | |
| 593 | + printk(KERN_ERR "wdrtas: couldn't get token for event-scan. " | |
| 594 | + "Terminating watchdog code.\n"); | |
| 595 | + return -EIO; | |
| 596 | + } | |
| 597 | + | |
| 598 | + return 0; | |
| 599 | +} | |
| 600 | + | |
| 601 | +/** | |
| 602 | + * wdrtas_unregister_devs - unregisters the misc dev handlers | |
| 603 | + * | |
| 604 | + * wdrtas_register_devs unregisters the watchdog and temperature watchdog | |
| 605 | + * misc devs | |
| 606 | + */ | |
| 607 | +static void | |
| 608 | +wdrtas_unregister_devs(void) | |
| 609 | +{ | |
| 610 | + misc_deregister(&wdrtas_miscdev); | |
| 611 | + if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) | |
| 612 | + misc_deregister(&wdrtas_tempdev); | |
| 613 | +} | |
| 614 | + | |
| 615 | +/** | |
| 616 | + * wdrtas_register_devs - registers the misc dev handlers | |
| 617 | + * | |
| 618 | + * returns 0 on succes, <0 on failure | |
| 619 | + * | |
| 620 | + * wdrtas_register_devs registers the watchdog and temperature watchdog | |
| 621 | + * misc devs | |
| 622 | + */ | |
| 623 | +static int | |
| 624 | +wdrtas_register_devs(void) | |
| 625 | +{ | |
| 626 | + int result; | |
| 627 | + | |
| 628 | + result = misc_register(&wdrtas_miscdev); | |
| 629 | + if (result) { | |
| 630 | + printk(KERN_ERR "wdrtas: couldn't register watchdog misc " | |
| 631 | + "device. Terminating watchdog code.\n"); | |
| 632 | + return result; | |
| 633 | + } | |
| 634 | + | |
| 635 | + if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) { | |
| 636 | + result = misc_register(&wdrtas_tempdev); | |
| 637 | + if (result) { | |
| 638 | + printk(KERN_WARNING "wdrtas: couldn't register " | |
| 639 | + "watchdog temperature misc device. Continuing " | |
| 640 | + "without temperature support.\n"); | |
| 641 | + wdrtas_token_get_sensor_state = RTAS_UNKNOWN_SERVICE; | |
| 642 | + } | |
| 643 | + } | |
| 644 | + | |
| 645 | + return 0; | |
| 646 | +} | |
| 647 | + | |
| 648 | +/** | |
| 649 | + * wdrtas_init - init function of the watchdog driver | |
| 650 | + * | |
| 651 | + * returns 0 on succes, <0 on failure | |
| 652 | + * | |
| 653 | + * registers the file handlers and the reboot notifier | |
| 654 | + */ | |
| 655 | +static int __init | |
| 656 | +wdrtas_init(void) | |
| 657 | +{ | |
| 658 | + if (wdrtas_get_tokens()) | |
| 659 | + return -ENODEV; | |
| 660 | + | |
| 661 | + if (wdrtas_register_devs()) | |
| 662 | + return -ENODEV; | |
| 663 | + | |
| 664 | + if (register_reboot_notifier(&wdrtas_notifier)) { | |
| 665 | + printk(KERN_ERR "wdrtas: could not register reboot notifier. " | |
| 666 | + "Terminating watchdog code.\n"); | |
| 667 | + wdrtas_unregister_devs(); | |
| 668 | + return -ENODEV; | |
| 669 | + } | |
| 670 | + | |
| 671 | + if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) | |
| 672 | + wdrtas_interval = WDRTAS_DEFAULT_INTERVAL; | |
| 673 | + else | |
| 674 | + wdrtas_interval = wdrtas_get_interval(WDRTAS_DEFAULT_INTERVAL); | |
| 675 | + | |
| 676 | + return 0; | |
| 677 | +} | |
| 678 | + | |
| 679 | +/** | |
| 680 | + * wdrtas_exit - exit function of the watchdog driver | |
| 681 | + * | |
| 682 | + * unregisters the file handlers and the reboot notifier | |
| 683 | + */ | |
| 684 | +static void __exit | |
| 685 | +wdrtas_exit(void) | |
| 686 | +{ | |
| 687 | + if (!wdrtas_nowayout) | |
| 688 | + wdrtas_timer_stop(); | |
| 689 | + | |
| 690 | + wdrtas_unregister_devs(); | |
| 691 | + | |
| 692 | + unregister_reboot_notifier(&wdrtas_notifier); | |
| 693 | +} | |
| 694 | + | |
| 695 | +module_init(wdrtas_init); | |
| 696 | +module_exit(wdrtas_exit); |
include/asm-ppc64/machdep.h
| ... | ... | @@ -76,6 +76,7 @@ |
| 76 | 76 | void (*tce_flush)(struct iommu_table *tbl); |
| 77 | 77 | void (*iommu_dev_setup)(struct pci_dev *dev); |
| 78 | 78 | void (*iommu_bus_setup)(struct pci_bus *bus); |
| 79 | + void (*irq_bus_setup)(struct pci_bus *bus); | |
| 79 | 80 | |
| 80 | 81 | int (*probe)(int platform); |
| 81 | 82 | void (*setup_arch)(void); |
include/asm-ppc64/mmu.h
| ... | ... | @@ -47,9 +47,10 @@ |
| 47 | 47 | #define SLB_VSID_KS ASM_CONST(0x0000000000000800) |
| 48 | 48 | #define SLB_VSID_KP ASM_CONST(0x0000000000000400) |
| 49 | 49 | #define SLB_VSID_N ASM_CONST(0x0000000000000200) /* no-execute */ |
| 50 | -#define SLB_VSID_L ASM_CONST(0x0000000000000100) /* largepage 16M */ | |
| 50 | +#define SLB_VSID_L ASM_CONST(0x0000000000000100) /* largepage */ | |
| 51 | 51 | #define SLB_VSID_C ASM_CONST(0x0000000000000080) /* class */ |
| 52 | - | |
| 52 | +#define SLB_VSID_LS ASM_CONST(0x0000000000000070) /* size of largepage */ | |
| 53 | + | |
| 53 | 54 | #define SLB_VSID_KERNEL (SLB_VSID_KP|SLB_VSID_C) |
| 54 | 55 | #define SLB_VSID_USER (SLB_VSID_KP|SLB_VSID_KS) |
| 55 | 56 |
include/asm-ppc64/nvram.h
include/asm-ppc64/processor.h
| ... | ... | @@ -138,8 +138,16 @@ |
| 138 | 138 | #define SPRN_NIADORM 0x3F3 /* Hardware Implementation Register 2 */ |
| 139 | 139 | #define SPRN_HID4 0x3F4 /* 970 HID4 */ |
| 140 | 140 | #define SPRN_HID5 0x3F6 /* 970 HID5 */ |
| 141 | -#define SPRN_TSC 0x3FD /* Thread switch control */ | |
| 142 | -#define SPRN_TST 0x3FC /* Thread switch timeout */ | |
| 141 | +#define SPRN_HID6 0x3F9 /* BE HID 6 */ | |
| 142 | +#define HID6_LB (0x0F<<12) /* Concurrent Large Page Modes */ | |
| 143 | +#define HID6_DLP (1<<20) /* Disable all large page modes (4K only) */ | |
| 144 | +#define SPRN_TSCR 0x399 /* Thread switch control on BE */ | |
| 145 | +#define SPRN_TTR 0x39A /* Thread switch timeout on BE */ | |
| 146 | +#define TSCR_DEC_ENABLE 0x200000 /* Decrementer Interrupt */ | |
| 147 | +#define TSCR_EE_ENABLE 0x100000 /* External Interrupt */ | |
| 148 | +#define TSCR_EE_BOOST 0x080000 /* External Interrupt Boost */ | |
| 149 | +#define SPRN_TSC 0x3FD /* Thread switch control on others */ | |
| 150 | +#define SPRN_TST 0x3FC /* Thread switch timeout on others */ | |
| 143 | 151 | #define SPRN_L2CR 0x3F9 /* Level 2 Cache Control Regsiter */ |
| 144 | 152 | #define SPRN_LR 0x008 /* Link Register */ |
| 145 | 153 | #define SPRN_PIR 0x3FF /* Processor Identification Register */ |
| ... | ... | @@ -259,6 +267,7 @@ |
| 259 | 267 | #define PV_970FX 0x003C |
| 260 | 268 | #define PV_630 0x0040 |
| 261 | 269 | #define PV_630p 0x0041 |
| 270 | +#define PV_BE 0x0070 | |
| 262 | 271 | |
| 263 | 272 | /* Platforms supported by PPC64 */ |
| 264 | 273 | #define PLATFORM_PSERIES 0x0100 |
| ... | ... | @@ -267,6 +276,7 @@ |
| 267 | 276 | #define PLATFORM_LPAR 0x0001 |
| 268 | 277 | #define PLATFORM_POWERMAC 0x0400 |
| 269 | 278 | #define PLATFORM_MAPLE 0x0500 |
| 279 | +#define PLATFORM_BPA 0x1000 | |
| 270 | 280 | |
| 271 | 281 | /* Compatibility with drivers coming from PPC32 world */ |
| 272 | 282 | #define _machine (systemcfg->platform) |
| ... | ... | @@ -278,6 +288,7 @@ |
| 278 | 288 | #define IC_INVALID 0 |
| 279 | 289 | #define IC_OPEN_PIC 1 |
| 280 | 290 | #define IC_PPC_XIC 2 |
| 291 | +#define IC_BPA_IIC 3 | |
| 281 | 292 | |
| 282 | 293 | #define XGLUE(a,b) a##b |
| 283 | 294 | #define GLUE(a,b) XGLUE(a,b) |
include/asm-ppc64/rtas.h
| ... | ... | @@ -186,7 +186,13 @@ |
| 186 | 186 | extern int rtas_get_power_level(int powerdomain, int *level); |
| 187 | 187 | extern int rtas_set_power_level(int powerdomain, int level, int *setlevel); |
| 188 | 188 | extern int rtas_set_indicator(int indicator, int index, int new_value); |
| 189 | +extern void rtas_progress(char *s, unsigned short hex); | |
| 189 | 190 | extern void rtas_initialize(void); |
| 191 | + | |
| 192 | +struct rtc_time; | |
| 193 | +extern void rtas_get_boot_time(struct rtc_time *rtc_time); | |
| 194 | +extern void rtas_get_rtc_time(struct rtc_time *rtc_time); | |
| 195 | +extern int rtas_set_rtc_time(struct rtc_time *rtc_time); | |
| 190 | 196 | |
| 191 | 197 | /* Given an RTAS status code of 9900..9905 compute the hinted delay */ |
| 192 | 198 | unsigned int rtas_extended_busy_delay_time(int status); |
include/asm-ppc64/smp.h
| ... | ... | @@ -85,6 +85,14 @@ |
| 85 | 85 | |
| 86 | 86 | extern struct smp_ops_t *smp_ops; |
| 87 | 87 | |
| 88 | +#ifdef CONFIG_PPC_PSERIES | |
| 89 | +void vpa_init(int cpu); | |
| 90 | +#else | |
| 91 | +static inline void vpa_init(int cpu) | |
| 92 | +{ | |
| 93 | +} | |
| 94 | +#endif /* CONFIG_PPC_PSERIES */ | |
| 95 | + | |
| 88 | 96 | #endif /* __ASSEMBLY__ */ |
| 89 | 97 | |
| 90 | 98 | #endif /* !(_PPC64_SMP_H) */ |
include/asm-ppc64/time.h
| ... | ... | @@ -34,6 +34,15 @@ |
| 34 | 34 | extern void to_tm(int tim, struct rtc_time * tm); |
| 35 | 35 | extern time_t last_rtc_update; |
| 36 | 36 | |
| 37 | +void generic_calibrate_decr(void); | |
| 38 | +void setup_default_decr(void); | |
| 39 | + | |
| 40 | +/* Some sane defaults: 125 MHz timebase, 1GHz processor */ | |
| 41 | +extern unsigned long ppc_proc_freq; | |
| 42 | +#define DEFAULT_PROC_FREQ (DEFAULT_TB_FREQ * 8) | |
| 43 | +extern unsigned long ppc_tb_freq; | |
| 44 | +#define DEFAULT_TB_FREQ 125000000UL | |
| 45 | + | |
| 37 | 46 | /* |
| 38 | 47 | * By putting all of this stuff into a single struct we |
| 39 | 48 | * reduce the number of cache lines touched by do_gettimeofday. |