Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/paulus/ppc64-2.6
This commit is contained in:
commit
24665cd00d
|
@ -504,6 +504,13 @@ L: bonding-devel@lists.sourceforge.net
|
|||
W: http://sourceforge.net/projects/bonding/
|
||||
S: Supported
|
||||
|
||||
BROADBAND PROCESSOR ARCHITECTURE
|
||||
P: Arnd Bergmann
|
||||
M: arnd@arndb.de
|
||||
L: linuxppc64-dev@ozlabs.org
|
||||
W: http://linuxppc64.org
|
||||
S: Supported
|
||||
|
||||
BTTV VIDEO4LINUX DRIVER
|
||||
P: Gerd Knorr
|
||||
M: kraxel@bytesex.org
|
||||
|
|
|
@ -77,6 +77,10 @@ config PPC_PSERIES
|
|||
bool " IBM pSeries & new iSeries"
|
||||
default y
|
||||
|
||||
config PPC_BPA
|
||||
bool " Broadband Processor Architecture"
|
||||
depends on PPC_MULTIPLATFORM
|
||||
|
||||
config PPC_PMAC
|
||||
depends on PPC_MULTIPLATFORM
|
||||
bool " Apple G5 based machines"
|
||||
|
@ -106,6 +110,21 @@ config PPC_OF
|
|||
bool
|
||||
default y
|
||||
|
||||
config XICS
|
||||
depends on PPC_PSERIES
|
||||
bool
|
||||
default y
|
||||
|
||||
config MPIC
|
||||
depends on PPC_PSERIES || PPC_PMAC || PPC_MAPLE
|
||||
bool
|
||||
default y
|
||||
|
||||
config BPA_IIC
|
||||
depends on PPC_BPA
|
||||
bool
|
||||
default y
|
||||
|
||||
# VMX is pSeries only for now until somebody writes the iSeries
|
||||
# exception vectors for it
|
||||
config ALTIVEC
|
||||
|
@ -292,7 +311,7 @@ config MSCHUNKS
|
|||
|
||||
config PPC_RTAS
|
||||
bool
|
||||
depends on PPC_PSERIES
|
||||
depends on PPC_PSERIES || PPC_BPA
|
||||
default y
|
||||
|
||||
config RTAS_PROC
|
||||
|
|
|
@ -90,12 +90,14 @@ boot := arch/ppc64/boot
|
|||
boottarget-$(CONFIG_PPC_PSERIES) := zImage zImage.initrd
|
||||
boottarget-$(CONFIG_PPC_MAPLE) := zImage zImage.initrd
|
||||
boottarget-$(CONFIG_PPC_ISERIES) := vmlinux.sminitrd vmlinux.initrd vmlinux.sm
|
||||
boottarget-$(CONFIG_PPC_BPA) := zImage zImage.initrd
|
||||
$(boottarget-y): vmlinux
|
||||
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
|
||||
|
||||
bootimage-$(CONFIG_PPC_PSERIES) := $(boot)/zImage
|
||||
bootimage-$(CONFIG_PPC_PMAC) := vmlinux
|
||||
bootimage-$(CONFIG_PPC_MAPLE) := $(boot)/zImage
|
||||
bootimage-$(CONFIG_PPC_BPA) := zImage
|
||||
bootimage-$(CONFIG_PPC_ISERIES) := vmlinux
|
||||
BOOTIMAGE := $(bootimage-y)
|
||||
install: vmlinux
|
||||
|
|
|
@ -27,17 +27,21 @@ obj-$(CONFIG_PPC_ISERIES) += HvCall.o HvLpConfig.o LparData.o \
|
|||
mf.o HvLpEvent.o iSeries_proc.o iSeries_htab.o \
|
||||
iSeries_iommu.o
|
||||
|
||||
obj-$(CONFIG_PPC_MULTIPLATFORM) += nvram.o i8259.o prom_init.o prom.o mpic.o
|
||||
obj-$(CONFIG_PPC_MULTIPLATFORM) += nvram.o i8259.o prom_init.o prom.o
|
||||
|
||||
obj-$(CONFIG_PPC_PSERIES) += pSeries_pci.o pSeries_lpar.o pSeries_hvCall.o \
|
||||
pSeries_nvram.o rtasd.o ras.o pSeries_reconfig.o \
|
||||
xics.o rtas.o pSeries_setup.o pSeries_iommu.o
|
||||
pSeries_setup.o pSeries_iommu.o
|
||||
|
||||
obj-$(CONFIG_PPC_BPA) += bpa_setup.o bpa_iommu.o bpa_nvram.o \
|
||||
bpa_iic.o spider-pic.o
|
||||
|
||||
obj-$(CONFIG_EEH) += eeh.o
|
||||
obj-$(CONFIG_PROC_FS) += proc_ppc64.o
|
||||
obj-$(CONFIG_RTAS_FLASH) += rtas_flash.o
|
||||
obj-$(CONFIG_SMP) += smp.o
|
||||
obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o
|
||||
obj-$(CONFIG_PPC_RTAS) += rtas.o rtas_pci.o
|
||||
obj-$(CONFIG_RTAS_PROC) += rtas-proc.o
|
||||
obj-$(CONFIG_SCANLOG) += scanlog.o
|
||||
obj-$(CONFIG_VIOPATH) += viopath.o
|
||||
|
@ -46,6 +50,8 @@ obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o
|
|||
obj-$(CONFIG_BOOTX_TEXT) += btext.o
|
||||
obj-$(CONFIG_HVCS) += hvcserver.o
|
||||
obj-$(CONFIG_IBMVIO) += vio.o
|
||||
obj-$(CONFIG_XICS) += xics.o
|
||||
obj-$(CONFIG_MPIC) += mpic.o
|
||||
|
||||
obj-$(CONFIG_PPC_PMAC) += pmac_setup.o pmac_feature.o pmac_pci.o \
|
||||
pmac_time.o pmac_nvram.o pmac_low_i2c.o
|
||||
|
@ -58,6 +64,7 @@ ifdef CONFIG_SMP
|
|||
obj-$(CONFIG_PPC_PMAC) += pmac_smp.o smp-tbsync.o
|
||||
obj-$(CONFIG_PPC_ISERIES) += iSeries_smp.o
|
||||
obj-$(CONFIG_PPC_PSERIES) += pSeries_smp.o
|
||||
obj-$(CONFIG_PPC_BPA) += pSeries_smp.o
|
||||
obj-$(CONFIG_PPC_MAPLE) += smp-tbsync.o
|
||||
endif
|
||||
|
||||
|
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* BPA Internal Interrupt Controller
|
||||
*
|
||||
* (C) Copyright IBM Deutschland Entwicklung GmbH 2005
|
||||
*
|
||||
* Author: Arnd Bergmann <arndb@de.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
#include "bpa_iic.h"
|
||||
|
||||
struct iic_pending_bits {
|
||||
u32 data;
|
||||
u8 flags;
|
||||
u8 class;
|
||||
u8 source;
|
||||
u8 prio;
|
||||
};
|
||||
|
||||
enum iic_pending_flags {
|
||||
IIC_VALID = 0x80,
|
||||
IIC_IPI = 0x40,
|
||||
};
|
||||
|
||||
struct iic_regs {
|
||||
struct iic_pending_bits pending;
|
||||
struct iic_pending_bits pending_destr;
|
||||
u64 generate;
|
||||
u64 prio;
|
||||
};
|
||||
|
||||
struct iic {
|
||||
struct iic_regs __iomem *regs;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct iic, iic);
|
||||
|
||||
void iic_local_enable(void)
|
||||
{
|
||||
out_be64(&__get_cpu_var(iic).regs->prio, 0xff);
|
||||
}
|
||||
|
||||
void iic_local_disable(void)
|
||||
{
|
||||
out_be64(&__get_cpu_var(iic).regs->prio, 0x0);
|
||||
}
|
||||
|
||||
static unsigned int iic_startup(unsigned int irq)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iic_enable(unsigned int irq)
|
||||
{
|
||||
iic_local_enable();
|
||||
}
|
||||
|
||||
static void iic_disable(unsigned int irq)
|
||||
{
|
||||
}
|
||||
|
||||
static void iic_end(unsigned int irq)
|
||||
{
|
||||
iic_local_enable();
|
||||
}
|
||||
|
||||
static struct hw_interrupt_type iic_pic = {
|
||||
.typename = " BPA-IIC ",
|
||||
.startup = iic_startup,
|
||||
.enable = iic_enable,
|
||||
.disable = iic_disable,
|
||||
.end = iic_end,
|
||||
};
|
||||
|
||||
static int iic_external_get_irq(struct iic_pending_bits pending)
|
||||
{
|
||||
int irq;
|
||||
unsigned char node, unit;
|
||||
|
||||
node = pending.source >> 4;
|
||||
unit = pending.source & 0xf;
|
||||
irq = -1;
|
||||
|
||||
/*
|
||||
* This mapping is specific to the Broadband
|
||||
* Engine. We might need to get the numbers
|
||||
* from the device tree to support future CPUs.
|
||||
*/
|
||||
switch (unit) {
|
||||
case 0x00:
|
||||
case 0x0b:
|
||||
/*
|
||||
* One of these units can be connected
|
||||
* to an external interrupt controller.
|
||||
*/
|
||||
if (pending.prio > 0x3f ||
|
||||
pending.class != 2)
|
||||
break;
|
||||
irq = IIC_EXT_OFFSET
|
||||
+ spider_get_irq(pending.prio + node * IIC_NODE_STRIDE)
|
||||
+ node * IIC_NODE_STRIDE;
|
||||
break;
|
||||
case 0x01 ... 0x04:
|
||||
case 0x07 ... 0x0a:
|
||||
/*
|
||||
* These units are connected to the SPEs
|
||||
*/
|
||||
if (pending.class > 2)
|
||||
break;
|
||||
irq = IIC_SPE_OFFSET
|
||||
+ pending.class * IIC_CLASS_STRIDE
|
||||
+ node * IIC_NODE_STRIDE
|
||||
+ unit;
|
||||
break;
|
||||
}
|
||||
if (irq == -1)
|
||||
printk(KERN_WARNING "Unexpected interrupt class %02x, "
|
||||
"source %02x, prio %02x, cpu %02x\n", pending.class,
|
||||
pending.source, pending.prio, smp_processor_id());
|
||||
return irq;
|
||||
}
|
||||
|
||||
/* Get an IRQ number from the pending state register of the IIC */
|
||||
int iic_get_irq(struct pt_regs *regs)
|
||||
{
|
||||
struct iic *iic;
|
||||
int irq;
|
||||
struct iic_pending_bits pending;
|
||||
|
||||
iic = &__get_cpu_var(iic);
|
||||
*(unsigned long *) &pending =
|
||||
in_be64((unsigned long __iomem *) &iic->regs->pending_destr);
|
||||
|
||||
irq = -1;
|
||||
if (pending.flags & IIC_VALID) {
|
||||
if (pending.flags & IIC_IPI) {
|
||||
irq = IIC_IPI_OFFSET + (pending.prio >> 4);
|
||||
/*
|
||||
if (irq > 0x80)
|
||||
printk(KERN_WARNING "Unexpected IPI prio %02x"
|
||||
"on CPU %02x\n", pending.prio,
|
||||
smp_processor_id());
|
||||
*/
|
||||
} else {
|
||||
irq = iic_external_get_irq(pending);
|
||||
}
|
||||
}
|
||||
return irq;
|
||||
}
|
||||
|
||||
static struct iic_regs __iomem *find_iic(int cpu)
|
||||
{
|
||||
struct device_node *np;
|
||||
int nodeid = cpu / 2;
|
||||
unsigned long regs;
|
||||
struct iic_regs __iomem *iic_regs;
|
||||
|
||||
for (np = of_find_node_by_type(NULL, "cpu");
|
||||
np;
|
||||
np = of_find_node_by_type(np, "cpu")) {
|
||||
if (nodeid == *(int *)get_property(np, "node-id", NULL))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!np) {
|
||||
printk(KERN_WARNING "IIC: CPU %d not found\n", cpu);
|
||||
iic_regs = NULL;
|
||||
} else {
|
||||
regs = *(long *)get_property(np, "iic", NULL);
|
||||
|
||||
/* hack until we have decided on the devtree info */
|
||||
regs += 0x400;
|
||||
if (cpu & 1)
|
||||
regs += 0x20;
|
||||
|
||||
printk(KERN_DEBUG "IIC for CPU %d at %lx\n", cpu, regs);
|
||||
iic_regs = __ioremap(regs, sizeof(struct iic_regs),
|
||||
_PAGE_NO_CACHE);
|
||||
}
|
||||
return iic_regs;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
void iic_setup_cpu(void)
|
||||
{
|
||||
out_be64(&__get_cpu_var(iic).regs->prio, 0xff);
|
||||
}
|
||||
|
||||
void iic_cause_IPI(int cpu, int mesg)
|
||||
{
|
||||
out_be64(&per_cpu(iic, cpu).regs->generate, mesg);
|
||||
}
|
||||
|
||||
static irqreturn_t iic_ipi_action(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
|
||||
smp_message_recv(irq - IIC_IPI_OFFSET, regs);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void iic_request_ipi(int irq, const char *name)
|
||||
{
|
||||
/* IPIs are marked SA_INTERRUPT as they must run with irqs
|
||||
* disabled */
|
||||
get_irq_desc(irq)->handler = &iic_pic;
|
||||
get_irq_desc(irq)->status |= IRQ_PER_CPU;
|
||||
request_irq(irq, iic_ipi_action, SA_INTERRUPT, name, NULL);
|
||||
}
|
||||
|
||||
void iic_request_IPIs(void)
|
||||
{
|
||||
iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_CALL_FUNCTION, "IPI-call");
|
||||
iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_RESCHEDULE, "IPI-resched");
|
||||
#ifdef CONFIG_DEBUGGER
|
||||
iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_DEBUGGER_BREAK, "IPI-debug");
|
||||
#endif /* CONFIG_DEBUGGER */
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
static void iic_setup_spe_handlers(void)
|
||||
{
|
||||
int be, isrc;
|
||||
|
||||
/* Assume two threads per BE are present */
|
||||
for (be=0; be < num_present_cpus() / 2; be++) {
|
||||
for (isrc = 0; isrc < IIC_CLASS_STRIDE * 3; isrc++) {
|
||||
int irq = IIC_NODE_STRIDE * be + IIC_SPE_OFFSET + isrc;
|
||||
get_irq_desc(irq)->handler = &iic_pic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void iic_init_IRQ(void)
|
||||
{
|
||||
int cpu, irq_offset;
|
||||
struct iic *iic;
|
||||
|
||||
irq_offset = 0;
|
||||
for_each_cpu(cpu) {
|
||||
iic = &per_cpu(iic, cpu);
|
||||
iic->regs = find_iic(cpu);
|
||||
if (iic->regs)
|
||||
out_be64(&iic->regs->prio, 0xff);
|
||||
}
|
||||
iic_setup_spe_handlers();
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
#ifndef ASM_BPA_IIC_H
|
||||
#define ASM_BPA_IIC_H
|
||||
#ifdef __KERNEL__
|
||||
/*
|
||||
* Mapping of IIC pending bits into per-node
|
||||
* interrupt numbers.
|
||||
*
|
||||
* IRQ FF CC SS PP FF CC SS PP Description
|
||||
*
|
||||
* 00-3f 80 02 +0 00 - 80 02 +0 3f South Bridge
|
||||
* 00-3f 80 02 +b 00 - 80 02 +b 3f South Bridge
|
||||
* 41-4a 80 00 +1 ** - 80 00 +a ** SPU Class 0
|
||||
* 51-5a 80 01 +1 ** - 80 01 +a ** SPU Class 1
|
||||
* 61-6a 80 02 +1 ** - 80 02 +a ** SPU Class 2
|
||||
* 70-7f C0 ** ** 00 - C0 ** ** 0f IPI
|
||||
*
|
||||
* F flags
|
||||
* C class
|
||||
* S source
|
||||
* P Priority
|
||||
* + node number
|
||||
* * don't care
|
||||
*
|
||||
* A node consists of a Broadband Engine and an optional
|
||||
* south bridge device providing a maximum of 64 IRQs.
|
||||
* The south bridge may be connected to either IOIF0
|
||||
* or IOIF1.
|
||||
* Each SPE is represented as three IRQ lines, one per
|
||||
* interrupt class.
|
||||
* 16 IRQ numbers are reserved for inter processor
|
||||
* interruptions, although these are only used in the
|
||||
* range of the first node.
|
||||
*
|
||||
* This scheme needs 128 IRQ numbers per BIF node ID,
|
||||
* which means that with the total of 512 lines
|
||||
* available, we can have a maximum of four nodes.
|
||||
*/
|
||||
|
||||
enum {
|
||||
IIC_EXT_OFFSET = 0x00, /* Start of south bridge IRQs */
|
||||
IIC_NUM_EXT = 0x40, /* Number of south bridge IRQs */
|
||||
IIC_SPE_OFFSET = 0x40, /* Start of SPE interrupts */
|
||||
IIC_CLASS_STRIDE = 0x10, /* SPE IRQs per class */
|
||||
IIC_IPI_OFFSET = 0x70, /* Start of IPI IRQs */
|
||||
IIC_NUM_IPIS = 0x10, /* IRQs reserved for IPI */
|
||||
IIC_NODE_STRIDE = 0x80, /* Total IRQs per node */
|
||||
};
|
||||
|
||||
extern void iic_init_IRQ(void);
|
||||
extern int iic_get_irq(struct pt_regs *regs);
|
||||
extern void iic_cause_IPI(int cpu, int mesg);
|
||||
extern void iic_request_IPIs(void);
|
||||
extern void iic_setup_cpu(void);
|
||||
extern void iic_local_enable(void);
|
||||
extern void iic_local_disable(void);
|
||||
|
||||
|
||||
extern void spider_init_IRQ(void);
|
||||
extern int spider_get_irq(unsigned long int_pending);
|
||||
|
||||
#endif
|
||||
#endif /* ASM_BPA_IIC_H */
|
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
* IOMMU implementation for Broadband Processor Architecture
|
||||
* We just establish a linear mapping at boot by setting all the
|
||||
* IOPT cache entries in the CPU.
|
||||
* The mapping functions should be identical to pci_direct_iommu,
|
||||
* except for the handling of the high order bit that is required
|
||||
* by the Spider bridge. These should be split into a separate
|
||||
* file at the point where we get a different bridge chip.
|
||||
*
|
||||
* Copyright (C) 2005 IBM Deutschland Entwicklung GmbH,
|
||||
* Arnd Bergmann <arndb@de.ibm.com>
|
||||
*
|
||||
* Based on linear mapping
|
||||
* Copyright (C) 2003 Benjamin Herrenschmidt (benh@kernel.crashing.org)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <asm/sections.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/pmac_feature.h>
|
||||
#include <asm/abs_addr.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#include "pci.h"
|
||||
#include "bpa_iommu.h"
|
||||
|
||||
static inline unsigned long
|
||||
get_iopt_entry(unsigned long real_address, unsigned long ioid,
|
||||
unsigned long prot)
|
||||
{
|
||||
return (prot & IOPT_PROT_MASK)
|
||||
| (IOPT_COHERENT)
|
||||
| (IOPT_ORDER_VC)
|
||||
| (real_address & IOPT_RPN_MASK)
|
||||
| (ioid & IOPT_IOID_MASK);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
unsigned long val;
|
||||
} ioste;
|
||||
|
||||
static inline ioste
|
||||
mk_ioste(unsigned long val)
|
||||
{
|
||||
ioste ioste = { .val = val, };
|
||||
return ioste;
|
||||
}
|
||||
|
||||
static inline ioste
|
||||
get_iost_entry(unsigned long iopt_base, unsigned long io_address, unsigned page_size)
|
||||
{
|
||||
unsigned long ps;
|
||||
unsigned long iostep;
|
||||
unsigned long nnpt;
|
||||
unsigned long shift;
|
||||
|
||||
switch (page_size) {
|
||||
case 0x1000000:
|
||||
ps = IOST_PS_16M;
|
||||
nnpt = 0; /* one page per segment */
|
||||
shift = 5; /* segment has 16 iopt entries */
|
||||
break;
|
||||
|
||||
case 0x100000:
|
||||
ps = IOST_PS_1M;
|
||||
nnpt = 0; /* one page per segment */
|
||||
shift = 1; /* segment has 256 iopt entries */
|
||||
break;
|
||||
|
||||
case 0x10000:
|
||||
ps = IOST_PS_64K;
|
||||
nnpt = 0x07; /* 8 pages per io page table */
|
||||
shift = 0; /* all entries are used */
|
||||
break;
|
||||
|
||||
case 0x1000:
|
||||
ps = IOST_PS_4K;
|
||||
nnpt = 0x7f; /* 128 pages per io page table */
|
||||
shift = 0; /* all entries are used */
|
||||
break;
|
||||
|
||||
default: /* not a known compile time constant */
|
||||
BUILD_BUG_ON(1);
|
||||
break;
|
||||
}
|
||||
|
||||
iostep = iopt_base +
|
||||
/* need 8 bytes per iopte */
|
||||
(((io_address / page_size * 8)
|
||||
/* align io page tables on 4k page boundaries */
|
||||
<< shift)
|
||||
/* nnpt+1 pages go into each iopt */
|
||||
& ~(nnpt << 12));
|
||||
|
||||
nnpt++; /* this seems to work, but the documentation is not clear
|
||||
about wether we put nnpt or nnpt-1 into the ioste bits.
|
||||
In theory, this can't work for 4k pages. */
|
||||
return mk_ioste(IOST_VALID_MASK
|
||||
| (iostep & IOST_PT_BASE_MASK)
|
||||
| ((nnpt << 5) & IOST_NNPT_MASK)
|
||||
| (ps & IOST_PS_MASK));
|
||||
}
|
||||
|
||||
/* compute the address of an io pte */
|
||||
static inline unsigned long
|
||||
get_ioptep(ioste iost_entry, unsigned long io_address)
|
||||
{
|
||||
unsigned long iopt_base;
|
||||
unsigned long page_size;
|
||||
unsigned long page_number;
|
||||
unsigned long iopt_offset;
|
||||
|
||||
iopt_base = iost_entry.val & IOST_PT_BASE_MASK;
|
||||
page_size = iost_entry.val & IOST_PS_MASK;
|
||||
|
||||
/* decode page size to compute page number */
|
||||
page_number = (io_address & 0x0fffffff) >> (10 + 2 * page_size);
|
||||
/* page number is an offset into the io page table */
|
||||
iopt_offset = (page_number << 3) & 0x7fff8ul;
|
||||
return iopt_base + iopt_offset;
|
||||
}
|
||||
|
||||
/* compute the tag field of the iopt cache entry */
|
||||
static inline unsigned long
|
||||
get_ioc_tag(ioste iost_entry, unsigned long io_address)
|
||||
{
|
||||
unsigned long iopte = get_ioptep(iost_entry, io_address);
|
||||
|
||||
return IOPT_VALID_MASK
|
||||
| ((iopte & 0x00000000000000ff8ul) >> 3)
|
||||
| ((iopte & 0x0000003fffffc0000ul) >> 9);
|
||||
}
|
||||
|
||||
/* compute the hashed 6 bit index for the 4-way associative pte cache */
|
||||
static inline unsigned long
|
||||
get_ioc_hash(ioste iost_entry, unsigned long io_address)
|
||||
{
|
||||
unsigned long iopte = get_ioptep(iost_entry, io_address);
|
||||
|
||||
return ((iopte & 0x000000000000001f8ul) >> 3)
|
||||
^ ((iopte & 0x00000000000020000ul) >> 17)
|
||||
^ ((iopte & 0x00000000000010000ul) >> 15)
|
||||
^ ((iopte & 0x00000000000008000ul) >> 13)
|
||||
^ ((iopte & 0x00000000000004000ul) >> 11)
|
||||
^ ((iopte & 0x00000000000002000ul) >> 9)
|
||||
^ ((iopte & 0x00000000000001000ul) >> 7);
|
||||
}
|
||||
|
||||
/* same as above, but pretend that we have a simpler 1-way associative
|
||||
pte cache with an 8 bit index */
|
||||
static inline unsigned long
|
||||
get_ioc_hash_1way(ioste iost_entry, unsigned long io_address)
|
||||
{
|
||||
unsigned long iopte = get_ioptep(iost_entry, io_address);
|
||||
|
||||
return ((iopte & 0x000000000000001f8ul) >> 3)
|
||||
^ ((iopte & 0x00000000000020000ul) >> 17)
|
||||
^ ((iopte & 0x00000000000010000ul) >> 15)
|
||||
^ ((iopte & 0x00000000000008000ul) >> 13)
|
||||
^ ((iopte & 0x00000000000004000ul) >> 11)
|
||||
^ ((iopte & 0x00000000000002000ul) >> 9)
|
||||
^ ((iopte & 0x00000000000001000ul) >> 7)
|
||||
^ ((iopte & 0x0000000000000c000ul) >> 8);
|
||||
}
|
||||
|
||||
static inline ioste
|
||||
get_iost_cache(void __iomem *base, unsigned long index)
|
||||
{
|
||||
unsigned long __iomem *p = (base + IOC_ST_CACHE_DIR);
|
||||
return mk_ioste(in_be64(&p[index]));
|
||||
}
|
||||
|
||||
static inline void
|
||||
set_iost_cache(void __iomem *base, unsigned long index, ioste ste)
|
||||
{
|
||||
unsigned long __iomem *p = (base + IOC_ST_CACHE_DIR);
|
||||
pr_debug("ioste %02lx was %016lx, store %016lx", index,
|
||||
get_iost_cache(base, index).val, ste.val);
|
||||
out_be64(&p[index], ste.val);
|
||||
pr_debug(" now %016lx\n", get_iost_cache(base, index).val);
|
||||
}
|
||||
|
||||
static inline unsigned long
|
||||
get_iopt_cache(void __iomem *base, unsigned long index, unsigned long *tag)
|
||||
{
|
||||
unsigned long __iomem *tags = (void *)(base + IOC_PT_CACHE_DIR);
|
||||
unsigned long __iomem *p = (void *)(base + IOC_PT_CACHE_REG);
|
||||
|
||||
*tag = tags[index];
|
||||
rmb();
|
||||
return *p;
|
||||
}
|
||||
|
||||
static inline void
|
||||
set_iopt_cache(void __iomem *base, unsigned long index,
|
||||
unsigned long tag, unsigned long val)
|
||||
{
|
||||
unsigned long __iomem *tags = base + IOC_PT_CACHE_DIR;
|
||||
unsigned long __iomem *p = base + IOC_PT_CACHE_REG;
|
||||
pr_debug("iopt %02lx was v%016lx/t%016lx, store v%016lx/t%016lx\n",
|
||||
index, get_iopt_cache(base, index, &oldtag), oldtag, val, tag);
|
||||
|
||||
out_be64(p, val);
|
||||
out_be64(&tags[index], tag);
|
||||
}
|
||||
|
||||
static inline void
|
||||
set_iost_origin(void __iomem *base)
|
||||
{
|
||||
unsigned long __iomem *p = base + IOC_ST_ORIGIN;
|
||||
unsigned long origin = IOSTO_ENABLE | IOSTO_SW;
|
||||
|
||||
pr_debug("iost_origin %016lx, now %016lx\n", in_be64(p), origin);
|
||||
out_be64(p, origin);
|
||||
}
|
||||
|
||||
static inline void
|
||||
set_iocmd_config(void __iomem *base)
|
||||
{
|
||||
unsigned long __iomem *p = base + 0xc00;
|
||||
unsigned long conf;
|
||||
|
||||
conf = in_be64(p);
|
||||
pr_debug("iost_conf %016lx, now %016lx\n", conf, conf | IOCMD_CONF_TE);
|
||||
out_be64(p, conf | IOCMD_CONF_TE);
|
||||
}
|
||||
|
||||
/* FIXME: get these from the device tree */
|
||||
#define ioc_base 0x20000511000ull
|
||||
#define ioc_mmio_base 0x20000510000ull
|
||||
#define ioid 0x48a
|
||||
#define iopt_phys_offset (- 0x20000000) /* We have a 512MB offset from the SB */
|
||||
#define io_page_size 0x1000000
|
||||
|
||||
static unsigned long map_iopt_entry(unsigned long address)
|
||||
{
|
||||
switch (address >> 20) {
|
||||
case 0x600:
|
||||
address = 0x24020000000ull; /* spider i/o */
|
||||
break;
|
||||
default:
|
||||
address += iopt_phys_offset;
|
||||
break;
|
||||
}
|
||||
|
||||
return get_iopt_entry(address, ioid, IOPT_PROT_RW);
|
||||
}
|
||||
|
||||
static void iommu_bus_setup_null(struct pci_bus *b) { }
|
||||
static void iommu_dev_setup_null(struct pci_dev *d) { }
|
||||
|
||||
/* initialize the iommu to support a simple linear mapping
|
||||
* for each DMA window used by any device. For now, we
|
||||
* happen to know that there is only one DMA window in use,
|
||||
* starting at iopt_phys_offset. */
|
||||
static void bpa_map_iommu(void)
|
||||
{
|
||||
unsigned long address;
|
||||
void __iomem *base;
|
||||
ioste ioste;
|
||||
unsigned long index;
|
||||
|
||||
base = __ioremap(ioc_base, 0x1000, _PAGE_NO_CACHE);
|
||||
pr_debug("%lx mapped to %p\n", ioc_base, base);
|
||||
set_iocmd_config(base);
|
||||
iounmap(base);
|
||||
|
||||
base = __ioremap(ioc_mmio_base, 0x1000, _PAGE_NO_CACHE);
|
||||
pr_debug("%lx mapped to %p\n", ioc_mmio_base, base);
|
||||
|
||||
set_iost_origin(base);
|
||||
|
||||
for (address = 0; address < 0x100000000ul; address += io_page_size) {
|
||||
ioste = get_iost_entry(0x10000000000ul, address, io_page_size);
|
||||
if ((address & 0xfffffff) == 0) /* segment start */
|
||||
set_iost_cache(base, address >> 28, ioste);
|
||||
index = get_ioc_hash_1way(ioste, address);
|
||||
pr_debug("addr %08lx, index %02lx, ioste %016lx\n",
|
||||
address, index, ioste.val);
|
||||
set_iopt_cache(base,
|
||||
get_ioc_hash_1way(ioste, address),
|
||||
get_ioc_tag(ioste, address),
|
||||
map_iopt_entry(address));
|
||||
}
|
||||
iounmap(base);
|
||||
}
|
||||
|
||||
|
||||
static void *bpa_alloc_coherent(struct device *hwdev, size_t size,
|
||||
dma_addr_t *dma_handle, unsigned int __nocast flag)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
ret = (void *)__get_free_pages(flag, get_order(size));
|
||||
if (ret != NULL) {
|
||||
memset(ret, 0, size);
|
||||
*dma_handle = virt_to_abs(ret) | BPA_DMA_VALID;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bpa_free_coherent(struct device *hwdev, size_t size,
|
||||
void *vaddr, dma_addr_t dma_handle)
|
||||
{
|
||||
free_pages((unsigned long)vaddr, get_order(size));
|
||||
}
|
||||
|
||||
static dma_addr_t bpa_map_single(struct device *hwdev, void *ptr,
|
||||
size_t size, enum dma_data_direction direction)
|
||||
{
|
||||
return virt_to_abs(ptr) | BPA_DMA_VALID;
|
||||
}
|
||||
|
||||
static void bpa_unmap_single(struct device *hwdev, dma_addr_t dma_addr,
|
||||
size_t size, enum dma_data_direction direction)
|
||||
{
|
||||
}
|
||||
|
||||
static int bpa_map_sg(struct device *hwdev, struct scatterlist *sg,
|
||||
int nents, enum dma_data_direction direction)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nents; i++, sg++) {
|
||||
sg->dma_address = (page_to_phys(sg->page) + sg->offset)
|
||||
| BPA_DMA_VALID;
|
||||
sg->dma_length = sg->length;
|
||||
}
|
||||
|
||||
return nents;
|
||||
}
|
||||
|
||||
static void bpa_unmap_sg(struct device *hwdev, struct scatterlist *sg,
|
||||
int nents, enum dma_data_direction direction)
|
||||
{
|
||||
}
|
||||
|
||||
static int bpa_dma_supported(struct device *dev, u64 mask)
|
||||
{
|
||||
return mask < 0x100000000ull;
|
||||
}
|
||||
|
||||
void bpa_init_iommu(void)
|
||||
{
|
||||
bpa_map_iommu();
|
||||
|
||||
/* Direct I/O, IOMMU off */
|
||||
ppc_md.iommu_dev_setup = iommu_dev_setup_null;
|
||||
ppc_md.iommu_bus_setup = iommu_bus_setup_null;
|
||||
|
||||
pci_dma_ops.alloc_coherent = bpa_alloc_coherent;
|
||||
pci_dma_ops.free_coherent = bpa_free_coherent;
|
||||
pci_dma_ops.map_single = bpa_map_single;
|
||||
pci_dma_ops.unmap_single = bpa_unmap_single;
|
||||
pci_dma_ops.map_sg = bpa_map_sg;
|
||||
pci_dma_ops.unmap_sg = bpa_unmap_sg;
|
||||
pci_dma_ops.dma_supported = bpa_dma_supported;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef BPA_IOMMU_H
|
||||
#define BPA_IOMMU_H
|
||||
|
||||
/* some constants */
|
||||
enum {
|
||||
/* segment table entries */
|
||||
IOST_VALID_MASK = 0x8000000000000000ul,
|
||||
IOST_TAG_MASK = 0x3000000000000000ul,
|
||||
IOST_PT_BASE_MASK = 0x000003fffffff000ul,
|
||||
IOST_NNPT_MASK = 0x0000000000000fe0ul,
|
||||
IOST_PS_MASK = 0x000000000000000ful,
|
||||
|
||||
IOST_PS_4K = 0x1,
|
||||
IOST_PS_64K = 0x3,
|
||||
IOST_PS_1M = 0x5,
|
||||
IOST_PS_16M = 0x7,
|
||||
|
||||
/* iopt tag register */
|
||||
IOPT_VALID_MASK = 0x0000000200000000ul,
|
||||
IOPT_TAG_MASK = 0x00000001fffffffful,
|
||||
|
||||
/* iopt cache register */
|
||||
IOPT_PROT_MASK = 0xc000000000000000ul,
|
||||
IOPT_PROT_NONE = 0x0000000000000000ul,
|
||||
IOPT_PROT_READ = 0x4000000000000000ul,
|
||||
IOPT_PROT_WRITE = 0x8000000000000000ul,
|
||||
IOPT_PROT_RW = 0xc000000000000000ul,
|
||||
IOPT_COHERENT = 0x2000000000000000ul,
|
||||
|
||||
IOPT_ORDER_MASK = 0x1800000000000000ul,
|
||||
/* order access to same IOID/VC on same address */
|
||||
IOPT_ORDER_ADDR = 0x0800000000000000ul,
|
||||
/* similar, but only after a write access */
|
||||
IOPT_ORDER_WRITES = 0x1000000000000000ul,
|
||||
/* Order all accesses to same IOID/VC */
|
||||
IOPT_ORDER_VC = 0x1800000000000000ul,
|
||||
|
||||
IOPT_RPN_MASK = 0x000003fffffff000ul,
|
||||
IOPT_HINT_MASK = 0x0000000000000800ul,
|
||||
IOPT_IOID_MASK = 0x00000000000007fful,
|
||||
|
||||
IOSTO_ENABLE = 0x8000000000000000ul,
|
||||
IOSTO_ORIGIN = 0x000003fffffff000ul,
|
||||
IOSTO_HW = 0x0000000000000800ul,
|
||||
IOSTO_SW = 0x0000000000000400ul,
|
||||
|
||||
IOCMD_CONF_TE = 0x0000800000000000ul,
|
||||
|
||||
/* memory mapped registers */
|
||||
IOC_PT_CACHE_DIR = 0x000,
|
||||
IOC_ST_CACHE_DIR = 0x800,
|
||||
IOC_PT_CACHE_REG = 0x910,
|
||||
IOC_ST_ORIGIN = 0x918,
|
||||
IOC_CONF = 0x930,
|
||||
|
||||
/* The high bit needs to be set on every DMA address,
|
||||
only 2GB are addressable */
|
||||
BPA_DMA_VALID = 0x80000000,
|
||||
BPA_DMA_MASK = 0x7fffffff,
|
||||
};
|
||||
|
||||
|
||||
void bpa_init_iommu(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* NVRAM for CPBW
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2005
|
||||
*
|
||||
* Authors : Utz Bacher <utz.bacher@de.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/nvram.h>
|
||||
#include <asm/prom.h>
|
||||
|
||||
static void __iomem *bpa_nvram_start;
|
||||
static long bpa_nvram_len;
|
||||
static spinlock_t bpa_nvram_lock = SPIN_LOCK_UNLOCKED;
|
||||
|
||||
static ssize_t bpa_nvram_read(char *buf, size_t count, loff_t *index)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (*index >= bpa_nvram_len)
|
||||
return 0;
|
||||
if (*index + count > bpa_nvram_len)
|
||||
count = bpa_nvram_len - *index;
|
||||
|
||||
spin_lock_irqsave(&bpa_nvram_lock, flags);
|
||||
|
||||
memcpy_fromio(buf, bpa_nvram_start + *index, count);
|
||||
|
||||
spin_unlock_irqrestore(&bpa_nvram_lock, flags);
|
||||
|
||||
*index += count;
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t bpa_nvram_write(char *buf, size_t count, loff_t *index)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (*index >= bpa_nvram_len)
|
||||
return 0;
|
||||
if (*index + count > bpa_nvram_len)
|
||||
count = bpa_nvram_len - *index;
|
||||
|
||||
spin_lock_irqsave(&bpa_nvram_lock, flags);
|
||||
|
||||
memcpy_toio(bpa_nvram_start + *index, buf, count);
|
||||
|
||||
spin_unlock_irqrestore(&bpa_nvram_lock, flags);
|
||||
|
||||
*index += count;
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t bpa_nvram_get_size(void)
|
||||
{
|
||||
return bpa_nvram_len;
|
||||
}
|
||||
|
||||
int __init bpa_nvram_init(void)
|
||||
{
|
||||
struct device_node *nvram_node;
|
||||
unsigned long *buffer;
|
||||
int proplen;
|
||||
unsigned long nvram_addr;
|
||||
int ret;
|
||||
|
||||
ret = -ENODEV;
|
||||
nvram_node = of_find_node_by_type(NULL, "nvram");
|
||||
if (!nvram_node)
|
||||
goto out;
|
||||
|
||||
ret = -EIO;
|
||||
buffer = (unsigned long *)get_property(nvram_node, "reg", &proplen);
|
||||
if (proplen != 2*sizeof(unsigned long))
|
||||
goto out;
|
||||
|
||||
ret = -ENODEV;
|
||||
nvram_addr = buffer[0];
|
||||
bpa_nvram_len = buffer[1];
|
||||
if ( (!bpa_nvram_len) || (!nvram_addr) )
|
||||
goto out;
|
||||
|
||||
bpa_nvram_start = ioremap(nvram_addr, bpa_nvram_len);
|
||||
if (!bpa_nvram_start)
|
||||
goto out;
|
||||
|
||||
printk(KERN_INFO "BPA NVRAM, %luk mapped to %p\n",
|
||||
bpa_nvram_len >> 10, bpa_nvram_start);
|
||||
|
||||
ppc_md.nvram_read = bpa_nvram_read;
|
||||
ppc_md.nvram_write = bpa_nvram_write;
|
||||
ppc_md.nvram_size = bpa_nvram_get_size;
|
||||
|
||||
out:
|
||||
of_node_put(nvram_node);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* linux/arch/ppc/kernel/bpa_setup.c
|
||||
*
|
||||
* Copyright (C) 1995 Linus Torvalds
|
||||
* Adapted from 'alpha' version by Gary Thomas
|
||||
* Modified by Cort Dougan (cort@cs.nmt.edu)
|
||||
* Modified by PPC64 Team, IBM Corp
|
||||
* Modified by BPA Team, IBM Deutschland Entwicklung GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
#undef DEBUG
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/root_dev.h>
|
||||
#include <linux/console.h>
|
||||
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/rtas.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/nvram.h>
|
||||
#include <asm/cputable.h>
|
||||
|
||||
#include "pci.h"
|
||||
#include "bpa_iic.h"
|
||||
#include "bpa_iommu.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(fmt...) udbg_printf(fmt)
|
||||
#else
|
||||
#define DBG(fmt...)
|
||||
#endif
|
||||
|
||||
void bpa_get_cpuinfo(struct seq_file *m)
|
||||
{
|
||||
struct device_node *root;
|
||||
const char *model = "";
|
||||
|
||||
root = of_find_node_by_path("/");
|
||||
if (root)
|
||||
model = get_property(root, "model", NULL);
|
||||
seq_printf(m, "machine\t\t: BPA %s\n", model);
|
||||
of_node_put(root);
|
||||
}
|
||||
|
||||
static void bpa_progress(char *s, unsigned short hex)
|
||||
{
|
||||
printk("*** %04x : %s\n", hex, s ? s : "");
|
||||
}
|
||||
|
||||
static void __init bpa_setup_arch(void)
|
||||
{
|
||||
ppc_md.init_IRQ = iic_init_IRQ;
|
||||
ppc_md.get_irq = iic_get_irq;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
smp_init_pSeries();
|
||||
#endif
|
||||
|
||||
/* init to some ~sane value until calibrate_delay() runs */
|
||||
loops_per_jiffy = 50000000;
|
||||
|
||||
if (ROOT_DEV == 0) {
|
||||
printk("No ramdisk, default root is /dev/hda2\n");
|
||||
ROOT_DEV = Root_HDA2;
|
||||
}
|
||||
|
||||
/* Find and initialize PCI host bridges */
|
||||
init_pci_config_tokens();
|
||||
find_and_init_phbs();
|
||||
spider_init_IRQ();
|
||||
#ifdef CONFIG_DUMMY_CONSOLE
|
||||
conswitchp = &dummy_con;
|
||||
#endif
|
||||
|
||||
bpa_nvram_init();
|
||||
}
|
||||
|
||||
/*
|
||||
* Early initialization. Relocation is on but do not reference unbolted pages
|
||||
*/
|
||||
static void __init bpa_init_early(void)
|
||||
{
|
||||
DBG(" -> bpa_init_early()\n");
|
||||
|
||||
hpte_init_native();
|
||||
|
||||
bpa_init_iommu();
|
||||
|
||||
ppc64_interrupt_controller = IC_BPA_IIC;
|
||||
|
||||
DBG(" <- bpa_init_early()\n");
|
||||
}
|
||||
|
||||
|
||||
static int __init bpa_probe(int platform)
|
||||
{
|
||||
if (platform != PLATFORM_BPA)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct machdep_calls __initdata bpa_md = {
|
||||
.probe = bpa_probe,
|
||||
.setup_arch = bpa_setup_arch,
|
||||
.init_early = bpa_init_early,
|
||||
.get_cpuinfo = bpa_get_cpuinfo,
|
||||
.restart = rtas_restart,
|
||||
.power_off = rtas_power_off,
|
||||
.halt = rtas_halt,
|
||||
.get_boot_time = rtas_get_boot_time,
|
||||
.get_rtc_time = rtas_get_rtc_time,
|
||||
.set_rtc_time = rtas_set_rtc_time,
|
||||
.calibrate_decr = generic_calibrate_decr,
|
||||
.progress = bpa_progress,
|
||||
};
|
|
@ -73,7 +73,21 @@ _GLOBAL(__970_cpu_preinit)
|
|||
|
||||
_GLOBAL(__setup_cpu_power4)
|
||||
blr
|
||||
|
||||
|
||||
_GLOBAL(__setup_cpu_be)
|
||||
/* Set large page sizes LP=0: 16MB, LP=1: 64KB */
|
||||
addi r3, 0, 0
|
||||
ori r3, r3, HID6_LB
|
||||
sldi r3, r3, 32
|
||||
nor r3, r3, r3
|
||||
mfspr r4, SPRN_HID6
|
||||
and r4, r4, r3
|
||||
addi r3, 0, 0x02000
|
||||
sldi r3, r3, 32
|
||||
or r4, r4, r3
|
||||
mtspr SPRN_HID6, r4
|
||||
blr
|
||||
|
||||
_GLOBAL(__setup_cpu_ppc970)
|
||||
mfspr r0,SPRN_HID0
|
||||
li r11,5 /* clear DOZE and SLEEP */
|
||||
|
|
|
@ -34,6 +34,7 @@ EXPORT_SYMBOL(cur_cpu_spec);
|
|||
extern void __setup_cpu_power3(unsigned long offset, struct cpu_spec* spec);
|
||||
extern void __setup_cpu_power4(unsigned long offset, struct cpu_spec* spec);
|
||||
extern void __setup_cpu_ppc970(unsigned long offset, struct cpu_spec* spec);
|
||||
extern void __setup_cpu_be(unsigned long offset, struct cpu_spec* spec);
|
||||
|
||||
|
||||
/* We only set the altivec features if the kernel was compiled with altivec
|
||||
|
@ -162,6 +163,16 @@ struct cpu_spec cpu_specs[] = {
|
|||
__setup_cpu_power4,
|
||||
COMMON_PPC64_FW
|
||||
},
|
||||
{ /* BE DD1.x */
|
||||
0xffff0000, 0x00700000, "Broadband Engine",
|
||||
CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE |
|
||||
CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_ALTIVEC_COMP |
|
||||
CPU_FTR_SMT,
|
||||
COMMON_USER_PPC64 | PPC_FEATURE_HAS_ALTIVEC_COMP,
|
||||
128, 128,
|
||||
__setup_cpu_be,
|
||||
COMMON_PPC64_FW
|
||||
},
|
||||
{ /* default match */
|
||||
0x00000000, 0x00000000, "POWER4 (compatible)",
|
||||
CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE |
|
||||
|
|
|
@ -671,9 +671,6 @@ static void __init iSeries_bolt_kernel(unsigned long saddr, unsigned long eaddr)
|
|||
}
|
||||
}
|
||||
|
||||
extern unsigned long ppc_proc_freq;
|
||||
extern unsigned long ppc_tb_freq;
|
||||
|
||||
/*
|
||||
* Document me.
|
||||
*/
|
||||
|
@ -772,8 +769,6 @@ static void iSeries_halt(void)
|
|||
mf_power_off();
|
||||
}
|
||||
|
||||
extern void setup_default_decr(void);
|
||||
|
||||
/*
|
||||
* void __init iSeries_calibrate_decr()
|
||||
*
|
||||
|
|
|
@ -395,6 +395,9 @@ int virt_irq_create_mapping(unsigned int real_irq)
|
|||
if (ppc64_interrupt_controller == IC_OPEN_PIC)
|
||||
return real_irq; /* no mapping for openpic (for now) */
|
||||
|
||||
if (ppc64_interrupt_controller == IC_BPA_IIC)
|
||||
return real_irq; /* no mapping for iic either */
|
||||
|
||||
/* don't map interrupts < MIN_VIRT_IRQ */
|
||||
if (real_irq < MIN_VIRT_IRQ) {
|
||||
virt_irq_to_real_map[real_irq] = real_irq;
|
||||
|
|
|
@ -78,17 +78,77 @@ extern int maple_pci_get_legacy_ide_irq(struct pci_dev *dev, int channel);
|
|||
extern void generic_find_legacy_serial_ports(u64 *physport,
|
||||
unsigned int *default_speed);
|
||||
|
||||
|
||||
static void maple_restart(char *cmd)
|
||||
{
|
||||
unsigned int maple_nvram_base;
|
||||
unsigned int maple_nvram_offset;
|
||||
unsigned int maple_nvram_command;
|
||||
struct device_node *rtcs;
|
||||
|
||||
/* find NVRAM device */
|
||||
rtcs = find_compatible_devices("nvram", "AMD8111");
|
||||
if (rtcs && rtcs->addrs) {
|
||||
maple_nvram_base = rtcs->addrs[0].address;
|
||||
} else {
|
||||
printk(KERN_EMERG "Maple: Unable to find NVRAM\n");
|
||||
printk(KERN_EMERG "Maple: Manual Restart Required\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* find service processor device */
|
||||
rtcs = find_devices("service-processor");
|
||||
if (!rtcs) {
|
||||
printk(KERN_EMERG "Maple: Unable to find Service Processor\n");
|
||||
printk(KERN_EMERG "Maple: Manual Restart Required\n");
|
||||
return;
|
||||
}
|
||||
maple_nvram_offset = *(unsigned int*) get_property(rtcs,
|
||||
"restart-addr", NULL);
|
||||
maple_nvram_command = *(unsigned int*) get_property(rtcs,
|
||||
"restart-value", NULL);
|
||||
|
||||
/* send command */
|
||||
outb_p(maple_nvram_command, maple_nvram_base + maple_nvram_offset);
|
||||
for (;;) ;
|
||||
}
|
||||
|
||||
static void maple_power_off(void)
|
||||
{
|
||||
unsigned int maple_nvram_base;
|
||||
unsigned int maple_nvram_offset;
|
||||
unsigned int maple_nvram_command;
|
||||
struct device_node *rtcs;
|
||||
|
||||
/* find NVRAM device */
|
||||
rtcs = find_compatible_devices("nvram", "AMD8111");
|
||||
if (rtcs && rtcs->addrs) {
|
||||
maple_nvram_base = rtcs->addrs[0].address;
|
||||
} else {
|
||||
printk(KERN_EMERG "Maple: Unable to find NVRAM\n");
|
||||
printk(KERN_EMERG "Maple: Manual Power-Down Required\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* find service processor device */
|
||||
rtcs = find_devices("service-processor");
|
||||
if (!rtcs) {
|
||||
printk(KERN_EMERG "Maple: Unable to find Service Processor\n");
|
||||
printk(KERN_EMERG "Maple: Manual Power-Down Required\n");
|
||||
return;
|
||||
}
|
||||
maple_nvram_offset = *(unsigned int*) get_property(rtcs,
|
||||
"power-off-addr", NULL);
|
||||
maple_nvram_command = *(unsigned int*) get_property(rtcs,
|
||||
"power-off-value", NULL);
|
||||
|
||||
/* send command */
|
||||
outb_p(maple_nvram_command, maple_nvram_base + maple_nvram_offset);
|
||||
for (;;) ;
|
||||
}
|
||||
|
||||
static void maple_halt(void)
|
||||
{
|
||||
maple_power_off();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
@ -235,6 +295,6 @@ struct machdep_calls __initdata maple_md = {
|
|||
.get_boot_time = maple_get_boot_time,
|
||||
.set_rtc_time = maple_set_rtc_time,
|
||||
.get_rtc_time = maple_get_rtc_time,
|
||||
.calibrate_decr = maple_calibrate_decr,
|
||||
.calibrate_decr = generic_calibrate_decr,
|
||||
.progress = maple_progress,
|
||||
};
|
||||
|
|
|
@ -42,11 +42,8 @@
|
|||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
extern void setup_default_decr(void);
|
||||
extern void GregorianDay(struct rtc_time * tm);
|
||||
|
||||
extern unsigned long ppc_tb_freq;
|
||||
extern unsigned long ppc_proc_freq;
|
||||
static int maple_rtc_addr;
|
||||
|
||||
static int maple_clock_read(int addr)
|
||||
|
@ -176,51 +173,3 @@ void __init maple_get_boot_time(struct rtc_time *tm)
|
|||
maple_get_rtc_time(tm);
|
||||
}
|
||||
|
||||
/* XXX FIXME: Some sane defaults: 125 MHz timebase, 1GHz processor */
|
||||
#define DEFAULT_TB_FREQ 125000000UL
|
||||
#define DEFAULT_PROC_FREQ (DEFAULT_TB_FREQ * 8)
|
||||
|
||||
void __init maple_calibrate_decr(void)
|
||||
{
|
||||
struct device_node *cpu;
|
||||
struct div_result divres;
|
||||
unsigned int *fp = NULL;
|
||||
|
||||
/*
|
||||
* The cpu node should have a timebase-frequency property
|
||||
* to tell us the rate at which the decrementer counts.
|
||||
*/
|
||||
cpu = of_find_node_by_type(NULL, "cpu");
|
||||
|
||||
ppc_tb_freq = DEFAULT_TB_FREQ;
|
||||
if (cpu != 0)
|
||||
fp = (unsigned int *)get_property(cpu, "timebase-frequency", NULL);
|
||||
if (fp != NULL)
|
||||
ppc_tb_freq = *fp;
|
||||
else
|
||||
printk(KERN_ERR "WARNING: Estimating decrementer frequency (not found)\n");
|
||||
fp = NULL;
|
||||
ppc_proc_freq = DEFAULT_PROC_FREQ;
|
||||
if (cpu != 0)
|
||||
fp = (unsigned int *)get_property(cpu, "clock-frequency", NULL);
|
||||
if (fp != NULL)
|
||||
ppc_proc_freq = *fp;
|
||||
else
|
||||
printk(KERN_ERR "WARNING: Estimating processor frequency (not found)\n");
|
||||
|
||||
of_node_put(cpu);
|
||||
|
||||
printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n",
|
||||
ppc_tb_freq/1000000, ppc_tb_freq%1000000);
|
||||
printk(KERN_INFO "time_init: processor frequency = %lu.%.6lu MHz\n",
|
||||
ppc_proc_freq/1000000, ppc_proc_freq%1000000);
|
||||
|
||||
tb_ticks_per_jiffy = ppc_tb_freq / HZ;
|
||||
tb_ticks_per_sec = tb_ticks_per_jiffy * HZ;
|
||||
tb_ticks_per_usec = ppc_tb_freq / 1000000;
|
||||
tb_to_us = mulhwu_scale_factor(ppc_tb_freq, 1000000);
|
||||
div128_by_32(1024*1024, 0, tb_ticks_per_sec, &divres);
|
||||
tb_to_xs = divres.result_low;
|
||||
|
||||
setup_default_decr();
|
||||
}
|
||||
|
|
|
@ -265,3 +265,6 @@ extern void mpic_send_ipi(unsigned int ipi_no, unsigned int cpu_mask);
|
|||
extern int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs);
|
||||
/* This one gets to the primary mpic */
|
||||
extern int mpic_get_irq(struct pt_regs *regs);
|
||||
|
||||
/* global mpic for pSeries */
|
||||
extern struct mpic *pSeries_mpic;
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
/*
|
||||
* pSeries_pci.c
|
||||
* arch/ppc64/kernel/pSeries_pci.c
|
||||
*
|
||||
* Copyright (C) 2001 Dave Engebretsen, IBM Corporation
|
||||
* Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM
|
||||
*
|
||||
* pSeries specific routines for PCI.
|
||||
*
|
||||
* Based on code from pci.c and chrp_pci.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
|
@ -23,430 +21,18 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bootmem.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/rtas.h>
|
||||
#include <asm/prom.h>
|
||||
|
||||
#include "mpic.h"
|
||||
#include "pci.h"
|
||||
|
||||
/* RTAS tokens */
|
||||
static int read_pci_config;
|
||||
static int write_pci_config;
|
||||
static int ibm_read_pci_config;
|
||||
static int ibm_write_pci_config;
|
||||
|
||||
static int s7a_workaround;
|
||||
|
||||
extern struct mpic *pSeries_mpic;
|
||||
|
||||
static int config_access_valid(struct device_node *dn, int where)
|
||||
{
|
||||
if (where < 256)
|
||||
return 1;
|
||||
if (where < 4096 && dn->pci_ext_config_space)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtas_read_config(struct device_node *dn, int where, int size, u32 *val)
|
||||
{
|
||||
int returnval = -1;
|
||||
unsigned long buid, addr;
|
||||
int ret;
|
||||
|
||||
if (!dn)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
if (!config_access_valid(dn, where))
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
addr = ((where & 0xf00) << 20) | (dn->busno << 16) |
|
||||
(dn->devfn << 8) | (where & 0xff);
|
||||
buid = dn->phb->buid;
|
||||
if (buid) {
|
||||
ret = rtas_call(ibm_read_pci_config, 4, 2, &returnval,
|
||||
addr, buid >> 32, buid & 0xffffffff, size);
|
||||
} else {
|
||||
ret = rtas_call(read_pci_config, 2, 2, &returnval, addr, size);
|
||||
}
|
||||
*val = returnval;
|
||||
|
||||
if (ret)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
if (returnval == EEH_IO_ERROR_VALUE(size)
|
||||
&& eeh_dn_check_failure (dn, NULL))
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int rtas_pci_read_config(struct pci_bus *bus,
|
||||
unsigned int devfn,
|
||||
int where, int size, u32 *val)
|
||||
{
|
||||
struct device_node *busdn, *dn;
|
||||
|
||||
if (bus->self)
|
||||
busdn = pci_device_to_OF_node(bus->self);
|
||||
else
|
||||
busdn = bus->sysdata; /* must be a phb */
|
||||
|
||||
/* Search only direct children of the bus */
|
||||
for (dn = busdn->child; dn; dn = dn->sibling)
|
||||
if (dn->devfn == devfn)
|
||||
return rtas_read_config(dn, where, size, val);
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
static int rtas_write_config(struct device_node *dn, int where, int size, u32 val)
|
||||
{
|
||||
unsigned long buid, addr;
|
||||
int ret;
|
||||
|
||||
if (!dn)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
if (!config_access_valid(dn, where))
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
addr = ((where & 0xf00) << 20) | (dn->busno << 16) |
|
||||
(dn->devfn << 8) | (where & 0xff);
|
||||
buid = dn->phb->buid;
|
||||
if (buid) {
|
||||
ret = rtas_call(ibm_write_pci_config, 5, 1, NULL, addr, buid >> 32, buid & 0xffffffff, size, (ulong) val);
|
||||
} else {
|
||||
ret = rtas_call(write_pci_config, 3, 1, NULL, addr, size, (ulong)val);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int rtas_pci_write_config(struct pci_bus *bus,
|
||||
unsigned int devfn,
|
||||
int where, int size, u32 val)
|
||||
{
|
||||
struct device_node *busdn, *dn;
|
||||
|
||||
if (bus->self)
|
||||
busdn = pci_device_to_OF_node(bus->self);
|
||||
else
|
||||
busdn = bus->sysdata; /* must be a phb */
|
||||
|
||||
/* Search only direct children of the bus */
|
||||
for (dn = busdn->child; dn; dn = dn->sibling)
|
||||
if (dn->devfn == devfn)
|
||||
return rtas_write_config(dn, where, size, val);
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
struct pci_ops rtas_pci_ops = {
|
||||
rtas_pci_read_config,
|
||||
rtas_pci_write_config
|
||||
};
|
||||
|
||||
int is_python(struct device_node *dev)
|
||||
{
|
||||
char *model = (char *)get_property(dev, "model", NULL);
|
||||
|
||||
if (model && strstr(model, "Python"))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_phb_reg_prop(struct device_node *dev,
|
||||
unsigned int addr_size_words,
|
||||
struct reg_property64 *reg)
|
||||
{
|
||||
unsigned int *ui_ptr = NULL, len;
|
||||
|
||||
/* Found a PHB, now figure out where his registers are mapped. */
|
||||
ui_ptr = (unsigned int *)get_property(dev, "reg", &len);
|
||||
if (ui_ptr == NULL)
|
||||
return 1;
|
||||
|
||||
if (addr_size_words == 1) {
|
||||
reg->address = ((struct reg_property32 *)ui_ptr)->address;
|
||||
reg->size = ((struct reg_property32 *)ui_ptr)->size;
|
||||
} else {
|
||||
*reg = *((struct reg_property64 *)ui_ptr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void python_countermeasures(struct device_node *dev,
|
||||
unsigned int addr_size_words)
|
||||
{
|
||||
struct reg_property64 reg_struct;
|
||||
void __iomem *chip_regs;
|
||||
volatile u32 val;
|
||||
|
||||
if (get_phb_reg_prop(dev, addr_size_words, ®_struct))
|
||||
return;
|
||||
|
||||
/* Python's register file is 1 MB in size. */
|
||||
chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), 0x100000);
|
||||
|
||||
/*
|
||||
* Firmware doesn't always clear this bit which is critical
|
||||
* for good performance - Anton
|
||||
*/
|
||||
|
||||
#define PRG_CL_RESET_VALID 0x00010000
|
||||
|
||||
val = in_be32(chip_regs + 0xf6030);
|
||||
if (val & PRG_CL_RESET_VALID) {
|
||||
printk(KERN_INFO "Python workaround: ");
|
||||
val &= ~PRG_CL_RESET_VALID;
|
||||
out_be32(chip_regs + 0xf6030, val);
|
||||
/*
|
||||
* We must read it back for changes to
|
||||
* take effect
|
||||
*/
|
||||
val = in_be32(chip_regs + 0xf6030);
|
||||
printk("reg0: %x\n", val);
|
||||
}
|
||||
|
||||
iounmap(chip_regs);
|
||||
}
|
||||
|
||||
void __init init_pci_config_tokens (void)
|
||||
{
|
||||
read_pci_config = rtas_token("read-pci-config");
|
||||
write_pci_config = rtas_token("write-pci-config");
|
||||
ibm_read_pci_config = rtas_token("ibm,read-pci-config");
|
||||
ibm_write_pci_config = rtas_token("ibm,write-pci-config");
|
||||
}
|
||||
|
||||
unsigned long __devinit get_phb_buid (struct device_node *phb)
|
||||
{
|
||||
int addr_cells;
|
||||
unsigned int *buid_vals;
|
||||
unsigned int len;
|
||||
unsigned long buid;
|
||||
|
||||
if (ibm_read_pci_config == -1) return 0;
|
||||
|
||||
/* PHB's will always be children of the root node,
|
||||
* or so it is promised by the current firmware. */
|
||||
if (phb->parent == NULL)
|
||||
return 0;
|
||||
if (phb->parent->parent)
|
||||
return 0;
|
||||
|
||||
buid_vals = (unsigned int *) get_property(phb, "reg", &len);
|
||||
if (buid_vals == NULL)
|
||||
return 0;
|
||||
|
||||
addr_cells = prom_n_addr_cells(phb);
|
||||
if (addr_cells == 1) {
|
||||
buid = (unsigned long) buid_vals[0];
|
||||
} else {
|
||||
buid = (((unsigned long)buid_vals[0]) << 32UL) |
|
||||
(((unsigned long)buid_vals[1]) & 0xffffffff);
|
||||
}
|
||||
return buid;
|
||||
}
|
||||
|
||||
static int phb_set_bus_ranges(struct device_node *dev,
|
||||
struct pci_controller *phb)
|
||||
{
|
||||
int *bus_range;
|
||||
unsigned int len;
|
||||
|
||||
bus_range = (int *) get_property(dev, "bus-range", &len);
|
||||
if (bus_range == NULL || len < 2 * sizeof(int)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
phb->first_busno = bus_range[0];
|
||||
phb->last_busno = bus_range[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit setup_phb(struct device_node *dev,
|
||||
struct pci_controller *phb,
|
||||
unsigned int addr_size_words)
|
||||
{
|
||||
pci_setup_pci_controller(phb);
|
||||
|
||||
if (is_python(dev))
|
||||
python_countermeasures(dev, addr_size_words);
|
||||
|
||||
if (phb_set_bus_ranges(dev, phb))
|
||||
return 1;
|
||||
|
||||
phb->arch_data = dev;
|
||||
phb->ops = &rtas_pci_ops;
|
||||
phb->buid = get_phb_buid(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devinit add_linux_pci_domain(struct device_node *dev,
|
||||
struct pci_controller *phb,
|
||||
struct property *of_prop)
|
||||
{
|
||||
memset(of_prop, 0, sizeof(struct property));
|
||||
of_prop->name = "linux,pci-domain";
|
||||
of_prop->length = sizeof(phb->global_number);
|
||||
of_prop->value = (unsigned char *)&of_prop[1];
|
||||
memcpy(of_prop->value, &phb->global_number, sizeof(phb->global_number));
|
||||
prom_add_property(dev, of_prop);
|
||||
}
|
||||
|
||||
static struct pci_controller * __init alloc_phb(struct device_node *dev,
|
||||
unsigned int addr_size_words)
|
||||
{
|
||||
struct pci_controller *phb;
|
||||
struct property *of_prop;
|
||||
|
||||
phb = alloc_bootmem(sizeof(struct pci_controller));
|
||||
if (phb == NULL)
|
||||
return NULL;
|
||||
|
||||
of_prop = alloc_bootmem(sizeof(struct property) +
|
||||
sizeof(phb->global_number));
|
||||
if (!of_prop)
|
||||
return NULL;
|
||||
|
||||
if (setup_phb(dev, phb, addr_size_words))
|
||||
return NULL;
|
||||
|
||||
add_linux_pci_domain(dev, phb, of_prop);
|
||||
|
||||
return phb;
|
||||
}
|
||||
|
||||
static struct pci_controller * __devinit alloc_phb_dynamic(struct device_node *dev, unsigned int addr_size_words)
|
||||
{
|
||||
struct pci_controller *phb;
|
||||
|
||||
phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller),
|
||||
GFP_KERNEL);
|
||||
if (phb == NULL)
|
||||
return NULL;
|
||||
|
||||
if (setup_phb(dev, phb, addr_size_words))
|
||||
return NULL;
|
||||
|
||||
phb->is_dynamic = 1;
|
||||
|
||||
/* TODO: linux,pci-domain? */
|
||||
|
||||
return phb;
|
||||
}
|
||||
|
||||
unsigned long __init find_and_init_phbs(void)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct pci_controller *phb;
|
||||
unsigned int root_size_cells = 0;
|
||||
unsigned int index;
|
||||
unsigned int *opprop = NULL;
|
||||
struct device_node *root = of_find_node_by_path("/");
|
||||
|
||||
if (ppc64_interrupt_controller == IC_OPEN_PIC) {
|
||||
opprop = (unsigned int *)get_property(root,
|
||||
"platform-open-pic", NULL);
|
||||
}
|
||||
|
||||
root_size_cells = prom_n_size_cells(root);
|
||||
|
||||
index = 0;
|
||||
|
||||
for (node = of_get_next_child(root, NULL);
|
||||
node != NULL;
|
||||
node = of_get_next_child(root, node)) {
|
||||
if (node->type == NULL || strcmp(node->type, "pci") != 0)
|
||||
continue;
|
||||
|
||||
phb = alloc_phb(node, root_size_cells);
|
||||
if (!phb)
|
||||
continue;
|
||||
|
||||
pci_process_bridge_OF_ranges(phb, node);
|
||||
pci_setup_phb_io(phb, index == 0);
|
||||
|
||||
if (ppc64_interrupt_controller == IC_OPEN_PIC && pSeries_mpic) {
|
||||
int addr = root_size_cells * (index + 2) - 1;
|
||||
mpic_assign_isu(pSeries_mpic, index, opprop[addr]);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
of_node_put(root);
|
||||
pci_devs_phb_init();
|
||||
|
||||
/*
|
||||
* pci_probe_only and pci_assign_all_buses can be set via properties
|
||||
* in chosen.
|
||||
*/
|
||||
if (of_chosen) {
|
||||
int *prop;
|
||||
|
||||
prop = (int *)get_property(of_chosen, "linux,pci-probe-only",
|
||||
NULL);
|
||||
if (prop)
|
||||
pci_probe_only = *prop;
|
||||
|
||||
prop = (int *)get_property(of_chosen,
|
||||
"linux,pci-assign-all-buses", NULL);
|
||||
if (prop)
|
||||
pci_assign_all_buses = *prop;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn)
|
||||
{
|
||||
struct device_node *root = of_find_node_by_path("/");
|
||||
unsigned int root_size_cells = 0;
|
||||
struct pci_controller *phb;
|
||||
struct pci_bus *bus;
|
||||
int primary;
|
||||
|
||||
root_size_cells = prom_n_size_cells(root);
|
||||
|
||||
primary = list_empty(&hose_list);
|
||||
phb = alloc_phb_dynamic(dn, root_size_cells);
|
||||
if (!phb)
|
||||
return NULL;
|
||||
|
||||
pci_process_bridge_OF_ranges(phb, dn);
|
||||
|
||||
pci_setup_phb_io_dynamic(phb, primary);
|
||||
of_node_put(root);
|
||||
|
||||
pci_devs_phb_init_dynamic(phb);
|
||||
phb->last_busno = 0xff;
|
||||
bus = pci_scan_bus(phb->first_busno, phb->ops, phb->arch_data);
|
||||
phb->bus = bus;
|
||||
phb->last_busno = bus->subordinate;
|
||||
|
||||
return phb;
|
||||
}
|
||||
EXPORT_SYMBOL(init_phb_dynamic);
|
||||
static int __initdata s7a_workaround = -1;
|
||||
|
||||
#if 0
|
||||
void pcibios_name_device(struct pci_dev *dev)
|
||||
|
@ -474,11 +60,12 @@ void pcibios_name_device(struct pci_dev *dev)
|
|||
DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_name_device);
|
||||
#endif
|
||||
|
||||
static void check_s7a(void)
|
||||
static void __init check_s7a(void)
|
||||
{
|
||||
struct device_node *root;
|
||||
char *model;
|
||||
|
||||
s7a_workaround = 0;
|
||||
root = of_find_node_by_path("/");
|
||||
if (root) {
|
||||
model = get_property(root, "model", NULL);
|
||||
|
@ -488,55 +75,23 @@ static void check_s7a(void)
|
|||
}
|
||||
}
|
||||
|
||||
/* RPA-specific bits for removing PHBs */
|
||||
int pcibios_remove_root_bus(struct pci_controller *phb)
|
||||
void __devinit pSeries_irq_bus_setup(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_bus *b = phb->bus;
|
||||
struct resource *res;
|
||||
int rc, i;
|
||||
struct pci_dev *dev;
|
||||
|
||||
res = b->resource[0];
|
||||
if (!res->flags) {
|
||||
printk(KERN_ERR "%s: no IO resource for PHB %s\n", __FUNCTION__,
|
||||
b->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
rc = unmap_bus_range(b);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "%s: failed to unmap IO on bus %s\n",
|
||||
__FUNCTION__, b->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (release_resource(res)) {
|
||||
printk(KERN_ERR "%s: failed to release IO on bus %s\n",
|
||||
__FUNCTION__, b->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 1; i < 3; ++i) {
|
||||
res = b->resource[i];
|
||||
if (!res->flags && i == 0) {
|
||||
printk(KERN_ERR "%s: no MEM resource for PHB %s\n",
|
||||
__FUNCTION__, b->name);
|
||||
return 1;
|
||||
}
|
||||
if (res->flags && release_resource(res)) {
|
||||
printk(KERN_ERR
|
||||
"%s: failed to release IO %d on bus %s\n",
|
||||
__FUNCTION__, i, b->name);
|
||||
return 1;
|
||||
if (s7a_workaround < 0)
|
||||
check_s7a();
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
pci_read_irq_line(dev);
|
||||
if (s7a_workaround) {
|
||||
if (dev->irq > 16) {
|
||||
dev->irq -= 3;
|
||||
pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
|
||||
dev->irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list_del(&phb->list_node);
|
||||
if (phb->is_dynamic)
|
||||
kfree(phb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(pcibios_remove_root_bus);
|
||||
|
||||
static void __init pSeries_request_regions(void)
|
||||
{
|
||||
|
@ -553,20 +108,6 @@ static void __init pSeries_request_regions(void)
|
|||
|
||||
void __init pSeries_final_fixup(void)
|
||||
{
|
||||
struct pci_dev *dev = NULL;
|
||||
|
||||
check_s7a();
|
||||
|
||||
for_each_pci_dev(dev) {
|
||||
pci_read_irq_line(dev);
|
||||
if (s7a_workaround) {
|
||||
if (dev->irq > 16) {
|
||||
dev->irq -= 3;
|
||||
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
phbs_remap_io();
|
||||
pSeries_request_regions();
|
||||
|
||||
|
|
|
@ -71,11 +71,6 @@
|
|||
#define DBG(fmt...)
|
||||
#endif
|
||||
|
||||
extern void pSeries_final_fixup(void);
|
||||
|
||||
extern void pSeries_get_boot_time(struct rtc_time *rtc_time);
|
||||
extern void pSeries_get_rtc_time(struct rtc_time *rtc_time);
|
||||
extern int pSeries_set_rtc_time(struct rtc_time *rtc_time);
|
||||
extern void find_udbg_vterm(void);
|
||||
extern void system_reset_fwnmi(void); /* from head.S */
|
||||
extern void machine_check_fwnmi(void); /* from head.S */
|
||||
|
@ -84,9 +79,6 @@ extern void generic_find_legacy_serial_ports(u64 *physport,
|
|||
|
||||
int fwnmi_active; /* TRUE if an FWNMI handler is present */
|
||||
|
||||
extern unsigned long ppc_proc_freq;
|
||||
extern unsigned long ppc_tb_freq;
|
||||
|
||||
extern void pSeries_system_reset_exception(struct pt_regs *regs);
|
||||
extern int pSeries_machine_check_exception(struct pt_regs *regs);
|
||||
|
||||
|
@ -381,171 +373,6 @@ static void __init pSeries_init_early(void)
|
|||
}
|
||||
|
||||
|
||||
static void pSeries_progress(char *s, unsigned short hex)
|
||||
{
|
||||
struct device_node *root;
|
||||
int width, *p;
|
||||
char *os;
|
||||
static int display_character, set_indicator;
|
||||
static int max_width;
|
||||
static DEFINE_SPINLOCK(progress_lock);
|
||||
static int pending_newline = 0; /* did last write end with unprinted newline? */
|
||||
|
||||
if (!rtas.base)
|
||||
return;
|
||||
|
||||
if (max_width == 0) {
|
||||
if ((root = find_path_device("/rtas")) &&
|
||||
(p = (unsigned int *)get_property(root,
|
||||
"ibm,display-line-length",
|
||||
NULL)))
|
||||
max_width = *p;
|
||||
else
|
||||
max_width = 0x10;
|
||||
display_character = rtas_token("display-character");
|
||||
set_indicator = rtas_token("set-indicator");
|
||||
}
|
||||
|
||||
if (display_character == RTAS_UNKNOWN_SERVICE) {
|
||||
/* use hex display if available */
|
||||
if (set_indicator != RTAS_UNKNOWN_SERVICE)
|
||||
rtas_call(set_indicator, 3, 1, NULL, 6, 0, hex);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock(&progress_lock);
|
||||
|
||||
/*
|
||||
* Last write ended with newline, but we didn't print it since
|
||||
* it would just clear the bottom line of output. Print it now
|
||||
* instead.
|
||||
*
|
||||
* If no newline is pending, print a CR to start output at the
|
||||
* beginning of the line.
|
||||
*/
|
||||
if (pending_newline) {
|
||||
rtas_call(display_character, 1, 1, NULL, '\r');
|
||||
rtas_call(display_character, 1, 1, NULL, '\n');
|
||||
pending_newline = 0;
|
||||
} else {
|
||||
rtas_call(display_character, 1, 1, NULL, '\r');
|
||||
}
|
||||
|
||||
width = max_width;
|
||||
os = s;
|
||||
while (*os) {
|
||||
if (*os == '\n' || *os == '\r') {
|
||||
/* Blank to end of line. */
|
||||
while (width-- > 0)
|
||||
rtas_call(display_character, 1, 1, NULL, ' ');
|
||||
|
||||
/* If newline is the last character, save it
|
||||
* until next call to avoid bumping up the
|
||||
* display output.
|
||||
*/
|
||||
if (*os == '\n' && !os[1]) {
|
||||
pending_newline = 1;
|
||||
spin_unlock(&progress_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* RTAS wants CR-LF, not just LF */
|
||||
|
||||
if (*os == '\n') {
|
||||
rtas_call(display_character, 1, 1, NULL, '\r');
|
||||
rtas_call(display_character, 1, 1, NULL, '\n');
|
||||
} else {
|
||||
/* CR might be used to re-draw a line, so we'll
|
||||
* leave it alone and not add LF.
|
||||
*/
|
||||
rtas_call(display_character, 1, 1, NULL, *os);
|
||||
}
|
||||
|
||||
width = max_width;
|
||||
} else {
|
||||
width--;
|
||||
rtas_call(display_character, 1, 1, NULL, *os);
|
||||
}
|
||||
|
||||
os++;
|
||||
|
||||
/* if we overwrite the screen length */
|
||||
if (width <= 0)
|
||||
while ((*os != 0) && (*os != '\n') && (*os != '\r'))
|
||||
os++;
|
||||
}
|
||||
|
||||
/* Blank to end of line. */
|
||||
while (width-- > 0)
|
||||
rtas_call(display_character, 1, 1, NULL, ' ');
|
||||
|
||||
spin_unlock(&progress_lock);
|
||||
}
|
||||
|
||||
extern void setup_default_decr(void);
|
||||
|
||||
/* Some sane defaults: 125 MHz timebase, 1GHz processor */
|
||||
#define DEFAULT_TB_FREQ 125000000UL
|
||||
#define DEFAULT_PROC_FREQ (DEFAULT_TB_FREQ * 8)
|
||||
|
||||
static void __init pSeries_calibrate_decr(void)
|
||||
{
|
||||
struct device_node *cpu;
|
||||
struct div_result divres;
|
||||
unsigned int *fp;
|
||||
int node_found;
|
||||
|
||||
/*
|
||||
* The cpu node should have a timebase-frequency property
|
||||
* to tell us the rate at which the decrementer counts.
|
||||
*/
|
||||
cpu = of_find_node_by_type(NULL, "cpu");
|
||||
|
||||
ppc_tb_freq = DEFAULT_TB_FREQ; /* hardcoded default */
|
||||
node_found = 0;
|
||||
if (cpu != 0) {
|
||||
fp = (unsigned int *)get_property(cpu, "timebase-frequency",
|
||||
NULL);
|
||||
if (fp != 0) {
|
||||
node_found = 1;
|
||||
ppc_tb_freq = *fp;
|
||||
}
|
||||
}
|
||||
if (!node_found)
|
||||
printk(KERN_ERR "WARNING: Estimating decrementer frequency "
|
||||
"(not found)\n");
|
||||
|
||||
ppc_proc_freq = DEFAULT_PROC_FREQ;
|
||||
node_found = 0;
|
||||
if (cpu != 0) {
|
||||
fp = (unsigned int *)get_property(cpu, "clock-frequency",
|
||||
NULL);
|
||||
if (fp != 0) {
|
||||
node_found = 1;
|
||||
ppc_proc_freq = *fp;
|
||||
}
|
||||
}
|
||||
if (!node_found)
|
||||
printk(KERN_ERR "WARNING: Estimating processor frequency "
|
||||
"(not found)\n");
|
||||
|
||||
of_node_put(cpu);
|
||||
|
||||
printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n",
|
||||
ppc_tb_freq/1000000, ppc_tb_freq%1000000);
|
||||
printk(KERN_INFO "time_init: processor frequency = %lu.%.6lu MHz\n",
|
||||
ppc_proc_freq/1000000, ppc_proc_freq%1000000);
|
||||
|
||||
tb_ticks_per_jiffy = ppc_tb_freq / HZ;
|
||||
tb_ticks_per_sec = tb_ticks_per_jiffy * HZ;
|
||||
tb_ticks_per_usec = ppc_tb_freq / 1000000;
|
||||
tb_to_us = mulhwu_scale_factor(ppc_tb_freq, 1000000);
|
||||
div128_by_32(1024*1024, 0, tb_ticks_per_sec, &divres);
|
||||
tb_to_xs = divres.result_low;
|
||||
|
||||
setup_default_decr();
|
||||
}
|
||||
|
||||
static int pSeries_check_legacy_ioport(unsigned int baseport)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
@ -596,16 +423,17 @@ struct machdep_calls __initdata pSeries_md = {
|
|||
.get_cpuinfo = pSeries_get_cpuinfo,
|
||||
.log_error = pSeries_log_error,
|
||||
.pcibios_fixup = pSeries_final_fixup,
|
||||
.irq_bus_setup = pSeries_irq_bus_setup,
|
||||
.restart = rtas_restart,
|
||||
.power_off = rtas_power_off,
|
||||
.halt = rtas_halt,
|
||||
.panic = rtas_os_term,
|
||||
.cpu_die = pSeries_mach_cpu_die,
|
||||
.get_boot_time = pSeries_get_boot_time,
|
||||
.get_rtc_time = pSeries_get_rtc_time,
|
||||
.set_rtc_time = pSeries_set_rtc_time,
|
||||
.calibrate_decr = pSeries_calibrate_decr,
|
||||
.progress = pSeries_progress,
|
||||
.get_boot_time = rtas_get_boot_time,
|
||||
.get_rtc_time = rtas_get_rtc_time,
|
||||
.set_rtc_time = rtas_set_rtc_time,
|
||||
.calibrate_decr = generic_calibrate_decr,
|
||||
.progress = rtas_progress,
|
||||
.check_legacy_ioport = pSeries_check_legacy_ioport,
|
||||
.system_reset_exception = pSeries_system_reset_exception,
|
||||
.machine_check_exception = pSeries_machine_check_exception,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SMP support for pSeries machines.
|
||||
* SMP support for pSeries and BPA machines.
|
||||
*
|
||||
* Dave Engebretsen, Peter Bergner, and
|
||||
* Mike Corrigan {engebret|bergner|mikec}@us.ibm.com
|
||||
|
@ -47,6 +47,7 @@
|
|||
#include <asm/pSeries_reconfig.h>
|
||||
|
||||
#include "mpic.h"
|
||||
#include "bpa_iic.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(fmt...) udbg_printf(fmt)
|
||||
|
@ -286,6 +287,7 @@ static inline int __devinit smp_startup_cpu(unsigned int lcpu)
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_XICS
|
||||
static inline void smp_xics_do_message(int cpu, int msg)
|
||||
{
|
||||
set_bit(msg, &xics_ipi_message[cpu].value);
|
||||
|
@ -327,6 +329,37 @@ static void __devinit smp_xics_setup_cpu(int cpu)
|
|||
cpu_clear(cpu, of_spin_map);
|
||||
|
||||
}
|
||||
#endif /* CONFIG_XICS */
|
||||
#ifdef CONFIG_BPA_IIC
|
||||
static void smp_iic_message_pass(int target, int msg)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (target < NR_CPUS) {
|
||||
iic_cause_IPI(target, msg);
|
||||
} else {
|
||||
for_each_online_cpu(i) {
|
||||
if (target == MSG_ALL_BUT_SELF
|
||||
&& i == smp_processor_id())
|
||||
continue;
|
||||
iic_cause_IPI(i, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int __init smp_iic_probe(void)
|
||||
{
|
||||
iic_request_IPIs();
|
||||
|
||||
return cpus_weight(cpu_possible_map);
|
||||
}
|
||||
|
||||
static void __devinit smp_iic_setup_cpu(int cpu)
|
||||
{
|
||||
if (cpu != boot_cpuid)
|
||||
iic_setup_cpu();
|
||||
}
|
||||
#endif /* CONFIG_BPA_IIC */
|
||||
|
||||
static DEFINE_SPINLOCK(timebase_lock);
|
||||
static unsigned long timebase = 0;
|
||||
|
@ -381,14 +414,15 @@ static int smp_pSeries_cpu_bootable(unsigned int nr)
|
|||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MPIC
|
||||
static struct smp_ops_t pSeries_mpic_smp_ops = {
|
||||
.message_pass = smp_mpic_message_pass,
|
||||
.probe = smp_mpic_probe,
|
||||
.kick_cpu = smp_pSeries_kick_cpu,
|
||||
.setup_cpu = smp_mpic_setup_cpu,
|
||||
};
|
||||
|
||||
#endif
|
||||
#ifdef CONFIG_XICS
|
||||
static struct smp_ops_t pSeries_xics_smp_ops = {
|
||||
.message_pass = smp_xics_message_pass,
|
||||
.probe = smp_xics_probe,
|
||||
|
@ -396,6 +430,16 @@ static struct smp_ops_t pSeries_xics_smp_ops = {
|
|||
.setup_cpu = smp_xics_setup_cpu,
|
||||
.cpu_bootable = smp_pSeries_cpu_bootable,
|
||||
};
|
||||
#endif
|
||||
#ifdef CONFIG_BPA_IIC
|
||||
static struct smp_ops_t bpa_iic_smp_ops = {
|
||||
.message_pass = smp_iic_message_pass,
|
||||
.probe = smp_iic_probe,
|
||||
.kick_cpu = smp_pSeries_kick_cpu,
|
||||
.setup_cpu = smp_iic_setup_cpu,
|
||||
.cpu_bootable = smp_pSeries_cpu_bootable,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* This is called very early */
|
||||
void __init smp_init_pSeries(void)
|
||||
|
@ -404,10 +448,25 @@ void __init smp_init_pSeries(void)
|
|||
|
||||
DBG(" -> smp_init_pSeries()\n");
|
||||
|
||||
if (ppc64_interrupt_controller == IC_OPEN_PIC)
|
||||
switch (ppc64_interrupt_controller) {
|
||||
#ifdef CONFIG_MPIC
|
||||
case IC_OPEN_PIC:
|
||||
smp_ops = &pSeries_mpic_smp_ops;
|
||||
else
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_XICS
|
||||
case IC_PPC_XIC:
|
||||
smp_ops = &pSeries_xics_smp_ops;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_BPA_IIC
|
||||
case IC_BPA_IIC:
|
||||
smp_ops = &bpa_iic_smp_ops;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
panic("Invalid interrupt controller");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
smp_ops->cpu_disable = pSeries_cpu_disable;
|
||||
|
|
|
@ -902,6 +902,9 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus)
|
|||
list_for_each_entry(dev, &bus->devices, bus_list)
|
||||
ppc_md.iommu_dev_setup(dev);
|
||||
|
||||
if (ppc_md.irq_bus_setup)
|
||||
ppc_md.irq_bus_setup(bus);
|
||||
|
||||
if (!pci_probe_only)
|
||||
return;
|
||||
|
||||
|
|
|
@ -40,10 +40,14 @@ struct device_node *fetch_dev_dn(struct pci_dev *dev);
|
|||
void pci_addr_cache_insert_device(struct pci_dev *dev);
|
||||
void pci_addr_cache_remove_device(struct pci_dev *dev);
|
||||
|
||||
/* From pSeries_pci.h */
|
||||
/* From rtas_pci.h */
|
||||
void init_pci_config_tokens (void);
|
||||
unsigned long get_phb_buid (struct device_node *);
|
||||
|
||||
/* From pSeries_pci.h */
|
||||
extern void pSeries_final_fixup(void);
|
||||
extern void pSeries_irq_bus_setup(struct pci_bus *bus);
|
||||
|
||||
extern unsigned long pci_probe_only;
|
||||
extern unsigned long pci_assign_all_buses;
|
||||
extern int pci_read_irq_line(struct pci_dev *pci_dev);
|
||||
|
|
|
@ -40,11 +40,6 @@
|
|||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
extern void setup_default_decr(void);
|
||||
|
||||
extern unsigned long ppc_tb_freq;
|
||||
extern unsigned long ppc_proc_freq;
|
||||
|
||||
/* Apparently the RTC stores seconds since 1 Jan 1904 */
|
||||
#define RTC_OFFSET 2082844800
|
||||
|
||||
|
@ -161,8 +156,7 @@ void __init pmac_get_boot_time(struct rtc_time *tm)
|
|||
|
||||
/*
|
||||
* Query the OF and get the decr frequency.
|
||||
* This was taken from the pmac time_init() when merging the prep/pmac
|
||||
* time functions.
|
||||
* FIXME: merge this with generic_calibrate_decr
|
||||
*/
|
||||
void __init pmac_calibrate_decr(void)
|
||||
{
|
||||
|
|
|
@ -53,7 +53,7 @@ static int __init proc_ppc64_create(void)
|
|||
if (!root)
|
||||
return 1;
|
||||
|
||||
if (!(systemcfg->platform & PLATFORM_PSERIES))
|
||||
if (!(systemcfg->platform & (PLATFORM_PSERIES | PLATFORM_BPA)))
|
||||
return 0;
|
||||
|
||||
if (!proc_mkdir("rtas", root))
|
||||
|
|
|
@ -1915,9 +1915,9 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, unsigned long
|
|||
prom_send_capabilities();
|
||||
|
||||
/*
|
||||
* On pSeries, copy the CPU hold code
|
||||
* On pSeries and BPA, copy the CPU hold code
|
||||
*/
|
||||
if (RELOC(of_platform) & PLATFORM_PSERIES)
|
||||
if (RELOC(of_platform) & (PLATFORM_PSERIES | PLATFORM_BPA))
|
||||
copy_and_flush(0, KERNELBASE - offset, 0x100, 0);
|
||||
|
||||
/*
|
||||
|
|
|
@ -371,11 +371,11 @@ static ssize_t ppc_rtas_progress_write(struct file *file,
|
|||
/* Lets see if the user passed hexdigits */
|
||||
hex = simple_strtoul(progress_led, NULL, 10);
|
||||
|
||||
ppc_md.progress ((char *)progress_led, hex);
|
||||
rtas_progress ((char *)progress_led, hex);
|
||||
return count;
|
||||
|
||||
/* clear the line */
|
||||
/* ppc_md.progress(" ", 0xffff);*/
|
||||
/* rtas_progress(" ", 0xffff);*/
|
||||
}
|
||||
/* ****************************************************************** */
|
||||
static int ppc_rtas_progress_show(struct seq_file *m, void *v)
|
||||
|
|
|
@ -91,6 +91,123 @@ call_rtas_display_status_delay(unsigned char c)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
rtas_progress(char *s, unsigned short hex)
|
||||
{
|
||||
struct device_node *root;
|
||||
int width, *p;
|
||||
char *os;
|
||||
static int display_character, set_indicator;
|
||||
static int display_width, display_lines, *row_width, form_feed;
|
||||
static DEFINE_SPINLOCK(progress_lock);
|
||||
static int current_line;
|
||||
static int pending_newline = 0; /* did last write end with unprinted newline? */
|
||||
|
||||
if (!rtas.base)
|
||||
return;
|
||||
|
||||
if (display_width == 0) {
|
||||
display_width = 0x10;
|
||||
if ((root = find_path_device("/rtas"))) {
|
||||
if ((p = (unsigned int *)get_property(root,
|
||||
"ibm,display-line-length", NULL)))
|
||||
display_width = *p;
|
||||
if ((p = (unsigned int *)get_property(root,
|
||||
"ibm,form-feed", NULL)))
|
||||
form_feed = *p;
|
||||
if ((p = (unsigned int *)get_property(root,
|
||||
"ibm,display-number-of-lines", NULL)))
|
||||
display_lines = *p;
|
||||
row_width = (unsigned int *)get_property(root,
|
||||
"ibm,display-truncation-length", NULL);
|
||||
}
|
||||
display_character = rtas_token("display-character");
|
||||
set_indicator = rtas_token("set-indicator");
|
||||
}
|
||||
|
||||
if (display_character == RTAS_UNKNOWN_SERVICE) {
|
||||
/* use hex display if available */
|
||||
if (set_indicator != RTAS_UNKNOWN_SERVICE)
|
||||
rtas_call(set_indicator, 3, 1, NULL, 6, 0, hex);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock(&progress_lock);
|
||||
|
||||
/*
|
||||
* Last write ended with newline, but we didn't print it since
|
||||
* it would just clear the bottom line of output. Print it now
|
||||
* instead.
|
||||
*
|
||||
* If no newline is pending and form feed is supported, clear the
|
||||
* display with a form feed; otherwise, print a CR to start output
|
||||
* at the beginning of the line.
|
||||
*/
|
||||
if (pending_newline) {
|
||||
rtas_call(display_character, 1, 1, NULL, '\r');
|
||||
rtas_call(display_character, 1, 1, NULL, '\n');
|
||||
pending_newline = 0;
|
||||
} else {
|
||||
current_line = 0;
|
||||
if (form_feed)
|
||||
rtas_call(display_character, 1, 1, NULL,
|
||||
(char)form_feed);
|
||||
else
|
||||
rtas_call(display_character, 1, 1, NULL, '\r');
|
||||
}
|
||||
|
||||
if (row_width)
|
||||
width = row_width[current_line];
|
||||
else
|
||||
width = display_width;
|
||||
os = s;
|
||||
while (*os) {
|
||||
if (*os == '\n' || *os == '\r') {
|
||||
/* If newline is the last character, save it
|
||||
* until next call to avoid bumping up the
|
||||
* display output.
|
||||
*/
|
||||
if (*os == '\n' && !os[1]) {
|
||||
pending_newline = 1;
|
||||
current_line++;
|
||||
if (current_line > display_lines-1)
|
||||
current_line = display_lines-1;
|
||||
spin_unlock(&progress_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* RTAS wants CR-LF, not just LF */
|
||||
|
||||
if (*os == '\n') {
|
||||
rtas_call(display_character, 1, 1, NULL, '\r');
|
||||
rtas_call(display_character, 1, 1, NULL, '\n');
|
||||
} else {
|
||||
/* CR might be used to re-draw a line, so we'll
|
||||
* leave it alone and not add LF.
|
||||
*/
|
||||
rtas_call(display_character, 1, 1, NULL, *os);
|
||||
}
|
||||
|
||||
if (row_width)
|
||||
width = row_width[current_line];
|
||||
else
|
||||
width = display_width;
|
||||
} else {
|
||||
width--;
|
||||
rtas_call(display_character, 1, 1, NULL, *os);
|
||||
}
|
||||
|
||||
os++;
|
||||
|
||||
/* if we overwrite the screen length */
|
||||
if (width <= 0)
|
||||
while ((*os != 0) && (*os != '\n') && (*os != '\r'))
|
||||
os++;
|
||||
}
|
||||
|
||||
spin_unlock(&progress_lock);
|
||||
}
|
||||
|
||||
int
|
||||
rtas_token(const char *service)
|
||||
{
|
||||
|
@ -425,8 +542,8 @@ rtas_flash_firmware(void)
|
|||
|
||||
printk(KERN_ALERT "FLASH: flash image is %ld bytes\n", image_size);
|
||||
printk(KERN_ALERT "FLASH: performing flash and reboot\n");
|
||||
ppc_md.progress("Flashing \n", 0x0);
|
||||
ppc_md.progress("Please Wait... ", 0x0);
|
||||
rtas_progress("Flashing \n", 0x0);
|
||||
rtas_progress("Please Wait... ", 0x0);
|
||||
printk(KERN_ALERT "FLASH: this will take several minutes. Do not power off!\n");
|
||||
status = rtas_call(update_token, 1, 1, NULL, rtas_block_list);
|
||||
switch (status) { /* should only get "bad" status */
|
||||
|
|
|
@ -0,0 +1,495 @@
|
|||
/*
|
||||
* arch/ppc64/kernel/rtas_pci.c
|
||||
*
|
||||
* Copyright (C) 2001 Dave Engebretsen, IBM Corporation
|
||||
* Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM
|
||||
*
|
||||
* RTAS specific routines for PCI.
|
||||
*
|
||||
* Based on code from pci.c, chrp_pci.c and pSeries_pci.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bootmem.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/rtas.h>
|
||||
|
||||
#include "mpic.h"
|
||||
#include "pci.h"
|
||||
|
||||
/* RTAS tokens */
|
||||
static int read_pci_config;
|
||||
static int write_pci_config;
|
||||
static int ibm_read_pci_config;
|
||||
static int ibm_write_pci_config;
|
||||
|
||||
static int config_access_valid(struct device_node *dn, int where)
|
||||
{
|
||||
if (where < 256)
|
||||
return 1;
|
||||
if (where < 4096 && dn->pci_ext_config_space)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtas_read_config(struct device_node *dn, int where, int size, u32 *val)
|
||||
{
|
||||
int returnval = -1;
|
||||
unsigned long buid, addr;
|
||||
int ret;
|
||||
|
||||
if (!dn)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
if (!config_access_valid(dn, where))
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
addr = ((where & 0xf00) << 20) | (dn->busno << 16) |
|
||||
(dn->devfn << 8) | (where & 0xff);
|
||||
buid = dn->phb->buid;
|
||||
if (buid) {
|
||||
ret = rtas_call(ibm_read_pci_config, 4, 2, &returnval,
|
||||
addr, buid >> 32, buid & 0xffffffff, size);
|
||||
} else {
|
||||
ret = rtas_call(read_pci_config, 2, 2, &returnval, addr, size);
|
||||
}
|
||||
*val = returnval;
|
||||
|
||||
if (ret)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
if (returnval == EEH_IO_ERROR_VALUE(size)
|
||||
&& eeh_dn_check_failure (dn, NULL))
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int rtas_pci_read_config(struct pci_bus *bus,
|
||||
unsigned int devfn,
|
||||
int where, int size, u32 *val)
|
||||
{
|
||||
struct device_node *busdn, *dn;
|
||||
|
||||
if (bus->self)
|
||||
busdn = pci_device_to_OF_node(bus->self);
|
||||
else
|
||||
busdn = bus->sysdata; /* must be a phb */
|
||||
|
||||
/* Search only direct children of the bus */
|
||||
for (dn = busdn->child; dn; dn = dn->sibling)
|
||||
if (dn->devfn == devfn)
|
||||
return rtas_read_config(dn, where, size, val);
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
static int rtas_write_config(struct device_node *dn, int where, int size, u32 val)
|
||||
{
|
||||
unsigned long buid, addr;
|
||||
int ret;
|
||||
|
||||
if (!dn)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
if (!config_access_valid(dn, where))
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
|
||||
addr = ((where & 0xf00) << 20) | (dn->busno << 16) |
|
||||
(dn->devfn << 8) | (where & 0xff);
|
||||
buid = dn->phb->buid;
|
||||
if (buid) {
|
||||
ret = rtas_call(ibm_write_pci_config, 5, 1, NULL, addr, buid >> 32, buid & 0xffffffff, size, (ulong) val);
|
||||
} else {
|
||||
ret = rtas_call(write_pci_config, 3, 1, NULL, addr, size, (ulong)val);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int rtas_pci_write_config(struct pci_bus *bus,
|
||||
unsigned int devfn,
|
||||
int where, int size, u32 val)
|
||||
{
|
||||
struct device_node *busdn, *dn;
|
||||
|
||||
if (bus->self)
|
||||
busdn = pci_device_to_OF_node(bus->self);
|
||||
else
|
||||
busdn = bus->sysdata; /* must be a phb */
|
||||
|
||||
/* Search only direct children of the bus */
|
||||
for (dn = busdn->child; dn; dn = dn->sibling)
|
||||
if (dn->devfn == devfn)
|
||||
return rtas_write_config(dn, where, size, val);
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
struct pci_ops rtas_pci_ops = {
|
||||
rtas_pci_read_config,
|
||||
rtas_pci_write_config
|
||||
};
|
||||
|
||||
int is_python(struct device_node *dev)
|
||||
{
|
||||
char *model = (char *)get_property(dev, "model", NULL);
|
||||
|
||||
if (model && strstr(model, "Python"))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_phb_reg_prop(struct device_node *dev,
|
||||
unsigned int addr_size_words,
|
||||
struct reg_property64 *reg)
|
||||
{
|
||||
unsigned int *ui_ptr = NULL, len;
|
||||
|
||||
/* Found a PHB, now figure out where his registers are mapped. */
|
||||
ui_ptr = (unsigned int *)get_property(dev, "reg", &len);
|
||||
if (ui_ptr == NULL)
|
||||
return 1;
|
||||
|
||||
if (addr_size_words == 1) {
|
||||
reg->address = ((struct reg_property32 *)ui_ptr)->address;
|
||||
reg->size = ((struct reg_property32 *)ui_ptr)->size;
|
||||
} else {
|
||||
*reg = *((struct reg_property64 *)ui_ptr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void python_countermeasures(struct device_node *dev,
|
||||
unsigned int addr_size_words)
|
||||
{
|
||||
struct reg_property64 reg_struct;
|
||||
void __iomem *chip_regs;
|
||||
volatile u32 val;
|
||||
|
||||
if (get_phb_reg_prop(dev, addr_size_words, ®_struct))
|
||||
return;
|
||||
|
||||
/* Python's register file is 1 MB in size. */
|
||||
chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), 0x100000);
|
||||
|
||||
/*
|
||||
* Firmware doesn't always clear this bit which is critical
|
||||
* for good performance - Anton
|
||||
*/
|
||||
|
||||
#define PRG_CL_RESET_VALID 0x00010000
|
||||
|
||||
val = in_be32(chip_regs + 0xf6030);
|
||||
if (val & PRG_CL_RESET_VALID) {
|
||||
printk(KERN_INFO "Python workaround: ");
|
||||
val &= ~PRG_CL_RESET_VALID;
|
||||
out_be32(chip_regs + 0xf6030, val);
|
||||
/*
|
||||
* We must read it back for changes to
|
||||
* take effect
|
||||
*/
|
||||
val = in_be32(chip_regs + 0xf6030);
|
||||
printk("reg0: %x\n", val);
|
||||
}
|
||||
|
||||
iounmap(chip_regs);
|
||||
}
|
||||
|
||||
void __init init_pci_config_tokens (void)
|
||||
{
|
||||
read_pci_config = rtas_token("read-pci-config");
|
||||
write_pci_config = rtas_token("write-pci-config");
|
||||
ibm_read_pci_config = rtas_token("ibm,read-pci-config");
|
||||
ibm_write_pci_config = rtas_token("ibm,write-pci-config");
|
||||
}
|
||||
|
||||
unsigned long __devinit get_phb_buid (struct device_node *phb)
|
||||
{
|
||||
int addr_cells;
|
||||
unsigned int *buid_vals;
|
||||
unsigned int len;
|
||||
unsigned long buid;
|
||||
|
||||
if (ibm_read_pci_config == -1) return 0;
|
||||
|
||||
/* PHB's will always be children of the root node,
|
||||
* or so it is promised by the current firmware. */
|
||||
if (phb->parent == NULL)
|
||||
return 0;
|
||||
if (phb->parent->parent)
|
||||
return 0;
|
||||
|
||||
buid_vals = (unsigned int *) get_property(phb, "reg", &len);
|
||||
if (buid_vals == NULL)
|
||||
return 0;
|
||||
|
||||
addr_cells = prom_n_addr_cells(phb);
|
||||
if (addr_cells == 1) {
|
||||
buid = (unsigned long) buid_vals[0];
|
||||
} else {
|
||||
buid = (((unsigned long)buid_vals[0]) << 32UL) |
|
||||
(((unsigned long)buid_vals[1]) & 0xffffffff);
|
||||
}
|
||||
return buid;
|
||||
}
|
||||
|
||||
static int phb_set_bus_ranges(struct device_node *dev,
|
||||
struct pci_controller *phb)
|
||||
{
|
||||
int *bus_range;
|
||||
unsigned int len;
|
||||
|
||||
bus_range = (int *) get_property(dev, "bus-range", &len);
|
||||
if (bus_range == NULL || len < 2 * sizeof(int)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
phb->first_busno = bus_range[0];
|
||||
phb->last_busno = bus_range[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit setup_phb(struct device_node *dev,
|
||||
struct pci_controller *phb,
|
||||
unsigned int addr_size_words)
|
||||
{
|
||||
pci_setup_pci_controller(phb);
|
||||
|
||||
if (is_python(dev))
|
||||
python_countermeasures(dev, addr_size_words);
|
||||
|
||||
if (phb_set_bus_ranges(dev, phb))
|
||||
return 1;
|
||||
|
||||
phb->arch_data = dev;
|
||||
phb->ops = &rtas_pci_ops;
|
||||
phb->buid = get_phb_buid(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devinit add_linux_pci_domain(struct device_node *dev,
|
||||
struct pci_controller *phb,
|
||||
struct property *of_prop)
|
||||
{
|
||||
memset(of_prop, 0, sizeof(struct property));
|
||||
of_prop->name = "linux,pci-domain";
|
||||
of_prop->length = sizeof(phb->global_number);
|
||||
of_prop->value = (unsigned char *)&of_prop[1];
|
||||
memcpy(of_prop->value, &phb->global_number, sizeof(phb->global_number));
|
||||
prom_add_property(dev, of_prop);
|
||||
}
|
||||
|
||||
static struct pci_controller * __init alloc_phb(struct device_node *dev,
|
||||
unsigned int addr_size_words)
|
||||
{
|
||||
struct pci_controller *phb;
|
||||
struct property *of_prop;
|
||||
|
||||
phb = alloc_bootmem(sizeof(struct pci_controller));
|
||||
if (phb == NULL)
|
||||
return NULL;
|
||||
|
||||
of_prop = alloc_bootmem(sizeof(struct property) +
|
||||
sizeof(phb->global_number));
|
||||
if (!of_prop)
|
||||
return NULL;
|
||||
|
||||
if (setup_phb(dev, phb, addr_size_words))
|
||||
return NULL;
|
||||
|
||||
add_linux_pci_domain(dev, phb, of_prop);
|
||||
|
||||
return phb;
|
||||
}
|
||||
|
||||
static struct pci_controller * __devinit alloc_phb_dynamic(struct device_node *dev, unsigned int addr_size_words)
|
||||
{
|
||||
struct pci_controller *phb;
|
||||
|
||||
phb = (struct pci_controller *)kmalloc(sizeof(struct pci_controller),
|
||||
GFP_KERNEL);
|
||||
if (phb == NULL)
|
||||
return NULL;
|
||||
|
||||
if (setup_phb(dev, phb, addr_size_words))
|
||||
return NULL;
|
||||
|
||||
phb->is_dynamic = 1;
|
||||
|
||||
/* TODO: linux,pci-domain? */
|
||||
|
||||
return phb;
|
||||
}
|
||||
|
||||
unsigned long __init find_and_init_phbs(void)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct pci_controller *phb;
|
||||
unsigned int root_size_cells = 0;
|
||||
unsigned int index;
|
||||
unsigned int *opprop = NULL;
|
||||
struct device_node *root = of_find_node_by_path("/");
|
||||
|
||||
if (ppc64_interrupt_controller == IC_OPEN_PIC) {
|
||||
opprop = (unsigned int *)get_property(root,
|
||||
"platform-open-pic", NULL);
|
||||
}
|
||||
|
||||
root_size_cells = prom_n_size_cells(root);
|
||||
|
||||
index = 0;
|
||||
|
||||
for (node = of_get_next_child(root, NULL);
|
||||
node != NULL;
|
||||
node = of_get_next_child(root, node)) {
|
||||
if (node->type == NULL || strcmp(node->type, "pci") != 0)
|
||||
continue;
|
||||
|
||||
phb = alloc_phb(node, root_size_cells);
|
||||
if (!phb)
|
||||
continue;
|
||||
|
||||
pci_process_bridge_OF_ranges(phb, node);
|
||||
pci_setup_phb_io(phb, index == 0);
|
||||
#ifdef CONFIG_PPC_PSERIES
|
||||
if (ppc64_interrupt_controller == IC_OPEN_PIC && pSeries_mpic) {
|
||||
int addr = root_size_cells * (index + 2) - 1;
|
||||
mpic_assign_isu(pSeries_mpic, index, opprop[addr]);
|
||||
}
|
||||
#endif
|
||||
index++;
|
||||
}
|
||||
|
||||
of_node_put(root);
|
||||
pci_devs_phb_init();
|
||||
|
||||
/*
|
||||
* pci_probe_only and pci_assign_all_buses can be set via properties
|
||||
* in chosen.
|
||||
*/
|
||||
if (of_chosen) {
|
||||
int *prop;
|
||||
|
||||
prop = (int *)get_property(of_chosen, "linux,pci-probe-only",
|
||||
NULL);
|
||||
if (prop)
|
||||
pci_probe_only = *prop;
|
||||
|
||||
prop = (int *)get_property(of_chosen,
|
||||
"linux,pci-assign-all-buses", NULL);
|
||||
if (prop)
|
||||
pci_assign_all_buses = *prop;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn)
|
||||
{
|
||||
struct device_node *root = of_find_node_by_path("/");
|
||||
unsigned int root_size_cells = 0;
|
||||
struct pci_controller *phb;
|
||||
struct pci_bus *bus;
|
||||
int primary;
|
||||
|
||||
root_size_cells = prom_n_size_cells(root);
|
||||
|
||||
primary = list_empty(&hose_list);
|
||||
phb = alloc_phb_dynamic(dn, root_size_cells);
|
||||
if (!phb)
|
||||
return NULL;
|
||||
|
||||
pci_process_bridge_OF_ranges(phb, dn);
|
||||
|
||||
pci_setup_phb_io_dynamic(phb, primary);
|
||||
of_node_put(root);
|
||||
|
||||
pci_devs_phb_init_dynamic(phb);
|
||||
phb->last_busno = 0xff;
|
||||
bus = pci_scan_bus(phb->first_busno, phb->ops, phb->arch_data);
|
||||
phb->bus = bus;
|
||||
phb->last_busno = bus->subordinate;
|
||||
|
||||
return phb;
|
||||
}
|
||||
EXPORT_SYMBOL(init_phb_dynamic);
|
||||
|
||||
/* RPA-specific bits for removing PHBs */
|
||||
int pcibios_remove_root_bus(struct pci_controller *phb)
|
||||
{
|
||||
struct pci_bus *b = phb->bus;
|
||||
struct resource *res;
|
||||
int rc, i;
|
||||
|
||||
res = b->resource[0];
|
||||
if (!res->flags) {
|
||||
printk(KERN_ERR "%s: no IO resource for PHB %s\n", __FUNCTION__,
|
||||
b->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
rc = unmap_bus_range(b);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "%s: failed to unmap IO on bus %s\n",
|
||||
__FUNCTION__, b->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (release_resource(res)) {
|
||||
printk(KERN_ERR "%s: failed to release IO on bus %s\n",
|
||||
__FUNCTION__, b->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 1; i < 3; ++i) {
|
||||
res = b->resource[i];
|
||||
if (!res->flags && i == 0) {
|
||||
printk(KERN_ERR "%s: no MEM resource for PHB %s\n",
|
||||
__FUNCTION__, b->name);
|
||||
return 1;
|
||||
}
|
||||
if (res->flags && release_resource(res)) {
|
||||
printk(KERN_ERR
|
||||
"%s: failed to release IO %d on bus %s\n",
|
||||
__FUNCTION__, i, b->name);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
list_del(&phb->list_node);
|
||||
if (phb->is_dynamic)
|
||||
kfree(phb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(pcibios_remove_root_bus);
|
|
@ -301,7 +301,7 @@ void iSeries_get_boot_time(struct rtc_time *tm)
|
|||
#ifdef CONFIG_PPC_RTAS
|
||||
#define MAX_RTC_WAIT 5000 /* 5 sec */
|
||||
#define RTAS_CLOCK_BUSY (-2)
|
||||
void pSeries_get_boot_time(struct rtc_time *rtc_tm)
|
||||
void rtas_get_boot_time(struct rtc_time *rtc_tm)
|
||||
{
|
||||
int ret[8];
|
||||
int error, wait_time;
|
||||
|
@ -336,7 +336,7 @@ void pSeries_get_boot_time(struct rtc_time *rtc_tm)
|
|||
* and if a delay is needed to read the clock. In this case we just
|
||||
* silently return without updating rtc_tm.
|
||||
*/
|
||||
void pSeries_get_rtc_time(struct rtc_time *rtc_tm)
|
||||
void rtas_get_rtc_time(struct rtc_time *rtc_tm)
|
||||
{
|
||||
int ret[8];
|
||||
int error, wait_time;
|
||||
|
@ -371,7 +371,7 @@ void pSeries_get_rtc_time(struct rtc_time *rtc_tm)
|
|||
rtc_tm->tm_year = ret[0] - 1900;
|
||||
}
|
||||
|
||||
int pSeries_set_rtc_time(struct rtc_time *tm)
|
||||
int rtas_set_rtc_time(struct rtc_time *tm)
|
||||
{
|
||||
int error, wait_time;
|
||||
unsigned long max_wait_tb;
|
||||
|
|
|
@ -344,6 +344,7 @@ static void __init setup_cpu_maps(void)
|
|||
extern struct machdep_calls pSeries_md;
|
||||
extern struct machdep_calls pmac_md;
|
||||
extern struct machdep_calls maple_md;
|
||||
extern struct machdep_calls bpa_md;
|
||||
|
||||
/* Ultimately, stuff them in an elf section like initcalls... */
|
||||
static struct machdep_calls __initdata *machines[] = {
|
||||
|
@ -356,6 +357,9 @@ static struct machdep_calls __initdata *machines[] = {
|
|||
#ifdef CONFIG_PPC_MAPLE
|
||||
&maple_md,
|
||||
#endif /* CONFIG_PPC_MAPLE */
|
||||
#ifdef CONFIG_PPC_BPA
|
||||
&bpa_md,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -679,6 +683,12 @@ void machine_restart(char *cmd)
|
|||
if (ppc_md.nvram_sync)
|
||||
ppc_md.nvram_sync();
|
||||
ppc_md.restart(cmd);
|
||||
#ifdef CONFIG_SMP
|
||||
smp_send_stop();
|
||||
#endif
|
||||
printk(KERN_EMERG "System Halted, OK to turn off power\n");
|
||||
local_irq_disable();
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(machine_restart);
|
||||
|
@ -688,6 +698,12 @@ void machine_power_off(void)
|
|||
if (ppc_md.nvram_sync)
|
||||
ppc_md.nvram_sync();
|
||||
ppc_md.power_off();
|
||||
#ifdef CONFIG_SMP
|
||||
smp_send_stop();
|
||||
#endif
|
||||
printk(KERN_EMERG "System Halted, OK to turn off power\n");
|
||||
local_irq_disable();
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(machine_power_off);
|
||||
|
@ -697,13 +713,16 @@ void machine_halt(void)
|
|||
if (ppc_md.nvram_sync)
|
||||
ppc_md.nvram_sync();
|
||||
ppc_md.halt();
|
||||
#ifdef CONFIG_SMP
|
||||
smp_send_stop();
|
||||
#endif
|
||||
printk(KERN_EMERG "System Halted, OK to turn off power\n");
|
||||
local_irq_disable();
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(machine_halt);
|
||||
|
||||
unsigned long ppc_proc_freq;
|
||||
unsigned long ppc_tb_freq;
|
||||
|
||||
static int ppc64_panic_event(struct notifier_block *this,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
|
@ -1080,11 +1099,11 @@ void __init setup_arch(char **cmdline_p)
|
|||
static void ppc64_do_msg(unsigned int src, const char *msg)
|
||||
{
|
||||
if (ppc_md.progress) {
|
||||
char buf[32];
|
||||
char buf[128];
|
||||
|
||||
sprintf(buf, "%08x \n", src);
|
||||
sprintf(buf, "%08X\n", src);
|
||||
ppc_md.progress(buf, 0);
|
||||
sprintf(buf, "%-16s", msg);
|
||||
snprintf(buf, 128, "%s", msg);
|
||||
ppc_md.progress(buf, 0);
|
||||
}
|
||||
}
|
||||
|
@ -1118,7 +1137,7 @@ void ppc64_dump_msg(unsigned int src, const char *msg)
|
|||
}
|
||||
|
||||
/* This should only be called on processor 0 during calibrate decr */
|
||||
void setup_default_decr(void)
|
||||
void __init setup_default_decr(void)
|
||||
{
|
||||
struct paca_struct *lpaca = get_paca();
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ void smp_call_function_interrupt(void);
|
|||
|
||||
int smt_enabled_at_boot = 1;
|
||||
|
||||
#ifdef CONFIG_PPC_MULTIPLATFORM
|
||||
#ifdef CONFIG_MPIC
|
||||
void smp_mpic_message_pass(int target, int msg)
|
||||
{
|
||||
/* make sure we're sending something that translates to an IPI */
|
||||
|
@ -128,7 +128,7 @@ void __devinit smp_generic_kick_cpu(int nr)
|
|||
smp_mb();
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PPC_MULTIPLATFORM */
|
||||
#endif /* CONFIG_MPIC */
|
||||
|
||||
static void __init smp_space_timers(unsigned int max_cpus)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* External Interrupt Controller on Spider South Bridge
|
||||
*
|
||||
* (C) Copyright IBM Deutschland Entwicklung GmbH 2005
|
||||
*
|
||||
* Author: Arnd Bergmann <arndb@de.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "bpa_iic.h"
|
||||
|
||||
/* register layout taken from Spider spec, table 7.4-4 */
|
||||
enum {
|
||||
TIR_DEN = 0x004, /* Detection Enable Register */
|
||||
TIR_MSK = 0x084, /* Mask Level Register */
|
||||
TIR_EDC = 0x0c0, /* Edge Detection Clear Register */
|
||||
TIR_PNDA = 0x100, /* Pending Register A */
|
||||
TIR_PNDB = 0x104, /* Pending Register B */
|
||||
TIR_CS = 0x144, /* Current Status Register */
|
||||
TIR_LCSA = 0x150, /* Level Current Status Register A */
|
||||
TIR_LCSB = 0x154, /* Level Current Status Register B */
|
||||
TIR_LCSC = 0x158, /* Level Current Status Register C */
|
||||
TIR_LCSD = 0x15c, /* Level Current Status Register D */
|
||||
TIR_CFGA = 0x200, /* Setting Register A0 */
|
||||
TIR_CFGB = 0x204, /* Setting Register B0 */
|
||||
/* 0x208 ... 0x3ff Setting Register An/Bn */
|
||||
TIR_PPNDA = 0x400, /* Packet Pending Register A */
|
||||
TIR_PPNDB = 0x404, /* Packet Pending Register B */
|
||||
TIR_PIERA = 0x408, /* Packet Output Error Register A */
|
||||
TIR_PIERB = 0x40c, /* Packet Output Error Register B */
|
||||
TIR_PIEN = 0x444, /* Packet Output Enable Register */
|
||||
TIR_PIPND = 0x454, /* Packet Output Pending Register */
|
||||
TIRDID = 0x484, /* Spider Device ID Register */
|
||||
REISTIM = 0x500, /* Reissue Command Timeout Time Setting */
|
||||
REISTIMEN = 0x504, /* Reissue Command Timeout Setting */
|
||||
REISWAITEN = 0x508, /* Reissue Wait Control*/
|
||||
};
|
||||
|
||||
static void __iomem *spider_pics[4];
|
||||
|
||||
static void __iomem *spider_get_pic(int irq)
|
||||
{
|
||||
int node = irq / IIC_NODE_STRIDE;
|
||||
irq %= IIC_NODE_STRIDE;
|
||||
|
||||
if (irq >= IIC_EXT_OFFSET &&
|
||||
irq < IIC_EXT_OFFSET + IIC_NUM_EXT &&
|
||||
spider_pics)
|
||||
return spider_pics[node];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int spider_get_nr(unsigned int irq)
|
||||
{
|
||||
return (irq % IIC_NODE_STRIDE) - IIC_EXT_OFFSET;
|
||||
}
|
||||
|
||||
static void __iomem *spider_get_irq_config(int irq)
|
||||
{
|
||||
void __iomem *pic;
|
||||
pic = spider_get_pic(irq);
|
||||
return pic + TIR_CFGA + 8 * spider_get_nr(irq);
|
||||
}
|
||||
|
||||
static void spider_enable_irq(unsigned int irq)
|
||||
{
|
||||
void __iomem *cfg = spider_get_irq_config(irq);
|
||||
irq = spider_get_nr(irq);
|
||||
|
||||
out_be32(cfg, in_be32(cfg) | 0x3107000eu);
|
||||
out_be32(cfg + 4, in_be32(cfg + 4) | 0x00020000u | irq);
|
||||
}
|
||||
|
||||
static void spider_disable_irq(unsigned int irq)
|
||||
{
|
||||
void __iomem *cfg = spider_get_irq_config(irq);
|
||||
irq = spider_get_nr(irq);
|
||||
|
||||
out_be32(cfg, in_be32(cfg) & ~0x30000000u);
|
||||
}
|
||||
|
||||
static unsigned int spider_startup_irq(unsigned int irq)
|
||||
{
|
||||
spider_enable_irq(irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spider_shutdown_irq(unsigned int irq)
|
||||
{
|
||||
spider_disable_irq(irq);
|
||||
}
|
||||
|
||||
static void spider_end_irq(unsigned int irq)
|
||||
{
|
||||
spider_enable_irq(irq);
|
||||
}
|
||||
|
||||
static void spider_ack_irq(unsigned int irq)
|
||||
{
|
||||
spider_disable_irq(irq);
|
||||
iic_local_enable();
|
||||
}
|
||||
|
||||
static struct hw_interrupt_type spider_pic = {
|
||||
.typename = " SPIDER ",
|
||||
.startup = spider_startup_irq,
|
||||
.shutdown = spider_shutdown_irq,
|
||||
.enable = spider_enable_irq,
|
||||
.disable = spider_disable_irq,
|
||||
.ack = spider_ack_irq,
|
||||
.end = spider_end_irq,
|
||||
};
|
||||
|
||||
|
||||
int spider_get_irq(unsigned long int_pending)
|
||||
{
|
||||
void __iomem *regs = spider_get_pic(int_pending);
|
||||
unsigned long cs;
|
||||
int irq;
|
||||
|
||||
cs = in_be32(regs + TIR_CS);
|
||||
|
||||
irq = cs >> 24;
|
||||
if (irq != 63)
|
||||
return irq;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void spider_init_IRQ(void)
|
||||
{
|
||||
int node;
|
||||
struct device_node *dn;
|
||||
unsigned int *property;
|
||||
long spiderpic;
|
||||
int n;
|
||||
|
||||
/* FIXME: detect multiple PICs as soon as the device tree has them */
|
||||
for (node = 0; node < 1; node++) {
|
||||
dn = of_find_node_by_path("/");
|
||||
n = prom_n_addr_cells(dn);
|
||||
property = (unsigned int *) get_property(dn,
|
||||
"platform-spider-pic", NULL);
|
||||
|
||||
if (!property)
|
||||
continue;
|
||||
for (spiderpic = 0; n > 0; --n)
|
||||
spiderpic = (spiderpic << 32) + *property++;
|
||||
printk(KERN_DEBUG "SPIDER addr: %lx\n", spiderpic);
|
||||
spider_pics[node] = __ioremap(spiderpic, 0x800, _PAGE_NO_CACHE);
|
||||
for (n = 0; n < IIC_NUM_EXT; n++) {
|
||||
int irq = n + IIC_EXT_OFFSET + node * IIC_NODE_STRIDE;
|
||||
get_irq_desc(irq)->handler = &spider_pic;
|
||||
|
||||
/* do not mask any interrupts because of level */
|
||||
out_be32(spider_pics[node] + TIR_MSK, 0x0);
|
||||
|
||||
/* disable edge detection clear */
|
||||
/* out_be32(spider_pics[node] + TIR_EDC, 0x0); */
|
||||
|
||||
/* enable interrupt packets to be output */
|
||||
out_be32(spider_pics[node] + TIR_PIEN,
|
||||
in_be32(spider_pics[node] + TIR_PIEN) | 0x1);
|
||||
|
||||
/* Enable the interrupt detection enable bit. Do this last! */
|
||||
out_be32(spider_pics[node] + TIR_DEN,
|
||||
in_be32(spider_pics[node] +TIR_DEN) | 0x1);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -107,6 +107,9 @@ void ppc_adjtimex(void);
|
|||
|
||||
static unsigned adjusting_time = 0;
|
||||
|
||||
unsigned long ppc_proc_freq;
|
||||
unsigned long ppc_tb_freq;
|
||||
|
||||
static __inline__ void timer_check_rtc(void)
|
||||
{
|
||||
/*
|
||||
|
@ -472,6 +475,66 @@ int do_settimeofday(struct timespec *tv)
|
|||
|
||||
EXPORT_SYMBOL(do_settimeofday);
|
||||
|
||||
#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_MAPLE) || defined(CONFIG_PPC_BPA)
|
||||
void __init generic_calibrate_decr(void)
|
||||
{
|
||||
struct device_node *cpu;
|
||||
struct div_result divres;
|
||||
unsigned int *fp;
|
||||
int node_found;
|
||||
|
||||
/*
|
||||
* The cpu node should have a timebase-frequency property
|
||||
* to tell us the rate at which the decrementer counts.
|
||||
*/
|
||||
cpu = of_find_node_by_type(NULL, "cpu");
|
||||
|
||||
ppc_tb_freq = DEFAULT_TB_FREQ; /* hardcoded default */
|
||||
node_found = 0;
|
||||
if (cpu != 0) {
|
||||
fp = (unsigned int *)get_property(cpu, "timebase-frequency",
|
||||
NULL);
|
||||
if (fp != 0) {
|
||||
node_found = 1;
|
||||
ppc_tb_freq = *fp;
|
||||
}
|
||||
}
|
||||
if (!node_found)
|
||||
printk(KERN_ERR "WARNING: Estimating decrementer frequency "
|
||||
"(not found)\n");
|
||||
|
||||
ppc_proc_freq = DEFAULT_PROC_FREQ;
|
||||
node_found = 0;
|
||||
if (cpu != 0) {
|
||||
fp = (unsigned int *)get_property(cpu, "clock-frequency",
|
||||
NULL);
|
||||
if (fp != 0) {
|
||||
node_found = 1;
|
||||
ppc_proc_freq = *fp;
|
||||
}
|
||||
}
|
||||
if (!node_found)
|
||||
printk(KERN_ERR "WARNING: Estimating processor frequency "
|
||||
"(not found)\n");
|
||||
|
||||
of_node_put(cpu);
|
||||
|
||||
printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n",
|
||||
ppc_tb_freq/1000000, ppc_tb_freq%1000000);
|
||||
printk(KERN_INFO "time_init: processor frequency = %lu.%.6lu MHz\n",
|
||||
ppc_proc_freq/1000000, ppc_proc_freq%1000000);
|
||||
|
||||
tb_ticks_per_jiffy = ppc_tb_freq / HZ;
|
||||
tb_ticks_per_sec = tb_ticks_per_jiffy * HZ;
|
||||
tb_ticks_per_usec = ppc_tb_freq / 1000000;
|
||||
tb_to_us = mulhwu_scale_factor(ppc_tb_freq, 1000000);
|
||||
div128_by_32(1024*1024, 0, tb_ticks_per_sec, &divres);
|
||||
tb_to_xs = divres.result_low;
|
||||
|
||||
setup_default_decr();
|
||||
}
|
||||
#endif
|
||||
|
||||
void __init time_init(void)
|
||||
{
|
||||
/* This function is only called on the boot processor */
|
||||
|
|
|
@ -126,6 +126,10 @@ int die(const char *str, struct pt_regs *regs, long err)
|
|||
printk("POWERMAC ");
|
||||
nl = 1;
|
||||
break;
|
||||
case PLATFORM_BPA:
|
||||
printk("BPA ");
|
||||
nl = 1;
|
||||
break;
|
||||
}
|
||||
if (nl)
|
||||
printk("\n");
|
||||
|
|
|
@ -414,6 +414,16 @@ config WATCHDOG_RIO
|
|||
machines. The watchdog timeout period is normally one minute but
|
||||
can be changed with a boot-time parameter.
|
||||
|
||||
# ppc64 RTAS watchdog
|
||||
config WATCHDOG_RTAS
|
||||
tristate "RTAS watchdog"
|
||||
depends on WATCHDOG && PPC_RTAS
|
||||
help
|
||||
This driver adds watchdog support for the RTAS watchdog.
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called wdrtas.
|
||||
|
||||
#
|
||||
# ISA-based Watchdog Cards
|
||||
#
|
||||
|
|
|
@ -33,6 +33,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
|
|||
obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
|
||||
obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o
|
||||
obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o
|
||||
obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o
|
||||
|
||||
# Only one watchdog can succeed. We probe the hardware watchdog
|
||||
# drivers first, then the softdog driver. This means if your hardware
|
||||
|
|
|
@ -0,0 +1,696 @@
|
|||
/*
|
||||
* FIXME: add wdrtas_get_status and wdrtas_get_boot_status as soon as
|
||||
* RTAS calls are available
|
||||
*/
|
||||
|
||||
/*
|
||||
* RTAS watchdog driver
|
||||
*
|
||||
* (C) Copyright IBM Corp. 2005
|
||||
* device driver to exploit watchdog RTAS functions
|
||||
*
|
||||
* Authors : Utz Bacher <utz.bacher@de.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#include <asm/rtas.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#define WDRTAS_MAGIC_CHAR 42
|
||||
#define WDRTAS_SUPPORTED_MASK (WDIOF_SETTIMEOUT | \
|
||||
WDIOF_MAGICCLOSE)
|
||||
|
||||
MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
|
||||
MODULE_DESCRIPTION("RTAS watchdog driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
|
||||
MODULE_ALIAS_MISCDEV(TEMP_MINOR);
|
||||
|
||||
#ifdef CONFIG_WATCHDOG_NOWAYOUT
|
||||
static int wdrtas_nowayout = 1;
|
||||
#else
|
||||
static int wdrtas_nowayout = 0;
|
||||
#endif
|
||||
|
||||
static atomic_t wdrtas_miscdev_open = ATOMIC_INIT(0);
|
||||
static char wdrtas_expect_close = 0;
|
||||
|
||||
static int wdrtas_interval;
|
||||
|
||||
#define WDRTAS_THERMAL_SENSOR 3
|
||||
static int wdrtas_token_get_sensor_state;
|
||||
#define WDRTAS_SURVEILLANCE_IND 9000
|
||||
static int wdrtas_token_set_indicator;
|
||||
#define WDRTAS_SP_SPI 28
|
||||
static int wdrtas_token_get_sp;
|
||||
static int wdrtas_token_event_scan;
|
||||
|
||||
#define WDRTAS_DEFAULT_INTERVAL 300
|
||||
|
||||
#define WDRTAS_LOGBUFFER_LEN 128
|
||||
static char wdrtas_logbuffer[WDRTAS_LOGBUFFER_LEN];
|
||||
|
||||
|
||||
/*** watchdog access functions */
|
||||
|
||||
/**
|
||||
* wdrtas_set_interval - sets the watchdog interval
|
||||
* @interval: new interval
|
||||
*
|
||||
* returns 0 on success, <0 on failures
|
||||
*
|
||||
* wdrtas_set_interval sets the watchdog keepalive interval by calling the
|
||||
* RTAS function set-indicator (surveillance). The unit of interval is
|
||||
* seconds.
|
||||
*/
|
||||
static int
|
||||
wdrtas_set_interval(int interval)
|
||||
{
|
||||
long result;
|
||||
static int print_msg = 10;
|
||||
|
||||
/* rtas uses minutes */
|
||||
interval = (interval + 59) / 60;
|
||||
|
||||
result = rtas_call(wdrtas_token_set_indicator, 3, 1, NULL,
|
||||
WDRTAS_SURVEILLANCE_IND, 0, interval);
|
||||
if ( (result < 0) && (print_msg) ) {
|
||||
printk(KERN_ERR "wdrtas: setting the watchdog to %i "
|
||||
"timeout failed: %li\n", interval, result);
|
||||
print_msg--;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_get_interval - returns the current watchdog interval
|
||||
* @fallback_value: value (in seconds) to use, if the RTAS call fails
|
||||
*
|
||||
* returns the interval
|
||||
*
|
||||
* wdrtas_get_interval returns the current watchdog keepalive interval
|
||||
* as reported by the RTAS function ibm,get-system-parameter. The unit
|
||||
* of the return value is seconds.
|
||||
*/
|
||||
static int
|
||||
wdrtas_get_interval(int fallback_value)
|
||||
{
|
||||
long result;
|
||||
char value[4];
|
||||
|
||||
result = rtas_call(wdrtas_token_get_sp, 3, 1, NULL,
|
||||
WDRTAS_SP_SPI, (void *)__pa(&value), 4);
|
||||
if ( (value[0] != 0) || (value[1] != 2) || (value[3] != 0) ||
|
||||
(result < 0) ) {
|
||||
printk(KERN_WARNING "wdrtas: could not get sp_spi watchdog "
|
||||
"timeout (%li). Continuing\n", result);
|
||||
return fallback_value;
|
||||
}
|
||||
|
||||
/* rtas uses minutes */
|
||||
return ((int)value[2]) * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_timer_start - starts watchdog
|
||||
*
|
||||
* wdrtas_timer_start starts the watchdog by calling the RTAS function
|
||||
* set-interval (surveillance)
|
||||
*/
|
||||
static void
|
||||
wdrtas_timer_start(void)
|
||||
{
|
||||
wdrtas_set_interval(wdrtas_interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_timer_stop - stops watchdog
|
||||
*
|
||||
* wdrtas_timer_stop stops the watchdog timer by calling the RTAS function
|
||||
* set-interval (surveillance)
|
||||
*/
|
||||
static void
|
||||
wdrtas_timer_stop(void)
|
||||
{
|
||||
wdrtas_set_interval(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_log_scanned_event - logs an event we received during keepalive
|
||||
*
|
||||
* wdrtas_log_scanned_event prints a message to the log buffer dumping
|
||||
* the results of the last event-scan call
|
||||
*/
|
||||
static void
|
||||
wdrtas_log_scanned_event(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < WDRTAS_LOGBUFFER_LEN; i += 16)
|
||||
printk(KERN_INFO "wdrtas: dumping event (line %i/%i), data = "
|
||||
"%02x %02x %02x %02x %02x %02x %02x %02x "
|
||||
"%02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
(i / 16) + 1, (WDRTAS_LOGBUFFER_LEN / 16),
|
||||
wdrtas_logbuffer[i + 0], wdrtas_logbuffer[i + 1],
|
||||
wdrtas_logbuffer[i + 2], wdrtas_logbuffer[i + 3],
|
||||
wdrtas_logbuffer[i + 4], wdrtas_logbuffer[i + 5],
|
||||
wdrtas_logbuffer[i + 6], wdrtas_logbuffer[i + 7],
|
||||
wdrtas_logbuffer[i + 8], wdrtas_logbuffer[i + 9],
|
||||
wdrtas_logbuffer[i + 10], wdrtas_logbuffer[i + 11],
|
||||
wdrtas_logbuffer[i + 12], wdrtas_logbuffer[i + 13],
|
||||
wdrtas_logbuffer[i + 14], wdrtas_logbuffer[i + 15]);
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_timer_keepalive - resets watchdog timer to keep system alive
|
||||
*
|
||||
* wdrtas_timer_keepalive restarts the watchdog timer by calling the
|
||||
* RTAS function event-scan and repeats these calls as long as there are
|
||||
* events available. All events will be dumped.
|
||||
*/
|
||||
static void
|
||||
wdrtas_timer_keepalive(void)
|
||||
{
|
||||
long result;
|
||||
|
||||
do {
|
||||
result = rtas_call(wdrtas_token_event_scan, 4, 1, NULL,
|
||||
RTAS_EVENT_SCAN_ALL_EVENTS, 0,
|
||||
(void *)__pa(wdrtas_logbuffer),
|
||||
WDRTAS_LOGBUFFER_LEN);
|
||||
if (result < 0)
|
||||
printk(KERN_ERR "wdrtas: event-scan failed: %li\n",
|
||||
result);
|
||||
if (result == 0)
|
||||
wdrtas_log_scanned_event();
|
||||
} while (result == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_get_temperature - returns current temperature
|
||||
*
|
||||
* returns temperature or <0 on failures
|
||||
*
|
||||
* wdrtas_get_temperature returns the current temperature in Fahrenheit. It
|
||||
* uses the RTAS call get-sensor-state, token 3 to do so
|
||||
*/
|
||||
static int
|
||||
wdrtas_get_temperature(void)
|
||||
{
|
||||
long result;
|
||||
int temperature = 0;
|
||||
|
||||
result = rtas_call(wdrtas_token_get_sensor_state, 2, 2,
|
||||
(void *)__pa(&temperature),
|
||||
WDRTAS_THERMAL_SENSOR, 0);
|
||||
|
||||
if (result < 0)
|
||||
printk(KERN_WARNING "wdrtas: reading the thermal sensor "
|
||||
"faild: %li\n", result);
|
||||
else
|
||||
temperature = ((temperature * 9) / 5) + 32; /* fahrenheit */
|
||||
|
||||
return temperature;
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_get_status - returns the status of the watchdog
|
||||
*
|
||||
* returns a bitmask of defines WDIOF_... as defined in
|
||||
* include/linux/watchdog.h
|
||||
*/
|
||||
static int
|
||||
wdrtas_get_status(void)
|
||||
{
|
||||
return 0; /* TODO */
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_get_boot_status - returns the reason for the last boot
|
||||
*
|
||||
* returns a bitmask of defines WDIOF_... as defined in
|
||||
* include/linux/watchdog.h, indicating why the watchdog rebooted the system
|
||||
*/
|
||||
static int
|
||||
wdrtas_get_boot_status(void)
|
||||
{
|
||||
return 0; /* TODO */
|
||||
}
|
||||
|
||||
/*** watchdog API and operations stuff */
|
||||
|
||||
/* wdrtas_write - called when watchdog device is written to
|
||||
* @file: file structure
|
||||
* @buf: user buffer with data
|
||||
* @len: amount to data written
|
||||
* @ppos: position in file
|
||||
*
|
||||
* returns the number of successfully processed characters, which is always
|
||||
* the number of bytes passed to this function
|
||||
*
|
||||
* wdrtas_write processes all the data given to it and looks for the magic
|
||||
* character 'V'. This character allows the watchdog device to be closed
|
||||
* properly.
|
||||
*/
|
||||
static ssize_t
|
||||
wdrtas_write(struct file *file, const char __user *buf,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
int i;
|
||||
char c;
|
||||
|
||||
if (!len)
|
||||
goto out;
|
||||
|
||||
if (!wdrtas_nowayout) {
|
||||
wdrtas_expect_close = 0;
|
||||
/* look for 'V' */
|
||||
for (i = 0; i < len; i++) {
|
||||
if (get_user(c, buf + i))
|
||||
return -EFAULT;
|
||||
/* allow to close device */
|
||||
if (c == 'V')
|
||||
wdrtas_expect_close = WDRTAS_MAGIC_CHAR;
|
||||
}
|
||||
}
|
||||
|
||||
wdrtas_timer_keepalive();
|
||||
|
||||
out:
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_ioctl - ioctl function for the watchdog device
|
||||
* @inode: inode structure
|
||||
* @file: file structure
|
||||
* @cmd: command for ioctl
|
||||
* @arg: argument pointer
|
||||
*
|
||||
* returns 0 on success, <0 on failure
|
||||
*
|
||||
* wdrtas_ioctl implements the watchdog API ioctls
|
||||
*/
|
||||
static int
|
||||
wdrtas_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int __user *argp = (void *)arg;
|
||||
int i;
|
||||
static struct watchdog_info wdinfo = {
|
||||
.options = WDRTAS_SUPPORTED_MASK,
|
||||
.firmware_version = 0,
|
||||
.identity = "wdrtas"
|
||||
};
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
if (copy_to_user(argp, &wdinfo, sizeof(wdinfo)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case WDIOC_GETSTATUS:
|
||||
i = wdrtas_get_status();
|
||||
return put_user(i, argp);
|
||||
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
i = wdrtas_get_boot_status();
|
||||
return put_user(i, argp);
|
||||
|
||||
case WDIOC_GETTEMP:
|
||||
if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
i = wdrtas_get_temperature();
|
||||
return put_user(i, argp);
|
||||
|
||||
case WDIOC_SETOPTIONS:
|
||||
if (get_user(i, argp))
|
||||
return -EFAULT;
|
||||
if (i & WDIOS_DISABLECARD)
|
||||
wdrtas_timer_stop();
|
||||
if (i & WDIOS_ENABLECARD) {
|
||||
wdrtas_timer_keepalive();
|
||||
wdrtas_timer_start();
|
||||
}
|
||||
if (i & WDIOS_TEMPPANIC) {
|
||||
/* not implemented. Done by H8 */
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WDIOC_KEEPALIVE:
|
||||
wdrtas_timer_keepalive();
|
||||
return 0;
|
||||
|
||||
case WDIOC_SETTIMEOUT:
|
||||
if (get_user(i, argp))
|
||||
return -EFAULT;
|
||||
|
||||
if (wdrtas_set_interval(i))
|
||||
return -EINVAL;
|
||||
|
||||
wdrtas_timer_keepalive();
|
||||
|
||||
if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
|
||||
wdrtas_interval = i;
|
||||
else
|
||||
wdrtas_interval = wdrtas_get_interval(i);
|
||||
/* fallthrough */
|
||||
|
||||
case WDIOC_GETTIMEOUT:
|
||||
return put_user(wdrtas_interval, argp);
|
||||
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_open - open function of watchdog device
|
||||
* @inode: inode structure
|
||||
* @file: file structure
|
||||
*
|
||||
* returns 0 on success, -EBUSY if the file has been opened already, <0 on
|
||||
* other failures
|
||||
*
|
||||
* function called when watchdog device is opened
|
||||
*/
|
||||
static int
|
||||
wdrtas_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
/* only open once */
|
||||
if (atomic_inc_return(&wdrtas_miscdev_open) > 1) {
|
||||
atomic_dec(&wdrtas_miscdev_open);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
wdrtas_timer_start();
|
||||
wdrtas_timer_keepalive();
|
||||
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_close - close function of watchdog device
|
||||
* @inode: inode structure
|
||||
* @file: file structure
|
||||
*
|
||||
* returns 0 on success
|
||||
*
|
||||
* close function. Always succeeds
|
||||
*/
|
||||
static int
|
||||
wdrtas_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
/* only stop watchdog, if this was announced using 'V' before */
|
||||
if (wdrtas_expect_close == WDRTAS_MAGIC_CHAR)
|
||||
wdrtas_timer_stop();
|
||||
else {
|
||||
printk(KERN_WARNING "wdrtas: got unexpected close. Watchdog "
|
||||
"not stopped.\n");
|
||||
wdrtas_timer_keepalive();
|
||||
}
|
||||
|
||||
wdrtas_expect_close = 0;
|
||||
atomic_dec(&wdrtas_miscdev_open);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_temp_read - gives back the temperature in fahrenheit
|
||||
* @file: file structure
|
||||
* @buf: user buffer
|
||||
* @count: number of bytes to be read
|
||||
* @ppos: position in file
|
||||
*
|
||||
* returns always 1 or -EFAULT in case of user space copy failures, <0 on
|
||||
* other failures
|
||||
*
|
||||
* wdrtas_temp_read gives the temperature to the users by copying this
|
||||
* value as one byte into the user space buffer. The unit is Fahrenheit...
|
||||
*/
|
||||
static ssize_t
|
||||
wdrtas_temp_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int temperature = 0;
|
||||
|
||||
temperature = wdrtas_get_temperature();
|
||||
if (temperature < 0)
|
||||
return temperature;
|
||||
|
||||
if (copy_to_user(buf, &temperature, 1))
|
||||
return -EFAULT;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_temp_open - open function of temperature device
|
||||
* @inode: inode structure
|
||||
* @file: file structure
|
||||
*
|
||||
* returns 0 on success, <0 on failure
|
||||
*
|
||||
* function called when temperature device is opened
|
||||
*/
|
||||
static int
|
||||
wdrtas_temp_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_temp_close - close function of temperature device
|
||||
* @inode: inode structure
|
||||
* @file: file structure
|
||||
*
|
||||
* returns 0 on success
|
||||
*
|
||||
* close function. Always succeeds
|
||||
*/
|
||||
static int
|
||||
wdrtas_temp_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_reboot - reboot notifier function
|
||||
* @nb: notifier block structure
|
||||
* @code: reboot code
|
||||
* @ptr: unused
|
||||
*
|
||||
* returns NOTIFY_DONE
|
||||
*
|
||||
* wdrtas_reboot stops the watchdog in case of a reboot
|
||||
*/
|
||||
static int
|
||||
wdrtas_reboot(struct notifier_block *this, unsigned long code, void *ptr)
|
||||
{
|
||||
if ( (code==SYS_DOWN) || (code==SYS_HALT) )
|
||||
wdrtas_timer_stop();
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/*** initialization stuff */
|
||||
|
||||
static struct file_operations wdrtas_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.write = wdrtas_write,
|
||||
.ioctl = wdrtas_ioctl,
|
||||
.open = wdrtas_open,
|
||||
.release = wdrtas_close,
|
||||
};
|
||||
|
||||
static struct miscdevice wdrtas_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &wdrtas_fops,
|
||||
};
|
||||
|
||||
static struct file_operations wdrtas_temp_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.read = wdrtas_temp_read,
|
||||
.open = wdrtas_temp_open,
|
||||
.release = wdrtas_temp_close,
|
||||
};
|
||||
|
||||
static struct miscdevice wdrtas_tempdev = {
|
||||
.minor = TEMP_MINOR,
|
||||
.name = "temperature",
|
||||
.fops = &wdrtas_temp_fops,
|
||||
};
|
||||
|
||||
static struct notifier_block wdrtas_notifier = {
|
||||
.notifier_call = wdrtas_reboot,
|
||||
};
|
||||
|
||||
/**
|
||||
* wdrtas_get_tokens - reads in RTAS tokens
|
||||
*
|
||||
* returns 0 on succes, <0 on failure
|
||||
*
|
||||
* wdrtas_get_tokens reads in the tokens for the RTAS calls used in
|
||||
* this watchdog driver. It tolerates, if "get-sensor-state" and
|
||||
* "ibm,get-system-parameter" are not available.
|
||||
*/
|
||||
static int
|
||||
wdrtas_get_tokens(void)
|
||||
{
|
||||
wdrtas_token_get_sensor_state = rtas_token("get-sensor-state");
|
||||
if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) {
|
||||
printk(KERN_WARNING "wdrtas: couldn't get token for "
|
||||
"get-sensor-state. Trying to continue without "
|
||||
"temperature support.\n");
|
||||
}
|
||||
|
||||
wdrtas_token_get_sp = rtas_token("ibm,get-system-parameter");
|
||||
if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) {
|
||||
printk(KERN_WARNING "wdrtas: couldn't get token for "
|
||||
"ibm,get-system-parameter. Trying to continue with "
|
||||
"a default timeout value of %i seconds.\n",
|
||||
WDRTAS_DEFAULT_INTERVAL);
|
||||
}
|
||||
|
||||
wdrtas_token_set_indicator = rtas_token("set-indicator");
|
||||
if (wdrtas_token_set_indicator == RTAS_UNKNOWN_SERVICE) {
|
||||
printk(KERN_ERR "wdrtas: couldn't get token for "
|
||||
"set-indicator. Terminating watchdog code.\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
wdrtas_token_event_scan = rtas_token("event-scan");
|
||||
if (wdrtas_token_event_scan == RTAS_UNKNOWN_SERVICE) {
|
||||
printk(KERN_ERR "wdrtas: couldn't get token for event-scan. "
|
||||
"Terminating watchdog code.\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_unregister_devs - unregisters the misc dev handlers
|
||||
*
|
||||
* wdrtas_register_devs unregisters the watchdog and temperature watchdog
|
||||
* misc devs
|
||||
*/
|
||||
static void
|
||||
wdrtas_unregister_devs(void)
|
||||
{
|
||||
misc_deregister(&wdrtas_miscdev);
|
||||
if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE)
|
||||
misc_deregister(&wdrtas_tempdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_register_devs - registers the misc dev handlers
|
||||
*
|
||||
* returns 0 on succes, <0 on failure
|
||||
*
|
||||
* wdrtas_register_devs registers the watchdog and temperature watchdog
|
||||
* misc devs
|
||||
*/
|
||||
static int
|
||||
wdrtas_register_devs(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = misc_register(&wdrtas_miscdev);
|
||||
if (result) {
|
||||
printk(KERN_ERR "wdrtas: couldn't register watchdog misc "
|
||||
"device. Terminating watchdog code.\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) {
|
||||
result = misc_register(&wdrtas_tempdev);
|
||||
if (result) {
|
||||
printk(KERN_WARNING "wdrtas: couldn't register "
|
||||
"watchdog temperature misc device. Continuing "
|
||||
"without temperature support.\n");
|
||||
wdrtas_token_get_sensor_state = RTAS_UNKNOWN_SERVICE;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_init - init function of the watchdog driver
|
||||
*
|
||||
* returns 0 on succes, <0 on failure
|
||||
*
|
||||
* registers the file handlers and the reboot notifier
|
||||
*/
|
||||
static int __init
|
||||
wdrtas_init(void)
|
||||
{
|
||||
if (wdrtas_get_tokens())
|
||||
return -ENODEV;
|
||||
|
||||
if (wdrtas_register_devs())
|
||||
return -ENODEV;
|
||||
|
||||
if (register_reboot_notifier(&wdrtas_notifier)) {
|
||||
printk(KERN_ERR "wdrtas: could not register reboot notifier. "
|
||||
"Terminating watchdog code.\n");
|
||||
wdrtas_unregister_devs();
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
|
||||
wdrtas_interval = WDRTAS_DEFAULT_INTERVAL;
|
||||
else
|
||||
wdrtas_interval = wdrtas_get_interval(WDRTAS_DEFAULT_INTERVAL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* wdrtas_exit - exit function of the watchdog driver
|
||||
*
|
||||
* unregisters the file handlers and the reboot notifier
|
||||
*/
|
||||
static void __exit
|
||||
wdrtas_exit(void)
|
||||
{
|
||||
if (!wdrtas_nowayout)
|
||||
wdrtas_timer_stop();
|
||||
|
||||
wdrtas_unregister_devs();
|
||||
|
||||
unregister_reboot_notifier(&wdrtas_notifier);
|
||||
}
|
||||
|
||||
module_init(wdrtas_init);
|
||||
module_exit(wdrtas_exit);
|
|
@ -76,6 +76,7 @@ struct machdep_calls {
|
|||
void (*tce_flush)(struct iommu_table *tbl);
|
||||
void (*iommu_dev_setup)(struct pci_dev *dev);
|
||||
void (*iommu_bus_setup)(struct pci_bus *bus);
|
||||
void (*irq_bus_setup)(struct pci_bus *bus);
|
||||
|
||||
int (*probe)(int platform);
|
||||
void (*setup_arch)(void);
|
||||
|
|
|
@ -47,9 +47,10 @@
|
|||
#define SLB_VSID_KS ASM_CONST(0x0000000000000800)
|
||||
#define SLB_VSID_KP ASM_CONST(0x0000000000000400)
|
||||
#define SLB_VSID_N ASM_CONST(0x0000000000000200) /* no-execute */
|
||||
#define SLB_VSID_L ASM_CONST(0x0000000000000100) /* largepage 16M */
|
||||
#define SLB_VSID_L ASM_CONST(0x0000000000000100) /* largepage */
|
||||
#define SLB_VSID_C ASM_CONST(0x0000000000000080) /* class */
|
||||
|
||||
#define SLB_VSID_LS ASM_CONST(0x0000000000000070) /* size of largepage */
|
||||
|
||||
#define SLB_VSID_KERNEL (SLB_VSID_KP|SLB_VSID_C)
|
||||
#define SLB_VSID_USER (SLB_VSID_KP|SLB_VSID_KS)
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ extern struct nvram_partition *nvram_find_partition(int sig, const char *name);
|
|||
|
||||
extern int pSeries_nvram_init(void);
|
||||
extern int pmac_nvram_init(void);
|
||||
extern int bpa_nvram_init(void);
|
||||
|
||||
/* PowerMac specific nvram stuffs */
|
||||
|
||||
|
|
|
@ -138,8 +138,16 @@
|
|||
#define SPRN_NIADORM 0x3F3 /* Hardware Implementation Register 2 */
|
||||
#define SPRN_HID4 0x3F4 /* 970 HID4 */
|
||||
#define SPRN_HID5 0x3F6 /* 970 HID5 */
|
||||
#define SPRN_TSC 0x3FD /* Thread switch control */
|
||||
#define SPRN_TST 0x3FC /* Thread switch timeout */
|
||||
#define SPRN_HID6 0x3F9 /* BE HID 6 */
|
||||
#define HID6_LB (0x0F<<12) /* Concurrent Large Page Modes */
|
||||
#define HID6_DLP (1<<20) /* Disable all large page modes (4K only) */
|
||||
#define SPRN_TSCR 0x399 /* Thread switch control on BE */
|
||||
#define SPRN_TTR 0x39A /* Thread switch timeout on BE */
|
||||
#define TSCR_DEC_ENABLE 0x200000 /* Decrementer Interrupt */
|
||||
#define TSCR_EE_ENABLE 0x100000 /* External Interrupt */
|
||||
#define TSCR_EE_BOOST 0x080000 /* External Interrupt Boost */
|
||||
#define SPRN_TSC 0x3FD /* Thread switch control on others */
|
||||
#define SPRN_TST 0x3FC /* Thread switch timeout on others */
|
||||
#define SPRN_L2CR 0x3F9 /* Level 2 Cache Control Regsiter */
|
||||
#define SPRN_LR 0x008 /* Link Register */
|
||||
#define SPRN_PIR 0x3FF /* Processor Identification Register */
|
||||
|
@ -259,6 +267,7 @@
|
|||
#define PV_970FX 0x003C
|
||||
#define PV_630 0x0040
|
||||
#define PV_630p 0x0041
|
||||
#define PV_BE 0x0070
|
||||
|
||||
/* Platforms supported by PPC64 */
|
||||
#define PLATFORM_PSERIES 0x0100
|
||||
|
@ -267,6 +276,7 @@
|
|||
#define PLATFORM_LPAR 0x0001
|
||||
#define PLATFORM_POWERMAC 0x0400
|
||||
#define PLATFORM_MAPLE 0x0500
|
||||
#define PLATFORM_BPA 0x1000
|
||||
|
||||
/* Compatibility with drivers coming from PPC32 world */
|
||||
#define _machine (systemcfg->platform)
|
||||
|
@ -278,6 +288,7 @@
|
|||
#define IC_INVALID 0
|
||||
#define IC_OPEN_PIC 1
|
||||
#define IC_PPC_XIC 2
|
||||
#define IC_BPA_IIC 3
|
||||
|
||||
#define XGLUE(a,b) a##b
|
||||
#define GLUE(a,b) XGLUE(a,b)
|
||||
|
|
|
@ -186,8 +186,14 @@ extern int rtas_get_sensor(int sensor, int index, int *state);
|
|||
extern int rtas_get_power_level(int powerdomain, int *level);
|
||||
extern int rtas_set_power_level(int powerdomain, int level, int *setlevel);
|
||||
extern int rtas_set_indicator(int indicator, int index, int new_value);
|
||||
extern void rtas_progress(char *s, unsigned short hex);
|
||||
extern void rtas_initialize(void);
|
||||
|
||||
struct rtc_time;
|
||||
extern void rtas_get_boot_time(struct rtc_time *rtc_time);
|
||||
extern void rtas_get_rtc_time(struct rtc_time *rtc_time);
|
||||
extern int rtas_set_rtc_time(struct rtc_time *rtc_time);
|
||||
|
||||
/* Given an RTAS status code of 9900..9905 compute the hinted delay */
|
||||
unsigned int rtas_extended_busy_delay_time(int status);
|
||||
static inline int rtas_is_extended_busy(int status)
|
||||
|
|
|
@ -85,6 +85,14 @@ extern void smp_generic_take_timebase(void);
|
|||
|
||||
extern struct smp_ops_t *smp_ops;
|
||||
|
||||
#ifdef CONFIG_PPC_PSERIES
|
||||
void vpa_init(int cpu);
|
||||
#else
|
||||
static inline void vpa_init(int cpu)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_PPC_PSERIES */
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* !(_PPC64_SMP_H) */
|
||||
|
|
|
@ -34,6 +34,15 @@ struct rtc_time;
|
|||
extern void to_tm(int tim, struct rtc_time * tm);
|
||||
extern time_t last_rtc_update;
|
||||
|
||||
void generic_calibrate_decr(void);
|
||||
void setup_default_decr(void);
|
||||
|
||||
/* Some sane defaults: 125 MHz timebase, 1GHz processor */
|
||||
extern unsigned long ppc_proc_freq;
|
||||
#define DEFAULT_PROC_FREQ (DEFAULT_TB_FREQ * 8)
|
||||
extern unsigned long ppc_tb_freq;
|
||||
#define DEFAULT_TB_FREQ 125000000UL
|
||||
|
||||
/*
|
||||
* By putting all of this stuff into a single struct we
|
||||
* reduce the number of cache lines touched by do_gettimeofday.
|
||||
|
|
Loading…
Reference in New Issue